diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..c686ec1f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,290 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +[*] +trim_trailing_whitespace = true +insert_final_newline = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_readonly_field = true:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_allow_multiple_blank_lines_experimental = true:silent +dotnet_style_allow_statement_immediately_after_block_experimental = true:silent +dotnet_code_quality_unused_parameters = all:suggestion +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_event = false:silent +dotnet_style_qualification_for_method = false:silent + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = true +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Types: use keywords instead of BCL types, and permit var only when the type is clear +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = false:none +csharp_style_var_elsewhere = false:suggestion + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Expression-level preferences +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = true + +# Parameter preferences +dotnet_code_quality_unused_parameters = all + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +# New line preferences +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = false:silent +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = false:silent + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_prefer_switch_expression = true:suggestion + +# Null-checking preferences +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_prefer_static_local_function = true:suggestion +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async + +# Code-block preferences +csharp_prefer_braces = true:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = block_scoped:silent + +# Expression-level preferences +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:silent + +# New line preferences +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true:none +csharp_preserve_single_line_statements = true:none + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.private_or_internal_field_should_be__fieldname.severity = suggestion +dotnet_naming_rule.private_or_internal_field_should_be__fieldname.symbols = private_or_internal_field +dotnet_naming_rule.private_or_internal_field_should_be__fieldname.style = _fieldname + +# name all constant fields using PascalCase +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.required_modifiers = const +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field +dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected +dotnet_naming_symbols.private_or_internal_field.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style._fieldname.required_prefix = _ +dotnet_naming_style._fieldname.required_suffix = +dotnet_naming_style._fieldname.word_separator = +dotnet_naming_style._fieldname.capitalization = camel_case +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_prefer_readonly_struct = true:suggestion +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..dfe07704 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..d33d9275 --- /dev/null +++ b/.gitignore @@ -0,0 +1,404 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Nuget personal access tokens and Credentials +nuget.config + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +.idea/ +*.sln.iml +.vs/Wino/v16/.suo +.vs/Wino/v16/.suo +*.v2 +Wino/obj/x86/Debug/App.g.cs +Wino/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache +Wino/obj/x86/Debug/XamlSaveStateFile.xml +.vs/Wino/v16/.suo +.vs/Wino/DesignTimeBuild/.dtbcache.v2 +.vs/Wino/v16/.suo +*.v2 +Wino/obj/x86/Debug/XamlSaveStateFile.xml +Wino/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache +Wino/obj/x86/Debug/XamlSaveStateFile.xml +Wino/obj/x86/Debug/XamlSaveStateFile.xml +*.cache +.vs/Wino/v16/.suo diff --git a/Wino.BackgroundTasks/AppUpdatedTask.cs b/Wino.BackgroundTasks/AppUpdatedTask.cs new file mode 100644 index 00000000..2818a6a2 --- /dev/null +++ b/Wino.BackgroundTasks/AppUpdatedTask.cs @@ -0,0 +1,34 @@ +using Microsoft.Toolkit.Uwp.Notifications; +using Windows.ApplicationModel; +using Windows.ApplicationModel.Background; +using Wino.Core.Domain; + +namespace Wino.BackgroundTasks +{ + /// + /// Creates a toast notification to notify user when the Store update happens. + /// + public sealed class AppUpdatedTask : IBackgroundTask + { + public void Run(IBackgroundTaskInstance taskInstance) + { + var def = taskInstance.GetDeferral(); + + var builder = new ToastContentBuilder(); + builder.SetToastScenario(ToastScenario.Default); + + Package package = Package.Current; + PackageId packageId = package.Id; + PackageVersion version = packageId.Version; + + var versionText = string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build, version.Revision); + + builder.AddText(Translator.Notifications_WinoUpdatedTitle); + builder.AddText(string.Format(Translator.Notifications_WinoUpdatedMessage, versionText)); + + builder.Show(); + + def.Complete(); + } + } +} diff --git a/Wino.BackgroundTasks/Properties/AssemblyInfo.cs b/Wino.BackgroundTasks/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..9887885d --- /dev/null +++ b/Wino.BackgroundTasks/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Wino.BackgroundTasks")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Wino.BackgroundTasks")] +[assembly: AssemblyCopyright("Copyright © 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/Wino.BackgroundTasks/SessionConnectedTask.cs b/Wino.BackgroundTasks/SessionConnectedTask.cs new file mode 100644 index 00000000..90db6769 --- /dev/null +++ b/Wino.BackgroundTasks/SessionConnectedTask.cs @@ -0,0 +1,48 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Serilog; +using Windows.ApplicationModel.Background; +using Windows.Storage; +using Wino.Core; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Services; +using Wino.Core.UWP; +using Wino.Services; + +namespace Wino.BackgroundTasks +{ + public sealed class SessionConnectedTask : IBackgroundTask + { + public async void Run(IBackgroundTaskInstance taskInstance) + { + var def = taskInstance.GetDeferral(); + + try + { + var services = new ServiceCollection(); + + services.RegisterCoreServices(); + services.RegisterCoreUWPServices(); + + var providere = services.BuildServiceProvider(); + + var backgroundTaskService = providere.GetService(); + var dbService = providere.GetService(); + var logInitializer = providere.GetService(); + + logInitializer.SetupLogger(ApplicationData.Current.LocalFolder.Path); + + await dbService.InitializeAsync(); + await backgroundTaskService.RunBackgroundSynchronizationAsync(Core.Domain.Enums.BackgroundSynchronizationReason.SessionConnected); + } + catch (Exception ex) + { + Log.Error(ex, "Background synchronization failed from background task."); + } + finally + { + def.Complete(); + } + } + } +} diff --git a/Wino.BackgroundTasks/Wino.BackgroundTasks.csproj b/Wino.BackgroundTasks/Wino.BackgroundTasks.csproj new file mode 100644 index 00000000..94ab7119 --- /dev/null +++ b/Wino.BackgroundTasks/Wino.BackgroundTasks.csproj @@ -0,0 +1,166 @@ + + + + + Debug + AnyCPU + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08} + winmdobj + Properties + Wino.BackgroundTasks + Wino.BackgroundTasks + en-US + UAP + 10.0.22621.0 + 10.0.17763.0 + 14 + 512 + {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + false + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + prompt + 4 + + + x86 + true + bin\x86\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + x86 + bin\x86\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + ARM + true + bin\ARM\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + ARM + bin\ARM\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + ARM64 + true + bin\ARM64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + ARM64 + bin\ARM64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + x64 + true + bin\x64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + x64 + bin\x64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + PackageReference + + + + + + + + + 6.2.14 + + + 7.1.3 + + + + + {CF3312E5-5DA0-4867-9945-49EA7598AF1F} + Wino.Core.Domain + + + {395f19ba-1e42-495c-9db5-1a6f537fccb8} + Wino.Core.UWP + + + {e6b1632a-8901-41e8-9ddf-6793c7698b0b} + Wino.Core + + + + + Windows Desktop Extensions for the UWP + + + + 14.0 + + + + \ No newline at end of file diff --git a/Wino.Calendar/App.xaml b/Wino.Calendar/App.xaml new file mode 100644 index 00000000..5707d1ac --- /dev/null +++ b/Wino.Calendar/App.xaml @@ -0,0 +1,7 @@ + + + diff --git a/Wino.Calendar/App.xaml.cs b/Wino.Calendar/App.xaml.cs new file mode 100644 index 00000000..971e694b --- /dev/null +++ b/Wino.Calendar/App.xaml.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.ApplicationModel; +using Windows.ApplicationModel.Activation; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; + +namespace Wino.Calendar +{ + /// + /// Provides application-specific behavior to supplement the default Application class. + /// + sealed partial class App : Application + { + /// + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public App() + { + this.InitializeComponent(); + this.Suspending += OnSuspending; + } + + /// + /// Invoked when the application is launched normally by the end user. Other entry points + /// will be used such as when the application is launched to open a specific file. + /// + /// Details about the launch request and process. + protected override void OnLaunched(LaunchActivatedEventArgs e) + { + Frame rootFrame = Window.Current.Content as Frame; + + // Do not repeat app initialization when the Window already has content, + // just ensure that the window is active + if (rootFrame == null) + { + // Create a Frame to act as the navigation context and navigate to the first page + rootFrame = new Frame(); + + rootFrame.NavigationFailed += OnNavigationFailed; + + if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) + { + //TODO: Load state from previously suspended application + } + + // Place the frame in the current Window + Window.Current.Content = rootFrame; + } + + if (e.PrelaunchActivated == false) + { + if (rootFrame.Content == null) + { + // When the navigation stack isn't restored navigate to the first page, + // configuring the new page by passing required information as a navigation + // parameter + rootFrame.Navigate(typeof(MainPage), e.Arguments); + } + // Ensure the current window is active + Window.Current.Activate(); + } + } + + /// + /// Invoked when Navigation to a certain page fails + /// + /// The Frame which failed navigation + /// Details about the navigation failure + void OnNavigationFailed(object sender, NavigationFailedEventArgs e) + { + throw new Exception("Failed to load Page " + e.SourcePageType.FullName); + } + + /// + /// Invoked when application execution is being suspended. Application state is saved + /// without knowing whether the application will be terminated or resumed with the contents + /// of memory still intact. + /// + /// The source of the suspend request. + /// Details about the suspend request. + private void OnSuspending(object sender, SuspendingEventArgs e) + { + var deferral = e.SuspendingOperation.GetDeferral(); + //TODO: Save application state and stop any background activity + deferral.Complete(); + } + } +} diff --git a/Wino.Calendar/Assets/LockScreenLogo.scale-200.png b/Wino.Calendar/Assets/LockScreenLogo.scale-200.png new file mode 100644 index 00000000..735f57ad Binary files /dev/null and b/Wino.Calendar/Assets/LockScreenLogo.scale-200.png differ diff --git a/Wino.Calendar/Assets/SplashScreen.scale-200.png b/Wino.Calendar/Assets/SplashScreen.scale-200.png new file mode 100644 index 00000000..023e7f1f Binary files /dev/null and b/Wino.Calendar/Assets/SplashScreen.scale-200.png differ diff --git a/Wino.Calendar/Assets/Square150x150Logo.scale-200.png b/Wino.Calendar/Assets/Square150x150Logo.scale-200.png new file mode 100644 index 00000000..af49fec1 Binary files /dev/null and b/Wino.Calendar/Assets/Square150x150Logo.scale-200.png differ diff --git a/Wino.Calendar/Assets/Square44x44Logo.scale-200.png b/Wino.Calendar/Assets/Square44x44Logo.scale-200.png new file mode 100644 index 00000000..ce342a2e Binary files /dev/null and b/Wino.Calendar/Assets/Square44x44Logo.scale-200.png differ diff --git a/Wino.Calendar/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/Wino.Calendar/Assets/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 00000000..f6c02ce9 Binary files /dev/null and b/Wino.Calendar/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/Wino.Calendar/Assets/StoreLogo.png b/Wino.Calendar/Assets/StoreLogo.png new file mode 100644 index 00000000..7385b56c Binary files /dev/null and b/Wino.Calendar/Assets/StoreLogo.png differ diff --git a/Wino.Calendar/Assets/Wide310x150Logo.scale-200.png b/Wino.Calendar/Assets/Wide310x150Logo.scale-200.png new file mode 100644 index 00000000..288995b3 Binary files /dev/null and b/Wino.Calendar/Assets/Wide310x150Logo.scale-200.png differ diff --git a/Wino.Calendar/MainPage.xaml b/Wino.Calendar/MainPage.xaml new file mode 100644 index 00000000..136e046c --- /dev/null +++ b/Wino.Calendar/MainPage.xaml @@ -0,0 +1,14 @@ + + + + + + diff --git a/Wino.Calendar/MainPage.xaml.cs b/Wino.Calendar/MainPage.xaml.cs new file mode 100644 index 00000000..281f422f --- /dev/null +++ b/Wino.Calendar/MainPage.xaml.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 + +namespace Wino.Calendar +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class MainPage : Page + { + public MainPage() + { + this.InitializeComponent(); + } + } +} diff --git a/Wino.Calendar/Package.appxmanifest b/Wino.Calendar/Package.appxmanifest new file mode 100644 index 00000000..6e261c84 --- /dev/null +++ b/Wino.Calendar/Package.appxmanifest @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + Wino Calendar + Burak KÖSE + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Wino.Calendar/Properties/AssemblyInfo.cs b/Wino.Calendar/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..979badd3 --- /dev/null +++ b/Wino.Calendar/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Wino.Calendar")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Wino.Calendar")] +[assembly: AssemblyCopyright("Copyright © 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/Wino.Calendar/Properties/Default.rd.xml b/Wino.Calendar/Properties/Default.rd.xml new file mode 100644 index 00000000..af00722c --- /dev/null +++ b/Wino.Calendar/Properties/Default.rd.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Wino.Calendar/Wino.Calendar.csproj b/Wino.Calendar/Wino.Calendar.csproj new file mode 100644 index 00000000..2a0e6530 --- /dev/null +++ b/Wino.Calendar/Wino.Calendar.csproj @@ -0,0 +1,172 @@ + + + + + Debug + x86 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35} + AppContainerExe + Properties + Wino.Calendar + Wino.Calendar + en-US + UAP + 10.0.22621.0 + 10.0.17763.0 + 14 + 512 + {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + true + True + 125A5273FCFE8D551C3FED87F67C87A663E98F1B + + True + + + true + bin\x86\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + x86 + false + prompt + true + + + bin\x86\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + x86 + false + prompt + true + true + + + true + bin\ARM\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + ARM + false + prompt + true + + + bin\ARM\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + ARM + false + prompt + true + true + + + true + bin\ARM64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + ARM64 + false + prompt + true + true + + + bin\ARM64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + ARM64 + false + prompt + true + true + + + true + bin\x64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + x64 + false + prompt + true + + + bin\x64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + x64 + false + prompt + true + true + + + PackageReference + + + + App.xaml + + + MainPage.xaml + + + + + + Designer + + + + + + + + + + + + + + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + + + 6.2.14 + + + + 14.0 + + + + \ No newline at end of file diff --git a/Wino.Core.Domain/Constants.cs b/Wino.Core.Domain/Constants.cs new file mode 100644 index 00000000..10046f85 --- /dev/null +++ b/Wino.Core.Domain/Constants.cs @@ -0,0 +1,14 @@ +namespace Wino.Core.Domain +{ + public static class Constants + { + /// + /// MIME header that exists in all the drafts created from Wino. + /// + public const string WinoLocalDraftHeader = "X-Wino-Draft-Id"; + public const string LocalDraftStartPrefix = "localDraft_"; + + public const string ToastMailItemIdKey = nameof(ToastMailItemIdKey); + public const string ToastActionKey = nameof(ToastActionKey); + } +} diff --git a/Wino.Core.Domain/Entities/AccountSignature.cs b/Wino.Core.Domain/Entities/AccountSignature.cs new file mode 100644 index 00000000..6c3c6841 --- /dev/null +++ b/Wino.Core.Domain/Entities/AccountSignature.cs @@ -0,0 +1,13 @@ +using System; +using SQLite; + +namespace Wino.Core.Domain.Entities +{ + public class AccountSignature + { + [PrimaryKey] + public Guid Id { get; set; } + + public string HtmlBody { get; set; } + } +} diff --git a/Wino.Core.Domain/Entities/AddressInformation.cs b/Wino.Core.Domain/Entities/AddressInformation.cs new file mode 100644 index 00000000..de2bc308 --- /dev/null +++ b/Wino.Core.Domain/Entities/AddressInformation.cs @@ -0,0 +1,53 @@ +using SQLite; +using System; +using System.Collections.Generic; + +namespace Wino.Core.Domain.Entities +{ + /// + /// Back storage for simple name-address book. + /// These values will be inserted during MIME fetch. + /// + + + // TODO: This can easily evolve to Contact store, just like People app in Windows 10/11. + // Do it. + public class AddressInformation : IEquatable + { + [PrimaryKey] + public string Address { get; set; } + public string Name { get; set; } + + public string DisplayName => Address == Name ? Address : $"{Name} <{Address}>"; + + public override bool Equals(object obj) + { + return Equals(obj as AddressInformation); + } + + public bool Equals(AddressInformation other) + { + return !(other is null) && + Address == other.Address && + Name == other.Name; + } + + public override int GetHashCode() + { + int hashCode = -1717786383; + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Address); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Name); + return hashCode; + } + + public static bool operator ==(AddressInformation left, AddressInformation right) + { + return EqualityComparer.Default.Equals(left, right); + } + + public static bool operator !=(AddressInformation left, AddressInformation right) + { + return !(left == right); + } + } +} diff --git a/Wino.Core.Domain/Entities/CustomServerInformation.cs b/Wino.Core.Domain/Entities/CustomServerInformation.cs new file mode 100644 index 00000000..3fc99b76 --- /dev/null +++ b/Wino.Core.Domain/Entities/CustomServerInformation.cs @@ -0,0 +1,49 @@ +using System; +using SQLite; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Entities +{ + public class CustomServerInformation + { + [PrimaryKey] + public Guid Id { get; set; } + + public Guid AccountId { get; set; } + + public string DisplayName { get; set; } + public string Address { get; set; } + public string IncomingServer { get; set; } + public string IncomingServerUsername { get; set; } + public string IncomingServerPassword { get; set; } + public string IncomingServerPort { get; set; } + + public CustomIncomingServerType IncomingServerType { get; set; } + + public string OutgoingServer { get; set; } + public string OutgoingServerPort { get; set; } + public string OutgoingServerUsername { get; set; } + public string OutgoingServerPassword { get; set; } + + /// + /// useSSL True: SslOnConnect + /// useSSL False: StartTlsWhenAvailable + /// + + public ImapConnectionSecurity IncomingServerSocketOption { get; set; } + public ImapAuthenticationMethod IncomingAuthenticationMethod { get; set; } + + + public ImapConnectionSecurity OutgoingServerSocketOption { get; set; } + public ImapAuthenticationMethod OutgoingAuthenticationMethod { get; set; } + + public string ProxyServer { get; set; } + public string ProxyServerPort { get; set; } + + [Obsolete("As 1.7.0")] + public bool IncomingRequiresSSL { get; set; } + + [Obsolete("As 1.7.0")] + public bool OutgoingRequresSSL { get; set; } + } +} diff --git a/Wino.Core.Domain/Entities/MailAccount.cs b/Wino.Core.Domain/Entities/MailAccount.cs new file mode 100644 index 00000000..2ca32057 --- /dev/null +++ b/Wino.Core.Domain/Entities/MailAccount.cs @@ -0,0 +1,78 @@ +using System; +using SQLite; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Entities +{ + public class MailAccount + { + [PrimaryKey] + public Guid Id { get; set; } + + /// + /// Given name of the account in Wino. + /// + public string Name { get; set; } + + /// + /// TODO: Display name of the authenticated user/account. + /// API integrations will query this value from the API. + /// IMAP is populated by user on setup dialog. + /// + + public string ProfileName { get; set; } + + /// + /// Account e-mail address. + /// + public string Address { get; set; } + + /// + /// Provider type of the account. Outlook,Gmail etc... + /// + public MailProviderType ProviderType { get; set; } + + /// + /// For tracking change delta. + /// Gmail : historyId + /// Outlook: deltaToken + /// + public string SynchronizationDeltaIdentifier { get; set; } + + /// + /// Gets or sets the signature to be used for this account. + /// Null if no signature should be used. + /// + public Guid? SignatureId { get; set; } + + /// + /// Gets or sets whether the account has any reason for an interactive user action to fix continue operating. + /// + public AccountAttentionReason AttentionReason { get; set; } + + /// + /// Gets or sets the id of the merged inbox this account belongs to. + /// + public Guid? MergedInboxId { get; set; } + + /// + /// Contains the merged inbox this account belongs to. + /// Ignored for all SQLite operations. + /// + [Ignore] + public MergedInbox MergedInbox { get; set; } + + /// + /// Populated only when account has custom server information. + /// + + [Ignore] + public CustomServerInformation ServerInformation { get; set; } + + /// + /// Account preferences. + /// + [Ignore] + public MailAccountPreferences Preferences { get; set; } + } +} diff --git a/Wino.Core.Domain/Entities/MailAccountPreferences.cs b/Wino.Core.Domain/Entities/MailAccountPreferences.cs new file mode 100644 index 00000000..3bcd23cd --- /dev/null +++ b/Wino.Core.Domain/Entities/MailAccountPreferences.cs @@ -0,0 +1,39 @@ +using System; +using SQLite; + +namespace Wino.Core.Domain.Entities +{ + public class MailAccountPreferences + { + [PrimaryKey] + public Guid Id { get; set; } + + /// + /// Id of the account in MailAccount table. + /// + public Guid AccountId { get; set; } + + /// + /// Gets or sets whether sent draft messages should be appended to the sent folder. + /// Some IMAP servers do this automatically, some don't. + /// It's disabled by default. + /// + public bool ShouldAppendMessagesToSentFolder { get; set; } + + /// + /// Gets or sets whether the notifications are enabled for the account. + /// + public bool IsNotificationsEnabled { get; set; } + + /// + /// Gets or sets the custom account identifier color in hex. + /// + public string AccountColorHex { get; set; } + + /// + /// Gets or sets whether the account has Focused inbox support. + /// Null if the account provider type doesn't support Focused inbox. + /// + public bool? IsFocusedInboxEnabled { get; set; } + } +} diff --git a/Wino.Core.Domain/Entities/MailCopy.cs b/Wino.Core.Domain/Entities/MailCopy.cs new file mode 100644 index 00000000..de56aa79 --- /dev/null +++ b/Wino.Core.Domain/Entities/MailCopy.cs @@ -0,0 +1,140 @@ +using System; +using SQLite; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.MailItem; + +namespace Wino.Core.Domain.Entities +{ + /// + /// Summary of the parsed MIME messages. + /// Wino will do non-network operations on this table and others from the original MIME. + /// + public class MailCopy : IMailItem + { + /// + /// Unique Id of the mail. + /// + [PrimaryKey] + public Guid UniqueId { get; set; } + + /// + /// Not unique id of the item. Some operations held on this Id, some on the UniqueId. + /// Same message can be in different folder. In that case UniqueId is used. + /// + public string Id { get; set; } + + /// + /// Folder that this mail belongs to. + /// + public Guid FolderId { get; set; } + + /// + /// Conversation id for the mail. + /// + public string ThreadId { get; set; } + + /// + /// MIME MessageId if exists. + /// + public string MessageId { get; set; } + + /// + /// References header from MIME + /// + public string References { get; set; } + + /// + /// In-Reply-To header from MIME + /// + public string InReplyTo { get; set; } + + /// + /// Name for the sender. + /// + public string FromName { get; set; } + + /// + /// Address of the sender. + /// + public string FromAddress { get; set; } + + /// + /// Subject of the mail. + /// + public string Subject { get; set; } + + /// + /// Short preview of the content. + /// + public string PreviewText { get; set; } + + /// + /// Date that represents this mail has been created in provider servers. + /// Stored always in UTC. + /// + public DateTime CreationDate { get; set; } + + /// + /// Importance of the mail. + /// + public MailImportance Importance { get; set; } + + /// + /// Read status for the mail. + /// + public bool IsRead { get; set; } + + /// + /// Flag status. + /// Flagged for Outlook. + /// Important for Gmail. + /// + public bool IsFlagged { get; set; } + + /// + /// To support Outlook. + /// Gmail doesn't use it. + /// + public bool IsFocused { get; set; } + + /// + /// Whether mail has attachments included or not. + /// + public bool HasAttachments { get; set; } + + /// + /// Assigned draft id. + /// + public string DraftId { get; set; } + + /// + /// Whether this copy is draft or not. + /// + public bool IsDraft { get; set; } + + /// + /// File id that this mail is assigned to. + /// This Id is immutable. It's used to find the file in the file system. + /// Even after mapping local draft to remote draft, it will not change. + /// + public Guid FileId { get; set; } + + /// + /// Folder that this mail is assigned to. + /// Warning: This field is not populated by queries. + /// Services or View Models are responsible for populating this field. + /// + [Ignore] + public MailItemFolder AssignedFolder { get; set; } + + /// + /// Account that this mail is assigned to. + /// Warning: This field is not populated by queries. + /// Services or View Models are responsible for populating this field. + /// + [Ignore] + public MailAccount AssignedAccount { get; set; } + + public override string ToString() => $"{Subject} <-> {Id}"; + } +} diff --git a/Wino.Core.Domain/Entities/MailItemFolder.cs b/Wino.Core.Domain/Entities/MailItemFolder.cs new file mode 100644 index 00000000..d33fbb15 --- /dev/null +++ b/Wino.Core.Domain/Entities/MailItemFolder.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using SQLite; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Folders; + +namespace Wino.Core.Domain.Entities +{ + [DebuggerDisplay("{FolderName} - {SpecialFolderType}")] + public class MailItemFolder : IMailItemFolder + { + [PrimaryKey] + public Guid Id { get; set; } + + public string RemoteFolderId { get; set; } + public string ParentRemoteFolderId { get; set; } + + public Guid MailAccountId { get; set; } + public string FolderName { get; set; } + public SpecialFolderType SpecialFolderType { get; set; } + public bool IsSystemFolder { get; set; } + public bool IsSticky { get; set; } + public bool IsSynchronizationEnabled { get; set; } + public bool IsHidden { get; set; } + public bool ShowUnreadCount { get; set; } + public DateTime? LastSynchronizedDate { get; set; } + + // For IMAP + public uint UidValidity { get; set; } + public long HighestModeSeq { get; set; } + + /// + /// Outlook shares delta changes per-folder. Gmail is for per-account. + /// This is only used for Outlook provider. + /// + public string DeltaToken { get; set; } + + // For GMail Labels + public string TextColorHex { get; set; } + public string BackgroundColorHex { get; set; } + + [Ignore] + public List ChildFolders { get; set; } = []; + + // Category and Move type folders are not valid move targets. + // These folders are virtual. They don't exist on the server. + public bool IsMoveTarget => !(SpecialFolderType == SpecialFolderType.More || SpecialFolderType == SpecialFolderType.Category); + + public bool ContainsSpecialFolderType(SpecialFolderType type) + { + if (SpecialFolderType == type) + return true; + + foreach (var child in ChildFolders) + { + if (child.SpecialFolderType == type) + { + return true; + } + else + { + return child.ContainsSpecialFolderType(type); + } + } + + return false; + } + + public override string ToString() => FolderName; + } +} diff --git a/Wino.Core.Domain/Entities/MergedInbox.cs b/Wino.Core.Domain/Entities/MergedInbox.cs new file mode 100644 index 00000000..b9d0a58a --- /dev/null +++ b/Wino.Core.Domain/Entities/MergedInbox.cs @@ -0,0 +1,13 @@ +using System; +using SQLite; + +namespace Wino.Core.Domain.Entities +{ + public class MergedInbox + { + [PrimaryKey] + public Guid Id { get; set; } + + public string Name { get; set; } + } +} diff --git a/Wino.Core.Domain/Entities/SystemFolderConfiguration.cs b/Wino.Core.Domain/Entities/SystemFolderConfiguration.cs new file mode 100644 index 00000000..b4c532d1 --- /dev/null +++ b/Wino.Core.Domain/Entities/SystemFolderConfiguration.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Wino.Core.Domain.Entities +{ + public record SystemFolderConfiguration(MailItemFolder SentFolder, + MailItemFolder DraftFolder, + MailItemFolder ArchiveFolder, + MailItemFolder TrashFolder, + MailItemFolder JunkFolder); +} diff --git a/Wino.Core.Domain/Entities/TokenInformation.cs b/Wino.Core.Domain/Entities/TokenInformation.cs new file mode 100644 index 00000000..88bf292c --- /dev/null +++ b/Wino.Core.Domain/Entities/TokenInformation.cs @@ -0,0 +1,26 @@ +using System; +using SQLite; +using Wino.Core.Domain.Models.Authentication; + +namespace Wino.Core.Domain.Entities +{ + public class TokenInformation : TokenInformationBase + { + [PrimaryKey] + public Guid Id { get; set; } + + public Guid AccountId { get; set; } + + public string Address { get; set; } + + public void RefreshTokens(TokenInformationBase tokenInformationBase) + { + if (tokenInformationBase == null) + throw new ArgumentNullException(nameof(tokenInformationBase)); + + AccessToken = tokenInformationBase.AccessToken; + RefreshToken = tokenInformationBase.RefreshToken; + ExpiresAt = tokenInformationBase.ExpiresAt; + } + } +} diff --git a/Wino.Core.Domain/Enums/AccountAttentionReason.cs b/Wino.Core.Domain/Enums/AccountAttentionReason.cs new file mode 100644 index 00000000..b192e74f --- /dev/null +++ b/Wino.Core.Domain/Enums/AccountAttentionReason.cs @@ -0,0 +1,9 @@ +namespace Wino.Core.Domain.Enums +{ + public enum AccountAttentionReason + { + None, + InvalidCredentials, + MissingSystemFolderConfiguration + } +} diff --git a/Wino.Core.Domain/Enums/AccountCreationDialogState.cs b/Wino.Core.Domain/Enums/AccountCreationDialogState.cs new file mode 100644 index 00000000..af724f47 --- /dev/null +++ b/Wino.Core.Domain/Enums/AccountCreationDialogState.cs @@ -0,0 +1,14 @@ +namespace Wino.Core.Domain.Enums +{ + public enum AccountCreationDialogState + { + Idle, + SigningIn, + PreparingFolders, + Completed, + ManuelSetupWaiting, + TestingConnection, + AutoDiscoverySetup, + AutoDiscoveryInProgress + } +} diff --git a/Wino.Core.Domain/Enums/AccountSynchronizerState.cs b/Wino.Core.Domain/Enums/AccountSynchronizerState.cs new file mode 100644 index 00000000..9a9e0810 --- /dev/null +++ b/Wino.Core.Domain/Enums/AccountSynchronizerState.cs @@ -0,0 +1,12 @@ +namespace Wino.Core.Domain.Enums +{ + /// + /// Indicates the state of synchronizer. + /// + public enum AccountSynchronizerState + { + Idle, + ExecutingRequests, + Synchronizing + } +} diff --git a/Wino.Core.Domain/Enums/AppLanguage.cs b/Wino.Core.Domain/Enums/AppLanguage.cs new file mode 100644 index 00000000..0f3a6b9f --- /dev/null +++ b/Wino.Core.Domain/Enums/AppLanguage.cs @@ -0,0 +1,16 @@ +namespace Wino.Core.Domain.Enums +{ + public enum AppLanguage + { + None, + English, + Deutsch, + Russian, + Turkish, + Polish, + Czech, + Chinese, + Spanish, + French + } +} diff --git a/Wino.Core.Domain/Enums/AppThemeType.cs b/Wino.Core.Domain/Enums/AppThemeType.cs new file mode 100644 index 00000000..138f499b --- /dev/null +++ b/Wino.Core.Domain/Enums/AppThemeType.cs @@ -0,0 +1,9 @@ +namespace Wino.Core.Domain.Enums +{ + public enum AppThemeType + { + System, + PreDefined, + Custom, + } +} diff --git a/Wino.Core.Domain/Enums/ApplicationElementTheme.cs b/Wino.Core.Domain/Enums/ApplicationElementTheme.cs new file mode 100644 index 00000000..6c3e0bca --- /dev/null +++ b/Wino.Core.Domain/Enums/ApplicationElementTheme.cs @@ -0,0 +1,9 @@ +namespace Wino.Core.Domain.Enums +{ + public enum ApplicationElementTheme + { + Default, + Light, + Dark + } +} diff --git a/Wino.Core.Domain/Enums/BackgroundSynchronizationReason.cs b/Wino.Core.Domain/Enums/BackgroundSynchronizationReason.cs new file mode 100644 index 00000000..eac0a746 --- /dev/null +++ b/Wino.Core.Domain/Enums/BackgroundSynchronizationReason.cs @@ -0,0 +1,8 @@ +namespace Wino.Core.Domain.Enums +{ + public enum BackgroundSynchronizationReason + { + SessionConnected, + Timer + } +} diff --git a/Wino.Core.Domain/Enums/ChangeRequestType.cs b/Wino.Core.Domain/Enums/ChangeRequestType.cs new file mode 100644 index 00000000..d0af1669 --- /dev/null +++ b/Wino.Core.Domain/Enums/ChangeRequestType.cs @@ -0,0 +1,24 @@ +namespace Wino.Core.Domain.Enums +{ + public enum ChangeRequestType + { + MailMarkAs, + MailChangeFlag, + MailHardDelete, + MailMove, + MailAlwaysMoveTo, + MailChangeFocused, + MailArchive, + MailUnarchive, + FolderMarkAsRead, + FolderDelete, + FolderEmpty, + FolderRename, + CreateNewDraft, + CreateReplyDraft, + CreateForwardDraft, + DiscardDraft, + SendDraft, + FetchSingleItem + } +} diff --git a/Wino.Core.Domain/Enums/CustomIncomingServerType.cs b/Wino.Core.Domain/Enums/CustomIncomingServerType.cs new file mode 100644 index 00000000..40312e4a --- /dev/null +++ b/Wino.Core.Domain/Enums/CustomIncomingServerType.cs @@ -0,0 +1,8 @@ +namespace Wino.Core.Domain.Enums +{ + public enum CustomIncomingServerType + { + POP3, + IMAP4 + } +} diff --git a/Wino.Core.Domain/Enums/DraftCreationReason.cs b/Wino.Core.Domain/Enums/DraftCreationReason.cs new file mode 100644 index 00000000..e988c03a --- /dev/null +++ b/Wino.Core.Domain/Enums/DraftCreationReason.cs @@ -0,0 +1,10 @@ +namespace Wino.Core.Domain.Enums +{ + public enum DraftCreationReason + { + Empty, + Reply, + ReplyAll, + Forward + } +} diff --git a/Wino.Core.Domain/Enums/EditorToolbarSectionType.cs b/Wino.Core.Domain/Enums/EditorToolbarSectionType.cs new file mode 100644 index 00000000..187c8cb2 --- /dev/null +++ b/Wino.Core.Domain/Enums/EditorToolbarSectionType.cs @@ -0,0 +1,11 @@ +namespace Wino.Core.Domain.Enums +{ + public enum EditorToolbarSectionType + { + None, + Format, + Insert, + Draw, + Options + } +} diff --git a/Wino.Core.Domain/Enums/FilterOptionType.cs b/Wino.Core.Domain/Enums/FilterOptionType.cs new file mode 100644 index 00000000..4882f76b --- /dev/null +++ b/Wino.Core.Domain/Enums/FilterOptionType.cs @@ -0,0 +1,10 @@ +namespace Wino.Core.Domain.Enums +{ + public enum FilterOptionType + { + All, + Unread, + Flagged, + Mentions + } +} diff --git a/Wino.Core.Domain/Enums/FolderOperation.cs b/Wino.Core.Domain/Enums/FolderOperation.cs new file mode 100644 index 00000000..2d33144f --- /dev/null +++ b/Wino.Core.Domain/Enums/FolderOperation.cs @@ -0,0 +1,23 @@ +namespace Wino.Core.Domain.Enums +{ + /// + /// Defines all possible folder operations that can be done. + /// Available values for each folder is returned by IContextMenuProvider + /// that integrators hold. + /// + public enum FolderOperation + { + None, + Pin, + Unpin, + MarkAllAsRead, + DontSync, + Empty, + Rename, + Delete, + Move, + TurnOffNotifications, + CreateSubFolder, + Seperator + } +} diff --git a/Wino.Core.Domain/Enums/ImapAuthenticationMethod.cs b/Wino.Core.Domain/Enums/ImapAuthenticationMethod.cs new file mode 100644 index 00000000..4390f247 --- /dev/null +++ b/Wino.Core.Domain/Enums/ImapAuthenticationMethod.cs @@ -0,0 +1,13 @@ +namespace Wino.Core.Domain.Enums +{ + public enum ImapAuthenticationMethod + { + Auto, + None, + NormalPassword, + EncryptedPassword, + Ntlm, + CramMd5, + DigestMd5 + } +} diff --git a/Wino.Core.Domain/Enums/ImapConnectionSecurity.cs b/Wino.Core.Domain/Enums/ImapConnectionSecurity.cs new file mode 100644 index 00000000..9a3e6e7b --- /dev/null +++ b/Wino.Core.Domain/Enums/ImapConnectionSecurity.cs @@ -0,0 +1,10 @@ +namespace Wino.Core.Domain.Enums +{ + public enum ImapConnectionSecurity + { + Auto, + None, + StartTls, + SslTls + } +} diff --git a/Wino.Core.Domain/Enums/InfoBarAnimationType.cs b/Wino.Core.Domain/Enums/InfoBarAnimationType.cs new file mode 100644 index 00000000..488b667b --- /dev/null +++ b/Wino.Core.Domain/Enums/InfoBarAnimationType.cs @@ -0,0 +1,8 @@ +namespace Wino.Core.Domain.Enums +{ + public enum InfoBarAnimationType + { + SlideFromRightToLeft, + SlideFromBottomToTop + } +} diff --git a/Wino.Core.Domain/Enums/InfoBarMessageType.cs b/Wino.Core.Domain/Enums/InfoBarMessageType.cs new file mode 100644 index 00000000..15df6dcf --- /dev/null +++ b/Wino.Core.Domain/Enums/InfoBarMessageType.cs @@ -0,0 +1,10 @@ +namespace Wino.Core.Domain.Enums +{ + public enum InfoBarMessageType + { + Information, + Success, + Warning, + Error + } +} diff --git a/Wino.Core.Domain/Enums/MailAttachmentType.cs b/Wino.Core.Domain/Enums/MailAttachmentType.cs new file mode 100644 index 00000000..b444b4c5 --- /dev/null +++ b/Wino.Core.Domain/Enums/MailAttachmentType.cs @@ -0,0 +1,16 @@ +namespace Wino.Core.Domain.Enums +{ + public enum MailAttachmentType + { + None, + Executable, + Image, + Audio, + Video, + PDF, + HTML, + RarArchive, + Archive, + Other + } +} diff --git a/Wino.Core.Domain/Enums/MailImportance.cs b/Wino.Core.Domain/Enums/MailImportance.cs new file mode 100644 index 00000000..22f2a6ab --- /dev/null +++ b/Wino.Core.Domain/Enums/MailImportance.cs @@ -0,0 +1,9 @@ +namespace Wino.Core.Domain.Enums +{ + public enum MailImportance + { + Low, + Normal, + High + } +} diff --git a/Wino.Core.Domain/Enums/MailListDisplayMode.cs b/Wino.Core.Domain/Enums/MailListDisplayMode.cs new file mode 100644 index 00000000..d27278e7 --- /dev/null +++ b/Wino.Core.Domain/Enums/MailListDisplayMode.cs @@ -0,0 +1,9 @@ +namespace Wino.Core.Domain.Enums +{ + public enum MailListDisplayMode + { + Spacious, + Medium, + Compact, + } +} diff --git a/Wino.Core.Domain/Enums/MailMarkAsOption.cs b/Wino.Core.Domain/Enums/MailMarkAsOption.cs new file mode 100644 index 00000000..e08ce803 --- /dev/null +++ b/Wino.Core.Domain/Enums/MailMarkAsOption.cs @@ -0,0 +1,9 @@ +namespace Wino.Core.Domain.Enums +{ + public enum MailMarkAsOption + { + WhenSelected, + DontMark, + AfterDelay + } +} diff --git a/Wino.Core.Domain/Enums/MailOperation.cs b/Wino.Core.Domain/Enums/MailOperation.cs new file mode 100644 index 00000000..d221a090 --- /dev/null +++ b/Wino.Core.Domain/Enums/MailOperation.cs @@ -0,0 +1,49 @@ +namespace Wino.Core.Domain.Enums +{ + // Synchronizer requests. + public enum MailSynchronizerOperation + { + MarkRead, + Move, + Delete, // Hard delete. + CreateDraft, + Send, + ChangeFlag, + AlwaysMoveTo, + MoveToFocused, + RenameFolder + } + + // UI requests + public enum MailOperation + { + None, + Archive, + UnArchive, + SoftDelete, + HardDelete, + Move, + MoveToJunk, + MoveToFocused, + MoveToOther, + AlwaysMoveToOther, + AlwaysMoveToFocused, + SetFlag, + ClearFlag, + MarkAsRead, + MarkAsUnread, + MarkAsNotJunk, + Seperator, + Ignore, + Reply, + ReplyAll, + Zoom, + SaveAs, + Find, + Forward, + DarkEditor, + LightEditor, + Print, + Navigate // For toast activation + } +} diff --git a/Wino.Core.Domain/Enums/MailProviderType.cs b/Wino.Core.Domain/Enums/MailProviderType.cs new file mode 100644 index 00000000..a9156f68 --- /dev/null +++ b/Wino.Core.Domain/Enums/MailProviderType.cs @@ -0,0 +1,11 @@ +namespace Wino.Core.Domain.Enums +{ + public enum MailProviderType + { + Outlook, + Gmail, + Office365, + Yahoo, + IMAP4 + } +} diff --git a/Wino.Core.Domain/Enums/MenuPaneMode.cs b/Wino.Core.Domain/Enums/MenuPaneMode.cs new file mode 100644 index 00000000..3d6e77b8 --- /dev/null +++ b/Wino.Core.Domain/Enums/MenuPaneMode.cs @@ -0,0 +1,8 @@ +namespace Wino.Core.Domain.Enums +{ + public enum MenuPaneMode + { + Visible, + Hidden + } +} diff --git a/Wino.Core.Domain/Enums/NavigationReferenceFrame.cs b/Wino.Core.Domain/Enums/NavigationReferenceFrame.cs new file mode 100644 index 00000000..1d116fdb --- /dev/null +++ b/Wino.Core.Domain/Enums/NavigationReferenceFrame.cs @@ -0,0 +1,8 @@ +namespace Wino.Core.Domain.Enums +{ + public enum NavigationReferenceFrame + { + ShellFrame, + RenderingFrame + } +} diff --git a/Wino.Core.Domain/Enums/PickFolderReason.cs b/Wino.Core.Domain/Enums/PickFolderReason.cs new file mode 100644 index 00000000..99067066 --- /dev/null +++ b/Wino.Core.Domain/Enums/PickFolderReason.cs @@ -0,0 +1,12 @@ +namespace Wino.Core.Domain.Enums +{ + /// + /// Defines the potential reasons for picking folder in the folder picking dialog. + /// + public enum PickFolderReason + { + Move, + SpecialFolder, + Any + } +} diff --git a/Wino.Core.Domain/Enums/ReaderFont.cs b/Wino.Core.Domain/Enums/ReaderFont.cs new file mode 100644 index 00000000..53c0e39a --- /dev/null +++ b/Wino.Core.Domain/Enums/ReaderFont.cs @@ -0,0 +1,15 @@ +namespace Wino.Core.Domain.Enums +{ + public enum ReaderFont + { + Arial, + TimesNewRoman, + Verdana, + Tahoma, + CourierNew, + Georgia, + TrebuchetMS, + Calibri, + Helvetica + } +} diff --git a/Wino.Core.Domain/Enums/SortingOptionType.cs b/Wino.Core.Domain/Enums/SortingOptionType.cs new file mode 100644 index 00000000..4a88d2fd --- /dev/null +++ b/Wino.Core.Domain/Enums/SortingOptionType.cs @@ -0,0 +1,8 @@ +namespace Wino.Core.Domain.Enums +{ + public enum SortingOptionType + { + ReceiveDate, + Sender + } +} diff --git a/Wino.Core.Domain/Enums/SpecialFolderType.cs b/Wino.Core.Domain/Enums/SpecialFolderType.cs new file mode 100644 index 00000000..b26ae4c3 --- /dev/null +++ b/Wino.Core.Domain/Enums/SpecialFolderType.cs @@ -0,0 +1,24 @@ +namespace Wino.Core.Domain.Enums +{ + public enum SpecialFolderType + { + Inbox, + Starred, + Important, + Sent, + Draft, + Archive, + Deleted, + Junk, + Chat, + Category, + Unread, + Forums, + Updates, + Personal, + Promotions, + Social, + Other, + More + } +} diff --git a/Wino.Core.Domain/Enums/StorePurchaseResult.cs b/Wino.Core.Domain/Enums/StorePurchaseResult.cs new file mode 100644 index 00000000..8364b81a --- /dev/null +++ b/Wino.Core.Domain/Enums/StorePurchaseResult.cs @@ -0,0 +1,19 @@ +namespace Wino.Core.Domain.Enums +{ + // From the SDK. + public enum StorePurchaseResult + { + // + // Summary: + // The purchase request succeeded. + Succeeded, + // + // Summary: + // The current user has already purchased the specified app or add-on. + AlreadyPurchased, + // + // Summary: + // The purchase request did not succeed. + NotPurchased, + } +} diff --git a/Wino.Core.Domain/Enums/SynchronizationCompletedState.cs b/Wino.Core.Domain/Enums/SynchronizationCompletedState.cs new file mode 100644 index 00000000..b4376afe --- /dev/null +++ b/Wino.Core.Domain/Enums/SynchronizationCompletedState.cs @@ -0,0 +1,9 @@ +namespace Wino.Core.Domain.Enums +{ + public enum SynchronizationCompletedState + { + Success, // All succeeded. + Canceled, // Canceled by user or HTTP call. + Failed // Exception. + } +} diff --git a/Wino.Core.Domain/Enums/SynchronizationType.cs b/Wino.Core.Domain/Enums/SynchronizationType.cs new file mode 100644 index 00000000..c95b7d52 --- /dev/null +++ b/Wino.Core.Domain/Enums/SynchronizationType.cs @@ -0,0 +1,11 @@ +namespace Wino.Core.Domain.Enums +{ + public enum SynchronizationType + { + FoldersOnly, // Only synchronize folder metadata. + ExecuteRequests, // Run the queued requests, and then synchronize if needed. + Inbox, // Only Inbox + Custom, // Only sync folders that are specified in the options. + Full, // Synchronize everything + } +} diff --git a/Wino.Core.Domain/Enums/WinoPage.cs b/Wino.Core.Domain/Enums/WinoPage.cs new file mode 100644 index 00000000..69fbd7fa --- /dev/null +++ b/Wino.Core.Domain/Enums/WinoPage.cs @@ -0,0 +1,25 @@ +namespace Wino.Core.Domain.Enums +{ + /// + /// All registered views. + /// + public enum WinoPage + { + None, + IdlePage, + ComposePage, + SettingsPage, + MailRenderingPage, + WelcomePage, + AccountDetailsPage, + MergedAccountDetailsPage, + AccountManagementPage, + SignatureManagementPage, + AboutPage, + PersonalizationPage, + MessageListPage, + MailListPage, + ReadingPanePage, + SettingOptionsPage + } +} diff --git a/Wino.Core.Domain/Exceptions/AccountSetupCanceledException.cs b/Wino.Core.Domain/Exceptions/AccountSetupCanceledException.cs new file mode 100644 index 00000000..03819076 --- /dev/null +++ b/Wino.Core.Domain/Exceptions/AccountSetupCanceledException.cs @@ -0,0 +1,7 @@ +namespace Wino.Core.Domain.Exceptions +{ + public class AccountSetupCanceledException : System.Exception + { + + } +} diff --git a/Wino.Core.Domain/Exceptions/AuthenticationAttentionException.cs b/Wino.Core.Domain/Exceptions/AuthenticationAttentionException.cs new file mode 100644 index 00000000..63b577f8 --- /dev/null +++ b/Wino.Core.Domain/Exceptions/AuthenticationAttentionException.cs @@ -0,0 +1,19 @@ +using System; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Domain.Exceptions +{ + /// + /// Thrown when IAuthenticator requires user interaction to fix authentication issues. + /// It can be expired and can't restorable token, or some stuff that requires re-authentication. + /// + public class AuthenticationAttentionException : Exception + { + public AuthenticationAttentionException(MailAccount account) + { + Account = account; + } + + public MailAccount Account { get; } + } +} diff --git a/Wino.Core.Domain/Exceptions/AuthenticationException.cs b/Wino.Core.Domain/Exceptions/AuthenticationException.cs new file mode 100644 index 00000000..87e622df --- /dev/null +++ b/Wino.Core.Domain/Exceptions/AuthenticationException.cs @@ -0,0 +1,18 @@ +using System; + +namespace Wino.Core.Domain.Exceptions +{ + /// + /// All exceptions related to authentication. + /// + public class AuthenticationException : Exception + { + public AuthenticationException(string message) : base(message) + { + } + + public AuthenticationException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/Wino.Core.Domain/Exceptions/BackgroundTaskExecutionRequestDeniedException.cs b/Wino.Core.Domain/Exceptions/BackgroundTaskExecutionRequestDeniedException.cs new file mode 100644 index 00000000..532addbd --- /dev/null +++ b/Wino.Core.Domain/Exceptions/BackgroundTaskExecutionRequestDeniedException.cs @@ -0,0 +1,9 @@ +using System; + +namespace Wino.Core.Domain.Exceptions +{ + /// + /// An exception thrown when the background task execution policies are denied for some reason. + /// + public class BackgroundTaskExecutionRequestDeniedException : Exception { } +} diff --git a/Wino.Core.Domain/Exceptions/BackgroundTaskRegistrationFailedException.cs b/Wino.Core.Domain/Exceptions/BackgroundTaskRegistrationFailedException.cs new file mode 100644 index 00000000..50d3d40d --- /dev/null +++ b/Wino.Core.Domain/Exceptions/BackgroundTaskRegistrationFailedException.cs @@ -0,0 +1,9 @@ +using System; + +namespace Wino.Core.Domain.Exceptions +{ + /// + /// An exception thrown when the background task registration is failed. + /// + public class BackgroundTaskRegistrationFailedException : Exception { } +} diff --git a/Wino.Core.Domain/Exceptions/ComposerMimeNotFoundException.cs b/Wino.Core.Domain/Exceptions/ComposerMimeNotFoundException.cs new file mode 100644 index 00000000..2a82d580 --- /dev/null +++ b/Wino.Core.Domain/Exceptions/ComposerMimeNotFoundException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Wino.Core.Domain.Exceptions +{ + /// + /// Thrown when composer cant find the mime to load. + /// + public class ComposerMimeNotFoundException : Exception + { + } +} diff --git a/Wino.Core.Domain/Exceptions/CustomThemeCreationFailedException.cs b/Wino.Core.Domain/Exceptions/CustomThemeCreationFailedException.cs new file mode 100644 index 00000000..8ffd3992 --- /dev/null +++ b/Wino.Core.Domain/Exceptions/CustomThemeCreationFailedException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Wino.Core.Domain.Exceptions +{ + public class CustomThemeCreationFailedException : Exception + { + public CustomThemeCreationFailedException(string message) : base(message) + { + } + } +} diff --git a/Wino.Core.Domain/Exceptions/GoogleAuthenticationException.cs b/Wino.Core.Domain/Exceptions/GoogleAuthenticationException.cs new file mode 100644 index 00000000..71d244c1 --- /dev/null +++ b/Wino.Core.Domain/Exceptions/GoogleAuthenticationException.cs @@ -0,0 +1,7 @@ +namespace Wino.Core.Domain.Exceptions +{ + public class GoogleAuthenticationException : System.Exception + { + public GoogleAuthenticationException(string message) : base(message) { } + } +} diff --git a/Wino.Core.Domain/Exceptions/ImapClientPoolException.cs b/Wino.Core.Domain/Exceptions/ImapClientPoolException.cs new file mode 100644 index 00000000..bd027024 --- /dev/null +++ b/Wino.Core.Domain/Exceptions/ImapClientPoolException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Wino.Core.Domain.Exceptions +{ + public class ImapClientPoolException : Exception + { + public ImapClientPoolException(Exception innerException) : base(Translator.Exception_ImapClientPoolFailed, innerException) + { + } + } +} diff --git a/Wino.Core.Domain/Exceptions/InvalidMoveTargetException.cs b/Wino.Core.Domain/Exceptions/InvalidMoveTargetException.cs new file mode 100644 index 00000000..05f3e7b4 --- /dev/null +++ b/Wino.Core.Domain/Exceptions/InvalidMoveTargetException.cs @@ -0,0 +1,6 @@ +using System; + +namespace Wino.Core.Domain.Exceptions +{ + public class InvalidMoveTargetException : Exception { } +} diff --git a/Wino.Core.Domain/Exceptions/SynchronizerEntityNotFoundException.cs b/Wino.Core.Domain/Exceptions/SynchronizerEntityNotFoundException.cs new file mode 100644 index 00000000..4d35aad6 --- /dev/null +++ b/Wino.Core.Domain/Exceptions/SynchronizerEntityNotFoundException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Wino.Core.Domain.Exceptions +{ + public class SynchronizerEntityNotFoundException : Exception + { + public SynchronizerEntityNotFoundException(string message) : base(message) + { + } + } +} diff --git a/Wino.Core.Domain/Exceptions/SynchronizerException.cs b/Wino.Core.Domain/Exceptions/SynchronizerException.cs new file mode 100644 index 00000000..cd2960fb --- /dev/null +++ b/Wino.Core.Domain/Exceptions/SynchronizerException.cs @@ -0,0 +1,15 @@ +using System; + +namespace Wino.Core.Domain.Exceptions +{ + public class SynchronizerException : Exception + { + public SynchronizerException(string message) : base(message) + { + } + + public SynchronizerException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/Wino.Core.Domain/Exceptions/SystemFolderConfigurationMissingException.cs b/Wino.Core.Domain/Exceptions/SystemFolderConfigurationMissingException.cs new file mode 100644 index 00000000..918aa253 --- /dev/null +++ b/Wino.Core.Domain/Exceptions/SystemFolderConfigurationMissingException.cs @@ -0,0 +1,7 @@ +namespace Wino.Core.Domain.Exceptions +{ + /// + /// When IMAP account's system folder configuration setup is not done yet. + /// + public class SystemFolderConfigurationMissingException : System.Exception { } +} diff --git a/Wino.Core.Domain/Exceptions/UnavailableSpecialFolderException.cs b/Wino.Core.Domain/Exceptions/UnavailableSpecialFolderException.cs new file mode 100644 index 00000000..9153cbaa --- /dev/null +++ b/Wino.Core.Domain/Exceptions/UnavailableSpecialFolderException.cs @@ -0,0 +1,20 @@ +using System; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Exceptions +{ + /// + /// Emitted when special folder is needed for an operation but it couldn't be found. + /// + public class UnavailableSpecialFolderException : Exception + { + public UnavailableSpecialFolderException(SpecialFolderType specialFolderType, Guid accountId) + { + SpecialFolderType = specialFolderType; + AccountId = accountId; + } + + public SpecialFolderType SpecialFolderType { get; } + public Guid AccountId { get; set; } + } +} diff --git a/Wino.Core.Domain/Interfaces/AfterRequestExecutionSynchronizationInterfaces.cs b/Wino.Core.Domain/Interfaces/AfterRequestExecutionSynchronizationInterfaces.cs new file mode 100644 index 00000000..b1784d08 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/AfterRequestExecutionSynchronizationInterfaces.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; + +namespace Wino.Core.Domain.Interfaces +{ + /// + /// An interface that should force synchronizer to do synchronization for only given folder ids + /// after the execution is completed. + /// + public interface ICustomFolderSynchronizationRequest + { + /// + /// Which folders to sync after this operation? + /// + List SynchronizationFolderIds { get; } + } +} diff --git a/Wino.Core.Domain/Interfaces/IAccountCreationDialog.cs b/Wino.Core.Domain/Interfaces/IAccountCreationDialog.cs new file mode 100644 index 00000000..c74a8413 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IAccountCreationDialog.cs @@ -0,0 +1,11 @@ +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IAccountCreationDialog + { + void ShowDialog(); + void Complete(); + AccountCreationDialogState State { get; set; } + } +} diff --git a/Wino.Core.Domain/Interfaces/IAccountMenuItem.cs b/Wino.Core.Domain/Interfaces/IAccountMenuItem.cs new file mode 100644 index 00000000..b3870e3e --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IAccountMenuItem.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IAccountMenuItem : IMenuItem + { + double SynchronizationProgress { get; set; } + int UnreadItemCount { get; set; } + IEnumerable HoldingAccounts { get; } + void UpdateAccount(MailAccount account); + } +} diff --git a/Wino.Core.Domain/Interfaces/IAccountPickerDialog.cs b/Wino.Core.Domain/Interfaces/IAccountPickerDialog.cs new file mode 100644 index 00000000..a43bbc26 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IAccountPickerDialog.cs @@ -0,0 +1,6 @@ +namespace Wino.Core.Domain.Interfaces +{ + public interface IAccountPickerDialog + { + } +} diff --git a/Wino.Core.Domain/Interfaces/IAccountProviderDetailViewModel.cs b/Wino.Core.Domain/Interfaces/IAccountProviderDetailViewModel.cs new file mode 100644 index 00000000..b535fea1 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IAccountProviderDetailViewModel.cs @@ -0,0 +1,17 @@ +using System; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IAccountProviderDetailViewModel + { + /// + /// Entity id that will help to identify the startup entity on launch. + /// + Guid StartupEntityId { get; } + + /// + /// Name representation of the view model that will be used to identify the startup entity on launch. + /// + string StartupEntityTitle { get; } + } +} diff --git a/Wino.Core.Domain/Interfaces/IAccountProviderDetails.cs b/Wino.Core.Domain/Interfaces/IAccountProviderDetails.cs new file mode 100644 index 00000000..65d0d5b7 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IAccountProviderDetails.cs @@ -0,0 +1,11 @@ +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IAccountProviderDetails + { + MailAccount Account { get; set; } + bool AutoExtend { get; set; } + IProviderDetail ProviderDetail { get; set; } + } +} diff --git a/Wino.Core.Domain/Interfaces/IAccountService.cs b/Wino.Core.Domain/Interfaces/IAccountService.cs new file mode 100644 index 00000000..99190af3 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IAccountService.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IAccountService + { + /// + /// Current IAuthenticator that should receive external authentication process to continue with. + /// For example: Google auth will launch a browser authentication. After it completes, this is the IAuthenticator + /// to continue process for token exchange. + /// + IAuthenticator ExternalAuthenticationAuthenticator { get; set; } + + /// + /// Returns all local accounts. + /// + /// All local accounts + Task> GetAccountsAsync(); + + /// + /// Returns single MailAccount + /// + /// AccountId. + Task GetAccountAsync(Guid accountId); + + /// + /// Deletes all information about the account, including token information. + /// + /// MailAccount to be removed + Task DeleteAccountAsync(MailAccount account); + + /// + /// Returns the custom server information for the given account id. + /// + Task GetAccountCustomServerInformationAsync(Guid accountId); + + /// + /// Updates the given account properties. + /// + Task UpdateAccountAsync(MailAccount account); + + /// + /// Creates new account with the given server information if any. + /// Also sets the account as Startup account if there are no accounts. + /// + Task CreateAccountAsync(MailAccount account, TokenInformation tokenInformation, CustomServerInformation customServerInformation); + + /// + /// Fixed authentication errors for account by forcing interactive login. + /// + Task FixTokenIssuesAsync(Guid accountId); + + /// + /// Removed the attention from an account. + /// + /// Account id to remove from + Task ClearAccountAttentionAsync(Guid accountId); + + /// + /// Updates the account synchronization identifier. + /// For example: Gmail uses this identifier to keep track of the last synchronization. + /// Update is ignored for Gmail if the new identifier is older than the current one. + /// + /// Identifier to update + /// Current account synchronization modifier. + Task UpdateSynchronizationIdentifierAsync(Guid accountId, string newIdentifier); + + Task RenameMergedAccountAsync(Guid mergedInboxId, string newName); + + Task CreateMergeAccountsAsync(MergedInbox mergedInbox, IEnumerable accountsToMerge); + + Task UpdateMergedInboxAsync(Guid mergedInboxId, IEnumerable linkedAccountIds); + + Task UnlinkMergedInboxAsync(Guid mergedInboxId); + } +} diff --git a/Wino.Core.Domain/Interfaces/IAppInitializerService.cs b/Wino.Core.Domain/Interfaces/IAppInitializerService.cs new file mode 100644 index 00000000..ffc6945d --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IAppInitializerService.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IAppInitializerService + { + string GetApplicationDataFolder(); + + Task MigrateAsync(); + } +} diff --git a/Wino.Core.Domain/Interfaces/IApplicationResourceManager.cs b/Wino.Core.Domain/Interfaces/IApplicationResourceManager.cs new file mode 100644 index 00000000..a8e1b22c --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IApplicationResourceManager.cs @@ -0,0 +1,11 @@ +namespace Wino.Core.Domain.Interfaces +{ + public interface IApplicationResourceManager + { + void RemoveResource(T resource); + void AddResource(T resource); + bool ContainsResourceKey(string resourceKey); + void ReplaceResource(string resourceKey, object resource); + T GetLastResource(); + } +} diff --git a/Wino.Core.Domain/Interfaces/IAuthenticationProvider.cs b/Wino.Core.Domain/Interfaces/IAuthenticationProvider.cs new file mode 100644 index 00000000..49390dcb --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IAuthenticationProvider.cs @@ -0,0 +1,9 @@ +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IAuthenticationProvider + { + IAuthenticator GetAuthenticator(MailProviderType providerType); + } +} diff --git a/Wino.Core.Domain/Interfaces/IAuthenticator.cs b/Wino.Core.Domain/Interfaces/IAuthenticator.cs new file mode 100644 index 00000000..ced495d6 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IAuthenticator.cs @@ -0,0 +1,53 @@ +using System; +using System.Threading.Tasks; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IAuthenticator + { + /// + /// Type of the provider. + /// + MailProviderType ProviderType { get; } + + /// + /// Gets the token from the cache if exists. + /// If the token is expired, tries to refresh. + /// This can throw AuthenticationAttentionException if silent refresh fails. + /// + /// Account to get token for. + /// Valid token info to be used in integrators. + Task GetTokenAsync(MailAccount account); + + /// + /// Initial creation of token. Requires user interaction. + /// This will save token into database, but still returns for account creation + /// since account address is required. + /// + /// Token cache might ask for regeneration of token for specific + /// account address. If one is provided and re-generation native token doesn't belong to this address + /// token saving to database won't happen. + /// Freshly created TokenInformation.. + Task GenerateTokenAsync(MailAccount account, bool saveToken); + + /// + /// Required for external authorization on launched browser to continue. + /// Used for Gmail. + /// + /// Response's redirect uri. + void ContinueAuthorization(Uri authorizationResponseUri); + + /// + /// For external browser required authentications. + /// Canceling Gmail authentication dialog etc. + /// + void CancelAuthorization(); + + /// + /// ClientId in case of needed for authorization/authentication. + /// + string ClientId { get; } + } +} diff --git a/Wino.Core.Domain/Interfaces/IAutoDiscoveryService.cs b/Wino.Core.Domain/Interfaces/IAutoDiscoveryService.cs new file mode 100644 index 00000000..6886022d --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IAutoDiscoveryService.cs @@ -0,0 +1,18 @@ +using System.Threading.Tasks; +using Wino.Core.Domain.Models.AutoDiscovery; + +namespace Wino.Core.Domain.Interfaces +{ + /// + /// Searches for Auto Discovery settings for custom mail accounts. + /// + public interface IAutoDiscoveryService + { + /// + /// Tries to return the best mail server settings using different techniques. + /// + /// Address to search settings for. + /// CustomServerInformation with only settings applied. + Task GetAutoDiscoverySettings(AutoDiscoveryMinimalSettings autoDiscoveryMinimalSettings); + } +} diff --git a/Wino.Core.Domain/Interfaces/IBackgroundTaskService.cs b/Wino.Core.Domain/Interfaces/IBackgroundTaskService.cs new file mode 100644 index 00000000..b6411b32 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IBackgroundTaskService.cs @@ -0,0 +1,19 @@ +using System.Threading.Tasks; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IBackgroundTaskService + { + /// + /// Manages background task registrations, requests access if needed, checks the statusses of them etc. + /// + /// If the access request is denied for some reason. + /// If one of the requires background tasks are failed during registration. + Task HandleBackgroundTaskRegistrations(); + + /// + /// Unregisters all existing background tasks. Useful for migrations. + /// + void UnregisterAllBackgroundTask(); + } +} diff --git a/Wino.Core.Domain/Interfaces/IClipboardService.cs b/Wino.Core.Domain/Interfaces/IClipboardService.cs new file mode 100644 index 00000000..f5bb4782 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IClipboardService.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IClipboardService + { + Task CopyClipboardAsync(string text); + } +} diff --git a/Wino.Core.Domain/Interfaces/IConfigurationService.cs b/Wino.Core.Domain/Interfaces/IConfigurationService.cs new file mode 100644 index 00000000..c08c463c --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IConfigurationService.cs @@ -0,0 +1,11 @@ +namespace Wino.Core.Domain.Interfaces +{ + public interface IConfigurationService + { + void Set(string key, object value); + T Get(string key, T defaultValue = default); + + void SetRoaming(string key, object value); + T GetRoaming(string key, T defaultValue = default); + } +} diff --git a/Wino.Core.Domain/Interfaces/IConfirmationDialog.cs b/Wino.Core.Domain/Interfaces/IConfirmationDialog.cs new file mode 100644 index 00000000..34630448 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IConfirmationDialog.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IConfirmationDialog + { + Task ShowDialogAsync(string title, string message, string approveButtonTitle); + } +} diff --git a/Wino.Core.Domain/Interfaces/IContextMenuItemService.cs b/Wino.Core.Domain/Interfaces/IContextMenuItemService.cs new file mode 100644 index 00000000..06d91240 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IContextMenuItemService.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Wino.Core.Domain.Models.Folders; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Menus; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IContextMenuItemService + { + IEnumerable GetFolderContextMenuActions(IBaseFolderMenuItem folderInformation); + IEnumerable GetMailItemContextMenuActions(IEnumerable selectedMailItems); + IEnumerable GetMailItemRenderMenuActions(IMailItem mailItem, bool isDarkEditor); + } +} diff --git a/Wino.Core.Domain/Interfaces/IContextMenuProvider.cs b/Wino.Core.Domain/Interfaces/IContextMenuProvider.cs new file mode 100644 index 00000000..968b2c2c --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IContextMenuProvider.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using Wino.Core.Domain.Models.Folders; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Menus; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IContextMenuProvider + { + /// + /// Calculates and returns available folder operations for the given folder. + /// + /// Folder to get actions for. + IEnumerable GetFolderContextMenuActions(IMailItemFolder folderInformation); + + /// + /// Calculates and returns available context menu items for selected mail items. + /// + /// Current folder that asks for the menu items. + /// Selected menu items in the given folder. + IEnumerable GetMailItemContextMenuActions(IMailItemFolder folderInformation, IEnumerable selectedMailItems); + + /// + /// Calculates and returns available mail operations for mail rendering CommandBar. + /// + /// Rendered mail item. + /// Folder that mail item belongs to. + IEnumerable GetMailItemRenderMenuActions(IMailItem mailItem, IMailItemFolder activeFolder, bool isDarkEditor); + } +} diff --git a/Wino.Core.Domain/Interfaces/ICustomServerAccountCreationDialog.cs b/Wino.Core.Domain/Interfaces/ICustomServerAccountCreationDialog.cs new file mode 100644 index 00000000..ef3a81f1 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/ICustomServerAccountCreationDialog.cs @@ -0,0 +1,19 @@ +using System.Threading.Tasks; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Domain.Interfaces +{ + public interface ICustomServerAccountCreationDialog : IAccountCreationDialog + { + /// + /// Returns the custom server information from the dialog.. + /// + /// Null if canceled. + Task GetCustomServerInformationAsync(); + + /// + /// Displays preparing folders page. + /// + void ShowPreparingFolders(); + } +} diff --git a/Wino.Core.Domain/Interfaces/IDialogService.cs b/Wino.Core.Domain/Interfaces/IDialogService.cs new file mode 100644 index 00000000..d1954e9c --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IDialogService.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Folders; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IDialogService + { + Task PickWindowsFolderAsync(); + Task PickWindowsFileContentAsync(params object[] typeFilters); + Task ShowConfirmationDialogAsync(string question, string title, string confirmationButtonTitle); + Task ShowHardDeleteConfirmationAsync(); + Task ShowRatingDialogAsync(); + Task HandleSystemFolderConfigurationDialogAsync(Guid accountId, IFolderService folderService); + Task ShowCustomThemeBuilderDialogAsync(); + + Task ShowMessageAsync(string message, string title); + void InfoBarMessage(string title, string message, InfoBarMessageType messageType); + void InfoBarMessage(string title, string message, InfoBarMessageType messageType, string actionButtonText, Action action); + + void ShowNotSupportedMessage(); + + // Custom dialogs + Task ShowMoveMailFolderDialogAsync(List availableFolders); + Task> ShowNewAccountMailProviderDialogAsync(List availableProviders); + IAccountCreationDialog GetAccountCreationDialog(MailProviderType type); + Task ShowTextInputDialogAsync(string currentInput, string dialogTitle, string dialogDescription); + Task ShowEditAccountDialogAsync(MailAccount account); + Task ShowAccountPickerDialogAsync(List availableAccounts); + + /// + /// Presents a dialog to the user for selecting folder. + /// + /// Account to get folders for. + /// The reason behind the picking operation + /// Selected folder structure. Null if none. + Task PickFolderAsync(Guid accountId, PickFolderReason reason, IFolderService folderService); + } +} diff --git a/Wino.Core.Domain/Interfaces/IDispatcher.cs b/Wino.Core.Domain/Interfaces/IDispatcher.cs new file mode 100644 index 00000000..0eb9f08b --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IDispatcher.cs @@ -0,0 +1,10 @@ +using System; +using System.Threading.Tasks; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IDispatcher + { + Task ExecuteOnUIThread(Action action); + } +} diff --git a/Wino.Core.Domain/Interfaces/IFileService.cs b/Wino.Core.Domain/Interfaces/IFileService.cs new file mode 100644 index 00000000..5500e2f9 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IFileService.cs @@ -0,0 +1,12 @@ +using System.IO; +using System.Threading.Tasks; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IFileService + { + Task CopyFileAsync(string sourceFilePath, string destinationFolderPath); + Task GetFileStreamAsync(string folderPath, string fileName); + Task GetFileContentByApplicationUriAsync(string resourcePath); + } +} diff --git a/Wino.Core.Domain/Interfaces/IFolderMenuItem.cs b/Wino.Core.Domain/Interfaces/IFolderMenuItem.cs new file mode 100644 index 00000000..a361144a --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IFolderMenuItem.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Folders; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IFolderMenuItem : IBaseFolderMenuItem + { + MailAccount ParentAccount { get; } + } + + public interface IMergedAccountFolderMenuItem : IBaseFolderMenuItem { } + + public interface IBaseFolderMenuItem : IMenuItem + { + string FolderName { get; } + bool IsSynchronizationEnabled { get; } + int UnreadItemCount { get; set; } + SpecialFolderType SpecialFolderType { get; } + IEnumerable HandlingFolders { get; } + bool IsMoveTarget { get; } + bool IsSticky { get; } + bool IsSystemFolder { get; } + bool ShowUnreadCount { get; } + string AssignedAccountName { get; } + + void UpdateFolder(IMailItemFolder folder); + } +} diff --git a/Wino.Core.Domain/Interfaces/IFolderService.cs b/Wino.Core.Domain/Interfaces/IFolderService.cs new file mode 100644 index 00000000..9b58ce11 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IFolderService.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Folders; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Synchronization; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IFolderService + { + Task GetFolderStructureForAccountAsync(Guid accountId, bool includeHiddenFolders); + Task GetFolderAsync(Guid folderId); + Task GetFolderAsync(Guid accountId, string remoteFolderId); + Task> GetFoldersAsync(Guid accountId); + Task> GetUnreadUpdateFoldersAsync(Guid accountId); + Task SetSpecialFolderAsync(Guid folderId, SpecialFolderType type); + Task GetSpecialFolderByAccountIdAsync(Guid accountId, SpecialFolderType type); + Task GetCurrentItemCountForFolder(Guid folderId); + Task GetFolderNotificationBadgeAsync(Guid folderId); + Task ChangeStickyStatusAsync(Guid folderId, bool isSticky); + Task UpdateCustomServerMailListAsync(Guid accountId, List folders); + + Task UpdateSystemFolderConfigurationAsync(Guid accountId, SystemFolderConfiguration configuration); + Task ChangeFolderSynchronizationStateAsync(Guid folderId, bool isSynchronizationEnabled); + Task ChangeFolderShowUnreadCountStateAsync(Guid folderId, bool showUnreadCount); + + Task> GetSynchronizationFoldersAsync(SynchronizationOptions options); + + /// + /// Returns the folder - mail mapping for the given mail copy ids. + /// + Task> GetMailFolderPairMetadatasAsync(IEnumerable mailCopyIds); + + /// + /// Returns the folder - mail mapping for the given mail copy id. + /// + Task> GetMailFolderPairMetadatasAsync(string mailCopyId); + + // v2 + + /// + /// Performs bulk update for the given folders. + /// Used in Gmail. + /// + /// Account that folders belong to. + /// Folders to update. + Task BulkUpdateFolderStructureAsync(Guid accountId, List allFolders); + + /// + /// Updates Folder's delta synchronization identifier. + /// Only used in Outlook since it does per-folder sync. + /// + /// Folder id + /// New synchronization identifier. + /// New identifier if success. + Task UpdateFolderDeltaSynchronizationIdentifierAsync(Guid folderId, string synchronizationIdentifier); + + /// + /// Deletes the folder for the given account by remote folder id. + /// + /// Account to remove from. + /// Remote folder id. + /// + Task DeleteFolderAsync(Guid accountId, string remoteFolderId); + + /// + /// Adds a new folder. + /// + /// Folder to add. + Task InsertFolderAsync(MailItemFolder folder); + + + /// + /// Returns the known uids for the given folder. + /// Only used for IMAP + /// + /// Folder to get uIds for + Task> GetKnownUidsForFolderAsync(Guid folderId); + + /// + /// Checks if Inbox special folder exists for an account. + /// + /// Account id to check for. + /// True if Inbox exists, False if not. + Task IsInboxAvailableForAccountAsync(Guid accountId); + + Task TestAsync(); + } +} diff --git a/Wino.Core.Domain/Interfaces/IFontService.cs b/Wino.Core.Domain/Interfaces/IFontService.cs new file mode 100644 index 00000000..ff00cdab --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IFontService.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Reader; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IFontService + { + List GetReaderFonts(); + ReaderFontModel GetCurrentReaderFont(); + int GetCurrentReaderFontSize(); + + void ChangeReaderFont(ReaderFont font); + void ChangeReaderFontSize(int size); + } +} diff --git a/Wino.Core.Domain/Interfaces/IImapTestService.cs b/Wino.Core.Domain/Interfaces/IImapTestService.cs new file mode 100644 index 00000000..1288bc39 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IImapTestService.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IImapTestService + { + Task TestImapConnectionAsync(CustomServerInformation serverInformation); + } +} diff --git a/Wino.Core.Domain/Interfaces/IInitializeAsync.cs b/Wino.Core.Domain/Interfaces/IInitializeAsync.cs new file mode 100644 index 00000000..1850a7c0 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IInitializeAsync.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; + +namespace Wino.Core.Domain.Interfaces +{ + /// + /// An interface that all startup services must implement. + /// + public interface IInitializeAsync + { + Task InitializeAsync(); + } +} diff --git a/Wino.Core.Domain/Interfaces/IKeyPressService.cs b/Wino.Core.Domain/Interfaces/IKeyPressService.cs new file mode 100644 index 00000000..724ad0c4 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IKeyPressService.cs @@ -0,0 +1,8 @@ +namespace Wino.Core.Domain.Interfaces +{ + public interface IKeyPressService + { + bool IsCtrlKeyPressed(); + bool IsShiftKeyPressed(); + } +} diff --git a/Wino.Core.Domain/Interfaces/ILaunchProtocolService.cs b/Wino.Core.Domain/Interfaces/ILaunchProtocolService.cs new file mode 100644 index 00000000..5c141d4b --- /dev/null +++ b/Wino.Core.Domain/Interfaces/ILaunchProtocolService.cs @@ -0,0 +1,10 @@ +using System.Collections.Specialized; + +namespace Wino.Core.Domain.Interfaces +{ + public interface ILaunchProtocolService + { + object LaunchParameter { get; set; } + NameValueCollection MailtoParameters { get; set; } + } +} diff --git a/Wino.Core.Domain/Interfaces/ILogInitializer.cs b/Wino.Core.Domain/Interfaces/ILogInitializer.cs new file mode 100644 index 00000000..f5f7e97d --- /dev/null +++ b/Wino.Core.Domain/Interfaces/ILogInitializer.cs @@ -0,0 +1,9 @@ +namespace Wino.Core.Domain.Interfaces +{ + public interface ILogInitializer + { + void SetupLogger(string logFolderPath); + + void RefreshLoggingLevel(); + } +} diff --git a/Wino.Core.Domain/Interfaces/IMailService.cs b/Wino.Core.Domain/Interfaces/IMailService.cs new file mode 100644 index 00000000..7b09dc51 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IMailService.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using MimeKit; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Models.MailItem; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IMailService + { + Task GetSingleMailItemAsync(string mailCopyId, string remoteFolderId); + Task GetSingleMailItemAsync(Guid uniqueMailId); + Task CreateDraftAsync(MailAccount composerAccount, MimeMessage generatedReplyMime, MimeMessage replyingMimeMessage = null, IMailItem replyingMailItem = null); + Task> FetchMailsAsync(MailListInitializationOptions options); + + Task> GetMailIdsByFolderIdAsync(Guid folderId); + + // v2 + + /// + /// Deletes all mail copies for all folders. + /// + /// Account to remove from + /// Mail copy id to remove. + Task DeleteMailAsync(Guid accountId, string mailCopyId); + + Task ChangeReadStatusAsync(string mailCopyId, bool isRead); + Task ChangeFlagStatusAsync(string mailCopyId, bool isFlagged); + + Task CreateAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId); + Task DeleteAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId); + + Task CreateMailAsync(Guid accountId, NewMailItemPackage package); + + /// + /// Maps new mail item with the existing local draft copy. + /// In case of failure, it returns false. + /// Then synchronizers must insert a new mail item. + /// + /// Id of the account. It's important to map to the account since if the user use the same account with different providers, this call must map the correct one. + /// UniqueId of the local draft copy. + /// New assigned remote mail item id. + /// New assigned draft id if exists. + /// New message's thread/conversation id. + /// True if mapping is done. False if local copy doesn't exists. + Task MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId); + + /// + /// Maps new mail item with the existing local draft copy. + /// + /// + /// + /// + /// + /// + Task MapLocalDraftAsync(string newMailCopyId, string newDraftId, string newThreadId); + + Task CreateDraftMimeMessageAsync(Guid accountId, DraftCreationOptions options); + Task UpdateMailAsync(MailCopy mailCopy); + + /// + /// Gets the new inserted unread mails after the synchronization. + /// + /// Account id. + /// + /// Mail ids that synchronizer tried to download. If there was an issue with the + /// Items that tried and actually downloaded may differ. This function will return only new inserted ones. + /// + /// Newly inserted unread mails inside the Inbox folder. + Task> GetDownloadedUnreadMailsAsync(Guid accountId, IEnumerable downloadedMailCopyIds); + + /// + /// Returns the account that this mail copy unique id is assigned. + /// Used in toast notification handler. + /// + /// Unique id of the mail item. + /// Account that mail belongs to. + Task GetMailAccountByUniqueIdAsync(Guid uniqueMailId); + } +} diff --git a/Wino.Core.Domain/Interfaces/IMenuItem.cs b/Wino.Core.Domain/Interfaces/IMenuItem.cs new file mode 100644 index 00000000..68997e3f --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IMenuItem.cs @@ -0,0 +1,41 @@ +using System; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IMenuItem + { + /// + /// An id that this menu item holds. + /// For an account, it's AccountId. + /// For folder, it's FolderId. + /// For merged account, it's MergedAccountId. + /// Null if it's a menu item that doesn't hold any valuable entity. + /// + Guid? EntityId { get; } + + /// + /// Is any of the sub items that this menu item contains selected. + /// + // bool IsChildSelected { get; } + + /// + /// Whether the menu item is expanded or not. + /// + bool IsExpanded { get; set; } + + /// + /// Whether the menu item is selected or not. + /// + bool IsSelected { get; set; } + + /// + /// Parent menu item that contains this menu item. + /// + IMenuItem ParentMenuItem { get; } + + /// + /// Recursively expand all parent menu items if parent exists, starting from parent. + /// + void Expand(); + } +} diff --git a/Wino.Core.Domain/Interfaces/IMenuOperation.cs b/Wino.Core.Domain/Interfaces/IMenuOperation.cs new file mode 100644 index 00000000..48279547 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IMenuOperation.cs @@ -0,0 +1,8 @@ +namespace Wino.Core.Domain.Interfaces +{ + public interface IMenuOperation + { + bool IsEnabled { get; } + string Identifier { get; } + } +} diff --git a/Wino.Core.Domain/Interfaces/INativeAppService.cs b/Wino.Core.Domain/Interfaces/INativeAppService.cs new file mode 100644 index 00000000..4abd1dc6 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/INativeAppService.cs @@ -0,0 +1,25 @@ +using System; +using System.Threading.Tasks; +using Wino.Core.Domain.Models.Authorization; + +namespace Wino.Core.Domain.Interfaces +{ + public interface INativeAppService + { + string GetWebAuthenticationBrokerUri(); + Task GetMimeMessageStoragePath(); + Task GetQuillEditorBundlePathAsync(); + Task LaunchFileAsync(string filePath); + Task LaunchUriAsync(Uri uri); + bool IsAppRunning(); + + string GetFullAppVersion(); + + Task PinAppToTaskbarAsync(); + + /// + /// Some cryptographic shit is needed for requesting Google authentication in UWP. + /// + GoogleAuthorizationRequest GetGoogleAuthorizationRequest(); + } +} diff --git a/Wino.Core.Domain/Interfaces/INavigationAware.cs b/Wino.Core.Domain/Interfaces/INavigationAware.cs new file mode 100644 index 00000000..222d7c31 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/INavigationAware.cs @@ -0,0 +1,10 @@ +using Wino.Core.Domain.Models.Navigation; + +namespace Wino.Core.Domain.Interfaces +{ + public interface INavigationAware + { + void OnNavigatedTo(NavigationMode mode, object parameters); + void OnNavigatedFrom(NavigationMode mode, object parameters); + } +} diff --git a/Wino.Core.Domain/Interfaces/INotificationBuilder.cs b/Wino.Core.Domain/Interfaces/INotificationBuilder.cs new file mode 100644 index 00000000..683bff44 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/INotificationBuilder.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Wino.Core.Domain.Models.MailItem; + +namespace Wino.Core.Domain.Interfaces +{ + public interface INotificationBuilder + { + /// + /// Creates toast notifications for new mails. + /// + Task CreateNotificationsAsync(Guid inboxFolderId, IEnumerable newMailItems); + + /// + /// Gets the unread Inbox messages for each account and updates the taskbar icon. + /// + /// + Task UpdateTaskbarIconBadgeAsync(); + + /// + /// Creates test notification for test purposes. + /// + Task CreateTestNotificationAsync(string title, string message); + } +} diff --git a/Wino.Core.Domain/Interfaces/IPreferencesService.cs b/Wino.Core.Domain/Interfaces/IPreferencesService.cs new file mode 100644 index 00000000..38474cdb --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IPreferencesService.cs @@ -0,0 +1,144 @@ +using System; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Reader; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IPreferencesService + { + /// + /// When any of the preferences are changed. + /// + event EventHandler PreferenceChanged; + + /// + /// Setting: For changing the mail display container mode. + /// + MailListDisplayMode MailItemDisplayMode { get; set; } + + /// + /// Setting: Marking the item as read preference mode. + /// + MailMarkAsOption MarkAsPreference { get; set; } + + /// + /// Setting: Preferred time format for mail display. + /// + bool Prefer24HourTimeFormat { get; set; } + + /// + /// Setting: How many seconds should be waited on rendering page to mark item as read. + /// + int MarkAsDelay { get; set; } + + /// + /// Setting: Ask comfirmation from the user during permanent delete. + /// + bool IsHardDeleteProtectionEnabled { get; set; } + + /// + /// Setting: Thread mails into conversations. + /// + bool IsThreadingEnabled { get; set; } + + /// + /// Setting: Show sender pictures in mail list. + /// + bool IsShowSenderPicturesEnabled { get; set; } + + /// + /// Setting: Show preview text in mail list. + /// + bool IsShowPreviewEnabled { get; set; } + + /// + /// Setting: Enable/disable semantic zoom on clicking date headers. + /// + bool IsSemanticZoomEnabled { get; set; } + + /// + /// Setting: Set whether 'img' tags in rendered HTMLs should be removed. + /// + bool RenderImages { get; set; } + + /// + /// Setting: Set whether 'style' tags in rendered HTMls should be removed. + /// + bool RenderStyles { get; set; } + + /// + /// Gets the preferred rendering options for HTML rendering. + /// + MailRenderingOptions GetRenderingOptions(); + + /// + /// Setting: Swipe mail operation when mails are swiped to right. + /// + MailOperation RightSwipeOperation { get; set; } + + /// + /// Setting: Swipe mail operation when mails are swiped to left. + /// + MailOperation LeftSwipeOperation { get; set; } + + /// + /// Setting: Whether hover actions on mail pointer hover is enabled or not. + /// + bool IsHoverActionsEnabled { get; set; } + + /// + /// Setting: Hover action on the left when the mail is hovered over. + /// + MailOperation LeftHoverAction { get; set; } + + /// + /// Setting: Hover action on the center when the mail is hovered over. + /// + MailOperation CenterHoverAction { get; set; } + + /// + /// Setting: Hover action on the right when the mail is hovered over. + /// + MailOperation RightHoverAction { get; set; } + + /// + /// Setting: Whether logs are enabled or not. + /// + bool IsLoggingEnabled { get; set; } + + /// + /// Setting: Whether Mailkit Protocol Logger is enabled for ImapTestService or not. + /// + bool IsMailkitProtocolLoggerEnabled { get; set; } + + /// + /// Setting: Which entity id (merged account or folder) should be expanded automatically on startup. + /// + Guid? StartupEntityId { get; set; } + + /// + /// Setting: Display language for the application. + /// + AppLanguage CurrentLanguage { get; set; } + + /// + /// Setting: Display font for the mail reader. Not composer. + /// + ReaderFont ReaderFont { get; set; } + + /// + /// Setting: Font size for the mail reader. Not composer. + /// + int ReaderFontSize { get; set; } + + /// + /// Setting: Whether the navigation pane is opened on the last session or not. + /// + bool IsNavigationPaneOpened { get; set; } + + /// + /// Setting: Whether the next item should be automatically selected once the current item is moved or removed. + /// + bool AutoSelectNextItem { get; set; } + } +} diff --git a/Wino.Core.Domain/Interfaces/IProviderDetail.cs b/Wino.Core.Domain/Interfaces/IProviderDetail.cs new file mode 100644 index 00000000..e43c6044 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IProviderDetail.cs @@ -0,0 +1,13 @@ +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IProviderDetail + { + MailProviderType Type { get; } + string Name { get; } + string Description { get; } + string ProviderImage { get; } + bool IsSupported { get; } + } +} diff --git a/Wino.Core.Domain/Interfaces/IProviderService.cs b/Wino.Core.Domain/Interfaces/IProviderService.cs new file mode 100644 index 00000000..f961b0ea --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IProviderService.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IProviderService + { + List GetProviderDetails(); + IProviderDetail GetProviderDetail(MailProviderType type); + } +} diff --git a/Wino.Core.Domain/Interfaces/IRequestBundle.cs b/Wino.Core.Domain/Interfaces/IRequestBundle.cs new file mode 100644 index 00000000..dca28129 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IRequestBundle.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Interfaces +{ + /// + /// Represents a group of requests. + /// + public interface IRequestBundle + { + string BundleId { get; set; } + IRequestBase Request { get; } + } + + /// + /// Represents a group of requests with their native response types. + /// + /// Native request type from each synchronizer to store. + public interface IRequestBundle : IRequestBundle + { + TRequest NativeRequest { get; } + } + + public interface IRequestBase + { + /// + /// Synchronizer option to perform. + /// + MailSynchronizerOperation Operation { get; } + + /// + /// UI changes to apply to the item before sending the request to the server. + /// Changes here only affect the UI, not the item itself. + /// Changes here are reverted if the request fails by calling . + /// + void ApplyUIChanges(); + + /// + /// Reverts the UI changes applied by if the request fails. + /// + void RevertUIChanges(); + } + + public interface IRequest : IRequestBase + { + MailCopy Item { get; } + IBatchChangeRequest CreateBatch(IEnumerable requests); + } + + public interface IFolderRequest : IRequestBase + { + MailItemFolder Folder { get; } + } + + public interface IBatchChangeRequest : IRequestBase + { + IEnumerable Items { get; } + } +} diff --git a/Wino.Core.Domain/Interfaces/ISignatureService.cs b/Wino.Core.Domain/Interfaces/ISignatureService.cs new file mode 100644 index 00000000..14f7f75c --- /dev/null +++ b/Wino.Core.Domain/Interfaces/ISignatureService.cs @@ -0,0 +1,33 @@ +using System; +using System.Threading.Tasks; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Domain.Interfaces +{ + public interface ISignatureService + { + /// + /// Returns the assigned account signature for the account. + /// + /// + /// + Task GetAccountSignatureAsync(Guid accountId); + + /// + /// Creates the initial signature for new created accounts. + /// + /// + /// + Task CreateDefaultSignatureAsync(Guid accountId); + + /// + /// Updates account's existing signature with the given HTML signature. + /// + Task UpdateAccountSignatureAsync(Guid accountId, string htmlBody); + + /// + /// Disabled signature for the account and deletes existing signature. + /// + Task DeleteAccountSignatureAssignment(Guid accountId); + } +} diff --git a/Wino.Core.Domain/Interfaces/IStatePersistenceService.cs b/Wino.Core.Domain/Interfaces/IStatePersistenceService.cs new file mode 100644 index 00000000..f6a8cf73 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IStatePersistenceService.cs @@ -0,0 +1,47 @@ +using System; +using System.ComponentModel; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IStatePersistanceService : INotifyPropertyChanged + { + event EventHandler StatePropertyChanged; + + /// + /// True when there is an active renderer for selected mail. + /// + bool IsReadingMail { get; set; } + + /// + /// Shell's app bar title string. + /// + string CoreWindowTitle { get; set; } + + /// + /// When only reader page is visible in small sized window. + /// + bool IsReaderNarrowed { get; set; } + + /// + /// Should display back button on the shell title bar. + /// + bool IsBackButtonVisible { get; } + + + /// + /// Setting: Opened pane length for the navigation view. + /// + double OpenPaneLength { get; set; } + + /// + /// Whether the mail rendering page should be shifted from top to adjust the design + /// for standalone EML viewer or not. + /// + bool ShouldShiftMailRenderingDesign { get; set; } + + /// + /// Setting: Mail list pane length for listing mails. + /// + double MailListPaneLength { get; set; } + } +} diff --git a/Wino.Core.Domain/Interfaces/IStoreManagementService.cs b/Wino.Core.Domain/Interfaces/IStoreManagementService.cs new file mode 100644 index 00000000..36e3ebc9 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IStoreManagementService.cs @@ -0,0 +1,19 @@ +using System.Threading.Tasks; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Store; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IStoreManagementService + { + /// + /// Checks whether user has the type of an add-on purchased. + /// + Task HasProductAsync(StoreProductType productType); + + /// + /// Attempts to purchase the given add-on. + /// + Task PurchaseAsync(StoreProductType productType); + } +} diff --git a/Wino.Core.Domain/Interfaces/IStoreRatingDialog.cs b/Wino.Core.Domain/Interfaces/IStoreRatingDialog.cs new file mode 100644 index 00000000..c56ef4a4 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IStoreRatingDialog.cs @@ -0,0 +1,8 @@ +namespace Wino.Core.Domain.Interfaces +{ + public interface IStoreRatingDialog + { + bool DontAskAgain { get; } + bool RateWinoClicked { get; } + } +} diff --git a/Wino.Core.Domain/Interfaces/IStoreRatingService.cs b/Wino.Core.Domain/Interfaces/IStoreRatingService.cs new file mode 100644 index 00000000..7b1ba764 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IStoreRatingService.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IStoreRatingService + { + Task PromptRatingDialogAsync(); + Task LaunchStorePageForReviewAsync(); + } +} diff --git a/Wino.Core.Domain/Interfaces/ISynchronizationProgress.cs b/Wino.Core.Domain/Interfaces/ISynchronizationProgress.cs new file mode 100644 index 00000000..eb952cf7 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/ISynchronizationProgress.cs @@ -0,0 +1,19 @@ +using System; + +namespace Wino.Core.Domain.Interfaces +{ + /// + /// An interface for reporting progress of the synchronization. + /// Gmail does not support reporting folder progress. + /// For others, account progress is calculated based on the number of folders. + /// + public interface ISynchronizationProgress + { + /// + /// Reports account synchronization progress. + /// + /// Account id for the report. + /// Value. This is always between 0 - 100 + void AccountProgressUpdated(Guid accountId, int progress); + } +} diff --git a/Wino.Core.Domain/Interfaces/IThemeService.cs b/Wino.Core.Domain/Interfaces/IThemeService.cs new file mode 100644 index 00000000..57c3f471 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IThemeService.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Personalization; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IThemeService : IInitializeAsync + { + event EventHandler ElementThemeChanged; + event EventHandler AccentColorChangedBySystem; + event EventHandler AccentColorChanged; + + Task> GetAvailableThemesAsync(); + Task CreateNewCustomThemeAsync(string themeName, string accentColor, byte[] wallpaperData); + Task> GetCurrentCustomThemesAsync(); + + Task ApplyCustomThemeAsync(bool isInitializing); + + // Settings + ApplicationElementTheme RootTheme { get; set; } + Guid CurrentApplicationThemeId { get; set; } + string AccentColor { get; set; } + string GetSystemAccentColorHex(); + } +} diff --git a/Wino.Core.Domain/Interfaces/IThreadingStrategy.cs b/Wino.Core.Domain/Interfaces/IThreadingStrategy.cs new file mode 100644 index 00000000..ed5d57f0 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IThreadingStrategy.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Models.MailItem; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IThreadingStrategy + { + Task> ThreadItemsAsync(List items); + bool ShouldThreadWithItem(IMailItem originalItem, IMailItem targetItem); + } +} diff --git a/Wino.Core.Domain/Interfaces/IThreadingStrategyProvider.cs b/Wino.Core.Domain/Interfaces/IThreadingStrategyProvider.cs new file mode 100644 index 00000000..9e3fa87b --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IThreadingStrategyProvider.cs @@ -0,0 +1,13 @@ +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IThreadingStrategyProvider + { + /// + /// Returns corresponding threading strategy that applies to given provider type. + /// + /// Provider type. + IThreadingStrategy GetStrategy(MailProviderType mailProviderType); + } +} diff --git a/Wino.Core.Domain/Interfaces/ITranslationService.cs b/Wino.Core.Domain/Interfaces/ITranslationService.cs new file mode 100644 index 00000000..e756f3cd --- /dev/null +++ b/Wino.Core.Domain/Interfaces/ITranslationService.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Translations; + +namespace Wino.Core.Domain.Interfaces +{ + public interface ITranslationService : IInitializeAsync + { + Task InitializeLanguageAsync(AppLanguage language, bool ignoreCurrentLanguageCheck = false); + List GetAvailableLanguages(); + } +} diff --git a/Wino.Core.Domain/Interfaces/IUnderlyingThemeService.cs b/Wino.Core.Domain/Interfaces/IUnderlyingThemeService.cs new file mode 100644 index 00000000..5de11c78 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IUnderlyingThemeService.cs @@ -0,0 +1,7 @@ +namespace Wino.Core.Domain.Interfaces +{ + public interface IUnderlyingThemeService + { + bool IsUnderlyingThemeDark(); + } +} diff --git a/Wino.Core.Domain/Interfaces/IWinoNavigationService.cs b/Wino.Core.Domain/Interfaces/IWinoNavigationService.cs new file mode 100644 index 00000000..ba5cf026 --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IWinoNavigationService.cs @@ -0,0 +1,18 @@ +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Navigation; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IWinoNavigationService + { + bool Navigate(WinoPage page, + object parameter = null, + NavigationReferenceFrame frame = NavigationReferenceFrame.ShellFrame, + NavigationTransitionType transition = NavigationTransitionType.None); + void NavigateCompose(IMailItem mailItem, NavigationTransitionType transition = NavigationTransitionType.None); + void NavigateRendering(IMailItem mailItem, NavigationTransitionType transition = NavigationTransitionType.None); + void NavigateRendering(MimeMessageInformation mimeMessageInformation, NavigationTransitionType transition = NavigationTransitionType.None); + void NavigateFolder(NavigateMailFolderEventArgs args); + } +} diff --git a/Wino.Core.Domain/Interfaces/IWinoRequestDelegator.cs b/Wino.Core.Domain/Interfaces/IWinoRequestDelegator.cs new file mode 100644 index 00000000..9782e4dd --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IWinoRequestDelegator.cs @@ -0,0 +1,35 @@ +using System.Threading.Tasks; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Folders; +using Wino.Core.Domain.Models.MailItem; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IWinoRequestDelegator + { + /// + /// Prepares requires IRequest collection for mail actions and executes them via proper synchronizers. + /// + /// Preperation model that encapsulates action and mail items. + Task ExecuteAsync(MailOperationPreperationRequest prerperationRequest); + + /// + /// Queues new draft creation request for synchronizer. + /// + /// A class that holds the parameters for creating a draft. + Task ExecuteAsync(DraftPreperationRequest draftPreperationRequest); + + /// + /// Queues a new request for synchronizer to send a draft. + /// + /// Draft sending request. + Task ExecuteAsync(SendDraftPreparationRequest sendDraftPreperationRequest); + + /// + /// Prepares requires IRequest collection for folder actions and executes them via proper synchronizers. + /// + /// Folder operation to execute. + /// Target folder + Task ExecuteAsync(FolderOperation operation, IMailItemFolder folderStructure); + } +} diff --git a/Wino.Core.Domain/Interfaces/IWinoRequestProcessor.cs b/Wino.Core.Domain/Interfaces/IWinoRequestProcessor.cs new file mode 100644 index 00000000..c35b4e9c --- /dev/null +++ b/Wino.Core.Domain/Interfaces/IWinoRequestProcessor.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Folders; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Requests; + +namespace Wino.Core.Domain.Interfaces +{ + public interface IWinoRequestProcessor + { + Task PrepareFolderRequestAsync(FolderOperation operation, IMailItemFolder mailItemFolder); + + /// + /// Prepares proper Wino requests for synchronizers to execute categorized by AccountId and FolderId. + /// + /// User action + /// Selected mails. + /// When required folder target is not available for account. + Task> PrepareRequestsAsync(MailOperationPreperationRequest request); + } +} diff --git a/Wino.Core.Domain/Models/Accounts/ImapAuthenticationMethodModel.cs b/Wino.Core.Domain/Models/Accounts/ImapAuthenticationMethodModel.cs new file mode 100644 index 00000000..f28b9d6e --- /dev/null +++ b/Wino.Core.Domain/Models/Accounts/ImapAuthenticationMethodModel.cs @@ -0,0 +1,10 @@ +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Models.Accounts +{ + public class ImapAuthenticationMethodModel(ImapAuthenticationMethod imapAuthenticationMethod, string displayName) + { + public ImapAuthenticationMethod ImapAuthenticationMethod { get; } = imapAuthenticationMethod; + public string DisplayName { get; } = displayName; + } +} diff --git a/Wino.Core.Domain/Models/Accounts/ImapConnectionSecurityModel.cs b/Wino.Core.Domain/Models/Accounts/ImapConnectionSecurityModel.cs new file mode 100644 index 00000000..a618a490 --- /dev/null +++ b/Wino.Core.Domain/Models/Accounts/ImapConnectionSecurityModel.cs @@ -0,0 +1,10 @@ +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Models.Accounts +{ + public class ImapConnectionSecurityModel(ImapConnectionSecurity imapConnectionSecurity, string displayName) + { + public ImapConnectionSecurity ImapConnectionSecurity { get; } = imapConnectionSecurity; + public string DisplayName { get; } = displayName; + } +} diff --git a/Wino.Core.Domain/Models/Accounts/ProviderDetail.cs b/Wino.Core.Domain/Models/Accounts/ProviderDetail.cs new file mode 100644 index 00000000..dc9a5824 --- /dev/null +++ b/Wino.Core.Domain/Models/Accounts/ProviderDetail.cs @@ -0,0 +1,47 @@ +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.Domain.Models.Accounts +{ + public class ProviderDetail : IProviderDetail + { + public MailProviderType Type { get; } + + public string Name { get; } + + public string Description { get; } + + public string ProviderImage => $"ms-appx:///Assets/Providers/{Type}.png"; + + public bool IsSupported => Type == MailProviderType.Outlook || Type == MailProviderType.Gmail || Type == MailProviderType.IMAP4; + + public ProviderDetail(MailProviderType type) + { + Type = type; + + switch (Type) + { + case MailProviderType.Outlook: + Name = "Outlook"; + Description = "Outlook.com, Live.com, Hotmail, MSN"; + break; + case MailProviderType.Office365: + Name = "Office 365"; + Description = "Office 365, Exchange"; + break; + case MailProviderType.Gmail: + Name = "Gmail"; + Description = Translator.ProviderDetail_Gmail_Description; + break; + case MailProviderType.Yahoo: + Name = "Yahoo"; + Description = "Yahoo Mail"; + break; + case MailProviderType.IMAP4: + Name = Translator.ProviderDetail_IMAP_Title; + Description = Translator.ProviderDetail_IMAP_Description; + break; + } + } + } +} diff --git a/Wino.Core.Domain/Models/Authentication/TokenInformationBase.cs b/Wino.Core.Domain/Models/Authentication/TokenInformationBase.cs new file mode 100644 index 00000000..27f69abf --- /dev/null +++ b/Wino.Core.Domain/Models/Authentication/TokenInformationBase.cs @@ -0,0 +1,20 @@ +using System; + +namespace Wino.Core.Domain.Models.Authentication +{ + public class TokenInformationBase + { + public string AccessToken { get; set; } + public string RefreshToken { get; set; } + + /// + /// UTC date for token expiration. + /// + public DateTime ExpiresAt { get; set; } + + /// + /// Gets the value indicating whether the token is expired or not. + /// + public bool IsExpired => DateTime.UtcNow >= ExpiresAt; + } +} diff --git a/Wino.Core.Domain/Models/Authorization/GoogleAuthorizationRequest.cs b/Wino.Core.Domain/Models/Authorization/GoogleAuthorizationRequest.cs new file mode 100644 index 00000000..455fff47 --- /dev/null +++ b/Wino.Core.Domain/Models/Authorization/GoogleAuthorizationRequest.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Wino.Core.Domain.Exceptions; + +namespace Wino.Core.Domain.Models.Authorization +{ + public class GoogleAuthorizationRequest + { + public const string RedirectUri = "google.pw.oauth2:/oauth2redirect"; + + const string authorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth"; + const string CodeChallangeMethod = "S256"; + + public GoogleAuthorizationRequest(string state, string codeVerifier, string codeChallange) + { + State = state; + CodeVerifier = codeVerifier; + CodeChallange = codeChallange; + } + + // Pre + public string State { get; set; } + public string CodeVerifier { get; set; } + public string CodeChallange { get; set; } + public string ClientId { get; set; } + + // Post + public string AuthorizationCode { get; set; } + + public string BuildRequest(string clientId) + { + ClientId = clientId; + + // Creates the OAuth 2.0 authorization request. + return string.Format("{0}?response_type=code&scope=https://mail.google.com/ https://www.googleapis.com/auth/gmail.labels&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}", + authorizationEndpoint, + Uri.EscapeDataString(RedirectUri), + ClientId, + State, + CodeChallange, + CodeChallangeMethod); + } + + public void ValidateAuthorizationCode(Uri callbackUri) + { + if (callbackUri == null) + throw new GoogleAuthenticationException(Translator.Exception_GoogleAuthCallbackNull); + + string queryString = callbackUri.Query; + + Dictionary queryStringParams = queryString.Substring(1).Split('&').ToDictionary(c => c.Split('=')[0], c => Uri.UnescapeDataString(c.Split('=')[1])); + + if (queryStringParams.ContainsKey("error")) + throw new GoogleAuthenticationException(string.Format(Translator.Exception_GoogleAuthError, queryStringParams["error"])); + + if (!queryStringParams.ContainsKey("code") || !queryStringParams.ContainsKey("state")) + throw new GoogleAuthenticationException(Translator.Exception_GoogleAuthCorruptedCode + queryString); + + // Gets the Authorization code & state + string code = queryStringParams["code"]; + string incomingState = queryStringParams["state"]; + + // Compares the receieved state to the expected value, to ensure that + // this app made the request which resulted in authorization + if (incomingState != State) + throw new GoogleAuthenticationException(string.Format(Translator.Exception_GoogleAuthInvalidResponse, incomingState)); + + AuthorizationCode = code; + } + } +} diff --git a/Wino.Core.Domain/Models/Authorization/GoogleTokenizationRequest.cs b/Wino.Core.Domain/Models/Authorization/GoogleTokenizationRequest.cs new file mode 100644 index 00000000..0590df09 --- /dev/null +++ b/Wino.Core.Domain/Models/Authorization/GoogleTokenizationRequest.cs @@ -0,0 +1,27 @@ +using System; +using Wino.Core.Domain.Exceptions; + +namespace Wino.Core.Domain.Models.Authorization +{ + public class GoogleTokenizationRequest + { + public GoogleTokenizationRequest(GoogleAuthorizationRequest authorizationRequest) + { + if (authorizationRequest == null) + throw new GoogleAuthenticationException("Authorization request is empty."); + + AuthorizationRequest = authorizationRequest; + + if (string.IsNullOrEmpty(AuthorizationRequest.AuthorizationCode)) + throw new GoogleAuthenticationException("Authorization request has empty code."); + } + + public GoogleAuthorizationRequest AuthorizationRequest { get; set; } + + public string BuildRequest() + { + return string.Format("code={0}&redirect_uri={1}&client_id={2}&code_verifier={3}&scope=&grant_type=authorization_code", + AuthorizationRequest.AuthorizationCode, Uri.EscapeDataString(GoogleAuthorizationRequest.RedirectUri), AuthorizationRequest.ClientId, AuthorizationRequest.CodeVerifier); + } + } +} diff --git a/Wino.Core.Domain/Models/AutoDiscovery/AutoDiscoveryConnectionTestFailedPackage.cs b/Wino.Core.Domain/Models/AutoDiscovery/AutoDiscoveryConnectionTestFailedPackage.cs new file mode 100644 index 00000000..b4668aac --- /dev/null +++ b/Wino.Core.Domain/Models/AutoDiscovery/AutoDiscoveryConnectionTestFailedPackage.cs @@ -0,0 +1,21 @@ +using System; + +namespace Wino.Core.Domain.Models.AutoDiscovery +{ + public class AutoDiscoveryConnectionTestFailedPackage + { + public AutoDiscoveryConnectionTestFailedPackage(AutoDiscoverySettings settings, Exception error) + { + Settings = settings ?? throw new ArgumentNullException(nameof(settings)); + Error = error ?? throw new ArgumentNullException(nameof(error)); + } + + public AutoDiscoveryConnectionTestFailedPackage(Exception error) + { + Error = error ?? throw new ArgumentNullException(nameof(error)); + } + + public AutoDiscoverySettings Settings { get; set; } + public Exception Error { get; set; } + } +} diff --git a/Wino.Core.Domain/Models/AutoDiscovery/AutoDiscoveryMinimalSettings.cs b/Wino.Core.Domain/Models/AutoDiscovery/AutoDiscoveryMinimalSettings.cs new file mode 100644 index 00000000..b1135a5b --- /dev/null +++ b/Wino.Core.Domain/Models/AutoDiscovery/AutoDiscoveryMinimalSettings.cs @@ -0,0 +1,9 @@ +namespace Wino.Core.Domain.Models.AutoDiscovery +{ + public class AutoDiscoveryMinimalSettings + { + public string DisplayName { get; set; } + public string Email { get; set; } + public string Password { get; set; } + } +} diff --git a/Wino.Core.Domain/Models/AutoDiscovery/AutoDiscoveryProviderSetting.cs b/Wino.Core.Domain/Models/AutoDiscovery/AutoDiscoveryProviderSetting.cs new file mode 100644 index 00000000..dca80eb0 --- /dev/null +++ b/Wino.Core.Domain/Models/AutoDiscovery/AutoDiscoveryProviderSetting.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace Wino.Core.Domain.Models.AutoDiscovery +{ + public class AutoDiscoveryProviderSetting + { + [JsonProperty("protocol")] + public string Protocol { get; set; } + + [JsonProperty("address")] + public string Address { get; set; } + + [JsonProperty("port")] + public int Port { get; set; } + + [JsonProperty("secure")] + public string Secure { get; set; } + + [JsonProperty("username")] + public string Username { get; set; } + } +} diff --git a/Wino.Core.Domain/Models/AutoDiscovery/AutoDiscoverySettings.cs b/Wino.Core.Domain/Models/AutoDiscovery/AutoDiscoverySettings.cs new file mode 100644 index 00000000..74f4ae2e --- /dev/null +++ b/Wino.Core.Domain/Models/AutoDiscovery/AutoDiscoverySettings.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Domain.Models.AutoDiscovery +{ + public class AutoDiscoverySettings + { + [JsonProperty("domain")] + public string Domain { get; set; } + + [JsonProperty("password")] + public string Password { get; set; } + + [JsonProperty("settings")] + public List Settings { get; set; } + + /// + /// Gets whether this domain requires additional steps for password like app-specific password or sth. + /// + public bool IsPasswordSupportLinkAvailable => !string.IsNullOrEmpty(Password) && Uri.TryCreate(Password, UriKind.Absolute, out _); + + public AutoDiscoveryMinimalSettings UserMinimalSettings { get; set; } + + public CustomServerInformation ToServerInformation() + { + var imapSettings = GetImapSettings(); + var smtpSettings = GetSmptpSettings(); + + if (imapSettings == null || smtpSettings == null) return null; + + bool imapRequiresSSL = imapSettings.Secure == "SSL"; + bool smtpRequiresSSL = smtpSettings.Secure == "SSL"; + + string imapUrl = imapSettings.Address; + string smtpUrl = smtpSettings.Address; + + string imapUsername = imapSettings.Username; + string smtpUsername = smtpSettings.Username; + + int imapPort = imapSettings.Port; + int smtpPort = smtpSettings.Port; + + var serverInfo = new CustomServerInformation + { + Id = Guid.NewGuid(), + DisplayName = UserMinimalSettings.DisplayName, + Address = UserMinimalSettings.Email, + IncomingServerPassword = UserMinimalSettings.Password, + OutgoingServerPassword = UserMinimalSettings.Password, + IncomingRequiresSSL = imapRequiresSSL, + OutgoingRequresSSL = smtpRequiresSSL, + IncomingServer = imapUrl, + OutgoingServer = smtpUrl, + IncomingServerPort = imapPort.ToString(), + OutgoingServerPort = smtpPort.ToString(), + IncomingServerType = Enums.CustomIncomingServerType.IMAP4, + IncomingServerUsername = imapUsername, + OutgoingServerUsername = smtpUsername + }; + + return serverInfo; + } + + public AutoDiscoveryProviderSetting GetImapSettings() + => Settings?.Find(a => a.Protocol == "IMAP"); + + public AutoDiscoveryProviderSetting GetSmptpSettings() + => Settings?.Find(a => a.Protocol == "SMTP"); + } +} diff --git a/Wino.Core.Domain/Models/Comparers/DateComparer.cs b/Wino.Core.Domain/Models/Comparers/DateComparer.cs new file mode 100644 index 00000000..c739ec9e --- /dev/null +++ b/Wino.Core.Domain/Models/Comparers/DateComparer.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Wino.Core.Domain.Models.MailItem; + +namespace Wino.Core.Domain.Models.Comparers +{ + public class DateComparer : IComparer, IEqualityComparer + { + public int Compare(IMailItem x, IMailItem y) + { + return DateTime.Compare(y.CreationDate, x.CreationDate); + } + + public new bool Equals(object x, object y) + { + if (x is IMailItem firstItem && y is IMailItem secondItem) + { + return firstItem.Equals(secondItem); + } + + return false; + } + + public int GetHashCode(object obj) => (obj as IMailItem).GetHashCode(); + + public DateComparer() + { + + } + } +} diff --git a/Wino.Core.Domain/Models/Comparers/DateTimeComparer.cs b/Wino.Core.Domain/Models/Comparers/DateTimeComparer.cs new file mode 100644 index 00000000..eab8185e --- /dev/null +++ b/Wino.Core.Domain/Models/Comparers/DateTimeComparer.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +namespace Wino.Core.Domain.Models.Comparers +{ + /// + /// Used to insert date grouping into proper place in Reader page. + /// + public class DateTimeComparer : IComparer + { + public int Compare(DateTime x, DateTime y) + { + return DateTime.Compare(y, x); + } + } +} diff --git a/Wino.Core.Domain/Models/Comparers/FolderNameComparer.cs b/Wino.Core.Domain/Models/Comparers/FolderNameComparer.cs new file mode 100644 index 00000000..8f7e0928 --- /dev/null +++ b/Wino.Core.Domain/Models/Comparers/FolderNameComparer.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Domain.Models.Comparers +{ + public class FolderNameComparer : IComparer + { + public int Compare(MailItemFolder x, MailItemFolder y) + { + return x.FolderName.CompareTo(y.FolderName); + } + } +} diff --git a/Wino.Core.Domain/Models/Comparers/ListItemComparer.cs b/Wino.Core.Domain/Models/Comparers/ListItemComparer.cs new file mode 100644 index 00000000..d73ca190 --- /dev/null +++ b/Wino.Core.Domain/Models/Comparers/ListItemComparer.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using Wino.Core.Domain.Models.MailItem; + +namespace Wino.Core.Domain.Models.Comparers +{ + public class ListItemComparer : IComparer + { + public bool SortByName { get; set; } + + public DateComparer DateComparer = new DateComparer(); + public readonly NameComparer NameComparer = new NameComparer(); + + public int Compare(object x, object y) + { + if (x is IMailItem xMail && y is IMailItem yMail) + { + var itemComparer = GetItemComparer(); + + return itemComparer.Compare(xMail, yMail); + } + else if (x is DateTime dateX && y is DateTime dateY) + return DateTime.Compare(dateY, dateX); + else if (x is string stringX && y is string stringY) + return stringY.CompareTo(stringX); + + return 0; + } + + public IComparer GetItemComparer() + { + if (SortByName) + return NameComparer; + else + return DateComparer; + } + } +} diff --git a/Wino.Core.Domain/Models/Comparers/NameComparer.cs b/Wino.Core.Domain/Models/Comparers/NameComparer.cs new file mode 100644 index 00000000..86b5c10f --- /dev/null +++ b/Wino.Core.Domain/Models/Comparers/NameComparer.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Wino.Core.Domain.Models.MailItem; + +namespace Wino.Core.Domain.Models.Comparers +{ + public class NameComparer : IComparer + { + public int Compare(IMailItem x, IMailItem y) + { + return string.Compare(x.FromName, y.FromName); + } + } +} diff --git a/Wino.Core.Domain/Models/Folders/AccountFolderTree.cs b/Wino.Core.Domain/Models/Folders/AccountFolderTree.cs new file mode 100644 index 00000000..7fb13dc9 --- /dev/null +++ b/Wino.Core.Domain/Models/Folders/AccountFolderTree.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Models.Folders +{ + /// + /// Grouped folder information for the menu for given account. + /// + public class AccountFolderTree + { + public MailAccount Account { get; } + public List Folders { get; set; } = new List(); + + public AccountFolderTree(MailAccount account) + { + Account = account; + } + + public bool HasSpecialTypeFolder(SpecialFolderType type) + { + foreach (var folderStructure in Folders) + { + bool hasSpecialFolder = folderStructure.ContainsSpecialFolderType(type); + + if (hasSpecialFolder) + return true; + } + + return false; + } + } +} diff --git a/Wino.Core.Domain/Models/Folders/FolderOperationMenuItem.cs b/Wino.Core.Domain/Models/Folders/FolderOperationMenuItem.cs new file mode 100644 index 00000000..e54afa7f --- /dev/null +++ b/Wino.Core.Domain/Models/Folders/FolderOperationMenuItem.cs @@ -0,0 +1,14 @@ +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Menus; + +namespace Wino.Core.Domain.Models.Folders +{ + public class FolderOperationMenuItem : MenuOperationItemBase, IMenuOperation + { + protected FolderOperationMenuItem(FolderOperation operation, bool isEnabled) : base(operation, isEnabled) { } + + public static FolderOperationMenuItem Create(FolderOperation operation, bool isEnabled = true) + => new FolderOperationMenuItem(operation, isEnabled); + } +} diff --git a/Wino.Core.Domain/Models/Folders/IMailItemFolder.cs b/Wino.Core.Domain/Models/Folders/IMailItemFolder.cs new file mode 100644 index 00000000..8853cbf7 --- /dev/null +++ b/Wino.Core.Domain/Models/Folders/IMailItemFolder.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Models.Folders +{ + public interface IMailItemFolder + { + string BackgroundColorHex { get; set; } + string DeltaToken { get; set; } + string FolderName { get; set; } + long HighestModeSeq { get; set; } + Guid Id { get; set; } + bool IsHidden { get; set; } + bool IsSticky { get; set; } + bool IsSynchronizationEnabled { get; set; } + bool IsSystemFolder { get; set; } + DateTime? LastSynchronizedDate { get; set; } + Guid MailAccountId { get; set; } + string ParentRemoteFolderId { get; set; } + string RemoteFolderId { get; set; } + SpecialFolderType SpecialFolderType { get; set; } + string TextColorHex { get; set; } + uint UidValidity { get; set; } + List ChildFolders { get; set; } + bool IsMoveTarget { get; } + bool ShowUnreadCount { get; set; } + + bool ContainsSpecialFolderType(SpecialFolderType type); + } +} diff --git a/Wino.Core.Domain/Models/MailItem/DraftCreationOptions.cs b/Wino.Core.Domain/Models/MailItem/DraftCreationOptions.cs new file mode 100644 index 00000000..aff9b515 --- /dev/null +++ b/Wino.Core.Domain/Models/MailItem/DraftCreationOptions.cs @@ -0,0 +1,40 @@ +using System.Collections.Specialized; +using System.Linq; +using MimeKit; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Models.MailItem +{ + public class DraftCreationOptions + { + public MimeMessage ReferenceMimeMessage { get; set; } + public MailCopy ReferenceMailCopy { get; set; } + public DraftCreationReason Reason { get; set; } + + #region Mailto Protocol Related Stuff + + public const string MailtoSubjectParameterKey = "subject"; + public const string MailtoBodyParameterKey = "body"; + public const string MailtoToParameterKey = "mailto"; + public const string MailtoCCParameterKey = "cc"; + public const string MailtoBCCParameterKey = "bcc"; + + public NameValueCollection MailtoParameters { get; set; } + + private bool IsMailtoParameterExists(string parameterKey) + => MailtoParameters != null + && MailtoParameters.AllKeys.Contains(parameterKey); + + public bool TryGetMailtoValue(string key, out string value) + { + bool valueExists = IsMailtoParameterExists(key); + + value = valueExists ? MailtoParameters[key] : string.Empty; + + return valueExists; + } + + #endregion + } +} diff --git a/Wino.Core.Domain/Models/MailItem/DraftPreperationRequest.cs b/Wino.Core.Domain/Models/MailItem/DraftPreperationRequest.cs new file mode 100644 index 00000000..2be737f4 --- /dev/null +++ b/Wino.Core.Domain/Models/MailItem/DraftPreperationRequest.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; +using MimeKit; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Domain.Models.MailItem +{ + public class DraftPreperationRequest : DraftCreationOptions + { + public DraftPreperationRequest(MailAccount account, MailCopy createdLocalDraftCopy, MimeMessage createdLocalDraftMimeMessage) + { + Account = account ?? throw new ArgumentNullException(nameof(account)); + + CreatedLocalDraftCopy = createdLocalDraftCopy ?? throw new ArgumentNullException(nameof(createdLocalDraftCopy)); + CreatedLocalDraftMimeMessage = createdLocalDraftMimeMessage ?? throw new ArgumentNullException(nameof(createdLocalDraftMimeMessage)); + } + + public MailCopy CreatedLocalDraftCopy { get; set; } + public MimeMessage CreatedLocalDraftMimeMessage { get; set; } + public MailAccount Account { get; } + } +} diff --git a/Wino.Core.Domain/Models/MailItem/IMailItem.cs b/Wino.Core.Domain/Models/MailItem/IMailItem.cs new file mode 100644 index 00000000..b75c24af --- /dev/null +++ b/Wino.Core.Domain/Models/MailItem/IMailItem.cs @@ -0,0 +1,33 @@ +using System; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Domain.Models.MailItem +{ + /// + /// Interface of simplest representation of a MailCopy. + /// + public interface IMailItem + { + Guid UniqueId { get; } + string Id { get; } + string Subject { get; } + string ThreadId { get; } + string MessageId { get; } + string References { get; } + string InReplyTo { get; } + string PreviewText { get; } + string FromName { get; } + DateTime CreationDate { get; } + string FromAddress { get; } + bool HasAttachments { get; } + bool IsFlagged { get; } + bool IsFocused { get; } + bool IsRead { get; } + string DraftId { get; } + bool IsDraft { get; } + Guid FileId { get; } + + MailItemFolder AssignedFolder { get; } + MailAccount AssignedAccount { get; } + } +} diff --git a/Wino.Core.Domain/Models/MailItem/IMailItemThread.cs b/Wino.Core.Domain/Models/MailItem/IMailItemThread.cs new file mode 100644 index 00000000..22fd0699 --- /dev/null +++ b/Wino.Core.Domain/Models/MailItem/IMailItemThread.cs @@ -0,0 +1,16 @@ +using System.Collections.ObjectModel; + +namespace Wino.Core.Domain.Models.MailItem +{ + /// + /// Interface that represents conversation threads. + /// Even though this type has 1 single UI representation most of the time, + /// it can contain multiple IMailItem. + /// + public interface IMailItemThread : IMailItem + { + ObservableCollection ThreadItems { get; } + IMailItem LatestMailItem { get; } + IMailItem FirstMailItem { get; } + } +} diff --git a/Wino.Core.Domain/Models/MailItem/MailDetailInformation.cs b/Wino.Core.Domain/Models/MailItem/MailDetailInformation.cs new file mode 100644 index 00000000..c79f1039 --- /dev/null +++ b/Wino.Core.Domain/Models/MailItem/MailDetailInformation.cs @@ -0,0 +1,18 @@ +using System; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Models.MailItem +{ + public class MailDetailInformation + { + public string Id { get; set; } + + public Guid AccountId { get; set; } + public Guid FolderId { get; set; } + public string RemoteFolderId { get; set; } + public SpecialFolderType SpecialFolderType { get; set; } + public bool IsRead { get; set; } + public bool IsFlagged { get; set; } + public bool IsDraft { get; set; } + } +} diff --git a/Wino.Core.Domain/Models/MailItem/MailDragPackage.cs b/Wino.Core.Domain/Models/MailItem/MailDragPackage.cs new file mode 100644 index 00000000..2f47a267 --- /dev/null +++ b/Wino.Core.Domain/Models/MailItem/MailDragPackage.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace Wino.Core.Domain.Models.MailItem +{ + /// + /// Class that holds information when the drag/drop of mails are performed. + /// + public class MailDragPackage + { + public MailDragPackage(IEnumerable draggingMails) + { + DraggingMails = draggingMails; + } + + public MailDragPackage(IMailItem draggingMail) + { + DraggingMails = + [ + draggingMail + ]; + } + + public IEnumerable DraggingMails { get; set; } + } +} diff --git a/Wino.Core.Domain/Models/MailItem/MailFolderPairMetadata.cs b/Wino.Core.Domain/Models/MailItem/MailFolderPairMetadata.cs new file mode 100644 index 00000000..158425c8 --- /dev/null +++ b/Wino.Core.Domain/Models/MailItem/MailFolderPairMetadata.cs @@ -0,0 +1,11 @@ +using System; + +namespace Wino.Core.Domain.Models.MailItem +{ + public class MailFolderPairMetadata + { + public Guid FolderId { get; set; } + public string RemoteFolderId { get; set; } + public string MailCopyId { get; set; } + } +} diff --git a/Wino.Core.Domain/Models/MailItem/MailInsertPackage.cs b/Wino.Core.Domain/Models/MailItem/MailInsertPackage.cs new file mode 100644 index 00000000..5d8a3a65 --- /dev/null +++ b/Wino.Core.Domain/Models/MailItem/MailInsertPackage.cs @@ -0,0 +1,7 @@ +using MimeKit; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Domain.Models.MailItem +{ + public record NewMailItemPackage(MailCopy Copy, MimeMessage Mime, string AssignedRemoteFolderId); +} diff --git a/Wino.Core.Domain/Models/MailItem/MailListInitializationOptions.cs b/Wino.Core.Domain/Models/MailItem/MailListInitializationOptions.cs new file mode 100644 index 00000000..170adcf6 --- /dev/null +++ b/Wino.Core.Domain/Models/MailItem/MailListInitializationOptions.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Folders; + +namespace Wino.Core.Domain.Models.MailItem +{ + public record MailListInitializationOptions(IEnumerable Folders, + FilterOptionType FilterType, + SortingOptionType SortingOptionType, + bool CreateThreads, + bool? IsFocusedOnly, + string SearchQuery, + IEnumerable ExistingUniqueIds); +} diff --git a/Wino.Core.Domain/Models/MailItem/MailOperationPreperationRequest.cs b/Wino.Core.Domain/Models/MailItem/MailOperationPreperationRequest.cs new file mode 100644 index 00000000..a12ac267 --- /dev/null +++ b/Wino.Core.Domain/Models/MailItem/MailOperationPreperationRequest.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Folders; + +namespace Wino.Core.Domain.Models.MailItem +{ + /// + /// Encapsulates the options for preparing requests to execute mail operations for mail items like Move, Delete, MarkAsRead, etc. + /// + public class MailOperationPreperationRequest + { + public MailOperationPreperationRequest(MailOperation action, + IEnumerable mailItems, + bool toggleExecution = false, + IMailItemFolder moveTargetFolder = null, + bool ignoreHardDeleteProtection = false) + { + Action = action; + MailItems = mailItems ?? throw new ArgumentNullException(nameof(mailItems)); + ToggleExecution = toggleExecution; + MoveTargetFolder = moveTargetFolder; + IgnoreHardDeleteProtection = ignoreHardDeleteProtection; + } + + public MailOperationPreperationRequest(MailOperation action, + MailCopy singleMailItem, + bool toggleExecution = false, + IMailItemFolder moveTargetFolder = null, + bool ignoreHardDeleteProtection = false) + { + Action = action; + MailItems = new List() { singleMailItem }; + ToggleExecution = toggleExecution; + MoveTargetFolder = moveTargetFolder; + IgnoreHardDeleteProtection = ignoreHardDeleteProtection; + } + + /// + /// Action to execute. + /// + public MailOperation Action { get; set; } + + /// + /// Mail copies execute the action on. + /// + public IEnumerable MailItems { get; set; } + + /// + /// Whether the operation can be reverted if needed. + /// eg. MarkAsRead on already read item will set the action to MarkAsUnread. + /// This is used in hover actions for example. + /// + public bool ToggleExecution { get; set; } + + /// + /// Whether hard delete protection should be ignored. + /// Discard draft requests for example should ignore hard delete protection. + /// + public bool IgnoreHardDeleteProtection { get; set; } + + /// + /// Moving folder for the Move operation. + /// If null and the action is Move, the user will be prompted to select a folder. + /// + public IMailItemFolder MoveTargetFolder { get; } + } +} diff --git a/Wino.Core.Domain/Models/MailItem/MimeMessageInformation.cs b/Wino.Core.Domain/Models/MailItem/MimeMessageInformation.cs new file mode 100644 index 00000000..ce8b4404 --- /dev/null +++ b/Wino.Core.Domain/Models/MailItem/MimeMessageInformation.cs @@ -0,0 +1,9 @@ +using MimeKit; + +namespace Wino.Core.Domain.Models.MailItem +{ + /// + /// Encapsulates MimeMessage and the path to the file. + /// + public record MimeMessageInformation(MimeMessage MimeMessage, string Path); +} diff --git a/Wino.Core.Domain/Models/MailItem/OutlookSpecialFolderIdInformation.cs b/Wino.Core.Domain/Models/MailItem/OutlookSpecialFolderIdInformation.cs new file mode 100644 index 00000000..9eadd52a --- /dev/null +++ b/Wino.Core.Domain/Models/MailItem/OutlookSpecialFolderIdInformation.cs @@ -0,0 +1,13 @@ +namespace Wino.Core.Domain.Models.MailItem +{ + /// + /// Class that holds immutable information about special folders in Outlook. + /// + /// + /// + /// + /// + /// + /// + public record OutlookSpecialFolderIdInformation(string InboxId, string TrashId, string JunkId, string DraftId, string SentId, string ArchiveId); +} diff --git a/Wino.Core.Domain/Models/MailItem/SendDraftPreparationRequest.cs b/Wino.Core.Domain/Models/MailItem/SendDraftPreparationRequest.cs new file mode 100644 index 00000000..f1159931 --- /dev/null +++ b/Wino.Core.Domain/Models/MailItem/SendDraftPreparationRequest.cs @@ -0,0 +1,7 @@ +using MimeKit; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Domain.Models.MailItem +{ + public record SendDraftPreparationRequest(MailCopy MailItem, MimeMessage Mime, MailItemFolder DraftFolder, MailItemFolder SentFolder, MailAccountPreferences AccountPreferences); +} diff --git a/Wino.Core.Domain/Models/MailItem/ThreadMailItem.cs b/Wino.Core.Domain/Models/MailItem/ThreadMailItem.cs new file mode 100644 index 00000000..8eb8085e --- /dev/null +++ b/Wino.Core.Domain/Models/MailItem/ThreadMailItem.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.ObjectModel; +using System.Linq; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Domain.Models.MailItem +{ + public class ThreadMailItem : IMailItemThread + { + // TODO: Ideally this should be SortedList. + public ObservableCollection ThreadItems { get; } = new ObservableCollection(); + + public IMailItem LatestMailItem => ThreadItems.LastOrDefault(); + public IMailItem FirstMailItem => ThreadItems.FirstOrDefault(); + + public void AddThreadItem(IMailItem item) + { + if (item == null) return; + + if (ThreadItems.Any(a => a.Id == item.Id)) + { + return; + } + + if (item != null && item.IsDraft) + { + ThreadItems.Insert(0, item); + return; + } + + var insertItem = ThreadItems.FirstOrDefault(a => !a.IsDraft && a.CreationDate < item.CreationDate); + + if (insertItem == null) + ThreadItems.Insert(ThreadItems.Count, item); + else + { + var index = ThreadItems.IndexOf(insertItem); + + ThreadItems.Insert(index, item); + } + } + + #region IMailItem + + public Guid UniqueId => LatestMailItem?.UniqueId ?? Guid.Empty; + public string Id => LatestMailItem?.Id ?? string.Empty; + + // Show subject from last item. + public string Subject => LatestMailItem?.Subject ?? string.Empty; + + public string ThreadId => LatestMailItem?.ThreadId ?? string.Empty; + + public string PreviewText => FirstMailItem?.PreviewText ?? string.Empty; + + public string FromName => LatestMailItem?.FromName ?? string.Empty; + + public string FromAddress => LatestMailItem?.FromAddress ?? string.Empty; + + public bool HasAttachments => ThreadItems.Any(a => a.HasAttachments); + + public bool IsFlagged => ThreadItems.Any(a => a.IsFlagged); + + public bool IsFocused => LatestMailItem?.IsFocused ?? false; + + public bool IsRead => ThreadItems.All(a => a.IsRead); + + public DateTime CreationDate => FirstMailItem?.CreationDate ?? DateTime.MinValue; + + public bool IsDraft => ThreadItems.Any(a => a.IsDraft); + + public string DraftId => string.Empty; + + public string MessageId => LatestMailItem?.MessageId; + + public string References => LatestMailItem?.References ?? string.Empty; + + public string InReplyTo => LatestMailItem?.InReplyTo ?? string.Empty; + + public MailItemFolder AssignedFolder => LatestMailItem?.AssignedFolder; + + public MailAccount AssignedAccount => LatestMailItem?.AssignedAccount; + + public Guid FileId => LatestMailItem?.FileId ?? Guid.Empty; + + #endregion + } +} diff --git a/Wino.Core.Domain/Models/Menus/MailOperationMenuItem.cs b/Wino.Core.Domain/Models/Menus/MailOperationMenuItem.cs new file mode 100644 index 00000000..c14d7604 --- /dev/null +++ b/Wino.Core.Domain/Models/Menus/MailOperationMenuItem.cs @@ -0,0 +1,21 @@ +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.Domain.Models.Menus +{ + public class MailOperationMenuItem : MenuOperationItemBase, IMenuOperation + { + /// + /// Gets or sets whether this menu item should be placed in SecondaryCommands if used in CommandBar. + /// + public bool IsSecondaryMenuPreferred { get; set; } + + protected MailOperationMenuItem(MailOperation operation, bool isEnabled, bool isSecondaryMenuItem = false) : base(operation, isEnabled) + { + IsSecondaryMenuPreferred = isSecondaryMenuItem; + } + + public static MailOperationMenuItem Create(MailOperation operation, bool isEnabled = true, bool isSecondaryMenuItem = false) + => new MailOperationMenuItem(operation, isEnabled, isSecondaryMenuItem); + } +} diff --git a/Wino.Core.Domain/Models/Menus/MenuOperationItemBase.cs b/Wino.Core.Domain/Models/Menus/MenuOperationItemBase.cs new file mode 100644 index 00000000..462cfe14 --- /dev/null +++ b/Wino.Core.Domain/Models/Menus/MenuOperationItemBase.cs @@ -0,0 +1,18 @@ +using System; + +namespace Wino.Core.Domain.Models.Menus +{ + public class MenuOperationItemBase where TOperation : Enum + { + public MenuOperationItemBase(TOperation operation, bool isEnabled) + { + Operation = operation; + IsEnabled = isEnabled; + Identifier = operation.ToString(); + } + + public TOperation Operation { get; set; } + public string Identifier { get; set; } + public bool IsEnabled { get; set; } + } +} diff --git a/Wino.Core.Domain/Models/Navigation/NavigateMailFolderEventArgs.cs b/Wino.Core.Domain/Models/Navigation/NavigateMailFolderEventArgs.cs new file mode 100644 index 00000000..de6a38ea --- /dev/null +++ b/Wino.Core.Domain/Models/Navigation/NavigateMailFolderEventArgs.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.Domain.Models.Navigation +{ + public class NavigateMailFolderEventArgs + { + public NavigateMailFolderEventArgs(IBaseFolderMenuItem baseFolderMenuItem, TaskCompletionSource folderInitLoadAwaitTask = null) + { + BaseFolderMenuItem = baseFolderMenuItem; + FolderInitLoadAwaitTask = folderInitLoadAwaitTask; + } + + /// + /// Base folder menu item. + /// + public IBaseFolderMenuItem BaseFolderMenuItem { get; set; } + + /// + /// Completion source for waiting folder's mail initialization. + /// + public TaskCompletionSource FolderInitLoadAwaitTask { get; } + } +} diff --git a/Wino.Core.Domain/Models/Navigation/NavigationMode.cs b/Wino.Core.Domain/Models/Navigation/NavigationMode.cs new file mode 100644 index 00000000..fbeb0fdd --- /dev/null +++ b/Wino.Core.Domain/Models/Navigation/NavigationMode.cs @@ -0,0 +1,10 @@ +namespace Wino.Core.Domain.Models.Navigation +{ + public enum NavigationMode + { + New, + Back, + Forward, + Refresh + } +} diff --git a/Wino.Core.Domain/Models/Navigation/NavigationTransitionType.cs b/Wino.Core.Domain/Models/Navigation/NavigationTransitionType.cs new file mode 100644 index 00000000..fba63741 --- /dev/null +++ b/Wino.Core.Domain/Models/Navigation/NavigationTransitionType.cs @@ -0,0 +1,8 @@ +namespace Wino.Core.Domain.Models.Navigation +{ + public enum NavigationTransitionType + { + None, // Supress + DrillIn, + } +} diff --git a/Wino.Core.Domain/Models/Personalization/AppThemeBase.cs b/Wino.Core.Domain/Models/Personalization/AppThemeBase.cs new file mode 100644 index 00000000..f9b74a40 --- /dev/null +++ b/Wino.Core.Domain/Models/Personalization/AppThemeBase.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading.Tasks; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Models.Personalization +{ + /// + /// Base class for all app themes. + /// + public abstract class AppThemeBase + { + public Guid Id { get; set; } + public string ThemeName { get; set; } + public ApplicationElementTheme ForceElementTheme { get; set; } + public string AccentColor { get; set; } + public bool IsAccentColorAssigned => !string.IsNullOrEmpty(AccentColor); + public string BackgroundPreviewImage => GetBackgroundPreviewImagePath(); + public abstract AppThemeType AppThemeType { get; } + + protected AppThemeBase(string themeName, Guid id) + { + ThemeName = themeName; + Id = id; + } + + public abstract Task GetThemeResourceDictionaryContentAsync(); + public abstract string GetBackgroundPreviewImagePath(); + } +} diff --git a/Wino.Core.Domain/Models/Personalization/CustomThemeMetadata.cs b/Wino.Core.Domain/Models/Personalization/CustomThemeMetadata.cs new file mode 100644 index 00000000..e6206725 --- /dev/null +++ b/Wino.Core.Domain/Models/Personalization/CustomThemeMetadata.cs @@ -0,0 +1,12 @@ +using System; + +namespace Wino.Core.Domain.Models.Personalization +{ + public class CustomThemeMetadata + { + public Guid Id { get; set; } + public string Name { get; set; } + public string AccentColorHex { get; set; } + public bool HasCustomAccentColor => !string.IsNullOrEmpty(AccentColorHex); + } +} diff --git a/Wino.Core.Domain/Models/Personalization/ElementThemeContainer.cs b/Wino.Core.Domain/Models/Personalization/ElementThemeContainer.cs new file mode 100644 index 00000000..42f79fc5 --- /dev/null +++ b/Wino.Core.Domain/Models/Personalization/ElementThemeContainer.cs @@ -0,0 +1,16 @@ +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Models.Personalization +{ + public class ElementThemeContainer + { + public ElementThemeContainer(ApplicationElementTheme nativeTheme, string title) + { + NativeTheme = nativeTheme; + Title = title; + } + + public ApplicationElementTheme NativeTheme { get; set; } + public string Title { get; set; } + } +} diff --git a/Wino.Core.Domain/Models/Personalization/MailListPaneLengthPreferences.cs b/Wino.Core.Domain/Models/Personalization/MailListPaneLengthPreferences.cs new file mode 100644 index 00000000..23841893 --- /dev/null +++ b/Wino.Core.Domain/Models/Personalization/MailListPaneLengthPreferences.cs @@ -0,0 +1,4 @@ +namespace Wino.Core.Domain.Models.Personalization +{ + public record MailListPaneLengthPreferences(string Title, double Length); +} diff --git a/Wino.Core.Domain/Models/Reader/EditorToolbarSection.cs b/Wino.Core.Domain/Models/Reader/EditorToolbarSection.cs new file mode 100644 index 00000000..e52fdea1 --- /dev/null +++ b/Wino.Core.Domain/Models/Reader/EditorToolbarSection.cs @@ -0,0 +1,30 @@ +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Models.Reader +{ + public class EditorToolbarSection + { + public EditorToolbarSectionType SectionType { get; set; } + public string Title + { + get + { + switch (SectionType) + { + case EditorToolbarSectionType.None: + return Translator.EditorToolbarOption_None; + case EditorToolbarSectionType.Format: + return Translator.EditorToolbarOption_Format; + case EditorToolbarSectionType.Insert: + return Translator.EditorToolbarOption_Insert; + case EditorToolbarSectionType.Draw: + return Translator.EditorToolbarOption_Draw; + case EditorToolbarSectionType.Options: + return Translator.EditorToolbarOption_Options; + default: + return "Unknown Editor Option"; + } + } + } + } +} diff --git a/Wino.Core.Domain/Models/Reader/FilterOption.cs b/Wino.Core.Domain/Models/Reader/FilterOption.cs new file mode 100644 index 00000000..883a8174 --- /dev/null +++ b/Wino.Core.Domain/Models/Reader/FilterOption.cs @@ -0,0 +1,16 @@ +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Models.Reader +{ + public class FilterOption + { + public FilterOptionType Type { get; set; } + public string Title { get; set; } + + public FilterOption(string title, FilterOptionType type) + { + Title = title; + Type = type; + } + } +} diff --git a/Wino.Core.Domain/Models/Reader/MailRenderModel.cs b/Wino.Core.Domain/Models/Reader/MailRenderModel.cs new file mode 100644 index 00000000..0f1c47f3 --- /dev/null +++ b/Wino.Core.Domain/Models/Reader/MailRenderModel.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using MimeKit; + +namespace Wino.Core.Domain.Models.Reader +{ + /// + /// Final model to be passed to renderer page. + /// Data here are created based on rendering settings. + /// + public class MailRenderModel + { + public string RenderHtml { get; } + public MailRenderingOptions MailRenderingOptions { get; } + public List Attachments { get; set; } = new List(); + + public string UnsubscribeLink { get; set; } + public bool CanUnsubscribe => !string.IsNullOrEmpty(UnsubscribeLink); + + public MailRenderModel(string renderHtml, MailRenderingOptions mailRenderingOptions = null) + { + RenderHtml = renderHtml; + MailRenderingOptions = mailRenderingOptions; + } + } +} diff --git a/Wino.Core.Domain/Models/Reader/MailRenderingOptions.cs b/Wino.Core.Domain/Models/Reader/MailRenderingOptions.cs new file mode 100644 index 00000000..739592aa --- /dev/null +++ b/Wino.Core.Domain/Models/Reader/MailRenderingOptions.cs @@ -0,0 +1,19 @@ +namespace Wino.Core.Domain.Models.Reader +{ + /// + /// Rendering options for mail. + /// + public class MailRenderingOptions + { + private const bool DefaultLoadImageValue = true; + private const bool DefaultLoadStylesValue = true; + + public bool LoadImages { get; set; } = DefaultLoadImageValue; + public bool LoadStyles { get; set; } = DefaultLoadStylesValue; + + // HtmlDocument.Load call is redundant if all the settings are in default values. + // Therefore we will purify the HTML only if needed. + + public bool IsPurifyingNeeded() => LoadImages != DefaultLoadImageValue || LoadStyles != DefaultLoadStylesValue; + } +} diff --git a/Wino.Core.Domain/Models/Reader/ReaderFontModel.cs b/Wino.Core.Domain/Models/Reader/ReaderFontModel.cs new file mode 100644 index 00000000..7530e92a --- /dev/null +++ b/Wino.Core.Domain/Models/Reader/ReaderFontModel.cs @@ -0,0 +1,6 @@ +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Models.Reader +{ + public record ReaderFontModel(ReaderFont Font, string FontFamilyName); +} diff --git a/Wino.Core.Domain/Models/Reader/SortingOption.cs b/Wino.Core.Domain/Models/Reader/SortingOption.cs new file mode 100644 index 00000000..3d253453 --- /dev/null +++ b/Wino.Core.Domain/Models/Reader/SortingOption.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Comparers; +using Wino.Core.Domain.Models.MailItem; + +namespace Wino.Core.Domain.Models.Reader +{ + public class SortingOption + { + public SortingOptionType Type { get; set; } + public string Title { get; set; } + public IComparer Comparer + { + get + { + if (Type == SortingOptionType.ReceiveDate) + return new DateComparer(); + else + return new NameComparer(); + } + } + + public SortingOption(string title, SortingOptionType type) + { + Title = title; + Type = type; + } + } +} diff --git a/Wino.Core.Domain/Models/Requests/IUIMessage.cs b/Wino.Core.Domain/Models/Requests/IUIMessage.cs new file mode 100644 index 00000000..44b74525 --- /dev/null +++ b/Wino.Core.Domain/Models/Requests/IUIMessage.cs @@ -0,0 +1,10 @@ +namespace Wino.Core.Domain.Models.Requests +{ + /// + /// Interface for all messages to report UI changes from synchronizers to UI. + /// None of these messages can't run a code that manipulates database. + /// They are sent either from processor or view models to signal some other + /// parts of the application. + /// + public interface IUIMessage; +} diff --git a/Wino.Core.Domain/Models/Requests/RequestBase.cs b/Wino.Core.Domain/Models/Requests/RequestBase.cs new file mode 100644 index 00000000..8aa3459b --- /dev/null +++ b/Wino.Core.Domain/Models/Requests/RequestBase.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.Domain.Models.Requests +{ + public abstract record RequestBase(MailCopy Item, MailSynchronizerOperation Operation) : IRequest + where TBatchRequestType : IBatchChangeRequest + { + public abstract IBatchChangeRequest CreateBatch(IEnumerable requests); + public abstract void ApplyUIChanges(); + public abstract void RevertUIChanges(); + } + + public abstract record FolderRequestBase(MailItemFolder Folder, MailSynchronizerOperation Operation) : IFolderRequest + { + public abstract void ApplyUIChanges(); + public abstract void RevertUIChanges(); + } + + public abstract record BatchRequestBase(IEnumerable Items, MailSynchronizerOperation Operation) : IBatchChangeRequest + { + public abstract void ApplyUIChanges(); + public abstract void RevertUIChanges(); + } +} diff --git a/Wino.Core.Domain/Models/Requests/ToggleRequestRule.cs b/Wino.Core.Domain/Models/Requests/ToggleRequestRule.cs new file mode 100644 index 00000000..b69db903 --- /dev/null +++ b/Wino.Core.Domain/Models/Requests/ToggleRequestRule.cs @@ -0,0 +1,15 @@ +using System; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.MailItem; + +namespace Wino.Core.Domain.Models.Requests +{ + /// + /// Defines a single rule for toggling user actions if needed. + /// For example: If user wants to mark a mail as read, but it's already read, then it should be marked as unread. + /// + /// + /// + /// + public record ToggleRequestRule(MailOperation SourceAction, MailOperation TargetAction, Func Condition); +} diff --git a/Wino.Core.Domain/Models/Store/StoreProductType.cs b/Wino.Core.Domain/Models/Store/StoreProductType.cs new file mode 100644 index 00000000..5c82003e --- /dev/null +++ b/Wino.Core.Domain/Models/Store/StoreProductType.cs @@ -0,0 +1,7 @@ +namespace Wino.Core.Domain.Models.Store +{ + public enum StoreProductType + { + UnlimitedAccounts + } +} diff --git a/Wino.Core.Domain/Models/Synchronization/SynchronizationOptions.cs b/Wino.Core.Domain/Models/Synchronization/SynchronizationOptions.cs new file mode 100644 index 00000000..31ced1de --- /dev/null +++ b/Wino.Core.Domain/Models/Synchronization/SynchronizationOptions.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.Domain.Models.Synchronization +{ + public class SynchronizationOptions + { + /// + /// Unique id of synchronization. + /// + public Guid Id { get; } = Guid.NewGuid(); + + /// + /// Account to execute synchronization for. + /// + public Guid AccountId { get; set; } + + /// + /// Type of the synchronization to be performed. + /// + public SynchronizationType Type { get; set; } + + /// + /// Collection of FolderId to perform SynchronizationType.Custom type sync. + /// + public List SynchronizationFolderIds { get; set; } + + /// + /// A listener to be notified about the progress of the synchronization. + /// + public ISynchronizationProgress ProgressListener { get; set; } + + /// + /// When doing a linked inbox synchronization, we must ignore reporting completion to the caller for each folder. + /// This Id will help tracking that. Id is unique, but this one can be the same for all sync requests + /// inside the same linked inbox sync. + /// + public Guid? GroupedSynchronizationTrackingId { get; set; } + + public override string ToString() => $"Type: {Type}, Folders: {(SynchronizationFolderIds == null ? "None" : string.Join(",", SynchronizationFolderIds))}"; + } +} diff --git a/Wino.Core.Domain/Models/Synchronization/SynchronizationResult.cs b/Wino.Core.Domain/Models/Synchronization/SynchronizationResult.cs new file mode 100644 index 00000000..c10a7b96 --- /dev/null +++ b/Wino.Core.Domain/Models/Synchronization/SynchronizationResult.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.MailItem; + +namespace Wino.Core.Domain.Models.Synchronization +{ + public class SynchronizationResult + { + protected SynchronizationResult() { } + + public IEnumerable DownloadedMessages { get; set; } = new List(); + public SynchronizationCompletedState CompletedState { get; set; } + + public static SynchronizationResult Empty => new() { CompletedState = SynchronizationCompletedState.Success }; + + public static SynchronizationResult Completed(IEnumerable downloadedMessages) + => new() { DownloadedMessages = downloadedMessages, CompletedState = SynchronizationCompletedState.Success }; + + public static SynchronizationResult Canceled => new() { CompletedState = SynchronizationCompletedState.Canceled }; + } +} diff --git a/Wino.Core.Domain/Models/Translations/AppLanguageModel.cs b/Wino.Core.Domain/Models/Translations/AppLanguageModel.cs new file mode 100644 index 00000000..65032c61 --- /dev/null +++ b/Wino.Core.Domain/Models/Translations/AppLanguageModel.cs @@ -0,0 +1,6 @@ +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Models.Translations +{ + public record AppLanguageModel(AppLanguage Language, string DisplayName); +} diff --git a/Wino.Core.Domain/Translations/WinoTranslationDictionary.cs b/Wino.Core.Domain/Translations/WinoTranslationDictionary.cs new file mode 100644 index 00000000..087f95b8 --- /dev/null +++ b/Wino.Core.Domain/Translations/WinoTranslationDictionary.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Translations +{ + public class WinoTranslationDictionary : Dictionary + { + // Return the key itself in case of translation is not found. + public string GetTranslatedString(string key) => TryGetValue(key, out string keyValue) ? keyValue : key; + + public Stream GetLanguageStream(AppLanguage language) + { + var path = GetLanguageFileNameRelativePath(language); + var executingAssembly = Assembly.GetExecutingAssembly(); + + string languageResourcePath = $"{executingAssembly.ManifestModule.Name.Replace(".dll", ".")}Translations.{path}.resources.json"; + return executingAssembly.GetManifestResourceStream(languageResourcePath); + } + + /// + /// Returns the relative path of the language file. + /// All translations are under Translations\{langCode}\resources.json + /// + /// Language + /// Relative folder for the language + private string GetLanguageFileNameRelativePath(AppLanguage language) + { + return language switch + { + AppLanguage.English => "en_US", + AppLanguage.Turkish => "tr_TR", + AppLanguage.Deutsch => "de_DE", + AppLanguage.Russian => "ru_RU", + AppLanguage.Polish => "pl_PL", + AppLanguage.Czech => "cs_CZ", + AppLanguage.French => "fr_FR", + AppLanguage.Chinese => "zh_CH", + AppLanguage.Spanish => "es_ES", + _ => "en_US", + }; + } + } +} diff --git a/Wino.Core.Domain/Translations/ca_ES/resources.json b/Wino.Core.Domain/Translations/ca_ES/resources.json new file mode 100644 index 00000000..54ddd05d --- /dev/null +++ b/Wino.Core.Domain/Translations/ca_ES/resources.json @@ -0,0 +1,468 @@ +{ + "AccountCreationDialog_Completed": "all done", + "AccountCreationDialog_Initializing": "initializing", + "AccountCreationDialog_PreparingFolders": "We are getting folder information at the moment.", + "AccountCreationDialog_SigninIn": "Account information is being saved.", + "AccountEditDialog_Message": "Account Name", + "AccountEditDialog_Title": "Edit Account", + "AccountPickerDialog_Title": "Pick an account", + "AddHyperlink": "Add", + "AutoDiscoveryProgressMessage": "Searching for mail settings...", + "BasicIMAPSetupDialog_AdvancedConfiguration": "Advanced Configuration", + "BasicIMAPSetupDialog_CredentialLocalMessage": "Your credentials will only be stored locally on your computer.", + "BasicIMAPSetupDialog_Description": "Some accounts require additional steps to sign in", + "BasicIMAPSetupDialog_DisplayName": "Display Name", + "BasicIMAPSetupDialog_DisplayNamePlaceholder": "eg. John Doe", + "BasicIMAPSetupDialog_LearnMore": "Learn more", + "BasicIMAPSetupDialog_MailAddress": "E-Mail Address", + "BasicIMAPSetupDialog_MailAddressPlaceholder": "johndoe@fabrikam.com", + "BasicIMAPSetupDialog_Password": "Password", + "BasicIMAPSetupDialog_Title": "IMAP Account", + "Buttons_AddAccount": "Add Account", + "Buttons_ApplyTheme": "Apply Theme", + "Buttons_Browse": "Browse", + "Buttons_Cancel": "Cancel", + "Buttons_Close": "Close", + "Buttons_Create": "Create", + "Buttons_CreateAccount": "Create Account", + "Buttons_Delete": "Delete", + "Buttons_Discard": "Discard", + "Buttons_EnableImageRendering": "Enable", + "Buttons_No": "No", + "Buttons_Open": "Open", + "Buttons_Purchase": "Purchase", + "Buttons_RateWino": "Rate Wino", + "Buttons_Save": "Save", + "Buttons_SaveConfiguration": "Save Configuration", + "Buttons_Share": "Share", + "Buttons_SignIn": "Sign In", + "Buttons_Yes": "Yes", + "Center": "Center", + "ComingSoon": "Coming soon...", + "ComposerFrom": "From: ", + "ComposerSubject": "Subject: ", + "ComposerTo": "To: ", + "ClipboardTextCopied_Message": "{0} copied to clipboard.", + "ClipboardTextCopied_Title": "Copied", + "ClipboardTextCopyFailed_Message": "Failed to copy {0} to clipboard.", + "ComposerToPlaceholder": "click enter to input addresses", + "CustomThemeBuilder_AccentColorDescription": "Set custom accent color if you wish. Not selecting a color will use your Windows accent color.", + "CustomThemeBuilder_AccentColorTitle": "Accent color", + "CustomThemeBuilder_PickColor": "Pick", + "CustomThemeBuilder_ThemeNameDescription": "Unique name for your custom theme.", + "CustomThemeBuilder_ThemeNameTitle": "Theme name", + "CustomThemeBuilder_Title": "Custom Theme Builder", + "CustomThemeBuilder_WallpaperDescription": "Set a custom wallpaper for Wino", + "CustomThemeBuilder_WallpaperTitle": "Set custom wallpaper", + "DialogMessage_AccountLimitMessage": "You have reached the account creation limit.\nWould you like to purchase 'Unlimited Account' add-on to continue?", + "DialogMessage_AccountLimitTitle": "Account Limit Reached", + "DialogMessage_CleanupFolderMessage": "Do you want to permanently delete all the mails in this folder?", + "DialogMessage_CleanupFolderTitle": "Cleanup Folder", + "DialogMessage_ComposerMissingRecipientMessage": "Message has no recipient.", + "DialogMessage_ComposerValidationFailedTitle": "Validation Failed", + "DialogMessage_CreateLinkedAccountMessage": "Give this new link a name. Accounts will be merged under this name.", + "DialogMessage_CreateLinkedAccountTitle": "Account Link Name", + "DialogMessage_DeleteAccountConfirmationMessage": "Delete {0}?", + "DialogMessage_DeleteAccountConfirmationTitle": "All data associated with this account will be deleted from disk permanently.", + "DialogMessage_DiscardDraftConfirmationMessage": "This draft will be discarded. Do you want to continue?", + "DialogMessage_DiscardDraftConfirmationTitle": "Discard Draft", + "DialogMessage_HardDeleteConfirmationMessage": "Permanent Delete", + "DialogMessage_HardDeleteConfirmationTitle": "Message(s) will be permanently deleted. Do you want to continue?", + "DialogMessage_NoAccountsForCreateMailMessage": "You don't have any accounts to create message from.", + "DialogMessage_NoAccountsForCreateMailTitle": "Account Missing", + "DialogMessage_RenameLinkedAccountsMessage": "Enter new name for linked account", + "DialogMessage_RenameLinkedAccountsTitle": "Rename Linked Account", + "DialogMessage_UnlinkAccountsConfirmationMessage": "This operation will not delete your accounts but only break the link for shared folder connections. Do you want to continue?", + "DialogMessage_UnlinkAccountsConfirmationTitle": "Unlink Accounts", + "DialogMessage_EmptySubjectConfirmation": "Missin Subject", + "DialogMessage_EmptySubjectConfirmationMessage": "Message has no subject. Do you want to continue?", + "Dialog_DontAskAgain": "Don't ask again", + "DiscordChannelDisclaimerMessage": "Wino doesn't have it's own Discord server, but special 'wino-mail' channel is hosted at 'Developer Sanctuary' server.\nTo get the updates about Wino please join Developer Sanctuary server and follow 'wino-mail' channel under 'Community Projects'\n\nYou will be directed to server URL since Discord doesn't support channel invites.", + "DiscordChannelDisclaimerTitle": "Important Discord Information", + "Draft": "Draft", + "EditorToolbarOption_Draw": "Draw", + "EditorToolbarOption_Format": "Format", + "EditorToolbarOption_Insert": "Insert", + "EditorToolbarOption_None": "None", + "EditorToolbarOption_Options": "Options", + "ElementTheme_Dark": "Dark mode", + "ElementTheme_Default": "Use system setting", + "ElementTheme_Light": "Light mode", + "Emoji": "Emoji", + "Exception_ImapClientPoolFailed": "IMAP Client Pool failed.", + "Exception_AuthenticationCanceled": "Authentication canceled", + "Exception_CustomThemeExists": "This theme already exists.", + "Exception_CustomThemeMissingName": "You must provide a name.", + "Exception_CustomThemeMissingWallpaper": "You must provide a custom background image.", + "Exception_FailedToSynchronizeFolders": "Failed to synchronize folders", + "Exception_GoogleAuthCallbackNull": "Callback uri is null on activation.", + "Exception_GoogleAuthCorruptedCode": "Corrupted authorization response.", + "Exception_GoogleAuthError": "OAuth authorization error: {0}", + "Exception_GoogleAuthInvalidResponse": "Received request with invalid state ({0})", + "Exception_GoogleAuthorizationCodeExchangeFailed": "Authorization code exchange failed.", + "Exception_InvalidSystemFolderConfiguration": "System folder configuration is not valid. Check configuration and try again.", + "Exception_NullAssignedAccount": "Assigned account is null", + "Exception_NullAssignedFolder": "Assigned folder is null", + "Exception_SynchronizerFailureHTTP": "Response handling failed with error HTTP code {0}", + "Exception_TokenGenerationFailed": "Token generation failed", + "Exception_TokenInfoRetrivalFailed": "Failed to get token information.", + "Exception_UnknowErrorDuringAuthentication": "Unknown error occurred during authentication", + "Exception_UnsupportedAction": "Action {0} is not implemented in request processor", + "Exception_UnsupportedProvider": "This provider is not supported.", + "Exception_UnsupportedSynchronizerOperation": "This operation is not supported for {0}", + "Exception_UserCancelSystemFolderSetupDialog": "User canceled system folder config dialog.", + "Files": "Files", + "FilteringOption_All": "All", + "FilteringOption_Flagged": "Flagged", + "FilteringOption_Unread": "Unread", + "Focused": "Focused", + "FolderOperation_CreateSubFolder": "Create sub folder", + "FolderOperation_Delete": "Delete", + "FolderOperation_DontSync": "Don't sync this folder", + "FolderOperation_Empty": "Empty this folder", + "FolderOperation_MarkAllAsRead": "Mark all as read", + "FolderOperation_Move": "Move", + "DragMoveToFolderCaption": "Move to {0}", + "FolderOperation_None": "None", + "FolderOperation_Pin": "Pin", + "FolderOperation_Rename": "Rename", + "FolderOperation_Unpin": "Unpin", + "HoverActionOption_Archive": "Archive", + "HoverActionOption_Delete": "Delete", + "HoverActionOption_MoveJunk": "Move to Junk", + "HoverActionOption_ToggleFlag": "Flag / Unflag", + "HoverActionOption_ToggleRead": "Read / Unread", + "MergedAccountCommonFolderInbox": "Inbox", + "MergedAccountCommonFolderSent": "Sent", + "MergedAccountCommonFolderDraft": "Draft", + "MergedAccountCommonFolderJunk": "Junk", + "MergedAccountCommonFolderTrash": "Deleted", + "MergedAccountCommonFolderArchive": "Archive", + "IMAPSetupDialog_AccountType": "Account type", + "IMAPSetupDialog_DisplayName": "Display Name", + "IMAPSetupDialog_DisplayNamePlaceholder": "eg. John Doe", + "IMAPSetupDialog_IncomingMailServer": "Incoming mail server", + "IMAPSetupDialog_IncomingMailServerPort": "Port", + "IMAPSetupDialog_MailAddress": "Email address", + "IMAPSetupDialog_MailAddressPlaceholder": "someone@example.com", + "IMAPSetupDialog_OutgoingMailServer": "Outgoing (SMTP) mail server", + "IMAPSetupDialog_OutgoingMailServerPassword": "Outgoing server password", + "IMAPSetupDialog_OutgoingMailServerPort": "Port", + "IMAPSetupDialog_OutgoingMailServerRequireAuthentication": "Outgoing server requires authentication", + "IMAPSetupDialog_OutgoingMailServerUsername": "Outgoing server user name", + "IMAPSetupDialog_Password": "Password", + "IMAPSetupDialog_RequireSSLForIncomingMail": "Require SSL for incoming email", + "IMAPSetupDialog_RequireSSLForOutgoingMail": "Require SSL for outgoing email", + "IMAPSetupDialog_Title": "Advanced IMAP Configuration", + "IMAPSetupDialog_UseSameConfig": "Use the same username and password for sending email", + "IMAPSetupDialog_Username": "Username", + "IMAPSetupDialog_UsernamePlaceholder": "johndoe, johndoe@fabrikam.com, domain/johndoe", + "ImageRenderingDisabled": "Image rendering is disabled for this message.", + "InfoBarAction_Enable": "Enable", + "InfoBarMessage_SynchronizationDisabledFolder": "This folder is disabled for synchronization.", + "InfoBarTitle_SynchronizationDisabledFolder": "Disabled Folder", + "GeneralTitle_Error": "Error", + "GeneralTitle_Warning": "Warning", + "GeneralTitle_Info": "Information", + "Info_AccountCreatedMessage": "{0} is created", + "Info_AccountCreatedTitle": "Account Creation", + "Info_AccountCreationFailedTitle": "Account Creation Failed", + "Info_AccountDeletedMessage": "{0} is successfuly deleted.", + "Info_AccountDeletedTitle": "Account Deleted", + "Info_AccountIssueFixFailedTitle": "Failed", + "Info_AccountIssueFixSuccessMessage": "Fixed all account issues.", + "Info_AccountIssueFixSuccessTitle": "Success", + "Info_AttachmentOpenFailedMessage": "Can't open this attachment.", + "Info_AttachmentOpenFailedTitle": "Failed", + "Info_AttachmentSaveFailedMessage": "Can't save this attachment.", + "Info_AttachmentSaveFailedTitle": "Failed", + "Info_AttachmentSaveSuccessMessage": "Attachment is saved.", + "Info_AttachmentSaveSuccessTitle": "Attachment Saved", + "Info_BackgroundExecutionDeniedMessage": "Background execution for the app is denied. This may affect background synchronization and live notifications.", + "Info_BackgroundExecutionDeniedTitle": "Denied Background Execution", + "Info_BackgroundExecutionUnknownErrorMessage": "Unknown exception occurred when registering background synchronizer.", + "Info_BackgroundExecutionUnknownErrorTitle": "Background Execution Failure", + "Info_ComposerMissingMIMEMessage": "Couldn't locate the MIME file. Synchronizing may help.", + "Info_ComposerMissingMIMETitle": "Failed", + "Info_ContactExistsMessage": "This contact is already in the recipient list.", + "Info_ContactExistsTitle": "Contact Exists", + "Info_DraftFolderMissingMessage": "Draft folder is missing for this account. Please check your account settings.", + "Info_DraftFolderMissingTitle": "Missing Draft Folder", + "Info_FileLaunchFailedTitle": "Failed to launch file", + "Info_InvalidAddressMessage": "'{0}' is not a valid e-mail address.", + "Info_InvalidAddressTitle": "Invalid Address", + "Info_InvalidMoveTargetMessage": "You can't move selected mails to this folder.", + "Info_InvalidMoveTargetTitle": "Invalid Move Target", + "Info_LogsNotFoundMessage": "There are no logs to share.", + "Info_LogsNotFoundTitle": "Logs Not Found", + "Info_LogsSavedMessage": "{0} is saved to selected folder.", + "Info_LogsSavedTitle": "Saved", + "Info_MailRenderingFailedMessage": "This mail is corrupted or can't be opened.\n{0}", + "Info_MailRenderingFailedTitle": "Render Failed", + "Info_MessageCorruptedMessage": "This message is corrupted.", + "Info_MessageCorruptedTitle": "Error", + "Info_MissingFolderMessage": "{0} doesn't exist for this account.", + "Info_MissingFolderTitle": "Missing Folder", + "Info_PurchaseExistsMessage": "Looks like this product has already been purchased before.", + "Info_PurchaseExistsTitle": "Existing Product", + "Info_PurchaseThankYouMessage": "Thank You", + "Info_PurchaseThankYouTitle": "Purchase successful", + "Info_RequestCreationFailedTitle": "Failed to Create Requests", + "Info_ReviewNetworkErrorMessage": "There was a network issue with your review.", + "Info_ReviewNetworkErrorTitle": "Network Issue", + "Info_ReviewNewMessage": "All feedbacks are appreciated. Thank you for the review!", + "Info_ReviewSuccessTitle": "Thank you", + "Info_ReviewUnknownErrorMessage": "There was an unknown issue with your review. ({0})", + "Info_ReviewUnknownErrorTitle": "Unknown Error", + "Info_ReviewUpdatedMessage": "Thank you for the updated review.", + "Info_SignatureDisabledMessage": "Disabled signature for this account", + "Info_SignatureDisabledTitle": "Success", + "Info_SignatureSavedMessage": "New signature is saved", + "Info_SignatureSavedTitle": "Success", + "Info_SyncCanceledMessage": "Canceled", + "Info_SyncCanceledTitle": "Synchronization", + "Info_SyncFailedTitle": "Synchronization Failed", + "Info_UnsupportedFunctionalityDescription": "This functionality is not implemented yet.", + "Info_UnsupportedFunctionalityTitle": "Unsupported", + "Info_UnsubscribeLinkInvalidTitle": "Invalid Unsubscribe Uri", + "Info_UnsubscribeLinkInvalidMessage": "This unsubscribe link is invalid. Failed to unsubscribe from the list.", + "ImapAdvancedSetupDialog_AuthenticationMethod": "Authentication method", + "ImapAdvancedSetupDialog_ConnectionSecurity": "Connection security", + "ImapAuthenticationMethod_Auto": "Auto", + "ImapAuthenticationMethod_CramMD5": "CRAM-MD5", + "ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5", + "ImapAuthenticationMethod_None": "No authentication", + "ImapAuthenticationMethod_Plain": "Normal password", + "ImapAuthenticationMethod_EncryptedPassword": "Encrypted password", + "ImapAuthenticationMethod_Ntlm": "NTLM", + "ImapConnectionSecurity_None": "None", + "ImapConnectionSecurity_SslTls": "SSL/TLS", + "ImapConnectionSecurity_StartTls": "STARTTLS", + "ImapConnectionSecurity_Auto": "Auto", + "Justify": "Justify", + "Left": "Left", + "Link": "Link", + "LinkedAccountsCreatePolicyMessage": "you must have at least 2 accounts to create link\nlink will be removed on save", + "LinkedAccountsTitle": "Linked Accounts", + "MailOperation_AlwaysMoveFocused": "Always Move to Focused", + "MailOperation_AlwaysMoveOther": "Always Move to Other", + "MailOperation_Archive": "Archive", + "MailOperation_ClearFlag": "Clear flag", + "MailOperation_DarkEditor": "Dark", + "MailOperation_Delete": "Delete", + "MailOperation_ExportPDF": "Export to PDF", + "MailOperation_Find": "Find", + "MailOperation_Forward": "Forward", + "MailOperation_Ignore": "Ignore", + "MailOperation_LightEditor": "Light", + "MailOperation_MarkAsJunk": "Mark as junk", + "MailOperation_MarkAsRead": "Mark as read", + "MailOperation_MarkAsUnread": "Mark as unread", + "MailOperation_MarkNotJunk": "Mark as Not Junk", + "MailOperation_Move": "Move", + "MailOperation_MoveFocused": "Move to Focused", + "MailOperation_MoveJunk": "Move to Junk", + "MailOperation_MoveOther": "Move to Other", + "MailOperation_Navigate": "Navigate", + "MailOperation_Print": "Print", + "MailOperation_Reply": "Reply", + "MailOperation_ReplyAll": "Reply all", + "MailOperation_SaveAs": "Save As", + "MailOperation_SetFlag": "Set flag", + "MailOperation_Unarchive": "Unarchive", + "MailOperation_Zoom": "Zoom", + "MailsSelected": "{0} item(s) selected", + "MarkFlagUnflag": "Mark as flagged/unflagged", + "MarkReadUnread": "Mark as read/unread", + "MenuManageAccounts": "Manage Accounts", + "MenuNewMail": "New Mail", + "MenuMergedAccountItemAccountsSuffix": " accounts", + "MenuRate": "Rate Wino", + "MenuSettings": "Settings", + "MergedAccountsAvailableAccountsTitle": "Available Accounts", + "More": "More", + "MoveMailDialog_InvalidFolderMessage": "{0} is not a valid folder for this mail.", + "MoveMailDialog_Title": "Pick a folder", + "NewAccountDialog_AccountName": "Account Name", + "NewAccountDialog_AccountNameDefaultValue": "Personal", + "NewAccountDialog_AccountNamePlaceholder": "eg. Personal Mail", + "NewAccountDialog_Title": "Add New Account", + "NoMailSelected": "no message selected", + "NoMessageCrieteria": "no messages match your search criteria.", + "NoMessageEmptyFolder": "this folder is empty.", + "Notifications_MultipleNotificationsMessage": "You have {0} new mails", + "Notifications_MultipleNotificationsTitle": "New Mails", + "Notifications_WinoUpdatedMessage": "Checkout new version {0}", + "Notifications_WinoUpdatedTitle": "Wino Mail has been updated.", + "Other": "Other", + "PaneLengthOption_Default": "Default", + "PaneLengthOption_ExtraLarge": "Extra Large", + "PaneLengthOption_Large": "Large", + "PaneLengthOption_Medium": "Medium", + "PaneLengthOption_Micro": "Micro", + "PaneLengthOption_Small": "Small", + "Photos": "Photos", + "PreparingFoldersMessage": "Preparing folders", + "ProviderDetail_Gmail_Description": "Google Account", + "ProviderDetail_IMAP_Description": "Custom IMAP/SMTP server", + "ProviderDetail_IMAP_Title": "IMAP Server", + "Results": "Results", + "Right": "Right", + "SynchronizationFolderReport_Success": "up to date", + "SynchronizationFolderReport_Failed": "synchronization is failed", + "SearchBarPlaceholder": "search", + "SearchingIn": "searching in", + "SettingsAboutGithub_Description": "Go to issue tracker GitHub repository.", + "SettingsAboutGithub_Title": "GitHub", + "SettingsAccountManagementAppendMessage_Title": "Append messages to Sent folder", + "SettingsAccountManagementAppendMessage_Description": "Create a copy of the message in Sent folder after the draft is sent. Enable this if you don't see your mails after you sent them in Sent folder.", + "SettingsEditLinkedInbox_Title": "Edit Linked Inbox", + "SettingsEditLinkedInbox_Description": "Add / remove accounts, rename or break the link between accounts.", + "SettingsAboutVersion": "Version ", + "SettingsAboutWinoDescription": "Lightweight mail client for Windows device families.", + "SettingsAbout_Description": "Learn more about Wino.", + "SettingsAbout_Title": "About", + "SettingsAccentColor_Description": "Change application's accent color", + "SettingsAccentColor_Title": "Accent Color", + "SettingsAccentColor_UseWindowsAccentColor": "Use my Windows accent color", + "SettingsAccountName_Description": "Change the name of the account.", + "SettingsAccountName_Title": "Account Name", + "SettingsApplicationTheme_Description": "Personalize Wino with different custom application themes for your like.", + "SettingsApplicationTheme_Title": "Application Theme", + "SettingsAvailableThemes_Description": "Select a theme from Wino's own collection for your taste or apply your own themes.", + "SettingsAvailableThemes_Title": "Available Themes", + "SettingsCustomTheme_Description": "Create your own custom theme with custom wallpaper and accent color.", + "SettingsCustomTheme_Title": "Custom Theme", + "SettingsDeleteAccount_Description": "Delete all e-mails and credentials associated with this account.", + "SettingsDeleteAccount_Title": "Delete this account", + "SettingsDeleteProtection_Description": "Should Wino ask you for comfirmation every time you try to permanently delete a mail using Shift + Del keys?", + "SettingsDeleteProtection_Title": "Permanent Delete Protection", + "SettingsDiagnostics_Description": "For developers", + "SettingsDiagnostics_Title": "Diagnostics", + "SettingsDiscord_Description": "Get regular development updates, join roadmap discussions and provide feedback.", + "SettingsDiscord_Title": "Discord Channel", + "SettingsElementThemeSelectionDisabled": "Element theme selection is disabled when application theme is selected other than Default.", + "SettingsElementTheme_Description": "Select a Windows theme for Wino", + "SettingsElementTheme_Title": "Element Theme", + "SettingsEnableHoverActions_Title": "Enable hover actions", + "SettingsEnableIMAPLogs_Description": "Enable this to provide details about IMAP connectivity issuses you had during IMAP server setup.", + "SettingsEnableIMAPLogs_Title": "Enable IMAP Protocol Logs", + "SettingsEnableLogs_Description": "I might need logs for crashes to diagnose issues you have opened in GitHub. None of the logs will expose your credentials or sensetive information to public.", + "SettingsEnableLogs_Title": "Enable Logs", + "SettingsEnableSignature": "Enable Signature", + "SettingsExpandOnStartup_Description": "Set whether Wino should expand this account's folders on startup.", + "SettingsExpandOnStartup_Title": "Expand Menu on Startup", + "SettingsExternalContent_Description": "Manage external content settings when rendering mails.", + "SettingsExternalContent_Title": "External Content", + "SettingsFocusedInbox_Description": "Set whether Inbox should be split into two as Focused - Other.", + "SettingsFocusedInbox_Title": "Focused Inbox", + "SettingsFolderSync_Description": "Enable or disable specific folders for synchronization.", + "SettingsFolderSync_Title": "Folder Synchronization", + "SettingsFolderOptions_Title": "Folder Configuration", + "SettingsFolderOptions_Description": "Change individual folder settings like enable/disable sync or show/hide unread badge.", + "SettingsHoverActionCenter": "Center Action", + "SettingsHoverActionLeft": "Left Action", + "SettingsHoverActionRight": "Right Action", + "SettingsHoverActions_Description": "Select 3 actions to show up when you hover over the mails with cursor.", + "SettingsHoverActions_Title": "Hover Actions", + "SettingsLanguage_Description": "Change display language for Wino.", + "SettingsLanguage_Title": "Display Language", + "CategoriesFolderNameOverride": "Categories", + "MoreFolderNameOverride": "More", + "SettingsOptions_Title": "Settings", + "SettingsLinkAccounts_Description": "Merge multiple accounts into one. See mails from one Inbox together.", + "SettingsLinkAccounts_Title": "Create Linked Accounts", + "SettingsLinkedAccountsSave_Description": "Modify the current link with the new accounts.", + "SettingsLinkedAccountsSave_Title": "Save Changes", + "SettingsLoadImages_Title": "Load images automatically", + "SettingsLoadStyles_Title": "Load styles automatically", + "SettingsMailSpacing_Description": "Adjust the spacing for listing mails.", + "SettingsMailSpacing_Title": "Mail Spacing", + "SettingsFolderMenuStyle_Title": "Create Nested Folders", + "SettingsFolderMenuStyle_Description": "Change whether account folders should be nested inside an account menu item or not. Toggle this off if you like the old menu system in Windows Mail", + "SettingsManageAccountSettings_Description": "Notifications, signatures, synchronization and other settings per account.", + "SettingsManageAccountSettings_Title": "Manage Account Settings", + "SettingsManageLink_Description": "Move items to add new link or remove existing link.", + "SettingsManageLink_Title": "Manage Link", + "SettingsMarkAsRead_Description": "Change what should happen to the selected item.", + "SettingsMarkAsRead_DontChange": "Don't automatically mark item as read", + "SettingsMarkAsRead_SecondsToWait": "Seconds to wait: ", + "SettingsMarkAsRead_Timer": "When viewed in the reading pane", + "SettingsMarkAsRead_Title": "Mark item as read", + "SettingsMarkAsRead_WhenSelected": "When selected", + "SettingsMessageList_Description": "Change how your messages should be organized in mail list.", + "SettingsMessageList_Title": "Message List", + "SettingsNoAccountSetupMessage": "You didn't setup any accounts yet.", + "SettingsNotifications_Description": "Turn on or off notifications for this account.", + "SettingsNotifications_Title": "Notifications", + "SettingsPaneLength_Description": "Change the width of the mail list.", + "SettingsPaneLength_Title": "Mail List Pane Length", + "SettingsPaypal_Description": "Show much more love ❤️ All donations are appreciated.", + "SettingsPaypal_Title": "Donate via PayPal", + "SettingsPersonalizationMailDisplayCompactMode": "Compact Mode", + "SettingsPersonalizationMailDisplayMediumMode": "Medium Mode", + "SettingsPersonalizationMailDisplaySpaciousMode": "Spacious Mode", + "SettingsPersonalization_Description": "Change appearance of Wino as you like.", + "SettingsPersonalization_Title": "Personalization", + "SettingsPrivacyPolicy_Description": "Review privacy policy.", + "SettingsPrivacyPolicy_Title": "Privacy Policy", + "SettingsReadingPane_Description": "Mail rendering options.", + "SettingsReadingPane_Title": "Reading Pane", + "SettingsReaderFont_Title": "Default Reader Font", + "SettingsReaderFontFamily_Description": "Change the default font family and font size for reading mails.", + "SettingsFontFamily_Title": "Font Family", + "SettingsFontSize_Title": "Font Size", + "SettingsFontPreview_Title": "Preview", + "SettingsComposerFont_Title": "Default Composer Font", + "SettingsComposerFontFamily_Description": "Change the default font family and font size for composing mails.", + "SettingsRenameMergeAccount_Description": "Change the display name of the linked accounts.", + "SettingsRenameMergeAccount_Title": "Rename", + "SettingsSemanticZoom_Description": "This will allow you to click on the headers in messages list and go to specific date", + "SettingsSemanticZoom_Title": "Semantic Zoom for Date Headers", + "SettingsShowPreviewText_Description": "Hide/show thepreview text.", + "SettingsShowPreviewText_Title": "Show Preview Text", + "SettingsShowSenderPictures_Description": "Hide/show the thumbnail sender pictures.", + "SettingsShowSenderPictures_Title": "Show Sender Avatars", + "SettingsPrefer24HourClock_Title": "Display Clock Format in 24 Hours", + "SettingsPrefer24HourClock_Description": "Mail recieve times will be displayed in 24 hour format instead of 12 (AM/PM)", + "SettingsSignature_Description": "Edit or remove account signature", + "SettingsSignature_Title": "Signature", + "SettingsStartupItem_Description": "Primary account item to load Inbox at startup.", + "SettingsStartupItem_Title": "Startup Item", + "SettingsStore_Description": "Show some love ❤️", + "SettingsStore_Title": "Rate in Store", + "SettingsThreads_Description": "Organize messages into conversation threads.", + "SettingsThreads_Title": "Conversation Threading", + "SettingsUnlinkAccounts_Description": "Remove the link between accounts. This will not delete your accounts.", + "SettingsUnlinkAccounts_Title": "Unlink Accounts", + "SortingOption_Date": "by date", + "SortingOption_Name": "by name", + "StoreRatingDialog_MessageFirstLine": "All feedbacks are appreciated and they will make much Wino better in the future. Would you like to rate Wino in Microsoft Store?", + "StoreRatingDialog_MessageSecondLine": "Would you like to rate Wino Mail in Microsoft Store?", + "StoreRatingDialog_Title": "Enjoying Wino?", + "SystemFolderConfigDialog_ArchiveFolderDescription": "Archived messages will be moved to here.", + "SystemFolderConfigDialog_ArchiveFolderHeader": "Archive Folder", + "SystemFolderConfigDialog_DeletedFolderDescription": "Deleted messages will be moved to here.", + "SystemFolderConfigDialog_DeletedFolderHeader": "Deleted Folder", + "SystemFolderConfigDialog_DraftFolderDescription": "New mails/replies will be crafted in here.", + "SystemFolderConfigDialog_DraftFolderHeader": "Draft Folder", + "SystemFolderConfigDialog_JunkFolderDescription": "All spam/junk mails will be here.", + "SystemFolderConfigDialog_JunkFolderHeader": "Junk/Spam Folder", + "SystemFolderConfigDialog_MessageFirstLine": "This IMAP server doesn't support SPECIAL-USE extension hence Wino couldn't setup the system folders properly.", + "SystemFolderConfigDialog_MessageSecondLine": "Please select the appropriate folders for specific functionalities.", + "SystemFolderConfigDialog_SentFolderDescription": "Folder that sent messages will be stored.", + "SystemFolderConfigDialog_SentFolderHeader": "Sent Folder", + "SystemFolderConfigDialog_Title": "Configure System Folders", + "TestingImapConnectionMessage": "Testing server connection...", + "Today": "Today", + "UnknownAddress": "unknown address", + "UnknownDateHeader": "Unknown Date", + "UnknownGroupAddress": "unknown Mail Group Address", + "UnknownSender": "Unknown Sender", + "Unsubscribe": "Unsubscribe", + "ViewContactDetails": "View Details", + "WinoUpgradeDescription": "Wino offers 3 accounts to start with for free. If you need more than 3 accounts, please upgrade", + "WinoUpgradeMessage": "Upgrade to Unlimited Accounts", + "WinoUpgradeRemainingAccountsMessage": "{0} out of {1} free accounts used.", + "Yesterday": "Yesterday" +} diff --git a/Wino.Core.Domain/Translations/cs_CZ/resources.json b/Wino.Core.Domain/Translations/cs_CZ/resources.json new file mode 100644 index 00000000..69a9d222 --- /dev/null +++ b/Wino.Core.Domain/Translations/cs_CZ/resources.json @@ -0,0 +1,468 @@ +{ + "AccountCreationDialog_Completed": "hotovo", + "AccountCreationDialog_Initializing": "inicializace", + "AccountCreationDialog_PreparingFolders": "Stahování informací o složkách.", + "AccountCreationDialog_SigninIn": "Probíhá ukládání informací o účtu.", + "AccountEditDialog_Message": "Název účtu", + "AccountEditDialog_Title": "Upravit účet", + "AccountPickerDialog_Title": "Vybrat účet", + "AddHyperlink": "Přidat", + "AutoDiscoveryProgressMessage": "Vyhledávání v nastaveních mailu...", + "BasicIMAPSetupDialog_AdvancedConfiguration": "Pokročilá nastavení", + "BasicIMAPSetupDialog_CredentialLocalMessage": "Vaše nastavení budou uložena pouze lokálně na vašem počítači.", + "BasicIMAPSetupDialog_Description": "Některé účty vyžadují další kroky k přihlášení", + "BasicIMAPSetupDialog_DisplayName": "Zobrazované jméno", + "BasicIMAPSetupDialog_DisplayNamePlaceholder": "např. Jan Novák", + "BasicIMAPSetupDialog_LearnMore": "Zjistit více", + "BasicIMAPSetupDialog_MailAddress": "Emailová adresa", + "BasicIMAPSetupDialog_MailAddressPlaceholder": "jan.novak@seznam.cz", + "BasicIMAPSetupDialog_Password": "Heslo", + "BasicIMAPSetupDialog_Title": "IMAP účet", + "Buttons_AddAccount": "Přidat účet", + "Buttons_ApplyTheme": "Použít motiv", + "Buttons_Browse": "Procházet", + "Buttons_Cancel": "Zrušit", + "Buttons_Close": "Zavřít", + "Buttons_Create": "Vytvořit", + "Buttons_CreateAccount": "Vytvořit účet", + "Buttons_Delete": "Smazat", + "Buttons_Discard": "Zahodit", + "Buttons_EnableImageRendering": "Povolit", + "Buttons_No": "Ne", + "Buttons_Open": "Otevřít", + "Buttons_Purchase": "Koupit", + "Buttons_RateWino": "Ohodnotit Wino", + "Buttons_Save": "Uložit", + "Buttons_SaveConfiguration": "Uložit nastavení", + "Buttons_Share": "Sdílet", + "Buttons_SignIn": "Přihlásit se", + "Buttons_Yes": "Ano", + "Center": "Nastřed", + "ComingSoon": "Již brzy...", + "ComposerFrom": "Od: ", + "ComposerSubject": "Předmět: ", + "ComposerTo": "Komu: ", + "ClipboardTextCopied_Message": "{0} copied to clipboard.", + "ClipboardTextCopied_Title": "Copied", + "ClipboardTextCopyFailed_Message": "Failed to copy {0} to clipboard.", + "ComposerToPlaceholder": "pro vložení adresy zmáčkni Enter", + "CustomThemeBuilder_AccentColorDescription": "Pokud chcete, můžete si nastavit barevný tón. Jinak se použije se výchozí barevný tón Windows.", + "CustomThemeBuilder_AccentColorTitle": "Barevný tón", + "CustomThemeBuilder_PickColor": "Vybrat", + "CustomThemeBuilder_ThemeNameDescription": "Unikátní název pro váš motiv.", + "CustomThemeBuilder_ThemeNameTitle": "Název motivu", + "CustomThemeBuilder_Title": "Nástroj na vytvoření vlastního motivu", + "CustomThemeBuilder_WallpaperDescription": "Nastav vlastní pozadí pro Wino", + "CustomThemeBuilder_WallpaperTitle": "Nastav vlastní pozadí", + "DialogMessage_AccountLimitMessage": "Dosáhli jste limitu vytvořených účtů. Chcete si zakoupit doplněk \"Neomezený účet\", aby jste mohli pokračovat?", + "DialogMessage_AccountLimitTitle": "Dosažen limit počtu účtú", + "DialogMessage_CleanupFolderMessage": "Přejete si trvale smazat všechny maily v této složce?", + "DialogMessage_CleanupFolderTitle": "Vyprázdnit složku", + "DialogMessage_ComposerMissingRecipientMessage": "Zpráva nemá žádného příjemce.", + "DialogMessage_ComposerValidationFailedTitle": "Ověření se nezdařilo", + "DialogMessage_CreateLinkedAccountMessage": "Dejte tomuto novému propojení název. Účty budou propojeny pod tímto názvem.", + "DialogMessage_CreateLinkedAccountTitle": "Název propojeného účtu", + "DialogMessage_DeleteAccountConfirmationMessage": "Odstranit {0}?", + "DialogMessage_DeleteAccountConfirmationTitle": "Všechna data spojená s tímto účtem budou trvale smazána z disku.", + "DialogMessage_DiscardDraftConfirmationMessage": "Tento koncept bude zahozen. Chcete pokračovat?", + "DialogMessage_DiscardDraftConfirmationTitle": "Zahodit koncept", + "DialogMessage_HardDeleteConfirmationMessage": "Trvalé smazání", + "DialogMessage_HardDeleteConfirmationTitle": "Zpráva(y) bude trvale odstraněna. Chcete pokračovat?", + "DialogMessage_NoAccountsForCreateMailMessage": "Nemáte žádný účet pro vytvoření zprávy.", + "DialogMessage_NoAccountsForCreateMailTitle": "Chybějící účet", + "DialogMessage_RenameLinkedAccountsMessage": "Zadejte nový název pro propojený účet", + "DialogMessage_RenameLinkedAccountsTitle": "Přejmenovat propojený účet", + "DialogMessage_UnlinkAccountsConfirmationMessage": "Tato operace neodstraní vaše mailové účty, ale pouze zruší jejich propojení. Chcete pokračovat?", + "DialogMessage_UnlinkAccountsConfirmationTitle": "Rozpojit účty", + "DialogMessage_EmptySubjectConfirmation": "Chybějící Předmět", + "DialogMessage_EmptySubjectConfirmationMessage": "Zpráva nemá Předmět. Chcete pokračovat?", + "Dialog_DontAskAgain": "Příště se neptat", + "DiscordChannelDisclaimerMessage": "Wino nemá vlastní Discord server, ale speciální kanál 'wino-mail' je hostován na serveru 'Developer Sanctuary'.\nChcete-li získat informace o Winu, připojte se k vývojářskému serveru a sledujte 'wino-mail' kanál v rámci 'Community Projects'. \n\nBudete přesměrováni na stránku serveru 'Developer Sanctuary', protože Discord nepodporuje pozvánky přímo do kanálů.", + "DiscordChannelDisclaimerTitle": "Důležité Discord informace", + "Draft": "Koncept", + "EditorToolbarOption_Draw": "Nakreslit", + "EditorToolbarOption_Format": "Formátovat", + "EditorToolbarOption_Insert": "Vložit", + "EditorToolbarOption_None": "Žádné", + "EditorToolbarOption_Options": "Možnosti", + "ElementTheme_Dark": "Tmavý režim", + "ElementTheme_Default": "Použít nastavení systému", + "ElementTheme_Light": "Světlý režim", + "Emoji": "Emoji", + "Exception_ImapClientPoolFailed": "IMAP Client Pool selhal.", + "Exception_AuthenticationCanceled": "Ověřování bylo zrušeno", + "Exception_CustomThemeExists": "Tento motiv už existuje.", + "Exception_CustomThemeMissingName": "Musíte zadat název.", + "Exception_CustomThemeMissingWallpaper": "Musíte zadat vlastní obrázek pozadí.", + "Exception_FailedToSynchronizeFolders": "Synchronizace složek se nezdařila", + "Exception_GoogleAuthCallbackNull": "Callback uri je při aktivaci null.", + "Exception_GoogleAuthCorruptedCode": "Odpověď z autorizačního serveru je chybná.", + "Exception_GoogleAuthError": "Chyba autorizace OAuth: {0}", + "Exception_GoogleAuthInvalidResponse": "Přijata chybová odpověď ({0})", + "Exception_GoogleAuthorizationCodeExchangeFailed": "Ověření autorizačního kódu selhalo.", + "Exception_InvalidSystemFolderConfiguration": "Konfigurace systémové složky není správná. Zkontrolujte konfiguraci a zkuste to znovu.", + "Exception_NullAssignedAccount": "Přiřazený účet je \"null\"", + "Exception_NullAssignedFolder": "Přiřazená složka je \"null\"", + "Exception_SynchronizerFailureHTTP": "Zpracování odpovědi se nezdařilo. HTTP kód chyby: {0}", + "Exception_TokenGenerationFailed": "Generování tokenu selhalo", + "Exception_TokenInfoRetrivalFailed": "Nepodařilo se získat informace o tokenu.", + "Exception_UnknowErrorDuringAuthentication": "Při ověřování došlo k neznámé chybě", + "Exception_UnsupportedAction": "Akce {0} není podporována", + "Exception_UnsupportedProvider": "Tento poskytovatel není podporován.", + "Exception_UnsupportedSynchronizerOperation": "Tato operace není podporována pro {0}", + "Exception_UserCancelSystemFolderSetupDialog": "Uživatel zrušil dialogové okno s konfigurací systémové složky.", + "Files": "Soubory", + "FilteringOption_All": "Všechny", + "FilteringOption_Flagged": "Označené", + "FilteringOption_Unread": "Nepřečtené", + "Focused": "Důležité", + "FolderOperation_CreateSubFolder": "Vytvořit podsložku", + "FolderOperation_Delete": "Odstranit", + "FolderOperation_DontSync": "Nesynchronizovat tuto složku", + "FolderOperation_Empty": "Vyprázdnit tuto složku", + "FolderOperation_MarkAllAsRead": "Označit vše jako přečtené", + "FolderOperation_Move": "Přesunout", + "DragMoveToFolderCaption": "Přesunout do {0}", + "FolderOperation_None": "Žádné", + "FolderOperation_Pin": "Připnout", + "FolderOperation_Rename": "Přejmenovat", + "FolderOperation_Unpin": "Odepnout", + "HoverActionOption_Archive": "Archivovat", + "HoverActionOption_Delete": "Smazat", + "HoverActionOption_MoveJunk": "Přesunout do Koše", + "HoverActionOption_ToggleFlag": "Označit / Zrušit označení", + "HoverActionOption_ToggleRead": "Přečtené / Nepřečtené", + "MergedAccountCommonFolderInbox": "Doručená pošta", + "MergedAccountCommonFolderSent": "Odesláno", + "MergedAccountCommonFolderDraft": "Koncepty", + "MergedAccountCommonFolderJunk": "Nevyžádaná pošta", + "MergedAccountCommonFolderTrash": "Koš", + "MergedAccountCommonFolderArchive": "Archív", + "IMAPSetupDialog_AccountType": "Typ účtu", + "IMAPSetupDialog_DisplayName": "Zobrazované jméno", + "IMAPSetupDialog_DisplayNamePlaceholder": "např. Jan Novák", + "IMAPSetupDialog_IncomingMailServer": "Server příchozí pošty", + "IMAPSetupDialog_IncomingMailServerPort": "Port", + "IMAPSetupDialog_MailAddress": "E-mailová adresa", + "IMAPSetupDialog_MailAddressPlaceholder": "jan.novák@seznam.cz", + "IMAPSetupDialog_OutgoingMailServer": "Server odchozí pošty (SMTP)", + "IMAPSetupDialog_OutgoingMailServerPassword": "Heslo odchozího serveru", + "IMAPSetupDialog_OutgoingMailServerPort": "Port", + "IMAPSetupDialog_OutgoingMailServerRequireAuthentication": "Odchozí server vyžaduje ověření", + "IMAPSetupDialog_OutgoingMailServerUsername": "Uživatelské jméno odchozího serveru", + "IMAPSetupDialog_Password": "Heslo", + "IMAPSetupDialog_RequireSSLForIncomingMail": "Vyžadovat SSL pro příchozí e-mail", + "IMAPSetupDialog_RequireSSLForOutgoingMail": "Vyžadovat SSL pro odchozí e-mail", + "IMAPSetupDialog_Title": "Pokročilé nastavení IMAP", + "IMAPSetupDialog_UseSameConfig": "Použít stejné uživatelské jméno a heslo pro odesílání e-mailu", + "IMAPSetupDialog_Username": "Uživatelské jméno", + "IMAPSetupDialog_UsernamePlaceholder": "jan.novak, jan.novak@seznam.cz", + "ImageRenderingDisabled": "Vykreslování obrázků je pro tuto zprávu zakázáno.", + "InfoBarAction_Enable": "Zapnout", + "InfoBarMessage_SynchronizationDisabledFolder": "Synchronizace této složky je vypnuta.", + "InfoBarTitle_SynchronizationDisabledFolder": "Synchronizace složky vypnuta", + "GeneralTitle_Error": "Error", + "GeneralTitle_Warning": "Warning", + "GeneralTitle_Info": "Information", + "Info_AccountCreatedMessage": "{0} je vytvořen", + "Info_AccountCreatedTitle": "Vytvoření účtu", + "Info_AccountCreationFailedTitle": "Vytvoření účtu selhalo", + "Info_AccountDeletedMessage": "{0} byl úspěšně smazán.", + "Info_AccountDeletedTitle": "Účet byl smazán", + "Info_AccountIssueFixFailedTitle": "Oprava účtu selhala", + "Info_AccountIssueFixSuccessMessage": "Opraveny všechny problémy s účtem.", + "Info_AccountIssueFixSuccessTitle": "Úspěšně dokončeno", + "Info_AttachmentOpenFailedMessage": "Tuto přílohu nelze otevřít.", + "Info_AttachmentOpenFailedTitle": "Chyba", + "Info_AttachmentSaveFailedMessage": "Tuto přílohu nelze uložit.", + "Info_AttachmentSaveFailedTitle": "Chyba", + "Info_AttachmentSaveSuccessMessage": "Příloha byla uložena.", + "Info_AttachmentSaveSuccessTitle": "Hotovo", + "Info_BackgroundExecutionDeniedMessage": "Spuštění aplikace na pozadí je zakázáno. To může mít vliv na synchronizaci na pozadí a oznámení.", + "Info_BackgroundExecutionDeniedTitle": "Zakázané spuštění na pozadí", + "Info_BackgroundExecutionUnknownErrorMessage": "Při registraci procesu synchronizace na pozadí došlo k neznámé výjimce.", + "Info_BackgroundExecutionUnknownErrorTitle": "Chyba procesu na pozadí", + "Info_ComposerMissingMIMEMessage": "Nelze zjistit MIME typ souboru. Synchronizace může pomoci.", + "Info_ComposerMissingMIMETitle": "Chyba", + "Info_ContactExistsMessage": "Tento kontakt je již v seznamu příjemců.", + "Info_ContactExistsTitle": "Kontakt existuje", + "Info_DraftFolderMissingMessage": "Složka Koncepty pro tento účet chybí. Zkontrolujte prosím nastavení účtu.", + "Info_DraftFolderMissingTitle": "Chybí složka Koncepty", + "Info_FileLaunchFailedTitle": "Spuštění souboru se nezdařilo", + "Info_InvalidAddressMessage": "'{0}' není platná e-mailová adresa.", + "Info_InvalidAddressTitle": "Neplatná adresa", + "Info_InvalidMoveTargetMessage": "Do této složky nelze přesunout vybrané e-maily.", + "Info_InvalidMoveTargetTitle": "Neplatná složka pro přesun", + "Info_LogsNotFoundMessage": "Neexistují žádné logy ke sdílení.", + "Info_LogsNotFoundTitle": "Logy nenalezeny", + "Info_LogsSavedMessage": "{0} je uložen do vybrané složky.", + "Info_LogsSavedTitle": "Uloženo", + "Info_MailRenderingFailedMessage": "Tento e-mail je poškozený nebo nelze otevřít.\n{0}", + "Info_MailRenderingFailedTitle": "Vykreslení selhalo", + "Info_MessageCorruptedMessage": "Tato zpráva je poškozena.", + "Info_MessageCorruptedTitle": "Chyba", + "Info_MissingFolderMessage": "{0} neexistuje pro tento účet.", + "Info_MissingFolderTitle": "Chybějící složka", + "Info_PurchaseExistsMessage": "Vypadá to, že tento produkt byl již zakoupen.", + "Info_PurchaseExistsTitle": "Stávající produkt", + "Info_PurchaseThankYouMessage": "Děkujeme", + "Info_PurchaseThankYouTitle": "Úspěšný nákup", + "Info_RequestCreationFailedTitle": "Nepodařilo se vytvořit požadavky", + "Info_ReviewNetworkErrorMessage": "Došlo k problému se sítí, při odesílání vaší recenze.", + "Info_ReviewNetworkErrorTitle": "Problém se sítí", + "Info_ReviewNewMessage": "Veškeré zpětné vazby si ceníme. Děkujeme za recenzi!", + "Info_ReviewSuccessTitle": "Děkujeme", + "Info_ReviewUnknownErrorMessage": "Došlo k neznámému problému s vaší recenzí ({0})", + "Info_ReviewUnknownErrorTitle": "Neznámá chyba", + "Info_ReviewUpdatedMessage": "Děkujeme za aktualizaci recenze.", + "Info_SignatureDisabledMessage": "Podpis pro tento účet vypnut", + "Info_SignatureDisabledTitle": "Hotovo", + "Info_SignatureSavedMessage": "Nový podpis byl uložen", + "Info_SignatureSavedTitle": "Hotovo", + "Info_SyncCanceledMessage": "Zrušeno", + "Info_SyncCanceledTitle": "Synchronizace", + "Info_SyncFailedTitle": "Synchronizace se nezdařila", + "Info_UnsupportedFunctionalityDescription": "Tato funkce zatím není implementována.", + "Info_UnsupportedFunctionalityTitle": "Nepodporováno", + "Info_UnsubscribeLinkInvalidTitle": "Neplatná URI pro odhlášení mailu", + "Info_UnsubscribeLinkInvalidMessage": "Tento odkaz pro odhlášení je neplatný. Nepodařilo se odhlásit automatizované zasílání e-mailu.", + "ImapAdvancedSetupDialog_AuthenticationMethod": "Způsob ověření", + "ImapAdvancedSetupDialog_ConnectionSecurity": "Zabezpečení připojení", + "ImapAuthenticationMethod_Auto": "Auto", + "ImapAuthenticationMethod_CramMD5": "CRAM-MD5", + "ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5", + "ImapAuthenticationMethod_None": "Žádné ověření", + "ImapAuthenticationMethod_Plain": "Normální heslo", + "ImapAuthenticationMethod_EncryptedPassword": "Zašifrované heslo", + "ImapAuthenticationMethod_Ntlm": "NTLM", + "ImapConnectionSecurity_None": "Žádné", + "ImapConnectionSecurity_SslTls": "SSL/TLS", + "ImapConnectionSecurity_StartTls": "STARTTLS", + "ImapConnectionSecurity_Auto": "Auto", + "Justify": "Do bloku", + "Left": "Zleva", + "Link": "Odkaz", + "LinkedAccountsCreatePolicyMessage": "musíte mít alespoň 2 účty, abyste vytvořili propojení\npropojení bude odstraněno po uložení", + "LinkedAccountsTitle": "Propojené účty", + "MailOperation_AlwaysMoveFocused": "Vždy přesunout do složky \"Důležité\"", + "MailOperation_AlwaysMoveOther": "Vždy se přesunout do složky \"Ostatní\"", + "MailOperation_Archive": "Archivovat", + "MailOperation_ClearFlag": "Zrušit označení", + "MailOperation_DarkEditor": "Tmavý", + "MailOperation_Delete": "Smazat", + "MailOperation_ExportPDF": "Exportovat do PDF", + "MailOperation_Find": "Vyhledat", + "MailOperation_Forward": "Přeposlat", + "MailOperation_Ignore": "Ignorovat", + "MailOperation_LightEditor": "Světlý", + "MailOperation_MarkAsJunk": "Označit jako nevyžádané", + "MailOperation_MarkAsRead": "Označit jako přečtené", + "MailOperation_MarkAsUnread": "Označit jako nepřečtené", + "MailOperation_MarkNotJunk": "Zrušit označení jako Nevyžádané", + "MailOperation_Move": "Přesunout", + "MailOperation_MoveFocused": "Přesunout do \"Důležité\"", + "MailOperation_MoveJunk": "Přesunout do \"Koš\"", + "MailOperation_MoveOther": "Přesunout do \"Ostatní\"", + "MailOperation_Navigate": "Přejděte", + "MailOperation_Print": "Vytisknout", + "MailOperation_Reply": "Odpovědět", + "MailOperation_ReplyAll": "Odpovědět všem", + "MailOperation_SaveAs": "Uložit jako…", + "MailOperation_SetFlag": "Označit vlajkou", + "MailOperation_Unarchive": "Odarchivovat", + "MailOperation_Zoom": "Přiblížit", + "MailsSelected": "Vybráno {0} položek", + "MarkFlagUnflag": "Označit / Zrušit označení vlajkou", + "MarkReadUnread": "Označit jako přečtené/nepřečtené", + "MenuManageAccounts": "Nastavení účtů", + "MenuNewMail": "Nový e-mail", + "MenuMergedAccountItemAccountsSuffix": " účty", + "MenuRate": "Ohodnotit Wino", + "MenuSettings": "Nastavení", + "MergedAccountsAvailableAccountsTitle": "Dostupné účty", + "More": "Více", + "MoveMailDialog_InvalidFolderMessage": "{0} není platná složka pro tento e-mail.", + "MoveMailDialog_Title": "Vyberte složku", + "NewAccountDialog_AccountName": "Název účtu", + "NewAccountDialog_AccountNameDefaultValue": "Osobní", + "NewAccountDialog_AccountNamePlaceholder": "např. Osobní účet", + "NewAccountDialog_Title": "Přidat nový účet", + "NoMailSelected": "nebyly vybrány žádné zprávy", + "NoMessageCrieteria": "žádná zpráva neodpovídá kritériím vyhledávání.", + "NoMessageEmptyFolder": "Tato složka je prázdná.", + "Notifications_MultipleNotificationsMessage": "Máš {0} nových zpráv.", + "Notifications_MultipleNotificationsTitle": "Nová pošta", + "Notifications_WinoUpdatedMessage": "Vyzkoušejte novou verzi {0}", + "Notifications_WinoUpdatedTitle": "Wino Mail byl aktualizován.", + "Other": "Ostatní", + "PaneLengthOption_Default": "Výchozí", + "PaneLengthOption_ExtraLarge": "Extra velké", + "PaneLengthOption_Large": "Velké", + "PaneLengthOption_Medium": "Střední", + "PaneLengthOption_Micro": "Mikro", + "PaneLengthOption_Small": "Malé", + "Photos": "Fotky", + "PreparingFoldersMessage": "Připravování složek", + "ProviderDetail_Gmail_Description": "Google účet", + "ProviderDetail_IMAP_Description": "Vlastní IMAP/SMTP server", + "ProviderDetail_IMAP_Title": "IMAP server", + "Results": "Výsledky", + "Right": "Vpravo", + "SynchronizationFolderReport_Success": "Aktuální", + "SynchronizationFolderReport_Failed": "Synchronizace se nezdařila.", + "SearchBarPlaceholder": "vyhledávaný výraz", + "SearchingIn": "Vyhledávání v", + "SettingsAboutGithub_Description": "Přejít na seznam chyb na GitHub.", + "SettingsAboutGithub_Title": "GitHub", + "SettingsAccountManagementAppendMessage_Title": "Přidat zprávy do složky Odeslané", + "SettingsAccountManagementAppendMessage_Description": "Vytvořit kopii zprávy ve složce \"Odeslané\" po odeslání konceptu. Povolte tuto možnost, pokud nevidíte své e-maily po odeslání ve složky \"Odeslané\".", + "SettingsEditLinkedInbox_Title": "Upravit propojený účet", + "SettingsEditLinkedInbox_Description": "Přidat / odebrat účty, přejmenovat nebo zrušit propojení mezi účty.", + "SettingsAboutVersion": "Verze ", + "SettingsAboutWinoDescription": "Lehký e-mailový klient pro systém Windows.", + "SettingsAbout_Description": "Zjistěte více o Wino.", + "SettingsAbout_Title": "O aplikaci", + "SettingsAccentColor_Description": "Změna barevného tónu aplikace", + "SettingsAccentColor_Title": "Barevný tón", + "SettingsAccentColor_UseWindowsAccentColor": "Použít barevný tón mých Windows", + "SettingsAccountName_Description": "Změnit název účtu", + "SettingsAccountName_Title": "Název účtu", + "SettingsApplicationTheme_Description": "Přizpůsobte si Wino různými motivy dle vaší libosti.", + "SettingsApplicationTheme_Title": "Motiv aplikace", + "SettingsAvailableThemes_Description": "Vyberte si šablonu ze sbírky Wino dle vaší libosti nebo použijte vlastní motiv.", + "SettingsAvailableThemes_Title": "Dostupné motivy", + "SettingsCustomTheme_Description": "Vytvořte si vlastní motiv s vlastním pozadím a barevným tónem.", + "SettingsCustomTheme_Title": "Vlastní motiv", + "SettingsDeleteAccount_Description": "Odstranit všechny e-maily a přihlašovací údaje spojené s tímto účtem.", + "SettingsDeleteAccount_Title": "Smazat tento účet", + "SettingsDeleteProtection_Description": "Měl by vás Wino požádat o potvrzení pokaždé, když se pokoušíte trvale smazat e-mail pomocí kláves Shift + Del?", + "SettingsDeleteProtection_Title": "Ochrana proti trvalému smazání", + "SettingsDiagnostics_Description": "Pro vývojáře", + "SettingsDiagnostics_Title": "Diagnostika", + "SettingsDiscord_Description": "Získejte pravidelné aktualizace z vývoje, připojte se k diskusím o rozvoji aplikace a poskytněte zpětnou vazbu.", + "SettingsDiscord_Title": "Discord kanál", + "SettingsElementThemeSelectionDisabled": "Výběr motivu prvku je zakázán, pokud je vybrán jiný motiv aplikace než výchozí.", + "SettingsElementTheme_Description": "Vyberte motiv Windows pro Wino", + "SettingsElementTheme_Title": "Motiv prvku", + "SettingsEnableHoverActions_Title": "Povolit akce při přejetí myší", + "SettingsEnableIMAPLogs_Description": "Povolte pro poskytnutí podrobností o problémech s připojením IMAP, které jste měli během nastavení serveru IMAP.", + "SettingsEnableIMAPLogs_Title": "Povolit logy protokolu IMAP", + "SettingsEnableLogs_Description": "Možná budu potřebovat protokol o pádech pro diagnostiku problémů, které jste vytvořili na GitHubu. Protokoly jsou anonymizované - žádný protokol neukáže vaše přihlašovací údaje nebo citlivé informace.", + "SettingsEnableLogs_Title": "Povolit logy", + "SettingsEnableSignature": "Zapnout podpis", + "SettingsExpandOnStartup_Description": "Nastavte, zda by měl Wino zobrazit složky tohoto účtu při spuštění rozbalené.", + "SettingsExpandOnStartup_Title": "Zobrazit menu při spuštění", + "SettingsExternalContent_Description": "Správa nastavení vykreslování externích e-mailů.", + "SettingsExternalContent_Title": "Externí obsah", + "SettingsFocusedInbox_Description": "Nastavte, zda by měla být doručená pošta rozdělena do dvou jako \"Důležité\" - \"Ostatní\".", + "SettingsFocusedInbox_Title": "Doporučené", + "SettingsFolderSync_Description": "Povolit nebo zakázat konkrétní složky pro synchronizaci.", + "SettingsFolderSync_Title": "Synchronizace složek", + "SettingsFolderOptions_Title": "Nastavení složky", + "SettingsFolderOptions_Description": "Změnit nastavení jednotlivých složek, například povolit/zakázat synchronizaci, nebo zobrazit/skrýt počet nepřečtených e-mailů.", + "SettingsHoverActionCenter": "Prostřední akce", + "SettingsHoverActionLeft": "Levá akce", + "SettingsHoverActionRight": "Pravá akce", + "SettingsHoverActions_Description": "Vyberte 3 akce, které se zobrazí při přejetí pošty kurzorem.", + "SettingsHoverActions_Title": "Akce při přejetí myší", + "SettingsLanguage_Description": "Změnit jazyk aplikace pro Wino.", + "SettingsLanguage_Title": "Jazyk aplikace", + "CategoriesFolderNameOverride": "Kategorie", + "MoreFolderNameOverride": "Více", + "SettingsOptions_Title": "Nastavení", + "SettingsLinkAccounts_Description": "Sloučit více účtů do jednoho. Podívejte se na e-maily v jedné složce \"Doručená pošta\" společně.", + "SettingsLinkAccounts_Title": "Vytvořit propojené účty", + "SettingsLinkedAccountsSave_Description": "Změnit aktuální propojení s novými účty.", + "SettingsLinkedAccountsSave_Title": "Uložit změny", + "SettingsLoadImages_Title": "Automaticky načítat obrázky", + "SettingsLoadStyles_Title": "Automaticky načítat styly", + "SettingsMailSpacing_Description": "Přizpůsobit rozestupy položek v seznamu e-mailů.", + "SettingsMailSpacing_Title": "Rozestupy e-mailů", + "SettingsFolderMenuStyle_Title": "Zapnout vnořené složky", + "SettingsFolderMenuStyle_Description": "Změnit, zda by složky účtu měly být vnořeny uvnitř nabídky účtu či nikoli. Vypněte tento režim, pokud se vám líbí starý systém menu Windows Mail", + "SettingsManageAccountSettings_Description": "Oznámení, podpisy, synchronizace a další nastavení pro jednotlivé účty.", + "SettingsManageAccountSettings_Title": "Správa nastavení účtů", + "SettingsManageLink_Description": "Přesunout položky pro přidání nového propojení účtů nebo odstranění již existujícího.", + "SettingsManageLink_Title": "Spravovat propojení", + "SettingsMarkAsRead_Description": "Změnit, co by se mělo stát s vybranou položkou.", + "SettingsMarkAsRead_DontChange": "Neoznačovat položku automaticky jako přečtenou", + "SettingsMarkAsRead_SecondsToWait": "Sekund k čekání: ", + "SettingsMarkAsRead_Timer": "Při zobrazení v panelu pro čtení", + "SettingsMarkAsRead_Title": "Označit položku jako přečtenou", + "SettingsMarkAsRead_WhenSelected": "Při výběru", + "SettingsMessageList_Description": "Změňte způsob, jakým by měly být zprávy uspořádány v seznamu e-mailů.", + "SettingsMessageList_Title": "Seznam zpráv", + "SettingsNoAccountSetupMessage": "Zatím jste nenastavili žádný účet.", + "SettingsNotifications_Description": "Zapnout nebo vypnout oznámení pro tento účet.", + "SettingsNotifications_Title": "Oznámení", + "SettingsPaneLength_Description": "Změnit šířku seznamu e-mailů.", + "SettingsPaneLength_Title": "Délka panelu e-mailů", + "SettingsPaypal_Description": "Ukažte mnohem více lásky ❤️ Všechny dary jsou vítany.", + "SettingsPaypal_Title": "Přispět přes PayPal", + "SettingsPersonalizationMailDisplayCompactMode": "Kompaktní režim", + "SettingsPersonalizationMailDisplayMediumMode": "Střední režim", + "SettingsPersonalizationMailDisplaySpaciousMode": "Prostorový režim", + "SettingsPersonalization_Description": "Změňte vzhled Wino, jak se vám líbí.", + "SettingsPersonalization_Title": "Přizpůsobení", + "SettingsPrivacyPolicy_Description": "Zkontrolujte zásady ochrany osobních údajů.", + "SettingsPrivacyPolicy_Title": "Zásady ochrany osobních údajů", + "SettingsReadingPane_Description": "Možnosti vykreslování e-mailů.", + "SettingsReadingPane_Title": "Panel s vykreskeným e-mailem", + "SettingsReaderFont_Title": "Výchozí font pro vykreslení e-mailu", + "SettingsReaderFontFamily_Description": "Změna výchozího fontu a jeho velikosti pro vykreslení e-mailu", + "SettingsFontFamily_Title": "Font", + "SettingsFontSize_Title": "Velikost", + "SettingsFontPreview_Title": "Náhled", + "SettingsComposerFont_Title": "Výchozí písmo editoru", + "SettingsComposerFontFamily_Description": "Změna výchozího fontu a jeho velikosti pro editor e-mailu", + "SettingsRenameMergeAccount_Description": "Změnit zobrazený název propojených účtů.", + "SettingsRenameMergeAccount_Title": "Přejmenovat", + "SettingsSemanticZoom_Description": "Toto vám umožní kliknout na hlavičky v seznamu zpráv a přejít na konkrétní datum", + "SettingsSemanticZoom_Title": "Sémanické přiblížení pro záhlaví data", + "SettingsShowPreviewText_Description": "Skrýt/zobrazit náhled textu.", + "SettingsShowPreviewText_Title": "Zobrazit náhled textu", + "SettingsShowSenderPictures_Description": "Skrýt/zobrazit náhled obrázku odesílatele.", + "SettingsShowSenderPictures_Title": "Zobrazit avatary odesílatele", + "SettingsPrefer24HourClock_Title": "Zobrazit 24-hodinový formát času", + "SettingsPrefer24HourClock_Description": "Čas přijetí pošty bude zobrazen ve 24-hodinovém formátu času, namísto 12-hodinového (AM/PM)", + "SettingsSignature_Description": "Upravit nebo odebrat podpis účtu", + "SettingsSignature_Title": "Podpis", + "SettingsStartupItem_Description": "Primární účet zobrazený po startu", + "SettingsStartupItem_Title": "Primární účet", + "SettingsStore_Description": "Ukaž trochu lásky ❤️", + "SettingsStore_Title": "Ohodnotit v obchodě", + "SettingsThreads_Description": "Uspořádat zprávy do konverzačních vláken.", + "SettingsThreads_Title": "Vlákna konverzací", + "SettingsUnlinkAccounts_Description": "Odebrat propojení mezi účty. Toto nesmaže vaše účty.", + "SettingsUnlinkAccounts_Title": "Rozpojit účty", + "SortingOption_Date": "podle data", + "SortingOption_Name": "podle jména", + "StoreRatingDialog_MessageFirstLine": "Veškerá zpětná vazba se cení a v bude mít vliv na zlepšení aplikace Wino. Chcete Wino ohodnotit v Microsoft Store?", + "StoreRatingDialog_MessageSecondLine": "Chcete ohodnotit Wino Mail v Microsoft Store?", + "StoreRatingDialog_Title": "Líbí se vám Wino?", + "SystemFolderConfigDialog_ArchiveFolderDescription": "Archivované zprávy budou přesunuty do zde.", + "SystemFolderConfigDialog_ArchiveFolderHeader": "složka \"Archív\"", + "SystemFolderConfigDialog_DeletedFolderDescription": "Smazané zprávy budou přesunuty zde.", + "SystemFolderConfigDialog_DeletedFolderHeader": "složka \"Koš\"", + "SystemFolderConfigDialog_DraftFolderDescription": "Zde budou vytvořeny nové zprávy/odpovědi.", + "SystemFolderConfigDialog_DraftFolderHeader": "složka \"Koncepty\"", + "SystemFolderConfigDialog_JunkFolderDescription": "Veškerá nevyžádaná pošta skončí zde.", + "SystemFolderConfigDialog_JunkFolderHeader": "složka \"Spam\"", + "SystemFolderConfigDialog_MessageFirstLine": "Tento IMAP server nepodporuje rozšíření SPECIAL-USE, proto Wino nemohla správně nastavit systémové složky.", + "SystemFolderConfigDialog_MessageSecondLine": "Vyberte prosím příslušné složky pro konkrétní funkce.", + "SystemFolderConfigDialog_SentFolderDescription": "Zprávy, které byly odeslány, skončí zde. ", + "SystemFolderConfigDialog_SentFolderHeader": "složka \"Odeslané\"", + "SystemFolderConfigDialog_Title": "Nastavit systémové složky", + "TestingImapConnectionMessage": "Testuji připojení k serveru...", + "Today": "Dnes", + "UnknownAddress": "neznámá adresa", + "UnknownDateHeader": "Neznámé datum", + "UnknownGroupAddress": "neznámá adresa skupiny e-mailů", + "UnknownSender": "Neznámý odesílatel", + "Unsubscribe": "Odhlásit se", + "ViewContactDetails": "Zobrazit podrobnosti", + "WinoUpgradeDescription": "Wino nabízí 3 účty zdarma. Pokud potřebujete více než 3 účty, upgradujte prosím.", + "WinoUpgradeMessage": "Přejít na neomezený počet účtů", + "WinoUpgradeRemainingAccountsMessage": "{0} z {1} použitých bezplatných účtů.", + "Yesterday": "Včera" +} diff --git a/Wino.Core.Domain/Translations/da_DK/resources.json b/Wino.Core.Domain/Translations/da_DK/resources.json new file mode 100644 index 00000000..54ddd05d --- /dev/null +++ b/Wino.Core.Domain/Translations/da_DK/resources.json @@ -0,0 +1,468 @@ +{ + "AccountCreationDialog_Completed": "all done", + "AccountCreationDialog_Initializing": "initializing", + "AccountCreationDialog_PreparingFolders": "We are getting folder information at the moment.", + "AccountCreationDialog_SigninIn": "Account information is being saved.", + "AccountEditDialog_Message": "Account Name", + "AccountEditDialog_Title": "Edit Account", + "AccountPickerDialog_Title": "Pick an account", + "AddHyperlink": "Add", + "AutoDiscoveryProgressMessage": "Searching for mail settings...", + "BasicIMAPSetupDialog_AdvancedConfiguration": "Advanced Configuration", + "BasicIMAPSetupDialog_CredentialLocalMessage": "Your credentials will only be stored locally on your computer.", + "BasicIMAPSetupDialog_Description": "Some accounts require additional steps to sign in", + "BasicIMAPSetupDialog_DisplayName": "Display Name", + "BasicIMAPSetupDialog_DisplayNamePlaceholder": "eg. John Doe", + "BasicIMAPSetupDialog_LearnMore": "Learn more", + "BasicIMAPSetupDialog_MailAddress": "E-Mail Address", + "BasicIMAPSetupDialog_MailAddressPlaceholder": "johndoe@fabrikam.com", + "BasicIMAPSetupDialog_Password": "Password", + "BasicIMAPSetupDialog_Title": "IMAP Account", + "Buttons_AddAccount": "Add Account", + "Buttons_ApplyTheme": "Apply Theme", + "Buttons_Browse": "Browse", + "Buttons_Cancel": "Cancel", + "Buttons_Close": "Close", + "Buttons_Create": "Create", + "Buttons_CreateAccount": "Create Account", + "Buttons_Delete": "Delete", + "Buttons_Discard": "Discard", + "Buttons_EnableImageRendering": "Enable", + "Buttons_No": "No", + "Buttons_Open": "Open", + "Buttons_Purchase": "Purchase", + "Buttons_RateWino": "Rate Wino", + "Buttons_Save": "Save", + "Buttons_SaveConfiguration": "Save Configuration", + "Buttons_Share": "Share", + "Buttons_SignIn": "Sign In", + "Buttons_Yes": "Yes", + "Center": "Center", + "ComingSoon": "Coming soon...", + "ComposerFrom": "From: ", + "ComposerSubject": "Subject: ", + "ComposerTo": "To: ", + "ClipboardTextCopied_Message": "{0} copied to clipboard.", + "ClipboardTextCopied_Title": "Copied", + "ClipboardTextCopyFailed_Message": "Failed to copy {0} to clipboard.", + "ComposerToPlaceholder": "click enter to input addresses", + "CustomThemeBuilder_AccentColorDescription": "Set custom accent color if you wish. Not selecting a color will use your Windows accent color.", + "CustomThemeBuilder_AccentColorTitle": "Accent color", + "CustomThemeBuilder_PickColor": "Pick", + "CustomThemeBuilder_ThemeNameDescription": "Unique name for your custom theme.", + "CustomThemeBuilder_ThemeNameTitle": "Theme name", + "CustomThemeBuilder_Title": "Custom Theme Builder", + "CustomThemeBuilder_WallpaperDescription": "Set a custom wallpaper for Wino", + "CustomThemeBuilder_WallpaperTitle": "Set custom wallpaper", + "DialogMessage_AccountLimitMessage": "You have reached the account creation limit.\nWould you like to purchase 'Unlimited Account' add-on to continue?", + "DialogMessage_AccountLimitTitle": "Account Limit Reached", + "DialogMessage_CleanupFolderMessage": "Do you want to permanently delete all the mails in this folder?", + "DialogMessage_CleanupFolderTitle": "Cleanup Folder", + "DialogMessage_ComposerMissingRecipientMessage": "Message has no recipient.", + "DialogMessage_ComposerValidationFailedTitle": "Validation Failed", + "DialogMessage_CreateLinkedAccountMessage": "Give this new link a name. Accounts will be merged under this name.", + "DialogMessage_CreateLinkedAccountTitle": "Account Link Name", + "DialogMessage_DeleteAccountConfirmationMessage": "Delete {0}?", + "DialogMessage_DeleteAccountConfirmationTitle": "All data associated with this account will be deleted from disk permanently.", + "DialogMessage_DiscardDraftConfirmationMessage": "This draft will be discarded. Do you want to continue?", + "DialogMessage_DiscardDraftConfirmationTitle": "Discard Draft", + "DialogMessage_HardDeleteConfirmationMessage": "Permanent Delete", + "DialogMessage_HardDeleteConfirmationTitle": "Message(s) will be permanently deleted. Do you want to continue?", + "DialogMessage_NoAccountsForCreateMailMessage": "You don't have any accounts to create message from.", + "DialogMessage_NoAccountsForCreateMailTitle": "Account Missing", + "DialogMessage_RenameLinkedAccountsMessage": "Enter new name for linked account", + "DialogMessage_RenameLinkedAccountsTitle": "Rename Linked Account", + "DialogMessage_UnlinkAccountsConfirmationMessage": "This operation will not delete your accounts but only break the link for shared folder connections. Do you want to continue?", + "DialogMessage_UnlinkAccountsConfirmationTitle": "Unlink Accounts", + "DialogMessage_EmptySubjectConfirmation": "Missin Subject", + "DialogMessage_EmptySubjectConfirmationMessage": "Message has no subject. Do you want to continue?", + "Dialog_DontAskAgain": "Don't ask again", + "DiscordChannelDisclaimerMessage": "Wino doesn't have it's own Discord server, but special 'wino-mail' channel is hosted at 'Developer Sanctuary' server.\nTo get the updates about Wino please join Developer Sanctuary server and follow 'wino-mail' channel under 'Community Projects'\n\nYou will be directed to server URL since Discord doesn't support channel invites.", + "DiscordChannelDisclaimerTitle": "Important Discord Information", + "Draft": "Draft", + "EditorToolbarOption_Draw": "Draw", + "EditorToolbarOption_Format": "Format", + "EditorToolbarOption_Insert": "Insert", + "EditorToolbarOption_None": "None", + "EditorToolbarOption_Options": "Options", + "ElementTheme_Dark": "Dark mode", + "ElementTheme_Default": "Use system setting", + "ElementTheme_Light": "Light mode", + "Emoji": "Emoji", + "Exception_ImapClientPoolFailed": "IMAP Client Pool failed.", + "Exception_AuthenticationCanceled": "Authentication canceled", + "Exception_CustomThemeExists": "This theme already exists.", + "Exception_CustomThemeMissingName": "You must provide a name.", + "Exception_CustomThemeMissingWallpaper": "You must provide a custom background image.", + "Exception_FailedToSynchronizeFolders": "Failed to synchronize folders", + "Exception_GoogleAuthCallbackNull": "Callback uri is null on activation.", + "Exception_GoogleAuthCorruptedCode": "Corrupted authorization response.", + "Exception_GoogleAuthError": "OAuth authorization error: {0}", + "Exception_GoogleAuthInvalidResponse": "Received request with invalid state ({0})", + "Exception_GoogleAuthorizationCodeExchangeFailed": "Authorization code exchange failed.", + "Exception_InvalidSystemFolderConfiguration": "System folder configuration is not valid. Check configuration and try again.", + "Exception_NullAssignedAccount": "Assigned account is null", + "Exception_NullAssignedFolder": "Assigned folder is null", + "Exception_SynchronizerFailureHTTP": "Response handling failed with error HTTP code {0}", + "Exception_TokenGenerationFailed": "Token generation failed", + "Exception_TokenInfoRetrivalFailed": "Failed to get token information.", + "Exception_UnknowErrorDuringAuthentication": "Unknown error occurred during authentication", + "Exception_UnsupportedAction": "Action {0} is not implemented in request processor", + "Exception_UnsupportedProvider": "This provider is not supported.", + "Exception_UnsupportedSynchronizerOperation": "This operation is not supported for {0}", + "Exception_UserCancelSystemFolderSetupDialog": "User canceled system folder config dialog.", + "Files": "Files", + "FilteringOption_All": "All", + "FilteringOption_Flagged": "Flagged", + "FilteringOption_Unread": "Unread", + "Focused": "Focused", + "FolderOperation_CreateSubFolder": "Create sub folder", + "FolderOperation_Delete": "Delete", + "FolderOperation_DontSync": "Don't sync this folder", + "FolderOperation_Empty": "Empty this folder", + "FolderOperation_MarkAllAsRead": "Mark all as read", + "FolderOperation_Move": "Move", + "DragMoveToFolderCaption": "Move to {0}", + "FolderOperation_None": "None", + "FolderOperation_Pin": "Pin", + "FolderOperation_Rename": "Rename", + "FolderOperation_Unpin": "Unpin", + "HoverActionOption_Archive": "Archive", + "HoverActionOption_Delete": "Delete", + "HoverActionOption_MoveJunk": "Move to Junk", + "HoverActionOption_ToggleFlag": "Flag / Unflag", + "HoverActionOption_ToggleRead": "Read / Unread", + "MergedAccountCommonFolderInbox": "Inbox", + "MergedAccountCommonFolderSent": "Sent", + "MergedAccountCommonFolderDraft": "Draft", + "MergedAccountCommonFolderJunk": "Junk", + "MergedAccountCommonFolderTrash": "Deleted", + "MergedAccountCommonFolderArchive": "Archive", + "IMAPSetupDialog_AccountType": "Account type", + "IMAPSetupDialog_DisplayName": "Display Name", + "IMAPSetupDialog_DisplayNamePlaceholder": "eg. John Doe", + "IMAPSetupDialog_IncomingMailServer": "Incoming mail server", + "IMAPSetupDialog_IncomingMailServerPort": "Port", + "IMAPSetupDialog_MailAddress": "Email address", + "IMAPSetupDialog_MailAddressPlaceholder": "someone@example.com", + "IMAPSetupDialog_OutgoingMailServer": "Outgoing (SMTP) mail server", + "IMAPSetupDialog_OutgoingMailServerPassword": "Outgoing server password", + "IMAPSetupDialog_OutgoingMailServerPort": "Port", + "IMAPSetupDialog_OutgoingMailServerRequireAuthentication": "Outgoing server requires authentication", + "IMAPSetupDialog_OutgoingMailServerUsername": "Outgoing server user name", + "IMAPSetupDialog_Password": "Password", + "IMAPSetupDialog_RequireSSLForIncomingMail": "Require SSL for incoming email", + "IMAPSetupDialog_RequireSSLForOutgoingMail": "Require SSL for outgoing email", + "IMAPSetupDialog_Title": "Advanced IMAP Configuration", + "IMAPSetupDialog_UseSameConfig": "Use the same username and password for sending email", + "IMAPSetupDialog_Username": "Username", + "IMAPSetupDialog_UsernamePlaceholder": "johndoe, johndoe@fabrikam.com, domain/johndoe", + "ImageRenderingDisabled": "Image rendering is disabled for this message.", + "InfoBarAction_Enable": "Enable", + "InfoBarMessage_SynchronizationDisabledFolder": "This folder is disabled for synchronization.", + "InfoBarTitle_SynchronizationDisabledFolder": "Disabled Folder", + "GeneralTitle_Error": "Error", + "GeneralTitle_Warning": "Warning", + "GeneralTitle_Info": "Information", + "Info_AccountCreatedMessage": "{0} is created", + "Info_AccountCreatedTitle": "Account Creation", + "Info_AccountCreationFailedTitle": "Account Creation Failed", + "Info_AccountDeletedMessage": "{0} is successfuly deleted.", + "Info_AccountDeletedTitle": "Account Deleted", + "Info_AccountIssueFixFailedTitle": "Failed", + "Info_AccountIssueFixSuccessMessage": "Fixed all account issues.", + "Info_AccountIssueFixSuccessTitle": "Success", + "Info_AttachmentOpenFailedMessage": "Can't open this attachment.", + "Info_AttachmentOpenFailedTitle": "Failed", + "Info_AttachmentSaveFailedMessage": "Can't save this attachment.", + "Info_AttachmentSaveFailedTitle": "Failed", + "Info_AttachmentSaveSuccessMessage": "Attachment is saved.", + "Info_AttachmentSaveSuccessTitle": "Attachment Saved", + "Info_BackgroundExecutionDeniedMessage": "Background execution for the app is denied. This may affect background synchronization and live notifications.", + "Info_BackgroundExecutionDeniedTitle": "Denied Background Execution", + "Info_BackgroundExecutionUnknownErrorMessage": "Unknown exception occurred when registering background synchronizer.", + "Info_BackgroundExecutionUnknownErrorTitle": "Background Execution Failure", + "Info_ComposerMissingMIMEMessage": "Couldn't locate the MIME file. Synchronizing may help.", + "Info_ComposerMissingMIMETitle": "Failed", + "Info_ContactExistsMessage": "This contact is already in the recipient list.", + "Info_ContactExistsTitle": "Contact Exists", + "Info_DraftFolderMissingMessage": "Draft folder is missing for this account. Please check your account settings.", + "Info_DraftFolderMissingTitle": "Missing Draft Folder", + "Info_FileLaunchFailedTitle": "Failed to launch file", + "Info_InvalidAddressMessage": "'{0}' is not a valid e-mail address.", + "Info_InvalidAddressTitle": "Invalid Address", + "Info_InvalidMoveTargetMessage": "You can't move selected mails to this folder.", + "Info_InvalidMoveTargetTitle": "Invalid Move Target", + "Info_LogsNotFoundMessage": "There are no logs to share.", + "Info_LogsNotFoundTitle": "Logs Not Found", + "Info_LogsSavedMessage": "{0} is saved to selected folder.", + "Info_LogsSavedTitle": "Saved", + "Info_MailRenderingFailedMessage": "This mail is corrupted or can't be opened.\n{0}", + "Info_MailRenderingFailedTitle": "Render Failed", + "Info_MessageCorruptedMessage": "This message is corrupted.", + "Info_MessageCorruptedTitle": "Error", + "Info_MissingFolderMessage": "{0} doesn't exist for this account.", + "Info_MissingFolderTitle": "Missing Folder", + "Info_PurchaseExistsMessage": "Looks like this product has already been purchased before.", + "Info_PurchaseExistsTitle": "Existing Product", + "Info_PurchaseThankYouMessage": "Thank You", + "Info_PurchaseThankYouTitle": "Purchase successful", + "Info_RequestCreationFailedTitle": "Failed to Create Requests", + "Info_ReviewNetworkErrorMessage": "There was a network issue with your review.", + "Info_ReviewNetworkErrorTitle": "Network Issue", + "Info_ReviewNewMessage": "All feedbacks are appreciated. Thank you for the review!", + "Info_ReviewSuccessTitle": "Thank you", + "Info_ReviewUnknownErrorMessage": "There was an unknown issue with your review. ({0})", + "Info_ReviewUnknownErrorTitle": "Unknown Error", + "Info_ReviewUpdatedMessage": "Thank you for the updated review.", + "Info_SignatureDisabledMessage": "Disabled signature for this account", + "Info_SignatureDisabledTitle": "Success", + "Info_SignatureSavedMessage": "New signature is saved", + "Info_SignatureSavedTitle": "Success", + "Info_SyncCanceledMessage": "Canceled", + "Info_SyncCanceledTitle": "Synchronization", + "Info_SyncFailedTitle": "Synchronization Failed", + "Info_UnsupportedFunctionalityDescription": "This functionality is not implemented yet.", + "Info_UnsupportedFunctionalityTitle": "Unsupported", + "Info_UnsubscribeLinkInvalidTitle": "Invalid Unsubscribe Uri", + "Info_UnsubscribeLinkInvalidMessage": "This unsubscribe link is invalid. Failed to unsubscribe from the list.", + "ImapAdvancedSetupDialog_AuthenticationMethod": "Authentication method", + "ImapAdvancedSetupDialog_ConnectionSecurity": "Connection security", + "ImapAuthenticationMethod_Auto": "Auto", + "ImapAuthenticationMethod_CramMD5": "CRAM-MD5", + "ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5", + "ImapAuthenticationMethod_None": "No authentication", + "ImapAuthenticationMethod_Plain": "Normal password", + "ImapAuthenticationMethod_EncryptedPassword": "Encrypted password", + "ImapAuthenticationMethod_Ntlm": "NTLM", + "ImapConnectionSecurity_None": "None", + "ImapConnectionSecurity_SslTls": "SSL/TLS", + "ImapConnectionSecurity_StartTls": "STARTTLS", + "ImapConnectionSecurity_Auto": "Auto", + "Justify": "Justify", + "Left": "Left", + "Link": "Link", + "LinkedAccountsCreatePolicyMessage": "you must have at least 2 accounts to create link\nlink will be removed on save", + "LinkedAccountsTitle": "Linked Accounts", + "MailOperation_AlwaysMoveFocused": "Always Move to Focused", + "MailOperation_AlwaysMoveOther": "Always Move to Other", + "MailOperation_Archive": "Archive", + "MailOperation_ClearFlag": "Clear flag", + "MailOperation_DarkEditor": "Dark", + "MailOperation_Delete": "Delete", + "MailOperation_ExportPDF": "Export to PDF", + "MailOperation_Find": "Find", + "MailOperation_Forward": "Forward", + "MailOperation_Ignore": "Ignore", + "MailOperation_LightEditor": "Light", + "MailOperation_MarkAsJunk": "Mark as junk", + "MailOperation_MarkAsRead": "Mark as read", + "MailOperation_MarkAsUnread": "Mark as unread", + "MailOperation_MarkNotJunk": "Mark as Not Junk", + "MailOperation_Move": "Move", + "MailOperation_MoveFocused": "Move to Focused", + "MailOperation_MoveJunk": "Move to Junk", + "MailOperation_MoveOther": "Move to Other", + "MailOperation_Navigate": "Navigate", + "MailOperation_Print": "Print", + "MailOperation_Reply": "Reply", + "MailOperation_ReplyAll": "Reply all", + "MailOperation_SaveAs": "Save As", + "MailOperation_SetFlag": "Set flag", + "MailOperation_Unarchive": "Unarchive", + "MailOperation_Zoom": "Zoom", + "MailsSelected": "{0} item(s) selected", + "MarkFlagUnflag": "Mark as flagged/unflagged", + "MarkReadUnread": "Mark as read/unread", + "MenuManageAccounts": "Manage Accounts", + "MenuNewMail": "New Mail", + "MenuMergedAccountItemAccountsSuffix": " accounts", + "MenuRate": "Rate Wino", + "MenuSettings": "Settings", + "MergedAccountsAvailableAccountsTitle": "Available Accounts", + "More": "More", + "MoveMailDialog_InvalidFolderMessage": "{0} is not a valid folder for this mail.", + "MoveMailDialog_Title": "Pick a folder", + "NewAccountDialog_AccountName": "Account Name", + "NewAccountDialog_AccountNameDefaultValue": "Personal", + "NewAccountDialog_AccountNamePlaceholder": "eg. Personal Mail", + "NewAccountDialog_Title": "Add New Account", + "NoMailSelected": "no message selected", + "NoMessageCrieteria": "no messages match your search criteria.", + "NoMessageEmptyFolder": "this folder is empty.", + "Notifications_MultipleNotificationsMessage": "You have {0} new mails", + "Notifications_MultipleNotificationsTitle": "New Mails", + "Notifications_WinoUpdatedMessage": "Checkout new version {0}", + "Notifications_WinoUpdatedTitle": "Wino Mail has been updated.", + "Other": "Other", + "PaneLengthOption_Default": "Default", + "PaneLengthOption_ExtraLarge": "Extra Large", + "PaneLengthOption_Large": "Large", + "PaneLengthOption_Medium": "Medium", + "PaneLengthOption_Micro": "Micro", + "PaneLengthOption_Small": "Small", + "Photos": "Photos", + "PreparingFoldersMessage": "Preparing folders", + "ProviderDetail_Gmail_Description": "Google Account", + "ProviderDetail_IMAP_Description": "Custom IMAP/SMTP server", + "ProviderDetail_IMAP_Title": "IMAP Server", + "Results": "Results", + "Right": "Right", + "SynchronizationFolderReport_Success": "up to date", + "SynchronizationFolderReport_Failed": "synchronization is failed", + "SearchBarPlaceholder": "search", + "SearchingIn": "searching in", + "SettingsAboutGithub_Description": "Go to issue tracker GitHub repository.", + "SettingsAboutGithub_Title": "GitHub", + "SettingsAccountManagementAppendMessage_Title": "Append messages to Sent folder", + "SettingsAccountManagementAppendMessage_Description": "Create a copy of the message in Sent folder after the draft is sent. Enable this if you don't see your mails after you sent them in Sent folder.", + "SettingsEditLinkedInbox_Title": "Edit Linked Inbox", + "SettingsEditLinkedInbox_Description": "Add / remove accounts, rename or break the link between accounts.", + "SettingsAboutVersion": "Version ", + "SettingsAboutWinoDescription": "Lightweight mail client for Windows device families.", + "SettingsAbout_Description": "Learn more about Wino.", + "SettingsAbout_Title": "About", + "SettingsAccentColor_Description": "Change application's accent color", + "SettingsAccentColor_Title": "Accent Color", + "SettingsAccentColor_UseWindowsAccentColor": "Use my Windows accent color", + "SettingsAccountName_Description": "Change the name of the account.", + "SettingsAccountName_Title": "Account Name", + "SettingsApplicationTheme_Description": "Personalize Wino with different custom application themes for your like.", + "SettingsApplicationTheme_Title": "Application Theme", + "SettingsAvailableThemes_Description": "Select a theme from Wino's own collection for your taste or apply your own themes.", + "SettingsAvailableThemes_Title": "Available Themes", + "SettingsCustomTheme_Description": "Create your own custom theme with custom wallpaper and accent color.", + "SettingsCustomTheme_Title": "Custom Theme", + "SettingsDeleteAccount_Description": "Delete all e-mails and credentials associated with this account.", + "SettingsDeleteAccount_Title": "Delete this account", + "SettingsDeleteProtection_Description": "Should Wino ask you for comfirmation every time you try to permanently delete a mail using Shift + Del keys?", + "SettingsDeleteProtection_Title": "Permanent Delete Protection", + "SettingsDiagnostics_Description": "For developers", + "SettingsDiagnostics_Title": "Diagnostics", + "SettingsDiscord_Description": "Get regular development updates, join roadmap discussions and provide feedback.", + "SettingsDiscord_Title": "Discord Channel", + "SettingsElementThemeSelectionDisabled": "Element theme selection is disabled when application theme is selected other than Default.", + "SettingsElementTheme_Description": "Select a Windows theme for Wino", + "SettingsElementTheme_Title": "Element Theme", + "SettingsEnableHoverActions_Title": "Enable hover actions", + "SettingsEnableIMAPLogs_Description": "Enable this to provide details about IMAP connectivity issuses you had during IMAP server setup.", + "SettingsEnableIMAPLogs_Title": "Enable IMAP Protocol Logs", + "SettingsEnableLogs_Description": "I might need logs for crashes to diagnose issues you have opened in GitHub. None of the logs will expose your credentials or sensetive information to public.", + "SettingsEnableLogs_Title": "Enable Logs", + "SettingsEnableSignature": "Enable Signature", + "SettingsExpandOnStartup_Description": "Set whether Wino should expand this account's folders on startup.", + "SettingsExpandOnStartup_Title": "Expand Menu on Startup", + "SettingsExternalContent_Description": "Manage external content settings when rendering mails.", + "SettingsExternalContent_Title": "External Content", + "SettingsFocusedInbox_Description": "Set whether Inbox should be split into two as Focused - Other.", + "SettingsFocusedInbox_Title": "Focused Inbox", + "SettingsFolderSync_Description": "Enable or disable specific folders for synchronization.", + "SettingsFolderSync_Title": "Folder Synchronization", + "SettingsFolderOptions_Title": "Folder Configuration", + "SettingsFolderOptions_Description": "Change individual folder settings like enable/disable sync or show/hide unread badge.", + "SettingsHoverActionCenter": "Center Action", + "SettingsHoverActionLeft": "Left Action", + "SettingsHoverActionRight": "Right Action", + "SettingsHoverActions_Description": "Select 3 actions to show up when you hover over the mails with cursor.", + "SettingsHoverActions_Title": "Hover Actions", + "SettingsLanguage_Description": "Change display language for Wino.", + "SettingsLanguage_Title": "Display Language", + "CategoriesFolderNameOverride": "Categories", + "MoreFolderNameOverride": "More", + "SettingsOptions_Title": "Settings", + "SettingsLinkAccounts_Description": "Merge multiple accounts into one. See mails from one Inbox together.", + "SettingsLinkAccounts_Title": "Create Linked Accounts", + "SettingsLinkedAccountsSave_Description": "Modify the current link with the new accounts.", + "SettingsLinkedAccountsSave_Title": "Save Changes", + "SettingsLoadImages_Title": "Load images automatically", + "SettingsLoadStyles_Title": "Load styles automatically", + "SettingsMailSpacing_Description": "Adjust the spacing for listing mails.", + "SettingsMailSpacing_Title": "Mail Spacing", + "SettingsFolderMenuStyle_Title": "Create Nested Folders", + "SettingsFolderMenuStyle_Description": "Change whether account folders should be nested inside an account menu item or not. Toggle this off if you like the old menu system in Windows Mail", + "SettingsManageAccountSettings_Description": "Notifications, signatures, synchronization and other settings per account.", + "SettingsManageAccountSettings_Title": "Manage Account Settings", + "SettingsManageLink_Description": "Move items to add new link or remove existing link.", + "SettingsManageLink_Title": "Manage Link", + "SettingsMarkAsRead_Description": "Change what should happen to the selected item.", + "SettingsMarkAsRead_DontChange": "Don't automatically mark item as read", + "SettingsMarkAsRead_SecondsToWait": "Seconds to wait: ", + "SettingsMarkAsRead_Timer": "When viewed in the reading pane", + "SettingsMarkAsRead_Title": "Mark item as read", + "SettingsMarkAsRead_WhenSelected": "When selected", + "SettingsMessageList_Description": "Change how your messages should be organized in mail list.", + "SettingsMessageList_Title": "Message List", + "SettingsNoAccountSetupMessage": "You didn't setup any accounts yet.", + "SettingsNotifications_Description": "Turn on or off notifications for this account.", + "SettingsNotifications_Title": "Notifications", + "SettingsPaneLength_Description": "Change the width of the mail list.", + "SettingsPaneLength_Title": "Mail List Pane Length", + "SettingsPaypal_Description": "Show much more love ❤️ All donations are appreciated.", + "SettingsPaypal_Title": "Donate via PayPal", + "SettingsPersonalizationMailDisplayCompactMode": "Compact Mode", + "SettingsPersonalizationMailDisplayMediumMode": "Medium Mode", + "SettingsPersonalizationMailDisplaySpaciousMode": "Spacious Mode", + "SettingsPersonalization_Description": "Change appearance of Wino as you like.", + "SettingsPersonalization_Title": "Personalization", + "SettingsPrivacyPolicy_Description": "Review privacy policy.", + "SettingsPrivacyPolicy_Title": "Privacy Policy", + "SettingsReadingPane_Description": "Mail rendering options.", + "SettingsReadingPane_Title": "Reading Pane", + "SettingsReaderFont_Title": "Default Reader Font", + "SettingsReaderFontFamily_Description": "Change the default font family and font size for reading mails.", + "SettingsFontFamily_Title": "Font Family", + "SettingsFontSize_Title": "Font Size", + "SettingsFontPreview_Title": "Preview", + "SettingsComposerFont_Title": "Default Composer Font", + "SettingsComposerFontFamily_Description": "Change the default font family and font size for composing mails.", + "SettingsRenameMergeAccount_Description": "Change the display name of the linked accounts.", + "SettingsRenameMergeAccount_Title": "Rename", + "SettingsSemanticZoom_Description": "This will allow you to click on the headers in messages list and go to specific date", + "SettingsSemanticZoom_Title": "Semantic Zoom for Date Headers", + "SettingsShowPreviewText_Description": "Hide/show thepreview text.", + "SettingsShowPreviewText_Title": "Show Preview Text", + "SettingsShowSenderPictures_Description": "Hide/show the thumbnail sender pictures.", + "SettingsShowSenderPictures_Title": "Show Sender Avatars", + "SettingsPrefer24HourClock_Title": "Display Clock Format in 24 Hours", + "SettingsPrefer24HourClock_Description": "Mail recieve times will be displayed in 24 hour format instead of 12 (AM/PM)", + "SettingsSignature_Description": "Edit or remove account signature", + "SettingsSignature_Title": "Signature", + "SettingsStartupItem_Description": "Primary account item to load Inbox at startup.", + "SettingsStartupItem_Title": "Startup Item", + "SettingsStore_Description": "Show some love ❤️", + "SettingsStore_Title": "Rate in Store", + "SettingsThreads_Description": "Organize messages into conversation threads.", + "SettingsThreads_Title": "Conversation Threading", + "SettingsUnlinkAccounts_Description": "Remove the link between accounts. This will not delete your accounts.", + "SettingsUnlinkAccounts_Title": "Unlink Accounts", + "SortingOption_Date": "by date", + "SortingOption_Name": "by name", + "StoreRatingDialog_MessageFirstLine": "All feedbacks are appreciated and they will make much Wino better in the future. Would you like to rate Wino in Microsoft Store?", + "StoreRatingDialog_MessageSecondLine": "Would you like to rate Wino Mail in Microsoft Store?", + "StoreRatingDialog_Title": "Enjoying Wino?", + "SystemFolderConfigDialog_ArchiveFolderDescription": "Archived messages will be moved to here.", + "SystemFolderConfigDialog_ArchiveFolderHeader": "Archive Folder", + "SystemFolderConfigDialog_DeletedFolderDescription": "Deleted messages will be moved to here.", + "SystemFolderConfigDialog_DeletedFolderHeader": "Deleted Folder", + "SystemFolderConfigDialog_DraftFolderDescription": "New mails/replies will be crafted in here.", + "SystemFolderConfigDialog_DraftFolderHeader": "Draft Folder", + "SystemFolderConfigDialog_JunkFolderDescription": "All spam/junk mails will be here.", + "SystemFolderConfigDialog_JunkFolderHeader": "Junk/Spam Folder", + "SystemFolderConfigDialog_MessageFirstLine": "This IMAP server doesn't support SPECIAL-USE extension hence Wino couldn't setup the system folders properly.", + "SystemFolderConfigDialog_MessageSecondLine": "Please select the appropriate folders for specific functionalities.", + "SystemFolderConfigDialog_SentFolderDescription": "Folder that sent messages will be stored.", + "SystemFolderConfigDialog_SentFolderHeader": "Sent Folder", + "SystemFolderConfigDialog_Title": "Configure System Folders", + "TestingImapConnectionMessage": "Testing server connection...", + "Today": "Today", + "UnknownAddress": "unknown address", + "UnknownDateHeader": "Unknown Date", + "UnknownGroupAddress": "unknown Mail Group Address", + "UnknownSender": "Unknown Sender", + "Unsubscribe": "Unsubscribe", + "ViewContactDetails": "View Details", + "WinoUpgradeDescription": "Wino offers 3 accounts to start with for free. If you need more than 3 accounts, please upgrade", + "WinoUpgradeMessage": "Upgrade to Unlimited Accounts", + "WinoUpgradeRemainingAccountsMessage": "{0} out of {1} free accounts used.", + "Yesterday": "Yesterday" +} diff --git a/Wino.Core.Domain/Translations/de_DE/resources.json b/Wino.Core.Domain/Translations/de_DE/resources.json new file mode 100644 index 00000000..7102c813 --- /dev/null +++ b/Wino.Core.Domain/Translations/de_DE/resources.json @@ -0,0 +1,468 @@ +{ + "AccountCreationDialog_Completed": "alles erledigt", + "AccountCreationDialog_Initializing": "initialisiere", + "AccountCreationDialog_PreparingFolders": "Es werden Ordner-Informationen gesammelt.", + "AccountCreationDialog_SigninIn": "Kontoinformationen wurden gespeichert.", + "AccountEditDialog_Message": "Konto-Name", + "AccountEditDialog_Title": "Konto bearbeiten", + "AccountPickerDialog_Title": "Konto auswählen", + "AddHyperlink": "Hinzufügen", + "AutoDiscoveryProgressMessage": "Es wird nach Mail-Einstellungen gesucht...", + "BasicIMAPSetupDialog_AdvancedConfiguration": "Erweiterte Konfiguration", + "BasicIMAPSetupDialog_CredentialLocalMessage": "Die Zugangsdaten werden nur lokal auf Ihrem Computer gespeichert.", + "BasicIMAPSetupDialog_Description": "Einige Konten benötigen zusätzliche Schritte zum Anmelden", + "BasicIMAPSetupDialog_DisplayName": "Anzeigename", + "BasicIMAPSetupDialog_DisplayNamePlaceholder": "z.B. John Doe", + "BasicIMAPSetupDialog_LearnMore": "Mehr erfahren", + "BasicIMAPSetupDialog_MailAddress": "E-Mail Adresse", + "BasicIMAPSetupDialog_MailAddressPlaceholder": "johndoe@fabrikam.com", + "BasicIMAPSetupDialog_Password": "Passwort", + "BasicIMAPSetupDialog_Title": "IMAP-Konto", + "Buttons_AddAccount": "Konto hinzufügen", + "Buttons_ApplyTheme": "Thema anwenden", + "Buttons_Browse": "Durchsuchen", + "Buttons_Cancel": "Abbrechen", + "Buttons_Close": "Schließen", + "Buttons_Create": "Erstellen", + "Buttons_CreateAccount": "Konto erstellen", + "Buttons_Delete": "Löschen", + "Buttons_Discard": "Verwerfen", + "Buttons_EnableImageRendering": "An", + "Buttons_No": "Nein", + "Buttons_Open": "Öffnen", + "Buttons_Purchase": "Kaufen", + "Buttons_RateWino": "Wino bewerten", + "Buttons_Save": "Speichern", + "Buttons_SaveConfiguration": "Einstellungen speichern", + "Buttons_Share": "Teilen", + "Buttons_SignIn": "Anmelden", + "Buttons_Yes": "Ja", + "Center": "Zentriert", + "ComingSoon": "Bald verfügbar...", + "ComposerFrom": "Von: ", + "ComposerSubject": "Betreff: ", + "ComposerTo": "An: ", + "ClipboardTextCopied_Message": "{0} copied to clipboard.", + "ClipboardTextCopied_Title": "Copied", + "ClipboardTextCopyFailed_Message": "Failed to copy {0} to clipboard.", + "ComposerToPlaceholder": "klicke Enter, um Adressen einzugeben", + "CustomThemeBuilder_AccentColorDescription": "Legen Sie eine benutzerdefinierte Akzentfarbe fest. Wenn Sie keine Farbe wählen, wird die Akzentfarbe von Windows verwendet.", + "CustomThemeBuilder_AccentColorTitle": "Akzentfarbe", + "CustomThemeBuilder_PickColor": "Auswählen", + "CustomThemeBuilder_ThemeNameDescription": "Eindeutiger Name für Ihr benutzerdefiniertes Thema.", + "CustomThemeBuilder_ThemeNameTitle": "Name des Designs", + "CustomThemeBuilder_Title": "Benutzerdefinierter Theme-Generator", + "CustomThemeBuilder_WallpaperDescription": "Eigenen Hintergrund für Wino festlegen", + "CustomThemeBuilder_WallpaperTitle": "Eigenen Hintergrund festlegen", + "DialogMessage_AccountLimitMessage": "Sie haben das Limit für die Kontenanzahl erreicht.\nMöchten Sie die Option \"Unbegrenzte Konten\" kaufen, um fortzufahren?", + "DialogMessage_AccountLimitTitle": "Kontolimit erreicht", + "DialogMessage_CleanupFolderMessage": "Möchten Sie alle Mails in diesem Ordner dauerhaft löschen?", + "DialogMessage_CleanupFolderTitle": "Bereinigungs-Ordner", + "DialogMessage_ComposerMissingRecipientMessage": "Nachricht hat keinen Empfänger.", + "DialogMessage_ComposerValidationFailedTitle": "Validierung fehlgeschlagen", + "DialogMessage_CreateLinkedAccountMessage": "Geben Sie diesem neuen Link einen Namen. Konten werden unter diesem Namen zusammengeführt.", + "DialogMessage_CreateLinkedAccountTitle": "Name des Konto-Links", + "DialogMessage_DeleteAccountConfirmationMessage": "{0} löschen?", + "DialogMessage_DeleteAccountConfirmationTitle": "Alle mit diesem Konto verknüpften Daten werden dauerhaft von der Festplatte gelöscht.", + "DialogMessage_DiscardDraftConfirmationMessage": "Dieser Entwurf wird verworfen. Möchten Sie fortfahren?", + "DialogMessage_DiscardDraftConfirmationTitle": "Entwurf verwerfen", + "DialogMessage_HardDeleteConfirmationMessage": "Dauerhaft löschen", + "DialogMessage_HardDeleteConfirmationTitle": "Nachricht(en) werden dauerhaft gelöscht. Möchten Sie fortfahren?", + "DialogMessage_NoAccountsForCreateMailMessage": "Sie haben keine Konten zum Erstellen von Nachrichten.", + "DialogMessage_NoAccountsForCreateMailTitle": "Konto fehlt", + "DialogMessage_RenameLinkedAccountsMessage": "Neuen Namen für verknüpftes Konto eingeben", + "DialogMessage_RenameLinkedAccountsTitle": "Verknüpftes Konto umbenennen", + "DialogMessage_UnlinkAccountsConfirmationMessage": "Dieser Vorgang wird Ihre Konten nicht löschen, sondern nur den Link für freigegebene Ordnerverbindungen zerstören. Möchten Sie fortfahren?", + "DialogMessage_UnlinkAccountsConfirmationTitle": "Konten trennen", + "DialogMessage_EmptySubjectConfirmation": "Betreff fehlt", + "DialogMessage_EmptySubjectConfirmationMessage": "Nachricht hat keinen Betreff. Möchten Sie fortfahren?", + "Dialog_DontAskAgain": "Nicht mehr fragen", + "DiscordChannelDisclaimerMessage": "Wino hat keinen eigenen Discord Server, aber der spezielle 'wino-mail'-Kanal wird auf dem 'Developer Sanctuary' Server gehostet (englisch).\nUm Updates über Wino zu erhalten, treten Sie gerne dem 'Developer Sactuary'-Server bei und folgen dem 'wino-mail'-Kanal unter 'Community Projects'.\n\nSie werden zur Server-URL weitergeleitet, da Discord keine Kanal-Einladungen unterstützt.", + "DiscordChannelDisclaimerTitle": "Wichtige Discord-Informationen", + "Draft": "Entwurf", + "EditorToolbarOption_Draw": "Zeichnen", + "EditorToolbarOption_Format": "Format", + "EditorToolbarOption_Insert": "Einfügen", + "EditorToolbarOption_None": "Keine", + "EditorToolbarOption_Options": "Optionen", + "ElementTheme_Dark": "Dunkler Modus", + "ElementTheme_Default": "Systemeinstellung verwenden", + "ElementTheme_Light": "Heller Modus", + "Emoji": "Emoji", + "Exception_ImapClientPoolFailed": "IMAP Client-Pool fehlgeschlagen.", + "Exception_AuthenticationCanceled": "Authentifizierung abgebrochen", + "Exception_CustomThemeExists": "Dieses Thema existiert bereits.", + "Exception_CustomThemeMissingName": "Sie müssen einen Namen angeben.", + "Exception_CustomThemeMissingWallpaper": "Sie müssen ein eigenes Hintergrundbild angeben.", + "Exception_FailedToSynchronizeFolders": "Fehler beim Synchronisieren der Ordner", + "Exception_GoogleAuthCallbackNull": "'Callback uri ist 'null' bei der Aktivierung.", + "Exception_GoogleAuthCorruptedCode": "Korrupte Autorisierungsantwort.", + "Exception_GoogleAuthError": "OAuth Autorisierungsfehler: {0}", + "Exception_GoogleAuthInvalidResponse": "Anfrage mit ungültigem Status ({0}) erhalten", + "Exception_GoogleAuthorizationCodeExchangeFailed": "Autorisierungscode-Austausch fehlgeschlagen.", + "Exception_InvalidSystemFolderConfiguration": "Die Konfiguration des Systemordners ist nicht gültig. Überprüfen Sie die Konfiguration und versuchen Sie es erneut.", + "Exception_NullAssignedAccount": "Zugewiesenes Konto ist 'null'", + "Exception_NullAssignedFolder": "Zugewiesener Ordner ist 'null'", + "Exception_SynchronizerFailureHTTP": "Antwortbehandlung fehlgeschlagen mit Fehler HTTP-Code {0}", + "Exception_TokenGenerationFailed": "Tokengenerierung fehlgeschlagen", + "Exception_TokenInfoRetrivalFailed": "Fehler beim Abrufen der Token-Informationen.", + "Exception_UnknowErrorDuringAuthentication": "Unbekannter Fehler bei der Authentifizierung", + "Exception_UnsupportedAction": "Aktion {0} ist nicht im Anfrageprozessor implementiert", + "Exception_UnsupportedProvider": "Dieser Anbieter wird nicht unterstützt.", + "Exception_UnsupportedSynchronizerOperation": "Diese Operation wird für {0} nicht unterstützt", + "Exception_UserCancelSystemFolderSetupDialog": "Benutzer hat den Konfigurationsdialog für den Systemordner abgebrochen.", + "Files": "Dateien", + "FilteringOption_All": "Alle", + "FilteringOption_Flagged": "Markiert", + "FilteringOption_Unread": "Ungelesen", + "Focused": "Fokussiert", + "FolderOperation_CreateSubFolder": "Unterordner erstellen", + "FolderOperation_Delete": "Löschen", + "FolderOperation_DontSync": "Ordner nicht synchronisieren", + "FolderOperation_Empty": "Ordner leeren", + "FolderOperation_MarkAllAsRead": "Alle als gelesen markieren", + "FolderOperation_Move": "Verschieben", + "DragMoveToFolderCaption": "Nach {0} verschieben", + "FolderOperation_None": "Keine", + "FolderOperation_Pin": "Anheften", + "FolderOperation_Rename": "Umbenennen", + "FolderOperation_Unpin": "Lösen", + "HoverActionOption_Archive": "Archivieren", + "HoverActionOption_Delete": "Löschen", + "HoverActionOption_MoveJunk": "In den Papierkorb verschieben", + "HoverActionOption_ToggleFlag": "Markieren / Markierung entfernen", + "HoverActionOption_ToggleRead": "Lesen / Ungelesen", + "MergedAccountCommonFolderInbox": "Posteingang", + "MergedAccountCommonFolderSent": "Gesendet", + "MergedAccountCommonFolderDraft": "Entwürfe", + "MergedAccountCommonFolderJunk": "Papierkorb", + "MergedAccountCommonFolderTrash": "Gelöscht", + "MergedAccountCommonFolderArchive": "Archiv", + "IMAPSetupDialog_AccountType": "Kontotyp", + "IMAPSetupDialog_DisplayName": "Anzeigename", + "IMAPSetupDialog_DisplayNamePlaceholder": "z.B. John Doe", + "IMAPSetupDialog_IncomingMailServer": "Eingehender Mail-Server", + "IMAPSetupDialog_IncomingMailServerPort": "Port", + "IMAPSetupDialog_MailAddress": "E-Mail Adresse", + "IMAPSetupDialog_MailAddressPlaceholder": "jemand@beispiel.de", + "IMAPSetupDialog_OutgoingMailServer": "Ausgehender (SMTP) Mail-Server", + "IMAPSetupDialog_OutgoingMailServerPassword": "Ausgehendes Server-Passwort", + "IMAPSetupDialog_OutgoingMailServerPort": "Port", + "IMAPSetupDialog_OutgoingMailServerRequireAuthentication": "Ausgehender Server erfordert Authentifizierung", + "IMAPSetupDialog_OutgoingMailServerUsername": "Benutzername für ausgehenden Server", + "IMAPSetupDialog_Password": "Passwort", + "IMAPSetupDialog_RequireSSLForIncomingMail": "SSL für eingehende E-Mails erforderlich machen", + "IMAPSetupDialog_RequireSSLForOutgoingMail": "SSL für ausgehende E-Mails erforderlich machen", + "IMAPSetupDialog_Title": "Erweiterte IMAP-Konfiguration", + "IMAPSetupDialog_UseSameConfig": "Den gleichen Benutzernamen und das gleiche Passwort für das Senden von E-Mails verwenden", + "IMAPSetupDialog_Username": "Benutzername", + "IMAPSetupDialog_UsernamePlaceholder": "johndoe, johndoe@fabrikam.com, domain/johndoe", + "ImageRenderingDisabled": "Bilddarstellung ist für diese Nachricht deaktiviert.", + "InfoBarAction_Enable": "Aktivieren", + "InfoBarMessage_SynchronizationDisabledFolder": "Dieser Ordner wird nicht synchronisiert.", + "InfoBarTitle_SynchronizationDisabledFolder": "Deaktivierter Ordner", + "GeneralTitle_Error": "Error", + "GeneralTitle_Warning": "Warning", + "GeneralTitle_Info": "Information", + "Info_AccountCreatedMessage": "{0} wurde erstellt", + "Info_AccountCreatedTitle": "Konto-Erstellung", + "Info_AccountCreationFailedTitle": "Konto-Erstellung fehlgeschlagen", + "Info_AccountDeletedMessage": "{0} wurde erfolgreich gelöscht.", + "Info_AccountDeletedTitle": "Konto gelöscht", + "Info_AccountIssueFixFailedTitle": "Fehlschlagen", + "Info_AccountIssueFixSuccessMessage": "Alle Konto-Probleme wurden behoben.", + "Info_AccountIssueFixSuccessTitle": "Erfolg", + "Info_AttachmentOpenFailedMessage": "Anhang kann nicht geöffnet werden.", + "Info_AttachmentOpenFailedTitle": "Fehlschlagen", + "Info_AttachmentSaveFailedMessage": "Anhang kann nicht gespeichert werden.", + "Info_AttachmentSaveFailedTitle": "Fehlschlagen", + "Info_AttachmentSaveSuccessMessage": "Anhang wird gespeichert.", + "Info_AttachmentSaveSuccessTitle": "Anhang gespeichert", + "Info_BackgroundExecutionDeniedMessage": "Hintergrundausführung der App wird verweigert. Dies kann sich auf die Hintergrundsynchronisierung und Live-Benachrichtigungen auswirken.", + "Info_BackgroundExecutionDeniedTitle": "Hintergrundausführung verweigert", + "Info_BackgroundExecutionUnknownErrorMessage": "Unbekannter Fehler beim Registrieren des Hintergrundsynchronisators.", + "Info_BackgroundExecutionUnknownErrorTitle": "Fehler bei der Hintergrundausführung", + "Info_ComposerMissingMIMEMessage": "Die MIME-Datei konnte nicht gefunden werden. Synchronisierung kann helfen.", + "Info_ComposerMissingMIMETitle": "Fehlschlagen", + "Info_ContactExistsMessage": "Dieser Kontakt ist bereits in der Empfängerliste.", + "Info_ContactExistsTitle": "Kontakt existiert", + "Info_DraftFolderMissingMessage": "Entwürfe-Ordner fehlt für dieses Konto. Bitte überprüfen Sie Ihre Konto-Einstellungen.", + "Info_DraftFolderMissingTitle": "Entwürfe-Ordner fehlt", + "Info_FileLaunchFailedTitle": "Fehler beim Starten der Datei", + "Info_InvalidAddressMessage": "'{0}' ist keine gültige E-Mail-Adresse.", + "Info_InvalidAddressTitle": "Ungültige Adresse", + "Info_InvalidMoveTargetMessage": "Sie können die ausgewählten Mails nicht in diesen Ordner verschieben.", + "Info_InvalidMoveTargetTitle": "Ungültiges Verschiebungsziel", + "Info_LogsNotFoundMessage": "Keine Logs zum Teilen vorhanden.", + "Info_LogsNotFoundTitle": "Logs nicht gefunden", + "Info_LogsSavedMessage": "{0} wird im ausgewählten Ordner gespeichert.", + "Info_LogsSavedTitle": "Gespeichert", + "Info_MailRenderingFailedMessage": "Diese Mail ist beschädigt oder kann nicht geöffnet werden.\n{0}", + "Info_MailRenderingFailedTitle": "Darstellen fehlgeschlagen", + "Info_MessageCorruptedMessage": "Diese Nachricht ist beschädigt.", + "Info_MessageCorruptedTitle": "Fehler", + "Info_MissingFolderMessage": "{0} existiert für dieses Konto nicht.", + "Info_MissingFolderTitle": "Fehlender Ordner", + "Info_PurchaseExistsMessage": "Es sieht so aus, als ob dieses Produkt bereits gekauft wurde.", + "Info_PurchaseExistsTitle": "Vorhandenes Produkt", + "Info_PurchaseThankYouMessage": "Vielen Dank", + "Info_PurchaseThankYouTitle": "Kauf abgeschlossen", + "Info_RequestCreationFailedTitle": "Fehler beim Erstellen von Anfragen", + "Info_ReviewNetworkErrorMessage": "Es gab ein Netzwerkproblem bei Ihrer Bewertung.", + "Info_ReviewNetworkErrorTitle": "Netzwerkfehler", + "Info_ReviewNewMessage": "Alle Rückmeldungen werden geschätzt. Vielen Dank für die Bewertung!", + "Info_ReviewSuccessTitle": "Vielen Dank", + "Info_ReviewUnknownErrorMessage": "Es gab ein unbekanntes Problem mit Ihrer Bewertung. ({0})", + "Info_ReviewUnknownErrorTitle": "Unbekannter Fehler", + "Info_ReviewUpdatedMessage": "Vielen Dank für die aktualisierte Bewertung.", + "Info_SignatureDisabledMessage": "Signatur für dieses Konto deaktiviert", + "Info_SignatureDisabledTitle": "Erfolg", + "Info_SignatureSavedMessage": "Neue Signatur gespeichert", + "Info_SignatureSavedTitle": "Erfolg", + "Info_SyncCanceledMessage": "Abgebrochen", + "Info_SyncCanceledTitle": "Synchronisierung", + "Info_SyncFailedTitle": "Synchronisierung fehlgeschlagen", + "Info_UnsupportedFunctionalityDescription": "Diese Funktionalität ist noch nicht implementiert.", + "Info_UnsupportedFunctionalityTitle": "Nicht unterstützt", + "Info_UnsubscribeLinkInvalidTitle": "Ungültige Abmelde-Uri", + "Info_UnsubscribeLinkInvalidMessage": "Dieser Abmeldelink ist ungültig. Fehler beim Abmelden der Liste.", + "ImapAdvancedSetupDialog_AuthenticationMethod": "Authentifizierungsmethode", + "ImapAdvancedSetupDialog_ConnectionSecurity": "Verbindungssicherheit", + "ImapAuthenticationMethod_Auto": "Auto", + "ImapAuthenticationMethod_CramMD5": "CRAM-MD5", + "ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5", + "ImapAuthenticationMethod_None": "Keine Authentifizierung", + "ImapAuthenticationMethod_Plain": "Normales Passwort", + "ImapAuthenticationMethod_EncryptedPassword": "Verschlüsseltes Passwort", + "ImapAuthenticationMethod_Ntlm": "NTLM", + "ImapConnectionSecurity_None": "Keine", + "ImapConnectionSecurity_SslTls": "SSL/TLS", + "ImapConnectionSecurity_StartTls": "STARTTLS", + "ImapConnectionSecurity_Auto": "Auto", + "Justify": "Block", + "Left": "Links", + "Link": "Link", + "LinkedAccountsCreatePolicyMessage": "Sie müssen mindestens 2 Konten haben, um Link zu erstellen\nLink wird beim Speichern entfernt", + "LinkedAccountsTitle": "Verknüpfte Konten", + "MailOperation_AlwaysMoveFocused": "Immer in Fokussiert", + "MailOperation_AlwaysMoveOther": "Immer in Andere verschieben", + "MailOperation_Archive": "Archiv", + "MailOperation_ClearFlag": "Markierung aufheben", + "MailOperation_DarkEditor": "Dunkel", + "MailOperation_Delete": "Löschen", + "MailOperation_ExportPDF": "Als PDF exportieren", + "MailOperation_Find": "Suchen", + "MailOperation_Forward": "Weiterleiten", + "MailOperation_Ignore": "Ignorieren", + "MailOperation_LightEditor": "Hell", + "MailOperation_MarkAsJunk": "Als Spam markieren", + "MailOperation_MarkAsRead": "Als gelesen markieren", + "MailOperation_MarkAsUnread": "Als ungelesen markieren", + "MailOperation_MarkNotJunk": "Nicht mehr als Spam markieren", + "MailOperation_Move": "Verschieben", + "MailOperation_MoveFocused": "Nach Fokussiert verschieben", + "MailOperation_MoveJunk": "In den Spam verschieben", + "MailOperation_MoveOther": "In Andere verschieben", + "MailOperation_Navigate": "Navigieren", + "MailOperation_Print": "Drucken", + "MailOperation_Reply": "Antworten", + "MailOperation_ReplyAll": "Allen antworten", + "MailOperation_SaveAs": "Speichern als", + "MailOperation_SetFlag": "Markierung setzen", + "MailOperation_Unarchive": "Archivierung aufheben", + "MailOperation_Zoom": "Zoom", + "MailsSelected": "{0} Element(e) ausgewählt", + "MarkFlagUnflag": "Als markiert/nicht markiert setzen", + "MarkReadUnread": "Als gelesen/ungelesen markieren", + "MenuManageAccounts": "Konten verwalten", + "MenuNewMail": "Neue Mail", + "MenuMergedAccountItemAccountsSuffix": " Konten", + "MenuRate": "Wino bewerten", + "MenuSettings": "Einstellungen", + "MergedAccountsAvailableAccountsTitle": "Verfügbare Konten", + "More": "Mehr", + "MoveMailDialog_InvalidFolderMessage": "{0} ist kein gültiger Ordner für diese Mail.", + "MoveMailDialog_Title": "Ordner wählen", + "NewAccountDialog_AccountName": "Konto-Name", + "NewAccountDialog_AccountNameDefaultValue": "Persönlich", + "NewAccountDialog_AccountNamePlaceholder": "z.B. Persönliche Mails", + "NewAccountDialog_Title": "Neues Konto hinzufügen", + "NoMailSelected": "keine Nachricht ausgewählt", + "NoMessageCrieteria": "Keine Nachrichten entsprechen Ihren Suchkriterien.", + "NoMessageEmptyFolder": "Dieser Ordner ist leer.", + "Notifications_MultipleNotificationsMessage": "Sie haben {0} neue Mails", + "Notifications_MultipleNotificationsTitle": "Neue Mails", + "Notifications_WinoUpdatedMessage": "Neue Version {0} herunterladen", + "Notifications_WinoUpdatedTitle": "Wino Mail wurde aktualisiert.", + "Other": "Andere", + "PaneLengthOption_Default": "Standard", + "PaneLengthOption_ExtraLarge": "Extra Groß", + "PaneLengthOption_Large": "Groß", + "PaneLengthOption_Medium": "Mittel", + "PaneLengthOption_Micro": "Mikro", + "PaneLengthOption_Small": "Klein", + "Photos": "Fotos", + "PreparingFoldersMessage": "Ordnervorbereitung", + "ProviderDetail_Gmail_Description": "Google-Konto", + "ProviderDetail_IMAP_Description": "Eigener IMAP/SMTP Server", + "ProviderDetail_IMAP_Title": "IMAP-Server", + "Results": "Ergebnisse", + "Right": "Rechts", + "SynchronizationFolderReport_Success": "aktuell", + "SynchronizationFolderReport_Failed": "Synchronisierung fehlgeschlagen", + "SearchBarPlaceholder": "Suche", + "SearchingIn": "suche in", + "SettingsAboutGithub_Description": "Gehen Sie zum Problem-Tracker GitHub-Repository.", + "SettingsAboutGithub_Title": "GitHub", + "SettingsAccountManagementAppendMessage_Title": "Nachrichten an Gesendete Ordner anhängen", + "SettingsAccountManagementAppendMessage_Description": "Erstellen Sie eine Kopie der Nachricht im Gesendete Ordner, nachdem der Entwurf gesendet wurde. Aktivieren Sie dies, wenn Sie Ihre Mails nicht sehen, nachdem Sie in den Ordner Gesendete verschoben wurden.", + "SettingsEditLinkedInbox_Title": "Verlinkten Posteingang bearbeiten", + "SettingsEditLinkedInbox_Description": "Konten hinzufügen/entfernen, umbenennen oder die Verbindung zwischen Konten unterbrechen.", + "SettingsAboutVersion": "Version ", + "SettingsAboutWinoDescription": "Leichte Mail-Anwendung für Windows-Geräte.", + "SettingsAbout_Description": "Erfahren Sie mehr über Wino.", + "SettingsAbout_Title": "Über", + "SettingsAccentColor_Description": "Akzentfarbe der Anwendung ändern", + "SettingsAccentColor_Title": "Akzentfarbe", + "SettingsAccentColor_UseWindowsAccentColor": "Windows-Akzentfarbe benutzen", + "SettingsAccountName_Description": "Name des Kontos ändern.", + "SettingsAccountName_Title": "Konto-Name", + "SettingsApplicationTheme_Description": "Passen Sie Wino mit verschiedenen benutzerdefinierten Anwendungs-Themen Ihrem Geschmack an.", + "SettingsApplicationTheme_Title": "Anwendungs-Thema", + "SettingsAvailableThemes_Description": "Wählen Sie ein Thema aus der Sammlung von Wino oder verwenden Sie Ihre eigenen Themen.", + "SettingsAvailableThemes_Title": "Verfügbare Themen", + "SettingsCustomTheme_Description": "Erstellen Sie Ihr eigenes Thema mit individuellem Hintergrundbild und Akzentfarbe.", + "SettingsCustomTheme_Title": "Eigenes Thema", + "SettingsDeleteAccount_Description": "Alle E-Mails und Anmeldeinformationen dieses Kontos löschen.", + "SettingsDeleteAccount_Title": "Dieses Konto löschen", + "SettingsDeleteProtection_Description": "Sollte Wino jedes Mal nachfragen, wenn Sie eine Mail mit Umschalten + Entfernen permanent löschen möchten?", + "SettingsDeleteProtection_Title": "Schutz vor permanenter Löschung", + "SettingsDiagnostics_Description": "Für Entwickler", + "SettingsDiagnostics_Title": "Diagnostik", + "SettingsDiscord_Description": "Bekommen Sie reguläre Entwicklungs-Updates, treten Sie Zeitplan-Diskussionen bei und geben Sie Feedback.", + "SettingsDiscord_Title": "Discord Kanal", + "SettingsElementThemeSelectionDisabled": "Auswahl des Element-Themas ist deaktiviert, wenn das Anwendungs-Thema nicht als Standard festgelegt ist.", + "SettingsElementTheme_Description": "Wählen Sie ein Windows-Thema für Wino", + "SettingsElementTheme_Title": "Element-Thema", + "SettingsEnableHoverActions_Title": "Hover-Aktionen aktivieren", + "SettingsEnableIMAPLogs_Description": "Aktivieren Sie dies, um Probleme bei der IMAP-Konnektivität zu übermitteln, die Sie während der Einrichtung des IMAP-Servers hatten.", + "SettingsEnableIMAPLogs_Title": "IMAP-Protokoll-Logs aktivieren", + "SettingsEnableLogs_Description": "Ich benötige möglicherweise Logs über Abstürze, um Probleme zu diagnostizieren, die du auf GitHub gemeldet hast. Keiner der Logs wird Ihre Zugangsdaten oder sensiblen Informationen öffentlich zugänglich machen.", + "SettingsEnableLogs_Title": "Logs aktivieren", + "SettingsEnableSignature": "Signatur aktivieren", + "SettingsExpandOnStartup_Description": "Legen Sie fest, ob Wino die Ordner dieses Kontos beim Start erweitern soll.", + "SettingsExpandOnStartup_Title": "Menü beim Start erweitern", + "SettingsExternalContent_Description": "Stellen Sie die Darstellung externer Inhalte innerhalb von E-Mails ein.", + "SettingsExternalContent_Title": "Externe Inhalte", + "SettingsFocusedInbox_Description": "Legen Sie fest, ob der Posteingang in Fokussiert - Andere aufgeteilt werden soll.", + "SettingsFocusedInbox_Title": "Fokussierter Posteingang", + "SettingsFolderSync_Description": "Bestimmte Ordner für die Synchronisierung aktivieren oder deaktivieren.", + "SettingsFolderSync_Title": "Ordner-Synchronisierung", + "SettingsFolderOptions_Title": "Ordnerkonfiguration", + "SettingsFolderOptions_Description": "Ändern Sie einzelne Ordnereinstellungen, wie z. B. das Ein-/Ausblenden des \"Ungelesen\"-Symbols oder das (De-)Aktivieren der Synchronisierung.", + "SettingsHoverActionCenter": "Zentrierte Aktion", + "SettingsHoverActionLeft": "Linke Aktion", + "SettingsHoverActionRight": "Rechte Aktion", + "SettingsHoverActions_Description": "Wählen Sie 3 Aktionen aus, die angezeigt werden sollen, wenn Sie mit dem Cursor über Mails hovern.", + "SettingsHoverActions_Title": "Hover-Aktionen", + "SettingsLanguage_Description": "Anzeigesprache für Wino ändern.", + "SettingsLanguage_Title": "Anzeigesprache", + "CategoriesFolderNameOverride": "Kategorien", + "MoreFolderNameOverride": "Mehr", + "SettingsOptions_Title": "Einstellungen", + "SettingsLinkAccounts_Description": "Mehrere Konten zu einem zusammenführen. Sehen Sie E-Mails in einem kombinierten Posteingang.", + "SettingsLinkAccounts_Title": "Verknüpftes Konto erstellen", + "SettingsLinkedAccountsSave_Description": "Ändern Sie den aktuellen Link mit den neuen Konten.", + "SettingsLinkedAccountsSave_Title": "Änderungen speichern", + "SettingsLoadImages_Title": "Bilder automatisch laden", + "SettingsLoadStyles_Title": "Stile automatisch laden", + "SettingsMailSpacing_Description": "Den Abstand für die Auflistung von Mails anpassen.", + "SettingsMailSpacing_Title": "Mail-Abstand", + "SettingsFolderMenuStyle_Title": "Verschachtelte Ordner erstellen", + "SettingsFolderMenuStyle_Description": "Ändern Sie, ob Kontoordner in einem Konten-Menüpunkt verschachtelt werden sollen oder nicht. Schalten Sie dies aus, wenn Sie das alte Menüsystem in Windows Mail mögen.", + "SettingsManageAccountSettings_Description": "Benachrichtigungen, Signaturen, Synchronisierung und andere Einstellungen pro Konto.", + "SettingsManageAccountSettings_Title": "Kontoeinstellungen verwalten", + "SettingsManageLink_Description": "Elemente verschieben, um neuen Link hinzuzufügen oder bestehenden Link zu entfernen.", + "SettingsManageLink_Title": "Link verwalten", + "SettingsMarkAsRead_Description": "Ändern Sie, was mit dem ausgewählten Element passieren soll.", + "SettingsMarkAsRead_DontChange": "Element nicht automatisch als gelesen markieren", + "SettingsMarkAsRead_SecondsToWait": "Wartezeit in Sekunden: ", + "SettingsMarkAsRead_Timer": "Beim Betrachten im Lesefenster", + "SettingsMarkAsRead_Title": "Element als gelesen markieren", + "SettingsMarkAsRead_WhenSelected": "Wenn ausgewählt", + "SettingsMessageList_Description": "Ändern Sie die Organisation Ihrer Nachrichten in der Mailliste.", + "SettingsMessageList_Title": "Nachrichten-Liste", + "SettingsNoAccountSetupMessage": "Sie haben noch keine Konten eingerichtet.", + "SettingsNotifications_Description": "Benachrichtigungen für dieses Konto ein- oder ausschalten.", + "SettingsNotifications_Title": "Benachrichtigungen", + "SettingsPaneLength_Description": "Ändern Sie die Breite der Mail-Liste.", + "SettingsPaneLength_Title": "Länge des Maillisten-Fensters", + "SettingsPaypal_Description": "Zeigen Sie viel mehr Liebe ❤️ Alle Spenden werden wertgeschätzt.", + "SettingsPaypal_Title": "Über PayPal spenden", + "SettingsPersonalizationMailDisplayCompactMode": "Kompakter Modus", + "SettingsPersonalizationMailDisplayMediumMode": "Mittlerer Modus", + "SettingsPersonalizationMailDisplaySpaciousMode": "Geräumiger Modus", + "SettingsPersonalization_Description": "Ändern Sie das Aussehen von Wino nach Ihren Belieben.", + "SettingsPersonalization_Title": "Personalisierung", + "SettingsPrivacyPolicy_Description": "Datenschutzrichtlinie ansehen.", + "SettingsPrivacyPolicy_Title": "Datenschutzerklärung", + "SettingsReadingPane_Description": "Mail-Darstellungs-Optionen.", + "SettingsReadingPane_Title": "Lesefenster", + "SettingsReaderFont_Title": "Standard Lese-Schriftart", + "SettingsReaderFontFamily_Description": "Ändern Sie die Standardschriftart und Schriftgröße für das Lesen von Mails.", + "SettingsFontFamily_Title": "Schriftart", + "SettingsFontSize_Title": "Schriftgröße", + "SettingsFontPreview_Title": "Vorschau", + "SettingsComposerFont_Title": "Standard Verfasser-Schriftart", + "SettingsComposerFontFamily_Description": "Ändern Sie die Standardschriftart und Schriftgröße für das Verfassen von Mails.", + "SettingsRenameMergeAccount_Description": "Den Anzeigenamen der verknüpften Konten ändern.", + "SettingsRenameMergeAccount_Title": "Umbenennen", + "SettingsSemanticZoom_Description": "Dadurch können Sie auf die Kopfzeilen in der Nachrichtenliste klicken und zu einem bestimmten Datum springen", + "SettingsSemanticZoom_Title": "Semantischer Zoom für Datumsüberschriften", + "SettingsShowPreviewText_Description": "Vorschautext ausblenden/anzeigen.", + "SettingsShowPreviewText_Title": "Vorschautext anzeigen", + "SettingsShowSenderPictures_Description": "Absender-Profilbilder ausblenden/anzeigen.", + "SettingsShowSenderPictures_Title": "Absender-Profilbilder anzeigen", + "SettingsPrefer24HourClock_Title": "Uhr-Format in 24 Stunden anzeigen", + "SettingsPrefer24HourClock_Description": "Empfangszeiten für Mails werden im 24-Stunden-Format statt 12 (AM/PM) angezeigt", + "SettingsSignature_Description": "Kontosignatur bearbeiten oder entfernen", + "SettingsSignature_Title": "Signatur", + "SettingsStartupItem_Description": "Primäres Konto zum Laden des Posteingangs beim Starten.", + "SettingsStartupItem_Title": "Startelement", + "SettingsStore_Description": "Zeigen Sie etwas Liebe ❤️", + "SettingsStore_Title": "Im Store bewerten", + "SettingsThreads_Description": "Nachrichten in Unterhaltungsthreads organisieren.", + "SettingsThreads_Title": "Unterhaltungsthreading", + "SettingsUnlinkAccounts_Description": "Entfernen Sie den Link zwischen den Konten. Dies wird Ihre Konten nicht löschen.", + "SettingsUnlinkAccounts_Title": "Konten trennen", + "SortingOption_Date": "nach Datum", + "SortingOption_Name": "nach Name", + "StoreRatingDialog_MessageFirstLine": "Jedes Feedback ist willkommen und wird Wino besser machen. Möchten Sie Wino im Microsoft Store bewerten?", + "StoreRatingDialog_MessageSecondLine": "Möchten Sie Wino Mail im Microsoft Store bewerten?", + "StoreRatingDialog_Title": "Gefällt Ihnen Wino?", + "SystemFolderConfigDialog_ArchiveFolderDescription": "Archivierte Nachrichten werden hierher verschoben.", + "SystemFolderConfigDialog_ArchiveFolderHeader": "Archivordner", + "SystemFolderConfigDialog_DeletedFolderDescription": "Gelöschte Nachrichten werden hierher verschoben.", + "SystemFolderConfigDialog_DeletedFolderHeader": "Gelöscht-Ordner", + "SystemFolderConfigDialog_DraftFolderDescription": "Neue Nachrichten/Antworten werden hier erstellt.", + "SystemFolderConfigDialog_DraftFolderHeader": "Entwürfe-Ordner", + "SystemFolderConfigDialog_JunkFolderDescription": "Alle Spam-Mails werden hier sein.", + "SystemFolderConfigDialog_JunkFolderHeader": "Müll-/Spam-Ordner", + "SystemFolderConfigDialog_MessageFirstLine": "Dieser IMAP-Server unterstützt keine SPECIAL-USE-Erweiterung, deshalb konnte Wino die Systemordner nicht richtig einrichten.", + "SystemFolderConfigDialog_MessageSecondLine": "Bitte wählen Sie die passenden Ordner für bestimmte Funktionalitäten.", + "SystemFolderConfigDialog_SentFolderDescription": "Ordner, in dem gesendete Nachrichten gespeichert werden sollen.", + "SystemFolderConfigDialog_SentFolderHeader": "Gesendete-Ordner", + "SystemFolderConfigDialog_Title": "Systemordner konfigurieren", + "TestingImapConnectionMessage": "Serververbindung wird getestet...", + "Today": "Heute", + "UnknownAddress": "unbekannte Adresse", + "UnknownDateHeader": "Unbekanntes Datum", + "UnknownGroupAddress": "unbekannte Mail-Gruppenadresse", + "UnknownSender": "Unbekannter Absender", + "Unsubscribe": "Abbestellen", + "ViewContactDetails": "Details anzeigen", + "WinoUpgradeDescription": "Wino bietet 3 Accounts zum kostenlosen Start an. Wenn Sie mehr als 3 Accounts benötigen, upgraden Sie bitte.", + "WinoUpgradeMessage": "Auf unbegrenzte Konten upgraden", + "WinoUpgradeRemainingAccountsMessage": "{0} von {1} kostenlosen Konten verwendet.", + "Yesterday": "Gestern" +} diff --git a/Wino.Core.Domain/Translations/en_US/resources.json b/Wino.Core.Domain/Translations/en_US/resources.json new file mode 100644 index 00000000..01b67ae0 --- /dev/null +++ b/Wino.Core.Domain/Translations/en_US/resources.json @@ -0,0 +1,482 @@ +{ + "AccountCreationDialog_Completed": "all done", + "AccountCreationDialog_Initializing": "initializing", + "AccountCreationDialog_PreparingFolders": "We are getting folder information at the moment.", + "AccountCreationDialog_SigninIn": "Account information is being saved.", + "AccountEditDialog_Message": "Account Name", + "AccountEditDialog_Title": "Edit Account", + "AccountPickerDialog_Title": "Pick an account", + "AddHyperlink": "Add", + "AutoDiscoveryProgressMessage": "Searching for mail settings...", + "BasicIMAPSetupDialog_AdvancedConfiguration": "Advanced Configuration", + "BasicIMAPSetupDialog_CredentialLocalMessage": "Your credentials will only be stored locally on your computer.", + "BasicIMAPSetupDialog_Description": "Some accounts require additional steps to sign in", + "BasicIMAPSetupDialog_DisplayName": "Display Name", + "BasicIMAPSetupDialog_DisplayNamePlaceholder": "eg. John Doe", + "BasicIMAPSetupDialog_LearnMore": "Learn more", + "BasicIMAPSetupDialog_MailAddress": "E-Mail Address", + "BasicIMAPSetupDialog_MailAddressPlaceholder": "johndoe@fabrikam.com", + "BasicIMAPSetupDialog_Password": "Password", + "BasicIMAPSetupDialog_Title": "IMAP Account", + "Buttons_AddAccount": "Add Account", + "Buttons_ApplyTheme": "Apply Theme", + "Buttons_Browse": "Browse", + "Buttons_Cancel": "Cancel", + "Buttons_Close": "Close", + "Buttons_Create": "Create", + "Buttons_CreateAccount": "Create Account", + "Buttons_Delete": "Delete", + "Buttons_Discard": "Discard", + "Buttons_EnableImageRendering": "Enable", + "Buttons_No": "No", + "Buttons_Open": "Open", + "Buttons_Purchase": "Purchase", + "Buttons_RateWino": "Rate Wino", + "Buttons_Save": "Save", + "Buttons_SaveConfiguration": "Save Configuration", + "Buttons_Share": "Share", + "Buttons_SignIn": "Sign In", + "Buttons_Yes": "Yes", + "Center": "Center", + "ComingSoon": "Coming soon...", + "ComposerFrom": "From: ", + "ComposerSubject": "Subject: ", + "ComposerTo": "To: ", + "ClipboardTextCopied_Message": "{0} copied to clipboard.", + "ClipboardTextCopied_Title": "Copied", + "ClipboardTextCopyFailed_Message": "Failed to copy {0} to clipboard.", + "ComposerToPlaceholder": "click enter to input addresses", + "CustomThemeBuilder_AccentColorDescription": "Set custom accent color if you wish. Not selecting a color will use your Windows accent color.", + "CustomThemeBuilder_AccentColorTitle": "Accent color", + "CustomThemeBuilder_PickColor": "Pick", + "CustomThemeBuilder_ThemeNameDescription": "Unique name for your custom theme.", + "CustomThemeBuilder_ThemeNameTitle": "Theme name", + "CustomThemeBuilder_Title": "Custom Theme Builder", + "CustomThemeBuilder_WallpaperDescription": "Set a custom wallpaper for Wino", + "CustomThemeBuilder_WallpaperTitle": "Set custom wallpaper", + "DialogMessage_AccountLimitMessage": "You have reached the account creation limit.\nWould you like to purchase 'Unlimited Account' add-on to continue?", + "DialogMessage_AccountLimitTitle": "Account Limit Reached", + "DialogMessage_CleanupFolderMessage": "Do you want to permanently delete all the mails in this folder?", + "DialogMessage_CleanupFolderTitle": "Cleanup Folder", + "DialogMessage_ComposerMissingRecipientMessage": "Message has no recipient.", + "DialogMessage_ComposerValidationFailedTitle": "Validation Failed", + "DialogMessage_CreateLinkedAccountMessage": "Give this new link a name. Accounts will be merged under this name.", + "DialogMessage_CreateLinkedAccountTitle": "Account Link Name", + "DialogMessage_DeleteAccountConfirmationMessage": "Delete {0}?", + "DialogMessage_DeleteAccountConfirmationTitle": "All data associated with this account will be deleted from disk permanently.", + "DialogMessage_DiscardDraftConfirmationMessage": "This draft will be discarded. Do you want to continue?", + "DialogMessage_DiscardDraftConfirmationTitle": "Discard Draft", + "DialogMessage_HardDeleteConfirmationMessage": "Permanent Delete", + "DialogMessage_HardDeleteConfirmationTitle": "Message(s) will be permanently deleted. Do you want to continue?", + "DialogMessage_NoAccountsForCreateMailMessage": "You don't have any accounts to create message from.", + "DialogMessage_NoAccountsForCreateMailTitle": "Account Missing", + "DialogMessage_RenameLinkedAccountsMessage": "Enter new name for linked account", + "DialogMessage_RenameLinkedAccountsTitle": "Rename Linked Account", + "DialogMessage_UnlinkAccountsConfirmationMessage": "This operation will not delete your accounts but only break the link for shared folder connections. Do you want to continue?", + "DialogMessage_UnlinkAccountsConfirmationTitle": "Unlink Accounts", + "DialogMessage_EmptySubjectConfirmation": "Missin Subject", + "DialogMessage_EmptySubjectConfirmationMessage": "Message has no subject. Do you want to continue?", + "Dialog_DontAskAgain": "Don't ask again", + "DiscordChannelDisclaimerMessage": "Wino doesn't have it's own Discord server, but special 'wino-mail' channel is hosted at 'Developer Sanctuary' server.\nTo get the updates about Wino please join Developer Sanctuary server and follow 'wino-mail' channel under 'Community Projects'\n\nYou will be directed to server URL since Discord doesn't support channel invites.", + "DiscordChannelDisclaimerTitle": "Important Discord Information", + "Draft": "Draft", + "EditorToolbarOption_Draw": "Draw", + "EditorToolbarOption_Format": "Format", + "EditorToolbarOption_Insert": "Insert", + "EditorToolbarOption_None": "None", + "EditorToolbarOption_Options": "Options", + "ElementTheme_Dark": "Dark mode", + "ElementTheme_Default": "Use system setting", + "ElementTheme_Light": "Light mode", + "Emoji": "Emoji", + "Exception_ImapClientPoolFailed": "IMAP Client Pool failed.", + "Exception_AuthenticationCanceled": "Authentication canceled", + "Exception_CustomThemeExists": "This theme already exists.", + "Exception_CustomThemeMissingName": "You must provide a name.", + "Exception_CustomThemeMissingWallpaper": "You must provide a custom background image.", + "Exception_FailedToSynchronizeFolders": "Failed to synchronize folders", + "Exception_GoogleAuthCallbackNull": "Callback uri is null on activation.", + "Exception_GoogleAuthCorruptedCode": "Corrupted authorization response.", + "Exception_GoogleAuthError": "OAuth authorization error: {0}", + "Exception_GoogleAuthInvalidResponse": "Received request with invalid state ({0})", + "Exception_GoogleAuthorizationCodeExchangeFailed": "Authorization code exchange failed.", + "Exception_InvalidSystemFolderConfiguration": "System folder configuration is not valid. Check configuration and try again.", + "Exception_NullAssignedAccount": "Assigned account is null", + "Exception_NullAssignedFolder": "Assigned folder is null", + "Exception_SynchronizerFailureHTTP": "Response handling failed with error HTTP code {0}", + "Exception_TokenGenerationFailed": "Token generation failed", + "Exception_TokenInfoRetrivalFailed": "Failed to get token information.", + "Exception_UnknowErrorDuringAuthentication": "Unknown error occurred during authentication", + "Exception_UnsupportedAction": "Action {0} is not implemented in request processor", + "Exception_UnsupportedProvider": "This provider is not supported.", + "Exception_UnsupportedSynchronizerOperation": "This operation is not supported for {0}", + "Exception_UserCancelSystemFolderSetupDialog": "User canceled system folder config dialog.", + "Exception_InboxNotAvailable": "Couldn't setup account folders.", + "Files": "Files", + "FilteringOption_All": "All", + "FilteringOption_Flagged": "Flagged", + "FilteringOption_Unread": "Unread", + "Focused": "Focused", + "FolderOperation_CreateSubFolder": "Create sub folder", + "FolderOperation_Delete": "Delete", + "FolderOperation_DontSync": "Don't sync this folder", + "FolderOperation_Empty": "Empty this folder", + "FolderOperation_MarkAllAsRead": "Mark all as read", + "FolderOperation_Move": "Move", + "DragMoveToFolderCaption": "Move to {0}", + "FolderOperation_None": "None", + "FolderOperation_Pin": "Pin", + "FolderOperation_Rename": "Rename", + "FolderOperation_Unpin": "Unpin", + "HoverActionOption_Archive": "Archive", + "HoverActionOption_Delete": "Delete", + "HoverActionOption_MoveJunk": "Move to Junk", + "HoverActionOption_ToggleFlag": "Flag / Unflag", + "HoverActionOption_ToggleRead": "Read / Unread", + "MergedAccountCommonFolderInbox": "Inbox", + "MergedAccountCommonFolderSent": "Sent", + "MergedAccountCommonFolderDraft": "Draft", + "MergedAccountCommonFolderJunk": "Junk", + "MergedAccountCommonFolderTrash": "Deleted", + "MergedAccountCommonFolderArchive": "Archive", + "IMAPSetupDialog_AccountType": "Account type", + "IMAPSetupDialog_DisplayName": "Display Name", + "IMAPSetupDialog_DisplayNamePlaceholder": "eg. John Doe", + "IMAPSetupDialog_IncomingMailServer": "Incoming mail server", + "IMAPSetupDialog_IncomingMailServerPort": "Port", + "IMAPSetupDialog_MailAddress": "Email address", + "IMAPSetupDialog_MailAddressPlaceholder": "someone@example.com", + "IMAPSetupDialog_OutgoingMailServer": "Outgoing (SMTP) mail server", + "IMAPSetupDialog_OutgoingMailServerPassword": "Outgoing server password", + "IMAPSetupDialog_OutgoingMailServerPort": "Port", + "IMAPSetupDialog_OutgoingMailServerRequireAuthentication": "Outgoing server requires authentication", + "IMAPSetupDialog_OutgoingMailServerUsername": "Outgoing server user name", + "IMAPSetupDialog_Password": "Password", + "IMAPSetupDialog_RequireSSLForIncomingMail": "Require SSL for incoming email", + "IMAPSetupDialog_RequireSSLForOutgoingMail": "Require SSL for outgoing email", + "IMAPSetupDialog_Title": "Advanced IMAP Configuration", + "IMAPSetupDialog_UseSameConfig": "Use the same username and password for sending email", + "IMAPSetupDialog_Username": "Username", + "IMAPSetupDialog_UsernamePlaceholder": "johndoe, johndoe@fabrikam.com, domain/johndoe", + "ImageRenderingDisabled": "Image rendering is disabled for this message.", + "InfoBarAction_Enable": "Enable", + "InfoBarMessage_SynchronizationDisabledFolder": "This folder is disabled for synchronization.", + "InfoBarTitle_SynchronizationDisabledFolder": "Disabled Folder", + "GeneralTitle_Error": "Error", + "GeneralTitle_Warning": "Warning", + "GeneralTitle_Info": "Information", + "Info_AccountCreatedMessage": "{0} is created", + "Info_AccountCreatedTitle": "Account Creation", + "Info_AccountCreationFailedTitle": "Account Creation Failed", + "Info_AccountDeletedMessage": "{0} is successfuly deleted.", + "Info_AccountDeletedTitle": "Account Deleted", + "Info_AccountIssueFixFailedTitle": "Failed", + "Info_AccountIssueFixSuccessMessage": "Fixed all account issues.", + "Info_AccountIssueFixSuccessTitle": "Success", + "Info_AttachmentOpenFailedMessage": "Can't open this attachment.", + "Info_AttachmentOpenFailedTitle": "Failed", + "Info_AttachmentSaveFailedMessage": "Can't save this attachment.", + "Info_AttachmentSaveFailedTitle": "Failed", + "Info_AttachmentSaveSuccessMessage": "Attachment is saved.", + "Info_AttachmentSaveSuccessTitle": "Attachment Saved", + "Info_BackgroundExecutionDeniedMessage": "Background execution for the app is denied. This may affect background synchronization and live notifications.", + "Info_BackgroundExecutionDeniedTitle": "Denied Background Execution", + "Info_BackgroundExecutionUnknownErrorMessage": "Unknown exception occurred when registering background synchronizer.", + "Info_BackgroundExecutionUnknownErrorTitle": "Background Execution Failure", + "Info_ComposerMissingMIMEMessage": "Couldn't locate the MIME file. Synchronizing may help.", + "Info_ComposerMissingMIMETitle": "Failed", + "Info_ContactExistsMessage": "This contact is already in the recipient list.", + "Info_ContactExistsTitle": "Contact Exists", + "Info_DraftFolderMissingMessage": "Draft folder is missing for this account. Please check your account settings.", + "Info_DraftFolderMissingTitle": "Missing Draft Folder", + "Info_FileLaunchFailedTitle": "Failed to launch file", + "Info_InvalidAddressMessage": "'{0}' is not a valid e-mail address.", + "Info_InvalidAddressTitle": "Invalid Address", + "Info_InvalidMoveTargetMessage": "You can't move selected mails to this folder.", + "Info_InvalidMoveTargetTitle": "Invalid Move Target", + "Info_LogsNotFoundMessage": "There are no logs to share.", + "Info_LogsNotFoundTitle": "Logs Not Found", + "Info_LogsSavedMessage": "{0} is saved to selected folder.", + "Info_LogsSavedTitle": "Saved", + "Info_MailRenderingFailedMessage": "This mail is corrupted or can't be opened.\n{0}", + "Info_MailRenderingFailedTitle": "Render Failed", + "Info_MessageCorruptedMessage": "This message is corrupted.", + "Info_MessageCorruptedTitle": "Error", + "Info_MissingFolderMessage": "{0} doesn't exist for this account.", + "Info_MissingFolderTitle": "Missing Folder", + "Info_PDFSaveSuccessTitle": "Success", + "Info_PDFSaveFailedTitle": "Failed to save PDF file", + "Info_PDFSaveSuccessMessage": "PDF file is saved to {0}", + "Info_PurchaseExistsMessage": "Looks like this product has already been purchased before.", + "Info_PurchaseExistsTitle": "Existing Product", + "Info_PurchaseThankYouMessage": "Thank You", + "Info_PurchaseThankYouTitle": "Purchase successful", + "Info_RequestCreationFailedTitle": "Failed to Create Requests", + "Info_ReviewNetworkErrorMessage": "There was a network issue with your review.", + "Info_ReviewNetworkErrorTitle": "Network Issue", + "Info_ReviewNewMessage": "All feedbacks are appreciated. Thank you for the review!", + "Info_ReviewSuccessTitle": "Thank you", + "Info_ReviewUnknownErrorMessage": "There was an unknown issue with your review. ({0})", + "Info_ReviewUnknownErrorTitle": "Unknown Error", + "Info_ReviewUpdatedMessage": "Thank you for the updated review.", + "Info_SignatureDisabledMessage": "Disabled signature for this account", + "Info_SignatureDisabledTitle": "Success", + "Info_SignatureSavedMessage": "New signature is saved", + "Info_SignatureSavedTitle": "Success", + "Info_SyncCanceledMessage": "Canceled", + "Info_SyncCanceledTitle": "Synchronization", + "Info_SyncFailedTitle": "Synchronization Failed", + "Info_UnsupportedFunctionalityDescription": "This functionality is not implemented yet.", + "Info_UnsupportedFunctionalityTitle": "Unsupported", + "Info_UnsubscribeLinkInvalidTitle": "Invalid Unsubscribe Uri", + "Info_UnsubscribeLinkInvalidMessage": "This unsubscribe link is invalid. Failed to unsubscribe from the list.", + "ImapAdvancedSetupDialog_AuthenticationMethod": "Authentication method", + "ImapAdvancedSetupDialog_ConnectionSecurity": "Connection security", + "ImapAuthenticationMethod_Auto": "Auto", + "ImapAuthenticationMethod_CramMD5": "CRAM-MD5", + "ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5", + "ImapAuthenticationMethod_None": "No authentication", + "ImapAuthenticationMethod_Plain": "Normal password", + "ImapAuthenticationMethod_EncryptedPassword": "Encrypted password", + "ImapAuthenticationMethod_Ntlm": "NTLM", + "ImapConnectionSecurity_None": "None", + "ImapConnectionSecurity_SslTls": "SSL/TLS", + "ImapConnectionSecurity_StartTls": "STARTTLS", + "ImapConnectionSecurity_Auto": "Auto", + "Justify": "Justify", + "Left": "Left", + "Link": "Link", + "LinkedAccountsCreatePolicyMessage": "you must have at least 2 accounts to create link\nlink will be removed on save", + "LinkedAccountsTitle": "Linked Accounts", + "MailOperation_AlwaysMoveFocused": "Always Move to Focused", + "MailOperation_AlwaysMoveOther": "Always Move to Other", + "MailOperation_Archive": "Archive", + "MailOperation_ClearFlag": "Clear flag", + "MailOperation_DarkEditor": "Dark", + "MailOperation_Delete": "Delete", + "MailOperation_ExportPDF": "Export to PDF", + "MailOperation_Find": "Find", + "MailOperation_Forward": "Forward", + "MailOperation_Ignore": "Ignore", + "MailOperation_LightEditor": "Light", + "MailOperation_MarkAsJunk": "Mark as junk", + "MailOperation_MarkAsRead": "Mark as read", + "MailOperation_MarkAsUnread": "Mark as unread", + "MailOperation_MarkNotJunk": "Mark as Not Junk", + "MailOperation_Move": "Move", + "MailOperation_MoveFocused": "Move to Focused", + "MailOperation_MoveJunk": "Move to Junk", + "MailOperation_MoveOther": "Move to Other", + "MailOperation_Navigate": "Navigate", + "MailOperation_Print": "Print", + "MailOperation_Reply": "Reply", + "MailOperation_ReplyAll": "Reply all", + "MailOperation_SaveAs": "Save As", + "MailOperation_SetFlag": "Set flag", + "MailOperation_Unarchive": "Unarchive", + "MailOperation_Zoom": "Zoom", + "MailsSelected": "{0} item(s) selected", + "MarkFlagUnflag": "Mark as flagged/unflagged", + "MarkReadUnread": "Mark as read/unread", + "MenuManageAccounts": "Manage Accounts", + "MenuNewMail": "New Mail", + "MenuMergedAccountItemAccountsSuffix": " accounts", + "MenuRate": "Rate Wino", + "MenuSettings": "Settings", + "MergedAccountsAvailableAccountsTitle": "Available Accounts", + "More": "More", + "MoveMailDialog_InvalidFolderMessage": "{0} is not a valid folder for this mail.", + "MoveMailDialog_Title": "Pick a folder", + "NewAccountDialog_AccountName": "Account Name", + "NewAccountDialog_AccountNameDefaultValue": "Personal", + "NewAccountDialog_AccountNamePlaceholder": "eg. Personal Mail", + "NewAccountDialog_Title": "Add New Account", + "NoMailSelected": "no message selected", + "NoMessageCrieteria": "no messages match your search criteria.", + "NoMessageEmptyFolder": "this folder is empty.", + "Notifications_MultipleNotificationsMessage": "You have {0} new mails", + "Notifications_MultipleNotificationsTitle": "New Mails", + "Notifications_WinoUpdatedMessage": "Checkout new version {0}", + "Notifications_WinoUpdatedTitle": "Wino Mail has been updated.", + "Other": "Other", + "PaneLengthOption_Default": "Default", + "PaneLengthOption_ExtraLarge": "Extra Large", + "PaneLengthOption_Large": "Large", + "PaneLengthOption_Medium": "Medium", + "PaneLengthOption_Micro": "Micro", + "PaneLengthOption_Small": "Small", + "Photos": "Photos", + "PreparingFoldersMessage": "Preparing folders", + "ProviderDetail_Gmail_Description": "Google Account", + "ProviderDetail_IMAP_Description": "Custom IMAP/SMTP server", + "ProviderDetail_IMAP_Title": "IMAP Server", + "Results": "Results", + "Right": "Right", + "SynchronizationFolderReport_Success": "up to date", + "SynchronizationFolderReport_Failed": "synchronization is failed", + "SearchBarPlaceholder": "search", + "SearchingIn": "searching in", + "SettingsAboutGithub_Description": "Go to issue tracker GitHub repository.", + "SettingsAboutGithub_Title": "GitHub", + "SettingsAccountManagementAppendMessage_Title": "Append messages to Sent folder", + "SettingsAccountManagementAppendMessage_Description": "Create a copy of the message in Sent folder after the draft is sent. Enable this if you don't see your mails after you sent them in Sent folder.", + "SettingsEditLinkedInbox_Title": "Edit Linked Inbox", + "SettingsEditLinkedInbox_Description": "Add / remove accounts, rename or break the link between accounts.", + "SettingsAboutVersion": "Version ", + "SettingsAboutWinoDescription": "Lightweight mail client for Windows device families.", + "SettingsAbout_Description": "Learn more about Wino.", + "SettingsAbout_Title": "About", + "SettingsAccentColor_Description": "Change application's accent color", + "SettingsAccentColor_Title": "Accent Color", + "SettingsAccentColor_UseWindowsAccentColor": "Use my Windows accent color", + "SettingsAccountName_Description": "Change the name of the account.", + "SettingsAccountName_Title": "Account Name", + "SettingsApplicationTheme_Description": "Personalize Wino with different custom application themes for your like.", + "SettingsApplicationTheme_Title": "Application Theme", + "SettingsAvailableThemes_Description": "Select a theme from Wino's own collection for your taste or apply your own themes.", + "SettingsAvailableThemes_Title": "Available Themes", + "SettingsAutoSelectNextItem_Title": "Auto select next item", + "SettingsAutoSelectNextItem_Description": "Select the next item after you delete or move a mail.", + "SettingsCustomTheme_Description": "Create your own custom theme with custom wallpaper and accent color.", + "SettingsCustomTheme_Title": "Custom Theme", + "SettingsConfigureSpecialFolders_Title": "Configure System Folders", + "SettingsConfigureSpecialFolders_Description": "Set folders with special functions. Folders such as Archive, Inbox, and Drafts are essential for Wino to function properly.", + "SettingConfigureSpecialFolders_Button": "Configure", + "Error_FailedToSetupSystemFolders_Title": "Failed to setup system folders", + "SettingsDeleteAccount_Description": "Delete all e-mails and credentials associated with this account.", + "SettingsDeleteAccount_Title": "Delete this account", + "SettingsDeleteProtection_Description": "Should Wino ask you for comfirmation every time you try to permanently delete a mail using Shift + Del keys?", + "SettingsDeleteProtection_Title": "Permanent Delete Protection", + "SettingsDiagnostics_Description": "For developers", + "SettingsDiagnostics_Title": "Diagnostics", + "SettingsDiscord_Description": "Get regular development updates, join roadmap discussions and provide feedback.", + "SettingsDiscord_Title": "Discord Channel", + "SettingsElementThemeSelectionDisabled": "Element theme selection is disabled when application theme is selected other than Default.", + "SettingsElementTheme_Description": "Select a Windows theme for Wino", + "SettingsElementTheme_Title": "Element Theme", + "SettingsEnableHoverActions_Title": "Enable hover actions", + "SettingsEnableIMAPLogs_Description": "Enable this to provide details about IMAP connectivity issuses you had during IMAP server setup.", + "SettingsEnableIMAPLogs_Title": "Enable IMAP Protocol Logs", + "SettingsEnableLogs_Description": "I might need logs for crashes to diagnose issues you have opened in GitHub. None of the logs will expose your credentials or sensetive information to public.", + "SettingsEnableLogs_Title": "Enable Logs", + "SettingsEnableSignature": "Enable Signature", + "SettingsExpandOnStartup_Description": "Set whether Wino should expand this account's folders on startup.", + "SettingsExpandOnStartup_Title": "Expand Menu on Startup", + "SettingsExternalContent_Description": "Manage external content settings when rendering mails.", + "SettingsExternalContent_Title": "External Content", + "SettingsFocusedInbox_Description": "Set whether Inbox should be split into two as Focused - Other.", + "SettingsFocusedInbox_Title": "Focused Inbox", + "SettingsFolderSync_Description": "Enable or disable specific folders for synchronization.", + "SettingsFolderSync_Title": "Folder Synchronization", + "SettingsFolderOptions_Title": "Folder Configuration", + "SettingsFolderOptions_Description": "Change individual folder settings like enable/disable sync or show/hide unread badge.", + "SettingsHoverActionCenter": "Center Action", + "SettingsHoverActionLeft": "Left Action", + "SettingsHoverActionRight": "Right Action", + "SettingsHoverActions_Description": "Select 3 actions to show up when you hover over the mails with cursor.", + "SettingsHoverActions_Title": "Hover Actions", + "SettingsLanguage_Description": "Change display language for Wino.", + "SettingsLanguage_Title": "Display Language", + "CategoriesFolderNameOverride": "Categories", + "MoreFolderNameOverride": "More", + "SettingsOptions_Title": "Settings", + "SettingsLinkAccounts_Description": "Merge multiple accounts into one. See mails from one Inbox together.", + "SettingsLinkAccounts_Title": "Create Linked Accounts", + "SettingsLinkedAccountsSave_Description": "Modify the current link with the new accounts.", + "SettingsLinkedAccountsSave_Title": "Save Changes", + "SettingsLoadImages_Title": "Load images automatically", + "SettingsLoadStyles_Title": "Load styles automatically", + "SettingsMailSpacing_Description": "Adjust the spacing for listing mails.", + "SettingsMailSpacing_Title": "Mail Spacing", + "SettingsFolderMenuStyle_Title": "Create Nested Folders", + "SettingsFolderMenuStyle_Description": "Change whether account folders should be nested inside an account menu item or not. Toggle this off if you like the old menu system in Windows Mail", + "SettingsManageAccountSettings_Description": "Notifications, signatures, synchronization and other settings per account.", + "SettingsManageAccountSettings_Title": "Manage Account Settings", + "SettingsManageLink_Description": "Move items to add new link or remove existing link.", + "SettingsManageLink_Title": "Manage Link", + "SettingsMarkAsRead_Description": "Change what should happen to the selected item.", + "SettingsMarkAsRead_DontChange": "Don't automatically mark item as read", + "SettingsMarkAsRead_SecondsToWait": "Seconds to wait: ", + "SettingsMarkAsRead_Timer": "When viewed in the reading pane", + "SettingsMarkAsRead_Title": "Mark item as read", + "SettingsMarkAsRead_WhenSelected": "When selected", + "SettingsMessageList_Description": "Change how your messages should be organized in mail list.", + "SettingsMessageList_Title": "Message List", + "SettingsNoAccountSetupMessage": "You didn't setup any accounts yet.", + "SettingsNotifications_Description": "Turn on or off notifications for this account.", + "SettingsNotifications_Title": "Notifications", + "SettingsPaneLength_Description": "Change the width of the mail list.", + "SettingsPaneLength_Title": "Mail List Pane Length", + "SettingsPaypal_Description": "Show much more love ❤️ All donations are appreciated.", + "SettingsPaypal_Title": "Donate via PayPal", + "SettingsPersonalizationMailDisplayCompactMode": "Compact Mode", + "SettingsPersonalizationMailDisplayMediumMode": "Medium Mode", + "SettingsPersonalizationMailDisplaySpaciousMode": "Spacious Mode", + "SettingsPersonalization_Description": "Change appearance of Wino as you like.", + "SettingsPersonalization_Title": "Personalization", + "SettingsPrivacyPolicy_Description": "Review privacy policy.", + "SettingsPrivacyPolicy_Title": "Privacy Policy", + "SettingsReadingPane_Description": "Mail rendering options.", + "SettingsReadingPane_Title": "Reading Pane", + "SettingsReaderFont_Title": "Default Reader Font", + "SettingsReaderFontFamily_Description": "Change the default font family and font size for reading mails.", + "SettingsFontFamily_Title": "Font Family", + "SettingsFontSize_Title": "Font Size", + "SettingsFontPreview_Title": "Preview", + "SettingsComposerFont_Title": "Default Composer Font", + "SettingsComposerFontFamily_Description": "Change the default font family and font size for composing mails.", + "SettingsRenameMergeAccount_Description": "Change the display name of the linked accounts.", + "SettingsRenameMergeAccount_Title": "Rename", + "SettingsSemanticZoom_Description": "This will allow you to click on the headers in messages list and go to specific date", + "SettingsSemanticZoom_Title": "Semantic Zoom for Date Headers", + "SettingsShowPreviewText_Description": "Hide/show thepreview text.", + "SettingsShowPreviewText_Title": "Show Preview Text", + "SettingsShowSenderPictures_Description": "Hide/show the thumbnail sender pictures.", + "SettingsShowSenderPictures_Title": "Show Sender Avatars", + "SettingsPrefer24HourClock_Title": "Display Clock Format in 24 Hours", + "SettingsPrefer24HourClock_Description": "Mail recieve times will be displayed in 24 hour format instead of 12 (AM/PM)", + "SettingsSignature_Description": "Edit or remove account signature", + "SettingsSignature_Title": "Signature", + "SettingsStartupItem_Description": "Primary account item to load Inbox at startup.", + "SettingsStartupItem_Title": "Startup Item", + "SettingsStore_Description": "Show some love ❤️", + "SettingsStore_Title": "Rate in Store", + "SettingsThreads_Description": "Organize messages into conversation threads.", + "SettingsThreads_Title": "Conversation Threading", + "SettingsUnlinkAccounts_Description": "Remove the link between accounts. This will not delete your accounts.", + "SettingsUnlinkAccounts_Title": "Unlink Accounts", + "SortingOption_Date": "by date", + "SortingOption_Name": "by name", + "StoreRatingDialog_MessageFirstLine": "All feedbacks are appreciated and they will make much Wino better in the future. Would you like to rate Wino in Microsoft Store?", + "StoreRatingDialog_MessageSecondLine": "Would you like to rate Wino Mail in Microsoft Store?", + "StoreRatingDialog_Title": "Enjoying Wino?", + "SystemFolderConfigDialog_ArchiveFolderDescription": "Archived messages will be moved to here.", + "SystemFolderConfigDialog_ArchiveFolderHeader": "Archive Folder", + "SystemFolderConfigDialog_DeletedFolderDescription": "Deleted messages will be moved to here.", + "SystemFolderConfigDialog_DeletedFolderHeader": "Deleted Folder", + "SystemFolderConfigDialog_DraftFolderDescription": "New mails/replies will be crafted in here.", + "SystemFolderConfigDialog_DraftFolderHeader": "Draft Folder", + "SystemFolderConfigDialog_JunkFolderDescription": "All spam/junk mails will be here.", + "SystemFolderConfigDialog_JunkFolderHeader": "Junk/Spam Folder", + "SystemFolderConfigDialog_MessageFirstLine": "This IMAP server doesn't support SPECIAL-USE extension hence Wino couldn't setup the system folders properly.", + "SystemFolderConfigDialog_MessageSecondLine": "Please select the appropriate folders for specific functionalities.", + "SystemFolderConfigDialog_SentFolderDescription": "Folder that sent messages will be stored.", + "SystemFolderConfigDialog_SentFolderHeader": "Sent Folder", + "SystemFolderConfigDialog_Title": "Configure System Folders", + "SystemFolderConfigDialogValidation_InboxSelected": "You can't assign Inbox folder to any other system folder.", + "SystemFolderConfigDialogValidation_DuplicateSystemFolders": "Some of the system folders are used more than once in the configuration.", + "SystemFolderConfigSetupSuccess_Title": "System Folders Setup", + "SystemFolderConfigSetupSuccess_Message": "System folders are successfully configured.", + "TestingImapConnectionMessage": "Testing server connection...", + "Today": "Today", + "UnknownAddress": "unknown address", + "UnknownDateHeader": "Unknown Date", + "UnknownGroupAddress": "unknown Mail Group Address", + "UnknownSender": "Unknown Sender", + "Unsubscribe": "Unsubscribe", + "ViewContactDetails": "View Details", + "WinoUpgradeDescription": "Wino offers 3 accounts to start with for free. If you need more than 3 accounts, please upgrade", + "WinoUpgradeMessage": "Upgrade to Unlimited Accounts", + "WinoUpgradeRemainingAccountsMessage": "{0} out of {1} free accounts used.", + "Yesterday": "Yesterday" +} diff --git a/Wino.Core.Domain/Translations/es_ES/resources.json b/Wino.Core.Domain/Translations/es_ES/resources.json new file mode 100644 index 00000000..00905e2d --- /dev/null +++ b/Wino.Core.Domain/Translations/es_ES/resources.json @@ -0,0 +1,468 @@ +{ + "AccountCreationDialog_Completed": "todo listo", + "AccountCreationDialog_Initializing": "inicializando", + "AccountCreationDialog_PreparingFolders": "Estamos obteniendo información de la carpeta en este momento.", + "AccountCreationDialog_SigninIn": "La información de la cuenta se está guardando.", + "AccountEditDialog_Message": "Nombre de la Cuenta", + "AccountEditDialog_Title": "Editar cuenta", + "AccountPickerDialog_Title": "Elija una cuenta", + "AddHyperlink": "Añadir", + "AutoDiscoveryProgressMessage": "Buscando ajustes de correo...", + "BasicIMAPSetupDialog_AdvancedConfiguration": "Configuración avanzada", + "BasicIMAPSetupDialog_CredentialLocalMessage": "Sus credenciales sólo se almacenarán localmente en su ordenador.", + "BasicIMAPSetupDialog_Description": "Algunas cuentas necesitan pasos adicionales para iniciar sesión", + "BasicIMAPSetupDialog_DisplayName": "Nombre a mostrar", + "BasicIMAPSetupDialog_DisplayNamePlaceholder": "por ejemplo, Fulano Mengano", + "BasicIMAPSetupDialog_LearnMore": "Aprender más", + "BasicIMAPSetupDialog_MailAddress": "Correo Electrónico", + "BasicIMAPSetupDialog_MailAddressPlaceholder": "johndoe@fabrikam.com", + "BasicIMAPSetupDialog_Password": "Contraseña", + "BasicIMAPSetupDialog_Title": "Cuenta IMAP", + "Buttons_AddAccount": "Añadir Cuenta", + "Buttons_ApplyTheme": "Aplicar Tema", + "Buttons_Browse": "Buscar", + "Buttons_Cancel": "Cancelar", + "Buttons_Close": "Cerrar", + "Buttons_Create": "Crear", + "Buttons_CreateAccount": "Crear Cuenta", + "Buttons_Delete": "Eliminar", + "Buttons_Discard": "Descartar", + "Buttons_EnableImageRendering": "Activar", + "Buttons_No": "No", + "Buttons_Open": "Abrir", + "Buttons_Purchase": "Comprar", + "Buttons_RateWino": "Califica a Wino", + "Buttons_Save": "Guardar", + "Buttons_SaveConfiguration": "Guardar Configuración", + "Buttons_Share": "Compartir", + "Buttons_SignIn": "Iniciar Sesión", + "Buttons_Yes": "Sí", + "Center": "Centrar", + "ComingSoon": "Próximamente...", + "ComposerFrom": "De: ", + "ComposerSubject": "Asunto: ", + "ComposerTo": "Para: ", + "ClipboardTextCopied_Message": "{0} copied to clipboard.", + "ClipboardTextCopied_Title": "Copied", + "ClipboardTextCopyFailed_Message": "Failed to copy {0} to clipboard.", + "ComposerToPlaceholder": "presiona enter para ingresar direcciones", + "CustomThemeBuilder_AccentColorDescription": "Establece un color de acento si lo deseas. Si no seleccionas uno se usará tu color de acento de Windows.", + "CustomThemeBuilder_AccentColorTitle": "Color de acento", + "CustomThemeBuilder_PickColor": "Elegir", + "CustomThemeBuilder_ThemeNameDescription": "Nombre único para su tema personalizado.", + "CustomThemeBuilder_ThemeNameTitle": "Nombre del tema", + "CustomThemeBuilder_Title": "Creador de Temas Personalizados", + "CustomThemeBuilder_WallpaperDescription": "Establecer una imagen de fondo personalizada para Wino", + "CustomThemeBuilder_WallpaperTitle": "Establecer imagen de fondo personalizada", + "DialogMessage_AccountLimitMessage": "Has alcanzado el límite para crear cuentas.\n¿Te gustaría comprar el add-on 'Cuentas Ilimitadas' para continuar?", + "DialogMessage_AccountLimitTitle": "Límite de Cuentas Alcanzado", + "DialogMessage_CleanupFolderMessage": "¿Quieres borrar permanentemente todos los correos en esta carpeta?", + "DialogMessage_CleanupFolderTitle": "Carpeta de Limpieza", + "DialogMessage_ComposerMissingRecipientMessage": "El mensaje no tiene destinatario.", + "DialogMessage_ComposerValidationFailedTitle": "Validación Fallida", + "DialogMessage_CreateLinkedAccountMessage": "Dar un nombre a este nuevo enlace. Las cuentas se combinarán con este nombre.", + "DialogMessage_CreateLinkedAccountTitle": "Nombre del enlace de cuenta", + "DialogMessage_DeleteAccountConfirmationMessage": "¿Eliminar {0}?", + "DialogMessage_DeleteAccountConfirmationTitle": "Todos los datos asociados a esta cuenta se eliminarán del disco permanentemente.", + "DialogMessage_DiscardDraftConfirmationMessage": "Este borrador se descartará. ¿Desea continuar?", + "DialogMessage_DiscardDraftConfirmationTitle": "Descartar borrador", + "DialogMessage_HardDeleteConfirmationMessage": "Eliminar Permanentemente", + "DialogMessage_HardDeleteConfirmationTitle": "Mensaje(s) se eliminarán permanentemente. ¿Desea continuar?", + "DialogMessage_NoAccountsForCreateMailMessage": "No tienes ninguna cuenta desde la que crear mensaje.", + "DialogMessage_NoAccountsForCreateMailTitle": "Falta cuenta", + "DialogMessage_RenameLinkedAccountsMessage": "Introduzca un nuevo nombre para la cuenta vinculada", + "DialogMessage_RenameLinkedAccountsTitle": "Renombrar cuenta vinculada", + "DialogMessage_UnlinkAccountsConfirmationMessage": "Esta operación no eliminará sus cuentas pero sólo romperá el enlace para conexiones de carpetas compartidas. ¿Desea continuar?", + "DialogMessage_UnlinkAccountsConfirmationTitle": "Desvincular Cuentas", + "DialogMessage_EmptySubjectConfirmation": "Sin Asunto", + "DialogMessage_EmptySubjectConfirmationMessage": "El mensaje no tiene asunto. ¿Desea continuar?", + "Dialog_DontAskAgain": "No preguntar de nuevo", + "DiscordChannelDisclaimerMessage": "Wino no tiene un servidor de Discordia propio, pero el canal especial 'wino-mail' está hospedado en servidor 'Developer Sanctuary'.\nPara obtener actualizaciones acerca de Wino únase al Santuario servidor de Desarrolladores y siga el canal 'wino-mail' en 'Community Projects'\n\nSerás redirigido a la URL del servidor cuando la Discordia no soporta canales invitados.", + "DiscordChannelDisclaimerTitle": "Información de Discord importante", + "Draft": "Borrador", + "EditorToolbarOption_Draw": "Dibujar", + "EditorToolbarOption_Format": "Formato", + "EditorToolbarOption_Insert": "Insertar", + "EditorToolbarOption_None": "Ninguno", + "EditorToolbarOption_Options": "Opciones", + "ElementTheme_Dark": "Modo oscuro", + "ElementTheme_Default": "Usar configuración del sistema", + "ElementTheme_Light": "Modo claro", + "Emoji": "Emoji", + "Exception_ImapClientPoolFailed": "Cola Cliente IMAP falló.", + "Exception_AuthenticationCanceled": "Autenticación cancelada", + "Exception_CustomThemeExists": "Este tema ya existe.", + "Exception_CustomThemeMissingName": "Debe proporcionar un nombre.", + "Exception_CustomThemeMissingWallpaper": "Debe proporcionar una imagen de fondo personalizada.", + "Exception_FailedToSynchronizeFolders": "Error al sincronizar carpetas", + "Exception_GoogleAuthCallbackNull": "Callback uri nulo al activarse.", + "Exception_GoogleAuthCorruptedCode": "Respuesta de autorización corrupta.", + "Exception_GoogleAuthError": "Error de autorización de OAuth: {0}", + "Exception_GoogleAuthInvalidResponse": "Solicitud recibida con estado no válido ({0})", + "Exception_GoogleAuthorizationCodeExchangeFailed": "El intercambio del código de autorización ha fallado.", + "Exception_InvalidSystemFolderConfiguration": "La configuración de la carpeta del sistema no es válida. Verifica la configuración y vuelve a intentar.", + "Exception_NullAssignedAccount": "La cuenta asignada es nula", + "Exception_NullAssignedFolder": "La carpeta asignada es nula", + "Exception_SynchronizerFailureHTTP": "Manejo de la respuesta ha fallado con código de error HTTP {0}", + "Exception_TokenGenerationFailed": "Falló la generación del token", + "Exception_TokenInfoRetrivalFailed": "Fallo al recuperar la información del token.", + "Exception_UnknowErrorDuringAuthentication": "Sucedió un error desconocido durante la autenticación", + "Exception_UnsupportedAction": "La acción {0} no está implementada en el procesador solicitado", + "Exception_UnsupportedProvider": "Este proveedor no está soportado.", + "Exception_UnsupportedSynchronizerOperation": "Esta operación no es compatible con {0}", + "Exception_UserCancelSystemFolderSetupDialog": "El usuario canceló la ventana de configuración de la carpeta del sistema.", + "Files": "Archivos", + "FilteringOption_All": "Todos", + "FilteringOption_Flagged": "Marcado", + "FilteringOption_Unread": "Sin leer", + "Focused": "Importante", + "FolderOperation_CreateSubFolder": "Crear sub carpeta", + "FolderOperation_Delete": "Eliminar", + "FolderOperation_DontSync": "No sincronizar esta carpeta", + "FolderOperation_Empty": "Vaciar esta carpeta", + "FolderOperation_MarkAllAsRead": "Marcar todo como leído", + "FolderOperation_Move": "Mover", + "DragMoveToFolderCaption": "Mover a {0}", + "FolderOperation_None": "Ninguno", + "FolderOperation_Pin": "Fijar", + "FolderOperation_Rename": "Renombrar", + "FolderOperation_Unpin": "Desfijar", + "HoverActionOption_Archive": "Archivar", + "HoverActionOption_Delete": "Eliminar", + "HoverActionOption_MoveJunk": "Mover a Correo no deseado", + "HoverActionOption_ToggleFlag": "Marcar / Desmarcar", + "HoverActionOption_ToggleRead": "Leídos / Sin leer", + "MergedAccountCommonFolderInbox": "Bandeja de Entrada", + "MergedAccountCommonFolderSent": "Enviados", + "MergedAccountCommonFolderDraft": "Borradores", + "MergedAccountCommonFolderJunk": "Correo no deseado", + "MergedAccountCommonFolderTrash": "Eliminados", + "MergedAccountCommonFolderArchive": "Archivo", + "IMAPSetupDialog_AccountType": "Tipo de cuenta", + "IMAPSetupDialog_DisplayName": "Nombre para mostrar", + "IMAPSetupDialog_DisplayNamePlaceholder": "por ejemplo. Fulano Mengano", + "IMAPSetupDialog_IncomingMailServer": "Servidor de correo entrante", + "IMAPSetupDialog_IncomingMailServerPort": "Puerto", + "IMAPSetupDialog_MailAddress": "Dirección de correo", + "IMAPSetupDialog_MailAddressPlaceholder": "alguien@ejemplo.com", + "IMAPSetupDialog_OutgoingMailServer": "Servidor de correo saliente (SMTP)", + "IMAPSetupDialog_OutgoingMailServerPassword": "Contraseña del servidor de salida", + "IMAPSetupDialog_OutgoingMailServerPort": "Puerto", + "IMAPSetupDialog_OutgoingMailServerRequireAuthentication": "El servidor de salida requiere autenticación", + "IMAPSetupDialog_OutgoingMailServerUsername": "Usuario del servidor de salida", + "IMAPSetupDialog_Password": "Contraseña", + "IMAPSetupDialog_RequireSSLForIncomingMail": "Requerir SSL para los correos entrantes", + "IMAPSetupDialog_RequireSSLForOutgoingMail": "Requerir SSL para los correos salientes", + "IMAPSetupDialog_Title": "Configuración IMAP Avanzada", + "IMAPSetupDialog_UseSameConfig": "Usar el mismo correo y contraseña para enviar correos", + "IMAPSetupDialog_Username": "Nombre de usuario", + "IMAPSetupDialog_UsernamePlaceholder": "fulanomengano, fulanomengano@fabrikam.com, dominio/fulanomengano", + "ImageRenderingDisabled": "El procesamiento de imágenes está desactivado para este mensaje.", + "InfoBarAction_Enable": "Activar", + "InfoBarMessage_SynchronizationDisabledFolder": "Esta carpeta está desactivada para la sincronización.", + "InfoBarTitle_SynchronizationDisabledFolder": "Carpeta desactivada", + "GeneralTitle_Error": "Error", + "GeneralTitle_Warning": "Warning", + "GeneralTitle_Info": "Information", + "Info_AccountCreatedMessage": "{0} se ha creado", + "Info_AccountCreatedTitle": "Creación de una cuenta", + "Info_AccountCreationFailedTitle": "Ocurrió un error al crear la cuenta", + "Info_AccountDeletedMessage": "{0} eliminado correctamente.", + "Info_AccountDeletedTitle": "Cuenta eliminada", + "Info_AccountIssueFixFailedTitle": "Fallido", + "Info_AccountIssueFixSuccessMessage": "Se han corregido todos los problemas de la cuenta.", + "Info_AccountIssueFixSuccessTitle": "Correcto", + "Info_AttachmentOpenFailedMessage": "No se puede abrir este adjunto.", + "Info_AttachmentOpenFailedTitle": "Fallido", + "Info_AttachmentSaveFailedMessage": "No se puede guardar este adjunto.", + "Info_AttachmentSaveFailedTitle": "Fallido", + "Info_AttachmentSaveSuccessMessage": "El adjunto ha sido guardado.", + "Info_AttachmentSaveSuccessTitle": "Adjunto guardado", + "Info_BackgroundExecutionDeniedMessage": "Se ha denegado la ejecución en segundo plano para la aplicación. Esto puede afectar a la sincronización en segundo plano y a las notificaciones en directo.", + "Info_BackgroundExecutionDeniedTitle": "Ejecución de fondo denegada", + "Info_BackgroundExecutionUnknownErrorMessage": "Se produjo una excepción desconocida al registrar el sincronizador en segundo plano.", + "Info_BackgroundExecutionUnknownErrorTitle": "Error de ejecución en segundo plano", + "Info_ComposerMissingMIMEMessage": "No se pudo localizar el archivo MIME. La sincronización puede ayudar.", + "Info_ComposerMissingMIMETitle": "Fallido", + "Info_ContactExistsMessage": "Este contacto ya está en la lista de destinatarios.", + "Info_ContactExistsTitle": "Contacto existente", + "Info_DraftFolderMissingMessage": "No existe la carpeta de borradores en esta cuenta. Por favor, comprueba los ajustes de la cuenta.", + "Info_DraftFolderMissingTitle": "Falta la carpeta borrador", + "Info_FileLaunchFailedTitle": "Error al iniciar archivo", + "Info_InvalidAddressMessage": "La dirección: {0} no es un e-mail válido.", + "Info_InvalidAddressTitle": "Dirección no válida", + "Info_InvalidMoveTargetMessage": "No puede mover los correos seleccionados a esta carpeta.", + "Info_InvalidMoveTargetTitle": "Objetivo mover no válido", + "Info_LogsNotFoundMessage": "No hay registros que compartir.", + "Info_LogsNotFoundTitle": "Registros No Encontrados", + "Info_LogsSavedMessage": "{0} se ha guardado en la carpeta seleccionada.", + "Info_LogsSavedTitle": "Guardado", + "Info_MailRenderingFailedMessage": "Este correo está dañado o no puede abrirse.\n{0}", + "Info_MailRenderingFailedTitle": "Error al renderizar", + "Info_MessageCorruptedMessage": "Este mensaje está dañado.", + "Info_MessageCorruptedTitle": "Error", + "Info_MissingFolderMessage": "{0} no existe para esta cuenta.", + "Info_MissingFolderTitle": "Falta carpeta", + "Info_PurchaseExistsMessage": "Parece que este producto ya ha sido comprado antes.", + "Info_PurchaseExistsTitle": "Producto existente", + "Info_PurchaseThankYouMessage": "Gracias", + "Info_PurchaseThankYouTitle": "Compra correcta", + "Info_RequestCreationFailedTitle": "Hubo una falla al Crear Solicitudes", + "Info_ReviewNetworkErrorMessage": "Hubo un error desconocido con tu opinión.", + "Info_ReviewNetworkErrorTitle": "Problema de red", + "Info_ReviewNewMessage": "Todas las opiniones se agradecen. ¡Gracias por dejar la tuya!", + "Info_ReviewSuccessTitle": "Gracias", + "Info_ReviewUnknownErrorMessage": "Hubo un error desconocido con tu opinión. ({0})", + "Info_ReviewUnknownErrorTitle": "Error Desconocido", + "Info_ReviewUpdatedMessage": "Gracias por tus comentarios actualizados.", + "Info_SignatureDisabledMessage": "Firma desactivada para esta cuenta", + "Info_SignatureDisabledTitle": "Éxito", + "Info_SignatureSavedMessage": "Se guardó la nueva firma", + "Info_SignatureSavedTitle": "Éxito", + "Info_SyncCanceledMessage": "Cancelado", + "Info_SyncCanceledTitle": "Sincronización", + "Info_SyncFailedTitle": "Falló la Sincronización", + "Info_UnsupportedFunctionalityDescription": "Esta funcionalidad aún no está implementada.", + "Info_UnsupportedFunctionalityTitle": "No soportado", + "Info_UnsubscribeLinkInvalidTitle": "Url de baja no válida", + "Info_UnsubscribeLinkInvalidMessage": "Este enlace de baja suscripción no es válido. Error al darse de baja de la lista.", + "ImapAdvancedSetupDialog_AuthenticationMethod": "Método de autenticación", + "ImapAdvancedSetupDialog_ConnectionSecurity": "Seguridad de la conexión", + "ImapAuthenticationMethod_Auto": "Auto", + "ImapAuthenticationMethod_CramMD5": "CRAM-MD5", + "ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5", + "ImapAuthenticationMethod_None": "Sin autenticación", + "ImapAuthenticationMethod_Plain": "Contraseña normal", + "ImapAuthenticationMethod_EncryptedPassword": "Contraseña encriptada", + "ImapAuthenticationMethod_Ntlm": "NTLM", + "ImapConnectionSecurity_None": "Ninguno", + "ImapConnectionSecurity_SslTls": "SSL/TLS", + "ImapConnectionSecurity_StartTls": "STARTTLS", + "ImapConnectionSecurity_Auto": "Auto", + "Justify": "Justificar", + "Left": "Izquierda", + "Link": "Enlace", + "LinkedAccountsCreatePolicyMessage": "debes tener al menos 2 cuentas para crear el enlace\nenlace se eliminará al guardar", + "LinkedAccountsTitle": "Cuentas Vinculadas", + "MailOperation_AlwaysMoveFocused": "Siempre Mover a Importantes", + "MailOperation_AlwaysMoveOther": "Siempre Mover a Otros", + "MailOperation_Archive": "Archivar", + "MailOperation_ClearFlag": "Quitar marca", + "MailOperation_DarkEditor": "Oscuro", + "MailOperation_Delete": "Eliminar", + "MailOperation_ExportPDF": "Exportar a PDF", + "MailOperation_Find": "Buscar", + "MailOperation_Forward": "Reenviar", + "MailOperation_Ignore": "Ignorar", + "MailOperation_LightEditor": "Claro", + "MailOperation_MarkAsJunk": "Marcar como Correo no deseado", + "MailOperation_MarkAsRead": "Marcar como leído", + "MailOperation_MarkAsUnread": "Marcar como no leído", + "MailOperation_MarkNotJunk": "Marcar como Correo deseado", + "MailOperation_Move": "Mover", + "MailOperation_MoveFocused": "Mover a Importantes", + "MailOperation_MoveJunk": "Mover a Correo no deseado", + "MailOperation_MoveOther": "Mover a Otros", + "MailOperation_Navigate": "Navegar", + "MailOperation_Print": "Imprimir", + "MailOperation_Reply": "Responder", + "MailOperation_ReplyAll": "Responder a todos", + "MailOperation_SaveAs": "Guardar Como", + "MailOperation_SetFlag": "Establecer marca", + "MailOperation_Unarchive": "Desarchivar", + "MailOperation_Zoom": "Zoom", + "MailsSelected": "{0} artículo(s) seleccionado(s)", + "MarkFlagUnflag": "Marcar como marcado/desmarcado", + "MarkReadUnread": "Marcar como leído/no leído", + "MenuManageAccounts": "Administrar Cuentas", + "MenuNewMail": "Nuevo Correo", + "MenuMergedAccountItemAccountsSuffix": " cuentas", + "MenuRate": "Valorar Wino", + "MenuSettings": "Ajustes", + "MergedAccountsAvailableAccountsTitle": "Cuentas Disponibles", + "More": "Más", + "MoveMailDialog_InvalidFolderMessage": "La carpeta {0} no es válida para este correo.", + "MoveMailDialog_Title": "Elija una carpeta", + "NewAccountDialog_AccountName": "Nombre de la Cuenta", + "NewAccountDialog_AccountNameDefaultValue": "Personal", + "NewAccountDialog_AccountNamePlaceholder": "p.ej. Correo Personal", + "NewAccountDialog_Title": "Agregar nueva cuenta", + "NoMailSelected": "ningún mensaje seleccionado", + "NoMessageCrieteria": "ningún mensaje coincide con sus criterios de búsqueda.", + "NoMessageEmptyFolder": "esta carpeta está vacía.", + "Notifications_MultipleNotificationsMessage": "Tienes {0} correos nuevos", + "Notifications_MultipleNotificationsTitle": "Nuevos correos", + "Notifications_WinoUpdatedMessage": "Comprobar nueva versión {0}", + "Notifications_WinoUpdatedTitle": "Wino Mail ha sido actualizado.", + "Other": "Otro", + "PaneLengthOption_Default": "Por defecto", + "PaneLengthOption_ExtraLarge": "Extra grande", + "PaneLengthOption_Large": "Grande", + "PaneLengthOption_Medium": "Mediano", + "PaneLengthOption_Micro": "Mini", + "PaneLengthOption_Small": "Pequeño", + "Photos": "Fotos", + "PreparingFoldersMessage": "Preparando carpetas", + "ProviderDetail_Gmail_Description": "Cuenta de Google", + "ProviderDetail_IMAP_Description": "Servidor IMAP/SMTP personalizado", + "ProviderDetail_IMAP_Title": "Servidor IMAP", + "Results": "Resultados", + "Right": "Derecha", + "SynchronizationFolderReport_Success": "actualizado", + "SynchronizationFolderReport_Failed": "sincronización fallida", + "SearchBarPlaceholder": "buscar", + "SearchingIn": "buscando en", + "SettingsAboutGithub_Description": "Ir al rastreador de problemas en el repositorio de GitHub.", + "SettingsAboutGithub_Title": "GitHub", + "SettingsAccountManagementAppendMessage_Title": "Añadir mensajes a la carpeta Enviada", + "SettingsAccountManagementAppendMessage_Description": "Crear una copia del mensaje en la carpeta Enviados luego de que el borrador se envíe. Activa esto si no ves tus correos en la carpeta Enviados después que los enviaste.", + "SettingsEditLinkedInbox_Title": "Editar bandeja de entrada vinculada", + "SettingsEditLinkedInbox_Description": "Añadir / quitar cuentas, renombrar o quitar vínculos entre cuentas.", + "SettingsAboutVersion": "Versión ", + "SettingsAboutWinoDescription": "Cliente de correo ligero para la familia de dispositivos Windows.", + "SettingsAbout_Description": "Conoce más sobre Wino.", + "SettingsAbout_Title": "Acerca de", + "SettingsAccentColor_Description": "Cambiar el color de acento de la aplicación", + "SettingsAccentColor_Title": "Color de Acento", + "SettingsAccentColor_UseWindowsAccentColor": "Usar mi color de acento de Windows", + "SettingsAccountName_Description": "Cambiar el nombre de la cuenta.", + "SettingsAccountName_Title": "Nombre de la Cuenta", + "SettingsApplicationTheme_Description": "Personaliza Wino con distintos temas personalizados a tu gusto.", + "SettingsApplicationTheme_Title": "Tema de la Aplicación", + "SettingsAvailableThemes_Description": "Escoge un tema que te agrade desde la colección de Wino o aplica tus propios temas.", + "SettingsAvailableThemes_Title": "Temas Disponibles", + "SettingsCustomTheme_Description": "Crea tu propio tema personalizado con una imagen de fondo y colores de acento personalizados.", + "SettingsCustomTheme_Title": "Tema Personalizado", + "SettingsDeleteAccount_Description": "Eliminar todos los correos y credenciales asociadas a esta cuenta.", + "SettingsDeleteAccount_Title": "Eliminar esta cuenta", + "SettingsDeleteProtection_Description": "¿Debería Wino pedirte confirmación cada vez que intentas eliminar un correo usando las teclas Shift + Supr?", + "SettingsDeleteProtection_Title": "Protección de Eliminación Permanente", + "SettingsDiagnostics_Description": "Para desarrolladores", + "SettingsDiagnostics_Title": "Diagnósticos", + "SettingsDiscord_Description": "Recibe actualizaciones regulares sobre el desarrollo, únete a las discusiones sobre los avances y proporciona comentarios.", + "SettingsDiscord_Title": "Canal de Discord", + "SettingsElementThemeSelectionDisabled": "La selección de elementos del tema se desactiva cuando el tema de la aplicación se selecciona de forma predeterminada.", + "SettingsElementTheme_Description": "Escoge un tema de Windows para Wino", + "SettingsElementTheme_Title": "Tema de elemento", + "SettingsEnableHoverActions_Title": "Activar acciones al mantener", + "SettingsEnableIMAPLogs_Description": "Habilite esto para proporcionar detalles sobre los errores de conectividad IMAP que tuvo durante la configuración del servidor IMAP.", + "SettingsEnableIMAPLogs_Title": "Habilitar registros de protocolo IMAP", + "SettingsEnableLogs_Description": "Podría necesitar registros para problemas diagnósticos que has abierto en GitHub. Ninguno de los registros expondrá sus credenciales o información sensible a la opinión pública.", + "SettingsEnableLogs_Title": "Activar registros", + "SettingsEnableSignature": "Habilitar firma", + "SettingsExpandOnStartup_Description": "Establezca si Wino debería expandir las carpetas de esta cuenta al inicio.", + "SettingsExpandOnStartup_Title": "Expandir Menú al Inicio", + "SettingsExternalContent_Description": "Administrar la configuración de contenido externo al renderizar los correos.", + "SettingsExternalContent_Title": "Contenido externo", + "SettingsFocusedInbox_Description": "Establezca si la Bandeja de Entrada debe dividirse en dos como Enfocada - Otro.", + "SettingsFocusedInbox_Title": "Bandeja de entrada concentrada", + "SettingsFolderSync_Description": "Activar o desactivar carpetas específicas para la sincronización.", + "SettingsFolderSync_Title": "Sincronización de carpetas", + "SettingsFolderOptions_Title": "Folder Configuration", + "SettingsFolderOptions_Description": "Change individual folder settings like enable/disable sync or show/hide unread badge.", + "SettingsHoverActionCenter": "Centro de Acción", + "SettingsHoverActionLeft": "Acción Izquierda", + "SettingsHoverActionRight": "Acción Derecha", + "SettingsHoverActions_Description": "Seleccione 3 acciones a mostrar cuando pase el cursor sobre los correos.", + "SettingsHoverActions_Title": "Acciones de Cursor", + "SettingsLanguage_Description": "Cambiar el idioma de visualización para Wino.", + "SettingsLanguage_Title": "Idioma de pantalla", + "CategoriesFolderNameOverride": "Categorías", + "MoreFolderNameOverride": "Más", + "SettingsOptions_Title": "Ajustes", + "SettingsLinkAccounts_Description": "Fusionar múltiples cuentas en una. Ver correos en una bandeja de entrada común.", + "SettingsLinkAccounts_Title": "Crear Cuentas Vinculadas", + "SettingsLinkedAccountsSave_Description": "Modificar el enlace actual con las nuevas cuentas.", + "SettingsLinkedAccountsSave_Title": "Guardar Cambios", + "SettingsLoadImages_Title": "Cargar imágenes automáticamente", + "SettingsLoadStyles_Title": "Cargar estilos automáticamente", + "SettingsMailSpacing_Description": "Ajustar el espacio para listar los correos.", + "SettingsMailSpacing_Title": "Espacio de Correo", + "SettingsFolderMenuStyle_Title": "Crear Carpetas Anidadas", + "SettingsFolderMenuStyle_Description": "Cambia si las carpetas de la cuenta deben anidarse dentro de un elemento de menú de la cuenta o no. Desactiva esto si te gusta el antiguo sistema de menú de Windows Mail", + "SettingsManageAccountSettings_Description": "Notificaciones, firmas, sincronización y otros ajustes por cuenta.", + "SettingsManageAccountSettings_Title": "Administrar ajustes de cuenta", + "SettingsManageLink_Description": "Mover elementos para añadir un nuevo enlace o eliminar el enlace existente.", + "SettingsManageLink_Title": "Administrar enlaces", + "SettingsMarkAsRead_Description": "Cambiar lo que debería pasar con el elemento seleccionado.", + "SettingsMarkAsRead_DontChange": "No marcar automáticamente el elemento como leído", + "SettingsMarkAsRead_SecondsToWait": "Segundos de espera: ", + "SettingsMarkAsRead_Timer": "Cuando se visualiza en el panel de lectura", + "SettingsMarkAsRead_Title": "Marcar artículo como leído", + "SettingsMarkAsRead_WhenSelected": "Al seleccionar", + "SettingsMessageList_Description": "Cambie cómo deben organizarse sus mensajes en la lista de correo.", + "SettingsMessageList_Title": "Lista de mensajes", + "SettingsNoAccountSetupMessage": "Aún no has configurado ninguna cuenta.", + "SettingsNotifications_Description": "Activar o desactivar notificaciones para esta cuenta.", + "SettingsNotifications_Title": "Notificaciones", + "SettingsPaneLength_Description": "Cambiar el ancho de la lista de correo.", + "SettingsPaneLength_Title": "Longitud del panel de la lista de correo", + "SettingsPaypal_Description": "Muestre mucho más amor ❤️ Todas las donaciones se agradecen.", + "SettingsPaypal_Title": "Dona vía PayPal", + "SettingsPersonalizationMailDisplayCompactMode": "Modo Compacto", + "SettingsPersonalizationMailDisplayMediumMode": "Modo Mediano", + "SettingsPersonalizationMailDisplaySpaciousMode": "Modo Espacioso", + "SettingsPersonalization_Description": "Cambia la apariencia de Wino a tu gusto.", + "SettingsPersonalization_Title": "Personalización", + "SettingsPrivacyPolicy_Description": "Revisar la Política de Privacidad.", + "SettingsPrivacyPolicy_Title": "Políticas de privacidad", + "SettingsReadingPane_Description": "Opciones de renderizado de correo.", + "SettingsReadingPane_Title": "Panel de lectura", + "SettingsReaderFont_Title": "Fuente por defecto del Lector", + "SettingsReaderFontFamily_Description": "Cambie el tamaño por defecto de la familia de fuentes y del tipo de letra para escribir correos.", + "SettingsFontFamily_Title": "Familia tipográfica", + "SettingsFontSize_Title": "Tamaño de la fuente", + "SettingsFontPreview_Title": "Vista previa", + "SettingsComposerFont_Title": "Fuente por defecto para Escribir", + "SettingsComposerFontFamily_Description": "Cambie el tamaño por defecto de la familia de fuentes y del tipo de letra para escribir correos.", + "SettingsRenameMergeAccount_Description": "Cambiar el nombre mostrado de las cuentas vinculadas.", + "SettingsRenameMergeAccount_Title": "Renombrar", + "SettingsSemanticZoom_Description": "Esto le permitirá hacer clic en los encabezados de la lista de mensajes e ir a una fecha específica", + "SettingsSemanticZoom_Title": "Zoom semántico para los encabezados de fechas", + "SettingsShowPreviewText_Description": "Ocultar/mostrar texto de la vista previa.", + "SettingsShowPreviewText_Title": "Mostrar texto de vista previa", + "SettingsShowSenderPictures_Description": "Ocultar/mostrar imágenes del remitente en miniatura.", + "SettingsShowSenderPictures_Title": "Mostrar Avatares de Remitente", + "SettingsPrefer24HourClock_Title": "Mostrar formato de reloj en 24 horas", + "SettingsPrefer24HourClock_Description": "Las horas de recepción de correos se mostrarán en formato de 24 horas en lugar de 12 (AM/PM)", + "SettingsSignature_Description": "Editar o eliminar firma de cuenta", + "SettingsSignature_Title": "Firma", + "SettingsStartupItem_Description": "Elemento principal de la cuenta para cargar la Bandeja de entrada al inicio.", + "SettingsStartupItem_Title": "Elemento de Inicio", + "SettingsStore_Description": "Mostrar un poco de amor ❤️", + "SettingsStore_Title": "Valorar en la tienda", + "SettingsThreads_Description": "Organizar mensajes en hilos de conversación.", + "SettingsThreads_Title": "Hilos de conversación", + "SettingsUnlinkAccounts_Description": "Eliminar el enlace entre cuentas. Esto no eliminará sus cuentas.", + "SettingsUnlinkAccounts_Title": "Desvincular Cuentas", + "SortingOption_Date": "por fecha", + "SortingOption_Name": "por nombre", + "StoreRatingDialog_MessageFirstLine": "Todos los comentarios son apreciados y harán mucho mejor Wino en el futuro. ¿Te gustaría calificar Wino en Microsoft Store?", + "StoreRatingDialog_MessageSecondLine": "¿Quieres calificar Wino Mail en Microsoft Store?", + "StoreRatingDialog_Title": "¿Te gusta Wino?", + "SystemFolderConfigDialog_ArchiveFolderDescription": "Archived messages will be moved to here.", + "SystemFolderConfigDialog_ArchiveFolderHeader": "Archive Folder", + "SystemFolderConfigDialog_DeletedFolderDescription": "Los mensajes borrados se moverán a aquí.", + "SystemFolderConfigDialog_DeletedFolderHeader": "Carpeta eliminados", + "SystemFolderConfigDialog_DraftFolderDescription": "Aquí se elaborarán nuevos correos/respuestas.", + "SystemFolderConfigDialog_DraftFolderHeader": "Carpeta de borrador", + "SystemFolderConfigDialog_JunkFolderDescription": "Todos los correos basura o spam estarán aquí.", + "SystemFolderConfigDialog_JunkFolderHeader": "Carpeta Basura/Spam", + "SystemFolderConfigDialog_MessageFirstLine": "Este servidor IMAP no soporta la extensión SPECIAL-USE, por lo que Wino no pudo configurar las carpetas del sistema correctamente.", + "SystemFolderConfigDialog_MessageSecondLine": "Por favor, seleccione las carpetas apropiadas para funciones específicas.", + "SystemFolderConfigDialog_SentFolderDescription": "Carpeta que los mensajes enviados serán almacenados.", + "SystemFolderConfigDialog_SentFolderHeader": "Carpeta Enviados", + "SystemFolderConfigDialog_Title": "Configurar Carpetas del Sistema", + "TestingImapConnectionMessage": "Probando conexión con el servidor...", + "Today": "Hoy", + "UnknownAddress": "dirección desconocida", + "UnknownDateHeader": "Fecha desconocida", + "UnknownGroupAddress": "dirección de grupo de correo desconocida", + "UnknownSender": "Remitente desconocido", + "Unsubscribe": "Darse de baja", + "ViewContactDetails": "Ver Detalles", + "WinoUpgradeDescription": "Wino offers 3 accounts to start with for free. If you need more than 3 accounts, please upgrade", + "WinoUpgradeMessage": "Actualizar a Cuentas Ilimitadas", + "WinoUpgradeRemainingAccountsMessage": "{0} de {1} cuentas gratuitas usadas.", + "Yesterday": "Ayer" +} diff --git a/Wino.Core.Domain/Translations/fr_FR/resources.json b/Wino.Core.Domain/Translations/fr_FR/resources.json new file mode 100644 index 00000000..38bab5eb --- /dev/null +++ b/Wino.Core.Domain/Translations/fr_FR/resources.json @@ -0,0 +1,468 @@ +{ + "AccountCreationDialog_Completed": "tout est prêt", + "AccountCreationDialog_Initializing": "initialisation", + "AccountCreationDialog_PreparingFolders": "Réception des informations du dossier.", + "AccountCreationDialog_SigninIn": "Enregistrement des informations du compte.", + "AccountEditDialog_Message": "Nom du compte", + "AccountEditDialog_Title": "Modifier le compte", + "AccountPickerDialog_Title": "Choisir un compte", + "AddHyperlink": "Ajouter", + "AutoDiscoveryProgressMessage": "Recherche des paramètres de messagerie...", + "BasicIMAPSetupDialog_AdvancedConfiguration": "Paramètres avancés", + "BasicIMAPSetupDialog_CredentialLocalMessage": "Vos identifiants seront uniquement stockés sur votre ordinateur.", + "BasicIMAPSetupDialog_Description": "Certains comptes requièrent des étapes supplémentaires pour se connecter", + "BasicIMAPSetupDialog_DisplayName": "Nom d'affichage", + "BasicIMAPSetupDialog_DisplayNamePlaceholder": "ex. Jean Dupont", + "BasicIMAPSetupDialog_LearnMore": "En savoir plus", + "BasicIMAPSetupDialog_MailAddress": "Adresse électronique", + "BasicIMAPSetupDialog_MailAddressPlaceholder": "jeandupont@exemple.fr", + "BasicIMAPSetupDialog_Password": "Mot de passe", + "BasicIMAPSetupDialog_Title": "Compte IMAP", + "Buttons_AddAccount": "Ajouter un compte", + "Buttons_ApplyTheme": "Appliquer le thème", + "Buttons_Browse": "Parcourir", + "Buttons_Cancel": "Annuler", + "Buttons_Close": "Fermer", + "Buttons_Create": "Créer", + "Buttons_CreateAccount": "Créer un compte", + "Buttons_Delete": "Supprimer", + "Buttons_Discard": "Ignorer", + "Buttons_EnableImageRendering": "Activer", + "Buttons_No": "Non", + "Buttons_Open": "Ouvrir", + "Buttons_Purchase": "Acheter", + "Buttons_RateWino": "Évaluer Wino", + "Buttons_Save": "Enregistrer", + "Buttons_SaveConfiguration": "Enregistrer la configuration", + "Buttons_Share": "Partager", + "Buttons_SignIn": "Connexion", + "Buttons_Yes": "Oui", + "Center": "Centrer", + "ComingSoon": "Bientôt disponible...", + "ComposerFrom": "De : ", + "ComposerSubject": "Objet : ", + "ComposerTo": "À : ", + "ClipboardTextCopied_Message": "{0} copied to clipboard.", + "ClipboardTextCopied_Title": "Copied", + "ClipboardTextCopyFailed_Message": "Failed to copy {0} to clipboard.", + "ComposerToPlaceholder": "click enter to input addresses", + "CustomThemeBuilder_AccentColorDescription": "Définir une couleur d'accentuation personnalisée si vous le souhaitez. Ne pas sélectionner une couleur utilisera votre couleur d'accentuation Windows.", + "CustomThemeBuilder_AccentColorTitle": "Couleur d'accentuation", + "CustomThemeBuilder_PickColor": "Choisir", + "CustomThemeBuilder_ThemeNameDescription": "Nom unique pour votre thème personnalisé.", + "CustomThemeBuilder_ThemeNameTitle": "Nom du thème", + "CustomThemeBuilder_Title": "Générateur de Thèmes personnalisés", + "CustomThemeBuilder_WallpaperDescription": "Définir un fond d'écran personnalisé pour Wino", + "CustomThemeBuilder_WallpaperTitle": "Définir un fond d'écran personnalisé", + "DialogMessage_AccountLimitMessage": "Vous avez atteint la limite de création de compte.\nVoulez-vous acheter le module 'Compte illimité' pour continuer ?", + "DialogMessage_AccountLimitTitle": "Limite de compte atteinte", + "DialogMessage_CleanupFolderMessage": "Voulez-vous supprimer définitivement tous les messages de ce dossier ?", + "DialogMessage_CleanupFolderTitle": "Nettoyer le dossier", + "DialogMessage_ComposerMissingRecipientMessage": "Le message n'a pas de destinataire.", + "DialogMessage_ComposerValidationFailedTitle": "Validation échouée", + "DialogMessage_CreateLinkedAccountMessage": "Give this new link a name. Accounts will be merged under this name.", + "DialogMessage_CreateLinkedAccountTitle": "Account Link Name", + "DialogMessage_DeleteAccountConfirmationMessage": "Supprimer {0} ?", + "DialogMessage_DeleteAccountConfirmationTitle": "Toutes les données associées à ce compte seront définitivement supprimées du disque.", + "DialogMessage_DiscardDraftConfirmationMessage": "Ce brouillon sera supprimé. Voulez-vous continuer ?", + "DialogMessage_DiscardDraftConfirmationTitle": "Supprimer le brouillon", + "DialogMessage_HardDeleteConfirmationMessage": "Supprimer définitivement", + "DialogMessage_HardDeleteConfirmationTitle": "Le ou les messages seront supprimés définitivement. Voulez-vous continuer ?", + "DialogMessage_NoAccountsForCreateMailMessage": "Vous n'avez aucun compte pour créer un message.", + "DialogMessage_NoAccountsForCreateMailTitle": "Compte manquant", + "DialogMessage_RenameLinkedAccountsMessage": "Entrez un nouveau nom pour le compte lié", + "DialogMessage_RenameLinkedAccountsTitle": "Rename Linked Account", + "DialogMessage_UnlinkAccountsConfirmationMessage": "Cette opération ne supprimera pas vos comptes mais ne supprimera que le lien pour les connexions de dossiers partagés. Voulez-vous continuer?", + "DialogMessage_UnlinkAccountsConfirmationTitle": "Dissocier les comptes", + "DialogMessage_EmptySubjectConfirmation": "Sujet manquant", + "DialogMessage_EmptySubjectConfirmationMessage": "Le message n'a pas d'objet. Voulez-vous continuer ?", + "Dialog_DontAskAgain": "Ne plus demander", + "DiscordChannelDisclaimerMessage": "Wino n'a pas son propre serveur Discord, mais un canal spécial « wino-mail » est hébergé sur le serveur « Developer Sanctuary ».\nPour obtenir les mises à jour sur Wino, veuillez rejoindre le serveur Developer Sanctuary et suivre le canal « wino-mail » sous « Projets communautaires ».\n\nVous serez dirigé vers l'URL du serveur car Discord ne prend pas en charge les invitations à des chaînes.", + "DiscordChannelDisclaimerTitle": "Informations importantes sur Discord", + "Draft": "Brouillon", + "EditorToolbarOption_Draw": "Dessiner", + "EditorToolbarOption_Format": "Format", + "EditorToolbarOption_Insert": "Insérer", + "EditorToolbarOption_None": "Aucun", + "EditorToolbarOption_Options": "Options", + "ElementTheme_Dark": "Mode sombre", + "ElementTheme_Default": "Utiliser les paramètres du système", + "ElementTheme_Light": "Mode clair", + "Emoji": "Emoji", + "Exception_ImapClientPoolFailed": "Échec du groupement de clients IMAP.", + "Exception_AuthenticationCanceled": "Authentification annulée", + "Exception_CustomThemeExists": "Ce thème existe déjà.", + "Exception_CustomThemeMissingName": "Vous devez indiquer un nom.", + "Exception_CustomThemeMissingWallpaper": "Vous devez fournir une image d'arrière-plan personnalisée.", + "Exception_FailedToSynchronizeFolders": "Échec de la synchronisation des dossiers", + "Exception_GoogleAuthCallbackNull": "Callback uri is null on activation.", + "Exception_GoogleAuthCorruptedCode": "Réponse d’autorisation corrompue.", + "Exception_GoogleAuthError": "Erreur d'autorisation OAuth : {0}", + "Exception_GoogleAuthInvalidResponse": "Received request with invalid state ({0})", + "Exception_GoogleAuthorizationCodeExchangeFailed": "Authorization code exchange failed.", + "Exception_InvalidSystemFolderConfiguration": "La configuration du dossier système n’est pas valide. Vérifiez la configuration et réessayez.", + "Exception_NullAssignedAccount": "Assigned account is null", + "Exception_NullAssignedFolder": "Assigned folder is null", + "Exception_SynchronizerFailureHTTP": "Response handling failed with error HTTP code {0}", + "Exception_TokenGenerationFailed": "Échec de la génération du jeton", + "Exception_TokenInfoRetrivalFailed": "Impossible de récupérer les informations de l'utilisateur.", + "Exception_UnknowErrorDuringAuthentication": "Unknown error occurred during authentication", + "Exception_UnsupportedAction": "Action {0} is not implemented in request processor", + "Exception_UnsupportedProvider": "Ce fournisseur n'est pas pris en charge.", + "Exception_UnsupportedSynchronizerOperation": "Cette opération n'est pas supportée pour {0}", + "Exception_UserCancelSystemFolderSetupDialog": "L'utilisateur a annulé la boîte de dialogue de configuration du dossier système.", + "Files": "Fichiers", + "FilteringOption_All": "Tout", + "FilteringOption_Flagged": "Marqué", + "FilteringOption_Unread": "Non lus", + "Focused": "Focused", + "FolderOperation_CreateSubFolder": "Créer un sous-dossier", + "FolderOperation_Delete": "Supprimer", + "FolderOperation_DontSync": "Ne pas synchroniser ce dossier", + "FolderOperation_Empty": "Vider ce dossier", + "FolderOperation_MarkAllAsRead": "Marquer tout comme lu", + "FolderOperation_Move": "Déplacer", + "DragMoveToFolderCaption": "Déplacer vers {0}", + "FolderOperation_None": "Aucun", + "FolderOperation_Pin": "Épingler", + "FolderOperation_Rename": "Renommer", + "FolderOperation_Unpin": "Désépingler", + "HoverActionOption_Archive": "Archiver", + "HoverActionOption_Delete": "Supprimer", + "HoverActionOption_MoveJunk": "Déplacer vers courriers indésirables", + "HoverActionOption_ToggleFlag": "Marquer / Démarquer", + "HoverActionOption_ToggleRead": "Lu / Non lu", + "MergedAccountCommonFolderInbox": "Boîte de réception", + "MergedAccountCommonFolderSent": "Envoyé", + "MergedAccountCommonFolderDraft": "Brouillon", + "MergedAccountCommonFolderJunk": "Indésirables", + "MergedAccountCommonFolderTrash": "Supprimé", + "MergedAccountCommonFolderArchive": "Archives", + "IMAPSetupDialog_AccountType": "Type de compte", + "IMAPSetupDialog_DisplayName": "Nom d'affichage", + "IMAPSetupDialog_DisplayNamePlaceholder": "ex. Jean Dupont", + "IMAPSetupDialog_IncomingMailServer": "Serveur de courrier entrant", + "IMAPSetupDialog_IncomingMailServerPort": "Port", + "IMAPSetupDialog_MailAddress": "Adresse e-mail", + "IMAPSetupDialog_MailAddressPlaceholder": "personne@example.com", + "IMAPSetupDialog_OutgoingMailServer": "Serveur de courrier sortant (SMTP)", + "IMAPSetupDialog_OutgoingMailServerPassword": "Mot de passe du serveur sortant", + "IMAPSetupDialog_OutgoingMailServerPort": "Port", + "IMAPSetupDialog_OutgoingMailServerRequireAuthentication": "Le serveur sortant nécessite une authentification", + "IMAPSetupDialog_OutgoingMailServerUsername": "Nom d'utilisateur du serveur sortant", + "IMAPSetupDialog_Password": "Mot de passe", + "IMAPSetupDialog_RequireSSLForIncomingMail": "SSL requis pour les courriers entrants", + "IMAPSetupDialog_RequireSSLForOutgoingMail": "SSL requis pour les courriers sortants", + "IMAPSetupDialog_Title": "Configuration IMAP avancée", + "IMAPSetupDialog_UseSameConfig": "Utilisez le même nom d'utilisateur et mot de passe pour envoyer un e-mail", + "IMAPSetupDialog_Username": "Nom d’utilisateur", + "IMAPSetupDialog_UsernamePlaceholder": "johndoe, johndoe@fabrikam.com, domaine/johndoe", + "ImageRenderingDisabled": "L'affichage d'image est désactivé pour ce message.", + "InfoBarAction_Enable": "Activer", + "InfoBarMessage_SynchronizationDisabledFolder": "Ce dossier est désactivé pour la synchronisation.", + "InfoBarTitle_SynchronizationDisabledFolder": "Dossier désactivé", + "GeneralTitle_Error": "Error", + "GeneralTitle_Warning": "Warning", + "GeneralTitle_Info": "Information", + "Info_AccountCreatedMessage": "{0} est créé", + "Info_AccountCreatedTitle": "Création de compte", + "Info_AccountCreationFailedTitle": "Échec de la création du compte", + "Info_AccountDeletedMessage": "{0} a été supprimé avec succès.", + "Info_AccountDeletedTitle": "Compte supprimé", + "Info_AccountIssueFixFailedTitle": "Échec", + "Info_AccountIssueFixSuccessMessage": "Correction de tous les problèmes de compte.", + "Info_AccountIssueFixSuccessTitle": "Réussi", + "Info_AttachmentOpenFailedMessage": "Impossible d'ouvrir la pièce-jointe.", + "Info_AttachmentOpenFailedTitle": "Échec", + "Info_AttachmentSaveFailedMessage": "Impossible d'enregistrer cette pièce-jointe.", + "Info_AttachmentSaveFailedTitle": "Échec", + "Info_AttachmentSaveSuccessMessage": "La pièce jointe est enregistrée.", + "Info_AttachmentSaveSuccessTitle": "Pièce jointe enregistrée", + "Info_BackgroundExecutionDeniedMessage": "L'exécution en arrière-plan de l'application est refusée. Cela peut affecter la synchronisation en arrière-plan et les notifications en direct.", + "Info_BackgroundExecutionDeniedTitle": "Exécution en arrière-plan refusée", + "Info_BackgroundExecutionUnknownErrorMessage": "Unknown exception occurred when registering background synchronizer.", + "Info_BackgroundExecutionUnknownErrorTitle": "Échec de l'exécution en arrière-plan", + "Info_ComposerMissingMIMEMessage": "Impossible de trouver le fichier MIME. La synchronisation peut aider.", + "Info_ComposerMissingMIMETitle": "Failed", + "Info_ContactExistsMessage": "Ce contact est déjà dans la liste des destinataires.", + "Info_ContactExistsTitle": "Contact Exists", + "Info_DraftFolderMissingMessage": "Le dossier Brouillon est manquant pour ce compte. Veuillez vérifier vos paramètres de compte.", + "Info_DraftFolderMissingTitle": "Dossier brouillon manquant", + "Info_FileLaunchFailedTitle": "Échec du lancement du fichier", + "Info_InvalidAddressMessage": "{0} n'est pas une adresse e-mail valide.", + "Info_InvalidAddressTitle": "Addresse invalide", + "Info_InvalidMoveTargetMessage": "Vous ne pouvez pas déplacer les messages sélectionnés dans ce dossier.", + "Info_InvalidMoveTargetTitle": "Cible non valide", + "Info_LogsNotFoundMessage": "Il n'y a pas de journaux à partager.", + "Info_LogsNotFoundTitle": "Journaux introuvables", + "Info_LogsSavedMessage": "{0} est sauvegardé dans le dossier sélectionné.", + "Info_LogsSavedTitle": "Enregistré", + "Info_MailRenderingFailedMessage": "Ce courrier est corrompu ou ne peut pas être ouvert.\n{0}", + "Info_MailRenderingFailedTitle": "Le rendu a échoué", + "Info_MessageCorruptedMessage": "Ce message est corrompu.", + "Info_MessageCorruptedTitle": "Erreur", + "Info_MissingFolderMessage": "{0} doesn't exist for this account.", + "Info_MissingFolderTitle": "Dossier manquant", + "Info_PurchaseExistsMessage": "Il semble que ce produit ait déjà été acheté auparavant.", + "Info_PurchaseExistsTitle": "Produit existant", + "Info_PurchaseThankYouMessage": "Merci", + "Info_PurchaseThankYouTitle": "Achat réussi", + "Info_RequestCreationFailedTitle": "Échec de la création de demandes", + "Info_ReviewNetworkErrorMessage": "Il y a eu un problème de réseau avec votre avis.", + "Info_ReviewNetworkErrorTitle": "Problème de réseau", + "Info_ReviewNewMessage": "Tous les commentaires sont appréciés. Merci pour votre avis !", + "Info_ReviewSuccessTitle": "Merci", + "Info_ReviewUnknownErrorMessage": "Il y a eu un problème inconnu avec votre avis. ({0})", + "Info_ReviewUnknownErrorTitle": "Erreur inconnue", + "Info_ReviewUpdatedMessage": "Merci pour la mise à jour de votre avis.", + "Info_SignatureDisabledMessage": "La signature pour ce compte est désactivée", + "Info_SignatureDisabledTitle": "Réussi", + "Info_SignatureSavedMessage": "La nouvelle signature est enregistrée", + "Info_SignatureSavedTitle": "Réussi", + "Info_SyncCanceledMessage": "Annulé", + "Info_SyncCanceledTitle": "Synchronisation", + "Info_SyncFailedTitle": "Synchronisation échouée", + "Info_UnsupportedFunctionalityDescription": "Cette fonctionnalité n’est pas encore implémentée.", + "Info_UnsupportedFunctionalityTitle": "Non supporté", + "Info_UnsubscribeLinkInvalidTitle": "Url de désinscription non valide", + "Info_UnsubscribeLinkInvalidMessage": "This unsubscribe link is invalid. Failed to unsubscribe from the list.", + "ImapAdvancedSetupDialog_AuthenticationMethod": "Méthode d’authentification", + "ImapAdvancedSetupDialog_ConnectionSecurity": "Sécurité de la connexion", + "ImapAuthenticationMethod_Auto": "Automatique", + "ImapAuthenticationMethod_CramMD5": "CRAM-MD5", + "ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5", + "ImapAuthenticationMethod_None": "Pas d'authentification", + "ImapAuthenticationMethod_Plain": "Mot de passe normal", + "ImapAuthenticationMethod_EncryptedPassword": "Mot de passe chiffré", + "ImapAuthenticationMethod_Ntlm": "NTLM", + "ImapConnectionSecurity_None": "Aucun", + "ImapConnectionSecurity_SslTls": "SSL/TLS", + "ImapConnectionSecurity_StartTls": "STARTTLS", + "ImapConnectionSecurity_Auto": "Automatique", + "Justify": "Justifier", + "Left": "Gauche", + "Link": "Lien", + "LinkedAccountsCreatePolicyMessage": "you must have at least 2 accounts to create link\nlink will be removed on save", + "LinkedAccountsTitle": "Comptes liés", + "MailOperation_AlwaysMoveFocused": "Always Move to Focused", + "MailOperation_AlwaysMoveOther": "Always Move to Other", + "MailOperation_Archive": "Archiver", + "MailOperation_ClearFlag": "Enlever la balise", + "MailOperation_DarkEditor": "Sombre", + "MailOperation_Delete": "Supprimer", + "MailOperation_ExportPDF": "Exporter en PDF", + "MailOperation_Find": "Rechercher", + "MailOperation_Forward": "Transférer", + "MailOperation_Ignore": "Ignorer", + "MailOperation_LightEditor": "Clair", + "MailOperation_MarkAsJunk": "Marquer comme indésirable", + "MailOperation_MarkAsRead": "Marquer comme lu", + "MailOperation_MarkAsUnread": "Marquer comme non lu", + "MailOperation_MarkNotJunk": "Marquer comme non indésirable", + "MailOperation_Move": "Déplacer", + "MailOperation_MoveFocused": "Move to Focused", + "MailOperation_MoveJunk": "Déplacer vers indésirables", + "MailOperation_MoveOther": "Déplacer vers autres", + "MailOperation_Navigate": "Naviguer", + "MailOperation_Print": "Imprimer", + "MailOperation_Reply": "Répondre", + "MailOperation_ReplyAll": "Répondre à tous", + "MailOperation_SaveAs": "Enregistrer sous", + "MailOperation_SetFlag": "Définir drapeau", + "MailOperation_Unarchive": "Désarchiver", + "MailOperation_Zoom": "Zoom", + "MailsSelected": "{0} item(s) selected", + "MarkFlagUnflag": "Marquer ou Démarquer ce message", + "MarkReadUnread": "Marquer comme lu/non lu", + "MenuManageAccounts": "Gérer les comptes", + "MenuNewMail": "Nouveau message", + "MenuMergedAccountItemAccountsSuffix": " Comptes", + "MenuRate": "Évaluer Wino", + "MenuSettings": "Paramètres", + "MergedAccountsAvailableAccountsTitle": "Comptes disponibles", + "More": "Plus", + "MoveMailDialog_InvalidFolderMessage": "{0} is not a valid folder for this mail.", + "MoveMailDialog_Title": "Choisissez un dossier", + "NewAccountDialog_AccountName": "Nom du compte", + "NewAccountDialog_AccountNameDefaultValue": "Personnel", + "NewAccountDialog_AccountNamePlaceholder": "Ex. courrier personnel", + "NewAccountDialog_Title": "Ajouter un nouveau compte", + "NoMailSelected": "aucun message sélectionné", + "NoMessageCrieteria": "aucuns messages ne correspond à vos critères de recherche.", + "NoMessageEmptyFolder": "ce dossier est vide.", + "Notifications_MultipleNotificationsMessage": "Vous avez {0} nouveaux messages", + "Notifications_MultipleNotificationsTitle": "Nouveaux courriers", + "Notifications_WinoUpdatedMessage": "Vérifier la nouvelle version {0}", + "Notifications_WinoUpdatedTitle": "Wino Mail a été mis à jour.", + "Other": "Autre", + "PaneLengthOption_Default": "Défaut", + "PaneLengthOption_ExtraLarge": "Très grand", + "PaneLengthOption_Large": "Grand", + "PaneLengthOption_Medium": "Moyen", + "PaneLengthOption_Micro": "Très petit", + "PaneLengthOption_Small": "Petit", + "Photos": "Images", + "PreparingFoldersMessage": "Préparation des dossiers", + "ProviderDetail_Gmail_Description": "Compte Google", + "ProviderDetail_IMAP_Description": "Serveur IMAP/SMTP personnalisé", + "ProviderDetail_IMAP_Title": "Serveur IMAP", + "Results": "Résultats", + "Right": "Droite", + "SynchronizationFolderReport_Success": "À jour", + "SynchronizationFolderReport_Failed": "la synchronisation a échouée", + "SearchBarPlaceholder": "Rechercher", + "SearchingIn": "searching in", + "SettingsAboutGithub_Description": "Allez au dépôt GitHub du gestionnaire de tickets.", + "SettingsAboutGithub_Title": "GitHub", + "SettingsAccountManagementAppendMessage_Title": "Append messages to Sent folder", + "SettingsAccountManagementAppendMessage_Description": "Créer une copie du message dans le dossier Envoyé après l'envoi du brouillon. Activez cette option si vous ne voyez pas vos e-mails après les avoir envoyés dans le dossier Envoyés.", + "SettingsEditLinkedInbox_Title": "Edit Linked Inbox", + "SettingsEditLinkedInbox_Description": "Add / remove accounts, rename or break the link between accounts.", + "SettingsAboutVersion": "Version ", + "SettingsAboutWinoDescription": "Client de messagerie léger pour les périphériques Windows.", + "SettingsAbout_Description": "En savoir plus sur Wino.", + "SettingsAbout_Title": "À propos", + "SettingsAccentColor_Description": "Changer la couleur d'accentuation de l'application", + "SettingsAccentColor_Title": "Couleur d'accentuation", + "SettingsAccentColor_UseWindowsAccentColor": "Utiliser ma couleur d'accentuation Windows", + "SettingsAccountName_Description": "Changer le nom du compte.", + "SettingsAccountName_Title": "Nom du compte", + "SettingsApplicationTheme_Description": "Personnalisez Wino avec différents thèmes d'application personnalisés pour vous.", + "SettingsApplicationTheme_Title": "Thème de l'application", + "SettingsAvailableThemes_Description": "Select a theme from Wino's own collection for your taste or apply your own themes.", + "SettingsAvailableThemes_Title": "Thèmes disponibles", + "SettingsCustomTheme_Description": "Créez votre propre thème personnalisé avec un fond d'écran personnalisé et une couleur d'accentuation.", + "SettingsCustomTheme_Title": "Thème personnalisé", + "SettingsDeleteAccount_Description": "Supprimer tous les e-mails et identifiants associés à ce compte.", + "SettingsDeleteAccount_Title": "Supprimer ce compte", + "SettingsDeleteProtection_Description": "Wino devrait-il vous demander une comfirmation chaque fois que vous essayez de supprimer définitivement un mail en utilisant les touches Maj + Supprimer?", + "SettingsDeleteProtection_Title": "Protection contre la suppression permanente", + "SettingsDiagnostics_Description": "Pour les développeurs", + "SettingsDiagnostics_Title": "Diagnostics", + "SettingsDiscord_Description": "Get regular development updates, join roadmap discussions and provide feedback.", + "SettingsDiscord_Title": "Canal Discord", + "SettingsElementThemeSelectionDisabled": "Element theme selection is disabled when application theme is selected other than Default.", + "SettingsElementTheme_Description": "Select a Windows theme for Wino", + "SettingsElementTheme_Title": "Thème de l'élément", + "SettingsEnableHoverActions_Title": "Activer les actions de survol", + "SettingsEnableIMAPLogs_Description": "Enable this to provide details about IMAP connectivity issuses you had during IMAP server setup.", + "SettingsEnableIMAPLogs_Title": "Activer les journaux de protocole IMAP", + "SettingsEnableLogs_Description": "Je pourrais avoir besoin de Logs pour que les tickets ouverts sur GitHub puissent être diagnostiqués. Aucun de ces journaux n'exposera vos identifiants ou vos informations sensées.", + "SettingsEnableLogs_Title": "Activer les journaux", + "SettingsEnableSignature": "Activer la signature", + "SettingsExpandOnStartup_Description": "Définissez si Wino doit étendre les dossiers de ce compte au démarrage.", + "SettingsExpandOnStartup_Title": "Développer le menu au démarrage", + "SettingsExternalContent_Description": "Gérer les paramètres de contenu externe lors du rendu des messages.", + "SettingsExternalContent_Title": "Contenu externe", + "SettingsFocusedInbox_Description": "Définit si la boîte de réception doit être divisée en deux en Prioritaire - Autre.", + "SettingsFocusedInbox_Title": "Boîte de réception Prioritaire", + "SettingsFolderSync_Description": "Enable or disable specific folders for synchronization.", + "SettingsFolderSync_Title": "Folder Synchronization", + "SettingsFolderOptions_Title": "Folder Configuration", + "SettingsFolderOptions_Description": "Change individual folder settings like enable/disable sync or show/hide unread badge.", + "SettingsHoverActionCenter": "Center Action", + "SettingsHoverActionLeft": "Action de gauche", + "SettingsHoverActionRight": "Action de droite", + "SettingsHoverActions_Description": "Select 3 actions to show up when you hover over the mails with cursor.", + "SettingsHoverActions_Title": "Actions de survol", + "SettingsLanguage_Description": "Changer la langue d'affichage de Wino.", + "SettingsLanguage_Title": "Langue d'affichage", + "CategoriesFolderNameOverride": "Catégories", + "MoreFolderNameOverride": "Plus", + "SettingsOptions_Title": "Paramètres", + "SettingsLinkAccounts_Description": "Merge multiple accounts into one. See mails from one Inbox together.", + "SettingsLinkAccounts_Title": "Create Linked Accounts", + "SettingsLinkedAccountsSave_Description": "Modify the current link with the new accounts.", + "SettingsLinkedAccountsSave_Title": "Enregistrer les modifications", + "SettingsLoadImages_Title": "Load images automatically", + "SettingsLoadStyles_Title": "Load styles automatically", + "SettingsMailSpacing_Description": "Adjust the spacing for listing mails.", + "SettingsMailSpacing_Title": "Espacement du courrier", + "SettingsFolderMenuStyle_Title": "Create Nested Folders", + "SettingsFolderMenuStyle_Description": "Change whether account folders should be nested inside an account menu item or not. Toggle this off if you like the old menu system in Windows Mail", + "SettingsManageAccountSettings_Description": "Notifications, signatures, synchronisation et autres paramètres par compte.", + "SettingsManageAccountSettings_Title": "Manage Account Settings", + "SettingsManageLink_Description": "Move items to add new link or remove existing link.", + "SettingsManageLink_Title": "Manage Link", + "SettingsMarkAsRead_Description": "Change what should happen to the selected item.", + "SettingsMarkAsRead_DontChange": "Don't automatically mark item as read", + "SettingsMarkAsRead_SecondsToWait": "Secondes à attendre : ", + "SettingsMarkAsRead_Timer": "When viewed in the reading pane", + "SettingsMarkAsRead_Title": "Marquer l'élément comme lu", + "SettingsMarkAsRead_WhenSelected": "Lorsque sélectionné", + "SettingsMessageList_Description": "Modifiez la façon dont vos messages doivent être organisés dans la liste de courriels.", + "SettingsMessageList_Title": "Liste des messages", + "SettingsNoAccountSetupMessage": "Vous n'avez pas encore configuré de comptes.", + "SettingsNotifications_Description": "Activer ou désactiver les notifications pour ce compte.", + "SettingsNotifications_Title": "Notifications", + "SettingsPaneLength_Description": "Modifie la largeur de la liste de courriels.", + "SettingsPaneLength_Title": "Longueur du panneau de la liste d'emails", + "SettingsPaypal_Description": "Montrez-nous plus d'amour ❤️ Tous les dons sont appréciés.", + "SettingsPaypal_Title": "Faire un don via PayPal", + "SettingsPersonalizationMailDisplayCompactMode": "Mode compact", + "SettingsPersonalizationMailDisplayMediumMode": "Mode moyen", + "SettingsPersonalizationMailDisplaySpaciousMode": "Mode spacieux", + "SettingsPersonalization_Description": "Changez l’apparence de Wino comme vous le souhaitez.", + "SettingsPersonalization_Title": "Personnalisation", + "SettingsPrivacyPolicy_Description": "Examinez la politique de confidentialité.", + "SettingsPrivacyPolicy_Title": "Politique de confidentialité", + "SettingsReadingPane_Description": "Options de rendu du courriel.", + "SettingsReadingPane_Title": "Panneau de lecture", + "SettingsReaderFont_Title": "Police par défaut du lecteur", + "SettingsReaderFontFamily_Description": "Modifier la police par défaut et la taille de police pour la lecture des messages.", + "SettingsFontFamily_Title": "Famille de polices", + "SettingsFontSize_Title": "Taille de la police", + "SettingsFontPreview_Title": "Aperçu", + "SettingsComposerFont_Title": "Police par défaut du compositeur", + "SettingsComposerFontFamily_Description": "Modifier la police et la taille par défaut lors de la composition des mails.", + "SettingsRenameMergeAccount_Description": "Changer le nom d'affichage des comptes liés.", + "SettingsRenameMergeAccount_Title": "Renommer", + "SettingsSemanticZoom_Description": "This will allow you to click on the headers in messages list and go to specific date", + "SettingsSemanticZoom_Title": "Semantic Zoom for Date Headers", + "SettingsShowPreviewText_Description": "Hide/show thepreview text.", + "SettingsShowPreviewText_Title": "Show Preview Text", + "SettingsShowSenderPictures_Description": "Masquer/afficher les vignettes des images de l'expéditeur.", + "SettingsShowSenderPictures_Title": "Afficher l'avatar de l'expéditeur", + "SettingsPrefer24HourClock_Title": "Display Clock Format in 24 Hours", + "SettingsPrefer24HourClock_Description": "Les heures de réception du courrier seront affichées au format 24 heures au lieu de 12 (AM/PM)", + "SettingsSignature_Description": "Éditer ou supprimer la signature du compte", + "SettingsSignature_Title": "Signature", + "SettingsStartupItem_Description": "Primary account item to load Inbox at startup.", + "SettingsStartupItem_Title": "Startup Item", + "SettingsStore_Description": "Montrer nous un peu d'amour ❤️", + "SettingsStore_Title": "Évaluez l'application sur le store", + "SettingsThreads_Description": "Organiser les messages en fils de conversation.", + "SettingsThreads_Title": "Affichage en mode conversation", + "SettingsUnlinkAccounts_Description": "Remove the link between accounts. This will not delete your accounts.", + "SettingsUnlinkAccounts_Title": "Dissocier les comptes", + "SortingOption_Date": "Par date", + "SortingOption_Name": "Par nom", + "StoreRatingDialog_MessageFirstLine": "All feedbacks are appreciated and they will make much Wino better in the future. Would you like to rate Wino in Microsoft Store?", + "StoreRatingDialog_MessageSecondLine": "Would you like to rate Wino Mail in Microsoft Store?", + "StoreRatingDialog_Title": "Vous aimez Wino ?", + "SystemFolderConfigDialog_ArchiveFolderDescription": "Archived messages will be moved to here.", + "SystemFolderConfigDialog_ArchiveFolderHeader": "Archive Folder", + "SystemFolderConfigDialog_DeletedFolderDescription": "Deleted messages will be moved to here.", + "SystemFolderConfigDialog_DeletedFolderHeader": "Dossier supprimé", + "SystemFolderConfigDialog_DraftFolderDescription": "Les nouveaux courriels/réponses seront créés ici.", + "SystemFolderConfigDialog_DraftFolderHeader": "Dossier de brouillon", + "SystemFolderConfigDialog_JunkFolderDescription": "Tous les courriers indésirables et spam/spam seront ici.", + "SystemFolderConfigDialog_JunkFolderHeader": "Dossier indésirable/spam", + "SystemFolderConfigDialog_MessageFirstLine": "This IMAP server doesn't support SPECIAL-USE extension hence Wino couldn't setup the system folders properly.", + "SystemFolderConfigDialog_MessageSecondLine": "Please select the appropriate folders for specific functionalities.", + "SystemFolderConfigDialog_SentFolderDescription": "Le dossier contenant les messages envoyés sera stocké.", + "SystemFolderConfigDialog_SentFolderHeader": "Dossier envoyé", + "SystemFolderConfigDialog_Title": "Configurer les dossiers système", + "TestingImapConnectionMessage": "Test de la connexion au serveur...", + "Today": "Aujourd'hui", + "UnknownAddress": "Adresse inconnue", + "UnknownDateHeader": "Date inconnue", + "UnknownGroupAddress": "Adresse du groupe de messagerie inconnue", + "UnknownSender": "Expéditeur inconnu", + "Unsubscribe": "Se désabonner", + "ViewContactDetails": "Voir les détails", + "WinoUpgradeDescription": "Wino offers 3 accounts to start with for free. If you need more than 3 accounts, please upgrade", + "WinoUpgradeMessage": "Mettre à niveau vers des comptes illimités", + "WinoUpgradeRemainingAccountsMessage": "{0} comptes gratuits utilisés sur {1}.", + "Yesterday": "Hier" +} diff --git a/Wino.Core.Domain/Translations/it_IT/resources.json b/Wino.Core.Domain/Translations/it_IT/resources.json new file mode 100644 index 00000000..c2f36e60 --- /dev/null +++ b/Wino.Core.Domain/Translations/it_IT/resources.json @@ -0,0 +1,468 @@ +{ + "AccountCreationDialog_Completed": "tutto fatto", + "AccountCreationDialog_Initializing": "inizializzazione", + "AccountCreationDialog_PreparingFolders": "Stiamo ricevendo informazioni sulle cartelle al momento.", + "AccountCreationDialog_SigninIn": "Le informazioni dell'account sono in fase di salvataggio.", + "AccountEditDialog_Message": "Nome account", + "AccountEditDialog_Title": "Modifica account", + "AccountPickerDialog_Title": "Scegli un account", + "AddHyperlink": "Aggiungi", + "AutoDiscoveryProgressMessage": "Ricerca delle impostazioni di posta...", + "BasicIMAPSetupDialog_AdvancedConfiguration": "Configurazione avanzata", + "BasicIMAPSetupDialog_CredentialLocalMessage": "Le tue credenziali verranno memorizzate solo localmente sul tuo computer.", + "BasicIMAPSetupDialog_Description": "Alcuni account richiedono passaggi aggiuntivi per accedere", + "BasicIMAPSetupDialog_DisplayName": "Mostra nome", + "BasicIMAPSetupDialog_DisplayNamePlaceholder": "ad esempio Mario Rossi", + "BasicIMAPSetupDialog_LearnMore": "Per saperne di più", + "BasicIMAPSetupDialog_MailAddress": "Indirizzo E-Mail", + "BasicIMAPSetupDialog_MailAddressPlaceholder": "johndoe@fabrikam.com", + "BasicIMAPSetupDialog_Password": "Password", + "BasicIMAPSetupDialog_Title": "Account IMAP", + "Buttons_AddAccount": "Aggiungi account", + "Buttons_ApplyTheme": "Applica tema", + "Buttons_Browse": "Sfoglia", + "Buttons_Cancel": "Annulla", + "Buttons_Close": "Chiudi", + "Buttons_Create": "Crea", + "Buttons_CreateAccount": "Crea account", + "Buttons_Delete": "Elimina", + "Buttons_Discard": "Scarta", + "Buttons_EnableImageRendering": "Attiva", + "Buttons_No": "No", + "Buttons_Open": "Apri", + "Buttons_Purchase": "Acquista", + "Buttons_RateWino": "Valuta Wino", + "Buttons_Save": "Salva", + "Buttons_SaveConfiguration": "Salva configurazione", + "Buttons_Share": "Condividi", + "Buttons_SignIn": "Accedi", + "Buttons_Yes": "Sì", + "Center": "Centra", + "ComingSoon": "Prossimamente...", + "ComposerFrom": "Da: ", + "ComposerSubject": "Oggetto: ", + "ComposerTo": "A: ", + "ClipboardTextCopied_Message": "{0} copied to clipboard.", + "ClipboardTextCopied_Title": "Copied", + "ClipboardTextCopyFailed_Message": "Failed to copy {0} to clipboard.", + "ComposerToPlaceholder": "clicca Invio per inserire gli indirizzi", + "CustomThemeBuilder_AccentColorDescription": "Imposta un colore principale personalizzato, se vuoi. Non selezionare un colore userà il colore principale di Windows.", + "CustomThemeBuilder_AccentColorTitle": "Colore principale", + "CustomThemeBuilder_PickColor": "Scegli", + "CustomThemeBuilder_ThemeNameDescription": "Nome unico del tuo tema personalizzato.", + "CustomThemeBuilder_ThemeNameTitle": "Nome del tema", + "CustomThemeBuilder_Title": "Creatore di Temi personalizzati", + "CustomThemeBuilder_WallpaperDescription": "Imposta uno sfondo personalizzato per Wino", + "CustomThemeBuilder_WallpaperTitle": "Imposta uno sfondo personalizzato", + "DialogMessage_AccountLimitMessage": "Hai raggiunto il limite di creazione degli account.\nVuoi acquistare il componente aggiuntivo 'Account Illimitati' per continuare?", + "DialogMessage_AccountLimitTitle": "Raggiunto il limite degli account", + "DialogMessage_CleanupFolderMessage": "Vuoi eliminare definitivamente tutte le email in questa cartella?", + "DialogMessage_CleanupFolderTitle": "Svuota cartella", + "DialogMessage_ComposerMissingRecipientMessage": "Il messaggio non ha alcun destinatario.", + "DialogMessage_ComposerValidationFailedTitle": "Convalida non riuscita", + "DialogMessage_CreateLinkedAccountMessage": "Dai un nome a questo nuovo collegamento. Gli account saranno uniti sotto questo nome.", + "DialogMessage_CreateLinkedAccountTitle": "Nome dell'account collegato", + "DialogMessage_DeleteAccountConfirmationMessage": "Eliminare {0}?", + "DialogMessage_DeleteAccountConfirmationTitle": "Tutti i dati associati a questo account verranno eliminati dal disco in modo permanente.", + "DialogMessage_DiscardDraftConfirmationMessage": "Questa bozza verrà scartata. Vuoi continuare?", + "DialogMessage_DiscardDraftConfirmationTitle": "Scarta bozza", + "DialogMessage_HardDeleteConfirmationMessage": "Elimina definitivamente", + "DialogMessage_HardDeleteConfirmationTitle": "I messaggi verranno eliminati definitivamente. Vuoi continuare?", + "DialogMessage_NoAccountsForCreateMailMessage": "Non hai alcun account da cui creare un messaggio.", + "DialogMessage_NoAccountsForCreateMailTitle": "Account mancante", + "DialogMessage_RenameLinkedAccountsMessage": "Inserisci un nuovo nome per l'account collegato", + "DialogMessage_RenameLinkedAccountsTitle": "Rinomina l'account collegato", + "DialogMessage_UnlinkAccountsConfirmationMessage": "Questa operazione non eliminerà i tuoi account, ma interromperà solo il collegamento verso le connessioni alla cartella condivisa. Vuoi continuare?", + "DialogMessage_UnlinkAccountsConfirmationTitle": "Scollega account", + "DialogMessage_EmptySubjectConfirmation": "Oggetto mancante", + "DialogMessage_EmptySubjectConfirmationMessage": "Il messaggio non ha oggetto. Vuoi continuare?", + "Dialog_DontAskAgain": "Non chiedermelo più", + "DiscordChannelDisclaimerMessage": "Wino non ha il proprio server Discord, ma il canale speciale 'wino-mail' è ospitato presso il server 'Developer Sanctuary'.\nPer ottenere gli aggiornamenti di Wino per favore unisciti al server Developer Sanctuary e segui il canale 'wino-mail' sotto 'Community Projects'.\n\nSarai reindirizzato all'URL del server poiché Discord non supporta gli inviti del canale.", + "DiscordChannelDisclaimerTitle": "Informazioni importanti su Discord", + "Draft": "Bozza", + "EditorToolbarOption_Draw": "Disegna", + "EditorToolbarOption_Format": "Formato", + "EditorToolbarOption_Insert": "Inserisci", + "EditorToolbarOption_None": "Vuoto", + "EditorToolbarOption_Options": "Opzioni", + "ElementTheme_Dark": "Tema scuro", + "ElementTheme_Default": "Usa le impostazioni di sistema", + "ElementTheme_Light": "Tema chiaro", + "Emoji": "Emoji", + "Exception_ImapClientPoolFailed": "IMAP Client Pool fallito.", + "Exception_AuthenticationCanceled": "Autenticazione annullata", + "Exception_CustomThemeExists": "Questo tema esiste già.", + "Exception_CustomThemeMissingName": "È necessario fornire un nome.", + "Exception_CustomThemeMissingWallpaper": "È necessario fornire un'immagine di sfondo personalizzata.", + "Exception_FailedToSynchronizeFolders": "Sincronizzazione delle cartelle non riuscita", + "Exception_GoogleAuthCallbackNull": "Il Callback uri è nullo all'attivazione.", + "Exception_GoogleAuthCorruptedCode": "Risposta all'autorizzazione corrotta.", + "Exception_GoogleAuthError": "Errore di autorizzazione OAuth: {0}", + "Exception_GoogleAuthInvalidResponse": "Ricevuta richiesta con stato non valido ({0})", + "Exception_GoogleAuthorizationCodeExchangeFailed": "Scambio del codice di autorizzazione non riuscito.", + "Exception_InvalidSystemFolderConfiguration": "La configurazione della cartella di sistema non è valida. Controlla la configurazione e riprova.", + "Exception_NullAssignedAccount": "L'account assegnato è nullo", + "Exception_NullAssignedFolder": "La cartella assegnata è nulla", + "Exception_SynchronizerFailureHTTP": "Gestione della risposta non riuscita con errore codice HTTP {0}", + "Exception_TokenGenerationFailed": "La generazione del token non è riuscita", + "Exception_TokenInfoRetrivalFailed": "Impossibile ottenere informazioni sul token.", + "Exception_UnknowErrorDuringAuthentication": "Errore sconosciuto durante l'autenticazione", + "Exception_UnsupportedAction": "Action {0} is not implemented in request processor", + "Exception_UnsupportedProvider": "Questo provider non è supportato.", + "Exception_UnsupportedSynchronizerOperation": "Questa operazione non è supportata per {0}", + "Exception_UserCancelSystemFolderSetupDialog": "Finestra di configurazione della cartella di sistema annullata dall'utente.", + "Files": "File", + "FilteringOption_All": "Tutti", + "FilteringOption_Flagged": "Speciali", + "FilteringOption_Unread": "Non letti", + "Focused": "Focused", + "FolderOperation_CreateSubFolder": "Crea sottocartella", + "FolderOperation_Delete": "Elimina", + "FolderOperation_DontSync": "Non sincronizzare questa cartella", + "FolderOperation_Empty": "Svuota questa cartella", + "FolderOperation_MarkAllAsRead": "Segna tutti come letti", + "FolderOperation_Move": "Sposta", + "DragMoveToFolderCaption": "Sposta in {0}", + "FolderOperation_None": "Nessuno", + "FolderOperation_Pin": "Fissa", + "FolderOperation_Rename": "Rinomina", + "FolderOperation_Unpin": "Sgancia", + "HoverActionOption_Archive": "Archivia", + "HoverActionOption_Delete": "Elimina", + "HoverActionOption_MoveJunk": "Sposta in Indesiderata", + "HoverActionOption_ToggleFlag": "Flag / Unflag", + "HoverActionOption_ToggleRead": "Read / Unread", + "MergedAccountCommonFolderInbox": "Inbox", + "MergedAccountCommonFolderSent": "Sent", + "MergedAccountCommonFolderDraft": "Draft", + "MergedAccountCommonFolderJunk": "Junk", + "MergedAccountCommonFolderTrash": "Deleted", + "MergedAccountCommonFolderArchive": "Archive", + "IMAPSetupDialog_AccountType": "Account type", + "IMAPSetupDialog_DisplayName": "Display Name", + "IMAPSetupDialog_DisplayNamePlaceholder": "es. Mario Rossi", + "IMAPSetupDialog_IncomingMailServer": "Incoming mail server", + "IMAPSetupDialog_IncomingMailServerPort": "Porta", + "IMAPSetupDialog_MailAddress": "Email address", + "IMAPSetupDialog_MailAddressPlaceholder": "someone@example.com", + "IMAPSetupDialog_OutgoingMailServer": "Server di posta in uscita (SMTP)", + "IMAPSetupDialog_OutgoingMailServerPassword": "Password server di posta in uscita", + "IMAPSetupDialog_OutgoingMailServerPort": "Porta", + "IMAPSetupDialog_OutgoingMailServerRequireAuthentication": "Il server di posta in uscita richiede l'autenticazione", + "IMAPSetupDialog_OutgoingMailServerUsername": "Nome utente server di posta in uscita", + "IMAPSetupDialog_Password": "Password", + "IMAPSetupDialog_RequireSSLForIncomingMail": "Require SSL for incoming email", + "IMAPSetupDialog_RequireSSLForOutgoingMail": "Require SSL for outgoing email", + "IMAPSetupDialog_Title": "Advanced IMAP Configuration", + "IMAPSetupDialog_UseSameConfig": "Use the same username and password for sending email", + "IMAPSetupDialog_Username": "Username", + "IMAPSetupDialog_UsernamePlaceholder": "johndoe, johndoe@fabrikam.com, domain/johndoe", + "ImageRenderingDisabled": "Image rendering is disabled for this message.", + "InfoBarAction_Enable": "Enable", + "InfoBarMessage_SynchronizationDisabledFolder": "This folder is disabled for synchronization.", + "InfoBarTitle_SynchronizationDisabledFolder": "Disabled Folder", + "GeneralTitle_Error": "Error", + "GeneralTitle_Warning": "Warning", + "GeneralTitle_Info": "Information", + "Info_AccountCreatedMessage": "{0} is created", + "Info_AccountCreatedTitle": "Account Creation", + "Info_AccountCreationFailedTitle": "Account Creation Failed", + "Info_AccountDeletedMessage": "{0} is successfuly deleted.", + "Info_AccountDeletedTitle": "Account Deleted", + "Info_AccountIssueFixFailedTitle": "Failed", + "Info_AccountIssueFixSuccessMessage": "Fixed all account issues.", + "Info_AccountIssueFixSuccessTitle": "Success", + "Info_AttachmentOpenFailedMessage": "Can't open this attachment.", + "Info_AttachmentOpenFailedTitle": "Failed", + "Info_AttachmentSaveFailedMessage": "Can't save this attachment.", + "Info_AttachmentSaveFailedTitle": "Failed", + "Info_AttachmentSaveSuccessMessage": "Attachment is saved.", + "Info_AttachmentSaveSuccessTitle": "Attachment Saved", + "Info_BackgroundExecutionDeniedMessage": "Background execution for the app is denied. This may affect background synchronization and live notifications.", + "Info_BackgroundExecutionDeniedTitle": "Denied Background Execution", + "Info_BackgroundExecutionUnknownErrorMessage": "Unknown exception occurred when registering background synchronizer.", + "Info_BackgroundExecutionUnknownErrorTitle": "Background Execution Failure", + "Info_ComposerMissingMIMEMessage": "Couldn't locate the MIME file. Synchronizing may help.", + "Info_ComposerMissingMIMETitle": "Failed", + "Info_ContactExistsMessage": "This contact is already in the recipient list.", + "Info_ContactExistsTitle": "Contact Exists", + "Info_DraftFolderMissingMessage": "Draft folder is missing for this account. Please check your account settings.", + "Info_DraftFolderMissingTitle": "Missing Draft Folder", + "Info_FileLaunchFailedTitle": "Failed to launch file", + "Info_InvalidAddressMessage": "'{0}' is not a valid e-mail address.", + "Info_InvalidAddressTitle": "Invalid Address", + "Info_InvalidMoveTargetMessage": "You can't move selected mails to this folder.", + "Info_InvalidMoveTargetTitle": "Invalid Move Target", + "Info_LogsNotFoundMessage": "There are no logs to share.", + "Info_LogsNotFoundTitle": "Logs Not Found", + "Info_LogsSavedMessage": "{0} is saved to selected folder.", + "Info_LogsSavedTitle": "Saved", + "Info_MailRenderingFailedMessage": "This mail is corrupted or can't be opened.\n{0}", + "Info_MailRenderingFailedTitle": "Render Failed", + "Info_MessageCorruptedMessage": "This message is corrupted.", + "Info_MessageCorruptedTitle": "Error", + "Info_MissingFolderMessage": "{0} doesn't exist for this account.", + "Info_MissingFolderTitle": "Missing Folder", + "Info_PurchaseExistsMessage": "Looks like this product has already been purchased before.", + "Info_PurchaseExistsTitle": "Existing Product", + "Info_PurchaseThankYouMessage": "Thank You", + "Info_PurchaseThankYouTitle": "Purchase successful", + "Info_RequestCreationFailedTitle": "Failed to Create Requests", + "Info_ReviewNetworkErrorMessage": "There was a network issue with your review.", + "Info_ReviewNetworkErrorTitle": "Problema di rete", + "Info_ReviewNewMessage": "Tutti i commenti sono apprezzati. Grazie per la recensione!", + "Info_ReviewSuccessTitle": "Grazie", + "Info_ReviewUnknownErrorMessage": "Si è verificato un problema sconosciuto con la tua recensione ({0})", + "Info_ReviewUnknownErrorTitle": "Errore sconosciuto", + "Info_ReviewUpdatedMessage": "Thank you for the updated review.", + "Info_SignatureDisabledMessage": "Disabled signature for this account", + "Info_SignatureDisabledTitle": "Success", + "Info_SignatureSavedMessage": "New signature is saved", + "Info_SignatureSavedTitle": "Success", + "Info_SyncCanceledMessage": "Canceled", + "Info_SyncCanceledTitle": "Synchronization", + "Info_SyncFailedTitle": "Sincronizzazione non riuscita", + "Info_UnsupportedFunctionalityDescription": "This functionality is not implemented yet.", + "Info_UnsupportedFunctionalityTitle": "Non supportato", + "Info_UnsubscribeLinkInvalidTitle": "Uri Di Cancellazione Non Valido", + "Info_UnsubscribeLinkInvalidMessage": "This unsubscribe link is invalid. Failed to unsubscribe from the list.", + "ImapAdvancedSetupDialog_AuthenticationMethod": "Authentication method", + "ImapAdvancedSetupDialog_ConnectionSecurity": "Sicurezza della connessione", + "ImapAuthenticationMethod_Auto": "Automatico", + "ImapAuthenticationMethod_CramMD5": "CRAM-MD5", + "ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5", + "ImapAuthenticationMethod_None": "Nessuna autenticazione", + "ImapAuthenticationMethod_Plain": "Normal password", + "ImapAuthenticationMethod_EncryptedPassword": "Encrypted password", + "ImapAuthenticationMethod_Ntlm": "NTLM", + "ImapConnectionSecurity_None": "None", + "ImapConnectionSecurity_SslTls": "SSL/TLS", + "ImapConnectionSecurity_StartTls": "STARTTLS", + "ImapConnectionSecurity_Auto": "Auto", + "Justify": "Justify", + "Left": "Left", + "Link": "Link", + "LinkedAccountsCreatePolicyMessage": "you must have at least 2 accounts to create link\nlink will be removed on save", + "LinkedAccountsTitle": "Linked Accounts", + "MailOperation_AlwaysMoveFocused": "Always Move to Focused", + "MailOperation_AlwaysMoveOther": "Always Move to Other", + "MailOperation_Archive": "Archive", + "MailOperation_ClearFlag": "Clear flag", + "MailOperation_DarkEditor": "Dark", + "MailOperation_Delete": "Delete", + "MailOperation_ExportPDF": "Export to PDF", + "MailOperation_Find": "Find", + "MailOperation_Forward": "Forward", + "MailOperation_Ignore": "Ignore", + "MailOperation_LightEditor": "Light", + "MailOperation_MarkAsJunk": "Mark as junk", + "MailOperation_MarkAsRead": "Mark as read", + "MailOperation_MarkAsUnread": "Mark as unread", + "MailOperation_MarkNotJunk": "Mark as Not Junk", + "MailOperation_Move": "Move", + "MailOperation_MoveFocused": "Move to Focused", + "MailOperation_MoveJunk": "Move to Junk", + "MailOperation_MoveOther": "Move to Other", + "MailOperation_Navigate": "Navigate", + "MailOperation_Print": "Print", + "MailOperation_Reply": "Reply", + "MailOperation_ReplyAll": "Reply all", + "MailOperation_SaveAs": "Save As", + "MailOperation_SetFlag": "Set flag", + "MailOperation_Unarchive": "Unarchive", + "MailOperation_Zoom": "Zoom", + "MailsSelected": "{0} item(s) selected", + "MarkFlagUnflag": "Mark as flagged/unflagged", + "MarkReadUnread": "Mark as read/unread", + "MenuManageAccounts": "Manage Accounts", + "MenuNewMail": "New Mail", + "MenuMergedAccountItemAccountsSuffix": " accounts", + "MenuRate": "Rate Wino", + "MenuSettings": "Settings", + "MergedAccountsAvailableAccountsTitle": "Available Accounts", + "More": "More", + "MoveMailDialog_InvalidFolderMessage": "{0} is not a valid folder for this mail.", + "MoveMailDialog_Title": "Pick a folder", + "NewAccountDialog_AccountName": "Account Name", + "NewAccountDialog_AccountNameDefaultValue": "Personal", + "NewAccountDialog_AccountNamePlaceholder": "eg. Personal Mail", + "NewAccountDialog_Title": "Add New Account", + "NoMailSelected": "no message selected", + "NoMessageCrieteria": "no messages match your search criteria.", + "NoMessageEmptyFolder": "this folder is empty.", + "Notifications_MultipleNotificationsMessage": "You have {0} new mails", + "Notifications_MultipleNotificationsTitle": "New Mails", + "Notifications_WinoUpdatedMessage": "Checkout new version {0}", + "Notifications_WinoUpdatedTitle": "Wino Mail has been updated.", + "Other": "Other", + "PaneLengthOption_Default": "Default", + "PaneLengthOption_ExtraLarge": "Extra Large", + "PaneLengthOption_Large": "Large", + "PaneLengthOption_Medium": "Medium", + "PaneLengthOption_Micro": "Micro", + "PaneLengthOption_Small": "Small", + "Photos": "Photos", + "PreparingFoldersMessage": "Preparing folders", + "ProviderDetail_Gmail_Description": "Google Account", + "ProviderDetail_IMAP_Description": "Custom IMAP/SMTP server", + "ProviderDetail_IMAP_Title": "IMAP Server", + "Results": "Results", + "Right": "Right", + "SynchronizationFolderReport_Success": "up to date", + "SynchronizationFolderReport_Failed": "synchronization is failed", + "SearchBarPlaceholder": "search", + "SearchingIn": "searching in", + "SettingsAboutGithub_Description": "Go to issue tracker GitHub repository.", + "SettingsAboutGithub_Title": "GitHub", + "SettingsAccountManagementAppendMessage_Title": "Append messages to Sent folder", + "SettingsAccountManagementAppendMessage_Description": "Create a copy of the message in Sent folder after the draft is sent. Enable this if you don't see your mails after you sent them in Sent folder.", + "SettingsEditLinkedInbox_Title": "Edit Linked Inbox", + "SettingsEditLinkedInbox_Description": "Add / remove accounts, rename or break the link between accounts.", + "SettingsAboutVersion": "Version ", + "SettingsAboutWinoDescription": "Lightweight mail client for Windows device families.", + "SettingsAbout_Description": "Learn more about Wino.", + "SettingsAbout_Title": "About", + "SettingsAccentColor_Description": "Change application's accent color", + "SettingsAccentColor_Title": "Accent Color", + "SettingsAccentColor_UseWindowsAccentColor": "Use my Windows accent color", + "SettingsAccountName_Description": "Change the name of the account.", + "SettingsAccountName_Title": "Account Name", + "SettingsApplicationTheme_Description": "Personalize Wino with different custom application themes for your like.", + "SettingsApplicationTheme_Title": "Application Theme", + "SettingsAvailableThemes_Description": "Select a theme from Wino's own collection for your taste or apply your own themes.", + "SettingsAvailableThemes_Title": "Available Themes", + "SettingsCustomTheme_Description": "Create your own custom theme with custom wallpaper and accent color.", + "SettingsCustomTheme_Title": "Custom Theme", + "SettingsDeleteAccount_Description": "Delete all e-mails and credentials associated with this account.", + "SettingsDeleteAccount_Title": "Delete this account", + "SettingsDeleteProtection_Description": "Should Wino ask you for comfirmation every time you try to permanently delete a mail using Shift + Del keys?", + "SettingsDeleteProtection_Title": "Permanent Delete Protection", + "SettingsDiagnostics_Description": "For developers", + "SettingsDiagnostics_Title": "Diagnostics", + "SettingsDiscord_Description": "Get regular development updates, join roadmap discussions and provide feedback.", + "SettingsDiscord_Title": "Discord Channel", + "SettingsElementThemeSelectionDisabled": "Element theme selection is disabled when application theme is selected other than Default.", + "SettingsElementTheme_Description": "Select a Windows theme for Wino", + "SettingsElementTheme_Title": "Element Theme", + "SettingsEnableHoverActions_Title": "Enable hover actions", + "SettingsEnableIMAPLogs_Description": "Enable this to provide details about IMAP connectivity issuses you had during IMAP server setup.", + "SettingsEnableIMAPLogs_Title": "Enable IMAP Protocol Logs", + "SettingsEnableLogs_Description": "I might need logs for crashes to diagnose issues you have opened in GitHub. None of the logs will expose your credentials or sensetive information to public.", + "SettingsEnableLogs_Title": "Enable Logs", + "SettingsEnableSignature": "Enable Signature", + "SettingsExpandOnStartup_Description": "Set whether Wino should expand this account's folders on startup.", + "SettingsExpandOnStartup_Title": "Expand Menu on Startup", + "SettingsExternalContent_Description": "Manage external content settings when rendering mails.", + "SettingsExternalContent_Title": "External Content", + "SettingsFocusedInbox_Description": "Set whether Inbox should be split into two as Focused - Other.", + "SettingsFocusedInbox_Title": "Focused Inbox", + "SettingsFolderSync_Description": "Enable or disable specific folders for synchronization.", + "SettingsFolderSync_Title": "Folder Synchronization", + "SettingsFolderOptions_Title": "Folder Configuration", + "SettingsFolderOptions_Description": "Change individual folder settings like enable/disable sync or show/hide unread badge.", + "SettingsHoverActionCenter": "Center Action", + "SettingsHoverActionLeft": "Left Action", + "SettingsHoverActionRight": "Right Action", + "SettingsHoverActions_Description": "Select 3 actions to show up when you hover over the mails with cursor.", + "SettingsHoverActions_Title": "Hover Actions", + "SettingsLanguage_Description": "Change display language for Wino.", + "SettingsLanguage_Title": "Display Language", + "CategoriesFolderNameOverride": "Categories", + "MoreFolderNameOverride": "More", + "SettingsOptions_Title": "Settings", + "SettingsLinkAccounts_Description": "Merge multiple accounts into one. See mails from one Inbox together.", + "SettingsLinkAccounts_Title": "Create Linked Accounts", + "SettingsLinkedAccountsSave_Description": "Modify the current link with the new accounts.", + "SettingsLinkedAccountsSave_Title": "Save Changes", + "SettingsLoadImages_Title": "Load images automatically", + "SettingsLoadStyles_Title": "Load styles automatically", + "SettingsMailSpacing_Description": "Adjust the spacing for listing mails.", + "SettingsMailSpacing_Title": "Mail Spacing", + "SettingsFolderMenuStyle_Title": "Create Nested Folders", + "SettingsFolderMenuStyle_Description": "Change whether account folders should be nested inside an account menu item or not. Toggle this off if you like the old menu system in Windows Mail", + "SettingsManageAccountSettings_Description": "Notifications, signatures, synchronization and other settings per account.", + "SettingsManageAccountSettings_Title": "Manage Account Settings", + "SettingsManageLink_Description": "Move items to add new link or remove existing link.", + "SettingsManageLink_Title": "Manage Link", + "SettingsMarkAsRead_Description": "Change what should happen to the selected item.", + "SettingsMarkAsRead_DontChange": "Don't automatically mark item as read", + "SettingsMarkAsRead_SecondsToWait": "Seconds to wait: ", + "SettingsMarkAsRead_Timer": "When viewed in the reading pane", + "SettingsMarkAsRead_Title": "Mark item as read", + "SettingsMarkAsRead_WhenSelected": "When selected", + "SettingsMessageList_Description": "Change how your messages should be organized in mail list.", + "SettingsMessageList_Title": "Message List", + "SettingsNoAccountSetupMessage": "You didn't setup any accounts yet.", + "SettingsNotifications_Description": "Turn on or off notifications for this account.", + "SettingsNotifications_Title": "Notifications", + "SettingsPaneLength_Description": "Change the width of the mail list.", + "SettingsPaneLength_Title": "Mail List Pane Length", + "SettingsPaypal_Description": "Show much more love ❤️ All donations are appreciated.", + "SettingsPaypal_Title": "Donate via PayPal", + "SettingsPersonalizationMailDisplayCompactMode": "Compact Mode", + "SettingsPersonalizationMailDisplayMediumMode": "Medium Mode", + "SettingsPersonalizationMailDisplaySpaciousMode": "Spacious Mode", + "SettingsPersonalization_Description": "Change appearance of Wino as you like.", + "SettingsPersonalization_Title": "Personalization", + "SettingsPrivacyPolicy_Description": "Review privacy policy.", + "SettingsPrivacyPolicy_Title": "Privacy Policy", + "SettingsReadingPane_Description": "Mail rendering options.", + "SettingsReadingPane_Title": "Reading Pane", + "SettingsReaderFont_Title": "Default Reader Font", + "SettingsReaderFontFamily_Description": "Change the default font family and font size for reading mails.", + "SettingsFontFamily_Title": "Font Family", + "SettingsFontSize_Title": "Font Size", + "SettingsFontPreview_Title": "Preview", + "SettingsComposerFont_Title": "Default Composer Font", + "SettingsComposerFontFamily_Description": "Change the default font family and font size for composing mails.", + "SettingsRenameMergeAccount_Description": "Change the display name of the linked accounts.", + "SettingsRenameMergeAccount_Title": "Rename", + "SettingsSemanticZoom_Description": "This will allow you to click on the headers in messages list and go to specific date", + "SettingsSemanticZoom_Title": "Semantic Zoom for Date Headers", + "SettingsShowPreviewText_Description": "Hide/show thepreview text.", + "SettingsShowPreviewText_Title": "Show Preview Text", + "SettingsShowSenderPictures_Description": "Hide/show the thumbnail sender pictures.", + "SettingsShowSenderPictures_Title": "Show Sender Avatars", + "SettingsPrefer24HourClock_Title": "Display Clock Format in 24 Hours", + "SettingsPrefer24HourClock_Description": "Mail recieve times will be displayed in 24 hour format instead of 12 (AM/PM)", + "SettingsSignature_Description": "Edit or remove account signature", + "SettingsSignature_Title": "Signature", + "SettingsStartupItem_Description": "Primary account item to load Inbox at startup.", + "SettingsStartupItem_Title": "Startup Item", + "SettingsStore_Description": "Show some love ❤️", + "SettingsStore_Title": "Rate in Store", + "SettingsThreads_Description": "Organize messages into conversation threads.", + "SettingsThreads_Title": "Conversation Threading", + "SettingsUnlinkAccounts_Description": "Remove the link between accounts. This will not delete your accounts.", + "SettingsUnlinkAccounts_Title": "Unlink Accounts", + "SortingOption_Date": "by date", + "SortingOption_Name": "by name", + "StoreRatingDialog_MessageFirstLine": "All feedbacks are appreciated and they will make much Wino better in the future. Would you like to rate Wino in Microsoft Store?", + "StoreRatingDialog_MessageSecondLine": "Would you like to rate Wino Mail in Microsoft Store?", + "StoreRatingDialog_Title": "Enjoying Wino?", + "SystemFolderConfigDialog_ArchiveFolderDescription": "Archived messages will be moved to here.", + "SystemFolderConfigDialog_ArchiveFolderHeader": "Archive Folder", + "SystemFolderConfigDialog_DeletedFolderDescription": "Deleted messages will be moved to here.", + "SystemFolderConfigDialog_DeletedFolderHeader": "Deleted Folder", + "SystemFolderConfigDialog_DraftFolderDescription": "New mails/replies will be crafted in here.", + "SystemFolderConfigDialog_DraftFolderHeader": "Draft Folder", + "SystemFolderConfigDialog_JunkFolderDescription": "All spam/junk mails will be here.", + "SystemFolderConfigDialog_JunkFolderHeader": "Junk/Spam Folder", + "SystemFolderConfigDialog_MessageFirstLine": "This IMAP server doesn't support SPECIAL-USE extension hence Wino couldn't setup the system folders properly.", + "SystemFolderConfigDialog_MessageSecondLine": "Please select the appropriate folders for specific functionalities.", + "SystemFolderConfigDialog_SentFolderDescription": "Folder that sent messages will be stored.", + "SystemFolderConfigDialog_SentFolderHeader": "Sent Folder", + "SystemFolderConfigDialog_Title": "Configure System Folders", + "TestingImapConnectionMessage": "Testing server connection...", + "Today": "Today", + "UnknownAddress": "unknown address", + "UnknownDateHeader": "Unknown Date", + "UnknownGroupAddress": "unknown Mail Group Address", + "UnknownSender": "Unknown Sender", + "Unsubscribe": "Unsubscribe", + "ViewContactDetails": "View Details", + "WinoUpgradeDescription": "Wino offers 3 accounts to start with for free. If you need more than 3 accounts, please upgrade", + "WinoUpgradeMessage": "Upgrade to Unlimited Accounts", + "WinoUpgradeRemainingAccountsMessage": "{0} out of {1} free accounts used.", + "Yesterday": "Yesterday" +} diff --git a/Wino.Core.Domain/Translations/ja_JP/resources.json b/Wino.Core.Domain/Translations/ja_JP/resources.json new file mode 100644 index 00000000..4fc52dd0 --- /dev/null +++ b/Wino.Core.Domain/Translations/ja_JP/resources.json @@ -0,0 +1,468 @@ +{ + "AccountCreationDialog_Completed": "完了", + "AccountCreationDialog_Initializing": "初期化中", + "AccountCreationDialog_PreparingFolders": "現在フォルダ情報を取得中です。", + "AccountCreationDialog_SigninIn": "アカウント情報を保存しています。", + "AccountEditDialog_Message": "アカウント名", + "AccountEditDialog_Title": "アカウントの編集", + "AccountPickerDialog_Title": "アカウントを選択", + "AddHyperlink": "追加", + "AutoDiscoveryProgressMessage": "メール設定を検索中...", + "BasicIMAPSetupDialog_AdvancedConfiguration": "詳細設定", + "BasicIMAPSetupDialog_CredentialLocalMessage": "認証情報はローカルにのみ保存されます。", + "BasicIMAPSetupDialog_Description": "一部のアカウントではサインインするために追加の手順が必要です。", + "BasicIMAPSetupDialog_DisplayName": "表示名", + "BasicIMAPSetupDialog_DisplayNamePlaceholder": "例: 田中太郎", + "BasicIMAPSetupDialog_LearnMore": "詳細はこちら", + "BasicIMAPSetupDialog_MailAddress": "メールアドレス", + "BasicIMAPSetupDialog_MailAddressPlaceholder": "johndoe@fabrikam.com", + "BasicIMAPSetupDialog_Password": "パスワード", + "BasicIMAPSetupDialog_Title": "IMAPアカウント", + "Buttons_AddAccount": "アカウントを追加", + "Buttons_ApplyTheme": "テーマを適用", + "Buttons_Browse": "閲覧", + "Buttons_Cancel": "キャンセル", + "Buttons_Close": "閉じる", + "Buttons_Create": "作成", + "Buttons_CreateAccount": "アカウント作成", + "Buttons_Delete": "削除", + "Buttons_Discard": "破棄", + "Buttons_EnableImageRendering": "有効化", + "Buttons_No": "いいえ", + "Buttons_Open": "開く", + "Buttons_Purchase": "購入", + "Buttons_RateWino": "Winoを評価", + "Buttons_Save": "保存", + "Buttons_SaveConfiguration": "設定を保存", + "Buttons_Share": "シェアする", + "Buttons_SignIn": "サインイン", + "Buttons_Yes": "はい", + "Center": "中央", + "ComingSoon": "近日公開", + "ComposerFrom": "差出人: ", + "ComposerSubject": "件名: ", + "ComposerTo": "宛先: ", + "ClipboardTextCopied_Message": "{0} copied to clipboard.", + "ClipboardTextCopied_Title": "Copied", + "ClipboardTextCopyFailed_Message": "Failed to copy {0} to clipboard.", + "ComposerToPlaceholder": "アドレスを入力するにはエンターをクリックしてください", + "CustomThemeBuilder_AccentColorDescription": "必要に応じてカスタムアクセントカラーを設定します。選択しない場合、Windowsアクセントカラーが使用されます。", + "CustomThemeBuilder_AccentColorTitle": "アクセントカラー", + "CustomThemeBuilder_PickColor": "選択", + "CustomThemeBuilder_ThemeNameDescription": "カスタムテーマの名前を設定する。", + "CustomThemeBuilder_ThemeNameTitle": "テーマ名", + "CustomThemeBuilder_Title": "カスタムテーマビルダー", + "CustomThemeBuilder_WallpaperDescription": "Winoのカスタム壁紙を設定", + "CustomThemeBuilder_WallpaperTitle": "カスタム壁紙を設定", + "DialogMessage_AccountLimitMessage": "アカウントの作成上限に達しました。\n「無制限アカウント」アドオンを購入して続行しますか?", + "DialogMessage_AccountLimitTitle": "アカウントの上限に達しました", + "DialogMessage_CleanupFolderMessage": "このフォルダ内のすべてのメールを完全に削除しますか?", + "DialogMessage_CleanupFolderTitle": "フォルダのクリーンアップ", + "DialogMessage_ComposerMissingRecipientMessage": "メッセージに宛先がありません。", + "DialogMessage_ComposerValidationFailedTitle": "検証に失敗しました", + "DialogMessage_CreateLinkedAccountMessage": "この新しいリンクに名前を付けてください。アカウントはこの名前でマージされます。", + "DialogMessage_CreateLinkedAccountTitle": "アカウントリンク名", + "DialogMessage_DeleteAccountConfirmationMessage": "{0} を削除しますか?", + "DialogMessage_DeleteAccountConfirmationTitle": "このアカウントに関連付けられたすべてのデータは永久にディスクから削除されます。", + "DialogMessage_DiscardDraftConfirmationMessage": "この下書きは破棄されます。続行しますか?", + "DialogMessage_DiscardDraftConfirmationTitle": "下書きを破棄", + "DialogMessage_HardDeleteConfirmationMessage": "完全に削除", + "DialogMessage_HardDeleteConfirmationTitle": "Message(s) will be permanently deleted. Do you want to continue?", + "DialogMessage_NoAccountsForCreateMailMessage": "You don't have any accounts to create message from.", + "DialogMessage_NoAccountsForCreateMailTitle": "Account Missing", + "DialogMessage_RenameLinkedAccountsMessage": "Enter new name for linked account", + "DialogMessage_RenameLinkedAccountsTitle": "Rename Linked Account", + "DialogMessage_UnlinkAccountsConfirmationMessage": "This operation will not delete your accounts but only break the link for shared folder connections. Do you want to continue?", + "DialogMessage_UnlinkAccountsConfirmationTitle": "Unlink Accounts", + "DialogMessage_EmptySubjectConfirmation": "Missin Subject", + "DialogMessage_EmptySubjectConfirmationMessage": "Message has no subject. Do you want to continue?", + "Dialog_DontAskAgain": "Don't ask again", + "DiscordChannelDisclaimerMessage": "Wino doesn't have it's own Discord server, but special 'wino-mail' channel is hosted at 'Developer Sanctuary' server.\nTo get the updates about Wino please join Developer Sanctuary server and follow 'wino-mail' channel under 'Community Projects'\n\nYou will be directed to server URL since Discord doesn't support channel invites.", + "DiscordChannelDisclaimerTitle": "Important Discord Information", + "Draft": "Draft", + "EditorToolbarOption_Draw": "Draw", + "EditorToolbarOption_Format": "Format", + "EditorToolbarOption_Insert": "Insert", + "EditorToolbarOption_None": "None", + "EditorToolbarOption_Options": "Options", + "ElementTheme_Dark": "Dark mode", + "ElementTheme_Default": "Use system setting", + "ElementTheme_Light": "Light mode", + "Emoji": "Emoji", + "Exception_ImapClientPoolFailed": "IMAP Client Pool failed.", + "Exception_AuthenticationCanceled": "Authentication canceled", + "Exception_CustomThemeExists": "This theme already exists.", + "Exception_CustomThemeMissingName": "You must provide a name.", + "Exception_CustomThemeMissingWallpaper": "You must provide a custom background image.", + "Exception_FailedToSynchronizeFolders": "Failed to synchronize folders", + "Exception_GoogleAuthCallbackNull": "Callback uri is null on activation.", + "Exception_GoogleAuthCorruptedCode": "Corrupted authorization response.", + "Exception_GoogleAuthError": "OAuth authorization error: {0}", + "Exception_GoogleAuthInvalidResponse": "Received request with invalid state ({0})", + "Exception_GoogleAuthorizationCodeExchangeFailed": "Authorization code exchange failed.", + "Exception_InvalidSystemFolderConfiguration": "System folder configuration is not valid. Check configuration and try again.", + "Exception_NullAssignedAccount": "割り当てられたアカウントがありません", + "Exception_NullAssignedFolder": "割り当てられたフォルダがありません", + "Exception_SynchronizerFailureHTTP": "Response handling failed with error HTTP code {0}", + "Exception_TokenGenerationFailed": "Token generation failed", + "Exception_TokenInfoRetrivalFailed": "Failed to get token information.", + "Exception_UnknowErrorDuringAuthentication": "Unknown error occurred during authentication", + "Exception_UnsupportedAction": "Action {0} is not implemented in request processor", + "Exception_UnsupportedProvider": "This provider is not supported.", + "Exception_UnsupportedSynchronizerOperation": "This operation is not supported for {0}", + "Exception_UserCancelSystemFolderSetupDialog": "User canceled system folder config dialog.", + "Files": "Files", + "FilteringOption_All": "All", + "FilteringOption_Flagged": "Flagged", + "FilteringOption_Unread": "Unread", + "Focused": "Focused", + "FolderOperation_CreateSubFolder": "Create sub folder", + "FolderOperation_Delete": "Delete", + "FolderOperation_DontSync": "Don't sync this folder", + "FolderOperation_Empty": "Empty this folder", + "FolderOperation_MarkAllAsRead": "Mark all as read", + "FolderOperation_Move": "Move", + "DragMoveToFolderCaption": "Move to {0}", + "FolderOperation_None": "None", + "FolderOperation_Pin": "Pin", + "FolderOperation_Rename": "Rename", + "FolderOperation_Unpin": "Unpin", + "HoverActionOption_Archive": "Archive", + "HoverActionOption_Delete": "Delete", + "HoverActionOption_MoveJunk": "Move to Junk", + "HoverActionOption_ToggleFlag": "Flag / Unflag", + "HoverActionOption_ToggleRead": "Read / Unread", + "MergedAccountCommonFolderInbox": "Inbox", + "MergedAccountCommonFolderSent": "Sent", + "MergedAccountCommonFolderDraft": "Draft", + "MergedAccountCommonFolderJunk": "Junk", + "MergedAccountCommonFolderTrash": "Deleted", + "MergedAccountCommonFolderArchive": "Archive", + "IMAPSetupDialog_AccountType": "Account type", + "IMAPSetupDialog_DisplayName": "Display Name", + "IMAPSetupDialog_DisplayNamePlaceholder": "eg. John Doe", + "IMAPSetupDialog_IncomingMailServer": "Incoming mail server", + "IMAPSetupDialog_IncomingMailServerPort": "Port", + "IMAPSetupDialog_MailAddress": "Email address", + "IMAPSetupDialog_MailAddressPlaceholder": "someone@example.com", + "IMAPSetupDialog_OutgoingMailServer": "Outgoing (SMTP) mail server", + "IMAPSetupDialog_OutgoingMailServerPassword": "Outgoing server password", + "IMAPSetupDialog_OutgoingMailServerPort": "Port", + "IMAPSetupDialog_OutgoingMailServerRequireAuthentication": "Outgoing server requires authentication", + "IMAPSetupDialog_OutgoingMailServerUsername": "Outgoing server user name", + "IMAPSetupDialog_Password": "Password", + "IMAPSetupDialog_RequireSSLForIncomingMail": "Require SSL for incoming email", + "IMAPSetupDialog_RequireSSLForOutgoingMail": "Require SSL for outgoing email", + "IMAPSetupDialog_Title": "Advanced IMAP Configuration", + "IMAPSetupDialog_UseSameConfig": "Use the same username and password for sending email", + "IMAPSetupDialog_Username": "Username", + "IMAPSetupDialog_UsernamePlaceholder": "johndoe, johndoe@fabrikam.com, domain/johndoe", + "ImageRenderingDisabled": "Image rendering is disabled for this message.", + "InfoBarAction_Enable": "Enable", + "InfoBarMessage_SynchronizationDisabledFolder": "This folder is disabled for synchronization.", + "InfoBarTitle_SynchronizationDisabledFolder": "Disabled Folder", + "GeneralTitle_Error": "Error", + "GeneralTitle_Warning": "Warning", + "GeneralTitle_Info": "Information", + "Info_AccountCreatedMessage": "{0} is created", + "Info_AccountCreatedTitle": "Account Creation", + "Info_AccountCreationFailedTitle": "Account Creation Failed", + "Info_AccountDeletedMessage": "{0} is successfuly deleted.", + "Info_AccountDeletedTitle": "Account Deleted", + "Info_AccountIssueFixFailedTitle": "Failed", + "Info_AccountIssueFixSuccessMessage": "Fixed all account issues.", + "Info_AccountIssueFixSuccessTitle": "Success", + "Info_AttachmentOpenFailedMessage": "Can't open this attachment.", + "Info_AttachmentOpenFailedTitle": "Failed", + "Info_AttachmentSaveFailedMessage": "Can't save this attachment.", + "Info_AttachmentSaveFailedTitle": "Failed", + "Info_AttachmentSaveSuccessMessage": "Attachment is saved.", + "Info_AttachmentSaveSuccessTitle": "Attachment Saved", + "Info_BackgroundExecutionDeniedMessage": "Background execution for the app is denied. This may affect background synchronization and live notifications.", + "Info_BackgroundExecutionDeniedTitle": "Denied Background Execution", + "Info_BackgroundExecutionUnknownErrorMessage": "Unknown exception occurred when registering background synchronizer.", + "Info_BackgroundExecutionUnknownErrorTitle": "Background Execution Failure", + "Info_ComposerMissingMIMEMessage": "Couldn't locate the MIME file. Synchronizing may help.", + "Info_ComposerMissingMIMETitle": "Failed", + "Info_ContactExistsMessage": "This contact is already in the recipient list.", + "Info_ContactExistsTitle": "Contact Exists", + "Info_DraftFolderMissingMessage": "Draft folder is missing for this account. Please check your account settings.", + "Info_DraftFolderMissingTitle": "Missing Draft Folder", + "Info_FileLaunchFailedTitle": "Failed to launch file", + "Info_InvalidAddressMessage": "'{0}' is not a valid e-mail address.", + "Info_InvalidAddressTitle": "Invalid Address", + "Info_InvalidMoveTargetMessage": "You can't move selected mails to this folder.", + "Info_InvalidMoveTargetTitle": "Invalid Move Target", + "Info_LogsNotFoundMessage": "There are no logs to share.", + "Info_LogsNotFoundTitle": "Logs Not Found", + "Info_LogsSavedMessage": "{0} is saved to selected folder.", + "Info_LogsSavedTitle": "Saved", + "Info_MailRenderingFailedMessage": "This mail is corrupted or can't be opened.\n{0}", + "Info_MailRenderingFailedTitle": "Render Failed", + "Info_MessageCorruptedMessage": "This message is corrupted.", + "Info_MessageCorruptedTitle": "Error", + "Info_MissingFolderMessage": "{0} doesn't exist for this account.", + "Info_MissingFolderTitle": "Missing Folder", + "Info_PurchaseExistsMessage": "Looks like this product has already been purchased before.", + "Info_PurchaseExistsTitle": "Existing Product", + "Info_PurchaseThankYouMessage": "Thank You", + "Info_PurchaseThankYouTitle": "Purchase successful", + "Info_RequestCreationFailedTitle": "Failed to Create Requests", + "Info_ReviewNetworkErrorMessage": "There was a network issue with your review.", + "Info_ReviewNetworkErrorTitle": "Network Issue", + "Info_ReviewNewMessage": "All feedbacks are appreciated. Thank you for the review!", + "Info_ReviewSuccessTitle": "Thank you", + "Info_ReviewUnknownErrorMessage": "There was an unknown issue with your review. ({0})", + "Info_ReviewUnknownErrorTitle": "Unknown Error", + "Info_ReviewUpdatedMessage": "Thank you for the updated review.", + "Info_SignatureDisabledMessage": "Disabled signature for this account", + "Info_SignatureDisabledTitle": "Success", + "Info_SignatureSavedMessage": "New signature is saved", + "Info_SignatureSavedTitle": "Success", + "Info_SyncCanceledMessage": "Canceled", + "Info_SyncCanceledTitle": "Synchronization", + "Info_SyncFailedTitle": "Synchronization Failed", + "Info_UnsupportedFunctionalityDescription": "This functionality is not implemented yet.", + "Info_UnsupportedFunctionalityTitle": "Unsupported", + "Info_UnsubscribeLinkInvalidTitle": "Invalid Unsubscribe Uri", + "Info_UnsubscribeLinkInvalidMessage": "This unsubscribe link is invalid. Failed to unsubscribe from the list.", + "ImapAdvancedSetupDialog_AuthenticationMethod": "Authentication method", + "ImapAdvancedSetupDialog_ConnectionSecurity": "Connection security", + "ImapAuthenticationMethod_Auto": "Auto", + "ImapAuthenticationMethod_CramMD5": "CRAM-MD5", + "ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5", + "ImapAuthenticationMethod_None": "No authentication", + "ImapAuthenticationMethod_Plain": "Normal password", + "ImapAuthenticationMethod_EncryptedPassword": "Encrypted password", + "ImapAuthenticationMethod_Ntlm": "NTLM", + "ImapConnectionSecurity_None": "None", + "ImapConnectionSecurity_SslTls": "SSL/TLS", + "ImapConnectionSecurity_StartTls": "STARTTLS", + "ImapConnectionSecurity_Auto": "Auto", + "Justify": "Justify", + "Left": "Left", + "Link": "Link", + "LinkedAccountsCreatePolicyMessage": "you must have at least 2 accounts to create link\nlink will be removed on save", + "LinkedAccountsTitle": "Linked Accounts", + "MailOperation_AlwaysMoveFocused": "Always Move to Focused", + "MailOperation_AlwaysMoveOther": "Always Move to Other", + "MailOperation_Archive": "Archive", + "MailOperation_ClearFlag": "Clear flag", + "MailOperation_DarkEditor": "Dark", + "MailOperation_Delete": "Delete", + "MailOperation_ExportPDF": "Export to PDF", + "MailOperation_Find": "Find", + "MailOperation_Forward": "Forward", + "MailOperation_Ignore": "Ignore", + "MailOperation_LightEditor": "Light", + "MailOperation_MarkAsJunk": "Mark as junk", + "MailOperation_MarkAsRead": "Mark as read", + "MailOperation_MarkAsUnread": "Mark as unread", + "MailOperation_MarkNotJunk": "Mark as Not Junk", + "MailOperation_Move": "Move", + "MailOperation_MoveFocused": "Move to Focused", + "MailOperation_MoveJunk": "Move to Junk", + "MailOperation_MoveOther": "Move to Other", + "MailOperation_Navigate": "Navigate", + "MailOperation_Print": "Print", + "MailOperation_Reply": "Reply", + "MailOperation_ReplyAll": "Reply all", + "MailOperation_SaveAs": "Save As", + "MailOperation_SetFlag": "Set flag", + "MailOperation_Unarchive": "Unarchive", + "MailOperation_Zoom": "Zoom", + "MailsSelected": "{0} item(s) selected", + "MarkFlagUnflag": "Mark as flagged/unflagged", + "MarkReadUnread": "Mark as read/unread", + "MenuManageAccounts": "Manage Accounts", + "MenuNewMail": "New Mail", + "MenuMergedAccountItemAccountsSuffix": " accounts", + "MenuRate": "Rate Wino", + "MenuSettings": "Settings", + "MergedAccountsAvailableAccountsTitle": "Available Accounts", + "More": "More", + "MoveMailDialog_InvalidFolderMessage": "{0} is not a valid folder for this mail.", + "MoveMailDialog_Title": "Pick a folder", + "NewAccountDialog_AccountName": "Account Name", + "NewAccountDialog_AccountNameDefaultValue": "Personal", + "NewAccountDialog_AccountNamePlaceholder": "eg. Personal Mail", + "NewAccountDialog_Title": "Add New Account", + "NoMailSelected": "no message selected", + "NoMessageCrieteria": "no messages match your search criteria.", + "NoMessageEmptyFolder": "this folder is empty.", + "Notifications_MultipleNotificationsMessage": "You have {0} new mails", + "Notifications_MultipleNotificationsTitle": "New Mails", + "Notifications_WinoUpdatedMessage": "Checkout new version {0}", + "Notifications_WinoUpdatedTitle": "Wino Mail has been updated.", + "Other": "Other", + "PaneLengthOption_Default": "Default", + "PaneLengthOption_ExtraLarge": "Extra Large", + "PaneLengthOption_Large": "Large", + "PaneLengthOption_Medium": "Medium", + "PaneLengthOption_Micro": "Micro", + "PaneLengthOption_Small": "Small", + "Photos": "Photos", + "PreparingFoldersMessage": "Preparing folders", + "ProviderDetail_Gmail_Description": "Google Account", + "ProviderDetail_IMAP_Description": "Custom IMAP/SMTP server", + "ProviderDetail_IMAP_Title": "IMAP Server", + "Results": "Results", + "Right": "Right", + "SynchronizationFolderReport_Success": "up to date", + "SynchronizationFolderReport_Failed": "synchronization is failed", + "SearchBarPlaceholder": "search", + "SearchingIn": "searching in", + "SettingsAboutGithub_Description": "Go to issue tracker GitHub repository.", + "SettingsAboutGithub_Title": "GitHub", + "SettingsAccountManagementAppendMessage_Title": "Append messages to Sent folder", + "SettingsAccountManagementAppendMessage_Description": "Create a copy of the message in Sent folder after the draft is sent. Enable this if you don't see your mails after you sent them in Sent folder.", + "SettingsEditLinkedInbox_Title": "Edit Linked Inbox", + "SettingsEditLinkedInbox_Description": "Add / remove accounts, rename or break the link between accounts.", + "SettingsAboutVersion": "Version ", + "SettingsAboutWinoDescription": "Lightweight mail client for Windows device families.", + "SettingsAbout_Description": "Learn more about Wino.", + "SettingsAbout_Title": "About", + "SettingsAccentColor_Description": "Change application's accent color", + "SettingsAccentColor_Title": "Accent Color", + "SettingsAccentColor_UseWindowsAccentColor": "Use my Windows accent color", + "SettingsAccountName_Description": "Change the name of the account.", + "SettingsAccountName_Title": "Account Name", + "SettingsApplicationTheme_Description": "Personalize Wino with different custom application themes for your like.", + "SettingsApplicationTheme_Title": "Application Theme", + "SettingsAvailableThemes_Description": "Select a theme from Wino's own collection for your taste or apply your own themes.", + "SettingsAvailableThemes_Title": "Available Themes", + "SettingsCustomTheme_Description": "Create your own custom theme with custom wallpaper and accent color.", + "SettingsCustomTheme_Title": "Custom Theme", + "SettingsDeleteAccount_Description": "Delete all e-mails and credentials associated with this account.", + "SettingsDeleteAccount_Title": "Delete this account", + "SettingsDeleteProtection_Description": "Should Wino ask you for comfirmation every time you try to permanently delete a mail using Shift + Del keys?", + "SettingsDeleteProtection_Title": "Permanent Delete Protection", + "SettingsDiagnostics_Description": "For developers", + "SettingsDiagnostics_Title": "Diagnostics", + "SettingsDiscord_Description": "Get regular development updates, join roadmap discussions and provide feedback.", + "SettingsDiscord_Title": "Discord Channel", + "SettingsElementThemeSelectionDisabled": "Element theme selection is disabled when application theme is selected other than Default.", + "SettingsElementTheme_Description": "Select a Windows theme for Wino", + "SettingsElementTheme_Title": "Element Theme", + "SettingsEnableHoverActions_Title": "Enable hover actions", + "SettingsEnableIMAPLogs_Description": "Enable this to provide details about IMAP connectivity issuses you had during IMAP server setup.", + "SettingsEnableIMAPLogs_Title": "Enable IMAP Protocol Logs", + "SettingsEnableLogs_Description": "I might need logs for crashes to diagnose issues you have opened in GitHub. None of the logs will expose your credentials or sensetive information to public.", + "SettingsEnableLogs_Title": "Enable Logs", + "SettingsEnableSignature": "Enable Signature", + "SettingsExpandOnStartup_Description": "Set whether Wino should expand this account's folders on startup.", + "SettingsExpandOnStartup_Title": "Expand Menu on Startup", + "SettingsExternalContent_Description": "Manage external content settings when rendering mails.", + "SettingsExternalContent_Title": "External Content", + "SettingsFocusedInbox_Description": "Set whether Inbox should be split into two as Focused - Other.", + "SettingsFocusedInbox_Title": "Focused Inbox", + "SettingsFolderSync_Description": "Enable or disable specific folders for synchronization.", + "SettingsFolderSync_Title": "Folder Synchronization", + "SettingsFolderOptions_Title": "Folder Configuration", + "SettingsFolderOptions_Description": "Change individual folder settings like enable/disable sync or show/hide unread badge.", + "SettingsHoverActionCenter": "Center Action", + "SettingsHoverActionLeft": "Left Action", + "SettingsHoverActionRight": "Right Action", + "SettingsHoverActions_Description": "Select 3 actions to show up when you hover over the mails with cursor.", + "SettingsHoverActions_Title": "Hover Actions", + "SettingsLanguage_Description": "Change display language for Wino.", + "SettingsLanguage_Title": "Display Language", + "CategoriesFolderNameOverride": "Categories", + "MoreFolderNameOverride": "More", + "SettingsOptions_Title": "Settings", + "SettingsLinkAccounts_Description": "Merge multiple accounts into one. See mails from one Inbox together.", + "SettingsLinkAccounts_Title": "Create Linked Accounts", + "SettingsLinkedAccountsSave_Description": "Modify the current link with the new accounts.", + "SettingsLinkedAccountsSave_Title": "Save Changes", + "SettingsLoadImages_Title": "Load images automatically", + "SettingsLoadStyles_Title": "Load styles automatically", + "SettingsMailSpacing_Description": "Adjust the spacing for listing mails.", + "SettingsMailSpacing_Title": "Mail Spacing", + "SettingsFolderMenuStyle_Title": "Create Nested Folders", + "SettingsFolderMenuStyle_Description": "Change whether account folders should be nested inside an account menu item or not. Toggle this off if you like the old menu system in Windows Mail", + "SettingsManageAccountSettings_Description": "Notifications, signatures, synchronization and other settings per account.", + "SettingsManageAccountSettings_Title": "Manage Account Settings", + "SettingsManageLink_Description": "Move items to add new link or remove existing link.", + "SettingsManageLink_Title": "Manage Link", + "SettingsMarkAsRead_Description": "Change what should happen to the selected item.", + "SettingsMarkAsRead_DontChange": "Don't automatically mark item as read", + "SettingsMarkAsRead_SecondsToWait": "Seconds to wait: ", + "SettingsMarkAsRead_Timer": "When viewed in the reading pane", + "SettingsMarkAsRead_Title": "Mark item as read", + "SettingsMarkAsRead_WhenSelected": "When selected", + "SettingsMessageList_Description": "Change how your messages should be organized in mail list.", + "SettingsMessageList_Title": "Message List", + "SettingsNoAccountSetupMessage": "You didn't setup any accounts yet.", + "SettingsNotifications_Description": "Turn on or off notifications for this account.", + "SettingsNotifications_Title": "Notifications", + "SettingsPaneLength_Description": "Change the width of the mail list.", + "SettingsPaneLength_Title": "Mail List Pane Length", + "SettingsPaypal_Description": "Show much more love ❤️ All donations are appreciated.", + "SettingsPaypal_Title": "Donate via PayPal", + "SettingsPersonalizationMailDisplayCompactMode": "Compact Mode", + "SettingsPersonalizationMailDisplayMediumMode": "Medium Mode", + "SettingsPersonalizationMailDisplaySpaciousMode": "Spacious Mode", + "SettingsPersonalization_Description": "Change appearance of Wino as you like.", + "SettingsPersonalization_Title": "Personalization", + "SettingsPrivacyPolicy_Description": "Review privacy policy.", + "SettingsPrivacyPolicy_Title": "Privacy Policy", + "SettingsReadingPane_Description": "Mail rendering options.", + "SettingsReadingPane_Title": "Reading Pane", + "SettingsReaderFont_Title": "Default Reader Font", + "SettingsReaderFontFamily_Description": "Change the default font family and font size for reading mails.", + "SettingsFontFamily_Title": "Font Family", + "SettingsFontSize_Title": "Font Size", + "SettingsFontPreview_Title": "Preview", + "SettingsComposerFont_Title": "Default Composer Font", + "SettingsComposerFontFamily_Description": "Change the default font family and font size for composing mails.", + "SettingsRenameMergeAccount_Description": "Change the display name of the linked accounts.", + "SettingsRenameMergeAccount_Title": "Rename", + "SettingsSemanticZoom_Description": "This will allow you to click on the headers in messages list and go to specific date", + "SettingsSemanticZoom_Title": "Semantic Zoom for Date Headers", + "SettingsShowPreviewText_Description": "Hide/show thepreview text.", + "SettingsShowPreviewText_Title": "Show Preview Text", + "SettingsShowSenderPictures_Description": "Hide/show the thumbnail sender pictures.", + "SettingsShowSenderPictures_Title": "Show Sender Avatars", + "SettingsPrefer24HourClock_Title": "Display Clock Format in 24 Hours", + "SettingsPrefer24HourClock_Description": "Mail recieve times will be displayed in 24 hour format instead of 12 (AM/PM)", + "SettingsSignature_Description": "Edit or remove account signature", + "SettingsSignature_Title": "Signature", + "SettingsStartupItem_Description": "Primary account item to load Inbox at startup.", + "SettingsStartupItem_Title": "Startup Item", + "SettingsStore_Description": "Show some love ❤️", + "SettingsStore_Title": "Rate in Store", + "SettingsThreads_Description": "Organize messages into conversation threads.", + "SettingsThreads_Title": "Conversation Threading", + "SettingsUnlinkAccounts_Description": "Remove the link between accounts. This will not delete your accounts.", + "SettingsUnlinkAccounts_Title": "Unlink Accounts", + "SortingOption_Date": "by date", + "SortingOption_Name": "by name", + "StoreRatingDialog_MessageFirstLine": "All feedbacks are appreciated and they will make much Wino better in the future. Would you like to rate Wino in Microsoft Store?", + "StoreRatingDialog_MessageSecondLine": "Would you like to rate Wino Mail in Microsoft Store?", + "StoreRatingDialog_Title": "Enjoying Wino?", + "SystemFolderConfigDialog_ArchiveFolderDescription": "Archived messages will be moved to here.", + "SystemFolderConfigDialog_ArchiveFolderHeader": "Archive Folder", + "SystemFolderConfigDialog_DeletedFolderDescription": "Deleted messages will be moved to here.", + "SystemFolderConfigDialog_DeletedFolderHeader": "Deleted Folder", + "SystemFolderConfigDialog_DraftFolderDescription": "New mails/replies will be crafted in here.", + "SystemFolderConfigDialog_DraftFolderHeader": "Draft Folder", + "SystemFolderConfigDialog_JunkFolderDescription": "All spam/junk mails will be here.", + "SystemFolderConfigDialog_JunkFolderHeader": "Junk/Spam Folder", + "SystemFolderConfigDialog_MessageFirstLine": "This IMAP server doesn't support SPECIAL-USE extension hence Wino couldn't setup the system folders properly.", + "SystemFolderConfigDialog_MessageSecondLine": "Please select the appropriate folders for specific functionalities.", + "SystemFolderConfigDialog_SentFolderDescription": "Folder that sent messages will be stored.", + "SystemFolderConfigDialog_SentFolderHeader": "Sent Folder", + "SystemFolderConfigDialog_Title": "Configure System Folders", + "TestingImapConnectionMessage": "Testing server connection...", + "Today": "Today", + "UnknownAddress": "unknown address", + "UnknownDateHeader": "Unknown Date", + "UnknownGroupAddress": "unknown Mail Group Address", + "UnknownSender": "Unknown Sender", + "Unsubscribe": "Unsubscribe", + "ViewContactDetails": "View Details", + "WinoUpgradeDescription": "Wino offers 3 accounts to start with for free. If you need more than 3 accounts, please upgrade", + "WinoUpgradeMessage": "Upgrade to Unlimited Accounts", + "WinoUpgradeRemainingAccountsMessage": "{0} out of {1} free accounts used.", + "Yesterday": "Yesterday" +} diff --git a/Wino.Core.Domain/Translations/nl_NL/resources.json b/Wino.Core.Domain/Translations/nl_NL/resources.json new file mode 100644 index 00000000..86426587 --- /dev/null +++ b/Wino.Core.Domain/Translations/nl_NL/resources.json @@ -0,0 +1,468 @@ +{ + "AccountCreationDialog_Completed": "klaar", + "AccountCreationDialog_Initializing": "bezig met initialiseren", + "AccountCreationDialog_PreparingFolders": "Bezig met ontvangen van mapgegevens.", + "AccountCreationDialog_SigninIn": "Accountinformatie wordt opgeslagen.", + "AccountEditDialog_Message": "Accountnaam", + "AccountEditDialog_Title": "Bewerk account", + "AccountPickerDialog_Title": "Kies een account", + "AddHyperlink": "Toevoegen", + "AutoDiscoveryProgressMessage": "Zoeken naar mailinstellingen...", + "BasicIMAPSetupDialog_AdvancedConfiguration": "Geavanceerde configuratie", + "BasicIMAPSetupDialog_CredentialLocalMessage": "Uw inloggegevens worden alleen lokaal opgeslagen op uw computer.", + "BasicIMAPSetupDialog_Description": "Sommige accounts vereisen aanvullende stappen om in te loggen", + "BasicIMAPSetupDialog_DisplayName": "Weergavenaam", + "BasicIMAPSetupDialog_DisplayNamePlaceholder": "bijv. Jan Smit", + "BasicIMAPSetupDialog_LearnMore": "Lees meer", + "BasicIMAPSetupDialog_MailAddress": "E-mailadres", + "BasicIMAPSetupDialog_MailAddressPlaceholder": "fritsbarend@voorbeeld.nl", + "BasicIMAPSetupDialog_Password": "Wachtwoord", + "BasicIMAPSetupDialog_Title": "IMAP account", + "Buttons_AddAccount": "Account toevoegen", + "Buttons_ApplyTheme": "Thema toepassen", + "Buttons_Browse": "Bladeren", + "Buttons_Cancel": "Annuleren", + "Buttons_Close": "Sluiten", + "Buttons_Create": "Aanmaken", + "Buttons_CreateAccount": "Account aanmaken", + "Buttons_Delete": "Verwijderen", + "Buttons_Discard": "Weggooien", + "Buttons_EnableImageRendering": "Inschakelen", + "Buttons_No": "Nee", + "Buttons_Open": "Open", + "Buttons_Purchase": "Aanschaffen", + "Buttons_RateWino": "Wino beoordelen", + "Buttons_Save": "Opslaan", + "Buttons_SaveConfiguration": "Configuratie opslaan", + "Buttons_Share": "Delen", + "Buttons_SignIn": "Aanmelden", + "Buttons_Yes": "Ja", + "Center": "Centreren", + "ComingSoon": "Binnenkort beschikbaar...", + "ComposerFrom": "Van: ", + "ComposerSubject": "Onderwerp: ", + "ComposerTo": "Aan: ", + "ClipboardTextCopied_Message": "{0} copied to clipboard.", + "ClipboardTextCopied_Title": "Copied", + "ClipboardTextCopyFailed_Message": "Failed to copy {0} to clipboard.", + "ComposerToPlaceholder": "klik op enter om adressen in te voeren", + "CustomThemeBuilder_AccentColorDescription": "Stel aan aangepaste accentkleur in. Maak geen keuze om de Windows-accentkleur te gebruiken.", + "CustomThemeBuilder_AccentColorTitle": "Accentkleur", + "CustomThemeBuilder_PickColor": "Kies", + "CustomThemeBuilder_ThemeNameDescription": "Unieke naam voor uw aangepaste thema.", + "CustomThemeBuilder_ThemeNameTitle": "Themanaam", + "CustomThemeBuilder_Title": "Aangepaste Thema Bouwer", + "CustomThemeBuilder_WallpaperDescription": "Stel een eigen achtergrond in voor Wino", + "CustomThemeBuilder_WallpaperTitle": "Aangepaste achtergrond instellen", + "DialogMessage_AccountLimitMessage": "U hebt de limiet voor het aanmaken van accounts bereikt.\nWilt u de 'Onbeperkt aantal accounts' add-on kopen om door te gaan?", + "DialogMessage_AccountLimitTitle": "Limiet accounts bereikt", + "DialogMessage_CleanupFolderMessage": "Wilt u alle e-mails in deze map permanent verwijderen?", + "DialogMessage_CleanupFolderTitle": "Opschonen map", + "DialogMessage_ComposerMissingRecipientMessage": "Bericht heeft geen ontvanger.", + "DialogMessage_ComposerValidationFailedTitle": "Validatie mislukt", + "DialogMessage_CreateLinkedAccountMessage": "Geef deze koppeling een naam. Accounts worden samengevoegd onder deze naam.", + "DialogMessage_CreateLinkedAccountTitle": "Naam van koppeling", + "DialogMessage_DeleteAccountConfirmationMessage": "Verwijder {0}?", + "DialogMessage_DeleteAccountConfirmationTitle": "Alle gegevens die gekoppeld zijn aan dit account worden permanent verwijderd.", + "DialogMessage_DiscardDraftConfirmationMessage": "Dit concept zal worden verwijderd. Wilt u doorgaan?", + "DialogMessage_DiscardDraftConfirmationTitle": "Concept verwijderen", + "DialogMessage_HardDeleteConfirmationMessage": "Permanent verwijderen", + "DialogMessage_HardDeleteConfirmationTitle": "Bericht(en) zullen permanent verwijderd worden. Wilt u doorgaan?", + "DialogMessage_NoAccountsForCreateMailMessage": "U heeft geen accounts om een bericht van te maken.", + "DialogMessage_NoAccountsForCreateMailTitle": "Account ontbreekt", + "DialogMessage_RenameLinkedAccountsMessage": "Voer een nieuwe naam in voor gekoppeld account", + "DialogMessage_RenameLinkedAccountsTitle": "Gekoppeld account hernoemen", + "DialogMessage_UnlinkAccountsConfirmationMessage": "Deze handeling zal je accounts niet verwijderen, maar alleen de koppeling van gedeelde mappen verbreken. Wil je doorgaan?", + "DialogMessage_UnlinkAccountsConfirmationTitle": "Ontkoppel accounts", + "DialogMessage_EmptySubjectConfirmation": "Onderwerp ontbreekt", + "DialogMessage_EmptySubjectConfirmationMessage": "Het bericht heeft geen onderwerp. Wilt u doorgaan?", + "Dialog_DontAskAgain": "Niet opnieuw vragen", + "DiscordChannelDisclaimerMessage": "Wino heeft geen eigen Discord server, maar het speciale 'wino-mail' kanaal is gehost op de 'Developer Sanctuary' server.\nOm updates over Wino te krijgen, sluit je je aan bij de Developer Sanctuary server en volg je 'wino-mail' kanaal onder 'Community Projects'\n\nJe wordt doorgestuurd naar de server-URL omdat Discord geen kanaaluitnodigingen ondersteunt.", + "DiscordChannelDisclaimerTitle": "Belangrijke informatie over Discord", + "Draft": "Concept", + "EditorToolbarOption_Draw": "Tekenen", + "EditorToolbarOption_Format": "Opmaak", + "EditorToolbarOption_Insert": "Invoegen", + "EditorToolbarOption_None": "Geen", + "EditorToolbarOption_Options": "Instellingen", + "ElementTheme_Dark": "Donkere modus", + "ElementTheme_Default": "Systeeminstellingen gebruiken", + "ElementTheme_Light": "Lichte modus", + "Emoji": "Emoji", + "Exception_ImapClientPoolFailed": "IMAP Client Pool is mislukt.", + "Exception_AuthenticationCanceled": "Authenticatie geannuleerd", + "Exception_CustomThemeExists": "Dit thema bestaat al.", + "Exception_CustomThemeMissingName": "U moet een naam invullen.", + "Exception_CustomThemeMissingWallpaper": "U moet een aangepaste achtergrondafbeelding invoeren.", + "Exception_FailedToSynchronizeFolders": "Synchroniseren van mappen mislukt", + "Exception_GoogleAuthCallbackNull": "Callback uri is null bij het activeren.", + "Exception_GoogleAuthCorruptedCode": "Beschadigd autorisatieantwoord.", + "Exception_GoogleAuthError": "OAuth autorisatiefout: {0}", + "Exception_GoogleAuthInvalidResponse": "Verzoek ontvangen met ongeldige status ({0})", + "Exception_GoogleAuthorizationCodeExchangeFailed": "Uitwisselen van autorisatiecode is mislukt.", + "Exception_InvalidSystemFolderConfiguration": "De systeemmapconfiguratie is niet geldig. Controleer de configuratie en probeer het opnieuw.", + "Exception_NullAssignedAccount": "Toegewezen account is ongeldig", + "Exception_NullAssignedFolder": "Toegewezen map is ongeldig", + "Exception_SynchronizerFailureHTTP": "Reactieverwerking mislukt met de HTTP-code {0}", + "Exception_TokenGenerationFailed": "Token genereren mislukt", + "Exception_TokenInfoRetrivalFailed": "Fout bij het ophalen van tokeninformatie.", + "Exception_UnknowErrorDuringAuthentication": "Onbekende fout opgetreden tijdens authenticatie", + "Exception_UnsupportedAction": "Actie {0} is niet geïmplementeerd in de aanvraagverwerker", + "Exception_UnsupportedProvider": "Deze aanbieder wordt niet ondersteund.", + "Exception_UnsupportedSynchronizerOperation": "Deze bewerking wordt niet ondersteund voor {0}", + "Exception_UserCancelSystemFolderSetupDialog": "Gebruiker heeft het dialoogvenster configureren systeemmappen geannuleerd.", + "Files": "Bestanden", + "FilteringOption_All": "Alle", + "FilteringOption_Flagged": "Gemarkeerd", + "FilteringOption_Unread": "Ongelezen", + "Focused": "Gefocust", + "FolderOperation_CreateSubFolder": "Maak submap", + "FolderOperation_Delete": "Verwijderen", + "FolderOperation_DontSync": "Deze map niet synchroniseren", + "FolderOperation_Empty": "Map legen", + "FolderOperation_MarkAllAsRead": "Alles als gelezen markeren", + "FolderOperation_Move": "Verplaatsen", + "DragMoveToFolderCaption": "Verplaatsen naar {0}", + "FolderOperation_None": "Geen", + "FolderOperation_Pin": "Vastzetten", + "FolderOperation_Rename": "Hernoemen", + "FolderOperation_Unpin": "Losmaken", + "HoverActionOption_Archive": "Archiveren", + "HoverActionOption_Delete": "Verwijderen", + "HoverActionOption_MoveJunk": "Verplaats naar Ongewenst", + "HoverActionOption_ToggleFlag": "Vlag aan / uit", + "HoverActionOption_ToggleRead": "Gelezen / Ongelezen", + "MergedAccountCommonFolderInbox": "Inbox", + "MergedAccountCommonFolderSent": "Verzonden", + "MergedAccountCommonFolderDraft": "Concept", + "MergedAccountCommonFolderJunk": "Ongewenst", + "MergedAccountCommonFolderTrash": "Verwijderd", + "MergedAccountCommonFolderArchive": "Archief", + "IMAPSetupDialog_AccountType": "Accounttype", + "IMAPSetupDialog_DisplayName": "Weergavenaam", + "IMAPSetupDialog_DisplayNamePlaceholder": "bijv. Jan Smit", + "IMAPSetupDialog_IncomingMailServer": "Inkomende (IMAP) e-mailserver", + "IMAPSetupDialog_IncomingMailServerPort": "Poort", + "IMAPSetupDialog_MailAddress": "E-mailadres", + "IMAPSetupDialog_MailAddressPlaceholder": "iemand@voorbeeld.nl", + "IMAPSetupDialog_OutgoingMailServer": "Uitgaande (SMTP) e-mailserver", + "IMAPSetupDialog_OutgoingMailServerPassword": "Uitgaande server-wachtwoord", + "IMAPSetupDialog_OutgoingMailServerPort": "Poort", + "IMAPSetupDialog_OutgoingMailServerRequireAuthentication": "Uitgaande server vereist authenticatie", + "IMAPSetupDialog_OutgoingMailServerUsername": "Uitgaande server gebruikersnaam", + "IMAPSetupDialog_Password": "Wachtwoord", + "IMAPSetupDialog_RequireSSLForIncomingMail": "SSL verplichten voor inkomende e-mail", + "IMAPSetupDialog_RequireSSLForOutgoingMail": "SSL verplichten voor uitgaande e-mail", + "IMAPSetupDialog_Title": "Geavanceerde IMAP-instellingen", + "IMAPSetupDialog_UseSameConfig": "Dezelfde gebruikersnaam en wachtwoord gebruiken voor het verzenden van e-mail", + "IMAPSetupDialog_Username": "Gebruikersnaam", + "IMAPSetupDialog_UsernamePlaceholder": "johndoe, johndoe@fabrikam.com, domein/johndoe", + "ImageRenderingDisabled": "Afbeeldingsweergave is voor dit bericht uitgeschakeld.", + "InfoBarAction_Enable": "Inschakelen", + "InfoBarMessage_SynchronizationDisabledFolder": "Synchronisatie is uitgeschakeld voor deze map.", + "InfoBarTitle_SynchronizationDisabledFolder": "Uitgeschakelde map", + "GeneralTitle_Error": "Error", + "GeneralTitle_Warning": "Warning", + "GeneralTitle_Info": "Information", + "Info_AccountCreatedMessage": "{0} is aangemaakt", + "Info_AccountCreatedTitle": "Account aanmaken", + "Info_AccountCreationFailedTitle": "Aanmaken van account is mislukt", + "Info_AccountDeletedMessage": "{0} is succesvol verwijderd.", + "Info_AccountDeletedTitle": "Account verwijderd", + "Info_AccountIssueFixFailedTitle": "Mislukt", + "Info_AccountIssueFixSuccessMessage": "Alle accountproblemen zijn opgelost.", + "Info_AccountIssueFixSuccessTitle": "Succesvol", + "Info_AttachmentOpenFailedMessage": "Deze bijlage kan niet geopend worden.", + "Info_AttachmentOpenFailedTitle": "Mislukt", + "Info_AttachmentSaveFailedMessage": "Deze bijlage kan niet opgeslagen worden.", + "Info_AttachmentSaveFailedTitle": "Mislukt", + "Info_AttachmentSaveSuccessMessage": "Bijlage is opgeslagen.", + "Info_AttachmentSaveSuccessTitle": "Bijlage is opgeslagen", + "Info_BackgroundExecutionDeniedMessage": "Uitvoering in de achtergrond voor de app is geweigerd. Dit kan invloed hebben op synchronisatie op de achtergrond en live meldingen.", + "Info_BackgroundExecutionDeniedTitle": "Achtergronduitvoering geweigerd", + "Info_BackgroundExecutionUnknownErrorMessage": "Onbekende fout opgetreden bij het registreren van achtergrondsynchronisatie.", + "Info_BackgroundExecutionUnknownErrorTitle": "Fout bij uitvoeren in achtergrond", + "Info_ComposerMissingMIMEMessage": "Kan het MIME-bestand niet vinden. Synchroniseren kan helpen.", + "Info_ComposerMissingMIMETitle": "Mislukt", + "Info_ContactExistsMessage": "Dit contact staat al in het lijst met ontvangers.", + "Info_ContactExistsTitle": "Contact bestaat", + "Info_DraftFolderMissingMessage": "Conceptmap ontbreekt voor dit account. Controleer uw accountinstellingen.", + "Info_DraftFolderMissingTitle": "Conceptmap ontbreekt", + "Info_FileLaunchFailedTitle": "Kan bestand niet starten", + "Info_InvalidAddressMessage": "{0} is geen geldig e-mailadres.", + "Info_InvalidAddressTitle": "Ongeldig adres", + "Info_InvalidMoveTargetMessage": "Geselecteerde e-mails kunnen niet verplaatst worden naar deze map.", + "Info_InvalidMoveTargetTitle": "Invalid Move Target", + "Info_LogsNotFoundMessage": "Er zijn geen logs om te delen.", + "Info_LogsNotFoundTitle": "Logs niet gevonden", + "Info_LogsSavedMessage": "{0} is opgeslagen in de geselecteerde map.", + "Info_LogsSavedTitle": "Opgeslagen", + "Info_MailRenderingFailedMessage": "Deze e-mail is beschadigd of kan niet worden geopend.\n{0}", + "Info_MailRenderingFailedTitle": "Render Failed", + "Info_MessageCorruptedMessage": "This message is corrupted.", + "Info_MessageCorruptedTitle": "Error", + "Info_MissingFolderMessage": "{0} doesn't exist for this account.", + "Info_MissingFolderTitle": "Missing Folder", + "Info_PurchaseExistsMessage": "Het lijkt erop dat dit product al eerder is gekocht.", + "Info_PurchaseExistsTitle": "Bestaand product", + "Info_PurchaseThankYouMessage": "Hartelijk bedankt!", + "Info_PurchaseThankYouTitle": "Aankoop geslaagd", + "Info_RequestCreationFailedTitle": "Aanvraag aanmaken is mislukt", + "Info_ReviewNetworkErrorMessage": "Er was een netwerkprobleem met uw beoordeling.", + "Info_ReviewNetworkErrorTitle": "Netwerkprobleem", + "Info_ReviewNewMessage": "Alle feedback wordt gewaardeerd. Bedankt voor uw beoordeling!", + "Info_ReviewSuccessTitle": "Bedankt", + "Info_ReviewUnknownErrorMessage": "Er is een onbekend probleem met uw beoordeling. ({0})", + "Info_ReviewUnknownErrorTitle": "Onbekende fout", + "Info_ReviewUpdatedMessage": "Hartelijk dank voor de bijgewerkte beoordeling.", + "Info_SignatureDisabledMessage": "Handtekening voor dit account uitschakelen", + "Info_SignatureDisabledTitle": "Gelukt", + "Info_SignatureSavedMessage": "Nieuwe handtekening is opgeslagen", + "Info_SignatureSavedTitle": "Gelukt", + "Info_SyncCanceledMessage": "Geannuleerd", + "Info_SyncCanceledTitle": "Synchronisatie", + "Info_SyncFailedTitle": "Synchronisatie is mislukt", + "Info_UnsupportedFunctionalityDescription": "Deze functionaliteit is nog niet geïmplementeerd.", + "Info_UnsupportedFunctionalityTitle": "Niet ondersteund", + "Info_UnsubscribeLinkInvalidTitle": "Ongeldige uitschrijf-URI", + "Info_UnsubscribeLinkInvalidMessage": "Deze afmeldlink is ongeldig. Afmelden van de lijst is mislukt.", + "ImapAdvancedSetupDialog_AuthenticationMethod": "Authenticatiemethode", + "ImapAdvancedSetupDialog_ConnectionSecurity": "Beveiliging van verbinding", + "ImapAuthenticationMethod_Auto": "Automatisch", + "ImapAuthenticationMethod_CramMD5": "CRAM-MD5", + "ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5", + "ImapAuthenticationMethod_None": "Geen authenticatie", + "ImapAuthenticationMethod_Plain": "Normaal wachtwoord", + "ImapAuthenticationMethod_EncryptedPassword": "Versleuteld wachtwoord", + "ImapAuthenticationMethod_Ntlm": "NTLM", + "ImapConnectionSecurity_None": "Geen", + "ImapConnectionSecurity_SslTls": "SSL/TLS", + "ImapConnectionSecurity_StartTls": "STARTTLS", + "ImapConnectionSecurity_Auto": "Automatisch", + "Justify": "Uitvullen", + "Left": "Links", + "Link": "Link", + "LinkedAccountsCreatePolicyMessage": "Je moet ten minste 2 accounts hebben om een koppeling te maken.\nDe koppeling zal verwijderd worden bij het opslaan.", + "LinkedAccountsTitle": "Gekoppelde accounts", + "MailOperation_AlwaysMoveFocused": "Altijd verplaatsen naar focus", + "MailOperation_AlwaysMoveOther": "Altijd verplaatsen naar andere", + "MailOperation_Archive": "Archiveren", + "MailOperation_ClearFlag": "Markering wissen", + "MailOperation_DarkEditor": "Donker", + "MailOperation_Delete": "Verwijderen", + "MailOperation_ExportPDF": "Exporteren naar PDF", + "MailOperation_Find": "Zoeken", + "MailOperation_Forward": "Doorsturen", + "MailOperation_Ignore": "Ignore", + "MailOperation_LightEditor": "Light", + "MailOperation_MarkAsJunk": "Mark as junk", + "MailOperation_MarkAsRead": "Mark as read", + "MailOperation_MarkAsUnread": "Mark as unread", + "MailOperation_MarkNotJunk": "Mark as Not Junk", + "MailOperation_Move": "Move", + "MailOperation_MoveFocused": "Move to Focused", + "MailOperation_MoveJunk": "Move to Junk", + "MailOperation_MoveOther": "Move to Other", + "MailOperation_Navigate": "Navigate", + "MailOperation_Print": "Print", + "MailOperation_Reply": "Reply", + "MailOperation_ReplyAll": "Reply all", + "MailOperation_SaveAs": "Save As", + "MailOperation_SetFlag": "Set flag", + "MailOperation_Unarchive": "Unarchive", + "MailOperation_Zoom": "Zoom", + "MailsSelected": "{0} item(s) selected", + "MarkFlagUnflag": "Mark as flagged/unflagged", + "MarkReadUnread": "Mark as read/unread", + "MenuManageAccounts": "Manage Accounts", + "MenuNewMail": "New Mail", + "MenuMergedAccountItemAccountsSuffix": " accounts", + "MenuRate": "Rate Wino", + "MenuSettings": "Settings", + "MergedAccountsAvailableAccountsTitle": "Available Accounts", + "More": "More", + "MoveMailDialog_InvalidFolderMessage": "{0} is not a valid folder for this mail.", + "MoveMailDialog_Title": "Pick a folder", + "NewAccountDialog_AccountName": "Account Name", + "NewAccountDialog_AccountNameDefaultValue": "Personal", + "NewAccountDialog_AccountNamePlaceholder": "eg. Personal Mail", + "NewAccountDialog_Title": "Add New Account", + "NoMailSelected": "no message selected", + "NoMessageCrieteria": "no messages match your search criteria.", + "NoMessageEmptyFolder": "this folder is empty.", + "Notifications_MultipleNotificationsMessage": "You have {0} new mails", + "Notifications_MultipleNotificationsTitle": "New Mails", + "Notifications_WinoUpdatedMessage": "Checkout new version {0}", + "Notifications_WinoUpdatedTitle": "Wino Mail has been updated.", + "Other": "Other", + "PaneLengthOption_Default": "Default", + "PaneLengthOption_ExtraLarge": "Extra Large", + "PaneLengthOption_Large": "Groot", + "PaneLengthOption_Medium": "Gemiddeld", + "PaneLengthOption_Micro": "Micro", + "PaneLengthOption_Small": "Klein", + "Photos": "Foto’s", + "PreparingFoldersMessage": "Mappen voorbereiden", + "ProviderDetail_Gmail_Description": "Google Account", + "ProviderDetail_IMAP_Description": "Aangepaste IMAP/SMTP server", + "ProviderDetail_IMAP_Title": "IMAP-server", + "Results": "Resultaten", + "Right": "Rechts", + "SynchronizationFolderReport_Success": "Up-to-date", + "SynchronizationFolderReport_Failed": "Synchronisatie is mislukt", + "SearchBarPlaceholder": "zoeken", + "SearchingIn": "Zoeken in", + "SettingsAboutGithub_Description": "Ga naar de Issue Tracker in de GitHub-repository.", + "SettingsAboutGithub_Title": "GitHub", + "SettingsAccountManagementAppendMessage_Title": "Berichten toevoegen aan map Verzonden", + "SettingsAccountManagementAppendMessage_Description": "Maak een kopie van het bericht in de map Verzonden nadat het concept is verzonden. Schakel dit in als u uw e-mails niet ziet nadat u ze hebt verzonden in de map 'Verzonden'.", + "SettingsEditLinkedInbox_Title": "Gekoppelde inbox bewerken", + "SettingsEditLinkedInbox_Description": "Accounts toevoegen, verwijderen, hernoemen of koppeling bewerken.", + "SettingsAboutVersion": "Versie ", + "SettingsAboutWinoDescription": "Lichtgewicht e-mail client voor Windows-apparaten.", + "SettingsAbout_Description": "Meer informatie over Wino.", + "SettingsAbout_Title": "Over", + "SettingsAccentColor_Description": "Wijzig accentkleur van applicatie", + "SettingsAccentColor_Title": "Accentkleur", + "SettingsAccentColor_UseWindowsAccentColor": "Mijn Windows-accentkleur gebruiken", + "SettingsAccountName_Description": "Verander de naam van het account.", + "SettingsAccountName_Title": "Accountnaam", + "SettingsApplicationTheme_Description": "Personaliseer Wino met aangepaste thema's.", + "SettingsApplicationTheme_Title": "Applicatie thema", + "SettingsAvailableThemes_Description": "Selecteer een thema uit Wino’s collectie of pas uw eigen thema's toe.", + "SettingsAvailableThemes_Title": "Beschikbare thema's", + "SettingsCustomTheme_Description": "Maak uw eigen aangepaste thema met aangepaste achtergrond en accentkleur.", + "SettingsCustomTheme_Title": "Aangepast Thema", + "SettingsDeleteAccount_Description": "Verwijder alle e-mails en referenties die aan dit account zijn gekoppeld.", + "SettingsDeleteAccount_Title": "Verwijder dit account", + "SettingsDeleteProtection_Description": "Moet Wino u om bevestiging vragen elke keer dat u een e-mail met Shift + Del verwijderd?", + "SettingsDeleteProtection_Title": "Permanente verwijder-bescherming", + "SettingsDiagnostics_Description": "Voor ontwikkelaars", + "SettingsDiagnostics_Title": "Diagnose", + "SettingsDiscord_Description": "Krijg regelmatige ontwikkelingsupdates, neem deel aan roadmap discussies en geef feedback.", + "SettingsDiscord_Title": "Discord-kanaal", + "SettingsElementThemeSelectionDisabled": "Element themaselectie is uitgeschakeld wanneer een niet-standaard thema is geselecteerd.", + "SettingsElementTheme_Description": "Selecteer een Windows-thema voor Wino", + "SettingsElementTheme_Title": "Element thema", + "SettingsEnableHoverActions_Title": "Inschakelen van aanwijsacties", + "SettingsEnableIMAPLogs_Description": "Schakel dit in om gegevens over IMAP-connectiviteit te verstrekken die je had tijdens de IMAP-server installatie.", + "SettingsEnableIMAPLogs_Title": "IMAP Protocol Logs inschakelen", + "SettingsEnableLogs_Description": "Mogelijk heb ik de logs nodig om de door jou geopende problemen op GitHub te kunnen analyseren. Geen van de logs zal uw inloggegevens of gevoelige informatie bevatten.", + "SettingsEnableLogs_Title": "Logs inschakelen", + "SettingsEnableSignature": "Handtekening inschakelen", + "SettingsExpandOnStartup_Description": "Set whether Wino should expand this account's folders on startup.", + "SettingsExpandOnStartup_Title": "Expand Menu on Startup", + "SettingsExternalContent_Description": "Manage external content settings when rendering mails.", + "SettingsExternalContent_Title": "External Content", + "SettingsFocusedInbox_Description": "Set whether Inbox should be split into two as Focused - Other.", + "SettingsFocusedInbox_Title": "Focused Inbox", + "SettingsFolderSync_Description": "Enable or disable specific folders for synchronization.", + "SettingsFolderSync_Title": "Folder Synchronization", + "SettingsFolderOptions_Title": "Folder Configuration", + "SettingsFolderOptions_Description": "Change individual folder settings like enable/disable sync or show/hide unread badge.", + "SettingsHoverActionCenter": "Center Action", + "SettingsHoverActionLeft": "Left Action", + "SettingsHoverActionRight": "Right Action", + "SettingsHoverActions_Description": "Select 3 actions to show up when you hover over the mails with cursor.", + "SettingsHoverActions_Title": "Hover Actions", + "SettingsLanguage_Description": "Change display language for Wino.", + "SettingsLanguage_Title": "Display Language", + "CategoriesFolderNameOverride": "Categories", + "MoreFolderNameOverride": "More", + "SettingsOptions_Title": "Settings", + "SettingsLinkAccounts_Description": "Merge multiple accounts into one. See mails from one Inbox together.", + "SettingsLinkAccounts_Title": "Create Linked Accounts", + "SettingsLinkedAccountsSave_Description": "Modify the current link with the new accounts.", + "SettingsLinkedAccountsSave_Title": "Save Changes", + "SettingsLoadImages_Title": "Load images automatically", + "SettingsLoadStyles_Title": "Load styles automatically", + "SettingsMailSpacing_Description": "Adjust the spacing for listing mails.", + "SettingsMailSpacing_Title": "Mail Spacing", + "SettingsFolderMenuStyle_Title": "Create Nested Folders", + "SettingsFolderMenuStyle_Description": "Change whether account folders should be nested inside an account menu item or not. Toggle this off if you like the old menu system in Windows Mail", + "SettingsManageAccountSettings_Description": "Notifications, signatures, synchronization and other settings per account.", + "SettingsManageAccountSettings_Title": "Manage Account Settings", + "SettingsManageLink_Description": "Move items to add new link or remove existing link.", + "SettingsManageLink_Title": "Manage Link", + "SettingsMarkAsRead_Description": "Change what should happen to the selected item.", + "SettingsMarkAsRead_DontChange": "Don't automatically mark item as read", + "SettingsMarkAsRead_SecondsToWait": "Seconds to wait: ", + "SettingsMarkAsRead_Timer": "When viewed in the reading pane", + "SettingsMarkAsRead_Title": "Mark item as read", + "SettingsMarkAsRead_WhenSelected": "When selected", + "SettingsMessageList_Description": "Change how your messages should be organized in mail list.", + "SettingsMessageList_Title": "Message List", + "SettingsNoAccountSetupMessage": "You didn't setup any accounts yet.", + "SettingsNotifications_Description": "Turn on or off notifications for this account.", + "SettingsNotifications_Title": "Notifications", + "SettingsPaneLength_Description": "Change the width of the mail list.", + "SettingsPaneLength_Title": "Mail List Pane Length", + "SettingsPaypal_Description": "Show much more love ❤️ All donations are appreciated.", + "SettingsPaypal_Title": "Donate via PayPal", + "SettingsPersonalizationMailDisplayCompactMode": "Compact Mode", + "SettingsPersonalizationMailDisplayMediumMode": "Medium Mode", + "SettingsPersonalizationMailDisplaySpaciousMode": "Spacious Mode", + "SettingsPersonalization_Description": "Change appearance of Wino as you like.", + "SettingsPersonalization_Title": "Personalization", + "SettingsPrivacyPolicy_Description": "Review privacy policy.", + "SettingsPrivacyPolicy_Title": "Privacy Policy", + "SettingsReadingPane_Description": "Mail rendering options.", + "SettingsReadingPane_Title": "Reading Pane", + "SettingsReaderFont_Title": "Default Reader Font", + "SettingsReaderFontFamily_Description": "Change the default font family and font size for reading mails.", + "SettingsFontFamily_Title": "Font Family", + "SettingsFontSize_Title": "Font Size", + "SettingsFontPreview_Title": "Preview", + "SettingsComposerFont_Title": "Default Composer Font", + "SettingsComposerFontFamily_Description": "Change the default font family and font size for composing mails.", + "SettingsRenameMergeAccount_Description": "Change the display name of the linked accounts.", + "SettingsRenameMergeAccount_Title": "Rename", + "SettingsSemanticZoom_Description": "This will allow you to click on the headers in messages list and go to specific date", + "SettingsSemanticZoom_Title": "Semantic Zoom for Date Headers", + "SettingsShowPreviewText_Description": "Hide/show thepreview text.", + "SettingsShowPreviewText_Title": "Show Preview Text", + "SettingsShowSenderPictures_Description": "Hide/show the thumbnail sender pictures.", + "SettingsShowSenderPictures_Title": "Show Sender Avatars", + "SettingsPrefer24HourClock_Title": "Display Clock Format in 24 Hours", + "SettingsPrefer24HourClock_Description": "Mail recieve times will be displayed in 24 hour format instead of 12 (AM/PM)", + "SettingsSignature_Description": "Edit or remove account signature", + "SettingsSignature_Title": "Signature", + "SettingsStartupItem_Description": "Primary account item to load Inbox at startup.", + "SettingsStartupItem_Title": "Startup Item", + "SettingsStore_Description": "Show some love ❤️", + "SettingsStore_Title": "Rate in Store", + "SettingsThreads_Description": "Organize messages into conversation threads.", + "SettingsThreads_Title": "Conversation Threading", + "SettingsUnlinkAccounts_Description": "Remove the link between accounts. This will not delete your accounts.", + "SettingsUnlinkAccounts_Title": "Unlink Accounts", + "SortingOption_Date": "by date", + "SortingOption_Name": "by name", + "StoreRatingDialog_MessageFirstLine": "All feedbacks are appreciated and they will make much Wino better in the future. Would you like to rate Wino in Microsoft Store?", + "StoreRatingDialog_MessageSecondLine": "Would you like to rate Wino Mail in Microsoft Store?", + "StoreRatingDialog_Title": "Enjoying Wino?", + "SystemFolderConfigDialog_ArchiveFolderDescription": "Archived messages will be moved to here.", + "SystemFolderConfigDialog_ArchiveFolderHeader": "Archive Folder", + "SystemFolderConfigDialog_DeletedFolderDescription": "Deleted messages will be moved to here.", + "SystemFolderConfigDialog_DeletedFolderHeader": "Deleted Folder", + "SystemFolderConfigDialog_DraftFolderDescription": "New mails/replies will be crafted in here.", + "SystemFolderConfigDialog_DraftFolderHeader": "Draft Folder", + "SystemFolderConfigDialog_JunkFolderDescription": "All spam/junk mails will be here.", + "SystemFolderConfigDialog_JunkFolderHeader": "Junk/Spam Folder", + "SystemFolderConfigDialog_MessageFirstLine": "This IMAP server doesn't support SPECIAL-USE extension hence Wino couldn't setup the system folders properly.", + "SystemFolderConfigDialog_MessageSecondLine": "Please select the appropriate folders for specific functionalities.", + "SystemFolderConfigDialog_SentFolderDescription": "Folder that sent messages will be stored.", + "SystemFolderConfigDialog_SentFolderHeader": "Sent Folder", + "SystemFolderConfigDialog_Title": "Configure System Folders", + "TestingImapConnectionMessage": "Testing server connection...", + "Today": "Today", + "UnknownAddress": "unknown address", + "UnknownDateHeader": "Unknown Date", + "UnknownGroupAddress": "unknown Mail Group Address", + "UnknownSender": "Unknown Sender", + "Unsubscribe": "Unsubscribe", + "ViewContactDetails": "View Details", + "WinoUpgradeDescription": "Wino offers 3 accounts to start with for free. If you need more than 3 accounts, please upgrade", + "WinoUpgradeMessage": "Upgrade to Unlimited Accounts", + "WinoUpgradeRemainingAccountsMessage": "{0} out of {1} free accounts used.", + "Yesterday": "Yesterday" +} diff --git a/Wino.Core.Domain/Translations/pl_PL/resources.json b/Wino.Core.Domain/Translations/pl_PL/resources.json new file mode 100644 index 00000000..580cc40f --- /dev/null +++ b/Wino.Core.Domain/Translations/pl_PL/resources.json @@ -0,0 +1,468 @@ +{ + "AccountCreationDialog_Completed": "gotowe", + "AccountCreationDialog_Initializing": "inicjowanie", + "AccountCreationDialog_PreparingFolders": "Trwa pobieranie informacji o folderach.", + "AccountCreationDialog_SigninIn": "Zapisywanie danych konta.", + "AccountEditDialog_Message": "Nazwa konta", + "AccountEditDialog_Title": "Edytuj konto", + "AccountPickerDialog_Title": "Wybierz konto", + "AddHyperlink": "Dodaj", + "AutoDiscoveryProgressMessage": "Wyszukiwanie ustawień poczty...", + "BasicIMAPSetupDialog_AdvancedConfiguration": "Konfiguracja zaawansowana", + "BasicIMAPSetupDialog_CredentialLocalMessage": "Twoje dane logowania będą przechowywane lokalnie tylko na Twoim komputerze.", + "BasicIMAPSetupDialog_Description": "Niektóre konta wymagają dodatkowych kroków do zalogowania", + "BasicIMAPSetupDialog_DisplayName": "Wyświetlana nazwa", + "BasicIMAPSetupDialog_DisplayNamePlaceholder": "np. Jan Kowalski", + "BasicIMAPSetupDialog_LearnMore": "Dowiedz się więcej", + "BasicIMAPSetupDialog_MailAddress": "Adres e-mail", + "BasicIMAPSetupDialog_MailAddressPlaceholder": "jankowalski@jakasdomena.pl", + "BasicIMAPSetupDialog_Password": "Hasło", + "BasicIMAPSetupDialog_Title": "Konto IMAP", + "Buttons_AddAccount": "Dodaj konto", + "Buttons_ApplyTheme": "Zastosuj motyw", + "Buttons_Browse": "Przeglądaj", + "Buttons_Cancel": "Anuluj", + "Buttons_Close": "Zamknij", + "Buttons_Create": "Utwórz", + "Buttons_CreateAccount": "Utwórz konto", + "Buttons_Delete": "Usuń", + "Buttons_Discard": "Odrzuć", + "Buttons_EnableImageRendering": "Włącz", + "Buttons_No": "Nie", + "Buttons_Open": "Otwórz", + "Buttons_Purchase": "Kup", + "Buttons_RateWino": "Oceń Wino", + "Buttons_Save": "Zapisz", + "Buttons_SaveConfiguration": "Zapisz konfigurację", + "Buttons_Share": "Udostępnij", + "Buttons_SignIn": "Zaloguj się", + "Buttons_Yes": "Tak", + "Center": "Wyśrodkuj", + "ComingSoon": "Już wkrótce...", + "ComposerFrom": "Od: ", + "ComposerSubject": "Temat: ", + "ComposerTo": "Do: ", + "ClipboardTextCopied_Message": "{0} copied to clipboard.", + "ClipboardTextCopied_Title": "Copied", + "ClipboardTextCopyFailed_Message": "Failed to copy {0} to clipboard.", + "ComposerToPlaceholder": "kliknij Enter, aby wprowadzić adresy", + "CustomThemeBuilder_AccentColorDescription": "Ustaw niestandardowy kolor akcentu, jeśli chcesz. Nie wybierając koloru, zostanie użyty kolor akcentu Windows.", + "CustomThemeBuilder_AccentColorTitle": "Kolor akcentów", + "CustomThemeBuilder_PickColor": "Wybierz", + "CustomThemeBuilder_ThemeNameDescription": "Unikalna nazwa dla Twojego motywu.", + "CustomThemeBuilder_ThemeNameTitle": "Nazwa motywu", + "CustomThemeBuilder_Title": "Kreator niestandardowych motywów", + "CustomThemeBuilder_WallpaperDescription": "Ustaw niestandardową tapetę dla Wino", + "CustomThemeBuilder_WallpaperTitle": "Ustaw niestandardową tapetę", + "DialogMessage_AccountLimitMessage": "Osiągnąłeś limit utworzonych kont.\nCzy chcesz kupić dodatek \"Nielimitowane konto\", aby kontynuować?", + "DialogMessage_AccountLimitTitle": "Osiągnięto limit konta", + "DialogMessage_CleanupFolderMessage": "Czy chcesz trwale usunąć wszystkie wiadomości z tego folderu?", + "DialogMessage_CleanupFolderTitle": "Opróżnianie folderu", + "DialogMessage_ComposerMissingRecipientMessage": "Wiadomość nie ma odbiorcy.", + "DialogMessage_ComposerValidationFailedTitle": "Walidacja nie powiodła się", + "DialogMessage_CreateLinkedAccountMessage": "Nadaj nowemu połączeniu nazwę. Konta zostaną scalone pod tą nazwą.", + "DialogMessage_CreateLinkedAccountTitle": "Nazwa połączonego konta", + "DialogMessage_DeleteAccountConfirmationMessage": "Usunąć {0}?", + "DialogMessage_DeleteAccountConfirmationTitle": "Wszystkie dane powiązane z tym kontem zostaną trwale usunięte z dysku.", + "DialogMessage_DiscardDraftConfirmationMessage": "Ta wersja robocza zostanie odrzucona. Czy chcesz kontynuować?", + "DialogMessage_DiscardDraftConfirmationTitle": "Porzuć wersje roboczą", + "DialogMessage_HardDeleteConfirmationMessage": "Usuń trwale", + "DialogMessage_HardDeleteConfirmationTitle": "Wiadomość(ci) zostaną trwale usunięte. Czy chcesz kontynuować?", + "DialogMessage_NoAccountsForCreateMailMessage": "Nie masz żadnych kont z których możesz utworzyć wiadomość.", + "DialogMessage_NoAccountsForCreateMailTitle": "Brak konta", + "DialogMessage_RenameLinkedAccountsMessage": "Wprowadź nową nazwę dla połączonego konta", + "DialogMessage_RenameLinkedAccountsTitle": "Zmień nazwę połączonego konta", + "DialogMessage_UnlinkAccountsConfirmationMessage": "Ta operacja nie usunie Twoich kont, tylko przerwie połączenie dla wspólnych folderów. Czy chcesz kontynuować?", + "DialogMessage_UnlinkAccountsConfirmationTitle": "Odłącz konto", + "DialogMessage_EmptySubjectConfirmation": "Brak tematu", + "DialogMessage_EmptySubjectConfirmationMessage": "Wiadomość nie ma tematu. Czy chcesz kontynuować?", + "Dialog_DontAskAgain": "Nie pytaj ponownie", + "DiscordChannelDisclaimerMessage": "Wino nie ma własnego serwera Discorda, ale specjalny kanał \"wino-mail\" który jest hostowany na serwerze \"Developer Sanctuary\".\nAby otrzymywać najświeższe informacje o Wino dołącz do serwera Developer Sanctuary i obserwuj kanał 'wino-mail' pod 'Community Projects'\n\nZostaniesz przekierowany na adres URL serwera, ponieważ Discord nie obsługuje kanałów zaproszeń.", + "DiscordChannelDisclaimerTitle": "Ważne informacje o kanale na Discordzie", + "Draft": "Wersja robocza", + "EditorToolbarOption_Draw": "Rysowanie", + "EditorToolbarOption_Format": "Formatowanie", + "EditorToolbarOption_Insert": "Wstawianie", + "EditorToolbarOption_None": "Żadne", + "EditorToolbarOption_Options": "Opcje", + "ElementTheme_Dark": "Tryb ciemny", + "ElementTheme_Default": "Użyj ustawień systemowych", + "ElementTheme_Light": "Tryb jasny", + "Emoji": "Emotikony", + "Exception_ImapClientPoolFailed": "Pula klienta IMAP nie powiodła się.", + "Exception_AuthenticationCanceled": "Anulowano uwierzytelnianie", + "Exception_CustomThemeExists": "Ten motyw już istnieje.", + "Exception_CustomThemeMissingName": "Musisz podać nazwę.", + "Exception_CustomThemeMissingWallpaper": "Musisz podać niestandardowy obraz tła.", + "Exception_FailedToSynchronizeFolders": "Nie udało się zsynchronizować folderów", + "Exception_GoogleAuthCallbackNull": "Adres uri wywołania zwrotnego jest pusty przy aktywacji.", + "Exception_GoogleAuthCorruptedCode": "Uszkodzona odpowiedź autoryzacji.", + "Exception_GoogleAuthError": "Błąd autoryzacji OAuth: {0}", + "Exception_GoogleAuthInvalidResponse": "Otrzymano żądanie z nieprawidłowym stanem ({0})", + "Exception_GoogleAuthorizationCodeExchangeFailed": "Wymiana kodu autoryzacji nie powiodła się.", + "Exception_InvalidSystemFolderConfiguration": "Konfiguracja folderu systemowego jest nieprawidłowa. Sprawdź konfigurację i spróbuj ponownie.", + "Exception_NullAssignedAccount": "Przypisane konto jest puste", + "Exception_NullAssignedFolder": "Przypisany folder jest pusty", + "Exception_SynchronizerFailureHTTP": "Obsługa odpowiedzi nie powiodła się. Kod błędu HTTP: {0}", + "Exception_TokenGenerationFailed": "Generowanie tokenu nie powiodło się", + "Exception_TokenInfoRetrivalFailed": "Nie udało się uzyskać informacji o tokenie.", + "Exception_UnknowErrorDuringAuthentication": "Wystąpił nieznany błąd podczas uwierzytelniania", + "Exception_UnsupportedAction": "Akcja {0} nie jest zaimplementowana w procesorze żądania", + "Exception_UnsupportedProvider": "Ten dostawca nie jest obsługiwany.", + "Exception_UnsupportedSynchronizerOperation": "Ta operacja nie jest obsługiwana dla {0}", + "Exception_UserCancelSystemFolderSetupDialog": "Użytkownik anulował okno dialogowe konfiguracji folderów systemowych.", + "Files": "Pliki", + "FilteringOption_All": "Wszystkie", + "FilteringOption_Flagged": "Oflagowane", + "FilteringOption_Unread": "Nieprzeczytane", + "Focused": "Priorytetowe", + "FolderOperation_CreateSubFolder": "Utwórz podfolder", + "FolderOperation_Delete": "Usuń", + "FolderOperation_DontSync": "Nie synchronizuj tego folderu", + "FolderOperation_Empty": "Opróżnij ten folder", + "FolderOperation_MarkAllAsRead": "Oznacz wszystkie jako przeczytane", + "FolderOperation_Move": "Przenieś", + "DragMoveToFolderCaption": "Przenieś do {0}", + "FolderOperation_None": "Żadne", + "FolderOperation_Pin": "Przypnij", + "FolderOperation_Rename": "Zmień nazwę", + "FolderOperation_Unpin": "Odepnij", + "HoverActionOption_Archive": "Archiwizuj", + "HoverActionOption_Delete": "Usuń", + "HoverActionOption_MoveJunk": "Przenieś do wiadomości-śmieci", + "HoverActionOption_ToggleFlag": "Oflaguj / Usuń flagę", + "HoverActionOption_ToggleRead": "Przeczytane / Nieprzeczytane", + "MergedAccountCommonFolderInbox": "Skrzynka odbiorcza", + "MergedAccountCommonFolderSent": "Wysłane", + "MergedAccountCommonFolderDraft": "Wersje robocze", + "MergedAccountCommonFolderJunk": "Wiadomości-śmieci", + "MergedAccountCommonFolderTrash": "Usunięte", + "MergedAccountCommonFolderArchive": "Archiwum", + "IMAPSetupDialog_AccountType": "Typ konta", + "IMAPSetupDialog_DisplayName": "Wyświetlana nazwa", + "IMAPSetupDialog_DisplayNamePlaceholder": "np. Jan Kowalski", + "IMAPSetupDialog_IncomingMailServer": "Serwer poczty przychodzącej", + "IMAPSetupDialog_IncomingMailServerPort": "Port", + "IMAPSetupDialog_MailAddress": "Adres e-mail", + "IMAPSetupDialog_MailAddressPlaceholder": "ktoś@przykład.com", + "IMAPSetupDialog_OutgoingMailServer": "Serwer poczty wychodzącej (SMTP)", + "IMAPSetupDialog_OutgoingMailServerPassword": "Hasło serwera wychodzącego", + "IMAPSetupDialog_OutgoingMailServerPort": "Port", + "IMAPSetupDialog_OutgoingMailServerRequireAuthentication": "Serwer wychodzący wymaga uwierzytelnienia", + "IMAPSetupDialog_OutgoingMailServerUsername": "Nazwa użytkownika serwera wychodzącego", + "IMAPSetupDialog_Password": "Hasło", + "IMAPSetupDialog_RequireSSLForIncomingMail": "Wymagaj SSL dla poczty przychodzącej", + "IMAPSetupDialog_RequireSSLForOutgoingMail": "Wymagaj SSL dla poczty wychodzącej", + "IMAPSetupDialog_Title": "Zaawansowana konfiguracja IMAP", + "IMAPSetupDialog_UseSameConfig": "Użyj tej samej nazwy użytkownika i hasła dla poczty wychodzącej", + "IMAPSetupDialog_Username": "Nazwa użytkownika", + "IMAPSetupDialog_UsernamePlaceholder": "jankowalski, jankowalski@jakasdomena.com, domena/jankowalski", + "ImageRenderingDisabled": "Wyświetlanie obrazów jest wyłączone dla tej wiadomości.", + "InfoBarAction_Enable": "Włącz", + "InfoBarMessage_SynchronizationDisabledFolder": "Ten folder jest wyłączony z synchronizacji.", + "InfoBarTitle_SynchronizationDisabledFolder": "Katalog niesynchronizowany", + "GeneralTitle_Error": "Error", + "GeneralTitle_Warning": "Warning", + "GeneralTitle_Info": "Information", + "Info_AccountCreatedMessage": "Utworzono {0}", + "Info_AccountCreatedTitle": "Tworzenie konta", + "Info_AccountCreationFailedTitle": "Tworzenie konta nie powiodło się", + "Info_AccountDeletedMessage": "Usunięto {0}.", + "Info_AccountDeletedTitle": "Konto usunięte", + "Info_AccountIssueFixFailedTitle": "Nie powiodło się", + "Info_AccountIssueFixSuccessMessage": "Naprawiono wszystkie problemy z kontem.", + "Info_AccountIssueFixSuccessTitle": "Zakończono pomyślnie", + "Info_AttachmentOpenFailedMessage": "Nie można otworzyć tego załącznika.", + "Info_AttachmentOpenFailedTitle": "Nie powiodło się", + "Info_AttachmentSaveFailedMessage": "Nie można zapisać tego załącznika.", + "Info_AttachmentSaveFailedTitle": "Nie powiodło się", + "Info_AttachmentSaveSuccessMessage": "Załącznik został zapisany.", + "Info_AttachmentSaveSuccessTitle": "Zapisano załącznik", + "Info_BackgroundExecutionDeniedMessage": "Odmowa działania aplikacji w tle. Może to mieć wpływ na synchronizację w tle i powiadomienia na żywo.", + "Info_BackgroundExecutionDeniedTitle": "Odmowa działania w tle", + "Info_BackgroundExecutionUnknownErrorMessage": "Wystąpił nieznany błąd podczas rejestracji synchronizacji w tle.", + "Info_BackgroundExecutionUnknownErrorTitle": "Działanie w tle nie powiodło się", + "Info_ComposerMissingMIMEMessage": "Nie można zlokalizować pliku MIME. Synchronizacja może pomóc.", + "Info_ComposerMissingMIMETitle": "Nie powiodło się", + "Info_ContactExistsMessage": "Ten kontakt jest już na liście odbiorców.", + "Info_ContactExistsTitle": "Kontakt istnieje", + "Info_DraftFolderMissingMessage": "Brak folderu wersji roboczych dla tego konta. Proszę sprawdzić ustawienia konta.", + "Info_DraftFolderMissingTitle": "Brak folderu wersji roboczych", + "Info_FileLaunchFailedTitle": "Nie udało się uruchomić pliku", + "Info_InvalidAddressMessage": "{0} nie jest prawidłowym adresem e-mail.", + "Info_InvalidAddressTitle": "Błędny adres", + "Info_InvalidMoveTargetMessage": "Nie możesz przenieść zaznaczonych wiadomości do tego folderu.", + "Info_InvalidMoveTargetTitle": "Nieprawidłowy folder docelowy", + "Info_LogsNotFoundMessage": "Brak logów do udostępnienia.", + "Info_LogsNotFoundTitle": "Nie znaleziono logów", + "Info_LogsSavedMessage": "{0} został zapisany w wybranym folderze.", + "Info_LogsSavedTitle": "Zapisano", + "Info_MailRenderingFailedMessage": "Ta wiadomość jest uszkodzona lub nie może zostać otwarta.\n{0}", + "Info_MailRenderingFailedTitle": "Wyświetlenie nie powiodło się", + "Info_MessageCorruptedMessage": "Ta wiadomość jest uszkodzona.", + "Info_MessageCorruptedTitle": "Błąd", + "Info_MissingFolderMessage": "{0} nie istnieje dla tego konta.", + "Info_MissingFolderTitle": "Brakujący folder", + "Info_PurchaseExistsMessage": "Wygląda na to, że ten produkt został już zakupiony wcześniej.", + "Info_PurchaseExistsTitle": "Produkt już został wykupiony", + "Info_PurchaseThankYouMessage": "Dziękuję", + "Info_PurchaseThankYouTitle": "Zakup zakończony pomyślnie", + "Info_RequestCreationFailedTitle": "Nie udało się utworzyć żądań", + "Info_ReviewNetworkErrorMessage": "Wystąpił problem sieciowy z Twoją recenzją.", + "Info_ReviewNetworkErrorTitle": "Problem z siecią", + "Info_ReviewNewMessage": "Wszelkie informacje zwrotne mile widziane. Dziękuję za Twoją opinię!", + "Info_ReviewSuccessTitle": "Dziękuję", + "Info_ReviewUnknownErrorMessage": "Wystąpił nieznany błąd podczas wystawiania recenzji. ({0})", + "Info_ReviewUnknownErrorTitle": "Nieznany błąd", + "Info_ReviewUpdatedMessage": "Dziękuję za aktualizację recenzji.", + "Info_SignatureDisabledMessage": "Wyłączono podpis dla tego konta", + "Info_SignatureDisabledTitle": "Zakończono pomyślnie", + "Info_SignatureSavedMessage": "Nowy podpis został zapisany", + "Info_SignatureSavedTitle": "Zakończono pomyślnie", + "Info_SyncCanceledMessage": "Anulowano", + "Info_SyncCanceledTitle": "Synchronizacja", + "Info_SyncFailedTitle": "Synchronizacja nieudana", + "Info_UnsupportedFunctionalityDescription": "Ta funkcjonalność nie jest jeszcze zaimplementowana.", + "Info_UnsupportedFunctionalityTitle": "Nieobsługiwane", + "Info_UnsubscribeLinkInvalidTitle": "Nieprawidłowy link anulowania subskrypcji", + "Info_UnsubscribeLinkInvalidMessage": "Link anulowania subskrypcji jest nieprawidłowy. Nie udało się wypisać z listy subskrybentów.", + "ImapAdvancedSetupDialog_AuthenticationMethod": "Metoda autoryzacji", + "ImapAdvancedSetupDialog_ConnectionSecurity": "Zabezpieczenia dot. połączenia", + "ImapAuthenticationMethod_Auto": "Automatyczne", + "ImapAuthenticationMethod_CramMD5": "CRAM-MD5", + "ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5", + "ImapAuthenticationMethod_None": "Brak uwierzytelnienia", + "ImapAuthenticationMethod_Plain": "Normale hasło", + "ImapAuthenticationMethod_EncryptedPassword": "Zaszyfrowane hasło", + "ImapAuthenticationMethod_Ntlm": "NTLM", + "ImapConnectionSecurity_None": "Brak", + "ImapConnectionSecurity_SslTls": "SSL/TLS", + "ImapConnectionSecurity_StartTls": "STARTTLS", + "ImapConnectionSecurity_Auto": "Automatyczne", + "Justify": "Wyjustuj", + "Left": "Wyrównaj do lewej", + "Link": "Odnośnik", + "LinkedAccountsCreatePolicyMessage": "musisz mieć co najmniej 2 konta, aby je połączyć\npołączenie zostanie usunięte przy zapisywaniu", + "LinkedAccountsTitle": "Połączone konta", + "MailOperation_AlwaysMoveFocused": "Zawsze Przenoś do \"Priorytetowe\"", + "MailOperation_AlwaysMoveOther": "Zawsze Przenoś do \"Inne\"", + "MailOperation_Archive": "Archiwizuj", + "MailOperation_ClearFlag": "Usuń flagę", + "MailOperation_DarkEditor": "Ciemny", + "MailOperation_Delete": "Usuń", + "MailOperation_ExportPDF": "Eksportuj do PDF", + "MailOperation_Find": "Znajdź", + "MailOperation_Forward": "Prześlij dalej", + "MailOperation_Ignore": "Ignoruj", + "MailOperation_LightEditor": "Jasny", + "MailOperation_MarkAsJunk": "Oznacz jako wiadomość-śmieć", + "MailOperation_MarkAsRead": "Oznacz jako przeczytane", + "MailOperation_MarkAsUnread": "Oznacz jako nieprzeczytane", + "MailOperation_MarkNotJunk": "Oznacz jako nieśmieciową wiadomość", + "MailOperation_Move": "Przenieś", + "MailOperation_MoveFocused": "Przenieś do: Priorytetowe", + "MailOperation_MoveJunk": "Przenieś do: Wiadomości-śmieci", + "MailOperation_MoveOther": "Przenieś do: Inne", + "MailOperation_Navigate": "Nawiguj", + "MailOperation_Print": "Drukuj", + "MailOperation_Reply": "Odpowiedz", + "MailOperation_ReplyAll": "Odpowiedz wszystkim", + "MailOperation_SaveAs": "Zapisz jako", + "MailOperation_SetFlag": "Ustaw flagę", + "MailOperation_Unarchive": "Odarchiwizuj", + "MailOperation_Zoom": "Powiększ", + "MailsSelected": "{0} zaznaczonych elementów", + "MarkFlagUnflag": "Oznacz jako oflagowane/nieoflagowane", + "MarkReadUnread": "Oznacz jako przeczytane/nieprzeczytane", + "MenuManageAccounts": "Zarządzaj kontami", + "MenuNewMail": "Nowa wiadomość", + "MenuMergedAccountItemAccountsSuffix": " kont", + "MenuRate": "Oceń Wino", + "MenuSettings": "Ustawienia", + "MergedAccountsAvailableAccountsTitle": "Dostępne konta", + "More": "Więcej", + "MoveMailDialog_InvalidFolderMessage": "{0} nie jest prawidłowym folderem dla tej wiadomości.", + "MoveMailDialog_Title": "Wybierz folder", + "NewAccountDialog_AccountName": "Nazwa konta", + "NewAccountDialog_AccountNameDefaultValue": "Osobiste", + "NewAccountDialog_AccountNamePlaceholder": "np. Osobista poczta", + "NewAccountDialog_Title": "Dodaj nowe konto", + "NoMailSelected": "nie wybrano żadnej wiadomości", + "NoMessageCrieteria": "żadne wiadomości nie spełniają kryteriów wyszukiwania.", + "NoMessageEmptyFolder": "ten folder jest pusty.", + "Notifications_MultipleNotificationsMessage": "Masz {0} nowych wiadomości", + "Notifications_MultipleNotificationsTitle": "Nowa poczta", + "Notifications_WinoUpdatedMessage": "Sprawdź nową wersję {0}", + "Notifications_WinoUpdatedTitle": "Wino Mail został zaktualizowany.", + "Other": "Inne", + "PaneLengthOption_Default": "Domyślne", + "PaneLengthOption_ExtraLarge": "Bardzo duża", + "PaneLengthOption_Large": "Duża", + "PaneLengthOption_Medium": "Średnia", + "PaneLengthOption_Micro": "Mikro", + "PaneLengthOption_Small": "Mała", + "Photos": "Zdjęcia", + "PreparingFoldersMessage": "Przygotowywanie folderów", + "ProviderDetail_Gmail_Description": "Konto Google", + "ProviderDetail_IMAP_Description": "Serwer IMAP/SMTP", + "ProviderDetail_IMAP_Title": "Serwer IMAP", + "Results": "Wyniki", + "Right": "Wyrównaj do prawej", + "SynchronizationFolderReport_Success": "aktualne", + "SynchronizationFolderReport_Failed": "synchronizacja nieudana", + "SearchBarPlaceholder": "wyszukaj", + "SearchingIn": "szukanie w", + "SettingsAboutGithub_Description": "Przejdź do repozytorium trackera zgłoszeń GitHub.", + "SettingsAboutGithub_Title": "GitHub", + "SettingsAccountManagementAppendMessage_Title": "Dołącz wiadomości do folderu Wysłane", + "SettingsAccountManagementAppendMessage_Description": "Utwórz kopię wiadomości w folderze Wysłane po wysłaniu wersji roboczej. Włącz to, jeśli nie widzisz swoich wiadomości po wysłaniu ich, w folderze Wysłane.", + "SettingsEditLinkedInbox_Title": "Edytuj połączoną skrzynkę odbiorczą", + "SettingsEditLinkedInbox_Description": "Dodaj / usuń konta, zmień nazwę lub zerwij powiązanie między kontami.", + "SettingsAboutVersion": "Wersja ", + "SettingsAboutWinoDescription": "Lekki klient poczty dla rodzin urządzeń Windows.", + "SettingsAbout_Description": "Dowiedz się więcej o Wino.", + "SettingsAbout_Title": "O aplikacji", + "SettingsAccentColor_Description": "Zmień kolor akcentu aplikacji", + "SettingsAccentColor_Title": "Kolor akcentu", + "SettingsAccentColor_UseWindowsAccentColor": "Użyj mojego koloru akcentu Windows", + "SettingsAccountName_Description": "Zmień nazwę konta.", + "SettingsAccountName_Title": "Nazwa konta", + "SettingsApplicationTheme_Description": "Spersonalizuj Wino z innymi niestandardowymi motywami aplikacji.", + "SettingsApplicationTheme_Title": "Motyw aplikacji", + "SettingsAvailableThemes_Description": "Wybierz motyw z kolekcji Wino wedle uznania lub zastosuj własne motywy.", + "SettingsAvailableThemes_Title": "Dostępne motywy", + "SettingsCustomTheme_Description": "Utwórz własny motyw z niestandardową tapetą i kolorem akcentu.", + "SettingsCustomTheme_Title": "Własny motyw", + "SettingsDeleteAccount_Description": "Usuń wszystkie e-maile i dane uwierzytelniające powiązane z tym kontem.", + "SettingsDeleteAccount_Title": "Usuń to konto", + "SettingsDeleteProtection_Description": "Czy Wino powinno poprosić Cię o potwierdzenie za każdym razem, gdy próbujesz trwale usunąć wiadomość za pomocą kombinacji Shift + Del?", + "SettingsDeleteProtection_Title": "Ochrona przed trwałym usunięciem", + "SettingsDiagnostics_Description": "Dla programistów", + "SettingsDiagnostics_Title": "Diagnostyka", + "SettingsDiscord_Description": "Otrzymuj regularne informacje dotyczące rozwoju aplikacji, dołącz do dyskusji o roadmapie i przedstawiaj opinie.", + "SettingsDiscord_Title": "Kanał Discord", + "SettingsElementThemeSelectionDisabled": "Wybór motywu elementu jest wyłączony, gdy wybrany motyw aplikacji jest inny niż Domyślny.", + "SettingsElementTheme_Description": "Wybierz motyw Windows dla Wino", + "SettingsElementTheme_Title": "Motyw elementu", + "SettingsEnableHoverActions_Title": "Włącz akcje po najechaniu kursorem", + "SettingsEnableIMAPLogs_Description": "Włącz tę opcję, aby dostarczyć szczegółów dotyczących problemów z połączeniem IMAP podczas konfiguracji serwera IMAP.", + "SettingsEnableIMAPLogs_Title": "Włącz logi protokołu IMAP", + "SettingsEnableLogs_Description": "Mogę potrzebować logów, kiedy aplikacja ulegnie awarii, by móc zdiagnozować problemy, które zgłosiłeś na GitHubie. Żaden z logów nie ujawni Twoich danych do logowania czy wrażliwych danych.", + "SettingsEnableLogs_Title": "Włącz logi", + "SettingsEnableSignature": "Włącz podpis", + "SettingsExpandOnStartup_Description": "Ustaw czy Wino powinno ukazywać listę folderów tego konta przy uruchomieniu.", + "SettingsExpandOnStartup_Title": "Rozwiń listę folderów przy uruchomieniu", + "SettingsExternalContent_Description": "Zarządzaj zewnętrznymi ustawieniami zawartości podczas wyświetlania wiadomości.", + "SettingsExternalContent_Title": "Zawartość zewnętrzna", + "SettingsFocusedInbox_Description": "Określ, czy skrzynka odbiorcza powinna być podzielona na dwie jako Priorytetowe - Inne.", + "SettingsFocusedInbox_Title": "Priorytetowa skrzynka odbiorcza", + "SettingsFolderSync_Description": "Włącz lub wyłącz synchronizacji określonych folderów.", + "SettingsFolderSync_Title": "Synchronizacja folderów", + "SettingsFolderOptions_Title": "Folder Configuration", + "SettingsFolderOptions_Description": "Zmień ustawienia poszczególnych folderów, takie jak wyłączenie lub włączenie synchronizacji lub pokazywanie i ukrywanie ikony nieprzeczytanych wiadomości.", + "SettingsHoverActionCenter": "Akcja ze środka", + "SettingsHoverActionLeft": "Akcja z lewej", + "SettingsHoverActionRight": "Akcja z prawej", + "SettingsHoverActions_Description": "Wybierz 3 akcje, które pojawią się, kiedy najedziesz kursorem na wiadomość.", + "SettingsHoverActions_Title": "Akcje po najechaniu kursorem", + "SettingsLanguage_Description": "Zmień język wyświetlania Wino.", + "SettingsLanguage_Title": "Ustawienia języka", + "CategoriesFolderNameOverride": "Kategorie", + "MoreFolderNameOverride": "Więcej", + "SettingsOptions_Title": "Ustawienia", + "SettingsLinkAccounts_Description": "Scal wiele kont w jedno. Przeglądaj wiadomości z jednej skrzynki odbiorczej razem.", + "SettingsLinkAccounts_Title": "Stwórz połączone konta", + "SettingsLinkedAccountsSave_Description": "Modyfikuj bieżące połączenie z nowymi kontami.", + "SettingsLinkedAccountsSave_Title": "Zapisz zmiany", + "SettingsLoadImages_Title": "Automatycznie wczytuj obrazy", + "SettingsLoadStyles_Title": "Automatycznie wczytuj style", + "SettingsMailSpacing_Description": "Dostosuj odstępy dla wiadomości na liście.", + "SettingsMailSpacing_Title": "Odstępy między wiadomościami", + "SettingsFolderMenuStyle_Title": "Utwórz zagnieżdżone foldery", + "SettingsFolderMenuStyle_Description": "Zmień czy foldery konta powinny być zagnieżdżone wewnątrz pozycji menu konta. Wyłącz, jeśli podoba Ci się stary system menu w Windows Mail", + "SettingsManageAccountSettings_Description": "Powiadomienia, podpisy, synchronizacja i inne ustawienia dla każdego konta osobno.", + "SettingsManageAccountSettings_Title": "Zarządzaj ustawieniami konta", + "SettingsManageLink_Description": "Przenieś elementy, aby dodać nowe połączenie lub usunąć istniejące połączenie.", + "SettingsManageLink_Title": "Zarządzaj połączeniem kont", + "SettingsMarkAsRead_Description": "Zmień, co powinno się stać z zaznaczonym elementem.", + "SettingsMarkAsRead_DontChange": "Nie oznaczaj automatycznie elementu jako przeczytany", + "SettingsMarkAsRead_SecondsToWait": "Sekundy do odczekania: ", + "SettingsMarkAsRead_Timer": "Po wyświetleniu w panelu czytania", + "SettingsMarkAsRead_Title": "Oznacz jako przeczytane", + "SettingsMarkAsRead_WhenSelected": "Gdy zaznaczone", + "SettingsMessageList_Description": "Zmień sposób organizacji wiadomości na liście.", + "SettingsMessageList_Title": "Lista wiadomości", + "SettingsNoAccountSetupMessage": "Nie skonfigurowano jeszcze żadnych kont.", + "SettingsNotifications_Description": "Włącz lub wyłącz powiadomienia dla tego konta.", + "SettingsNotifications_Title": "Powiadomienia", + "SettingsPaneLength_Description": "Zmień szerokość listy wiadomości.", + "SettingsPaneLength_Title": "Długość panelu listy wiadomości", + "SettingsPaypal_Description": "Okaż dużo więcej miłości ❤️ Za wszystkie darowizny dziękuję.", + "SettingsPaypal_Title": "Wspomóż przez PayPal", + "SettingsPersonalizationMailDisplayCompactMode": "Tryb kompaktowy", + "SettingsPersonalizationMailDisplayMediumMode": "Tryb umiarkowany", + "SettingsPersonalizationMailDisplaySpaciousMode": "Tryb przestronny", + "SettingsPersonalization_Description": "Zmień wygląd Wino tak, jak chcesz.", + "SettingsPersonalization_Title": "Personalizacja", + "SettingsPrivacyPolicy_Description": "Zapoznaj się z Polityką prywatności.", + "SettingsPrivacyPolicy_Title": "Politykę prywatności", + "SettingsReadingPane_Description": "Opcje wyświetlania wiadomości.", + "SettingsReadingPane_Title": "Panel czytania wiadomości", + "SettingsReaderFont_Title": "Domyślna czcionka przy czytaniu wiadomości", + "SettingsReaderFontFamily_Description": "Zmień domyślny rozmiar czcionki i czcionki do odczytu wiadomości.", + "SettingsFontFamily_Title": "Czcionka", + "SettingsFontSize_Title": "Rozmiar czcionki", + "SettingsFontPreview_Title": "Podgląd", + "SettingsComposerFont_Title": "Domyślna czcionka przy pisaniu wiadomości", + "SettingsComposerFontFamily_Description": "Zmień domyślny rozmiar czcionki i czcionki dla pisanej wiadomości.", + "SettingsRenameMergeAccount_Description": "Zmień wyświetlaną nazwę połączonych kont.", + "SettingsRenameMergeAccount_Title": "Zmień nazwę", + "SettingsSemanticZoom_Description": "To pozwoli Ci kliknąć w nagłówki na liście wiadomości i przejść do określonej daty", + "SettingsSemanticZoom_Title": "Semantyczne powiększenie dla nagłówków dat", + "SettingsShowPreviewText_Description": "Ukryj/pokaż tekst podglądu.", + "SettingsShowPreviewText_Title": "Pokaż podgląd tekstu", + "SettingsShowSenderPictures_Description": "Ukryj/pokaż miniaturki zdjęć nadawcy.", + "SettingsShowSenderPictures_Title": "Pokaż awatary nadawcy", + "SettingsPrefer24HourClock_Title": "Wyświetl czas w formacie 24-godzinnym", + "SettingsPrefer24HourClock_Description": "Czas odbioru wiadomości będzie wyświetlany w formacie 24-godzinnym zamiast 12-godzinnym (AM/PM)", + "SettingsSignature_Description": "Edytuj lub usuń podpis konta", + "SettingsSignature_Title": "Podpis", + "SettingsStartupItem_Description": "Primary account item to load Inbox at startup.", + "SettingsStartupItem_Title": "Startup Item", + "SettingsStore_Description": "Okaż trochę miłości ❤️", + "SettingsStore_Title": "Oceń w sklepie", + "SettingsThreads_Description": "Organizuj wiadomości w wątki konwersacji.", + "SettingsThreads_Title": "Wątkowanie konwersacji", + "SettingsUnlinkAccounts_Description": "Usuń połączenie między kontami. Nie spowoduje to usunięcia Twoich kont.", + "SettingsUnlinkAccounts_Title": "Odłącz konto", + "SortingOption_Date": "według daty", + "SortingOption_Name": "według nazwy", + "StoreRatingDialog_MessageFirstLine": "Wszystkie opinie są mile widziane i znacznie poprawią Wino w przyszłości. Czy chcesz ocenić Wino w Microsoft Store?", + "StoreRatingDialog_MessageSecondLine": "Czy chcesz ocenić Wino Mail w Microsoft Store?", + "StoreRatingDialog_Title": "Podoba Ci się Wino?", + "SystemFolderConfigDialog_ArchiveFolderDescription": "Archived messages will be moved to here.", + "SystemFolderConfigDialog_ArchiveFolderHeader": "Archive Folder", + "SystemFolderConfigDialog_DeletedFolderDescription": "Usunięte wiadomości zostaną przeniesione tutaj.", + "SystemFolderConfigDialog_DeletedFolderHeader": "Usunięty folder", + "SystemFolderConfigDialog_DraftFolderDescription": "Nowe wiadomości/odpowiedzi zostaną tutaj stworzone.", + "SystemFolderConfigDialog_DraftFolderHeader": "Folder wersji roboczych", + "SystemFolderConfigDialog_JunkFolderDescription": "Wszelki spam/wiadomości-śmieci będą tutaj.", + "SystemFolderConfigDialog_JunkFolderHeader": "Folder wiadomości-śmieci/spamu", + "SystemFolderConfigDialog_MessageFirstLine": "Ten serwer IMAP nie obsługuje rozszerzenia SPECIAL-USE, dlatego Wino nie mogło prawidłowo skonfigurować folderów systemowych.", + "SystemFolderConfigDialog_MessageSecondLine": "Proszę wybrać odpowiednie foldery dla określonych funkcji.", + "SystemFolderConfigDialog_SentFolderDescription": "Folder, który wysłał wiadomości zostanie zapisany.", + "SystemFolderConfigDialog_SentFolderHeader": "Folder „Wysłane”", + "SystemFolderConfigDialog_Title": "Konfiguruj foldery systemowe", + "TestingImapConnectionMessage": "Testowanie połączenia z serwerem...", + "Today": "Dzisiaj", + "UnknownAddress": "nieznany adres", + "UnknownDateHeader": "Nieznana data", + "UnknownGroupAddress": "nieznana grupa adresów", + "UnknownSender": "Nieznany nadawca", + "Unsubscribe": "Anuluj subskrypcję", + "ViewContactDetails": "Wyświetl szczegóły", + "WinoUpgradeDescription": "Wino offers 3 accounts to start with for free. If you need more than 3 accounts, please upgrade", + "WinoUpgradeMessage": "Ulepsz do nielimitowanych kont", + "WinoUpgradeRemainingAccountsMessage": "Wykorzystano {0} z {1} darmowych kont.", + "Yesterday": "Wczoraj" +} diff --git a/Wino.Core.Domain/Translations/ru_RU/resources.json b/Wino.Core.Domain/Translations/ru_RU/resources.json new file mode 100644 index 00000000..f7ff0909 --- /dev/null +++ b/Wino.Core.Domain/Translations/ru_RU/resources.json @@ -0,0 +1,468 @@ +{ + "AccountCreationDialog_Completed": "все готово", + "AccountCreationDialog_Initializing": "инициализация", + "AccountCreationDialog_PreparingFolders": "На данный момент мы получаем информацию о папках.", + "AccountCreationDialog_SigninIn": "Данные учетной записи сохраняются.", + "AccountEditDialog_Message": "Имя пользователя", + "AccountEditDialog_Title": "Редактировать учетную запись", + "AccountPickerDialog_Title": "Выберите учетную запись", + "AddHyperlink": "Добавить", + "AutoDiscoveryProgressMessage": "Поиск настроек почты...", + "BasicIMAPSetupDialog_AdvancedConfiguration": "Расширенная конфигурация", + "BasicIMAPSetupDialog_CredentialLocalMessage": "Ваши учетные данные будут храниться только локально на вашем компьютере.", + "BasicIMAPSetupDialog_Description": "Некоторые учетные записи требуют дополнительных шагов для входа", + "BasicIMAPSetupDialog_DisplayName": "Отображаемое имя", + "BasicIMAPSetupDialog_DisplayNamePlaceholder": "напр. Иван Иванов", + "BasicIMAPSetupDialog_LearnMore": "Подробнее", + "BasicIMAPSetupDialog_MailAddress": "Адрес электронной почты", + "BasicIMAPSetupDialog_MailAddressPlaceholder": "ivanivanov@fabrikam.com", + "BasicIMAPSetupDialog_Password": "Пароль", + "BasicIMAPSetupDialog_Title": "Учетная запись IMAP", + "Buttons_AddAccount": "Добавить учетную запись", + "Buttons_ApplyTheme": "Применить тему", + "Buttons_Browse": "Обзор", + "Buttons_Cancel": "Отмена", + "Buttons_Close": "Закрыть", + "Buttons_Create": "Создать", + "Buttons_CreateAccount": "Создать учетную запись", + "Buttons_Delete": "Удалить", + "Buttons_Discard": "Отклонить", + "Buttons_EnableImageRendering": "Включить", + "Buttons_No": "Нет", + "Buttons_Open": "Открыть", + "Buttons_Purchase": "Купить", + "Buttons_RateWino": "Оцените Wino", + "Buttons_Save": "Сохранить", + "Buttons_SaveConfiguration": "Сохранить конфигурацию", + "Buttons_Share": "Поделиться", + "Buttons_SignIn": "Войти", + "Buttons_Yes": "Да", + "Center": "Центр", + "ComingSoon": "Скоро...", + "ComposerFrom": "От: ", + "ComposerSubject": "Тема: ", + "ComposerTo": "Кому: ", + "ClipboardTextCopied_Message": "{0} copied to clipboard.", + "ClipboardTextCopied_Title": "Copied", + "ClipboardTextCopyFailed_Message": "Failed to copy {0} to clipboard.", + "ComposerToPlaceholder": "нажмите Enter для ввода адресов", + "CustomThemeBuilder_AccentColorDescription": "Установите пользовательский контрастный цвет, если хотите. Если цвет не выбран, будет использоваться контрастный цвет Windows.", + "CustomThemeBuilder_AccentColorTitle": "Контрастный цвет", + "CustomThemeBuilder_PickColor": "Выбрать", + "CustomThemeBuilder_ThemeNameDescription": "Уникальное имя для пользовательской темы.", + "CustomThemeBuilder_ThemeNameTitle": "Название темы", + "CustomThemeBuilder_Title": "Создатель пользовательских тем", + "CustomThemeBuilder_WallpaperDescription": "Установить пользовательские обои для Wino", + "CustomThemeBuilder_WallpaperTitle": "Установить пользовательские обои", + "DialogMessage_AccountLimitMessage": "Вы достигли предела создания учетной записи.\nХотите приобрести дополнение 'Неограниченная учетная запись', чтобы продолжить?", + "DialogMessage_AccountLimitTitle": "Достигнут лимит учетных записей", + "DialogMessage_CleanupFolderMessage": "Вы хотите навсегда удалить все письма в этой папке?", + "DialogMessage_CleanupFolderTitle": "Очистить папку", + "DialogMessage_ComposerMissingRecipientMessage": "Сообщение не имеет получателя.", + "DialogMessage_ComposerValidationFailedTitle": "Проверка не удалась", + "DialogMessage_CreateLinkedAccountMessage": "Дайте этой связке имя. Учетные записи будут объединены под этим именем.", + "DialogMessage_CreateLinkedAccountTitle": "Название связки учетных записей", + "DialogMessage_DeleteAccountConfirmationMessage": "Удалить {0}?", + "DialogMessage_DeleteAccountConfirmationTitle": "Все данные, связанные с этой учетной записью, будут окончательно удалены с диска.", + "DialogMessage_DiscardDraftConfirmationMessage": "Этот черновик будет удален. Вы хотите продолжить?", + "DialogMessage_DiscardDraftConfirmationTitle": "Удалить черновик", + "DialogMessage_HardDeleteConfirmationMessage": "Удалить безвозвратно", + "DialogMessage_HardDeleteConfirmationTitle": "Сообщение(я) будут удалены навсегда. Продолжить?", + "DialogMessage_NoAccountsForCreateMailMessage": "У вас нет учетных записей для создания сообщений.", + "DialogMessage_NoAccountsForCreateMailTitle": "Учетная запись отсутствует", + "DialogMessage_RenameLinkedAccountsMessage": "Введите новое имя для связанных учетных записей", + "DialogMessage_RenameLinkedAccountsTitle": "Переименовать имя связанных учетных записей", + "DialogMessage_UnlinkAccountsConfirmationMessage": "Эта операция не удалит ваши учетные записи, но только нарушает ссылку на соединение с общими папками. Вы хотите продолжить?", + "DialogMessage_UnlinkAccountsConfirmationTitle": "Отвязать учетные записи", + "DialogMessage_EmptySubjectConfirmation": "Не указана тема письма", + "DialogMessage_EmptySubjectConfirmationMessage": "Сообщение не имеет темы. Вы хотите продолжить?", + "Dialog_DontAskAgain": "Больше не спрашивать", + "DiscordChannelDisclaimerMessage": "У Wino нет собственного сервера Discord, но специальный канал 'wino-mail' размещен на сервере 'Developer Sanctuary'.\nЧтобы получать обновления о Wino, пожалуйста, присоединитесь к серверу Developer Sanctuary и следите за каналом 'wino-mail' в разделе 'Community Projects'.\n\nВы будете направлены на URL сервера, так как Discord не поддерживает приглашения на канал.", + "DiscordChannelDisclaimerTitle": "Важная информация о Discord", + "Draft": "Черновик", + "EditorToolbarOption_Draw": "Рисование", + "EditorToolbarOption_Format": "Форматирование", + "EditorToolbarOption_Insert": "Вставить", + "EditorToolbarOption_None": "Нет", + "EditorToolbarOption_Options": "Настройки", + "ElementTheme_Dark": "Темная тема", + "ElementTheme_Default": "Использовать системные настройки", + "ElementTheme_Light": "Светлая тема", + "Emoji": "Эмодзи", + "Exception_ImapClientPoolFailed": "Сбой пула клиентов IMAP.", + "Exception_AuthenticationCanceled": "Аутентификация отменена", + "Exception_CustomThemeExists": "Такая тема уже существует.", + "Exception_CustomThemeMissingName": "Необходимо указать название.", + "Exception_CustomThemeMissingWallpaper": "Необходимо предоставить пользовательское фоновое изображение.", + "Exception_FailedToSynchronizeFolders": "Не удалось синхронизировать папки", + "Exception_GoogleAuthCallbackNull": "Callback uri пустой при активации.", + "Exception_GoogleAuthCorruptedCode": "Поврежденный ответ авторизации.", + "Exception_GoogleAuthError": "Ошибка авторизации OAuth: {0}", + "Exception_GoogleAuthInvalidResponse": "Получен запрос с недопустимым состоянием ({0})", + "Exception_GoogleAuthorizationCodeExchangeFailed": "Не удалось выполнить обмен кодом авторизации.", + "Exception_InvalidSystemFolderConfiguration": "Конфигурация системных папок неверна. Проверьте конфигурацию и повторите попытку.", + "Exception_NullAssignedAccount": "Назначенная учетная запись пустая", + "Exception_NullAssignedFolder": "Назначенная папка пустая", + "Exception_SynchronizerFailureHTTP": "Не удалось обработать ответ с ошибкой HTTP код {0}", + "Exception_TokenGenerationFailed": "Ошибка генерации токена", + "Exception_TokenInfoRetrivalFailed": "Не удалось получить информацию о токене.", + "Exception_UnknowErrorDuringAuthentication": "Во время аутентификации произошла неизвестная ошибка", + "Exception_UnsupportedAction": "Действие {0} не реализовано в процессоре запросов", + "Exception_UnsupportedProvider": "Этот провайдер не поддерживается.", + "Exception_UnsupportedSynchronizerOperation": "Эта операция не поддерживается для {0}", + "Exception_UserCancelSystemFolderSetupDialog": "Пользователь отменил диалог конфигурации системной папки.", + "Files": "Файлы", + "FilteringOption_All": "Все", + "FilteringOption_Flagged": "Помеченные", + "FilteringOption_Unread": "Непрочитанные", + "Focused": "Фокусировка", + "FolderOperation_CreateSubFolder": "Создать подпапку", + "FolderOperation_Delete": "Удалить", + "FolderOperation_DontSync": "Не синхронизировать эту папку", + "FolderOperation_Empty": "Очистить папку", + "FolderOperation_MarkAllAsRead": "Пометить все как прочитанные", + "FolderOperation_Move": "Переместить", + "DragMoveToFolderCaption": "Переместить в {0}", + "FolderOperation_None": "Нет", + "FolderOperation_Pin": "Закрепить", + "FolderOperation_Rename": "Переименовать", + "FolderOperation_Unpin": "Открепить", + "HoverActionOption_Archive": "Архивировать", + "HoverActionOption_Delete": "Удалить", + "HoverActionOption_MoveJunk": "Переместить в мусор", + "HoverActionOption_ToggleFlag": "Флаг / Убрать флаг", + "HoverActionOption_ToggleRead": "Прочитанное / Непрочитанное", + "MergedAccountCommonFolderInbox": "Входящие", + "MergedAccountCommonFolderSent": "Отправленные", + "MergedAccountCommonFolderDraft": "Черновики", + "MergedAccountCommonFolderJunk": "Мусор", + "MergedAccountCommonFolderTrash": "Удалено", + "MergedAccountCommonFolderArchive": "Архив", + "IMAPSetupDialog_AccountType": "Тип учетной записи", + "IMAPSetupDialog_DisplayName": "Отображаемое имя", + "IMAPSetupDialog_DisplayNamePlaceholder": "напр. Иван Иванов", + "IMAPSetupDialog_IncomingMailServer": "Сервер входящей почты", + "IMAPSetupDialog_IncomingMailServerPort": "Порт", + "IMAPSetupDialog_MailAddress": "Адрес электронной почты", + "IMAPSetupDialog_MailAddressPlaceholder": "кто-то@example.com", + "IMAPSetupDialog_OutgoingMailServer": "Сервер исходящей почты (SMTP)", + "IMAPSetupDialog_OutgoingMailServerPassword": "Пароль сервера исходящей почты", + "IMAPSetupDialog_OutgoingMailServerPort": "Порт", + "IMAPSetupDialog_OutgoingMailServerRequireAuthentication": "Сервер исходящей почты требует аутентификации", + "IMAPSetupDialog_OutgoingMailServerUsername": "Имя пользователя исходящей сервера", + "IMAPSetupDialog_Password": "Пароль", + "IMAPSetupDialog_RequireSSLForIncomingMail": "Требовать SSL для входящего сообщения", + "IMAPSetupDialog_RequireSSLForOutgoingMail": "Требовать SSL для исходящей почты", + "IMAPSetupDialog_Title": "Расширенная конфигурация IMAP", + "IMAPSetupDialog_UseSameConfig": "Используйте то же имя пользователя и пароль для отправки электронной почты", + "IMAPSetupDialog_Username": "Имя пользователя", + "IMAPSetupDialog_UsernamePlaceholder": "иваниванов, ivanivanov@fabrikam.com, домен/иваниванов", + "ImageRenderingDisabled": "Отображение изображений отключено для этого сообщения.", + "InfoBarAction_Enable": "Включено", + "InfoBarMessage_SynchronizationDisabledFolder": "Эта папка отключена для синхронизации.", + "InfoBarTitle_SynchronizationDisabledFolder": "Папка отключена", + "GeneralTitle_Error": "Error", + "GeneralTitle_Warning": "Warning", + "GeneralTitle_Info": "Information", + "Info_AccountCreatedMessage": "{0} создано", + "Info_AccountCreatedTitle": "Создание учетной записи", + "Info_AccountCreationFailedTitle": "Не удалось создать учетную запись", + "Info_AccountDeletedMessage": "{0} успешно удалён.", + "Info_AccountDeletedTitle": "Учетная запись удалена", + "Info_AccountIssueFixFailedTitle": "Ошибка", + "Info_AccountIssueFixSuccessMessage": "Исправлены все проблемы с учетной записью.", + "Info_AccountIssueFixSuccessTitle": "Успешно", + "Info_AttachmentOpenFailedMessage": "Не удается открыть это вложение.", + "Info_AttachmentOpenFailedTitle": "Ошибка", + "Info_AttachmentSaveFailedMessage": "Не удается сохранить это вложение.", + "Info_AttachmentSaveFailedTitle": "Ошибка", + "Info_AttachmentSaveSuccessMessage": "Вложение сохранено.", + "Info_AttachmentSaveSuccessTitle": "Вложение сохранено", + "Info_BackgroundExecutionDeniedMessage": "Отказано в выполнении фонового режима. Это может повлиять на фоновую синхронизацию и прямые уведомления.", + "Info_BackgroundExecutionDeniedTitle": "Отказано в выполнении фонового режима", + "Info_BackgroundExecutionUnknownErrorMessage": "Произошла неизвестная ошибка при регистрации фоновой синхронизации.", + "Info_BackgroundExecutionUnknownErrorTitle": "Ошибка исполнения фона", + "Info_ComposerMissingMIMEMessage": "Не удалось найти MIME-файл. Синхронизация может помочь.", + "Info_ComposerMissingMIMETitle": "Ошибка", + "Info_ContactExistsMessage": "Этот контакт уже есть в списке получателей.", + "Info_ContactExistsTitle": "Контакт уже существует", + "Info_DraftFolderMissingMessage": "У этой учетной записи отсутствует папка \"Черновики\". Проверьте настройки учетной записи.", + "Info_DraftFolderMissingTitle": "Отсутствует папка \"Черновики\"", + "Info_FileLaunchFailedTitle": "Не удалось запустить файл", + "Info_InvalidAddressMessage": "'{0}' не является допустимым адресом электронной почты.", + "Info_InvalidAddressTitle": "Недопустимый адрес", + "Info_InvalidMoveTargetMessage": "Вы не можете переместить выбранные письма в эту папку.", + "Info_InvalidMoveTargetTitle": "Недопустимая цель перемещения", + "Info_LogsNotFoundMessage": "Нет журналов для отправки.", + "Info_LogsNotFoundTitle": "Журналы не найдены", + "Info_LogsSavedMessage": "{0} сохранено в выбранную папку.", + "Info_LogsSavedTitle": "Сохранено", + "Info_MailRenderingFailedMessage": "Это письмо повреждено или не может быть открыто.\n{0}", + "Info_MailRenderingFailedTitle": "Ошибка отображения", + "Info_MessageCorruptedMessage": "Это сообщение повреждено.", + "Info_MessageCorruptedTitle": "Ошибка", + "Info_MissingFolderMessage": "{0} не существует для этой учетной записи.", + "Info_MissingFolderTitle": "Отсутствует папка", + "Info_PurchaseExistsMessage": "Похоже, этот товар уже был куплен ранее.", + "Info_PurchaseExistsTitle": "Существующий товар", + "Info_PurchaseThankYouMessage": "Спасибо!", + "Info_PurchaseThankYouTitle": "Покупка прошла успешно", + "Info_RequestCreationFailedTitle": "Не удалось создать запросы", + "Info_ReviewNetworkErrorMessage": "При отправке вашего отзыва возникли проблемы с сетью.", + "Info_ReviewNetworkErrorTitle": "Проблема с сетью", + "Info_ReviewNewMessage": "Все замечания приветствуются. Спасибо за отзыв!", + "Info_ReviewSuccessTitle": "Спасибо!", + "Info_ReviewUnknownErrorMessage": "При отправке вашего отзыва возникла неизвестная проблема. ({0})", + "Info_ReviewUnknownErrorTitle": "Неизвестная ошибка", + "Info_ReviewUpdatedMessage": "Спасибо за обновленный отзыв.", + "Info_SignatureDisabledMessage": "Отключена подпись для этой учетной записи", + "Info_SignatureDisabledTitle": "Успешно", + "Info_SignatureSavedMessage": "Новая подпись сохранена", + "Info_SignatureSavedTitle": "Успешно", + "Info_SyncCanceledMessage": "Отменено", + "Info_SyncCanceledTitle": "Синхронизация", + "Info_SyncFailedTitle": "Ошибка синхронизации", + "Info_UnsupportedFunctionalityDescription": "Эта функция еще не реализована.", + "Info_UnsupportedFunctionalityTitle": "Не поддерживается", + "Info_UnsubscribeLinkInvalidTitle": "Неправильный адрес отписки", + "Info_UnsubscribeLinkInvalidMessage": "Ссылка недействительна. Не удалось отписаться от списка рассылки.", + "ImapAdvancedSetupDialog_AuthenticationMethod": "Метод авторизации", + "ImapAdvancedSetupDialog_ConnectionSecurity": "Безопасность соединения", + "ImapAuthenticationMethod_Auto": "Автоматически", + "ImapAuthenticationMethod_CramMD5": "CRAM-MD5", + "ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5", + "ImapAuthenticationMethod_None": "Без авторизации", + "ImapAuthenticationMethod_Plain": "Обычный пароль", + "ImapAuthenticationMethod_EncryptedPassword": "Зашифрованный пароль", + "ImapAuthenticationMethod_Ntlm": "NTLM", + "ImapConnectionSecurity_None": "Нет", + "ImapConnectionSecurity_SslTls": "SSL/TLS", + "ImapConnectionSecurity_StartTls": "STARTTLS", + "ImapConnectionSecurity_Auto": "Автоматически", + "Justify": "Выравнять", + "Left": "Слева", + "Link": "Связать", + "LinkedAccountsCreatePolicyMessage": "Для создания связки у вас должно быть не менее 2 учетных записей\nсвязка будет удалена при сохранении", + "LinkedAccountsTitle": "Связанные учетные записи", + "MailOperation_AlwaysMoveFocused": "Всегда перемещать в Фокусировку", + "MailOperation_AlwaysMoveOther": "Всегда перемещать в Другие", + "MailOperation_Archive": "Архивировать", + "MailOperation_ClearFlag": "Убрать пометку", + "MailOperation_DarkEditor": "Темная тема", + "MailOperation_Delete": "Удалить", + "MailOperation_ExportPDF": "Экспорт в PDF", + "MailOperation_Find": "Найти", + "MailOperation_Forward": "Переслать", + "MailOperation_Ignore": "Игнорировать", + "MailOperation_LightEditor": "Светлая тема", + "MailOperation_MarkAsJunk": "Пометить как мусор", + "MailOperation_MarkAsRead": "Пометить как прочитанное", + "MailOperation_MarkAsUnread": "Пометить как непрочитанное", + "MailOperation_MarkNotJunk": "Пометить как не мусор", + "MailOperation_Move": "Переместить", + "MailOperation_MoveFocused": "Переместить в Фокусировку", + "MailOperation_MoveJunk": "Переместить в мусор", + "MailOperation_MoveOther": "Переместить в Другие", + "MailOperation_Navigate": "Перейти", + "MailOperation_Print": "Печать", + "MailOperation_Reply": "Ответить", + "MailOperation_ReplyAll": "Ответить всем", + "MailOperation_SaveAs": "Сохранить как", + "MailOperation_SetFlag": "Пометить", + "MailOperation_Unarchive": "Вернуть из архива", + "MailOperation_Zoom": "Масштаб", + "MailsSelected": "Выбрано бесед: {0}", + "MarkFlagUnflag": "Пометить как помеченное/неотмеченное", + "MarkReadUnread": "Пометить как прочитанное/непрочитанное", + "MenuManageAccounts": "Учетные записи", + "MenuNewMail": "Новое сообщение", + "MenuMergedAccountItemAccountsSuffix": " учетных записей", + "MenuRate": "Оценить Wino", + "MenuSettings": "Параметры", + "MergedAccountsAvailableAccountsTitle": "Доступные учетные записи", + "More": "Подробнее", + "MoveMailDialog_InvalidFolderMessage": "{0} не является допустимой папкой для этого письма.", + "MoveMailDialog_Title": "Выбрать папку", + "NewAccountDialog_AccountName": "Имя учетной записи", + "NewAccountDialog_AccountNameDefaultValue": "Личная", + "NewAccountDialog_AccountNamePlaceholder": "например \"Личная почта\"", + "NewAccountDialog_Title": "Добавить учетную запись", + "NoMailSelected": "не выбрано сообщение", + "NoMessageCrieteria": "нет сообщений, удовлетворяющих критериям поиска.", + "NoMessageEmptyFolder": "эта папка пуста.", + "Notifications_MultipleNotificationsMessage": "У вас {0} новых писем", + "Notifications_MultipleNotificationsTitle": "Новые письма", + "Notifications_WinoUpdatedMessage": "Ознакомьтесь с новой версией {0}", + "Notifications_WinoUpdatedTitle": "Почта Wino обновлена.", + "Other": "Другое", + "PaneLengthOption_Default": "По умолчанию", + "PaneLengthOption_ExtraLarge": "Очень большая", + "PaneLengthOption_Large": "Большая", + "PaneLengthOption_Medium": "Средняя", + "PaneLengthOption_Micro": "Очень маленькая", + "PaneLengthOption_Small": "Маленькая", + "Photos": "Фотографии", + "PreparingFoldersMessage": "Подготовка папок", + "ProviderDetail_Gmail_Description": "Учетная запись Google", + "ProviderDetail_IMAP_Description": "Пользовательский сервер IMAP/SMTP", + "ProviderDetail_IMAP_Title": "Сервер IMAP", + "Results": "Результаты", + "Right": "Справа", + "SynchronizationFolderReport_Success": "Обновлено", + "SynchronizationFolderReport_Failed": "Сбой синхронизации", + "SearchBarPlaceholder": "Поиск", + "SearchingIn": "Поиск в", + "SettingsAboutGithub_Description": "Перейти к трекеру задач репозитория GitHub.", + "SettingsAboutGithub_Title": "GitHub", + "SettingsAccountManagementAppendMessage_Title": "Добавлять сообщения в папку \"Отправленные\"", + "SettingsAccountManagementAppendMessage_Description": "После отправки черновика создается копия сообщения в папке \"Отправленные\". Включите эту функцию, если вы не видите свои письма в папке \"Отправленные\" после их отправки.", + "SettingsEditLinkedInbox_Title": "Редактировать связанную папку \"Входящие\"", + "SettingsEditLinkedInbox_Description": "Добавляйте/удаляйте учетные записи, переименовывайте или разрывайте связь между ними.", + "SettingsAboutVersion": "Версия ", + "SettingsAboutWinoDescription": "Легкий почтовый клиент для семейства устройств Windows.", + "SettingsAbout_Description": "Узнать больше о Wino.", + "SettingsAbout_Title": "О программе", + "SettingsAccentColor_Description": "Измените цветовую схему приложения", + "SettingsAccentColor_Title": "Цветовая схема", + "SettingsAccentColor_UseWindowsAccentColor": "Использовать цветовую схему Windows", + "SettingsAccountName_Description": "Измените имя учетной записи.", + "SettingsAccountName_Title": "Имя учетной записи", + "SettingsApplicationTheme_Description": "Персонализируйте Wino с помощью различных пользовательских тем приложения по своему вкусу.", + "SettingsApplicationTheme_Title": "Тема приложения", + "SettingsAvailableThemes_Description": "Выберите тему из коллекции Wino на свой вкус или используйте свои собственные темы.", + "SettingsAvailableThemes_Title": "Доступные темы", + "SettingsCustomTheme_Description": "Создайте свою собственную тему с помощью собственных обоев и цветовой схемы.", + "SettingsCustomTheme_Title": "Пользовательская тема", + "SettingsDeleteAccount_Description": "Удалите все письма и учетные данные, связанные с этой учетной записью.", + "SettingsDeleteAccount_Title": "Удалить эту учетную запись", + "SettingsDeleteProtection_Description": "Должен ли Wino запрашивать у вас подтверждение каждый раз, когда вы пытаетесь окончательно удалить письмо с помощью клавиш Shift + Del?", + "SettingsDeleteProtection_Title": "Защита от окончательного удаления", + "SettingsDiagnostics_Description": "Для разработчиков", + "SettingsDiagnostics_Title": "Диагностика", + "SettingsDiscord_Description": "Получайте регулярные обновления о разработке, присоединяйтесь к обсуждениям дорожной карты и делитесь отзывами.", + "SettingsDiscord_Title": "Канал Discord", + "SettingsElementThemeSelectionDisabled": "Выбор режима темы не работает, если выбрана тема приложения, отличная от темы \"По умолчанию\".", + "SettingsElementTheme_Description": "Выберите тему Windows для Wino", + "SettingsElementTheme_Title": "Режим темы", + "SettingsEnableHoverActions_Title": "Включить действия при наведении", + "SettingsEnableIMAPLogs_Description": "Включите эту опцию, чтобы получить подробную информацию о проблемах подключения IMAP, возникших во время настройки сервера IMAP.", + "SettingsEnableIMAPLogs_Title": "Включить журнал протокола IMAP", + "SettingsEnableLogs_Description": "Мне могут понадобиться журналы сбоев для диагностики задач, которые вы открыли на GitHub. Ни один из журналов не раскрывает ваши учетные данные или конфиденциальную информацию.", + "SettingsEnableLogs_Title": "Включить журналы", + "SettingsEnableSignature": "Включить подпись", + "SettingsExpandOnStartup_Description": "Укажите, следует ли Wino разворачивать папки этой учетной записи при запуске.", + "SettingsExpandOnStartup_Title": "Разворачивать меню при загрузке", + "SettingsExternalContent_Description": "Управление настройками внешнего содержимого при отображении писем.", + "SettingsExternalContent_Title": "Внешнее содержимое", + "SettingsFocusedInbox_Description": "Укажите, следует ли разделять папку \"Входящие\" на две папки \"Фокусировка\" - \"Другие\".", + "SettingsFocusedInbox_Title": "Фокусировка", + "SettingsFolderSync_Description": "Включите или отключите синхронизацию определенных папок.", + "SettingsFolderSync_Title": "Синхронизация папок", + "SettingsFolderOptions_Title": "Folder Configuration", + "SettingsFolderOptions_Description": "Change individual folder settings like enable/disable sync or show/hide unread badge.", + "SettingsHoverActionCenter": "Кнопка по центру", + "SettingsHoverActionLeft": "Левая кнопка", + "SettingsHoverActionRight": "Правая кнопка", + "SettingsHoverActions_Description": "Выберите 3 действия, которые будут показаны при наведении курсора на письма.", + "SettingsHoverActions_Title": "Действия при наведении", + "SettingsLanguage_Description": "Изменить язык интерфейса Wino.", + "SettingsLanguage_Title": "Язык интерфейса", + "CategoriesFolderNameOverride": "Категории", + "MoreFolderNameOverride": "Ещё", + "SettingsOptions_Title": "Параметры", + "SettingsLinkAccounts_Description": "Объедините несколько учетных записей в одну. Просматривайте письма из одной папки \"Входящие\".", + "SettingsLinkAccounts_Title": "Создать связанные учетные записи", + "SettingsLinkedAccountsSave_Description": "Изменить текущую привязку на новые учетные записи.", + "SettingsLinkedAccountsSave_Title": "Сохранить изменения", + "SettingsLoadImages_Title": "Автозагрузка изображений", + "SettingsLoadStyles_Title": "Автозагрузка стилей", + "SettingsMailSpacing_Description": "Настройте интервал между письмами.", + "SettingsMailSpacing_Title": "Интервал между письмами", + "SettingsFolderMenuStyle_Title": "Создать вложенные папки", + "SettingsFolderMenuStyle_Description": "Выберите, должны ли папки учетных записей находится внутри пункта меню «Учетные записи». Выключите эту опцию, если вам нравится старая система меню в Почте Windows", + "SettingsManageAccountSettings_Description": "Уведомления, подписи, синхронизация и другие настройки для каждой учетной записи.", + "SettingsManageAccountSettings_Title": "Управление настройками учетной записи", + "SettingsManageLink_Description": "Перемещайте элементы, чтобы добавить новую или удалить существующую привязку.", + "SettingsManageLink_Title": "Управление привязкой", + "SettingsMarkAsRead_Description": "Выберите, что должно произойти с выбранным элементом.", + "SettingsMarkAsRead_DontChange": "Не помечать элементы как прочитанные автоматически", + "SettingsMarkAsRead_SecondsToWait": "Секунд осталось: ", + "SettingsMarkAsRead_Timer": "При просмотре в панели чтения", + "SettingsMarkAsRead_Title": "Пометить как прочитанное", + "SettingsMarkAsRead_WhenSelected": "При выборе", + "SettingsMessageList_Description": "Измените способ организации сообщений в почтовом списке.", + "SettingsMessageList_Title": "Список сообщений", + "SettingsNoAccountSetupMessage": "Вы еще не создали ни одной учетной записи.", + "SettingsNotifications_Description": "Включите или отключите уведомления для этой учетной записи.", + "SettingsNotifications_Title": "Уведомления", + "SettingsPaneLength_Description": "Измените ширину почтового списка.", + "SettingsPaneLength_Title": "Длина панели почтового списка", + "SettingsPaypal_Description": "Проявите больше любви ❤️ Все пожертвования высоко ценятся.", + "SettingsPaypal_Title": "Пожертвовать через PayPal", + "SettingsPersonalizationMailDisplayCompactMode": "Компактный режим", + "SettingsPersonalizationMailDisplayMediumMode": "Средний режим", + "SettingsPersonalizationMailDisplaySpaciousMode": "Просторный режим", + "SettingsPersonalization_Description": "Измените внешний вид Wino по своему вкусу.", + "SettingsPersonalization_Title": "Персонализация", + "SettingsPrivacyPolicy_Description": "Ознакомьтесь с политикой конфиденциальности.", + "SettingsPrivacyPolicy_Title": "Политика конфиденциальности", + "SettingsReadingPane_Description": "Параметры отображения почты.", + "SettingsReadingPane_Title": "Панель чтения", + "SettingsReaderFont_Title": "Шрифт по умолчанию", + "SettingsReaderFontFamily_Description": "Измените семейство и размер шрифта по умолчанию для чтения писем.", + "SettingsFontFamily_Title": "Семейство шрифтов", + "SettingsFontSize_Title": "Размер шрифта", + "SettingsFontPreview_Title": "Предпросмотр", + "SettingsComposerFont_Title": "Шрифт редактора по умолчанию", + "SettingsComposerFontFamily_Description": "Измените семейство и размер шрифта по умолчанию при написании писем.", + "SettingsRenameMergeAccount_Description": "Измените отображаемое имя связанных учетных записей.", + "SettingsRenameMergeAccount_Title": "Переименовать", + "SettingsSemanticZoom_Description": "Это позволит вам нажимать на заголовки в списке сообщений и переходить к определенной дате", + "SettingsSemanticZoom_Title": "Семантическое масштабирование заголовков даты", + "SettingsShowPreviewText_Description": "Скрыть/показать текст предпросмотра.", + "SettingsShowPreviewText_Title": "Показать текст предпросмотра", + "SettingsShowSenderPictures_Description": "Скрыть/показать миниатюру изображения отправителя.", + "SettingsShowSenderPictures_Title": "Показывать аватары отправителя", + "SettingsPrefer24HourClock_Title": "24-часовой формат отображения времени", + "SettingsPrefer24HourClock_Description": "Время получения почты будет отображаться в 24-часовом формате вместо 12 (AM/PM)", + "SettingsSignature_Description": "Редактировать или удалить подпись учетной записи", + "SettingsSignature_Title": "Подпись", + "SettingsStartupItem_Description": "Основной элемент учетной записи для загрузки папки \"Входящие\" при запуске.", + "SettingsStartupItem_Title": "Элемент при запуске", + "SettingsStore_Description": "Проявите любовь ❤️", + "SettingsStore_Title": "Оценить в магазине приложений", + "SettingsThreads_Description": "Организуйте сообщения в беседы.", + "SettingsThreads_Title": "Беседы", + "SettingsUnlinkAccounts_Description": "Удалить связь между учетными записями. Это не приведет к удалению учетных записей.", + "SettingsUnlinkAccounts_Title": "Отвязать учетные записи", + "SortingOption_Date": "по дате", + "SortingOption_Name": "по имени", + "StoreRatingDialog_MessageFirstLine": "Все отзывы приветствуются, и они помогут сделать Wino лучше в будущем. Вы хотите оценить Wino в Microsoft Store?", + "StoreRatingDialog_MessageSecondLine": "Вы хотите оценить Почту Wino в Microsoft Store?", + "StoreRatingDialog_Title": "Вам нравится Wino?", + "SystemFolderConfigDialog_ArchiveFolderDescription": "Archived messages will be moved to here.", + "SystemFolderConfigDialog_ArchiveFolderHeader": "Archive Folder", + "SystemFolderConfigDialog_DeletedFolderDescription": "Удаленные сообщения будут перемещены сюда.", + "SystemFolderConfigDialog_DeletedFolderHeader": "Папка \"Удаленные\"", + "SystemFolderConfigDialog_DraftFolderDescription": "Здесь будут создаваться новые письма/ответы.", + "SystemFolderConfigDialog_DraftFolderHeader": "Папка \"Черновики\"", + "SystemFolderConfigDialog_JunkFolderDescription": "Все спам/нежелательные письма будут здесь.", + "SystemFolderConfigDialog_JunkFolderHeader": "Папка \"Спам\"", + "SystemFolderConfigDialog_MessageFirstLine": "Этот IMAP-сервер не поддерживает расширение SPECIAL-USE, поэтому Wino не смог правильно настроить системные папки.", + "SystemFolderConfigDialog_MessageSecondLine": "Пожалуйста, выберите соответствующие папки для определенных функций.", + "SystemFolderConfigDialog_SentFolderDescription": "Папка, в которой будут храниться отправленные сообщения.", + "SystemFolderConfigDialog_SentFolderHeader": "Папка \"Отправленные\"", + "SystemFolderConfigDialog_Title": "Настройка системных папок", + "TestingImapConnectionMessage": "Проверка соединения с сервером...", + "Today": "Сегодня", + "UnknownAddress": "Неизвестный адрес", + "UnknownDateHeader": "Неизвестная дата", + "UnknownGroupAddress": "Неизвестный адрес почтовой группы", + "UnknownSender": "Неизвестный отправитель", + "Unsubscribe": "Отписаться", + "ViewContactDetails": "Показать подробности", + "WinoUpgradeDescription": "Wino offers 3 accounts to start with for free. If you need more than 3 accounts, please upgrade", + "WinoUpgradeMessage": "Улучшить до неограниченного количества учетных записей", + "WinoUpgradeRemainingAccountsMessage": "Использовано {0} из {1} бесплатных учетных записей.", + "Yesterday": "Вчера" +} diff --git a/Wino.Core.Domain/Translations/tr_TR/resources.json b/Wino.Core.Domain/Translations/tr_TR/resources.json new file mode 100644 index 00000000..15218a84 --- /dev/null +++ b/Wino.Core.Domain/Translations/tr_TR/resources.json @@ -0,0 +1,468 @@ +{ + "AccountCreationDialog_Completed": "all done", + "AccountCreationDialog_Initializing": "başlatılıyor", + "AccountCreationDialog_PreparingFolders": "Klasör bilgileri yükleniyor.", + "AccountCreationDialog_SigninIn": "Hesap bilgileri kaydediliyor.", + "AccountEditDialog_Message": "Account Name", + "AccountEditDialog_Title": "Hesabı Düzenle", + "AccountPickerDialog_Title": "Pick an account", + "AddHyperlink": "Add", + "AutoDiscoveryProgressMessage": "Searching for mail settings...", + "BasicIMAPSetupDialog_AdvancedConfiguration": "Advanced Configuration", + "BasicIMAPSetupDialog_CredentialLocalMessage": "Your credentials will only be stored locally on your computer.", + "BasicIMAPSetupDialog_Description": "Some accounts require additional steps to sign in", + "BasicIMAPSetupDialog_DisplayName": "Display Name", + "BasicIMAPSetupDialog_DisplayNamePlaceholder": "eg. John Doe", + "BasicIMAPSetupDialog_LearnMore": "Learn more", + "BasicIMAPSetupDialog_MailAddress": "E-Mail Address", + "BasicIMAPSetupDialog_MailAddressPlaceholder": "johndoe@fabrikam.com", + "BasicIMAPSetupDialog_Password": "Password", + "BasicIMAPSetupDialog_Title": "IMAP Account", + "Buttons_AddAccount": "Add Account", + "Buttons_ApplyTheme": "Apply Theme", + "Buttons_Browse": "Browse", + "Buttons_Cancel": "Cancel", + "Buttons_Close": "Close", + "Buttons_Create": "Create", + "Buttons_CreateAccount": "Create Account", + "Buttons_Delete": "Delete", + "Buttons_Discard": "Discard", + "Buttons_EnableImageRendering": "Enable", + "Buttons_No": "No", + "Buttons_Open": "Open", + "Buttons_Purchase": "Purchase", + "Buttons_RateWino": "Rate Wino", + "Buttons_Save": "Save", + "Buttons_SaveConfiguration": "Save Configuration", + "Buttons_Share": "Share", + "Buttons_SignIn": "Sign In", + "Buttons_Yes": "Yes", + "Center": "Center", + "ComingSoon": "Coming soon...", + "ComposerFrom": "From: ", + "ComposerSubject": "Subject: ", + "ComposerTo": "To: ", + "ClipboardTextCopied_Message": "{0} copied to clipboard.", + "ClipboardTextCopied_Title": "Copied", + "ClipboardTextCopyFailed_Message": "Failed to copy {0} to clipboard.", + "ComposerToPlaceholder": "click enter to input addresses", + "CustomThemeBuilder_AccentColorDescription": "Set custom accent color if you wish. Not selecting a color will use your Windows accent color.", + "CustomThemeBuilder_AccentColorTitle": "Accent color", + "CustomThemeBuilder_PickColor": "Pick", + "CustomThemeBuilder_ThemeNameDescription": "Unique name for your custom theme.", + "CustomThemeBuilder_ThemeNameTitle": "Theme name", + "CustomThemeBuilder_Title": "Custom Theme Builder", + "CustomThemeBuilder_WallpaperDescription": "Set a custom wallpaper for Wino", + "CustomThemeBuilder_WallpaperTitle": "Set custom wallpaper", + "DialogMessage_AccountLimitMessage": "You have reached the account creation limit.\nWould you like to purchase 'Unlimited Account' add-on to continue?", + "DialogMessage_AccountLimitTitle": "Account Limit Reached", + "DialogMessage_CleanupFolderMessage": "Do you want to permanently delete all the mails in this folder?", + "DialogMessage_CleanupFolderTitle": "Cleanup Folder", + "DialogMessage_ComposerMissingRecipientMessage": "Message has no recipient.", + "DialogMessage_ComposerValidationFailedTitle": "Validation Failed", + "DialogMessage_CreateLinkedAccountMessage": "Give this new link a name. Accounts will be merged under this name.", + "DialogMessage_CreateLinkedAccountTitle": "Account Link Name", + "DialogMessage_DeleteAccountConfirmationMessage": "Delete {0}?", + "DialogMessage_DeleteAccountConfirmationTitle": "All data associated with this account will be deleted from disk permanently.", + "DialogMessage_DiscardDraftConfirmationMessage": "This draft will be discarded. Do you want to continue?", + "DialogMessage_DiscardDraftConfirmationTitle": "Discard Draft", + "DialogMessage_HardDeleteConfirmationMessage": "Permanent Delete", + "DialogMessage_HardDeleteConfirmationTitle": "Message(s) will be permanently deleted. Do you want to continue?", + "DialogMessage_NoAccountsForCreateMailMessage": "You don't have any accounts to create message from.", + "DialogMessage_NoAccountsForCreateMailTitle": "Account Missing", + "DialogMessage_RenameLinkedAccountsMessage": "Enter new name for linked account", + "DialogMessage_RenameLinkedAccountsTitle": "Rename Linked Account", + "DialogMessage_UnlinkAccountsConfirmationMessage": "This operation will not delete your accounts but only break the link for shared folder connections. Do you want to continue?", + "DialogMessage_UnlinkAccountsConfirmationTitle": "Unlink Accounts", + "DialogMessage_EmptySubjectConfirmation": "Missin Subject", + "DialogMessage_EmptySubjectConfirmationMessage": "Message has no subject. Do you want to continue?", + "Dialog_DontAskAgain": "Don't ask again", + "DiscordChannelDisclaimerMessage": "Wino doesn't have it's own Discord server, but special 'wino-mail' channel is hosted at 'Developer Sanctuary' server.\nTo get the updates about Wino please join Developer Sanctuary server and follow 'wino-mail' channel under 'Community Projects'\n\nYou will be directed to server URL since Discord doesn't support channel invites.", + "DiscordChannelDisclaimerTitle": "Important Discord Information", + "Draft": "Draft", + "EditorToolbarOption_Draw": "Draw", + "EditorToolbarOption_Format": "Format", + "EditorToolbarOption_Insert": "Insert", + "EditorToolbarOption_None": "None", + "EditorToolbarOption_Options": "Options", + "ElementTheme_Dark": "Dark mode", + "ElementTheme_Default": "Use system setting", + "ElementTheme_Light": "Light mode", + "Emoji": "Emoji", + "Exception_ImapClientPoolFailed": "IMAP Client Pool failed.", + "Exception_AuthenticationCanceled": "Authentication canceled", + "Exception_CustomThemeExists": "This theme already exists.", + "Exception_CustomThemeMissingName": "You must provide a name.", + "Exception_CustomThemeMissingWallpaper": "You must provide a custom background image.", + "Exception_FailedToSynchronizeFolders": "Failed to synchronize folders", + "Exception_GoogleAuthCallbackNull": "Callback uri is null on activation.", + "Exception_GoogleAuthCorruptedCode": "Corrupted authorization response.", + "Exception_GoogleAuthError": "OAuth authorization error: {0}", + "Exception_GoogleAuthInvalidResponse": "Received request with invalid state ({0})", + "Exception_GoogleAuthorizationCodeExchangeFailed": "Authorization code exchange failed.", + "Exception_InvalidSystemFolderConfiguration": "System folder configuration is not valid. Check configuration and try again.", + "Exception_NullAssignedAccount": "Assigned account is null", + "Exception_NullAssignedFolder": "Assigned folder is null", + "Exception_SynchronizerFailureHTTP": "Response handling failed with error HTTP code {0}", + "Exception_TokenGenerationFailed": "Token generation failed", + "Exception_TokenInfoRetrivalFailed": "Failed to get token information.", + "Exception_UnknowErrorDuringAuthentication": "Unknown error occurred during authentication", + "Exception_UnsupportedAction": "Action {0} is not implemented in request processor", + "Exception_UnsupportedProvider": "This provider is not supported.", + "Exception_UnsupportedSynchronizerOperation": "This operation is not supported for {0}", + "Exception_UserCancelSystemFolderSetupDialog": "User canceled system folder config dialog.", + "Files": "Files", + "FilteringOption_All": "All", + "FilteringOption_Flagged": "Flagged", + "FilteringOption_Unread": "Unread", + "Focused": "Focused", + "FolderOperation_CreateSubFolder": "Create sub folder", + "FolderOperation_Delete": "Delete", + "FolderOperation_DontSync": "Don't sync this folder", + "FolderOperation_Empty": "Empty this folder", + "FolderOperation_MarkAllAsRead": "Mark all as read", + "FolderOperation_Move": "Move", + "DragMoveToFolderCaption": "Move to {0}", + "FolderOperation_None": "None", + "FolderOperation_Pin": "Pin", + "FolderOperation_Rename": "Rename", + "FolderOperation_Unpin": "Unpin", + "HoverActionOption_Archive": "Archive", + "HoverActionOption_Delete": "Delete", + "HoverActionOption_MoveJunk": "Move to Junk", + "HoverActionOption_ToggleFlag": "Flag / Unflag", + "HoverActionOption_ToggleRead": "Read / Unread", + "MergedAccountCommonFolderInbox": "Inbox", + "MergedAccountCommonFolderSent": "Sent", + "MergedAccountCommonFolderDraft": "Draft", + "MergedAccountCommonFolderJunk": "Junk", + "MergedAccountCommonFolderTrash": "Deleted", + "MergedAccountCommonFolderArchive": "Archive", + "IMAPSetupDialog_AccountType": "Account type", + "IMAPSetupDialog_DisplayName": "Display Name", + "IMAPSetupDialog_DisplayNamePlaceholder": "eg. John Doe", + "IMAPSetupDialog_IncomingMailServer": "Incoming mail server", + "IMAPSetupDialog_IncomingMailServerPort": "Port", + "IMAPSetupDialog_MailAddress": "Email address", + "IMAPSetupDialog_MailAddressPlaceholder": "someone@example.com", + "IMAPSetupDialog_OutgoingMailServer": "Outgoing (SMTP) mail server", + "IMAPSetupDialog_OutgoingMailServerPassword": "Outgoing server password", + "IMAPSetupDialog_OutgoingMailServerPort": "Port", + "IMAPSetupDialog_OutgoingMailServerRequireAuthentication": "Outgoing server requires authentication", + "IMAPSetupDialog_OutgoingMailServerUsername": "Outgoing server user name", + "IMAPSetupDialog_Password": "Password", + "IMAPSetupDialog_RequireSSLForIncomingMail": "Require SSL for incoming email", + "IMAPSetupDialog_RequireSSLForOutgoingMail": "Require SSL for outgoing email", + "IMAPSetupDialog_Title": "Advanced IMAP Configuration", + "IMAPSetupDialog_UseSameConfig": "Use the same username and password for sending email", + "IMAPSetupDialog_Username": "Username", + "IMAPSetupDialog_UsernamePlaceholder": "johndoe, johndoe@fabrikam.com, domain/johndoe", + "ImageRenderingDisabled": "Image rendering is disabled for this message.", + "InfoBarAction_Enable": "Enable", + "InfoBarMessage_SynchronizationDisabledFolder": "This folder is disabled for synchronization.", + "InfoBarTitle_SynchronizationDisabledFolder": "Disabled Folder", + "GeneralTitle_Error": "Error", + "GeneralTitle_Warning": "Warning", + "GeneralTitle_Info": "Information", + "Info_AccountCreatedMessage": "{0} is created", + "Info_AccountCreatedTitle": "Account Creation", + "Info_AccountCreationFailedTitle": "Account Creation Failed", + "Info_AccountDeletedMessage": "{0} is successfuly deleted.", + "Info_AccountDeletedTitle": "Account Deleted", + "Info_AccountIssueFixFailedTitle": "Failed", + "Info_AccountIssueFixSuccessMessage": "Fixed all account issues.", + "Info_AccountIssueFixSuccessTitle": "Success", + "Info_AttachmentOpenFailedMessage": "Can't open this attachment.", + "Info_AttachmentOpenFailedTitle": "Failed", + "Info_AttachmentSaveFailedMessage": "Can't save this attachment.", + "Info_AttachmentSaveFailedTitle": "Failed", + "Info_AttachmentSaveSuccessMessage": "Attachment is saved.", + "Info_AttachmentSaveSuccessTitle": "Attachment Saved", + "Info_BackgroundExecutionDeniedMessage": "Background execution for the app is denied. This may affect background synchronization and live notifications.", + "Info_BackgroundExecutionDeniedTitle": "Denied Background Execution", + "Info_BackgroundExecutionUnknownErrorMessage": "Unknown exception occurred when registering background synchronizer.", + "Info_BackgroundExecutionUnknownErrorTitle": "Background Execution Failure", + "Info_ComposerMissingMIMEMessage": "Couldn't locate the MIME file. Synchronizing may help.", + "Info_ComposerMissingMIMETitle": "Failed", + "Info_ContactExistsMessage": "This contact is already in the recipient list.", + "Info_ContactExistsTitle": "Contact Exists", + "Info_DraftFolderMissingMessage": "Draft folder is missing for this account. Please check your account settings.", + "Info_DraftFolderMissingTitle": "Missing Draft Folder", + "Info_FileLaunchFailedTitle": "Failed to launch file", + "Info_InvalidAddressMessage": "'{0}' is not a valid e-mail address.", + "Info_InvalidAddressTitle": "Invalid Address", + "Info_InvalidMoveTargetMessage": "You can't move selected mails to this folder.", + "Info_InvalidMoveTargetTitle": "Invalid Move Target", + "Info_LogsNotFoundMessage": "There are no logs to share.", + "Info_LogsNotFoundTitle": "Logs Not Found", + "Info_LogsSavedMessage": "{0} is saved to selected folder.", + "Info_LogsSavedTitle": "Saved", + "Info_MailRenderingFailedMessage": "This mail is corrupted or can't be opened.\n{0}", + "Info_MailRenderingFailedTitle": "Render Failed", + "Info_MessageCorruptedMessage": "This message is corrupted.", + "Info_MessageCorruptedTitle": "Error", + "Info_MissingFolderMessage": "{0} doesn't exist for this account.", + "Info_MissingFolderTitle": "Missing Folder", + "Info_PurchaseExistsMessage": "Looks like this product has already been purchased before.", + "Info_PurchaseExistsTitle": "Existing Product", + "Info_PurchaseThankYouMessage": "Thank You", + "Info_PurchaseThankYouTitle": "Purchase successful", + "Info_RequestCreationFailedTitle": "Failed to Create Requests", + "Info_ReviewNetworkErrorMessage": "There was a network issue with your review.", + "Info_ReviewNetworkErrorTitle": "Network Issue", + "Info_ReviewNewMessage": "All feedbacks are appreciated. Thank you for the review!", + "Info_ReviewSuccessTitle": "Thank you", + "Info_ReviewUnknownErrorMessage": "There was an unknown issue with your review. ({0})", + "Info_ReviewUnknownErrorTitle": "Unknown Error", + "Info_ReviewUpdatedMessage": "Thank you for the updated review.", + "Info_SignatureDisabledMessage": "Disabled signature for this account", + "Info_SignatureDisabledTitle": "Success", + "Info_SignatureSavedMessage": "New signature is saved", + "Info_SignatureSavedTitle": "Success", + "Info_SyncCanceledMessage": "Canceled", + "Info_SyncCanceledTitle": "Synchronization", + "Info_SyncFailedTitle": "Synchronization Failed", + "Info_UnsupportedFunctionalityDescription": "This functionality is not implemented yet.", + "Info_UnsupportedFunctionalityTitle": "Unsupported", + "Info_UnsubscribeLinkInvalidTitle": "Invalid Unsubscribe Uri", + "Info_UnsubscribeLinkInvalidMessage": "This unsubscribe link is invalid. Failed to unsubscribe from the list.", + "ImapAdvancedSetupDialog_AuthenticationMethod": "Authentication method", + "ImapAdvancedSetupDialog_ConnectionSecurity": "Connection security", + "ImapAuthenticationMethod_Auto": "Auto", + "ImapAuthenticationMethod_CramMD5": "CRAM-MD5", + "ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5", + "ImapAuthenticationMethod_None": "No authentication", + "ImapAuthenticationMethod_Plain": "Normal password", + "ImapAuthenticationMethod_EncryptedPassword": "Encrypted password", + "ImapAuthenticationMethod_Ntlm": "NTLM", + "ImapConnectionSecurity_None": "None", + "ImapConnectionSecurity_SslTls": "SSL/TLS", + "ImapConnectionSecurity_StartTls": "STARTTLS", + "ImapConnectionSecurity_Auto": "Auto", + "Justify": "Justify", + "Left": "Left", + "Link": "Link", + "LinkedAccountsCreatePolicyMessage": "you must have at least 2 accounts to create link\nlink will be removed on save", + "LinkedAccountsTitle": "Linked Accounts", + "MailOperation_AlwaysMoveFocused": "Always Move to Focused", + "MailOperation_AlwaysMoveOther": "Always Move to Other", + "MailOperation_Archive": "Archive", + "MailOperation_ClearFlag": "Clear flag", + "MailOperation_DarkEditor": "Dark", + "MailOperation_Delete": "Delete", + "MailOperation_ExportPDF": "Export to PDF", + "MailOperation_Find": "Find", + "MailOperation_Forward": "Forward", + "MailOperation_Ignore": "Ignore", + "MailOperation_LightEditor": "Light", + "MailOperation_MarkAsJunk": "Mark as junk", + "MailOperation_MarkAsRead": "Mark as read", + "MailOperation_MarkAsUnread": "Mark as unread", + "MailOperation_MarkNotJunk": "Mark as Not Junk", + "MailOperation_Move": "Move", + "MailOperation_MoveFocused": "Move to Focused", + "MailOperation_MoveJunk": "Move to Junk", + "MailOperation_MoveOther": "Move to Other", + "MailOperation_Navigate": "Navigate", + "MailOperation_Print": "Print", + "MailOperation_Reply": "Reply", + "MailOperation_ReplyAll": "Reply all", + "MailOperation_SaveAs": "Save As", + "MailOperation_SetFlag": "Set flag", + "MailOperation_Unarchive": "Unarchive", + "MailOperation_Zoom": "Zoom", + "MailsSelected": "{0} item(s) selected", + "MarkFlagUnflag": "Mark as flagged/unflagged", + "MarkReadUnread": "Mark as read/unread", + "MenuManageAccounts": "Manage Accounts", + "MenuNewMail": "New Mail", + "MenuMergedAccountItemAccountsSuffix": " accounts", + "MenuRate": "Rate Wino", + "MenuSettings": "Settings", + "MergedAccountsAvailableAccountsTitle": "Available Accounts", + "More": "More", + "MoveMailDialog_InvalidFolderMessage": "{0} is not a valid folder for this mail.", + "MoveMailDialog_Title": "Pick a folder", + "NewAccountDialog_AccountName": "Account Name", + "NewAccountDialog_AccountNameDefaultValue": "Personal", + "NewAccountDialog_AccountNamePlaceholder": "eg. Personal Mail", + "NewAccountDialog_Title": "Add New Account", + "NoMailSelected": "no message selected", + "NoMessageCrieteria": "no messages match your search criteria.", + "NoMessageEmptyFolder": "this folder is empty.", + "Notifications_MultipleNotificationsMessage": "You have {0} new mails", + "Notifications_MultipleNotificationsTitle": "New Mails", + "Notifications_WinoUpdatedMessage": "Checkout new version {0}", + "Notifications_WinoUpdatedTitle": "Wino Mail has been updated.", + "Other": "Other", + "PaneLengthOption_Default": "Default", + "PaneLengthOption_ExtraLarge": "Extra Large", + "PaneLengthOption_Large": "Large", + "PaneLengthOption_Medium": "Medium", + "PaneLengthOption_Micro": "Micro", + "PaneLengthOption_Small": "Small", + "Photos": "Photos", + "PreparingFoldersMessage": "Preparing folders", + "ProviderDetail_Gmail_Description": "Google Account", + "ProviderDetail_IMAP_Description": "Custom IMAP/SMTP server", + "ProviderDetail_IMAP_Title": "IMAP Server", + "Results": "Results", + "Right": "Right", + "SynchronizationFolderReport_Success": "up to date", + "SynchronizationFolderReport_Failed": "synchronization is failed", + "SearchBarPlaceholder": "search", + "SearchingIn": "searching in", + "SettingsAboutGithub_Description": "Go to issue tracker GitHub repository.", + "SettingsAboutGithub_Title": "GitHub", + "SettingsAccountManagementAppendMessage_Title": "Append messages to Sent folder", + "SettingsAccountManagementAppendMessage_Description": "Create a copy of the message in Sent folder after the draft is sent. Enable this if you don't see your mails after you sent them in Sent folder.", + "SettingsEditLinkedInbox_Title": "Edit Linked Inbox", + "SettingsEditLinkedInbox_Description": "Add / remove accounts, rename or break the link between accounts.", + "SettingsAboutVersion": "Version ", + "SettingsAboutWinoDescription": "Lightweight mail client for Windows device families.", + "SettingsAbout_Description": "Learn more about Wino.", + "SettingsAbout_Title": "About", + "SettingsAccentColor_Description": "Change application's accent color", + "SettingsAccentColor_Title": "Accent Color", + "SettingsAccentColor_UseWindowsAccentColor": "Use my Windows accent color", + "SettingsAccountName_Description": "Change the name of the account.", + "SettingsAccountName_Title": "Account Name", + "SettingsApplicationTheme_Description": "Personalize Wino with different custom application themes for your like.", + "SettingsApplicationTheme_Title": "Application Theme", + "SettingsAvailableThemes_Description": "Select a theme from Wino's own collection for your taste or apply your own themes.", + "SettingsAvailableThemes_Title": "Available Themes", + "SettingsCustomTheme_Description": "Create your own custom theme with custom wallpaper and accent color.", + "SettingsCustomTheme_Title": "Custom Theme", + "SettingsDeleteAccount_Description": "Delete all e-mails and credentials associated with this account.", + "SettingsDeleteAccount_Title": "Delete this account", + "SettingsDeleteProtection_Description": "Should Wino ask you for comfirmation every time you try to permanently delete a mail using Shift + Del keys?", + "SettingsDeleteProtection_Title": "Permanent Delete Protection", + "SettingsDiagnostics_Description": "For developers", + "SettingsDiagnostics_Title": "Diagnostics", + "SettingsDiscord_Description": "Get regular development updates, join roadmap discussions and provide feedback.", + "SettingsDiscord_Title": "Discord Channel", + "SettingsElementThemeSelectionDisabled": "Element theme selection is disabled when application theme is selected other than Default.", + "SettingsElementTheme_Description": "Select a Windows theme for Wino", + "SettingsElementTheme_Title": "Element Theme", + "SettingsEnableHoverActions_Title": "Enable hover actions", + "SettingsEnableIMAPLogs_Description": "Enable this to provide details about IMAP connectivity issuses you had during IMAP server setup.", + "SettingsEnableIMAPLogs_Title": "Enable IMAP Protocol Logs", + "SettingsEnableLogs_Description": "I might need logs for crashes to diagnose issues you have opened in GitHub. None of the logs will expose your credentials or sensetive information to public.", + "SettingsEnableLogs_Title": "Enable Logs", + "SettingsEnableSignature": "Enable Signature", + "SettingsExpandOnStartup_Description": "Set whether Wino should expand this account's folders on startup.", + "SettingsExpandOnStartup_Title": "Expand Menu on Startup", + "SettingsExternalContent_Description": "Manage external content settings when rendering mails.", + "SettingsExternalContent_Title": "External Content", + "SettingsFocusedInbox_Description": "Set whether Inbox should be split into two as Focused - Other.", + "SettingsFocusedInbox_Title": "Focused Inbox", + "SettingsFolderSync_Description": "Enable or disable specific folders for synchronization.", + "SettingsFolderSync_Title": "Folder Synchronization", + "SettingsFolderOptions_Title": "Folder Configuration", + "SettingsFolderOptions_Description": "Change individual folder settings like enable/disable sync or show/hide unread badge.", + "SettingsHoverActionCenter": "Center Action", + "SettingsHoverActionLeft": "Left Action", + "SettingsHoverActionRight": "Right Action", + "SettingsHoverActions_Description": "Select 3 actions to show up when you hover over the mails with cursor.", + "SettingsHoverActions_Title": "Hover Actions", + "SettingsLanguage_Description": "Change display language for Wino.", + "SettingsLanguage_Title": "Display Language", + "CategoriesFolderNameOverride": "Categories", + "MoreFolderNameOverride": "More", + "SettingsOptions_Title": "Settings", + "SettingsLinkAccounts_Description": "Merge multiple accounts into one. See mails from one Inbox together.", + "SettingsLinkAccounts_Title": "Create Linked Accounts", + "SettingsLinkedAccountsSave_Description": "Modify the current link with the new accounts.", + "SettingsLinkedAccountsSave_Title": "Save Changes", + "SettingsLoadImages_Title": "Load images automatically", + "SettingsLoadStyles_Title": "Load styles automatically", + "SettingsMailSpacing_Description": "Adjust the spacing for listing mails.", + "SettingsMailSpacing_Title": "Mail Spacing", + "SettingsFolderMenuStyle_Title": "Create Nested Folders", + "SettingsFolderMenuStyle_Description": "Change whether account folders should be nested inside an account menu item or not. Toggle this off if you like the old menu system in Windows Mail", + "SettingsManageAccountSettings_Description": "Notifications, signatures, synchronization and other settings per account.", + "SettingsManageAccountSettings_Title": "Manage Account Settings", + "SettingsManageLink_Description": "Move items to add new link or remove existing link.", + "SettingsManageLink_Title": "Manage Link", + "SettingsMarkAsRead_Description": "Change what should happen to the selected item.", + "SettingsMarkAsRead_DontChange": "Don't automatically mark item as read", + "SettingsMarkAsRead_SecondsToWait": "Seconds to wait: ", + "SettingsMarkAsRead_Timer": "When viewed in the reading pane", + "SettingsMarkAsRead_Title": "Mark item as read", + "SettingsMarkAsRead_WhenSelected": "When selected", + "SettingsMessageList_Description": "Change how your messages should be organized in mail list.", + "SettingsMessageList_Title": "Message List", + "SettingsNoAccountSetupMessage": "You didn't setup any accounts yet.", + "SettingsNotifications_Description": "Turn on or off notifications for this account.", + "SettingsNotifications_Title": "Notifications", + "SettingsPaneLength_Description": "Change the width of the mail list.", + "SettingsPaneLength_Title": "Mail List Pane Length", + "SettingsPaypal_Description": "Show much more love ❤️ All donations are appreciated.", + "SettingsPaypal_Title": "Donate via PayPal", + "SettingsPersonalizationMailDisplayCompactMode": "Compact Mode", + "SettingsPersonalizationMailDisplayMediumMode": "Medium Mode", + "SettingsPersonalizationMailDisplaySpaciousMode": "Spacious Mode", + "SettingsPersonalization_Description": "Change appearance of Wino as you like.", + "SettingsPersonalization_Title": "Personalization", + "SettingsPrivacyPolicy_Description": "Review privacy policy.", + "SettingsPrivacyPolicy_Title": "Privacy Policy", + "SettingsReadingPane_Description": "Mail rendering options.", + "SettingsReadingPane_Title": "Reading Pane", + "SettingsReaderFont_Title": "Default Reader Font", + "SettingsReaderFontFamily_Description": "Change the default font family and font size for reading mails.", + "SettingsFontFamily_Title": "Font Family", + "SettingsFontSize_Title": "Font Size", + "SettingsFontPreview_Title": "Preview", + "SettingsComposerFont_Title": "Default Composer Font", + "SettingsComposerFontFamily_Description": "Change the default font family and font size for composing mails.", + "SettingsRenameMergeAccount_Description": "Change the display name of the linked accounts.", + "SettingsRenameMergeAccount_Title": "Rename", + "SettingsSemanticZoom_Description": "This will allow you to click on the headers in messages list and go to specific date", + "SettingsSemanticZoom_Title": "Semantic Zoom for Date Headers", + "SettingsShowPreviewText_Description": "Hide/show thepreview text.", + "SettingsShowPreviewText_Title": "Show Preview Text", + "SettingsShowSenderPictures_Description": "Hide/show the thumbnail sender pictures.", + "SettingsShowSenderPictures_Title": "Show Sender Avatars", + "SettingsPrefer24HourClock_Title": "Display Clock Format in 24 Hours", + "SettingsPrefer24HourClock_Description": "Mail recieve times will be displayed in 24 hour format instead of 12 (AM/PM)", + "SettingsSignature_Description": "Edit or remove account signature", + "SettingsSignature_Title": "Signature", + "SettingsStartupItem_Description": "Primary account item to load Inbox at startup.", + "SettingsStartupItem_Title": "Startup Item", + "SettingsStore_Description": "Show some love ❤️", + "SettingsStore_Title": "Rate in Store", + "SettingsThreads_Description": "Organize messages into conversation threads.", + "SettingsThreads_Title": "Conversation Threading", + "SettingsUnlinkAccounts_Description": "Remove the link between accounts. This will not delete your accounts.", + "SettingsUnlinkAccounts_Title": "Unlink Accounts", + "SortingOption_Date": "by date", + "SortingOption_Name": "by name", + "StoreRatingDialog_MessageFirstLine": "All feedbacks are appreciated and they will make much Wino better in the future. Would you like to rate Wino in Microsoft Store?", + "StoreRatingDialog_MessageSecondLine": "Would you like to rate Wino Mail in Microsoft Store?", + "StoreRatingDialog_Title": "Enjoying Wino?", + "SystemFolderConfigDialog_ArchiveFolderDescription": "Archived messages will be moved to here.", + "SystemFolderConfigDialog_ArchiveFolderHeader": "Archive Folder", + "SystemFolderConfigDialog_DeletedFolderDescription": "Deleted messages will be moved to here.", + "SystemFolderConfigDialog_DeletedFolderHeader": "Deleted Folder", + "SystemFolderConfigDialog_DraftFolderDescription": "New mails/replies will be crafted in here.", + "SystemFolderConfigDialog_DraftFolderHeader": "Draft Folder", + "SystemFolderConfigDialog_JunkFolderDescription": "All spam/junk mails will be here.", + "SystemFolderConfigDialog_JunkFolderHeader": "Junk/Spam Folder", + "SystemFolderConfigDialog_MessageFirstLine": "This IMAP server doesn't support SPECIAL-USE extension hence Wino couldn't setup the system folders properly.", + "SystemFolderConfigDialog_MessageSecondLine": "Please select the appropriate folders for specific functionalities.", + "SystemFolderConfigDialog_SentFolderDescription": "Folder that sent messages will be stored.", + "SystemFolderConfigDialog_SentFolderHeader": "Sent Folder", + "SystemFolderConfigDialog_Title": "Configure System Folders", + "TestingImapConnectionMessage": "Testing server connection...", + "Today": "Today", + "UnknownAddress": "unknown address", + "UnknownDateHeader": "Unknown Date", + "UnknownGroupAddress": "unknown Mail Group Address", + "UnknownSender": "Unknown Sender", + "Unsubscribe": "Unsubscribe", + "ViewContactDetails": "View Details", + "WinoUpgradeDescription": "Wino offers 3 accounts to start with for free. If you need more than 3 accounts, please upgrade", + "WinoUpgradeMessage": "Upgrade to Unlimited Accounts", + "WinoUpgradeRemainingAccountsMessage": "{0} out of {1} free accounts used.", + "Yesterday": "Yesterday" +} diff --git a/Wino.Core.Domain/Translations/uk_UA/resources.json b/Wino.Core.Domain/Translations/uk_UA/resources.json new file mode 100644 index 00000000..54ddd05d --- /dev/null +++ b/Wino.Core.Domain/Translations/uk_UA/resources.json @@ -0,0 +1,468 @@ +{ + "AccountCreationDialog_Completed": "all done", + "AccountCreationDialog_Initializing": "initializing", + "AccountCreationDialog_PreparingFolders": "We are getting folder information at the moment.", + "AccountCreationDialog_SigninIn": "Account information is being saved.", + "AccountEditDialog_Message": "Account Name", + "AccountEditDialog_Title": "Edit Account", + "AccountPickerDialog_Title": "Pick an account", + "AddHyperlink": "Add", + "AutoDiscoveryProgressMessage": "Searching for mail settings...", + "BasicIMAPSetupDialog_AdvancedConfiguration": "Advanced Configuration", + "BasicIMAPSetupDialog_CredentialLocalMessage": "Your credentials will only be stored locally on your computer.", + "BasicIMAPSetupDialog_Description": "Some accounts require additional steps to sign in", + "BasicIMAPSetupDialog_DisplayName": "Display Name", + "BasicIMAPSetupDialog_DisplayNamePlaceholder": "eg. John Doe", + "BasicIMAPSetupDialog_LearnMore": "Learn more", + "BasicIMAPSetupDialog_MailAddress": "E-Mail Address", + "BasicIMAPSetupDialog_MailAddressPlaceholder": "johndoe@fabrikam.com", + "BasicIMAPSetupDialog_Password": "Password", + "BasicIMAPSetupDialog_Title": "IMAP Account", + "Buttons_AddAccount": "Add Account", + "Buttons_ApplyTheme": "Apply Theme", + "Buttons_Browse": "Browse", + "Buttons_Cancel": "Cancel", + "Buttons_Close": "Close", + "Buttons_Create": "Create", + "Buttons_CreateAccount": "Create Account", + "Buttons_Delete": "Delete", + "Buttons_Discard": "Discard", + "Buttons_EnableImageRendering": "Enable", + "Buttons_No": "No", + "Buttons_Open": "Open", + "Buttons_Purchase": "Purchase", + "Buttons_RateWino": "Rate Wino", + "Buttons_Save": "Save", + "Buttons_SaveConfiguration": "Save Configuration", + "Buttons_Share": "Share", + "Buttons_SignIn": "Sign In", + "Buttons_Yes": "Yes", + "Center": "Center", + "ComingSoon": "Coming soon...", + "ComposerFrom": "From: ", + "ComposerSubject": "Subject: ", + "ComposerTo": "To: ", + "ClipboardTextCopied_Message": "{0} copied to clipboard.", + "ClipboardTextCopied_Title": "Copied", + "ClipboardTextCopyFailed_Message": "Failed to copy {0} to clipboard.", + "ComposerToPlaceholder": "click enter to input addresses", + "CustomThemeBuilder_AccentColorDescription": "Set custom accent color if you wish. Not selecting a color will use your Windows accent color.", + "CustomThemeBuilder_AccentColorTitle": "Accent color", + "CustomThemeBuilder_PickColor": "Pick", + "CustomThemeBuilder_ThemeNameDescription": "Unique name for your custom theme.", + "CustomThemeBuilder_ThemeNameTitle": "Theme name", + "CustomThemeBuilder_Title": "Custom Theme Builder", + "CustomThemeBuilder_WallpaperDescription": "Set a custom wallpaper for Wino", + "CustomThemeBuilder_WallpaperTitle": "Set custom wallpaper", + "DialogMessage_AccountLimitMessage": "You have reached the account creation limit.\nWould you like to purchase 'Unlimited Account' add-on to continue?", + "DialogMessage_AccountLimitTitle": "Account Limit Reached", + "DialogMessage_CleanupFolderMessage": "Do you want to permanently delete all the mails in this folder?", + "DialogMessage_CleanupFolderTitle": "Cleanup Folder", + "DialogMessage_ComposerMissingRecipientMessage": "Message has no recipient.", + "DialogMessage_ComposerValidationFailedTitle": "Validation Failed", + "DialogMessage_CreateLinkedAccountMessage": "Give this new link a name. Accounts will be merged under this name.", + "DialogMessage_CreateLinkedAccountTitle": "Account Link Name", + "DialogMessage_DeleteAccountConfirmationMessage": "Delete {0}?", + "DialogMessage_DeleteAccountConfirmationTitle": "All data associated with this account will be deleted from disk permanently.", + "DialogMessage_DiscardDraftConfirmationMessage": "This draft will be discarded. Do you want to continue?", + "DialogMessage_DiscardDraftConfirmationTitle": "Discard Draft", + "DialogMessage_HardDeleteConfirmationMessage": "Permanent Delete", + "DialogMessage_HardDeleteConfirmationTitle": "Message(s) will be permanently deleted. Do you want to continue?", + "DialogMessage_NoAccountsForCreateMailMessage": "You don't have any accounts to create message from.", + "DialogMessage_NoAccountsForCreateMailTitle": "Account Missing", + "DialogMessage_RenameLinkedAccountsMessage": "Enter new name for linked account", + "DialogMessage_RenameLinkedAccountsTitle": "Rename Linked Account", + "DialogMessage_UnlinkAccountsConfirmationMessage": "This operation will not delete your accounts but only break the link for shared folder connections. Do you want to continue?", + "DialogMessage_UnlinkAccountsConfirmationTitle": "Unlink Accounts", + "DialogMessage_EmptySubjectConfirmation": "Missin Subject", + "DialogMessage_EmptySubjectConfirmationMessage": "Message has no subject. Do you want to continue?", + "Dialog_DontAskAgain": "Don't ask again", + "DiscordChannelDisclaimerMessage": "Wino doesn't have it's own Discord server, but special 'wino-mail' channel is hosted at 'Developer Sanctuary' server.\nTo get the updates about Wino please join Developer Sanctuary server and follow 'wino-mail' channel under 'Community Projects'\n\nYou will be directed to server URL since Discord doesn't support channel invites.", + "DiscordChannelDisclaimerTitle": "Important Discord Information", + "Draft": "Draft", + "EditorToolbarOption_Draw": "Draw", + "EditorToolbarOption_Format": "Format", + "EditorToolbarOption_Insert": "Insert", + "EditorToolbarOption_None": "None", + "EditorToolbarOption_Options": "Options", + "ElementTheme_Dark": "Dark mode", + "ElementTheme_Default": "Use system setting", + "ElementTheme_Light": "Light mode", + "Emoji": "Emoji", + "Exception_ImapClientPoolFailed": "IMAP Client Pool failed.", + "Exception_AuthenticationCanceled": "Authentication canceled", + "Exception_CustomThemeExists": "This theme already exists.", + "Exception_CustomThemeMissingName": "You must provide a name.", + "Exception_CustomThemeMissingWallpaper": "You must provide a custom background image.", + "Exception_FailedToSynchronizeFolders": "Failed to synchronize folders", + "Exception_GoogleAuthCallbackNull": "Callback uri is null on activation.", + "Exception_GoogleAuthCorruptedCode": "Corrupted authorization response.", + "Exception_GoogleAuthError": "OAuth authorization error: {0}", + "Exception_GoogleAuthInvalidResponse": "Received request with invalid state ({0})", + "Exception_GoogleAuthorizationCodeExchangeFailed": "Authorization code exchange failed.", + "Exception_InvalidSystemFolderConfiguration": "System folder configuration is not valid. Check configuration and try again.", + "Exception_NullAssignedAccount": "Assigned account is null", + "Exception_NullAssignedFolder": "Assigned folder is null", + "Exception_SynchronizerFailureHTTP": "Response handling failed with error HTTP code {0}", + "Exception_TokenGenerationFailed": "Token generation failed", + "Exception_TokenInfoRetrivalFailed": "Failed to get token information.", + "Exception_UnknowErrorDuringAuthentication": "Unknown error occurred during authentication", + "Exception_UnsupportedAction": "Action {0} is not implemented in request processor", + "Exception_UnsupportedProvider": "This provider is not supported.", + "Exception_UnsupportedSynchronizerOperation": "This operation is not supported for {0}", + "Exception_UserCancelSystemFolderSetupDialog": "User canceled system folder config dialog.", + "Files": "Files", + "FilteringOption_All": "All", + "FilteringOption_Flagged": "Flagged", + "FilteringOption_Unread": "Unread", + "Focused": "Focused", + "FolderOperation_CreateSubFolder": "Create sub folder", + "FolderOperation_Delete": "Delete", + "FolderOperation_DontSync": "Don't sync this folder", + "FolderOperation_Empty": "Empty this folder", + "FolderOperation_MarkAllAsRead": "Mark all as read", + "FolderOperation_Move": "Move", + "DragMoveToFolderCaption": "Move to {0}", + "FolderOperation_None": "None", + "FolderOperation_Pin": "Pin", + "FolderOperation_Rename": "Rename", + "FolderOperation_Unpin": "Unpin", + "HoverActionOption_Archive": "Archive", + "HoverActionOption_Delete": "Delete", + "HoverActionOption_MoveJunk": "Move to Junk", + "HoverActionOption_ToggleFlag": "Flag / Unflag", + "HoverActionOption_ToggleRead": "Read / Unread", + "MergedAccountCommonFolderInbox": "Inbox", + "MergedAccountCommonFolderSent": "Sent", + "MergedAccountCommonFolderDraft": "Draft", + "MergedAccountCommonFolderJunk": "Junk", + "MergedAccountCommonFolderTrash": "Deleted", + "MergedAccountCommonFolderArchive": "Archive", + "IMAPSetupDialog_AccountType": "Account type", + "IMAPSetupDialog_DisplayName": "Display Name", + "IMAPSetupDialog_DisplayNamePlaceholder": "eg. John Doe", + "IMAPSetupDialog_IncomingMailServer": "Incoming mail server", + "IMAPSetupDialog_IncomingMailServerPort": "Port", + "IMAPSetupDialog_MailAddress": "Email address", + "IMAPSetupDialog_MailAddressPlaceholder": "someone@example.com", + "IMAPSetupDialog_OutgoingMailServer": "Outgoing (SMTP) mail server", + "IMAPSetupDialog_OutgoingMailServerPassword": "Outgoing server password", + "IMAPSetupDialog_OutgoingMailServerPort": "Port", + "IMAPSetupDialog_OutgoingMailServerRequireAuthentication": "Outgoing server requires authentication", + "IMAPSetupDialog_OutgoingMailServerUsername": "Outgoing server user name", + "IMAPSetupDialog_Password": "Password", + "IMAPSetupDialog_RequireSSLForIncomingMail": "Require SSL for incoming email", + "IMAPSetupDialog_RequireSSLForOutgoingMail": "Require SSL for outgoing email", + "IMAPSetupDialog_Title": "Advanced IMAP Configuration", + "IMAPSetupDialog_UseSameConfig": "Use the same username and password for sending email", + "IMAPSetupDialog_Username": "Username", + "IMAPSetupDialog_UsernamePlaceholder": "johndoe, johndoe@fabrikam.com, domain/johndoe", + "ImageRenderingDisabled": "Image rendering is disabled for this message.", + "InfoBarAction_Enable": "Enable", + "InfoBarMessage_SynchronizationDisabledFolder": "This folder is disabled for synchronization.", + "InfoBarTitle_SynchronizationDisabledFolder": "Disabled Folder", + "GeneralTitle_Error": "Error", + "GeneralTitle_Warning": "Warning", + "GeneralTitle_Info": "Information", + "Info_AccountCreatedMessage": "{0} is created", + "Info_AccountCreatedTitle": "Account Creation", + "Info_AccountCreationFailedTitle": "Account Creation Failed", + "Info_AccountDeletedMessage": "{0} is successfuly deleted.", + "Info_AccountDeletedTitle": "Account Deleted", + "Info_AccountIssueFixFailedTitle": "Failed", + "Info_AccountIssueFixSuccessMessage": "Fixed all account issues.", + "Info_AccountIssueFixSuccessTitle": "Success", + "Info_AttachmentOpenFailedMessage": "Can't open this attachment.", + "Info_AttachmentOpenFailedTitle": "Failed", + "Info_AttachmentSaveFailedMessage": "Can't save this attachment.", + "Info_AttachmentSaveFailedTitle": "Failed", + "Info_AttachmentSaveSuccessMessage": "Attachment is saved.", + "Info_AttachmentSaveSuccessTitle": "Attachment Saved", + "Info_BackgroundExecutionDeniedMessage": "Background execution for the app is denied. This may affect background synchronization and live notifications.", + "Info_BackgroundExecutionDeniedTitle": "Denied Background Execution", + "Info_BackgroundExecutionUnknownErrorMessage": "Unknown exception occurred when registering background synchronizer.", + "Info_BackgroundExecutionUnknownErrorTitle": "Background Execution Failure", + "Info_ComposerMissingMIMEMessage": "Couldn't locate the MIME file. Synchronizing may help.", + "Info_ComposerMissingMIMETitle": "Failed", + "Info_ContactExistsMessage": "This contact is already in the recipient list.", + "Info_ContactExistsTitle": "Contact Exists", + "Info_DraftFolderMissingMessage": "Draft folder is missing for this account. Please check your account settings.", + "Info_DraftFolderMissingTitle": "Missing Draft Folder", + "Info_FileLaunchFailedTitle": "Failed to launch file", + "Info_InvalidAddressMessage": "'{0}' is not a valid e-mail address.", + "Info_InvalidAddressTitle": "Invalid Address", + "Info_InvalidMoveTargetMessage": "You can't move selected mails to this folder.", + "Info_InvalidMoveTargetTitle": "Invalid Move Target", + "Info_LogsNotFoundMessage": "There are no logs to share.", + "Info_LogsNotFoundTitle": "Logs Not Found", + "Info_LogsSavedMessage": "{0} is saved to selected folder.", + "Info_LogsSavedTitle": "Saved", + "Info_MailRenderingFailedMessage": "This mail is corrupted or can't be opened.\n{0}", + "Info_MailRenderingFailedTitle": "Render Failed", + "Info_MessageCorruptedMessage": "This message is corrupted.", + "Info_MessageCorruptedTitle": "Error", + "Info_MissingFolderMessage": "{0} doesn't exist for this account.", + "Info_MissingFolderTitle": "Missing Folder", + "Info_PurchaseExistsMessage": "Looks like this product has already been purchased before.", + "Info_PurchaseExistsTitle": "Existing Product", + "Info_PurchaseThankYouMessage": "Thank You", + "Info_PurchaseThankYouTitle": "Purchase successful", + "Info_RequestCreationFailedTitle": "Failed to Create Requests", + "Info_ReviewNetworkErrorMessage": "There was a network issue with your review.", + "Info_ReviewNetworkErrorTitle": "Network Issue", + "Info_ReviewNewMessage": "All feedbacks are appreciated. Thank you for the review!", + "Info_ReviewSuccessTitle": "Thank you", + "Info_ReviewUnknownErrorMessage": "There was an unknown issue with your review. ({0})", + "Info_ReviewUnknownErrorTitle": "Unknown Error", + "Info_ReviewUpdatedMessage": "Thank you for the updated review.", + "Info_SignatureDisabledMessage": "Disabled signature for this account", + "Info_SignatureDisabledTitle": "Success", + "Info_SignatureSavedMessage": "New signature is saved", + "Info_SignatureSavedTitle": "Success", + "Info_SyncCanceledMessage": "Canceled", + "Info_SyncCanceledTitle": "Synchronization", + "Info_SyncFailedTitle": "Synchronization Failed", + "Info_UnsupportedFunctionalityDescription": "This functionality is not implemented yet.", + "Info_UnsupportedFunctionalityTitle": "Unsupported", + "Info_UnsubscribeLinkInvalidTitle": "Invalid Unsubscribe Uri", + "Info_UnsubscribeLinkInvalidMessage": "This unsubscribe link is invalid. Failed to unsubscribe from the list.", + "ImapAdvancedSetupDialog_AuthenticationMethod": "Authentication method", + "ImapAdvancedSetupDialog_ConnectionSecurity": "Connection security", + "ImapAuthenticationMethod_Auto": "Auto", + "ImapAuthenticationMethod_CramMD5": "CRAM-MD5", + "ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5", + "ImapAuthenticationMethod_None": "No authentication", + "ImapAuthenticationMethod_Plain": "Normal password", + "ImapAuthenticationMethod_EncryptedPassword": "Encrypted password", + "ImapAuthenticationMethod_Ntlm": "NTLM", + "ImapConnectionSecurity_None": "None", + "ImapConnectionSecurity_SslTls": "SSL/TLS", + "ImapConnectionSecurity_StartTls": "STARTTLS", + "ImapConnectionSecurity_Auto": "Auto", + "Justify": "Justify", + "Left": "Left", + "Link": "Link", + "LinkedAccountsCreatePolicyMessage": "you must have at least 2 accounts to create link\nlink will be removed on save", + "LinkedAccountsTitle": "Linked Accounts", + "MailOperation_AlwaysMoveFocused": "Always Move to Focused", + "MailOperation_AlwaysMoveOther": "Always Move to Other", + "MailOperation_Archive": "Archive", + "MailOperation_ClearFlag": "Clear flag", + "MailOperation_DarkEditor": "Dark", + "MailOperation_Delete": "Delete", + "MailOperation_ExportPDF": "Export to PDF", + "MailOperation_Find": "Find", + "MailOperation_Forward": "Forward", + "MailOperation_Ignore": "Ignore", + "MailOperation_LightEditor": "Light", + "MailOperation_MarkAsJunk": "Mark as junk", + "MailOperation_MarkAsRead": "Mark as read", + "MailOperation_MarkAsUnread": "Mark as unread", + "MailOperation_MarkNotJunk": "Mark as Not Junk", + "MailOperation_Move": "Move", + "MailOperation_MoveFocused": "Move to Focused", + "MailOperation_MoveJunk": "Move to Junk", + "MailOperation_MoveOther": "Move to Other", + "MailOperation_Navigate": "Navigate", + "MailOperation_Print": "Print", + "MailOperation_Reply": "Reply", + "MailOperation_ReplyAll": "Reply all", + "MailOperation_SaveAs": "Save As", + "MailOperation_SetFlag": "Set flag", + "MailOperation_Unarchive": "Unarchive", + "MailOperation_Zoom": "Zoom", + "MailsSelected": "{0} item(s) selected", + "MarkFlagUnflag": "Mark as flagged/unflagged", + "MarkReadUnread": "Mark as read/unread", + "MenuManageAccounts": "Manage Accounts", + "MenuNewMail": "New Mail", + "MenuMergedAccountItemAccountsSuffix": " accounts", + "MenuRate": "Rate Wino", + "MenuSettings": "Settings", + "MergedAccountsAvailableAccountsTitle": "Available Accounts", + "More": "More", + "MoveMailDialog_InvalidFolderMessage": "{0} is not a valid folder for this mail.", + "MoveMailDialog_Title": "Pick a folder", + "NewAccountDialog_AccountName": "Account Name", + "NewAccountDialog_AccountNameDefaultValue": "Personal", + "NewAccountDialog_AccountNamePlaceholder": "eg. Personal Mail", + "NewAccountDialog_Title": "Add New Account", + "NoMailSelected": "no message selected", + "NoMessageCrieteria": "no messages match your search criteria.", + "NoMessageEmptyFolder": "this folder is empty.", + "Notifications_MultipleNotificationsMessage": "You have {0} new mails", + "Notifications_MultipleNotificationsTitle": "New Mails", + "Notifications_WinoUpdatedMessage": "Checkout new version {0}", + "Notifications_WinoUpdatedTitle": "Wino Mail has been updated.", + "Other": "Other", + "PaneLengthOption_Default": "Default", + "PaneLengthOption_ExtraLarge": "Extra Large", + "PaneLengthOption_Large": "Large", + "PaneLengthOption_Medium": "Medium", + "PaneLengthOption_Micro": "Micro", + "PaneLengthOption_Small": "Small", + "Photos": "Photos", + "PreparingFoldersMessage": "Preparing folders", + "ProviderDetail_Gmail_Description": "Google Account", + "ProviderDetail_IMAP_Description": "Custom IMAP/SMTP server", + "ProviderDetail_IMAP_Title": "IMAP Server", + "Results": "Results", + "Right": "Right", + "SynchronizationFolderReport_Success": "up to date", + "SynchronizationFolderReport_Failed": "synchronization is failed", + "SearchBarPlaceholder": "search", + "SearchingIn": "searching in", + "SettingsAboutGithub_Description": "Go to issue tracker GitHub repository.", + "SettingsAboutGithub_Title": "GitHub", + "SettingsAccountManagementAppendMessage_Title": "Append messages to Sent folder", + "SettingsAccountManagementAppendMessage_Description": "Create a copy of the message in Sent folder after the draft is sent. Enable this if you don't see your mails after you sent them in Sent folder.", + "SettingsEditLinkedInbox_Title": "Edit Linked Inbox", + "SettingsEditLinkedInbox_Description": "Add / remove accounts, rename or break the link between accounts.", + "SettingsAboutVersion": "Version ", + "SettingsAboutWinoDescription": "Lightweight mail client for Windows device families.", + "SettingsAbout_Description": "Learn more about Wino.", + "SettingsAbout_Title": "About", + "SettingsAccentColor_Description": "Change application's accent color", + "SettingsAccentColor_Title": "Accent Color", + "SettingsAccentColor_UseWindowsAccentColor": "Use my Windows accent color", + "SettingsAccountName_Description": "Change the name of the account.", + "SettingsAccountName_Title": "Account Name", + "SettingsApplicationTheme_Description": "Personalize Wino with different custom application themes for your like.", + "SettingsApplicationTheme_Title": "Application Theme", + "SettingsAvailableThemes_Description": "Select a theme from Wino's own collection for your taste or apply your own themes.", + "SettingsAvailableThemes_Title": "Available Themes", + "SettingsCustomTheme_Description": "Create your own custom theme with custom wallpaper and accent color.", + "SettingsCustomTheme_Title": "Custom Theme", + "SettingsDeleteAccount_Description": "Delete all e-mails and credentials associated with this account.", + "SettingsDeleteAccount_Title": "Delete this account", + "SettingsDeleteProtection_Description": "Should Wino ask you for comfirmation every time you try to permanently delete a mail using Shift + Del keys?", + "SettingsDeleteProtection_Title": "Permanent Delete Protection", + "SettingsDiagnostics_Description": "For developers", + "SettingsDiagnostics_Title": "Diagnostics", + "SettingsDiscord_Description": "Get regular development updates, join roadmap discussions and provide feedback.", + "SettingsDiscord_Title": "Discord Channel", + "SettingsElementThemeSelectionDisabled": "Element theme selection is disabled when application theme is selected other than Default.", + "SettingsElementTheme_Description": "Select a Windows theme for Wino", + "SettingsElementTheme_Title": "Element Theme", + "SettingsEnableHoverActions_Title": "Enable hover actions", + "SettingsEnableIMAPLogs_Description": "Enable this to provide details about IMAP connectivity issuses you had during IMAP server setup.", + "SettingsEnableIMAPLogs_Title": "Enable IMAP Protocol Logs", + "SettingsEnableLogs_Description": "I might need logs for crashes to diagnose issues you have opened in GitHub. None of the logs will expose your credentials or sensetive information to public.", + "SettingsEnableLogs_Title": "Enable Logs", + "SettingsEnableSignature": "Enable Signature", + "SettingsExpandOnStartup_Description": "Set whether Wino should expand this account's folders on startup.", + "SettingsExpandOnStartup_Title": "Expand Menu on Startup", + "SettingsExternalContent_Description": "Manage external content settings when rendering mails.", + "SettingsExternalContent_Title": "External Content", + "SettingsFocusedInbox_Description": "Set whether Inbox should be split into two as Focused - Other.", + "SettingsFocusedInbox_Title": "Focused Inbox", + "SettingsFolderSync_Description": "Enable or disable specific folders for synchronization.", + "SettingsFolderSync_Title": "Folder Synchronization", + "SettingsFolderOptions_Title": "Folder Configuration", + "SettingsFolderOptions_Description": "Change individual folder settings like enable/disable sync or show/hide unread badge.", + "SettingsHoverActionCenter": "Center Action", + "SettingsHoverActionLeft": "Left Action", + "SettingsHoverActionRight": "Right Action", + "SettingsHoverActions_Description": "Select 3 actions to show up when you hover over the mails with cursor.", + "SettingsHoverActions_Title": "Hover Actions", + "SettingsLanguage_Description": "Change display language for Wino.", + "SettingsLanguage_Title": "Display Language", + "CategoriesFolderNameOverride": "Categories", + "MoreFolderNameOverride": "More", + "SettingsOptions_Title": "Settings", + "SettingsLinkAccounts_Description": "Merge multiple accounts into one. See mails from one Inbox together.", + "SettingsLinkAccounts_Title": "Create Linked Accounts", + "SettingsLinkedAccountsSave_Description": "Modify the current link with the new accounts.", + "SettingsLinkedAccountsSave_Title": "Save Changes", + "SettingsLoadImages_Title": "Load images automatically", + "SettingsLoadStyles_Title": "Load styles automatically", + "SettingsMailSpacing_Description": "Adjust the spacing for listing mails.", + "SettingsMailSpacing_Title": "Mail Spacing", + "SettingsFolderMenuStyle_Title": "Create Nested Folders", + "SettingsFolderMenuStyle_Description": "Change whether account folders should be nested inside an account menu item or not. Toggle this off if you like the old menu system in Windows Mail", + "SettingsManageAccountSettings_Description": "Notifications, signatures, synchronization and other settings per account.", + "SettingsManageAccountSettings_Title": "Manage Account Settings", + "SettingsManageLink_Description": "Move items to add new link or remove existing link.", + "SettingsManageLink_Title": "Manage Link", + "SettingsMarkAsRead_Description": "Change what should happen to the selected item.", + "SettingsMarkAsRead_DontChange": "Don't automatically mark item as read", + "SettingsMarkAsRead_SecondsToWait": "Seconds to wait: ", + "SettingsMarkAsRead_Timer": "When viewed in the reading pane", + "SettingsMarkAsRead_Title": "Mark item as read", + "SettingsMarkAsRead_WhenSelected": "When selected", + "SettingsMessageList_Description": "Change how your messages should be organized in mail list.", + "SettingsMessageList_Title": "Message List", + "SettingsNoAccountSetupMessage": "You didn't setup any accounts yet.", + "SettingsNotifications_Description": "Turn on or off notifications for this account.", + "SettingsNotifications_Title": "Notifications", + "SettingsPaneLength_Description": "Change the width of the mail list.", + "SettingsPaneLength_Title": "Mail List Pane Length", + "SettingsPaypal_Description": "Show much more love ❤️ All donations are appreciated.", + "SettingsPaypal_Title": "Donate via PayPal", + "SettingsPersonalizationMailDisplayCompactMode": "Compact Mode", + "SettingsPersonalizationMailDisplayMediumMode": "Medium Mode", + "SettingsPersonalizationMailDisplaySpaciousMode": "Spacious Mode", + "SettingsPersonalization_Description": "Change appearance of Wino as you like.", + "SettingsPersonalization_Title": "Personalization", + "SettingsPrivacyPolicy_Description": "Review privacy policy.", + "SettingsPrivacyPolicy_Title": "Privacy Policy", + "SettingsReadingPane_Description": "Mail rendering options.", + "SettingsReadingPane_Title": "Reading Pane", + "SettingsReaderFont_Title": "Default Reader Font", + "SettingsReaderFontFamily_Description": "Change the default font family and font size for reading mails.", + "SettingsFontFamily_Title": "Font Family", + "SettingsFontSize_Title": "Font Size", + "SettingsFontPreview_Title": "Preview", + "SettingsComposerFont_Title": "Default Composer Font", + "SettingsComposerFontFamily_Description": "Change the default font family and font size for composing mails.", + "SettingsRenameMergeAccount_Description": "Change the display name of the linked accounts.", + "SettingsRenameMergeAccount_Title": "Rename", + "SettingsSemanticZoom_Description": "This will allow you to click on the headers in messages list and go to specific date", + "SettingsSemanticZoom_Title": "Semantic Zoom for Date Headers", + "SettingsShowPreviewText_Description": "Hide/show thepreview text.", + "SettingsShowPreviewText_Title": "Show Preview Text", + "SettingsShowSenderPictures_Description": "Hide/show the thumbnail sender pictures.", + "SettingsShowSenderPictures_Title": "Show Sender Avatars", + "SettingsPrefer24HourClock_Title": "Display Clock Format in 24 Hours", + "SettingsPrefer24HourClock_Description": "Mail recieve times will be displayed in 24 hour format instead of 12 (AM/PM)", + "SettingsSignature_Description": "Edit or remove account signature", + "SettingsSignature_Title": "Signature", + "SettingsStartupItem_Description": "Primary account item to load Inbox at startup.", + "SettingsStartupItem_Title": "Startup Item", + "SettingsStore_Description": "Show some love ❤️", + "SettingsStore_Title": "Rate in Store", + "SettingsThreads_Description": "Organize messages into conversation threads.", + "SettingsThreads_Title": "Conversation Threading", + "SettingsUnlinkAccounts_Description": "Remove the link between accounts. This will not delete your accounts.", + "SettingsUnlinkAccounts_Title": "Unlink Accounts", + "SortingOption_Date": "by date", + "SortingOption_Name": "by name", + "StoreRatingDialog_MessageFirstLine": "All feedbacks are appreciated and they will make much Wino better in the future. Would you like to rate Wino in Microsoft Store?", + "StoreRatingDialog_MessageSecondLine": "Would you like to rate Wino Mail in Microsoft Store?", + "StoreRatingDialog_Title": "Enjoying Wino?", + "SystemFolderConfigDialog_ArchiveFolderDescription": "Archived messages will be moved to here.", + "SystemFolderConfigDialog_ArchiveFolderHeader": "Archive Folder", + "SystemFolderConfigDialog_DeletedFolderDescription": "Deleted messages will be moved to here.", + "SystemFolderConfigDialog_DeletedFolderHeader": "Deleted Folder", + "SystemFolderConfigDialog_DraftFolderDescription": "New mails/replies will be crafted in here.", + "SystemFolderConfigDialog_DraftFolderHeader": "Draft Folder", + "SystemFolderConfigDialog_JunkFolderDescription": "All spam/junk mails will be here.", + "SystemFolderConfigDialog_JunkFolderHeader": "Junk/Spam Folder", + "SystemFolderConfigDialog_MessageFirstLine": "This IMAP server doesn't support SPECIAL-USE extension hence Wino couldn't setup the system folders properly.", + "SystemFolderConfigDialog_MessageSecondLine": "Please select the appropriate folders for specific functionalities.", + "SystemFolderConfigDialog_SentFolderDescription": "Folder that sent messages will be stored.", + "SystemFolderConfigDialog_SentFolderHeader": "Sent Folder", + "SystemFolderConfigDialog_Title": "Configure System Folders", + "TestingImapConnectionMessage": "Testing server connection...", + "Today": "Today", + "UnknownAddress": "unknown address", + "UnknownDateHeader": "Unknown Date", + "UnknownGroupAddress": "unknown Mail Group Address", + "UnknownSender": "Unknown Sender", + "Unsubscribe": "Unsubscribe", + "ViewContactDetails": "View Details", + "WinoUpgradeDescription": "Wino offers 3 accounts to start with for free. If you need more than 3 accounts, please upgrade", + "WinoUpgradeMessage": "Upgrade to Unlimited Accounts", + "WinoUpgradeRemainingAccountsMessage": "{0} out of {1} free accounts used.", + "Yesterday": "Yesterday" +} diff --git a/Wino.Core.Domain/Translations/zh_CN/resources.json b/Wino.Core.Domain/Translations/zh_CN/resources.json new file mode 100644 index 00000000..a0968cad --- /dev/null +++ b/Wino.Core.Domain/Translations/zh_CN/resources.json @@ -0,0 +1,468 @@ +{ + "AccountCreationDialog_Completed": "已完成", + "AccountCreationDialog_Initializing": "正在初始化", + "AccountCreationDialog_PreparingFolders": "我们正在获取文件夹信息。", + "AccountCreationDialog_SigninIn": "正在保存账户信息。", + "AccountEditDialog_Message": "账户名称", + "AccountEditDialog_Title": "编辑账户", + "AccountPickerDialog_Title": "选择账户", + "AddHyperlink": "添加", + "AutoDiscoveryProgressMessage": "正在搜索邮件设置…", + "BasicIMAPSetupDialog_AdvancedConfiguration": "高级设置", + "BasicIMAPSetupDialog_CredentialLocalMessage": "您的凭据将仅存储在您的计算机上。", + "BasicIMAPSetupDialog_Description": "部分账户需要额外的登录步骤", + "BasicIMAPSetupDialog_DisplayName": "显示名称", + "BasicIMAPSetupDialog_DisplayNamePlaceholder": "例如 John Doe", + "BasicIMAPSetupDialog_LearnMore": "了解更多", + "BasicIMAPSetupDialog_MailAddress": "电子邮件地址", + "BasicIMAPSetupDialog_MailAddressPlaceholder": "johndoe@fabrikam.com", + "BasicIMAPSetupDialog_Password": "密码", + "BasicIMAPSetupDialog_Title": "IMAP 账户", + "Buttons_AddAccount": "添加账户", + "Buttons_ApplyTheme": "应用主题", + "Buttons_Browse": "浏览", + "Buttons_Cancel": "取消", + "Buttons_Close": "关闭", + "Buttons_Create": "创建", + "Buttons_CreateAccount": "添加账户", + "Buttons_Delete": "删除", + "Buttons_Discard": "放弃", + "Buttons_EnableImageRendering": "启用", + "Buttons_No": "否", + "Buttons_Open": "打开", + "Buttons_Purchase": "购买", + "Buttons_RateWino": "评价 Wino", + "Buttons_Save": "保存", + "Buttons_SaveConfiguration": "保存设置", + "Buttons_Share": "分享", + "Buttons_SignIn": "登录", + "Buttons_Yes": "是", + "Center": "中心", + "ComingSoon": "即将到来...", + "ComposerFrom": "来自: ", + "ComposerSubject": "主题: ", + "ComposerTo": "至: ", + "ClipboardTextCopied_Message": "{0} copied to clipboard.", + "ClipboardTextCopied_Title": "Copied", + "ClipboardTextCopyFailed_Message": "Failed to copy {0} to clipboard.", + "ComposerToPlaceholder": "点击输入地址", + "CustomThemeBuilder_AccentColorDescription": "设置自定义颜色。若未选择颜色,将使用 Windows 默认颜色。", + "CustomThemeBuilder_AccentColorTitle": "主题色", + "CustomThemeBuilder_PickColor": "选择", + "CustomThemeBuilder_ThemeNameDescription": "自定义主题的名称。", + "CustomThemeBuilder_ThemeNameTitle": "主题名称", + "CustomThemeBuilder_Title": "自定义主题生成器", + "CustomThemeBuilder_WallpaperDescription": "为Wino设置自定义壁纸", + "CustomThemeBuilder_WallpaperTitle": "设置自定义壁纸", + "DialogMessage_AccountLimitMessage": "您已达到免费用户可创建账户数量上限(3个)。\n是否购买支持作者以“解除可创建账户数量上限”?", + "DialogMessage_AccountLimitTitle": "已达到账户限制", + "DialogMessage_CleanupFolderMessage": "您想永久删除此文件夹中的所有邮件吗?", + "DialogMessage_CleanupFolderTitle": "清空文件夹", + "DialogMessage_ComposerMissingRecipientMessage": "邮件没有收件人。", + "DialogMessage_ComposerValidationFailedTitle": "验证失败", + "DialogMessage_CreateLinkedAccountMessage": "给这个新链接命名。账户将在此名称下合并。", + "DialogMessage_CreateLinkedAccountTitle": "账户链接名称", + "DialogMessage_DeleteAccountConfirmationMessage": "删除 {0}?", + "DialogMessage_DeleteAccountConfirmationTitle": "与此账户相关的所有数据将从磁盘上永久删除。", + "DialogMessage_DiscardDraftConfirmationMessage": "草稿将被删除。你想要继续吗?", + "DialogMessage_DiscardDraftConfirmationTitle": "舍弃草稿", + "DialogMessage_HardDeleteConfirmationMessage": "永久删除", + "DialogMessage_HardDeleteConfirmationTitle": "邮件将被永久删除。是否继续?", + "DialogMessage_NoAccountsForCreateMailMessage": "您没有任何账户可创建邮件。", + "DialogMessage_NoAccountsForCreateMailTitle": "没有邮件账户", + "DialogMessage_RenameLinkedAccountsMessage": "输入链接账户的新名称", + "DialogMessage_RenameLinkedAccountsTitle": "重命名已链接账户", + "DialogMessage_UnlinkAccountsConfirmationMessage": "此操作不会删除您的账户,只会断开共享文件夹的连接。是否继续?", + "DialogMessage_UnlinkAccountsConfirmationTitle": "取消链接账户", + "DialogMessage_EmptySubjectConfirmation": "缺少主题", + "DialogMessage_EmptySubjectConfirmationMessage": "邮件没有主题。您想要继续吗?", + "Dialog_DontAskAgain": "不再询问", + "DiscordChannelDisclaimerMessage": "Wino 没有自己的 Discord 服务器,但在 Developer Sanctuary 服务器上设有专门的 wino-mail 频道。如果要获取有关 Wino 的更新,请加入 Developer Sanctuary 服务器,并关注 Community Projects下的 wino-mail 频道。\n\n因为 Discord 不支持频道邀请,所以你需要通过网页加入频道。", + "DiscordChannelDisclaimerTitle": "重要的 Discord 信息", + "Draft": "草稿", + "EditorToolbarOption_Draw": "绘制", + "EditorToolbarOption_Format": "格式", + "EditorToolbarOption_Insert": "插入", + "EditorToolbarOption_None": "无", + "EditorToolbarOption_Options": "选项", + "ElementTheme_Dark": "深色模式", + "ElementTheme_Default": "使用系统设置", + "ElementTheme_Light": "浅色模式", + "Emoji": "表情", + "Exception_ImapClientPoolFailed": "IMAP 客户端池失败。", + "Exception_AuthenticationCanceled": "身份验证已取消", + "Exception_CustomThemeExists": "此主题已经存在。", + "Exception_CustomThemeMissingName": "您必须提供名称。", + "Exception_CustomThemeMissingWallpaper": "您必须提供自定义背景图像。", + "Exception_FailedToSynchronizeFolders": "同步文件夹失败", + "Exception_GoogleAuthCallbackNull": "Callback uri 在激活时无效。", + "Exception_GoogleAuthCorruptedCode": "授权响应不正确。", + "Exception_GoogleAuthError": "OAuth 授权错误: {0}", + "Exception_GoogleAuthInvalidResponse": "收到无效状态请求 ({0})", + "Exception_GoogleAuthorizationCodeExchangeFailed": "授权代码交换失败。", + "Exception_InvalidSystemFolderConfiguration": "系统文件夹配置无效。请检查配置,然后重试。", + "Exception_NullAssignedAccount": "分配的账户为空", + "Exception_NullAssignedFolder": "分配的文件夹为空", + "Exception_SynchronizerFailureHTTP": "处理响应失败,HTTP 错误代码: {0}", + "Exception_TokenGenerationFailed": "令牌生成失败", + "Exception_TokenInfoRetrivalFailed": "获取令牌信息失败。", + "Exception_UnknowErrorDuringAuthentication": "身份验证时发生未知错误", + "Exception_UnsupportedAction": "请求处理器不支持 {0} 操作。", + "Exception_UnsupportedProvider": "不支持该提供商。", + "Exception_UnsupportedSynchronizerOperation": "{0} 不支持此操作", + "Exception_UserCancelSystemFolderSetupDialog": "用户取消了配置系统文件夹的对话框。", + "Files": "文件", + "FilteringOption_All": "全选", + "FilteringOption_Flagged": "已标记", + "FilteringOption_Unread": "未读", + "Focused": "重点", + "FolderOperation_CreateSubFolder": "创建子文件夹", + "FolderOperation_Delete": "删除", + "FolderOperation_DontSync": "不要同步此文件夹", + "FolderOperation_Empty": "清空此文件夹", + "FolderOperation_MarkAllAsRead": "全部标记为已读", + "FolderOperation_Move": "移动", + "DragMoveToFolderCaption": "移动到 {0}", + "FolderOperation_None": "无", + "FolderOperation_Pin": "置顶", + "FolderOperation_Rename": "重命名", + "FolderOperation_Unpin": "取消置顶", + "HoverActionOption_Archive": "归档", + "HoverActionOption_Delete": "删除", + "HoverActionOption_MoveJunk": "移至垃圾箱", + "HoverActionOption_ToggleFlag": "标记/取消标记", + "HoverActionOption_ToggleRead": "已读/未读", + "MergedAccountCommonFolderInbox": "收件箱", + "MergedAccountCommonFolderSent": "已发送", + "MergedAccountCommonFolderDraft": "草稿箱", + "MergedAccountCommonFolderJunk": "垃圾箱", + "MergedAccountCommonFolderTrash": "已删除", + "MergedAccountCommonFolderArchive": "归档", + "IMAPSetupDialog_AccountType": "账户名称", + "IMAPSetupDialog_DisplayName": "显示名称", + "IMAPSetupDialog_DisplayNamePlaceholder": "例如 John Doe", + "IMAPSetupDialog_IncomingMailServer": "收件邮件服务器", + "IMAPSetupDialog_IncomingMailServerPort": "端口", + "IMAPSetupDialog_MailAddress": "电子邮件地址", + "IMAPSetupDialog_MailAddressPlaceholder": "somone@example.com", + "IMAPSetupDialog_OutgoingMailServer": "发送 (SMTP) 邮件服务器", + "IMAPSetupDialog_OutgoingMailServerPassword": "发送服务器密码", + "IMAPSetupDialog_OutgoingMailServerPort": "端口", + "IMAPSetupDialog_OutgoingMailServerRequireAuthentication": "发送服务器需要身份验证", + "IMAPSetupDialog_OutgoingMailServerUsername": "发送服务器用户名", + "IMAPSetupDialog_Password": "密码", + "IMAPSetupDialog_RequireSSLForIncomingMail": "接收邮件需要 SSL", + "IMAPSetupDialog_RequireSSLForOutgoingMail": "接收邮件需要 SSL", + "IMAPSetupDialog_Title": "IMAP 高级选项", + "IMAPSetupDialog_UseSameConfig": "发送电子邮件时使用同一用户名和密码", + "IMAPSetupDialog_Username": "用户名", + "IMAPSetupDialog_UsernamePlaceholder": "示例:johndoe, johndoe@fabrikam.com, domain/johndoe", + "ImageRenderingDisabled": "此邮件的图像渲染已禁用。", + "InfoBarAction_Enable": "启用", + "InfoBarMessage_SynchronizationDisabledFolder": "此文件夹已禁用同步。", + "InfoBarTitle_SynchronizationDisabledFolder": "已禁用文件夹", + "GeneralTitle_Error": "Error", + "GeneralTitle_Warning": "Warning", + "GeneralTitle_Info": "Information", + "Info_AccountCreatedMessage": "{0} 已创建", + "Info_AccountCreatedTitle": "创建账户", + "Info_AccountCreationFailedTitle": "账户创建失败", + "Info_AccountDeletedMessage": "{0} 已成功删除。", + "Info_AccountDeletedTitle": "账户已删除", + "Info_AccountIssueFixFailedTitle": "失败", + "Info_AccountIssueFixSuccessMessage": "已修复所有账户问题。", + "Info_AccountIssueFixSuccessTitle": "成功", + "Info_AttachmentOpenFailedMessage": "无法打开此附件。", + "Info_AttachmentOpenFailedTitle": "失败", + "Info_AttachmentSaveFailedMessage": "无法保存此附件。", + "Info_AttachmentSaveFailedTitle": "失败", + "Info_AttachmentSaveSuccessMessage": "附件已保存。", + "Info_AttachmentSaveSuccessTitle": "附件已保存", + "Info_BackgroundExecutionDeniedMessage": "应用的后台运行被禁止。这可能会影响后台同步和实时通知。", + "Info_BackgroundExecutionDeniedTitle": "后台执行被拒绝", + "Info_BackgroundExecutionUnknownErrorMessage": "注册后台同步器时发生未知异常。", + "Info_BackgroundExecutionUnknownErrorTitle": "后台执行失败", + "Info_ComposerMissingMIMEMessage": "无法找到 MIME 文件。同步可能有帮助。", + "Info_ComposerMissingMIMETitle": "失败", + "Info_ContactExistsMessage": "此联系人已经在收件人列表中。", + "Info_ContactExistsTitle": "联系人已存在", + "Info_DraftFolderMissingMessage": "此账户缺少草稿文件夹。请检查您的账户设置。", + "Info_DraftFolderMissingTitle": "缺少草稿文件夹", + "Info_FileLaunchFailedTitle": "无法启动文件", + "Info_InvalidAddressMessage": "{0} 不是有效的电子邮件地址。", + "Info_InvalidAddressTitle": "无效地址", + "Info_InvalidMoveTargetMessage": "您不能将选中的邮件移动到此文件夹。", + "Info_InvalidMoveTargetTitle": "无效的移动目标", + "Info_LogsNotFoundMessage": "没有可共享的日志。", + "Info_LogsNotFoundTitle": "未找到日志", + "Info_LogsSavedMessage": "{0} 已保存到所选文件夹。", + "Info_LogsSavedTitle": "已保存", + "Info_MailRenderingFailedMessage": "这封邮件已损坏或无法打开。\n{0}", + "Info_MailRenderingFailedTitle": "渲染失败", + "Info_MessageCorruptedMessage": "此邮件已损坏。", + "Info_MessageCorruptedTitle": "错误", + "Info_MissingFolderMessage": "此账户不存在 {0}。", + "Info_MissingFolderTitle": "缺少文件夹", + "Info_PurchaseExistsMessage": "看起来这个产品已经被购买了。", + "Info_PurchaseExistsTitle": "产品已购买", + "Info_PurchaseThankYouMessage": "感谢您", + "Info_PurchaseThankYouTitle": "购买成功", + "Info_RequestCreationFailedTitle": "请求创建失败", + "Info_ReviewNetworkErrorMessage": "您的评论有一个网络问题。", + "Info_ReviewNetworkErrorTitle": "网络问题", + "Info_ReviewNewMessage": "感谢所有反馈。谢谢您的评论!", + "Info_ReviewSuccessTitle": "谢谢", + "Info_ReviewUnknownErrorMessage": "您的评论有一个未知的问题。({0})", + "Info_ReviewUnknownErrorTitle": "未知错误", + "Info_ReviewUpdatedMessage": "感谢您的最新评论。", + "Info_SignatureDisabledMessage": "已禁用此账户的签名", + "Info_SignatureDisabledTitle": "成功", + "Info_SignatureSavedMessage": "已保存新签名", + "Info_SignatureSavedTitle": "成功", + "Info_SyncCanceledMessage": "已取消", + "Info_SyncCanceledTitle": "同步", + "Info_SyncFailedTitle": "同步失败", + "Info_UnsupportedFunctionalityDescription": "此功能尚未实现。", + "Info_UnsupportedFunctionalityTitle": "不支持", + "Info_UnsubscribeLinkInvalidTitle": "无效的取消订阅 Uri", + "Info_UnsubscribeLinkInvalidMessage": "此退订链接无效。取消订阅列表失败。", + "ImapAdvancedSetupDialog_AuthenticationMethod": "身份验证方法", + "ImapAdvancedSetupDialog_ConnectionSecurity": "连接安全性", + "ImapAuthenticationMethod_Auto": "自动", + "ImapAuthenticationMethod_CramMD5": "CRAM-MD5", + "ImapAuthenticationMethod_DigestMD5": "DIGEST-MD5", + "ImapAuthenticationMethod_None": "无身份验证", + "ImapAuthenticationMethod_Plain": "常规密码", + "ImapAuthenticationMethod_EncryptedPassword": "加密密码", + "ImapAuthenticationMethod_Ntlm": "NTLM", + "ImapConnectionSecurity_None": "无", + "ImapConnectionSecurity_SslTls": "SSL/TLS", + "ImapConnectionSecurity_StartTls": "STARTTLS", + "ImapConnectionSecurity_Auto": "自动", + "Justify": "对齐", + "Left": "左侧", + "Link": "链接", + "LinkedAccountsCreatePolicyMessage": "您必须拥有至少 2 个账户才能创建链接\n链接将在保存时删除", + "LinkedAccountsTitle": "已绑定的账户", + "MailOperation_AlwaysMoveFocused": "总是移动到重点。", + "MailOperation_AlwaysMoveOther": "总是移动到其他", + "MailOperation_Archive": "归档", + "MailOperation_ClearFlag": "清除标记", + "MailOperation_DarkEditor": "深色", + "MailOperation_Delete": "删除", + "MailOperation_ExportPDF": "导出为 PDF", + "MailOperation_Find": "搜索", + "MailOperation_Forward": "转发", + "MailOperation_Ignore": "忽略", + "MailOperation_LightEditor": "浅色", + "MailOperation_MarkAsJunk": "标记为垃圾邮件", + "MailOperation_MarkAsRead": "标记为已读", + "MailOperation_MarkAsUnread": "标记为未读", + "MailOperation_MarkNotJunk": "标记为非垃圾邮件", + "MailOperation_Move": "移动", + "MailOperation_MoveFocused": "移动到重点", + "MailOperation_MoveJunk": "移至垃圾箱", + "MailOperation_MoveOther": "移至其他", + "MailOperation_Navigate": "导航", + "MailOperation_Print": "打印", + "MailOperation_Reply": "回复", + "MailOperation_ReplyAll": "回复所有", + "MailOperation_SaveAs": "另存为", + "MailOperation_SetFlag": "设置标记", + "MailOperation_Unarchive": "取消归档", + "MailOperation_Zoom": "缩放", + "MailsSelected": "已选择 {0} 项", + "MarkFlagUnflag": "标记为已标记/未标记", + "MarkReadUnread": "标记为已读/未读", + "MenuManageAccounts": "管理账户", + "MenuNewMail": "新邮件", + "MenuMergedAccountItemAccountsSuffix": " 账户", + "MenuRate": "评价 Wino", + "MenuSettings": "设置", + "MergedAccountsAvailableAccountsTitle": "可用账户", + "More": "更多", + "MoveMailDialog_InvalidFolderMessage": "{0} 不是此邮件的有效文件夹。", + "MoveMailDialog_Title": "选择文件夹", + "NewAccountDialog_AccountName": "账户名称", + "NewAccountDialog_AccountNameDefaultValue": "个人信息", + "NewAccountDialog_AccountNamePlaceholder": "例如:个人邮件", + "NewAccountDialog_Title": "添加新账户", + "NoMailSelected": "未选择任何邮件", + "NoMessageCrieteria": "没有符合您搜索条件的邮件。", + "NoMessageEmptyFolder": "此文件夹为空。", + "Notifications_MultipleNotificationsMessage": "您有 {0} 条新邮件", + "Notifications_MultipleNotificationsTitle": "新邮件", + "Notifications_WinoUpdatedMessage": "检查新版本 {0}", + "Notifications_WinoUpdatedTitle": "Wino 邮件已更新。", + "Other": "其他", + "PaneLengthOption_Default": "默认", + "PaneLengthOption_ExtraLarge": "超大", + "PaneLengthOption_Large": "大", + "PaneLengthOption_Medium": "中", + "PaneLengthOption_Micro": "极小", + "PaneLengthOption_Small": "小", + "Photos": "图片", + "PreparingFoldersMessage": "正在准备文件夹", + "ProviderDetail_Gmail_Description": "Google 账号", + "ProviderDetail_IMAP_Description": "自定义 IMAP/SMTP 服务器", + "ProviderDetail_IMAP_Title": "IMAP 服务器", + "Results": "结果", + "Right": "右侧", + "SynchronizationFolderReport_Success": "已是最新", + "SynchronizationFolderReport_Failed": "同步失败", + "SearchBarPlaceholder": "搜索", + "SearchingIn": "搜索于", + "SettingsAboutGithub_Description": "转到 GitHub 仓库的问题追踪器", + "SettingsAboutGithub_Title": "GitHub", + "SettingsAccountManagementAppendMessage_Title": "添加邮件到已发送文件夹", + "SettingsAccountManagementAppendMessage_Description": "在草稿发送后在发送文件夹中创建邮件的副本。 如果您在发送文件夹中没有看到您的邮件,请启用此功能。", + "SettingsEditLinkedInbox_Title": "编辑已链接收件箱", + "SettingsEditLinkedInbox_Description": "添加/删除账户,重命名或断开账户之间的链接。", + "SettingsAboutVersion": "版本 ", + "SettingsAboutWinoDescription": "Windows 系列设备的轻量邮件客户端。", + "SettingsAbout_Description": "了解更多关于 Wino 的信息。", + "SettingsAbout_Title": "关于", + "SettingsAccentColor_Description": "更改应用的主题颜色", + "SettingsAccentColor_Title": "主题颜色", + "SettingsAccentColor_UseWindowsAccentColor": "使用 Windows 主题色", + "SettingsAccountName_Description": "更改账户名称。", + "SettingsAccountName_Title": "账户名称", + "SettingsApplicationTheme_Description": "根据你的喜好,用不同的自定义应用主题来个性化 Wino。", + "SettingsApplicationTheme_Title": "应用主题", + "SettingsAvailableThemes_Description": "从 Wino 的自带收藏中选择一个符合您口味的主题,或应用您自己的主题。", + "SettingsAvailableThemes_Title": "可用的主题", + "SettingsCustomTheme_Description": "用自定义壁纸和主题色创建您自己的自定义主题。", + "SettingsCustomTheme_Title": "自定义主题", + "SettingsDeleteAccount_Description": "删除与此账户关联的所有电子邮件和凭据。", + "SettingsDeleteAccount_Title": "删除此账户", + "SettingsDeleteProtection_Description": "每次您尝试使用 Shift + Del 键永久删除邮件时,Wino 应该向您二次确认吗?", + "SettingsDeleteProtection_Title": "永久性删除保护", + "SettingsDiagnostics_Description": "开发者选项", + "SettingsDiagnostics_Title": "诊断信息", + "SettingsDiscord_Description": "获取定期开发更新,加入路线图讨论并提供反馈。", + "SettingsDiscord_Title": "Discord 频道", + "SettingsElementThemeSelectionDisabled": "当选择的应用主题不是默认主题时,元素主题选择将被禁用。", + "SettingsElementTheme_Description": "为 Wino 选择一个 Windows 主题", + "SettingsElementTheme_Title": "元素主题", + "SettingsEnableHoverActions_Title": "启用悬停动作", + "SettingsEnableIMAPLogs_Description": "启用此选项可提供在设置 IMAP 服务器过程中遇到的 IMAP 连接问题的详细信息。", + "SettingsEnableIMAPLogs_Title": "启用 IMAP 协议日志", + "SettingsEnableLogs_Description": "我可能需要日志来分析您在 GitHub 中打开的问题。 所有日志都不会公开您的凭据或敏感信息。", + "SettingsEnableLogs_Title": "启用日志", + "SettingsEnableSignature": "启用签名", + "SettingsExpandOnStartup_Description": "设置 Wino 是否应该在启动时展开此账户的文件夹。", + "SettingsExpandOnStartup_Title": "启动时展开菜单", + "SettingsExternalContent_Description": "在渲染邮件时管理外部内容设置。", + "SettingsExternalContent_Title": "外部内容", + "SettingsFocusedInbox_Description": "设置是否应将收件箱分为两部分 - 重点和其他。", + "SettingsFocusedInbox_Title": "重点收件箱", + "SettingsFolderSync_Description": "启用或禁用特定文件夹同步。", + "SettingsFolderSync_Title": "文件夹同步", + "SettingsFolderOptions_Title": "Folder Configuration", + "SettingsFolderOptions_Description": "Change individual folder settings like enable/disable sync or show/hide unread badge.", + "SettingsHoverActionCenter": "中心动作", + "SettingsHoverActionLeft": "左侧动作", + "SettingsHoverActionRight": "右侧动作", + "SettingsHoverActions_Description": "选择 3 个动作,当你用鼠标悬停在邮件上时显示。", + "SettingsHoverActions_Title": "悬停动作", + "SettingsLanguage_Description": "更改 Wino 的显示语言。", + "SettingsLanguage_Title": "显示语言", + "CategoriesFolderNameOverride": "分类", + "MoreFolderNameOverride": "更多", + "SettingsOptions_Title": "设置", + "SettingsLinkAccounts_Description": "将多个账户合并为一个。在同一个收件箱内查看所有邮件。", + "SettingsLinkAccounts_Title": "创建链接账户", + "SettingsLinkedAccountsSave_Description": "修改当前与新账户的链接。", + "SettingsLinkedAccountsSave_Title": "保存更改", + "SettingsLoadImages_Title": "自动加载图片", + "SettingsLoadStyles_Title": "自动加载样式", + "SettingsMailSpacing_Description": "调整邮件列表的间距。", + "SettingsMailSpacing_Title": "邮件间距", + "SettingsFolderMenuStyle_Title": "创建内部文件夹", + "SettingsFolderMenuStyle_Description": "控制账户文件夹是否应嵌套在账户菜单中。 如果您喜欢Windows邮件中的旧菜单,关闭此选项", + "SettingsManageAccountSettings_Description": "每个账户的通知、签名、同步等设置。", + "SettingsManageAccountSettings_Title": "管理账户设置", + "SettingsManageLink_Description": "移动项目以添加新链接或删除现有链接。", + "SettingsManageLink_Title": "管理账户链接", + "SettingsMarkAsRead_Description": "更改应对选定项目进行何种操作。", + "SettingsMarkAsRead_DontChange": "不自动标记项目为已读", + "SettingsMarkAsRead_SecondsToWait": "等待时间: ", + "SettingsMarkAsRead_Timer": "当在阅读面板中查看时", + "SettingsMarkAsRead_Title": "标记为已读", + "SettingsMarkAsRead_WhenSelected": "选中时", + "SettingsMessageList_Description": "更改邮件列表中的消息应如何组织。", + "SettingsMessageList_Title": "邮件列表", + "SettingsNoAccountSetupMessage": "您尚未设置任何账户。", + "SettingsNotifications_Description": "开启或关闭此账户的通知。", + "SettingsNotifications_Title": "通知", + "SettingsPaneLength_Description": "更改邮件列表的宽度。", + "SettingsPaneLength_Title": "邮件列表视图长度", + "SettingsPaypal_Description": "展示更多的爱吧 ❤️ 我们感激所有的捐赠。", + "SettingsPaypal_Title": "通过 PayPal 捐赠", + "SettingsPersonalizationMailDisplayCompactMode": "紧凑模式", + "SettingsPersonalizationMailDisplayMediumMode": "中等模式", + "SettingsPersonalizationMailDisplaySpaciousMode": "宽敞模式", + "SettingsPersonalization_Description": "按照您喜欢的方式更改 Wino 的外观。", + "SettingsPersonalization_Title": "个性化", + "SettingsPrivacyPolicy_Description": "查看隐私政策。", + "SettingsPrivacyPolicy_Title": "隐私政策", + "SettingsReadingPane_Description": "邮件渲染选项", + "SettingsReadingPane_Title": "阅读面板", + "SettingsReaderFont_Title": "默认阅读字体", + "SettingsReaderFontFamily_Description": "更改阅读邮件的默认字体和字体大小。", + "SettingsFontFamily_Title": "字体", + "SettingsFontSize_Title": "字体大小", + "SettingsFontPreview_Title": "预览", + "SettingsComposerFont_Title": "默认撰写字体", + "SettingsComposerFontFamily_Description": "更改撰写邮件时的默认字体和字体大小。", + "SettingsRenameMergeAccount_Description": "更改链接账户的显示名称。", + "SettingsRenameMergeAccount_Title": "重命名", + "SettingsSemanticZoom_Description": "这将允许您点击消息列表中的标题并转到指定日期", + "SettingsSemanticZoom_Title": "为日期标题启用语义缩放", + "SettingsShowPreviewText_Description": "隐藏/显示预览文本。", + "SettingsShowPreviewText_Title": "显示预览文本", + "SettingsShowSenderPictures_Description": "隐藏/显示缩略图发件人图片。", + "SettingsShowSenderPictures_Title": "显示发件人头像", + "SettingsPrefer24HourClock_Title": "使用 24 小时制时间格式", + "SettingsPrefer24HourClock_Description": "邮件接收时间将显示为 24 小时制,而不是 12 小时制(上午/下午)", + "SettingsSignature_Description": "编辑或删除账户签名", + "SettingsSignature_Title": "签名", + "SettingsStartupItem_Description": "启动时加载收件箱的主要账户项。", + "SettingsStartupItem_Title": "启动项", + "SettingsStore_Description": "展示更多的爱❤️", + "SettingsStore_Title": "在商店中评分", + "SettingsThreads_Description": "将邮件组织成对话主题。", + "SettingsThreads_Title": "邮件会话", + "SettingsUnlinkAccounts_Description": "删除账户之间的链接。这不会删除您的账户。", + "SettingsUnlinkAccounts_Title": "取消链接账户", + "SortingOption_Date": "按日期", + "SortingOption_Name": "按名称", + "StoreRatingDialog_MessageFirstLine": "所有反馈都值得赞赏,它们将来会使 Wino 变得更好。您想要在 Microsoft Store 给 Wino 打分吗?", + "StoreRatingDialog_MessageSecondLine": "您想在 Microsoft Store 中给 Wino Mail 打分吗?", + "StoreRatingDialog_Title": "喜欢 Wino 吗?", + "SystemFolderConfigDialog_ArchiveFolderDescription": "Archived messages will be moved to here.", + "SystemFolderConfigDialog_ArchiveFolderHeader": "Archive Folder", + "SystemFolderConfigDialog_DeletedFolderDescription": "已删除的邮件将被移至此处。", + "SystemFolderConfigDialog_DeletedFolderHeader": "已删除文件夹", + "SystemFolderConfigDialog_DraftFolderDescription": "将在这里撰写新邮件/回复。", + "SystemFolderConfigDialog_DraftFolderHeader": "草稿文件夹", + "SystemFolderConfigDialog_JunkFolderDescription": "所有垃圾邮件都在这里。", + "SystemFolderConfigDialog_JunkFolderHeader": "垃圾邮件文件夹", + "SystemFolderConfigDialog_MessageFirstLine": "此 IMAP 服务器不支持 SPECIAL-USE 扩展,因此 Wino 无法正确设置系统文件夹。", + "SystemFolderConfigDialog_MessageSecondLine": "请为特定功能选择相应的文件夹。", + "SystemFolderConfigDialog_SentFolderDescription": "发送的邮件将被存储在该文件夹中。", + "SystemFolderConfigDialog_SentFolderHeader": "已发送文件夹", + "SystemFolderConfigDialog_Title": "配置系统文件夹", + "TestingImapConnectionMessage": "正在测试服务器连接...", + "Today": "今天", + "UnknownAddress": "未知地址", + "UnknownDateHeader": "未知日期", + "UnknownGroupAddress": "未知邮件群组地址", + "UnknownSender": "未知发件人", + "Unsubscribe": "取消订阅", + "ViewContactDetails": "查看详情", + "WinoUpgradeDescription": "Wino offers 3 accounts to start with for free. If you need more than 3 accounts, please upgrade", + "WinoUpgradeMessage": "升级为无限账户数", + "WinoUpgradeRemainingAccountsMessage": "已使用 {0} 个免费账户,共 {1} 个。", + "Yesterday": "昨天" +} diff --git a/Wino.Core.Domain/Translator.Designer.cs b/Wino.Core.Domain/Translator.Designer.cs new file mode 100644 index 00000000..a858e9c3 --- /dev/null +++ b/Wino.Core.Domain/Translator.Designer.cs @@ -0,0 +1,2916 @@ + + + + + + + + + + + + + + + +namespace Wino.Core.Domain +{ + public class Translator + { + private static global::Wino.Core.Domain.Translations.WinoTranslationDictionary _dictionary; + + public static global::Wino.Core.Domain.Translations.WinoTranslationDictionary Resources + { + get + { + if (_dictionary == null) + { + _dictionary = new global::Wino.Core.Domain.Translations.WinoTranslationDictionary(); + } + + return _dictionary; + } + } + + + /// + /// all done + /// + public static string AccountCreationDialog_Completed => Resources.GetTranslatedString(@"AccountCreationDialog_Completed"); + + + /// + /// initializing + /// + public static string AccountCreationDialog_Initializing => Resources.GetTranslatedString(@"AccountCreationDialog_Initializing"); + + + /// + /// We are getting folder information at the moment. + /// + public static string AccountCreationDialog_PreparingFolders => Resources.GetTranslatedString(@"AccountCreationDialog_PreparingFolders"); + + + /// + /// Account information is being saved. + /// + public static string AccountCreationDialog_SigninIn => Resources.GetTranslatedString(@"AccountCreationDialog_SigninIn"); + + + /// + /// Account Name + /// + public static string AccountEditDialog_Message => Resources.GetTranslatedString(@"AccountEditDialog_Message"); + + + /// + /// Edit Account + /// + public static string AccountEditDialog_Title => Resources.GetTranslatedString(@"AccountEditDialog_Title"); + + + /// + /// Pick an account + /// + public static string AccountPickerDialog_Title => Resources.GetTranslatedString(@"AccountPickerDialog_Title"); + + + /// + /// Add + /// + public static string AddHyperlink => Resources.GetTranslatedString(@"AddHyperlink"); + + + /// + /// Searching for mail settings... + /// + public static string AutoDiscoveryProgressMessage => Resources.GetTranslatedString(@"AutoDiscoveryProgressMessage"); + + + /// + /// Advanced Configuration + /// + public static string BasicIMAPSetupDialog_AdvancedConfiguration => Resources.GetTranslatedString(@"BasicIMAPSetupDialog_AdvancedConfiguration"); + + + /// + /// Your credentials will only be stored locally on your computer. + /// + public static string BasicIMAPSetupDialog_CredentialLocalMessage => Resources.GetTranslatedString(@"BasicIMAPSetupDialog_CredentialLocalMessage"); + + + /// + /// Some accounts require additional steps to sign in + /// + public static string BasicIMAPSetupDialog_Description => Resources.GetTranslatedString(@"BasicIMAPSetupDialog_Description"); + + + /// + /// Display Name + /// + public static string BasicIMAPSetupDialog_DisplayName => Resources.GetTranslatedString(@"BasicIMAPSetupDialog_DisplayName"); + + + /// + /// eg. John Doe + /// + public static string BasicIMAPSetupDialog_DisplayNamePlaceholder => Resources.GetTranslatedString(@"BasicIMAPSetupDialog_DisplayNamePlaceholder"); + + + /// + /// Learn more + /// + public static string BasicIMAPSetupDialog_LearnMore => Resources.GetTranslatedString(@"BasicIMAPSetupDialog_LearnMore"); + + + /// + /// E-Mail Address + /// + public static string BasicIMAPSetupDialog_MailAddress => Resources.GetTranslatedString(@"BasicIMAPSetupDialog_MailAddress"); + + + /// + /// johndoe@fabrikam.com + /// + public static string BasicIMAPSetupDialog_MailAddressPlaceholder => Resources.GetTranslatedString(@"BasicIMAPSetupDialog_MailAddressPlaceholder"); + + + /// + /// Password + /// + public static string BasicIMAPSetupDialog_Password => Resources.GetTranslatedString(@"BasicIMAPSetupDialog_Password"); + + + /// + /// IMAP Account + /// + public static string BasicIMAPSetupDialog_Title => Resources.GetTranslatedString(@"BasicIMAPSetupDialog_Title"); + + + /// + /// Add Account + /// + public static string Buttons_AddAccount => Resources.GetTranslatedString(@"Buttons_AddAccount"); + + + /// + /// Apply Theme + /// + public static string Buttons_ApplyTheme => Resources.GetTranslatedString(@"Buttons_ApplyTheme"); + + + /// + /// Browse + /// + public static string Buttons_Browse => Resources.GetTranslatedString(@"Buttons_Browse"); + + + /// + /// Cancel + /// + public static string Buttons_Cancel => Resources.GetTranslatedString(@"Buttons_Cancel"); + + + /// + /// Close + /// + public static string Buttons_Close => Resources.GetTranslatedString(@"Buttons_Close"); + + + /// + /// Create + /// + public static string Buttons_Create => Resources.GetTranslatedString(@"Buttons_Create"); + + + /// + /// Create Account + /// + public static string Buttons_CreateAccount => Resources.GetTranslatedString(@"Buttons_CreateAccount"); + + + /// + /// Delete + /// + public static string Buttons_Delete => Resources.GetTranslatedString(@"Buttons_Delete"); + + + /// + /// Discard + /// + public static string Buttons_Discard => Resources.GetTranslatedString(@"Buttons_Discard"); + + + /// + /// Enable + /// + public static string Buttons_EnableImageRendering => Resources.GetTranslatedString(@"Buttons_EnableImageRendering"); + + + /// + /// No + /// + public static string Buttons_No => Resources.GetTranslatedString(@"Buttons_No"); + + + /// + /// Open + /// + public static string Buttons_Open => Resources.GetTranslatedString(@"Buttons_Open"); + + + /// + /// Purchase + /// + public static string Buttons_Purchase => Resources.GetTranslatedString(@"Buttons_Purchase"); + + + /// + /// Rate Wino + /// + public static string Buttons_RateWino => Resources.GetTranslatedString(@"Buttons_RateWino"); + + + /// + /// Save + /// + public static string Buttons_Save => Resources.GetTranslatedString(@"Buttons_Save"); + + + /// + /// Save Configuration + /// + public static string Buttons_SaveConfiguration => Resources.GetTranslatedString(@"Buttons_SaveConfiguration"); + + + /// + /// Share + /// + public static string Buttons_Share => Resources.GetTranslatedString(@"Buttons_Share"); + + + /// + /// Sign In + /// + public static string Buttons_SignIn => Resources.GetTranslatedString(@"Buttons_SignIn"); + + + /// + /// Yes + /// + public static string Buttons_Yes => Resources.GetTranslatedString(@"Buttons_Yes"); + + + /// + /// Center + /// + public static string Center => Resources.GetTranslatedString(@"Center"); + + + /// + /// Coming soon... + /// + public static string ComingSoon => Resources.GetTranslatedString(@"ComingSoon"); + + + /// + /// From: + /// + public static string ComposerFrom => Resources.GetTranslatedString(@"ComposerFrom"); + + + /// + /// Subject: + /// + public static string ComposerSubject => Resources.GetTranslatedString(@"ComposerSubject"); + + + /// + /// To: + /// + public static string ComposerTo => Resources.GetTranslatedString(@"ComposerTo"); + + + /// + /// {0} copied to clipboard. + /// + public static string ClipboardTextCopied_Message => Resources.GetTranslatedString(@"ClipboardTextCopied_Message"); + + + /// + /// Copied + /// + public static string ClipboardTextCopied_Title => Resources.GetTranslatedString(@"ClipboardTextCopied_Title"); + + + /// + /// Failed to copy {0} to clipboard. + /// + public static string ClipboardTextCopyFailed_Message => Resources.GetTranslatedString(@"ClipboardTextCopyFailed_Message"); + + + /// + /// click enter to input addresses + /// + public static string ComposerToPlaceholder => Resources.GetTranslatedString(@"ComposerToPlaceholder"); + + + /// + /// Set custom accent color if you wish. Not selecting a color will use your Windows accent color. + /// + public static string CustomThemeBuilder_AccentColorDescription => Resources.GetTranslatedString(@"CustomThemeBuilder_AccentColorDescription"); + + + /// + /// Accent color + /// + public static string CustomThemeBuilder_AccentColorTitle => Resources.GetTranslatedString(@"CustomThemeBuilder_AccentColorTitle"); + + + /// + /// Pick + /// + public static string CustomThemeBuilder_PickColor => Resources.GetTranslatedString(@"CustomThemeBuilder_PickColor"); + + + /// + /// Unique name for your custom theme. + /// + public static string CustomThemeBuilder_ThemeNameDescription => Resources.GetTranslatedString(@"CustomThemeBuilder_ThemeNameDescription"); + + + /// + /// Theme name + /// + public static string CustomThemeBuilder_ThemeNameTitle => Resources.GetTranslatedString(@"CustomThemeBuilder_ThemeNameTitle"); + + + /// + /// Custom Theme Builder + /// + public static string CustomThemeBuilder_Title => Resources.GetTranslatedString(@"CustomThemeBuilder_Title"); + + + /// + /// Set a custom wallpaper for Wino + /// + public static string CustomThemeBuilder_WallpaperDescription => Resources.GetTranslatedString(@"CustomThemeBuilder_WallpaperDescription"); + + + /// + /// Set custom wallpaper + /// + public static string CustomThemeBuilder_WallpaperTitle => Resources.GetTranslatedString(@"CustomThemeBuilder_WallpaperTitle"); + + + /// + /// You have reached the account creation limit. Would you like to purchase 'Unlimited Account' add-on to continue? + /// + public static string DialogMessage_AccountLimitMessage => Resources.GetTranslatedString(@"DialogMessage_AccountLimitMessage"); + + + /// + /// Account Limit Reached + /// + public static string DialogMessage_AccountLimitTitle => Resources.GetTranslatedString(@"DialogMessage_AccountLimitTitle"); + + + /// + /// Do you want to permanently delete all the mails in this folder? + /// + public static string DialogMessage_CleanupFolderMessage => Resources.GetTranslatedString(@"DialogMessage_CleanupFolderMessage"); + + + /// + /// Cleanup Folder + /// + public static string DialogMessage_CleanupFolderTitle => Resources.GetTranslatedString(@"DialogMessage_CleanupFolderTitle"); + + + /// + /// Message has no recipient. + /// + public static string DialogMessage_ComposerMissingRecipientMessage => Resources.GetTranslatedString(@"DialogMessage_ComposerMissingRecipientMessage"); + + + /// + /// Validation Failed + /// + public static string DialogMessage_ComposerValidationFailedTitle => Resources.GetTranslatedString(@"DialogMessage_ComposerValidationFailedTitle"); + + + /// + /// Give this new link a name. Accounts will be merged under this name. + /// + public static string DialogMessage_CreateLinkedAccountMessage => Resources.GetTranslatedString(@"DialogMessage_CreateLinkedAccountMessage"); + + + /// + /// Account Link Name + /// + public static string DialogMessage_CreateLinkedAccountTitle => Resources.GetTranslatedString(@"DialogMessage_CreateLinkedAccountTitle"); + + + /// + /// Delete {0}? + /// + public static string DialogMessage_DeleteAccountConfirmationMessage => Resources.GetTranslatedString(@"DialogMessage_DeleteAccountConfirmationMessage"); + + + /// + /// All data associated with this account will be deleted from disk permanently. + /// + public static string DialogMessage_DeleteAccountConfirmationTitle => Resources.GetTranslatedString(@"DialogMessage_DeleteAccountConfirmationTitle"); + + + /// + /// This draft will be discarded. Do you want to continue? + /// + public static string DialogMessage_DiscardDraftConfirmationMessage => Resources.GetTranslatedString(@"DialogMessage_DiscardDraftConfirmationMessage"); + + + /// + /// Discard Draft + /// + public static string DialogMessage_DiscardDraftConfirmationTitle => Resources.GetTranslatedString(@"DialogMessage_DiscardDraftConfirmationTitle"); + + + /// + /// Permanent Delete + /// + public static string DialogMessage_HardDeleteConfirmationMessage => Resources.GetTranslatedString(@"DialogMessage_HardDeleteConfirmationMessage"); + + + /// + /// Message(s) will be permanently deleted. Do you want to continue? + /// + public static string DialogMessage_HardDeleteConfirmationTitle => Resources.GetTranslatedString(@"DialogMessage_HardDeleteConfirmationTitle"); + + + /// + /// You don't have any accounts to create message from. + /// + public static string DialogMessage_NoAccountsForCreateMailMessage => Resources.GetTranslatedString(@"DialogMessage_NoAccountsForCreateMailMessage"); + + + /// + /// Account Missing + /// + public static string DialogMessage_NoAccountsForCreateMailTitle => Resources.GetTranslatedString(@"DialogMessage_NoAccountsForCreateMailTitle"); + + + /// + /// Enter new name for linked account + /// + public static string DialogMessage_RenameLinkedAccountsMessage => Resources.GetTranslatedString(@"DialogMessage_RenameLinkedAccountsMessage"); + + + /// + /// Rename Linked Account + /// + public static string DialogMessage_RenameLinkedAccountsTitle => Resources.GetTranslatedString(@"DialogMessage_RenameLinkedAccountsTitle"); + + + /// + /// This operation will not delete your accounts but only break the link for shared folder connections. Do you want to continue? + /// + public static string DialogMessage_UnlinkAccountsConfirmationMessage => Resources.GetTranslatedString(@"DialogMessage_UnlinkAccountsConfirmationMessage"); + + + /// + /// Unlink Accounts + /// + public static string DialogMessage_UnlinkAccountsConfirmationTitle => Resources.GetTranslatedString(@"DialogMessage_UnlinkAccountsConfirmationTitle"); + + + /// + /// Missin Subject + /// + public static string DialogMessage_EmptySubjectConfirmation => Resources.GetTranslatedString(@"DialogMessage_EmptySubjectConfirmation"); + + + /// + /// Message has no subject. Do you want to continue? + /// + public static string DialogMessage_EmptySubjectConfirmationMessage => Resources.GetTranslatedString(@"DialogMessage_EmptySubjectConfirmationMessage"); + + + /// + /// Don't ask again + /// + public static string Dialog_DontAskAgain => Resources.GetTranslatedString(@"Dialog_DontAskAgain"); + + + /// + /// Wino doesn't have it's own Discord server, but special 'wino-mail' channel is hosted at 'Developer Sanctuary' server. To get the updates about Wino please join Developer Sanctuary server and follow 'wino-mail' channel under 'Community Projects' You will be directed to server URL since Discord doesn't support channel invites. + /// + public static string DiscordChannelDisclaimerMessage => Resources.GetTranslatedString(@"DiscordChannelDisclaimerMessage"); + + + /// + /// Important Discord Information + /// + public static string DiscordChannelDisclaimerTitle => Resources.GetTranslatedString(@"DiscordChannelDisclaimerTitle"); + + + /// + /// Draft + /// + public static string Draft => Resources.GetTranslatedString(@"Draft"); + + + /// + /// Draw + /// + public static string EditorToolbarOption_Draw => Resources.GetTranslatedString(@"EditorToolbarOption_Draw"); + + + /// + /// Format + /// + public static string EditorToolbarOption_Format => Resources.GetTranslatedString(@"EditorToolbarOption_Format"); + + + /// + /// Insert + /// + public static string EditorToolbarOption_Insert => Resources.GetTranslatedString(@"EditorToolbarOption_Insert"); + + + /// + /// None + /// + public static string EditorToolbarOption_None => Resources.GetTranslatedString(@"EditorToolbarOption_None"); + + + /// + /// Options + /// + public static string EditorToolbarOption_Options => Resources.GetTranslatedString(@"EditorToolbarOption_Options"); + + + /// + /// Dark mode + /// + public static string ElementTheme_Dark => Resources.GetTranslatedString(@"ElementTheme_Dark"); + + + /// + /// Use system setting + /// + public static string ElementTheme_Default => Resources.GetTranslatedString(@"ElementTheme_Default"); + + + /// + /// Light mode + /// + public static string ElementTheme_Light => Resources.GetTranslatedString(@"ElementTheme_Light"); + + + /// + /// Emoji + /// + public static string Emoji => Resources.GetTranslatedString(@"Emoji"); + + + /// + /// IMAP Client Pool failed. + /// + public static string Exception_ImapClientPoolFailed => Resources.GetTranslatedString(@"Exception_ImapClientPoolFailed"); + + + /// + /// Authentication canceled + /// + public static string Exception_AuthenticationCanceled => Resources.GetTranslatedString(@"Exception_AuthenticationCanceled"); + + + /// + /// This theme already exists. + /// + public static string Exception_CustomThemeExists => Resources.GetTranslatedString(@"Exception_CustomThemeExists"); + + + /// + /// You must provide a name. + /// + public static string Exception_CustomThemeMissingName => Resources.GetTranslatedString(@"Exception_CustomThemeMissingName"); + + + /// + /// You must provide a custom background image. + /// + public static string Exception_CustomThemeMissingWallpaper => Resources.GetTranslatedString(@"Exception_CustomThemeMissingWallpaper"); + + + /// + /// Failed to synchronize folders + /// + public static string Exception_FailedToSynchronizeFolders => Resources.GetTranslatedString(@"Exception_FailedToSynchronizeFolders"); + + + /// + /// Callback uri is null on activation. + /// + public static string Exception_GoogleAuthCallbackNull => Resources.GetTranslatedString(@"Exception_GoogleAuthCallbackNull"); + + + /// + /// Corrupted authorization response. + /// + public static string Exception_GoogleAuthCorruptedCode => Resources.GetTranslatedString(@"Exception_GoogleAuthCorruptedCode"); + + + /// + /// OAuth authorization error: {0} + /// + public static string Exception_GoogleAuthError => Resources.GetTranslatedString(@"Exception_GoogleAuthError"); + + + /// + /// Received request with invalid state ({0}) + /// + public static string Exception_GoogleAuthInvalidResponse => Resources.GetTranslatedString(@"Exception_GoogleAuthInvalidResponse"); + + + /// + /// Authorization code exchange failed. + /// + public static string Exception_GoogleAuthorizationCodeExchangeFailed => Resources.GetTranslatedString(@"Exception_GoogleAuthorizationCodeExchangeFailed"); + + + /// + /// System folder configuration is not valid. Check configuration and try again. + /// + public static string Exception_InvalidSystemFolderConfiguration => Resources.GetTranslatedString(@"Exception_InvalidSystemFolderConfiguration"); + + + /// + /// Assigned account is null + /// + public static string Exception_NullAssignedAccount => Resources.GetTranslatedString(@"Exception_NullAssignedAccount"); + + + /// + /// Assigned folder is null + /// + public static string Exception_NullAssignedFolder => Resources.GetTranslatedString(@"Exception_NullAssignedFolder"); + + + /// + /// Response handling failed with error HTTP code {0} + /// + public static string Exception_SynchronizerFailureHTTP => Resources.GetTranslatedString(@"Exception_SynchronizerFailureHTTP"); + + + /// + /// Token generation failed + /// + public static string Exception_TokenGenerationFailed => Resources.GetTranslatedString(@"Exception_TokenGenerationFailed"); + + + /// + /// Failed to get token information. + /// + public static string Exception_TokenInfoRetrivalFailed => Resources.GetTranslatedString(@"Exception_TokenInfoRetrivalFailed"); + + + /// + /// Unknown error occurred during authentication + /// + public static string Exception_UnknowErrorDuringAuthentication => Resources.GetTranslatedString(@"Exception_UnknowErrorDuringAuthentication"); + + + /// + /// Action {0} is not implemented in request processor + /// + public static string Exception_UnsupportedAction => Resources.GetTranslatedString(@"Exception_UnsupportedAction"); + + + /// + /// This provider is not supported. + /// + public static string Exception_UnsupportedProvider => Resources.GetTranslatedString(@"Exception_UnsupportedProvider"); + + + /// + /// This operation is not supported for {0} + /// + public static string Exception_UnsupportedSynchronizerOperation => Resources.GetTranslatedString(@"Exception_UnsupportedSynchronizerOperation"); + + + /// + /// User canceled system folder config dialog. + /// + public static string Exception_UserCancelSystemFolderSetupDialog => Resources.GetTranslatedString(@"Exception_UserCancelSystemFolderSetupDialog"); + + + /// + /// Couldn't setup account folders. + /// + public static string Exception_InboxNotAvailable => Resources.GetTranslatedString(@"Exception_InboxNotAvailable"); + + + /// + /// Files + /// + public static string Files => Resources.GetTranslatedString(@"Files"); + + + /// + /// All + /// + public static string FilteringOption_All => Resources.GetTranslatedString(@"FilteringOption_All"); + + + /// + /// Flagged + /// + public static string FilteringOption_Flagged => Resources.GetTranslatedString(@"FilteringOption_Flagged"); + + + /// + /// Unread + /// + public static string FilteringOption_Unread => Resources.GetTranslatedString(@"FilteringOption_Unread"); + + + /// + /// Focused + /// + public static string Focused => Resources.GetTranslatedString(@"Focused"); + + + /// + /// Create sub folder + /// + public static string FolderOperation_CreateSubFolder => Resources.GetTranslatedString(@"FolderOperation_CreateSubFolder"); + + + /// + /// Delete + /// + public static string FolderOperation_Delete => Resources.GetTranslatedString(@"FolderOperation_Delete"); + + + /// + /// Don't sync this folder + /// + public static string FolderOperation_DontSync => Resources.GetTranslatedString(@"FolderOperation_DontSync"); + + + /// + /// Empty this folder + /// + public static string FolderOperation_Empty => Resources.GetTranslatedString(@"FolderOperation_Empty"); + + + /// + /// Mark all as read + /// + public static string FolderOperation_MarkAllAsRead => Resources.GetTranslatedString(@"FolderOperation_MarkAllAsRead"); + + + /// + /// Move + /// + public static string FolderOperation_Move => Resources.GetTranslatedString(@"FolderOperation_Move"); + + + /// + /// Move to {0} + /// + public static string DragMoveToFolderCaption => Resources.GetTranslatedString(@"DragMoveToFolderCaption"); + + + /// + /// None + /// + public static string FolderOperation_None => Resources.GetTranslatedString(@"FolderOperation_None"); + + + /// + /// Pin + /// + public static string FolderOperation_Pin => Resources.GetTranslatedString(@"FolderOperation_Pin"); + + + /// + /// Rename + /// + public static string FolderOperation_Rename => Resources.GetTranslatedString(@"FolderOperation_Rename"); + + + /// + /// Unpin + /// + public static string FolderOperation_Unpin => Resources.GetTranslatedString(@"FolderOperation_Unpin"); + + + /// + /// Archive + /// + public static string HoverActionOption_Archive => Resources.GetTranslatedString(@"HoverActionOption_Archive"); + + + /// + /// Delete + /// + public static string HoverActionOption_Delete => Resources.GetTranslatedString(@"HoverActionOption_Delete"); + + + /// + /// Move to Junk + /// + public static string HoverActionOption_MoveJunk => Resources.GetTranslatedString(@"HoverActionOption_MoveJunk"); + + + /// + /// Flag / Unflag + /// + public static string HoverActionOption_ToggleFlag => Resources.GetTranslatedString(@"HoverActionOption_ToggleFlag"); + + + /// + /// Read / Unread + /// + public static string HoverActionOption_ToggleRead => Resources.GetTranslatedString(@"HoverActionOption_ToggleRead"); + + + /// + /// Inbox + /// + public static string MergedAccountCommonFolderInbox => Resources.GetTranslatedString(@"MergedAccountCommonFolderInbox"); + + + /// + /// Sent + /// + public static string MergedAccountCommonFolderSent => Resources.GetTranslatedString(@"MergedAccountCommonFolderSent"); + + + /// + /// Draft + /// + public static string MergedAccountCommonFolderDraft => Resources.GetTranslatedString(@"MergedAccountCommonFolderDraft"); + + + /// + /// Junk + /// + public static string MergedAccountCommonFolderJunk => Resources.GetTranslatedString(@"MergedAccountCommonFolderJunk"); + + + /// + /// Deleted + /// + public static string MergedAccountCommonFolderTrash => Resources.GetTranslatedString(@"MergedAccountCommonFolderTrash"); + + + /// + /// Archive + /// + public static string MergedAccountCommonFolderArchive => Resources.GetTranslatedString(@"MergedAccountCommonFolderArchive"); + + + /// + /// Account type + /// + public static string IMAPSetupDialog_AccountType => Resources.GetTranslatedString(@"IMAPSetupDialog_AccountType"); + + + /// + /// Display Name + /// + public static string IMAPSetupDialog_DisplayName => Resources.GetTranslatedString(@"IMAPSetupDialog_DisplayName"); + + + /// + /// eg. John Doe + /// + public static string IMAPSetupDialog_DisplayNamePlaceholder => Resources.GetTranslatedString(@"IMAPSetupDialog_DisplayNamePlaceholder"); + + + /// + /// Incoming mail server + /// + public static string IMAPSetupDialog_IncomingMailServer => Resources.GetTranslatedString(@"IMAPSetupDialog_IncomingMailServer"); + + + /// + /// Port + /// + public static string IMAPSetupDialog_IncomingMailServerPort => Resources.GetTranslatedString(@"IMAPSetupDialog_IncomingMailServerPort"); + + + /// + /// Email address + /// + public static string IMAPSetupDialog_MailAddress => Resources.GetTranslatedString(@"IMAPSetupDialog_MailAddress"); + + + /// + /// someone@example.com + /// + public static string IMAPSetupDialog_MailAddressPlaceholder => Resources.GetTranslatedString(@"IMAPSetupDialog_MailAddressPlaceholder"); + + + /// + /// Outgoing (SMTP) mail server + /// + public static string IMAPSetupDialog_OutgoingMailServer => Resources.GetTranslatedString(@"IMAPSetupDialog_OutgoingMailServer"); + + + /// + /// Outgoing server password + /// + public static string IMAPSetupDialog_OutgoingMailServerPassword => Resources.GetTranslatedString(@"IMAPSetupDialog_OutgoingMailServerPassword"); + + + /// + /// Port + /// + public static string IMAPSetupDialog_OutgoingMailServerPort => Resources.GetTranslatedString(@"IMAPSetupDialog_OutgoingMailServerPort"); + + + /// + /// Outgoing server requires authentication + /// + public static string IMAPSetupDialog_OutgoingMailServerRequireAuthentication => Resources.GetTranslatedString(@"IMAPSetupDialog_OutgoingMailServerRequireAuthentication"); + + + /// + /// Outgoing server user name + /// + public static string IMAPSetupDialog_OutgoingMailServerUsername => Resources.GetTranslatedString(@"IMAPSetupDialog_OutgoingMailServerUsername"); + + + /// + /// Password + /// + public static string IMAPSetupDialog_Password => Resources.GetTranslatedString(@"IMAPSetupDialog_Password"); + + + /// + /// Require SSL for incoming email + /// + public static string IMAPSetupDialog_RequireSSLForIncomingMail => Resources.GetTranslatedString(@"IMAPSetupDialog_RequireSSLForIncomingMail"); + + + /// + /// Require SSL for outgoing email + /// + public static string IMAPSetupDialog_RequireSSLForOutgoingMail => Resources.GetTranslatedString(@"IMAPSetupDialog_RequireSSLForOutgoingMail"); + + + /// + /// Advanced IMAP Configuration + /// + public static string IMAPSetupDialog_Title => Resources.GetTranslatedString(@"IMAPSetupDialog_Title"); + + + /// + /// Use the same username and password for sending email + /// + public static string IMAPSetupDialog_UseSameConfig => Resources.GetTranslatedString(@"IMAPSetupDialog_UseSameConfig"); + + + /// + /// Username + /// + public static string IMAPSetupDialog_Username => Resources.GetTranslatedString(@"IMAPSetupDialog_Username"); + + + /// + /// johndoe, johndoe@fabrikam.com, domain/johndoe + /// + public static string IMAPSetupDialog_UsernamePlaceholder => Resources.GetTranslatedString(@"IMAPSetupDialog_UsernamePlaceholder"); + + + /// + /// Image rendering is disabled for this message. + /// + public static string ImageRenderingDisabled => Resources.GetTranslatedString(@"ImageRenderingDisabled"); + + + /// + /// Enable + /// + public static string InfoBarAction_Enable => Resources.GetTranslatedString(@"InfoBarAction_Enable"); + + + /// + /// This folder is disabled for synchronization. + /// + public static string InfoBarMessage_SynchronizationDisabledFolder => Resources.GetTranslatedString(@"InfoBarMessage_SynchronizationDisabledFolder"); + + + /// + /// Disabled Folder + /// + public static string InfoBarTitle_SynchronizationDisabledFolder => Resources.GetTranslatedString(@"InfoBarTitle_SynchronizationDisabledFolder"); + + + /// + /// Error + /// + public static string GeneralTitle_Error => Resources.GetTranslatedString(@"GeneralTitle_Error"); + + + /// + /// Warning + /// + public static string GeneralTitle_Warning => Resources.GetTranslatedString(@"GeneralTitle_Warning"); + + + /// + /// Information + /// + public static string GeneralTitle_Info => Resources.GetTranslatedString(@"GeneralTitle_Info"); + + + /// + /// {0} is created + /// + public static string Info_AccountCreatedMessage => Resources.GetTranslatedString(@"Info_AccountCreatedMessage"); + + + /// + /// Account Creation + /// + public static string Info_AccountCreatedTitle => Resources.GetTranslatedString(@"Info_AccountCreatedTitle"); + + + /// + /// Account Creation Failed + /// + public static string Info_AccountCreationFailedTitle => Resources.GetTranslatedString(@"Info_AccountCreationFailedTitle"); + + + /// + /// {0} is successfuly deleted. + /// + public static string Info_AccountDeletedMessage => Resources.GetTranslatedString(@"Info_AccountDeletedMessage"); + + + /// + /// Account Deleted + /// + public static string Info_AccountDeletedTitle => Resources.GetTranslatedString(@"Info_AccountDeletedTitle"); + + + /// + /// Failed + /// + public static string Info_AccountIssueFixFailedTitle => Resources.GetTranslatedString(@"Info_AccountIssueFixFailedTitle"); + + + /// + /// Fixed all account issues. + /// + public static string Info_AccountIssueFixSuccessMessage => Resources.GetTranslatedString(@"Info_AccountIssueFixSuccessMessage"); + + + /// + /// Success + /// + public static string Info_AccountIssueFixSuccessTitle => Resources.GetTranslatedString(@"Info_AccountIssueFixSuccessTitle"); + + + /// + /// Can't open this attachment. + /// + public static string Info_AttachmentOpenFailedMessage => Resources.GetTranslatedString(@"Info_AttachmentOpenFailedMessage"); + + + /// + /// Failed + /// + public static string Info_AttachmentOpenFailedTitle => Resources.GetTranslatedString(@"Info_AttachmentOpenFailedTitle"); + + + /// + /// Can't save this attachment. + /// + public static string Info_AttachmentSaveFailedMessage => Resources.GetTranslatedString(@"Info_AttachmentSaveFailedMessage"); + + + /// + /// Failed + /// + public static string Info_AttachmentSaveFailedTitle => Resources.GetTranslatedString(@"Info_AttachmentSaveFailedTitle"); + + + /// + /// Attachment is saved. + /// + public static string Info_AttachmentSaveSuccessMessage => Resources.GetTranslatedString(@"Info_AttachmentSaveSuccessMessage"); + + + /// + /// Attachment Saved + /// + public static string Info_AttachmentSaveSuccessTitle => Resources.GetTranslatedString(@"Info_AttachmentSaveSuccessTitle"); + + + /// + /// Background execution for the app is denied. This may affect background synchronization and live notifications. + /// + public static string Info_BackgroundExecutionDeniedMessage => Resources.GetTranslatedString(@"Info_BackgroundExecutionDeniedMessage"); + + + /// + /// Denied Background Execution + /// + public static string Info_BackgroundExecutionDeniedTitle => Resources.GetTranslatedString(@"Info_BackgroundExecutionDeniedTitle"); + + + /// + /// Unknown exception occurred when registering background synchronizer. + /// + public static string Info_BackgroundExecutionUnknownErrorMessage => Resources.GetTranslatedString(@"Info_BackgroundExecutionUnknownErrorMessage"); + + + /// + /// Background Execution Failure + /// + public static string Info_BackgroundExecutionUnknownErrorTitle => Resources.GetTranslatedString(@"Info_BackgroundExecutionUnknownErrorTitle"); + + + /// + /// Couldn't locate the MIME file. Synchronizing may help. + /// + public static string Info_ComposerMissingMIMEMessage => Resources.GetTranslatedString(@"Info_ComposerMissingMIMEMessage"); + + + /// + /// Failed + /// + public static string Info_ComposerMissingMIMETitle => Resources.GetTranslatedString(@"Info_ComposerMissingMIMETitle"); + + + /// + /// This contact is already in the recipient list. + /// + public static string Info_ContactExistsMessage => Resources.GetTranslatedString(@"Info_ContactExistsMessage"); + + + /// + /// Contact Exists + /// + public static string Info_ContactExistsTitle => Resources.GetTranslatedString(@"Info_ContactExistsTitle"); + + + /// + /// Draft folder is missing for this account. Please check your account settings. + /// + public static string Info_DraftFolderMissingMessage => Resources.GetTranslatedString(@"Info_DraftFolderMissingMessage"); + + + /// + /// Missing Draft Folder + /// + public static string Info_DraftFolderMissingTitle => Resources.GetTranslatedString(@"Info_DraftFolderMissingTitle"); + + + /// + /// Failed to launch file + /// + public static string Info_FileLaunchFailedTitle => Resources.GetTranslatedString(@"Info_FileLaunchFailedTitle"); + + + /// + /// '{0}' is not a valid e-mail address. + /// + public static string Info_InvalidAddressMessage => Resources.GetTranslatedString(@"Info_InvalidAddressMessage"); + + + /// + /// Invalid Address + /// + public static string Info_InvalidAddressTitle => Resources.GetTranslatedString(@"Info_InvalidAddressTitle"); + + + /// + /// You can't move selected mails to this folder. + /// + public static string Info_InvalidMoveTargetMessage => Resources.GetTranslatedString(@"Info_InvalidMoveTargetMessage"); + + + /// + /// Invalid Move Target + /// + public static string Info_InvalidMoveTargetTitle => Resources.GetTranslatedString(@"Info_InvalidMoveTargetTitle"); + + + /// + /// There are no logs to share. + /// + public static string Info_LogsNotFoundMessage => Resources.GetTranslatedString(@"Info_LogsNotFoundMessage"); + + + /// + /// Logs Not Found + /// + public static string Info_LogsNotFoundTitle => Resources.GetTranslatedString(@"Info_LogsNotFoundTitle"); + + + /// + /// {0} is saved to selected folder. + /// + public static string Info_LogsSavedMessage => Resources.GetTranslatedString(@"Info_LogsSavedMessage"); + + + /// + /// Saved + /// + public static string Info_LogsSavedTitle => Resources.GetTranslatedString(@"Info_LogsSavedTitle"); + + + /// + /// This mail is corrupted or can't be opened. {0} + /// + public static string Info_MailRenderingFailedMessage => Resources.GetTranslatedString(@"Info_MailRenderingFailedMessage"); + + + /// + /// Render Failed + /// + public static string Info_MailRenderingFailedTitle => Resources.GetTranslatedString(@"Info_MailRenderingFailedTitle"); + + + /// + /// This message is corrupted. + /// + public static string Info_MessageCorruptedMessage => Resources.GetTranslatedString(@"Info_MessageCorruptedMessage"); + + + /// + /// Error + /// + public static string Info_MessageCorruptedTitle => Resources.GetTranslatedString(@"Info_MessageCorruptedTitle"); + + + /// + /// {0} doesn't exist for this account. + /// + public static string Info_MissingFolderMessage => Resources.GetTranslatedString(@"Info_MissingFolderMessage"); + + + /// + /// Missing Folder + /// + public static string Info_MissingFolderTitle => Resources.GetTranslatedString(@"Info_MissingFolderTitle"); + + + /// + /// Success + /// + public static string Info_PDFSaveSuccessTitle => Resources.GetTranslatedString(@"Info_PDFSaveSuccessTitle"); + + + /// + /// Failed to save PDF file + /// + public static string Info_PDFSaveFailedTitle => Resources.GetTranslatedString(@"Info_PDFSaveFailedTitle"); + + + /// + /// PDF file is saved to {0} + /// + public static string Info_PDFSaveSuccessMessage => Resources.GetTranslatedString(@"Info_PDFSaveSuccessMessage"); + + + /// + /// Looks like this product has already been purchased before. + /// + public static string Info_PurchaseExistsMessage => Resources.GetTranslatedString(@"Info_PurchaseExistsMessage"); + + + /// + /// Existing Product + /// + public static string Info_PurchaseExistsTitle => Resources.GetTranslatedString(@"Info_PurchaseExistsTitle"); + + + /// + /// Thank You + /// + public static string Info_PurchaseThankYouMessage => Resources.GetTranslatedString(@"Info_PurchaseThankYouMessage"); + + + /// + /// Purchase successful + /// + public static string Info_PurchaseThankYouTitle => Resources.GetTranslatedString(@"Info_PurchaseThankYouTitle"); + + + /// + /// Failed to Create Requests + /// + public static string Info_RequestCreationFailedTitle => Resources.GetTranslatedString(@"Info_RequestCreationFailedTitle"); + + + /// + /// There was a network issue with your review. + /// + public static string Info_ReviewNetworkErrorMessage => Resources.GetTranslatedString(@"Info_ReviewNetworkErrorMessage"); + + + /// + /// Network Issue + /// + public static string Info_ReviewNetworkErrorTitle => Resources.GetTranslatedString(@"Info_ReviewNetworkErrorTitle"); + + + /// + /// All feedbacks are appreciated. Thank you for the review! + /// + public static string Info_ReviewNewMessage => Resources.GetTranslatedString(@"Info_ReviewNewMessage"); + + + /// + /// Thank you + /// + public static string Info_ReviewSuccessTitle => Resources.GetTranslatedString(@"Info_ReviewSuccessTitle"); + + + /// + /// There was an unknown issue with your review. ({0}) + /// + public static string Info_ReviewUnknownErrorMessage => Resources.GetTranslatedString(@"Info_ReviewUnknownErrorMessage"); + + + /// + /// Unknown Error + /// + public static string Info_ReviewUnknownErrorTitle => Resources.GetTranslatedString(@"Info_ReviewUnknownErrorTitle"); + + + /// + /// Thank you for the updated review. + /// + public static string Info_ReviewUpdatedMessage => Resources.GetTranslatedString(@"Info_ReviewUpdatedMessage"); + + + /// + /// Disabled signature for this account + /// + public static string Info_SignatureDisabledMessage => Resources.GetTranslatedString(@"Info_SignatureDisabledMessage"); + + + /// + /// Success + /// + public static string Info_SignatureDisabledTitle => Resources.GetTranslatedString(@"Info_SignatureDisabledTitle"); + + + /// + /// New signature is saved + /// + public static string Info_SignatureSavedMessage => Resources.GetTranslatedString(@"Info_SignatureSavedMessage"); + + + /// + /// Success + /// + public static string Info_SignatureSavedTitle => Resources.GetTranslatedString(@"Info_SignatureSavedTitle"); + + + /// + /// Canceled + /// + public static string Info_SyncCanceledMessage => Resources.GetTranslatedString(@"Info_SyncCanceledMessage"); + + + /// + /// Synchronization + /// + public static string Info_SyncCanceledTitle => Resources.GetTranslatedString(@"Info_SyncCanceledTitle"); + + + /// + /// Synchronization Failed + /// + public static string Info_SyncFailedTitle => Resources.GetTranslatedString(@"Info_SyncFailedTitle"); + + + /// + /// This functionality is not implemented yet. + /// + public static string Info_UnsupportedFunctionalityDescription => Resources.GetTranslatedString(@"Info_UnsupportedFunctionalityDescription"); + + + /// + /// Unsupported + /// + public static string Info_UnsupportedFunctionalityTitle => Resources.GetTranslatedString(@"Info_UnsupportedFunctionalityTitle"); + + + /// + /// Invalid Unsubscribe Uri + /// + public static string Info_UnsubscribeLinkInvalidTitle => Resources.GetTranslatedString(@"Info_UnsubscribeLinkInvalidTitle"); + + + /// + /// This unsubscribe link is invalid. Failed to unsubscribe from the list. + /// + public static string Info_UnsubscribeLinkInvalidMessage => Resources.GetTranslatedString(@"Info_UnsubscribeLinkInvalidMessage"); + + + /// + /// Authentication method + /// + public static string ImapAdvancedSetupDialog_AuthenticationMethod => Resources.GetTranslatedString(@"ImapAdvancedSetupDialog_AuthenticationMethod"); + + + /// + /// Connection security + /// + public static string ImapAdvancedSetupDialog_ConnectionSecurity => Resources.GetTranslatedString(@"ImapAdvancedSetupDialog_ConnectionSecurity"); + + + /// + /// Auto + /// + public static string ImapAuthenticationMethod_Auto => Resources.GetTranslatedString(@"ImapAuthenticationMethod_Auto"); + + + /// + /// CRAM-MD5 + /// + public static string ImapAuthenticationMethod_CramMD5 => Resources.GetTranslatedString(@"ImapAuthenticationMethod_CramMD5"); + + + /// + /// DIGEST-MD5 + /// + public static string ImapAuthenticationMethod_DigestMD5 => Resources.GetTranslatedString(@"ImapAuthenticationMethod_DigestMD5"); + + + /// + /// No authentication + /// + public static string ImapAuthenticationMethod_None => Resources.GetTranslatedString(@"ImapAuthenticationMethod_None"); + + + /// + /// Normal password + /// + public static string ImapAuthenticationMethod_Plain => Resources.GetTranslatedString(@"ImapAuthenticationMethod_Plain"); + + + /// + /// Encrypted password + /// + public static string ImapAuthenticationMethod_EncryptedPassword => Resources.GetTranslatedString(@"ImapAuthenticationMethod_EncryptedPassword"); + + + /// + /// NTLM + /// + public static string ImapAuthenticationMethod_Ntlm => Resources.GetTranslatedString(@"ImapAuthenticationMethod_Ntlm"); + + + /// + /// None + /// + public static string ImapConnectionSecurity_None => Resources.GetTranslatedString(@"ImapConnectionSecurity_None"); + + + /// + /// SSL/TLS + /// + public static string ImapConnectionSecurity_SslTls => Resources.GetTranslatedString(@"ImapConnectionSecurity_SslTls"); + + + /// + /// STARTTLS + /// + public static string ImapConnectionSecurity_StartTls => Resources.GetTranslatedString(@"ImapConnectionSecurity_StartTls"); + + + /// + /// Auto + /// + public static string ImapConnectionSecurity_Auto => Resources.GetTranslatedString(@"ImapConnectionSecurity_Auto"); + + + /// + /// Justify + /// + public static string Justify => Resources.GetTranslatedString(@"Justify"); + + + /// + /// Left + /// + public static string Left => Resources.GetTranslatedString(@"Left"); + + + /// + /// Link + /// + public static string Link => Resources.GetTranslatedString(@"Link"); + + + /// + /// you must have at least 2 accounts to create link link will be removed on save + /// + public static string LinkedAccountsCreatePolicyMessage => Resources.GetTranslatedString(@"LinkedAccountsCreatePolicyMessage"); + + + /// + /// Linked Accounts + /// + public static string LinkedAccountsTitle => Resources.GetTranslatedString(@"LinkedAccountsTitle"); + + + /// + /// Always Move to Focused + /// + public static string MailOperation_AlwaysMoveFocused => Resources.GetTranslatedString(@"MailOperation_AlwaysMoveFocused"); + + + /// + /// Always Move to Other + /// + public static string MailOperation_AlwaysMoveOther => Resources.GetTranslatedString(@"MailOperation_AlwaysMoveOther"); + + + /// + /// Archive + /// + public static string MailOperation_Archive => Resources.GetTranslatedString(@"MailOperation_Archive"); + + + /// + /// Clear flag + /// + public static string MailOperation_ClearFlag => Resources.GetTranslatedString(@"MailOperation_ClearFlag"); + + + /// + /// Dark + /// + public static string MailOperation_DarkEditor => Resources.GetTranslatedString(@"MailOperation_DarkEditor"); + + + /// + /// Delete + /// + public static string MailOperation_Delete => Resources.GetTranslatedString(@"MailOperation_Delete"); + + + /// + /// Export to PDF + /// + public static string MailOperation_ExportPDF => Resources.GetTranslatedString(@"MailOperation_ExportPDF"); + + + /// + /// Find + /// + public static string MailOperation_Find => Resources.GetTranslatedString(@"MailOperation_Find"); + + + /// + /// Forward + /// + public static string MailOperation_Forward => Resources.GetTranslatedString(@"MailOperation_Forward"); + + + /// + /// Ignore + /// + public static string MailOperation_Ignore => Resources.GetTranslatedString(@"MailOperation_Ignore"); + + + /// + /// Light + /// + public static string MailOperation_LightEditor => Resources.GetTranslatedString(@"MailOperation_LightEditor"); + + + /// + /// Mark as junk + /// + public static string MailOperation_MarkAsJunk => Resources.GetTranslatedString(@"MailOperation_MarkAsJunk"); + + + /// + /// Mark as read + /// + public static string MailOperation_MarkAsRead => Resources.GetTranslatedString(@"MailOperation_MarkAsRead"); + + + /// + /// Mark as unread + /// + public static string MailOperation_MarkAsUnread => Resources.GetTranslatedString(@"MailOperation_MarkAsUnread"); + + + /// + /// Mark as Not Junk + /// + public static string MailOperation_MarkNotJunk => Resources.GetTranslatedString(@"MailOperation_MarkNotJunk"); + + + /// + /// Move + /// + public static string MailOperation_Move => Resources.GetTranslatedString(@"MailOperation_Move"); + + + /// + /// Move to Focused + /// + public static string MailOperation_MoveFocused => Resources.GetTranslatedString(@"MailOperation_MoveFocused"); + + + /// + /// Move to Junk + /// + public static string MailOperation_MoveJunk => Resources.GetTranslatedString(@"MailOperation_MoveJunk"); + + + /// + /// Move to Other + /// + public static string MailOperation_MoveOther => Resources.GetTranslatedString(@"MailOperation_MoveOther"); + + + /// + /// Navigate + /// + public static string MailOperation_Navigate => Resources.GetTranslatedString(@"MailOperation_Navigate"); + + + /// + /// Print + /// + public static string MailOperation_Print => Resources.GetTranslatedString(@"MailOperation_Print"); + + + /// + /// Reply + /// + public static string MailOperation_Reply => Resources.GetTranslatedString(@"MailOperation_Reply"); + + + /// + /// Reply all + /// + public static string MailOperation_ReplyAll => Resources.GetTranslatedString(@"MailOperation_ReplyAll"); + + + /// + /// Save As + /// + public static string MailOperation_SaveAs => Resources.GetTranslatedString(@"MailOperation_SaveAs"); + + + /// + /// Set flag + /// + public static string MailOperation_SetFlag => Resources.GetTranslatedString(@"MailOperation_SetFlag"); + + + /// + /// Unarchive + /// + public static string MailOperation_Unarchive => Resources.GetTranslatedString(@"MailOperation_Unarchive"); + + + /// + /// Zoom + /// + public static string MailOperation_Zoom => Resources.GetTranslatedString(@"MailOperation_Zoom"); + + + /// + /// {0} item(s) selected + /// + public static string MailsSelected => Resources.GetTranslatedString(@"MailsSelected"); + + + /// + /// Mark as flagged/unflagged + /// + public static string MarkFlagUnflag => Resources.GetTranslatedString(@"MarkFlagUnflag"); + + + /// + /// Mark as read/unread + /// + public static string MarkReadUnread => Resources.GetTranslatedString(@"MarkReadUnread"); + + + /// + /// Manage Accounts + /// + public static string MenuManageAccounts => Resources.GetTranslatedString(@"MenuManageAccounts"); + + + /// + /// New Mail + /// + public static string MenuNewMail => Resources.GetTranslatedString(@"MenuNewMail"); + + + /// + /// accounts + /// + public static string MenuMergedAccountItemAccountsSuffix => Resources.GetTranslatedString(@"MenuMergedAccountItemAccountsSuffix"); + + + /// + /// Rate Wino + /// + public static string MenuRate => Resources.GetTranslatedString(@"MenuRate"); + + + /// + /// Settings + /// + public static string MenuSettings => Resources.GetTranslatedString(@"MenuSettings"); + + + /// + /// Available Accounts + /// + public static string MergedAccountsAvailableAccountsTitle => Resources.GetTranslatedString(@"MergedAccountsAvailableAccountsTitle"); + + + /// + /// More + /// + public static string More => Resources.GetTranslatedString(@"More"); + + + /// + /// {0} is not a valid folder for this mail. + /// + public static string MoveMailDialog_InvalidFolderMessage => Resources.GetTranslatedString(@"MoveMailDialog_InvalidFolderMessage"); + + + /// + /// Pick a folder + /// + public static string MoveMailDialog_Title => Resources.GetTranslatedString(@"MoveMailDialog_Title"); + + + /// + /// Account Name + /// + public static string NewAccountDialog_AccountName => Resources.GetTranslatedString(@"NewAccountDialog_AccountName"); + + + /// + /// Personal + /// + public static string NewAccountDialog_AccountNameDefaultValue => Resources.GetTranslatedString(@"NewAccountDialog_AccountNameDefaultValue"); + + + /// + /// eg. Personal Mail + /// + public static string NewAccountDialog_AccountNamePlaceholder => Resources.GetTranslatedString(@"NewAccountDialog_AccountNamePlaceholder"); + + + /// + /// Add New Account + /// + public static string NewAccountDialog_Title => Resources.GetTranslatedString(@"NewAccountDialog_Title"); + + + /// + /// no message selected + /// + public static string NoMailSelected => Resources.GetTranslatedString(@"NoMailSelected"); + + + /// + /// no messages match your search criteria. + /// + public static string NoMessageCrieteria => Resources.GetTranslatedString(@"NoMessageCrieteria"); + + + /// + /// this folder is empty. + /// + public static string NoMessageEmptyFolder => Resources.GetTranslatedString(@"NoMessageEmptyFolder"); + + + /// + /// You have {0} new mails + /// + public static string Notifications_MultipleNotificationsMessage => Resources.GetTranslatedString(@"Notifications_MultipleNotificationsMessage"); + + + /// + /// New Mails + /// + public static string Notifications_MultipleNotificationsTitle => Resources.GetTranslatedString(@"Notifications_MultipleNotificationsTitle"); + + + /// + /// Checkout new version {0} + /// + public static string Notifications_WinoUpdatedMessage => Resources.GetTranslatedString(@"Notifications_WinoUpdatedMessage"); + + + /// + /// Wino Mail has been updated. + /// + public static string Notifications_WinoUpdatedTitle => Resources.GetTranslatedString(@"Notifications_WinoUpdatedTitle"); + + + /// + /// Other + /// + public static string Other => Resources.GetTranslatedString(@"Other"); + + + /// + /// Default + /// + public static string PaneLengthOption_Default => Resources.GetTranslatedString(@"PaneLengthOption_Default"); + + + /// + /// Extra Large + /// + public static string PaneLengthOption_ExtraLarge => Resources.GetTranslatedString(@"PaneLengthOption_ExtraLarge"); + + + /// + /// Large + /// + public static string PaneLengthOption_Large => Resources.GetTranslatedString(@"PaneLengthOption_Large"); + + + /// + /// Medium + /// + public static string PaneLengthOption_Medium => Resources.GetTranslatedString(@"PaneLengthOption_Medium"); + + + /// + /// Micro + /// + public static string PaneLengthOption_Micro => Resources.GetTranslatedString(@"PaneLengthOption_Micro"); + + + /// + /// Small + /// + public static string PaneLengthOption_Small => Resources.GetTranslatedString(@"PaneLengthOption_Small"); + + + /// + /// Photos + /// + public static string Photos => Resources.GetTranslatedString(@"Photos"); + + + /// + /// Preparing folders + /// + public static string PreparingFoldersMessage => Resources.GetTranslatedString(@"PreparingFoldersMessage"); + + + /// + /// Google Account + /// + public static string ProviderDetail_Gmail_Description => Resources.GetTranslatedString(@"ProviderDetail_Gmail_Description"); + + + /// + /// Custom IMAP/SMTP server + /// + public static string ProviderDetail_IMAP_Description => Resources.GetTranslatedString(@"ProviderDetail_IMAP_Description"); + + + /// + /// IMAP Server + /// + public static string ProviderDetail_IMAP_Title => Resources.GetTranslatedString(@"ProviderDetail_IMAP_Title"); + + + /// + /// Results + /// + public static string Results => Resources.GetTranslatedString(@"Results"); + + + /// + /// Right + /// + public static string Right => Resources.GetTranslatedString(@"Right"); + + + /// + /// up to date + /// + public static string SynchronizationFolderReport_Success => Resources.GetTranslatedString(@"SynchronizationFolderReport_Success"); + + + /// + /// synchronization is failed + /// + public static string SynchronizationFolderReport_Failed => Resources.GetTranslatedString(@"SynchronizationFolderReport_Failed"); + + + /// + /// search + /// + public static string SearchBarPlaceholder => Resources.GetTranslatedString(@"SearchBarPlaceholder"); + + + /// + /// searching in + /// + public static string SearchingIn => Resources.GetTranslatedString(@"SearchingIn"); + + + /// + /// Go to issue tracker GitHub repository. + /// + public static string SettingsAboutGithub_Description => Resources.GetTranslatedString(@"SettingsAboutGithub_Description"); + + + /// + /// GitHub + /// + public static string SettingsAboutGithub_Title => Resources.GetTranslatedString(@"SettingsAboutGithub_Title"); + + + /// + /// Append messages to Sent folder + /// + public static string SettingsAccountManagementAppendMessage_Title => Resources.GetTranslatedString(@"SettingsAccountManagementAppendMessage_Title"); + + + /// + /// Create a copy of the message in Sent folder after the draft is sent. Enable this if you don't see your mails after you sent them in Sent folder. + /// + public static string SettingsAccountManagementAppendMessage_Description => Resources.GetTranslatedString(@"SettingsAccountManagementAppendMessage_Description"); + + + /// + /// Edit Linked Inbox + /// + public static string SettingsEditLinkedInbox_Title => Resources.GetTranslatedString(@"SettingsEditLinkedInbox_Title"); + + + /// + /// Add / remove accounts, rename or break the link between accounts. + /// + public static string SettingsEditLinkedInbox_Description => Resources.GetTranslatedString(@"SettingsEditLinkedInbox_Description"); + + + /// + /// Version + /// + public static string SettingsAboutVersion => Resources.GetTranslatedString(@"SettingsAboutVersion"); + + + /// + /// Lightweight mail client for Windows device families. + /// + public static string SettingsAboutWinoDescription => Resources.GetTranslatedString(@"SettingsAboutWinoDescription"); + + + /// + /// Learn more about Wino. + /// + public static string SettingsAbout_Description => Resources.GetTranslatedString(@"SettingsAbout_Description"); + + + /// + /// About + /// + public static string SettingsAbout_Title => Resources.GetTranslatedString(@"SettingsAbout_Title"); + + + /// + /// Change application's accent color + /// + public static string SettingsAccentColor_Description => Resources.GetTranslatedString(@"SettingsAccentColor_Description"); + + + /// + /// Accent Color + /// + public static string SettingsAccentColor_Title => Resources.GetTranslatedString(@"SettingsAccentColor_Title"); + + + /// + /// Use my Windows accent color + /// + public static string SettingsAccentColor_UseWindowsAccentColor => Resources.GetTranslatedString(@"SettingsAccentColor_UseWindowsAccentColor"); + + + /// + /// Change the name of the account. + /// + public static string SettingsAccountName_Description => Resources.GetTranslatedString(@"SettingsAccountName_Description"); + + + /// + /// Account Name + /// + public static string SettingsAccountName_Title => Resources.GetTranslatedString(@"SettingsAccountName_Title"); + + + /// + /// Personalize Wino with different custom application themes for your like. + /// + public static string SettingsApplicationTheme_Description => Resources.GetTranslatedString(@"SettingsApplicationTheme_Description"); + + + /// + /// Application Theme + /// + public static string SettingsApplicationTheme_Title => Resources.GetTranslatedString(@"SettingsApplicationTheme_Title"); + + + /// + /// Select a theme from Wino's own collection for your taste or apply your own themes. + /// + public static string SettingsAvailableThemes_Description => Resources.GetTranslatedString(@"SettingsAvailableThemes_Description"); + + + /// + /// Available Themes + /// + public static string SettingsAvailableThemes_Title => Resources.GetTranslatedString(@"SettingsAvailableThemes_Title"); + + + /// + /// Auto select next item + /// + public static string SettingsAutoSelectNextItem_Title => Resources.GetTranslatedString(@"SettingsAutoSelectNextItem_Title"); + + + /// + /// Select the next item after you delete or move a mail. + /// + public static string SettingsAutoSelectNextItem_Description => Resources.GetTranslatedString(@"SettingsAutoSelectNextItem_Description"); + + + /// + /// Create your own custom theme with custom wallpaper and accent color. + /// + public static string SettingsCustomTheme_Description => Resources.GetTranslatedString(@"SettingsCustomTheme_Description"); + + + /// + /// Custom Theme + /// + public static string SettingsCustomTheme_Title => Resources.GetTranslatedString(@"SettingsCustomTheme_Title"); + + + /// + /// Configure System Folders + /// + public static string SettingsConfigureSpecialFolders_Title => Resources.GetTranslatedString(@"SettingsConfigureSpecialFolders_Title"); + + + /// + /// Set folders with special functions. Folders such as Archive, Inbox, and Drafts are essential for Wino to function properly. + /// + public static string SettingsConfigureSpecialFolders_Description => Resources.GetTranslatedString(@"SettingsConfigureSpecialFolders_Description"); + + + /// + /// Configure + /// + public static string SettingConfigureSpecialFolders_Button => Resources.GetTranslatedString(@"SettingConfigureSpecialFolders_Button"); + + + /// + /// Failed to setup system folders + /// + public static string Error_FailedToSetupSystemFolders_Title => Resources.GetTranslatedString(@"Error_FailedToSetupSystemFolders_Title"); + + + /// + /// Delete all e-mails and credentials associated with this account. + /// + public static string SettingsDeleteAccount_Description => Resources.GetTranslatedString(@"SettingsDeleteAccount_Description"); + + + /// + /// Delete this account + /// + public static string SettingsDeleteAccount_Title => Resources.GetTranslatedString(@"SettingsDeleteAccount_Title"); + + + /// + /// Should Wino ask you for comfirmation every time you try to permanently delete a mail using Shift + Del keys? + /// + public static string SettingsDeleteProtection_Description => Resources.GetTranslatedString(@"SettingsDeleteProtection_Description"); + + + /// + /// Permanent Delete Protection + /// + public static string SettingsDeleteProtection_Title => Resources.GetTranslatedString(@"SettingsDeleteProtection_Title"); + + + /// + /// For developers + /// + public static string SettingsDiagnostics_Description => Resources.GetTranslatedString(@"SettingsDiagnostics_Description"); + + + /// + /// Diagnostics + /// + public static string SettingsDiagnostics_Title => Resources.GetTranslatedString(@"SettingsDiagnostics_Title"); + + + /// + /// Get regular development updates, join roadmap discussions and provide feedback. + /// + public static string SettingsDiscord_Description => Resources.GetTranslatedString(@"SettingsDiscord_Description"); + + + /// + /// Discord Channel + /// + public static string SettingsDiscord_Title => Resources.GetTranslatedString(@"SettingsDiscord_Title"); + + + /// + /// Element theme selection is disabled when application theme is selected other than Default. + /// + public static string SettingsElementThemeSelectionDisabled => Resources.GetTranslatedString(@"SettingsElementThemeSelectionDisabled"); + + + /// + /// Select a Windows theme for Wino + /// + public static string SettingsElementTheme_Description => Resources.GetTranslatedString(@"SettingsElementTheme_Description"); + + + /// + /// Element Theme + /// + public static string SettingsElementTheme_Title => Resources.GetTranslatedString(@"SettingsElementTheme_Title"); + + + /// + /// Enable hover actions + /// + public static string SettingsEnableHoverActions_Title => Resources.GetTranslatedString(@"SettingsEnableHoverActions_Title"); + + + /// + /// Enable this to provide details about IMAP connectivity issuses you had during IMAP server setup. + /// + public static string SettingsEnableIMAPLogs_Description => Resources.GetTranslatedString(@"SettingsEnableIMAPLogs_Description"); + + + /// + /// Enable IMAP Protocol Logs + /// + public static string SettingsEnableIMAPLogs_Title => Resources.GetTranslatedString(@"SettingsEnableIMAPLogs_Title"); + + + /// + /// I might need logs for crashes to diagnose issues you have opened in GitHub. None of the logs will expose your credentials or sensetive information to public. + /// + public static string SettingsEnableLogs_Description => Resources.GetTranslatedString(@"SettingsEnableLogs_Description"); + + + /// + /// Enable Logs + /// + public static string SettingsEnableLogs_Title => Resources.GetTranslatedString(@"SettingsEnableLogs_Title"); + + + /// + /// Enable Signature + /// + public static string SettingsEnableSignature => Resources.GetTranslatedString(@"SettingsEnableSignature"); + + + /// + /// Set whether Wino should expand this account's folders on startup. + /// + public static string SettingsExpandOnStartup_Description => Resources.GetTranslatedString(@"SettingsExpandOnStartup_Description"); + + + /// + /// Expand Menu on Startup + /// + public static string SettingsExpandOnStartup_Title => Resources.GetTranslatedString(@"SettingsExpandOnStartup_Title"); + + + /// + /// Manage external content settings when rendering mails. + /// + public static string SettingsExternalContent_Description => Resources.GetTranslatedString(@"SettingsExternalContent_Description"); + + + /// + /// External Content + /// + public static string SettingsExternalContent_Title => Resources.GetTranslatedString(@"SettingsExternalContent_Title"); + + + /// + /// Set whether Inbox should be split into two as Focused - Other. + /// + public static string SettingsFocusedInbox_Description => Resources.GetTranslatedString(@"SettingsFocusedInbox_Description"); + + + /// + /// Focused Inbox + /// + public static string SettingsFocusedInbox_Title => Resources.GetTranslatedString(@"SettingsFocusedInbox_Title"); + + + /// + /// Enable or disable specific folders for synchronization. + /// + public static string SettingsFolderSync_Description => Resources.GetTranslatedString(@"SettingsFolderSync_Description"); + + + /// + /// Folder Synchronization + /// + public static string SettingsFolderSync_Title => Resources.GetTranslatedString(@"SettingsFolderSync_Title"); + + + /// + /// Folder Configuration + /// + public static string SettingsFolderOptions_Title => Resources.GetTranslatedString(@"SettingsFolderOptions_Title"); + + + /// + /// Change individual folder settings like enable/disable sync or show/hide unread badge. + /// + public static string SettingsFolderOptions_Description => Resources.GetTranslatedString(@"SettingsFolderOptions_Description"); + + + /// + /// Center Action + /// + public static string SettingsHoverActionCenter => Resources.GetTranslatedString(@"SettingsHoverActionCenter"); + + + /// + /// Left Action + /// + public static string SettingsHoverActionLeft => Resources.GetTranslatedString(@"SettingsHoverActionLeft"); + + + /// + /// Right Action + /// + public static string SettingsHoverActionRight => Resources.GetTranslatedString(@"SettingsHoverActionRight"); + + + /// + /// Select 3 actions to show up when you hover over the mails with cursor. + /// + public static string SettingsHoverActions_Description => Resources.GetTranslatedString(@"SettingsHoverActions_Description"); + + + /// + /// Hover Actions + /// + public static string SettingsHoverActions_Title => Resources.GetTranslatedString(@"SettingsHoverActions_Title"); + + + /// + /// Change display language for Wino. + /// + public static string SettingsLanguage_Description => Resources.GetTranslatedString(@"SettingsLanguage_Description"); + + + /// + /// Display Language + /// + public static string SettingsLanguage_Title => Resources.GetTranslatedString(@"SettingsLanguage_Title"); + + + /// + /// Categories + /// + public static string CategoriesFolderNameOverride => Resources.GetTranslatedString(@"CategoriesFolderNameOverride"); + + + /// + /// More + /// + public static string MoreFolderNameOverride => Resources.GetTranslatedString(@"MoreFolderNameOverride"); + + + /// + /// Settings + /// + public static string SettingsOptions_Title => Resources.GetTranslatedString(@"SettingsOptions_Title"); + + + /// + /// Merge multiple accounts into one. See mails from one Inbox together. + /// + public static string SettingsLinkAccounts_Description => Resources.GetTranslatedString(@"SettingsLinkAccounts_Description"); + + + /// + /// Create Linked Accounts + /// + public static string SettingsLinkAccounts_Title => Resources.GetTranslatedString(@"SettingsLinkAccounts_Title"); + + + /// + /// Modify the current link with the new accounts. + /// + public static string SettingsLinkedAccountsSave_Description => Resources.GetTranslatedString(@"SettingsLinkedAccountsSave_Description"); + + + /// + /// Save Changes + /// + public static string SettingsLinkedAccountsSave_Title => Resources.GetTranslatedString(@"SettingsLinkedAccountsSave_Title"); + + + /// + /// Load images automatically + /// + public static string SettingsLoadImages_Title => Resources.GetTranslatedString(@"SettingsLoadImages_Title"); + + + /// + /// Load styles automatically + /// + public static string SettingsLoadStyles_Title => Resources.GetTranslatedString(@"SettingsLoadStyles_Title"); + + + /// + /// Adjust the spacing for listing mails. + /// + public static string SettingsMailSpacing_Description => Resources.GetTranslatedString(@"SettingsMailSpacing_Description"); + + + /// + /// Mail Spacing + /// + public static string SettingsMailSpacing_Title => Resources.GetTranslatedString(@"SettingsMailSpacing_Title"); + + + /// + /// Create Nested Folders + /// + public static string SettingsFolderMenuStyle_Title => Resources.GetTranslatedString(@"SettingsFolderMenuStyle_Title"); + + + /// + /// Change whether account folders should be nested inside an account menu item or not. Toggle this off if you like the old menu system in Windows Mail + /// + public static string SettingsFolderMenuStyle_Description => Resources.GetTranslatedString(@"SettingsFolderMenuStyle_Description"); + + + /// + /// Notifications, signatures, synchronization and other settings per account. + /// + public static string SettingsManageAccountSettings_Description => Resources.GetTranslatedString(@"SettingsManageAccountSettings_Description"); + + + /// + /// Manage Account Settings + /// + public static string SettingsManageAccountSettings_Title => Resources.GetTranslatedString(@"SettingsManageAccountSettings_Title"); + + + /// + /// Move items to add new link or remove existing link. + /// + public static string SettingsManageLink_Description => Resources.GetTranslatedString(@"SettingsManageLink_Description"); + + + /// + /// Manage Link + /// + public static string SettingsManageLink_Title => Resources.GetTranslatedString(@"SettingsManageLink_Title"); + + + /// + /// Change what should happen to the selected item. + /// + public static string SettingsMarkAsRead_Description => Resources.GetTranslatedString(@"SettingsMarkAsRead_Description"); + + + /// + /// Don't automatically mark item as read + /// + public static string SettingsMarkAsRead_DontChange => Resources.GetTranslatedString(@"SettingsMarkAsRead_DontChange"); + + + /// + /// Seconds to wait: + /// + public static string SettingsMarkAsRead_SecondsToWait => Resources.GetTranslatedString(@"SettingsMarkAsRead_SecondsToWait"); + + + /// + /// When viewed in the reading pane + /// + public static string SettingsMarkAsRead_Timer => Resources.GetTranslatedString(@"SettingsMarkAsRead_Timer"); + + + /// + /// Mark item as read + /// + public static string SettingsMarkAsRead_Title => Resources.GetTranslatedString(@"SettingsMarkAsRead_Title"); + + + /// + /// When selected + /// + public static string SettingsMarkAsRead_WhenSelected => Resources.GetTranslatedString(@"SettingsMarkAsRead_WhenSelected"); + + + /// + /// Change how your messages should be organized in mail list. + /// + public static string SettingsMessageList_Description => Resources.GetTranslatedString(@"SettingsMessageList_Description"); + + + /// + /// Message List + /// + public static string SettingsMessageList_Title => Resources.GetTranslatedString(@"SettingsMessageList_Title"); + + + /// + /// You didn't setup any accounts yet. + /// + public static string SettingsNoAccountSetupMessage => Resources.GetTranslatedString(@"SettingsNoAccountSetupMessage"); + + + /// + /// Turn on or off notifications for this account. + /// + public static string SettingsNotifications_Description => Resources.GetTranslatedString(@"SettingsNotifications_Description"); + + + /// + /// Notifications + /// + public static string SettingsNotifications_Title => Resources.GetTranslatedString(@"SettingsNotifications_Title"); + + + /// + /// Change the width of the mail list. + /// + public static string SettingsPaneLength_Description => Resources.GetTranslatedString(@"SettingsPaneLength_Description"); + + + /// + /// Mail List Pane Length + /// + public static string SettingsPaneLength_Title => Resources.GetTranslatedString(@"SettingsPaneLength_Title"); + + + /// + /// Show much more love ❤️ All donations are appreciated. + /// + public static string SettingsPaypal_Description => Resources.GetTranslatedString(@"SettingsPaypal_Description"); + + + /// + /// Donate via PayPal + /// + public static string SettingsPaypal_Title => Resources.GetTranslatedString(@"SettingsPaypal_Title"); + + + /// + /// Compact Mode + /// + public static string SettingsPersonalizationMailDisplayCompactMode => Resources.GetTranslatedString(@"SettingsPersonalizationMailDisplayCompactMode"); + + + /// + /// Medium Mode + /// + public static string SettingsPersonalizationMailDisplayMediumMode => Resources.GetTranslatedString(@"SettingsPersonalizationMailDisplayMediumMode"); + + + /// + /// Spacious Mode + /// + public static string SettingsPersonalizationMailDisplaySpaciousMode => Resources.GetTranslatedString(@"SettingsPersonalizationMailDisplaySpaciousMode"); + + + /// + /// Change appearance of Wino as you like. + /// + public static string SettingsPersonalization_Description => Resources.GetTranslatedString(@"SettingsPersonalization_Description"); + + + /// + /// Personalization + /// + public static string SettingsPersonalization_Title => Resources.GetTranslatedString(@"SettingsPersonalization_Title"); + + + /// + /// Review privacy policy. + /// + public static string SettingsPrivacyPolicy_Description => Resources.GetTranslatedString(@"SettingsPrivacyPolicy_Description"); + + + /// + /// Privacy Policy + /// + public static string SettingsPrivacyPolicy_Title => Resources.GetTranslatedString(@"SettingsPrivacyPolicy_Title"); + + + /// + /// Mail rendering options. + /// + public static string SettingsReadingPane_Description => Resources.GetTranslatedString(@"SettingsReadingPane_Description"); + + + /// + /// Reading Pane + /// + public static string SettingsReadingPane_Title => Resources.GetTranslatedString(@"SettingsReadingPane_Title"); + + + /// + /// Default Reader Font + /// + public static string SettingsReaderFont_Title => Resources.GetTranslatedString(@"SettingsReaderFont_Title"); + + + /// + /// Change the default font family and font size for reading mails. + /// + public static string SettingsReaderFontFamily_Description => Resources.GetTranslatedString(@"SettingsReaderFontFamily_Description"); + + + /// + /// Font Family + /// + public static string SettingsFontFamily_Title => Resources.GetTranslatedString(@"SettingsFontFamily_Title"); + + + /// + /// Font Size + /// + public static string SettingsFontSize_Title => Resources.GetTranslatedString(@"SettingsFontSize_Title"); + + + /// + /// Preview + /// + public static string SettingsFontPreview_Title => Resources.GetTranslatedString(@"SettingsFontPreview_Title"); + + + /// + /// Default Composer Font + /// + public static string SettingsComposerFont_Title => Resources.GetTranslatedString(@"SettingsComposerFont_Title"); + + + /// + /// Change the default font family and font size for composing mails. + /// + public static string SettingsComposerFontFamily_Description => Resources.GetTranslatedString(@"SettingsComposerFontFamily_Description"); + + + /// + /// Change the display name of the linked accounts. + /// + public static string SettingsRenameMergeAccount_Description => Resources.GetTranslatedString(@"SettingsRenameMergeAccount_Description"); + + + /// + /// Rename + /// + public static string SettingsRenameMergeAccount_Title => Resources.GetTranslatedString(@"SettingsRenameMergeAccount_Title"); + + + /// + /// This will allow you to click on the headers in messages list and go to specific date + /// + public static string SettingsSemanticZoom_Description => Resources.GetTranslatedString(@"SettingsSemanticZoom_Description"); + + + /// + /// Semantic Zoom for Date Headers + /// + public static string SettingsSemanticZoom_Title => Resources.GetTranslatedString(@"SettingsSemanticZoom_Title"); + + + /// + /// Hide/show thepreview text. + /// + public static string SettingsShowPreviewText_Description => Resources.GetTranslatedString(@"SettingsShowPreviewText_Description"); + + + /// + /// Show Preview Text + /// + public static string SettingsShowPreviewText_Title => Resources.GetTranslatedString(@"SettingsShowPreviewText_Title"); + + + /// + /// Hide/show the thumbnail sender pictures. + /// + public static string SettingsShowSenderPictures_Description => Resources.GetTranslatedString(@"SettingsShowSenderPictures_Description"); + + + /// + /// Show Sender Avatars + /// + public static string SettingsShowSenderPictures_Title => Resources.GetTranslatedString(@"SettingsShowSenderPictures_Title"); + + + /// + /// Display Clock Format in 24 Hours + /// + public static string SettingsPrefer24HourClock_Title => Resources.GetTranslatedString(@"SettingsPrefer24HourClock_Title"); + + + /// + /// Mail recieve times will be displayed in 24 hour format instead of 12 (AM/PM) + /// + public static string SettingsPrefer24HourClock_Description => Resources.GetTranslatedString(@"SettingsPrefer24HourClock_Description"); + + + /// + /// Edit or remove account signature + /// + public static string SettingsSignature_Description => Resources.GetTranslatedString(@"SettingsSignature_Description"); + + + /// + /// Signature + /// + public static string SettingsSignature_Title => Resources.GetTranslatedString(@"SettingsSignature_Title"); + + + /// + /// Primary account item to load Inbox at startup. + /// + public static string SettingsStartupItem_Description => Resources.GetTranslatedString(@"SettingsStartupItem_Description"); + + + /// + /// Startup Item + /// + public static string SettingsStartupItem_Title => Resources.GetTranslatedString(@"SettingsStartupItem_Title"); + + + /// + /// Show some love ❤️ + /// + public static string SettingsStore_Description => Resources.GetTranslatedString(@"SettingsStore_Description"); + + + /// + /// Rate in Store + /// + public static string SettingsStore_Title => Resources.GetTranslatedString(@"SettingsStore_Title"); + + + /// + /// Organize messages into conversation threads. + /// + public static string SettingsThreads_Description => Resources.GetTranslatedString(@"SettingsThreads_Description"); + + + /// + /// Conversation Threading + /// + public static string SettingsThreads_Title => Resources.GetTranslatedString(@"SettingsThreads_Title"); + + + /// + /// Remove the link between accounts. This will not delete your accounts. + /// + public static string SettingsUnlinkAccounts_Description => Resources.GetTranslatedString(@"SettingsUnlinkAccounts_Description"); + + + /// + /// Unlink Accounts + /// + public static string SettingsUnlinkAccounts_Title => Resources.GetTranslatedString(@"SettingsUnlinkAccounts_Title"); + + + /// + /// by date + /// + public static string SortingOption_Date => Resources.GetTranslatedString(@"SortingOption_Date"); + + + /// + /// by name + /// + public static string SortingOption_Name => Resources.GetTranslatedString(@"SortingOption_Name"); + + + /// + /// All feedbacks are appreciated and they will make much Wino better in the future. Would you like to rate Wino in Microsoft Store? + /// + public static string StoreRatingDialog_MessageFirstLine => Resources.GetTranslatedString(@"StoreRatingDialog_MessageFirstLine"); + + + /// + /// Would you like to rate Wino Mail in Microsoft Store? + /// + public static string StoreRatingDialog_MessageSecondLine => Resources.GetTranslatedString(@"StoreRatingDialog_MessageSecondLine"); + + + /// + /// Enjoying Wino? + /// + public static string StoreRatingDialog_Title => Resources.GetTranslatedString(@"StoreRatingDialog_Title"); + + + /// + /// Archived messages will be moved to here. + /// + public static string SystemFolderConfigDialog_ArchiveFolderDescription => Resources.GetTranslatedString(@"SystemFolderConfigDialog_ArchiveFolderDescription"); + + + /// + /// Archive Folder + /// + public static string SystemFolderConfigDialog_ArchiveFolderHeader => Resources.GetTranslatedString(@"SystemFolderConfigDialog_ArchiveFolderHeader"); + + + /// + /// Deleted messages will be moved to here. + /// + public static string SystemFolderConfigDialog_DeletedFolderDescription => Resources.GetTranslatedString(@"SystemFolderConfigDialog_DeletedFolderDescription"); + + + /// + /// Deleted Folder + /// + public static string SystemFolderConfigDialog_DeletedFolderHeader => Resources.GetTranslatedString(@"SystemFolderConfigDialog_DeletedFolderHeader"); + + + /// + /// New mails/replies will be crafted in here. + /// + public static string SystemFolderConfigDialog_DraftFolderDescription => Resources.GetTranslatedString(@"SystemFolderConfigDialog_DraftFolderDescription"); + + + /// + /// Draft Folder + /// + public static string SystemFolderConfigDialog_DraftFolderHeader => Resources.GetTranslatedString(@"SystemFolderConfigDialog_DraftFolderHeader"); + + + /// + /// All spam/junk mails will be here. + /// + public static string SystemFolderConfigDialog_JunkFolderDescription => Resources.GetTranslatedString(@"SystemFolderConfigDialog_JunkFolderDescription"); + + + /// + /// Junk/Spam Folder + /// + public static string SystemFolderConfigDialog_JunkFolderHeader => Resources.GetTranslatedString(@"SystemFolderConfigDialog_JunkFolderHeader"); + + + /// + /// This IMAP server doesn't support SPECIAL-USE extension hence Wino couldn't setup the system folders properly. + /// + public static string SystemFolderConfigDialog_MessageFirstLine => Resources.GetTranslatedString(@"SystemFolderConfigDialog_MessageFirstLine"); + + + /// + /// Please select the appropriate folders for specific functionalities. + /// + public static string SystemFolderConfigDialog_MessageSecondLine => Resources.GetTranslatedString(@"SystemFolderConfigDialog_MessageSecondLine"); + + + /// + /// Folder that sent messages will be stored. + /// + public static string SystemFolderConfigDialog_SentFolderDescription => Resources.GetTranslatedString(@"SystemFolderConfigDialog_SentFolderDescription"); + + + /// + /// Sent Folder + /// + public static string SystemFolderConfigDialog_SentFolderHeader => Resources.GetTranslatedString(@"SystemFolderConfigDialog_SentFolderHeader"); + + + /// + /// Configure System Folders + /// + public static string SystemFolderConfigDialog_Title => Resources.GetTranslatedString(@"SystemFolderConfigDialog_Title"); + + + /// + /// You can't assign Inbox folder to any other system folder. + /// + public static string SystemFolderConfigDialogValidation_InboxSelected => Resources.GetTranslatedString(@"SystemFolderConfigDialogValidation_InboxSelected"); + + + /// + /// Some of the system folders are used more than once in the configuration. + /// + public static string SystemFolderConfigDialogValidation_DuplicateSystemFolders => Resources.GetTranslatedString(@"SystemFolderConfigDialogValidation_DuplicateSystemFolders"); + + + /// + /// System Folders Setup + /// + public static string SystemFolderConfigSetupSuccess_Title => Resources.GetTranslatedString(@"SystemFolderConfigSetupSuccess_Title"); + + + /// + /// System folders are successfully configured. + /// + public static string SystemFolderConfigSetupSuccess_Message => Resources.GetTranslatedString(@"SystemFolderConfigSetupSuccess_Message"); + + + /// + /// Testing server connection... + /// + public static string TestingImapConnectionMessage => Resources.GetTranslatedString(@"TestingImapConnectionMessage"); + + + /// + /// Today + /// + public static string Today => Resources.GetTranslatedString(@"Today"); + + + /// + /// unknown address + /// + public static string UnknownAddress => Resources.GetTranslatedString(@"UnknownAddress"); + + + /// + /// Unknown Date + /// + public static string UnknownDateHeader => Resources.GetTranslatedString(@"UnknownDateHeader"); + + + /// + /// unknown Mail Group Address + /// + public static string UnknownGroupAddress => Resources.GetTranslatedString(@"UnknownGroupAddress"); + + + /// + /// Unknown Sender + /// + public static string UnknownSender => Resources.GetTranslatedString(@"UnknownSender"); + + + /// + /// Unsubscribe + /// + public static string Unsubscribe => Resources.GetTranslatedString(@"Unsubscribe"); + + + /// + /// View Details + /// + public static string ViewContactDetails => Resources.GetTranslatedString(@"ViewContactDetails"); + + + /// + /// Wino offers 3 accounts to start with for free. If you need more than 3 accounts, please upgrade + /// + public static string WinoUpgradeDescription => Resources.GetTranslatedString(@"WinoUpgradeDescription"); + + + /// + /// Upgrade to Unlimited Accounts + /// + public static string WinoUpgradeMessage => Resources.GetTranslatedString(@"WinoUpgradeMessage"); + + + /// + /// {0} out of {1} free accounts used. + /// + public static string WinoUpgradeRemainingAccountsMessage => Resources.GetTranslatedString(@"WinoUpgradeRemainingAccountsMessage"); + + + /// + /// Yesterday + /// + public static string Yesterday => Resources.GetTranslatedString(@"Yesterday"); + +} +} diff --git a/Wino.Core.Domain/Translator.tt b/Wino.Core.Domain/Translator.tt new file mode 100644 index 00000000..296dee9b --- /dev/null +++ b/Wino.Core.Domain/Translator.tt @@ -0,0 +1,60 @@ +<#@ template debug="true" hostspecific="true" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ assembly name="System.Text.Json" #> +<#@ assembly name="System.Memory" #> +<#@ assembly name="System" #> +<#@ import namespace="System.Text.Json" #> +<#@ import namespace="System" #> +<#@ assembly name="NetStandard" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.IO" #> +<#@ output extension="Designer.cs" #> +<# string filename = this.Host.ResolvePath("Translations/en_US/resources.json"); + var allText = File.ReadAllText(filename); + var resourceKeys = JsonSerializer.Deserialize>(allText); +#> + +namespace Wino.Core.Domain +{ + public class Translator + { + private static global::Wino.Core.Domain.Translations.WinoTranslationDictionary _dictionary; + + public static global::Wino.Core.Domain.Translations.WinoTranslationDictionary Resources + { + get + { + if (_dictionary == null) + { + _dictionary = new global::Wino.Core.Domain.Translations.WinoTranslationDictionary(); + } + + return _dictionary; + } + } + <# + + string[] escapeChars = new string[] { " ", ";", "@", "$", "&", "(",")","-","#",":","!","'","?","{","}","," }; + + foreach (var key in resourceKeys) + { + // Generate proper allowed variable name by C# + var allowedPropertyName = escapeChars.Aggregate(key.Key, (c1, c2) => c1.Replace(c2, string.Empty)); + + // There might be null values for some keys. Those will display as (null string) in the Comment; + // The actual translation for the key will be the key itself at runtime. + var beautifiedValue = key.Value == null ? "(null string)" : key.Value; + + // We need to trim the line ending literals for comments. + var beautifiedComment = beautifiedValue.Replace('\r',' ').Replace('\n',' '); + #> + + /// + /// <#= beautifiedComment #> + /// + public static string <#= allowedPropertyName #> => Resources.GetTranslatedString(@"<#= key.Key #>"); + <# } #> +} +} diff --git a/Wino.Core.Domain/Wino.Core.Domain.csproj b/Wino.Core.Domain/Wino.Core.Domain.csproj new file mode 100644 index 00000000..6a4ba33f --- /dev/null +++ b/Wino.Core.Domain/Wino.Core.Domain.csproj @@ -0,0 +1,64 @@ + + + + netstandard2.0 + true + 12.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + True + True + Translator.tt + + + + + TextTemplatingFileGenerator + Translator.Designer.cs + + + diff --git a/Wino.Core.UWP/CoreUWPContainerSetup.cs b/Wino.Core.UWP/CoreUWPContainerSetup.cs new file mode 100644 index 00000000..295c907c --- /dev/null +++ b/Wino.Core.UWP/CoreUWPContainerSetup.cs @@ -0,0 +1,27 @@ +using Microsoft.Extensions.DependencyInjection; +using Wino.Core.Domain.Interfaces; +using Wino.Core.UWP.Services; +using Wino.Services; + +namespace Wino.Core.UWP +{ + public static class CoreUWPContainerSetup + { + public static void RegisterCoreUWPServices(this IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + } + } +} diff --git a/Wino.Core.UWP/Dispatcher.cs b/Wino.Core.UWP/Dispatcher.cs new file mode 100644 index 00000000..060607f8 --- /dev/null +++ b/Wino.Core.UWP/Dispatcher.cs @@ -0,0 +1,20 @@ +using System; +using System.Threading.Tasks; +using Windows.UI.Core; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.UWP +{ + public class UWPDispatcher : IDispatcher + { + private readonly CoreDispatcher _coreDispatcher; + + public UWPDispatcher(CoreDispatcher coreDispatcher) + { + _coreDispatcher = coreDispatcher; + } + + public Task ExecuteOnUIThread(Action action) + => _coreDispatcher.RunAsync(CoreDispatcherPriority.Normal, () => action()).AsTask(); + } +} diff --git a/Wino.Core.UWP/Extensions/ElementThemeExtensions.cs b/Wino.Core.UWP/Extensions/ElementThemeExtensions.cs new file mode 100644 index 00000000..4f7fd145 --- /dev/null +++ b/Wino.Core.UWP/Extensions/ElementThemeExtensions.cs @@ -0,0 +1,34 @@ +using Windows.UI.Xaml; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.UWP.Extensions +{ + public static class ElementThemeExtensions + { + public static ApplicationElementTheme ToWinoElementTheme(this ElementTheme elementTheme) + { + switch (elementTheme) + { + case ElementTheme.Light: + return ApplicationElementTheme.Light; + case ElementTheme.Dark: + return ApplicationElementTheme.Dark; + } + + return ApplicationElementTheme.Default; + } + + public static ElementTheme ToWindowsElementTheme(this ApplicationElementTheme elementTheme) + { + switch (elementTheme) + { + case ApplicationElementTheme.Light: + return ElementTheme.Light; + case ApplicationElementTheme.Dark: + return ElementTheme.Dark; + } + + return ElementTheme.Default; + } + } +} diff --git a/Wino.Core.UWP/Models/Personalization/CustomAppTheme.cs b/Wino.Core.UWP/Models/Personalization/CustomAppTheme.cs new file mode 100644 index 00000000..3f821e5a --- /dev/null +++ b/Wino.Core.UWP/Models/Personalization/CustomAppTheme.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading.Tasks; +using Windows.Storage; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Personalization; +using Wino.Services; + +namespace Wino.Core.UWP.Models.Personalization +{ + /// + /// Custom themes that are generated by users. + /// + public class CustomAppTheme : AppThemeBase + { + public CustomAppTheme(CustomThemeMetadata metadata) : base(metadata.Name, metadata.Id) + { + AccentColor = metadata.AccentColorHex; + } + + public override AppThemeType AppThemeType => AppThemeType.Custom; + + public override string GetBackgroundPreviewImagePath() + => $"ms-appdata:///local/{ThemeService.CustomThemeFolderName}/{Id}_preview.jpg"; + + public override async Task GetThemeResourceDictionaryContentAsync() + { + var customAppThemeFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///AppThemes/Custom.xaml")); + return await FileIO.ReadTextAsync(customAppThemeFile); + } + } +} diff --git a/Wino.Core.UWP/Models/Personalization/PreDefinedAppTheme.cs b/Wino.Core.UWP/Models/Personalization/PreDefinedAppTheme.cs new file mode 100644 index 00000000..107f4a1a --- /dev/null +++ b/Wino.Core.UWP/Models/Personalization/PreDefinedAppTheme.cs @@ -0,0 +1,34 @@ +using System; +using System.Threading.Tasks; +using Windows.Storage; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Personalization; + +namespace Wino.Core.UWP.Models.Personalization +{ + /// + /// Forest, Nighty, Clouds etc. applies to pre-defined themes in Wino. + /// + public class PreDefinedAppTheme : AppThemeBase + { + public PreDefinedAppTheme(string themeName, + Guid id, + string accentColor = "", + ApplicationElementTheme forcedElementTheme = ApplicationElementTheme.Default) : base(themeName, id) + { + AccentColor = accentColor; + ForceElementTheme = forcedElementTheme; + } + + public override AppThemeType AppThemeType => AppThemeType.PreDefined; + + public override string GetBackgroundPreviewImagePath() + => $"ms-appx:///BackgroundImages/{ThemeName}.jpg"; + + public override async Task GetThemeResourceDictionaryContentAsync() + { + var xamlDictionaryFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri($"ms-appx:///AppThemes/{ThemeName}.xaml")); + return await FileIO.ReadTextAsync(xamlDictionaryFile); + } + } +} diff --git a/Wino.Core.UWP/Models/Personalization/SystemAppTheme.cs b/Wino.Core.UWP/Models/Personalization/SystemAppTheme.cs new file mode 100644 index 00000000..3580b1d0 --- /dev/null +++ b/Wino.Core.UWP/Models/Personalization/SystemAppTheme.cs @@ -0,0 +1,13 @@ +using System; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.UWP.Models.Personalization +{ + // Mica - Acrylic. + public class SystemAppTheme : PreDefinedAppTheme + { + public SystemAppTheme(string themeName, Guid id) : base(themeName, id, "") { } + + public override AppThemeType AppThemeType => AppThemeType.System; + } +} diff --git a/Wino.Core.UWP/Properties/AssemblyInfo.cs b/Wino.Core.UWP/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..8d1893a5 --- /dev/null +++ b/Wino.Core.UWP/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Wino.Core.UWP")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Wino.Core.UWP")] +[assembly: AssemblyCopyright("Copyright © 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/Wino.Core.UWP/Properties/Wino.Core.UWP.rd.xml b/Wino.Core.UWP/Properties/Wino.Core.UWP.rd.xml new file mode 100644 index 00000000..ba802981 --- /dev/null +++ b/Wino.Core.UWP/Properties/Wino.Core.UWP.rd.xml @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/Wino.Core.UWP/Services/AppInitializerService.cs b/Wino.Core.UWP/Services/AppInitializerService.cs new file mode 100644 index 00000000..e56f00e9 --- /dev/null +++ b/Wino.Core.UWP/Services/AppInitializerService.cs @@ -0,0 +1,52 @@ +using System; +using System.Threading.Tasks; +using Windows.Storage; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.UWP.Services +{ + public class AppInitializerService : IAppInitializerService + { + private readonly IBackgroundTaskService _backgroundTaskService; + + public AppInitializerService(IBackgroundTaskService backgroundTaskService) + { + _backgroundTaskService = backgroundTaskService; + } + + public string GetApplicationDataFolder() => ApplicationData.Current.GetPublisherCacheFolder("WinoShared").Path; + + // TODO: Pre 1.7.0 for Wino Calendar... + //public string GetApplicationDataFolder() => ApplicationData.Current.LocalFolder.Path; + + public Task MigrateAsync() + { + UnregisterAllBackgroundTasks(); + + return Task.CompletedTask; + } + + #region 1.6.8 -> 1.6.9 + + private void UnregisterAllBackgroundTasks() + { + _backgroundTaskService.UnregisterAllBackgroundTask(); + } + + #endregion + + #region 1.7.0 + + /// + /// We decided to use publisher cache folder as a database going forward. + /// This migration will move the file from application local folder and delete it. + /// Going forward database will be initialized from publisher cache folder. + /// + private async Task MoveExistingDatabaseToSharedCacheFolderAsync() + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/Wino.Core.UWP/Services/BackgroundSynchronizer.cs b/Wino.Core.UWP/Services/BackgroundSynchronizer.cs new file mode 100644 index 00000000..64fbb57b --- /dev/null +++ b/Wino.Core.UWP/Services/BackgroundSynchronizer.cs @@ -0,0 +1,144 @@ +using System; +using System.Threading.Tasks; +using Serilog; +using Windows.Storage; +using Wino.Core; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Exceptions; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Synchronization; +using Wino.Core.Synchronizers; + +namespace Wino.Services +{ + public interface IBackgroundSynchronizer + { + Task RunBackgroundSynchronizationAsync(BackgroundSynchronizationReason reason); + void CreateLock(); + void ReleaseLock(); + bool IsBackgroundSynchronizationLocked(); + } + + /// + /// Service responsible for handling background synchronization on timer and session connected events. + /// + public class BackgroundSynchronizer : IBackgroundSynchronizer + { + private const string BackgroundSynchronizationLock = nameof(BackgroundSynchronizationLock); + + private readonly IAccountService _accountService; + private readonly IFolderService _folderService; + private readonly IWinoSynchronizerFactory _winoSynchronizerFactory; + + public BackgroundSynchronizer(IAccountService accountService, + IFolderService folderService, + IWinoSynchronizerFactory winoSynchronizerFactory) + { + _accountService = accountService; + _folderService = folderService; + _winoSynchronizerFactory = winoSynchronizerFactory; + } + + public void CreateLock() => ApplicationData.Current.LocalSettings.Values[BackgroundSynchronizationLock] = true; + public void ReleaseLock() => ApplicationData.Current.LocalSettings.Values[BackgroundSynchronizationLock] = false; + + public bool IsBackgroundSynchronizationLocked() + => ApplicationData.Current.LocalSettings.Values.ContainsKey(BackgroundSynchronizationLock) + && ApplicationData.Current.LocalSettings.Values[BackgroundSynchronizationLock] is bool boolValue && boolValue; + + public async Task RunBackgroundSynchronizationAsync(BackgroundSynchronizationReason reason) + { + Log.Information($"{reason} background synchronization is kicked in."); + + // This should never crash. + // We might be in-process or out-of-process. + + //if (IsBackgroundSynchronizationLocked()) + //{ + // Log.Warning("Background synchronization is locked. Hence another background synchronization is canceled."); + // return; + //} + + try + { + CreateLock(); + + var accounts = await _accountService.GetAccountsAsync(); + + foreach (var account in accounts) + { + // We can't sync broken account. + if (account.AttentionReason != AccountAttentionReason.None) + continue; + + // TODO + // We can't synchronize without system folder setup is done. + //var isSystemFolderSetupDone = await _folderService.CheckSystemFolderSetupDoneAsync(account.Id); + + //// No need to throw here. It's a background process. + //if (!isSystemFolderSetupDone) + // continue; + + var synchronizer = _winoSynchronizerFactory.GetAccountSynchronizer(account.Id); + + if (synchronizer.State != AccountSynchronizerState.Idle) + { + Log.Information("Skipping background synchronization for {Name} since current state is {State}", synchronizer.Account.Name, synchronizer.State); + + return; + } + + await HandleSynchronizationAsync(synchronizer, reason); + } + } + catch (Exception ex) + { + Log.Error($"[BackgroundSynchronization] Failed with message {ex.Message}"); + } + finally + { + ReleaseLock(); + } + } + + private async Task HandleSynchronizationAsync(IBaseSynchronizer synchronizer, BackgroundSynchronizationReason reason) + { + if (synchronizer.State != AccountSynchronizerState.Idle) return; + + var account = synchronizer.Account; + + try + { + // SessionConnected will do Full synchronization for logon, Timer task will do Inbox only. + + var syncType = reason == BackgroundSynchronizationReason.SessionConnected ? SynchronizationType.Full : SynchronizationType.Inbox; + + var options = new SynchronizationOptions() + { + AccountId = account.Id, + Type = syncType, + }; + + await synchronizer.SynchronizeAsync(options); + } + catch (AuthenticationAttentionException authenticationAttentionException) + { + Log.Error(authenticationAttentionException, $"[BackgroundSync] Invalid credentials for account {account.Address}"); + + account.AttentionReason = AccountAttentionReason.InvalidCredentials; + await _accountService.UpdateAccountAsync(account); + } + catch (SystemFolderConfigurationMissingException configMissingException) + { + Log.Error(configMissingException, $"[BackgroundSync] Missing system folder configuration for account {account.Address}"); + + account.AttentionReason = AccountAttentionReason.MissingSystemFolderConfiguration; + await _accountService.UpdateAccountAsync(account); + } + catch (Exception ex) + { + Log.Error(ex, "[BackgroundSync] Synchronization failed."); + } + } + } +} diff --git a/Wino.Core.UWP/Services/BackgroundTaskService.cs b/Wino.Core.UWP/Services/BackgroundTaskService.cs new file mode 100644 index 00000000..a0ac1c07 --- /dev/null +++ b/Wino.Core.UWP/Services/BackgroundTaskService.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Serilog; +using Windows.ApplicationModel.Background; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Exceptions; + +namespace Wino.Core.UWP.Services +{ + public class BackgroundTaskService : IBackgroundTaskService + { + private const string IsBackgroundExecutionDeniedMessageKey = nameof(IsBackgroundExecutionDeniedMessageKey); + + public const string BackgroundSynchronizationTimerTaskNameEx = nameof(BackgroundSynchronizationTimerTaskNameEx); + public const string ToastActivationTaskEx = nameof(ToastActivationTaskEx); + + private const string SessionConnectedTaskEntryPoint = "Wino.BackgroundTasks.SessionConnectedTask"; + private const string SessionConnectedTaskName = "SessionConnectedTask"; + + private readonly IConfigurationService _configurationService; + private readonly List registeredBackgroundTaskNames = new List(); + + public BackgroundTaskService(IConfigurationService configurationService) + { + _configurationService = configurationService; + + LoadRegisteredTasks(); + } + + // Calling WinRT all the time for registered tasks might be slow. Cache them on ctor. + private void LoadRegisteredTasks() + { + foreach (var task in BackgroundTaskRegistration.AllTasks) + { + registeredBackgroundTaskNames.Add(task.Value.Name); + } + + Log.Information($"Found {registeredBackgroundTaskNames.Count} registered background tasks. [{string.Join(',', registeredBackgroundTaskNames)}]"); + } + + public async Task HandleBackgroundTaskRegistrations() + { + var response = await BackgroundExecutionManager.RequestAccessAsync(); + + if (response == BackgroundAccessStatus.DeniedBySystemPolicy || + response == BackgroundAccessStatus.DeniedByUser) + { + // Only notify users about disabled background execution once. + + bool isNotifiedBefore = _configurationService.Get(IsBackgroundExecutionDeniedMessageKey, false); + + if (!isNotifiedBefore) + { + _configurationService.Set(IsBackgroundExecutionDeniedMessageKey, true); + + throw new BackgroundTaskExecutionRequestDeniedException(); + } + } + else + { + RegisterSessionConnectedTask(); + RegisterTimerSynchronizationTask(); + RegisterToastNotificationHandlerBackgroundTask(); + } + } + + private bool IsBackgroundTaskRegistered(string taskName) + => registeredBackgroundTaskNames.Contains(taskName); + + public void UnregisterAllBackgroundTask() + { + foreach (var task in BackgroundTaskRegistration.AllTasks) + { + task.Value.Unregister(true); + } + } + + private void LogBackgroundTaskRegistration(string taskName) + { + Log.Information($"Registered new background task -> {taskName}"); + + registeredBackgroundTaskNames.Add($"{taskName}"); + } + + private BackgroundTaskRegistration RegisterSessionConnectedTask() + { + if (IsBackgroundTaskRegistered(SessionConnectedTaskName)) return null; + + var builder = new BackgroundTaskBuilder + { + Name = SessionConnectedTaskName, + TaskEntryPoint = SessionConnectedTaskEntryPoint + }; + + builder.SetTrigger(new SystemTrigger(SystemTriggerType.SessionConnected, false)); + + LogBackgroundTaskRegistration(SessionConnectedTaskName); + + return builder.Register(); + } + + private BackgroundTaskRegistration RegisterToastNotificationHandlerBackgroundTask() + { + if (IsBackgroundTaskRegistered(ToastActivationTaskEx)) return null; + + var builder = new BackgroundTaskBuilder + { + Name = ToastActivationTaskEx + }; + + builder.SetTrigger(new ToastNotificationActionTrigger()); + + LogBackgroundTaskRegistration(ToastActivationTaskEx); + + return builder.Register(); + } + + private BackgroundTaskRegistration RegisterTimerSynchronizationTask() + { + if (IsBackgroundTaskRegistered(BackgroundSynchronizationTimerTaskNameEx)) return null; + + var builder = new BackgroundTaskBuilder + { + Name = BackgroundSynchronizationTimerTaskNameEx + }; + + builder.SetTrigger(new TimeTrigger(15, false)); + builder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable)); + + LogBackgroundTaskRegistration(BackgroundSynchronizationTimerTaskNameEx); + + return builder.Register(); + } + } +} diff --git a/Wino.Core.UWP/Services/ClipboardService.cs b/Wino.Core.UWP/Services/ClipboardService.cs new file mode 100644 index 00000000..9e3f5c02 --- /dev/null +++ b/Wino.Core.UWP/Services/ClipboardService.cs @@ -0,0 +1,19 @@ +using System.Threading.Tasks; +using Windows.ApplicationModel.DataTransfer; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.UWP.Services +{ + public class ClipboardService : IClipboardService + { + public Task CopyClipboardAsync(string text) + { + var package = new DataPackage(); + package.SetText(text); + + Clipboard.SetContent(package); + + return Task.CompletedTask; + } + } +} diff --git a/Wino.Core.UWP/Services/ConfigurationService.cs b/Wino.Core.UWP/Services/ConfigurationService.cs new file mode 100644 index 00000000..79455800 --- /dev/null +++ b/Wino.Core.UWP/Services/ConfigurationService.cs @@ -0,0 +1,46 @@ +using System; +using System.ComponentModel; +using Windows.Foundation.Collections; +using Windows.Storage; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.UWP.Services +{ + public class ConfigurationService : IConfigurationService + { + public T Get(string key, T defaultValue = default) + => GetInternal(key, ApplicationData.Current.LocalSettings.Values, defaultValue); + + public T GetRoaming(string key, T defaultValue = default) + => GetInternal(key, ApplicationData.Current.RoamingSettings.Values, defaultValue); + + public void Set(string key, object value) + => SetInternal(key, value, ApplicationData.Current.LocalSettings.Values); + + public void SetRoaming(string key, object value) + => SetInternal(key, value, ApplicationData.Current.RoamingSettings.Values); + + private T GetInternal(string key, IPropertySet collection, T defaultValue = default) + { + if (collection.ContainsKey(key)) + { + var value = collection[key]?.ToString(); + + if (typeof(T).IsEnum) + return (T)Enum.Parse(typeof(T), value); + + if (typeof(T) == typeof(Guid?) && Guid.TryParse(value, out Guid guidResult)) + { + return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromInvariantString(value); + } + + return (T)Convert.ChangeType(value, typeof(T)); + } + + return defaultValue; + } + + private void SetInternal(string key, object value, IPropertySet collection) + => collection[key] = value?.ToString(); + } +} diff --git a/Wino.Core.UWP/Services/FileService.cs b/Wino.Core.UWP/Services/FileService.cs new file mode 100644 index 00000000..fdfbf5ab --- /dev/null +++ b/Wino.Core.UWP/Services/FileService.cs @@ -0,0 +1,38 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Windows.Storage; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.UWP.Services +{ + public class FileService : IFileService + { + public async Task CopyFileAsync(string sourceFilePath, string destinationFolderPath) + { + var fileName = Path.GetFileName(sourceFilePath); + + var sourceFileHandle = await StorageFile.GetFileFromPathAsync(sourceFilePath); + var destinationFolder = await StorageFolder.GetFolderFromPathAsync(destinationFolderPath); + + var copiedFile = await sourceFileHandle.CopyAsync(destinationFolder, fileName, NameCollisionOption.GenerateUniqueName); + + return copiedFile.Path; + } + + public async Task GetFileContentByApplicationUriAsync(string resourcePath) + { + var releaseNoteFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(resourcePath)); + + return await FileIO.ReadTextAsync(releaseNoteFile); + } + + public async Task GetFileStreamAsync(string folderPath, string fileName) + { + var folder = await StorageFolder.GetFolderFromPathAsync(folderPath); + var createdFile = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting); + + return await createdFile.OpenStreamForWriteAsync(); + } + } +} diff --git a/Wino.Core.UWP/Services/KeyPressService.cs b/Wino.Core.UWP/Services/KeyPressService.cs new file mode 100644 index 00000000..02a557d0 --- /dev/null +++ b/Wino.Core.UWP/Services/KeyPressService.cs @@ -0,0 +1,16 @@ +using Windows.System; +using Windows.UI.Core; +using Windows.UI.Xaml; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.UWP.Services +{ + public class KeyPressService : IKeyPressService + { + public bool IsCtrlKeyPressed() + => Window.Current?.CoreWindow?.GetKeyState(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down) ?? false; + + public bool IsShiftKeyPressed() + => Window.Current?.CoreWindow?.GetKeyState(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down) ?? false; + } +} diff --git a/Wino.Core.UWP/Services/NativeAppService.cs b/Wino.Core.UWP/Services/NativeAppService.cs new file mode 100644 index 00000000..54ac5bff --- /dev/null +++ b/Wino.Core.UWP/Services/NativeAppService.cs @@ -0,0 +1,131 @@ +using System; +using System.Threading.Tasks; +using Windows.ApplicationModel; +using Windows.Foundation.Metadata; +using Windows.Security.Authentication.Web; +using Windows.Security.Cryptography; +using Windows.Security.Cryptography.Core; +using Windows.Storage; +using Windows.Storage.Streams; +using Windows.System; +using Windows.UI.Shell; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Authorization; + +namespace Wino.Services +{ + public class NativeAppService : INativeAppService + { + private string _mimeMessagesFolder; + private string _editorBundlePath; + + public string GetWebAuthenticationBrokerUri() => WebAuthenticationBroker.GetCurrentApplicationCallbackUri().AbsoluteUri; + + public async Task GetMimeMessageStoragePath() + { + if (!string.IsNullOrEmpty(_mimeMessagesFolder)) + return _mimeMessagesFolder; + + var localFolder = ApplicationData.Current.LocalFolder; + var mimeFolder = await localFolder.CreateFolderAsync("Mime", CreationCollisionOption.OpenIfExists); + + _mimeMessagesFolder = mimeFolder.Path; + + return _mimeMessagesFolder; + } + + #region Cryptography + + public string randomDataBase64url(uint length) + { + IBuffer buffer = CryptographicBuffer.GenerateRandom(length); + return base64urlencodeNoPadding(buffer); + } + + public IBuffer sha256(string inputString) + { + HashAlgorithmProvider sha = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256); + IBuffer buff = CryptographicBuffer.ConvertStringToBinary(inputString, BinaryStringEncoding.Utf8); + return sha.HashData(buff); + } + + public string base64urlencodeNoPadding(IBuffer buffer) + { + string base64 = CryptographicBuffer.EncodeToBase64String(buffer); + + // Converts base64 to base64url. + base64 = base64.Replace("+", "-"); + base64 = base64.Replace("/", "_"); + + // Strips padding. + base64 = base64.Replace("=", ""); + + return base64; + } + + #endregion + + // GMail Integration. + public GoogleAuthorizationRequest GetGoogleAuthorizationRequest() + { + string state = randomDataBase64url(32); + string code_verifier = randomDataBase64url(32); + string code_challenge = base64urlencodeNoPadding(sha256(code_verifier)); + + return new GoogleAuthorizationRequest(state, code_verifier, code_challenge); + } + + public async Task GetQuillEditorBundlePathAsync() + { + if (string.IsNullOrEmpty(_editorBundlePath)) + { + var editorFileFromBundle = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///JS/Quill/full.html")) + .AsTask() + .ConfigureAwait(false); + + _editorBundlePath = editorFileFromBundle.Path; + } + + return _editorBundlePath; + } + + public bool IsAppRunning() => (Window.Current?.Content as Frame)?.Content != null; + + public async Task LaunchFileAsync(string filePath) + { + var file = await StorageFile.GetFileFromPathAsync(filePath); + + await Launcher.LaunchFileAsync(file); + } + + public Task LaunchUriAsync(Uri uri) => Xamarin.Essentials.Launcher.OpenAsync(uri); + + public string GetFullAppVersion() + { + Package package = Package.Current; + PackageId packageId = package.Id; + PackageVersion version = packageId.Version; + + return string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build, version.Revision); + } + + public async Task PinAppToTaskbarAsync() + { + // If Start screen manager API's aren't present + if (!ApiInformation.IsTypePresent("Windows.UI.Shell.TaskbarManager")) return; + + // Get the taskbar manager + var taskbarManager = TaskbarManager.GetDefault(); + + // If Taskbar doesn't allow pinning, don't show the tip + if (!taskbarManager.IsPinningAllowed) return; + + // If already pinned, don't show the tip + if (await taskbarManager.IsCurrentAppPinnedAsync()) return; + + await taskbarManager.RequestPinCurrentAppAsync(); + } + } +} diff --git a/Wino.Core.UWP/Services/NotificationBuilder.cs b/Wino.Core.UWP/Services/NotificationBuilder.cs new file mode 100644 index 00000000..3e389f8d --- /dev/null +++ b/Wino.Core.UWP/Services/NotificationBuilder.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Toolkit.Uwp.Notifications; +using Windows.Data.Xml.Dom; +using Windows.UI.Notifications; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Services; + +namespace Wino.Core.UWP.Services +{ + // TODO: Refactor this thing. It's garbage. + + public class NotificationBuilder : INotificationBuilder + { + private readonly IUnderlyingThemeService _underlyingThemeService; + private readonly IAccountService _accountService; + private readonly IFolderService _folderService; + + public NotificationBuilder(IUnderlyingThemeService underlyingThemeService, IAccountService accountService, IFolderService folderService) + { + _underlyingThemeService = underlyingThemeService; + _accountService = accountService; + _folderService = folderService; + } + + public async Task CreateNotificationsAsync(Guid inboxFolderId, IEnumerable newMailItems) + { + var mailCount = newMailItems.Count(); + + // If there are more than 3 mails, just display 1 general toast. + if (mailCount > 3) + { + var builder = new ToastContentBuilder(); + builder.SetToastScenario(ToastScenario.Default); + + builder.AddText(Translator.Notifications_MultipleNotificationsTitle); + builder.AddText(string.Format(Translator.Notifications_MultipleNotificationsTitle, mailCount)); + + builder.AddButton(GetDismissButton()); + + builder.Show(); + } + else + { + foreach (var mailItem in newMailItems) + { + if (mailItem.IsRead) + continue; + + var builder = new ToastContentBuilder(); + builder.SetToastScenario(ToastScenario.Default); + + var host = ThumbnailService.GetHost(mailItem.FromAddress); + + var knownTuple = ThumbnailService.CheckIsKnown(host); + + bool isKnown = knownTuple.Item1; + host = knownTuple.Item2; + + if (isKnown) + builder.AddAppLogoOverride(new System.Uri(ThumbnailService.GetKnownHostImage(host)), hintCrop: ToastGenericAppLogoCrop.Default); + else + { + // TODO: https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/adaptive-interactive-toasts?tabs=toolkit + // Follow official guides for icons/theme. + + bool isOSDarkTheme = _underlyingThemeService.IsUnderlyingThemeDark(); + string profileLogoName = isOSDarkTheme ? "profile-dark.png" : "profile-light.png"; + + builder.AddAppLogoOverride(new System.Uri($"ms-appx:///Assets/NotificationIcons/{profileLogoName}"), hintCrop: ToastGenericAppLogoCrop.Circle); + } + + // Override system notification timetamp with received date of the mail. + // It may create confusion for some users, but still it's the truth... + builder.AddCustomTimeStamp(mailItem.CreationDate.ToLocalTime()); + + builder.AddText(mailItem.FromName); + builder.AddText(mailItem.Subject); + builder.AddText(mailItem.PreviewText); + + builder.AddArgument(Constants.ToastMailItemIdKey, mailItem.UniqueId.ToString()); + builder.AddArgument(Constants.ToastActionKey, MailOperation.Navigate); + + builder.AddButton(GetMarkedAsRead(mailItem.Id)); + builder.AddButton(GetDeleteButton(mailItem.Id)); + builder.AddButton(GetDismissButton()); + + builder.Show(); + } + + await UpdateTaskbarIconBadgeAsync(); + } + } + + private ToastButton GetDismissButton() + => new ToastButton() + .SetDismissActivation() + .SetImageUri(new Uri("ms-appx:///Assets/NotificationIcons/dismiss.png")); + + private ToastButton GetDeleteButton(string mailCopyId) + => new ToastButton() + .SetContent(Translator.MailOperation_Delete) + .SetImageUri(new Uri("ms-appx:///Assets/NotificationIcons/delete.png")) + .AddArgument(Constants.ToastMailItemIdKey, mailCopyId) + .AddArgument(Constants.ToastActionKey, MailOperation.SoftDelete) + .SetBackgroundActivation(); + + private ToastButton GetMarkedAsRead(string mailCopyId) + => new ToastButton() + .SetContent(Translator.MailOperation_MarkAsRead) + .SetImageUri(new System.Uri("ms-appx:///Assets/NotificationIcons/markread.png")) + .AddArgument(Constants.ToastMailItemIdKey, mailCopyId) + .AddArgument(Constants.ToastActionKey, MailOperation.MarkAsRead) + .SetBackgroundActivation(); + + public async Task UpdateTaskbarIconBadgeAsync() + { + int totalUnreadCount = 0; + var badgeUpdater = BadgeUpdateManager.CreateBadgeUpdaterForApplication(); + + try + { + var accounts = await _accountService.GetAccountsAsync(); + + foreach (var account in accounts) + { + var accountInbox = await _folderService.GetSpecialFolderByAccountIdAsync(account.Id, SpecialFolderType.Inbox); + + if (accountInbox == null) + continue; + + var inboxUnreadCount = await _folderService.GetFolderNotificationBadgeAsync(accountInbox.Id); + + totalUnreadCount += inboxUnreadCount; + } + + if (totalUnreadCount > 0) + { + // Get the blank badge XML payload for a badge number + XmlDocument badgeXml = BadgeUpdateManager.GetTemplateContent(BadgeTemplateType.BadgeNumber); + + // Set the value of the badge in the XML to our number + XmlElement badgeElement = badgeXml.SelectSingleNode("/badge") as XmlElement; + badgeElement.SetAttribute("value", totalUnreadCount.ToString()); + + // Create the badge notification + BadgeNotification badge = new BadgeNotification(badgeXml); + + // And update the badge + badgeUpdater.Update(badge); + } + else + badgeUpdater.Clear(); + } + catch (System.Exception ex) + { + // TODO: Log exceptions. + + badgeUpdater.Clear(); + } + } + + public async Task CreateTestNotificationAsync(string title, string message) + { + // with args test. + await CreateNotificationsAsync(Guid.Parse("28c3c39b-7147-4de3-b209-949bd19eede6"), new List() + { + new MailCopy() + { + Subject = "test subject", + PreviewText = "preview html", + CreationDate = DateTime.UtcNow, + FromAddress = "bkaankose@outlook.com", + Id = "AAkALgAAAAAAHYQDEapmEc2byACqAC-EWg0AnMdP0zg8wkS_Ib2Eeh80LAAGq91I3QAA", + } + }); + + //var builder = new ToastContentBuilder(); + //builder.SetToastScenario(ToastScenario.Default); + + //builder.AddText(title); + //builder.AddText(message); + + //builder.Show(); + + //await Task.CompletedTask; + } + } +} diff --git a/Wino.Core.UWP/Services/StoreManagementService.cs b/Wino.Core.UWP/Services/StoreManagementService.cs new file mode 100644 index 00000000..b8f40787 --- /dev/null +++ b/Wino.Core.UWP/Services/StoreManagementService.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Windows.Services.Store; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Store; + +namespace Wino.Core.UWP.Services +{ + public class StoreManagementService : IStoreManagementService + { + private StoreContext CurrentContext { get; } + + private readonly Dictionary productIds = new Dictionary() + { + { StoreProductType.UnlimitedAccounts, "UnlimitedAccounts" } + }; + + private readonly Dictionary skuIds = new Dictionary() + { + { StoreProductType.UnlimitedAccounts, "9P02MXZ42GSM" } + }; + + public StoreManagementService() + { + CurrentContext = StoreContext.GetDefault(); + } + + public async Task HasProductAsync(StoreProductType productType) + { + var productKey = productIds[productType]; + var appLicense = await CurrentContext.GetAppLicenseAsync(); + + if (appLicense == null) + return false; + + // Access the valid licenses for durable add-ons for this app. + foreach (KeyValuePair item in appLicense.AddOnLicenses) + { + StoreLicense addOnLicense = item.Value; + + if (addOnLicense.InAppOfferToken == productKey) + { + return addOnLicense.IsActive; + } + } + + return false; + } + + public async Task PurchaseAsync(StoreProductType productType) + { + if (await HasProductAsync(productType)) + return Domain.Enums.StorePurchaseResult.AlreadyPurchased; + else + { + var productKey = skuIds[productType]; + + var result = await CurrentContext.RequestPurchaseAsync(productKey); + + switch (result.Status) + { + case StorePurchaseStatus.Succeeded: + return Domain.Enums.StorePurchaseResult.Succeeded; + case StorePurchaseStatus.AlreadyPurchased: + return Domain.Enums.StorePurchaseResult.AlreadyPurchased; + default: + return Domain.Enums.StorePurchaseResult.NotPurchased; + } + } + } + } +} diff --git a/Wino.Core.UWP/Services/StoreRatingService.cs b/Wino.Core.UWP/Services/StoreRatingService.cs new file mode 100644 index 00000000..df33dcab --- /dev/null +++ b/Wino.Core.UWP/Services/StoreRatingService.cs @@ -0,0 +1,137 @@ +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using Windows.ApplicationModel.Core; +using Windows.Services.Store; +using Windows.System; +using Wino.Core.Domain; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.UWP.Services +{ + public class StoreRatingService : IStoreRatingService + { + private const string RatedStorageKey = nameof(RatedStorageKey); + private const string LatestAskedKey = nameof(LatestAskedKey); + + private readonly IConfigurationService _configurationService; + private readonly IDialogService _dialogService; + + public StoreRatingService(IConfigurationService configurationService, IDialogService dialogService) + { + _configurationService = configurationService; + _dialogService = dialogService; + } + + private void SetRated() + => _configurationService.SetRoaming(RatedStorageKey, true); + + private bool IsAskingThresholdExceeded() + { + var latestAskedDate = _configurationService.Get(LatestAskedKey, DateTime.MinValue); + + // Never asked before. + // Set the threshold and wait for the next trigger. + + if (latestAskedDate == DateTime.MinValue) + { + _configurationService.Set(LatestAskedKey, DateTime.UtcNow); + } + else if (DateTime.UtcNow >= latestAskedDate.AddMinutes(30)) + { + return true; + } + + return false; + } + + public async Task PromptRatingDialogAsync() + { + // Annoying. + if (Debugger.IsAttached) return; + + // Swallow all exceptions. App should not crash in any errors. + + try + { + bool isRated = _configurationService.GetRoaming(RatedStorageKey, false); + + if (isRated) return; + + if (!isRated) + { + if (!IsAskingThresholdExceeded()) return; + + var ratingDialogResult = await _dialogService.ShowRatingDialogAsync(); + + if (ratingDialogResult == null) + return; + + if (ratingDialogResult.DontAskAgain) + SetRated(); + + if (ratingDialogResult.RateWinoClicked) + { + // In case of failure of this call, we will navigate users to Store page directly. + + try + { + await ShowPortableRatingDialogAsync(); + } + catch (Exception) + { + await Launcher.LaunchUriAsync(new Uri($"ms-windows-store://review/?ProductId=9NCRCVJC50WL")); + } + } + } + } + catch (Exception) { } + finally + { + _configurationService.Set(LatestAskedKey, DateTime.UtcNow); + } + } + + private async Task ShowPortableRatingDialogAsync() + { + var _storeContext = StoreContext.GetDefault(); + + StoreRateAndReviewResult result = await _storeContext.RequestRateAndReviewAppAsync(); + + // Check status + switch (result.Status) + { + case StoreRateAndReviewStatus.Succeeded: + if (result.WasUpdated) + _dialogService.InfoBarMessage(Translator.Info_ReviewSuccessTitle, Translator.Info_ReviewUpdatedMessage, Domain.Enums.InfoBarMessageType.Success); + else + _dialogService.InfoBarMessage(Translator.Info_ReviewSuccessTitle, Translator.Info_ReviewNewMessage, Domain.Enums.InfoBarMessageType.Success); + + SetRated(); + break; + case StoreRateAndReviewStatus.CanceledByUser: + break; + + case StoreRateAndReviewStatus.NetworkError: + _dialogService.InfoBarMessage(Translator.Info_ReviewNetworkErrorTitle, Translator.Info_ReviewNetworkErrorMessage, Domain.Enums.InfoBarMessageType.Warning); + break; + default: + _dialogService.InfoBarMessage(Translator.Info_ReviewUnknownErrorTitle, string.Format(Translator.Info_ReviewUnknownErrorMessage, result.ExtendedError.Message), Domain.Enums.InfoBarMessageType.Warning); + break; + } + } + + public async Task LaunchStorePageForReviewAsync() + { + try + { + await CoreApplication.GetCurrentView()?.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => + { + // TODO: Get it from package info. + await Launcher.LaunchUriAsync(new Uri($"ms-windows-store://review/?ProductId=9NCRCVJC50WL")); + }); + } + catch (Exception) { } + } + } +} diff --git a/Wino.Core.UWP/Services/ThemeService.cs b/Wino.Core.UWP/Services/ThemeService.cs new file mode 100644 index 00000000..9649a625 --- /dev/null +++ b/Wino.Core.UWP/Services/ThemeService.cs @@ -0,0 +1,446 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.Messaging; +using Microsoft.Toolkit.Uwp.Helpers; +using Newtonsoft.Json; +using Windows.Storage; +using Windows.UI; +using Windows.UI.ViewManagement; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Markup; +using Windows.UI.Xaml.Media; +using Wino.Core.Domain; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Exceptions; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Personalization; +using Wino.Core.Messages.Shell; +using Wino.Core.UWP.Extensions; +using Wino.Core.UWP.Models.Personalization; +using Wino.Core.UWP.Services; + +namespace Wino.Services +{ + /// + /// Class providing functionality around switching and restoring theme settings + /// + public class ThemeService : IThemeService + { + public const string CustomThemeFolderName = "CustomThemes"; + + private static string _micaThemeId = "a160b1b0-2ab8-4e97-a803-f4050f036e25"; + private static string _acrylicThemeId = "fc08e58c-36fd-46e2-a562-26cf277f1467"; + private static string _cloudsThemeId = "3b621cc2-e270-4a76-8477-737917cccda0"; + private static string _forestThemeId = "8bc89b37-a7c5-4049-86e2-de1ae8858dbd"; + private static string _nightyThemeId = "5b65e04e-fd7e-4c2d-8221-068d3e02d23a"; + private static string _snowflakeThemeId = "e143ddde-2e28-4846-9d98-dad63d6505f1"; + private static string _gardenThemeId = "698e4466-f88c-4799-9c61-f0ea1308ed49"; + + private Frame mainApplicationFrame = null; + + public event EventHandler ElementThemeChanged; + public event EventHandler AccentColorChanged; + public event EventHandler AccentColorChangedBySystem; + + private const string AccentColorKey = nameof(AccentColorKey); + private const string CurrentApplicationThemeKey = nameof(CurrentApplicationThemeKey); + + // Custom theme + public const string CustomThemeAccentColorKey = nameof(CustomThemeAccentColorKey); + + // Keep reference so it does not get optimized/garbage collected + private readonly UISettings uiSettings = new UISettings(); + + private readonly IConfigurationService _configurationService; + private readonly IUnderlyingThemeService _underlyingThemeService; + private readonly IApplicationResourceManager _applicationResourceManager; + + private List preDefinedThemes { get; set; } = new List() + { + new SystemAppTheme("Mica", Guid.Parse(_micaThemeId)), + new SystemAppTheme("Acrylic", Guid.Parse(_acrylicThemeId)), + new PreDefinedAppTheme("Nighty", Guid.Parse(_nightyThemeId), "#e1b12c", ApplicationElementTheme.Dark), + new PreDefinedAppTheme("Forest", Guid.Parse(_forestThemeId), "#16a085", ApplicationElementTheme.Dark), + new PreDefinedAppTheme("Clouds", Guid.Parse(_cloudsThemeId), "#0984e3", ApplicationElementTheme.Light), + new PreDefinedAppTheme("Snowflake", Guid.Parse(_snowflakeThemeId), "#4a69bd", ApplicationElementTheme.Light), + new PreDefinedAppTheme("Garden", Guid.Parse(_gardenThemeId), "#05c46b", ApplicationElementTheme.Light), + }; + + public ThemeService(IConfigurationService configurationService, + IUnderlyingThemeService underlyingThemeService, + IApplicationResourceManager applicationResourceManager) + { + _configurationService = configurationService; + _underlyingThemeService = underlyingThemeService; + _applicationResourceManager = applicationResourceManager; + } + + /// + /// Gets or sets (with LocalSettings persistence) the RequestedTheme of the root element. + /// + public ApplicationElementTheme RootTheme + { + get + { + if (mainApplicationFrame == null) return ApplicationElementTheme.Default; + + return mainApplicationFrame.RequestedTheme.ToWinoElementTheme(); + } + set + { + if (mainApplicationFrame == null) + return; + + mainApplicationFrame.RequestedTheme = value.ToWindowsElementTheme(); + + _configurationService.Set(UnderlyingThemeService.SelectedAppThemeKey, value); + + UpdateSystemCaptionButtonColors(); + + // PopupRoot usually needs to react to changes. + NotifyThemeUpdate(); + } + } + + + private Guid currentApplicationThemeId; + + public Guid CurrentApplicationThemeId + { + get { return currentApplicationThemeId; } + set + { + currentApplicationThemeId = value; + + _configurationService.Set(CurrentApplicationThemeKey, value); + + _ = mainApplicationFrame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, async () => + { + await ApplyCustomThemeAsync(false); + }); + } + } + + + private string accentColor; + + public string AccentColor + { + get { return accentColor; } + set + { + accentColor = value; + + UpdateAccentColor(value); + + _configurationService.Set(AccentColorKey, value); + AccentColorChanged?.Invoke(this, value); + } + } + + public async Task InitializeAsync() + { + // Already initialized. There is no need. + if (mainApplicationFrame != null) + return; + + // Save reference as this might be null when the user is in another app + + mainApplicationFrame = Window.Current.Content as Frame; + + if (mainApplicationFrame == null) return; + + RootTheme = _configurationService.Get(UnderlyingThemeService.SelectedAppThemeKey, ApplicationElementTheme.Default); + AccentColor = _configurationService.Get(AccentColorKey, string.Empty); + + // Set the current theme id. Default to Mica. + var applicationThemeGuid = _configurationService.Get(CurrentApplicationThemeKey, _micaThemeId); + + currentApplicationThemeId = Guid.Parse(applicationThemeGuid); + + await ApplyCustomThemeAsync(true); + + // Registering to color changes, thus we notice when user changes theme system wide + uiSettings.ColorValuesChanged += UISettingsColorChanged; + } + + private void NotifyThemeUpdate() + { + if (mainApplicationFrame == null || mainApplicationFrame.Dispatcher == null) return; + + _ = mainApplicationFrame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () => + { + ElementThemeChanged?.Invoke(this, RootTheme); + WeakReferenceMessenger.Default.Send(new ApplicationThemeChanged(_underlyingThemeService.IsUnderlyingThemeDark())); + }); + } + + private void UISettingsColorChanged(UISettings sender, object args) + { + // Make sure we have a reference to our window so we dispatch a UI change + if (mainApplicationFrame != null) + { + // Dispatch on UI thread so that we have a current appbar to access and change + + _ = mainApplicationFrame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () => + { + UpdateSystemCaptionButtonColors(); + + var accentColor = sender.GetColorValue(UIColorType.Accent); + //AccentColorChangedBySystem?.Invoke(this, accentColor.ToHex()); + }); + } + + NotifyThemeUpdate(); + } + + public void UpdateSystemCaptionButtonColors() + { + if (mainApplicationFrame == null) return; + + _ = mainApplicationFrame.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => + { + ApplicationViewTitleBar titleBar = ApplicationView.GetForCurrentView().TitleBar; + + if (titleBar == null) return; + + if (_underlyingThemeService.IsUnderlyingThemeDark()) + { + titleBar.ButtonForegroundColor = Colors.White; + } + else + { + titleBar.ButtonForegroundColor = Colors.Black; + } + }); + } + + public void UpdateAccentColor(string hex) + { + // Change accent color if specified. + if (!string.IsNullOrEmpty(hex)) + { + var brush = new SolidColorBrush(Microsoft.Toolkit.Uwp.Helpers.ColorHelper.ToColor(hex)); + + if (_applicationResourceManager.ContainsResourceKey("SystemAccentColor")) + _applicationResourceManager.ReplaceResource("SystemAccentColor", brush); + + if (_applicationResourceManager.ContainsResourceKey("NavigationViewSelectionIndicatorForeground")) + _applicationResourceManager.ReplaceResource("NavigationViewSelectionIndicatorForeground", brush); + + RefreshThemeResource(); + } + } + + private void RefreshThemeResource() + { + if (mainApplicationFrame == null) return; + + if (mainApplicationFrame.RequestedTheme == ElementTheme.Dark) + { + mainApplicationFrame.RequestedTheme = ElementTheme.Light; + mainApplicationFrame.RequestedTheme = ElementTheme.Dark; + } + else if (mainApplicationFrame.RequestedTheme == ElementTheme.Light) + { + mainApplicationFrame.RequestedTheme = ElementTheme.Dark; + mainApplicationFrame.RequestedTheme = ElementTheme.Light; + } + else + { + var isUnderlyingDark = _underlyingThemeService.IsUnderlyingThemeDark(); + + mainApplicationFrame.RequestedTheme = isUnderlyingDark ? ElementTheme.Light : ElementTheme.Dark; + mainApplicationFrame.RequestedTheme = ElementTheme.Default; + } + } + + public async Task ApplyCustomThemeAsync(bool isInitializing) + { + AppThemeBase applyingTheme = null; + + var controlThemeList = new List(preDefinedThemes); + + // Don't search for custom themes if applying theme is already in pre-defined templates. + // This is important for startup performance because we won't be loading the custom themes on launch. + + bool isApplyingPreDefinedTheme = preDefinedThemes.Exists(a => a.Id == currentApplicationThemeId); + + if (isApplyingPreDefinedTheme) + { + applyingTheme = preDefinedThemes.Find(a => a.Id == currentApplicationThemeId); + } + else + { + // User applied custom theme. Load custom themes and find it there. + // Fallback to Mica if nothing found. + + var customThemes = await GetCurrentCustomThemesAsync(); + + controlThemeList.AddRange(customThemes.Select(a => new CustomAppTheme(a))); + + applyingTheme = controlThemeList.Find(a => a.Id == currentApplicationThemeId) ?? preDefinedThemes.First(a => a.Id == Guid.Parse(_micaThemeId)); + } + + try + { + var existingThemeDictionary = _applicationResourceManager.GetLastResource(); + + if (existingThemeDictionary != null && existingThemeDictionary.TryGetValue("ThemeName", out object themeNameString)) + { + var themeName = themeNameString.ToString(); + + // Applying different theme. + if (themeName != applyingTheme.ThemeName) + { + var resourceDictionaryContent = await applyingTheme.GetThemeResourceDictionaryContentAsync(); + + var resourceDictionary = XamlReader.Load(resourceDictionaryContent) as ResourceDictionary; + + // Custom themes require special attention for background image because + // they share the same base theme resource dictionary. + + if (applyingTheme is CustomAppTheme) + { + resourceDictionary["ThemeBackgroundImage"] = $"ms-appdata:///local/{CustomThemeFolderName}/{applyingTheme.Id}.jpg"; + } + + _applicationResourceManager.RemoveResource(existingThemeDictionary); + _applicationResourceManager.AddResource(resourceDictionary); + + bool isSystemTheme = applyingTheme is SystemAppTheme || applyingTheme is CustomAppTheme; + + if (isSystemTheme) + { + // For system themes, set the RootElement theme from saved values. + // Potential bug: When we set it to system default, theme is not applied when system and + // app element theme is different :) + + var savedElement = _configurationService.Get(UnderlyingThemeService.SelectedAppThemeKey, ApplicationElementTheme.Default); + RootTheme = savedElement; + + // Quickly switch theme to apply theme resource changes. + RefreshThemeResource(); + } + else + RootTheme = applyingTheme.ForceElementTheme; + + // Theme has accent color. Override. + if (!isInitializing) + { + AccentColor = applyingTheme.AccentColor; + } + } + else + UpdateSystemCaptionButtonColors(); + } + } + catch (Exception ex) + { + Debug.WriteLine($"Apply theme failed -> {ex.Message}"); + } + } + + public async Task> GetAvailableThemesAsync() + { + var availableThemes = new List(preDefinedThemes); + + var customThemes = await GetCurrentCustomThemesAsync(); + + availableThemes.AddRange(customThemes.Select(a => new CustomAppTheme(a))); + + return availableThemes; + } + + public async Task CreateNewCustomThemeAsync(string themeName, string accentColor, byte[] wallpaperData) + { + if (wallpaperData == null || wallpaperData.Length == 0) + throw new CustomThemeCreationFailedException(Translator.Exception_CustomThemeMissingWallpaper); + + if (string.IsNullOrEmpty(themeName)) + throw new CustomThemeCreationFailedException(Translator.Exception_CustomThemeMissingName); + + var themes = await GetCurrentCustomThemesAsync(); + + if (themes.Exists(a => a.Name == themeName)) + throw new CustomThemeCreationFailedException(Translator.Exception_CustomThemeExists); + + var newTheme = new CustomThemeMetadata() + { + Id = Guid.NewGuid(), + Name = themeName, + AccentColorHex = accentColor + }; + + // Save wallpaper. + // Filename would be the same as metadata id, in jpg format. + + var themeFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(CustomThemeFolderName, CreationCollisionOption.OpenIfExists); + + var wallpaperFile = await themeFolder.CreateFileAsync($"{newTheme.Id}.jpg", CreationCollisionOption.ReplaceExisting); + await FileIO.WriteBytesAsync(wallpaperFile, wallpaperData); + + // Generate thumbnail for settings page. + + var thumbnail = await wallpaperFile.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.PicturesView); + var thumbnailFile = await themeFolder.CreateFileAsync($"{newTheme.Id}_preview.jpg", CreationCollisionOption.ReplaceExisting); + + using (var readerStream = thumbnail.AsStreamForRead()) + { + byte[] bytes = new byte[readerStream.Length]; + + await readerStream.ReadAsync(bytes, 0, bytes.Length); + + var buffer = bytes.AsBuffer(); + + await FileIO.WriteBufferAsync(thumbnailFile, buffer); + } + + // Save metadata. + var metadataFile = await themeFolder.CreateFileAsync($"{newTheme.Id}.json", CreationCollisionOption.ReplaceExisting); + + var serialized = JsonConvert.SerializeObject(newTheme); + await FileIO.WriteTextAsync(metadataFile, serialized); + + return newTheme; + } + + public async Task> GetCurrentCustomThemesAsync() + { + var results = new List(); + + var themeFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(CustomThemeFolderName, CreationCollisionOption.OpenIfExists); + + var allFiles = await themeFolder.GetFilesAsync(); + + var themeMetadatas = allFiles.Where(a => a.FileType == ".json"); + + foreach (var theme in themeMetadatas) + { + var metadata = await GetCustomMetadataAsync(theme).ConfigureAwait(false); + + if (metadata == null) continue; + + results.Add(metadata); + } + + return results; + } + + private async Task GetCustomMetadataAsync(IStorageFile file) + { + var fileContent = await FileIO.ReadTextAsync(file); + + return JsonConvert.DeserializeObject(fileContent); + } + + public string GetSystemAccentColorHex() + => uiSettings.GetColorValue(UIColorType.Accent).ToHex(); + } +} diff --git a/Wino.Core.UWP/Services/UnderlyingThemeService.cs b/Wino.Core.UWP/Services/UnderlyingThemeService.cs new file mode 100644 index 00000000..5f88a393 --- /dev/null +++ b/Wino.Core.UWP/Services/UnderlyingThemeService.cs @@ -0,0 +1,32 @@ +using Windows.UI.ViewManagement; +using Windows.UI.Xaml; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.UWP.Services +{ + public class UnderlyingThemeService : IUnderlyingThemeService + { + public const string SelectedAppThemeKey = nameof(SelectedAppThemeKey); + + private readonly UISettings uiSettings = new UISettings(); + private readonly IConfigurationService _configurationService; + + public UnderlyingThemeService(IConfigurationService configurationService) + { + _configurationService = configurationService; + } + + // This should not rely on application window to be present. + // Check theme from the settings, rely on UISettings background color if Default. + + public bool IsUnderlyingThemeDark() + { + var currentTheme = _configurationService.Get(SelectedAppThemeKey, ElementTheme.Default); + + if (currentTheme == ElementTheme.Default) + return uiSettings.GetColorValue(UIColorType.Background).ToString() == "#FF000000"; + else + return currentTheme == ElementTheme.Dark; + } + } +} diff --git a/Wino.Core.UWP/Wino.Core.UWP.csproj b/Wino.Core.UWP/Wino.Core.UWP.csproj new file mode 100644 index 00000000..b46724c3 --- /dev/null +++ b/Wino.Core.UWP/Wino.Core.UWP.csproj @@ -0,0 +1,184 @@ + + + + + Debug + AnyCPU + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8} + Library + Properties + Wino.Core.UWP + Wino.Core.UWP + en-US + UAP + 10.0.22621.0 + 10.0.17763.0 + 14 + 512 + {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + prompt + 4 + + + x86 + true + bin\x86\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + x86 + bin\x86\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + ARM + true + bin\ARM\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + ARM + bin\ARM\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + ARM64 + true + bin\ARM64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + ARM64 + bin\ARM64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + x64 + true + bin\x64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + false + prompt + + + x64 + bin\x64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + false + prompt + + + PackageReference + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5.0.3 + + + 6.2.14 + + + 7.1.3 + + + 7.1.3 + + + + + {cf3312e5-5da0-4867-9945-49ea7598af1f} + Wino.Core.Domain + + + {e6b1632a-8901-41e8-9ddf-6793c7698b0b} + Wino.Core + + + + + 14.0 + + + + \ No newline at end of file diff --git a/Wino.Core/Authenticators/BaseAuthenticator.cs b/Wino.Core/Authenticators/BaseAuthenticator.cs new file mode 100644 index 00000000..b1ab4374 --- /dev/null +++ b/Wino.Core/Authenticators/BaseAuthenticator.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Services; + +namespace Wino.Core.Authenticators +{ + public abstract class BaseAuthenticator + { + public abstract MailProviderType ProviderType { get; } + + protected ITokenService TokenService { get; } + + protected BaseAuthenticator(ITokenService tokenService) + { + TokenService = tokenService; + } + + internal Task SaveTokenInternalAsync(MailAccount account, TokenInformation tokenInformation) + => TokenService.SaveTokenInformationAsync(account.Id, tokenInformation); + } +} diff --git a/Wino.Core/Authenticators/CustomAuthenticator.cs b/Wino.Core/Authenticators/CustomAuthenticator.cs new file mode 100644 index 00000000..a254d66c --- /dev/null +++ b/Wino.Core/Authenticators/CustomAuthenticator.cs @@ -0,0 +1,34 @@ +using System; +using System.Threading.Tasks; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Services; + +namespace Wino.Core.Authenticators +{ + public class CustomAuthenticator : BaseAuthenticator, IAuthenticator + { + public CustomAuthenticator(ITokenService tokenService) : base(tokenService) { } + + public override MailProviderType ProviderType => MailProviderType.IMAP4; + + public string ClientId => throw new NotImplementedException(); // Not needed. + + public event EventHandler InteractiveAuthenticationRequired; + + public void CancelAuthorization() { } + + public void ContinueAuthorization(Uri authorizationResponseUri) { } + + public Task GenerateTokenAsync(MailAccount account, bool saveToken) + { + throw new NotImplementedException(); + } + + public Task GetTokenAsync(MailAccount account) + { + throw new NotImplementedException(); + } + } +} diff --git a/Wino.Core/Authenticators/GmailAuthenticator.cs b/Wino.Core/Authenticators/GmailAuthenticator.cs new file mode 100644 index 00000000..3c77136e --- /dev/null +++ b/Wino.Core/Authenticators/GmailAuthenticator.cs @@ -0,0 +1,219 @@ +using System; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; +using Nito.AsyncEx; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Exceptions; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Authentication; +using Wino.Core.Domain.Models.Authorization; +using Wino.Core.Services; +using Xamarin.Essentials; + +namespace Wino.Core.Authenticators +{ + public class GmailAuthenticator : BaseAuthenticator, IAuthenticator + { + public string ClientId { get; } = "973025879644-s7b4ur9p3rlgop6a22u7iuptdc0brnrn.apps.googleusercontent.com"; + + private const string TokenEndpoint = "https://www.googleapis.com/oauth2/v4/token"; + private const string RefreshTokenEndpoint = "https://oauth2.googleapis.com/token"; + private const string UserInfoEndpoint = "https://gmail.googleapis.com/gmail/v1/users/me/profile"; + + public override MailProviderType ProviderType => MailProviderType.Gmail; + + private TaskCompletionSource _authorizationCompletionSource = null; + private CancellationTokenSource _authorizationCancellationTokenSource = null; + + private readonly INativeAppService _nativeAppService; + + public event EventHandler InteractiveAuthenticationRequired; + + public GmailAuthenticator(ITokenService tokenService, INativeAppService nativeAppService) : base(tokenService) + { + _nativeAppService = nativeAppService; + } + + /// + /// Performs tokenization code exchange and retrieves the actual Access - Refresh tokens from Google + /// after redirect uri returns from browser. + /// + /// Tokenization request. + /// In case of network or parsing related error. + private async Task PerformCodeExchangeAsync(GoogleTokenizationRequest tokenizationRequest) + { + var uri = tokenizationRequest.BuildRequest(); + + var content = new StringContent(uri, Encoding.UTF8, "application/x-www-form-urlencoded"); + + var handler = new HttpClientHandler() + { + AllowAutoRedirect = true + }; + + var client = new HttpClient(handler); + + var response = await client.PostAsync(TokenEndpoint, content); + string responseString = await response.Content.ReadAsStringAsync(); + + if (!response.IsSuccessStatusCode) + throw new GoogleAuthenticationException(Translator.Exception_GoogleAuthorizationCodeExchangeFailed); + + var parsed = JObject.Parse(responseString); + + if (parsed.ContainsKey("error")) + throw new GoogleAuthenticationException(parsed["error"]["message"].Value()); + + var accessToken = parsed["access_token"].Value(); + var refreshToken = parsed["refresh_token"].Value(); + var expiresIn = parsed["expires_in"].Value(); + + var expirationDate = DateTime.UtcNow.AddSeconds(expiresIn); + + client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); + + // Get basic user info for UserName. + + var userinfoResponse = await client.GetAsync(UserInfoEndpoint); + string userinfoResponseContent = await userinfoResponse.Content.ReadAsStringAsync(); + + var parsedUserInfo = JObject.Parse(userinfoResponseContent); + + if (parsedUserInfo.ContainsKey("error")) + throw new GoogleAuthenticationException(parsedUserInfo["error"]["message"].Value()); + + var username = parsedUserInfo["emailAddress"].Value(); + + return new TokenInformation() + { + Id = Guid.NewGuid(), + Address = username, + AccessToken = accessToken, + RefreshToken = refreshToken, + ExpiresAt = expirationDate + }; + } + + public void ContinueAuthorization(Uri authorizationResponseUri) => _authorizationCompletionSource?.TrySetResult(authorizationResponseUri); + + public async Task GetTokenAsync(MailAccount account) + { + var cachedToken = await TokenService.GetTokenInformationAsync(account.Id) + ?? throw new AuthenticationAttentionException(account); + + if (cachedToken.IsExpired) + { + // Refresh token with new exchanges. + // No need to check Username for account. + + var refreshedTokenInfoBase = await RefreshTokenAsync(cachedToken.RefreshToken); + + cachedToken.RefreshTokens(refreshedTokenInfoBase); + + // Save new token and return. + await SaveTokenInternalAsync(account, cachedToken); + } + + return cachedToken; + } + + + public async Task GenerateTokenAsync(MailAccount account, bool saveToken) + { + var authRequest = _nativeAppService.GetGoogleAuthorizationRequest(); + + _authorizationCompletionSource = new TaskCompletionSource(); + _authorizationCancellationTokenSource = new CancellationTokenSource(); + + var authorizationUri = authRequest.BuildRequest(ClientId); + + await Browser.OpenAsync(authorizationUri, BrowserLaunchMode.SystemPreferred); + + Uri responseRedirectUri = null; + + try + { + responseRedirectUri = await _authorizationCompletionSource.Task.WaitAsync(_authorizationCancellationTokenSource.Token); + } + catch (OperationCanceledException) + { + throw new AuthenticationException(Translator.Exception_AuthenticationCanceled); + } + finally + { + _authorizationCancellationTokenSource.Dispose(); + _authorizationCancellationTokenSource = null; + _authorizationCompletionSource = null; + } + + authRequest.ValidateAuthorizationCode(responseRedirectUri); + + // Start tokenization. + var tokenizationRequest = new GoogleTokenizationRequest(authRequest); + var tokenInformation = await PerformCodeExchangeAsync(tokenizationRequest); + + if (saveToken) + { + await SaveTokenInternalAsync(account, tokenInformation); + } + + return tokenInformation; + } + + /// + /// Internally exchanges refresh token with a new access token and returns new TokenInformation. + /// + /// Token to be used in refreshing. + /// New TokenInformationBase that has new tokens and expiration date without a username. This token is not saved to database after returned. + private async Task RefreshTokenAsync(string refresh_token) + { + // TODO: This doesn't work. + var refreshUri = string.Format("client_id={0}&refresh_token={1}&grant_type=refresh_token", ClientId, refresh_token); + + //Uri.EscapeDataString(refreshUri); + var content = new StringContent(refreshUri, Encoding.UTF8, "application/x-www-form-urlencoded"); + + var client = new HttpClient(); + + var response = await client.PostAsync(RefreshTokenEndpoint, content); + + string responseString = await response.Content.ReadAsStringAsync(); + + var parsed = JObject.Parse(responseString); + + // TODO: Error parsing is incorrect. + if (parsed.ContainsKey("error")) + throw new GoogleAuthenticationException(parsed["error_description"].Value()); + + var accessToken = parsed["access_token"].Value(); + + string activeRefreshToken = refresh_token; + + // Refresh token might not be returned. + // In this case older refresh token is still available for new refreshes. + // Only change if provided. + + if (parsed.ContainsKey("refresh_token")) + { + activeRefreshToken = parsed["refresh_token"].Value(); + } + + var expiresIn = parsed["expires_in"].Value(); + var expirationDate = DateTime.UtcNow.AddSeconds(expiresIn); + + return new TokenInformationBase() + { + AccessToken = accessToken, + ExpiresAt = expirationDate, + RefreshToken = activeRefreshToken + }; + } + + public void CancelAuthorization() => _authorizationCancellationTokenSource?.Cancel(); + } +} diff --git a/Wino.Core/Authenticators/Office365Authenticator.cs b/Wino.Core/Authenticators/Office365Authenticator.cs new file mode 100644 index 00000000..916be96c --- /dev/null +++ b/Wino.Core/Authenticators/Office365Authenticator.cs @@ -0,0 +1,13 @@ +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Services; + +namespace Wino.Core.Authenticators +{ + public class Office365Authenticator : OutlookAuthenticator + { + public Office365Authenticator(ITokenService tokenService, INativeAppService nativeAppService) : base(tokenService, nativeAppService) { } + + public override MailProviderType ProviderType => MailProviderType.Office365; + } +} diff --git a/Wino.Core/Authenticators/OutlookAuthenticator.cs b/Wino.Core/Authenticators/OutlookAuthenticator.cs new file mode 100644 index 00000000..878d1c91 --- /dev/null +++ b/Wino.Core/Authenticators/OutlookAuthenticator.cs @@ -0,0 +1,115 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Identity.Client; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Exceptions; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Extensions; +using Wino.Core.Services; + +namespace Wino.Core.Authenticators +{ + public class OutlookAuthenticator : BaseAuthenticator, IAuthenticator + { + // Outlook + private const string Authority = "https://login.microsoftonline.com/common"; + + public string ClientId { get; } = "b19c2035-d740-49ff-b297-de6ec561b208"; + + private readonly string[] MailScope = new string[] { "email", "mail.readwrite", "offline_access", "mail.send" }; + + public override MailProviderType ProviderType => MailProviderType.Outlook; + + private readonly IPublicClientApplication _publicClientApplication; + + public OutlookAuthenticator(ITokenService tokenService, INativeAppService nativeAppService) : base(tokenService) + { + var authenticationRedirectUri = nativeAppService.GetWebAuthenticationBrokerUri(); + + _publicClientApplication = PublicClientApplicationBuilder.Create(ClientId) + .WithAuthority(Authority) + .WithRedirectUri(authenticationRedirectUri) + .Build(); + } + +#pragma warning disable S1133 // Deprecated code should be removed + [Obsolete("Not used for OutlookAuthenticator.")] +#pragma warning restore S1133 // Deprecated code should be removed + public void ContinueAuthorization(Uri authorizationResponseUri) { } + +#pragma warning disable S1133 // Deprecated code should be removed + [Obsolete("Not used for OutlookAuthenticator.")] +#pragma warning restore S1133 // Deprecated code should be removed + public void CancelAuthorization() { } + + public async Task GetTokenAsync(MailAccount account) + { + var cachedToken = await TokenService.GetTokenInformationAsync(account.Id) + ?? throw new AuthenticationAttentionException(account); + + // We have token but it's expired. + // Silently refresh the token and save new token. + + if (cachedToken.IsExpired) + { + var cachedOutlookAccount = (await _publicClientApplication.GetAccountsAsync()).FirstOrDefault(a => a.Username == account.Address); + + // Again, not expected at all... + // Force interactive login at this point. + + if (cachedOutlookAccount == null) + { + // What if interactive login info is for different account? + + return await GenerateTokenAsync(account, true); + } + else + { + // Silently refresh token from cache. + + AuthenticationResult authResult = await _publicClientApplication.AcquireTokenSilent(MailScope, cachedOutlookAccount).ExecuteAsync(); + + // Save refreshed token and return + var refreshedTokenInformation = authResult.CreateTokenInformation(); + + await TokenService.SaveTokenInformationAsync(account.Id, refreshedTokenInformation); + + return refreshedTokenInformation; + } + } + else + return cachedToken; + } + + public async Task GenerateTokenAsync(MailAccount account, bool saveToken) + { + try + { + var authResult = await _publicClientApplication + .AcquireTokenInteractive(MailScope) + .ExecuteAsync(); + + var tokenInformation = authResult.CreateTokenInformation(); + + if (saveToken) + { + await SaveTokenInternalAsync(account, tokenInformation); + } + + return tokenInformation; + } + catch (MsalClientException msalClientException) + { + if (msalClientException.ErrorCode == "authentication_canceled" || msalClientException.ErrorCode == "access_denied") + throw new AccountSetupCanceledException(); + + throw; + } + + throw new AuthenticationException(Translator.Exception_UnknowErrorDuringAuthentication, new Exception(Translator.Exception_TokenGenerationFailed)); + } + } +} diff --git a/Wino.Core/Authenticators/YahooAuthenticator.cs b/Wino.Core/Authenticators/YahooAuthenticator.cs new file mode 100644 index 00000000..f42842ca --- /dev/null +++ b/Wino.Core/Authenticators/YahooAuthenticator.cs @@ -0,0 +1,40 @@ +using System; +using System.Threading.Tasks; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Services; + +namespace Wino.Core.Authenticators +{ + public class YahooAuthenticator : BaseAuthenticator, IAuthenticator + { + public YahooAuthenticator(ITokenService tokenService) : base(tokenService) { } + + public override MailProviderType ProviderType => MailProviderType.Yahoo; + + public string ClientId => throw new NotImplementedException(); + + public event EventHandler InteractiveAuthenticationRequired; + + public void CancelAuthorization() + { + throw new NotImplementedException(); + } + + public void ContinueAuthorization(Uri authorizationResponseUri) + { + throw new NotImplementedException(); + } + + public Task GenerateTokenAsync(MailAccount account, bool saveToken) + { + throw new NotImplementedException(); + } + + public Task GetTokenAsync(MailAccount account) + { + throw new NotImplementedException(); + } + } +} diff --git a/Wino.Core/CoreContainerSetup.cs b/Wino.Core/CoreContainerSetup.cs new file mode 100644 index 00000000..0554b0a0 --- /dev/null +++ b/Wino.Core/CoreContainerSetup.cs @@ -0,0 +1,46 @@ +using Microsoft.Extensions.DependencyInjection; +using Serilog.Core; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Integration.Processors; +using Wino.Core.Integration.Threading; +using Wino.Core.Services; + +namespace Wino.Core +{ + public static class CoreContainerSetup + { + public static void RegisterCoreServices(this IServiceCollection services) + { + var loggerLevelSwitcher = new LoggingLevelSwitch(); + + services.AddSingleton(loggerLevelSwitcher); + services.AddSingleton(); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + } + } +} diff --git a/Wino.Core/Extensions/FolderTreeExtensions.cs b/Wino.Core/Extensions/FolderTreeExtensions.cs new file mode 100644 index 00000000..12c46447 --- /dev/null +++ b/Wino.Core/Extensions/FolderTreeExtensions.cs @@ -0,0 +1,54 @@ +using System.Linq; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Folders; +using Wino.Core.MenuItems; + +namespace Wino.Core.Extensions +{ + public static class FolderTreeExtensions + { + public static AccountMenuItem GetAccountMenuTree(this AccountFolderTree accountTree, IMenuItem parentMenuItem = null) + { + var accountMenuItem = new AccountMenuItem(accountTree.Account, parentMenuItem); + + foreach (var structure in accountTree.Folders) + { + var tree = GetMenuItemByFolderRecursive(structure, accountMenuItem, null); + + accountMenuItem.SubMenuItems.Add(tree); + } + + + // Create flat folder hierarchy for ease of access. + accountMenuItem.FlattenedFolderHierarchy = ListExtensions + .FlattenBy(accountMenuItem.SubMenuItems, a => a.SubMenuItems) + .Where(a => a is FolderMenuItem) + .Cast() + .ToList(); + + return accountMenuItem; + } + + private static MenuItemBase GetMenuItemByFolderRecursive(IMailItemFolder structure, AccountMenuItem parentAccountMenuItem, IMenuItem parentFolderItem) + { + MenuItemBase parentMenuItem = new FolderMenuItem(structure, parentAccountMenuItem.Parameter, parentAccountMenuItem); + + var childStructures = structure.ChildFolders; + + foreach (var childFolder in childStructures) + { + if (childFolder == null) continue; + + // Folder menu item. + var subChildrenFolderTree = GetMenuItemByFolderRecursive(childFolder, parentAccountMenuItem, parentMenuItem); + + if (subChildrenFolderTree is FolderMenuItem folderItem) + { + parentMenuItem.SubMenuItems.Add(folderItem); + } + } + + return parentMenuItem; + } + } +} diff --git a/Wino.Core/Extensions/GoogleIntegratorExtensions.cs b/Wino.Core/Extensions/GoogleIntegratorExtensions.cs new file mode 100644 index 00000000..89a3c8f5 --- /dev/null +++ b/Wino.Core/Extensions/GoogleIntegratorExtensions.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Web; +using Google.Apis.Gmail.v1.Data; +using MimeKit; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Extensions +{ + public static class GoogleIntegratorExtensions + { + public const string INBOX_LABEL_ID = "INBOX"; + public const string UNREAD_LABEL_ID = "UNREAD"; + public const string IMPORTANT_LABEL_ID = "IMPORTANT"; + public const string STARRED_LABEL_ID = "STARRED"; + public const string DRAFT_LABEL_ID = "DRAFT"; + public const string SENT_LABEL_ID = "SENT"; + + private const string SYSTEM_FOLDER_IDENTIFIER = "system"; + private const string FOLDER_HIDE_IDENTIFIER = "labelHide"; + + private static Dictionary KnownFolderDictioanry = new Dictionary() + { + { INBOX_LABEL_ID, SpecialFolderType.Inbox }, + { "CHAT", SpecialFolderType.Chat }, + { IMPORTANT_LABEL_ID, SpecialFolderType.Important }, + { "TRASH", SpecialFolderType.Deleted }, + { DRAFT_LABEL_ID, SpecialFolderType.Draft }, + { SENT_LABEL_ID, SpecialFolderType.Sent }, + { "SPAM", SpecialFolderType.Junk }, + { STARRED_LABEL_ID, SpecialFolderType.Starred }, + { UNREAD_LABEL_ID, SpecialFolderType.Unread }, + { "FORUMS", SpecialFolderType.Forums }, + { "UPDATES", SpecialFolderType.Updates }, + { "PROMOTIONS", SpecialFolderType.Promotions }, + { "SOCIAL", SpecialFolderType.Social}, + { "PERSONAL", SpecialFolderType.Personal}, + }; + + public static MailItemFolder GetLocalFolder(this Label label, Guid accountId) + { + var unchangedFolderName = label.Name; + + if (label.Name.StartsWith("CATEGORY_")) + label.Name = label.Name.Replace("CATEGORY_", ""); + + bool isSpecialFolder = KnownFolderDictioanry.ContainsKey(label.Name); + bool isAllCapital = label.Name.All(a => char.IsUpper(a)); + + var specialFolderType = isSpecialFolder ? KnownFolderDictioanry[label.Name] : SpecialFolderType.Other; + + return new MailItemFolder() + { + TextColorHex = label.Color?.TextColor, + BackgroundColorHex = label.Color?.BackgroundColor, + FolderName = isAllCapital ? char.ToUpper(label.Name[0]) + label.Name.Substring(1).ToLower() : label.Name, // Capitilize only first letter. + RemoteFolderId = label.Id, + Id = Guid.NewGuid(), + MailAccountId = accountId, + IsSynchronizationEnabled = true, + SpecialFolderType = specialFolderType, + IsSystemFolder = label.Type == SYSTEM_FOLDER_IDENTIFIER, + IsSticky = isSpecialFolder && specialFolderType != SpecialFolderType.Category && !unchangedFolderName.StartsWith("CATEGORY"), + IsHidden = label.LabelListVisibility == FOLDER_HIDE_IDENTIFIER, + + // By default, all special folders update unread count in the UI except Trash. + ShowUnreadCount = specialFolderType != SpecialFolderType.Deleted || specialFolderType != SpecialFolderType.Other + }; + } + + public static bool GetIsDraft(this Message message) + => message?.LabelIds?.Any(a => a == DRAFT_LABEL_ID) ?? false; + + public static bool GetIsUnread(this Message message) + => message?.LabelIds?.Any(a => a == UNREAD_LABEL_ID) ?? false; + + public static bool GetIsFocused(this Message message) + => message?.LabelIds?.Any(a => a == IMPORTANT_LABEL_ID) ?? false; + + public static bool GetIsFlagged(this Message message) + => message?.LabelIds?.Any(a => a == STARRED_LABEL_ID) ?? false; + + /// + /// Returns MailCopy out of native Gmail message and converted MimeMessage of that native messaage. + /// + /// Gmail Message + /// MimeMessage representation of that native message. + /// MailCopy object that is ready to be inserted to database. + public static MailCopy AsMailCopy(this Message gmailMessage, MimeMessage mimeMessage) + { + bool isUnread = gmailMessage.GetIsUnread(); + bool isFocused = gmailMessage.GetIsFocused(); + bool isFlagged = gmailMessage.GetIsFlagged(); + bool isDraft = gmailMessage.GetIsDraft(); + + return new MailCopy() + { + CreationDate = mimeMessage.Date.UtcDateTime, + Subject = HttpUtility.HtmlDecode(mimeMessage.Subject), + FromName = MailkitClientExtensions.GetActualSenderName(mimeMessage), + FromAddress = MailkitClientExtensions.GetActualSenderAddress(mimeMessage), + PreviewText = HttpUtility.HtmlDecode(gmailMessage.Snippet), + ThreadId = gmailMessage.ThreadId, + Importance = (MailImportance)mimeMessage.Importance, + Id = gmailMessage.Id, + IsDraft = isDraft, + HasAttachments = mimeMessage.Attachments.Any(), + IsRead = !isUnread, + IsFlagged = isFlagged, + IsFocused = isFocused, + InReplyTo = mimeMessage.InReplyTo, + MessageId = mimeMessage.MessageId, + References = mimeMessage.References.GetReferences(), + FileId = Guid.NewGuid() + }; + } + + public static Tuple> GetMailDetails(this Message message) + { + MimeMessage mimeMessage = message.GetGmailMimeMessage(); + + if (mimeMessage == null) + { + // This should never happen. + Debugger.Break(); + + return default; + } + + bool isUnread = message.GetIsUnread(); + bool isFocused = message.GetIsFocused(); + bool isFlagged = message.GetIsFlagged(); + bool isDraft = message.GetIsDraft(); + + var mailCopy = new MailCopy() + { + CreationDate = mimeMessage.Date.UtcDateTime, + Subject = HttpUtility.HtmlDecode(mimeMessage.Subject), + FromName = MailkitClientExtensions.GetActualSenderName(mimeMessage), + FromAddress = MailkitClientExtensions.GetActualSenderAddress(mimeMessage), + PreviewText = HttpUtility.HtmlDecode(message.Snippet), + ThreadId = message.ThreadId, + Importance = (MailImportance)mimeMessage.Importance, + Id = message.Id, + IsDraft = isDraft, + HasAttachments = mimeMessage.Attachments.Any(), + IsRead = !isUnread, + IsFlagged = isFlagged, + IsFocused = isFocused, + InReplyTo = mimeMessage.InReplyTo, + MessageId = mimeMessage.MessageId, + References = mimeMessage.References.GetReferences() + }; + + return new Tuple>(mailCopy, mimeMessage, message.LabelIds); + } + + } +} diff --git a/Wino.Core/Extensions/HtmlAgilityPackExtensions.cs b/Wino.Core/Extensions/HtmlAgilityPackExtensions.cs new file mode 100644 index 00000000..1c1cc61f --- /dev/null +++ b/Wino.Core/Extensions/HtmlAgilityPackExtensions.cs @@ -0,0 +1,121 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using HtmlAgilityPack; + +namespace Wino.Core.Extensions +{ + public static class HtmlAgilityPackExtensions + { + /// + /// Clears out the src attribute for all `img` and `v:fill` tags. + /// + /// + public static void ClearImages(this HtmlDocument document) + { + if (document.DocumentNode.InnerHtml.Contains(" + /// Removes `style` tags from the document. + /// + /// + public static void ClearStyles(this HtmlDocument document) + { + document.DocumentNode + .Descendants() + .Where(n => n.Name.Equals("script", StringComparison.OrdinalIgnoreCase) + || n.Name.Equals("style", StringComparison.OrdinalIgnoreCase) + || n.Name.Equals("#comment", StringComparison.OrdinalIgnoreCase)) + .ToList() + .ForEach(n => n.Remove()); + } + + /// + /// Returns plain text from the HTML content. + /// + /// Content to get preview from. + /// Text body for the html. + public static string GetPreviewText(string htmlContent) + { + if (string.IsNullOrEmpty(htmlContent)) return string.Empty; + + HtmlDocument doc = new HtmlDocument(); + doc.LoadHtml(htmlContent); + + StringWriter sw = new StringWriter(); + ConvertTo(doc.DocumentNode, sw); + sw.Flush(); + + return sw.ToString().Replace(Environment.NewLine, ""); + } + + private static void ConvertContentTo(HtmlNode node, TextWriter outText) + { + foreach (HtmlNode subnode in node.ChildNodes) + { + ConvertTo(subnode, outText); + } + } + + private static void ConvertTo(HtmlNode node, TextWriter outText) + { + string html; + switch (node.NodeType) + { + case HtmlNodeType.Comment: + // don't output comments + break; + + case HtmlNodeType.Document: + ConvertContentTo(node, outText); + break; + + case HtmlNodeType.Text: + // script and style must not be output + string parentName = node.ParentNode.Name; + if ((parentName == "script") || (parentName == "style")) + break; + + // get text + html = ((HtmlTextNode)node).Text; + + // is it in fact a special closing node output as text? + if (HtmlNode.IsOverlappedClosingElement(html)) + break; + + // check the text is meaningful and not a bunch of whitespaces + if (html.Trim().Length > 0) + { + outText.Write(HtmlEntity.DeEntitize(html)); + } + break; + + case HtmlNodeType.Element: + switch (node.Name) + { + case "p": + // treat paragraphs as crlf + outText.Write("\r\n"); + break; + case "br": + outText.Write("\r\n"); + break; + } + + if (node.HasChildNodes) + { + ConvertContentTo(node, outText); + } + break; + } + } + } +} diff --git a/Wino.Core/Extensions/ListExtensions.cs b/Wino.Core/Extensions/ListExtensions.cs new file mode 100644 index 00000000..7330c44e --- /dev/null +++ b/Wino.Core/Extensions/ListExtensions.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.Extensions +{ + public static class ListExtensions + { + public static IEnumerable FlattenBy(this IEnumerable nodes, Func> selector) + { + if (nodes.Any() == false) + return nodes; + + var descendants = nodes + .SelectMany(selector) + .FlattenBy(selector); + + return nodes.Concat(descendants); + } + + public static IEnumerable CreateBatch(this IEnumerable> items) + { + IBatchChangeRequest batch = null; + + foreach (var group in items) + { + var key = group.Key; + } + + yield return batch; + } + + public static void AddSorted(this List @this, T item) where T : IComparable + { + if (@this.Count == 0) + { + @this.Add(item); + return; + } + if (@this[@this.Count - 1].CompareTo(item) <= 0) + { + @this.Add(item); + return; + } + if (@this[0].CompareTo(item) >= 0) + { + @this.Insert(0, item); + return; + } + int index = @this.BinarySearch(item); + if (index < 0) + index = ~index; + @this.Insert(index, item); + } + } +} diff --git a/Wino.Core/Extensions/LongExtensions.cs b/Wino.Core/Extensions/LongExtensions.cs new file mode 100644 index 00000000..2fa1198f --- /dev/null +++ b/Wino.Core/Extensions/LongExtensions.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Wino.Core.Extensions +{ + public static class LongExtensions + { + // Returns the human-readable file size for an arbitrary, 64-bit file size + // The default format is "0.### XB", e.g. "4.2 KB" or "1.434 GB" + public static string GetBytesReadable(this long i) + { + // Get absolute value + long absolute_i = (i < 0 ? -i : i); + // Determine the suffix and readable value + string suffix; + double readable; + if (absolute_i >= 0x1000000000000000) // Exabyte + { + suffix = "EB"; + readable = (i >> 50); + } + else if (absolute_i >= 0x4000000000000) // Petabyte + { + suffix = "PB"; + readable = (i >> 40); + } + else if (absolute_i >= 0x10000000000) // Terabyte + { + suffix = "TB"; + readable = (i >> 30); + } + else if (absolute_i >= 0x40000000) // Gigabyte + { + suffix = "GB"; + readable = (i >> 20); + } + else if (absolute_i >= 0x100000) // Megabyte + { + suffix = "MB"; + readable = (i >> 10); + } + else if (absolute_i >= 0x400) // Kilobyte + { + suffix = "KB"; + readable = i; + } + else + { + return i.ToString("0 B"); // Byte + } + // Divide by 1024 to get fractional value + readable = (readable / 1024); + // Return formatted number with suffix + return readable.ToString("0.# ") + suffix; + } + } +} diff --git a/Wino.Core/Extensions/MailkitClientExtensions.cs b/Wino.Core/Extensions/MailkitClientExtensions.cs new file mode 100644 index 00000000..1fb1fb58 --- /dev/null +++ b/Wino.Core/Extensions/MailkitClientExtensions.cs @@ -0,0 +1,187 @@ +using System; +using System.Linq; +using MailKit; +using MimeKit; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Extensions +{ + public static class MailkitClientExtensions + { + public static char MailCopyUidSeparator = '_'; + + public static uint ResolveUid(string mailCopyId) + { + var splitted = mailCopyId.Split(MailCopyUidSeparator); + + if (splitted.Length > 1 && uint.TryParse(splitted[1], out uint parsedUint)) return parsedUint; + + throw new ArgumentOutOfRangeException(nameof(mailCopyId), mailCopyId, "Invalid mailCopyId format."); + } + + public static string CreateUid(Guid folderId, uint messageUid) + => $"{folderId}{MailCopyUidSeparator}{messageUid}"; + + public static MailImportance GetImportance(this MimeMessage messageSummary) + { + if (messageSummary.Headers != null && messageSummary.Headers.Contains(HeaderId.Importance)) + { + var rawImportance = messageSummary.Headers[HeaderId.Importance]; + + return rawImportance switch + { + "Low" => MailImportance.Low, + "High" => MailImportance.High, + _ => MailImportance.Normal, + }; + } + + return MailImportance.Normal; + } + + public static bool GetIsRead(this MessageFlags? flags) + => flags.GetValueOrDefault().HasFlag(MessageFlags.Seen); + + public static bool GetIsFlagged(this MessageFlags? flags) + => flags.GetValueOrDefault().HasFlag(MessageFlags.Flagged); + + public static string GetThreadId(this IMessageSummary messageSummary) + { + // First check whether we have the default values. + + if (!string.IsNullOrEmpty(messageSummary.ThreadId)) + return messageSummary.ThreadId; + + if (messageSummary.GMailThreadId != null) + return messageSummary.GMailThreadId.ToString(); + + return default; + } + + public static string GetMessageId(this MimeMessage mimeMessage) + => mimeMessage.MessageId; + + public static string GetReferences(this MessageIdList messageIdList) + => string.Join(";", messageIdList); + + public static string GetInReplyTo(this MimeMessage mimeMessage) + { + if (mimeMessage.Headers.Contains(HeaderId.InReplyTo)) + { + // Normalize if <> brackets are there. + var inReplyTo = mimeMessage.Headers[HeaderId.InReplyTo]; + + if (inReplyTo.StartsWith("<") && inReplyTo.EndsWith(">")) + return inReplyTo.Substring(1, inReplyTo.Length - 2); + + return inReplyTo; + } + + return string.Empty; + } + + private static string GetPreviewText(this MimeMessage message) + { + if (string.IsNullOrEmpty(message.HtmlBody)) + return message.TextBody; + else + return HtmlAgilityPackExtensions.GetPreviewText(message.HtmlBody); + } + + public static MailCopy GetMailDetails(this IMessageSummary messageSummary, MailItemFolder folder, MimeMessage mime) + { + // MessageSummary will only have UniqueId, Flags, ThreadId. + // Other properties are extracted directly from the MimeMessage. + + // IMAP doesn't have unique id for mails. + // All mails are mapped to specific folders with incremental Id. + // Uid 1 may belong to different messages in different folders, but can never be + // same for different messages in same folders. + // Here we create arbitrary Id that maps the Id of the message with Folder UniqueId. + // When folder becomes invalid, we'll clear out these MailCopies as well. + + var messageUid = CreateUid(folder.Id, messageSummary.UniqueId.Id); + var previewText = mime.GetPreviewText(); + + var copy = new MailCopy() + { + Id = messageUid, + CreationDate = mime.Date.UtcDateTime, + ThreadId = messageSummary.GetThreadId(), + MessageId = mime.GetMessageId(), + Subject = mime.Subject, + IsRead = messageSummary.Flags.GetIsRead(), + IsFlagged = messageSummary.Flags.GetIsFlagged(), + PreviewText = previewText, + FromAddress = GetActualSenderAddress(mime), + FromName = GetActualSenderName(mime), + IsFocused = false, + Importance = mime.GetImportance(), + References = mime.References?.GetReferences(), + InReplyTo = mime.GetInReplyTo(), + HasAttachments = mime.Attachments.Any(), + FileId = Guid.NewGuid() + }; + + return copy; + } + + // TODO: Name and Address parsing should be handled better. + // At some point Wino needs better contact management. + + public static string GetActualSenderName(MimeMessage message) + { + if (message == null) + return string.Empty; + + // From MimeKit + + // The "From" header specifies the author(s) of the message. + // If more than one MimeKit.MailboxAddress is added to the list of "From" addresses, + // the MimeKit.MimeMessage.Sender should be set to the single MimeKit.MailboxAddress + // of the personal actually sending the message. + + // Also handle: https://stackoverflow.com/questions/46474030/mailkit-from-address + + if (message.Sender != null) + return string.IsNullOrEmpty(message.Sender.Name) ? message.Sender.Address : message.Sender.Name; + else if (message.From?.Mailboxes != null) + { + var firstAvailableName = message.From.Mailboxes.FirstOrDefault(a => !string.IsNullOrEmpty(a.Name))?.Name; + + if (string.IsNullOrEmpty(firstAvailableName)) + { + var firstAvailableAddress = message.From.Mailboxes.FirstOrDefault(a => !string.IsNullOrEmpty(a.Address))?.Address; + + if (!string.IsNullOrEmpty(firstAvailableAddress)) + { + return firstAvailableAddress; + } + } + + return firstAvailableName; + } + + // No sender, no from, I don't know what to do. + return Translator.UnknownSender; + } + + // TODO: This is wrong. + public static string GetActualSenderAddress(MimeMessage mime) + { + if (mime == null) + return string.Empty; + + bool hasSingleFromMailbox = mime.From.Mailboxes.Count() == 1; + + if (hasSingleFromMailbox) + return mime.From.Mailboxes.First().GetAddress(idnEncode: true); + else if (mime.Sender != null) + return mime.Sender.GetAddress(idnEncode: true); + else + return Translator.UnknownSender; + } + } +} diff --git a/Wino.Core/Extensions/MailkitExtensions.cs b/Wino.Core/Extensions/MailkitExtensions.cs new file mode 100644 index 00000000..c2b1c91e --- /dev/null +++ b/Wino.Core/Extensions/MailkitExtensions.cs @@ -0,0 +1,22 @@ +using System; +using MailKit; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Extensions +{ + public static class MailkitExtensions + { + public static MailItemFolder GetLocalFolder(this IMailFolder mailkitMailFolder) + { + return new MailItemFolder() + { + Id = Guid.NewGuid(), + FolderName = mailkitMailFolder.Name, + RemoteFolderId = mailkitMailFolder.FullName, + ParentRemoteFolderId = mailkitMailFolder.ParentFolder?.FullName, + SpecialFolderType = Domain.Enums.SpecialFolderType.Other, + IsSynchronizationEnabled = true + }; + } + } +} diff --git a/Wino.Core/Extensions/MimeExtensions.cs b/Wino.Core/Extensions/MimeExtensions.cs new file mode 100644 index 00000000..624645d1 --- /dev/null +++ b/Wino.Core/Extensions/MimeExtensions.cs @@ -0,0 +1,52 @@ +using System.IO; +using System.Text; +using Google.Apis.Gmail.v1.Data; +using MimeKit; +using MimeKit.IO; +using MimeKit.IO.Filters; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Extensions +{ + public static class MimeExtensions + { + /// + /// Returns MimeKit.MimeMessage instance for this GMail Message's Raw content. + /// + /// GMail message. + public static MimeMessage GetGmailMimeMessage(this Message message) + { + if (message == null || message.Raw == null) + return null; + + // Gmail raw is not base64 but base64Safe. We need to remove this HTML things. + var base64Encoded = message.Raw.Replace(",", "=").Replace("-", "+").Replace("_", "/"); + + byte[] bytes = Encoding.ASCII.GetBytes(base64Encoded); + + var stream = new MemoryStream(bytes); + + // This method will dispose outer stream. + + using (stream) + { + using var filtered = new FilteredStream(stream); + filtered.Add(DecoderFilter.Create(ContentEncoding.Base64)); + + return MimeMessage.Load(filtered); + } + } + + public static AddressInformation ToAddressInformation(this MailboxAddress address) + { + if (address == null) + return new AddressInformation() { Name = Translator.UnknownSender, Address = Translator.UnknownAddress }; + + if (string.IsNullOrEmpty(address.Name)) + address.Name = address.Address; + + return new AddressInformation() { Name = address.Name, Address = address.Address }; + } + } +} diff --git a/Wino.Core/Extensions/OutlookIntegratorExtensions.cs b/Wino.Core/Extensions/OutlookIntegratorExtensions.cs new file mode 100644 index 00000000..2d542849 --- /dev/null +++ b/Wino.Core/Extensions/OutlookIntegratorExtensions.cs @@ -0,0 +1,65 @@ +using System; +using Microsoft.Graph.Models; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Extensions +{ + public static class OutlookIntegratorExtensions + { + public static MailItemFolder GetLocalFolder(this MailFolder nativeFolder, Guid accountId) + { + return new MailItemFolder() + { + Id = Guid.NewGuid(), + FolderName = nativeFolder.DisplayName, + RemoteFolderId = nativeFolder.Id, + ParentRemoteFolderId = nativeFolder.ParentFolderId, + IsSynchronizationEnabled = true, + MailAccountId = accountId, + IsHidden = nativeFolder.IsHidden.GetValueOrDefault() + }; + } + + public static bool GetIsDraft(this Message message) + => message != null && message.IsDraft.GetValueOrDefault(); + + public static bool GetIsRead(this Message message) + => message != null && message.IsRead.GetValueOrDefault(); + + public static bool GetIsFocused(this Message message) + => message?.InferenceClassification != null && message.InferenceClassification.Value == InferenceClassificationType.Focused; + + public static bool GetIsFlagged(this Message message) + => message?.Flag?.FlagStatus != null && message.Flag.FlagStatus == FollowupFlagStatus.Flagged; + + public static MailCopy AsMailCopy(this Message outlookMessage) + { + bool isDraft = GetIsDraft(outlookMessage); + + var mailCopy = new MailCopy() + { + MessageId = outlookMessage.InternetMessageId, + IsFlagged = GetIsFlagged(outlookMessage), + IsFocused = GetIsFocused(outlookMessage), + Importance = !outlookMessage.Importance.HasValue ? MailImportance.Normal : (MailImportance)outlookMessage.Importance.Value, + IsRead = GetIsRead(outlookMessage), + IsDraft = isDraft, + CreationDate = outlookMessage.ReceivedDateTime.GetValueOrDefault().DateTime, + HasAttachments = outlookMessage.HasAttachments.GetValueOrDefault(), + PreviewText = outlookMessage.BodyPreview, + Id = outlookMessage.Id, + ThreadId = outlookMessage.ConversationId, + FromName = outlookMessage.From?.EmailAddress?.Name, + FromAddress = outlookMessage.From?.EmailAddress?.Address, + Subject = outlookMessage.Subject, + FileId = Guid.NewGuid() + }; + + if (mailCopy.IsDraft) + mailCopy.DraftId = mailCopy.ThreadId; + + return mailCopy; + } + } +} diff --git a/Wino.Core/Extensions/SqlKataExtensions.cs b/Wino.Core/Extensions/SqlKataExtensions.cs new file mode 100644 index 00000000..a974d013 --- /dev/null +++ b/Wino.Core/Extensions/SqlKataExtensions.cs @@ -0,0 +1,15 @@ +using SqlKata; +using SqlKata.Compilers; + +namespace Wino.Core.Extensions +{ + public static class SqlKataExtensions + { + private static SqliteCompiler Compiler = new SqliteCompiler(); + + public static string GetRawQuery(this Query query) + { + return Compiler.Compile(query).ToString(); + } + } +} diff --git a/Wino.Core/Extensions/StringExtensions.cs b/Wino.Core/Extensions/StringExtensions.cs new file mode 100644 index 00000000..499e67a6 --- /dev/null +++ b/Wino.Core/Extensions/StringExtensions.cs @@ -0,0 +1,22 @@ +using System; + +namespace Wino.Core.Extensions +{ + public static class StringExtensions + { + public static bool Contains(this string source, string toCheck, StringComparison comp) + { + return source?.IndexOf(toCheck, comp) >= 0; + } + + public static string ReplaceFirst(this string text, string search, string replace) + { + int pos = text.IndexOf(search); + if (pos < 0) + { + return text; + } + return text.Substring(0, pos) + replace + text.Substring(pos + search.Length); + } + } +} diff --git a/Wino.Core/Extensions/TokenizationExtensions.cs b/Wino.Core/Extensions/TokenizationExtensions.cs new file mode 100644 index 00000000..3ffefb57 --- /dev/null +++ b/Wino.Core/Extensions/TokenizationExtensions.cs @@ -0,0 +1,31 @@ +using System; +using Microsoft.Identity.Client; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Extensions +{ + public static class TokenizationExtensions + { + public static TokenInformation CreateTokenInformation(this AuthenticationResult clientBuilderResult) + { + var expirationDate = clientBuilderResult.ExpiresOn.UtcDateTime; + var accesToken = clientBuilderResult.AccessToken; + var userName = clientBuilderResult.Account.Username; + + // MSAL does not expose refresh token for security reasons. + // This token info will be created without refresh token. + // but OutlookIntegrator will ask for publicApplication to refresh it + // in case of expiration. + + var tokenInfo = new TokenInformation() + { + ExpiresAt = expirationDate, + AccessToken = accesToken, + Address = userName, + Id = Guid.NewGuid(), + }; + + return tokenInfo; + } + } +} diff --git a/Wino.Core/GlobalSuppressions.cs b/Wino.Core/GlobalSuppressions.cs new file mode 100644 index 00000000..c217dec0 --- /dev/null +++ b/Wino.Core/GlobalSuppressions.cs @@ -0,0 +1,10 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~P:Wino.Core.Models.IMailDisplayInformation.asd")] +[assembly: SuppressMessage("Minor Code Smell", "S3267:Loops should be simplified with \"LINQ\" expressions", Justification = "", Scope = "member", Target = "~M:Wino.Core.Services.WinoRequestProcessor.PrepareRequestsAsync(Wino.Core.Domain.Enums.MailOperation,System.Collections.Generic.IEnumerable{System.String})~System.Threading.Tasks.Task{System.Collections.Generic.List{Wino.Core.Abstractions.Interfaces.Data.IWinoChangeRequest}}")] +[assembly: SuppressMessage("Minor Code Smell", "S3267:Loops should be simplified with \"LINQ\" expressions", Justification = "", Scope = "member", Target = "~M:Wino.Core.Services.SynchronizationWorker.QueueAsync(System.Collections.Generic.IEnumerable{Wino.Core.Abstractions.Interfaces.Data.IWinoChangeRequest})")] diff --git a/Wino.Core/Http/GmailClientMessageHandler.cs b/Wino.Core/Http/GmailClientMessageHandler.cs new file mode 100644 index 00000000..c1b6a77a --- /dev/null +++ b/Wino.Core/Http/GmailClientMessageHandler.cs @@ -0,0 +1,29 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Google.Apis.Http; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Http +{ + internal class GmailClientMessageHandler : ConfigurableMessageHandler + { + public Func> TokenRetrieveDelegate { get; } + + public GmailClientMessageHandler(Func> tokenRetrieveDelegate) : base(new HttpClientHandler()) + { + TokenRetrieveDelegate = tokenRetrieveDelegate; + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var tokenizationTask = TokenRetrieveDelegate.Invoke(); + var tokenInformation = await tokenizationTask; + + request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", tokenInformation.AccessToken); + + return await base.SendAsync(request, cancellationToken); + } + } +} diff --git a/Wino.Core/Http/MicrosoftImmutableIdHandler.cs b/Wino.Core/Http/MicrosoftImmutableIdHandler.cs new file mode 100644 index 00000000..d839d76b --- /dev/null +++ b/Wino.Core/Http/MicrosoftImmutableIdHandler.cs @@ -0,0 +1,19 @@ +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace Wino.Core.Http +{ + /// + /// Adds additional Prefer header for immutable id support in the Graph service client. + /// + public class MicrosoftImmutableIdHandler : DelegatingHandler + { + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + request.Headers.TryAddWithoutValidation("Prefer", "IdType=\"ImmutableId\""); + + return base.SendAsync(request, cancellationToken); + } + } +} diff --git a/Wino.Core/Http/MicrosoftJsonContractResolver.cs b/Wino.Core/Http/MicrosoftJsonContractResolver.cs new file mode 100644 index 00000000..79148cbe --- /dev/null +++ b/Wino.Core/Http/MicrosoftJsonContractResolver.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace Wino.Core.Http +{ + + /// + /// We need to generate HttpRequestMessage for batch requests, and sometimes we need to + /// serialize content as json. However, some of the fields like 'ODataType' must be ignored + /// in order PATCH requests to succeed. Therefore Microsoft account synchronizer uses + /// special JsonSerializerSettings for ignoring some of the properties. + /// + public class MicrosoftJsonContractResolver : DefaultContractResolver + { + private readonly HashSet ignoreProps = new HashSet() + { + "ODataType" + }; + + protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) + { + JsonProperty property = base.CreateProperty(member, memberSerialization); + + if (ignoreProps.Contains(property.PropertyName)) + { + property.ShouldSerialize = _ => false; + } + + return property; + } + } +} diff --git a/Wino.Core/Http/MicrosoftTokenProvider.cs b/Wino.Core/Http/MicrosoftTokenProvider.cs new file mode 100644 index 00000000..606d35ae --- /dev/null +++ b/Wino.Core/Http/MicrosoftTokenProvider.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Kiota.Abstractions.Authentication; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.Http +{ + public class MicrosoftTokenProvider : IAccessTokenProvider + { + private readonly MailAccount _account; + private readonly IAuthenticator _authenticator; + + public MicrosoftTokenProvider(MailAccount account, IAuthenticator authenticator) + { + _account = account; + _authenticator = authenticator; + } + + public AllowedHostsValidator AllowedHostsValidator { get; } + + public async Task GetAuthorizationTokenAsync(Uri uri, + Dictionary additionalAuthenticationContext = null, + CancellationToken cancellationToken = default) + { + var token = await _authenticator.GetTokenAsync(_account).ConfigureAwait(false); + + return token?.AccessToken; + } + } +} diff --git a/Wino.Core/Integration/BaseMailIntegrator.cs b/Wino.Core/Integration/BaseMailIntegrator.cs new file mode 100644 index 00000000..a4bda8ea --- /dev/null +++ b/Wino.Core/Integration/BaseMailIntegrator.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using MailKit.Net.Imap; +using MoreLinq; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Requests; +using Wino.Core.Requests.Bundles; + +namespace Wino.Core.Integration +{ + public abstract class BaseMailIntegrator + { + /// + /// How many items per single HTTP call can be modified. + /// + public abstract uint BatchModificationSize { get; } + + /// + /// How many items must be downloaded per folder when the folder is first synchronized. + /// + public abstract uint InitialMessageDownloadCountPerFolder { get; } + + /// + /// Creates a batched HttpBundle without a response for a collection of MailItem. + /// + /// Generated batch request. + /// An action to get the native request from the MailItem. + /// Collection of http bundle that contains batch and native request. + public IEnumerable> CreateBatchedHttpBundleFromGroup( + IBatchChangeRequest batchChangeRequest, + Func, TNativeRequestType> action) + { + if (batchChangeRequest.Items == null) yield break; + + var groupedItems = batchChangeRequest.Items.Batch((int)BatchModificationSize); + + foreach (var group in groupedItems) + yield return new HttpRequestBundle(action(group), batchChangeRequest); + } + + public IEnumerable> CreateBatchedHttpBundle( + IBatchChangeRequest batchChangeRequest, + Func action) + { + if (batchChangeRequest.Items == null) yield break; + + var groupedItems = batchChangeRequest.Items.Batch((int)BatchModificationSize); + + foreach (var group in groupedItems) + foreach (var item in group) + yield return new HttpRequestBundle(action(item), item); + + yield break; + } + + /// + /// Creates a single HttpBundle without a response for a collection of MailItem. + /// + /// Batch request + /// An action to get the native request from the MailItem + /// Collection of http bundle that contains batch and native request. + public IEnumerable> CreateHttpBundle( + IBatchChangeRequest batchChangeRequest, + Func action) + { + if (batchChangeRequest.Items == null) yield break; + + foreach (var item in batchChangeRequest.Items) + yield return new HttpRequestBundle(action(item), batchChangeRequest); + } + + public IEnumerable> CreateHttpBundle( + IBatchChangeRequest batchChangeRequest, + Func action) + { + if (batchChangeRequest.Items == null) yield break; + + foreach (var item in batchChangeRequest.Items) + yield return new HttpRequestBundle(action(item), item); + } + + /// + /// Creates HttpBundle with TResponse of expected response type from the http call for each of the items in the batch. + /// + /// Expected http response type after the call. + /// Generated batch request. + /// An action to get the native request from the MailItem. + /// Collection of http bundle that contains batch and native request. + public IEnumerable> CreateHttpBundleWithResponse( + IBatchChangeRequest batchChangeRequest, + Func action) + { + if (batchChangeRequest.Items == null) yield break; + + foreach (var item in batchChangeRequest.Items) + yield return new HttpRequestBundle(action(item), batchChangeRequest); + } + + /// + /// Creates a batched HttpBundle with TResponse of expected response type from the http call for each of the items in the batch. + /// Func will be executed for each item separately in the batch request. + /// + /// Expected http response type after the call. + /// Generated batch request. + /// An action to get the native request from the MailItem. + /// Collection of http bundle that contains batch and native request. + public IEnumerable> CreateBatchedHttpBundle( + IBatchChangeRequest batchChangeRequest, + Func action) + { + if (batchChangeRequest.Items == null) yield break; + + var groupedItems = batchChangeRequest.Items.Batch((int)BatchModificationSize); + + foreach (var group in groupedItems) + foreach (var item in group) + yield return new HttpRequestBundle(action(item), item); + + yield break; + } + + public IEnumerable> CreateTaskBundle(Func value, IRequestBase request) + { + var imapreq = new ImapRequest(value, request); + + return [new ImapRequestBundle(imapreq, request)]; + } + } +} diff --git a/Wino.Core/Integration/ImapClientPool.cs b/Wino.Core/Integration/ImapClientPool.cs new file mode 100644 index 00000000..8746b218 --- /dev/null +++ b/Wino.Core/Integration/ImapClientPool.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; +using MailKit.Net.Imap; +using MailKit.Net.Proxy; +using MailKit.Security; +using Serilog; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Exceptions; + +namespace Wino.Core.Integration +{ + /// + /// Provides a pooling mechanism for ImapClient. + /// Makes sure that we don't have too many connections to the server. + /// Rents a connected & authenticated client from the pool all the time. + /// TODO: Keeps the clients alive by sending NOOP command periodically. + /// TODO: Listens to the Inbox folder for new messages. + /// + /// Connection/Authentication info to be used to configure ImapClient. + public class ImapClientPool + { + // Hardcoded implementation details for ID extension if the server supports. + // Some providers like Chinese 126 require Id to be sent before authentication. + // We don't expose any customer data here. Therefore it's safe for now. + // Later on maybe we can make it configurable and leave it to the user with passing + // real implementation details. + + private readonly ImapImplementation _implementation = new ImapImplementation() + { + Version = "1.0", + OS = "Windows", + Vendor = "Wino" + }; + + private const int MaxPoolSize = 5; + + private readonly ConcurrentBag _clients = []; + private readonly SemaphoreSlim _semaphore = new(MaxPoolSize); + private readonly CustomServerInformation _customServerInformation; + private readonly ILogger _logger = Log.ForContext(); + + public ImapClientPool(CustomServerInformation customServerInformation) + { + _customServerInformation = customServerInformation; + } + + private async Task EnsureConnectivityAsync(ImapClient client, bool isCreatedNew) + { + try + { + await EnsureConnectedAsync(client); + + if (isCreatedNew && client.IsConnected) + { + // Activate supported pre-auth capabilities. + if (client.Capabilities.HasFlag(ImapCapabilities.Compress)) + await client.CompressAsync(); + + // Identify if the server supports ID extension. + if (client.Capabilities.HasFlag(ImapCapabilities.Id)) + await client.IdentifyAsync(_implementation); + } + + await EnsureAuthenticatedAsync(client); + + if (isCreatedNew && client.IsAuthenticated) + { + // Activate post-auth capabilities. + if (client.Capabilities.HasFlag(ImapCapabilities.QuickResync)) + await client.EnableQuickResyncAsync(); + } + } + catch (Exception ex) + { + throw new ImapClientPoolException(ex); + } + finally + { + // Release it even if it fails. + _semaphore.Release(); + } + } + + public async Task GetClientAsync() + { + await _semaphore.WaitAsync(); + + if (_clients.TryTake(out ImapClient item)) + { + await EnsureConnectivityAsync(item, false); + + return item; + } + + var client = CreateNewClient(); + + await EnsureConnectivityAsync(client, true); + + return client; + } + + public void Release(ImapClient item) + { + if (item != null) + { + _clients.Add(item); + _semaphore.Release(); + } + } + + public ImapClient CreateNewClient() + { + var client = new ImapClient(); + + HttpProxyClient proxyClient = null; + + // Add proxy client if exists. + if (!string.IsNullOrEmpty(_customServerInformation.ProxyServer)) + { + proxyClient = new HttpProxyClient(_customServerInformation.ProxyServer, int.Parse(_customServerInformation.ProxyServerPort)); + } + + client.ProxyClient = proxyClient; + + _logger.Debug("Created new ImapClient. Current clients: {Count}", _clients.Count); + + return client; + } + + private SecureSocketOptions GetSocketOptions(ImapConnectionSecurity connectionSecurity) + => connectionSecurity switch + { + ImapConnectionSecurity.Auto => SecureSocketOptions.Auto, + ImapConnectionSecurity.None => SecureSocketOptions.None, + ImapConnectionSecurity.StartTls => SecureSocketOptions.StartTlsWhenAvailable, + ImapConnectionSecurity.SslTls => SecureSocketOptions.SslOnConnect, + _ => SecureSocketOptions.None + }; + + public async Task EnsureConnectedAsync(ImapClient client) + { + if (client.IsConnected) return; + + await client.ConnectAsync(_customServerInformation.IncomingServer, + int.Parse(_customServerInformation.IncomingServerPort), + GetSocketOptions(_customServerInformation.IncomingServerSocketOption)); + } + + public async Task EnsureAuthenticatedAsync(ImapClient client) + { + if (client.IsAuthenticated) return; + + switch (_customServerInformation.IncomingAuthenticationMethod) + { + case ImapAuthenticationMethod.Auto: + break; + case ImapAuthenticationMethod.None: + break; + case ImapAuthenticationMethod.NormalPassword: + break; + case ImapAuthenticationMethod.EncryptedPassword: + break; + case ImapAuthenticationMethod.Ntlm: + break; + case ImapAuthenticationMethod.CramMd5: + break; + case ImapAuthenticationMethod.DigestMd5: + break; + default: + break; + } + + await client.AuthenticateAsync(_customServerInformation.IncomingServerUsername, _customServerInformation.IncomingServerPassword); + } + } +} diff --git a/Wino.Core/Integration/Processors/DefaultChangeProcessor.cs b/Wino.Core/Integration/Processors/DefaultChangeProcessor.cs new file mode 100644 index 00000000..ecf10231 --- /dev/null +++ b/Wino.Core/Integration/Processors/DefaultChangeProcessor.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using MimeKit; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Synchronization; +using Wino.Core.Services; + +namespace Wino.Core.Integration.Processors +{ + /// + /// Database change processor that handles common operations for all synchronizers. + /// When a synchronizer detects a change, it should call the appropriate method in this class to reflect the change in the database. + /// Different synchronizers might need additional implementations. + /// and + /// None of the synchronizers can directly change anything in the database. + /// + public interface IDefaultChangeProcessor + { + Task UpdateAccountDeltaSynchronizationIdentifierAsync(Guid accountId, string deltaSynchronizationIdentifier); + Task UpdateFolderDeltaSynchronizationIdentifierAsync(Guid folderId, string deltaSynchronizationIdentifier); + + Task CreateAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId); + Task DeleteAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId); + + Task ChangeMailReadStatusAsync(string mailCopyId, bool isRead); + Task ChangeFlagStatusAsync(string mailCopyId, bool isFlagged); + + Task CreateMailAsync(Guid AccountId, NewMailItemPackage package); + Task DeleteMailAsync(Guid accountId, string mailId); + + Task MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId); + Task MapLocalDraftAsync(string mailCopyId, string newDraftId, string newThreadId); + + Task> GetDownloadedUnreadMailsAsync(Guid accountId, IEnumerable downloadedMailCopyIds); + + Task SaveMimeFileAsync(Guid fileId, MimeMessage mimeMessage, Guid accountId); + + // For gmail. + Task UpdateFolderStructureAsync(Guid accountId, List allFolders); + + Task DeleteFolderAsync(Guid accountId, string remoteFolderId); + Task> GetSynchronizationFoldersAsync(SynchronizationOptions options); + Task InsertFolderAsync(MailItemFolder folder); + + Task> GetKnownUidsForFolderAsync(Guid folderId); + } + + public interface IGmailChangeProcessor : IDefaultChangeProcessor + { + + } + + public interface IOutlookChangeProcessor : IDefaultChangeProcessor + { + + } + + public class DefaultChangeProcessor(IDatabaseService databaseService, + IFolderService folderService, + IMailService mailService, + IAccountService accountService, + IMimeFileService mimeFileService) : BaseDatabaseService(databaseService), IDefaultChangeProcessor + { + private readonly IFolderService _folderService = folderService; + private readonly IMailService _mailService = mailService; + private readonly IAccountService _accountService = accountService; + private readonly IMimeFileService _mimeFileService = mimeFileService; + + public Task UpdateAccountDeltaSynchronizationIdentifierAsync(Guid accountId, string synchronizationDeltaIdentifier) + => _accountService.UpdateSynchronizationIdentifierAsync(accountId, synchronizationDeltaIdentifier); + + public Task ChangeFlagStatusAsync(string mailCopyId, bool isFlagged) + => _mailService.ChangeFlagStatusAsync(mailCopyId, isFlagged); + + public Task ChangeMailReadStatusAsync(string mailCopyId, bool isRead) + => _mailService.ChangeReadStatusAsync(mailCopyId, isRead); + + public Task DeleteAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId) + => _mailService.DeleteAssignmentAsync(accountId, mailCopyId, remoteFolderId); + + public Task CreateAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId) + => _mailService.CreateAssignmentAsync(accountId, mailCopyId, remoteFolderId); + + public Task DeleteMailAsync(Guid accountId, string mailId) + => _mailService.DeleteMailAsync(accountId, mailId); + + public Task CreateMailAsync(Guid accountId, NewMailItemPackage package) + => _mailService.CreateMailAsync(accountId, package); + + // Folder methods + public Task UpdateFolderStructureAsync(Guid accountId, List allFolders) + => _folderService.BulkUpdateFolderStructureAsync(accountId, allFolders); + + public Task MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId) + => _mailService.MapLocalDraftAsync(accountId, localDraftCopyUniqueId, newMailCopyId, newDraftId, newThreadId); + + public Task MapLocalDraftAsync(string mailCopyId, string newDraftId, string newThreadId) + => _mailService.MapLocalDraftAsync(mailCopyId, newDraftId, newThreadId); + + public Task> GetSynchronizationFoldersAsync(SynchronizationOptions options) + => _folderService.GetSynchronizationFoldersAsync(options); + + public Task UpdateFolderDeltaSynchronizationIdentifierAsync(Guid folderId, string deltaSynchronizationIdentifier) + => _folderService.UpdateFolderDeltaSynchronizationIdentifierAsync(folderId, deltaSynchronizationIdentifier); + + public Task DeleteFolderAsync(Guid accountId, string remoteFolderId) + => _folderService.DeleteFolderAsync(accountId, remoteFolderId); + + public Task InsertFolderAsync(MailItemFolder folder) + => _folderService.InsertFolderAsync(folder); + + public Task> GetDownloadedUnreadMailsAsync(Guid accountId, IEnumerable downloadedMailCopyIds) + => _mailService.GetDownloadedUnreadMailsAsync(accountId, downloadedMailCopyIds); + + public Task> GetKnownUidsForFolderAsync(Guid folderId) + => _folderService.GetKnownUidsForFolderAsync(folderId); + + public Task SaveMimeFileAsync(Guid fileId, MimeMessage mimeMessage, Guid accountId) + => _mimeFileService.SaveMimeMessageAsync(fileId, mimeMessage, accountId); + } +} diff --git a/Wino.Core/Integration/Threading/APIThreadingStrategy.cs b/Wino.Core/Integration/Threading/APIThreadingStrategy.cs new file mode 100644 index 00000000..c1685f95 --- /dev/null +++ b/Wino.Core/Integration/Threading/APIThreadingStrategy.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Services; + +namespace Wino.Core.Integration.Threading +{ + public class APIThreadingStrategy : IThreadingStrategy + { + private readonly IDatabaseService _databaseService; + private readonly IFolderService _folderService; + + public APIThreadingStrategy(IDatabaseService databaseService, IFolderService folderService) + { + _databaseService = databaseService; + _folderService = folderService; + } + + public virtual bool ShouldThreadWithItem(IMailItem originalItem, IMailItem targetItem) + { + return originalItem.ThreadId != null && originalItem.ThreadId == targetItem.ThreadId; + } + + public async Task> ThreadItemsAsync(List items) + { + var accountId = items.First().AssignedAccount.Id; + + var threads = new List(); + var assignedAccount = items.First().AssignedAccount; + + // TODO: Can be optimized by moving to the caller. + var sentFolder = await _folderService.GetSpecialFolderByAccountIdAsync(accountId, Domain.Enums.SpecialFolderType.Sent); + var draftFolder = await _folderService.GetSpecialFolderByAccountIdAsync(accountId, Domain.Enums.SpecialFolderType.Draft); + + if (sentFolder == null || draftFolder == null) return default; + + // Child -> Parent approach. + + var potentialThreadItems = items.Distinct().Where(a => !string.IsNullOrEmpty(a.ThreadId)); + + var mailLookupTable = new Dictionary(); + + // Fill up the mail lookup table to prevent double thread creation. + foreach (var mail in items) + if (!mailLookupTable.ContainsKey(mail.Id)) + mailLookupTable.Add(mail.Id, false); + + foreach (var potentialItem in potentialThreadItems) + { + if (mailLookupTable[potentialItem.Id]) + continue; + + mailLookupTable[potentialItem.Id] = true; + + var allThreadItems = await GetThreadItemsAsync(potentialItem.ThreadId, accountId, potentialItem.AssignedFolder, sentFolder.Id, draftFolder.Id); + + if (allThreadItems.Count == 1) + { + // It's a single item. + // Mark as not-processed as thread. + + mailLookupTable[potentialItem.Id] = false; + } + else + { + // Thread item. Mark all items as true in dict. + var threadItem = new ThreadMailItem(); + + foreach (var childThreadItem in allThreadItems) + { + if (mailLookupTable.ContainsKey(childThreadItem.Id)) + mailLookupTable[childThreadItem.Id] = true; + + childThreadItem.AssignedAccount = assignedAccount; + childThreadItem.AssignedFolder = await _folderService.GetFolderAsync(childThreadItem.FolderId); + + threadItem.AddThreadItem(childThreadItem); + } + + // Multiple mail copy ids from different folders are thing for Gmail. + if (threadItem.ThreadItems.Count == 1) + mailLookupTable[potentialItem.Id] = false; + else + threads.Add(threadItem); + } + } + + // At this points all mails in the list belong to single items. + // Merge with threads. + // Last sorting will be done later on in MailService. + + // Remove single mails that are included in thread. + items.RemoveAll(a => mailLookupTable.ContainsKey(a.Id) && mailLookupTable[a.Id]); + + var finalList = new List(items); + + finalList.AddRange(threads); + + return finalList; + } + + private async Task> GetThreadItemsAsync(string threadId, + Guid accountId, + MailItemFolder threadingFolder, + Guid sentFolderId, + Guid draftFolderId) + { + // Only items from the folder that we are threading for, sent and draft folder items must be included. + // This is important because deleted items or item assignments that belongs to different folder is + // affecting the thread creation here. + + // If the threading is done from Sent or Draft folder, include everything... + + // TODO: Convert to SQLKata query. + + string query = string.Empty; + + if (threadingFolder.SpecialFolderType == SpecialFolderType.Draft || threadingFolder.SpecialFolderType == SpecialFolderType.Sent) + { + query = @$"SELECT DISTINCT MC.* FROM MailCopy MC + INNER JOIN MailItemFolder MF on MF.Id = MC.FolderId + WHERE MF.MailAccountId == '{accountId}' AND MC.ThreadId = '{threadId}'"; + } + else + { + query = @$"SELECT DISTINCT MC.* FROM MailCopy MC + INNER JOIN MailItemFolder MF on MF.Id = MC.FolderId + WHERE MF.MailAccountId == '{accountId}' AND MC.FolderId IN ('{threadingFolder.Id}','{sentFolderId}','{draftFolderId}') + AND MC.ThreadId = '{threadId}'"; + } + + + return await _databaseService.Connection.QueryAsync(query); + } + } +} diff --git a/Wino.Core/Integration/Threading/GmailThreadingStrategy.cs b/Wino.Core/Integration/Threading/GmailThreadingStrategy.cs new file mode 100644 index 00000000..5cd0ce1c --- /dev/null +++ b/Wino.Core/Integration/Threading/GmailThreadingStrategy.cs @@ -0,0 +1,10 @@ +using Wino.Core.Domain.Interfaces; +using Wino.Core.Services; + +namespace Wino.Core.Integration.Threading +{ + public class GmailThreadingStrategy : APIThreadingStrategy + { + public GmailThreadingStrategy(IDatabaseService databaseService, IFolderService folderService) : base(databaseService, folderService) { } + } +} diff --git a/Wino.Core/Integration/Threading/ImapThreadStrategy.cs b/Wino.Core/Integration/Threading/ImapThreadStrategy.cs new file mode 100644 index 00000000..0a2d7da8 --- /dev/null +++ b/Wino.Core/Integration/Threading/ImapThreadStrategy.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SqlKata; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Extensions; +using Wino.Core.Services; + +namespace Wino.Core.Integration.Threading +{ + public class ImapThreadStrategy : IThreadingStrategy + { + private readonly IDatabaseService _databaseService; + private readonly IFolderService _folderService; + + public ImapThreadStrategy(IDatabaseService databaseService, IFolderService folderService) + { + _databaseService = databaseService; + _folderService = folderService; + } + + private Task GetReplyParentAsync(IMailItem replyItem, Guid accountId, Guid threadingFolderId, Guid sentFolderId, Guid draftFolderId) + { + if (string.IsNullOrEmpty(replyItem?.MessageId)) return Task.FromResult(null); + + var query = new Query("MailCopy") + .Distinct() + .Take(1) + .Join("MailItemFolder", "MailItemFolder.Id", "MailCopy.FolderId") + .Where("MailItemFolder.MailAccountId", accountId) + .WhereIn("MailItemFolder.Id", new List { threadingFolderId, sentFolderId, draftFolderId }) + .Where("MailCopy.MessageId", replyItem.InReplyTo) + .WhereNot("MailCopy.Id", replyItem.Id) + .Select("MailCopy.*"); + + return _databaseService.Connection.FindWithQueryAsync(query.GetRawQuery()); + } + + private Task GetInReplyToReplyAsync(IMailItem originalItem, Guid accountId, Guid threadingFolderId, Guid sentFolderId, Guid draftFolderId) + { + if (string.IsNullOrEmpty(originalItem?.MessageId)) return Task.FromResult(null); + + var query = new Query("MailCopy") + .Distinct() + .Take(1) + .Join("MailItemFolder", "MailItemFolder.Id", "MailCopy.FolderId") + .WhereNot("MailCopy.Id", originalItem.Id) + .Where("MailItemFolder.MailAccountId", accountId) + .Where("MailCopy.InReplyTo", originalItem.MessageId) + .WhereIn("MailItemFolder.Id", new List { threadingFolderId, sentFolderId, draftFolderId }) + .Select("MailCopy.*"); + + var raq = query.GetRawQuery(); + + return _databaseService.Connection.FindWithQueryAsync(query.GetRawQuery()); + } + + public async Task> ThreadItemsAsync(List items) + { + var threads = new List(); + + var account = items.First().AssignedAccount; + var accountId = account.Id; + + // Child -> Parent approach. + + var mailLookupTable = new Dictionary(); + + // Fill up the mail lookup table to prevent double thread creation. + foreach (var mail in items) + if (!mailLookupTable.ContainsKey(mail.Id)) + mailLookupTable.Add(mail.Id, false); + + var sentFolder = await _folderService.GetSpecialFolderByAccountIdAsync(accountId, Domain.Enums.SpecialFolderType.Sent); + var draftFolder = await _folderService.GetSpecialFolderByAccountIdAsync(accountId, Domain.Enums.SpecialFolderType.Draft); + + if (sentFolder == null || draftFolder == null) return default; + + foreach (var replyItem in items) + { + if (mailLookupTable[replyItem.Id]) + continue; + + mailLookupTable[replyItem.Id] = true; + + var threadItem = new ThreadMailItem(); + + threadItem.AddThreadItem(replyItem); + + var replyToChild = await GetReplyParentAsync(replyItem, accountId, replyItem.AssignedFolder.Id, sentFolder.Id, draftFolder.Id); + + // Build up + while (replyToChild != null) + { + replyToChild.AssignedAccount = account; + + if (replyToChild.FolderId == draftFolder.Id) + replyToChild.AssignedFolder = draftFolder; + + if (replyToChild.FolderId == sentFolder.Id) + replyToChild.AssignedFolder = sentFolder; + + if (replyToChild.FolderId == replyItem.AssignedFolder.Id) + replyToChild.AssignedFolder = replyItem.AssignedFolder; + + threadItem.AddThreadItem(replyToChild); + + if (mailLookupTable.ContainsKey(replyToChild.Id)) + mailLookupTable[replyToChild.Id] = true; + + replyToChild = await GetReplyParentAsync(replyToChild, accountId, replyToChild.AssignedFolder.Id, sentFolder.Id, draftFolder.Id); + } + + // Build down + var replyToParent = await GetInReplyToReplyAsync(replyItem, accountId, replyItem.AssignedFolder.Id, sentFolder.Id, draftFolder.Id); + + while (replyToParent != null) + { + replyToParent.AssignedAccount = account; + + if (replyToParent.FolderId == draftFolder.Id) + replyToParent.AssignedFolder = draftFolder; + + if (replyToParent.FolderId == sentFolder.Id) + replyToParent.AssignedFolder = sentFolder; + + if (replyToParent.FolderId == replyItem.AssignedFolder.Id) + replyToParent.AssignedFolder = replyItem.AssignedFolder; + + threadItem.AddThreadItem(replyToParent); + + if (mailLookupTable.ContainsKey(replyToParent.Id)) + mailLookupTable[replyToParent.Id] = true; + + replyToParent = await GetInReplyToReplyAsync(replyToParent, accountId, replyToParent.AssignedFolder.Id, sentFolder.Id, draftFolder.Id); + } + + // It's a thread item. + + if (threadItem.ThreadItems.Count > 1 && !threads.Exists(a => a.Id == threadItem.Id)) + { + threads.Add(threadItem); + } + else + { + // False alert. This is not a thread item. + mailLookupTable[replyItem.Id] = false; + + // TODO: Here potentially check other algorithms for threading like References. + } + } + + // At this points all mails in the list belong to single items. + // Merge with threads. + // Last sorting will be done later on in MailService. + + // Remove single mails that are included in thread. + items.RemoveAll(a => mailLookupTable.ContainsKey(a.Id) && mailLookupTable[a.Id]); + + var finalList = new List(items); + + finalList.AddRange(threads); + + return finalList; + } + + public bool ShouldThreadWithItem(IMailItem originalItem, IMailItem targetItem) + { + bool isChild = originalItem.InReplyTo != null && originalItem.InReplyTo == targetItem.MessageId; + bool isParent = originalItem.MessageId != null && originalItem.MessageId == targetItem.InReplyTo; + + return isChild || isParent; + } + } +} diff --git a/Wino.Core/Integration/Threading/OutlookThreadingStrategy.cs b/Wino.Core/Integration/Threading/OutlookThreadingStrategy.cs new file mode 100644 index 00000000..89c4bd89 --- /dev/null +++ b/Wino.Core/Integration/Threading/OutlookThreadingStrategy.cs @@ -0,0 +1,14 @@ +using Wino.Core.Domain.Interfaces; +using Wino.Core.Services; + +namespace Wino.Core.Integration.Threading +{ + // Outlook and Gmail is using the same threading strategy. + // Outlook: ConversationId -> it's set as ThreadId + // Gmail: ThreadId + + public class OutlookThreadingStrategy : APIThreadingStrategy + { + public OutlookThreadingStrategy(IDatabaseService databaseService, IFolderService folderService) : base(databaseService, folderService) { } + } +} diff --git a/Wino.Core/MenuItems/AccountMenuItem.cs b/Wino.Core/MenuItems/AccountMenuItem.cs new file mode 100644 index 00000000..3bd7d13f --- /dev/null +++ b/Wino.Core/MenuItems/AccountMenuItem.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CommunityToolkit.Mvvm.ComponentModel; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Folders; + +namespace Wino.Core.MenuItems +{ + public partial class AccountMenuItem : MenuItemBase>, IAccountMenuItem + { + public List FlattenedFolderHierarchy { get; set; } + + [ObservableProperty] + private int unreadItemCount; + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(IsSynchronizationProgressVisible))] + private double synchronizationProgress; + + public bool IsAttentionRequired => AttentionReason != AccountAttentionReason.None; + public bool IsSynchronizationProgressVisible => SynchronizationProgress > 0 && SynchronizationProgress < 100; + public Guid AccountId => Parameter.Id; + + private AccountAttentionReason attentionReason; + + public AccountAttentionReason AttentionReason + { + get => attentionReason; + set + { + if (SetProperty(ref attentionReason, value)) + { + OnPropertyChanged(nameof(IsAttentionRequired)); + + UpdateFixAccountIssueMenuItem(); + } + } + } + + public string AccountName + { + get => Parameter.Name; + set => SetProperty(Parameter.Name, value, Parameter, (u, n) => u.Name = n); + } + + public IEnumerable HoldingAccounts => new List { Parameter }; + + public AccountMenuItem(MailAccount account, IMenuItem parent = null) : base(account, account.Id, parent) + { + UpdateAccount(account); + } + + public void UpdateAccount(MailAccount account) + { + Parameter = account; + AccountName = account.Name; + AttentionReason = account.AttentionReason; + } + + private void UpdateFixAccountIssueMenuItem() + { + if (AttentionReason != AccountAttentionReason.None && !SubMenuItems.Any(a => a is FixAccountIssuesMenuItem)) + { + // Add fix issue item if not exists. + SubMenuItems.Insert(0, new FixAccountIssuesMenuItem(Parameter, this)); + } + else + { + // Remove existing if issue is resolved. + var fixAccountIssueItem = SubMenuItems.FirstOrDefault(a => a is FixAccountIssuesMenuItem); + + if (fixAccountIssueItem != null) + { + SubMenuItems.Remove(fixAccountIssueItem); + } + } + } + + public int GetUnreadItemCountByFolderType(SpecialFolderType specialFolderType) + => FlattenedFolderHierarchy?.Where(a => a.SpecialFolderType == specialFolderType).Sum(a => a.UnreadItemCount) ?? 0; + } +} diff --git a/Wino.Core/MenuItems/FixAccountIssuesMenuItem.cs b/Wino.Core/MenuItems/FixAccountIssuesMenuItem.cs new file mode 100644 index 00000000..ce953cb6 --- /dev/null +++ b/Wino.Core/MenuItems/FixAccountIssuesMenuItem.cs @@ -0,0 +1,16 @@ +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Folders; + +namespace Wino.Core.MenuItems +{ + public class FixAccountIssuesMenuItem : MenuItemBase + { + public MailAccount Account { get; } + + public FixAccountIssuesMenuItem(MailAccount account, IMenuItem parentAccountMenuItem) : base(null, null, parentAccountMenuItem) + { + Account = account; + } + } +} diff --git a/Wino.Core/MenuItems/FolderMenuItem.cs b/Wino.Core/MenuItems/FolderMenuItem.cs new file mode 100644 index 00000000..a192b70e --- /dev/null +++ b/Wino.Core/MenuItems/FolderMenuItem.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; +using System.Linq; +using CommunityToolkit.Mvvm.ComponentModel; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Folders; + +namespace Wino.Core.MenuItems +{ + public partial class FolderMenuItem : MenuItemBase, IFolderMenuItem + { + [ObservableProperty] + private int unreadItemCount; + + public bool HasTextColor => !string.IsNullOrEmpty(Parameter.TextColorHex); + public bool IsMoveTarget => HandlingFolders.All(a => a.IsMoveTarget); + + public SpecialFolderType SpecialFolderType => Parameter.SpecialFolderType; + public bool IsSticky => Parameter.IsSticky; + public bool IsSystemFolder => Parameter.IsSystemFolder; + + /// + /// Display name of the folder. More and Category folders have localized display names. + /// + public string FolderName + { + get + { + if (Parameter.SpecialFolderType == SpecialFolderType.More) + return Translator.MoreFolderNameOverride; + else if (Parameter.SpecialFolderType == SpecialFolderType.Category) + return Translator.CategoriesFolderNameOverride; + else + return Parameter.FolderName; + } + set => SetProperty(Parameter.FolderName, value, Parameter, (u, n) => u.FolderName = n); + } + + public bool IsSynchronizationEnabled + { + get => Parameter.IsSynchronizationEnabled; + set => SetProperty(Parameter.IsSynchronizationEnabled, value, Parameter, (u, n) => u.IsSynchronizationEnabled = n); + } + + public IEnumerable HandlingFolders => new List() { Parameter }; + + public MailAccount ParentAccount { get; } + + public string AssignedAccountName => ParentAccount?.Name; + + public bool ShowUnreadCount => Parameter.ShowUnreadCount; + + public FolderMenuItem(IMailItemFolder folderStructure, MailAccount parentAccount, IMenuItem parentMenuItem) : base(folderStructure, folderStructure.Id, parentMenuItem) + { + ParentAccount = parentAccount; + } + + public void UpdateFolder(IMailItemFolder folder) + { + Parameter = folder; + + OnPropertyChanged(nameof(IsSynchronizationEnabled)); + OnPropertyChanged(nameof(ShowUnreadCount)); + OnPropertyChanged(nameof(HasTextColor)); + OnPropertyChanged(nameof(IsSystemFolder)); + OnPropertyChanged(nameof(SpecialFolderType)); + OnPropertyChanged(nameof(IsSticky)); + OnPropertyChanged(nameof(FolderName)); + } + + public override string ToString() => FolderName; + } +} diff --git a/Wino.Core/MenuItems/ManageAccountsMenuItem.cs b/Wino.Core/MenuItems/ManageAccountsMenuItem.cs new file mode 100644 index 00000000..2d69f5b0 --- /dev/null +++ b/Wino.Core/MenuItems/ManageAccountsMenuItem.cs @@ -0,0 +1,4 @@ +namespace Wino.Core.MenuItems +{ + public class ManageAccountsMenuItem : MenuItemBase { } +} diff --git a/Wino.Core/MenuItems/MenuItemBase.cs b/Wino.Core/MenuItems/MenuItemBase.cs new file mode 100644 index 00000000..fef4fffd --- /dev/null +++ b/Wino.Core/MenuItems/MenuItemBase.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.ObjectModel; +using CommunityToolkit.Mvvm.ComponentModel; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.MenuItems +{ + public partial class MenuItemBase : ObservableObject, IMenuItem + { + [ObservableProperty] + private bool _isExpanded; + + [ObservableProperty] + private bool _isSelected; + + public IMenuItem ParentMenuItem { get; } + + public Guid? EntityId { get; } + + public MenuItemBase(Guid? entityId = null, IMenuItem parentMenuItem = null) + { + EntityId = entityId; + ParentMenuItem = parentMenuItem; + } + + public void Expand() + { + // Recursively expand all parent menu items if parent exists, starting from parent. + if (ParentMenuItem != null) + { + IMenuItem parentMenuItem = ParentMenuItem; + + while (parentMenuItem != null) + { + parentMenuItem.IsExpanded = true; + + parentMenuItem = parentMenuItem.ParentMenuItem; + } + } + + // Finally expand itself. + IsExpanded = true; + } + } + + public partial class MenuItemBase : MenuItemBase + { + [ObservableProperty] + private T _parameter; + + public MenuItemBase(T parameter, Guid? entityId, IMenuItem parentMenuItem = null) : base(entityId, parentMenuItem) => Parameter = parameter; + } + + public partial class MenuItemBase : MenuItemBase + { + [ObservableProperty] + private bool _isChildSelected; + + protected MenuItemBase(TValue parameter, Guid? entityId, IMenuItem parentMenuItem = null) : base(parameter, entityId, parentMenuItem) { } + + public ObservableCollection SubMenuItems { get; set; } = []; + } +} diff --git a/Wino.Core/MenuItems/MenuItemCollection.cs b/Wino.Core/MenuItems/MenuItemCollection.cs new file mode 100644 index 00000000..f166a08a --- /dev/null +++ b/Wino.Core/MenuItems/MenuItemCollection.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.MenuItems +{ + public class MenuItemCollection : ObservableRangeCollection + { + public IEnumerable GetFolderItems(Guid folderId) + { + var rootItems = this.OfType() + .SelectMany(a => a.FlattenedFolderHierarchy) + .Where(a => a.Parameter?.Id == folderId) + .Cast(); + + // Accounts that are merged can't exist in the root items. + // Therefore if the folder is found in root items, return it without searching inside merged accounts. + + if (rootItems.Any()) return rootItems; + + var mergedItems = this.OfType() + .SelectMany(a => a.SubMenuItems.OfType() + .Where(a => a.Parameter.Any(b => b.Id == folderId))) + .Cast(); + + // Folder is found in the MergedInbox shared folders. + if (mergedItems.Any()) return mergedItems; + + // Folder is not in any of the above. Looks inside the individual accounts in merged inbox account menu item. + var mergedAccountItems = this.OfType() + .SelectMany(a => a.SubMenuItems.OfType() + .SelectMany(a => a.FlattenedFolderHierarchy) + .Where(a => a.Parameter?.Id == folderId)) + .Cast(); + + return mergedAccountItems; + } + + public IBaseFolderMenuItem GetFolderItem(Guid folderId) => GetFolderItems(folderId).FirstOrDefault(); + + public IAccountMenuItem GetAccountMenuItem(Guid accountId) + { + if (accountId == null) return null; + + if (TryGetRootAccountMenuItem(accountId, out IAccountMenuItem rootAccountMenuItem)) return rootAccountMenuItem; + + return null; + } + + // Pattern: Look for root account menu item only. Don't search inside the merged account menu item. + public bool TryGetRootAccountMenuItem(Guid accountId, out IAccountMenuItem value) + { + value = this.OfType().FirstOrDefault(a => a.HoldingAccounts.Any(b => b.Id == accountId)); + + value ??= this.OfType().FirstOrDefault(a => a.EntityId == accountId); + + return value != null; + } + + // Pattern: Look for root account menu item only and return the folder menu item inside the account menu item that has specific special folder type. + public bool TryGetRootSpecialFolderMenuItem(Guid accountId, SpecialFolderType specialFolderType, out FolderMenuItem value) + { + value = this.OfType() + .Where(a => a.HoldingAccounts.Any(b => b.Id == accountId)) + .SelectMany(a => a.FlattenedFolderHierarchy) + .FirstOrDefault(a => a.Parameter?.SpecialFolderType == specialFolderType); + + return value != null; + } + + // Pattern: Look for special folder menu item inside the loaded folders for Windows Mail style menu items. + public bool TryGetWindowsStyleRootSpecialFolderMenuItem(Guid accountId, SpecialFolderType specialFolderType, out FolderMenuItem value) + { + value = this.OfType() + .FirstOrDefault(a => a.HandlingFolders.Any(b => b.MailAccountId == accountId && b.SpecialFolderType == specialFolderType)) as FolderMenuItem; + + return value != null; + } + + // Pattern: Find the merged account menu item and return the special folder menu item that belongs to the merged account menu item. + // This will not look for the folders inside individual account menu items inside merged account menu item. + public bool TryGetMergedAccountSpecialFolderMenuItem(Guid mergedInboxId, SpecialFolderType specialFolderType, out IBaseFolderMenuItem value) + { + value = this.OfType() + .Where(a => a.EntityId == mergedInboxId) + .SelectMany(a => a.SubMenuItems) + .OfType() + .FirstOrDefault(a => a.SpecialFolderType == specialFolderType); + + return value != null; + } + + // Pattern: Find the child account menu item inside the merged account menu item, locate the special folder menu item inside the child account menu item. + public bool TryGetMergedAccountFolderMenuItemByAccountId(Guid accountId, SpecialFolderType specialFolderType, out FolderMenuItem value) + { + value = this.OfType() + .SelectMany(a => a.SubMenuItems) + .OfType() + .FirstOrDefault(a => a.HoldingAccounts.Any(b => b.Id == accountId)) + ?.FlattenedFolderHierarchy + .OfType() + .FirstOrDefault(a => a.Parameter?.SpecialFolderType == specialFolderType); + + return value != null; + } + + // Pattern: Find the common folder menu item with special folder type inside the merged account menu item for the given AccountId. + public bool TryGetMergedAccountRootFolderMenuItemByAccountId(Guid accountId, SpecialFolderType specialFolderType, out MergedAccountFolderMenuItem value) + { + value = this.OfType() + .Where(a => a.HoldingAccounts.Any(b => b.Id == accountId)) + .SelectMany(a => a.SubMenuItems) + .OfType() + .FirstOrDefault(a => a.SpecialFolderType == specialFolderType); + + return value != null; + } + + /// + /// Skips the merged account menu item, but directly returns the Account menu item inside the merged account menu item. + /// + /// Account id to look for. + /// Direct AccountMenuItem inside the Merged Account menu item if exists. + public AccountMenuItem GetSpecificAccountMenuItem(Guid accountId) + { + AccountMenuItem accountMenuItem = null; + + accountMenuItem = this.OfType().FirstOrDefault(a => a.HoldingAccounts.Any(b => b.Id == accountId)); + + // Look for the items inside the merged accounts if regular menu item is not found. + accountMenuItem ??= this.OfType() + .FirstOrDefault(a => a.HoldingAccounts.Any(b => b.Id == accountId))?.SubMenuItems + .OfType() + .FirstOrDefault(); + + return accountMenuItem; + } + + public void ReplaceFolders(IEnumerable folders) + { + ClearFolderAreaMenuItems(); + + Items.Add(new SeperatorItem()); + AddRange(folders); + } + + public void AddAccountMenuItem(IAccountMenuItem accountMenuItem) + { + var lastAccount = Items.OfType().LastOrDefault(); + + // Index 0 is always the New Mail button. + var insertIndex = lastAccount == null ? 1 : Items.IndexOf(lastAccount) + 1; + + Insert(insertIndex, accountMenuItem); + } + + private void ClearFolderAreaMenuItems() + { + var cloneItems = Items.ToList(); + + foreach (var item in cloneItems) + { + if (item is SeperatorItem || item is IBaseFolderMenuItem || item is MergedAccountMoreFolderMenuItem) + { + item.IsSelected = false; + item.IsExpanded = false; + + Remove(item); + } + } + } + } +} diff --git a/Wino.Core/MenuItems/MergedAccountFolderMenuItem.cs b/Wino.Core/MenuItems/MergedAccountFolderMenuItem.cs new file mode 100644 index 00000000..4ad3601e --- /dev/null +++ b/Wino.Core/MenuItems/MergedAccountFolderMenuItem.cs @@ -0,0 +1,110 @@ +using System.Collections.Generic; +using System.Linq; +using CommunityToolkit.Diagnostics; +using CommunityToolkit.Mvvm.ComponentModel; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Folders; + +namespace Wino.Core.MenuItems +{ + /// + /// Menu item that holds a list of folders under the merged account menu item. + /// + public partial class MergedAccountFolderMenuItem : MenuItemBase>, IMergedAccountFolderMenuItem + { + public SpecialFolderType FolderType { get; } + + public string FolderName { get; private set; } + + // Any of the folders is enough to determine the synchronization enable/disable state. + public bool IsSynchronizationEnabled => HandlingFolders.Any(a => a.IsSynchronizationEnabled); + public bool IsMoveTarget => HandlingFolders.All(a => a.IsMoveTarget); + public IEnumerable HandlingFolders => Parameter; + + // All folders in the list should have the same type. + public SpecialFolderType SpecialFolderType => HandlingFolders.First().SpecialFolderType; + + public bool IsSticky => true; + + public bool IsSystemFolder => true; + + public string AssignedAccountName => MergedInbox?.Name; + + public MergedInbox MergedInbox { get; set; } + + public bool ShowUnreadCount => HandlingFolders?.Any(a => a.ShowUnreadCount) ?? false; + + [ObservableProperty] + private int unreadItemCount; + + // Merged account's shared folder menu item does not have an entity id. + // Navigations to specific folders are done by explicit folder id if needed. + + public MergedAccountFolderMenuItem(List parameter, IMenuItem parentMenuItem, MergedInbox mergedInbox) : base(parameter, null, parentMenuItem) + { + Guard.IsNotNull(mergedInbox, nameof(mergedInbox)); + Guard.IsNotNull(parameter, nameof(parameter)); + Guard.HasSizeGreaterThan(parameter, 0, nameof(parameter)); + + MergedInbox = mergedInbox; + + SetFolderName(); + + // All folders in the list should have the same type. + FolderType = parameter[0].SpecialFolderType; + } + + private void SetFolderName() + { + // Folders that hold more than 1 folder belong to merged account. + // These folders will be displayed as their localized names based on the + // special type they have. + + if (HandlingFolders.Count() > 1) + { + FolderName = GetSpecialFolderName(HandlingFolders.First()); + } + else + { + // Folder only holds 1 Id, but it's displayed as merged account folder. + FolderName = HandlingFolders.First().FolderName; + } + } + + private string GetSpecialFolderName(IMailItemFolder folder) + { + var specialType = folder.SpecialFolderType; + + // We only handle 5 different types for combining folders. + // Rest of the types are not supported. + + return specialType switch + { + SpecialFolderType.Inbox => Translator.MergedAccountCommonFolderInbox, + SpecialFolderType.Draft => Translator.MergedAccountCommonFolderDraft, + SpecialFolderType.Sent => Translator.MergedAccountCommonFolderSent, + SpecialFolderType.Deleted => Translator.MergedAccountCommonFolderTrash, + SpecialFolderType.Junk => Translator.MergedAccountCommonFolderJunk, + SpecialFolderType.Archive => Translator.MergedAccountCommonFolderArchive, + _ => folder.FolderName, + }; + } + + public void UpdateFolder(IMailItemFolder folder) + { + var existingFolder = Parameter.FirstOrDefault(a => a.Id == folder.Id); + + if (existingFolder == null) return; + + Parameter.Remove(existingFolder); + Parameter.Add(folder); + + SetFolderName(); + OnPropertyChanged(nameof(ShowUnreadCount)); + OnPropertyChanged(nameof(IsSynchronizationEnabled)); + } + } +} diff --git a/Wino.Core/MenuItems/MergedAccountMenuItem.cs b/Wino.Core/MenuItems/MergedAccountMenuItem.cs new file mode 100644 index 00000000..42058afe --- /dev/null +++ b/Wino.Core/MenuItems/MergedAccountMenuItem.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using System.Linq; +using CommunityToolkit.Mvvm.ComponentModel; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.MenuItems +{ + public partial class MergedAccountMenuItem : MenuItemBase, IAccountMenuItem + { + public int MergedAccountCount => GetAccountMenuItems().Count(); + + public IEnumerable HoldingAccounts => GetAccountMenuItems()?.SelectMany(a => a.HoldingAccounts); + + [ObservableProperty] + private int unreadItemCount; + + [ObservableProperty] + private double synchronizationProgress; + + [ObservableProperty] + private string mergedAccountName; + + public MergedAccountMenuItem(MergedInbox mergedInbox, IMenuItem parent) : base(mergedInbox, mergedInbox.Id, parent) + { + MergedAccountName = mergedInbox.Name; + } + + public void RefreshFolderItemCount() + { + UnreadItemCount = GetAccountMenuItems().Select(a => a.GetUnreadItemCountByFolderType(SpecialFolderType.Inbox)).Sum(); + + var unreadUpdateFolders = SubMenuItems.OfType().Where(a => a.ShowUnreadCount); + + foreach (var folder in unreadUpdateFolders) + { + folder.UnreadItemCount = GetAccountMenuItems().Select(a => a.GetUnreadItemCountByFolderType(folder.SpecialFolderType)).Sum(); + } + } + + // Accounts are always located in More folder of Merged Inbox menu item. + public IEnumerable GetAccountMenuItems() + { + var moreFolder = SubMenuItems.OfType().FirstOrDefault(); + + if (moreFolder == null) return default; + + return moreFolder.SubMenuItems.OfType(); + } + + public void UpdateAccount(MailAccount account) + => GetAccountMenuItems().FirstOrDefault(a => a.HoldingAccounts.Any(b => b.Id == account.Id))?.UpdateAccount(account); + } +} diff --git a/Wino.Core/MenuItems/MergedAccountMoreFolderMenuItem.cs b/Wino.Core/MenuItems/MergedAccountMoreFolderMenuItem.cs new file mode 100644 index 00000000..f0a8b0cc --- /dev/null +++ b/Wino.Core/MenuItems/MergedAccountMoreFolderMenuItem.cs @@ -0,0 +1,12 @@ +using System; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.MenuItems +{ + public class MergedAccountMoreFolderMenuItem : MenuItemBase + { + public MergedAccountMoreFolderMenuItem(object parameter, Guid? entityId, IMenuItem parentMenuItem = null) : base(parameter, entityId, parentMenuItem) + { + } + } +} diff --git a/Wino.Core/MenuItems/NewMailMenuItem.cs b/Wino.Core/MenuItems/NewMailMenuItem.cs new file mode 100644 index 00000000..dc543c2d --- /dev/null +++ b/Wino.Core/MenuItems/NewMailMenuItem.cs @@ -0,0 +1,4 @@ +namespace Wino.Core.MenuItems +{ + public class NewMailMenuItem : MenuItemBase { } +} diff --git a/Wino.Core/MenuItems/ObservableRangeCollection.cs b/Wino.Core/MenuItems/ObservableRangeCollection.cs new file mode 100644 index 00000000..17025fd0 --- /dev/null +++ b/Wino.Core/MenuItems/ObservableRangeCollection.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; + +namespace Wino.Core.MenuItems +{ + /// + /// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed. + /// + /// + public class ObservableRangeCollection : ObservableCollection + { + + /// + /// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class. + /// + public ObservableRangeCollection() + : base() + { + } + + /// + /// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection. + /// + /// collection: The collection from which the elements are copied. + /// The collection parameter cannot be null. + public ObservableRangeCollection(IEnumerable collection) + : base(collection) + { + } + + /// + /// Adds the elements of the specified collection to the end of the ObservableCollection(Of T). + /// + public void AddRange(IEnumerable collection, NotifyCollectionChangedAction notificationMode = NotifyCollectionChangedAction.Add) + { + if (notificationMode != NotifyCollectionChangedAction.Add && notificationMode != NotifyCollectionChangedAction.Reset) + throw new ArgumentException("Mode must be either Add or Reset for AddRange.", nameof(notificationMode)); + if (collection == null) + throw new ArgumentNullException(nameof(collection)); + + CheckReentrancy(); + + var startIndex = Count; + + var itemsAdded = AddArrangeCore(collection); + + if (!itemsAdded) + return; + + if (notificationMode == NotifyCollectionChangedAction.Reset) + { + RaiseChangeNotificationEvents(action: NotifyCollectionChangedAction.Reset); + + return; + } + + var changedItems = collection is List ? (List)collection : new List(collection); + + RaiseChangeNotificationEvents( + action: NotifyCollectionChangedAction.Add, + changedItems: changedItems, + startingIndex: startIndex); + } + + /// + /// Removes the first occurence of each item in the specified collection from ObservableCollection(Of T). NOTE: with notificationMode = Remove, removed items starting index is not set because items are not guaranteed to be consecutive. + /// + public void RemoveRange(IEnumerable collection, NotifyCollectionChangedAction notificationMode = NotifyCollectionChangedAction.Reset) + { + if (notificationMode != NotifyCollectionChangedAction.Remove && notificationMode != NotifyCollectionChangedAction.Reset) + throw new ArgumentException("Mode must be either Remove or Reset for RemoveRange.", nameof(notificationMode)); + if (collection == null) + throw new ArgumentNullException(nameof(collection)); + + CheckReentrancy(); + + if (notificationMode == NotifyCollectionChangedAction.Reset) + { + var raiseEvents = false; + foreach (var item in collection) + { + Items.Remove(item); + raiseEvents = true; + } + + if (raiseEvents) + RaiseChangeNotificationEvents(action: NotifyCollectionChangedAction.Reset); + + return; + } + + var changedItems = new List(collection); + for (var i = 0; i < changedItems.Count; i++) + { + if (!Items.Remove(changedItems[i])) + { + changedItems.RemoveAt(i); //Can't use a foreach because changedItems is intended to be (carefully) modified + i--; + } + } + + if (changedItems.Count == 0) + return; + + RaiseChangeNotificationEvents( + action: NotifyCollectionChangedAction.Remove, + changedItems: changedItems); + } + + /// + /// Clears the current collection and replaces it with the specified item. + /// + public void Replace(T item) => ReplaceRange(new T[] { item }); + + /// + /// Clears the current collection and replaces it with the specified collection. + /// + public void ReplaceRange(IEnumerable collection) + { + if (collection == null) + throw new ArgumentNullException(nameof(collection)); + + CheckReentrancy(); + + var previouslyEmpty = Items.Count == 0; + + Items.Clear(); + + AddArrangeCore(collection); + + var currentlyEmpty = Items.Count == 0; + + if (previouslyEmpty && currentlyEmpty) + return; + + RaiseChangeNotificationEvents(action: NotifyCollectionChangedAction.Reset); + } + + private bool AddArrangeCore(IEnumerable collection) + { + var itemAdded = false; + foreach (var item in collection) + { + Items.Add(item); + itemAdded = true; + } + return itemAdded; + } + + private void RaiseChangeNotificationEvents(NotifyCollectionChangedAction action, List? changedItems = null, int startingIndex = -1) + { + OnPropertyChanged(new PropertyChangedEventArgs(nameof(Count))); + OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); + + if (changedItems is null) + OnCollectionChanged(new NotifyCollectionChangedEventArgs(action)); + else + OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, changedItems: changedItems, startingIndex: startingIndex)); + } + } +} diff --git a/Wino.Core/MenuItems/RateMenuItem.cs b/Wino.Core/MenuItems/RateMenuItem.cs new file mode 100644 index 00000000..cb339a1a --- /dev/null +++ b/Wino.Core/MenuItems/RateMenuItem.cs @@ -0,0 +1,4 @@ +namespace Wino.Core.MenuItems +{ + public class RateMenuItem : MenuItemBase { } +} diff --git a/Wino.Core/MenuItems/SeperatorItem.cs b/Wino.Core/MenuItems/SeperatorItem.cs new file mode 100644 index 00000000..974f36cc --- /dev/null +++ b/Wino.Core/MenuItems/SeperatorItem.cs @@ -0,0 +1,4 @@ +namespace Wino.Core.MenuItems +{ + public class SeperatorItem : MenuItemBase { } +} diff --git a/Wino.Core/MenuItems/SettingsItem.cs b/Wino.Core/MenuItems/SettingsItem.cs new file mode 100644 index 00000000..12aafba9 --- /dev/null +++ b/Wino.Core/MenuItems/SettingsItem.cs @@ -0,0 +1,4 @@ +namespace Wino.Core.MenuItems +{ + public class SettingsItem : MenuItemBase { } +} diff --git a/Wino.Core/Messages/Accounts/AccountMenuItemExtended.cs b/Wino.Core/Messages/Accounts/AccountMenuItemExtended.cs new file mode 100644 index 00000000..737ae9c3 --- /dev/null +++ b/Wino.Core/Messages/Accounts/AccountMenuItemExtended.cs @@ -0,0 +1,14 @@ +using System; +using Wino.Core.Domain.Models.MailItem; + +namespace Wino.Core.Messages.Accounts +{ + /// + /// When menu item for the account is requested to be extended. + /// Additional properties are also supported to navigate to correct IMailItem. + /// + /// Account to extend menu item for. + /// Folder to select after expansion. + /// Mail item to select if possible in the expanded folder. + public record AccountMenuItemExtended(Guid FolderId, IMailItem NavigateMailItem); +} diff --git a/Wino.Core/Messages/Accounts/AccountsMenuRefreshRequested.cs b/Wino.Core/Messages/Accounts/AccountsMenuRefreshRequested.cs new file mode 100644 index 00000000..d6848622 --- /dev/null +++ b/Wino.Core/Messages/Accounts/AccountsMenuRefreshRequested.cs @@ -0,0 +1,7 @@ +namespace Wino.Core.Messages.Accounts +{ + /// + /// When a full menu refresh for accounts menu is requested. + /// + public record AccountsMenuRefreshRequested(bool AutomaticallyNavigateFirstItem = true); +} diff --git a/Wino.Core/Messages/Authorization/ProtocolAuthorizationCallbackReceived.cs b/Wino.Core/Messages/Authorization/ProtocolAuthorizationCallbackReceived.cs new file mode 100644 index 00000000..d748f6df --- /dev/null +++ b/Wino.Core/Messages/Authorization/ProtocolAuthorizationCallbackReceived.cs @@ -0,0 +1,10 @@ +using System; + +namespace Wino.Core.Messages.Authorization +{ + /// + /// When Google authentication makes a callback to the app via protocol activation to the app. + /// + /// Callback Uri that Google returned. + public record ProtocolAuthorizationCallbackReceived(Uri AuthorizationResponseUri); +} diff --git a/Wino.Core/Messages/Mails/CancelRenderingContentRequested.cs b/Wino.Core/Messages/Mails/CancelRenderingContentRequested.cs new file mode 100644 index 00000000..0df0be2a --- /dev/null +++ b/Wino.Core/Messages/Mails/CancelRenderingContentRequested.cs @@ -0,0 +1,7 @@ +namespace Wino.Core.Messages.Mails +{ + /// + /// When rendered html is requested to cancel. + /// + public record CancelRenderingContentRequested; +} diff --git a/Wino.Core/Messages/Mails/ClearMailSelectionsRequested.cs b/Wino.Core/Messages/Mails/ClearMailSelectionsRequested.cs new file mode 100644 index 00000000..d38af5f4 --- /dev/null +++ b/Wino.Core/Messages/Mails/ClearMailSelectionsRequested.cs @@ -0,0 +1,7 @@ +namespace Wino.Core.Messages.Mails +{ + /// + /// When reset all mail selections requested. + /// + public record ClearMailSelectionsRequested; +} diff --git a/Wino.Core/Messages/Mails/CreateNewComposeMailRequested.cs b/Wino.Core/Messages/Mails/CreateNewComposeMailRequested.cs new file mode 100644 index 00000000..60a7586d --- /dev/null +++ b/Wino.Core/Messages/Mails/CreateNewComposeMailRequested.cs @@ -0,0 +1,10 @@ +using Wino.Core.Domain.Models.Reader; + +namespace Wino.Core.Messages.Mails +{ + /// + /// When a new composing requested. + /// + /// + public record CreateNewComposeMailRequested(MailRenderModel RenderModel); +} diff --git a/Wino.Core/Messages/Mails/HtmlRenderingRequested.cs b/Wino.Core/Messages/Mails/HtmlRenderingRequested.cs new file mode 100644 index 00000000..f7b572ec --- /dev/null +++ b/Wino.Core/Messages/Mails/HtmlRenderingRequested.cs @@ -0,0 +1,8 @@ +namespace Wino.Core.Messages.Mails +{ + /// + /// When existing a new html is requested to be rendered due to mail selection or signature. + /// + /// HTML to render in WebView2. + public record HtmlRenderingRequested(string HtmlBody); +} diff --git a/Wino.Core/Messages/Mails/ImapSetupBackNavigationRequested.cs b/Wino.Core/Messages/Mails/ImapSetupBackNavigationRequested.cs new file mode 100644 index 00000000..54ebddb6 --- /dev/null +++ b/Wino.Core/Messages/Mails/ImapSetupBackNavigationRequested.cs @@ -0,0 +1,11 @@ +using System; + +namespace Wino.Core.Messages.Mails +{ + /// + /// When IMAP setup dialog requestes back breadcrumb navigation. + /// + /// Type to go back. + /// Back parameters. + public record ImapSetupBackNavigationRequested(Type PageType, object Parameter); +} diff --git a/Wino.Core/Messages/Mails/ImapSetupDismissRequested.cs b/Wino.Core/Messages/Mails/ImapSetupDismissRequested.cs new file mode 100644 index 00000000..5a02a441 --- /dev/null +++ b/Wino.Core/Messages/Mails/ImapSetupDismissRequested.cs @@ -0,0 +1,10 @@ +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Messages.Mails +{ + /// + /// When user asked to dismiss IMAP setup dialog. + /// + /// Validated server information that is ready to be saved to database. + public record ImapSetupDismissRequested(CustomServerInformation CompletedServerInformation = null); +} diff --git a/Wino.Core/Messages/Mails/ImapSetupNavigationRequested.cs b/Wino.Core/Messages/Mails/ImapSetupNavigationRequested.cs new file mode 100644 index 00000000..4c74be90 --- /dev/null +++ b/Wino.Core/Messages/Mails/ImapSetupNavigationRequested.cs @@ -0,0 +1,11 @@ +using System; + +namespace Wino.Core.Messages.Mails +{ + /// + /// When IMAP setup dialog breadcrumb navigation requested. + /// + /// Page type to navigate. + /// Navigation parameters. + public record ImapSetupNavigationRequested(Type PageType, object Parameter); +} diff --git a/Wino.Core/Messages/Mails/MailItemNavigationRequested.cs b/Wino.Core/Messages/Mails/MailItemNavigationRequested.cs new file mode 100644 index 00000000..f64721bf --- /dev/null +++ b/Wino.Core/Messages/Mails/MailItemNavigationRequested.cs @@ -0,0 +1,11 @@ +using System; + +namespace Wino.Core.Messages.Mails +{ + /// + /// When a IMailItem needs to be navigated (or selected) + /// + /// UniqueId of the mail to navigate. + /// Whether navigated item should be scrolled to or not.. + public record MailItemNavigationRequested(Guid UniqueMailId, bool ScrollToItem = false); +} diff --git a/Wino.Core/Messages/Mails/NavigateMailFolderEvent.cs b/Wino.Core/Messages/Mails/NavigateMailFolderEvent.cs new file mode 100644 index 00000000..d6814c92 --- /dev/null +++ b/Wino.Core/Messages/Mails/NavigateMailFolderEvent.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Navigation; + +namespace Wino.Core.Messages.Mails +{ + /// + /// Selects the given FolderMenuItem in the shell folders list. + /// + public class NavigateMailFolderEvent : NavigateMailFolderEventArgs + { + public NavigateMailFolderEvent(IBaseFolderMenuItem baseFolderMenuItem, TaskCompletionSource folderInitLoadAwaitTask = null) + : base(baseFolderMenuItem, folderInitLoadAwaitTask) + { + } + } +} diff --git a/Wino.Core/Messages/Mails/RefreshUnreadCountsMessage.cs b/Wino.Core/Messages/Mails/RefreshUnreadCountsMessage.cs new file mode 100644 index 00000000..5e06029a --- /dev/null +++ b/Wino.Core/Messages/Mails/RefreshUnreadCountsMessage.cs @@ -0,0 +1,6 @@ +using System; + +namespace Wino.Core.Messages.Mails +{ + public record RefreshUnreadCountsMessage(Guid AccountId); +} diff --git a/Wino.Core/Messages/Mails/SaveAsPDFRequested.cs b/Wino.Core/Messages/Mails/SaveAsPDFRequested.cs new file mode 100644 index 00000000..e5d4e55c --- /dev/null +++ b/Wino.Core/Messages/Mails/SaveAsPDFRequested.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Wino.Core.Messages.Mails +{ + /// + /// When mail save as PDF requested. + /// + public record SaveAsPDFRequested(string FileSavePath); +} diff --git a/Wino.Core/Messages/Mails/SelectedMailItemsChanged.cs b/Wino.Core/Messages/Mails/SelectedMailItemsChanged.cs new file mode 100644 index 00000000..44ac929e --- /dev/null +++ b/Wino.Core/Messages/Mails/SelectedMailItemsChanged.cs @@ -0,0 +1,8 @@ +namespace Wino.Core.Messages.Mails +{ + /// + /// When selected mail count is changed. + /// + /// New selected mail count. + public record SelectedMailItemsChanged(int SelectedItemCount); +} diff --git a/Wino.Core/Messages/Navigation/BackBreadcrumNavigationRequested.cs b/Wino.Core/Messages/Navigation/BackBreadcrumNavigationRequested.cs new file mode 100644 index 00000000..1b485fae --- /dev/null +++ b/Wino.Core/Messages/Navigation/BackBreadcrumNavigationRequested.cs @@ -0,0 +1,7 @@ +namespace Wino.Core.Messages.Navigation +{ + /// + /// When back navigation is requested for breadcrumb pages. + /// + public record BackBreadcrumNavigationRequested { } +} diff --git a/Wino.Core/Messages/Navigation/BreadcrumbNavigationRequested.cs b/Wino.Core/Messages/Navigation/BreadcrumbNavigationRequested.cs new file mode 100644 index 00000000..e89ac11b --- /dev/null +++ b/Wino.Core/Messages/Navigation/BreadcrumbNavigationRequested.cs @@ -0,0 +1,12 @@ +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Messages.Navigation +{ + /// + /// When Breadcrumb control navigation requested. + /// + /// Title to display for the page. + /// Enum equilavent of the page to navigate. + /// Additional parameters to the page. + public record BreadcrumbNavigationRequested(string PageTitle, WinoPage PageType, object Parameter = null); +} diff --git a/Wino.Core/Messages/Navigation/NavigateSettingsRequested.cs b/Wino.Core/Messages/Navigation/NavigateSettingsRequested.cs new file mode 100644 index 00000000..1e4a9477 --- /dev/null +++ b/Wino.Core/Messages/Navigation/NavigateSettingsRequested.cs @@ -0,0 +1,7 @@ +namespace Wino.Core.Messages.Navigation +{ + /// + /// Navigates to settings page. + /// + public record NavigateSettingsRequested; +} \ No newline at end of file diff --git a/Wino.Core/Messages/Shell/ApplicationThemeChanged.cs b/Wino.Core/Messages/Shell/ApplicationThemeChanged.cs new file mode 100644 index 00000000..a688f33b --- /dev/null +++ b/Wino.Core/Messages/Shell/ApplicationThemeChanged.cs @@ -0,0 +1,8 @@ +namespace Wino.Core.Messages.Shell +{ + /// + /// When the application theme changed. + /// + /// + public record ApplicationThemeChanged(bool IsUnderlyingThemeDark); +} diff --git a/Wino.Core/Messages/Shell/CreateNewMailWithMultipleAccountsRequested.cs b/Wino.Core/Messages/Shell/CreateNewMailWithMultipleAccountsRequested.cs new file mode 100644 index 00000000..ac938562 --- /dev/null +++ b/Wino.Core/Messages/Shell/CreateNewMailWithMultipleAccountsRequested.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Messages.Shell +{ + /// + /// When + /// - There is no selection of any folder for any account + /// - Multiple accounts exists + /// - User clicked 'Create New Mail' + /// + /// flyout must be presented to pick correct account. + /// This message will be picked up by UWP Shell. + /// + public record CreateNewMailWithMultipleAccountsRequested(IEnumerable AllAccounts); +} diff --git a/Wino.Core/Messages/Shell/InfoBarMessageRequested.cs b/Wino.Core/Messages/Shell/InfoBarMessageRequested.cs new file mode 100644 index 00000000..a67daea7 --- /dev/null +++ b/Wino.Core/Messages/Shell/InfoBarMessageRequested.cs @@ -0,0 +1,17 @@ +using System; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Messages.Shell +{ + /// + /// For displaying right sliding notification message in shell. + /// + /// Severity of notification. + /// Title of the message. + /// Message content. + public record InfoBarMessageRequested(InfoBarMessageType Severity, + string Title, + string Message, + string ActionButtonTitle = "", + Action Action = null); +} diff --git a/Wino.Core/Messages/Shell/LanguageChanged.cs b/Wino.Core/Messages/Shell/LanguageChanged.cs new file mode 100644 index 00000000..3c98746f --- /dev/null +++ b/Wino.Core/Messages/Shell/LanguageChanged.cs @@ -0,0 +1,7 @@ +namespace Wino.Core.Messages.Shell +{ + /// + /// When application language is updated. + /// + public record LanguageChanged; +} diff --git a/Wino.Core/Messages/Shell/MailtoProtocolMessageRequested.cs b/Wino.Core/Messages/Shell/MailtoProtocolMessageRequested.cs new file mode 100644 index 00000000..9ed37bc4 --- /dev/null +++ b/Wino.Core/Messages/Shell/MailtoProtocolMessageRequested.cs @@ -0,0 +1,4 @@ +namespace Wino.Core.Messages.Shell +{ + public class MailtoProtocolMessageRequested { } +} diff --git a/Wino.Core/Messages/Shell/NavigationPaneModeChanged.cs b/Wino.Core/Messages/Shell/NavigationPaneModeChanged.cs new file mode 100644 index 00000000..994d33ed --- /dev/null +++ b/Wino.Core/Messages/Shell/NavigationPaneModeChanged.cs @@ -0,0 +1,10 @@ +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Messages.Shell +{ + /// + /// When navigation pane mode is changed. + /// + /// New navigation mode. + public record NavigationPaneModeChanged(MenuPaneMode NewMode); +} diff --git a/Wino.Core/Messages/Shell/ShellStateUpdated.cs b/Wino.Core/Messages/Shell/ShellStateUpdated.cs new file mode 100644 index 00000000..4d07af9e --- /dev/null +++ b/Wino.Core/Messages/Shell/ShellStateUpdated.cs @@ -0,0 +1,7 @@ +namespace Wino.Core.Messages.Shell +{ + /// + /// When reading mail state or reader pane narrowed state is changed. + /// + public record ShellStateUpdated; +} diff --git a/Wino.Core/Messages/Synchronization/AccountSynchronizationCompleted.cs b/Wino.Core/Messages/Synchronization/AccountSynchronizationCompleted.cs new file mode 100644 index 00000000..c45033fe --- /dev/null +++ b/Wino.Core/Messages/Synchronization/AccountSynchronizationCompleted.cs @@ -0,0 +1,7 @@ +using System; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Messages.Synchronization +{ + public record AccountSynchronizationCompleted(Guid AccountId, SynchronizationCompletedState Result, Guid? SynchronizationTrackingId); +} diff --git a/Wino.Core/Messages/Synchronization/AccountSynchronizerStateChanged.cs b/Wino.Core/Messages/Synchronization/AccountSynchronizerStateChanged.cs new file mode 100644 index 00000000..ea7db120 --- /dev/null +++ b/Wino.Core/Messages/Synchronization/AccountSynchronizerStateChanged.cs @@ -0,0 +1,12 @@ +using Wino.Core.Domain.Enums; +using Wino.Core.Synchronizers; + +namespace Wino.Core.Messages.Synchronization +{ + /// + /// Emitted when synchronizer state is updated. + /// + /// Account Synchronizer + /// New state. + public record AccountSynchronizerStateChanged(IBaseSynchronizer Synchronizer, AccountSynchronizerState NewState); +} diff --git a/Wino.Core/Messages/Synchronization/NewSynchronizationRequested.cs b/Wino.Core/Messages/Synchronization/NewSynchronizationRequested.cs new file mode 100644 index 00000000..593d6c89 --- /dev/null +++ b/Wino.Core/Messages/Synchronization/NewSynchronizationRequested.cs @@ -0,0 +1,10 @@ +using Wino.Core.Domain.Models.Synchronization; + +namespace Wino.Core.Messages.Synchronization +{ + /// + /// Triggers a new synchronization if possible. + /// + /// Options for synchronization. + public record NewSynchronizationRequested(SynchronizationOptions Options); +} diff --git a/Wino.Core/Mime/HtmlPreviewVisitor.cs b/Wino.Core/Mime/HtmlPreviewVisitor.cs new file mode 100644 index 00000000..7c1e9029 --- /dev/null +++ b/Wino.Core/Mime/HtmlPreviewVisitor.cs @@ -0,0 +1,248 @@ +using System; +using System.Collections.Generic; +using System.IO; +using MimeKit; +using MimeKit.Text; +using MimeKit.Tnef; + +namespace Wino.Core.Mime +{ + /// + /// Visits a MimeMessage and generates HTML suitable to be rendered by a browser control. + /// + public class HtmlPreviewVisitor : MimeVisitor + { + List stack = new List(); + List attachments = new List(); + + readonly string tempDir; + + public string Body { get; set; } + + /// + /// Creates a new HtmlPreviewVisitor. + /// + /// A temporary directory used for storing image files. + public HtmlPreviewVisitor(string tempDirectory) + { + tempDir = tempDirectory; + } + + /// + /// The list of attachments that were in the MimeMessage. + /// + public IList Attachments + { + get { return attachments; } + } + + /// + /// The HTML string that can be set on the BrowserControl. + /// + public string HtmlBody + { + get { return Body ?? string.Empty; } + } + + protected override void VisitMultipartAlternative(MultipartAlternative alternative) + { + // walk the multipart/alternative children backwards from greatest level of faithfulness to the least faithful + for (int i = alternative.Count - 1; i >= 0 && Body == null; i--) + alternative[i].Accept(this); + } + + protected override void VisitMultipartRelated(MultipartRelated related) + { + var root = related.Root; + + // push this multipart/related onto our stack + stack.Add(related); + + // visit the root document + root.Accept(this); + + // pop this multipart/related off our stack + stack.RemoveAt(stack.Count - 1); + } + + // look up the image based on the img src url within our multipart/related stack + bool TryGetImage(string url, out MimePart image) + { + UriKind kind; + int index; + Uri uri; + + if (Uri.IsWellFormedUriString(url, UriKind.Absolute)) + kind = UriKind.Absolute; + else if (Uri.IsWellFormedUriString(url, UriKind.Relative)) + kind = UriKind.Relative; + else + kind = UriKind.RelativeOrAbsolute; + + try + { + uri = new Uri(url, kind); + } + catch + { + image = null; + return false; + } + + for (int i = stack.Count - 1; i >= 0; i--) + { + if ((index = stack[i].IndexOf(uri)) == -1) + continue; + + image = stack[i][index] as MimePart; + return image != null; + } + + image = null; + + return false; + } + + // Save the image to our temp directory and return a "file://" url suitable for + // the browser control to load. + // Note: if you'd rather embed the image data into the HTML, you can construct a + // "data:" url instead. + string SaveImage(MimePart image) + { + using (var memory = new MemoryStream()) + { + image.Content.DecodeTo(memory); + var buffer = memory.GetBuffer(); + var length = (int)memory.Length; + var base64 = Convert.ToBase64String(buffer, 0, length); + + return string.Format("data:{0};base64,{1}", image.ContentType.MimeType, base64); + } + + //string fileName = url + // .Replace(':', '_') + // .Replace('\\', '_') + // .Replace('/', '_'); + + //string path = Path.Combine(tempDir, fileName); + + //if (!File.Exists(path)) + //{ + // using (var output = File.Create(path)) + // image.Content.DecodeTo(output); + //} + + //return "file://" + path.Replace('\\', '/'); + } + + // Replaces urls that refer to images embedded within the message with + // "file://" urls that the browser control will actually be able to load. + void HtmlTagCallback(HtmlTagContext ctx, HtmlWriter htmlWriter) + { + if (ctx.TagId == HtmlTagId.Image && !ctx.IsEndTag && stack.Count > 0) + { + ctx.WriteTag(htmlWriter, false); + + // replace the src attribute with a file:// URL + foreach (var attribute in ctx.Attributes) + { + if (attribute.Id == HtmlAttributeId.Src) + { + MimePart image; + string url; + + if (!TryGetImage(attribute.Value, out image)) + { + htmlWriter.WriteAttribute(attribute); + continue; + } + + url = SaveImage(image); + + htmlWriter.WriteAttributeName(attribute.Name); + htmlWriter.WriteAttributeValue(url); + } + else + { + htmlWriter.WriteAttribute(attribute); + } + } + } + else if (ctx.TagId == HtmlTagId.Body && !ctx.IsEndTag) + { + ctx.WriteTag(htmlWriter, false); + + // add and/or replace oncontextmenu="return false;" + foreach (var attribute in ctx.Attributes) + { + if (attribute.Name.ToLowerInvariant() == "oncontextmenu") + continue; + + htmlWriter.WriteAttribute(attribute); + } + + htmlWriter.WriteAttribute("oncontextmenu", "return false;"); + } + else + { + // pass the tag through to the output + ctx.WriteTag(htmlWriter, true); + } + } + + protected override void VisitTextPart(TextPart entity) + { + TextConverter converter; + + if (Body != null) + { + // since we've already found the body, treat this as an attachment + attachments.Add(entity); + return; + } + + if (entity.IsHtml) + { + converter = new HtmlToHtml + { + HtmlTagCallback = HtmlTagCallback + }; + } + else if (entity.IsFlowed) + { + var flowed = new FlowedToHtml(); + string delsp; + + if (entity.ContentType.Parameters.TryGetValue("delsp", out delsp)) + flowed.DeleteSpace = delsp.ToLowerInvariant() == "yes"; + + converter = flowed; + } + else + { + converter = new TextToHtml(); + } + + Body = converter.Convert(entity.Text); + } + + protected override void VisitTnefPart(TnefPart entity) + { + // extract any attachments in the MS-TNEF part + attachments.AddRange(entity.ExtractAttachments()); + } + + protected override void VisitMessagePart(MessagePart entity) + { + // treat message/rfc822 parts as attachments + attachments.Add(entity); + } + + protected override void VisitMimePart(MimePart entity) + { + // realistically, if we've gotten this far, then we can treat this as an attachment + // even if the IsAttachment property is false. + attachments.Add(entity); + } + } +} diff --git a/Wino.Core/Mime/ImapMessageCreationPackage.cs b/Wino.Core/Mime/ImapMessageCreationPackage.cs new file mode 100644 index 00000000..ae482d98 --- /dev/null +++ b/Wino.Core/Mime/ImapMessageCreationPackage.cs @@ -0,0 +1,9 @@ +using MailKit; + +namespace Wino.Core.Mime +{ + /// + /// Encapsulates all required information to create a MimeMessage for IMAP synchronizer. + /// + public record ImapMessageCreationPackage(IMessageSummary MessageSummary, IMailFolder MailFolder); +} diff --git a/Wino.Core/Misc/RequestComparer.cs b/Wino.Core/Misc/RequestComparer.cs new file mode 100644 index 00000000..e884e5ea --- /dev/null +++ b/Wino.Core/Misc/RequestComparer.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Requests; + +namespace Wino.Core.Misc +{ + /// + /// This is incomplete. + /// + internal class RequestComparer : IEqualityComparer + { + public bool Equals(IRequestBase x, IRequestBase y) + { + if (x is MoveRequest sourceMoveRequest && y is MoveRequest targetMoveRequest) + { + return sourceMoveRequest.FromFolder.Id == targetMoveRequest.FromFolder.Id && sourceMoveRequest.ToFolder.Id == targetMoveRequest.ToFolder.Id; + } + else if (x is ChangeFlagRequest sourceFlagRequest && y is ChangeFlagRequest targetFlagRequest) + { + return sourceFlagRequest.IsFlagged == targetFlagRequest.IsFlagged; + } + else if (x is MarkReadRequest sourceMarkReadRequest && y is MarkReadRequest targetMarkReadRequest) + { + return sourceMarkReadRequest.Item.IsRead == targetMarkReadRequest.Item.IsRead; + } + else if (x is DeleteRequest sourceDeleteRequest && y is DeleteRequest targetDeleteRequest) + { + return sourceDeleteRequest.MailItem.AssignedFolder.Id == targetDeleteRequest.MailItem.AssignedFolder.Id; + } + + return true; + } + + public int GetHashCode(IRequestBase obj) => obj.Operation.GetHashCode(); + } +} diff --git a/Wino.Core/Requests/AlwaysMoveToRequest.cs b/Wino.Core/Requests/AlwaysMoveToRequest.cs new file mode 100644 index 00000000..db3ac3fe --- /dev/null +++ b/Wino.Core/Requests/AlwaysMoveToRequest.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.ComponentModel; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Requests; + +namespace Wino.Core.Requests +{ + public record AlwaysMoveToRequest(MailCopy Item, bool MoveToFocused) : RequestBase(Item, MailSynchronizerOperation.AlwaysMoveTo) + { + public override IBatchChangeRequest CreateBatch(IEnumerable matchingItems) + => new BatchAlwaysMoveToRequest(matchingItems, MoveToFocused); + + public override void ApplyUIChanges() + { + + } + + public override void RevertUIChanges() + { + + } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public record BatchAlwaysMoveToRequest(IEnumerable Items, bool MoveToFocused) : BatchRequestBase(Items, MailSynchronizerOperation.AlwaysMoveTo) + { + public override void ApplyUIChanges() + { + + } + + public override void RevertUIChanges() + { + + } + } +} diff --git a/Wino.Core/Requests/Bundles/RequestBundle.cs b/Wino.Core/Requests/Bundles/RequestBundle.cs new file mode 100644 index 00000000..bf8a7045 --- /dev/null +++ b/Wino.Core/Requests/Bundles/RequestBundle.cs @@ -0,0 +1,59 @@ +using System; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.Domain.Models.Requests +{ + + + /// + /// Bundle that encapsulates batch request and native request without a response. + /// + /// Http type for each integrator. eg. ClientServiceRequest for Gmail and RequestInformation for Microsoft Graph. + /// Native type to send via http. + /// Batch request that is generated by base synchronizer. + public record HttpRequestBundle(TRequest NativeRequest, IRequestBase Request) : IRequestBundle + { + public string BundleId { get; set; } = string.Empty; + + public override string ToString() + { + if (Request is IRequest singleRequest) + return $"Single {singleRequest.Operation}. No response."; + else if (Request is IBatchChangeRequest batchChangeRequest) + return $"Batch {batchChangeRequest.Operation} for {batchChangeRequest.Items.Count()} items. No response."; + else + return "Unknown http request bundle."; + } + } + + /// + /// Bundle that encapsulates batch request and native request with response. + /// + /// Http type for each integrator. eg. ClientServiceRequest for Gmail and RequestInformation for Microsoft Graph. + /// Native type to send via http. + /// Batch request that is generated by base synchronizer. + public record HttpRequestBundle(TRequest NativeRequest, IRequestBase Request) : HttpRequestBundle(NativeRequest, Request) + { + public async Task DeserializeBundleAsync(HttpResponseMessage httpResponse, CancellationToken cancellationToken = default) + { + var content = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + + return JsonConvert.DeserializeObject(content) ?? throw new InvalidOperationException("Invalid Http Response Deserialization"); + } + + public override string ToString() + { + if (Request is IRequest singleRequest) + return $"Single {singleRequest.Operation}. Expecting '{typeof(TResponse).FullName}' type."; + else if (Request is IBatchChangeRequest batchChangeRequest) + return $"Batch {batchChangeRequest.Operation} for {batchChangeRequest.Items.Count()} items. Expecting '{typeof(TResponse).FullName}' type."; + else + return "Unknown http request bundle."; + } + } +} diff --git a/Wino.Core/Requests/Bundles/TaskRequestBundle.cs b/Wino.Core/Requests/Bundles/TaskRequestBundle.cs new file mode 100644 index 00000000..32b0991e --- /dev/null +++ b/Wino.Core/Requests/Bundles/TaskRequestBundle.cs @@ -0,0 +1,24 @@ +using System; +using System.Threading.Tasks; +using MailKit.Net.Imap; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.Requests.Bundles +{ + //public abstract record TaskRequestBundleBase() + //{ + // public abstract Task ExecuteAsync(ImapClient executorImapClient); + //} + + //public record TaskRequestBundle(Func NativeRequest) : TaskRequestBundleBase + //{ + // public override async Task ExecuteAsync(ImapClient executorImapClient) => await NativeRequest(executorImapClient).ConfigureAwait(false); + //} + + public record ImapRequest(Func IntegratorTask, IRequestBase Request) { } + + public record ImapRequestBundle(ImapRequest NativeRequest, IRequestBase Request) : IRequestBundle + { + public string BundleId { get; set; } = Guid.NewGuid().ToString(); + } +} diff --git a/Wino.Core/Requests/ChangeFlagRequest.cs b/Wino.Core/Requests/ChangeFlagRequest.cs new file mode 100644 index 00000000..e7259c27 --- /dev/null +++ b/Wino.Core/Requests/ChangeFlagRequest.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using CommunityToolkit.Mvvm.Messaging; +using MoreLinq; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Requests; + +namespace Wino.Core.Requests +{ + public record ChangeFlagRequest(MailCopy Item, bool IsFlagged) : RequestBase(Item, MailSynchronizerOperation.ChangeFlag), + ICustomFolderSynchronizationRequest + { + public List SynchronizationFolderIds => [Item.FolderId]; + + public override IBatchChangeRequest CreateBatch(IEnumerable matchingItems) + => new BatchChangeFlagRequest(matchingItems, IsFlagged); + + public override void ApplyUIChanges() + { + Item.IsFlagged = IsFlagged; + + WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(Item)); + } + + public override void RevertUIChanges() + { + Item.IsFlagged = !IsFlagged; + + WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(Item)); + } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public record BatchChangeFlagRequest(IEnumerable Items, bool IsFlagged) : BatchRequestBase(Items, MailSynchronizerOperation.ChangeFlag) + { + public override void ApplyUIChanges() + { + Items.ForEach(item => + { + item.Item.IsFlagged = IsFlagged; + + WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(item.Item)); + }); + } + + public override void RevertUIChanges() + { + Items.ForEach(item => + { + item.Item.IsFlagged = !IsFlagged; + + WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(item.Item)); + }); + } + } +} diff --git a/Wino.Core/Requests/CreateDraftRequest.cs b/Wino.Core/Requests/CreateDraftRequest.cs new file mode 100644 index 00000000..9887ed4a --- /dev/null +++ b/Wino.Core/Requests/CreateDraftRequest.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using CommunityToolkit.Mvvm.Messaging; +using MoreLinq; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Requests; + +namespace Wino.Core.Requests +{ + public record CreateDraftRequest(DraftPreperationRequest DraftPreperationRequest) + : RequestBase(DraftPreperationRequest.CreatedLocalDraftCopy, MailSynchronizerOperation.CreateDraft), + ICustomFolderSynchronizationRequest + { + public List SynchronizationFolderIds => + [ + DraftPreperationRequest.CreatedLocalDraftCopy.AssignedFolder.Id + ]; + + public override IBatchChangeRequest CreateBatch(IEnumerable matchingItems) + => new BatchCreateDraftRequest(matchingItems, DraftPreperationRequest); + + public override void ApplyUIChanges() + { + // No need for it since Draft folder is automatically navigated and draft item is added + selected. + // We only need to revert changes in case of network fails to create the draft. + } + + public override void RevertUIChanges() + { + WeakReferenceMessenger.Default.Send(new MailRemovedMessage(Item)); + } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public record class BatchCreateDraftRequest(IEnumerable Items, DraftPreperationRequest DraftPreperationRequest) + : BatchRequestBase(Items, MailSynchronizerOperation.CreateDraft) + { + public override void ApplyUIChanges() + { + // No need for it since Draft folder is automatically navigated and draft item is added + selected. + // We only need to revert changes in case of network fails to create the draft. + } + + public override void RevertUIChanges() + { + Items.ForEach(item => WeakReferenceMessenger.Default.Send(new MailRemovedMessage(item.Item))); + } + } +} diff --git a/Wino.Core/Requests/DeleteRequest.cs b/Wino.Core/Requests/DeleteRequest.cs new file mode 100644 index 00000000..f8cf966d --- /dev/null +++ b/Wino.Core/Requests/DeleteRequest.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using CommunityToolkit.Mvvm.Messaging; +using MoreLinq; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Requests; + +namespace Wino.Core.Requests +{ + /// + /// Hard delete request. This request will delete the mail item from the server without moving it to the trash folder. + /// + /// Item to delete permanently. + public record DeleteRequest(MailCopy MailItem) : RequestBase(MailItem, MailSynchronizerOperation.Delete), + ICustomFolderSynchronizationRequest + { + public List SynchronizationFolderIds => [Item.FolderId]; + + public override IBatchChangeRequest CreateBatch(IEnumerable matchingItems) + => new BatchDeleteRequest(matchingItems); + + public override void ApplyUIChanges() + { + WeakReferenceMessenger.Default.Send(new MailRemovedMessage(Item)); + } + + public override void RevertUIChanges() + { + WeakReferenceMessenger.Default.Send(new MailAddedMessage(Item)); + } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public record class BatchDeleteRequest(IEnumerable Items) : BatchRequestBase(Items, MailSynchronizerOperation.Delete) + { + public override void ApplyUIChanges() + { + Items.ForEach(item => WeakReferenceMessenger.Default.Send(new MailRemovedMessage(item.Item))); + } + + public override void RevertUIChanges() + { + Items.ForEach(item => WeakReferenceMessenger.Default.Send(new MailAddedMessage(item.Item))); + } + } +} diff --git a/Wino.Core/Requests/MarkReadRequest.cs b/Wino.Core/Requests/MarkReadRequest.cs new file mode 100644 index 00000000..c4f138a2 --- /dev/null +++ b/Wino.Core/Requests/MarkReadRequest.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using CommunityToolkit.Mvvm.Messaging; +using MoreLinq; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Requests; + +namespace Wino.Core.Requests +{ + public record MarkReadRequest(MailCopy Item, bool IsRead) : RequestBase(Item, MailSynchronizerOperation.MarkRead), + ICustomFolderSynchronizationRequest + { + public List SynchronizationFolderIds => [Item.FolderId]; + + public override IBatchChangeRequest CreateBatch(IEnumerable matchingItems) + => new BatchMarkReadRequest(matchingItems, IsRead); + + public override void ApplyUIChanges() + { + Item.IsRead = IsRead; + + WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(Item)); + } + + public override void RevertUIChanges() + { + Item.IsRead = !IsRead; + + WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(Item)); + } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public record BatchMarkReadRequest(IEnumerable Items, bool IsRead) : BatchRequestBase(Items, MailSynchronizerOperation.MarkRead) + { + public override void ApplyUIChanges() + { + Items.ForEach(item => + { + item.Item.IsRead = IsRead; + + WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(item.Item)); + }); + } + + public override void RevertUIChanges() + { + Items.ForEach(item => + { + item.Item.IsRead = !IsRead; + + WeakReferenceMessenger.Default.Send(new MailUpdatedMessage(item.Item)); + }); + } + } +} diff --git a/Wino.Core/Requests/MoveRequest.cs b/Wino.Core/Requests/MoveRequest.cs new file mode 100644 index 00000000..bbae7f74 --- /dev/null +++ b/Wino.Core/Requests/MoveRequest.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using CommunityToolkit.Mvvm.Messaging; +using MoreLinq; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Requests; + +namespace Wino.Core.Requests +{ + public record MoveRequest(MailCopy Item, MailItemFolder FromFolder, MailItemFolder ToFolder) + : RequestBase(Item, MailSynchronizerOperation.Move), ICustomFolderSynchronizationRequest + { + public List SynchronizationFolderIds => new() { FromFolder.Id, ToFolder.Id }; + + public override IBatchChangeRequest CreateBatch(IEnumerable matchingItems) + => new BatchMoveRequest(matchingItems, FromFolder, ToFolder); + + public override void ApplyUIChanges() + { + WeakReferenceMessenger.Default.Send(new MailRemovedMessage(Item)); + } + + public override void RevertUIChanges() + { + WeakReferenceMessenger.Default.Send(new MailAddedMessage(Item)); + } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public record BatchMoveRequest(IEnumerable Items, MailItemFolder FromFolder, MailItemFolder ToFolder) : BatchRequestBase(Items, MailSynchronizerOperation.Move) + { + public override void ApplyUIChanges() + { + Items.ForEach(item => WeakReferenceMessenger.Default.Send(new MailRemovedMessage(item.Item))); + } + + public override void RevertUIChanges() + { + Items.ForEach(item => WeakReferenceMessenger.Default.Send(new MailAddedMessage(item.Item))); + } + } +} diff --git a/Wino.Core/Requests/MoveToFocusedRequest.cs b/Wino.Core/Requests/MoveToFocusedRequest.cs new file mode 100644 index 00000000..42edefe0 --- /dev/null +++ b/Wino.Core/Requests/MoveToFocusedRequest.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.ComponentModel; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Requests; + +namespace Wino.Core.Requests +{ + public record MoveToFocusedRequest(MailCopy Item, bool MoveToFocused) : RequestBase(Item, MailSynchronizerOperation.Move) + { + public override IBatchChangeRequest CreateBatch(IEnumerable matchingItems) + => new BatchMoveToFocusedRequest(matchingItems, MoveToFocused); + + public override void ApplyUIChanges() { } + + public override void RevertUIChanges() { } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public record BatchMoveToFocusedRequest(IEnumerable Items, bool MoveToFocused) : BatchRequestBase(Items, MailSynchronizerOperation.Move) + { + public override void ApplyUIChanges() { } + + public override void RevertUIChanges() { } + } +} diff --git a/Wino.Core/Requests/RenameFolderRequest.cs b/Wino.Core/Requests/RenameFolderRequest.cs new file mode 100644 index 00000000..6b431101 --- /dev/null +++ b/Wino.Core/Requests/RenameFolderRequest.cs @@ -0,0 +1,19 @@ +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Requests; + +namespace Wino.Core.Requests +{ + public record RenameFolderRequest(MailItemFolder Folder) : FolderRequestBase(Folder, MailSynchronizerOperation.RenameFolder) + { + public override void ApplyUIChanges() + { + + } + + public override void RevertUIChanges() + { + + } + } +} diff --git a/Wino.Core/Requests/SendDraftRequest.cs b/Wino.Core/Requests/SendDraftRequest.cs new file mode 100644 index 00000000..4f3ce10e --- /dev/null +++ b/Wino.Core/Requests/SendDraftRequest.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.ComponentModel; +using CommunityToolkit.Mvvm.Messaging; +using MoreLinq; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Requests; + +namespace Wino.Core.Requests +{ + public record SendDraftRequest(SendDraftPreparationRequest Request) : RequestBase(Request.MailItem, MailSynchronizerOperation.Send) + { + public override IBatchChangeRequest CreateBatch(IEnumerable matchingItems) + => new BatchSendDraftRequestRequest(matchingItems, Request); + + public override void ApplyUIChanges() + { + WeakReferenceMessenger.Default.Send(new MailRemovedMessage(Item)); + } + + public override void RevertUIChanges() + { + WeakReferenceMessenger.Default.Send(new MailAddedMessage(Item)); + } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public record BatchSendDraftRequestRequest(IEnumerable Items, SendDraftPreparationRequest Request) : BatchRequestBase(Items, MailSynchronizerOperation.Send) + { + public override void ApplyUIChanges() + { + Items.ForEach(item => WeakReferenceMessenger.Default.Send(new MailRemovedMessage(item.Item))); + } + + public override void RevertUIChanges() + { + Items.ForEach(item => WeakReferenceMessenger.Default.Send(new MailAddedMessage(item.Item))); + } + } +} diff --git a/Wino.Core/Requests/UIMessages.cs b/Wino.Core/Requests/UIMessages.cs new file mode 100644 index 00000000..f47c7644 --- /dev/null +++ b/Wino.Core/Requests/UIMessages.cs @@ -0,0 +1,25 @@ +using System; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Models.Requests; + +namespace Wino.Core.Requests +{ + public record MailAddedMessage(MailCopy AddedMail) : IUIMessage; + public record MailRemovedMessage(MailCopy RemovedMail) : IUIMessage; + public record MailUpdatedMessage(MailCopy UpdatedMail) : IUIMessage; + public record MailDownloadedMessage(MailCopy DownloadedMail) : IUIMessage; + + public record FolderAddedMessage(MailItemFolder AddedFolder, MailAccount Account) : IUIMessage; + public record FolderRemovedMessage(MailItemFolder RemovedFolder, MailAccount Account) : IUIMessage; + public record FolderUpdatedMessage(MailItemFolder UpdatedFolder, MailAccount Account) : IUIMessage; + + public record AccountCreatedMessage(MailAccount Account) : IUIMessage; + public record AccountRemovedMessage(MailAccount Account) : IUIMessage; + public record AccountUpdatedMessage(MailAccount Account) : IUIMessage; + + public record DraftCreated(MailCopy DraftMail, MailAccount Account) : IUIMessage; + public record DraftFailed(MailCopy DraftMail, MailAccount Account) : IUIMessage; + public record DraftMapped(string LocalDraftCopyId, string RemoteDraftCopyId) : IUIMessage; + + public record MergedInboxRenamed(Guid MergedInboxId, string NewName) : IUIMessage; +} diff --git a/Wino.Core/Services/AccountService.cs b/Wino.Core/Services/AccountService.cs new file mode 100644 index 00000000..850139f2 --- /dev/null +++ b/Wino.Core/Services/AccountService.cs @@ -0,0 +1,403 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using CommunityToolkit.Diagnostics; +using CommunityToolkit.Mvvm.Messaging; +using Serilog; +using SqlKata; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Extensions; +using Wino.Core.Messages.Accounts; +using Wino.Core.Requests; + +namespace Wino.Core.Services +{ + public class AccountService : BaseDatabaseService, IAccountService + { + public IAuthenticator ExternalAuthenticationAuthenticator { get; set; } + + private readonly IAuthenticationProvider _authenticationProvider; + private readonly ISignatureService _signatureService; + private readonly IPreferencesService _preferencesService; + + private readonly ILogger _logger = Log.ForContext(); + + public AccountService(IDatabaseService databaseService, + IAuthenticationProvider authenticationProvider, + ISignatureService signatureService, + IPreferencesService preferencesService) : base(databaseService) + { + _authenticationProvider = authenticationProvider; + _signatureService = signatureService; + _preferencesService = preferencesService; + } + + + public async Task ClearAccountAttentionAsync(Guid accountId) + { + var account = await GetAccountAsync(accountId); + + Guard.IsNotNull(account); + + account.AttentionReason = AccountAttentionReason.None; + + await UpdateAccountAsync(account); + } + + public async Task UpdateMergedInboxAsync(Guid mergedInboxId, IEnumerable linkedAccountIds) + { + // First, remove all accounts from merged inbox. + await Connection.ExecuteAsync("UPDATE MailAccount SET MergedInboxId = NULL WHERE MergedInboxId = ?", mergedInboxId); + + // Then, add new accounts to merged inbox. + var query = new Query("MailAccount") + .WhereIn("Id", linkedAccountIds) + .AsUpdate(new + { + MergedInboxId = mergedInboxId + }); + + await Connection.ExecuteAsync(query.GetRawQuery()); + + WeakReferenceMessenger.Default.Send(new AccountsMenuRefreshRequested()); + } + + public async Task UnlinkMergedInboxAsync(Guid mergedInboxId) + { + var mergedInbox = await Connection.Table().FirstOrDefaultAsync(a => a.Id == mergedInboxId).ConfigureAwait(false); + + if (mergedInbox == null) + { + _logger.Warning("Could not find merged inbox with id {MergedInboxId}", mergedInboxId); + + return; + } + + var query = new Query("MailAccount") + .Where("MergedInboxId", mergedInboxId) + .AsUpdate(new + { + MergedInboxId = (Guid?)null + }); + + await Connection.ExecuteAsync(query.GetRawQuery()).ConfigureAwait(false); + await Connection.DeleteAsync(mergedInbox).ConfigureAwait(false); + + // Change the startup entity id if it was the merged inbox. + // Take the first account as startup account. + + if (_preferencesService.StartupEntityId == mergedInboxId) + { + var firstAccount = await Connection.Table().FirstOrDefaultAsync(); + + if (firstAccount != null) + { + _preferencesService.StartupEntityId = firstAccount.Id; + } + else + { + _preferencesService.StartupEntityId = null; + } + } + + WeakReferenceMessenger.Default.Send(new AccountsMenuRefreshRequested()); + } + + public async Task CreateMergeAccountsAsync(MergedInbox mergedInbox, IEnumerable accountsToMerge) + { + if (mergedInbox == null) return; + + // 0. Give the merged inbox a new Guid. + mergedInbox.Id = Guid.NewGuid(); + + var accountFolderDictionary = new Dictionary>(); + + // 1. Make all folders in the accounts unsticky. We will stick them based on common special folder types. + foreach (var account in accountsToMerge) + { + var accountFolderList = new List(); + + var folders = await Connection.Table().Where(a => a.MailAccountId == account.Id).ToListAsync(); + + foreach (var folder in folders) + { + accountFolderList.Add(folder); + folder.IsSticky = false; + + await Connection.UpdateAsync(folder); + } + + accountFolderDictionary.Add(account, accountFolderList); + } + + // 2. Find the common special folders and stick them. + // Only following types will be considered as common special folder. + SpecialFolderType[] commonSpecialTypes = + [ + SpecialFolderType.Inbox, + SpecialFolderType.Sent, + SpecialFolderType.Draft, + SpecialFolderType.Archive, + SpecialFolderType.Junk, + SpecialFolderType.Deleted + ]; + + foreach (var type in commonSpecialTypes) + { + var isCommonType = accountFolderDictionary + .Select(a => a.Value) + .Where(a => a.Any(a => a.SpecialFolderType == type)) + .Count() == accountsToMerge.Count(); + + if (isCommonType) + { + foreach (var account in accountsToMerge) + { + var folder = accountFolderDictionary[account].FirstOrDefault(a => a.SpecialFolderType == type); + + if (folder != null) + { + folder.IsSticky = true; + + await Connection.UpdateAsync(folder); + } + } + } + } + + // 3. Insert merged inbox and assign accounts. + await Connection.InsertAsync(mergedInbox); + + foreach (var account in accountsToMerge) + { + account.MergedInboxId = mergedInbox.Id; + + await Connection.UpdateAsync(account); + } + + WeakReferenceMessenger.Default.Send(new AccountsMenuRefreshRequested()); + } + + public async Task RenameMergedAccountAsync(Guid mergedInboxId, string newName) + { + var query = new Query("MergedInbox") + .Where("Id", mergedInboxId) + .AsUpdate(new + { + Name = newName + }); + + await Connection.ExecuteAsync(query.GetRawQuery()); + + ReportUIChange(new MergedInboxRenamed(mergedInboxId, newName)); + } + + public async Task FixTokenIssuesAsync(Guid accountId) + { + var account = await Connection.Table().FirstOrDefaultAsync(a => a.Id == accountId); + + if (account == null) return; + + var authenticator = _authenticationProvider.GetAuthenticator(account.ProviderType); + + // This will re-generate token. + var token = await authenticator.GenerateTokenAsync(account, true); + + Guard.IsNotNull(token); + } + + private Task GetAccountPreferencesAsync(Guid accountId) + => Connection.Table().FirstOrDefaultAsync(a => a.AccountId == accountId); + + public async Task> GetAccountsAsync() + { + var accounts = await Connection.Table().ToListAsync(); + + foreach (var account in accounts) + { + // Load IMAP server configuration. + if (account.ProviderType == MailProviderType.IMAP4) + account.ServerInformation = await GetAccountCustomServerInformationAsync(account.Id); + + // Load MergedInbox information. + if (account.MergedInboxId != null) + account.MergedInbox = await GetMergedInboxInformationAsync(account.MergedInboxId.Value); + + account.Preferences = await GetAccountPreferencesAsync(account.Id); + } + + return accounts; + } + + private Task GetMergedInboxInformationAsync(Guid mergedInboxId) + => Connection.Table().FirstOrDefaultAsync(a => a.Id == mergedInboxId); + + public async Task DeleteAccountAsync(MailAccount account) + { + // TODO: Delete mime messages and attachments. + + await Connection.ExecuteAsync("DELETE FROM MailCopy WHERE Id IN(SELECT Id FROM MailCopy WHERE FolderId IN (SELECT Id from MailItemFolder WHERE MailAccountId == ?))", account.Id); + + await Connection.Table().Where(a => a.AccountId == account.Id).DeleteAsync(); + await Connection.Table().DeleteAsync(a => a.MailAccountId == account.Id); + + if (account.SignatureId != null) + await Connection.Table().DeleteAsync(a => a.Id == account.SignatureId); + + // Account belongs to a merged inbox. + // In case of there'll be a single account in the merged inbox, remove the merged inbox as well. + + if (account.MergedInboxId != null) + { + var mergedInboxAccountCount = await Connection.Table().Where(a => a.MergedInboxId == account.MergedInboxId.Value).CountAsync(); + + // There will be only one account in the merged inbox. Remove the link for the other account as well. + if (mergedInboxAccountCount == 2) + { + var query = new Query("MailAccount") + .Where("MergedInboxId", account.MergedInboxId.Value) + .AsUpdate(new + { + MergedInboxId = (Guid?)null + }); + + await Connection.ExecuteAsync(query.GetRawQuery()).ConfigureAwait(false); + } + } + + if (account.ProviderType == MailProviderType.IMAP4) + await Connection.Table().DeleteAsync(a => a.AccountId == account.Id); + + if (account.Preferences != null) + await Connection.DeleteAsync(account.Preferences); + + await Connection.DeleteAsync(account); + + // Clear out or set up a new startup entity id. + // Next account after the deleted one will be the startup account. + + if (_preferencesService.StartupEntityId == account.Id || _preferencesService.StartupEntityId == account.MergedInboxId) + { + var firstNonStartupAccount = await Connection.Table().FirstOrDefaultAsync(a => a.Id != account.Id); + + if (firstNonStartupAccount != null) + { + _preferencesService.StartupEntityId = firstNonStartupAccount.Id; + } + else + { + _preferencesService.StartupEntityId = null; + } + } + + ReportUIChange(new AccountRemovedMessage(account)); + } + + public async Task GetAccountAsync(Guid accountId) + { + var account = await Connection.Table().FirstOrDefaultAsync(a => a.Id == accountId); + + if (account?.ProviderType == MailProviderType.IMAP4) + account.ServerInformation = await GetAccountCustomServerInformationAsync(account.Id); + + account.Preferences = await GetAccountPreferencesAsync(account.Id); + + return account; + } + + public Task GetAccountCustomServerInformationAsync(Guid accountId) + => Connection.Table().FirstOrDefaultAsync(a => a.AccountId == accountId); + + public async Task UpdateAccountAsync(MailAccount account) + { + if (account.Preferences == null) + { + Debugger.Break(); + } + + await Connection.UpdateAsync(account.Preferences); + await Connection.UpdateAsync(account); + + ReportUIChange(new AccountUpdatedMessage(account)); + } + + public async Task CreateAccountAsync(MailAccount account, TokenInformation tokenInformation, CustomServerInformation customServerInformation) + { + Guard.IsNotNull(account); + + var accountCount = await Connection.Table().CountAsync(); + + // If there are no accounts before this one, set it as startup account. + if (accountCount == 0) + { + _preferencesService.StartupEntityId = account.Id; + } + + await Connection.InsertAsync(account); + + var preferences = new MailAccountPreferences() + { + Id = Guid.NewGuid(), + AccountId = account.Id, + IsNotificationsEnabled = true, + ShouldAppendMessagesToSentFolder = false + }; + + account.Preferences = preferences; + + // Outlook & Office 365 supports Focused inbox. Enabled by default. + bool isMicrosoftProvider = account.ProviderType == MailProviderType.Outlook || account.ProviderType == MailProviderType.Office365; + + if (isMicrosoftProvider) + account.Preferences.IsFocusedInboxEnabled = true; + + await Connection.InsertAsync(preferences); + + // Create default signature. + var defaultSignature = await _signatureService.CreateDefaultSignatureAsync(account.Id); + + account.SignatureId = defaultSignature.Id; + + if (customServerInformation != null) + await Connection.InsertAsync(customServerInformation); + + if (tokenInformation != null) + await Connection.InsertAsync(tokenInformation); + } + + public async Task UpdateSynchronizationIdentifierAsync(Guid accountId, string newIdentifier) + { + var account = await GetAccountAsync(accountId); + + if (account == null) + { + _logger.Error("Could not find account with id {AccountId}", accountId); + return string.Empty; + } + + var currentIdentifier = account.SynchronizationDeltaIdentifier; + + bool shouldUpdateIdentifier = account.ProviderType == MailProviderType.Gmail ? + ((string.IsNullOrEmpty(currentIdentifier) ? true : !string.IsNullOrEmpty(currentIdentifier) + && ulong.TryParse(currentIdentifier, out ulong currentIdentifierValue) + && ulong.TryParse(newIdentifier, out ulong newIdentifierValue) + && newIdentifierValue > currentIdentifierValue)) : true; + + if (shouldUpdateIdentifier) + { + _logger.Debug("Updating synchronization identifier for {Name}. From: {SynchronizationDeltaIdentifier} To: {NewIdentifier}", account.Name, account.SynchronizationDeltaIdentifier, newIdentifier); + account.SynchronizationDeltaIdentifier = newIdentifier; + + await UpdateAccountAsync(account); + } + + return account.SynchronizationDeltaIdentifier; + } + + + } +} diff --git a/Wino.Core/Services/AuthenticationProvider.cs b/Wino.Core/Services/AuthenticationProvider.cs new file mode 100644 index 00000000..20c78bcc --- /dev/null +++ b/Wino.Core/Services/AuthenticationProvider.cs @@ -0,0 +1,34 @@ +using System; +using Wino.Core.Authenticators; +using Wino.Core.Domain; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using IAuthenticationProvider = Wino.Core.Domain.Interfaces.IAuthenticationProvider; + +namespace Wino.Core.Services +{ + public class AuthenticationProvider : IAuthenticationProvider + { + private readonly INativeAppService _nativeAppService; + private readonly ITokenService _tokenService; + + public AuthenticationProvider(INativeAppService nativeAppService, ITokenService tokenService) + { + _nativeAppService = nativeAppService; + _tokenService = tokenService; + } + + public IAuthenticator GetAuthenticator(MailProviderType providerType) + { + return providerType switch + { + MailProviderType.Outlook => new OutlookAuthenticator(_tokenService, _nativeAppService), + MailProviderType.Office365 => new Office365Authenticator(_tokenService, _nativeAppService), + MailProviderType.Gmail => new GmailAuthenticator(_tokenService, _nativeAppService), + MailProviderType.Yahoo => new YahooAuthenticator(_tokenService), + MailProviderType.IMAP4 => new CustomAuthenticator(_tokenService), + _ => throw new ArgumentException(Translator.Exception_UnsupportedProvider), + }; + } + } +} diff --git a/Wino.Core/Services/AutoDiscoveryService.cs b/Wino.Core/Services/AutoDiscoveryService.cs new file mode 100644 index 00000000..c788b130 --- /dev/null +++ b/Wino.Core/Services/AutoDiscoveryService.cs @@ -0,0 +1,56 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Serilog; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.AutoDiscovery; + +namespace Wino.Core.Services +{ + /// + /// We have 2 methods to do auto discovery. + /// 1. Use https://emailsettings.firetrust.com/settings?q={address} API + /// 2. TODO: Thunderbird auto discovery file. + /// + public class AutoDiscoveryService : IAutoDiscoveryService + { + private const string FiretrustURL = " https://emailsettings.firetrust.com/settings?q="; + + // TODO: Try Thunderbird Auto Discovery as second approach. + + public Task GetAutoDiscoverySettings(AutoDiscoveryMinimalSettings autoDiscoveryMinimalSettings) + => GetSettingsFromFiretrustAsync(autoDiscoveryMinimalSettings.Email); + + private async Task GetSettingsFromFiretrustAsync(string mailAddress) + { + using var client = new HttpClient(); + var response = await client.GetAsync($"{FiretrustURL}{mailAddress}"); + + if (response.IsSuccessStatusCode) + return await DeserializeFiretrustResponse(response); + else + { + Log.Warning($"Firetrust AutoDiscovery failed. ({response.StatusCode})"); + + return null; + } + } + + private async Task DeserializeFiretrustResponse(HttpResponseMessage response) + { + try + { + var content = await response.Content.ReadAsStringAsync(); + + return JsonConvert.DeserializeObject(content); + } + catch (Exception ex) + { + Log.Error(ex, "Failed to deserialize Firetrust response."); + } + + return null; + } + } +} diff --git a/Wino.Core/Services/BaseDatabaseService.cs b/Wino.Core/Services/BaseDatabaseService.cs new file mode 100644 index 00000000..cd0136e2 --- /dev/null +++ b/Wino.Core/Services/BaseDatabaseService.cs @@ -0,0 +1,22 @@ +using CommunityToolkit.Mvvm.Messaging; +using SQLite; +using Wino.Core.Domain.Models.Requests; + +namespace Wino.Core.Services +{ + public class BaseDatabaseService + { + protected IMessenger Messenger => WeakReferenceMessenger.Default; + protected SQLiteAsyncConnection Connection => _databaseService.Connection; + + private readonly IDatabaseService _databaseService; + + public BaseDatabaseService(IDatabaseService databaseService) + { + _databaseService = databaseService; + } + + public void ReportUIChange(TMessage message) where TMessage : class, IUIMessage + => Messenger.Send(message); + } +} diff --git a/Wino.Core/Services/ContactService.cs b/Wino.Core/Services/ContactService.cs new file mode 100644 index 00000000..889c141f --- /dev/null +++ b/Wino.Core/Services/ContactService.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using MimeKit; +using SqlKata; +using Wino.Core.Domain.Entities; +using Wino.Core.Extensions; + +namespace Wino.Core.Services +{ + public interface IContactService + { + Task> GetAddressInformationAsync(string queryText); + Task GetAddressInformationByAddressAsync(string address); + Task SaveAddressInformationAsync(MimeMessage message); + } + + public class ContactService : BaseDatabaseService, IContactService + { + public ContactService(IDatabaseService databaseService) : base(databaseService) { } + + public Task> GetAddressInformationAsync(string queryText) + { + if (queryText == null || queryText.Length < 2) + return Task.FromResult>(null); + + var query = new Query(nameof(AddressInformation)); + query.WhereContains("Address", queryText); + query.OrWhereContains("Name", queryText); + + var rawLikeQuery = query.GetRawQuery(); + + return Connection.QueryAsync(rawLikeQuery); + } + + public async Task GetAddressInformationByAddressAsync(string address) + { + return await Connection.Table().Where(a => a.Address == address).FirstOrDefaultAsync() + ?? new AddressInformation() { Name = address, Address = address }; + } + + public async Task SaveAddressInformationAsync(MimeMessage message) + { + var recipients = message + .GetRecipients(true) + .Where(a => !string.IsNullOrEmpty(a.Name) && !string.IsNullOrEmpty(a.Address)); + + var addressInformations = recipients.Select(a => new AddressInformation() { Name = a.Name, Address = a.Address }); + + foreach (var info in addressInformations) + await Connection.InsertOrReplaceAsync(info).ConfigureAwait(false); + } + } +} diff --git a/Wino.Core/Services/ContextMenuItemService.cs b/Wino.Core/Services/ContextMenuItemService.cs new file mode 100644 index 00000000..3c1040fe --- /dev/null +++ b/Wino.Core/Services/ContextMenuItemService.cs @@ -0,0 +1,182 @@ +using System.Collections.Generic; +using System.Linq; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Folders; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Menus; + +namespace Wino.Core.Services +{ + public class ContextMenuItemService : IContextMenuItemService + { + public virtual IEnumerable GetFolderContextMenuActions(IBaseFolderMenuItem folderInformation) + { + var list = new List(); + + if (folderInformation.IsSticky) + list.Add(FolderOperationMenuItem.Create(FolderOperation.Unpin)); + else + list.Add(FolderOperationMenuItem.Create(FolderOperation.Pin)); + + list.Add(FolderOperationMenuItem.Create(FolderOperation.Seperator)); + + // Following 4 items are disabled for system folders. + + list.Add(FolderOperationMenuItem.Create(FolderOperation.Rename, !folderInformation.IsSystemFolder)); + list.Add(FolderOperationMenuItem.Create(FolderOperation.Delete, !folderInformation.IsSystemFolder)); + list.Add(FolderOperationMenuItem.Create(FolderOperation.CreateSubFolder, !folderInformation.IsSystemFolder)); + + list.Add(FolderOperationMenuItem.Create(FolderOperation.Seperator)); + + list.Add(FolderOperationMenuItem.Create(FolderOperation.Empty)); + + list.Add(FolderOperationMenuItem.Create(FolderOperation.MarkAllAsRead)); + + return list; + } + public virtual IEnumerable GetMailItemContextMenuActions(IEnumerable selectedMailItems) + { + if (selectedMailItems == null) + return default; + + var operationList = new List(); + + // Disable archive button for Archive folder itself. + + bool isArchiveFolder = selectedMailItems.All(a => a.AssignedFolder.SpecialFolderType == SpecialFolderType.Archive); + bool isDraftOrSent = selectedMailItems.All(a => a.AssignedFolder.SpecialFolderType == SpecialFolderType.Draft || a.AssignedFolder.SpecialFolderType == SpecialFolderType.Sent); + bool isJunkFolder = selectedMailItems.All(a => a.AssignedFolder.SpecialFolderType == SpecialFolderType.Junk); + + bool isSingleItem = selectedMailItems.Count() == 1; + + IMailItem singleItem = selectedMailItems.FirstOrDefault(); + + // Archive button. + + if (isArchiveFolder) + operationList.Add(MailOperationMenuItem.Create(MailOperation.UnArchive)); + else + operationList.Add(MailOperationMenuItem.Create(MailOperation.Archive)); + + // Delete button. + operationList.Add(MailOperationMenuItem.Create(MailOperation.SoftDelete)); + + // Move button. + operationList.Add(MailOperationMenuItem.Create(MailOperation.Move, !isDraftOrSent)); + + // Independent flag, read etc. + if (isSingleItem) + { + if (singleItem.IsFlagged) + operationList.Add(MailOperationMenuItem.Create(MailOperation.ClearFlag)); + else + operationList.Add(MailOperationMenuItem.Create(MailOperation.SetFlag)); + + if (singleItem.IsRead) + operationList.Add(MailOperationMenuItem.Create(MailOperation.MarkAsUnread)); + else + operationList.Add(MailOperationMenuItem.Create(MailOperation.MarkAsRead)); + } + else + { + bool isAllFlagged = selectedMailItems.All(a => a.IsFlagged); + bool isAllRead = selectedMailItems.All(a => a.IsRead); + bool isAllUnread = selectedMailItems.All(a => !a.IsRead); + + if (isAllRead) + operationList.Add(MailOperationMenuItem.Create(MailOperation.MarkAsUnread)); + else + { + if (!isAllUnread) + operationList.Add(MailOperationMenuItem.Create(MailOperation.MarkAsUnread)); + + operationList.Add(MailOperationMenuItem.Create(MailOperation.MarkAsRead)); + } + + if (isAllFlagged) + operationList.Add(MailOperationMenuItem.Create(MailOperation.ClearFlag)); + else + { + operationList.Add(MailOperationMenuItem.Create(MailOperation.ClearFlag)); + operationList.Add(MailOperationMenuItem.Create(MailOperation.SetFlag)); + } + } + + // Ignore + if (!isDraftOrSent) + operationList.Add(MailOperationMenuItem.Create(MailOperation.Ignore)); + + // Seperator + operationList.Add(MailOperationMenuItem.Create(MailOperation.Seperator)); + + // Junk folder + if (isJunkFolder) + operationList.Add(MailOperationMenuItem.Create(MailOperation.MarkAsNotJunk)); + else if (!isDraftOrSent) + operationList.Add(MailOperationMenuItem.Create(MailOperation.MoveToJunk)); + + // TODO: Focus folder support. + + // Remove the separator if it's the last item remaining. + // It's creating unpleasent UI glitch. + + if (operationList.LastOrDefault()?.Operation == MailOperation.Seperator) + operationList.RemoveAt(operationList.Count - 1); + + return operationList; + } + public virtual IEnumerable GetMailItemRenderMenuActions(IMailItem mailItem, bool isDarkEditor) + { + var actionList = new List(); + + bool isArchiveFolder = mailItem.AssignedFolder.SpecialFolderType == SpecialFolderType.Archive; + + // Add light/dark editor theme switch. + if (isDarkEditor) + actionList.Add(MailOperationMenuItem.Create(MailOperation.LightEditor)); + else + actionList.Add(MailOperationMenuItem.Create(MailOperation.DarkEditor)); + + actionList.Add(MailOperationMenuItem.Create(MailOperation.Seperator)); + + // You can't do these to draft items. + if (!mailItem.IsDraft) + { + // Reply + actionList.Add(MailOperationMenuItem.Create(MailOperation.Reply)); + + // Reply All + actionList.Add(MailOperationMenuItem.Create(MailOperation.ReplyAll)); + + // Forward + actionList.Add(MailOperationMenuItem.Create(MailOperation.Forward)); + } + + // Archive - Unarchive + if (isArchiveFolder) + actionList.Add(MailOperationMenuItem.Create(MailOperation.UnArchive)); + else + actionList.Add(MailOperationMenuItem.Create(MailOperation.Archive)); + + // Delete + actionList.Add(MailOperationMenuItem.Create(MailOperation.SoftDelete)); + + // Flag - Clear Flag + if (mailItem.IsFlagged) + actionList.Add(MailOperationMenuItem.Create(MailOperation.ClearFlag)); + else + actionList.Add(MailOperationMenuItem.Create(MailOperation.SetFlag)); + + // Secondary items. + + // Read - Unread + if (mailItem.IsRead) + actionList.Add(MailOperationMenuItem.Create(MailOperation.MarkAsUnread, true, false)); + else + actionList.Add(MailOperationMenuItem.Create(MailOperation.MarkAsRead, true, false)); + + return actionList; + } + } +} diff --git a/Wino.Core/Services/DatabaseService.cs b/Wino.Core/Services/DatabaseService.cs new file mode 100644 index 00000000..759df13d --- /dev/null +++ b/Wino.Core/Services/DatabaseService.cs @@ -0,0 +1,69 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using SQLite; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.Services +{ + public interface IDatabaseService : IInitializeAsync + { + SQLiteAsyncConnection Connection { get; } + } + + public class DatabaseService : IDatabaseService + { + private string DatabaseName => "Wino.db"; + + private bool _isInitialized = false; + private readonly IAppInitializerService _appInitializerService; + + public SQLiteAsyncConnection Connection { get; private set; } + + public DatabaseService(IAppInitializerService appInitializerService) + { + _appInitializerService = appInitializerService; + } + + public async Task InitializeAsync() + { + if (_isInitialized) + return; + + var applicationData = _appInitializerService.GetApplicationDataFolder(); + var databaseFileName = Path.Combine(applicationData, DatabaseName); + + Connection = new SQLiteAsyncConnection(databaseFileName) + { + // Enable for debugging sqlite. + Trace = true, + Tracer = new Action((t) => + { + // Debug.WriteLine(t); + // Log.Debug(t); + }) + }; + + + await CreateTablesAsync(); + + _isInitialized = true; + } + + private async Task CreateTablesAsync() + { + await Connection.CreateTablesAsync(CreateFlags.None, + typeof(MailCopy), + typeof(MailItemFolder), + typeof(MailAccount), + typeof(TokenInformation), + typeof(AddressInformation), + typeof(CustomServerInformation), + typeof(AccountSignature), + typeof(MergedInbox), + typeof(MailAccountPreferences) + ); + } + } +} diff --git a/Wino.Core/Services/FolderService.cs b/Wino.Core/Services/FolderService.cs new file mode 100644 index 00000000..07449f1b --- /dev/null +++ b/Wino.Core/Services/FolderService.cs @@ -0,0 +1,592 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using MoreLinq; +using Serilog; +using SqlKata; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Folders; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Synchronization; +using Wino.Core.Extensions; +using Wino.Core.Requests; + +namespace Wino.Core.Services +{ + public class FolderService : BaseDatabaseService, IFolderService + { + private readonly IAccountService _accountService; + private readonly IMimeFileService _mimeFileService; + private readonly ILogger _logger = Log.ForContext(); + + private readonly SpecialFolderType[] gmailCategoryFolderTypes = + [ + SpecialFolderType.Promotions, + SpecialFolderType.Social, + SpecialFolderType.Updates, + SpecialFolderType.Forums, + SpecialFolderType.Personal + ]; + + public FolderService(IDatabaseService databaseService, + IAccountService accountService, + IMimeFileService mimeFileService) : base(databaseService) + { + _accountService = accountService; + _mimeFileService = mimeFileService; + } + + public async Task ChangeStickyStatusAsync(Guid folderId, bool isSticky) + => await Connection.ExecuteAsync("UPDATE MailItemFolder SET IsSticky = ? WHERE Id = ?", isSticky, folderId); + + public async Task GetFolderNotificationBadgeAsync(Guid folderId) + { + var folder = await GetFolderAsync(folderId); + + if (folder == null || !folder.ShowUnreadCount) return default; + + var account = await _accountService.GetAccountAsync(folder.MailAccountId); + + if (account == null) return default; + + var query = new Query("MailCopy") + .Where("FolderId", folderId) + .SelectRaw("count (DISTINCT Id)"); + + // If focused inbox is enabled, we need to check if this is the inbox folder. + if (account.Preferences.IsFocusedInboxEnabled.GetValueOrDefault() && folder.SpecialFolderType == SpecialFolderType.Inbox) + { + query.Where("IsFocused", 1); + } + + // Draft and Junk folders are not counted as unread. They must return the item count instead. + + if (folder.SpecialFolderType != SpecialFolderType.Draft || folder.SpecialFolderType != SpecialFolderType.Junk) + { + query.Where("IsRead", 0); + } + + return await Connection.ExecuteScalarAsync(query.GetRawQuery()); + } + + public async Task GetFolderStructureForAccountAsync(Guid accountId, bool includeHiddenFolders) + { + var account = await _accountService.GetAccountAsync(accountId); + + if (account == null) + throw new ArgumentException(nameof(account)); + + var accountTree = new AccountFolderTree(account); + + // Account folders. + var folderQuery = Connection.Table().Where(a => a.MailAccountId == accountId); + + if (!includeHiddenFolders) + folderQuery = folderQuery.Where(a => !a.IsHidden); + + // Load child folders for each folder. + var allFolders = await folderQuery.OrderBy(a => a.SpecialFolderType).ToListAsync(); + + if (allFolders.Any()) + { + // Get sticky folders. Category type is always sticky. + // Sticky folders don't have tree structure. So they can be added to the main tree. + var stickyFolders = allFolders.Where(a => a.IsSticky && a.SpecialFolderType != SpecialFolderType.Category); + + foreach (var stickyFolder in stickyFolders) + { + var childStructure = await GetChildFolderItemsRecursiveAsync(stickyFolder.Id, accountId); + + accountTree.Folders.Add(childStructure); + } + + // Check whether we need special 'Categories' kind of folder. + var categoryExists = allFolders.Any(a => a.SpecialFolderType == SpecialFolderType.Category); + + if (categoryExists) + { + var categoryFolder = allFolders.First(a => a.SpecialFolderType == SpecialFolderType.Category); + + // Construct category items under pinned items. + var categoryFolders = allFolders.Where(a => gmailCategoryFolderTypes.Contains(a.SpecialFolderType)); + + foreach (var categoryFolderSubItem in categoryFolders) + { + categoryFolder.ChildFolders.Add(categoryFolderSubItem); + } + + accountTree.Folders.Add(categoryFolder); + allFolders.Remove(categoryFolder); + } + + // Move rest of the items into virtual More folder if any. + var nonStickyFolders = allFolders.Except(stickyFolders); + + if (nonStickyFolders.Any()) + { + var virtualMoreFolder = new MailItemFolder() + { + FolderName = Translator.More, + SpecialFolderType = SpecialFolderType.More + }; + + foreach (var unstickyItem in nonStickyFolders) + { + if (account.ProviderType == MailProviderType.Gmail) + { + // Gmail requires this check to not include child folders as + // separate folder without their parent for More folder... + + if (!string.IsNullOrEmpty(unstickyItem.ParentRemoteFolderId)) + continue; + } + else if (account.ProviderType == MailProviderType.Outlook || account.ProviderType == MailProviderType.Office365) + { + bool belongsToExistingParent = (await Connection + .Table() + .Where(a => unstickyItem.ParentRemoteFolderId == a.RemoteFolderId) + .CountAsync()) > 0; + + // No need to include this as unsticky. + if (belongsToExistingParent) continue; + } + + var structure = await GetChildFolderItemsRecursiveAsync(unstickyItem.Id, accountId); + + virtualMoreFolder.ChildFolders.Add(structure); + } + + // Only add more if there are any. + if (virtualMoreFolder.ChildFolders.Count > 0) + accountTree.Folders.Add(virtualMoreFolder); + } + } + + return accountTree; + } + + private async Task GetChildFolderItemsRecursiveAsync(Guid folderId, Guid accountId) + { + var folder = await Connection.Table().Where(a => a.Id == folderId && a.MailAccountId == accountId).FirstOrDefaultAsync(); + + if (folder == null) + return null; + + var childFolders = await Connection.Table() + .Where(a => a.ParentRemoteFolderId == folder.RemoteFolderId && a.MailAccountId == folder.MailAccountId) + .ToListAsync(); + + foreach (var childFolder in childFolders) + { + var subChild = await GetChildFolderItemsRecursiveAsync(childFolder.Id, accountId); + folder.ChildFolders.Add(subChild); + } + + return folder; + } + + public async Task GetSpecialFolderByAccountIdAsync(Guid accountId, SpecialFolderType type) + => await Connection.Table().FirstOrDefaultAsync(a => a.MailAccountId == accountId && a.SpecialFolderType == type); + + public async Task GetFolderAsync(Guid folderId) + => await Connection.Table().FirstOrDefaultAsync(a => a.Id.Equals(folderId)); + + public Task GetCurrentItemCountForFolder(Guid folderId) + => Connection.Table().Where(a => a.FolderId == folderId).CountAsync(); + + public Task> GetFoldersAsync(Guid accountId) + => Connection.Table().Where(a => a.MailAccountId == accountId).ToListAsync(); + + public async Task UpdateCustomServerMailListAsync(Guid accountId, List folders) + { + var account = await Connection.Table().FirstOrDefaultAsync(a => a.Id == accountId); + + if (account == null) + return; + + // IMAP servers don't have unique identifier for folders all the time. + // We'll map them with parent-name relation. + + var currentFolders = await GetFoldersAsync(accountId); + + // These folders don't exist anymore. Remove them. + var localRemoveFolders = currentFolders.ExceptBy(folders, a => a.RemoteFolderId); + + foreach (var currentFolder in currentFolders) + { + // Check if we have this folder locally. + var remotelyExistFolder = folders.FirstOrDefault(a => a.RemoteFolderId == currentFolder.RemoteFolderId + && a.ParentRemoteFolderId == currentFolder.ParentRemoteFolderId); + + if (remotelyExistFolder == null) + { + // This folder is removed. + // Remove everything for this folder. + + } + } + + foreach (var folder in folders) + { + var currentFolder = await Connection.Table().FirstOrDefaultAsync(a => a.MailAccountId == accountId && a.RemoteFolderId == folder.RemoteFolderId); + + // Nothing is changed, it's still the same folder. + // Just update Id of the folder. + + if (currentFolder != null) + folder.Id = currentFolder.Id; + + await Connection.InsertOrReplaceAsync(folder); + } + } + + public async Task> GetKnownUidsForFolderAsync(Guid folderId) + { + var folder = await GetFolderAsync(folderId); + + if (folder == null) return default; + + var mailCopyIds = await GetMailCopyIdsByFolderIdAsync(folderId); + + // Make sure we don't include Ids that doesn't have uid separator. + // Local drafts might not have it for example. + + return new List(mailCopyIds.Where(a => a.Contains(MailkitClientExtensions.MailCopyUidSeparator)).Select(a => MailkitClientExtensions.ResolveUid(a))); + } + + public async Task UpdateSystemFolderConfigurationAsync(Guid accountId, SystemFolderConfiguration configuration) + { + if (configuration == null) + throw new ArgumentNullException(nameof(configuration)); + + var account = await _accountService.GetAccountAsync(accountId); + + if (account == null) + throw new ArgumentNullException(nameof(account)); + + // Update system folders for this account. + + await Task.WhenAll(UpdateSystemFolderInternalAsync(configuration.SentFolder, SpecialFolderType.Sent), + UpdateSystemFolderInternalAsync(configuration.DraftFolder, SpecialFolderType.Draft), + UpdateSystemFolderInternalAsync(configuration.JunkFolder, SpecialFolderType.Junk), + UpdateSystemFolderInternalAsync(configuration.TrashFolder, SpecialFolderType.Deleted)); + + await _accountService.UpdateAccountAsync(account); + + return account; + } + + private Task UpdateSystemFolderInternalAsync(MailItemFolder folder, SpecialFolderType assignedSpecialFolderType) + { + if (folder == null) return Task.CompletedTask; + + folder.IsSticky = true; + folder.IsSynchronizationEnabled = true; + folder.IsSystemFolder = true; + folder.SpecialFolderType = assignedSpecialFolderType; + + return UpdateFolderAsync(folder); + } + + public async Task ChangeFolderSynchronizationStateAsync(Guid folderId, bool isSynchronizationEnabled) + { + var localFolder = await Connection.Table().FirstOrDefaultAsync(a => a.Id == folderId); + + if (localFolder != null) + { + localFolder.IsSynchronizationEnabled = isSynchronizationEnabled; + + await UpdateFolderAsync(localFolder).ConfigureAwait(false); + } + } + + #region Repository Calls + + public async Task InsertFolderAsync(MailItemFolder folder) + { + if (folder == null) + { + _logger.Warning("Folder is null. Cannot insert."); + + return; + } + + var account = await _accountService.GetAccountAsync(folder.MailAccountId); + + if (account == null) + { + _logger.Warning("Account with id {MailAccountId} does not exist. Cannot insert folder.", folder.MailAccountId); + + return; + } + + var existingFolder = await GetFolderAsync(folder.Id).ConfigureAwait(false); + + // IMAP servers don't have unique identifier for folders all the time. + // So we'll try to match them with remote folder id and account id relation. + // If we have a match, we'll update the folder instead of inserting. + + existingFolder ??= await GetFolderAsync(folder.MailAccountId, folder.RemoteFolderId).ConfigureAwait(false); + + if (existingFolder == null) + { + _logger.Debug("Inserting folder {Id} - {FolderName}", folder.Id, folder.FolderName, folder.MailAccountId); + + await Connection.InsertAsync(folder).ConfigureAwait(false); + + ReportUIChange(new FolderAddedMessage(folder, account)); + } + else + { + _logger.Debug("Folder {Id} - {FolderName} already exists. Updating.", folder.Id, folder.FolderName); + + await UpdateFolderAsync(folder).ConfigureAwait(false); + } + } + + private async Task UpdateFolderAsync(MailItemFolder folder) + { + if (folder == null) + { + _logger.Warning("Folder is null. Cannot update."); + + return; + } + + var account = await _accountService.GetAccountAsync(folder.MailAccountId).ConfigureAwait(false); + if (account == null) + { + _logger.Warning("Account with id {MailAccountId} does not exist. Cannot update folder.", folder.MailAccountId); + return; + } + +#if !DEBUG // Annoying + _logger.Debug("Updating folder {FolderName}", folder.Id, folder.FolderName); +#endif + + await Connection.UpdateAsync(folder).ConfigureAwait(false); + + ReportUIChange(new FolderUpdatedMessage(folder, account)); + } + + private async Task DeleteFolderAsync(MailItemFolder folder) + { + if (folder == null) + { + _logger.Warning("Folder is null. Cannot delete."); + + return; + } + + var account = await _accountService.GetAccountAsync(folder.MailAccountId).ConfigureAwait(false); + if (account == null) + { + _logger.Warning("Account with id {MailAccountId} does not exist. Cannot delete folder.", folder.MailAccountId); + return; + } + + _logger.Debug("Deleting folder {FolderName}", folder.FolderName); + + await Connection.DeleteAsync(folder).ConfigureAwait(false); + + ReportUIChange(new FolderRemovedMessage(folder, account)); + } + + #endregion + + private Task> GetMailCopyIdsByFolderIdAsync(Guid folderId) + { + var query = new Query("MailCopy") + .Where("FolderId", folderId) + .Select("Id"); + + return Connection.QueryScalarsAsync(query.GetRawQuery()); + } + + public async Task> GetMailFolderPairMetadatasAsync(IEnumerable mailCopyIds) + { + // Get all assignments for all items. + var query = new Query(nameof(MailCopy)) + .Join(nameof(MailItemFolder), $"{nameof(MailCopy)}.FolderId", $"{nameof(MailItemFolder)}.Id") + .WhereIn($"{nameof(MailCopy)}.Id", mailCopyIds) + .SelectRaw($"{nameof(MailCopy)}.Id as MailCopyId, {nameof(MailItemFolder)}.Id as FolderId, {nameof(MailItemFolder)}.RemoteFolderId as RemoteFolderId") + .Distinct(); + + var rowQuery = query.GetRawQuery(); + + return await Connection.QueryAsync(rowQuery); + } + + public Task> GetMailFolderPairMetadatasAsync(string mailCopyId) + => GetMailFolderPairMetadatasAsync(new List() { mailCopyId }); + + public async Task SetSpecialFolderAsync(Guid folderId, SpecialFolderType type) + => await Connection.ExecuteAsync("UPDATE MailItemFolder SET SpecialFolderType = ? WHERE Id = ?", type, folderId); + + public async Task> GetSynchronizationFoldersAsync(SynchronizationOptions options) + { + var folders = new List(); + + if (options.Type == SynchronizationType.Inbox) + { + var inboxFolder = await GetSpecialFolderByAccountIdAsync(options.AccountId, SpecialFolderType.Inbox); + var sentFolder = await GetSpecialFolderByAccountIdAsync(options.AccountId, SpecialFolderType.Sent); + var draftFolder = await GetSpecialFolderByAccountIdAsync(options.AccountId, SpecialFolderType.Draft); + + // For properly creating threads we need Sent and Draft to be synchronized as well. + + if (sentFolder != null && sentFolder.IsSynchronizationEnabled) + { + folders.Add(sentFolder); + } + + if (draftFolder != null && draftFolder.IsSynchronizationEnabled) + { + folders.Add(draftFolder); + } + + // User might've disabled inbox synchronization somehow... + if (inboxFolder != null && inboxFolder.IsSynchronizationEnabled) + { + folders.Add(inboxFolder); + } + } + else if (options.Type == SynchronizationType.Full) + { + // Only get sync enabled folders. + + var synchronizationFolders = await Connection.Table() + .Where(a => a.MailAccountId == options.AccountId && a.IsSynchronizationEnabled) + .OrderBy(a => a.SpecialFolderType) + .ToListAsync(); + + folders.AddRange(synchronizationFolders); + } + else if (options.Type == SynchronizationType.Custom) + { + // Only get the specified and enabled folders. + + var synchronizationFolders = await Connection.Table() + .Where(a => a.MailAccountId == options.AccountId && a.IsSynchronizationEnabled && options.SynchronizationFolderIds.Contains(a.Id)) + .ToListAsync(); + + folders.AddRange(synchronizationFolders); + } + + return folders; + } + + public Task GetFolderAsync(Guid accountId, string remoteFolderId) + => Connection.Table().FirstOrDefaultAsync(a => a.MailAccountId == accountId && a.RemoteFolderId == remoteFolderId); + + // v2 + public async Task BulkUpdateFolderStructureAsync(Guid accountId, List allFolders) + { + var existingFolders = await GetFoldersAsync(accountId).ConfigureAwait(false); + + var foldersToInsert = allFolders.ExceptBy(existingFolders, a => a.RemoteFolderId); + var foldersToDelete = existingFolders.ExceptBy(allFolders, a => a.RemoteFolderId); + var foldersToUpdate = allFolders.Except(foldersToInsert).Except(foldersToDelete); + + _logger.Debug("Found {0} folders to insert, {1} folders to update and {2} folders to delete.", + foldersToInsert.Count(), + foldersToUpdate.Count(), + foldersToDelete.Count()); + + foreach (var folder in foldersToInsert) + { + await InsertFolderAsync(folder).ConfigureAwait(false); + } + + foreach (var folder in foldersToUpdate) + { + await UpdateFolderAsync(folder).ConfigureAwait(false); + } + + foreach (var folder in foldersToDelete) + { + await DeleteFolderAsync(folder).ConfigureAwait(false); + } + } + + public async Task UpdateFolderDeltaSynchronizationIdentifierAsync(Guid folderId, string synchronizationIdentifier) + { + var folder = await GetFolderAsync(folderId).ConfigureAwait(false); + + if (folder == null) + { + _logger.Warning("Folder with id {FolderId} does not exist.", folderId); + + return string.Empty; + } + + folder.DeltaToken = synchronizationIdentifier; + + await UpdateFolderAsync(folder).ConfigureAwait(false); + + return synchronizationIdentifier; + } + + public async Task DeleteFolderAsync(Guid accountId, string remoteFolderId) + { + var folder = await GetFolderAsync(accountId, remoteFolderId); + + if (folder == null) + { + _logger.Warning("Folder with id {RemoteFolderId} does not exist. Delete folder canceled.", remoteFolderId); + + return; + } + + await DeleteFolderAsync(folder).ConfigureAwait(false); + } + + public async Task ChangeFolderShowUnreadCountStateAsync(Guid folderId, bool showUnreadCount) + { + var localFolder = await GetFolderAsync(folderId); + + if (localFolder != null) + { + localFolder.ShowUnreadCount = showUnreadCount; + + await UpdateFolderAsync(localFolder).ConfigureAwait(false); + } + } + + // Inbox folder is always included for account menu item unread count. + public Task> GetUnreadUpdateFoldersAsync(Guid accountId) + => Connection.Table().Where(a => a.MailAccountId == accountId && (a.ShowUnreadCount || a.SpecialFolderType == SpecialFolderType.Inbox)).ToListAsync(); + + public async Task TestAsync() + { + var account = new MailAccount() + { + Address = "test@test.com", + ProviderType = MailProviderType.Gmail, + Name = "Test Account", + Id = Guid.NewGuid() + }; + + await Connection.InsertAsync(account); + + var pref = new MailAccountPreferences + { + Id = Guid.NewGuid(), + AccountId = account.Id + }; + + await Connection.InsertAsync(pref); + + ReportUIChange(new AccountCreatedMessage(account)); + } + + public async Task IsInboxAvailableForAccountAsync(Guid accountId) + => (await Connection.Table() + .Where(a => a.SpecialFolderType == SpecialFolderType.Inbox && a.MailAccountId == accountId) + .CountAsync()) == 1; + } +} diff --git a/Wino.Core/Services/FontService.cs b/Wino.Core/Services/FontService.cs new file mode 100644 index 00000000..b6dfc16a --- /dev/null +++ b/Wino.Core/Services/FontService.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using Serilog; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Reader; + +namespace Wino.Core.Services +{ + public class FontService : IFontService + { + private readonly IPreferencesService _preferencesService; + private ILogger _logger = Log.ForContext(); + + private readonly List _availableFonts = + [ + new ReaderFontModel(ReaderFont.Arial, "Arial"), + new ReaderFontModel(ReaderFont.Calibri, "Calibri"), + new ReaderFontModel(ReaderFont.TimesNewRoman, "Times New Roman"), + new ReaderFontModel(ReaderFont.TrebuchetMS, "Trebuchet MS"), + new ReaderFontModel(ReaderFont.Tahoma, "Tahoma"), + new ReaderFontModel(ReaderFont.Verdana, "Verdana"), + new ReaderFontModel(ReaderFont.Georgia, "Georgia"), + new ReaderFontModel(ReaderFont.CourierNew, "Courier New") + ]; + + public FontService(IPreferencesService preferencesService) + { + _preferencesService = preferencesService; + } + + public List GetReaderFonts() => _availableFonts; + + public void ChangeReaderFont(ReaderFont font) + { + _preferencesService.ReaderFont = font; + + _logger.Information("Default reader font is changed to {Font}", font); + } + + public void ChangeReaderFontSize(int size) + { + _preferencesService.ReaderFontSize = size; + + _logger.Information("Default reader font size is changed to {Size}", size); + } + + public ReaderFontModel GetCurrentReaderFont() => _availableFonts.Find(f => f.Font == _preferencesService.ReaderFont); + public int GetCurrentReaderFontSize() => _preferencesService.ReaderFontSize; + } +} diff --git a/Wino.Core/Services/ImapTestService.cs b/Wino.Core/Services/ImapTestService.cs new file mode 100644 index 00000000..c91ad119 --- /dev/null +++ b/Wino.Core/Services/ImapTestService.cs @@ -0,0 +1,53 @@ +using System.IO; +using System.Threading.Tasks; +using MailKit; +using MailKit.Net.Imap; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.Services +{ + public class ImapTestService : IImapTestService + { + public const string ProtocolLogFileName = "ImapProtocolLog.log"; + + private readonly IPreferencesService _preferencesService; + private readonly IAppInitializerService _appInitializerService; + + public ImapTestService(IPreferencesService preferencesService, IAppInitializerService appInitializerService) + { + _preferencesService = preferencesService; + _appInitializerService = appInitializerService; + } + + public async Task TestImapConnectionAsync(CustomServerInformation serverInformation) + { + ImapClient client = null; + + if (_preferencesService.IsMailkitProtocolLoggerEnabled) + { + // Create new file for protocol logger. + + var localAppFolderPath = _appInitializerService.GetApplicationDataFolder(); + + var logFile = Path.Combine(localAppFolderPath, ProtocolLogFileName); + + if (File.Exists(logFile)) + File.Delete(logFile); + + var stream = File.Create(logFile); + + client = new ImapClient(new ProtocolLogger(stream)); + } + else + client = new ImapClient(); + + using (client) + { + // todo: test connection + // await client.InitializeAsync(serverInformation); + await client.DisconnectAsync(true); + } + } + } +} diff --git a/Wino.Core/Services/LogInitializer.cs b/Wino.Core/Services/LogInitializer.cs new file mode 100644 index 00000000..624723c6 --- /dev/null +++ b/Wino.Core/Services/LogInitializer.cs @@ -0,0 +1,41 @@ +using System.IO; +using Serilog; +using Serilog.Core; +using Serilog.Exceptions; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.Services +{ + public class LogInitializer : ILogInitializer + { + public const string WinoLogFileName = "WinoDiagnostics.log"; + + private readonly LoggingLevelSwitch _levelSwitch = new LoggingLevelSwitch(); + private readonly IPreferencesService _preferencesService; + + public LogInitializer(IPreferencesService preferencesService) + { + _preferencesService = preferencesService; + + RefreshLoggingLevel(); + } + + public void RefreshLoggingLevel() + { + _levelSwitch.MinimumLevel = _preferencesService.IsLoggingEnabled ? Serilog.Events.LogEventLevel.Debug : Serilog.Events.LogEventLevel.Fatal; + } + + public void SetupLogger(string logFolderPath) + { + string logFilePath = Path.Combine(logFolderPath, WinoLogFileName); + + Log.Logger = new LoggerConfiguration() + .MinimumLevel.ControlledBy(_levelSwitch) + .WriteTo.File(logFilePath) + .WriteTo.Debug() + .Enrich.FromLogContext() + .Enrich.WithExceptionDetails() + .CreateLogger(); + } + } +} diff --git a/Wino.Core/Services/MailService.cs b/Wino.Core/Services/MailService.cs new file mode 100644 index 00000000..7cc0bf63 --- /dev/null +++ b/Wino.Core/Services/MailService.cs @@ -0,0 +1,833 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using MimeKit; +using MimeKit.Text; +using MoreLinq; +using Serilog; +using SqlKata; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Comparers; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Extensions; +using Wino.Core.Requests; + +namespace Wino.Core.Services +{ + public class MailService : BaseDatabaseService, IMailService + { + private const int ItemLoadCount = 20; + + private readonly IFolderService _folderService; + private readonly IContactService _contactService; + private readonly IAccountService _accountService; + private readonly ISignatureService _signatureService; + private readonly IThreadingStrategyProvider _threadingStrategyProvider; + private readonly IMimeFileService _mimeFileService; + + + private readonly ILogger _logger = Log.ForContext(); + + public MailService(IDatabaseService databaseService, + IFolderService folderService, + IContactService contactService, + IAccountService accountService, + ISignatureService signatureService, + IThreadingStrategyProvider threadingStrategyProvider, + IMimeFileService mimeFileService) : base(databaseService) + { + _folderService = folderService; + _contactService = contactService; + _accountService = accountService; + _signatureService = signatureService; + _threadingStrategyProvider = threadingStrategyProvider; + _mimeFileService = mimeFileService; + } + + public async Task CreateDraftAsync(MailAccount composerAccount, + MimeMessage createdDraftMimeMessage, + MimeMessage replyingMimeMessage = null, + IMailItem replyingMailItem = null) + { + bool isImapAccount = composerAccount.ServerInformation != null; + + string fromName; + + if (isImapAccount) + fromName = composerAccount.ServerInformation.DisplayName; + else + { + var composerContact = await _contactService.GetAddressInformationByAddressAsync(composerAccount.Address); + + fromName = composerContact?.Name ?? composerAccount.Address; + } + + var draftFolder = await _folderService.GetSpecialFolderByAccountIdAsync(composerAccount.Id, SpecialFolderType.Draft); + + // Get locally created unique id from the mime headers. + // This header will be used to map the local draft copy with the remote draft copy. + var mimeUniqueId = createdDraftMimeMessage.Headers[Constants.WinoLocalDraftHeader]; + + var copy = new MailCopy + { + UniqueId = Guid.Parse(mimeUniqueId), + Id = Guid.NewGuid().ToString(), // This will be replaced after network call with the remote draft id. + CreationDate = DateTime.UtcNow, + FromAddress = composerAccount.Address, + FromName = fromName, + HasAttachments = false, + Importance = MailImportance.Normal, + Subject = createdDraftMimeMessage.Subject, + PreviewText = createdDraftMimeMessage.TextBody, + IsRead = true, + IsDraft = true, + FolderId = draftFolder.Id, + DraftId = $"{Constants.LocalDraftStartPrefix}{Guid.NewGuid()}", + AssignedFolder = draftFolder, + AssignedAccount = composerAccount, + FileId = Guid.NewGuid() + }; + + // If replying, add In-Reply-To, ThreadId and References. + bool isReplying = replyingMimeMessage != null; + + if (isReplying) + { + if (replyingMimeMessage.References != null) + copy.References = string.Join(",", replyingMimeMessage.References); + + if (!string.IsNullOrEmpty(replyingMimeMessage.MessageId)) + copy.InReplyTo = replyingMimeMessage.MessageId; + + if (!string.IsNullOrEmpty(replyingMailItem?.ThreadId)) + copy.ThreadId = replyingMailItem.ThreadId; + } + + await Connection.InsertAsync(copy); + + + await _mimeFileService.SaveMimeMessageAsync(copy.FileId, createdDraftMimeMessage, composerAccount.Id); + + ReportUIChange(new DraftCreated(copy, composerAccount)); + + return copy; + } + + public Task> GetMailIdsByFolderIdAsync(Guid folderId) + => Connection.QueryScalarsAsync("SELECT Id FROM MailCopy WHERE FolderId = ?", folderId); + + + private string BuildMailFetchQuery(MailListInitializationOptions options) + { + // If the search query is there, we should ignore some properties and trim it. + //if (!string.IsNullOrEmpty(options.SearchQuery)) + //{ + // options.IsFocusedOnly = null; + // filterType = FilterOptionType.All; + + // searchQuery = searchQuery.Trim(); + //} + + // SQLite PCL doesn't support joins. + // We make the query using SqlKatka and execute it directly on SQLite-PCL. + + var query = new Query("MailCopy") + .Join("MailItemFolder", "MailCopy.FolderId", "MailItemFolder.Id") + .WhereIn("MailCopy.FolderId", options.Folders.Select(a => a.Id)) + .Take(ItemLoadCount) + .SelectRaw("MailCopy.*"); + + if (options.SortingOptionType == SortingOptionType.ReceiveDate) + query.OrderByDesc("CreationDate"); + else if (options.SortingOptionType == SortingOptionType.Sender) + query.OrderBy("FromName"); + + // Conditional where. + switch (options.FilterType) + { + case FilterOptionType.Unread: + query.Where("MailCopy.IsRead", false); + break; + case FilterOptionType.Flagged: + query.Where("MailCopy.IsFlagged", true); + break; + } + + if (options.IsFocusedOnly != null) + query.Where("MailCopy.IsFocused", options.IsFocusedOnly.Value); + + if (!string.IsNullOrEmpty(options.SearchQuery)) + query.Where(a => + a.OrWhereContains("MailCopy.PreviewText", options.SearchQuery) + .OrWhereContains("MailCopy.Subject", options.SearchQuery) + .OrWhereContains("MailCopy.FromName", options.SearchQuery) + .OrWhereContains("MailCopy.FromAddress", options.SearchQuery)); + + if (options.ExistingUniqueIds?.Any() ?? false) + { + query.WhereNotIn("MailCopy.UniqueId", options.ExistingUniqueIds); + } + + //if (options.Skip > 0) + //{ + // query.Skip(options.Skip); + //} + + return query.GetRawQuery(); + } + + public async Task> FetchMailsAsync(MailListInitializationOptions options) + { + var query = BuildMailFetchQuery(options); + + var mails = await Connection.QueryAsync(query); + + // Fill in assigned account and folder for each mail. + // To speed things up a bit, we'll load account and assigned folder in groups + // to reduce the query time. + + var groupedByFolders = mails.GroupBy(a => a.FolderId); + + foreach (var group in groupedByFolders) + { + MailItemFolder folderAssignment = null; + MailAccount accountAssignment = null; + + folderAssignment = await _folderService.GetFolderAsync(group.Key).ConfigureAwait(false); + + if (folderAssignment != null) + { + accountAssignment = await _accountService.GetAccountAsync(folderAssignment.MailAccountId).ConfigureAwait(false); + } + + group.ForEach(a => + { + a.AssignedFolder = folderAssignment; + a.AssignedAccount = accountAssignment; + }); + } + + // Remove items that has no assigned account or folder. + mails.RemoveAll(a => a.AssignedAccount == null || a.AssignedFolder == null); + + // Each account items must be threaded separately. + + if (options.CreateThreads) + { + var threadedItems = new List(); + + var groupedByAccounts = mails.GroupBy(a => a.AssignedAccount.Id); + + foreach (var group in groupedByAccounts) + { + if (!group.Any()) continue; + + var accountId = group.Key; + var groupAccount = mails.First(a => a.AssignedAccount.Id == accountId).AssignedAccount; + + var threadingStrategy = _threadingStrategyProvider.GetStrategy(groupAccount.ProviderType); + + // Only thread items from Draft and Sent folders must present here. + // Otherwise this strategy will fetch the items that are in Deleted folder as well. + var accountThreadedItems = await threadingStrategy.ThreadItemsAsync(group.ToList()); + + if (accountThreadedItems != null) + { + threadedItems.AddRange(accountThreadedItems); + } + } + + threadedItems.Sort(options.SortingOptionType == SortingOptionType.ReceiveDate ? new DateComparer() : new NameComparer()); + + return threadedItems; + } + else + { + // Threading is disabled. Just return everything as it is. + + mails.Sort(options.SortingOptionType == SortingOptionType.ReceiveDate ? new DateComparer() : new NameComparer()); + + return new List(mails); + } + } + + private async Task> GetMailItemsAsync(string mailCopyId) + { + var mailCopies = await Connection.Table().Where(a => a.Id == mailCopyId).ToListAsync(); + + foreach (var mailCopy in mailCopies) + { + await LoadAssignedPropertiesAsync(mailCopy).ConfigureAwait(false); + } + + return mailCopies; + } + + private async Task LoadAssignedPropertiesAsync(MailCopy mailCopy) + { + if (mailCopy == null) return; + + // Load AssignedAccount and AssignedFolder. + + var folder = await _folderService.GetFolderAsync(mailCopy.FolderId); + + if (folder == null) return; + + var account = await _accountService.GetAccountAsync(folder.MailAccountId); + + if (account == null) return; + + mailCopy.AssignedAccount = account; + mailCopy.AssignedFolder = folder; + } + + public async Task GetSingleMailItemWithoutFolderAssignmentAsync(string mailCopyId) + { + var mailCopy = await Connection.Table().FirstOrDefaultAsync(a => a.Id == mailCopyId); + + if (mailCopy == null) return null; + + await LoadAssignedPropertiesAsync(mailCopy).ConfigureAwait(false); + + return mailCopy; + } + + public async Task GetSingleMailItemAsync(string mailCopyId, string remoteFolderId) + { + var query = new Query("MailCopy") + .Join("MailItemFolder", "MailCopy.FolderId", "MailItemFolder.Id") + .Where("MailCopy.Id", mailCopyId) + .Where("MailItemFolder.RemoteFolderId", remoteFolderId) + .SelectRaw("MailCopy.*") + .GetRawQuery(); + + var mailItem = await Connection.FindWithQueryAsync(query); + + if (mailItem == null) return null; + + await LoadAssignedPropertiesAsync(mailItem).ConfigureAwait(false); + + return mailItem; + } + + public async Task GetSingleMailItemAsync(Guid uniqueMailId) + { + var mailItem = await Connection.FindAsync(uniqueMailId); + + if (mailItem == null) return null; + + await LoadAssignedPropertiesAsync(mailItem).ConfigureAwait(false); + + return mailItem; + } + + // v2 + + public async Task DeleteMailAsync(Guid accountId, string mailCopyId) + { + var allMails = await GetMailItemsAsync(mailCopyId).ConfigureAwait(false); + + foreach (var mailItem in allMails) + { + await DeleteMailInternalAsync(mailItem).ConfigureAwait(false); + + // Delete mime file. + // Even though Gmail might have multiple copies for the same mail, we only have one MIME file for all. + // Their FileId is inserted same. + await _mimeFileService.DeleteMimeMessageAsync(accountId, mailItem.FileId); + } + } + + #region Repository Calls + + private async Task InsertMailAsync(MailCopy mailCopy) + { + if (mailCopy == null) + { + _logger.Warning("Null mail passed to InsertMailAsync call."); + return; + } + + if (mailCopy.FolderId == Guid.Empty) + { + _logger.Warning("Invalid FolderId for MailCopyId {Id} for InsertMailAsync", mailCopy.Id); + return; + } + + _logger.Debug("Inserting mail {MailCopyId} to Folder {FolderId}", mailCopy.Id, mailCopy.FolderId); + + await Connection.InsertAsync(mailCopy).ConfigureAwait(false); + + ReportUIChange(new MailAddedMessage(mailCopy)); + } + + public async Task UpdateMailAsync(MailCopy mailCopy) + { + if (mailCopy == null) + { + _logger.Warning("Null mail passed to UpdateMailAsync call."); + + return; + } + + _logger.Debug("Updating mail {MailCopyId} with Folder {FolderId}", mailCopy.Id, mailCopy.FolderId); + + await Connection.UpdateAsync(mailCopy).ConfigureAwait(false); + + ReportUIChange(new MailUpdatedMessage(mailCopy)); + } + + private async Task DeleteMailInternalAsync(MailCopy mailCopy) + { + if (mailCopy == null) + { + _logger.Warning("Null mail passed to DeleteMailAsync call."); + + return; + } + + _logger.Debug("Deleting mail {Id} with Folder {FolderId}", mailCopy.Id, mailCopy.FolderId); + + await Connection.DeleteAsync(mailCopy).ConfigureAwait(false); + + ReportUIChange(new MailRemovedMessage(mailCopy)); + } + + #endregion + + private async Task UpdateAllMailCopiesAsync(string mailCopyId, Func action) + { + var mailCopies = await GetMailItemsAsync(mailCopyId); + + if (mailCopies == null || !mailCopies.Any()) + { + _logger.Warning("Updating mail copies failed because there are no copies available with Id {MailCopyId}", mailCopyId); + + return; + } + + _logger.Information("Updating {MailCopyCount} mail copies with Id {MailCopyId}", mailCopies.Count, mailCopyId); + + foreach (var mailCopy in mailCopies) + { + bool shouldUpdateItem = action(mailCopy); + + if (shouldUpdateItem) + { + await UpdateMailAsync(mailCopy).ConfigureAwait(false); + } + else + _logger.Information("Skipped updating mail because it is already in the desired state."); + } + } + + public Task ChangeReadStatusAsync(string mailCopyId, bool isRead) + => UpdateAllMailCopiesAsync(mailCopyId, (item) => + { + item.IsRead = isRead; + + return true; + }); + + public Task ChangeFlagStatusAsync(string mailCopyId, bool isFlagged) + => UpdateAllMailCopiesAsync(mailCopyId, (item) => + { + item.IsFlagged = isFlagged; + + return true; + }); + + public async Task CreateAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId) + { + // Note: Folder might not be available at the moment due to user not syncing folders before the delta processing. + // This is a problem, because assignments won't be created. + // Therefore we sync folders every time before the delta processing. + + var localFolder = await _folderService.GetFolderAsync(accountId, remoteFolderId); + + if (localFolder == null) + { + _logger.Warning("Local folder not found for remote folder {RemoteFolderId}", remoteFolderId); + _logger.Warning("Skipping assignment creation for the the message {MailCopyId}", mailCopyId); + + return; + } + + var mailCopy = await GetSingleMailItemWithoutFolderAssignmentAsync(mailCopyId); + + if (mailCopy == null) + { + _logger.Warning("Can't create assignment for mail {MailCopyId} because it does not exist.", mailCopyId); + + return; + } + + // Copy one of the mail copy and assign it to the new folder. + // We don't need to create a new MIME pack. + // Therefore FileId is not changed for the new MailCopy. + + mailCopy.UniqueId = Guid.NewGuid(); + mailCopy.FolderId = localFolder.Id; + mailCopy.AssignedFolder = localFolder; + + await InsertMailAsync(mailCopy).ConfigureAwait(false); + } + + public async Task DeleteAssignmentAsync(Guid accountId, string mailCopyId, string remoteFolderId) + { + var mailItem = await GetSingleMailItemAsync(mailCopyId, remoteFolderId).ConfigureAwait(false); + + if (mailItem == null) + { + _logger.Warning("Mail not found with id {MailCopyId} with remote folder {RemoteFolderId}", mailCopyId, remoteFolderId); + + return; + } + + var localFolder = await _folderService.GetFolderAsync(accountId, remoteFolderId); + + if (localFolder == null) + { + _logger.Warning("Local folder not found for remote folder {RemoteFolderId}", remoteFolderId); + + return; + } + + await DeleteMailInternalAsync(mailItem).ConfigureAwait(false); + } + + public async Task CreateMailAsync(Guid accountId, NewMailItemPackage package) + { + var account = await _accountService.GetAccountAsync(accountId).ConfigureAwait(false); + + if (account == null) return false; + + if (string.IsNullOrEmpty(package.AssignedRemoteFolderId)) + { + _logger.Warning("Remote folder id is not set for {MailCopyId}.", package.Copy.Id); + _logger.Warning("Ignoring creation of mail."); + + return false; + } + + var assignedFolder = await _folderService.GetFolderAsync(accountId, package.AssignedRemoteFolderId).ConfigureAwait(false); + + if (assignedFolder == null) + { + _logger.Warning("Assigned folder not found for {MailCopyId}.", package.Copy.Id); + _logger.Warning("Ignoring creation of mail."); + + return false; + } + + var mailCopy = package.Copy; + var mimeMessage = package.Mime; + + mailCopy.UniqueId = Guid.NewGuid(); + mailCopy.AssignedAccount = account; + mailCopy.AssignedFolder = assignedFolder; + mailCopy.FolderId = assignedFolder.Id; + + // Only save MIME files if they don't exists. + // This is because 1 mail may have multiple copies in different folders. + // but only single MIME to represent all. + + // Save mime file to disk. + var isMimeExists = await _mimeFileService.IsMimeExistAsync(accountId, mailCopy.FileId); + + if (!isMimeExists) + { + bool isMimeSaved = await _mimeFileService.SaveMimeMessageAsync(mailCopy.FileId, mimeMessage, accountId).ConfigureAwait(false); + + if (!isMimeSaved) + { + _logger.Warning("Failed to save mime file for {MailCopyId}.", mailCopy.Id); + } + } + + // Save contact information. + await _contactService.SaveAddressInformationAsync(mimeMessage).ConfigureAwait(false); + + // Create mail copy in the database. + // Update if exists. + + var existingCopyItem = await Connection.Table() + .FirstOrDefaultAsync(a => a.Id == mailCopy.Id && a.FolderId == assignedFolder.Id); + + if (existingCopyItem != null) + { + mailCopy.UniqueId = existingCopyItem.UniqueId; + + await UpdateMailAsync(mailCopy).ConfigureAwait(false); + + return false; + } + else + { + await InsertMailAsync(mailCopy).ConfigureAwait(false); + + return true; + } + } + + public async Task CreateDraftMimeMessageAsync(Guid accountId, DraftCreationOptions draftCreationOptions) + { + // This unique id is stored in mime headers for Wino to identify remote message with local copy. + // Same unique id will be used for the local copy as well. + // Synchronizer will map this unique id to the local draft copy after synchronization. + + var messageUniqueId = Guid.NewGuid(); + + var message = new MimeMessage() + { + Headers = { { Constants.WinoLocalDraftHeader, messageUniqueId.ToString() } } + }; + + var builder = new BodyBuilder(); + + var account = await _accountService.GetAccountAsync(accountId).ConfigureAwait(false); + + if (account == null) + { + _logger.Warning("Can't create draft mime message because account {AccountId} does not exist.", accountId); + + return null; + } + + var reason = draftCreationOptions.Reason; + var referenceMessage = draftCreationOptions.ReferenceMimeMessage; + + // For API synchronizers we should get this from contacts. + if (account.ServerInformation == null) + { + var fromContact = await _contactService.GetAddressInformationByAddressAsync(account.Address).ConfigureAwait(false) + ?? new AddressInformation() { Name = account.Address, Address = account.Address }; + + message.From.Add(new MailboxAddress(fromContact.Name, fromContact.Address)); + } + else + { + // For IMAP synchronizer, we have already Display Name in the settings. + message.From.Add(new MailboxAddress(account.ServerInformation.DisplayName, account.ServerInformation.Address)); + } + + // Manage "To" + if (reason == DraftCreationReason.Reply || reason == DraftCreationReason.ReplyAll) + { + // Reply to the sender of the message + + if (referenceMessage.ReplyTo.Count > 0) + message.To.AddRange(referenceMessage.ReplyTo); + else if (referenceMessage.From.Count > 0) + message.To.AddRange(referenceMessage.From); + else if (referenceMessage.Sender != null) + message.To.Add(referenceMessage.Sender); + + if (reason == DraftCreationReason.ReplyAll) + { + // Include all of the other original recipients + message.To.AddRange(referenceMessage.To); + + // Find self and remove + var self = message.To.FirstOrDefault(a => a is MailboxAddress mailboxAddress && mailboxAddress.Address == account.Address); + + if (self != null) + message.To.Remove(self); + + message.Cc.AddRange(referenceMessage.Cc); + } + + // Manage "ThreadId-ConversationId" + if (!string.IsNullOrEmpty(referenceMessage.MessageId)) + { + message.InReplyTo = referenceMessage.MessageId; + + foreach (var id in referenceMessage.References) + message.References.Add(id); + + message.References.Add(referenceMessage.MessageId); + } + + message.Headers.Add("Thread-Topic", referenceMessage.Subject); + } + + var previewer = new HtmlTextPreviewer(); + + if (reason == DraftCreationReason.Forward) + { + var visitor = _mimeFileService.CreateHTMLPreviewVisitor(referenceMessage, string.Empty); + visitor.Visit(referenceMessage); + + builder.HtmlBody = visitor.HtmlBody; + } + else + { + // Add signature if any. + var accountSignature = await _signatureService.GetAccountSignatureAsync(account.Id); + + if (accountSignature != null) + { + // Leave some space for new mail content. + + builder.HtmlBody = @$"

{accountSignature.HtmlBody}"; + } + } + + // Manage Subject + if (reason == DraftCreationReason.Forward && !referenceMessage.Subject.StartsWith("FW: ", StringComparison.OrdinalIgnoreCase)) + message.Subject = $"FW: {referenceMessage.Subject}"; + else if ((reason == DraftCreationReason.Reply || reason == DraftCreationReason.ReplyAll) && + !referenceMessage.Subject.StartsWith("RE: ", StringComparison.OrdinalIgnoreCase)) + message.Subject = $"RE: {referenceMessage.Subject}"; + else if (referenceMessage != null) + message.Subject = referenceMessage.Subject; + + // Only include attachments if forwarding. + if (reason == DraftCreationReason.Forward && (referenceMessage?.Attachments?.Any() ?? false)) + { + foreach (var attachment in referenceMessage.Attachments) + { + builder.Attachments.Add(attachment); + } + } + + if (!string.IsNullOrEmpty(builder.HtmlBody)) + { + builder.TextBody = HtmlAgilityPackExtensions.GetPreviewText(builder.HtmlBody); + } + + message.Body = builder.ToMessageBody(); + + // Apply mail-to protocol parameters if exists. + + if (draftCreationOptions.MailtoParameters != null) + { + if (draftCreationOptions.TryGetMailtoValue(DraftCreationOptions.MailtoSubjectParameterKey, out string subjectParameter)) + message.Subject = subjectParameter; + + if (draftCreationOptions.TryGetMailtoValue(DraftCreationOptions.MailtoBodyParameterKey, out string bodyParameter)) + { + builder.TextBody = bodyParameter; + builder.HtmlBody = bodyParameter; + + message.Body = builder.ToMessageBody(); + } + + InternetAddressList ExtractRecipients(string parameterValue) + { + var list = new InternetAddressList(); + + var splittedRecipients = parameterValue.Split(','); + + foreach (var recipient in splittedRecipients) + list.Add(new MailboxAddress(recipient, recipient)); + + return list; + + } + + if (draftCreationOptions.TryGetMailtoValue(DraftCreationOptions.MailtoToParameterKey, out string toParameter)) + message.To.AddRange(ExtractRecipients(toParameter)); + + if (draftCreationOptions.TryGetMailtoValue(DraftCreationOptions.MailtoCCParameterKey, out string ccParameter)) + message.Cc.AddRange(ExtractRecipients(ccParameter)); + + if (draftCreationOptions.TryGetMailtoValue(DraftCreationOptions.MailtoBCCParameterKey, out string bccParameter)) + message.Bcc.AddRange(ExtractRecipients(bccParameter)); + } + else + { + // Update TextBody from existing HtmlBody if exists. + } + + return message; + } + + public async Task MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId) + { + var query = new Query("MailCopy") + .Join("MailItemFolder", "MailCopy.FolderId", "MailItemFolder.Id") + .Where("MailCopy.UniqueId", localDraftCopyUniqueId) + .Where("MailItemFolder.MailAccountId", accountId) + .SelectRaw("MailCopy.*") + .GetRawQuery(); + + var localDraftCopy = await Connection.FindWithQueryAsync(query); + + if (localDraftCopy == null) + { + _logger.Warning("Draft mapping failed because local draft copy with unique id {LocalDraftCopyUniqueId} does not exist.", localDraftCopyUniqueId); + + return false; + } + + var oldLocalDraftId = localDraftCopy.Id; + + await LoadAssignedPropertiesAsync(localDraftCopy).ConfigureAwait(false); + + bool isIdChanging = localDraftCopy.Id != newMailCopyId; + + localDraftCopy.Id = newMailCopyId; + localDraftCopy.DraftId = newDraftId; + localDraftCopy.ThreadId = newThreadId; + + await UpdateMailAsync(localDraftCopy).ConfigureAwait(false); + + ReportUIChange(new DraftMapped(oldLocalDraftId, newDraftId)); + + return true; + } + + public Task MapLocalDraftAsync(string mailCopyId, string newDraftId, string newThreadId) + { + return UpdateAllMailCopiesAsync(mailCopyId, (item) => + { + if (item.ThreadId != newThreadId || item.DraftId != newDraftId) + { + var oldDraftId = item.DraftId; + + item.DraftId = newDraftId; + item.ThreadId = newThreadId; + + ReportUIChange(new DraftMapped(oldDraftId, newDraftId)); + + return true; + } + + return false; + }); + } + + public Task> GetDownloadedUnreadMailsAsync(Guid accountId, IEnumerable downloadedMailCopyIds) + { + var rawQuery = new Query("MailCopy") + .Join("MailItemFolder", "MailCopy.FolderId", "MailItemFolder.Id") + .WhereIn("MailCopy.Id", downloadedMailCopyIds) + .Where("MailCopy.IsRead", false) + .Where("MailItemFolder.MailAccountId", accountId) + .Where("MailItemFolder.SpecialFolderType", SpecialFolderType.Inbox) + .SelectRaw("MailCopy.*") + .GetRawQuery(); + + return Connection.QueryAsync(rawQuery); + } + + public Task GetMailAccountByUniqueIdAsync(Guid uniqueMailId) + { + var query = new Query("MailCopy") + .Join("MailItemFolder", "MailCopy.FolderId", "MailItemFolder.Id") + .Join("MailAccount", "MailItemFolder.MailAccountId", "MailAccount.Id") + .Where("MailCopy.UniqueId", uniqueMailId) + .SelectRaw("MailAccount.*") + .GetRawQuery(); + + return Connection.FindWithQueryAsync(query); + } + + + } +} diff --git a/Wino.Core/Services/MimeFileService.cs b/Wino.Core/Services/MimeFileService.cs new file mode 100644 index 00000000..03cb14da --- /dev/null +++ b/Wino.Core/Services/MimeFileService.cs @@ -0,0 +1,242 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using MimeKit; +using Serilog; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Reader; +using Wino.Core.Extensions; +using Wino.Core.Mime; + +namespace Wino.Core.Services +{ + public interface IMimeFileService + { + /// + /// Finds the EML file for the given mail id for address, parses and returns MimeMessage. + /// + /// Cancellation token + /// Mime message information + Task GetMimeMessageInformationAsync(Guid fileId, Guid accountId, CancellationToken cancellationToken = default); + + /// + /// Gets the mime message information for the given EML file bytes. + /// This override is used when EML file association launch is used + /// because we may not have the access to the file path. + /// + /// Byte array of the file. + /// Cancellation token + /// Mime message information + Task GetMimeMessageInformationAsync(byte[] fileBytes, string emlFilePath, CancellationToken cancellationToken = default); + + /// + /// Saves EML file to the disk. + /// + /// MailCopy of the native message. + /// MimeMessage that is parsed from native message. + /// Which account Id to save this file for. + Task SaveMimeMessageAsync(Guid fileId, MimeMessage mimeMessage, Guid accountId); + + /// + /// Returns a path that all Mime resources (including eml) is stored for this MailCopyId + /// This is useful for storing previously rendered attachments as well. + /// + /// Account address + /// Resource mail copy id + Task GetMimeResourcePathAsync(Guid accountId, Guid fileId); + + /// + /// Returns whether mime file exists locally or not. + /// + Task IsMimeExistAsync(Guid accountId, Guid fileId); + + /// + /// Creates HtmlPreviewVisitor for the given MimeMessage. + /// + /// Mime + /// File path that mime is located to load resources. + HtmlPreviewVisitor CreateHTMLPreviewVisitor(MimeMessage message, string mimeLocalPath); + + /// + /// Deletes the given mime file from the disk. + /// + Task DeleteMimeMessageAsync(Guid accountId, Guid fileId); + + /// + /// Prepares the final model containing rendering details. + /// + /// Message to render. + /// File path that physical MimeMessage is located. + /// Rendering options + MailRenderModel GetMailRenderModel(MimeMessage message, string mimeLocalPath, MailRenderingOptions options = null); + } + + public class MimeFileService : IMimeFileService + { + private readonly INativeAppService _nativeAppService; + private ILogger _logger = Log.ForContext(); + + public MimeFileService(INativeAppService nativeAppService) + { + _nativeAppService = nativeAppService; + } + + public async Task GetMimeMessageInformationAsync(Guid fileId, Guid accountId, CancellationToken cancellationToken = default) + { + var resourcePath = await GetMimeResourcePathAsync(accountId, fileId).ConfigureAwait(false); + var mimeFilePath = GetEMLPath(resourcePath); + + var loadedMimeMessage = await MimeMessage.LoadAsync(mimeFilePath, cancellationToken).ConfigureAwait(false); + + return new MimeMessageInformation(loadedMimeMessage, resourcePath); + } + + public async Task GetMimeMessageInformationAsync(byte[] fileBytes, string emlDirectoryPath, CancellationToken cancellationToken = default) + { + var memoryStream = new MemoryStream(fileBytes); + + var loadedMimeMessage = await MimeMessage.LoadAsync(memoryStream, cancellationToken).ConfigureAwait(false); + return new MimeMessageInformation(loadedMimeMessage, emlDirectoryPath); + } + + public async Task SaveMimeMessageAsync(Guid fileId, MimeMessage mimeMessage, Guid accountId) + { + try + { + var resourcePath = await GetMimeResourcePathAsync(accountId, fileId).ConfigureAwait(false); + var completeFilePath = GetEMLPath(resourcePath); + + var fileStream = File.Create(completeFilePath); + + using (fileStream) + { + await mimeMessage.WriteToAsync(fileStream).ConfigureAwait(false); + } + + return true; + } + catch (Exception ex) + { + _logger.Error(ex, "Could not save mime file for FileId: {FileId}", fileId); + } + + return false; + } + + private string GetEMLPath(string resourcePath) => $"{resourcePath}\\mail.eml"; + + public async Task GetMimeResourcePathAsync(Guid accountId, Guid fileId) + { + var mimeFolderPath = await _nativeAppService.GetMimeMessageStoragePath().ConfigureAwait(false); + var mimeDirectory = Path.Combine(mimeFolderPath, accountId.ToString(), fileId.ToString()); + + if (!Directory.Exists(mimeDirectory)) + Directory.CreateDirectory(mimeDirectory); + + return mimeDirectory; + } + + public async Task IsMimeExistAsync(Guid accountId, Guid fileId) + { + var resourcePath = await GetMimeResourcePathAsync(accountId, fileId); + var completeFilePath = GetEMLPath(resourcePath); + + return File.Exists(completeFilePath); + } + + public HtmlPreviewVisitor CreateHTMLPreviewVisitor(MimeMessage message, string mimeLocalPath) + { + var visitor = new HtmlPreviewVisitor(mimeLocalPath); + + message.Accept(visitor); + + // TODO: Match cid with attachments if any. + + return visitor; + } + + public async Task DeleteMimeMessageAsync(Guid accountId, Guid fileId) + { + var resourcePath = await GetMimeResourcePathAsync(accountId, fileId); + var completeFilePath = GetEMLPath(resourcePath); + + if (File.Exists(completeFilePath)) + { + try + { + File.Delete(completeFilePath); + + _logger.Information("Mime file deleted for {FileId}", fileId); + + return true; + } + catch (Exception ex) + { + _logger.Error(ex, "Could not delete mime file for {FileId}", fileId); + } + + return false; + } + + return true; + } + + public MailRenderModel GetMailRenderModel(MimeMessage message, string mimeLocalPath, MailRenderingOptions options = null) + { + var visitor = CreateHTMLPreviewVisitor(message, mimeLocalPath); + + string finalRenderHtml = visitor.HtmlBody; + + // Check whether we need to purify the generated HTML from visitor. + // No need to create HtmlDocument if not required. + + if (options != null && options.IsPurifyingNeeded()) + { + var document = new HtmlAgilityPack.HtmlDocument(); + document.LoadHtml(visitor.HtmlBody); + + // Clear src attribute. + + if (!options.LoadImages) + document.ClearImages(); + + if (!options.LoadStyles) + document.ClearStyles(); + + // Update final HTML. + finalRenderHtml = document.DocumentNode.OuterHtml; + } + + var renderingModel = new MailRenderModel(finalRenderHtml, options); + + // Create attachments. + + foreach (var attachment in visitor.Attachments) + { + if (attachment.IsAttachment && attachment is MimePart attachmentPart) + { + renderingModel.Attachments.Add(attachmentPart); + } + } + + // Check for List-Unsubscribe link if possible. + + if (message.Headers.Contains(HeaderId.ListUnsubscribe)) + { + renderingModel.UnsubscribeLink = message.Headers[HeaderId.ListUnsubscribe].Normalize(); + + // Sometimes this link is wrapped with < >, remove them. + if (renderingModel.UnsubscribeLink.StartsWith("<")) + { + renderingModel.UnsubscribeLink = renderingModel.UnsubscribeLink.Substring(1, renderingModel.UnsubscribeLink.Length - 2); + } + } + + return renderingModel; + } + + + } +} diff --git a/Wino.Core/Services/ProviderService.cs b/Wino.Core/Services/ProviderService.cs new file mode 100644 index 00000000..aba85d8f --- /dev/null +++ b/Wino.Core/Services/ProviderService.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using System.Linq; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Accounts; + +namespace Wino.Core.Services +{ + /// + /// Service that is returning available provider details. + /// + public class ProviderService : IProviderService + { + public IProviderDetail GetProviderDetail(MailProviderType type) + { + var details = GetProviderDetails(); + + return details.FirstOrDefault(a => a.Type == type); + } + + public List GetProviderDetails() + { + var providerList = new List(); + + var providers = new MailProviderType[] + { + MailProviderType.Outlook, + MailProviderType.Gmail, + MailProviderType.IMAP4 + }; + + foreach (var type in providers) + { + providerList.Add(new ProviderDetail(type)); + } + + return providerList; + } + } +} diff --git a/Wino.Core/Services/SignatureService.cs b/Wino.Core/Services/SignatureService.cs new file mode 100644 index 00000000..064b9df1 --- /dev/null +++ b/Wino.Core/Services/SignatureService.cs @@ -0,0 +1,91 @@ +using System; +using System.Threading.Tasks; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.Services +{ + public class SignatureService : BaseDatabaseService, ISignatureService + { + public SignatureService(IDatabaseService databaseService) : base(databaseService) { } + + public async Task CreateDefaultSignatureAsync(Guid accountId) + { + var account = await Connection.Table().FirstOrDefaultAsync(a => a.Id == accountId); + + var defaultSignature = GetDefaultSignature(); + + await Connection.InsertAsync(defaultSignature); + + account.SignatureId = defaultSignature.Id; + + await Connection.UpdateAsync(account); + + return defaultSignature; + } + + public async Task DeleteAccountSignatureAssignment(Guid accountId) + { + var existingSignature = await GetAccountSignatureAsync(accountId); + + if (existingSignature != null) + { + await Connection.DeleteAsync(existingSignature); + + var account = await Connection.Table().FirstOrDefaultAsync(a => a.Id == accountId); + + account.SignatureId = null; + + await Connection.UpdateAsync(account); + } + } + + public async Task GetAccountSignatureAsync(Guid accountId) + { + var account = await Connection.Table().FirstOrDefaultAsync(a => a.Id == accountId); + + if (account?.SignatureId == null) + return null; + + return await Connection.Table().FirstOrDefaultAsync(a => a.Id == account.SignatureId); + } + + public async Task UpdateAccountSignatureAsync(Guid accountId, string htmlBody) + { + var signature = await GetAccountSignatureAsync(accountId); + var account = await Connection.Table().FirstOrDefaultAsync(a => a.Id == accountId); + + if (signature == null) + { + signature = new AccountSignature() + { + Id = Guid.NewGuid(), + HtmlBody = htmlBody + }; + + await Connection.InsertAsync(signature); + } + else + { + signature.HtmlBody = htmlBody; + + await Connection.UpdateAsync(signature); + } + + account.SignatureId = signature.Id; + + await Connection.UpdateAsync(account); + + return signature; + } + + private AccountSignature GetDefaultSignature() + { + return new AccountSignature() + { + Id = Guid.NewGuid(), + HtmlBody = @"

Sent from Wino Mail for Windows

" + }; + } + } +} diff --git a/Wino.Core/Services/ThreadingStrategyProvider.cs b/Wino.Core/Services/ThreadingStrategyProvider.cs new file mode 100644 index 00000000..65ba8b10 --- /dev/null +++ b/Wino.Core/Services/ThreadingStrategyProvider.cs @@ -0,0 +1,32 @@ +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Integration.Threading; + +namespace Wino.Core.Services +{ + public class ThreadingStrategyProvider : IThreadingStrategyProvider + { + private readonly OutlookThreadingStrategy _outlookThreadingStrategy; + private readonly GmailThreadingStrategy _gmailThreadingStrategy; + private readonly ImapThreadStrategy _imapThreadStrategy; + + public ThreadingStrategyProvider(OutlookThreadingStrategy outlookThreadingStrategy, + GmailThreadingStrategy gmailThreadingStrategy, + ImapThreadStrategy imapThreadStrategy) + { + _outlookThreadingStrategy = outlookThreadingStrategy; + _gmailThreadingStrategy = gmailThreadingStrategy; + _imapThreadStrategy = imapThreadStrategy; + } + + public IThreadingStrategy GetStrategy(MailProviderType mailProviderType) + { + return mailProviderType switch + { + MailProviderType.Outlook or MailProviderType.Office365 => _outlookThreadingStrategy, + MailProviderType.Gmail => _gmailThreadingStrategy, + _ => _imapThreadStrategy, + }; + } + } +} diff --git a/Wino.Core/Services/ThumbnailService.cs b/Wino.Core/Services/ThumbnailService.cs new file mode 100644 index 00000000..bc32803a --- /dev/null +++ b/Wino.Core/Services/ThumbnailService.cs @@ -0,0 +1,64 @@ +using System; +using System.Linq; +using System.Net.Mail; + +namespace Wino.Core.Services +{ + public static class ThumbnailService + { + private static string[] knownCompanies = new string[] + { + "microsoft.com", "apple.com", "google.com", "steampowered.com", "airbnb.com", "youtube.com", "uber.com" + }; + + public static bool IsKnown(string mailHost) => !string.IsNullOrEmpty(mailHost) && knownCompanies.Contains(mailHost); + + public static string GetHost(string address) + { + if (string.IsNullOrEmpty(address)) + return string.Empty; + + if (address.Contains('@')) + { + var splitted = address.Split('@'); + + if (splitted.Length >= 2 && !string.IsNullOrEmpty(splitted[1])) + { + try + { + return new MailAddress(address).Host; + } + catch (Exception) + { + // TODO: Exceptions are ignored for now. + } + } + } + + return string.Empty; + } + + public static Tuple CheckIsKnown(string host) + { + // Check known hosts. + // Apply company logo if available. + + try + { + var last = host.Split('.'); + + if (last.Length > 2) + host = $"{last[last.Length - 2]}.{last[last.Length - 1]}"; + } + catch (Exception) + { + return new Tuple(false, host); + } + + return new Tuple(ThumbnailService.IsKnown(host), host); + } + + public static string GetKnownHostImage(string host) + => $"ms-appx:///Assets/Thumbnails/{host}.png"; + } +} diff --git a/Wino.Core/Services/TokenService.cs b/Wino.Core/Services/TokenService.cs new file mode 100644 index 00000000..175ea38d --- /dev/null +++ b/Wino.Core/Services/TokenService.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading.Tasks; +using Wino.Core.Domain.Entities; + +namespace Wino.Core.Services +{ + public interface ITokenService + { + Task GetTokenInformationAsync(Guid accountId); + Task SaveTokenInformationAsync(Guid accountId, TokenInformation tokenInformation); + } + + public class TokenService : BaseDatabaseService, ITokenService + { + public TokenService(IDatabaseService databaseService) : base(databaseService) { } + + public Task GetTokenInformationAsync(Guid accountId) + => Connection.Table().FirstOrDefaultAsync(a => a.AccountId == accountId); + + public async Task SaveTokenInformationAsync(Guid accountId, TokenInformation tokenInformation) + { + // Delete all tokens for this account. + await Connection.Table().DeleteAsync(a => a.AccountId == accountId); + + // Save new token info to the account. + tokenInformation.AccountId = accountId; + + await Connection.InsertOrReplaceAsync(tokenInformation); + } + } +} diff --git a/Wino.Core/Services/TranslationService.cs b/Wino.Core/Services/TranslationService.cs new file mode 100644 index 00000000..c383cf0d --- /dev/null +++ b/Wino.Core/Services/TranslationService.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.Messaging; +using Newtonsoft.Json; +using Serilog; +using Wino.Core.Domain; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Translations; +using Wino.Core.Messages.Shell; + +namespace Wino.Core.Services +{ + public class TranslationService : ITranslationService + { + public const AppLanguage DefaultAppLanguage = AppLanguage.English; + + private ILogger _logger = Log.ForContext(); + private readonly IPreferencesService _preferencesService; + private bool isInitialized = false; + + public TranslationService(IPreferencesService preferencesService) + { + _preferencesService = preferencesService; + } + + // Initialize default language with ignoring current language check. + public Task InitializeAsync() => InitializeLanguageAsync(_preferencesService.CurrentLanguage, ignoreCurrentLanguageCheck: true); + + public async Task InitializeLanguageAsync(AppLanguage language, bool ignoreCurrentLanguageCheck = false) + { + if (!ignoreCurrentLanguageCheck && _preferencesService.CurrentLanguage == language) + { + _logger.Warning("Changing language is ignored because current language and requested language are same."); + + return; + } + + if (ignoreCurrentLanguageCheck && isInitialized) return; + + var currentDictionary = Translator.Resources; + using var resourceStream = currentDictionary.GetLanguageStream(language); + + var stremValue = await new StreamReader(resourceStream).ReadToEndAsync().ConfigureAwait(false); + + var translationLookups = JsonConvert.DeserializeObject>(stremValue); + + // Insert new translation key-value pairs. + // Overwrite existing values for the same keys. + + foreach (var pair in translationLookups) + { + // Replace existing value. + if (currentDictionary.ContainsKey(pair.Key)) + { + currentDictionary[pair.Key] = pair.Value; + } + else + { + currentDictionary.Add(pair.Key, pair.Value); + } + } + + _preferencesService.CurrentLanguage = language; + + isInitialized = true; + WeakReferenceMessenger.Default.Send(new LanguageChanged()); + } + + public List GetAvailableLanguages() + => + [ + new AppLanguageModel(AppLanguage.English, "English"), + new AppLanguageModel(AppLanguage.Deutsch, "Deutsch"), + new AppLanguageModel(AppLanguage.Russian, "Russian"), + new AppLanguageModel(AppLanguage.Turkish, "Türkçe"), + new AppLanguageModel(AppLanguage.Polish, "Polski"), + new AppLanguageModel(AppLanguage.Czech, "Czech"), + new AppLanguageModel(AppLanguage.Spanish, "Spanish"), + new AppLanguageModel(AppLanguage.French, "French"), + ]; + } +} diff --git a/Wino.Core/Services/WinoRequestDelegator.cs b/Wino.Core/Services/WinoRequestDelegator.cs new file mode 100644 index 00000000..8aba36b9 --- /dev/null +++ b/Wino.Core/Services/WinoRequestDelegator.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.Messaging; +using Serilog; +using Wino.Core.Domain; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Exceptions; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Folders; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Requests; +using Wino.Core.Domain.Models.Synchronization; +using Wino.Core.Messages.Synchronization; +using Wino.Core.Requests; + +namespace Wino.Core.Services +{ + public class WinoRequestDelegator : IWinoRequestDelegator + { + private readonly IWinoRequestProcessor _winoRequestProcessor; + private readonly IWinoSynchronizerFactory _winoSynchronizerFactory; + private readonly IFolderService _folderService; + private readonly IDialogService _dialogService; + private readonly ILogger _logger = Log.ForContext(); + + public WinoRequestDelegator(IWinoRequestProcessor winoRequestProcessor, + IWinoSynchronizerFactory winoSynchronizerFactory, + IFolderService folderService, + IDialogService dialogService) + { + _winoRequestProcessor = winoRequestProcessor; + _winoSynchronizerFactory = winoSynchronizerFactory; + _folderService = folderService; + _dialogService = dialogService; + } + + public async Task ExecuteAsync(MailOperationPreperationRequest request) + { + var requests = new List(); + + try + { + requests = await _winoRequestProcessor.PrepareRequestsAsync(request); + } + catch (UnavailableSpecialFolderException unavailableSpecialFolderException) + { + _dialogService.InfoBarMessage(Translator.Info_MissingFolderTitle, + string.Format(Translator.Info_MissingFolderMessage, unavailableSpecialFolderException.SpecialFolderType), + InfoBarMessageType.Warning, + Translator.SettingConfigureSpecialFolders_Button, + () => + { + _dialogService.HandleSystemFolderConfigurationDialogAsync(unavailableSpecialFolderException.AccountId, _folderService); + }); + } + catch (InvalidMoveTargetException) + { + _dialogService.InfoBarMessage(Translator.Info_InvalidMoveTargetTitle, Translator.Info_InvalidMoveTargetMessage, InfoBarMessageType.Warning); + } + catch (NotImplementedException) + { + _dialogService.ShowNotSupportedMessage(); + } + catch (Exception ex) + { + Log.Error(ex, "Request creation failed."); + _dialogService.InfoBarMessage(Translator.Info_RequestCreationFailedTitle, ex.Message, InfoBarMessageType.Error); + } + + if (requests == null || !requests.Any()) return; + + var accountIds = requests.GroupBy(a => a.Item.AssignedAccount.Id); + + // Queue requests for each account and start synchronization. + foreach (var accountId in accountIds) + { + foreach (var accountRequest in accountId) + { + QueueRequest(accountRequest, accountId.Key); + } + + QueueSynchronization(accountId.Key); + } + } + + public async Task ExecuteAsync(FolderOperation operation, IMailItemFolder folderStructure) + { + IRequest request = null; + + try + { + request = await _winoRequestProcessor.PrepareFolderRequestAsync(operation, folderStructure); + } + catch (NotImplementedException) + { + _dialogService.ShowNotSupportedMessage(); + } + catch (Exception ex) + { + Log.Error(ex, "Folder operation execution failed."); + } + + // _synchronizationWorker.Queue(request); + } + + public Task ExecuteAsync(DraftPreperationRequest draftPreperationRequest) + { + var request = new CreateDraftRequest(draftPreperationRequest); + + QueueRequest(request, draftPreperationRequest.Account.Id); + QueueSynchronization(draftPreperationRequest.Account.Id); + + return Task.CompletedTask; + } + + public Task ExecuteAsync(SendDraftPreparationRequest sendDraftPreperationRequest) + { + var request = new SendDraftRequest(sendDraftPreperationRequest); + + QueueRequest(request, sendDraftPreperationRequest.MailItem.AssignedAccount.Id); + QueueSynchronization(sendDraftPreperationRequest.MailItem.AssignedAccount.Id); + + return Task.CompletedTask; + } + + private void QueueRequest(IRequest request, Guid accountId) + { + var synchronizer = _winoSynchronizerFactory.GetAccountSynchronizer(accountId); + + if (synchronizer == null) + { + _logger.Warning("Synchronizer not found for account {AccountId}.", accountId); + _logger.Warning("Skipping queueing request {Operation}.", request.Operation); + + return; + } + + synchronizer.QueueRequest(request); + } + + private void QueueSynchronization(Guid accountId) + { + var options = new SynchronizationOptions() + { + AccountId = accountId, + Type = SynchronizationType.ExecuteRequests + }; + + WeakReferenceMessenger.Default.Send(new NewSynchronizationRequested(options)); + } + } +} diff --git a/Wino.Core/Services/WinoRequestProcessor.cs b/Wino.Core/Services/WinoRequestProcessor.cs new file mode 100644 index 00000000..598395ff --- /dev/null +++ b/Wino.Core/Services/WinoRequestProcessor.cs @@ -0,0 +1,239 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Exceptions; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Folders; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Requests; +using Wino.Core.Requests; + +namespace Wino.Core.Services +{ + /// + /// Intermediary processor for converting a user action to executable Wino requests. + /// Primarily responsible for batching requests by AccountId and FolderId. + /// + public class WinoRequestProcessor : BaseDatabaseService, IWinoRequestProcessor + { + private readonly IFolderService _folderService; + private readonly IKeyPressService _keyPressService; + private readonly IPreferencesService _preferencesService; + private readonly IAccountService _accountService; + private readonly IDialogService _dialogService; + + /// + /// Set of rules that defines which action should be executed if user wants to toggle an action. + /// + private readonly List _toggleRequestRules = + [ + new ToggleRequestRule(MailOperation.MarkAsRead, MailOperation.MarkAsUnread, new System.Func((item) => item.IsRead)), + new ToggleRequestRule(MailOperation.MarkAsUnread, MailOperation.MarkAsRead, new System.Func((item) => !item.IsRead)), + new ToggleRequestRule(MailOperation.SetFlag, MailOperation.ClearFlag, new System.Func((item) => item.IsFlagged)), + new ToggleRequestRule(MailOperation.ClearFlag, MailOperation.SetFlag, new System.Func((item) => !item.IsFlagged)), + ]; + + public WinoRequestProcessor(IDatabaseService databaseService, + IFolderService folderService, + IKeyPressService keyPressService, + IPreferencesService preferencesService, + IAccountService accountService, + IDialogService dialogService) : base(databaseService) + { + _folderService = folderService; + _keyPressService = keyPressService; + _preferencesService = preferencesService; + _accountService = accountService; + _dialogService = dialogService; + } + + public async Task> PrepareRequestsAsync(MailOperationPreperationRequest preperationRequest) + { + var action = preperationRequest.Action; + var moveTargetStructure = preperationRequest.MoveTargetFolder; + + // Ask confirmation for permanent delete operation. + // Drafts are always hard deleted without any protection. + + if (!preperationRequest.IgnoreHardDeleteProtection && ((action == MailOperation.SoftDelete && _keyPressService.IsShiftKeyPressed()) || action == MailOperation.HardDelete)) + { + if (_preferencesService.IsHardDeleteProtectionEnabled) + { + var shouldDelete = await _dialogService.ShowHardDeleteConfirmationAsync(); + + if (!shouldDelete) return default; + } + + action = MailOperation.HardDelete; + } + + // Make sure there is a move target folder if action is move. + // Let user pick a folder to move from the dialog. + + if (action == MailOperation.Move && moveTargetStructure == null) + { + // TODO: Handle multiple accounts for move operation. + // What happens if we move 2 different mails from 2 different accounts? + + var accountId = preperationRequest.MailItems.FirstOrDefault().AssignedAccount.Id; + + moveTargetStructure = await _dialogService.PickFolderAsync(accountId, PickFolderReason.Move, _folderService); + + if (moveTargetStructure == null) + return default; + } + + var requests = new List(); + + foreach (var item in preperationRequest.MailItems) + { + requests.Add(await GetSingleRequestAsync(item, action, moveTargetStructure, preperationRequest.ToggleExecution)); + } + + return requests; + } + + private async Task GetSingleRequestAsync(MailCopy mailItem, MailOperation action, IMailItemFolder moveTargetStructure, bool shouldToggleActions) + { + if (mailItem.AssignedAccount == null) throw new ArgumentException(Translator.Exception_NullAssignedAccount); + if (mailItem.AssignedFolder == null) throw new ArgumentException(Translator.Exception_NullAssignedFolder); + + // Rule: Soft deletes from Trash folder must perform Hard Delete. + if (action == MailOperation.SoftDelete && mailItem.AssignedFolder.SpecialFolderType == SpecialFolderType.Deleted) + action = MailOperation.HardDelete; + + // Rule: SoftDelete draft items must be performed as hard delete. + if (action == MailOperation.SoftDelete && mailItem.IsDraft) + action = MailOperation.HardDelete; + + // Rule: Toggle actions must be reverted if ToggleExecution is passed true. + if (shouldToggleActions) + { + var toggleRule = _toggleRequestRules.Find(a => a.SourceAction == action); + + if (toggleRule != null && toggleRule.Condition(mailItem)) + { + action = toggleRule.TargetAction; + } + } + + if (action == MailOperation.MarkAsRead) + return new MarkReadRequest(mailItem, true); + else if (action == MailOperation.MarkAsUnread) + return new MarkReadRequest(mailItem, false); + else if (action == MailOperation.SetFlag) + return new ChangeFlagRequest(mailItem, true); + else if (action == MailOperation.ClearFlag) + return new ChangeFlagRequest(mailItem, false); + else if (action == MailOperation.HardDelete) + return new DeleteRequest(mailItem); + else if (action == MailOperation.Move) + { + if (moveTargetStructure == null) + throw new InvalidMoveTargetException(); + + // TODO + // Rule: You can't move items to non-move target folders; + // Rule: You can't move items from a folder to itself. + + //if (!moveTargetStructure.IsMoveTarget || moveTargetStructure.FolderId == mailItem.AssignedFolder.Id) + // throw new InvalidMoveTargetException(); + + var pickedFolderItem = await _folderService.GetFolderAsync(moveTargetStructure.Id); + + return new MoveRequest(mailItem, mailItem.AssignedFolder, pickedFolderItem); + } + else if (action == MailOperation.Archive) + { + // Validate archive folder exists. + + var archiveFolder = await _folderService.GetSpecialFolderByAccountIdAsync(mailItem.AssignedAccount.Id, SpecialFolderType.Archive) + ?? throw new UnavailableSpecialFolderException(SpecialFolderType.Archive, mailItem.AssignedAccount.Id); + + return new MoveRequest(mailItem, mailItem.AssignedFolder, archiveFolder); + } + else if (action == MailOperation.UnArchive || action == MailOperation.MarkAsNotJunk) + { + var inboxFolder = await _folderService.GetSpecialFolderByAccountIdAsync(mailItem.AssignedAccount.Id, SpecialFolderType.Inbox) + ?? throw new UnavailableSpecialFolderException(SpecialFolderType.Inbox, mailItem.AssignedAccount.Id); + + return new MoveRequest(mailItem, mailItem.AssignedFolder, inboxFolder); + } + else if (action == MailOperation.SoftDelete) + { + var trashFolder = await _folderService.GetSpecialFolderByAccountIdAsync(mailItem.AssignedAccount.Id, SpecialFolderType.Deleted) + ?? throw new UnavailableSpecialFolderException(SpecialFolderType.Deleted, mailItem.AssignedAccount.Id); + + return new MoveRequest(mailItem, mailItem.AssignedFolder, trashFolder); + } + else if (action == MailOperation.MoveToJunk) + { + var junkFolder = await _folderService.GetSpecialFolderByAccountIdAsync(mailItem.AssignedAccount.Id, SpecialFolderType.Junk) + ?? throw new UnavailableSpecialFolderException(SpecialFolderType.Junk, mailItem.AssignedAccount.Id); + + return new MoveRequest(mailItem, mailItem.AssignedFolder, junkFolder); + } + else if (action == MailOperation.AlwaysMoveToFocused || action == MailOperation.AlwaysMoveToOther) + return new AlwaysMoveToRequest(mailItem, action == MailOperation.AlwaysMoveToFocused); + else + throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedAction, action)); + } + + public async Task PrepareFolderRequestAsync(FolderOperation operation, IMailItemFolder mailItemFolder) + { + if (mailItemFolder == null) return default; + + var accountId = mailItemFolder.MailAccountId; + + IRequest change = null; + + switch (operation) + { + case FolderOperation.Pin: + case FolderOperation.Unpin: + await _folderService.ChangeStickyStatusAsync(mailItemFolder.Id, operation == FolderOperation.Pin); + break; + //case FolderOperation.MarkAllAsRead: + // // Get all mails in the folder. + + // var mailItems = await _folderService.GetAllUnreadItemsByFolderIdAsync(accountId, folderStructure.RemoteFolderId).ConfigureAwait(false); + + // if (mailItems.Any()) + // change = new FolderMarkAsReadRequest(accountId, mailItems.Select(a => a.Id).Distinct(), folderStructure.RemoteFolderId, folderStructure.FolderId); + + // break; + //case FolderOperation.Empty: + // // Get all mails in the folder. + + // var mailsToDelete = await _folderService.GetMailByFolderIdAsync(folderStructure.FolderId).ConfigureAwait(false); + + // if (mailsToDelete.Any()) + // change = new FolderEmptyRequest(accountId, mailsToDelete.Select(a => a.Id).Distinct(), folderStructure.RemoteFolderId, folderStructure.FolderId); + + // break; + //case FolderOperation.Rename: + // var newFolderName = await _dialogService.ShowRenameFolderDialogAsync(folderStructure.FolderName); + + // if (!string.IsNullOrEmpty(newFolderName)) + // change = new RenameFolderRequest(accountId, folderStructure.RemoteFolderId, folderStructure.FolderId, newFolderName, folderStructure.FolderName); + + // break; + //case FolderOperation.Delete: + // var isConfirmed = await _dialogService.ShowConfirmationDialogAsync($"'{folderStructure.FolderName}' is going to be deleted. Do you want to continue?", "Are you sure?", "Yes delete."); + + // if (isConfirmed) + // change = new DeleteFolderRequest(accountId, folderStructure.RemoteFolderId, folderStructure.FolderId); + + // break; + //default: + // throw new NotImplementedException(); + } + + return change; + } + } +} diff --git a/Wino.Core/Synchronizers/BaseSynchronizer.cs b/Wino.Core/Synchronizers/BaseSynchronizer.cs new file mode 100644 index 00000000..bd97d7c0 --- /dev/null +++ b/Wino.Core/Synchronizers/BaseSynchronizer.cs @@ -0,0 +1,331 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.Messaging; +using MailKit; +using Serilog; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Synchronization; +using Wino.Core.Integration; +using Wino.Core.Messages.Mails; +using Wino.Core.Messages.Synchronization; +using Wino.Core.Misc; +using Wino.Core.Requests; + +namespace Wino.Core.Synchronizers +{ + public interface IBaseSynchronizer + { + /// + /// Account that is assigned for this synchronizer. + /// + MailAccount Account { get; } + + /// + /// Synchronizer state. + /// + AccountSynchronizerState State { get; } + + /// + /// Queues a single request to be executed in the next synchronization. + /// + /// Request to queue. + void QueueRequest(IRequestBase request); + + /// + /// TODO + /// + /// Whether active synchronization is stopped or not. + bool CancelActiveSynchronization(); + + /// + /// Performs a full synchronization with the server with given options. + /// This will also prepares batch requests for execution. + /// Requests are executed in the order they are queued and happens before the synchronization. + /// Result of the execution queue is processed during the synchronization. + /// + /// Options for synchronization. + /// Cancellation token. + /// Result summary of synchronization. + Task SynchronizeAsync(SynchronizationOptions options, CancellationToken cancellationToken = default); + + /// + /// Downloads a single MIME message from the server and saves it to disk. + /// + /// Mail item to download from server. + /// Optional progress reporting for download operation. + /// Cancellation token. + Task DownloadMissingMimeMessageAsync(IMailItem mailItem, ITransferProgress transferProgress, CancellationToken cancellationToken = default); + } + + public abstract class BaseSynchronizer : BaseMailIntegrator, IBaseSynchronizer + { + private SemaphoreSlim synchronizationSemaphore = new(1); + private CancellationToken activeSynchronizationCancellationToken; + + protected ConcurrentBag changeRequestQueue = []; + protected ILogger Logger = Log.ForContext>(); + + protected BaseSynchronizer(MailAccount account) + { + Account = account; + } + + public MailAccount Account { get; } + + private AccountSynchronizerState state; + public AccountSynchronizerState State + { + get { return state; } + private set + { + state = value; + + WeakReferenceMessenger.Default.Send(new AccountSynchronizerStateChanged(this, value)); + } + } + + /// + /// Queues a single request to be executed in the next synchronization. + /// + /// Request to execute. + public void QueueRequest(IRequestBase request) => changeRequestQueue.Add(request); + + /// + /// Creates a new Wino Mail Item package out of native message type with full Mime. + /// + /// Native message type for the synchronizer. + /// Cancellation token + /// Package that encapsulates downloaded Mime and additional information for adding new mail. + public abstract Task> CreateNewMailPackagesAsync(TMessageType message, MailItemFolder assignedFolder, CancellationToken cancellationToken = default); + + /// + /// Runs existing queued requests in the queue. + /// + /// Batched requests to execute. Integrator methods will only receive batched requests. + /// Cancellation token + public abstract Task ExecuteNativeRequestsAsync(IEnumerable> batchedRequests, CancellationToken cancellationToken = default); + + public abstract Task SynchronizeInternalAsync(SynchronizationOptions options, CancellationToken cancellationToken = default); + + public async Task SynchronizeAsync(SynchronizationOptions options, CancellationToken cancellationToken = default) + { + try + { + activeSynchronizationCancellationToken = cancellationToken; + + var batches = CreateBatchRequests().Distinct(); + + if (batches.Any()) + { + Logger.Information($"{batches?.Count() ?? 0} batched requests"); + + State = AccountSynchronizerState.ExecutingRequests; + + var nativeRequests = CreateNativeRequestBundles(batches); + + Console.WriteLine($"Prepared {nativeRequests.Count()} native requests"); + + await ExecuteNativeRequestsAsync(nativeRequests, activeSynchronizationCancellationToken); + + PublishUnreadItemChanges(); + + // Execute request sync options should be re-calculated after execution. + // This is the part we decide which individual folders must be synchronized + // after the batch request execution. + if (options.Type == SynchronizationType.ExecuteRequests) + options = GetSynchronizationOptionsAfterRequestExecution(batches); + } + + State = AccountSynchronizerState.Synchronizing; + + await synchronizationSemaphore.WaitAsync(activeSynchronizationCancellationToken); + + // Start the internal synchronization. + var synchronizationResult = await SynchronizeInternalAsync(options, activeSynchronizationCancellationToken).ConfigureAwait(false); + + PublishUnreadItemChanges(); + + return synchronizationResult; + } + catch (OperationCanceledException) + { + Logger.Warning("Synchronization cancelled."); + return SynchronizationResult.Canceled; + } + catch (Exception) + { + // Disable maybe? + throw; + } + finally + { + // Reset account progress to hide the progress. + options.ProgressListener?.AccountProgressUpdated(Account.Id, 0); + + State = AccountSynchronizerState.Idle; + synchronizationSemaphore.Release(); + } + } + + /// + /// Updates unread item counts for some folders and account. + /// Sends a message that shell can pick up and update the UI. + /// + private void PublishUnreadItemChanges() + => WeakReferenceMessenger.Default.Send(new RefreshUnreadCountsMessage(Account.Id)); + + /// + /// 1. Group all requests by operation type. + /// 2. Group all individual operation type requests with equality check. + /// Equality comparison in the records are done with RequestComparer + /// to ignore Item property. Each request can have their own logic for comparison. + /// For example, move requests for different mails from the same folder to the same folder + /// must be dispatched in the same batch. This is much faster for the server. Specially IMAP + /// since all folders must be asynchronously opened/closed. + /// + /// Batch request collection for all these single requests. + private List CreateBatchRequests() + { + var batchList = new List(); + var comparer = new RequestComparer(); + + while (changeRequestQueue.Count > 0) + { + if (changeRequestQueue.TryPeek(out IRequestBase request)) + { + // Mail request, must be batched. + if (request is IRequest mailRequest) + { + var equalItems = changeRequestQueue + .Where(a => a is IRequest && comparer.Equals(a, request)) + .Cast() + .ToList(); + + batchList.Add(mailRequest.CreateBatch(equalItems)); + + // Remove these items from the queue. + foreach (var item in equalItems) + { + changeRequestQueue.TryTake(out _); + } + } + else + batchList.Add(request); + } + } + + return batchList; + } + + /// + /// Converts batched requests into HTTP/Task calls that derived synchronizers can execute. + /// + /// Batch requests to be converted. + /// Collection of native requests for individual synchronizer type. + private IEnumerable> CreateNativeRequestBundles(IEnumerable batchChangeRequests) + { + IEnumerable>> GetNativeRequests() + { + foreach (var item in batchChangeRequests) + { + switch (item.Operation) + { + case MailSynchronizerOperation.Send: + yield return SendDraft((BatchSendDraftRequestRequest)item); + break; + case MailSynchronizerOperation.MarkRead: + yield return MarkRead((BatchMarkReadRequest)item); + break; + case MailSynchronizerOperation.Move: + yield return Move((BatchMoveRequest)item); + break; + case MailSynchronizerOperation.Delete: + yield return Delete((BatchDeleteRequest)item); + break; + case MailSynchronizerOperation.ChangeFlag: + yield return ChangeFlag((BatchChangeFlagRequest)item); + break; + case MailSynchronizerOperation.AlwaysMoveTo: + yield return AlwaysMoveTo((BatchAlwaysMoveToRequest)item); + break; + case MailSynchronizerOperation.MoveToFocused: + yield return MoveToFocused((BatchMoveToFocusedRequest)item); + break; + case MailSynchronizerOperation.CreateDraft: + yield return CreateDraft((BatchCreateDraftRequest)item); + break; + } + } + }; + + return GetNativeRequests().SelectMany(collections => collections); + } + + /// + /// Attempts to find out the best possible synchronization options after the batch request execution. + /// + /// Batch requests to run in synchronization. + /// New synchronization options with minimal HTTP effort. + private SynchronizationOptions GetSynchronizationOptionsAfterRequestExecution(IEnumerable batches) + { + // TODO: Check folders only. + var batchItems = batches.Where(a => a is IBatchChangeRequest).Cast(); + + var requests = batchItems.SelectMany(a => a.Items); + + var options = new SynchronizationOptions() + { + AccountId = Account.Id, + Type = SynchronizationType.FoldersOnly + }; + + bool isCustomSynchronization = requests.All(a => a is ICustomFolderSynchronizationRequest); + + if (isCustomSynchronization) + { + // Gather FolderIds to synchronize. + + options.Type = SynchronizationType.Custom; + options.SynchronizationFolderIds = requests.Cast().SelectMany(a => a.SynchronizationFolderIds).ToList(); + } + else + { + // At this point it's a mix of everything. Do full sync. + options.Type = SynchronizationType.Full; + } + + return options; + } + + public virtual IEnumerable> Move(BatchMoveRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType())); + public virtual IEnumerable> ChangeFlag(BatchChangeFlagRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType())); + public virtual IEnumerable> MarkRead(BatchMarkReadRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType())); + public virtual IEnumerable> Delete(BatchDeleteRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType())); + public virtual IEnumerable> AlwaysMoveTo(BatchAlwaysMoveToRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType())); + public virtual IEnumerable> MoveToFocused(BatchMoveToFocusedRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType())); + public virtual IEnumerable> CreateDraft(BatchCreateDraftRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType())); + public virtual IEnumerable> SendDraft(BatchSendDraftRequestRequest request) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType())); + + /// + /// Downloads a single missing message from synchronizer and saves it to given FileId from IMailItem. + /// + /// Mail item that its mime file does not exist on the disk. + /// Optional download progress for IMAP synchronizer. + /// Cancellation token. + public virtual Task DownloadMissingMimeMessageAsync(IMailItem mailItem, ITransferProgress transferProgress = null, CancellationToken cancellationToken = default) => throw new NotSupportedException(string.Format(Translator.Exception_UnsupportedSynchronizerOperation, this.GetType())); + + public bool CancelActiveSynchronization() + { + // TODO: What if account is deleted during synchronization? + return true; + } + } +} diff --git a/Wino.Core/Synchronizers/GmailSynchronizer.cs b/Wino.Core/Synchronizers/GmailSynchronizer.cs new file mode 100644 index 00000000..217ad727 --- /dev/null +++ b/Wino.Core/Synchronizers/GmailSynchronizer.cs @@ -0,0 +1,917 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Google.Apis.Gmail.v1; +using Google.Apis.Gmail.v1.Data; +using Google.Apis.Http; +using Google.Apis.Requests; +using Google.Apis.Services; +using MailKit; +using Microsoft.IdentityModel.Tokens; +using MimeKit; +using MoreLinq; +using Serilog; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Exceptions; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Requests; +using Wino.Core.Domain.Models.Synchronization; +using Wino.Core.Extensions; +using Wino.Core.Http; +using Wino.Core.Integration.Processors; +using Wino.Core.Requests; + +namespace Wino.Core.Synchronizers +{ + public class GmailSynchronizer : BaseSynchronizer, IHttpClientFactory + { + public override uint BatchModificationSize => 1000; + public override uint InitialMessageDownloadCountPerFolder => 1200; + + // It's actually 100. But Gmail SDK has internal bug for Out of Memory exception. + // https://github.com/googleapis/google-api-dotnet-client/issues/2603 + private const uint MaximumAllowedBatchRequestSize = 10; + + private readonly ConfigurableHttpClient _gmailHttpClient; + private readonly GmailService _gmailService; + private readonly IAuthenticator _authenticator; + private readonly IDefaultChangeProcessor _gmailChangeProcessor; + private readonly ILogger _logger = Log.ForContext(); + + public GmailSynchronizer(MailAccount account, + IAuthenticator authenticator, + IDefaultChangeProcessor gmailChangeProcessor) : base(account) + { + var messageHandler = new GmailClientMessageHandler(() => _authenticator.GetTokenAsync(Account)); + + var initializer = new BaseClientService.Initializer() + { + HttpClientFactory = this + }; + + _gmailHttpClient = new ConfigurableHttpClient(messageHandler); + _gmailService = new GmailService(initializer); + _authenticator = authenticator; + _gmailChangeProcessor = gmailChangeProcessor; + } + + public ConfigurableHttpClient CreateHttpClient(CreateHttpClientArgs args) => _gmailHttpClient; + + public override async Task SynchronizeInternalAsync(SynchronizationOptions options, CancellationToken cancellationToken = default) + { + _logger.Information("Internal synchronization started for {Name}", Account.Name); + + // Gmail must always synchronize folders before because it doesn't have a per-folder sync. + bool shouldSynchronizeFolders = true; + + if (shouldSynchronizeFolders) + { + _logger.Information("Synchronizing folders for {Name}", Account.Name); + + await SynchronizeFoldersAsync(cancellationToken).ConfigureAwait(false); + + _logger.Information("Synchronizing folders for {Name} is completed", Account.Name); + } + + // There is no specific folder synchronization in Gmail. + // Therefore we need to stop the synchronization at this point + // if type is only folder metadata sync. + + if (options.Type == SynchronizationType.FoldersOnly) return SynchronizationResult.Empty; + + cancellationToken.ThrowIfCancellationRequested(); + + bool isInitialSync = string.IsNullOrEmpty(Account.SynchronizationDeltaIdentifier); + + _logger.Debug("Is initial synchronization: {IsInitialSync}", isInitialSync); + + var missingMessageIds = new List(); + + var deltaChanges = new List(); // For tracking delta changes. + var listChanges = new List(); // For tracking initial sync changes. + + /* Processing flow order is important to preserve the validity of history. + * 1 - Process added mails. Because we need to create the mail first before assigning it to labels. + * 2 - Process label assignments. + * 3 - Process removed mails. + * This affects reporting progres if done individually for each history change. + * Therefore we need to process all changes in one go after the fetch. + */ + + if (isInitialSync) + { + // Initial synchronization. + // Google sends message id and thread id in this query. + // We'll collect them and send a Batch request to get details of the messages. + + var messageRequest = _gmailService.Users.Messages.List("me"); + + // Gmail doesn't do per-folder sync. So our per-folder count is the same as total message count. + messageRequest.MaxResults = InitialMessageDownloadCountPerFolder; + messageRequest.IncludeSpamTrash = true; + + ListMessagesResponse result = null; + + string nextPageToken = string.Empty; + + while (true) + { + if (!string.IsNullOrEmpty(nextPageToken)) + { + messageRequest.PageToken = nextPageToken; + } + + result = await messageRequest.ExecuteAsync(cancellationToken); + + nextPageToken = result.NextPageToken; + + listChanges.Add(result); + + // Nothing to fetch anymore. Break the loop. + if (nextPageToken == null) + break; + } + } + else + { + var startHistoryId = ulong.Parse(Account.SynchronizationDeltaIdentifier); + var nextPageToken = ulong.Parse(Account.SynchronizationDeltaIdentifier).ToString(); + + var historyRequest = _gmailService.Users.History.List("me"); + historyRequest.StartHistoryId = startHistoryId; + + while (!string.IsNullOrEmpty(nextPageToken)) + { + // If this is the first delta check, start from the last history id. + // Otherwise start from the next page token. We set them both to the same value for start. + // For each different page we set the page token to the next page token. + + bool isFirstDeltaCheck = nextPageToken == startHistoryId.ToString(); + + if (!isFirstDeltaCheck) + historyRequest.PageToken = nextPageToken; + + var historyResponse = await historyRequest.ExecuteAsync(cancellationToken); + + nextPageToken = historyResponse.NextPageToken; + + if (historyResponse.History == null) + continue; + + deltaChanges.Add(historyResponse); + } + } + + // Add initial message ids from initial sync. + missingMessageIds.AddRange(listChanges.Where(a => a.Messages != null).SelectMany(a => a.Messages).Select(a => a.Id)); + + // Add missing message ids from delta changes. + foreach (var historyResponse in deltaChanges) + { + var addedMessageIds = historyResponse.History + .Where(a => a.MessagesAdded != null) + .SelectMany(a => a.MessagesAdded) + .Where(a => a.Message != null) + .Select(a => a.Message.Id); + + missingMessageIds.AddRange(addedMessageIds); + } + + // Consolidate added/deleted elements. + // For example: History change might report downloading a mail first, then deleting it in another history change. + // In that case, downloading mail will return entity not found error. + // Plus, it's a redundant download the mail. + // Purge missing message ids from potentially deleted mails to prevent this. + + var messageDeletedHistoryChanges = deltaChanges + .Where(a => a.History != null) + .SelectMany(a => a.History) + .Where(a => a.MessagesDeleted != null) + .SelectMany(a => a.MessagesDeleted); + + var deletedMailIdsInHistory = messageDeletedHistoryChanges.Select(a => a.Message.Id); + + if (deletedMailIdsInHistory.Any()) + { + var mailIdsToConsolidate = missingMessageIds.Where(a => deletedMailIdsInHistory.Contains(a)).ToList(); + + int consolidatedMessageCount = missingMessageIds.RemoveAll(a => deletedMailIdsInHistory.Contains(a)); + + if (consolidatedMessageCount > 0) + { + // TODO: Also delete the history changes that are related to these mails. + // This will prevent unwanted logs and additional queries to look for them in processing. + + _logger.Information($"Purged {consolidatedMessageCount} missing mail downloads. ({string.Join(",", mailIdsToConsolidate)})"); + } + } + + // Start downloading missing messages. + await BatchDownloadMessagesAsync(missingMessageIds, options.ProgressListener, cancellationToken).ConfigureAwait(false); + + // Map remote drafts to local drafts. + await MapDraftIdsAsync(cancellationToken).ConfigureAwait(false); + + // Start processing delta changes. + foreach (var historyResponse in deltaChanges) + { + await ProcessHistoryChangesAsync(historyResponse).ConfigureAwait(false); + } + + // Take the max history id from delta changes and update the account sync modifier. + var maxHistoryId = deltaChanges.Max(a => a.HistoryId); + + if (maxHistoryId != null) + { + // TODO: This is not good. Centralize the identifier fetch and prevent direct access here. + Account.SynchronizationDeltaIdentifier = await _gmailChangeProcessor.UpdateAccountDeltaSynchronizationIdentifierAsync(Account.Id, maxHistoryId.ToString()).ConfigureAwait(false); + + _logger.Debug("Final sync identifier {SynchronizationDeltaIdentifier}", Account.SynchronizationDeltaIdentifier); + } + + // Get all unred new downloaded items and return in the result. + // This is primarily used in notifications. + + var unreadNewItems = await _gmailChangeProcessor.GetDownloadedUnreadMailsAsync(Account.Id, missingMessageIds).ConfigureAwait(false); + + return SynchronizationResult.Completed(unreadNewItems); + } + + private async Task SynchronizeFoldersAsync(CancellationToken cancellationToken = default) + { + var folderRequest = _gmailService.Users.Labels.List("me"); + + var labelsResponse = await folderRequest.ExecuteAsync(cancellationToken).ConfigureAwait(false); + + if (labelsResponse.Labels == null) + { + _logger.Warning("No folders found for {Name}", Account.Name); + return; + } + + _logger.Debug($"Gmail folders found: {string.Join(",", labelsResponse.Labels.Select(a => a.Name))}"); + + // Gmail has special Categories parent folder. + var categoriesFolder = new MailItemFolder() + { + SpecialFolderType = SpecialFolderType.Category, + MailAccountId = Account.Id, + FolderName = "Categories", + IsSynchronizationEnabled = false, + IsSticky = true, + Id = Guid.NewGuid(), + RemoteFolderId = "Categories" + }; + + var initializedFolders = new List() { categoriesFolder }; + + foreach (var label in labelsResponse.Labels) + { + var localFolder = label.GetLocalFolder(Account.Id); + + localFolder.MailAccountId = Account.Id; + + initializedFolders.Add(localFolder); + } + + // 1. Create parent-child relations but first order by name desc to be able to not mess up FolderNames in memory. + // 2. Mark special folder types that belong to categories folder. + + var ordered = initializedFolders.OrderByDescending(a => a.FolderName); + + // TODO: This can be refactored better. + foreach (var label in ordered) + { + // Gmail categorizes sub-labels by '/' For example: + // ParentTest/SubTest in the name of label means + // SubTest is a sub-label of ParentTest label. + + if (label.SpecialFolderType == SpecialFolderType.Promotions || + label.SpecialFolderType == SpecialFolderType.Updates || + label.SpecialFolderType == SpecialFolderType.Social || + label.SpecialFolderType == SpecialFolderType.Forums || + label.SpecialFolderType == SpecialFolderType.Personal) + { + label.ParentRemoteFolderId = categoriesFolder.RemoteFolderId; + + // These folders can not be a sub folder. + continue; + } + + var isSubFolder = label.FolderName.Contains("/"); + + if (isSubFolder) + { + var splittedFolderName = label.FolderName.Split('/'); + var partCount = splittedFolderName.Length; + + if (partCount > 1) + { + // Only make the last part connection since other relations will build up in the loop. + + var realChildFolderName = splittedFolderName[splittedFolderName.Length - 1]; + + var childFolder = initializedFolders.Find(a => a.FolderName.EndsWith(realChildFolderName)); + + string GetParentFolderName(string[] parts) + { + var builder = new StringBuilder(); + + for (int i = 0; i < parts.Length - 1; i++) + { + builder.Append(parts[i]); + + if (i != parts.Length - 2) + builder.Append("/"); + } + + return builder.ToString(); + } + + var parentFolderName = GetParentFolderName(splittedFolderName); + + var parentFolder = initializedFolders.Find(a => a.FolderName == parentFolderName); + + if (childFolder != null && parentFolder != null) + { + childFolder.FolderName = realChildFolderName; + childFolder.ParentRemoteFolderId = parentFolder.RemoteFolderId; + } + } + } + } + + await _gmailChangeProcessor.UpdateFolderStructureAsync(Account.Id, initializedFolders).ConfigureAwait(false); + } + + /// + /// Returns a single get request to retrieve the raw message with the given id + /// + /// Message to download. + /// Get request for raw mail. + private UsersResource.MessagesResource.GetRequest CreateSingleMessageGet(string messageId) + { + var singleRequest = _gmailService.Users.Messages.Get("me", messageId); + singleRequest.Format = UsersResource.MessagesResource.GetRequest.FormatEnum.Raw; + + return singleRequest; + } + + /// + /// Downloads given message ids per batch and processes them. + /// + /// Gmail message ids to download. + /// Cancellation token. + private async Task BatchDownloadMessagesAsync(IEnumerable messageIds, ISynchronizationProgress progressListener = null, CancellationToken cancellationToken = default) + { + var totalDownloadCount = messageIds.Count(); + + if (totalDownloadCount == 0) return; + + var downloadedItemCount = 0; + + _logger.Debug("Batch downloading {Count} messages for {Name}", messageIds.Count(), Account.Name); + + var allDownloadRequests = messageIds.Select(CreateSingleMessageGet); + + // Respect the batch size limit for batch requests. + var batchedDownloadRequests = allDownloadRequests.Batch((int)MaximumAllowedBatchRequestSize); + + _logger.Debug("Total items to download: {TotalDownloadCount}. Created {Count} batch download requests for {Name}.", batchedDownloadRequests.Count(), Account.Name, totalDownloadCount); + + // Gmail SDK's BatchRequest has Action delegate for callback, not Task. + // Therefore it's not possible to make sure that downloaded item is processed in the database before this + // async callback is finished. Therefore we need to wrap all local database processings into task list and wait all of them to finish + // Batch execution finishes after response parsing is done. + + var batchProcessCallbacks = new List(); + + foreach (var batchBundle in batchedDownloadRequests) + { + cancellationToken.ThrowIfCancellationRequested(); + + var batchRequest = new BatchRequest(_gmailService); + + // Queue each request into this batch. + batchBundle.ForEach(request => + { + batchRequest.Queue(request, (content, error, index, message) => + { + var downloadingMessageId = messageIds.ElementAt(index); + + batchProcessCallbacks.Add(HandleSingleItemDownloadedCallbackAsync(content, error, downloadingMessageId, cancellationToken)); + + downloadedItemCount++; + + var progressValue = downloadedItemCount * 100 / Math.Max(1, totalDownloadCount); + + progressListener?.AccountProgressUpdated(Account.Id, progressValue); + }); + }); + + _logger.Information("Executing batch download with {Count} items.", batchRequest.Count); + + await batchRequest.ExecuteAsync(cancellationToken); + + // This is important due to bug in Gmail SDK. + // We force GC here to prevent Out of Memory exception. + // https://github.com/googleapis/google-api-dotnet-client/issues/2603 + + GC.Collect(); + } + + // Wait for all processing to finish. + await Task.WhenAll(batchProcessCallbacks).ConfigureAwait(false); + } + + /// + /// Processes the delta changes for the given history changes. + /// Message downloads are not handled here since it's better to batch them. + /// + /// List of history changes. + private async Task ProcessHistoryChangesAsync(ListHistoryResponse listHistoryResponse) + { + _logger.Debug("Processing delta change {HistoryId} for {Name}", Account.Name, listHistoryResponse.HistoryId.GetValueOrDefault()); + + foreach (var history in listHistoryResponse.History) + { + // Handle label additions. + if (history.LabelsAdded is not null) + { + foreach (var addedLabel in history.LabelsAdded) + { + await HandleLabelAssignmentAsync(addedLabel); + } + } + + // Handle label removals. + if (history.LabelsRemoved is not null) + { + foreach (var removedLabel in history.LabelsRemoved) + { + await HandleLabelRemovalAsync(removedLabel); + } + } + + // Handle removed messages. + if (history.MessagesDeleted is not null) + { + foreach (var deletedMessage in history.MessagesDeleted) + { + var messageId = deletedMessage.Message.Id; + + _logger.Debug("Processing message deletion for {MessageId}", messageId); + + await _gmailChangeProcessor.DeleteMailAsync(Account.Id, messageId).ConfigureAwait(false); + } + } + } + } + + private async Task HandleLabelAssignmentAsync(HistoryLabelAdded addedLabel) + { + var messageId = addedLabel.Message.Id; + + _logger.Debug("Processing label assignment for message {MessageId}", messageId); + + foreach (var labelId in addedLabel.LabelIds) + { + // When UNREAD label is added mark the message as un-read. + if (labelId == GoogleIntegratorExtensions.UNREAD_LABEL_ID) + await _gmailChangeProcessor.ChangeMailReadStatusAsync(messageId, false).ConfigureAwait(false); + + // When STARRED label is added mark the message as flagged. + if (labelId == GoogleIntegratorExtensions.STARRED_LABEL_ID) + await _gmailChangeProcessor.ChangeFlagStatusAsync(messageId, true).ConfigureAwait(false); + + await _gmailChangeProcessor.CreateAssignmentAsync(Account.Id, messageId, labelId).ConfigureAwait(false); + } + } + + private async Task HandleLabelRemovalAsync(HistoryLabelRemoved removedLabel) + { + var messageId = removedLabel.Message.Id; + + _logger.Debug("Processing label removed for message {MessageId}", messageId); + + foreach (var labelId in removedLabel.LabelIds) + { + // When UNREAD label is removed mark the message as read. + if (labelId == GoogleIntegratorExtensions.UNREAD_LABEL_ID) + await _gmailChangeProcessor.ChangeMailReadStatusAsync(messageId, true).ConfigureAwait(false); + + // When STARRED label is removed mark the message as un-flagged. + if (labelId == GoogleIntegratorExtensions.STARRED_LABEL_ID) + await _gmailChangeProcessor.ChangeFlagStatusAsync(messageId, false).ConfigureAwait(false); + + // For other labels remove the mail assignment. + await _gmailChangeProcessor.DeleteAssignmentAsync(Account.Id, messageId, labelId).ConfigureAwait(false); + } + } + + /// + /// Prepares Gmail Draft object from Google SDK. + /// If provided, ThreadId ties the draft to a thread. Used when replying messages. + /// If provided, DraftId updates the draft instead of creating a new one. + /// + /// MailKit MimeMessage to include as raw message into Gmail request. + /// ThreadId that this draft should be tied to. + /// Existing DraftId from Gmail to update existing draft. + /// + private Draft PrepareGmailDraft(MimeMessage mimeMessage, string messageThreadId = "", string messageDraftId = "") + { + mimeMessage.Prepare(EncodingConstraint.None); + + var mimeString = mimeMessage.ToString(); + var base64UrlEncodedMime = Base64UrlEncoder.Encode(mimeString); + + var nativeMessage = new Message() + { + Raw = base64UrlEncodedMime, + }; + + if (!string.IsNullOrEmpty(messageThreadId)) + nativeMessage.ThreadId = messageThreadId; + + var draft = new Draft() + { + Message = nativeMessage, + Id = messageDraftId + }; + + return draft; + } + + #region Mail Integrations + + public override IEnumerable> Move(BatchMoveRequest request) + { + return CreateBatchedHttpBundleFromGroup(request, (items) => + { + var batchModifyRequest = new BatchModifyMessagesRequest + { + Ids = items.Select(a => a.Item.Id.ToString()).ToList(), + AddLabelIds = new[] { request.ToFolder.RemoteFolderId }, + RemoveLabelIds = new[] { request.FromFolder.RemoteFolderId } + }; + + return _gmailService.Users.Messages.BatchModify(batchModifyRequest, "me"); + }); + } + + public override IEnumerable> ChangeFlag(BatchChangeFlagRequest request) + { + return CreateBatchedHttpBundleFromGroup(request, (items) => + { + var batchModifyRequest = new BatchModifyMessagesRequest + { + Ids = items.Select(a => a.Item.Id.ToString()).ToList(), + }; + + if (request.IsFlagged) + batchModifyRequest.AddLabelIds = new List() { GoogleIntegratorExtensions.STARRED_LABEL_ID }; + else + batchModifyRequest.RemoveLabelIds = new List() { GoogleIntegratorExtensions.STARRED_LABEL_ID }; + + return _gmailService.Users.Messages.BatchModify(batchModifyRequest, "me"); + }); + } + + public override IEnumerable> MarkRead(BatchMarkReadRequest request) + { + return CreateBatchedHttpBundleFromGroup(request, (items) => + { + var batchModifyRequest = new BatchModifyMessagesRequest + { + Ids = items.Select(a => a.Item.Id.ToString()).ToList(), + }; + + if (request.IsRead) + batchModifyRequest.RemoveLabelIds = new List() { GoogleIntegratorExtensions.UNREAD_LABEL_ID }; + else + batchModifyRequest.AddLabelIds = new List() { GoogleIntegratorExtensions.UNREAD_LABEL_ID }; + + return _gmailService.Users.Messages.BatchModify(batchModifyRequest, "me"); + }); + } + + public override IEnumerable> Delete(BatchDeleteRequest request) + { + return CreateBatchedHttpBundleFromGroup(request, (items) => + { + var batchModifyRequest = new BatchDeleteMessagesRequest + { + Ids = items.Select(a => a.Item.Id.ToString()).ToList(), + }; + + return _gmailService.Users.Messages.BatchDelete(batchModifyRequest, "me"); + }); + } + + public override IEnumerable> CreateDraft(BatchCreateDraftRequest request) + { + return CreateHttpBundle(request, (item) => + { + if (item is not CreateDraftRequest singleRequest) + throw new ArgumentException("BatchCreateDraftRequest collection must be of type CreateDraftRequest."); + + Draft draft = null; + + // It's new mail. Not a reply + if (singleRequest.DraftPreperationRequest.ReferenceMailCopy == null) + draft = PrepareGmailDraft(singleRequest.DraftPreperationRequest.CreatedLocalDraftMimeMessage); + else + draft = PrepareGmailDraft(singleRequest.DraftPreperationRequest.CreatedLocalDraftMimeMessage, + singleRequest.DraftPreperationRequest.ReferenceMailCopy.ThreadId, + singleRequest.DraftPreperationRequest.ReferenceMailCopy.DraftId); + + return _gmailService.Users.Drafts.Create(draft, "me"); + }); + } + + public override IEnumerable> SendDraft(BatchSendDraftRequestRequest request) + { + return CreateHttpBundle(request, (item) => + { + if (item is not SendDraftRequest singleDraftRequest) + throw new ArgumentException("BatchSendDraftRequestRequest collection must be of type SendDraftRequest."); + + var message = new Message(); + + singleDraftRequest.Request.Mime.Prepare(EncodingConstraint.None); + + var mimeString = singleDraftRequest.Request.Mime.ToString(); + var base64UrlEncodedMime = Base64UrlEncoder.Encode(mimeString); + message.Raw = base64UrlEncodedMime; + + var draft = new Draft() + { + Id = singleDraftRequest.Request.MailItem.DraftId, + Message = message + }; + + return _gmailService.Users.Drafts.Send(draft, "me"); + }); + } + + public override async Task DownloadMissingMimeMessageAsync(IMailItem mailItem, + ITransferProgress transferProgress = null, + CancellationToken cancellationToken = default) + { + var gmailMessage = await _gmailService.Users.Messages.Get("me", mailItem.Id).ExecuteAsync(cancellationToken).ConfigureAwait(false); + var mimeMessage = gmailMessage.GetGmailMimeMessage(); + + await _gmailChangeProcessor.SaveMimeFileAsync(mailItem.FileId, mimeMessage, Account.Id).ConfigureAwait(false); + } + + #endregion + + #region Request Execution + + public override async Task ExecuteNativeRequestsAsync(IEnumerable> batchedRequests, + CancellationToken cancellationToken = default) + { + var batchedBundles = batchedRequests.Batch((int)MaximumAllowedBatchRequestSize); + var bundleCount = batchedBundles.Count(); + + for (int i = 0; i < bundleCount; i++) + { + var bundle = batchedBundles.ElementAt(i); + + var nativeBatchRequest = new BatchRequest(_gmailService); + + var bundleRequestCount = bundle.Count(); + + for (int k = 0; k < bundleRequestCount; k++) + { + var requestBundle = bundle.ElementAt(k); + + var nativeRequest = requestBundle.NativeRequest; + var request = requestBundle.Request; + + request.ApplyUIChanges(); + + // TODO: Queue is synchronous. Create a task bucket to await all processing. + nativeBatchRequest.Queue(nativeRequest, async (content, error, index, message) + => await ProcessSingleNativeRequestResponseAsync(requestBundle, error, message, cancellationToken).ConfigureAwait(false)); + } + + await nativeBatchRequest.ExecuteAsync(cancellationToken).ConfigureAwait(false); + } + } + + private void ProcessGmailRequestError(RequestError error) + { + if (error == null) return; + + // OutOfMemoryException is a known bug in Gmail SDK. + if (error.Code == 0) + { + throw new OutOfMemoryException(error.Message); + } + + // Entity not found. + if (error.Code == 404) + { + throw new SynchronizerEntityNotFoundException(error.Message); + } + + if (!string.IsNullOrEmpty(error.Message)) + { + error.Errors?.ForEach(error => _logger.Error("Unknown Gmail SDK error for {Name}\n{Error}", Account.Name, error)); + + // TODO: Debug + // throw new SynchronizerException(error.Message); + } + } + + /// + /// Handles after each single message download. + /// This involves adding the Gmail message into Wino database. + /// + /// + /// + /// + /// + private async Task HandleSingleItemDownloadedCallbackAsync(Message message, + RequestError error, + string downloadingMessageId, + CancellationToken cancellationToken = default) + { + try + { + ProcessGmailRequestError(error); + } + catch (OutOfMemoryException) + { + _logger.Warning("Gmail SDK got OutOfMemoryException due to bug in the SDK"); + } + catch (SynchronizerEntityNotFoundException) + { + _logger.Warning("Resource not found for {DownloadingMessageId}", downloadingMessageId); + } + catch (SynchronizerException synchronizerException) + { + _logger.Error("Gmail SDK returned error for {DownloadingMessageId}\n{SynchronizerException}", downloadingMessageId, synchronizerException); + } + + if (message == null) + { + _logger.Warning("Skipped GMail message download for {DownloadingMessageId}", downloadingMessageId); + + return; + } + + // Gmail has LabelId property for each message. + // Therefore we can pass null as the assigned folder safely. + var mailPackage = await CreateNewMailPackagesAsync(message, null, cancellationToken); + + // If CreateNewMailPackagesAsync returns null it means local draft mapping is done. + // We don't need to insert anything else. + if (mailPackage == null) + return; + + foreach (var package in mailPackage) + { + await _gmailChangeProcessor.CreateMailAsync(Account.Id, package).ConfigureAwait(false); + } + + // Try updating the history change identifier if any. + if (message.HistoryId == null) return; + + // Delta changes also has history id but the maximum id is preserved in the account service. + // TODO: This is not good. Centralize the identifier fetch and prevent direct access here. + Account.SynchronizationDeltaIdentifier = await _gmailChangeProcessor.UpdateAccountDeltaSynchronizationIdentifierAsync(Account.Id, message.HistoryId.ToString()); + } + + private async Task ProcessSingleNativeRequestResponseAsync(IRequestBundle bundle, + RequestError error, + HttpResponseMessage httpResponseMessage, + CancellationToken cancellationToken = default) + { + ProcessGmailRequestError(error); + + if (bundle is HttpRequestBundle messageBundle) + { + var gmailMessage = await messageBundle.DeserializeBundleAsync(httpResponseMessage, cancellationToken).ConfigureAwait(false); + + if (gmailMessage == null) return; + + await HandleSingleItemDownloadedCallbackAsync(gmailMessage, error, "unknown", cancellationToken); + } + else if (bundle is HttpRequestBundle folderBundle) + { + var gmailLabel = await folderBundle.DeserializeBundleAsync(httpResponseMessage, cancellationToken).ConfigureAwait(false); + + if (gmailLabel == null) return; + + // TODO: Handle new Gmail Label added or updated. + } + else if (bundle is HttpRequestBundle draftBundle && draftBundle.Request is CreateDraftRequest createDraftRequest) + { + // New draft mail is created. + + var messageDraft = await draftBundle.DeserializeBundleAsync(httpResponseMessage, cancellationToken).ConfigureAwait(false); + + if (messageDraft == null) return; + + var localDraftCopy = createDraftRequest.DraftPreperationRequest.CreatedLocalDraftCopy; + + // Here we have DraftId, MessageId and ThreadId. + // Update the local copy properties and re-synchronize to get the original message and update history. + + // We don't fetch the single message here because it may skip some of the history changes when the + // fetch updates the historyId. Therefore we need to re-synchronize to get the latest history changes + // which will have the original message downloaded eventually. + + await _gmailChangeProcessor.MapLocalDraftAsync(Account.Id, localDraftCopy.UniqueId, messageDraft.Message.Id, messageDraft.Id, messageDraft.Message.ThreadId); + + var options = new SynchronizationOptions() + { + AccountId = Account.Id, + Type = SynchronizationType.Full + }; + + await SynchronizeInternalAsync(options, cancellationToken); + } + } + + + /// + /// Maps existing Gmail Draft resources to local mail copies. + /// This uses indexed search, therefore it's quite fast. + /// It's safe to execute this after each Draft creation + batch message download. + /// + private async Task MapDraftIdsAsync(CancellationToken cancellationToken = default) + { + // TODO: This call is not necessary if we don't have any local drafts. + // Remote drafts will be downloaded in missing message batches anyways. + // Fix it by checking whether we need to do this or not. + + var drafts = await _gmailService.Users.Drafts.List("me").ExecuteAsync(cancellationToken); + + if (drafts.Drafts == null) + { + _logger.Information("There are no drafts to map for {Name}", Account.Name); + + return; + } + + foreach (var draft in drafts.Drafts) + { + await _gmailChangeProcessor.MapLocalDraftAsync(draft.Message.Id, draft.Id, draft.Message.ThreadId); + } + } + + /// + /// Creates new mail packages for the given message. + /// AssignedFolder is null since the LabelId is parsed out of the Message. + /// + /// Gmail message to create package for. + /// Null, not used. + /// Cancellation token + /// New mail package that change processor can use to insert new mail into database. + public override async Task> CreateNewMailPackagesAsync(Message message, + MailItemFolder assignedFolder, + CancellationToken cancellationToken = default) + { + var packageList = new List(); + + MimeMessage mimeMessage = message.GetGmailMimeMessage(); + var mailCopy = message.AsMailCopy(mimeMessage); + + // Check whether this message is mapped to any local draft. + // Previously we were using Draft resource response as mapping drafts. + // This seem to be a worse approach. Now both Outlook and Gmail use X-Wino-Draft-Id header to map drafts. + // This is a better approach since we don't need to fetch the draft resource to get the draft id. + + if (mimeMessage.Headers.Contains(Domain.Constants.WinoLocalDraftHeader) + && Guid.TryParse(mimeMessage.Headers[Domain.Constants.WinoLocalDraftHeader], out Guid localDraftCopyUniqueId)) + { + // This message belongs to existing local draft copy. + // We don't need to create a new mail copy for this message, just update the existing one. + + bool isMappingSuccesfull = await _gmailChangeProcessor.MapLocalDraftAsync(Account.Id, localDraftCopyUniqueId, mailCopy.Id, mailCopy.DraftId, mailCopy.ThreadId); + + if (isMappingSuccesfull) return null; + + // Local copy doesn't exists. Continue execution to insert mail copy. + } + + foreach (var labelId in message.LabelIds) + { + packageList.Add(new NewMailItemPackage(mailCopy, mimeMessage, labelId)); + } + + return packageList; + } + + #endregion + } +} diff --git a/Wino.Core/Synchronizers/ImapSynchronizer.cs b/Wino.Core/Synchronizers/ImapSynchronizer.cs new file mode 100644 index 00000000..05227106 --- /dev/null +++ b/Wino.Core/Synchronizers/ImapSynchronizer.cs @@ -0,0 +1,866 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MailKit; +using MailKit.Net.Imap; +using MailKit.Search; +using MimeKit; +using MoreLinq; +using Serilog; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Exceptions; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Synchronization; +using Wino.Core.Extensions; +using Wino.Core.Integration; +using Wino.Core.Integration.Processors; +using Wino.Core.Mime; +using Wino.Core.Requests; +using Wino.Core.Requests.Bundles; + +namespace Wino.Core.Synchronizers +{ + public class ImapSynchronizer : BaseSynchronizer + { + private CancellationTokenSource idleDoneToken; + private CancellationTokenSource cancelInboxListeningToken = new CancellationTokenSource(); + + private IMailFolder inboxFolder; + + private readonly ILogger _logger = Log.ForContext(); + private readonly ImapClientPool _clientPool; + private readonly IDefaultChangeProcessor _imapChangeProcessor; + + // Minimum summary items to Fetch for mail synchronization from IMAP. + private readonly MessageSummaryItems mailSynchronizationFlags = + MessageSummaryItems.Flags | + MessageSummaryItems.UniqueId | + MessageSummaryItems.ThreadId | + MessageSummaryItems.EmailId | + MessageSummaryItems.Headers | + MessageSummaryItems.PreviewText | + MessageSummaryItems.GMailThreadId | + MessageSummaryItems.References | + MessageSummaryItems.ModSeq; + + /// + /// Timer that keeps the alive for the lifetime of the pool. + /// Sends NOOP command to the server periodically. + /// + private Timer _noOpTimer; + + /// + /// ImapClient that keeps the Inbox folder opened all the time for listening notifications. + /// + private ImapClient _inboxIdleClient; + + public override uint BatchModificationSize => 1000; + public override uint InitialMessageDownloadCountPerFolder => 500; + + public ImapSynchronizer(MailAccount account, IDefaultChangeProcessor imapChangeProcessor) : base(account) + { + _clientPool = new ImapClientPool(Account.ServerInformation); + + idleDoneToken = new CancellationTokenSource(); + _imapChangeProcessor = imapChangeProcessor; + } + + // TODO + // private async void NoOpTimerTriggered(object state) => await AwaitInboxIdleAsync(); + + private async Task AwaitInboxIdleAsync() + { + if (_inboxIdleClient == null) + { + _logger.Warning("InboxClient is null. Cannot send NOOP command."); + return; + } + + await _clientPool.EnsureConnectedAsync(_inboxIdleClient); + await _clientPool.EnsureAuthenticatedAsync(_inboxIdleClient); + + try + { + if (inboxFolder == null) + { + inboxFolder = _inboxIdleClient.Inbox; + await inboxFolder.OpenAsync(FolderAccess.ReadOnly, cancelInboxListeningToken.Token); + } + + idleDoneToken = new CancellationTokenSource(); + + await _inboxIdleClient.IdleAsync(idleDoneToken.Token, cancelInboxListeningToken.Token); + } + finally + { + idleDoneToken.Dispose(); + idleDoneToken = null; + } + } + + private async Task StopInboxListeningAsync() + { + if (inboxFolder != null) + { + inboxFolder.CountChanged -= InboxFolderCountChanged; + inboxFolder.MessageExpunged -= InboxFolderMessageExpunged; + inboxFolder.MessageFlagsChanged -= InboxFolderMessageFlagsChanged; + } + + if (_noOpTimer != null) + { + _noOpTimer.Dispose(); + _noOpTimer = null; + } + + if (idleDoneToken != null) + { + idleDoneToken.Cancel(); + idleDoneToken.Dispose(); + idleDoneToken = null; + } + + if (_inboxIdleClient != null) + { + await _inboxIdleClient.DisconnectAsync(true); + _inboxIdleClient.Dispose(); + _inboxIdleClient = null; + } + } + + /// + /// Tries to connect & authenticate with the given credentials. + /// Prepares synchronizer for active listening of Inbox folder. + /// + public async Task StartInboxListeningAsync() + { + _inboxIdleClient = await _clientPool.GetClientAsync(); + + // Run it every 8 minutes after 1 minute delay. + // _noOpTimer = new Timer(NoOpTimerTriggered, null, 60000, 8 * 60 * 1000); + + await _clientPool.EnsureConnectedAsync(_inboxIdleClient); + await _clientPool.EnsureAuthenticatedAsync(_inboxIdleClient); + + if (!_inboxIdleClient.Capabilities.HasFlag(ImapCapabilities.Idle)) + { + _logger.Information("Imap server does not support IDLE command. Listening live changes is not supported for {Name}", Account.Name); + return; + } + + inboxFolder = _inboxIdleClient.Inbox; + + if (inboxFolder == null) + { + _logger.Information("Inbox folder is null. Cannot listen for changes."); + return; + } + + inboxFolder.CountChanged += InboxFolderCountChanged; + inboxFolder.MessageExpunged += InboxFolderMessageExpunged; + inboxFolder.MessageFlagsChanged += InboxFolderMessageFlagsChanged; + + while (!cancelInboxListeningToken.IsCancellationRequested) + { + await AwaitInboxIdleAsync(); + } + + await StopInboxListeningAsync(); + } + + private void InboxFolderMessageFlagsChanged(object sender, MessageFlagsChangedEventArgs e) + { + Console.WriteLine("Flags have changed for message #{0} ({1}).", e.Index, e.Flags); + } + + private void InboxFolderMessageExpunged(object sender, MessageEventArgs e) + { + _logger.Information("Inbox folder message expunged"); + } + + private void InboxFolderCountChanged(object sender, EventArgs e) + { + _logger.Information("Inbox folder count changed."); + } + + /// + /// Parses List of string of mail copy ids and return valid uIds. + /// Follow the rules for creating arbitrary unique id for mail copies. + /// + private UniqueIdSet GetUniqueIds(IEnumerable mailCopyIds) + => new(mailCopyIds.Select(a => new UniqueId(MailkitClientExtensions.ResolveUid(a)))); + + #region Mail Integrations + + // Items are grouped before being passed to this method. + // Meaning that all items will come from and to the same folder. + // It's fine to assume that here. + + public override IEnumerable> Move(BatchMoveRequest request) + { + return CreateTaskBundle(async (ImapClient client) => + { + var uniqueIds = GetUniqueIds(request.Items.Select(a => a.Item.Id)); + + var sourceFolder = await client.GetFolderAsync(request.FromFolder.RemoteFolderId); + var destinationFolder = await client.GetFolderAsync(request.ToFolder.RemoteFolderId); + + // Only opening source folder is enough. + await sourceFolder.OpenAsync(FolderAccess.ReadWrite).ConfigureAwait(false); + await sourceFolder.MoveToAsync(uniqueIds, destinationFolder).ConfigureAwait(false); + await sourceFolder.CloseAsync().ConfigureAwait(false); + }, request); + } + + public override IEnumerable> ChangeFlag(BatchChangeFlagRequest request) + { + return CreateTaskBundle(async (ImapClient client) => + { + var folder = request.Items.First().Item.AssignedFolder; + var remoteFolder = await client.GetFolderAsync(folder.RemoteFolderId); + var uniqueIds = GetUniqueIds(request.Items.Select(a => a.Item.Id)); + + await remoteFolder.OpenAsync(FolderAccess.ReadWrite).ConfigureAwait(false); + await remoteFolder.StoreAsync(uniqueIds, new StoreFlagsRequest(request.IsFlagged ? StoreAction.Add : StoreAction.Remove, MessageFlags.Flagged) { Silent = true }).ConfigureAwait(false); + await remoteFolder.CloseAsync().ConfigureAwait(false); + }, request); + } + + public override IEnumerable> Delete(BatchDeleteRequest request) + { + return CreateTaskBundle(async (ImapClient client) => + { + var folder = request.Items.First().Item.AssignedFolder; + var remoteFolder = await client.GetFolderAsync(folder.RemoteFolderId).ConfigureAwait(false); + var uniqueIds = GetUniqueIds(request.Items.Select(a => a.Item.Id)); + + await remoteFolder.OpenAsync(FolderAccess.ReadWrite).ConfigureAwait(false); + await remoteFolder.StoreAsync(uniqueIds, new StoreFlagsRequest(StoreAction.Add, MessageFlags.Deleted) { Silent = true }).ConfigureAwait(false); + await remoteFolder.ExpungeAsync().ConfigureAwait(false); + await remoteFolder.CloseAsync().ConfigureAwait(false); + }, request); + } + + public override IEnumerable> MarkRead(BatchMarkReadRequest request) + { + return CreateTaskBundle(async (ImapClient client) => + { + var folder = request.Items.First().Item.AssignedFolder; + var remoteFolder = await client.GetFolderAsync(folder.RemoteFolderId); + var uniqueIds = GetUniqueIds(request.Items.Select(a => a.Item.Id)); + + await remoteFolder.OpenAsync(FolderAccess.ReadWrite).ConfigureAwait(false); + await remoteFolder.StoreAsync(uniqueIds, new StoreFlagsRequest(request.IsRead ? StoreAction.Add : StoreAction.Remove, MessageFlags.Seen) { Silent = true }).ConfigureAwait(false); + await remoteFolder.CloseAsync().ConfigureAwait(false); + }, request); + } + + public override IEnumerable> CreateDraft(BatchCreateDraftRequest request) + { + return CreateTaskBundle(async (ImapClient client) => + { + + var remoteDraftFolder = await client.GetFolderAsync(request.DraftPreperationRequest.CreatedLocalDraftCopy.AssignedFolder.RemoteFolderId).ConfigureAwait(false); + + await remoteDraftFolder.OpenAsync(FolderAccess.ReadWrite).ConfigureAwait(false); + await remoteDraftFolder.AppendAsync(request.DraftPreperationRequest.CreatedLocalDraftMimeMessage, MessageFlags.Draft).ConfigureAwait(false); + await remoteDraftFolder.CloseAsync().ConfigureAwait(false); + }, request); + } + + public override IEnumerable> SendDraft(BatchSendDraftRequestRequest request) + { + return CreateTaskBundle(async (ImapClient client) => + { + // Batch sending is not supported. It will always be a single request therefore no need for a loop here. + + var singleRequest = request.Request; + + singleRequest.Mime.Prepare(EncodingConstraint.None); + + using var smtpClient = new MailKit.Net.Smtp.SmtpClient(); + + if (smtpClient.IsConnected && client.IsAuthenticated) return; + + if (!smtpClient.IsConnected) + await smtpClient.ConnectAsync(Account.ServerInformation.OutgoingServer, int.Parse(Account.ServerInformation.OutgoingServerPort), MailKit.Security.SecureSocketOptions.Auto); + + if (!smtpClient.IsAuthenticated) + await smtpClient.AuthenticateAsync(Account.ServerInformation.OutgoingServerUsername, Account.ServerInformation.OutgoingServerPassword); + + // TODO: Transfer progress implementation as popup in the UI. + await smtpClient.SendAsync(singleRequest.Mime, default); + await smtpClient.DisconnectAsync(true); + + // SMTP sent the message, but we need to remove it from the Draft folder. + var draftFolder = singleRequest.MailItem.AssignedFolder; + + var folder = await client.GetFolderAsync(draftFolder.RemoteFolderId); + + await folder.OpenAsync(FolderAccess.ReadWrite); + + var notUpdatedIds = await folder.StoreAsync(new UniqueId(MailkitClientExtensions.ResolveUid(singleRequest.MailItem.Id)), new StoreFlagsRequest(StoreAction.Add, MessageFlags.Deleted) { Silent = true }); + + await folder.ExpungeAsync(); + await folder.CloseAsync(); + + // Check whether we need to create a copy of the message to Sent folder. + // This comes from the account preferences. + + if (singleRequest.AccountPreferences.ShouldAppendMessagesToSentFolder && singleRequest.SentFolder != null) + { + var sentFolder = await client.GetFolderAsync(singleRequest.SentFolder.RemoteFolderId); + + await sentFolder.OpenAsync(FolderAccess.ReadWrite); + + // Delete local Wino draft header. Otherwise mapping will be applied on re-sync. + singleRequest.Mime.Headers.Remove(Domain.Constants.WinoLocalDraftHeader); + + await sentFolder.AppendAsync(singleRequest.Mime, MessageFlags.Seen); + await sentFolder.CloseAsync(); + } + }, request); + } + + public override async Task DownloadMissingMimeMessageAsync(IMailItem mailItem, + ITransferProgress transferProgress = null, + CancellationToken cancellationToken = default) + { + var folder = mailItem.AssignedFolder; + var remoteFolderId = folder.RemoteFolderId; + + var client = await _clientPool.GetClientAsync().ConfigureAwait(false); + var remoteFolder = await client.GetFolderAsync(remoteFolderId, cancellationToken).ConfigureAwait(false); + + var uniqueId = new UniqueId(MailkitClientExtensions.ResolveUid(mailItem.Id)); + + await remoteFolder.OpenAsync(FolderAccess.ReadOnly, cancellationToken).ConfigureAwait(false); + + var message = await remoteFolder.GetMessageAsync(uniqueId, cancellationToken, transferProgress).ConfigureAwait(false); + + await _imapChangeProcessor.SaveMimeFileAsync(mailItem.FileId, message, Account.Id).ConfigureAwait(false); + await remoteFolder.CloseAsync(false, cancellationToken).ConfigureAwait(false); + + _clientPool.Release(client); + } + + #endregion + + public override async Task> CreateNewMailPackagesAsync(ImapMessageCreationPackage message, MailItemFolder assignedFolder, CancellationToken cancellationToken = default) + { + var imapFolder = message.MailFolder; + var summary = message.MessageSummary; + + var mimeMessage = await imapFolder.GetMessageAsync(summary.UniqueId, cancellationToken).ConfigureAwait(false); + var mailCopy = summary.GetMailDetails(assignedFolder, mimeMessage); + + // Draft folder message updates must be updated as IsDraft. + // I couldn't find it in MimeMessage... + + mailCopy.IsDraft = assignedFolder.SpecialFolderType == SpecialFolderType.Draft; + + // Check draft mapping. + // This is the same implementation as in the OutlookSynchronizer. + + if (mimeMessage.Headers.Contains(Domain.Constants.WinoLocalDraftHeader) + && Guid.TryParse(mimeMessage.Headers[Domain.Constants.WinoLocalDraftHeader], out Guid localDraftCopyUniqueId)) + { + // This message belongs to existing local draft copy. + // We don't need to create a new mail copy for this message, just update the existing one. + + bool isMappingSuccessful = await _imapChangeProcessor.MapLocalDraftAsync(Account.Id, localDraftCopyUniqueId, mailCopy.Id, mailCopy.DraftId, mailCopy.ThreadId); + + if (isMappingSuccessful) return null; + + // Local copy doesn't exists. Continue execution to insert mail copy. + } + + var package = new NewMailItemPackage(mailCopy, mimeMessage, assignedFolder.RemoteFolderId); + + return + [ + package + ]; + } + + public override async Task SynchronizeInternalAsync(SynchronizationOptions options, CancellationToken cancellationToken = default) + { + // options.Type = SynchronizationType.FoldersOnly; + + var downloadedMessageIds = new List(); + + _logger.Information("Internal synchronization started for {Name}", Account.Name); + _logger.Information("Options: {Options}", options); + + options.ProgressListener?.AccountProgressUpdated(Account.Id, 1); + + // Only do folder sync for these types. + // Opening folder and checking their UidValidity is slow. + // Therefore this should be avoided as many times as possible. + + // This may create some inconsistencies, but nothing we can do... + if (options.Type == SynchronizationType.FoldersOnly || options.Type == SynchronizationType.Full) + { + await SynchronizeFoldersAsync(cancellationToken).ConfigureAwait(false); + } + + if (options.Type != SynchronizationType.FoldersOnly) + { + var synchronizationFolders = await _imapChangeProcessor.GetSynchronizationFoldersAsync(options).ConfigureAwait(false); + + for (int i = 0; i < synchronizationFolders.Count; i++) + { + var folder = synchronizationFolders[i]; + var progress = (int)Math.Round((double)(i + 1) / synchronizationFolders.Count * 100); + + options.ProgressListener?.AccountProgressUpdated(Account.Id, progress); + + var folderDownloadedMessageIds = await SynchronizeFolderInternalAsync(folder, cancellationToken).ConfigureAwait(false); + downloadedMessageIds.AddRange(folderDownloadedMessageIds); + } + } + + options.ProgressListener?.AccountProgressUpdated(Account.Id, 100); + + // Get all unread new downloaded items and return in the result. + // This is primarily used in notifications. + + var unreadNewItems = await _imapChangeProcessor.GetDownloadedUnreadMailsAsync(Account.Id, downloadedMessageIds).ConfigureAwait(false); + + return SynchronizationResult.Completed(unreadNewItems); + } + + public override async Task ExecuteNativeRequestsAsync(IEnumerable> batchedRequests, CancellationToken cancellationToken = default) + { + // First apply the UI changes for each bundle. + // This is important to reflect changes to the UI before the network call is done. + + foreach (var item in batchedRequests) + { + item.Request.ApplyUIChanges(); + } + + // All task bundles will execute on the same client. + // Tasks themselves don't pull the client from the pool + // because exception handling is easier this way. + // Also we might parallelize these bundles later on for additional performance. + + foreach (var item in batchedRequests) + { + // At this point this client is ready to execute async commands. + // Each task bundle will await and execution will continue in case of error. + + ImapClient executorClient = null; + + try + { + executorClient = await _clientPool.GetClientAsync(); + } + catch (ImapClientPoolException) + { + // Client pool failed to get a client. + // Requests may not be executed at this point. + + item.Request.RevertUIChanges(); + + throw; + } + finally + { + if (executorClient != null) + { + _clientPool.Release(executorClient); + } + } + + // TODO: Retry pattern. + // TODO: Error handling. + try + { + await item.NativeRequest.IntegratorTask(executorClient).ConfigureAwait(false); + } + catch (Exception) + { + + throw; + } + finally + { + _clientPool.Release(executorClient); + } + } + } + + // TODO: This can be optimized by starting checking the local folders UidValidtyNext first. + // TODO: We need to determine deleted folders here. Also for that we need to start by local folders. + // TODO: UpdateFolderStructureAsync + private async Task SynchronizeFoldersAsync(CancellationToken cancellationToken = default) + { + // https://www.rfc-editor.org/rfc/rfc4549#section-1.1 + + var _synchronizationClient = await _clientPool.GetClientAsync(); + + var nameSpace = _synchronizationClient.PersonalNamespaces[0]; + + var folders = (await _synchronizationClient.GetFoldersAsync(nameSpace, cancellationToken: cancellationToken)).ToList(); + + // Special folders + + bool isSpecialFoldersSupported = _synchronizationClient.Capabilities.HasFlag(ImapCapabilities.SpecialUse) || _synchronizationClient.Capabilities.HasFlag(ImapCapabilities.XList); + + // Inbox is always available. + IMailFolder inbox = _synchronizationClient.Inbox, drafts = null, junk = null, trash = null, sent = null, archive = null, important = null, starred = null; + + if (isSpecialFoldersSupported) + { + drafts = _synchronizationClient.GetFolder(SpecialFolder.Drafts); + junk = _synchronizationClient.GetFolder(SpecialFolder.Junk); + trash = _synchronizationClient.GetFolder(SpecialFolder.Trash); + sent = _synchronizationClient.GetFolder(SpecialFolder.Sent); + archive = _synchronizationClient.GetFolder(SpecialFolder.Archive); + important = _synchronizationClient.GetFolder(SpecialFolder.Important); + starred = _synchronizationClient.GetFolder(SpecialFolder.Flagged); + } + + var mailItemFolders = new List(); + + // Sometimes Inbox is the root namespace. We need to check for that. + if (inbox != null && !folders.Contains(inbox)) + folders.Add(inbox); + + foreach (var item in folders) + { + // Namespaces are not needed as folders. + // Non-existed folders don't need to be synchronized. + + if ((item.IsNamespace && !item.Attributes.HasFlag(FolderAttributes.Inbox)) || !item.Exists) + continue; + + // Try to synchronize all folders. + + try + { + var localFolder = item.GetLocalFolder(); + + if (item == inbox) + localFolder.SpecialFolderType = SpecialFolderType.Inbox; + + if (isSpecialFoldersSupported) + { + if (item == drafts) + localFolder.SpecialFolderType = SpecialFolderType.Draft; + else if (item == junk) + localFolder.SpecialFolderType = SpecialFolderType.Junk; + else if (item == trash) + localFolder.SpecialFolderType = SpecialFolderType.Deleted; + else if (item == sent) + localFolder.SpecialFolderType = SpecialFolderType.Sent; + else if (item == archive) + localFolder.SpecialFolderType = SpecialFolderType.Archive; + else if (item == important) + localFolder.SpecialFolderType = SpecialFolderType.Important; + else if (item == starred) + localFolder.SpecialFolderType = SpecialFolderType.Starred; + } + + if (localFolder.SpecialFolderType != SpecialFolderType.Other) + { + localFolder.IsSystemFolder = true; + localFolder.IsSticky = true; + localFolder.IsSynchronizationEnabled = true; + } + else + { + localFolder.IsSynchronizationEnabled = false; + } + + // By default, all special folders update unread count in the UI except Trash. + localFolder.ShowUnreadCount = localFolder.SpecialFolderType != SpecialFolderType.Deleted || localFolder.SpecialFolderType != SpecialFolderType.Other; + + localFolder.MailAccountId = Account.Id; + + // Sometimes sub folders are parented under Inbox. + // Even though this makes sense in server level, in the client it sucks. + // That will make sub folders to be parented under Inbox in the client. + // Instead, we will mark them as non-parented folders. + // This is better. Model allows personalized folder structure anyways + // even though we don't have the page/control to adjust it. + + if (item.ParentFolder == _synchronizationClient.Inbox) + localFolder.ParentRemoteFolderId = string.Empty; + + // Set UidValidity for cache expiration. + // Folder must be opened for this. + + await item.OpenAsync(FolderAccess.ReadOnly); + + localFolder.UidValidity = item.UidValidity; + + await item.CloseAsync(); + + mailItemFolders.Add(localFolder); + } + catch (Exception ex) + { + Log.Error(ex, $"Folder with name '{item.Name}' failed to be synchronized."); + } + } + + await _imapChangeProcessor.UpdateFolderStructureAsync(Account.Id, mailItemFolders); + } + + private async Task> SynchronizeFolderInternalAsync(MailItemFolder folder, CancellationToken cancellationToken = default) + { + if (!folder.IsSynchronizationEnabled) return default; + + var downloadedMessageIds = new List(); + + // STEP1: Ask for flag changes for older mails. + // STEP2: Get new mail changes. + // https://www.rfc-editor.org/rfc/rfc4549 - Section 4.3 + + var _synchronizationClient = await _clientPool.GetClientAsync(); + + IMailFolder imapFolder = null; + + var knownMailIds = new UniqueIdSet(); + var locallyKnownMailUids = await _imapChangeProcessor.GetKnownUidsForFolderAsync(folder.Id); + knownMailIds.AddRange(locallyKnownMailUids.Select(a => new UniqueId(a))); + + var highestUniqueId = Math.Max(0, locallyKnownMailUids.Count == 0 ? 0 : locallyKnownMailUids.Max()); + + var missingMailIds = new UniqueIdSet(); + + var uidValidity = folder.UidValidity; + var highestModeSeq = folder.HighestModeSeq; + + var logger = Log.ForContext("FolderName", folder.FolderName); + + logger.Verbose("HighestModeSeq: {HighestModeSeq}, HighestUniqueId: {HighestUniqueId}, UIDValidity: {UIDValidity}", highestModeSeq, highestUniqueId, uidValidity); + + // Event handlers are placed here to handle existing MailItemFolder and IIMailFolder from MailKit. + // MailKit doesn't expose folder data when these events are emitted. + + // Use local folder's UidValidty because cache might've been expired for remote IMAP folder. + // That will make our mail copy id invalid. + + EventHandler MessageVanishedHandler = async (s, e) => + { + if (imapFolder == null) return; + + foreach (var uniqueId in e.UniqueIds) + { + var localMailCopyId = MailkitClientExtensions.CreateUid(folder.Id, uniqueId.Id); + + await _imapChangeProcessor.DeleteMailAsync(Account.Id, localMailCopyId); + } + }; + + EventHandler MessageFlagsChangedHandler = async (s, e) => + { + if (imapFolder == null) return; + + var localMailCopyId = MailkitClientExtensions.CreateUid(folder.Id, e.UniqueId.Value.Id); + + var isFlagged = MailkitClientExtensions.GetIsFlagged(e.Flags); + var isRead = MailkitClientExtensions.GetIsRead(e.Flags); + + await _imapChangeProcessor.ChangeMailReadStatusAsync(localMailCopyId, isRead); + await _imapChangeProcessor.ChangeFlagStatusAsync(localMailCopyId, isFlagged); + }; + + EventHandler MessageExpungedHandler = async (s, e) => + { + if (imapFolder == null) return; + if (e.UniqueId == null) return; + + var localMailCopyId = MailkitClientExtensions.CreateUid(folder.Id, e.UniqueId.Value.Id); + await _imapChangeProcessor.DeleteMailAsync(Account.Id, localMailCopyId); + }; + + try + { + imapFolder = await _synchronizationClient.GetFolderAsync(folder.RemoteFolderId, cancellationToken); + + imapFolder.MessageFlagsChanged += MessageFlagsChangedHandler; + + // TODO: Bug: Enabling quick re-sync actually doesn't enable it. + + var qsyncEnabled = false; // _synchronizationClient.Capabilities.HasFlag(ImapCapabilities.QuickResync); + var condStoreEnabled = _synchronizationClient.Capabilities.HasFlag(ImapCapabilities.CondStore); + + if (qsyncEnabled) + { + + imapFolder.MessagesVanished += MessageVanishedHandler; + + await imapFolder.OpenAsync(FolderAccess.ReadWrite, uidValidity, (ulong)highestModeSeq, knownMailIds, cancellationToken); + + // Check the folder validity. + // We'll delete our existing cache if it's not. + + // Get all messages after the last successful synchronization date. + // This is fine for Wino synchronization because we're not really looking to + // synchronize all folder. + + var allMessageIds = await imapFolder.SearchAsync(SearchQuery.All, cancellationToken); + + if (uidValidity != imapFolder.UidValidity) + { + // TODO: Cache is invalid. Delete all local cache. + //await ChangeProcessor.FolderService.ClearImapFolderCacheAsync(folder.Id); + + folder.UidValidity = imapFolder.UidValidity; + missingMailIds.AddRange(allMessageIds); + } + else + { + // Cache is valid. + // Add missing mails only. + + missingMailIds.AddRange(allMessageIds.Except(knownMailIds).Where(a => a.Id > highestUniqueId)); + } + } + else + { + // QSYNC extension is not enabled for the server. + // We rely on ConditionalStore. + + imapFolder.MessageExpunged += MessageExpungedHandler; + await imapFolder.OpenAsync(FolderAccess.ReadWrite, cancellationToken); + + // Get all messages after the last succesful synchronization date. + // This is fine for Wino synchronization because we're not really looking to + // synchronize all folder. + + var allMessageIds = await imapFolder.SearchAsync(SearchQuery.All, cancellationToken); + + if (uidValidity != imapFolder.UidValidity) + { + // TODO: Cache is invalid. Delete all local cache. + // await ChangeProcessor.FolderService.ClearImapFolderCacheAsync(folder.Id); + + folder.UidValidity = imapFolder.UidValidity; + missingMailIds.AddRange(allMessageIds); + } + else + { + // Cache is valid. + + var purgedMessages = knownMailIds.Except(allMessageIds); + + foreach (var purgedMessage in purgedMessages) + { + var mailId = MailkitClientExtensions.CreateUid(folder.Id, purgedMessage.Id); + + await _imapChangeProcessor.DeleteMailAsync(Account.Id, mailId); + } + + IList changed; + + if (knownMailIds.Count > 0) + { + // CONDSTORE enabled. Fetch items with highest mode seq for known items + // to track flag changes. Otherwise just get changes without the mode seq. + + if (condStoreEnabled) + changed = await imapFolder.FetchAsync(knownMailIds, (ulong)highestModeSeq, MessageSummaryItems.Flags | MessageSummaryItems.ModSeq | MessageSummaryItems.UniqueId); + else + changed = await imapFolder.FetchAsync(knownMailIds, MessageSummaryItems.Flags | MessageSummaryItems.UniqueId); + + foreach (var changedItem in changed) + { + var localMailCopyId = MailkitClientExtensions.CreateUid(folder.Id, changedItem.UniqueId.Id); + + var isFlagged = MailkitClientExtensions.GetIsFlagged(changedItem.Flags); + var isRead = MailkitClientExtensions.GetIsRead(changedItem.Flags); + + await _imapChangeProcessor.ChangeMailReadStatusAsync(localMailCopyId, isRead); + await _imapChangeProcessor.ChangeFlagStatusAsync(localMailCopyId, isFlagged); + } + } + + // We're only interested in items that has highier known uid than we fetched before. + // Others are just older messages. + + missingMailIds.AddRange(allMessageIds.Except(knownMailIds).Where(a => a.Id > highestUniqueId)); + } + } + + // Fetch completely missing new items in the end. + + // Limit check. + //if (missingMailIds.Count > TAKE_COUNT) + //{ + // missingMailIds = new UniqueIdSet(missingMailIds.TakeLast(TAKE_COUNT)); + //} + + // In case of the high input, we'll batch them by 50 to reflect changes quickly. + var batchedMissingMailIds = missingMailIds.Batch(50).Select(a => new UniqueIdSet(a, SortOrder.Descending)); + + foreach (var batchMissingMailIds in batchedMissingMailIds) + { + var summaries = await imapFolder.FetchAsync(batchMissingMailIds, mailSynchronizationFlags, cancellationToken); + + foreach (var summary in summaries) + { + // We pass the opened folder and summary to retrieve raw MimeMessage. + + var creationPackage = new ImapMessageCreationPackage(summary, imapFolder); + var createdMailPackages = await CreateNewMailPackagesAsync(creationPackage, folder, cancellationToken).ConfigureAwait(false); + + // Local draft is mapped. We don't need to create a new mail copy. + if (createdMailPackages == null) + continue; + + foreach (var mailPackage in createdMailPackages) + { + await _imapChangeProcessor.CreateMailAsync(Account.Id, mailPackage); + } + } + } + + if (folder.HighestModeSeq != (long)imapFolder.HighestModSeq) + { + folder.HighestModeSeq = (long)imapFolder.HighestModSeq; + await _imapChangeProcessor.InsertFolderAsync(folder); + } + + // Update last synchronization identifier. + // This will update last sync date for the folder. + + await _imapChangeProcessor.UpdateFolderDeltaSynchronizationIdentifierAsync(folder.Id, string.Empty).ConfigureAwait(false); + + return downloadedMessageIds; + } + catch (FolderNotFoundException) + { + await _imapChangeProcessor.DeleteFolderAsync(Account.Id, folder.RemoteFolderId); + + return default; + } + catch (Exception) + { + throw; + } + finally + { + if (imapFolder != null) + { + imapFolder.MessageFlagsChanged -= MessageFlagsChangedHandler; + imapFolder.MessageExpunged -= MessageExpungedHandler; + imapFolder.MessagesVanished -= MessageVanishedHandler; + + if (imapFolder.IsOpen) + await imapFolder.CloseAsync(); + } + + _clientPool.Release(_synchronizationClient); + } + } + } +} diff --git a/Wino.Core/Synchronizers/OutlookSynchronizer.cs b/Wino.Core/Synchronizers/OutlookSynchronizer.cs new file mode 100644 index 00000000..738b8672 --- /dev/null +++ b/Wino.Core/Synchronizers/OutlookSynchronizer.cs @@ -0,0 +1,632 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Graph; +using Microsoft.Graph.Models; +using Microsoft.Kiota.Abstractions; +using Microsoft.Kiota.Abstractions.Authentication; +using MimeKit; +using MoreLinq.Extensions; +using Serilog; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Exceptions; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Requests; +using Wino.Core.Domain.Models.Synchronization; +using Wino.Core.Extensions; +using Wino.Core.Http; +using Wino.Core.Integration.Processors; +using Wino.Core.Requests; + +namespace Wino.Core.Synchronizers +{ + public class OutlookSynchronizer : BaseSynchronizer + { + public override uint BatchModificationSize => 20; + public override uint InitialMessageDownloadCountPerFolder => 250; + private const uint MaximumAllowedBatchRequestSize = 20; + + private const string INBOX_NAME = "inbox"; + private const string SENT_NAME = "sentitems"; + private const string DELETED_NAME = "deleteditems"; + private const string JUNK_NAME = "junkemail"; + private const string DRAFTS_NAME = "drafts"; + private const string ARCHIVE_NAME = "archive"; + + private readonly string[] outlookMessageSelectParameters = + [ + "InferenceClassification", + "Flag", + "Importance", + "IsRead", + "IsDraft", + "ReceivedDateTime", + "HasAttachments", + "BodyPreview", + "Id", + "ConversationId", + "From", + "Subject", + "ParentFolderId", + "InternetMessageId", + ]; + + private readonly ILogger _logger = Log.ForContext(); + private readonly IDefaultChangeProcessor _outlookChangeProcessor; + private readonly GraphServiceClient _graphClient; + public OutlookSynchronizer(MailAccount account, + IAuthenticator authenticator, + IDefaultChangeProcessor outlookChangeProcessor) : base(account) + { + var tokenProvider = new MicrosoftTokenProvider(Account, authenticator); + + // Add immutable id preffered client. + var handlers = GraphClientFactory.CreateDefaultHandlers(); + handlers.Add(new MicrosoftImmutableIdHandler()); + + var httpClient = GraphClientFactory.Create(handlers); + + _graphClient = new GraphServiceClient(httpClient, new BaseBearerTokenAuthenticationProvider(tokenProvider)); + _outlookChangeProcessor = outlookChangeProcessor; + + // Specify to use TLS 1.2 as default connection + System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; + } + + public override async Task SynchronizeInternalAsync(SynchronizationOptions options, CancellationToken cancellationToken = default) + { + var downloadedMessageIds = new List(); + + _logger.Information("Internal synchronization started for {Name}", Account.Name); + _logger.Information("Options: {Options}", options); + + try + { + + options.ProgressListener?.AccountProgressUpdated(Account.Id, 1); + + await SynchronizeFoldersAsync(cancellationToken).ConfigureAwait(false); + + if (options.Type != SynchronizationType.FoldersOnly) + { + var synchronizationFolders = await _outlookChangeProcessor.GetSynchronizationFoldersAsync(options).ConfigureAwait(false); + + for (int i = 0; i < synchronizationFolders.Count; i++) + { + var folder = synchronizationFolders[i]; + var progress = (int)Math.Round((double)(i + 1) / synchronizationFolders.Count * 100); + + options.ProgressListener?.AccountProgressUpdated(Account.Id, progress); + + var folderDownloadedMessageIds = await SynchronizeFolderAsync(folder, cancellationToken).ConfigureAwait(false); + downloadedMessageIds.AddRange(folderDownloadedMessageIds); + } + } + } + catch (Exception ex) + { + _logger.Error(ex, "Synchronization failed for {Name}", Account.Name); + + throw; + } + finally + { + options.ProgressListener?.AccountProgressUpdated(Account.Id, 100); + } + + // Get all unred new downloaded items and return in the result. + // This is primarily used in notifications. + + var unreadNewItems = await _outlookChangeProcessor.GetDownloadedUnreadMailsAsync(Account.Id, downloadedMessageIds).ConfigureAwait(false); + + return SynchronizationResult.Completed(unreadNewItems); + } + + private async Task> SynchronizeFolderAsync(MailItemFolder folder, CancellationToken cancellationToken = default) + { + var downloadedMessageIds = new List(); + + _logger.Debug("Started synchronization for folder {FolderName}", folder.FolderName); + + cancellationToken.ThrowIfCancellationRequested(); + + string latestDeltaLink = string.Empty; + + bool isInitialSync = string.IsNullOrEmpty(folder.DeltaToken); + + Microsoft.Graph.Me.MailFolders.Item.Messages.Delta.DeltaGetResponse messageCollectionPage = null; + + if (isInitialSync) + { + // No delta link. Performing initial sync. + + messageCollectionPage = await _graphClient.Me.MailFolders[folder.RemoteFolderId].Messages.Delta.GetAsDeltaGetResponseAsync((config) => + { + config.QueryParameters.Top = (int)InitialMessageDownloadCountPerFolder; + config.QueryParameters.Select = outlookMessageSelectParameters; + config.QueryParameters.Orderby = ["receivedDateTime desc"]; + }, cancellationToken).ConfigureAwait(false); + } + else + { + var currentDeltaToken = folder.DeltaToken; + + var requestInformation = _graphClient.Me.MailFolders[folder.RemoteFolderId].Messages.Delta.ToGetRequestInformation((config) => + { + config.QueryParameters.Top = (int)InitialMessageDownloadCountPerFolder; + config.QueryParameters.Select = outlookMessageSelectParameters; + config.QueryParameters.Orderby = ["receivedDateTime desc"]; + }); + + requestInformation.UrlTemplate = requestInformation.UrlTemplate.Insert(requestInformation.UrlTemplate.Length - 1, ",%24deltatoken"); + requestInformation.QueryParameters.Add("%24deltatoken", currentDeltaToken); + + messageCollectionPage = await _graphClient.RequestAdapter.SendAsync(requestInformation, Microsoft.Graph.Me.MailFolders.Item.Messages.Delta.DeltaGetResponse.CreateFromDiscriminatorValue); + } + + var messageIteratorAsync = PageIterator.CreatePageIterator(_graphClient, messageCollectionPage, async (item) => + { + return await HandleItemRetrievedAsync(item, folder, downloadedMessageIds, cancellationToken); + }); + + await messageIteratorAsync + .IterateAsync(cancellationToken) + .ConfigureAwait(false); + + latestDeltaLink = messageIteratorAsync.Deltalink; + + //Store delta link for tracking new changes. + if (!string.IsNullOrEmpty(latestDeltaLink)) + { + // Parse Delta Token from Delta Link since v5 of Graph SDK works based on the token, not the link. + + var deltaToken = Regex.Split(messageIteratorAsync.Deltalink, "deltatoken=")[1]; + + await _outlookChangeProcessor.UpdateFolderDeltaSynchronizationIdentifierAsync(folder.Id, deltaToken).ConfigureAwait(false); + } + + return downloadedMessageIds; + } + + private bool IsResourceDeleted(IDictionary additionalData) + => additionalData != null && additionalData.ContainsKey("@removed"); + + private bool IsResourceUpdated(IDictionary additionalData) + => additionalData == null || !additionalData.Any(); + + private async Task HandleFolderRetrievedAsync(MailFolder folder, OutlookSpecialFolderIdInformation outlookSpecialFolderIdInformation, CancellationToken cancellationToken = default) + { + if (IsResourceDeleted(folder.AdditionalData)) + { + await _outlookChangeProcessor.DeleteFolderAsync(Account.Id, folder.Id).ConfigureAwait(false); + } + else if (IsResourceUpdated(folder.AdditionalData)) + { + // TODO + Debugger.Break(); + } + else + { + // New folder created. + + var item = folder.GetLocalFolder(Account.Id); + + if (item.RemoteFolderId.Equals(outlookSpecialFolderIdInformation.InboxId)) + item.SpecialFolderType = SpecialFolderType.Inbox; + else if (item.RemoteFolderId.Equals(outlookSpecialFolderIdInformation.SentId)) + item.SpecialFolderType = SpecialFolderType.Sent; + else if (item.RemoteFolderId.Equals(outlookSpecialFolderIdInformation.DraftId)) + item.SpecialFolderType = SpecialFolderType.Draft; + else if (item.RemoteFolderId.Equals(outlookSpecialFolderIdInformation.TrashId)) + item.SpecialFolderType = SpecialFolderType.Deleted; + else if (item.RemoteFolderId.Equals(outlookSpecialFolderIdInformation.JunkId)) + item.SpecialFolderType = SpecialFolderType.Junk; + else if (item.RemoteFolderId.Equals(outlookSpecialFolderIdInformation.ArchiveId)) + item.SpecialFolderType = SpecialFolderType.Archive; + else + item.SpecialFolderType = SpecialFolderType.Other; + + // Automatically mark special folders as Sticky for better visibility. + item.IsSticky = item.SpecialFolderType != SpecialFolderType.Other; + + // By default, all non-others are system folder. + item.IsSystemFolder = item.SpecialFolderType != SpecialFolderType.Other; + + // By default, all special folders update unread count in the UI except Trash. + item.ShowUnreadCount = item.SpecialFolderType != SpecialFolderType.Deleted || item.SpecialFolderType != SpecialFolderType.Other; + + await _outlookChangeProcessor.InsertFolderAsync(item).ConfigureAwait(false); + } + + return true; + } + + private async Task HandleItemRetrievedAsync(Message item, MailItemFolder folder, IList downloadedMessageIds, CancellationToken cancellationToken = default) + { + if (IsResourceDeleted(item.AdditionalData)) + { + // Deleting item with this override instead of the other one that deletes all mail copies. + // Outlook mails have 1 assignment per-folder, unlike Gmail that has one to many. + + await _outlookChangeProcessor.DeleteAssignmentAsync(Account.Id, item.Id, folder.RemoteFolderId).ConfigureAwait(false); + } + else if (IsResourceUpdated(item.AdditionalData)) + { + // Some of the properties of the item are updated. + + if (item.IsRead != null) + { + await _outlookChangeProcessor.ChangeMailReadStatusAsync(item.Id, item.IsRead.GetValueOrDefault()).ConfigureAwait(false); + } + + if (item.Flag?.FlagStatus != null) + { + await _outlookChangeProcessor.ChangeFlagStatusAsync(item.Id, item.Flag.FlagStatus.GetValueOrDefault() == FollowupFlagStatus.Flagged) + .ConfigureAwait(false); + } + } + else + { + // Package may return null on some cases mapping the remote draft to existing local draft. + + var newMailPackages = await CreateNewMailPackagesAsync(item, folder, cancellationToken); + + if (newMailPackages != null) + { + foreach (var package in newMailPackages) + { + // Only add to downloaded message ids if it's inserted successfuly. + // Updates should not be added to the list because they are not new. + bool isInserted = await _outlookChangeProcessor.CreateMailAsync(Account.Id, package).ConfigureAwait(false); + + if (isInserted) + { + downloadedMessageIds.Add(package.Copy.Id); + } + } + } + } + + return true; + } + + private async Task SynchronizeFoldersAsync(CancellationToken cancellationToken = default) + { + // Gather special folders by default. + // Others will be other type. + + // Get well known folder ids by batch. + + var wellKnownFolderIdBatch = new BatchRequestContentCollection(_graphClient); + + var inboxRequest = _graphClient.Me.MailFolders[INBOX_NAME].ToGetRequestInformation((t) => { t.QueryParameters.Select = ["id"]; }); + var sentRequest = _graphClient.Me.MailFolders[SENT_NAME].ToGetRequestInformation((t) => { t.QueryParameters.Select = ["id"]; }); + var deletedRequest = _graphClient.Me.MailFolders[DELETED_NAME].ToGetRequestInformation((t) => { t.QueryParameters.Select = ["id"]; }); + var junkRequest = _graphClient.Me.MailFolders[JUNK_NAME].ToGetRequestInformation((t) => { t.QueryParameters.Select = ["id"]; }); + var draftsRequest = _graphClient.Me.MailFolders[DRAFTS_NAME].ToGetRequestInformation((t) => { t.QueryParameters.Select = ["id"]; }); + var archiveRequest = _graphClient.Me.MailFolders[ARCHIVE_NAME].ToGetRequestInformation((t) => { t.QueryParameters.Select = ["id"]; }); + + var inboxId = await wellKnownFolderIdBatch.AddBatchRequestStepAsync(inboxRequest); + var sentId = await wellKnownFolderIdBatch.AddBatchRequestStepAsync(sentRequest); + var deletedId = await wellKnownFolderIdBatch.AddBatchRequestStepAsync(deletedRequest); + var junkId = await wellKnownFolderIdBatch.AddBatchRequestStepAsync(junkRequest); + var draftsId = await wellKnownFolderIdBatch.AddBatchRequestStepAsync(draftsRequest); + var archiveId = await wellKnownFolderIdBatch.AddBatchRequestStepAsync(archiveRequest); + + var returnedResponse = await _graphClient.Batch.PostAsync(wellKnownFolderIdBatch, cancellationToken).ConfigureAwait(false); + + var inboxFolderId = (await returnedResponse.GetResponseByIdAsync(inboxId)).Id; + var sentFolderId = (await returnedResponse.GetResponseByIdAsync(sentId)).Id; + var deletedFolderId = (await returnedResponse.GetResponseByIdAsync(deletedId)).Id; + var junkFolderId = (await returnedResponse.GetResponseByIdAsync(junkId)).Id; + var draftsFolderId = (await returnedResponse.GetResponseByIdAsync(draftsId)).Id; + var archiveFolderId = (await returnedResponse.GetResponseByIdAsync(archiveId)).Id; + + var specialFolderInfo = new OutlookSpecialFolderIdInformation(inboxFolderId, deletedFolderId, junkFolderId, draftsFolderId, sentFolderId, archiveFolderId); + + Microsoft.Graph.Me.MailFolders.Delta.DeltaGetResponse graphFolders = null; + + if (string.IsNullOrEmpty(Account.SynchronizationDeltaIdentifier)) + { + // Initial folder sync. + + var deltaRequest = _graphClient.Me.MailFolders.Delta.ToGetRequestInformation(); + + deltaRequest.UrlTemplate = deltaRequest.UrlTemplate.Insert(deltaRequest.UrlTemplate.Length - 1, ",includehiddenfolders"); + deltaRequest.QueryParameters.Add("includehiddenfolders", "true"); + + graphFolders = await _graphClient.RequestAdapter.SendAsync(deltaRequest, + Microsoft.Graph.Me.MailFolders.Delta.DeltaGetResponse.CreateFromDiscriminatorValue, + cancellationToken: cancellationToken); + } + else + { + var currentDeltaLink = Account.SynchronizationDeltaIdentifier; + + var deltaRequest = _graphClient.Me.MailFolders.Delta.ToGetRequestInformation(); + + deltaRequest.UrlTemplate = deltaRequest.UrlTemplate.Insert(deltaRequest.UrlTemplate.Length - 1, ",%24deltaToken"); + deltaRequest.QueryParameters.Add("%24deltaToken", currentDeltaLink); + graphFolders = await _graphClient.RequestAdapter.SendAsync(deltaRequest, + Microsoft.Graph.Me.MailFolders.Delta.DeltaGetResponse.CreateFromDiscriminatorValue, + cancellationToken: cancellationToken); + } + + var iterator = PageIterator.CreatePageIterator(_graphClient, graphFolders, (folder) => + { + return HandleFolderRetrievedAsync(folder, specialFolderInfo, cancellationToken); + }); + + await iterator.IterateAsync(); + + if (!string.IsNullOrEmpty(iterator.Deltalink)) + { + // Get the second part of the query that its the deltaToken + var deltaToken = iterator.Deltalink.Split('=')[1]; + + var latestAccountDeltaToken = await _outlookChangeProcessor.UpdateAccountDeltaSynchronizationIdentifierAsync(Account.Id, deltaToken); + + if (!string.IsNullOrEmpty(latestAccountDeltaToken)) + { + Account.SynchronizationDeltaIdentifier = latestAccountDeltaToken; + } + } + } + + #region Mail Integration + + public override IEnumerable> Move(BatchMoveRequest request) + { + var requestBody = new Microsoft.Graph.Me.Messages.Item.Move.MovePostRequestBody() + { + DestinationId = request.ToFolder.RemoteFolderId + }; + + return CreateBatchedHttpBundle(request, (item) => + { + return _graphClient.Me.Messages[item.Item.Id.ToString()].Move.ToPostRequestInformation(requestBody); + }); + } + + public override IEnumerable> ChangeFlag(BatchChangeFlagRequest request) + { + return CreateBatchedHttpBundle(request, (item) => + { + var message = new Message() + { + Flag = new FollowupFlag() { FlagStatus = request.IsFlagged ? FollowupFlagStatus.Flagged : FollowupFlagStatus.NotFlagged } + }; + + return _graphClient.Me.Messages[item.Item.Id.ToString()].ToPatchRequestInformation(message); + }); + } + + public override IEnumerable> MarkRead(BatchMarkReadRequest request) + { + return CreateBatchedHttpBundle(request, (item) => + { + var message = new Message() + { + IsRead = request.IsRead + }; + + return _graphClient.Me.Messages[item.Item.Id].ToPatchRequestInformation(message); + }); + } + + public override IEnumerable> Delete(BatchDeleteRequest request) + { + return CreateBatchedHttpBundle(request, (item) => + { + return _graphClient.Me.Messages[item.Item.Id].ToDeleteRequestInformation(); + }); + } + + public override IEnumerable> MoveToFocused(BatchMoveToFocusedRequest request) + { + return CreateBatchedHttpBundleFromGroup(request, (item) => + { + if (item is MoveToFocusedRequest moveToFocusedRequest) + { + var message = new Message() + { + InferenceClassification = moveToFocusedRequest.MoveToFocused ? InferenceClassificationType.Focused : InferenceClassificationType.Other + }; + + return _graphClient.Me.Messages[moveToFocusedRequest.Item.Id].ToPatchRequestInformation(message); + } + + throw new Exception("Invalid request type."); + }); + + } + + public override IEnumerable> AlwaysMoveTo(BatchAlwaysMoveToRequest request) + { + return CreateBatchedHttpBundle(request, (item) => + { + if (item is AlwaysMoveToRequest alwaysMoveToRequest) + { + var inferenceClassificationOverride = new InferenceClassificationOverride + { + ClassifyAs = alwaysMoveToRequest.MoveToFocused ? InferenceClassificationType.Focused : InferenceClassificationType.Other, + SenderEmailAddress = new EmailAddress + { + Name = alwaysMoveToRequest.Item.FromName, + Address = alwaysMoveToRequest.Item.FromAddress + } + }; + + return _graphClient.Me.InferenceClassification.Overrides.ToPostRequestInformation(inferenceClassificationOverride); + } + + throw new Exception("Invalid request type."); + }); + } + + public override IEnumerable> CreateDraft(BatchCreateDraftRequest request) + { + return CreateHttpBundle(request, (item) => + { + if (item is CreateDraftRequest createDraftRequest) + { + createDraftRequest.DraftPreperationRequest.CreatedLocalDraftMimeMessage.Prepare(EncodingConstraint.None); + + var plainTextBytes = Encoding.UTF8.GetBytes(createDraftRequest.DraftPreperationRequest.CreatedLocalDraftMimeMessage.ToString()); + var base64Encoded = Convert.ToBase64String(plainTextBytes); + + var requestInformation = _graphClient.Me.Messages.ToPostRequestInformation(new Message()); + + requestInformation.Headers.Clear();// replace the json content header + requestInformation.Headers.Add("Content-Type", "text/plain"); + + requestInformation.SetStreamContent(new MemoryStream(Encoding.UTF8.GetBytes(base64Encoded)), "text/plain"); + + return requestInformation; + } + + return default; + }); + } + + public override async Task DownloadMissingMimeMessageAsync(IMailItem mailItem, + MailKit.ITransferProgress transferProgress = null, + CancellationToken cancellationToken = default) + { + var mimeMessage = await DownloadMimeMessageAsync(mailItem.Id, cancellationToken).ConfigureAwait(false); + await _outlookChangeProcessor.SaveMimeFileAsync(mailItem.FileId, mimeMessage, Account.Id).ConfigureAwait(false); + } + + #endregion + + public override async Task ExecuteNativeRequestsAsync(IEnumerable> batchedRequests, CancellationToken cancellationToken = default) + { + var batchRequestInformations = BatchExtension.Batch(batchedRequests, (int)MaximumAllowedBatchRequestSize); + + foreach (var batch in batchRequestInformations) + { + var batchContent = new BatchRequestContentCollection(_graphClient); + + var itemCount = batch.Count(); + + for (int i = 0; i < itemCount; i++) + { + var bundle = batch.ElementAt(i); + + var request = bundle.Request; + var nativeRequest = bundle.NativeRequest; + + request.ApplyUIChanges(); + + await batchContent.AddBatchRequestStepAsync(nativeRequest).ConfigureAwait(false); + + // Map BundleId to batch request step's key. + // This is how we can identify which step succeeded or failed in the bundle. + + bundle.BundleId = batchContent.BatchRequestSteps.ElementAt(i).Key; + } + + if (!batchContent.BatchRequestSteps.Any()) + continue; + + // Execute batch. This will collect responses from network call for each batch step. + var batchRequestResponse = await _graphClient.Batch.PostAsync(batchContent).ConfigureAwait(false); + + // Check responses for each bundle id. + // Each bundle id must return some HttpResponseMessage ideally. + + var bundleIds = batchContent.BatchRequestSteps.Select(a => a.Key); + + // TODO: Handling responses. They used to work in v1 core, but not in v2. + + foreach (var bundleId in bundleIds) + { + var bundle = batch.FirstOrDefault(a => a.BundleId == bundleId); + + if (bundle == null) + continue; + + var httpResponseMessage = await batchRequestResponse.GetResponseByIdAsync(bundleId); + + using (httpResponseMessage) + { + await ProcessSingleNativeRequestResponseAsync(bundle, httpResponseMessage, cancellationToken).ConfigureAwait(false); + } + } + } + } + + private async Task ProcessSingleNativeRequestResponseAsync(IRequestBundle bundle, + HttpResponseMessage httpResponseMessage, + CancellationToken cancellationToken = default) + { + if (bundle is HttpRequestBundle messageBundle) + { + var outlookMessage = await messageBundle.DeserializeBundleAsync(httpResponseMessage, cancellationToken); + + if (outlookMessage == null) return; + + // TODO: Handle new message added or updated. + } + else if (bundle is HttpRequestBundle folderBundle) + { + var outlookFolder = await folderBundle.DeserializeBundleAsync(httpResponseMessage, cancellationToken); + + if (outlookFolder == null) return; + + // TODO: Handle new folder added or updated. + } + else if (bundle is HttpRequestBundle mimeBundle) + { + // TODO: Handle mime retrieve message. + } + else if (!httpResponseMessage.IsSuccessStatusCode) + { + // TODO: Should we even handle this? + throw new SynchronizerException(string.Format(Translator.Exception_SynchronizerFailureHTTP, httpResponseMessage.StatusCode)); + } + } + + private async Task DownloadMimeMessageAsync(string messageId, CancellationToken cancellationToken = default) + { + var mimeContentStream = await _graphClient.Me.Messages[messageId].Content.GetAsync(null, cancellationToken).ConfigureAwait(false); + return await MimeMessage.LoadAsync(mimeContentStream).ConfigureAwait(false); + } + + public override async Task> CreateNewMailPackagesAsync(Message message, MailItemFolder assignedFolder, CancellationToken cancellationToken = default) + { + var mimeMessage = await DownloadMimeMessageAsync(message.Id).ConfigureAwait(false); + var mailCopy = message.AsMailCopy(); + + if (mimeMessage.Headers.Contains(Domain.Constants.WinoLocalDraftHeader) + && Guid.TryParse(mimeMessage.Headers[Domain.Constants.WinoLocalDraftHeader], out Guid localDraftCopyUniqueId)) + { + // This message belongs to existing local draft copy. + // We don't need to create a new mail copy for this message, just update the existing one. + + bool isMappingSuccessful = await _outlookChangeProcessor.MapLocalDraftAsync(Account.Id, localDraftCopyUniqueId, mailCopy.Id, mailCopy.DraftId, mailCopy.ThreadId); + + if (isMappingSuccessful) return null; + + // Local copy doesn't exists. Continue execution to insert mail copy. + } + + // Outlook messages can only be assigned to 1 folder at a time. + // Therefore we don't need to create multiple copies of the same message for different folders. + var package = new NewMailItemPackage(mailCopy, mimeMessage, assignedFolder.RemoteFolderId); + + return [package]; + } + } +} diff --git a/Wino.Core/Wino.Core.csproj b/Wino.Core/Wino.Core.csproj new file mode 100644 index 00000000..7e12fb03 --- /dev/null +++ b/Wino.Core/Wino.Core.csproj @@ -0,0 +1,48 @@ + + + + netstandard2.0 + Wino.Core + Debug;Release + 12 + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Core/WinoErrors.cs b/Wino.Core/WinoErrors.cs new file mode 100644 index 00000000..1ef74d87 --- /dev/null +++ b/Wino.Core/WinoErrors.cs @@ -0,0 +1,42 @@ +namespace Wino.Core +{ + /// + /// Error codes for Wino application. + /// Pretty outdated. + /// + public static class WinoErrors + { + public const string AccountStructureRender = nameof(AccountStructureRender); + public const string MimeRendering = nameof(MimeRendering); + public const string MailRendering = nameof(MailRendering); + public const string FolderOperationExecution = nameof(FolderOperationExecution); + public const string StartupAccountExtendFail = nameof(StartupAccountExtendFail); + public const string AccountNavigateInboxFail = nameof(AccountNavigateInboxFail); + public const string AccountCreation = nameof(AccountCreation); + + public const string OutlookIntegratorFolderSync = nameof(OutlookIntegratorFolderSync); + public const string GoogleSynchronizerAccountSync = nameof(GoogleSynchronizerAccountSync); + public const string ImapFolderSync = nameof(ImapFolderSync); + + public const string RendererCommandMailOperation = nameof(RendererCommandMailOperation); + public const string MailListingMailOperation = nameof(MailListingMailOperation); + + public const string AutoMarkAsRead = nameof(AutoMarkAsRead); + public const string MailListGetItem = nameof(MailListGetItem); + public const string MailListCollectionUpdate = nameof(MailListCollectionUpdate); + public const string MailListRefreshFolder = nameof(MailListRefreshFolder); + public const string ProcessorTaskFailed = nameof(ProcessorTaskFailed); + public const string SearchFailed = nameof(SearchFailed); + + public const string BatchExecutionFailed = nameof(BatchExecutionFailed); + public const string SingleBatchExecutionFailedGoogle = nameof(SingleBatchExecutionFailedGoogle); + + public const string SynchronizationWorkerException = nameof(SynchronizationWorkerException); + public const string StoreRatingSubmission = nameof(StoreRatingSubmission); + + public const string OpenAttachment = nameof(OpenAttachment); + public const string SaveAttachment = nameof(SaveAttachment); + + public const string OutlookMimeSaveFailure = nameof(OutlookMimeSaveFailure); + } +} diff --git a/Wino.Core/WinoSynchronizerFactory.cs b/Wino.Core/WinoSynchronizerFactory.cs new file mode 100644 index 00000000..58a31ee6 --- /dev/null +++ b/Wino.Core/WinoSynchronizerFactory.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Wino.Core.Authenticators; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Integration.Processors; +using Wino.Core.Services; +using Wino.Core.Synchronizers; + +namespace Wino.Core +{ + public interface IWinoSynchronizerFactory : IInitializeAsync + { + IBaseSynchronizer GetAccountSynchronizer(Guid accountId); + IBaseSynchronizer CreateNewSynchronizer(MailAccount account); + void DeleteSynchronizer(MailAccount account); + } + + /// + /// Factory that keeps track of all integrator with associated mail accounts. + /// Synchronizer per-account makes sense because re-generating synchronizers are not ideal. + /// Users might interact with multiple accounts in 1 app session. + /// + public class WinoSynchronizerFactory : IWinoSynchronizerFactory + { + private readonly List synchronizerCache = new List(); + + private bool isInitialized = false; + private readonly INativeAppService _nativeAppService; + private readonly ITokenService _tokenService; + private readonly IFolderService _folderService; + private readonly IAccountService _accountService; + private readonly IContactService _contactService; + + private readonly INotificationBuilder _notificationBuilder; + private readonly ISignatureService _signatureService; + private readonly IDatabaseService _databaseService; + private readonly IMimeFileService _mimeFileService; + private readonly IDefaultChangeProcessor _defaultChangeProcessor; + + public WinoSynchronizerFactory(INativeAppService nativeAppService, + ITokenService tokenService, + IFolderService folderService, + IAccountService accountService, + IContactService contactService, + INotificationBuilder notificationBuilder, + ISignatureService signatureService, + IDatabaseService databaseService, + IMimeFileService mimeFileService, + IDefaultChangeProcessor defaultChangeProcessor) + { + _contactService = contactService; + _notificationBuilder = notificationBuilder; + _nativeAppService = nativeAppService; + _tokenService = tokenService; + _folderService = folderService; + _accountService = accountService; + _signatureService = signatureService; + _databaseService = databaseService; + _mimeFileService = mimeFileService; + _defaultChangeProcessor = defaultChangeProcessor; + } + + public IBaseSynchronizer GetAccountSynchronizer(Guid accountId) + => synchronizerCache.Find(a => a.Account.Id == accountId); + + private IBaseSynchronizer CreateIntegratorWithDefaultProcessor(MailAccount mailAccount) + { + var providerType = mailAccount.ProviderType; + + switch (providerType) + { + case Domain.Enums.MailProviderType.Outlook: + var outlookAuthenticator = new OutlookAuthenticator(_tokenService, _nativeAppService); + return new OutlookSynchronizer(mailAccount, outlookAuthenticator, _defaultChangeProcessor); + case Domain.Enums.MailProviderType.Gmail: + var gmailAuthenticator = new GmailAuthenticator(_tokenService, _nativeAppService); + + return new GmailSynchronizer(mailAccount, gmailAuthenticator, _defaultChangeProcessor); + case Domain.Enums.MailProviderType.Office365: + break; + case Domain.Enums.MailProviderType.Yahoo: + break; + case Domain.Enums.MailProviderType.IMAP4: + return new ImapSynchronizer(mailAccount, _defaultChangeProcessor); + default: + break; + } + + return null; + } + + public void DeleteSynchronizer(MailAccount account) + { + var synchronizer = GetAccountSynchronizer(account.Id); + + if (synchronizer == null) return; + + synchronizerCache.Remove(synchronizer); + } + + public async Task InitializeAsync() + { + if (isInitialized) return; + + var accounts = await _accountService.GetAccountsAsync(); + + foreach (var account in accounts) + { + CreateNewSynchronizer(account); + } + + isInitialized = true; + } + + public IBaseSynchronizer CreateNewSynchronizer(MailAccount account) + { + var synchronizer = CreateIntegratorWithDefaultProcessor(account); + + synchronizerCache.Add(synchronizer); + + return synchronizer; + } + } +} diff --git a/Wino.Mail.ViewModels/AboutPageViewModel.cs b/Wino.Mail.ViewModels/AboutPageViewModel.cs new file mode 100644 index 00000000..3b363854 --- /dev/null +++ b/Wino.Mail.ViewModels/AboutPageViewModel.cs @@ -0,0 +1,120 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.Input; +using Wino.Core.Domain; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Services; + +namespace Wino.Mail.ViewModels +{ + public class AboutPageViewModel : BaseViewModel + { + private readonly IStoreRatingService _storeRatingService; + private readonly INativeAppService _nativeAppService; + private readonly IAppInitializerService _appInitializerService; + private readonly IFileService _fileService; + private readonly ILogInitializer _logInitializer; + + public string VersionName => _nativeAppService.GetFullAppVersion(); + public string DiscordChannelUrl => "https://discord.gg/windows-apps-hub-714581497222398064"; + public string GitHubUrl => "https://github.com/bkaankose/Wino-Mail/"; + public string PrivacyPolicyUrl => "https://www.winomail.app/privacy_policy.html"; + public string PaypalUrl => "https://paypal.me/bkaankose?country.x=PL&locale.x=en_US"; + + public AsyncRelayCommand NavigateCommand { get; set; } + public AsyncRelayCommand ShareWinoLogCommand { get; set; } + public AsyncRelayCommand ShareProtocolLogCommand { get; set; } + public IPreferencesService PreferencesService { get; } + + public AboutPageViewModel(IStoreRatingService storeRatingService, + IDialogService dialogService, + INativeAppService nativeAppService, + IPreferencesService preferencesService, + IAppInitializerService appInitializerService, + IFileService fileService, + ILogInitializer logInitializer) : base(dialogService) + { + _storeRatingService = storeRatingService; + _nativeAppService = nativeAppService; + _logInitializer = logInitializer; + _appInitializerService = appInitializerService; + _fileService = fileService; + + PreferencesService = preferencesService; + NavigateCommand = new AsyncRelayCommand(Navigate); + ShareWinoLogCommand = new AsyncRelayCommand(ShareWinoLogAsync); + ShareProtocolLogCommand = new AsyncRelayCommand(ShareProtocolLogAsync); + } + + protected override void OnActivated() + { + base.OnActivated(); + + PreferencesService.PreferenceChanged -= PreferencesChanged; + PreferencesService.PreferenceChanged += PreferencesChanged; + } + + protected override void OnDeactivated() + { + base.OnDeactivated(); + + PreferencesService.PreferenceChanged -= PreferencesChanged; + } + + private void PreferencesChanged(object sender, string e) + { + if (e == nameof(PreferencesService.IsLoggingEnabled)) + { + _logInitializer.RefreshLoggingLevel(); + } + } + + private Task ShareProtocolLogAsync() + => SaveLogInternalAsync(ImapTestService.ProtocolLogFileName); + private Task ShareWinoLogAsync() + => SaveLogInternalAsync(LogInitializer.WinoLogFileName); + + private async Task SaveLogInternalAsync(string sourceFileName) + { + var appDataFolder = _appInitializerService.GetApplicationDataFolder(); + + var logFile = Path.Combine(appDataFolder, sourceFileName); + + if (!File.Exists(logFile)) + { + DialogService.InfoBarMessage(Translator.Info_LogsNotFoundTitle, Translator.Info_LogsNotFoundMessage, Core.Domain.Enums.InfoBarMessageType.Warning); + return; + } + + var selectedFolderPath = await DialogService.PickWindowsFolderAsync(); + + if (string.IsNullOrEmpty(selectedFolderPath)) return; + + var copiedFilePath = await _fileService.CopyFileAsync(logFile, selectedFolderPath); + + var copiedFileName = Path.GetFileName(copiedFilePath); + + DialogService.InfoBarMessage(Translator.Info_LogsSavedTitle, string.Format(Translator.Info_LogsSavedMessage, copiedFileName), Core.Domain.Enums.InfoBarMessageType.Success); + } + + private async Task Navigate(object url) + { + if (url is string stringUrl) + { + if (stringUrl == "Store") + await ShowRateDialogAsync(); + else + { + // Discord disclaimer message about server. + if (stringUrl == DiscordChannelUrl) + await DialogService.ShowMessageAsync(Translator.DiscordChannelDisclaimerMessage, Translator.DiscordChannelDisclaimerTitle); + + await _nativeAppService.LaunchUriAsync(new Uri(stringUrl)); + } + } + } + + private Task ShowRateDialogAsync() => _storeRatingService.LaunchStorePageForReviewAsync(); + } +} diff --git a/Wino.Mail.ViewModels/AccountDetailsPageViewModel.cs b/Wino.Mail.ViewModels/AccountDetailsPageViewModel.cs new file mode 100644 index 00000000..1a60a282 --- /dev/null +++ b/Wino.Mail.ViewModels/AccountDetailsPageViewModel.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using Wino.Core; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Folders; +using Wino.Core.Domain.Models.Navigation; +using Wino.Core.Messages.Navigation; +using Wino.Core.Requests; + +namespace Wino.Mail.ViewModels +{ + public partial class AccountDetailsPageViewModel : BaseViewModel + { + private readonly IWinoSynchronizerFactory _synchronizerFactory; + private readonly IAccountService _accountService; + private readonly IFolderService _folderService; + + public MailAccount Account { get; set; } + public ObservableCollection CurrentFolders { get; set; } = new ObservableCollection(); + + [ObservableProperty] + private bool isFocusedInboxEnabled; + + [ObservableProperty] + private bool areNotificationsEnabled; + + [ObservableProperty] + private bool isAppendMessageSettingVisible; + + [ObservableProperty] + private bool isAppendMessageSettinEnabled; + + public bool IsFocusedInboxSupportedForAccount => Account != null && Account.Preferences.IsFocusedInboxEnabled != null; + + + public AccountDetailsPageViewModel(IDialogService dialogService, + IWinoSynchronizerFactory synchronizerFactory, + IAccountService accountService, + IFolderService folderService) : base(dialogService) + { + _synchronizerFactory = synchronizerFactory; + _accountService = accountService; + _folderService = folderService; + } + + [RelayCommand] + private Task SetupSpecialFolders() + => DialogService.HandleSystemFolderConfigurationDialogAsync(Account.Id, _folderService); + + [RelayCommand] + private void EditSignature() + => Messenger.Send(new BreadcrumbNavigationRequested("Signature", WinoPage.SignatureManagementPage, Account.Id)); + + public Task FolderSyncToggledAsync(IMailItemFolder folderStructure, bool isEnabled) + => _folderService.ChangeFolderSynchronizationStateAsync(folderStructure.Id, isEnabled); + + public Task FolderShowUnreadToggled(IMailItemFolder folderStructure, bool isEnabled) + => _folderService.ChangeFolderShowUnreadCountStateAsync(folderStructure.Id, isEnabled); + + [RelayCommand] + private async Task RenameAccount() + { + if (Account == null) + return; + + var updatedAccount = await DialogService.ShowEditAccountDialogAsync(Account); + + if (updatedAccount != null) + { + await _accountService.UpdateAccountAsync(updatedAccount); + + ReportUIChange(new AccountUpdatedMessage(updatedAccount)); + } + } + + [RelayCommand] + private async Task DeleteAccount() + { + if (Account == null) + return; + + var confirmation = await DialogService.ShowConfirmationDialogAsync(Translator.DialogMessage_DeleteAccountConfirmationTitle, + string.Format(Translator.DialogMessage_DeleteAccountConfirmationMessage, Account.Name), + Translator.Buttons_Delete); + + if (!confirmation) + return; + + await _accountService.DeleteAccountAsync(Account); + + _synchronizerFactory.DeleteSynchronizer(Account); + + // TODO: Clear existing requests. + // _synchronizationWorker.ClearRequests(Account.Id); + + DialogService.InfoBarMessage(Translator.Info_AccountDeletedTitle, string.Format(Translator.Info_AccountDeletedMessage, Account.Name), InfoBarMessageType.Success); + + Messenger.Send(new BackBreadcrumNavigationRequested()); + } + + public override async void OnNavigatedTo(NavigationMode mode, object parameters) + { + base.OnNavigatedTo(mode, parameters); + + if (parameters is Guid accountId) + { + Account = await _accountService.GetAccountAsync(accountId); + + IsFocusedInboxEnabled = Account.Preferences.IsFocusedInboxEnabled.GetValueOrDefault(); + AreNotificationsEnabled = Account.Preferences.IsNotificationsEnabled; + + IsAppendMessageSettingVisible = Account.ProviderType == MailProviderType.IMAP4; + IsAppendMessageSettinEnabled = Account.Preferences.ShouldAppendMessagesToSentFolder; + + OnPropertyChanged(nameof(IsFocusedInboxSupportedForAccount)); + + var folderStructures = (await _folderService.GetFolderStructureForAccountAsync(Account.Id, true)).Folders; + + foreach (var folder in folderStructures) + { + CurrentFolders.Add(folder); + } + } + } + + protected override async void OnPropertyChanged(PropertyChangedEventArgs e) + { + base.OnPropertyChanged(e); + + if (e.PropertyName == nameof(IsFocusedInboxEnabled) && IsFocusedInboxSupportedForAccount) + { + Account.Preferences.IsFocusedInboxEnabled = IsFocusedInboxEnabled; + + await _accountService.UpdateAccountAsync(Account); + } + else if (e.PropertyName == nameof(AreNotificationsEnabled)) + { + Account.Preferences.IsNotificationsEnabled = AreNotificationsEnabled; + + await _accountService.UpdateAccountAsync(Account); + } + else if (e.PropertyName == nameof(IsAppendMessageSettinEnabled)) + { + Account.Preferences.ShouldAppendMessagesToSentFolder = IsAppendMessageSettinEnabled; + + await _accountService.UpdateAccountAsync(Account); + } + } + } +} diff --git a/Wino.Mail.ViewModels/AccountManagementViewModel.cs b/Wino.Mail.ViewModels/AccountManagementViewModel.cs new file mode 100644 index 00000000..d2f53972 --- /dev/null +++ b/Wino.Mail.ViewModels/AccountManagementViewModel.cs @@ -0,0 +1,377 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using Microsoft.AppCenter.Crashes; +using Serilog; +using Wino.Core; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Exceptions; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Navigation; +using Wino.Core.Domain.Models.Store; +using Wino.Core.Domain.Models.Synchronization; +using Wino.Core.Messages.Authorization; +using Wino.Core.Messages.Navigation; +using Wino.Core.Requests; +using Wino.Mail.ViewModels.Data; + +namespace Wino.Mail.ViewModels +{ + public partial class AccountManagementViewModel : BaseViewModel, IRecipient + { + public int FREE_ACCOUNT_COUNT { get; } = 3; + + private readonly IDialogService _dialogService; + private readonly IAccountService _accountService; + private readonly IProviderService _providerService; + private readonly IFolderService _folderService; + private readonly IStoreManagementService _storeManagementService; + private readonly IPreferencesService _preferencesService; + private readonly IAuthenticationProvider _authenticationProvider; + private readonly IWinoSynchronizerFactory _synchronizerFactory; + + public ObservableCollection Accounts { get; set; } = []; + + public bool IsPurchasePanelVisible => !HasUnlimitedAccountProduct; + public bool IsAccountCreationAlmostOnLimit => Accounts != null && Accounts.Count == FREE_ACCOUNT_COUNT - 1; + public bool HasAccountsDefined => Accounts != null && Accounts.Any(); + + public string UsedAccountsString => string.Format(Translator.WinoUpgradeRemainingAccountsMessage, Accounts.Count, FREE_ACCOUNT_COUNT); + + [ObservableProperty] + private IAccountProviderDetailViewModel _startupAccount; + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(IsPurchasePanelVisible))] + private bool hasUnlimitedAccountProduct; + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(IsAccountCreationAlmostOnLimit))] + [NotifyPropertyChangedFor(nameof(IsPurchasePanelVisible))] + private bool isAccountCreationBlocked; + + public AccountManagementViewModel(IDialogService dialogService, + IWinoNavigationService navigationService, + IWinoSynchronizerFactory synchronizerFactory, + IAccountService accountService, + IProviderService providerService, + IFolderService folderService, + IStoreManagementService storeManagementService, + IPreferencesService preferencesService, + IAuthenticationProvider authenticationProvider) : base(dialogService) + { + _accountService = accountService; + _synchronizerFactory = synchronizerFactory; + _dialogService = dialogService; + _providerService = providerService; + _folderService = folderService; + _storeManagementService = storeManagementService; + _preferencesService = preferencesService; + _authenticationProvider = authenticationProvider; + } + + [RelayCommand] + private void NavigateAccountDetails(AccountProviderDetailViewModel accountDetails) + { + Messenger.Send(new BreadcrumbNavigationRequested(accountDetails.Account.Name, + WinoPage.AccountDetailsPage, + accountDetails.Account.Id)); + } + + [RelayCommand] + private async Task CreateMergedAccountAsync() + { + var linkName = await DialogService.ShowTextInputDialogAsync(string.Empty, Translator.DialogMessage_CreateLinkedAccountTitle, Translator.DialogMessage_CreateLinkedAccountMessage); + + if (string.IsNullOrEmpty(linkName)) return; + + // Create arbitary empty merged inbox with an empty Guid and go to edit page. + var mergedInbox = new MergedInbox() + { + Id = Guid.Empty, + Name = linkName + }; + + var mergedAccountProviderDetailViewModel = new MergedAccountProviderDetailViewModel(mergedInbox, new List()); + + Messenger.Send(new BreadcrumbNavigationRequested(mergedAccountProviderDetailViewModel.MergedInbox.Name, + WinoPage.MergedAccountDetailsPage, + mergedAccountProviderDetailViewModel)); + } + + [RelayCommand] + private async Task PurchaseUnlimitedAccountAsync() + { + var purchaseResult = await _storeManagementService.PurchaseAsync(StoreProductType.UnlimitedAccounts); + + if (purchaseResult == StorePurchaseResult.Succeeded) + DialogService.InfoBarMessage(Translator.Info_PurchaseThankYouTitle, Translator.Info_PurchaseThankYouMessage, InfoBarMessageType.Success); + else if (purchaseResult == StorePurchaseResult.AlreadyPurchased) + DialogService.InfoBarMessage(Translator.Info_PurchaseExistsTitle, Translator.Info_PurchaseExistsMessage, InfoBarMessageType.Warning); + + bool shouldRefreshPurchasePanel = purchaseResult == StorePurchaseResult.Succeeded || purchaseResult == StorePurchaseResult.AlreadyPurchased; + + if (shouldRefreshPurchasePanel) + { + await ManageStorePurchasesAsync(); + } + } + + [RelayCommand] + private async Task AddNewAccountAsync() + { + if (IsAccountCreationBlocked) + { + var isPurchaseClicked = await DialogService.ShowConfirmationDialogAsync(Translator.DialogMessage_AccountLimitMessage, Translator.DialogMessage_AccountLimitTitle, Translator.Buttons_Purchase); + + if (!isPurchaseClicked) return; + + await PurchaseUnlimitedAccountAsync(); + + return; + } + + MailAccount createdAccount = null; + IAccountCreationDialog creationDialog = null; + + try + { + var providers = _providerService.GetProviderDetails(); + + // Select provider. + var accountInformationTuple = await _dialogService.ShowNewAccountMailProviderDialogAsync(providers); + + if (accountInformationTuple != null) + { + creationDialog = _dialogService.GetAccountCreationDialog(accountInformationTuple.Item2); + + var accountName = accountInformationTuple.Item1; + var providerType = accountInformationTuple.Item2; + + _accountService.ExternalAuthenticationAuthenticator = _authenticationProvider.GetAuthenticator(providerType); + + CustomServerInformation customServerInformation = null; + + createdAccount = new MailAccount() + { + ProviderType = providerType, + Name = accountName, + Id = Guid.NewGuid() + }; + + creationDialog.ShowDialog(); + creationDialog.State = AccountCreationDialogState.SigningIn; + + TokenInformation tokenInformation = null; + + // Custom server implementation requires more async waiting. + if (creationDialog is ICustomServerAccountCreationDialog customServerDialog) + { + customServerInformation = await customServerDialog.GetCustomServerInformationAsync() + ?? throw new AccountSetupCanceledException(); + + // At this point connection is successful. + // Save the server setup information and later on we'll fetch folders. + + customServerInformation.AccountId = createdAccount.Id; + + createdAccount.Address = customServerInformation.Address; + createdAccount.ServerInformation = customServerInformation; + } + else + { + // For OAuth authentications, we just generate token and assign it to the MailAccount. + + tokenInformation = await _accountService.ExternalAuthenticationAuthenticator.GenerateTokenAsync(createdAccount, false) + ?? throw new AuthenticationException(Translator.Exception_TokenInfoRetrivalFailed); + + createdAccount.Address = tokenInformation.Address; + tokenInformation.AccountId = createdAccount.Id; + } + + await _accountService.CreateAccountAsync(createdAccount, tokenInformation, customServerInformation); + + // Local account has been created. + // Create new synchronizer and start synchronization. + + var synchronizer = _synchronizerFactory.CreateNewSynchronizer(createdAccount); + + if (creationDialog is ICustomServerAccountCreationDialog customServerAccountCreationDialog) + customServerAccountCreationDialog.ShowPreparingFolders(); + else + creationDialog.State = AccountCreationDialogState.PreparingFolders; + + var options = new SynchronizationOptions() + { + AccountId = createdAccount.Id, + Type = SynchronizationType.FoldersOnly + }; + + var synchronizationResult = await synchronizer.SynchronizeAsync(options); + + if (synchronizationResult.CompletedState != SynchronizationCompletedState.Success) + throw new Exception(Translator.Exception_FailedToSynchronizeFolders); + + // Check if Inbox folder is available for the account after synchronization. + var isInboxAvailable = await _folderService.IsInboxAvailableForAccountAsync(createdAccount.Id); + + if (!isInboxAvailable) + throw new Exception(Translator.Exception_InboxNotAvailable); + + // Send changes to listeners. + ReportUIChange(new AccountCreatedMessage(createdAccount)); + + // Notify success. + _dialogService.InfoBarMessage(Translator.Info_AccountCreatedTitle, string.Format(Translator.Info_AccountCreatedMessage, createdAccount.Address), InfoBarMessageType.Success); + } + } + catch (AccountSetupCanceledException) + { + // Ignore + } + catch (Exception ex) + { + Log.Error(ex, WinoErrors.AccountCreation); + Crashes.TrackError(ex); + + _dialogService.InfoBarMessage(Translator.Info_AccountCreationFailedTitle, ex.Message, InfoBarMessageType.Error); + + // Delete account in case of failure. + if (createdAccount != null) + { + await _accountService.DeleteAccountAsync(createdAccount); + } + } + finally + { + creationDialog?.Complete(); + } + } + + [RelayCommand] + private void EditMergedAccounts(MergedAccountProviderDetailViewModel mergedAccountProviderDetailViewModel) + { + Messenger.Send(new BreadcrumbNavigationRequested(mergedAccountProviderDetailViewModel.MergedInbox.Name, + WinoPage.MergedAccountDetailsPage, + mergedAccountProviderDetailViewModel)); + } + + public override void OnNavigatedFrom(NavigationMode mode, object parameters) + { + base.OnNavigatedFrom(mode, parameters); + + Accounts.CollectionChanged -= AccountCollectionChanged; + + PropertyChanged -= PagePropertyChanged; + } + + private void AccountCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + OnPropertyChanged(nameof(HasAccountsDefined)); + OnPropertyChanged(nameof(UsedAccountsString)); + } + + private void PagePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(StartupAccount) && StartupAccount != null) + { + _preferencesService.StartupEntityId = StartupAccount.StartupEntityId; + } + } + + public override async void OnNavigatedTo(NavigationMode mode, object parameters) + { + base.OnNavigatedTo(mode, parameters); + + Accounts.CollectionChanged -= AccountCollectionChanged; + Accounts.CollectionChanged += AccountCollectionChanged; + + await InitializeAccountsAsync(); + + PropertyChanged -= PagePropertyChanged; + PropertyChanged += PagePropertyChanged; + } + + private async Task InitializeAccountsAsync() + { + StartupAccount = null; + + Accounts.Clear(); + + var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false); + + // Group accounts and display merged ones at the top. + var groupedAccounts = accounts.GroupBy(a => a.MergedInboxId); + + await ExecuteUIThread(() => + { + foreach (var accountGroup in groupedAccounts) + { + var mergedInboxId = accountGroup.Key; + + if (mergedInboxId == null) + { + foreach (var account in accountGroup) + { + var accountDetails = GetAccountProviderDetails(account); + + Accounts.Add(accountDetails); + } + } + else + { + var mergedInbox = accountGroup.First(a => a.MergedInboxId == mergedInboxId).MergedInbox; + + var holdingAccountProviderDetails = accountGroup.Select(a => GetAccountProviderDetails(a)).ToList(); + var mergedAccountViewModel = new MergedAccountProviderDetailViewModel(mergedInbox, holdingAccountProviderDetails); + + Accounts.Add(mergedAccountViewModel); + } + } + + // Handle startup entity. + if (_preferencesService.StartupEntityId != null) + { + StartupAccount = Accounts.FirstOrDefault(a => a.StartupEntityId == _preferencesService.StartupEntityId); + } + }); + + + await ManageStorePurchasesAsync().ConfigureAwait(false); + } + + private async Task ManageStorePurchasesAsync() + { + await ExecuteUIThread(async () => + { + HasUnlimitedAccountProduct = await _storeManagementService.HasProductAsync(StoreProductType.UnlimitedAccounts); + + if (!HasUnlimitedAccountProduct) + IsAccountCreationBlocked = Accounts.Count >= FREE_ACCOUNT_COUNT; + else + IsAccountCreationBlocked = false; + }); + } + + private AccountProviderDetailViewModel GetAccountProviderDetails(MailAccount account) + { + var provider = _providerService.GetProviderDetail(account.ProviderType); + + return new AccountProviderDetailViewModel(provider, account); + } + + public void Receive(ProtocolAuthorizationCallbackReceived message) + { + // Authorization must be completed in account service. + + _accountService.ExternalAuthenticationAuthenticator?.ContinueAuthorization(message.AuthorizationResponseUri); + } + } +} diff --git a/Wino.Mail.ViewModels/AppShellViewModel.cs b/Wino.Mail.ViewModels/AppShellViewModel.cs new file mode 100644 index 00000000..3067d2b9 --- /dev/null +++ b/Wino.Mail.ViewModels/AppShellViewModel.cs @@ -0,0 +1,1063 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Messaging; +using Microsoft.AppCenter.Crashes; +using MoreLinq; +using MoreLinq.Extensions; +using Serilog; +using Wino.Core; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Exceptions; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Folders; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Navigation; +using Wino.Core.Domain.Models.Synchronization; +using Wino.Core.Extensions; +using Wino.Core.MenuItems; +using Wino.Core.Messages.Accounts; +using Wino.Core.Messages.Mails; +using Wino.Core.Messages.Navigation; +using Wino.Core.Messages.Shell; +using Wino.Core.Messages.Synchronization; +using Wino.Core.Requests; +using Wino.Core.Services; + +namespace Wino.Mail.ViewModels +{ + public partial class AppShellViewModel : BaseViewModel, + ISynchronizationProgress, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient + { + #region Menu Items + + [ObservableProperty] + private object selectedMenuItem; + + private IAccountMenuItem latestSelectedAccountMenuItem; + + public MenuItemCollection FooterItems { get; set; } = []; + public MenuItemCollection MenuItems { get; set; } = []; + + private readonly SettingsItem SettingsItem = new SettingsItem(); + + private readonly RateMenuItem RatingItem = new RateMenuItem(); + + private readonly ManageAccountsMenuItem ManageAccountsMenuItem = new ManageAccountsMenuItem(); + + public NewMailMenuItem CreateMailMenuItem = new NewMailMenuItem(); + + #endregion + + public IStatePersistanceService StatePersistenceService { get; } + public IPreferencesService PreferencesService { get; } + public IWinoNavigationService NavigationService { get; } + + private readonly IFolderService _folderService; + private readonly IAccountService _accountService; + private readonly IContextMenuItemService _contextMenuItemService; + private readonly IStoreRatingService _storeRatingService; + private readonly ILaunchProtocolService _launchProtocolService; + private readonly INotificationBuilder _notificationBuilder; + private readonly IWinoRequestDelegator _winoRequestDelegator; + + private readonly IWinoSynchronizerFactory _synchronizerFactory; + private readonly IBackgroundTaskService _backgroundTaskService; + private readonly IMimeFileService _mimeFileService; + + private readonly INativeAppService _nativeAppService; + private readonly IMailService _mailService; + + private readonly SemaphoreSlim accountInitFolderUpdateSlim = new SemaphoreSlim(1); + + public AppShellViewModel(IDialogService dialogService, + IWinoNavigationService navigationService, + IWinoSynchronizerFactory synchronizerFactory, + IBackgroundTaskService backgroundTaskService, + IMimeFileService mimeFileService, + INativeAppService nativeAppService, + IMailService mailService, + IAccountService accountService, + IContextMenuItemService contextMenuItemService, + IStoreRatingService storeRatingService, + IPreferencesService preferencesService, + ILaunchProtocolService launchProtocolService, + INotificationBuilder notificationBuilder, + IWinoRequestDelegator winoRequestDelegator, + IFolderService folderService, + IStatePersistanceService statePersistanceService) : base(dialogService) + { + StatePersistenceService = statePersistanceService; + PreferencesService = preferencesService; + NavigationService = navigationService; + + _synchronizerFactory = synchronizerFactory; + _backgroundTaskService = backgroundTaskService; + _mimeFileService = mimeFileService; + _nativeAppService = nativeAppService; + _mailService = mailService; + _folderService = folderService; + _accountService = accountService; + _contextMenuItemService = contextMenuItemService; + _storeRatingService = storeRatingService; + _launchProtocolService = launchProtocolService; + _notificationBuilder = notificationBuilder; + _winoRequestDelegator = winoRequestDelegator; + } + + public IEnumerable GetFolderContextMenuActions(IBaseFolderMenuItem folder) + { + if (folder == null || folder.SpecialFolderType == SpecialFolderType.Category || folder.SpecialFolderType == SpecialFolderType.More) + return default; + + return _contextMenuItemService.GetFolderContextMenuActions(folder); + } + + private async Task CreateFooterItemsAsync() + { + await ExecuteUIThread(() => + { + // TODO: Selected footer item container still remains selected after re-creation. + // To reproduce, go settings and change the language. + + foreach (var item in FooterItems) + { + item.IsExpanded = false; + item.IsSelected = false; + } + + FooterItems.Clear(); + + FooterItems.Add(ManageAccountsMenuItem); + FooterItems.Add(RatingItem); + FooterItems.Add(SettingsItem); + }); + } + + private async Task LoadAccountsAsync() + { + var accounts = await _accountService.GetAccountsAsync(); + + // Group accounts by merged account. + var groupedAccounts = accounts.GroupBy(a => a.MergedInboxId); + + foreach (var accountGroup in groupedAccounts) + { + var mergedInbox = accountGroup.Key; + + if (mergedInbox == null) + { + // This account is not merged. Create menu item for each account. + foreach (var account in accountGroup) + { + await CreateNestedAccountMenuItem(account); + } + } + else + { + // Accounts are merged. Create menu item for merged inbox. + await CreateMergedInboxMenuItemAsync(accountGroup); + } + } + + // Re-assign latest selected account menu item for containers to reflect changes better. + // Also , this will ensure that the latest selected account is still selected after re-creation. + + if (latestSelectedAccountMenuItem != null) + { + latestSelectedAccountMenuItem = MenuItems.GetAccountMenuItem(latestSelectedAccountMenuItem.EntityId.GetValueOrDefault()); + + if (latestSelectedAccountMenuItem != null) + { + latestSelectedAccountMenuItem.IsSelected = true; + } + } + } + + protected override async void OnFolderUpdated(MailItemFolder updatedFolder, MailAccount account) + { + base.OnFolderUpdated(updatedFolder, account); + + if (updatedFolder == null) return; + + var folderMenuItemsToUpdate = MenuItems.GetFolderItems(updatedFolder.Id); + + foreach (var item in folderMenuItemsToUpdate) + { + await ExecuteUIThread(() => + { + item.UpdateFolder(updatedFolder); + }); + } + } + + private async Task CreateMergedInboxMenuItemAsync(IEnumerable accounts) + { + var mergedInbox = accounts.First().MergedInbox; + var mergedInboxMenuItem = new MergedAccountMenuItem(mergedInbox, null); // Merged accounts are parentless. + + // Store common special type folders. + var commonFolderList = new Dictionary(); + + // Map special folder types for each account. + var accountTreeList = new List(); + + foreach (var account in accounts) + { + var accountStructure = await _folderService.GetFolderStructureForAccountAsync(account.Id, includeHiddenFolders: true); + accountTreeList.Add(accountStructure); + } + + var allFolders = accountTreeList.SelectMany(a => a.Folders); + + // 1. Group sticky folders by special folder type. + // 2. Merge all folders that are sticky and have the same special folder type. + // 3. Add merged folder menu items to the merged inbox menu item. + // 4. Add remaining sticky folders that doesn't exist in all accounts as plain folder menu items. + + var stickyFolders = allFolders.Where(a => a.IsSticky); + + var grouped = stickyFolders + .GroupBy(a => a.SpecialFolderType) + .Where(a => accountTreeList.All(b => b.HasSpecialTypeFolder(a.Key))); + + var mergedInboxItems = grouped.Select(a => new MergedAccountFolderMenuItem(a.ToList(), mergedInboxMenuItem, mergedInbox)); + + // Shared common folders. + foreach (var mergedInboxFolder in mergedInboxItems) + { + mergedInboxMenuItem.SubMenuItems.Add(mergedInboxFolder); + } + + var usedFolderIds = mergedInboxItems.SelectMany(a => a.Parameter.Select(a => a.Id)); + var remainingStickyFolders = stickyFolders.Where(a => !usedFolderIds.Contains(a.Id)); + + // Marked as sticky, but doesn't exist in all accounts. Add as plain folder menu item. + foreach (var remainingStickyFolder in remainingStickyFolders) + { + var account = accounts.FirstOrDefault(a => a.Id == remainingStickyFolder.MailAccountId); + mergedInboxMenuItem.SubMenuItems.Add(new FolderMenuItem(remainingStickyFolder, account, mergedInboxMenuItem)); + } + + + var mergedMoreItem = new MergedAccountMoreFolderMenuItem(null, null, mergedInboxMenuItem); + + // 2. Sticky folder preparation is done. Continue with regular account menu items. + + foreach (var accountTree in accountTreeList) + { + var tree = accountTree.GetAccountMenuTree(mergedInboxMenuItem); + + mergedMoreItem.SubMenuItems.Add(tree); + } + + mergedInboxMenuItem.SubMenuItems.Add(mergedMoreItem); + + MenuItems.Add(mergedInboxMenuItem); + + // Instead of refreshing all accounts, refresh the merged account only. + // Receiver will handle it. + + Messenger.Send(new RefreshUnreadCountsMessage(mergedInbox.Id)); + } + + private async Task CreateNestedAccountMenuItem(MailAccount account) + { + try + { + await accountInitFolderUpdateSlim.WaitAsync(); + + // Don't remove but replace existing record. + int existingIndex = -1; + + var existingAccountMenuItem = MenuItems.FirstOrDefault(a => a is AccountMenuItem accountMenuItem && accountMenuItem.Parameter.Id == account.Id); + + if (existingAccountMenuItem != null) + { + existingIndex = MenuItems.IndexOf(existingAccountMenuItem); + } + + // Create account structure with integrator for this menu item. + var accountStructure = await _folderService.GetFolderStructureForAccountAsync(account.Id, includeHiddenFolders: false); + + var createdMenuItem = accountStructure.GetAccountMenuTree(); + + await ExecuteUIThread(() => + { + if (existingIndex >= 0) + { + createdMenuItem.IsExpanded = existingAccountMenuItem.IsExpanded; + + MenuItems.RemoveAt(existingIndex); + MenuItems.Insert(existingIndex, createdMenuItem); + } + else + { + MenuItems.AddAccountMenuItem(createdMenuItem); + } + }); + + Messenger.Send(new RefreshUnreadCountsMessage(account.Id)); + + return createdMenuItem; + } + catch (Exception ex) + { + Log.Error(ex, WinoErrors.AccountStructureRender); + } + finally + { + accountInitFolderUpdateSlim.Release(); + } + + return null; + } + + public override async void OnNavigatedTo(NavigationMode mode, object parameters) + { + base.OnNavigatedTo(mode, parameters); + + await CreateFooterItemsAsync(); + + await RecreateMenuItemsAsync(); + await ProcessLaunchOptionsAsync(); + +#if !DEBUG + await ForceAllAccountSynchronizationsAsync(); +#endif + await ConfigureBackgroundTasksAsync(); + } + + private async Task ConfigureBackgroundTasksAsync() + { + try + { + await _backgroundTaskService.HandleBackgroundTaskRegistrations(); + } + catch (BackgroundTaskExecutionRequestDeniedException) + { + await DialogService.ShowMessageAsync(Translator.Info_BackgroundExecutionDeniedMessage, Translator.Info_BackgroundExecutionDeniedTitle); + } + catch (Exception ex) + { + Crashes.TrackError(ex); + + DialogService.InfoBarMessage(Translator.Info_BackgroundExecutionUnknownErrorTitle, Translator.Info_BackgroundExecutionUnknownErrorMessage, InfoBarMessageType.Error); + } + } + + private async Task ForceAllAccountSynchronizationsAsync() + { + // Run Inbox synchronization for all accounts on startup. + var accounts = await _accountService.GetAccountsAsync(); + + foreach (var account in accounts) + { + var options = new SynchronizationOptions() + { + AccountId = account.Id, + Type = SynchronizationType.Inbox + }; + + + Messenger.Send(new NewSynchronizationRequested(options)); + } + } + + // Navigate to startup account's Inbox. + private async Task ProcessLaunchOptionsAsync() + { + try + { + // Check whether we have saved navigation item from toast. + + bool hasToastActivation = _launchProtocolService.LaunchParameter != null; + + if (hasToastActivation) + { + if (_launchProtocolService.LaunchParameter is AccountMenuItemExtended accountExtendedMessage) + { + // Find the account that this folder and mail belongs to. + var account = await _mailService.GetMailAccountByUniqueIdAsync(accountExtendedMessage.NavigateMailItem.UniqueId).ConfigureAwait(false); + + if (account != null && MenuItems.GetAccountMenuItem(account.Id) is IAccountMenuItem accountMenuItem) + { + ChangeLoadedAccount(accountMenuItem); + + WeakReferenceMessenger.Default.Send(accountExtendedMessage); + + _launchProtocolService.LaunchParameter = null; + } + else + { + ProcessLaunchDefault(); + } + } + } + else + { + bool hasMailtoActivation = _launchProtocolService.MailtoParameters != null; + + if (hasMailtoActivation) + { + // mailto activation. Create new mail with specific delivered address as receiver. + + WeakReferenceMessenger.Default.Send(new MailtoProtocolMessageRequested()); + } + else + { + // Use default startup extending. + ProcessLaunchDefault(); + } + } + } + catch (Exception ex) + { + Log.Error(ex, WinoErrors.StartupAccountExtendFail); + } + } + + private void ProcessLaunchDefault() + { + if (PreferencesService.StartupEntityId == null) + { + NavigationService.Navigate(WinoPage.WelcomePage); + } + else + { + var startupEntityId = PreferencesService.StartupEntityId.Value; + + // startupEntityId is the id of the entity to be expanded on startup. + // This can be either AccountId or MergedAccountId right now. + // If accountId, we'll find the root account and extend Inbox folder for it. + // If mergedAccountId, merged account's Inbox folder will be extended. + + var startupEntityMenuItem = MenuItems.FirstOrDefault(a => a.EntityId == startupEntityId); + + if (startupEntityMenuItem != null) + { + startupEntityMenuItem.Expand(); + + if (startupEntityMenuItem is IAccountMenuItem startupAccountMenuItem) + { + ChangeLoadedAccount(startupAccountMenuItem); + } + } + } + } + + public async Task NavigateFolderAsync(IBaseFolderMenuItem baseFolderMenuItem) + { + // It's already there. Don't navigate again. + if (SelectedMenuItem == baseFolderMenuItem) return; + + SelectedMenuItem = baseFolderMenuItem; + baseFolderMenuItem.IsSelected = true; + + var mailInitCompletionSource = new TaskCompletionSource(); + var args = new NavigateMailFolderEventArgs(baseFolderMenuItem, mailInitCompletionSource); + + NavigationService.NavigateFolder(args); + StatePersistenceService.CoreWindowTitle = $"{baseFolderMenuItem.AssignedAccountName} - {baseFolderMenuItem.FolderName}"; + + // Wait until mail list page picks up the event and finish initialization of the mails. + await mailInitCompletionSource.Task; + } + + private async Task NavigateSpecialFolderAsync(MailAccount account, SpecialFolderType specialFolderType, bool extendAccountMenu) + { + try + { + if (account == null) return; + + // If the account is inside a merged account, expand the merged account and navigate to shared folder. + if (MenuItems.TryGetMergedAccountRootFolderMenuItemByAccountId(account.Id, specialFolderType, out MergedAccountFolderMenuItem mergedFolderItem)) + { + mergedFolderItem.Expand(); + await NavigateFolderAsync(mergedFolderItem); + } + else if (MenuItems.TryGetRootSpecialFolderMenuItem(account.Id, specialFolderType, out FolderMenuItem rootFolderMenuItem)) + { + // Account is not in merged account. Navigate to root folder. + + rootFolderMenuItem.Expand(); + await NavigateFolderAsync(rootFolderMenuItem); + } + } + catch (Exception ex) + { + Log.Error(ex, WinoErrors.AccountNavigateInboxFail); + } + } + + /// + /// Performs move operation for given items to target folder. + /// Used with drag and drop from Shell. + /// + /// Items to move. + /// Folder menu item to move to. Can be merged folder as well. + public async Task PerformMoveOperationAsync(IEnumerable items, IBaseFolderMenuItem targetFolderMenuItem) + { + if (!items.Any() || targetFolderMenuItem == null) return; + + // User dropped mails to merged account folder. + if (targetFolderMenuItem is IMergedAccountFolderMenuItem mergedAccountFolderMenuItem) + { + // Mail items must be grouped by their account and move + // operation should be targeted towards that account's special type. + // Multiple move packages will be created if there are multiple accounts. + + var folderSpecialType = mergedAccountFolderMenuItem.SpecialFolderType; + + var groupedByAccount = items.GroupBy(a => a.AssignedAccount.Id); + + foreach (var group in groupedByAccount) + { + var accountId = group.Key; + + // Find the target folder for this account. + var handlingAccountFolder = mergedAccountFolderMenuItem.HandlingFolders.FirstOrDefault(a => a.MailAccountId == accountId); + + if (handlingAccountFolder == null) + { + Log.Warning("Failed to find the account in the merged account folder menu item for account id {AccountId}", accountId); + continue; + } + + var package = new MailOperationPreperationRequest(MailOperation.Move, group, false, handlingAccountFolder); + await _winoRequestDelegator.ExecuteAsync(package); + } + } + else if (targetFolderMenuItem is IFolderMenuItem singleFolderMenuItem) + { + // User dropped mails to a single folder. + // Create a single move package for this folder. + + var package = new MailOperationPreperationRequest(MailOperation.Move, items, false, targetFolderMenuItem.HandlingFolders.First()); + + await _winoRequestDelegator.ExecuteAsync(package); + } + } + + public async Task PerformFolderOperationAsync(FolderOperation operation, IBaseFolderMenuItem folderMenuItem) + { + if (folderMenuItem == null) + return; + + // Ask confirmation for cleaning up the folder. + if (operation == FolderOperation.Empty) + { + var result = await DialogService.ShowConfirmationDialogAsync(Translator.DialogMessage_CleanupFolderMessage, Translator.DialogMessage_CleanupFolderTitle, Translator.Buttons_Yes); + + if (!result) return; + } + + foreach (var folder in folderMenuItem.HandlingFolders) + { + await _winoRequestDelegator.ExecuteAsync(operation, folder); + } + + // Refresh the pins. + if (operation == FolderOperation.Pin || operation == FolderOperation.Unpin) + { + Messenger.Send(new AccountsMenuRefreshRequested(true)); + } + } + + private async Task FixAccountIssuesAsync(MailAccount account) + { + // TODO: This area is very unclear. Needs to be rewritten with care. + // Fix account issues are expected to not work, but may work for some cases. + + try + { + if (account.AttentionReason == AccountAttentionReason.InvalidCredentials) + await _accountService.FixTokenIssuesAsync(account.Id); + else if (account.AttentionReason == AccountAttentionReason.MissingSystemFolderConfiguration) + await DialogService.HandleSystemFolderConfigurationDialogAsync(account.Id, _folderService); + + await _accountService.ClearAccountAttentionAsync(account.Id); + + DialogService.InfoBarMessage(Translator.Info_AccountIssueFixFailedTitle, Translator.Info_AccountIssueFixSuccessMessage, InfoBarMessageType.Success); + } + catch (Exception ex) + { + DialogService.InfoBarMessage(Translator.Info_AccountIssueFixFailedTitle, ex.Message, InfoBarMessageType.Error); + } + } + + public void NavigatePage(WinoPage winoPage) + { + NavigationService.Navigate(winoPage); + + StatePersistenceService.CoreWindowTitle = "Wino Mail"; + } + + public async Task MenuItemInvokedOrSelectedAsync(IMenuItem clickedMenuItem) + { + if (clickedMenuItem == null) return; + + // Regular menu item clicked without page navigation. + if (clickedMenuItem is FixAccountIssuesMenuItem fixAccountItem) + { + await FixAccountIssuesAsync(fixAccountItem.Account); + } + else if (clickedMenuItem is RateMenuItem) + { + await _storeRatingService.LaunchStorePageForReviewAsync(); + } + else if (clickedMenuItem is NewMailMenuItem) + { + await HandleCreateNewMailAsync(); + } + else if (clickedMenuItem is IBaseFolderMenuItem baseFolderMenuItem && baseFolderMenuItem.HandlingFolders.All(a => a.IsMoveTarget)) + { + // Don't navigate to base folders that contain non-move target folders. + // Theory: This is a special folder like Categories or More. Don't navigate to it. + + // Prompt user rating dialog if eligible. + _ = _storeRatingService.PromptRatingDialogAsync(); + + await NavigateFolderAsync(baseFolderMenuItem); + } + else if (clickedMenuItem is SettingsItem) + { + NavigationService.Navigate(WinoPage.SettingsPage); + } + else if (clickedMenuItem is ManageAccountsMenuItem) + { + NavigationService.Navigate(WinoPage.AccountManagementPage); + } + else if (clickedMenuItem is IAccountMenuItem clickedAccountMenuItem && latestSelectedAccountMenuItem != clickedAccountMenuItem) + { + ChangeLoadedAccount(clickedAccountMenuItem); + } + } + + private async void ChangeLoadedAccount(IAccountMenuItem clickedBaseAccountMenuItem, bool navigateInbox = true) + { + if (clickedBaseAccountMenuItem == null) return; + + // User clicked an account in Windows Mail style menu. + // List folders for this account and select Inbox. + + await ExecuteUIThread(() => + { + if (latestSelectedAccountMenuItem != null) + { + latestSelectedAccountMenuItem.IsSelected = false; + } + + clickedBaseAccountMenuItem.IsSelected = true; + + + latestSelectedAccountMenuItem = clickedBaseAccountMenuItem; + + + if (clickedBaseAccountMenuItem is AccountMenuItem accountMenuItem) + { + MenuItems.ReplaceFolders(accountMenuItem.SubMenuItems); + } + else if (clickedBaseAccountMenuItem is MergedAccountMenuItem mergedAccountMenuItem) + { + MenuItems.ReplaceFolders(mergedAccountMenuItem.SubMenuItems); + } + }); + + if (navigateInbox) + { + await Task.Yield(); + + await ExecuteUIThread(() => + { + NavigateInbox(clickedBaseAccountMenuItem); + }); + } + } + + private async void NavigateInbox(IAccountMenuItem clickedBaseAccountMenuItem) + { + if (clickedBaseAccountMenuItem is AccountMenuItem accountMenuItem) + { + if (MenuItems.TryGetWindowsStyleRootSpecialFolderMenuItem(accountMenuItem.AccountId, SpecialFolderType.Inbox, out FolderMenuItem inboxFolder)) + { + await NavigateFolderAsync(inboxFolder); + } + } + else if (clickedBaseAccountMenuItem is MergedAccountMenuItem mergedAccountMenuItem) + { + if (MenuItems.TryGetMergedAccountSpecialFolderMenuItem(mergedAccountMenuItem.EntityId.GetValueOrDefault(), SpecialFolderType.Inbox, out IBaseFolderMenuItem inboxFolder)) + { + await NavigateFolderAsync(inboxFolder); + } + } + } + + public async Task HandleCreateNewMailAsync() + { + _ = _storeRatingService.PromptRatingDialogAsync(); + + MailAccount operationAccount = null; + + // Check whether we have active folder item selected for any account. + // We have selected account. New mail creation should be targeted for this account. + + if (SelectedMenuItem is FolderMenuItem selectedFolderMenuItem) + { + operationAccount = selectedFolderMenuItem.ParentAccount; + } + + // We couldn't find any account so far. + // If there is only 1 account to use, use it. If not, + // send a message for flyout so user can pick from it. + + if (operationAccount == null) + { + // No selected account. + // List all accounts and let user pick one. + + var accounts = await _accountService.GetAccountsAsync(); + + if (!accounts.Any()) + { + await DialogService.ShowMessageAsync(Translator.DialogMessage_NoAccountsForCreateMailMessage, Translator.DialogMessage_NoAccountsForCreateMailTitle); + return; + } + + if (accounts.Count() == 1) + operationAccount = accounts.FirstOrDefault(); + else + { + // There are multiple accounts and there is no selection. + Messenger.Send(new CreateNewMailWithMultipleAccountsRequested(accounts)); + } + } + + if (operationAccount != null) + await CreateNewMailForAsync(operationAccount); + } + + public async Task CreateNewMailForAsync(MailAccount account) + { + if (account == null) return; + + // Find draft folder. + var draftFolder = await _folderService.GetSpecialFolderByAccountIdAsync(account.Id, SpecialFolderType.Draft); + + if (draftFolder == null) + { + DialogService.InfoBarMessage(Translator.Info_DraftFolderMissingTitle, + Translator.Info_DraftFolderMissingMessage, + InfoBarMessageType.Error, + Translator.SettingConfigureSpecialFolders_Button, + () => + { + DialogService.HandleSystemFolderConfigurationDialogAsync(account.Id, _folderService); + }); + return; + } + + // Navigate to draft folder. + await NavigateSpecialFolderAsync(account, SpecialFolderType.Draft, true); + + // Generate empty mime message. + var draftOptions = new DraftCreationOptions + { + Reason = DraftCreationReason.Empty, + + // Include mail to parameters for parsing mailto if any. + MailtoParameters = _launchProtocolService.MailtoParameters + }; + + var createdMimeMessage = await _mailService.CreateDraftMimeMessageAsync(account.Id, draftOptions).ConfigureAwait(false); + var createdDraftMailMessage = await _mailService.CreateDraftAsync(account, createdMimeMessage).ConfigureAwait(false); + + var draftPreperationRequest = new DraftPreperationRequest(account, createdDraftMailMessage, createdMimeMessage); + await _winoRequestDelegator.ExecuteAsync(draftPreperationRequest); + } + + + + public async void Receive(NewSynchronizationRequested message) + { + // Don't send message for sync completion when we execute requests. + // People are usually interested in seeing the notification after they trigger the synchronization. + + bool shouldReportSynchronizationResult = message.Options.Type != SynchronizationType.ExecuteRequests; + + var synchronizer = _synchronizerFactory.GetAccountSynchronizer(message.Options.AccountId); + + if (synchronizer == null) return; + + var accountId = message.Options.AccountId; + + message.Options.ProgressListener = this; + + bool isSynchronizationSucceeded = false; + + try + { + // TODO: Cancellation Token + var synchronizationResult = await synchronizer.SynchronizeAsync(message.Options); + + isSynchronizationSucceeded = synchronizationResult.CompletedState == SynchronizationCompletedState.Success; + + // Create notification for synchronization result. + if (synchronizationResult.DownloadedMessages.Any()) + { + var accountInboxFolder = await _folderService.GetSpecialFolderByAccountIdAsync(message.Options.AccountId, SpecialFolderType.Inbox); + + if (accountInboxFolder == null) return; + + await _notificationBuilder.CreateNotificationsAsync(accountInboxFolder.Id, synchronizationResult.DownloadedMessages); + } + } + catch (AuthenticationAttentionException) + { + await SetAccountAttentionAsync(accountId, AccountAttentionReason.InvalidCredentials); + } + catch (SystemFolderConfigurationMissingException) + { + await SetAccountAttentionAsync(accountId, AccountAttentionReason.MissingSystemFolderConfiguration); + } + catch (OperationCanceledException) + { + DialogService.InfoBarMessage(Translator.Info_SyncCanceledMessage, Translator.Info_SyncCanceledMessage, InfoBarMessageType.Warning); + } + catch (Exception ex) + { + DialogService.InfoBarMessage(Translator.Info_SyncFailedTitle, ex.Message, InfoBarMessageType.Error); + } + finally + { + if (shouldReportSynchronizationResult) + Messenger.Send(new AccountSynchronizationCompleted(accountId, + isSynchronizationSucceeded ? SynchronizationCompletedState.Success : SynchronizationCompletedState.Failed, + message.Options.GroupedSynchronizationTrackingId)); + } + } + + + protected override async void OnAccountUpdated(MailAccount updatedAccount) + => await ExecuteUIThread(() => { MenuItems.GetAccountMenuItem(updatedAccount.Id)?.UpdateAccount(updatedAccount); }); + + protected override void OnAccountRemoved(MailAccount removedAccount) + => Messenger.Send(new AccountsMenuRefreshRequested(true)); + + protected override async void OnAccountCreated(MailAccount createdAccount) + { + var createdMenuItem = await CreateNestedAccountMenuItem(createdAccount); + + if (createdMenuItem == null) return; + + ChangeLoadedAccount(createdMenuItem); + + // Each created account should start a new synchronization automatically. + var options = new SynchronizationOptions() + { + AccountId = createdAccount.Id, + Type = SynchronizationType.Full, + }; + + Messenger.Send(new NewSynchronizationRequested(options)); + + await _nativeAppService.PinAppToTaskbarAsync(); + } + + /// + /// Updates given single account menu item's unread count for all folders. + /// + /// Menu item to update unread count for. + /// Unread item count for Inbox only. + private async Task UpdateSingleAccountMenuItemUnreadCountAsync(AccountMenuItem accountMenuItem) + { + var accountId = accountMenuItem.AccountId; + int inboxItemCount = 0; + + // Get the folders needed to be refreshed. + var allFolders = await _folderService.GetUnreadUpdateFoldersAsync(accountId); + + foreach (var folder in allFolders) + { + var unreadItemCount = await UpdateAccountFolderUnreadItemCountAsync(accountMenuItem, folder.Id); + + if (folder.SpecialFolderType == SpecialFolderType.Inbox) + { + inboxItemCount = unreadItemCount; + + await ExecuteUIThread(() => { accountMenuItem.UnreadItemCount = unreadItemCount; }); + } + } + + return inboxItemCount; + } + + private async Task RefreshUnreadCountsForAccountAsync(Guid accountId) + { + // TODO: Merged accounts unread item count. + + var accountMenuItem = MenuItems.GetAccountMenuItem(accountId); + + if (accountMenuItem == null) return; + + if (accountMenuItem is AccountMenuItem singleAccountMenuItem) + { + await UpdateSingleAccountMenuItemUnreadCountAsync(singleAccountMenuItem); + + } + else if (accountMenuItem is MergedAccountMenuItem mergedAccountMenuItem) + { + // Merged account. + // Root account should include all parent accounts' unread item count. + + int totalUnreadCount = 0; + + var individualAccountMenuItems = mergedAccountMenuItem.GetAccountMenuItems(); + + foreach (var singleMenuItem in individualAccountMenuItems) + { + totalUnreadCount += await UpdateSingleAccountMenuItemUnreadCountAsync(singleMenuItem); + } + + // At this point all single accounts are calculated. + // Merge account folder's menu items can be calculated from those values for precision. + + await ExecuteUIThread(() => + { + mergedAccountMenuItem.RefreshFolderItemCount(); + mergedAccountMenuItem.UnreadItemCount = totalUnreadCount; + }); + } + + await ExecuteUIThread(async () => { await _notificationBuilder.UpdateTaskbarIconBadgeAsync(); }); + } + + private async Task UpdateAccountFolderUnreadItemCountAsync(AccountMenuItem accountMenuItem, Guid folderId) + { + if (accountMenuItem == null) return 0; + + var folder = accountMenuItem.FlattenedFolderHierarchy.Find(a => a.Parameter?.Id == folderId); + + if (folder == null) return 0; + + int folderUnreadItemCount = 0; + + folderUnreadItemCount = await _folderService.GetFolderNotificationBadgeAsync(folder.Parameter.Id).ConfigureAwait(false); + + await ExecuteUIThread(() => { folder.UnreadItemCount = folderUnreadItemCount; }); + + return folderUnreadItemCount; + } + + private async Task SetAccountAttentionAsync(Guid accountId, AccountAttentionReason reason) + { + var accountMenuItem = MenuItems.GetAccountMenuItem(accountId); + + if (accountMenuItem == null) return; + + var accountModel = accountMenuItem.HoldingAccounts.First(a => a.Id == accountId); + + accountModel.AttentionReason = reason; + + await _accountService.UpdateAccountAsync(accountModel); + + accountMenuItem.UpdateAccount(accountModel); + } + + public void Receive(NavigateSettingsRequested message) => SelectedMenuItem = ManageAccountsMenuItem; + + public async void Receive(MailtoProtocolMessageRequested message) + { + var accounts = await _accountService.GetAccountsAsync(); + + MailAccount targetAccount = null; + + if (!accounts.Any()) + { + await DialogService.ShowMessageAsync(Translator.DialogMessage_NoAccountsForCreateMailMessage, Translator.DialogMessage_NoAccountsForCreateMailTitle); + } + else if (accounts.Count == 1) + { + targetAccount = accounts[0]; + } + else + { + // User must pick an account. + + targetAccount = await DialogService.ShowAccountPickerDialogAsync(accounts); + } + + if (targetAccount == null) return; + + await CreateNewMailForAsync(targetAccount); + } + + public async void AccountProgressUpdated(Guid accountId, int progress) + { + var accountMenuItem = MenuItems.GetSpecificAccountMenuItem(accountId); + + if (accountMenuItem == null) return; + + await ExecuteUIThread(() => { accountMenuItem.SynchronizationProgress = progress; }); + } + + private async Task RecreateMenuItemsAsync() + { + await ExecuteUIThread(() => + { + MenuItems.Clear(); + MenuItems.Add(CreateMailMenuItem); + }); + + await LoadAccountsAsync(); + } + + public async void Receive(RefreshUnreadCountsMessage message) + => await RefreshUnreadCountsForAccountAsync(message.AccountId); + + public async void Receive(AccountsMenuRefreshRequested message) + { + await RecreateMenuItemsAsync(); + + if (message.AutomaticallyNavigateFirstItem) + { + if (MenuItems.FirstOrDefault(a => a is IAccountMenuItem) is IAccountMenuItem firstAccount) + { + ChangeLoadedAccount(firstAccount); + } + } + } + + public async void Receive(MergedInboxRenamed message) + { + var mergedInboxMenuItem = MenuItems.FirstOrDefault(a => a.EntityId == message.MergedInboxId); + + if (mergedInboxMenuItem == null) return; + + if (mergedInboxMenuItem is MergedAccountMenuItem mergedAccountMenuItemCasted) + { + await ExecuteUIThread(() => { mergedAccountMenuItemCasted.MergedAccountName = message.NewName; }); + } + } + + public async void Receive(LanguageChanged message) + { + await CreateFooterItemsAsync(); + await RecreateMenuItemsAsync(); + + ChangeLoadedAccount(latestSelectedAccountMenuItem, navigateInbox: false); + } + } +} diff --git a/Wino.Mail.ViewModels/BaseViewModel.cs b/Wino.Mail.ViewModels/BaseViewModel.cs new file mode 100644 index 00000000..d454b43c --- /dev/null +++ b/Wino.Mail.ViewModels/BaseViewModel.cs @@ -0,0 +1,95 @@ +using System; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Messaging; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Navigation; +using Wino.Core.Domain.Models.Requests; +using Wino.Core.Requests; + +namespace Wino.Mail.ViewModels +{ + public class BaseViewModel : ObservableRecipient, + INavigationAware, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient + { + private IDispatcher _dispatcher; + public IDispatcher Dispatcher + { + get + { + return _dispatcher; + } + set + { + _dispatcher = value; + + if (value != null) + { + OnDispatcherAssigned(); + } + } + } + + protected IDialogService DialogService { get; } + + public BaseViewModel(IDialogService dialogService) => DialogService = dialogService ?? throw new ArgumentNullException(nameof(dialogService)); + + public async Task ExecuteUIThread(Action action) => await Dispatcher?.ExecuteOnUIThread(action); + + public virtual void OnNavigatedTo(NavigationMode mode, object parameters) { IsActive = true; } + + public virtual void OnNavigatedFrom(NavigationMode mode, object parameters) { IsActive = false; } + + protected virtual void OnDispatcherAssigned() { } + + protected virtual void OnMailAdded(MailCopy addedMail) { } + protected virtual void OnMailRemoved(MailCopy removedMail) { } + protected virtual void OnMailUpdated(MailCopy updatedMail) { } + protected virtual void OnMailDownloaded(MailCopy downloadedMail) { } + + protected virtual void OnAccountCreated(MailAccount createdAccount) { } + protected virtual void OnAccountRemoved(MailAccount removedAccount) { } + protected virtual void OnAccountUpdated(MailAccount updatedAccount) { } + + protected virtual void OnFolderAdded(MailItemFolder addedFolder, MailAccount account) { } + protected virtual void OnFolderRemoved(MailItemFolder removedFolder, MailAccount account) { } + protected virtual void OnFolderUpdated(MailItemFolder updatedFolder, MailAccount account) { } + + protected virtual void OnDraftCreated(MailCopy draftMail, MailAccount account) { } + protected virtual void OnDraftFailed(MailCopy draftMail, MailAccount account) { } + protected virtual void OnDraftMapped(string localDraftCopyId, string remoteDraftCopyId) { } + + public void ReportUIChange(TMessage message) where TMessage : class, IUIMessage + => Messenger.Send(message); + + void IRecipient.Receive(AccountCreatedMessage message) => OnAccountCreated(message.Account); + void IRecipient.Receive(AccountRemovedMessage message) => OnAccountRemoved(message.Account); + void IRecipient.Receive(AccountUpdatedMessage message) => OnAccountUpdated(message.Account); + + void IRecipient.Receive(FolderAddedMessage message) => OnFolderAdded(message.AddedFolder, message.Account); + void IRecipient.Receive(FolderUpdatedMessage message) => OnFolderUpdated(message.UpdatedFolder, message.Account); + void IRecipient.Receive(FolderRemovedMessage message) => OnFolderAdded(message.RemovedFolder, message.Account); + + void IRecipient.Receive(MailAddedMessage message) => OnMailAdded(message.AddedMail); + void IRecipient.Receive(MailRemovedMessage message) => OnMailRemoved(message.RemovedMail); + void IRecipient.Receive(MailUpdatedMessage message) => OnMailUpdated(message.UpdatedMail); + void IRecipient.Receive(MailDownloadedMessage message) => OnMailDownloaded(message.DownloadedMail); + void IRecipient.Receive(DraftMapped message) => OnDraftMapped(message.LocalDraftCopyId, message.RemoteDraftCopyId); + void IRecipient.Receive(DraftFailed message) => OnDraftFailed(message.DraftMail, message.Account); + void IRecipient.Receive(DraftCreated message) => OnDraftCreated(message.DraftMail, message.Account); + } +} diff --git a/Wino.Mail.ViewModels/Collections/WinoMailCollection.cs b/Wino.Mail.ViewModels/Collections/WinoMailCollection.cs new file mode 100644 index 00000000..dc5e3d82 --- /dev/null +++ b/Wino.Mail.ViewModels/Collections/WinoMailCollection.cs @@ -0,0 +1,467 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.Collections; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Comparers; +using Wino.Core.Domain.Models.MailItem; +using Wino.Mail.ViewModels.Data; + +namespace Wino.Mail.ViewModels.Collections +{ + public class WinoMailCollection + { + // We cache each mail copy id for faster access on updates. + // If the item provider here for update or removal doesn't exist here + // we can ignore the operation. + + public HashSet MailCopyIdHashSet = new HashSet(); + + private ListItemComparer listComparer = new ListItemComparer(); + + private readonly ObservableGroupedCollection _mailItemSource = new ObservableGroupedCollection(); + + public ReadOnlyObservableGroupedCollection MailItems { get; } + + /// + /// Property that defines how the item sorting should be done in the collection. + /// + public SortingOptionType SortingType { get; set; } + + /// + /// Threading strategy that will help thread items according to the account type. + /// + public IThreadingStrategyProvider ThreadingStrategyProvider { get; set; } + + /// + /// Automatically deletes single mail items after the delete operation or thread->single transition. + /// This is useful when reply draft is discarded in the thread. Only enabled for Draft folder for now. + /// + public bool PruneSingleNonDraftItems { get; set; } + + public int Count => _mailItemSource.Count; + + public IDispatcher CoreDispatcher { get; set; } + + public WinoMailCollection() + { + MailItems = new ReadOnlyObservableGroupedCollection(_mailItemSource); + } + + public void Clear() => _mailItemSource.Clear(); + + private object GetGroupingKey(IMailItem mailItem) + { + if (SortingType == SortingOptionType.ReceiveDate) + return mailItem.CreationDate.ToLocalTime().Date; + else + return mailItem.FromName; + } + + private async Task InsertItemInternalAsync(object groupKey, IMailItem mailItem) + => await ExecuteUIThread(() => + { + if (mailItem is MailCopy mailCopy) + { + MailCopyIdHashSet.Add(mailCopy.UniqueId); + + _mailItemSource.InsertItem(groupKey, listComparer, new MailItemViewModel(mailCopy), listComparer.GetItemComparer()); + } + else if (mailItem is ThreadMailItem threadMailItem) + { + foreach (var item in threadMailItem.ThreadItems) + { + MailCopyIdHashSet.Add(item.UniqueId); + } + + _mailItemSource.InsertItem(groupKey, listComparer, new ThreadMailItemViewModel(threadMailItem), listComparer.GetItemComparer()); + } + else if (mailItem is MailItemViewModel) + { + MailCopyIdHashSet.Add(mailItem.UniqueId); + + _mailItemSource.InsertItem(groupKey, listComparer, mailItem, listComparer.GetItemComparer()); + } + }); + + private async Task RemoveItemInternalAsync(ObservableGroup group, IMailItem mailItem) + { + MailCopyIdHashSet.Remove(mailItem.UniqueId); + + await ExecuteUIThread(() => + { + group.Remove(mailItem); + + if (group.Count == 0) + { + _mailItemSource.RemoveGroup(group.Key); + } + }); + } + + public async Task AddAsync(MailCopy addedItem) + { + // Check all items for whether this item should be threaded with them. + bool shouldExit = false; + + var groupCount = _mailItemSource.Count; + + for (int i = 0; i < groupCount; i++) + { + if (shouldExit) break; + + var group = _mailItemSource[i]; + + for (int k = 0; k < group.Count; k++) + { + var item = group[k]; + + var addedAccountProviderType = addedItem.AssignedAccount.ProviderType; + + var threadingStrategy = ThreadingStrategyProvider.GetStrategy(addedAccountProviderType); + + if (threadingStrategy?.ShouldThreadWithItem(addedItem, item) ?? false) + { + shouldExit = true; + + if (item is ThreadMailItemViewModel threadMailItemViewModel) + { + // Item belongs to existing thread. + + /* Add original item to the thread. + * If new group key is not the same as existing thread: + * -> Remove the whole thread from list + * -> Add the thread to the list again for sorting. + * Update thread properties. + */ + + var existingGroupKey = GetGroupingKey(threadMailItemViewModel); + + threadMailItemViewModel.AddMailItemViewModel(addedItem); + + var newGroupKey = GetGroupingKey(threadMailItemViewModel); + + if (!existingGroupKey.Equals(newGroupKey)) + { + await RemoveItemInternalAsync(group, threadMailItemViewModel); + await InsertItemInternalAsync(newGroupKey, threadMailItemViewModel); + } + + await ExecuteUIThread(() => { threadMailItemViewModel.NotifyPropertyChanges(); }); + + break; + } + else + { + // Item belongs to a single mail item that is not threaded yet. + // Same item might've been tried to added as well. + // In that case we must just update the item but not thread it. + + /* Remove target item. + * Create a new thread with both items. + * Add new thread to the list. + */ + + if (item.Id == addedItem.Id) + { + // Item is already added to the list. + // We need to update the copy it holds. + + if (item is MailItemViewModel itemViewModel) + { + itemViewModel.Update(addedItem); + } + } + else + { + // Single item that must be threaded together with added item. + + var threadMailItem = new ThreadMailItem(); + + threadMailItem.AddThreadItem(item); + threadMailItem.AddThreadItem(addedItem); + + if (threadMailItem.ThreadItems.Count == 1) return; + + var newGroupKey = GetGroupingKey(threadMailItem); + + await RemoveItemInternalAsync(group, item); + await InsertItemInternalAsync(newGroupKey, threadMailItem); + } + + break; + } + } + else + { + // Update properties. + if (item.Id == addedItem.Id && item is MailItemViewModel itemViewModel) + { + await ExecuteUIThread(() => { itemViewModel.Update(addedItem); }); + + shouldExit = true; + } + } + } + } + + if (!shouldExit) + { + // At this point all items are already checked and not suitable option was available. + // Item doesn't belong to any thread. + // Just add it to the collection. + + var groupKey = GetGroupingKey(addedItem); + + await InsertItemInternalAsync(groupKey, addedItem); + } + } + + public void AddRange(IEnumerable items, bool clearIdCache) + { + if (clearIdCache) + { + MailCopyIdHashSet.Clear(); + } + + var groupedByName = items + .GroupBy(a => GetGroupingKey(a)) + .Select(a => new ObservableGroup(a.Key, a)); + + foreach (var group in groupedByName) + { + // Store all mail copy ids for faster access. + foreach (var item in group) + { + if (item is MailItemViewModel mailCopyItem && !MailCopyIdHashSet.Contains(item.UniqueId)) + { + MailCopyIdHashSet.Add(item.UniqueId); + } + else if (item is ThreadMailItemViewModel threadMailItem) + { + foreach (var mailItem in threadMailItem.ThreadItems) + { + if (!MailCopyIdHashSet.Contains(mailItem.UniqueId)) + { + MailCopyIdHashSet.Add(mailItem.UniqueId); + } + } + } + } + + var existingGroup = _mailItemSource.FirstGroupByKeyOrDefault(group.Key); + + if (existingGroup == null) + { + _mailItemSource.AddGroup(group.Key, group); + } + else + { + + foreach (var item in group) + { + existingGroup.Add(item); + + // _mailItemSource.InsertItem(existingGroup, item); + } + } + } + } + + public MailItemContainer GetMailItemContainer(Guid uniqueMailId) + { + var groupCount = _mailItemSource.Count; + + for (int i = 0; i < groupCount; i++) + { + var group = _mailItemSource[i]; + + for (int k = 0; k < group.Count; k++) + { + var item = group[k]; + + if (item is MailItemViewModel singleMailItemViewModel && singleMailItemViewModel.UniqueId == uniqueMailId) + return new MailItemContainer(singleMailItemViewModel); + else if (item is ThreadMailItemViewModel threadMailItemViewModel && threadMailItemViewModel.HasUniqueId(uniqueMailId)) + { + var singleItemViewModel = threadMailItemViewModel.GetItemById(uniqueMailId) as MailItemViewModel; + + return new MailItemContainer(singleItemViewModel, threadMailItemViewModel); + } + } + } + + return null; + } + + /// + /// Fins the item container that updated mail copy belongs to and updates it. + /// + /// Updated mail copy. + /// + public async Task UpdateMailCopy(MailCopy updatedMailCopy) + { + // This item doesn't exist in the list. + if (!MailCopyIdHashSet.Contains(updatedMailCopy.UniqueId)) + + { + return; + } + + await ExecuteUIThread(() => + { + var itemContainer = GetMailItemContainer(updatedMailCopy.UniqueId); + + if (itemContainer == null) return; + + // mailCopyIdHashSet.Remove(itemContainer.ItemViewModel.UniqueId); + + itemContainer.ItemViewModel?.Update(updatedMailCopy); + + // mailCopyIdHashSet.Add(updatedMailCopy.UniqueId); + + // Call thread notifications if possible. + itemContainer.ThreadViewModel?.NotifyPropertyChanges(); + }); + } + + public MailItemViewModel GetNextItem(MailCopy mailCopy) + { + var groupCount = _mailItemSource.Count; + + for (int i = 0; i < groupCount; i++) + { + var group = _mailItemSource[i]; + + for (int k = 0; k < group.Count; k++) + { + var item = group[k]; + + if (item is MailItemViewModel singleMailItemViewModel && singleMailItemViewModel.UniqueId == mailCopy.UniqueId) + { + if (k + 1 < group.Count) + { + return group[k + 1] as MailItemViewModel; + } + else if (i + 1 < groupCount) + { + return _mailItemSource[i + 1][0] as MailItemViewModel; + } + else + { + return null; + } + } + else if (item is ThreadMailItemViewModel threadMailItemViewModel && threadMailItemViewModel.HasUniqueId(mailCopy.UniqueId)) + { + var singleItemViewModel = threadMailItemViewModel.GetItemById(mailCopy.UniqueId) as MailItemViewModel; + + if (singleItemViewModel == null) return null; + + var singleItemIndex = threadMailItemViewModel.ThreadItems.IndexOf(singleItemViewModel); + + if (singleItemIndex + 1 < threadMailItemViewModel.ThreadItems.Count) + { + return threadMailItemViewModel.ThreadItems[singleItemIndex + 1] as MailItemViewModel; + } + else if (i + 1 < groupCount) + { + return _mailItemSource[i + 1][0] as MailItemViewModel; + } + else + { + return null; + } + } + } + } + + return null; + } + + public async Task RemoveAsync(MailCopy removeItem) + { + // This item doesn't exist in the list. + if (!MailCopyIdHashSet.Contains(removeItem.UniqueId)) return; + + // Check all items for whether this item should be threaded with them. + bool shouldExit = false; + + var groupCount = _mailItemSource.Count; + + for (int i = 0; i < groupCount; i++) + { + if (shouldExit) break; + + var group = _mailItemSource[i]; + + for (int k = 0; k < group.Count; k++) + { + var item = group[k]; + + if (item is ThreadMailItemViewModel threadMailItemViewModel && threadMailItemViewModel.HasUniqueId(removeItem.UniqueId)) + { + var removalItem = threadMailItemViewModel.GetItemById(removeItem.UniqueId); + + if (removalItem == null) return; + + // Threads' Id is equal to the last item they hold. + // We can't do Id check here because that'd remove the whole thread. + + /* Remove item from the thread. + * If thread had 1 item inside: + * -> Remove the thread and insert item as single item. + * If thread had 0 item inside: + * -> Remove the thread. + */ + + await ExecuteUIThread(() => { threadMailItemViewModel.RemoveCopyItem(removalItem); }); + + if (threadMailItemViewModel.ThreadItems.Count == 1) + { + // Convert to single item. + + var singleViewModel = threadMailItemViewModel.GetSingleItemViewModel(); + var groupKey = GetGroupingKey(singleViewModel); + + await RemoveItemInternalAsync(group, threadMailItemViewModel); + + // If thread->single conversion is being done, we should ignore it for non-draft items. + // eg. Deleting a reply message from draft folder. Single non-draft item should not be re-added. + + if (!PruneSingleNonDraftItems || singleViewModel.IsDraft) + { + await InsertItemInternalAsync(groupKey, singleViewModel); + } + } + else if (threadMailItemViewModel.ThreadItems.Count == 0) + { + await RemoveItemInternalAsync(group, threadMailItemViewModel); + } + else + { + // Item inside the thread is removed. + + threadMailItemViewModel.ThreadItems.Remove(removalItem); + } + + shouldExit = true; + break; + } + else if (item.UniqueId == removeItem.UniqueId) + { + await RemoveItemInternalAsync(group, item); + shouldExit = true; + + break; + } + } + } + } + + private async Task ExecuteUIThread(Action action) => await CoreDispatcher?.ExecuteOnUIThread(action); + } +} diff --git a/Wino.Mail.ViewModels/ComposePageViewModel.cs b/Wino.Mail.ViewModels/ComposePageViewModel.cs new file mode 100644 index 00000000..e2e30b34 --- /dev/null +++ b/Wino.Mail.ViewModels/ComposePageViewModel.cs @@ -0,0 +1,494 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using MimeKit; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Exceptions; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Navigation; +using Wino.Core.Domain.Models.Reader; +using Wino.Core.Extensions; +using Wino.Core.Messages.Mails; +using Wino.Core.Services; +using Wino.Mail.ViewModels.Data; + +namespace Wino.Mail.ViewModels +{ + public partial class ComposePageViewModel : BaseViewModel + { + public Func> GetHTMLBodyFunction; + + // When we send the message or discard it, we need to block the mime update + // Update is triggered when we leave the page. + private bool isUpdatingMimeBlocked = false; + + public bool CanSendMail => ComposingAccount != null && !IsLocalDraft && currentMimeMessage != null; + + private MimeMessage currentMimeMessage = null; + private readonly BodyBuilder bodyBuilder = new BodyBuilder(); + + public bool IsLocalDraft => CurrentMailDraftItem != null + && !string.IsNullOrEmpty(CurrentMailDraftItem.DraftId) + && CurrentMailDraftItem.DraftId.StartsWith(Constants.LocalDraftStartPrefix); + + + #region Properties + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(IsLocalDraft))] + [NotifyPropertyChangedFor(nameof(CanSendMail))] + private MailItemViewModel currentMailDraftItem; + + [ObservableProperty] + private bool isImportanceSelected; + + [ObservableProperty] + private MessageImportance selectedMessageImportance; + + [ObservableProperty] + private bool isCCBCCVisible = true; + + [ObservableProperty] + private string subject; + + [ObservableProperty] + private MailAccount composingAccount; + + public ObservableCollection IncludedAttachments { get; set; } = new ObservableCollection(); + + public ObservableCollection Accounts { get; set; } = new ObservableCollection(); + public ObservableCollection ToItems { get; set; } = new ObservableCollection(); + public ObservableCollection CCItemsItems { get; set; } = new ObservableCollection(); + public ObservableCollection BCCItems { get; set; } = new ObservableCollection(); + + + public List ToolbarSections { get; set; } = new List() + { + new EditorToolbarSection(){ SectionType = EditorToolbarSectionType.Format }, + new EditorToolbarSection(){ SectionType = EditorToolbarSectionType.Insert }, + new EditorToolbarSection(){ SectionType = EditorToolbarSectionType.Draw }, + new EditorToolbarSection(){ SectionType = EditorToolbarSectionType.Options } + }; + + private EditorToolbarSection selectedToolbarSection; + + public EditorToolbarSection SelectedToolbarSection + { + get => selectedToolbarSection; + set => SetProperty(ref selectedToolbarSection, value); + } + + #endregion + + public INativeAppService NativeAppService { get; } + + private readonly IMailService _mailService; + private readonly ILaunchProtocolService _launchProtocolService; + private readonly IMimeFileService _mimeFileService; + private readonly IStatePersistanceService _statePersistanceService; + private readonly IFolderService _folderService; + private readonly IAccountService _accountService; + private readonly IWinoRequestDelegator _worker; + public readonly IContactService ContactService; + + public ComposePageViewModel(IDialogService dialogService, + IMailService mailService, + ILaunchProtocolService launchProtocolService, + IMimeFileService mimeFileService, + IStatePersistanceService statePersistanceService, + INativeAppService nativeAppService, + IFolderService folderService, + IAccountService accountService, + IWinoRequestDelegator worker, + IContactService contactService) : base(dialogService) + { + NativeAppService = nativeAppService; + _folderService = folderService; + ContactService = contactService; + + _mailService = mailService; + _launchProtocolService = launchProtocolService; + _mimeFileService = mimeFileService; + _statePersistanceService = statePersistanceService; + _accountService = accountService; + _worker = worker; + + SelectedToolbarSection = ToolbarSections[0]; + } + + [RelayCommand] + private void RemoveAttachment(MailAttachmentViewModel attachmentViewModel) + => IncludedAttachments.Remove(attachmentViewModel); + + [RelayCommand] + private async Task SendAsync() + { + // TODO: More detailed mail validations. + + if (!ToItems.Any()) + { + await DialogService.ShowMessageAsync(Translator.DialogMessage_ComposerMissingRecipientMessage, Translator.DialogMessage_ComposerValidationFailedTitle); + return; + } + + if (string.IsNullOrEmpty(Subject)) + { + var isConfirmed = await DialogService.ShowConfirmationDialogAsync(Translator.DialogMessage_EmptySubjectConfirmationMessage, Translator.DialogMessage_EmptySubjectConfirmation, Translator.Buttons_Yes); + + if (!isConfirmed) return; + } + + // Save mime changes before sending. + await UpdateMimeChangesAsync().ConfigureAwait(false); + + isUpdatingMimeBlocked = true; + + var assignedAccount = CurrentMailDraftItem.AssignedAccount; + + MailItemFolder sentFolder = null; + + // Load the Sent folder if user wanted to have a copy there. + if (assignedAccount.Preferences.ShouldAppendMessagesToSentFolder) + { + sentFolder = await _folderService.GetSpecialFolderByAccountIdAsync(assignedAccount.Id, SpecialFolderType.Sent); + } + + var draftSendPreparationRequest = new SendDraftPreparationRequest(CurrentMailDraftItem.MailCopy, currentMimeMessage, CurrentMailDraftItem.AssignedFolder, sentFolder, CurrentMailDraftItem.AssignedAccount.Preferences); + + await _worker.ExecuteAsync(draftSendPreparationRequest); + } + + private async Task UpdateMimeChangesAsync() + { + if (isUpdatingMimeBlocked || currentMimeMessage == null || ComposingAccount == null || CurrentMailDraftItem == null) return; + + // Save recipients. + + SaveAddressInfo(ToItems, currentMimeMessage.To); + SaveAddressInfo(CCItemsItems, currentMimeMessage.Cc); + SaveAddressInfo(BCCItems, currentMimeMessage.Bcc); + + SaveImportance(); + SaveSubject(); + + await SaveAttachmentsAsync(); + await SaveBodyAsync(); + await UpdateMailCopyAsync(); + + // Save mime file. + await _mimeFileService.SaveMimeMessageAsync(CurrentMailDraftItem.MailCopy.FileId, currentMimeMessage, ComposingAccount.Id).ConfigureAwait(false); + } + + private async Task UpdateMailCopyAsync() + { + CurrentMailDraftItem.Subject = currentMimeMessage.Subject; + CurrentMailDraftItem.PreviewText = currentMimeMessage.TextBody; + + // Update database. + await _mailService.UpdateMailAsync(CurrentMailDraftItem.MailCopy); + } + + private async Task SaveAttachmentsAsync() + { + bodyBuilder.Attachments.Clear(); + + foreach (var path in IncludedAttachments) + { + if (path.Content == null) continue; + + await bodyBuilder.Attachments.AddAsync(path.FileName, new MemoryStream(path.Content)); + } + } + + private void SaveImportance() { currentMimeMessage.Importance = IsImportanceSelected ? SelectedMessageImportance : MessageImportance.Normal; } + + private void SaveSubject() + { + if (Subject != null) + { + currentMimeMessage.Subject = Subject; + } + } + + private async Task SaveBodyAsync() + { + if (GetHTMLBodyFunction != null) + bodyBuilder.HtmlBody = Regex.Unescape(await GetHTMLBodyFunction()); + + if (!string.IsNullOrEmpty(bodyBuilder.HtmlBody)) + bodyBuilder.TextBody = HtmlAgilityPackExtensions.GetPreviewText(bodyBuilder.HtmlBody); + + if (bodyBuilder.HtmlBody != null && bodyBuilder.TextBody != null) + currentMimeMessage.Body = bodyBuilder.ToMessageBody(); + } + + [RelayCommand] + private async Task DiscardAsync() + { + if (ComposingAccount == null) + { + DialogService.InfoBarMessage(Translator.Info_MessageCorruptedTitle, Translator.Info_MessageCorruptedMessage, InfoBarMessageType.Error); + return; + } + + var confirmation = await DialogService.ShowConfirmationDialogAsync(Translator.DialogMessage_DiscardDraftConfirmationMessage, + Translator.DialogMessage_DiscardDraftConfirmationTitle, + Translator.Buttons_Yes); + + if (confirmation) + { + isUpdatingMimeBlocked = true; + + // Don't send delete request for local drafts. Just delete the record and mime locally. + if (CurrentMailDraftItem.IsLocalDraft) + { + await _mailService.DeleteMailAsync(ComposingAccount.Id, CurrentMailDraftItem.Id); + } + else + { + var deletePackage = new MailOperationPreperationRequest(MailOperation.HardDelete, CurrentMailDraftItem.MailCopy, ignoreHardDeleteProtection: true); + await _worker.ExecuteAsync(deletePackage).ConfigureAwait(false); + } + } + } + + public override async void OnNavigatedFrom(NavigationMode mode, object parameters) + { + base.OnNavigatedFrom(mode, parameters); + + await UpdateMimeChangesAsync().ConfigureAwait(false); + + await ExecuteUIThread(() => { _statePersistanceService.IsReadingMail = false; }); + } + + public override async void OnNavigatedTo(NavigationMode mode, object parameters) + { + base.OnNavigatedTo(mode, parameters); + + if (parameters != null && parameters is MailItemViewModel mailItem) + { + await LoadAccountsAsync(); + + CurrentMailDraftItem = mailItem; + + _ = TryPrepareComposeAsync(true); + } + + ToItems.CollectionChanged -= ContactListCollectionChanged; + ToItems.CollectionChanged += ContactListCollectionChanged; + + _statePersistanceService.IsReadingMail = true; + + // Check if there is any delivering mail address from protocol launch. + + if (_launchProtocolService.MailtoParameters != null) + { + // TODO + //var requestedMailContact = await GetAddressInformationAsync(_launchProtocolService.MailtoParameters, ToItems); + + //if (requestedMailContact != null) + //{ + // ToItems.Add(requestedMailContact); + //} + //else + // DialogService.InfoBarMessage("Invalid Address", "Address is not a valid e-mail address.", InfoBarMessageType.Warning); + + // Clear the address. + _launchProtocolService.MailtoParameters = null; + } + } + + private void ContactListCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) + { + // Prevent duplicates. + if (!(sender is ObservableCollection list)) + return; + + foreach (var item in e.NewItems) + { + if (item is AddressInformation addedInfo && list.Count(a => a == addedInfo) > 1) + { + var addedIndex = list.IndexOf(addedInfo); + list.RemoveAt(addedIndex); + } + } + } + } + + private async Task LoadAccountsAsync() + { + // Load accounts + + var accounts = await _accountService.GetAccountsAsync(); + + foreach (var account in accounts) + { + Accounts.Add(account); + } + } + + private async Task InitializeComposerAccountAsync() + { + if (ComposingAccount != null) return true; + + if (CurrentMailDraftItem == null) + return false; + + await ExecuteUIThread(() => + { + ComposingAccount = Accounts.FirstOrDefault(a => a.Id == CurrentMailDraftItem.AssignedAccount.Id); + }); + + return ComposingAccount != null; + } + + private async Task TryPrepareComposeAsync(bool downloadIfNeeded) + { + if (CurrentMailDraftItem == null) + return; + + bool isComposerInitialized = await InitializeComposerAccountAsync(); + + if (!isComposerInitialized) + { + return; + } + + // Replying existing message. + MimeMessageInformation mimeMessageInformation = null; + + try + { + mimeMessageInformation = await _mimeFileService.GetMimeMessageInformationAsync(CurrentMailDraftItem.MailCopy.FileId, ComposingAccount.Id).ConfigureAwait(false); + } + catch (FileNotFoundException) + { + if (downloadIfNeeded) + { + // TODO: Folder id needs to be passed. + // TODO: Send mail retrieve request. + // _worker.Queue(new FetchSingleItemRequest(ComposingAccount.Id, CurrentMailDraftItem.Id, string.Empty)); + } + //else + // DialogService.ShowMIMENotFoundMessage(); + + return; + } + catch (ComposerMimeNotFoundException) + { + DialogService.InfoBarMessage(Translator.Info_ComposerMissingMIMETitle, Translator.Info_ComposerMissingMIMEMessage, InfoBarMessageType.Error); + } + + if (mimeMessageInformation == null) + return; + + var replyingMime = mimeMessageInformation.MimeMessage; + var mimeFilePath = mimeMessageInformation.Path; + + var renderModel = _mimeFileService.GetMailRenderModel(replyingMime, mimeFilePath); + + await ExecuteUIThread(() => + { + // Extract information + + ToItems.Clear(); + CCItemsItems.Clear(); + BCCItems.Clear(); + + LoadAddressInfo(replyingMime.To, ToItems); + LoadAddressInfo(replyingMime.Cc, CCItemsItems); + LoadAddressInfo(replyingMime.Bcc, BCCItems); + + LoadAttachments(replyingMime.Attachments); + + Subject = replyingMime.Subject; + + currentMimeMessage = replyingMime; + + OnPropertyChanged(nameof(CanSendMail)); + Messenger.Send(new CreateNewComposeMailRequested(renderModel)); + }); + } + + private void LoadAttachments(IEnumerable mimeEntities) + { + foreach (var attachment in mimeEntities) + { + if (attachment.IsAttachment && attachment is MimePart attachmentPart) + { + IncludedAttachments.Add(new MailAttachmentViewModel(attachmentPart)); + } + } + } + + private void LoadAddressInfo(InternetAddressList list, ObservableCollection collection) + { + foreach (var item in list) + { + if (item is MailboxAddress mailboxAddress) + collection.Add(mailboxAddress.ToAddressInformation()); + else if (item is GroupAddress groupAddress) + LoadAddressInfo(groupAddress.Members, collection); + } + } + + private void SaveAddressInfo(IEnumerable addresses, InternetAddressList list) + { + list.Clear(); + + foreach (var item in addresses) + list.Add(new MailboxAddress(item.Name, item.Address)); + } + + public async Task GetAddressInformationAsync(string tokenText, ObservableCollection collection) + { + // Get model from the service. This will make sure the name is properly included if there is any record. + + var info = await ContactService.GetAddressInformationByAddressAsync(tokenText); + + // Don't add if there is already that address in the collection. + if (collection.Any(a => a.Address == info.Address)) + return null; + + return info; + } + + public void NotifyAddressExists() + { + DialogService.InfoBarMessage(Translator.Info_ContactExistsTitle, Translator.Info_ContactExistsMessage, InfoBarMessageType.Warning); + } + + public void NotifyInvalidEmail(string address) + { + DialogService.InfoBarMessage(Translator.Info_InvalidAddressTitle, string.Format(Translator.Info_InvalidAddressMessage, address), InfoBarMessageType.Warning); + } + + protected override async void OnMailUpdated(MailCopy updatedMail) + { + base.OnMailUpdated(updatedMail); + + if (CurrentMailDraftItem == null) return; + + if (updatedMail.UniqueId == CurrentMailDraftItem.UniqueId) + { + await ExecuteUIThread(() => + { + CurrentMailDraftItem.Update(updatedMail); + OnPropertyChanged(nameof(CanSendMail)); + }); + } + } + } +} diff --git a/Wino.Mail.ViewModels/Data/AccountProviderDetailViewModel.cs b/Wino.Mail.ViewModels/Data/AccountProviderDetailViewModel.cs new file mode 100644 index 00000000..4f629e99 --- /dev/null +++ b/Wino.Mail.ViewModels/Data/AccountProviderDetailViewModel.cs @@ -0,0 +1,26 @@ +using System; +using CommunityToolkit.Mvvm.ComponentModel; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Mail.ViewModels.Data +{ + public partial class AccountProviderDetailViewModel : ObservableObject, IAccountProviderDetailViewModel + { + + [ObservableProperty] + private MailAccount account; + + public IProviderDetail ProviderDetail { get; set; } + + public Guid StartupEntityId => Account.Id; + + public string StartupEntityTitle => Account.Name; + + public AccountProviderDetailViewModel(IProviderDetail providerDetail, MailAccount account) + { + ProviderDetail = providerDetail; + Account = account; + } + } +} diff --git a/Wino.Mail.ViewModels/Data/AppColorViewModel.cs b/Wino.Mail.ViewModels/Data/AppColorViewModel.cs new file mode 100644 index 00000000..4f3072d0 --- /dev/null +++ b/Wino.Mail.ViewModels/Data/AppColorViewModel.cs @@ -0,0 +1,23 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace Wino.Mail.ViewModels.Data +{ + public class AppColorViewModel : ObservableObject + { + private string _hex; + + public string Hex + { + get => _hex; + set => SetProperty(ref _hex, value); + } + + public bool IsAccentColor { get; } + + public AppColorViewModel(string hex, bool isAccentColor = false) + { + IsAccentColor = isAccentColor; + Hex = hex; + } + } +} diff --git a/Wino.Mail.ViewModels/Data/BreadcrumbNavigationItemViewModel.cs b/Wino.Mail.ViewModels/Data/BreadcrumbNavigationItemViewModel.cs new file mode 100644 index 00000000..616e2dea --- /dev/null +++ b/Wino.Mail.ViewModels/Data/BreadcrumbNavigationItemViewModel.cs @@ -0,0 +1,33 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using Wino.Core.Messages.Navigation; + +namespace Wino.Mail.ViewModels.Data +{ + public class BreadcrumbNavigationItemViewModel : ObservableObject + { + public BreadcrumbNavigationRequested Request { get; set; } + + public BreadcrumbNavigationItemViewModel(BreadcrumbNavigationRequested request, bool isActive) + { + Request = request; + Title = request.PageTitle; + + this.isActive = isActive; + } + + private string title; + public string Title + { + get => title; + set => SetProperty(ref title, value); + } + + private bool isActive; + + public bool IsActive + { + get => isActive; + set => SetProperty(ref isActive, value); + } + } +} diff --git a/Wino.Mail.ViewModels/Data/FolderPivotViewModel.cs b/Wino.Mail.ViewModels/Data/FolderPivotViewModel.cs new file mode 100644 index 00000000..69620759 --- /dev/null +++ b/Wino.Mail.ViewModels/Data/FolderPivotViewModel.cs @@ -0,0 +1,33 @@ +using System.Diagnostics; +using CommunityToolkit.Mvvm.ComponentModel; +using Wino.Core.Domain; + +namespace Wino.Mail.ViewModels.Data +{ + [DebuggerDisplay("{FolderTitle}")] + public partial class FolderPivotViewModel : ObservableObject + { + public bool? IsFocused { get; set; } + public string FolderTitle { get; } + + public bool ShouldDisplaySelectedItemCount => IsExtendedMode ? SelectedItemCount > 1 : SelectedItemCount > 0; + + [ObservableProperty] + private bool isSelected; + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(ShouldDisplaySelectedItemCount))] + private int selectedItemCount; + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(ShouldDisplaySelectedItemCount))] + private bool isExtendedMode = true; + + public FolderPivotViewModel(string folderName, bool? isFocused) + { + IsFocused = isFocused; + + FolderTitle = IsFocused == null ? folderName : (IsFocused == true ? Translator.Focused : Translator.Other); + } + } +} diff --git a/Wino.Mail.ViewModels/Data/MailAttachmentViewModel.cs b/Wino.Mail.ViewModels/Data/MailAttachmentViewModel.cs new file mode 100644 index 00000000..90387ce4 --- /dev/null +++ b/Wino.Mail.ViewModels/Data/MailAttachmentViewModel.cs @@ -0,0 +1,100 @@ +using System.IO; +using CommunityToolkit.Mvvm.ComponentModel; +using MimeKit; +using Wino.Core.Domain.Enums; +using Wino.Core.Extensions; + +namespace Wino.Mail.ViewModels.Data +{ + public class MailAttachmentViewModel : ObservableObject + { + private bool isBusy; + private readonly MimePart _mimePart; + + public MailAttachmentType AttachmentType { get; } + public string FileName { get; } + public string FilePath { get; set; } + public string ReadableSize { get; } + public byte[] Content { get; set; } + + public IMimeContent MimeContent => _mimePart.Content; + + /// + /// Gets or sets whether attachment is busy with opening or saving etc. + /// + public bool IsBusy + { + get => isBusy; + set => SetProperty(ref isBusy, value); + } + + public MailAttachmentViewModel(MimePart mimePart) + { + _mimePart = mimePart; + + var array = new byte[_mimePart.Content.Stream.Length]; + _mimePart.Content.Stream.Read(array, 0, (int)_mimePart.Content.Stream.Length); + + Content = array; + + FileName = mimePart.FileName; + ReadableSize = mimePart.Content.Stream.Length.GetBytesReadable(); + + var extension = Path.GetExtension(FileName); + AttachmentType = GetAttachmentType(extension); + } + + public MailAttachmentViewModel(string fullFilePath, byte[] content) + { + Content = content; + + FileName = Path.GetFileName(fullFilePath); + FilePath = fullFilePath; + + ReadableSize = ((long)content.Length).GetBytesReadable(); + + var extension = Path.GetExtension(FileName); + AttachmentType = GetAttachmentType(extension); + } + + public MailAttachmentType GetAttachmentType(string mediaSubtype) + { + if (string.IsNullOrEmpty(mediaSubtype)) + return MailAttachmentType.None; + + switch (mediaSubtype.ToLower()) + { + case ".exe": + return MailAttachmentType.Executable; + case ".rar": + return MailAttachmentType.RarArchive; + case ".zip": + return MailAttachmentType.Archive; + case ".ogg": + case ".mp3": + case ".wav": + case ".aac": + case ".alac": + return MailAttachmentType.Audio; + case ".mp4": + case ".wmv": + case ".avi": + case ".flv": + return MailAttachmentType.Video; + case ".pdf": + return MailAttachmentType.PDF; + case ".htm": + case ".html": + return MailAttachmentType.HTML; + case ".png": + case ".jpg": + case ".jpeg": + case ".gif": + case ".jiff": + return MailAttachmentType.Image; + default: + return MailAttachmentType.Other; + } + } + } +} diff --git a/Wino.Mail.ViewModels/Data/MailItemContainer.cs b/Wino.Mail.ViewModels/Data/MailItemContainer.cs new file mode 100644 index 00000000..da845606 --- /dev/null +++ b/Wino.Mail.ViewModels/Data/MailItemContainer.cs @@ -0,0 +1,20 @@ +using System; + +namespace Wino.Mail.ViewModels.Data +{ + public class MailItemContainer + { + public MailItemViewModel ItemViewModel { get; set; } + public ThreadMailItemViewModel ThreadViewModel { get; set; } + + public MailItemContainer(MailItemViewModel itemViewModel, ThreadMailItemViewModel threadViewModel) : this(itemViewModel) + { + ThreadViewModel = threadViewModel ?? throw new ArgumentNullException(nameof(threadViewModel)); + } + + public MailItemContainer(MailItemViewModel itemViewModel) + { + ItemViewModel = itemViewModel ?? throw new ArgumentNullException(nameof(itemViewModel)); + } + } +} diff --git a/Wino.Mail.ViewModels/Data/MailItemViewModel.cs b/Wino.Mail.ViewModels/Data/MailItemViewModel.cs new file mode 100644 index 00000000..26d82931 --- /dev/null +++ b/Wino.Mail.ViewModels/Data/MailItemViewModel.cs @@ -0,0 +1,106 @@ +using System; +using CommunityToolkit.Mvvm.ComponentModel; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Models.MailItem; + +namespace Wino.Mail.ViewModels.Data +{ + /// + /// Single view model for IMailItem representation. + /// + public partial class MailItemViewModel(MailCopy mailCopy) : ObservableObject, IMailItem + { + public MailCopy MailCopy { get; private set; } = mailCopy; + + public bool IsLocalDraft => !string.IsNullOrEmpty(DraftId) && DraftId.StartsWith(Constants.LocalDraftStartPrefix); + + public Guid UniqueId => ((IMailItem)MailCopy).UniqueId; + public string ThreadId => ((IMailItem)MailCopy).ThreadId; + public string MessageId => ((IMailItem)MailCopy).MessageId; + public string FromName => ((IMailItem)MailCopy).FromName ?? FromAddress; + public DateTime CreationDate => ((IMailItem)MailCopy).CreationDate; + public string FromAddress => ((IMailItem)MailCopy).FromAddress; + public bool HasAttachments => ((IMailItem)MailCopy).HasAttachments; + public string References => ((IMailItem)MailCopy).References; + public string InReplyTo => ((IMailItem)MailCopy).InReplyTo; + + [ObservableProperty] + private bool isCustomFocused; + + [ObservableProperty] + private bool isSelected; + + public bool IsFlagged + { + get => MailCopy.IsFlagged; + set => SetProperty(MailCopy.IsFlagged, value, MailCopy, (u, n) => u.IsFlagged = n); + } + + public bool IsFocused + { + get => MailCopy.IsFocused; + set => SetProperty(MailCopy.IsFocused, value, MailCopy, (u, n) => u.IsFocused = n); + } + + public bool IsRead + { + get => MailCopy.IsRead; + set => SetProperty(MailCopy.IsRead, value, MailCopy, (u, n) => u.IsRead = n); + } + + public bool IsDraft + { + get => MailCopy.IsDraft; + set => SetProperty(MailCopy.IsDraft, value, MailCopy, (u, n) => u.IsDraft = n); + } + + public string DraftId + { + get => MailCopy.DraftId; + set => SetProperty(MailCopy.DraftId, value, MailCopy, (u, n) => u.DraftId = n); + } + + public string Id + { + get => MailCopy.Id; + set => SetProperty(MailCopy.Id, value, MailCopy, (u, n) => u.Id = n); + } + + public string Subject + { + get => MailCopy.Subject; + set => SetProperty(MailCopy.Subject, value, MailCopy, (u, n) => u.Subject = n); + } + + public string PreviewText + { + get => MailCopy.PreviewText; + set => SetProperty(MailCopy.PreviewText, value, MailCopy, (u, n) => u.PreviewText = n); + } + + public MailItemFolder AssignedFolder => ((IMailItem)MailCopy).AssignedFolder; + + public MailAccount AssignedAccount => ((IMailItem)MailCopy).AssignedAccount; + + public Guid FileId => ((IMailItem)MailCopy).FileId; + + public void Update(MailCopy updatedMailItem) + { + MailCopy = updatedMailItem; + + // DEBUG + //if (updatedMailItem.AssignedAccount == null || updatedMailItem.AssignedFolder == null) + // throw new Exception("Assigned account or folder is null."); + + OnPropertyChanged(nameof(IsRead)); + OnPropertyChanged(nameof(IsFocused)); + OnPropertyChanged(nameof(IsFlagged)); + OnPropertyChanged(nameof(IsDraft)); + OnPropertyChanged(nameof(DraftId)); + OnPropertyChanged(nameof(Subject)); + OnPropertyChanged(nameof(PreviewText)); + OnPropertyChanged(nameof(IsLocalDraft)); + } + } +} diff --git a/Wino.Mail.ViewModels/Data/MergedAccountProviderDetailViewModel.cs b/Wino.Mail.ViewModels/Data/MergedAccountProviderDetailViewModel.cs new file mode 100644 index 00000000..6dda7ac3 --- /dev/null +++ b/Wino.Mail.ViewModels/Data/MergedAccountProviderDetailViewModel.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CommunityToolkit.Mvvm.ComponentModel; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Mail.ViewModels.Data +{ + public partial class MergedAccountProviderDetailViewModel : ObservableObject, IAccountProviderDetailViewModel + { + public List HoldingAccounts { get; } + public MergedInbox MergedInbox { get; } + + public string AccountAddresses => string.Join(", ", HoldingAccounts.Select(a => a.Account.Address)); + + public Guid StartupEntityId => MergedInbox.Id; + + public string StartupEntityTitle => MergedInbox.Name; + + public MergedAccountProviderDetailViewModel(MergedInbox mergedInbox, List holdingAccounts) + { + MergedInbox = mergedInbox; + HoldingAccounts = holdingAccounts; + } + } +} diff --git a/Wino.Mail.ViewModels/Data/ThreadMailItemViewModel.cs b/Wino.Mail.ViewModels/Data/ThreadMailItemViewModel.cs new file mode 100644 index 00000000..74360ff5 --- /dev/null +++ b/Wino.Mail.ViewModels/Data/ThreadMailItemViewModel.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; +using CommunityToolkit.Mvvm.ComponentModel; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Models.MailItem; + +namespace Wino.Mail.ViewModels.Data +{ + /// + /// Thread mail item (multiple IMailItem) view model representation. + /// + public class ThreadMailItemViewModel : ObservableObject, IMailItemThread, IComparable, IComparable + { + public ObservableCollection ThreadItems => ((IMailItemThread)_threadMailItem).ThreadItems; + + private readonly ThreadMailItem _threadMailItem; + + private bool isThreadExpanded; + public bool IsThreadExpanded + { + get => isThreadExpanded; + set => SetProperty(ref isThreadExpanded, value); + } + + public ThreadMailItemViewModel(ThreadMailItem threadMailItem) + { + _threadMailItem = new ThreadMailItem(); + + // Local copies + foreach (var item in threadMailItem.ThreadItems) + { + AddMailItemViewModel(item); + } + } + + public IEnumerable GetMailCopies() + => ThreadItems.OfType().Select(a => a.MailCopy); + + public void AddMailItemViewModel(IMailItem mailItem) + { + if (mailItem == null) return; + + if (mailItem is MailCopy mailCopy) + _threadMailItem.AddThreadItem(new MailItemViewModel(mailCopy)); + else if (mailItem is MailItemViewModel mailItemViewModel) + _threadMailItem.AddThreadItem(mailItemViewModel); + else + Debugger.Break(); + } + + public bool HasUniqueId(Guid uniqueMailId) + => ThreadItems.Any(a => a.UniqueId == uniqueMailId); + + public IMailItem GetItemById(Guid uniqueMailId) + => ThreadItems.FirstOrDefault(a => a.UniqueId == uniqueMailId); + + public void RemoveCopyItem(IMailItem item) + { + MailCopy copyToRemove = null; + + if (item is MailItemViewModel mailItemViewModel) + copyToRemove = mailItemViewModel.MailCopy; + else if (item is MailCopy copyItem) + copyToRemove = copyItem; + + var existedItem = ThreadItems.FirstOrDefault(a => a.Id == copyToRemove.Id); + + if (existedItem == null) return; + + ThreadItems.Remove(existedItem); + + NotifyPropertyChanges(); + } + + public void NotifyPropertyChanges() + { + OnPropertyChanged(nameof(Subject)); + OnPropertyChanged(nameof(PreviewText)); + OnPropertyChanged(nameof(FromName)); + OnPropertyChanged(nameof(FromAddress)); + OnPropertyChanged(nameof(HasAttachments)); + + OnPropertyChanged(nameof(IsFlagged)); + OnPropertyChanged(nameof(IsDraft)); + OnPropertyChanged(nameof(IsRead)); + OnPropertyChanged(nameof(IsFocused)); + OnPropertyChanged(nameof(CreationDate)); + } + + public IMailItem LatestMailItem => ((IMailItemThread)_threadMailItem).LatestMailItem; + public IMailItem FirstMailItem => ((IMailItemThread)_threadMailItem).FirstMailItem; + + public string Id => ((IMailItem)_threadMailItem).Id; + public string Subject => ((IMailItem)_threadMailItem).Subject; + public string ThreadId => ((IMailItem)_threadMailItem).ThreadId; + public string MessageId => ((IMailItem)_threadMailItem).MessageId; + public string References => ((IMailItem)_threadMailItem).References; + public string PreviewText => ((IMailItem)_threadMailItem).PreviewText; + public string FromName => ((IMailItem)_threadMailItem).FromName; + public DateTime CreationDate => ((IMailItem)_threadMailItem).CreationDate; + public string FromAddress => ((IMailItem)_threadMailItem).FromAddress; + public bool HasAttachments => ((IMailItem)_threadMailItem).HasAttachments; + public bool IsFlagged => ((IMailItem)_threadMailItem).IsFlagged; + public bool IsFocused => ((IMailItem)_threadMailItem).IsFocused; + public bool IsRead => ((IMailItem)_threadMailItem).IsRead; + public bool IsDraft => ((IMailItem)_threadMailItem).IsDraft; + public string DraftId => string.Empty; + public string InReplyTo => ((IMailItem)_threadMailItem).InReplyTo; + + public MailItemFolder AssignedFolder => ((IMailItem)_threadMailItem).AssignedFolder; + + public MailAccount AssignedAccount => ((IMailItem)_threadMailItem).AssignedAccount; + + public Guid UniqueId => ((IMailItem)_threadMailItem).UniqueId; + + public Guid FileId => ((IMailItem)_threadMailItem).FileId; + + public int CompareTo(DateTime other) => CreationDate.CompareTo(other); + public int CompareTo(string other) => FromName.CompareTo(other); + + // Get single mail item view model out of the only item in thread items. + public MailItemViewModel GetSingleItemViewModel() => ThreadItems.First() as MailItemViewModel; + } +} diff --git a/Wino.Mail.ViewModels/GlobalSuppressions.cs b/Wino.Mail.ViewModels/GlobalSuppressions.cs new file mode 100644 index 00000000..5fe2a5bb --- /dev/null +++ b/Wino.Mail.ViewModels/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~F:Wino.Mail.ViewModels.ComposePageViewModel.isImportanceSelected")] diff --git a/Wino.Mail.ViewModels/IdlePageViewModel.cs b/Wino.Mail.ViewModels/IdlePageViewModel.cs new file mode 100644 index 00000000..a164f85b --- /dev/null +++ b/Wino.Mail.ViewModels/IdlePageViewModel.cs @@ -0,0 +1,38 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Messaging; +using Wino.Core.Domain; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Navigation; +using Wino.Core.Messages.Mails; + +namespace Wino.Mail.ViewModels +{ + public partial class IdlePageViewModel : BaseViewModel, IRecipient + { + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(HasSelectedItems))] + [NotifyPropertyChangedFor(nameof(SelectedMessageText))] + private int selectedItemCount; + + public bool HasSelectedItems => SelectedItemCount > 0; + + public string SelectedMessageText => HasSelectedItems ? string.Format(Translator.MailsSelected, SelectedItemCount) : Translator.NoMailSelected; + + public IdlePageViewModel(IDialogService dialogService) : base(dialogService) { } + + public void Receive(SelectedMailItemsChanged message) + { + SelectedItemCount = message.SelectedItemCount; + } + + public override void OnNavigatedTo(NavigationMode mode, object parameters) + { + base.OnNavigatedTo(mode, parameters); + + if (parameters != null && parameters is int selectedItemCount) + SelectedItemCount = selectedItemCount; + else + SelectedItemCount = 0; + } + } +} diff --git a/Wino.Mail.ViewModels/MailListPageViewModel.cs b/Wino.Mail.ViewModels/MailListPageViewModel.cs new file mode 100644 index 00000000..8fd1f0cf --- /dev/null +++ b/Wino.Mail.ViewModels/MailListPageViewModel.cs @@ -0,0 +1,922 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Diagnostics; +using System.Linq; +using System.Reactive.Linq; +using System.Threading; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using Microsoft.AppCenter.Crashes; +using MoreLinq; +using Nito.AsyncEx; +using Serilog; +using Wino.Core; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Menus; +using Wino.Core.Domain.Models.Reader; +using Wino.Core.Domain.Models.Synchronization; +using Wino.Core.Messages.Mails; +using Wino.Core.Messages.Synchronization; +using Wino.Mail.ViewModels.Collections; +using Wino.Mail.ViewModels.Data; +using Wino.Mail.ViewModels.Messages; + +namespace Wino.Mail.ViewModels +{ + public partial class MailListPageViewModel : BaseViewModel, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient, + IRecipient + { + private bool isChangingFolder = false; + + private Guid? trackingSynchronizationId = null; + private int completedTrackingSynchronizationCount = 0; + + private IObservable> selectionChangedObservable = null; + + public WinoMailCollection MailCollection { get; } = new WinoMailCollection(); + + public ObservableCollection SelectedItems { get; set; } = new ObservableCollection(); + public ObservableCollection PivotFolders { get; set; } = new ObservableCollection(); + + private readonly SemaphoreSlim listManipulationSemepahore = new SemaphoreSlim(1); + private CancellationTokenSource listManipulationCancellationTokenSource = new CancellationTokenSource(); + + public IWinoNavigationService NavigationService { get; } + public IStatePersistanceService StatePersistanceService { get; } + public IPreferencesService PreferencesService { get; } + + private readonly IMailService _mailService; + private readonly INotificationBuilder _notificationBuilder; + private readonly IFolderService _folderService; + private readonly IWinoSynchronizerFactory _winoSynchronizerFactory; + private readonly IThreadingStrategyProvider _threadingStrategyProvider; + private readonly IContextMenuItemService _contextMenuItemService; + private readonly IWinoRequestDelegator _winoRequestDelegator; + private readonly IKeyPressService _keyPressService; + + private MailItemViewModel _activeMailItem; + + public List SortingOptions { get; } = + [ + new(Translator.SortingOption_Date, SortingOptionType.ReceiveDate), + new(Translator.SortingOption_Name, SortingOptionType.Sender), + ]; + + public List FilterOptions { get; } = + [ + new (Translator.FilteringOption_All, FilterOptionType.All), + new (Translator.FilteringOption_Unread, FilterOptionType.Unread), + new (Translator.FilteringOption_Flagged, FilterOptionType.Flagged) + ]; + + private FolderPivotViewModel _selectedFolderPivot; + + [ObservableProperty] + private string searchQuery; + + [ObservableProperty] + private FilterOption _selectedFilterOption; + private SortingOption _selectedSortingOption; + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(IsEmpty))] + [NotifyPropertyChangedFor(nameof(IsCriteriaFailed))] + [NotifyPropertyChangedFor(nameof(IsFolderEmpty))] + private bool isInitializingFolder; + + [ObservableProperty] + private InfoBarMessageType barSeverity; + + [ObservableProperty] + private string barMessage; + + [ObservableProperty] + private string barTitle; + + [ObservableProperty] + private bool isBarOpen; + + public MailListPageViewModel(IDialogService dialogService, + IWinoNavigationService navigationService, + IMailService mailService, + INotificationBuilder notificationBuilder, + IStatePersistanceService statePersistanceService, + IFolderService folderService, + IWinoSynchronizerFactory winoSynchronizerFactory, + IThreadingStrategyProvider threadingStrategyProvider, + IContextMenuItemService contextMenuItemService, + IWinoRequestDelegator winoRequestDelegator, + IKeyPressService keyPressService, + IPreferencesService preferencesService) : base(dialogService) + { + PreferencesService = preferencesService; + StatePersistanceService = statePersistanceService; + NavigationService = navigationService; + + _mailService = mailService; + _notificationBuilder = notificationBuilder; + _folderService = folderService; + _winoSynchronizerFactory = winoSynchronizerFactory; + _threadingStrategyProvider = threadingStrategyProvider; + _contextMenuItemService = contextMenuItemService; + _winoRequestDelegator = winoRequestDelegator; + _keyPressService = keyPressService; + + SelectedFilterOption = FilterOptions[0]; + SelectedSortingOption = SortingOptions[0]; + + selectionChangedObservable = Observable.FromEventPattern(SelectedItems, nameof(SelectedItems.CollectionChanged)); + selectionChangedObservable + .Throttle(TimeSpan.FromMilliseconds(100)) + .Subscribe(async a => + { + await ExecuteUIThread(() => { SelectedItemCollectionUpdated(a.EventArgs); }); + }); + } + + /// + /// Executes the requested mail operation for currently selected items. + /// + /// Action to execute for selected items. + [RelayCommand] + private async Task MailOperationAsync(int mailOperationIndex) + { + if (!SelectedItems.Any()) return; + + // Commands don't like enums. So it has to be int. + var operation = (MailOperation)mailOperationIndex; + + var package = new MailOperationPreperationRequest(operation, SelectedItems.Select(a => a.MailCopy)); + + await ExecuteMailOperationAsync(package); + } + + /// + /// Sens a new message to synchronize current folder. + /// + [RelayCommand] + private void SyncFolder() + { + if (!CanSynchronize) return; + + // Only synchronize listed folders. + + // When doing linked inbox sync, we need to save the sync id to report progress back only once. + // Otherwise, we will report progress for each folder and that's what we don't want. + + trackingSynchronizationId = Guid.NewGuid(); + completedTrackingSynchronizationCount = 0; + + foreach (var folder in ActiveFolder.HandlingFolders) + { + var options = new SynchronizationOptions() + { + AccountId = folder.MailAccountId, + Type = SynchronizationType.Custom, + SynchronizationFolderIds = [folder.Id], + GroupedSynchronizationTrackingId = trackingSynchronizationId + }; + + Messenger.Send(new NewSynchronizationRequested(options)); + } + } + + private async void ActiveMailItemChanged(MailItemViewModel selectedMailItemViewModel) + { + if (_activeMailItem == selectedMailItemViewModel) return; + + // Don't update active mail item if Ctrl key is pressed. + // User is probably trying to select multiple items. + // This is not the same behavior in Windows Mail, + // but it's a trash behavior. + + var isCtrlKeyPressed = _keyPressService.IsCtrlKeyPressed(); + + if (isCtrlKeyPressed) return; + + _activeMailItem = selectedMailItemViewModel; + + Messenger.Send(new ActiveMailItemChangedEvent(selectedMailItemViewModel)); + + if (selectedMailItemViewModel == null) return; + + // Automatically set mark as read or not based on preferences. + + var markAsPreference = PreferencesService.MarkAsPreference; + + if (markAsPreference == MailMarkAsOption.WhenSelected) + { + if (selectedMailItemViewModel != null && !selectedMailItemViewModel.IsRead) + { + var operation = MailOperation.MarkAsRead; + var package = new MailOperationPreperationRequest(operation,_activeMailItem.MailCopy); + + await ExecuteMailOperationAsync(package); + } + } + else if (markAsPreference == MailMarkAsOption.AfterDelay && PreferencesService.MarkAsDelay >= 0) + { + // TODO: Start a timer then queue. + } + } + + /// + /// Selected internal folder. This can be either folder's own name or Focused-Other. + /// + public FolderPivotViewModel SelectedFolderPivot + { + get => _selectedFolderPivot; + set + { + if (_selectedFolderPivot != null) + _selectedFolderPivot.SelectedItemCount = 0; + + SetProperty(ref _selectedFolderPivot, value); + } + } + + /// + /// Selected sorting option. + /// + public SortingOption SelectedSortingOption + { + get => _selectedSortingOption; + set + { + if (SetProperty(ref _selectedSortingOption, value)) + { + if (value != null && MailCollection != null) + { + MailCollection.SortingType = value.Type; + } + } + } + } + + /// + /// Current folder that is being represented from the menu. + /// + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(CanSynchronize))] + [NotifyPropertyChangedFor(nameof(IsFolderSynchronizationEnabled))] + private IBaseFolderMenuItem activeFolder; + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(CanSynchronize))] + private bool isAccountSynchronizerInSynchronization; + + public bool CanSynchronize => !IsAccountSynchronizerInSynchronization && IsFolderSynchronizationEnabled; + + public bool IsFolderSynchronizationEnabled => ActiveFolder?.IsSynchronizationEnabled ?? false; + + #region Properties + + public int SelectedItemCount => SelectedItems.Count; + public bool HasMultipleItemSelections => SelectedItemCount > 1; + public bool HasSelectedItems => SelectedItems.Any(); + public bool IsArchiveSpecialFolder => ActiveFolder?.SpecialFolderType == SpecialFolderType.Archive; + public bool IsEmpty => !IsPerformingSearch && MailCollection.Count == 0; + public bool IsCriteriaFailed => IsEmpty && IsInSearchMode; + public bool IsFolderEmpty => !IsInitializingFolder && IsEmpty && !IsInSearchMode; + + private bool _isPerformingSearch; + + public bool IsPerformingSearch + { + get => _isPerformingSearch; + set + { + if (SetProperty(ref _isPerformingSearch, value)) + { + NotifyItemFoundState(); + } + } + } + + public bool IsInSearchMode => !string.IsNullOrEmpty(SearchQuery); + + #endregion + + public void NotifyItemSelected() + { + OnPropertyChanged(nameof(HasSelectedItems)); + OnPropertyChanged(nameof(SelectedItemCount)); + OnPropertyChanged(nameof(HasMultipleItemSelections)); + + if (SelectedFolderPivot != null) + SelectedFolderPivot.SelectedItemCount = SelectedItemCount; + } + + private void NotifyItemFoundState() + { + OnPropertyChanged(nameof(IsEmpty)); + OnPropertyChanged(nameof(IsCriteriaFailed)); + OnPropertyChanged(nameof(IsFolderEmpty)); + } + + [RelayCommand] + public Task ExecuteHoverAction(MailOperationPreperationRequest request) => ExecuteMailOperationAsync(request); + + public Task ExecuteMailOperationAsync(MailOperationPreperationRequest package) => _winoRequestDelegator.ExecuteAsync(package); + + protected override void OnDispatcherAssigned() + { + base.OnDispatcherAssigned(); + + MailCollection.CoreDispatcher = Dispatcher; + } + + protected override async void OnFolderUpdated(MailItemFolder updatedFolder, MailAccount account) + { + base.OnFolderUpdated(updatedFolder, account); + + // Don't need to update if the folder update does not belong to the current folder menu item. + if (ActiveFolder == null || updatedFolder == null || !ActiveFolder.HandlingFolders.Any(a => a.Id == updatedFolder.Id)) return; + + await ExecuteUIThread(() => + { + ActiveFolder.UpdateFolder(updatedFolder); + + OnPropertyChanged(nameof(CanSynchronize)); + OnPropertyChanged(nameof(IsFolderSynchronizationEnabled)); + }); + + // Force synchronization after enabling the folder. + SyncFolder(); + } + + private async void UpdateBarMessage(InfoBarMessageType severity, string title, string message) + { + await ExecuteUIThread(() => + { + BarSeverity = severity; + BarTitle = title; + BarMessage = message; + + IsBarOpen = true; + }); + } + + private void SelectedItemCollectionUpdated(NotifyCollectionChangedEventArgs e) + { + if (SelectedItems.Count == 1) + { + ActiveMailItemChanged(SelectedItems[0]); + } + else + { + // At this point, either we don't have any item selected + // or we have multiple item selected. In either case + // there should be no active item. + + ActiveMailItemChanged(null); + } + + NotifyItemSelected(); + + Messenger.Send(new SelectedMailItemsChanged(SelectedItems.Count)); + } + + private void UpdateFolderPivots() + { + PivotFolders.Clear(); + SelectedFolderPivot = null; + + if (ActiveFolder == null) return; + + // Merged folders don't support focused feature. + + if (ActiveFolder is IMergedAccountFolderMenuItem) + { + PivotFolders.Add(new FolderPivotViewModel(ActiveFolder.FolderName, null)); + } + else if (ActiveFolder is IFolderMenuItem singleFolderMenuItem) + { + var parentAccount = singleFolderMenuItem.ParentAccount; + + bool isAccountSupportsFocusedInbox = parentAccount.Preferences.IsFocusedInboxEnabled != null; + bool isFocusedInboxEnabled = isAccountSupportsFocusedInbox && parentAccount.Preferences.IsFocusedInboxEnabled.GetValueOrDefault(); + bool isInboxFolder = ActiveFolder.SpecialFolderType == SpecialFolderType.Inbox; + + // Folder supports Focused - Other + if (isInboxFolder && isFocusedInboxEnabled) + { + // Can be passed as empty string. Focused - Other will be used regardless. + var focusedItem = new FolderPivotViewModel(string.Empty, true); + var otherItem = new FolderPivotViewModel(string.Empty, false); + + PivotFolders.Add(focusedItem); + PivotFolders.Add(otherItem); + } + else + { + // If the account and folder doesn't support focused feature, just add itself. + PivotFolders.Add(new FolderPivotViewModel(singleFolderMenuItem.FolderName, null)); + } + } + + // This will trigger refresh. + SelectedFolderPivot = PivotFolders.FirstOrDefault(); + } + + [RelayCommand] + private async Task SelectedPivotChanged() + { + if (isChangingFolder) return; + + await InitializeFolderAsync(); + } + + [RelayCommand] + private async Task SelectedSortingChanged(SortingOption option) + { + SelectedSortingOption = option; + + if (isChangingFolder) return; + + await InitializeFolderAsync(); + } + + [RelayCommand] + private async Task SelectedFilterChanged(FilterOption option) + { + SelectedFilterOption = option; + + if (isChangingFolder) return; + + await InitializeFolderAsync(); + } + + public IEnumerable GetTargetMailItemViewModels(IMailItem clickedItem) + { + // Threat threads as a whole and include everything in the group. Except single selections outside of the thread. + IEnumerable contextMailItems = null; + + if (clickedItem is ThreadMailItemViewModel clickedThreadItem) + { + // Clicked item is a thread. + + clickedThreadItem.IsThreadExpanded = true; + contextMailItems = clickedThreadItem.ThreadItems.Cast(); + + // contextMailItems = clickedThreadItem.GetMailCopies(); + } + else if (clickedItem is MailItemViewModel clickedMailItemViewModel) + { + // If the clicked item is included in SelectedItems, then we need to thing them as whole. + // If there are selected items, but clicked item is not one of them, then it's a single context menu. + + bool includedInSelectedItems = SelectedItems.Contains(clickedItem); + + if (includedInSelectedItems) + contextMailItems = SelectedItems; + else + contextMailItems = new List() { clickedMailItemViewModel }; + } + + return contextMailItems; + } + + public IEnumerable GetAvailableMailActions(IEnumerable contextMailItems) + => _contextMenuItemService.GetMailItemContextMenuActions(contextMailItems); + + public void ChangeCustomFocusedState(IEnumerable mailItems, bool isFocused) + => mailItems.Where(a => a is MailItemViewModel).Cast().ForEach(a => a.IsCustomFocused = isFocused); + + private bool ShouldPreventItemAdd(IMailItem mailItem) + { + bool condition2 = false; + + bool condition1 = mailItem.IsRead + && SelectedFilterOption.Type == FilterOptionType.Unread + || !mailItem.IsFlagged + && SelectedFilterOption.Type == FilterOptionType.Flagged; + + return condition1 || condition2; + } + + protected override async void OnMailAdded(MailCopy addedMail) + { + base.OnMailAdded(addedMail); + + try + { + await listManipulationSemepahore.WaitAsync(); + + if (ActiveFolder == null) return; + + // Messages coming to sent or draft folder must be inserted regardless of the filter. + bool shouldPreventIgnoringFilter = addedMail.AssignedFolder.SpecialFolderType == SpecialFolderType.Draft || + addedMail.AssignedFolder.SpecialFolderType == SpecialFolderType.Sent; + + // Item does not belong to this folder and doesn't have special type to be inserted. + if (!shouldPreventIgnoringFilter && !ActiveFolder.HandlingFolders.Any(a => a.Id == addedMail.AssignedFolder.Id)) return; + + if (!shouldPreventIgnoringFilter && ShouldPreventItemAdd(addedMail)) return; + + await ExecuteUIThread(async () => + { + await MailCollection.AddAsync(addedMail); + + NotifyItemFoundState(); + }); + } + catch (Exception) { } + finally + { + listManipulationSemepahore.Release(); + } + } + + protected override async void OnMailUpdated(MailCopy updatedMail) + { + base.OnMailUpdated(updatedMail); + + Debug.WriteLine($"Updating {updatedMail.Id}-> {updatedMail.UniqueId}"); + + await MailCollection.UpdateMailCopy(updatedMail); + } + + protected override async void OnMailRemoved(MailCopy removedMail) + { + base.OnMailRemoved(removedMail); + + // We should delete the items only if: + // 1. They are deleted from the active folder. + // 2. Deleted from draft or sent folder. + // Delete/sent are special folders that can list their items in other folders. + + bool removedFromActiveFolder = ActiveFolder.HandlingFolders.Any(a => a.Id == removedMail.AssignedFolder.Id); + bool removedFromDraftOrSent = removedMail.AssignedFolder.SpecialFolderType == SpecialFolderType.Draft || + removedMail.AssignedFolder.SpecialFolderType == SpecialFolderType.Sent; + + if (removedFromActiveFolder || removedFromDraftOrSent) + { + bool isDeletedMailSelected = SelectedItems.Any(a => a.MailCopy.UniqueId == removedMail.UniqueId); + + // Automatically select the next item in the list if the setting is enabled. + MailItemViewModel nextItem = null; + + if (isDeletedMailSelected && PreferencesService.AutoSelectNextItem) + { + nextItem = MailCollection.GetNextItem(removedMail); + } + + // Remove the deleted item from the list. + await MailCollection.RemoveAsync(removedMail); + + if (nextItem != null) + WeakReferenceMessenger.Default.Send(new SelectMailItemContainerEvent(nextItem, ScrollToItem: true)); + else if (isDeletedMailSelected) + { + // There are no next item to select, but we removed the last item which was selected. + // Clearing selected item will dispose rendering page. + + SelectedItems.Clear(); + } + + await ExecuteUIThread(() => { NotifyItemFoundState(); }); + } + } + protected override async void OnDraftCreated(MailCopy draftMail, MailAccount account) + { + base.OnDraftCreated(draftMail, account); + + try + { + // If the draft is created in another folder, we need to wait for that folder to be initialized. + // Otherwise the draft mail item will be duplicated on the next add execution. + await listManipulationSemepahore.WaitAsync(); + + // Create the item. Draft folder navigation is already done at this point. + await ExecuteUIThread(async () => + { + await MailCollection.AddAsync(draftMail); + + // New draft is created by user. Select the item. + Messenger.Send(new MailItemNavigationRequested(draftMail.UniqueId, ScrollToItem: true)); + + NotifyItemFoundState(); + }); + } + finally + { + listManipulationSemepahore.Release(); + } + } + + private IEnumerable PrepareMailViewModels(IEnumerable mailItems) + { + foreach (var item in mailItems) + { + if (item is MailCopy singleMailItem) + yield return new MailItemViewModel(singleMailItem); + else if (item is ThreadMailItem threadMailItem) + yield return new ThreadMailItemViewModel(threadMailItem); + } + } + + [RelayCommand] + private async Task LoadMoreItemsAsync() + { + if (IsInitializingFolder) return; + + await ExecuteUIThread(() => { IsInitializingFolder = true; }); + + var initializationOptions = new MailListInitializationOptions(ActiveFolder.HandlingFolders, + SelectedFilterOption.Type, + SelectedSortingOption.Type, + PreferencesService.IsThreadingEnabled, + SelectedFolderPivot.IsFocused, + SearchQuery, + MailCollection.MailCopyIdHashSet); + + var items = await _mailService.FetchMailsAsync(initializationOptions).ConfigureAwait(false); + + var viewModels = PrepareMailViewModels(items); + + await ExecuteUIThread(() => { MailCollection.AddRange(viewModels, clearIdCache: false); }); + await ExecuteUIThread(() => { IsInitializingFolder = false; }); + } + + private async Task InitializeFolderAsync() + { + if (SelectedFilterOption == null || SelectedFolderPivot == null || SelectedSortingOption == null) + return; + + try + { + // Clear search query if not performing search. + if (!IsPerformingSearch) + SearchQuery = string.Empty; + + MailCollection.Clear(); + MailCollection.MailCopyIdHashSet.Clear(); + + SelectedItems.Clear(); + + if (ActiveFolder == null) + return; + + await ExecuteUIThread(() => { IsInitializingFolder = true; }); + + // Folder is changed during initialization. + // Just cancel the existing one and wait for new initialization. + + if (listManipulationSemepahore.CurrentCount == 0) + { + Debug.WriteLine("Canceling initialization of mails."); + + listManipulationCancellationTokenSource.Cancel(); + listManipulationCancellationTokenSource.Token.ThrowIfCancellationRequested(); + } + + listManipulationCancellationTokenSource = new CancellationTokenSource(); + + var cancellationToken = listManipulationCancellationTokenSource.Token; + + await listManipulationSemepahore.WaitAsync(cancellationToken); + + // Setup MailCollection configuration. + + // Don't pass any threading strategy if disabled in settings. + MailCollection.ThreadingStrategyProvider = PreferencesService.IsThreadingEnabled ? _threadingStrategyProvider : null; + + // TODO: This should go inside + MailCollection.PruneSingleNonDraftItems = ActiveFolder.SpecialFolderType == SpecialFolderType.Draft; + + // Here items are sorted and filtered. + + var initializationOptions = new MailListInitializationOptions(ActiveFolder.HandlingFolders, + SelectedFilterOption.Type, + SelectedSortingOption.Type, + PreferencesService.IsThreadingEnabled, + SelectedFolderPivot.IsFocused, + SearchQuery, + MailCollection.MailCopyIdHashSet); + + var items = await _mailService.FetchMailsAsync(initializationOptions).ConfigureAwait(false); + + // Here they are already threaded if needed. + // We don't need to insert them one by one. + // Just create VMs and do bulk insert. + + var viewModels = PrepareMailViewModels(items); + + await ExecuteUIThread(() => { MailCollection.AddRange(viewModels, true); }); + } + catch (OperationCanceledException) + { + Debug.WriteLine("Initialization of mails canceled."); + } + catch (Exception ex) + { + Debugger.Break(); + + if (IsInSearchMode) + Log.Error(ex, WinoErrors.SearchFailed); + else + Log.Error(ex, WinoErrors.MailListRefreshFolder); + + Crashes.TrackError(ex); + } + finally + { + listManipulationSemepahore.Release(); + + await ExecuteUIThread(() => + { + IsInitializingFolder = false; + + OnPropertyChanged(nameof(CanSynchronize)); + NotifyItemFoundState(); + }); + } + } + + [RelayCommand] + private async Task EnableFolderSynchronizationAsync() + { + if (ActiveFolder == null) return; + + foreach (var folder in ActiveFolder.HandlingFolders) + { + await _folderService.ChangeFolderSynchronizationStateAsync(folder.Id, true); + } + + // TODO + //ActiveFolder.IsSynchronizationEnabled = true; + + //OnPropertyChanged(nameof(IsFolderSynchronizationEnabled)); + //OnPropertyChanged(nameof(CanSynchronize)); + + //SyncFolderCommand?.Execute(null); + } + + void IRecipient.Receive(MailItemNavigationRequested message) + { + // Find mail item and add to selected items. + + MailItemViewModel navigatingMailItem = null; + ThreadMailItemViewModel threadMailItemViewModel = null; + + for (int i = 0; i < 3; i++) + { + var mailContainer = MailCollection.GetMailItemContainer(message.UniqueMailId); + + if (mailContainer != null) + { + navigatingMailItem = mailContainer.ItemViewModel; + threadMailItemViewModel = mailContainer.ThreadViewModel; + + break; + } + } + + if (threadMailItemViewModel != null) + threadMailItemViewModel.IsThreadExpanded = true; + + if (navigatingMailItem != null) + WeakReferenceMessenger.Default.Send(new SelectMailItemContainerEvent(navigatingMailItem, message.ScrollToItem)); + else + Debugger.Break(); + } + + async void IRecipient.Receive(ActiveMailFolderChangedEvent message) + { + isChangingFolder = true; + + ActiveFolder = message.BaseFolderMenuItem; + + trackingSynchronizationId = null; + completedTrackingSynchronizationCount = 0; + + // Check whether the account synchronizer that this folder belongs to is already in synchronization. + await CheckIfAccountIsSynchronizingAsync(); + + // Notify change for archive-unarchive app bar button. + OnPropertyChanged(nameof(IsArchiveSpecialFolder)); + + // Prepare Focused - Other or folder name tabs. + UpdateFolderPivots(); + + await InitializeFolderAsync(); + + // TODO: This should be done in a better way. + while (IsInitializingFolder) + { + await Task.Delay(100); + } + + // Let awaiters know about the completion of mail init. + message.FolderInitLoadAwaitTask?.TrySetResult(true); + + isChangingFolder = false; + } + + void IRecipient.Receive(MailItemSelectedEvent message) + => SelectedItems.Add(message.SelectedMailItem); + + void IRecipient.Receive(MailItemSelectionRemovedEvent message) + => SelectedItems.Remove(message.RemovedMailItem); + + public void Receive(AccountSynchronizationCompleted message) + { + if (ActiveFolder == null) return; + + bool isLinkedInboxSyncResult = message.SynchronizationTrackingId == trackingSynchronizationId; + + if (isLinkedInboxSyncResult) + { + var isCompletedAccountListed = ActiveFolder.HandlingFolders.Any(a => a.MailAccountId == message.AccountId); + + if (isCompletedAccountListed) completedTrackingSynchronizationCount++; + + // Group sync is started but not all folders are synchronized yet. Don't report progress. + if (completedTrackingSynchronizationCount < ActiveFolder.HandlingFolders.Count()) return; + } + + bool isReportingActiveAccountResult = ActiveFolder.HandlingFolders.Any(a => a.MailAccountId == message.AccountId); + + if (!isReportingActiveAccountResult) return; + + // At this point either all folders or a single folder sync is completed. + switch (message.Result) + { + case SynchronizationCompletedState.Success: + UpdateBarMessage(InfoBarMessageType.Success, ActiveFolder.FolderName, Translator.SynchronizationFolderReport_Success); + break; + case SynchronizationCompletedState.Failed: + UpdateBarMessage(InfoBarMessageType.Error, ActiveFolder.FolderName, Translator.SynchronizationFolderReport_Failed); + break; + default: + break; + } + } + + public async void Receive(NewSynchronizationRequested message) + => await ExecuteUIThread(() => { OnPropertyChanged(nameof(CanSynchronize)); }); + + [RelayCommand] + public async Task PerformSearchAsync() + { + try + { + IsPerformingSearch = !string.IsNullOrEmpty(SearchQuery); + + await InitializeFolderAsync(); + } + finally + { + IsPerformingSearch = false; + } + } + + public async void Receive(AccountSynchronizerStateChanged message) + => await CheckIfAccountIsSynchronizingAsync(); + + private async Task CheckIfAccountIsSynchronizingAsync() + { + bool isAnyAccountSynchronizing = false; + + // Check each account that this page is listing folders from. + // If any of the synchronizers are synchronizing, we disable sync. + + if (ActiveFolder != null) + { + var accountIds = ActiveFolder.HandlingFolders.Select(a => a.MailAccountId); + + foreach (var accountId in accountIds) + { + var synchronizer = _winoSynchronizerFactory.GetAccountSynchronizer(accountId); + + if (synchronizer == null) continue; + + bool isAccountSynchronizing = synchronizer.State != AccountSynchronizerState.Idle; + + if (isAccountSynchronizing) + { + isAnyAccountSynchronizing = true; + break; + } + } + } + + await ExecuteUIThread(() => { IsAccountSynchronizerInSynchronization = isAnyAccountSynchronizing; }); + } + } +} diff --git a/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs b/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs new file mode 100644 index 00000000..d51d2073 --- /dev/null +++ b/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs @@ -0,0 +1,619 @@ +using System; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using MailKit; +using Microsoft.AppCenter.Crashes; +using MimeKit; +using Serilog; +using Wino.Core; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Menus; +using Wino.Core.Domain.Models.Navigation; +using Wino.Core.Domain.Models.Reader; +using Wino.Core.Extensions; +using Wino.Core.Messages.Mails; +using Wino.Core.Services; +using Wino.Mail.ViewModels.Data; + +namespace Wino.Mail.ViewModels +{ + public partial class MailRenderingPageViewModel : BaseViewModel, + ITransferProgress // For listening IMAP message download progress. + { + private readonly IUnderlyingThemeService _underlyingThemeService; + + private readonly IMimeFileService _mimeFileService; + private readonly Core.Domain.Interfaces.IMailService _mailService; + private readonly IFileService _fileService; + private readonly IWinoSynchronizerFactory _winoSynchronizerFactory; + private readonly IWinoRequestDelegator _requestDelegator; + private readonly IClipboardService _clipboardService; + + private bool forceImageLoading = false; + + private MailItemViewModel initializedMailItemViewModel = null; + private MimeMessageInformation initializedMimeMessageInformation = null; + + #region Properties + + public bool ShouldDisplayDownloadProgress => IsIndetermineProgress || (CurrentDownloadPercentage > 0 && CurrentDownloadPercentage <= 100); + public bool CanUnsubscribe => CurrentRenderModel?.CanUnsubscribe ?? false; + public bool IsJunkMail => initializedMailItemViewModel?.AssignedFolder != null && initializedMailItemViewModel.AssignedFolder.SpecialFolderType == SpecialFolderType.Junk; + + public bool IsImageRenderingDisabled + { + get + { + if (IsJunkMail) + { + return !forceImageLoading; + } + else + { + return !CurrentRenderModel?.MailRenderingOptions?.LoadImages ?? false; + } + } + } + + private bool isDarkWebviewRenderer; + public bool IsDarkWebviewRenderer + { + get => isDarkWebviewRenderer; + set + { + if (SetProperty(ref isDarkWebviewRenderer, value)) + { + InitializeCommandBarItems(); + } + } + } + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(ShouldDisplayDownloadProgress))] + private bool isIndetermineProgress; + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(ShouldDisplayDownloadProgress))] + private double currentDownloadPercentage; + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(CanUnsubscribe))] + private MailRenderModel currentRenderModel; + + [ObservableProperty] + private string subject; + + [ObservableProperty] + private string fromAddress; + + [ObservableProperty] + private string fromName; + + [ObservableProperty] + private DateTime creationDate; + + + public ObservableCollection ToItems { get; set; } = new ObservableCollection(); + public ObservableCollection CCItemsItems { get; set; } = new ObservableCollection(); + public ObservableCollection BCCItems { get; set; } = new ObservableCollection(); + public ObservableCollection Attachments { get; set; } = new ObservableCollection(); + public ObservableCollection MenuItems { get; set; } = new ObservableCollection(); + + #endregion + + public INativeAppService NativeAppService { get; } + public IStatePersistanceService StatePersistanceService { get; } + public IPreferencesService PreferencesService { get; } + + public MailRenderingPageViewModel(IDialogService dialogService, + INativeAppService nativeAppService, + IUnderlyingThemeService underlyingThemeService, + IMimeFileService mimeFileService, + Core.Domain.Interfaces.IMailService mailService, + IFileService fileService, + IWinoSynchronizerFactory winoSynchronizerFactory, + IWinoRequestDelegator requestDelegator, + IStatePersistanceService statePersistanceService, + IClipboardService clipboardService, + IPreferencesService preferencesService) : base(dialogService) + { + NativeAppService = nativeAppService; + StatePersistanceService = statePersistanceService; + PreferencesService = preferencesService; + + _clipboardService = clipboardService; + _underlyingThemeService = underlyingThemeService; + _mimeFileService = mimeFileService; + _mailService = mailService; + _fileService = fileService; + _winoSynchronizerFactory = winoSynchronizerFactory; + _requestDelegator = requestDelegator; + } + + + [RelayCommand] + private async Task CopyClipboard(string copyText) + { + try + { + await _clipboardService.CopyClipboardAsync(copyText); + + DialogService.InfoBarMessage(Translator.ClipboardTextCopied_Title, string.Format(Translator.ClipboardTextCopied_Message, copyText), InfoBarMessageType.Information); + } + catch (Exception) + { + DialogService.InfoBarMessage(Translator.GeneralTitle_Error, string.Format(Translator.ClipboardTextCopyFailed_Message, copyText), InfoBarMessageType.Error); + } + } + + [RelayCommand] + private async Task ForceImageLoading() + { + forceImageLoading = true; + + if (initializedMailItemViewModel == null && initializedMimeMessageInformation == null) return; + + if (initializedMailItemViewModel != null) + await RenderAsync(initializedMimeMessageInformation); + else + await RenderAsync(initializedMimeMessageInformation); + } + + [RelayCommand] + private async Task UnsubscribeAsync() + { + if (!CurrentRenderModel?.CanUnsubscribe ?? false) return; + + // TODO: Support for List-Unsubscribe-Post header. It can be done without launching browser. + // https://certified-senders.org/wp-content/uploads/2017/07/CSA_one-click_list-unsubscribe.pdf + + // TODO: Sometimes unsubscribe link can be a mailto: link. + // or sometimes with mailto AND http link. We need to handle this. + + if (Uri.IsWellFormedUriString(CurrentRenderModel.UnsubscribeLink, UriKind.RelativeOrAbsolute)) + await NativeAppService.LaunchUriAsync(new Uri((CurrentRenderModel.UnsubscribeLink))); + else + DialogService.InfoBarMessage(Translator.Info_UnsubscribeLinkInvalidTitle, Translator.Info_UnsubscribeLinkInvalidMessage, InfoBarMessageType.Error); + } + + [RelayCommand] + private async Task OperationClicked(MailOperationMenuItem menuItem) + { + if (menuItem == null) return; + + await HandleMailOperationAsync(menuItem.Operation); + } + + private async Task HandleMailOperationAsync(MailOperation operation) + { + // Toggle theme + if (operation == MailOperation.DarkEditor || operation == MailOperation.LightEditor) + IsDarkWebviewRenderer = !IsDarkWebviewRenderer; + else if (operation == MailOperation.SaveAs) + { + // Save as PDF + var pickedFolder = await DialogService.PickWindowsFolderAsync(); + + if (!string.IsNullOrEmpty(pickedFolder)) + { + var fullPath = Path.Combine(pickedFolder, $"{initializedMailItemViewModel.FromAddress}.pdf"); + Messenger.Send(new SaveAsPDFRequested(fullPath)); + } + } + else if (operation == MailOperation.Reply || operation == MailOperation.ReplyAll || operation == MailOperation.Forward) + { + if (initializedMailItemViewModel == null) return; + + // Create new draft. + var draftOptions = new DraftCreationOptions(); + + if (operation == MailOperation.Reply) + draftOptions.Reason = DraftCreationReason.Reply; + else if (operation == MailOperation.ReplyAll) + draftOptions.Reason = DraftCreationReason.ReplyAll; + else if (operation == MailOperation.Forward) + draftOptions.Reason = DraftCreationReason.Forward; + + // TODO: Separate mailto related stuff out of DraftCreationOptions and provide better + // model for draft preperation request. Right now it's a mess. + + draftOptions.ReferenceMailCopy = initializedMailItemViewModel.MailCopy; + draftOptions.ReferenceMimeMessage = initializedMimeMessageInformation.MimeMessage; + + var createdMimeMessage = await _mailService.CreateDraftMimeMessageAsync(initializedMailItemViewModel.AssignedAccount.Id, draftOptions).ConfigureAwait(false); + + var createdDraftMailMessage = await _mailService.CreateDraftAsync(initializedMailItemViewModel.AssignedAccount, + createdMimeMessage, + initializedMimeMessageInformation.MimeMessage, + initializedMailItemViewModel).ConfigureAwait(false); + + var draftPreperationRequest = new DraftPreperationRequest(initializedMailItemViewModel.AssignedAccount, createdDraftMailMessage, createdMimeMessage) + { + ReferenceMimeMessage = initializedMimeMessageInformation.MimeMessage, + ReferenceMailCopy = initializedMailItemViewModel.MailCopy + }; + + await _requestDelegator.ExecuteAsync(draftPreperationRequest); + + } + else if (initializedMailItemViewModel != null) + { + // All other operations require a mail item. + var prepRequest = new MailOperationPreperationRequest(operation, initializedMailItemViewModel.MailCopy); + await _requestDelegator.ExecuteAsync(prepRequest); + } + } + + private CancellationTokenSource renderCancellationTokenSource = new CancellationTokenSource(); + + public override async void OnNavigatedTo(NavigationMode mode, object parameters) + { + base.OnNavigatedTo(mode, parameters); + + renderCancellationTokenSource.Cancel(); + + initializedMailItemViewModel = null; + initializedMimeMessageInformation = null; + + // This page can be accessed for 2 purposes. + // 1. Rendering a mail item when the user selects. + // 2. Rendering an existing EML file with MimeMessage. + + // MimeMessage rendering must be readonly and no command bar items must be shown except common + // items like dark/light editor, zoom, print etc. + + // Configure common rendering properties first. + IsDarkWebviewRenderer = _underlyingThemeService.IsUnderlyingThemeDark(); + + await ResetPagePropertiesAsync(); + + renderCancellationTokenSource = new CancellationTokenSource(); + + // Mime content might not be available for now and might require a download. + try + { + if (parameters is MailItemViewModel selectedMailItemViewModel) + await RenderAsync(selectedMailItemViewModel, renderCancellationTokenSource.Token); + else if (parameters is MimeMessageInformation mimeMessageInformation) + await RenderAsync(mimeMessageInformation); + + InitializeCommandBarItems(); + } + catch (OperationCanceledException) + { + Log.Information("Canceled mail rendering."); + } + catch (Exception ex) + { + DialogService.InfoBarMessage(Translator.Info_MailRenderingFailedTitle, string.Format(Translator.Info_MailRenderingFailedMessage, ex.Message), InfoBarMessageType.Error); + + Crashes.TrackError(ex); + Log.Error(ex, "Render Failed"); + } + finally + { + StatePersistanceService.IsReadingMail = true; + } + } + + + private async Task HandleSingleItemDownloadAsync(MailItemViewModel mailItemViewModel) + { + var synchronizer = _winoSynchronizerFactory.GetAccountSynchronizer(mailItemViewModel.AssignedAccount.Id); + + try + { + // To show the progress on the UI. + CurrentDownloadPercentage = 1; + + await synchronizer.DownloadMissingMimeMessageAsync(mailItemViewModel.MailCopy, this, renderCancellationTokenSource.Token); + } + catch (OperationCanceledException) + { + Log.Information("MIME download is canceled."); + } + catch (Exception ex) + { + DialogService.InfoBarMessage(Translator.GeneralTitle_Error, ex.Message, InfoBarMessageType.Error); + } + finally + { + ResetProgress(); + } + } + + private async Task RenderAsync(MailItemViewModel mailItemViewModel, CancellationToken cancellationToken = default) + { + var isMimeExists = await _mimeFileService.IsMimeExistAsync(mailItemViewModel.AssignedAccount.Id, mailItemViewModel.MailCopy.FileId); + + if (!isMimeExists) + { + await HandleSingleItemDownloadAsync(mailItemViewModel); + } + + // Find the MIME for this item and render it. + var mimeMessageInformation = await _mimeFileService.GetMimeMessageInformationAsync(mailItemViewModel.MailCopy.FileId, + mailItemViewModel.AssignedAccount.Id, + cancellationToken) + .ConfigureAwait(false); + + if (mimeMessageInformation == null) + { + DialogService.InfoBarMessage(Translator.Info_MessageCorruptedTitle, Translator.Info_MessageCorruptedMessage, InfoBarMessageType.Error); + return; + } + + initializedMailItemViewModel = mailItemViewModel; + await RenderAsync(mimeMessageInformation); + } + + private async Task RenderAsync(MimeMessageInformation mimeMessageInformation) + { + var message = mimeMessageInformation.MimeMessage; + var messagePath = mimeMessageInformation.Path; + + initializedMimeMessageInformation = mimeMessageInformation; + + // TODO: Handle S/MIME decryption. + // initializedMimeMessageInformation.MimeMessage.Body is MultipartSigned + + var renderingOptions = PreferencesService.GetRenderingOptions(); + + await ExecuteUIThread(() => + { + Subject = message.Subject; + + // TODO: FromName and FromAddress is probably not correct here for mail lists. + FromAddress = message.From.Mailboxes.FirstOrDefault()?.Address ?? Translator.UnknownAddress; + FromName = message.From.Mailboxes.FirstOrDefault()?.Name ?? Translator.UnknownSender; + CreationDate = message.Date.DateTime; + + // Extract to,cc and bcc + LoadAddressInfo(message.To, ToItems); + LoadAddressInfo(message.Cc, CCItemsItems); + LoadAddressInfo(message.Bcc, BCCItems); + + // Automatically disable images for Junk folder to prevent pixel tracking. + // This can only work for selected mail item rendering, not for EML file rendering. + if (initializedMailItemViewModel != null && + initializedMailItemViewModel.AssignedFolder.SpecialFolderType == SpecialFolderType.Junk) + { + renderingOptions.LoadImages = false; + } + + // Load images if forced. + if (forceImageLoading) + { + renderingOptions.LoadImages = true; + } + + CurrentRenderModel = _mimeFileService.GetMailRenderModel(message, messagePath, renderingOptions); + + Messenger.Send(new HtmlRenderingRequested(CurrentRenderModel.RenderHtml)); + + foreach (var attachment in CurrentRenderModel.Attachments) + { + Attachments.Add(new MailAttachmentViewModel(attachment)); + } + + OnPropertyChanged(nameof(IsImageRenderingDisabled)); + }); + } + + public override void OnNavigatedFrom(NavigationMode mode, object parameters) + { + base.OnNavigatedFrom(mode, parameters); + + renderCancellationTokenSource.Cancel(); + CurrentDownloadPercentage = 0d; + + initializedMailItemViewModel = null; + initializedMimeMessageInformation = null; + + StatePersistanceService.IsReadingMail = false; + forceImageLoading = false; + } + + private void LoadAddressInfo(InternetAddressList list, ObservableCollection collection) + { + foreach (var item in list) + { + if (item is MailboxAddress mailboxAddress) + collection.Add(mailboxAddress.ToAddressInformation()); + else if (item is GroupAddress groupAddress) + LoadAddressInfo(groupAddress.Members, collection); + } + } + + private void ResetProgress() + { + CurrentDownloadPercentage = 0; + IsIndetermineProgress = false; + } + + private async Task ResetPagePropertiesAsync() + { + await ExecuteUIThread(() => + { + ResetProgress(); + + ToItems.Clear(); + CCItemsItems.Clear(); + BCCItems.Clear(); + Attachments.Clear(); + + // Dispose existing content first. + Messenger.Send(new CancelRenderingContentRequested()); + }); + } + + private void InitializeCommandBarItems() + { + MenuItems.Clear(); + + // Add light/dark editor theme switch. + if (_underlyingThemeService.IsUnderlyingThemeDark()) + MenuItems.Add(MailOperationMenuItem.Create(MailOperation.LightEditor)); + else + MenuItems.Add(MailOperationMenuItem.Create(MailOperation.DarkEditor)); + + // Save As PDF + MenuItems.Add(MailOperationMenuItem.Create(MailOperation.SaveAs, true, true)); + + if (initializedMailItemViewModel == null) + return; + + MenuItems.Add(MailOperationMenuItem.Create(MailOperation.Seperator)); + + // You can't do these to draft items. + if (!initializedMailItemViewModel.IsDraft) + { + // Reply + MenuItems.Add(MailOperationMenuItem.Create(MailOperation.Reply)); + + // Reply All + MenuItems.Add(MailOperationMenuItem.Create(MailOperation.ReplyAll)); + + // Forward + MenuItems.Add(MailOperationMenuItem.Create(MailOperation.Forward)); + } + + // Archive - Unarchive + if (initializedMailItemViewModel.AssignedFolder.SpecialFolderType == SpecialFolderType.Archive) + MenuItems.Add(MailOperationMenuItem.Create(MailOperation.UnArchive)); + else + MenuItems.Add(MailOperationMenuItem.Create(MailOperation.Archive)); + + // Delete + MenuItems.Add(MailOperationMenuItem.Create(MailOperation.SoftDelete)); + + // Flag - Clear Flag + if (initializedMailItemViewModel.IsFlagged) + MenuItems.Add(MailOperationMenuItem.Create(MailOperation.ClearFlag)); + else + MenuItems.Add(MailOperationMenuItem.Create(MailOperation.SetFlag)); + + // Secondary items. + + // Read - Unread + if (initializedMailItemViewModel.IsRead) + MenuItems.Add(MailOperationMenuItem.Create(MailOperation.MarkAsUnread, true, false)); + else + MenuItems.Add(MailOperationMenuItem.Create(MailOperation.MarkAsRead, true, false)); + } + + protected override async void OnMailUpdated(MailCopy updatedMail) + { + base.OnMailUpdated(updatedMail); + + if (initializedMailItemViewModel == null) return; + + // Check if the updated mail is the same mail item we are rendering. + // This is done with UniqueId to include FolderId into calculations. + if (initializedMailItemViewModel.UniqueId != updatedMail.UniqueId) return; + + // Mail operation might change the mail item like mark read/unread or change flag. + // So we need to update the mail item view model when this happens. + // Also command bar items must be re-initialized since the items loaded based on the mail item. + + await ExecuteUIThread(() => { InitializeCommandBarItems(); }); + } + + [RelayCommand] + private async Task OpenAttachmentAsync(MailAttachmentViewModel attachmentViewModel) + { + try + { + var fileFolderPath = Path.Combine(initializedMimeMessageInformation.Path, attachmentViewModel.FileName); + var directoryInfo = new DirectoryInfo(initializedMimeMessageInformation.Path); + + var fileExists = File.Exists(fileFolderPath); + + if (!fileExists) + await SaveAttachmentInternalAsync(attachmentViewModel, initializedMimeMessageInformation.Path); + + await LaunchFileInternalAsync(fileFolderPath); + } + catch (Exception ex) + { + Log.Error(ex, WinoErrors.OpenAttachment); + Crashes.TrackError(ex); + + DialogService.InfoBarMessage(Translator.Info_AttachmentOpenFailedTitle, Translator.Info_AttachmentOpenFailedMessage, InfoBarMessageType.Error); + } + } + + [RelayCommand] + private async Task SaveAttachmentAsync(MailAttachmentViewModel attachmentViewModel) + { + if (attachmentViewModel == null) + return; + + try + { + attachmentViewModel.IsBusy = true; + + var pickedPath = await DialogService.PickWindowsFolderAsync(); + + if (string.IsNullOrEmpty(pickedPath)) return; + + await SaveAttachmentInternalAsync(attachmentViewModel, pickedPath); + + DialogService.InfoBarMessage(Translator.Info_AttachmentSaveSuccessTitle, Translator.Info_AttachmentSaveSuccessMessage, InfoBarMessageType.Success); + } + catch (Exception ex) + { + Log.Error(ex, WinoErrors.SaveAttachment); + Crashes.TrackError(ex); + + DialogService.InfoBarMessage(Translator.Info_AttachmentSaveFailedTitle, Translator.Info_AttachmentSaveFailedMessage, InfoBarMessageType.Error); + } + finally + { + attachmentViewModel.IsBusy = false; + } + } + + // Returns created file path. + private async Task SaveAttachmentInternalAsync(MailAttachmentViewModel attachmentViewModel, string saveFolderPath) + { + var fullFilePath = Path.Combine(saveFolderPath, attachmentViewModel.FileName); + var stream = await _fileService.GetFileStreamAsync(saveFolderPath, attachmentViewModel.FileName); + + using (stream) + { + await attachmentViewModel.MimeContent.DecodeToAsync(stream); + } + + return fullFilePath; + } + + private async Task LaunchFileInternalAsync(string filePath) + { + try + { + await NativeAppService.LaunchFileAsync(filePath); + } + catch (Exception ex) + { + DialogService.InfoBarMessage(Translator.Info_FileLaunchFailedTitle, ex.Message, InfoBarMessageType.Error); + } + } + + void ITransferProgress.Report(long bytesTransferred, long totalSize) + => _ = ExecuteUIThread(() => { CurrentDownloadPercentage = bytesTransferred * 100 / Math.Max(1, totalSize); }); + + // For upload. + void ITransferProgress.Report(long bytesTransferred) { } + } +} diff --git a/Wino.Mail.ViewModels/MergedAccountDetailsPageViewModel.cs b/Wino.Mail.ViewModels/MergedAccountDetailsPageViewModel.cs new file mode 100644 index 00000000..430ca336 --- /dev/null +++ b/Wino.Mail.ViewModels/MergedAccountDetailsPageViewModel.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using Wino.Core.Domain; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Navigation; +using Wino.Core.Messages.Navigation; +using Wino.Core.Requests; +using Wino.Mail.ViewModels.Data; + +namespace Wino.Mail.ViewModels +{ + public partial class MergedAccountDetailsPageViewModel : BaseViewModel, + IRecipient + { + [ObservableProperty] + private MergedAccountProviderDetailViewModel editingMergedAccount; + + [ObservableProperty] + private string mergedAccountName; + + public ObservableCollection LinkedAccounts { get; set; } = []; + public ObservableCollection UnlinkedAccounts { get; set; } = []; + + // Empty Guid is passed for new created merged inboxes. + public bool IsMergedInboxSaved => EditingMergedAccount != null && EditingMergedAccount.MergedInbox.Id != Guid.Empty; + + public bool CanUnlink => IsMergedInboxSaved; + + // There must be at least 2 accounts linked to a merged account for link to exist. + public bool ShouldDeleteMergedAccount => LinkedAccounts.Count < 2; + + public bool CanSaveChanges + { + get + { + if (IsMergedInboxSaved) + { + return ShouldDeleteMergedAccount || IsEditingAccountsDirty(); + } + else + { + return LinkedAccounts.Any(); + } + } + } + + private readonly IAccountService _accountService; + private readonly IPreferencesService _preferencesService; + private readonly IProviderService _providerService; + + public MergedAccountDetailsPageViewModel(IDialogService dialogService, + IAccountService accountService, + IPreferencesService preferencesService, + IProviderService providerService) : base(dialogService) + { + _accountService = accountService; + _preferencesService = preferencesService; + _providerService = providerService; + } + + [RelayCommand(CanExecute = nameof(CanUnlink))] + private async Task UnlinkAccountsAsync() + { + if (EditingMergedAccount == null) return; + + var isConfirmed = await DialogService.ShowConfirmationDialogAsync(Translator.DialogMessage_UnlinkAccountsConfirmationMessage, Translator.DialogMessage_UnlinkAccountsConfirmationTitle, Translator.Buttons_Yes); + + if (!isConfirmed) return; + + await _accountService.UnlinkMergedInboxAsync(EditingMergedAccount.MergedInbox.Id); + + Messenger.Send(new BackBreadcrumNavigationRequested()); + } + + [RelayCommand(CanExecute = nameof(CanSaveChanges))] + private async Task SaveChangesAsync() + { + if (ShouldDeleteMergedAccount) + { + await UnlinkAccountsAsync(); + } + else + { + if (IsMergedInboxSaved) + { + await _accountService.UpdateMergedInboxAsync(EditingMergedAccount.MergedInbox.Id, LinkedAccounts.Select(a => a.Account.Id).ToList()); + } + else + { + await _accountService.CreateMergeAccountsAsync(EditingMergedAccount.MergedInbox, LinkedAccounts.Select(a => a.Account).ToList()); + } + + // Startup entity is linked now. Change the startup entity. + if (_preferencesService.StartupEntityId != null && LinkedAccounts.Any(a => a.StartupEntityId == _preferencesService.StartupEntityId)) + { + _preferencesService.StartupEntityId = EditingMergedAccount.MergedInbox.Id; + } + } + + Messenger.Send(new BackBreadcrumNavigationRequested()); + } + + [RelayCommand] + private async Task RenameLinkAsync() + { + if (EditingMergedAccount == null) return; + + var newName = await DialogService.ShowTextInputDialogAsync(EditingMergedAccount.MergedInbox.Name, + Translator.DialogMessage_RenameLinkedAccountsTitle, + Translator.DialogMessage_RenameLinkedAccountsMessage); + + if (string.IsNullOrWhiteSpace(newName)) return; + + EditingMergedAccount.MergedInbox.Name = newName; + + // Update database record as well. + if (IsMergedInboxSaved) + { + await _accountService.RenameMergedAccountAsync(EditingMergedAccount.MergedInbox.Id, newName); + } + else + { + // Publish the message manually since the merged inbox is not saved yet. + // This is only for breadcrump item update. + + Messenger.Send(new MergedInboxRenamed(EditingMergedAccount.MergedInbox.Id, newName)); + } + } + + [RelayCommand] + private void LinkAccount(AccountProviderDetailViewModel account) + { + LinkedAccounts.Add(account); + UnlinkedAccounts.Remove(account); + } + + [RelayCommand] + private void UnlinkAccount(AccountProviderDetailViewModel account) + { + UnlinkedAccounts.Add(account); + LinkedAccounts.Remove(account); + } + + private bool IsEditingAccountsDirty() + { + if (EditingMergedAccount == null) return false; + + return EditingMergedAccount.HoldingAccounts.Count != LinkedAccounts.Count || + EditingMergedAccount.HoldingAccounts.Any(a => !LinkedAccounts.Any(la => la.Account.Id == a.Account.Id)); + } + + public override void OnNavigatedFrom(NavigationMode mode, object parameters) + { + base.OnNavigatedFrom(mode, parameters); + + LinkedAccounts.CollectionChanged -= LinkedAccountsUpdated; + } + + private void LinkedAccountsUpdated(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + OnPropertyChanged(nameof(ShouldDeleteMergedAccount)); + SaveChangesCommand.NotifyCanExecuteChanged(); + + // TODO: Preview common folders for all linked accounts. + // Basically showing a preview of how menu items will look. + } + + public override async void OnNavigatedTo(NavigationMode mode, object parameters) + { + base.OnNavigatedTo(mode, parameters); + + LinkedAccounts.CollectionChanged -= LinkedAccountsUpdated; + LinkedAccounts.CollectionChanged += LinkedAccountsUpdated; + + if (parameters is MergedAccountProviderDetailViewModel editingMergedAccount) + { + MergedAccountName = editingMergedAccount.MergedInbox.Name; + EditingMergedAccount = editingMergedAccount; + + foreach (var account in editingMergedAccount.HoldingAccounts) + { + LinkedAccounts.Add(account); + } + + // Load unlinked accounts. + + var allAccounts = await _accountService.GetAccountsAsync(); + + foreach (var account in allAccounts) + { + if (!LinkedAccounts.Any(a => a.Account.Id == account.Id)) + { + var provider = _providerService.GetProviderDetail(account.ProviderType); + + UnlinkedAccounts.Add(new AccountProviderDetailViewModel(provider, account)); + } + } + } + + UnlinkAccountsCommand.NotifyCanExecuteChanged(); + } + + public void Receive(MergedInboxRenamed message) + { + if (EditingMergedAccount?.MergedInbox.Id == message.MergedInboxId) + { + EditingMergedAccount.MergedInbox.Name = message.NewName; + } + } + } +} diff --git a/Wino.Mail.ViewModels/MessageListPageViewModel.cs b/Wino.Mail.ViewModels/MessageListPageViewModel.cs new file mode 100644 index 00000000..8ec9dc68 --- /dev/null +++ b/Wino.Mail.ViewModels/MessageListPageViewModel.cs @@ -0,0 +1,87 @@ +using System.Collections.Generic; +using Wino.Core.Domain; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Mail.ViewModels +{ + public class MessageListPageViewModel : BaseViewModel + { + public IPreferencesService PreferencesService { get; } + + private List availableHoverActions = new List + { + MailOperation.Archive, + MailOperation.SoftDelete, + MailOperation.SetFlag, + MailOperation.MarkAsRead, + MailOperation.MoveToJunk + }; + + public List AvailableHoverActionsTranslations { get; set; } = new List() + { + Translator.HoverActionOption_Archive, + Translator.HoverActionOption_Delete, + Translator.HoverActionOption_ToggleFlag, + Translator.HoverActionOption_ToggleRead, + Translator.HoverActionOption_MoveJunk + }; + + #region Properties + + private int leftHoverActionIndex; + + public int LeftHoverActionIndex + { + get => leftHoverActionIndex; + set + { + if (SetProperty(ref leftHoverActionIndex, value)) + { + PreferencesService.LeftHoverAction = availableHoverActions[value]; + } + } + } + + + private int centerHoverActionIndex; + + public int CenterHoverActionIndex + { + get => centerHoverActionIndex; + set + { + if (SetProperty(ref centerHoverActionIndex, value)) + { + PreferencesService.CenterHoverAction = availableHoverActions[value]; + } + } + } + + private int rightHoverActionIndex; + + public int RightHoverActionIndex + { + get => rightHoverActionIndex; + set + { + if (SetProperty(ref rightHoverActionIndex, value)) + { + PreferencesService.RightHoverAction = availableHoverActions[value]; + } + } + } + + #endregion + + public MessageListPageViewModel(IDialogService dialogService, + IPreferencesService preferencesService) : base(dialogService) + { + PreferencesService = preferencesService; + + leftHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.LeftHoverAction); + centerHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.CenterHoverAction); + rightHoverActionIndex = availableHoverActions.IndexOf(PreferencesService.RightHoverAction); + } + } +} diff --git a/Wino.Mail.ViewModels/Messages/ActiveMailFolderChangedEvent.cs b/Wino.Mail.ViewModels/Messages/ActiveMailFolderChangedEvent.cs new file mode 100644 index 00000000..4cd8cce7 --- /dev/null +++ b/Wino.Mail.ViewModels/Messages/ActiveMailFolderChangedEvent.cs @@ -0,0 +1,14 @@ +using System.Threading.Tasks; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Navigation; + +namespace Wino.Mail.ViewModels.Messages +{ + public class ActiveMailFolderChangedEvent : NavigateMailFolderEventArgs + { + public ActiveMailFolderChangedEvent(IBaseFolderMenuItem baseFolderMenuItem, + TaskCompletionSource folderInitLoadAwaitTask = null) : base(baseFolderMenuItem, folderInitLoadAwaitTask) + { + } + } +} diff --git a/Wino.Mail.ViewModels/Messages/ActiveMailItemChangedEvent.cs b/Wino.Mail.ViewModels/Messages/ActiveMailItemChangedEvent.cs new file mode 100644 index 00000000..fb0fc2b9 --- /dev/null +++ b/Wino.Mail.ViewModels/Messages/ActiveMailItemChangedEvent.cs @@ -0,0 +1,20 @@ +using System; +using Wino.Core.MenuItems; +using Wino.Mail.ViewModels.Data; + +namespace Wino.Mail.ViewModels.Messages +{ + /// + /// When active mail item in the reader is updated. + /// + public class ActiveMailItemChangedEvent + { + public ActiveMailItemChangedEvent(MailItemViewModel selectedMailItemViewModel) + { + // SelectedMailItemViewModel can be null. + SelectedMailItemViewModel = selectedMailItemViewModel; + } + + public MailItemViewModel SelectedMailItemViewModel { get; set; } + } +} diff --git a/Wino.Mail.ViewModels/Messages/MailItemSelectedEvent.cs b/Wino.Mail.ViewModels/Messages/MailItemSelectedEvent.cs new file mode 100644 index 00000000..abf21811 --- /dev/null +++ b/Wino.Mail.ViewModels/Messages/MailItemSelectedEvent.cs @@ -0,0 +1,19 @@ +using Wino.Mail.ViewModels.Data; + +namespace Wino.Mail.ViewModels.Messages +{ + /// + /// Wino has complex selected item detection mechanism with nested ListViews that + /// supports multi selection with threads. Each list view will raise this for mail list page + /// to react. + /// + public class MailItemSelectedEvent + { + public MailItemSelectedEvent(MailItemViewModel selectedMailItem) + { + SelectedMailItem = selectedMailItem; + } + + public MailItemViewModel SelectedMailItem { get; set; } + } +} diff --git a/Wino.Mail.ViewModels/Messages/MailItemSelectionRemovedEvent.cs b/Wino.Mail.ViewModels/Messages/MailItemSelectionRemovedEvent.cs new file mode 100644 index 00000000..909b7a24 --- /dev/null +++ b/Wino.Mail.ViewModels/Messages/MailItemSelectionRemovedEvent.cs @@ -0,0 +1,17 @@ +using Wino.Mail.ViewModels.Data; + +namespace Wino.Mail.ViewModels.Messages +{ + /// + /// Selected item removed event. + /// + public class MailItemSelectionRemovedEvent + { + public MailItemSelectionRemovedEvent(MailItemViewModel removedMailItem) + { + RemovedMailItem = removedMailItem; + } + + public MailItemViewModel RemovedMailItem { get; set; } + } +} diff --git a/Wino.Mail.ViewModels/Messages/ResetSingleMailItemSelectionEvent.cs b/Wino.Mail.ViewModels/Messages/ResetSingleMailItemSelectionEvent.cs new file mode 100644 index 00000000..f90907e2 --- /dev/null +++ b/Wino.Mail.ViewModels/Messages/ResetSingleMailItemSelectionEvent.cs @@ -0,0 +1,19 @@ +using Wino.Mail.ViewModels.Data; + +namespace Wino.Mail.ViewModels.Messages +{ + + /// + /// When a thread conversation listview has single selection, all other listviews + /// must unselect all their items. + /// + public class ResetSingleMailItemSelectionEvent + { + public ResetSingleMailItemSelectionEvent(MailItemViewModel selectedViewModel) + { + SelectedViewModel = selectedViewModel; + } + + public MailItemViewModel SelectedViewModel { get; set; } + } +} diff --git a/Wino.Mail.ViewModels/Messages/SelectMailItemContainerEvent.cs b/Wino.Mail.ViewModels/Messages/SelectMailItemContainerEvent.cs new file mode 100644 index 00000000..7db82a86 --- /dev/null +++ b/Wino.Mail.ViewModels/Messages/SelectMailItemContainerEvent.cs @@ -0,0 +1,9 @@ +using Wino.Mail.ViewModels.Data; + +namespace Wino.Mail.ViewModels.Messages +{ + /// + /// When listing view model manipulated the selected mail container in the UI. + /// + public record SelectMailItemContainerEvent(MailItemViewModel SelectedMailViewModel, bool ScrollToItem = false); +} diff --git a/Wino.Mail.ViewModels/NewAccountManagementPageViewModel.cs b/Wino.Mail.ViewModels/NewAccountManagementPageViewModel.cs new file mode 100644 index 00000000..2cf70adc --- /dev/null +++ b/Wino.Mail.ViewModels/NewAccountManagementPageViewModel.cs @@ -0,0 +1,11 @@ +using Wino.Core.Domain.Interfaces; + +namespace Wino.Mail.ViewModels +{ + public class NewAccountManagementPageViewModel : BaseViewModel + { + public NewAccountManagementPageViewModel(IDialogService dialogService) : base(dialogService) + { + } + } +} diff --git a/Wino.Mail.ViewModels/PersonalizationPageViewModel.cs b/Wino.Mail.ViewModels/PersonalizationPageViewModel.cs new file mode 100644 index 00000000..1e302f88 --- /dev/null +++ b/Wino.Mail.ViewModels/PersonalizationPageViewModel.cs @@ -0,0 +1,303 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using Wino.Core.Domain; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Personalization; +using Wino.Mail.ViewModels.Data; + +namespace Wino.Mail.ViewModels +{ + public partial class PersonalizationPageViewModel : BaseViewModel + { + public IStatePersistanceService StatePersistanceService { get; } + public IPreferencesService PreferencesService { get; } + + private readonly IThemeService _themeService; + + private bool isPropChangeDisabled = false; + + #region Personalization + + public bool IsSelectedWindowsAccentColor => SelectedAppColor == Colors.LastOrDefault(); + + public ObservableCollection Colors { get; set; } = new ObservableCollection(); + + public List ElementThemes { get; set; } = new List() + { + new ElementThemeContainer(ApplicationElementTheme.Light, Translator.ElementTheme_Light), + new ElementThemeContainer(ApplicationElementTheme.Dark, Translator.ElementTheme_Dark), + new ElementThemeContainer(ApplicationElementTheme.Default, Translator.ElementTheme_Default), + }; + + public List PaneLengths { get; set; } = new List() + { + new MailListPaneLengthPreferences(Translator.PaneLengthOption_Micro, 300), + new MailListPaneLengthPreferences(Translator.PaneLengthOption_Small, 350), + new MailListPaneLengthPreferences(Translator.PaneLengthOption_Default, 420), + new MailListPaneLengthPreferences(Translator.PaneLengthOption_Medium, 700), + new MailListPaneLengthPreferences(Translator.PaneLengthOption_Large, 900), + new MailListPaneLengthPreferences(Translator.PaneLengthOption_ExtraLarge, 1200), + }; + + public List InformationDisplayModes { get; set; } = new List() + { + MailListDisplayMode.Compact, + MailListDisplayMode.Medium, + MailListDisplayMode.Spacious + }; + + public List AppThemes { get; set; } + + [ObservableProperty] + private MailListPaneLengthPreferences selectedMailListPaneLength; + + [ObservableProperty] + private ElementThemeContainer selectedElementTheme; + + [ObservableProperty] + private MailListDisplayMode selectedInfoDisplayMode; + + private AppColorViewModel _selectedAppColor; + + public AppColorViewModel SelectedAppColor + { + get => _selectedAppColor; + set + { + if (SetProperty(ref _selectedAppColor, value)) + { + UseAccentColor = value == Colors?.LastOrDefault(); + } + } + } + + private bool _useAccentColor; + public bool UseAccentColor + { + get => _useAccentColor; + set + { + if (SetProperty(ref _useAccentColor, value)) + { + if (value) + { + SelectedAppColor = Colors?.LastOrDefault(); + } + else if (SelectedAppColor == Colors?.LastOrDefault()) + { + // Unchecking from accent color. + + SelectedAppColor = Colors?.FirstOrDefault(); + } + } + } + } + + // Allow app theme change for system themes. + public bool CanSelectElementTheme => SelectedAppTheme != null && + (SelectedAppTheme.AppThemeType == AppThemeType.System || SelectedAppTheme.AppThemeType == AppThemeType.Custom); + + private AppThemeBase _selectedAppTheme; + + public AppThemeBase SelectedAppTheme + { + get => _selectedAppTheme; + set + { + if (SetProperty(ref _selectedAppTheme, value)) + { + OnPropertyChanged(nameof(CanSelectElementTheme)); + + if (!CanSelectElementTheme) + { + SelectedElementTheme = null; + } + } + } + } + + #endregion + + public AsyncRelayCommand CreateCustomThemeCommand { get; set; } + public PersonalizationPageViewModel(IDialogService dialogService, + IStatePersistanceService statePersistanceService, + IThemeService themeService, + IPreferencesService preferencesService) : base(dialogService) + { + CreateCustomThemeCommand = new AsyncRelayCommand(CreateCustomThemeAsync); + + StatePersistanceService = statePersistanceService; + + _themeService = themeService; + PreferencesService = preferencesService; + } + + private async Task CreateCustomThemeAsync() + { + bool isThemeCreated = await DialogService.ShowCustomThemeBuilderDialogAsync(); + + if (isThemeCreated) + { + // Reload themes. + + await InitializeSettingsAsync(); + } + } + + private void InitializeColors() + { + Colors.Add(new AppColorViewModel("#0078d7")); + Colors.Add(new AppColorViewModel("#00838c")); + Colors.Add(new AppColorViewModel("#e3008c")); + Colors.Add(new AppColorViewModel("#ca4f07")); + Colors.Add(new AppColorViewModel("#e81123")); + Colors.Add(new AppColorViewModel("#00819e")); + Colors.Add(new AppColorViewModel("#10893e")); + Colors.Add(new AppColorViewModel("#881798")); + Colors.Add(new AppColorViewModel("#c239b3")); + Colors.Add(new AppColorViewModel("#767676")); + Colors.Add(new AppColorViewModel("#e1b12c")); + Colors.Add(new AppColorViewModel("#16a085")); + Colors.Add(new AppColorViewModel("#0984e3")); + Colors.Add(new AppColorViewModel("#4a69bd")); + Colors.Add(new AppColorViewModel("#05c46b")); + + // Add system accent color as last item. + + Colors.Add(new AppColorViewModel(_themeService.GetSystemAccentColorHex(), true)); + } + + /// + /// Set selections from settings service. + /// + private void SetInitialValues() + { + SelectedElementTheme = ElementThemes.Find(a => a.NativeTheme == _themeService.RootTheme); + SelectedInfoDisplayMode = PreferencesService.MailItemDisplayMode; + SelectedMailListPaneLength = PaneLengths.Find(a => a.Length == StatePersistanceService.MailListPaneLength); + + var currentAccentColor = _themeService.AccentColor; + + bool isWindowsColor = string.IsNullOrEmpty(currentAccentColor); + + if (isWindowsColor) + { + SelectedAppColor = Colors.LastOrDefault(); + UseAccentColor = true; + } + else + SelectedAppColor = Colors.FirstOrDefault(a => a.Hex == currentAccentColor); + + SelectedAppTheme = AppThemes.Find(a => a.Id == _themeService.CurrentApplicationThemeId); + } + + protected override async void OnActivated() + { + base.OnActivated(); + + await InitializeSettingsAsync(); + } + + private async Task InitializeSettingsAsync() + { + Deactivate(); + + AppThemes = await _themeService.GetAvailableThemesAsync(); + + OnPropertyChanged(nameof(AppThemes)); + + InitializeColors(); + SetInitialValues(); + + PropertyChanged -= PersonalizationSettingsUpdated; + PropertyChanged += PersonalizationSettingsUpdated; + + _themeService.AccentColorChanged -= AccentColorChanged; + _themeService.ElementThemeChanged -= ElementThemeChanged; + _themeService.AccentColorChangedBySystem -= AccentColorChangedBySystem; + + _themeService.AccentColorChanged += AccentColorChanged; + _themeService.ElementThemeChanged += ElementThemeChanged; + _themeService.AccentColorChangedBySystem += AccentColorChangedBySystem; + } + + private void AccentColorChangedBySystem(object sender, string newAccentColorHex) + { + var accentInList = Colors.FirstOrDefault(a => a.IsAccentColor); + + if (accentInList != null) + { + accentInList.Hex = newAccentColorHex; + } + } + + private void AccentColorChanged(object sender, string e) + { + isPropChangeDisabled = true; + + SelectedAppColor = Colors.FirstOrDefault(a => a.Hex == e); + + isPropChangeDisabled = false; + } + + private void ElementThemeChanged(object sender, ApplicationElementTheme e) + { + isPropChangeDisabled = true; + + SelectedElementTheme = ElementThemes.Find(a => a.NativeTheme == e); + + isPropChangeDisabled = false; + } + + protected override void OnDeactivated() + { + base.OnDeactivated(); + + Deactivate(); + } + + private void Deactivate() + { + PropertyChanged -= PersonalizationSettingsUpdated; + + _themeService.AccentColorChanged -= AccentColorChanged; + _themeService.ElementThemeChanged -= ElementThemeChanged; + _themeService.AccentColorChangedBySystem -= AccentColorChangedBySystem; + + if (AppThemes != null) + { + AppThemes.Clear(); + AppThemes = null; + } + } + + private void PersonalizationSettingsUpdated(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (isPropChangeDisabled) + return; + + if (e.PropertyName == nameof(SelectedElementTheme) && SelectedElementTheme != null) + { + _themeService.RootTheme = SelectedElementTheme.NativeTheme; + } + else if (e.PropertyName == nameof(SelectedAppTheme)) + { + _themeService.CurrentApplicationThemeId = SelectedAppTheme.Id; + } + else if (e.PropertyName == nameof(SelectedMailListPaneLength) && SelectedMailListPaneLength != null) + StatePersistanceService.MailListPaneLength = SelectedMailListPaneLength.Length; + else + { + if (e.PropertyName == nameof(SelectedInfoDisplayMode)) + PreferencesService.MailItemDisplayMode = SelectedInfoDisplayMode; + else if (e.PropertyName == nameof(SelectedAppColor)) + _themeService.AccentColor = SelectedAppColor.Hex; + } + } + } +} diff --git a/Wino.Mail.ViewModels/ReadingPanePageViewModel.cs b/Wino.Mail.ViewModels/ReadingPanePageViewModel.cs new file mode 100644 index 00000000..5acd18f3 --- /dev/null +++ b/Wino.Mail.ViewModels/ReadingPanePageViewModel.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Messaging; +using CommunityToolkit.Mvvm.Messaging.Messages; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Reader; + +namespace Wino.Mail.ViewModels +{ + public partial class ReadingPanePageViewModel : BaseViewModel, + IRecipient>, + IRecipient> + { + public IPreferencesService PreferencesService { get; set; } + + private int selectedMarkAsOptionIndex; + private readonly IFontService _fontService; + + public int SelectedMarkAsOptionIndex + { + get => selectedMarkAsOptionIndex; + set + { + if (SetProperty(ref selectedMarkAsOptionIndex, value)) + { + if (value >= 0) + { + PreferencesService.MarkAsPreference = (MailMarkAsOption)Enum.GetValues(typeof(MailMarkAsOption)).GetValue(value); + } + } + } + } + + public List ReaderFonts => _fontService.GetReaderFonts(); + + [ObservableProperty] + [NotifyPropertyChangedRecipients] + ReaderFontModel currentReaderFont; + + [ObservableProperty] + [NotifyPropertyChangedRecipients] + int currentReaderFontSize; + + public ReadingPanePageViewModel(IDialogService dialogService, + IFontService fontService, + IPreferencesService preferencesService) : base(dialogService) + { + _fontService = fontService; + + PreferencesService = preferencesService; + SelectedMarkAsOptionIndex = Array.IndexOf(Enum.GetValues(typeof(MailMarkAsOption)), PreferencesService.MarkAsPreference); + + CurrentReaderFont = fontService.GetCurrentReaderFont(); + CurrentReaderFontSize = fontService.GetCurrentReaderFontSize(); + } + + public void Receive(PropertyChangedMessage message) + { + if (message.OldValue != message.NewValue) + { + _fontService.ChangeReaderFont(message.NewValue.Font); + Debug.WriteLine("Changed reader font."); + } + } + + public void Receive(PropertyChangedMessage message) + { + if (message.PropertyName == nameof(CurrentReaderFontSize)) + { + _fontService.ChangeReaderFontSize(CurrentReaderFontSize); + } + } + } +} diff --git a/Wino.Mail.ViewModels/SettingOptionsPageViewModel.cs b/Wino.Mail.ViewModels/SettingOptionsPageViewModel.cs new file mode 100644 index 00000000..01ad29de --- /dev/null +++ b/Wino.Mail.ViewModels/SettingOptionsPageViewModel.cs @@ -0,0 +1,85 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Navigation; +using Wino.Core.Domain.Models.Translations; +using Wino.Core.Messages.Navigation; + +namespace Wino.Mail.ViewModels +{ + public partial class SettingOptionsPageViewModel : BaseViewModel + { + private readonly ITranslationService _translationService; + private readonly IPreferencesService _preferencesService; + + [ObservableProperty] + private List _availableLanguages; + + [ObservableProperty] + private AppLanguageModel _selectedLanguage; + + private bool isInitialized = false; + + public SettingOptionsPageViewModel(IDialogService dialogService, + ITranslationService translationService, + IPreferencesService preferencesService) : base(dialogService) + { + _translationService = translationService; + _preferencesService = preferencesService; + } + + [RelayCommand] + private void GoAccountSettings() => Messenger.Send(); + + public override void OnNavigatedTo(NavigationMode mode, object parameters) + { + base.OnNavigatedTo(mode, parameters); + + AvailableLanguages = _translationService.GetAvailableLanguages(); + + SelectedLanguage = AvailableLanguages.FirstOrDefault(a => a.Language == _preferencesService.CurrentLanguage); + + isInitialized = true; + } + + protected override async void OnPropertyChanged(PropertyChangedEventArgs e) + { + base.OnPropertyChanged(e); + + if (!isInitialized) return; + + if (e.PropertyName == nameof(SelectedLanguage)) + { + await _translationService.InitializeLanguageAsync(SelectedLanguage.Language); + } + } + + [RelayCommand] + public void NavigateSubDetail(object type) + { + if (type is string stringParameter) + { + WinoPage pageType = default; + + string pageTitle = stringParameter; + + // They are just params and don't have to be localized. Don't change. + if (stringParameter == "Personalization") + pageType = WinoPage.PersonalizationPage; + else if (stringParameter == "About") + pageType = WinoPage.AboutPage; + else if (stringParameter == "Message List") + pageType = WinoPage.MessageListPage; + else if (stringParameter == "Reading Pane") + pageType = WinoPage.ReadingPanePage; + + Messenger.Send(new BreadcrumbNavigationRequested(pageTitle, pageType)); + } + } + } +} diff --git a/Wino.Mail.ViewModels/SettingsPageViewModel.cs b/Wino.Mail.ViewModels/SettingsPageViewModel.cs new file mode 100644 index 00000000..212e698f --- /dev/null +++ b/Wino.Mail.ViewModels/SettingsPageViewModel.cs @@ -0,0 +1,11 @@ +using Wino.Core.Domain.Interfaces; + +namespace Wino.Mail.ViewModels +{ + public class SettingsPageViewModel : BaseViewModel + { + public SettingsPageViewModel(IDialogService dialogService) : base(dialogService) + { + } + } +} diff --git a/Wino.Mail.ViewModels/SettingsViewModel.cs b/Wino.Mail.ViewModels/SettingsViewModel.cs new file mode 100644 index 00000000..b905f9c5 --- /dev/null +++ b/Wino.Mail.ViewModels/SettingsViewModel.cs @@ -0,0 +1,11 @@ +using Wino.Core.Domain.Interfaces; + +namespace Wino.Mail.ViewModels +{ + public class SettingsDialogViewModel : BaseViewModel + { + public SettingsDialogViewModel(IDialogService dialogService) : base(dialogService) + { + } + } +} diff --git a/Wino.Mail.ViewModels/SignatureManagementPageViewModel.cs b/Wino.Mail.ViewModels/SignatureManagementPageViewModel.cs new file mode 100644 index 00000000..bd161bce --- /dev/null +++ b/Wino.Mail.ViewModels/SignatureManagementPageViewModel.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Navigation; +using Wino.Core.Domain.Models.Reader; +using Wino.Core.Messages.Mails; + +namespace Wino.Mail.ViewModels +{ + public partial class SignatureManagementPageViewModel : BaseViewModel + { + public Func> GetHTMLBodyFunction; + public Func> GetTextBodyFunction; + + public List ToolbarSections { get; set; } = new List() + { + new EditorToolbarSection(){ SectionType = EditorToolbarSectionType.Format }, + new EditorToolbarSection(){ SectionType = EditorToolbarSectionType.Insert }, + }; + + [ObservableProperty] + private EditorToolbarSection selectedToolbarSection; + + [ObservableProperty] + private bool isSignatureEnabled; + + public MailAccount Account { get; set; } + + public AsyncRelayCommand SaveSignatureCommand { get; set; } + + public INativeAppService NativeAppService { get; } + private readonly ISignatureService _signatureService; + private readonly IAccountService _accountService; + + public SignatureManagementPageViewModel(IDialogService dialogService, + INativeAppService nativeAppService, + ISignatureService signatureService, + IAccountService accountService) : base(dialogService) + { + SelectedToolbarSection = ToolbarSections[0]; + NativeAppService = nativeAppService; + _signatureService = signatureService; + _accountService = accountService; + SaveSignatureCommand = new AsyncRelayCommand(SaveSignatureAsync); + } + + public override async void OnNavigatedTo(NavigationMode mode, object parameters) + { + base.OnNavigatedTo(mode, parameters); + + if (parameters is Guid accountId) + Account = await _accountService.GetAccountAsync(accountId); + + if (Account != null) + { + var accountSignature = await _signatureService.GetAccountSignatureAsync(Account.Id); + + IsSignatureEnabled = accountSignature != null; + + if (IsSignatureEnabled) + Messenger.Send(new HtmlRenderingRequested(accountSignature.HtmlBody)); + else + Messenger.Send(new HtmlRenderingRequested(string.Empty)); // To get the theme changes. Render empty html. + } + } + + private async Task SaveSignatureAsync() + { + if (IsSignatureEnabled) + { + var newSignature = Regex.Unescape(await GetHTMLBodyFunction()); + + await _signatureService.UpdateAccountSignatureAsync(Account.Id, newSignature); + + DialogService.InfoBarMessage(Translator.Info_SignatureSavedTitle, Translator.Info_SignatureSavedMessage, Core.Domain.Enums.InfoBarMessageType.Success); + } + else + { + await _signatureService.DeleteAccountSignatureAssignment(Account.Id); + + DialogService.InfoBarMessage(Translator.Info_SignatureDisabledTitle, Translator.Info_SignatureDisabledMessage, Core.Domain.Enums.InfoBarMessageType.Success); + } + } + } +} diff --git a/Wino.Mail.ViewModels/WelcomePageViewModel.cs b/Wino.Mail.ViewModels/WelcomePageViewModel.cs new file mode 100644 index 00000000..ffcbf56e --- /dev/null +++ b/Wino.Mail.ViewModels/WelcomePageViewModel.cs @@ -0,0 +1,37 @@ +using System; +using CommunityToolkit.Mvvm.ComponentModel; +using Wino.Core.Domain; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Navigation; + +namespace Wino.Mail.ViewModels +{ + public partial class WelcomePageViewModel : BaseViewModel + { + public const string VersionFile = "170.md"; + + private readonly IFileService _fileService; + + [ObservableProperty] + private string currentVersionNotes; + + public WelcomePageViewModel(IDialogService dialogService, IFileService fileService) : base(dialogService) + { + _fileService = fileService; + } + + public override async void OnNavigatedTo(NavigationMode mode, object parameters) + { + base.OnNavigatedTo(mode, parameters); + + try + { + CurrentVersionNotes = await _fileService.GetFileContentByApplicationUriAsync($"ms-appx:///Assets/ReleaseNotes/{VersionFile}"); + } + catch (Exception) + { + DialogService.InfoBarMessage(Translator.GeneralTitle_Error, "Can't find the patch notes.", Core.Domain.Enums.InfoBarMessageType.Information); + } + } + } +} diff --git a/Wino.Mail.ViewModels/Wino.Mail.ViewModels.csproj b/Wino.Mail.ViewModels/Wino.Mail.ViewModels.csproj new file mode 100644 index 00000000..16a363ee --- /dev/null +++ b/Wino.Mail.ViewModels/Wino.Mail.ViewModels.csproj @@ -0,0 +1,22 @@ + + + + netstandard2.0 + 12 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + diff --git a/Wino.Mail/Activation/ActivationHandler.cs b/Wino.Mail/Activation/ActivationHandler.cs new file mode 100644 index 00000000..5beef227 --- /dev/null +++ b/Wino.Mail/Activation/ActivationHandler.cs @@ -0,0 +1,39 @@ +using System.Threading.Tasks; + +namespace Wino.Activation +{ + // For more information on understanding and extending activation flow see + // https://github.com/microsoft/TemplateStudio/blob/main/docs/UWP/activation.md + internal abstract class ActivationHandler + { + public abstract bool CanHandle(object args); + + public abstract Task HandleAsync(object args); + } + + // Extend this class to implement new ActivationHandlers + internal abstract class ActivationHandler : ActivationHandler + where T : class + { + // Override this method to add the activation logic in your activation handler + protected abstract Task HandleInternalAsync(T args); + + public override async Task HandleAsync(object args) + { + await HandleInternalAsync(args as T); + } + + public override bool CanHandle(object args) + { + // CanHandle checks the args is of type you have configured + return args is T && CanHandleInternal(args as T); + } + + // You can override this method to add extra validation on activation args + // to determine if your ActivationHandler should handle this activation args + protected virtual bool CanHandleInternal(T args) + { + return true; + } + } +} diff --git a/Wino.Mail/Activation/BackgroundActivationHandler.cs b/Wino.Mail/Activation/BackgroundActivationHandler.cs new file mode 100644 index 00000000..a88f1107 --- /dev/null +++ b/Wino.Mail/Activation/BackgroundActivationHandler.cs @@ -0,0 +1,174 @@ +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using Microsoft.Toolkit.Uwp.Notifications; +using Serilog; +using Windows.ApplicationModel.Activation; +using Windows.ApplicationModel.Background; +using Windows.ApplicationModel.Core; +using Windows.UI.Core; +using Windows.UI.Notifications; +using Wino.Core; +using Wino.Core.Domain; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Synchronization; +using Wino.Core.UWP.Services; +using Wino.Services; + +namespace Wino.Activation +{ + internal class BackgroundActivationHandler : ActivationHandler + { + private const string BackgroundExecutionLogTag = "[BackgroundExecution] "; + + private readonly IWinoRequestDelegator _winoRequestDelegator; + private readonly IBackgroundSynchronizer _backgroundSynchronizer; + private readonly INativeAppService _nativeAppService; + private readonly IWinoRequestProcessor _winoRequestProcessor; + private readonly IWinoSynchronizerFactory _winoSynchronizerFactory; + private readonly IMailService _mailService; + private ToastArguments _toastArguments; + + BackgroundTaskDeferral _deferral; + public BackgroundActivationHandler(IWinoRequestDelegator winoRequestDelegator, + IBackgroundSynchronizer backgroundSynchronizer, + INativeAppService nativeAppService, + IWinoRequestProcessor winoRequestProcessor, + IWinoSynchronizerFactory winoSynchronizerFactory, + IMailService mailService) + { + _winoRequestDelegator = winoRequestDelegator; + _backgroundSynchronizer = backgroundSynchronizer; + _nativeAppService = nativeAppService; + _winoRequestProcessor = winoRequestProcessor; + _winoSynchronizerFactory = winoSynchronizerFactory; + _mailService = mailService; + } + + protected override async Task HandleInternalAsync(BackgroundActivatedEventArgs args) + { + var instance = args.TaskInstance; + var taskName = instance.Task.Name; + + instance.Canceled -= OnBackgroundExecutionCanceled; + instance.Canceled += OnBackgroundExecutionCanceled; + + _deferral = instance.GetDeferral(); + + if (taskName == BackgroundTaskService.ToastActivationTaskEx) + { + // ToastNotificationActionTriggerDetail somehow use UI thread. + // Calling this without a dispatcher will result in error. + + await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.High, () => + { + if (instance.TriggerDetails is ToastNotificationActionTriggerDetail toastNotificationActionTriggerDetail) + _toastArguments = ToastArguments.Parse(toastNotificationActionTriggerDetail.Argument); + }); + + // All toast activation mail actions are handled here like mark as read or delete. + // This should not launch the application on the foreground. + + // Get the action and mail item id. + // Prepare package and send to delegator. + + if (_toastArguments.TryGetValue(Constants.ToastMailItemIdKey, out string mailItemId) && + _toastArguments.TryGetValue(Constants.ToastActionKey, out MailOperation action)) + { + // TODO: Remote folder id. + var mailItem = await _mailService.GetSingleMailItemAsync(mailItemId, string.Empty); + + if (mailItem == null) return; + + if (_nativeAppService.IsAppRunning()) + { + // Just send the package. We should reflect the UI changes as well. + var package = new MailOperationPreperationRequest(action, mailItem); + + await _winoRequestDelegator.ExecuteAsync(package); + } + else + { + // We need to synchronize changes without reflection the UI changes. + + var synchronizer = _winoSynchronizerFactory.GetAccountSynchronizer(mailItem.AssignedAccount.Id); + var prepRequest = new MailOperationPreperationRequest(action, mailItem); + + var requests = await _winoRequestProcessor.PrepareRequestsAsync(prepRequest); + + foreach (var request in requests) + { + synchronizer.QueueRequest(request); + } + + var options = new SynchronizationOptions() + { + Type = SynchronizationType.ExecuteRequests, + AccountId = mailItem.AssignedAccount.Id + }; + + await synchronizer.SynchronizeAsync(options); + } + } + } + else if (taskName == BackgroundTaskService.BackgroundSynchronizationTimerTaskNameEx) + { + var watch = new Stopwatch(); + watch.Start(); + + // Run timer based background synchronization. + + await _backgroundSynchronizer.RunBackgroundSynchronizationAsync(BackgroundSynchronizationReason.Timer); + + watch.Stop(); + Log.Information($"{BackgroundExecutionLogTag}Background synchronization is completed in {watch.Elapsed.TotalSeconds} seconds."); + } + + instance.Canceled -= OnBackgroundExecutionCanceled; + + _deferral.Complete(); + } + + private void OnBackgroundExecutionCanceled(Windows.ApplicationModel.Background.IBackgroundTaskInstance sender, Windows.ApplicationModel.Background.BackgroundTaskCancellationReason reason) + { + Log.Error($"{BackgroundExecutionLogTag} ({sender.Task.Name}) Background task is canceled. Reason -> {reason}"); + + _deferral?.Complete(); + } + + protected override bool CanHandleInternal(BackgroundActivatedEventArgs args) + { + var instance = args.TaskInstance; + var taskName = instance.Task.Name; + + if (taskName == BackgroundTaskService.ToastActivationTaskEx) + { + // User clicked Mark as Read or Delete in toast notification. + // MailId and Action must present in the arguments. + + return true; + + //if (instance.TriggerDetails is ToastNotificationActionTriggerDetail toastNotificationActionTriggerDetail) + //{ + // _toastArguments = ToastArguments.Parse(toastNotificationActionTriggerDetail.Argument); + + // return + // _toastArguments.Contains(Constants.ToastMailItemIdKey) && + // _toastArguments.Contains(Constants.ToastActionKey); + //} + + } + else if (taskName == BackgroundTaskService.BackgroundSynchronizationTimerTaskNameEx) + { + // This is timer based background synchronization. + + + return true; + } + + return false; + } + } +} diff --git a/Wino.Mail/Activation/DefaultActivationHandler.cs b/Wino.Mail/Activation/DefaultActivationHandler.cs new file mode 100644 index 00000000..6a6563fa --- /dev/null +++ b/Wino.Mail/Activation/DefaultActivationHandler.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using Windows.ApplicationModel.Activation; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media.Animation; +using Wino.Views; + +namespace Wino.Activation +{ + internal class DefaultActivationHandler : ActivationHandler + { + protected override Task HandleInternalAsync(IActivatedEventArgs args) + { + (Window.Current.Content as Frame).Navigate(typeof(AppShell), null, new DrillInNavigationTransitionInfo()); + + return Task.CompletedTask; + } + + // Only navigate if Frame content doesn't exist. + protected override bool CanHandleInternal(IActivatedEventArgs args) + => (Window.Current?.Content as Frame)?.Content == null; + } +} diff --git a/Wino.Mail/Activation/FileActivationHandler.cs b/Wino.Mail/Activation/FileActivationHandler.cs new file mode 100644 index 00000000..6aa7ca47 --- /dev/null +++ b/Wino.Mail/Activation/FileActivationHandler.cs @@ -0,0 +1,68 @@ +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Windows.ApplicationModel.Activation; +using Windows.Storage; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media.Animation; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Services; +using Wino.Helpers; +using Wino.Views; + +namespace Wino.Activation +{ + internal class FileActivationHandler : ActivationHandler + { + private readonly INativeAppService _nativeAppService; + private readonly IMimeFileService _mimeFileService; + private readonly IStatePersistanceService _statePersistanceService; + private readonly IWinoNavigationService _winoNavigationService; + + public FileActivationHandler(INativeAppService nativeAppService, + IMimeFileService mimeFileService, + IStatePersistanceService statePersistanceService, + IWinoNavigationService winoNavigationService) + { + _nativeAppService = nativeAppService; + _mimeFileService = mimeFileService; + _statePersistanceService = statePersistanceService; + _winoNavigationService = winoNavigationService; + } + + protected override async Task HandleInternalAsync(FileActivatedEventArgs args) + { + // Always handle the last item passed. + // Multiple files are not supported. + + var file = args.Files.Last() as StorageFile; + + // Only EML files are supported now. + var fileExtension = Path.GetExtension(file.Path); + + if (string.Equals(fileExtension, ".eml", StringComparison.OrdinalIgnoreCase)) + { + var fileBytes = await file.ReadBytesAsync(); + var directoryName = Path.GetDirectoryName(file.Path); + + var messageInformation = await _mimeFileService.GetMimeMessageInformationAsync(fileBytes, directoryName).ConfigureAwait(false); + + if (_nativeAppService.IsAppRunning()) + { + // TODO: Activate another Window and go to mail rendering page. + _winoNavigationService.NavigateRendering(messageInformation); + } + else + { + _statePersistanceService.ShouldShiftMailRenderingDesign = true; + (Window.Current.Content as Frame).Navigate(typeof(MailRenderingPage), messageInformation, new DrillInNavigationTransitionInfo()); + } + } + } + + protected override bool CanHandleInternal(FileActivatedEventArgs args) => args.Files.Any(); + + } +} diff --git a/Wino.Mail/Activation/ProtocolActivationHandler.cs b/Wino.Mail/Activation/ProtocolActivationHandler.cs new file mode 100644 index 00000000..f6ad3aa9 --- /dev/null +++ b/Wino.Mail/Activation/ProtocolActivationHandler.cs @@ -0,0 +1,56 @@ +using System.Threading.Tasks; +using System.Web; +using CommunityToolkit.Mvvm.Messaging; +using Windows.ApplicationModel.Activation; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Messages.Authorization; +using Wino.Core.Messages.Shell; + +namespace Wino.Activation +{ + internal class ProtocolActivationHandler : ActivationHandler + { + private const string GoogleAuthorizationProtocolTag = "google.pw.oauth2"; + private const string MailtoProtocolTag = "mailto:"; + + private readonly INativeAppService _nativeAppService; + private readonly ILaunchProtocolService _launchProtocolService; + + public ProtocolActivationHandler(INativeAppService nativeAppService, ILaunchProtocolService launchProtocolService) + { + _nativeAppService = nativeAppService; + _launchProtocolService = launchProtocolService; + } + + protected override Task HandleInternalAsync(ProtocolActivatedEventArgs args) + { + // Check URI prefix. + + var protocolString = args.Uri.AbsoluteUri; + + // Google OAuth Response + if (protocolString.StartsWith(GoogleAuthorizationProtocolTag)) + { + // App must be working already. No need to check for running state. + WeakReferenceMessenger.Default.Send(new ProtocolAuthorizationCallbackReceived(args.Uri)); + } + else if (protocolString.StartsWith(MailtoProtocolTag)) + { + // mailto activation. Try to parse params. + + var replaced = protocolString.Replace(MailtoProtocolTag, "mailto="); + replaced = Wino.Core.Extensions.StringExtensions.ReplaceFirst(replaced, "?", "&"); + + _launchProtocolService.MailtoParameters = HttpUtility.ParseQueryString(replaced); + + if (_nativeAppService.IsAppRunning()) + { + // Just send publish a message. Shell will continue. + WeakReferenceMessenger.Default.Send(new MailtoProtocolMessageRequested()); + } + } + + return Task.CompletedTask; + } + } +} diff --git a/Wino.Mail/Activation/ToastNotificationActivationHandler.cs b/Wino.Mail/Activation/ToastNotificationActivationHandler.cs new file mode 100644 index 00000000..212d37da --- /dev/null +++ b/Wino.Mail/Activation/ToastNotificationActivationHandler.cs @@ -0,0 +1,79 @@ +using System; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.Messaging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Toolkit.Uwp.Notifications; +using Serilog; +using Windows.ApplicationModel.Activation; +using Wino.Core.Domain; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Messages.Accounts; + +namespace Wino.Activation +{ + /// + /// This handler will only handle the toasts that runs on foreground. + /// Background executions are not handled here like mark as read or delete. + /// + internal class ToastNotificationActivationHandler : ActivationHandler + { + private readonly INativeAppService _nativeAppService; + private readonly IMailService _mailService; + private readonly IFolderService _folderService; + + private ToastArguments _toastArguments; + + public ToastNotificationActivationHandler(INativeAppService nativeAppService, + IMailService mailService, + IFolderService folderService) + { + _nativeAppService = nativeAppService; + _mailService = mailService; + _folderService = folderService; + } + + protected override async Task HandleInternalAsync(ToastNotificationActivatedEventArgs args) + { + // Create the mail item navigation event. + // If the app is running, it'll be picked up by the Messenger. + // Otherwise we'll save it and handle it when the shell loads all accounts. + + // Parse the mail unique id and perform above actions. + if (Guid.TryParse(_toastArguments[Constants.ToastMailItemIdKey], out Guid mailItemUniqueId)) + { + var account = await _mailService.GetMailAccountByUniqueIdAsync(mailItemUniqueId).ConfigureAwait(false); + if (account == null) return; + + var mailItem = await _mailService.GetSingleMailItemAsync(mailItemUniqueId).ConfigureAwait(false); + if (mailItem == null) return; + + var message = new AccountMenuItemExtended(mailItem.AssignedFolder.Id, mailItem); + + // Delegate this event to LaunchProtocolService so app shell can pick it up on launch if app doesn't work. + var launchProtocolService = App.Current.Services.GetService(); + launchProtocolService.LaunchParameter = message; + + // Send the messsage anyways. Launch protocol service will be ignored if the message is picked up by subscriber shell. + WeakReferenceMessenger.Default.Send(message); + } + } + + protected override bool CanHandleInternal(ToastNotificationActivatedEventArgs args) + { + try + { + _toastArguments = ToastArguments.Parse(args.Argument); + + return + _toastArguments.Contains(Constants.ToastMailItemIdKey) && + _toastArguments.Contains(Constants.ToastActionKey); + } + catch (Exception ex) + { + Log.Error(ex, "Couldn't handle parsing toast notification arguments for foreground navigate."); + } + + return false; + } + } +} diff --git a/Wino.Mail/App.xaml b/Wino.Mail/App.xaml new file mode 100644 index 00000000..cae3f997 --- /dev/null +++ b/Wino.Mail/App.xaml @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + 19 + 19 + 24,24,24,24 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/App.xaml.cs b/Wino.Mail/App.xaml.cs new file mode 100644 index 00000000..e854c1e4 --- /dev/null +++ b/Wino.Mail/App.xaml.cs @@ -0,0 +1,334 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AppCenter; +using Microsoft.AppCenter.Analytics; +using Microsoft.AppCenter.Crashes; +using Microsoft.Extensions.DependencyInjection; +using Serilog; +using Windows.ApplicationModel; +using Windows.ApplicationModel.Activation; +using Windows.ApplicationModel.Core; +using Windows.ApplicationModel.Resources; +using Windows.Foundation.Metadata; +using Windows.Globalization; +using Windows.Storage; +using Windows.System.Profile; +using Windows.UI; +using Windows.UI.ViewManagement; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Activation; +using Wino.Core; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Services; +using Wino.Core.UWP; +using Wino.Core.UWP.Services; +using Wino.Mail.ViewModels; +using Wino.Services; + +namespace Wino +{ + public sealed partial class App : Application + { + private const string WinoLaunchLogPrefix = "[Wino Launch] "; + private const string AppCenterKey = "90deb1d0-a77f-47d0-8a6b-7eaf111c6b72"; + + public new static App Current => (App)Application.Current; + public IServiceProvider Services { get; } + + private readonly ILogInitializer _logInitializer; + private readonly IThemeService _themeService; + private readonly IDatabaseService _databaseService; + private readonly IAppInitializerService _appInitializerService; + private readonly IWinoSynchronizerFactory _synchronizerFactory; + private readonly ITranslationService _translationService; + + // Order matters. + private List initializeServices => new List() + { + _translationService, + _databaseService, + _themeService, + _synchronizerFactory + }; + + public App() + { + InitializeComponent(); + + UnhandledException += OnAppUnhandledException; + EnteredBackground += OnEnteredBackground; + LeavingBackground += OnLeavingBackground; + + Services = ConfigureServices(); + + _logInitializer = Services.GetService(); + + ConfigureLogger(); + ConfigureAppCenter(); + ConfigurePrelaunch(); + ConfigureXbox(); + + _themeService = Services.GetService(); + _databaseService = Services.GetService(); + _appInitializerService = Services.GetService(); + _synchronizerFactory = Services.GetService(); + _translationService = Services.GetService(); + + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + } + + private void LogActivation(string log) => Log.Information($"{WinoLaunchLogPrefix}{log}"); + private void OnLeavingBackground(object sender, LeavingBackgroundEventArgs e) => LogActivation($"Wino went foreground."); + private void OnEnteredBackground(object sender, EnteredBackgroundEventArgs e) => LogActivation($"Wino went background."); + private IServiceProvider ConfigureServices() + { + var services = new ServiceCollection(); + + services.RegisterCoreServices(); + services.RegisterCoreUWPServices(); + + RegisterUWPServices(services); + RegisterViewModels(services); + RegisterActivationHandlers(services); + + return services.BuildServiceProvider(); + } + + #region Dependency Injection + + private void RegisterActivationHandlers(IServiceCollection services) + { + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + } + + private void RegisterUWPServices(IServiceCollection services) + { + services.AddSingleton, ApplicationResourceManager>(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + } + + private void RegisterViewModels(IServiceCollection services) + { + services.AddSingleton(typeof(AppShellViewModel)); + services.AddTransient(typeof(SettingsDialogViewModel)); + services.AddTransient(typeof(PersonalizationPageViewModel)); + services.AddTransient(typeof(SettingOptionsPageViewModel)); + services.AddTransient(typeof(MailListPageViewModel)); + services.AddTransient(typeof(MailRenderingPageViewModel)); + services.AddTransient(typeof(AccountManagementViewModel)); + services.AddTransient(typeof(WelcomePageViewModel)); + services.AddTransient(typeof(AboutPageViewModel)); + services.AddTransient(typeof(ComposePageViewModel)); + services.AddTransient(typeof(IdlePageViewModel)); + services.AddTransient(typeof(SettingsPageViewModel)); + services.AddTransient(typeof(NewAccountManagementPageViewModel)); + services.AddTransient(typeof(AccountDetailsPageViewModel)); + services.AddTransient(typeof(SignatureManagementPageViewModel)); + services.AddTransient(typeof(MessageListPageViewModel)); + services.AddTransient(typeof(ReadingPanePageViewModel)); + services.AddTransient(typeof(MergedAccountDetailsPageViewModel)); + } + + #endregion + + #region Misc Configuration + + private void ConfigureLogger() => _logInitializer.SetupLogger(ApplicationData.Current.LocalFolder.Path); + + private void ConfigureAppCenter() => AppCenter.Start(AppCenterKey, typeof(Analytics), typeof(Crashes)); + + private void ConfigurePrelaunch() + { + if (ApiInformation.IsMethodPresent("Windows.ApplicationModel.Core.CoreApplication", "EnablePrelaunch")) + CoreApplication.EnablePrelaunch(true); + } + + private void ConfigureXbox() + { + // Xbox users should use Reveal focus. + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 6)) + { + FocusVisualKind = AnalyticsInfo.VersionInfo.DeviceFamily == "Xbox" ? FocusVisualKind.Reveal : FocusVisualKind.HighVisibility; + } + } + + private void ConfigureTitleBar() + { + var coreTitleBar = CoreApplication.GetCurrentView().TitleBar; + var applicationViewTitleBar = ApplicationView.GetForCurrentView().TitleBar; + + // Extend shell content into core window to meet design requirements. + coreTitleBar.ExtendViewIntoTitleBar = true; + + // Change system buttons and background colors to meet design requirements. + applicationViewTitleBar.ButtonBackgroundColor = Colors.Transparent; + applicationViewTitleBar.BackgroundColor = Colors.Transparent; + applicationViewTitleBar.ButtonInactiveBackgroundColor = Colors.Transparent; + applicationViewTitleBar.ButtonForegroundColor = Colors.White; + } + + #endregion + + protected override void OnWindowCreated(WindowCreatedEventArgs args) + { + base.OnWindowCreated(args); + + LogActivation("Window is created."); + + ConfigureTitleBar(); + } + + protected override async void OnLaunched(LaunchActivatedEventArgs args) + { + LogActivation($"OnLaunched -> {args.GetType().Name}, Kind -> {args.Kind}, PreviousExecutionState -> {args.PreviousExecutionState}, IsPrelaunch -> {args.PrelaunchActivated}"); + + if (!args.PrelaunchActivated) + { + await ActivateWinoAsync(args); + } + } + + protected override async void OnFileActivated(FileActivatedEventArgs args) + { + base.OnFileActivated(args); + + Log.Information($"File activation for {args.Files.Count} item(s)."); + + await ActivateWinoAsync(args); + } + + protected override async void OnActivated(IActivatedEventArgs args) + { + base.OnActivated(args); + + Log.Information($"OnActivated -> {args.GetType().Name}, Kind -> {args.Kind}, Prev Execution State -> {args.PreviousExecutionState}"); + + await ActivateWinoAsync(args); + } + + protected override async void OnBackgroundActivated(BackgroundActivatedEventArgs args) + { + base.OnBackgroundActivated(args); + + LogActivation($"OnBackgroundActivated -> {args.GetType().Name}, TaskInstanceIdName -> {args.TaskInstance?.Task?.Name ?? "NA"}"); + + await ActivateWinoAsync(args); + } + + private void OnAppUnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e) + { + var parameters = new Dictionary() + { + { "BaseMessage", e.Exception.GetBaseException().Message }, + { "BaseStackTrace", e.Exception.GetBaseException().StackTrace }, + { "StackTrace", e.Exception.StackTrace }, + { "Message", e.Exception.Message }, + }; + + Log.Error(e.Exception, "[Wino Crash]"); + + Crashes.TrackError(e.Exception, parameters); + Analytics.TrackEvent("Wino Crashed", parameters); + } + + private bool IsInteractiveLaunchArgs(object args) => args is IActivatedEventArgs; + + private async Task ActivateWinoAsync(object args) + { + await PreInitializationAsync(); + + if (IsInteractiveLaunchArgs(args)) + { + if (Window.Current.Content == null) + { + var mainFrame = new Frame(); + + Window.Current.Content = mainFrame; + + await _themeService.InitializeAsync(); + } + } + + await HandleActivationAsync(args); + + if (IsInteractiveLaunchArgs(args)) + { + Window.Current.Activate(); + + LogActivation("Window activated"); + } + } + + /// + /// Tasks that must run before the activation and launch. + /// Regardless of whether it's an interactive launch or not. + /// + private async Task PreInitializationAsync() + { + // Handle migrations. + // TODO: Automate migration process with more proper way. + + if (!ApplicationData.Current.LocalSettings.Values.ContainsKey("Migration_169")) + { + try + { + await _appInitializerService.MigrateAsync(); + } + catch (Exception ex) + { + Log.Error(ex, $"{WinoLaunchLogPrefix}Migration_169 failed."); + } + finally + { + ApplicationData.Current.LocalSettings.Values["Migration_169"] = true; + } + } + + foreach (var service in initializeServices) + { + await service.InitializeAsync(); + } + } + + private async Task HandleActivationAsync(object activationArgs) + { + var activationHandler = GetActivationHandlers().FirstOrDefault(h => h.CanHandle(activationArgs)); + + if (activationHandler != null) + { + await activationHandler.HandleAsync(activationArgs); + } + + if (IsInteractiveLaunchArgs(activationArgs)) + { + var defaultHandler = new DefaultActivationHandler(); + if (defaultHandler.CanHandle(activationArgs)) + { + await defaultHandler.HandleAsync(activationArgs); + } + } + } + + private IEnumerable GetActivationHandlers() + { + yield return Services.GetService(); + yield return Services.GetService(); + yield return Services.GetService(); + yield return Services.GetService(); + } + } +} diff --git a/Wino.Mail/AppShell.xaml b/Wino.Mail/AppShell.xaml new file mode 100644 index 00000000..2cc625e3 --- /dev/null +++ b/Wino.Mail/AppShell.xaml @@ -0,0 +1,613 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/AppShell.xaml.cs b/Wino.Mail/AppShell.xaml.cs new file mode 100644 index 00000000..4fb83614 --- /dev/null +++ b/Wino.Mail/AppShell.xaml.cs @@ -0,0 +1,303 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using Windows.ApplicationModel.Core; +using Windows.Foundation; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Input; +using Wino.Controls; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Folders; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Navigation; +using Wino.Core.Messages.Accounts; +using Wino.Core.Messages.Mails; +using Wino.Core.Messages.Shell; +using Wino.Extensions; +using Wino.Mail.ViewModels.Data; +using Wino.MenuFlyouts; +using Wino.MenuFlyouts.Context; +using Wino.Views.Abstract; + +namespace Wino.Views +{ + public sealed partial class AppShell : AppShellAbstract, + IRecipient, + IRecipient, + IRecipient, + IRecipient + { + public AppShell() : base() + { + InitializeComponent(); + + var coreTitleBar = CoreApplication.GetCurrentView().TitleBar; + coreTitleBar.LayoutMetricsChanged += TitleBarLayoutUpdated; + } + + private void TitleBarLayoutUpdated(CoreApplicationViewTitleBar sender, object args) => UpdateTitleBarLayout(sender); + + private void UpdateTitleBarLayout(CoreApplicationViewTitleBar coreTitleBar) => RealAppBar.SystemReserved = coreTitleBar.SystemOverlayRightInset; + + private async void ItemDroppedOnFolder(object sender, DragEventArgs e) + { + // Validate package content. + if (sender is WinoNavigationViewItem droppedContainer) + { + droppedContainer.IsDraggingItemOver = false; + + if (CanContinueDragDrop(droppedContainer, e)) + { + if (droppedContainer.DataContext is IBaseFolderMenuItem draggingFolder) + { + var mailCopies = new List(); + + var dragPackage = e.DataView.Properties[nameof(MailDragPackage)] as MailDragPackage; + e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move; + + // Extract mail copies from IMailItem. + // ThreadViewModels will be divided into pieces. + + foreach (var item in dragPackage.DraggingMails) + { + if (item is MailItemViewModel singleMailItemViewModel) + { + mailCopies.Add(singleMailItemViewModel.MailCopy); + } + else if (item is ThreadMailItemViewModel threadViewModel) + { + mailCopies.AddRange(threadViewModel.GetMailCopies()); + } + } + + await ViewModel.PerformMoveOperationAsync(mailCopies, draggingFolder); + } + } + } + } + + private void ItemDragLeaveFromFolder(object sender, DragEventArgs e) + { + if (sender is WinoNavigationViewItem leavingContainer) + { + leavingContainer.IsDraggingItemOver = false; + } + } + + private bool CanContinueDragDrop(WinoNavigationViewItem interactingContainer, DragEventArgs args) + { + // TODO: Maybe override caption with some information why the validation failed? + // Note: Caption has a max length. It may be trimmed in some languages. + + if (interactingContainer == null || !args.DataView.Properties.ContainsKey(nameof(MailDragPackage))) return false; + + var dragPackage = args.DataView.Properties[nameof(MailDragPackage)] as MailDragPackage; + + // Invalid package. + if (!dragPackage.DraggingMails.Any()) return false; + + // Check whether source and target folder are the same. + if (interactingContainer.IsSelected) return false; + + // Check if the interacting container is a folder. + if (!(interactingContainer.DataContext is IBaseFolderMenuItem folderMenuItem)) return false; + + // Check if the folder is a move target. + if (!folderMenuItem.IsMoveTarget) return false; + + // Check whether the moving item's account has at least one same as the target folder's account. + var draggedAccountIds = folderMenuItem.HandlingFolders.Select(a => a.MailAccountId); + + if (!dragPackage.DraggingMails.Any(a => draggedAccountIds.Contains(a.AssignedAccount.Id))) return false; + + return true; + } + + private void ItemDragEnterOnFolder(object sender, DragEventArgs e) + { + // Validate package content. + if (sender is WinoNavigationViewItem droppedContainer && CanContinueDragDrop(droppedContainer, e)) + { + droppedContainer.IsDraggingItemOver = true; + + var draggingFolder = droppedContainer.DataContext as IBaseFolderMenuItem; + + e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move; + e.DragUIOverride.Caption = string.Format(Translator.DragMoveToFolderCaption, draggingFolder.FolderName); + } + } + + public async void Receive(AccountMenuItemExtended message) + { + await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, async () => + { + if (message.FolderId == default) return; + + var menuItem = ViewModel.MenuItems.GetFolderItem(message.FolderId); + + if (menuItem == null) return; + + menuItem.Expand(); + + await ViewModel.NavigateFolderAsync(menuItem); + + navigationView.SelectedItem = menuItem; + + if (message.NavigateMailItem == null) return; + + // At this point folder is navigated and items are loaded. + WeakReferenceMessenger.Default.Send(new MailItemNavigationRequested(message.NavigateMailItem.UniqueId)); + }); + } + + private async void MenuSelectionChanged(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args) + { + if (args.SelectedItem is IMenuItem invokedMenuItem) + { + await ViewModel.MenuItemInvokedOrSelectedAsync(invokedMenuItem); + } + } + + private async void NavigationViewItemInvoked(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewItemInvokedEventArgs args) + { + // SelectsOnInvoked is handled in MenuSelectionChanged. + // This part is only for the items that are not selectable. + if (args.InvokedItemContainer is WinoNavigationViewItem winoNavigationViewItem) + { + if (winoNavigationViewItem.SelectsOnInvoked) return; + + await ViewModel.MenuItemInvokedOrSelectedAsync(winoNavigationViewItem.DataContext as IMenuItem); + } + } + + public void Receive(NavigateMailFolderEvent message) + { + if (message.BaseFolderMenuItem == null) return; + + if (navigationView.SelectedItem != message.BaseFolderMenuItem) + { + var navigateFolderArgs = new NavigateMailFolderEventArgs(message.BaseFolderMenuItem, message.FolderInitLoadAwaitTask); + + ViewModel.NavigationService.NavigateFolder(navigateFolderArgs); + + // Prevent double navigation. + navigationView.SelectionChanged -= MenuSelectionChanged; + navigationView.SelectedItem = message.BaseFolderMenuItem; + navigationView.SelectionChanged += MenuSelectionChanged; + } + else + { + // Complete the init task since we are already on the right page. + message.FolderInitLoadAwaitTask?.TrySetResult(true); + } + } + + private void ShellFrameContentNavigated(object sender, Windows.UI.Xaml.Navigation.NavigationEventArgs e) + => RealAppBar.ShellFrameContent = (e.Content as BasePage).ShellContent; + + private void BackButtonClicked(Controls.Advanced.WinoAppTitleBar sender, RoutedEventArgs args) + { + WeakReferenceMessenger.Default.Send(new ClearMailSelectionsRequested()); + } + + private void OnShellLoaded(object sender, RoutedEventArgs e) + { + + } + + private async void MenuItemContextRequested(UIElement sender, ContextRequestedEventArgs args) + { + // Delegate this request to ViewModel. + // VM will prepare available actions for this folder and show Menu Flyout. + + if (sender is WinoNavigationViewItem menuItem && + menuItem.DataContext is IBaseFolderMenuItem baseFolderMenuItem && + baseFolderMenuItem.IsMoveTarget && + args.TryGetPosition(sender, out Point p)) + { + args.Handled = true; + + var source = new TaskCompletionSource(); + + var actions = ViewModel.GetFolderContextMenuActions(baseFolderMenuItem); + var flyout = new FolderOperationFlyout(actions, source); + + flyout.ShowAt(menuItem, new FlyoutShowOptions() + { + ShowMode = FlyoutShowMode.Standard, + Position = new Point(p.X + 30, p.Y - 20) + }); + + var operation = await source.Task; + + flyout.Dispose(); + + // No action selected. + if (operation == null) return; + + await ViewModel.PerformFolderOperationAsync(operation.Operation, baseFolderMenuItem); + } + } + + public void Receive(CreateNewMailWithMultipleAccountsRequested message) + { + // Find the NewMail menu item container. + + var container = navigationView.ContainerFromMenuItem(ViewModel.CreateMailMenuItem); + + var flyout = new AccountSelectorFlyout(message.AllAccounts, ViewModel.CreateNewMailForAsync); + + flyout.ShowAt(container, new FlyoutShowOptions() + { + ShowMode = FlyoutShowMode.Auto, + Placement = FlyoutPlacementMode.Right + }); + } + + private void NavigationPaneOpening(Microsoft.UI.Xaml.Controls.NavigationView sender, object args) + { + // It's annoying that NavigationView doesn't respect expansion state of the items in Minimal display mode. + // Expanded items are collaped, and users need to expand them again. + // Regardless of the reason, we will expand the selected item if it's a folder with parent account for visibility. + + if (sender.DisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Minimal && sender.SelectedItem is IFolderMenuItem selectedFolderMenuItem) + { + selectedFolderMenuItem.Expand(); + } + } + + /// + /// InfoBar message is requested. + /// + public async void Receive(InfoBarMessageRequested message) + { + await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => + { + if (string.IsNullOrEmpty(message.ActionButtonTitle) || message.Action == null) + { + ShellInfoBar.ActionButton = null; + } + else + { + ShellInfoBar.ActionButton = new Button() + { + Content = message.ActionButtonTitle, + Command = new RelayCommand(message.Action) + }; + } + + ShellInfoBar.Message = message.Message; + ShellInfoBar.Title = message.Title; + ShellInfoBar.Severity = message.Severity.AsMUXCInfoBarSeverity(); + ShellInfoBar.IsOpen = true; + }); + } + } +} diff --git a/Wino.Mail/AppThemes/Acrylic.xaml b/Wino.Mail/AppThemes/Acrylic.xaml new file mode 100644 index 00000000..5c833a97 --- /dev/null +++ b/Wino.Mail/AppThemes/Acrylic.xaml @@ -0,0 +1,43 @@ + + + Acrylic + False + + Transparent + 0,0,4,0 + 6,6,6,6 + + + + + + white + + + #ecf0f1 + + + + + + Transparent + #2C2C2C + + + + + diff --git a/Wino.Mail/AppThemes/Clouds.xaml b/Wino.Mail/AppThemes/Clouds.xaml new file mode 100644 index 00000000..93d3d314 --- /dev/null +++ b/Wino.Mail/AppThemes/Clouds.xaml @@ -0,0 +1,27 @@ + + + Clouds + ms-appx:///BackgroundImages/Clouds.jpg + False + + + Transparent + 4,0,4,0 + 6,6,6,6 + + + + + #A3FFFFFF + #FFFFFF + + + + #A3262626 + #5413191F + + + diff --git a/Wino.Mail/AppThemes/Custom.xaml b/Wino.Mail/AppThemes/Custom.xaml new file mode 100644 index 00000000..0714844b --- /dev/null +++ b/Wino.Mail/AppThemes/Custom.xaml @@ -0,0 +1,53 @@ + + + Custom + ms-appdata:///local/CustomWallpaper.jpg + False + + + + 0,0,0,0 + 0,6,6,0 + + + 0,0,0,0 + 0,1,0,0 + 0,0,0,0 + + + + + #ecf0f1 + + #D9FFFFFF + + + + + + + + + + + + #1f1f1f + + #E61F1F1F + + + + + + + + + + + diff --git a/Wino.Mail/AppThemes/Forest.xaml b/Wino.Mail/AppThemes/Forest.xaml new file mode 100644 index 00000000..88afd2fe --- /dev/null +++ b/Wino.Mail/AppThemes/Forest.xaml @@ -0,0 +1,27 @@ + + + Forest + ms-appx:///BackgroundImages/Forest.jpg + False + + + Transparent + 4,0,4,0 + 6,6,6,6 + + + + + #2900F00A + #A800D608 + + + + #A3262626 + #59001C01 + + + diff --git a/Wino.Mail/AppThemes/Garden.xaml b/Wino.Mail/AppThemes/Garden.xaml new file mode 100644 index 00000000..4f904bd1 --- /dev/null +++ b/Wino.Mail/AppThemes/Garden.xaml @@ -0,0 +1,27 @@ + + + Garden + ms-appx:///BackgroundImages/Garden.jpg + False + + + Transparent + 4,0,4,0 + 6,6,6,6 + + + + + #A3FFFFFF + #FFFFFF + + + + #A3262626 + #5413191F + + + diff --git a/Wino.Mail/AppThemes/Mica.xaml b/Wino.Mail/AppThemes/Mica.xaml new file mode 100644 index 00000000..baa97183 --- /dev/null +++ b/Wino.Mail/AppThemes/Mica.xaml @@ -0,0 +1,28 @@ + + + Mica + True + + Transparent + Transparent + 0,0,4,0 + 6,6,6,6 + + + + + + white + + + #ecf0f1 + + + Transparent + #1f1f1f + + + diff --git a/Wino.Mail/AppThemes/Nighty.xaml b/Wino.Mail/AppThemes/Nighty.xaml new file mode 100644 index 00000000..3644c954 --- /dev/null +++ b/Wino.Mail/AppThemes/Nighty.xaml @@ -0,0 +1,27 @@ + + + Nighty + ms-appx:///BackgroundImages/Nighty.jpg + False + + + Transparent + 4,0,4,0 + 6,6,6,6 + + + + + #A3FFFFFF + #fdcb6e + + + + #A3262626 + #5413191F + + + diff --git a/Wino.Mail/AppThemes/Snowflake.xaml b/Wino.Mail/AppThemes/Snowflake.xaml new file mode 100644 index 00000000..54a0b247 --- /dev/null +++ b/Wino.Mail/AppThemes/Snowflake.xaml @@ -0,0 +1,27 @@ + + + Snowflake + ms-appx:///BackgroundImages/Snowflake.jpg + False + + + Transparent + 4,0,4,0 + 6,6,6,6 + + + + + #A3FFFFFF + #FFFFFF + + + + #A3262626 + #5413191F + + + diff --git a/Wino.Mail/AppThemes/TestTheme.xaml b/Wino.Mail/AppThemes/TestTheme.xaml new file mode 100644 index 00000000..903b065b --- /dev/null +++ b/Wino.Mail/AppThemes/TestTheme.xaml @@ -0,0 +1,22 @@ + + + TestTheme.xaml + + + + + ms-appx:///BackgroundImages/bg6.jpg + #A3FFFFFF + #A3FFFFFF + #fdcb6e + + + + ms-appx:///BackgroundImages/bg6.jpg + + #A3000000 + #A3000000 + #A3262626 + + + diff --git a/Wino.Mail/Assets/BadgeLogo.scale-100.png b/Wino.Mail/Assets/BadgeLogo.scale-100.png new file mode 100644 index 00000000..ae8a28a3 Binary files /dev/null and b/Wino.Mail/Assets/BadgeLogo.scale-100.png differ diff --git a/Wino.Mail/Assets/BadgeLogo.scale-125.png b/Wino.Mail/Assets/BadgeLogo.scale-125.png new file mode 100644 index 00000000..53fafc0d Binary files /dev/null and b/Wino.Mail/Assets/BadgeLogo.scale-125.png differ diff --git a/Wino.Mail/Assets/BadgeLogo.scale-150.png b/Wino.Mail/Assets/BadgeLogo.scale-150.png new file mode 100644 index 00000000..bfcd6b8d Binary files /dev/null and b/Wino.Mail/Assets/BadgeLogo.scale-150.png differ diff --git a/Wino.Mail/Assets/BadgeLogo.scale-200.png b/Wino.Mail/Assets/BadgeLogo.scale-200.png new file mode 100644 index 00000000..8e7b9e08 Binary files /dev/null and b/Wino.Mail/Assets/BadgeLogo.scale-200.png differ diff --git a/Wino.Mail/Assets/BadgeLogo.scale-400.png b/Wino.Mail/Assets/BadgeLogo.scale-400.png new file mode 100644 index 00000000..55f300a9 Binary files /dev/null and b/Wino.Mail/Assets/BadgeLogo.scale-400.png differ diff --git a/Wino.Mail/Assets/EML/eml.png b/Wino.Mail/Assets/EML/eml.png new file mode 100644 index 00000000..7aeeb98f Binary files /dev/null and b/Wino.Mail/Assets/EML/eml.png differ diff --git a/Wino.Mail/Assets/FileTypes/type_archive.png b/Wino.Mail/Assets/FileTypes/type_archive.png new file mode 100644 index 00000000..b4227523 Binary files /dev/null and b/Wino.Mail/Assets/FileTypes/type_archive.png differ diff --git a/Wino.Mail/Assets/FileTypes/type_audio.png b/Wino.Mail/Assets/FileTypes/type_audio.png new file mode 100644 index 00000000..5489d5e9 Binary files /dev/null and b/Wino.Mail/Assets/FileTypes/type_audio.png differ diff --git a/Wino.Mail/Assets/FileTypes/type_executable.png b/Wino.Mail/Assets/FileTypes/type_executable.png new file mode 100644 index 00000000..e8fc6e6e Binary files /dev/null and b/Wino.Mail/Assets/FileTypes/type_executable.png differ diff --git a/Wino.Mail/Assets/FileTypes/type_html.png b/Wino.Mail/Assets/FileTypes/type_html.png new file mode 100644 index 00000000..2bf4e140 Binary files /dev/null and b/Wino.Mail/Assets/FileTypes/type_html.png differ diff --git a/Wino.Mail/Assets/FileTypes/type_image.png b/Wino.Mail/Assets/FileTypes/type_image.png new file mode 100644 index 00000000..378b9559 Binary files /dev/null and b/Wino.Mail/Assets/FileTypes/type_image.png differ diff --git a/Wino.Mail/Assets/FileTypes/type_none.png b/Wino.Mail/Assets/FileTypes/type_none.png new file mode 100644 index 00000000..900b00cc Binary files /dev/null and b/Wino.Mail/Assets/FileTypes/type_none.png differ diff --git a/Wino.Mail/Assets/FileTypes/type_other.png b/Wino.Mail/Assets/FileTypes/type_other.png new file mode 100644 index 00000000..c16c9edd Binary files /dev/null and b/Wino.Mail/Assets/FileTypes/type_other.png differ diff --git a/Wino.Mail/Assets/FileTypes/type_pdf.png b/Wino.Mail/Assets/FileTypes/type_pdf.png new file mode 100644 index 00000000..b08ee4a2 Binary files /dev/null and b/Wino.Mail/Assets/FileTypes/type_pdf.png differ diff --git a/Wino.Mail/Assets/FileTypes/type_rar.png b/Wino.Mail/Assets/FileTypes/type_rar.png new file mode 100644 index 00000000..4260115a Binary files /dev/null and b/Wino.Mail/Assets/FileTypes/type_rar.png differ diff --git a/Wino.Mail/Assets/FileTypes/type_video.png b/Wino.Mail/Assets/FileTypes/type_video.png new file mode 100644 index 00000000..829bd686 Binary files /dev/null and b/Wino.Mail/Assets/FileTypes/type_video.png differ diff --git a/Wino.Mail/Assets/LargeTile.scale-100.png b/Wino.Mail/Assets/LargeTile.scale-100.png new file mode 100644 index 00000000..bf83e51b Binary files /dev/null and b/Wino.Mail/Assets/LargeTile.scale-100.png differ diff --git a/Wino.Mail/Assets/LargeTile.scale-125.png b/Wino.Mail/Assets/LargeTile.scale-125.png new file mode 100644 index 00000000..06dbbaeb Binary files /dev/null and b/Wino.Mail/Assets/LargeTile.scale-125.png differ diff --git a/Wino.Mail/Assets/LargeTile.scale-150.png b/Wino.Mail/Assets/LargeTile.scale-150.png new file mode 100644 index 00000000..a5a24dd8 Binary files /dev/null and b/Wino.Mail/Assets/LargeTile.scale-150.png differ diff --git a/Wino.Mail/Assets/LargeTile.scale-200.png b/Wino.Mail/Assets/LargeTile.scale-200.png new file mode 100644 index 00000000..213bc118 Binary files /dev/null and b/Wino.Mail/Assets/LargeTile.scale-200.png differ diff --git a/Wino.Mail/Assets/LargeTile.scale-400.png b/Wino.Mail/Assets/LargeTile.scale-400.png new file mode 100644 index 00000000..5bbe0abb Binary files /dev/null and b/Wino.Mail/Assets/LargeTile.scale-400.png differ diff --git a/Wino.Mail/Assets/NotificationIcons/delete.png b/Wino.Mail/Assets/NotificationIcons/delete.png new file mode 100644 index 00000000..bc46276e Binary files /dev/null and b/Wino.Mail/Assets/NotificationIcons/delete.png differ diff --git a/Wino.Mail/Assets/NotificationIcons/dismiss.png b/Wino.Mail/Assets/NotificationIcons/dismiss.png new file mode 100644 index 00000000..63086036 Binary files /dev/null and b/Wino.Mail/Assets/NotificationIcons/dismiss.png differ diff --git a/Wino.Mail/Assets/NotificationIcons/markread.png b/Wino.Mail/Assets/NotificationIcons/markread.png new file mode 100644 index 00000000..114ab059 Binary files /dev/null and b/Wino.Mail/Assets/NotificationIcons/markread.png differ diff --git a/Wino.Mail/Assets/NotificationIcons/profile-dark.png b/Wino.Mail/Assets/NotificationIcons/profile-dark.png new file mode 100644 index 00000000..114b3af6 Binary files /dev/null and b/Wino.Mail/Assets/NotificationIcons/profile-dark.png differ diff --git a/Wino.Mail/Assets/NotificationIcons/profile-light.png b/Wino.Mail/Assets/NotificationIcons/profile-light.png new file mode 100644 index 00000000..a3bac71c Binary files /dev/null and b/Wino.Mail/Assets/NotificationIcons/profile-light.png differ diff --git a/Wino.Mail/Assets/Providers/Gmail.png b/Wino.Mail/Assets/Providers/Gmail.png new file mode 100644 index 00000000..79dcff5b Binary files /dev/null and b/Wino.Mail/Assets/Providers/Gmail.png differ diff --git a/Wino.Mail/Assets/Providers/IMAP4.png b/Wino.Mail/Assets/Providers/IMAP4.png new file mode 100644 index 00000000..145023d0 Binary files /dev/null and b/Wino.Mail/Assets/Providers/IMAP4.png differ diff --git a/Wino.Mail/Assets/Providers/Office 365.png b/Wino.Mail/Assets/Providers/Office 365.png new file mode 100644 index 00000000..8cecb7cb Binary files /dev/null and b/Wino.Mail/Assets/Providers/Office 365.png differ diff --git a/Wino.Mail/Assets/Providers/Outlook.png b/Wino.Mail/Assets/Providers/Outlook.png new file mode 100644 index 00000000..d1956be1 Binary files /dev/null and b/Wino.Mail/Assets/Providers/Outlook.png differ diff --git a/Wino.Mail/Assets/Providers/Yahoo.png b/Wino.Mail/Assets/Providers/Yahoo.png new file mode 100644 index 00000000..7823af45 Binary files /dev/null and b/Wino.Mail/Assets/Providers/Yahoo.png differ diff --git a/Wino.Mail/Assets/ReleaseNotes/170.md b/Wino.Mail/Assets/ReleaseNotes/170.md new file mode 100644 index 00000000..67fff691 --- /dev/null +++ b/Wino.Mail/Assets/ReleaseNotes/170.md @@ -0,0 +1,11 @@ +# 🚀 Good job on building Wino Mail from the source 🙂 + +## Once we finish pre-release testing, I will add the 1.7.0 patch notes here. + +### Right now, it's pretty lonely here. 🤷‍♂️ + +- Make sure you read the contributuion guidelines before you start diving into the code. ⚠️ +- You may find some redundant methods, interfaces, classes, etc. that are not used in the project. Please ignore them for now 🥺. I will clean them up in the future. +- If you want to disable the unlimited account check, go to AccountManagementViewModel.cs and update the Free Account Count property at the top 😁 +- If you have any questions, feel free to ask me on [Twitter 🐦](https://twitter.com/TrayhopeR) or project's Discord channels. +- Good luck! 🍀 diff --git a/Wino.Mail/Assets/SmallTile.scale-100.png b/Wino.Mail/Assets/SmallTile.scale-100.png new file mode 100644 index 00000000..cc4fce7c Binary files /dev/null and b/Wino.Mail/Assets/SmallTile.scale-100.png differ diff --git a/Wino.Mail/Assets/SmallTile.scale-125.png b/Wino.Mail/Assets/SmallTile.scale-125.png new file mode 100644 index 00000000..29829897 Binary files /dev/null and b/Wino.Mail/Assets/SmallTile.scale-125.png differ diff --git a/Wino.Mail/Assets/SmallTile.scale-150.png b/Wino.Mail/Assets/SmallTile.scale-150.png new file mode 100644 index 00000000..f3769431 Binary files /dev/null and b/Wino.Mail/Assets/SmallTile.scale-150.png differ diff --git a/Wino.Mail/Assets/SmallTile.scale-200.png b/Wino.Mail/Assets/SmallTile.scale-200.png new file mode 100644 index 00000000..852517b5 Binary files /dev/null and b/Wino.Mail/Assets/SmallTile.scale-200.png differ diff --git a/Wino.Mail/Assets/SmallTile.scale-400.png b/Wino.Mail/Assets/SmallTile.scale-400.png new file mode 100644 index 00000000..f3047502 Binary files /dev/null and b/Wino.Mail/Assets/SmallTile.scale-400.png differ diff --git a/Wino.Mail/Assets/SplashScreen.scale-100.png b/Wino.Mail/Assets/SplashScreen.scale-100.png new file mode 100644 index 00000000..0283361d Binary files /dev/null and b/Wino.Mail/Assets/SplashScreen.scale-100.png differ diff --git a/Wino.Mail/Assets/SplashScreen.scale-125.png b/Wino.Mail/Assets/SplashScreen.scale-125.png new file mode 100644 index 00000000..d84f59a5 Binary files /dev/null and b/Wino.Mail/Assets/SplashScreen.scale-125.png differ diff --git a/Wino.Mail/Assets/SplashScreen.scale-150.png b/Wino.Mail/Assets/SplashScreen.scale-150.png new file mode 100644 index 00000000..f9449b85 Binary files /dev/null and b/Wino.Mail/Assets/SplashScreen.scale-150.png differ diff --git a/Wino.Mail/Assets/SplashScreen.scale-200.png b/Wino.Mail/Assets/SplashScreen.scale-200.png new file mode 100644 index 00000000..9782abaa Binary files /dev/null and b/Wino.Mail/Assets/SplashScreen.scale-200.png differ diff --git a/Wino.Mail/Assets/SplashScreen.scale-400.png b/Wino.Mail/Assets/SplashScreen.scale-400.png new file mode 100644 index 00000000..58e5483e Binary files /dev/null and b/Wino.Mail/Assets/SplashScreen.scale-400.png differ diff --git a/Wino.Mail/Assets/Square150x150Logo.scale-100.png b/Wino.Mail/Assets/Square150x150Logo.scale-100.png new file mode 100644 index 00000000..d45248a3 Binary files /dev/null and b/Wino.Mail/Assets/Square150x150Logo.scale-100.png differ diff --git a/Wino.Mail/Assets/Square150x150Logo.scale-125.png b/Wino.Mail/Assets/Square150x150Logo.scale-125.png new file mode 100644 index 00000000..74dc2515 Binary files /dev/null and b/Wino.Mail/Assets/Square150x150Logo.scale-125.png differ diff --git a/Wino.Mail/Assets/Square150x150Logo.scale-150.png b/Wino.Mail/Assets/Square150x150Logo.scale-150.png new file mode 100644 index 00000000..5a07de0c Binary files /dev/null and b/Wino.Mail/Assets/Square150x150Logo.scale-150.png differ diff --git a/Wino.Mail/Assets/Square150x150Logo.scale-200.png b/Wino.Mail/Assets/Square150x150Logo.scale-200.png new file mode 100644 index 00000000..a7c7a181 Binary files /dev/null and b/Wino.Mail/Assets/Square150x150Logo.scale-200.png differ diff --git a/Wino.Mail/Assets/Square150x150Logo.scale-400.png b/Wino.Mail/Assets/Square150x150Logo.scale-400.png new file mode 100644 index 00000000..14530295 Binary files /dev/null and b/Wino.Mail/Assets/Square150x150Logo.scale-400.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png b/Wino.Mail/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png new file mode 100644 index 00000000..cad729ce Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png b/Wino.Mail/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png new file mode 100644 index 00000000..0d699618 Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png b/Wino.Mail/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png new file mode 100644 index 00000000..02b23248 Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png b/Wino.Mail/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png new file mode 100644 index 00000000..90a2077c Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png b/Wino.Mail/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png new file mode 100644 index 00000000..0ed92a7e Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.altform-unplated_targetsize-16.png b/Wino.Mail/Assets/Square44x44Logo.altform-unplated_targetsize-16.png new file mode 100644 index 00000000..cad729ce Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.altform-unplated_targetsize-16.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.altform-unplated_targetsize-256.png b/Wino.Mail/Assets/Square44x44Logo.altform-unplated_targetsize-256.png new file mode 100644 index 00000000..02b23248 Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.altform-unplated_targetsize-256.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.altform-unplated_targetsize-32.png b/Wino.Mail/Assets/Square44x44Logo.altform-unplated_targetsize-32.png new file mode 100644 index 00000000..90a2077c Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.altform-unplated_targetsize-32.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.altform-unplated_targetsize-48.png b/Wino.Mail/Assets/Square44x44Logo.altform-unplated_targetsize-48.png new file mode 100644 index 00000000..0ed92a7e Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.altform-unplated_targetsize-48.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.scale-100.png b/Wino.Mail/Assets/Square44x44Logo.scale-100.png new file mode 100644 index 00000000..d58fd9fc Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.scale-100.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.scale-125.png b/Wino.Mail/Assets/Square44x44Logo.scale-125.png new file mode 100644 index 00000000..a6bff2df Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.scale-125.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.scale-150.png b/Wino.Mail/Assets/Square44x44Logo.scale-150.png new file mode 100644 index 00000000..0151282b Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.scale-150.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.scale-200.png b/Wino.Mail/Assets/Square44x44Logo.scale-200.png new file mode 100644 index 00000000..2368352c Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.scale-200.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.scale-400.png b/Wino.Mail/Assets/Square44x44Logo.scale-400.png new file mode 100644 index 00000000..59055316 Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.scale-400.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.targetsize-16.png b/Wino.Mail/Assets/Square44x44Logo.targetsize-16.png new file mode 100644 index 00000000..de43ce90 Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.targetsize-16.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.targetsize-24.png b/Wino.Mail/Assets/Square44x44Logo.targetsize-24.png new file mode 100644 index 00000000..976f5e52 Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.targetsize-24.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/Wino.Mail/Assets/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 00000000..0d699618 Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.targetsize-256.png b/Wino.Mail/Assets/Square44x44Logo.targetsize-256.png new file mode 100644 index 00000000..c6f762e0 Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.targetsize-256.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.targetsize-32.png b/Wino.Mail/Assets/Square44x44Logo.targetsize-32.png new file mode 100644 index 00000000..30341066 Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.targetsize-32.png differ diff --git a/Wino.Mail/Assets/Square44x44Logo.targetsize-48.png b/Wino.Mail/Assets/Square44x44Logo.targetsize-48.png new file mode 100644 index 00000000..cb1aaf03 Binary files /dev/null and b/Wino.Mail/Assets/Square44x44Logo.targetsize-48.png differ diff --git a/Wino.Mail/Assets/StoreLogo.backup.png b/Wino.Mail/Assets/StoreLogo.backup.png new file mode 100644 index 00000000..7385b56c Binary files /dev/null and b/Wino.Mail/Assets/StoreLogo.backup.png differ diff --git a/Wino.Mail/Assets/StoreLogo.scale-100.png b/Wino.Mail/Assets/StoreLogo.scale-100.png new file mode 100644 index 00000000..303a7ff2 Binary files /dev/null and b/Wino.Mail/Assets/StoreLogo.scale-100.png differ diff --git a/Wino.Mail/Assets/StoreLogo.scale-125.png b/Wino.Mail/Assets/StoreLogo.scale-125.png new file mode 100644 index 00000000..849b69a2 Binary files /dev/null and b/Wino.Mail/Assets/StoreLogo.scale-125.png differ diff --git a/Wino.Mail/Assets/StoreLogo.scale-150.png b/Wino.Mail/Assets/StoreLogo.scale-150.png new file mode 100644 index 00000000..90d52577 Binary files /dev/null and b/Wino.Mail/Assets/StoreLogo.scale-150.png differ diff --git a/Wino.Mail/Assets/StoreLogo.scale-200.png b/Wino.Mail/Assets/StoreLogo.scale-200.png new file mode 100644 index 00000000..393052ea Binary files /dev/null and b/Wino.Mail/Assets/StoreLogo.scale-200.png differ diff --git a/Wino.Mail/Assets/StoreLogo.scale-400.png b/Wino.Mail/Assets/StoreLogo.scale-400.png new file mode 100644 index 00000000..d3f01397 Binary files /dev/null and b/Wino.Mail/Assets/StoreLogo.scale-400.png differ diff --git a/Wino.Mail/Assets/Thumbnails/airbnb.com.png b/Wino.Mail/Assets/Thumbnails/airbnb.com.png new file mode 100644 index 00000000..9713a7af Binary files /dev/null and b/Wino.Mail/Assets/Thumbnails/airbnb.com.png differ diff --git a/Wino.Mail/Assets/Thumbnails/apple.com.png b/Wino.Mail/Assets/Thumbnails/apple.com.png new file mode 100644 index 00000000..18621ea7 Binary files /dev/null and b/Wino.Mail/Assets/Thumbnails/apple.com.png differ diff --git a/Wino.Mail/Assets/Thumbnails/google.com.png b/Wino.Mail/Assets/Thumbnails/google.com.png new file mode 100644 index 00000000..5af3ede9 Binary files /dev/null and b/Wino.Mail/Assets/Thumbnails/google.com.png differ diff --git a/Wino.Mail/Assets/Thumbnails/microsoft.com.png b/Wino.Mail/Assets/Thumbnails/microsoft.com.png new file mode 100644 index 00000000..e0a0d837 Binary files /dev/null and b/Wino.Mail/Assets/Thumbnails/microsoft.com.png differ diff --git a/Wino.Mail/Assets/Thumbnails/steampowered.com.png b/Wino.Mail/Assets/Thumbnails/steampowered.com.png new file mode 100644 index 00000000..a5d6087f Binary files /dev/null and b/Wino.Mail/Assets/Thumbnails/steampowered.com.png differ diff --git a/Wino.Mail/Assets/Thumbnails/uber.com.png b/Wino.Mail/Assets/Thumbnails/uber.com.png new file mode 100644 index 00000000..5dbb4ea8 Binary files /dev/null and b/Wino.Mail/Assets/Thumbnails/uber.com.png differ diff --git a/Wino.Mail/Assets/Thumbnails/youtube.com.png b/Wino.Mail/Assets/Thumbnails/youtube.com.png new file mode 100644 index 00000000..0df5bd9b Binary files /dev/null and b/Wino.Mail/Assets/Thumbnails/youtube.com.png differ diff --git a/Wino.Mail/Assets/Wide310x150Logo.scale-100.png b/Wino.Mail/Assets/Wide310x150Logo.scale-100.png new file mode 100644 index 00000000..7cf08614 Binary files /dev/null and b/Wino.Mail/Assets/Wide310x150Logo.scale-100.png differ diff --git a/Wino.Mail/Assets/Wide310x150Logo.scale-125.png b/Wino.Mail/Assets/Wide310x150Logo.scale-125.png new file mode 100644 index 00000000..23246be6 Binary files /dev/null and b/Wino.Mail/Assets/Wide310x150Logo.scale-125.png differ diff --git a/Wino.Mail/Assets/Wide310x150Logo.scale-150.png b/Wino.Mail/Assets/Wide310x150Logo.scale-150.png new file mode 100644 index 00000000..33238ff7 Binary files /dev/null and b/Wino.Mail/Assets/Wide310x150Logo.scale-150.png differ diff --git a/Wino.Mail/Assets/Wide310x150Logo.scale-200.png b/Wino.Mail/Assets/Wide310x150Logo.scale-200.png new file mode 100644 index 00000000..0283361d Binary files /dev/null and b/Wino.Mail/Assets/Wide310x150Logo.scale-200.png differ diff --git a/Wino.Mail/Assets/Wide310x150Logo.scale-400.png b/Wino.Mail/Assets/Wide310x150Logo.scale-400.png new file mode 100644 index 00000000..9782abaa Binary files /dev/null and b/Wino.Mail/Assets/Wide310x150Logo.scale-400.png differ diff --git a/Wino.Mail/Assets/WinoIcons.ttf b/Wino.Mail/Assets/WinoIcons.ttf new file mode 100644 index 00000000..505f058a Binary files /dev/null and b/Wino.Mail/Assets/WinoIcons.ttf differ diff --git a/Wino.Mail/BackgroundImages/Acrylic.jpg b/Wino.Mail/BackgroundImages/Acrylic.jpg new file mode 100644 index 00000000..48c3b7d9 Binary files /dev/null and b/Wino.Mail/BackgroundImages/Acrylic.jpg differ diff --git a/Wino.Mail/BackgroundImages/Clouds.jpg b/Wino.Mail/BackgroundImages/Clouds.jpg new file mode 100644 index 00000000..f643572d Binary files /dev/null and b/Wino.Mail/BackgroundImages/Clouds.jpg differ diff --git a/Wino.Mail/BackgroundImages/Forest.jpg b/Wino.Mail/BackgroundImages/Forest.jpg new file mode 100644 index 00000000..aa58e6af Binary files /dev/null and b/Wino.Mail/BackgroundImages/Forest.jpg differ diff --git a/Wino.Mail/BackgroundImages/Garden.jpg b/Wino.Mail/BackgroundImages/Garden.jpg new file mode 100644 index 00000000..eaf1d05a Binary files /dev/null and b/Wino.Mail/BackgroundImages/Garden.jpg differ diff --git a/Wino.Mail/BackgroundImages/Mica.jpg b/Wino.Mail/BackgroundImages/Mica.jpg new file mode 100644 index 00000000..10519a41 Binary files /dev/null and b/Wino.Mail/BackgroundImages/Mica.jpg differ diff --git a/Wino.Mail/BackgroundImages/Nighty.jpg b/Wino.Mail/BackgroundImages/Nighty.jpg new file mode 100644 index 00000000..b79c8f1b Binary files /dev/null and b/Wino.Mail/BackgroundImages/Nighty.jpg differ diff --git a/Wino.Mail/BackgroundImages/Snowflake.jpg b/Wino.Mail/BackgroundImages/Snowflake.jpg new file mode 100644 index 00000000..f5129640 Binary files /dev/null and b/Wino.Mail/BackgroundImages/Snowflake.jpg differ diff --git a/Wino.Mail/BasePage.cs b/Wino.Mail/BasePage.cs new file mode 100644 index 00000000..71d29561 --- /dev/null +++ b/Wino.Mail/BasePage.cs @@ -0,0 +1,78 @@ +using System; +using System.Diagnostics; +using CommunityToolkit.Mvvm.Messaging; +using Microsoft.Extensions.DependencyInjection; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Navigation; +using Wino.Core.Messages.Shell; +using Wino.Core.UWP; +using Wino.Mail.ViewModels; + +namespace Wino +{ + public class BasePage : Page, IRecipient + { + public UIElement ShellContent + { + get { return (UIElement)GetValue(ShellContentProperty); } + set { SetValue(ShellContentProperty, value); } + } + + public static readonly DependencyProperty ShellContentProperty = DependencyProperty.Register(nameof(ShellContent), typeof(UIElement), typeof(BasePage), new PropertyMetadata(null)); + + public void Receive(LanguageChanged message) + { + OnLanguageChanged(); + } + + public virtual void OnLanguageChanged() { } + } + + public abstract class BasePage : BasePage where T : BaseViewModel + { + public T ViewModel { get; } = App.Current.Services.GetService(); + + protected BasePage() + { + ViewModel.Dispatcher = new UWPDispatcher(Dispatcher); + } + + ~BasePage() + { + Debug.WriteLine($"Disposed {this.GetType().Name}"); + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + base.OnNavigatedTo(e); + + var mode = GetNavigationMode(e.NavigationMode); + var parameter = e.Parameter; + + WeakReferenceMessenger.Default.UnregisterAll(this); + WeakReferenceMessenger.Default.RegisterAll(this); + + ViewModel.OnNavigatedTo(mode, parameter); + } + + protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) + { + base.OnNavigatingFrom(e); + + var mode = GetNavigationMode(e.NavigationMode); + var parameter = e.Parameter; + + WeakReferenceMessenger.Default.UnregisterAll(this); + + ViewModel.OnNavigatedFrom(mode, parameter); + + GC.Collect(); + } + + private Core.Domain.Models.Navigation.NavigationMode GetNavigationMode(NavigationMode mode) + { + return (Core.Domain.Models.Navigation.NavigationMode)mode; + } + } +} diff --git a/Wino.Mail/Behaviors/BindableCommandBarBehavior.cs b/Wino.Mail/Behaviors/BindableCommandBarBehavior.cs new file mode 100644 index 00000000..d554fdea --- /dev/null +++ b/Wino.Mail/Behaviors/BindableCommandBarBehavior.cs @@ -0,0 +1,202 @@ +using Microsoft.Xaml.Interactivity; +using System.Collections; +using System.Collections.Specialized; +using System.Windows.Input; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Wino.Controls; +using Wino.Core.Domain.Models.Menus; +using Wino.Helpers; + +namespace Wino.Behaviors +{ + public class BindableCommandBarBehavior : Behavior + { + public static readonly DependencyProperty PrimaryCommandsProperty = DependencyProperty.Register( + "PrimaryCommands", typeof(object), typeof(BindableCommandBarBehavior), + new PropertyMetadata(null, UpdateCommands)); + + public static readonly DependencyProperty ItemTemplateSelectorProperty = DependencyProperty.Register( + "ItemTemplateSelector", typeof(DataTemplateSelector), typeof(BindableCommandBarBehavior), + new PropertyMetadata(null, null)); + + public ICommand ItemClickedCommand + { + get { return (ICommand)GetValue(ItemClickedCommandProperty); } + set { SetValue(ItemClickedCommandProperty, value); } + } + + public static readonly DependencyProperty ItemClickedCommandProperty = DependencyProperty.Register(nameof(ItemClickedCommand), typeof(ICommand), typeof(BindableCommandBarBehavior), new PropertyMetadata(null)); + + public DataTemplateSelector ItemTemplateSelector + { + get { return (DataTemplateSelector)GetValue(ItemTemplateSelectorProperty); } + set { SetValue(ItemTemplateSelectorProperty, value); } + } + + public object PrimaryCommands + { + get { return GetValue(PrimaryCommandsProperty); } + set { SetValue(PrimaryCommandsProperty, value); } + } + + protected override void OnDetaching() + { + base.OnDetaching(); + + AttachChanges(false); + + if (PrimaryCommands is IEnumerable enumerable) + { + foreach (var item in enumerable) + { + if (item is ButtonBase button) + { + button.Click -= Button_Click; + } + } + } + } + + private void UpdatePrimaryCommands() + { + if (AssociatedObject == null) + return; + + if (PrimaryCommands == null) + return; + + if (AssociatedObject.PrimaryCommands is IEnumerable enumerableObjects) + { + foreach (var item in enumerableObjects) + { + if (item is ButtonBase button) + { + button.Click -= Button_Click; + } + } + } + + if (AssociatedObject.SecondaryCommands is IEnumerable secondaryObject) + { + foreach (var item in secondaryObject) + { + if (item is ButtonBase button) + { + button.Click -= Button_Click; + } + } + } + + AssociatedObject.PrimaryCommands.Clear(); + AssociatedObject.SecondaryCommands.Clear(); + + if (!(PrimaryCommands is IEnumerable enumerable)) return; + + foreach (var command in enumerable) + { + if (command is MailOperationMenuItem mailOperationMenuItem) + { + if (mailOperationMenuItem.Operation == Core.Domain.Enums.MailOperation.Seperator) + { + var seperator = new AppBarSeparator(); + + if (mailOperationMenuItem.IsSecondaryMenuPreferred) + { + AssociatedObject.SecondaryCommands.Add(seperator); + } + else + { + AssociatedObject.PrimaryCommands.Add(seperator); + } + } + else + { + var menuItem = new AppBarButton() + { + Icon = new WinoFontIcon() { Glyph = ControlConstants.WinoIconFontDictionary[XamlHelpers.GetWinoIconGlyph(mailOperationMenuItem.Operation)] }, + Label = XamlHelpers.GetOperationString(mailOperationMenuItem.Operation), + DataContext = mailOperationMenuItem, + }; + + menuItem.Click -= Button_Click; + menuItem.Click += Button_Click; + + if (mailOperationMenuItem.IsSecondaryMenuPreferred) + { + AssociatedObject.SecondaryCommands.Add(menuItem); + } + else + { + AssociatedObject.PrimaryCommands.Add(menuItem); + } + } + } + + //if (dependencyObject is ICommandBarElement icommandBarElement) + //{ + // if (dependencyObject is ButtonBase button) + // { + // button.Click -= Button_Click; + // button.Click += Button_Click; + // } + + // if (command is MailOperationMenuItem mailOperationMenuItem) + // { + + // } + //} + } + } + + private void Button_Click(object sender, RoutedEventArgs e) + { + ItemClickedCommand?.Execute(((ButtonBase)sender).DataContext); + } + + protected override void OnAttached() + { + base.OnAttached(); + + AttachChanges(true); + + UpdatePrimaryCommands(); + } + + private void PrimaryCommandsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + UpdatePrimaryCommands(); + } + + private static void UpdateCommands(DependencyObject dependencyObject, + DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) + { + if (!(dependencyObject is BindableCommandBarBehavior behavior)) return; + + if (dependencyPropertyChangedEventArgs.OldValue is INotifyCollectionChanged oldList) + { + oldList.CollectionChanged -= behavior.PrimaryCommandsCollectionChanged; + } + + behavior.AttachChanges(true); + behavior.UpdatePrimaryCommands(); + } + + private void AttachChanges(bool register) + { + if (PrimaryCommands is null) return; + + if (PrimaryCommands is INotifyCollectionChanged collectionChanged) + { + if (register) + { + collectionChanged.CollectionChanged -= PrimaryCommandsCollectionChanged; + collectionChanged.CollectionChanged += PrimaryCommandsCollectionChanged; + } + else + collectionChanged.CollectionChanged -= PrimaryCommandsCollectionChanged; + } + } + } +} diff --git a/Wino.Mail/Behaviors/CreateMailNavigationItemBehavior.cs b/Wino.Mail/Behaviors/CreateMailNavigationItemBehavior.cs new file mode 100644 index 00000000..6726a351 --- /dev/null +++ b/Wino.Mail/Behaviors/CreateMailNavigationItemBehavior.cs @@ -0,0 +1,86 @@ +using System.Collections.ObjectModel; +using Microsoft.Xaml.Interactivity; +using Windows.UI.Xaml; +using Wino.Controls; +using Wino.Core.MenuItems; + +namespace Wino.Behaviors +{ + public class CreateMailNavigationItemBehavior : Behavior + { + public MenuItemBase SelectedMenuItem + { + get { return (MenuItemBase)GetValue(SelectedMenuItemProperty); } + set { SetValue(SelectedMenuItemProperty, value); } + } + + public ObservableCollection MenuItems + { + get { return (ObservableCollection)GetValue(MenuItemsProperty); } + set { SetValue(MenuItemsProperty, value); } + } + + public static readonly DependencyProperty MenuItemsProperty = DependencyProperty.Register(nameof(MenuItems), typeof(ObservableCollection), typeof(CreateMailNavigationItemBehavior), new PropertyMetadata(null, OnMenuItemsChanged)); + public static readonly DependencyProperty SelectedMenuItemProperty = DependencyProperty.Register(nameof(SelectedMenuItem), typeof(MenuItemBase), typeof(CreateMailNavigationItemBehavior), new PropertyMetadata(null, OnSelectedMenuItemChanged)); + + public CreateMailNavigationItemBehavior() + { + + } + + protected override void OnAttached() + { + base.OnAttached(); + } + + protected override void OnDetaching() + { + base.OnDetaching(); + } + + private static void OnMenuItemsChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) + { + if (dependencyObject is CreateMailNavigationItemBehavior behavior) + { + if (dependencyPropertyChangedEventArgs.NewValue != null) + behavior.RegisterMenuItemChanges(); + + behavior.ManageAccounts(); + } + } + + private void RegisterMenuItemChanges() + { + if (MenuItems != null) + { + MenuItems.CollectionChanged -= MenuCollectionUpdated; + MenuItems.CollectionChanged += MenuCollectionUpdated; + } + } + + private void MenuCollectionUpdated(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + ManageAccounts(); + } + + private static void OnSelectedMenuItemChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) + { + if (dependencyObject is CreateMailNavigationItemBehavior behavior) + { + behavior.ManageAccounts(); + } + } + + private void ManageAccounts() + { + if (MenuItems == null) return; + + AssociatedObject.MenuItems.Clear(); + + if (SelectedMenuItem == null) + { + // ?? + } + } + } +} diff --git a/Wino.Mail/Controls/AccountNavigationItem.cs b/Wino.Mail/Controls/AccountNavigationItem.cs new file mode 100644 index 00000000..90aeda7a --- /dev/null +++ b/Wino.Mail/Controls/AccountNavigationItem.cs @@ -0,0 +1,55 @@ +using System.Diagnostics; +using System.Numerics; +using CommunityToolkit.WinUI; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml; + +namespace Wino.Controls +{ + public class AccountNavigationItem : WinoNavigationViewItem + { + public bool IsActiveAccount + { + get { return (bool)GetValue(IsActiveAccountProperty); } + set { SetValue(IsActiveAccountProperty, value); } + } + + public static readonly DependencyProperty IsActiveAccountProperty = DependencyProperty.Register(nameof(IsActiveAccount), typeof(bool), typeof(AccountNavigationItem), new PropertyMetadata(false, new PropertyChangedCallback(OnIsActiveAccountChanged))); + + + private const string PART_NavigationViewItemMenuItemsHost = "NavigationViewItemMenuItemsHost"; + private const string PART_SelectionIndicator = "SelectionIndicator"; + + private ItemsRepeater _itemsRepeater; + private Windows.UI.Xaml.Shapes.Rectangle _selectionIndicator; + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + _itemsRepeater = GetTemplateChild(PART_NavigationViewItemMenuItemsHost) as ItemsRepeater; + _selectionIndicator = GetTemplateChild(PART_SelectionIndicator) as Windows.UI.Xaml.Shapes.Rectangle; + + if (_itemsRepeater == null) return; + + (_itemsRepeater.Layout as StackLayout).Spacing = 0; + + UpdateSelectionBorder(); + } + + private static void OnIsActiveAccountChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is AccountNavigationItem control) + control.UpdateSelectionBorder(); + } + + private void UpdateSelectionBorder() + { + if (_selectionIndicator == null) return; + + _selectionIndicator.Scale = IsActiveAccount ? new Vector3(1,1,1) : new Vector3(0,0,0); + _selectionIndicator.Visibility = IsActiveAccount ? Visibility.Visible : Visibility.Collapsed; + } + } +} diff --git a/Wino.Mail/Controls/Advanced/WinoAppTitleBar.xaml b/Wino.Mail/Controls/Advanced/WinoAppTitleBar.xaml new file mode 100644 index 00000000..191a87d6 --- /dev/null +++ b/Wino.Mail/Controls/Advanced/WinoAppTitleBar.xaml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Controls/Advanced/WinoAppTitleBar.xaml.cs b/Wino.Mail/Controls/Advanced/WinoAppTitleBar.xaml.cs new file mode 100644 index 00000000..7396bc91 --- /dev/null +++ b/Wino.Mail/Controls/Advanced/WinoAppTitleBar.xaml.cs @@ -0,0 +1,169 @@ +using Windows.Foundation; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace Wino.Controls.Advanced +{ + public sealed partial class WinoAppTitleBar : UserControl + { + public event TypedEventHandler BackButtonClicked; + + public string CoreWindowText + { + get { return (string)GetValue(CoreWindowTextProperty); } + set { SetValue(CoreWindowTextProperty, value); } + } + + public double SystemReserved + { + get { return (double)GetValue(SystemReservedProperty); } + set { SetValue(SystemReservedProperty, value); } + } + + public UIElement ShellFrameContent + { + get { return (UIElement)GetValue(ShellFrameContentProperty); } + set { SetValue(ShellFrameContentProperty, value); } + } + + public Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode NavigationViewDisplayMode + { + get { return (Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode)GetValue(NavigationViewDisplayModeProperty); } + set { SetValue(NavigationViewDisplayModeProperty, value); } + } + + public bool IsNavigationPaneOpen + { + get { return (bool)GetValue(IsNavigationPaneOpenProperty); } + set { SetValue(IsNavigationPaneOpenProperty, value); } + } + + public double OpenPaneLength + { + get { return (double)GetValue(OpenPaneLengthProperty); } + set { SetValue(OpenPaneLengthProperty, value); } + } + + public bool IsBackButtonVisible + { + get { return (bool)GetValue(IsBackButtonVisibleProperty); } + set { SetValue(IsBackButtonVisibleProperty, value); } + } + + public bool IsReaderNarrowed + { + get { return (bool)GetValue(IsReaderNarrowedProperty); } + set { SetValue(IsReaderNarrowedProperty, value); } + } + + public bool IsRenderingPaneVisible + { + get { return (bool)GetValue(IsRenderingPaneVisibleProperty); } + set { SetValue(IsRenderingPaneVisibleProperty, value); } + } + + public static readonly DependencyProperty IsRenderingPaneVisibleProperty = DependencyProperty.Register(nameof(IsRenderingPaneVisible), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnDrawingPropertyChanged)); + public static readonly DependencyProperty IsReaderNarrowedProperty = DependencyProperty.Register(nameof(IsReaderNarrowed), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnIsReaderNarrowedChanged)); + public static readonly DependencyProperty IsBackButtonVisibleProperty = DependencyProperty.Register(nameof(IsBackButtonVisible), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnDrawingPropertyChanged)); + public static readonly DependencyProperty OpenPaneLengthProperty = DependencyProperty.Register(nameof(OpenPaneLength), typeof(double), typeof(WinoAppTitleBar), new PropertyMetadata(0d, OnDrawingPropertyChanged)); + public static readonly DependencyProperty IsNavigationPaneOpenProperty = DependencyProperty.Register(nameof(IsNavigationPaneOpen), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnDrawingPropertyChanged)); + public static readonly DependencyProperty NavigationViewDisplayModeProperty = DependencyProperty.Register(nameof(NavigationViewDisplayMode), typeof(Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode), typeof(WinoAppTitleBar), new PropertyMetadata(Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Compact, OnDrawingPropertyChanged)); + public static readonly DependencyProperty ShellFrameContentProperty = DependencyProperty.Register(nameof(ShellFrameContent), typeof(UIElement), typeof(WinoAppTitleBar), new PropertyMetadata(null, OnDrawingPropertyChanged)); + public static readonly DependencyProperty SystemReservedProperty = DependencyProperty.Register(nameof(SystemReserved), typeof(double), typeof(WinoAppTitleBar), new PropertyMetadata(0, OnDrawingPropertyChanged)); + public static readonly DependencyProperty CoreWindowTextProperty = DependencyProperty.Register(nameof(CoreWindowText), typeof(string), typeof(WinoAppTitleBar), new PropertyMetadata(string.Empty, OnDrawingPropertyChanged)); + public static readonly DependencyProperty ReadingPaneLengthProperty = DependencyProperty.Register(nameof(ReadingPaneLength), typeof(double), typeof(WinoAppTitleBar), new PropertyMetadata(420d, OnDrawingPropertyChanged)); + + + public double ReadingPaneLength + { + get { return (double)GetValue(ReadingPaneLengthProperty); } + set { SetValue(ReadingPaneLengthProperty, value); } + } + + private static void OnIsReaderNarrowedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is WinoAppTitleBar bar) + { + bar.DrawTitleBar(); + } + } + + private static void OnDrawingPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is WinoAppTitleBar bar) + { + bar.DrawTitleBar(); + } + } + + private void DrawTitleBar() + { + UpdateLayout(); + + CoreWindowTitleTextBlock.Visibility = Visibility.Collapsed; + ContentGrid.Width = double.NaN; + ContentGrid.Margin = new Thickness(0, 0, 0, 0); + ContentGrid.HorizontalAlignment = HorizontalAlignment.Stretch; + + EmptySpaceWidth.Width = new GridLength(1, GridUnitType.Star); + + // Menu is not visible. + if (NavigationViewDisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Minimal) + { + + } + else if (NavigationViewDisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Compact) + { + // Icons are visible. + + if (!IsReaderNarrowed) + { + ContentGrid.HorizontalAlignment = HorizontalAlignment.Left; + ContentGrid.Width = ReadingPaneLength; + } + } + else if (NavigationViewDisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Expanded) + { + if (IsNavigationPaneOpen) + { + CoreWindowTitleTextBlock.Visibility = Visibility.Visible; + + // LMargin = OpenPaneLength - LeftMenuStackPanel + ContentGrid.Margin = new Thickness(OpenPaneLength - LeftMenuStackPanel.ActualSize.X, 0, 0, 0); + + if (!IsReaderNarrowed) + { + ContentGrid.HorizontalAlignment = HorizontalAlignment.Left; + ContentGrid.Width = ReadingPaneLength; + } + } + else + { + EmptySpaceWidth.Width = new GridLength(ReadingPaneLength, GridUnitType.Pixel); + } + } + } + + public WinoAppTitleBar() + { + this.InitializeComponent(); + + Window.Current.SetTitleBar(dragbar); + } + + private void BackClicked(object sender, RoutedEventArgs e) + { + BackButtonClicked?.Invoke(this, e); + } + + private void PaneClicked(object sender, RoutedEventArgs e) + { + IsNavigationPaneOpen = !IsNavigationPaneOpen; + } + + private void asd(object sender, SizeChangedEventArgs e) + { + DrawTitleBar(); + } + } +} diff --git a/Wino.Mail/Controls/Advanced/WinoListView.cs b/Wino.Mail/Controls/Advanced/WinoListView.cs new file mode 100644 index 00000000..a6382321 --- /dev/null +++ b/Wino.Mail/Controls/Advanced/WinoListView.cs @@ -0,0 +1,382 @@ +using System; +using System.Linq; +using System.Windows.Input; +using CommunityToolkit.Mvvm.Messaging; +using Microsoft.UI.Xaml.Controls; +using MoreLinq; +using Serilog; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.MailItem; +using Wino.Extensions; +using Wino.Mail.ViewModels.Data; +using Wino.Mail.ViewModels.Messages; + +namespace Wino.Controls.Advanced +{ + /// + /// Custom ListView control that handles multiple selection with Extended/Multiple selection mode + /// and supports threads. + /// + public class WinoListView : ListView, IDisposable + { + private ILogger logger = Log.ForContext(); + + private const string PART_ScrollViewer = "ScrollViewer"; + private ScrollViewer internalScrollviewer; + + /// + /// Gets or sets whether this ListView belongs to thread items. + /// This is important for detecting selected items etc. + /// + public bool IsThreadListView + { + get { return (bool)GetValue(IsThreadListViewProperty); } + set { SetValue(IsThreadListViewProperty, value); } + } + + public ICommand ItemDeletedCommand + { + get { return (ICommand)GetValue(ItemDeletedCommandProperty); } + set { SetValue(ItemDeletedCommandProperty, value); } + } + + public ICommand LoadMoreCommand + { + get { return (ICommand)GetValue(LoadMoreCommandProperty); } + set { SetValue(LoadMoreCommandProperty, value); } + } + + public static readonly DependencyProperty LoadMoreCommandProperty = DependencyProperty.Register(nameof(LoadMoreCommand), typeof(ICommand), typeof(WinoListView), new PropertyMetadata(null)); + public static readonly DependencyProperty IsThreadListViewProperty = DependencyProperty.Register(nameof(IsThreadListView), typeof(bool), typeof(WinoListView), new PropertyMetadata(false)); + public static readonly DependencyProperty ItemDeletedCommandProperty = DependencyProperty.Register(nameof(ItemDeletedCommand), typeof(ICommand), typeof(WinoListView), new PropertyMetadata(null)); + + public WinoListView() + { + CanDragItems = true; + IsItemClickEnabled = true; + IsMultiSelectCheckBoxEnabled = true; + IsRightTapEnabled = true; + SelectionMode = ListViewSelectionMode.Extended; + ShowsScrollingPlaceholders = false; + SingleSelectionFollowsFocus = true; + + DragItemsCompleted += ItemDragCompleted; + DragItemsStarting += ItemDragStarting; + SelectionChanged += SelectedItemsChanged; + ItemClick += MailItemClicked; + ProcessKeyboardAccelerators += ProcessDelKey; + } + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + internalScrollviewer = GetTemplateChild(PART_ScrollViewer) as ScrollViewer; + + if (internalScrollviewer == null) + { + logger.Warning("WinoListView does not have an internal ScrollViewer. Infinite scrolling behavior might be effected."); + return; + } + + internalScrollviewer.ViewChanged -= InternalScrollVeiwerViewChanged; + internalScrollviewer.ViewChanged += InternalScrollVeiwerViewChanged; + } + + private double lastestRaisedOffset = 0; + private int lastItemSize = 0; + + // TODO: This is buggy. Does not work all the time. Debug. + + private void InternalScrollVeiwerViewChanged(object sender, ScrollViewerViewChangedEventArgs e) + { + if (internalScrollviewer == null) return; + + // No need to raise init request if there are no items in the list. + if (Items.Count == 0) return; + + // If the scrolling is finished, check the current viewport height. + if (e.IsIntermediate) + { + var currentOffset = internalScrollviewer.VerticalOffset; + var maxOffset = internalScrollviewer.ScrollableHeight; + + if (currentOffset + 10 >= maxOffset && lastestRaisedOffset != maxOffset && Items.Count != lastItemSize) + { + // We must load more. + lastestRaisedOffset = maxOffset; + lastItemSize = Items.Count; + + LoadMoreCommand?.Execute(null); + } + } + } + + private void ProcessDelKey(UIElement sender, Windows.UI.Xaml.Input.ProcessKeyboardAcceleratorEventArgs args) + { + if (args.Key == Windows.System.VirtualKey.Delete) + { + args.Handled = true; + + ItemDeletedCommand?.Execute((int)MailOperation.SoftDelete); + } + } + + private void ItemDragCompleted(ListViewBase sender, DragItemsCompletedEventArgs args) + { + if (args.Items.Any(a => a is MailItemViewModel)) + { + args.Items.Cast().ForEach(a => a.IsCustomFocused = false); + } + } + + private void ItemDragStarting(object sender, DragItemsStartingEventArgs args) + { + // Dragging multiple mails from different accounts/folders are supported with the condition below: + // All mails belongs to the drag will be matched on the dropped folder's account. + // Meaning that if users drag 1 mail from Account A/Inbox and 1 mail from Account B/Inbox, + // and drop to Account A/Inbox, the mail from Account B/Inbox will NOT be moved. + + if (IsThreadListView) + { + var allItems = args.Items.Cast(); + + // Highlight all items + allItems.ForEach(a => a.IsCustomFocused = true); + + // Set native drag arg properties. + + var dragPackage = new MailDragPackage(allItems.Cast()); + + args.Data.Properties.Add(nameof(MailDragPackage), dragPackage); + } + else + { + var dragPackage = new MailDragPackage(args.Items.Cast()); + + args.Data.Properties.Add(nameof(MailDragPackage), dragPackage); + } + } + + private void MailItemClicked(object sender, ItemClickEventArgs e) + { + if (e.ClickedItem is ThreadMailItemViewModel clickedThread) + { + clickedThread.IsThreadExpanded = !clickedThread.IsThreadExpanded; + + if (!clickedThread.IsThreadExpanded) + { + SelectedItems.Clear(); + } + } + } + + public void ChangeSelectionMode(ListViewSelectionMode selectionMode) + { + SelectionMode = selectionMode; + + if (!IsThreadListView) + { + Items.Where(a => a is ThreadMailItemViewModel).Cast().ForEach(c => + { + var threadListView = GetThreadInternalListView(c); + + if (threadListView != null) + { + threadListView.SelectionMode = selectionMode; + } + }); + } + } + + /// + /// Finds the container for given mail item and adds it to selected items. + /// + /// Mail to be added to selected items. + /// Whether selection was successful or not. + public bool SelectMailItemContainer(MailItemViewModel mailItemViewModel) + { + var itemContainer = ContainerFromItem(mailItemViewModel); + + // This item might be in thread container. + if (itemContainer == null) + { + bool found = false; + + Items.Where(a => a is ThreadMailItemViewModel).Cast().ForEach(c => + { + if (!found) + { + var threadListView = GetThreadInternalListView(c); + + if (threadListView != null) + found = threadListView.SelectMailItemContainer(mailItemViewModel); + } + }); + + return found; + } + + SelectedItems.Add(mailItemViewModel); + return true; + } + + /// + /// Recursively clears all selections except the given mail. + /// + /// Exceptional mail item to be not unselected. + /// Whether expansion states of thread containers should stay as it is or not. + public void ClearSelections(MailItemViewModel exceptViewModel = null, bool preserveThreadExpanding = false) + { + SelectedItems.Clear(); + + Items.Where(a => a is ThreadMailItemViewModel).Cast().ForEach(c => + { + var threadListView = GetThreadInternalListView(c); + + if (threadListView == null) + return; + + if (exceptViewModel != null) + { + if (!threadListView.SelectedItems.Contains(exceptViewModel)) + { + if (!preserveThreadExpanding) + { + c.IsThreadExpanded = false; + } + + threadListView.SelectedItems.Clear(); + } + } + else + { + if (!preserveThreadExpanding) + { + c.IsThreadExpanded = false; + } + + threadListView.SelectedItems.Clear(); + } + }); + } + + /// + /// Recursively selects all mails, including thread items. + /// + public void SelectAllWino() + { + SelectAll(); + + Items.Where(a => a is ThreadMailItemViewModel).Cast().ForEach(c => + { + c.IsThreadExpanded = true; + + var threadListView = GetThreadInternalListView(c); + + threadListView?.SelectAll(); + }); + } + + // SelectedItems changed. + private void SelectedItemsChanged(object sender, SelectionChangedEventArgs e) + { + if (e.RemovedItems != null) + { + foreach (var removedItem in e.RemovedItems) + { + if (removedItem is MailItemViewModel removedMailItemViewModel) + { + // Mail item un-selected. + + removedMailItemViewModel.IsSelected = false; + WeakReferenceMessenger.Default.Send(new MailItemSelectionRemovedEvent(removedMailItemViewModel)); + } + } + } + + if (e.AddedItems != null) + { + foreach (var addedItem in e.AddedItems) + { + if (addedItem is MailItemViewModel addedMailItemViewModel) + { + // Mail item selected. + + addedMailItemViewModel.IsSelected = true; + + WeakReferenceMessenger.Default.Send(new MailItemSelectedEvent(addedMailItemViewModel)); + } + else if (addedItem is ThreadMailItemViewModel threadMailItemViewModel) + { + threadMailItemViewModel.IsThreadExpanded = true; + + // Don't select thread containers. + SelectedItems.Remove(addedItem); + } + } + } + + if (!IsThreadListView) + { + if (SelectionMode == ListViewSelectionMode.Extended && SelectedItems.Count == 1) + { + // Only 1 single item is selected in extended mode for main list view. + // We should un-select all thread items. + + Items.Where(a => a is ThreadMailItemViewModel).Cast().ForEach(c => + { + // c.IsThreadExpanded = false; + + var threadListView = GetThreadInternalListView(c); + + threadListView?.SelectedItems.Clear(); + }); + } + } + else + { + if (SelectionMode == ListViewSelectionMode.Extended && SelectedItems.Count == 1) + { + // Tell main list view to unselect all his items. + + if (SelectedItems[0] is MailItemViewModel selectedMailItemViewModel) + { + WeakReferenceMessenger.Default.Send(new ResetSingleMailItemSelectionEvent(selectedMailItemViewModel)); + } + } + } + } + + private WinoListView GetThreadInternalListView(ThreadMailItemViewModel threadMailItemViewModel) + { + var itemContainer = ContainerFromItem(threadMailItemViewModel); + + if (itemContainer is ListViewItem listItem) + { + var expander = listItem.GetChildByName("ThreadExpander"); + + if (expander != null) + return expander.Content as WinoListView; + } + + return null; + } + + public void Dispose() + { + DragItemsCompleted -= ItemDragCompleted; + DragItemsStarting -= ItemDragStarting; + SelectionChanged -= SelectedItemsChanged; + ItemClick -= MailItemClicked; + ProcessKeyboardAccelerators -= ProcessDelKey; + + if (internalScrollviewer != null) + { + internalScrollviewer.ViewChanged -= InternalScrollVeiwerViewChanged; + } + } + } +} diff --git a/Wino.Mail/Controls/ControlConstants.cs b/Wino.Mail/Controls/ControlConstants.cs new file mode 100644 index 00000000..4353f2f6 --- /dev/null +++ b/Wino.Mail/Controls/ControlConstants.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; + +namespace Wino.Controls +{ + public static class ControlConstants + { + public static Dictionary WinoIconFontDictionary = new Dictionary() + { + { WinoIconGlyph.None, "\u0020" }, + { WinoIconGlyph.Archive, "\uE066" }, + { WinoIconGlyph.UnArchive, "\uE06C" }, + { WinoIconGlyph.Reply, "\uF176" }, + { WinoIconGlyph.ReplyAll, "\uF17A" }, + { WinoIconGlyph.Sync, "\uE902" }, + { WinoIconGlyph.Send, "\uEA8E" }, + { WinoIconGlyph.LightEditor, "\uE1F6" }, + { WinoIconGlyph.Delete, "\uEEA6" }, + { WinoIconGlyph.DarkEditor, "\uEE44" }, + { WinoIconGlyph.Draft, "\uF3BE" }, + { WinoIconGlyph.Flag, "\uF40C" }, + { WinoIconGlyph.ClearFlag, "\uF40F" }, + { WinoIconGlyph.Folder, "\uE643" }, + { WinoIconGlyph.Forward, "\uE7AA" }, + { WinoIconGlyph.Inbox, "\uF516" }, + { WinoIconGlyph.MarkRead, "\uF522" }, + { WinoIconGlyph.MarkUnread, "\uF529" }, + { WinoIconGlyph.MultiSelect, "\uE84D" }, + { WinoIconGlyph.Save, "\uEA43" }, + { WinoIconGlyph.CreateFolder, "\uE645" }, + { WinoIconGlyph.Pin, "\uF5FF" }, + { WinoIconGlyph.UnPin, "\uE985" }, + { WinoIconGlyph.Star, "\uF70D" }, + { WinoIconGlyph.Ignore, "\uF5D0" }, + { WinoIconGlyph.Junk, "\uE903" }, + { WinoIconGlyph.Find, "\uEA7D" }, + { WinoIconGlyph.Zoom, "\uEE8E" }, + { WinoIconGlyph.SpecialFolderInbox, "\uF516" }, + { WinoIconGlyph.SpecialFolderStarred, "\uF70D" }, + { WinoIconGlyph.SpecialFolderImportant, "\uE2F4" }, + { WinoIconGlyph.SpecialFolderSent, "\uEA8E" }, + { WinoIconGlyph.SpecialFolderDraft, "\uF3BE" }, + { WinoIconGlyph.SpecialFolderArchive, "\uE066" }, + { WinoIconGlyph.SpecialFolderDeleted, "\uEEA6" }, + { WinoIconGlyph.SpecialFolderJunk, "\uE903" }, + { WinoIconGlyph.SpecialFolderChat, "\uE2E3" }, + { WinoIconGlyph.SpecialFolderCategory, "\uF599" }, + { WinoIconGlyph.SpecialFolderUnread, "\uF529" }, + { WinoIconGlyph.SpecialFolderForums, "\uF5B8" }, + { WinoIconGlyph.SpecialFolderUpdated, "\uF565" }, + { WinoIconGlyph.SpecialFolderPersonal, "\uE25A" }, + { WinoIconGlyph.SpecialFolderPromotions, "\uF7B6" }, + { WinoIconGlyph.SpecialFolderSocial, "\uEEEB" }, + { WinoIconGlyph.SpecialFolderOther, "\uE643" }, + { WinoIconGlyph.SpecialFolderMore, "\uF0F4" }, + { WinoIconGlyph.Microsoft, "\uE900" }, + { WinoIconGlyph.Google, "\uE901" }, + { WinoIconGlyph.NewMail, "\uF107" }, + { WinoIconGlyph.TurnOfNotifications, "\uF11D" }, + { WinoIconGlyph.Rename, "\uF668" }, + { WinoIconGlyph.EmptyFolder, "\uE47E" }, + { WinoIconGlyph.DontSync, "\uF195" }, + { WinoIconGlyph.Move, "\uE7B8" }, + { WinoIconGlyph.Mail, "\uF509" }, + { WinoIconGlyph.More, "\uE824" }, + { WinoIconGlyph.CustomServer, "\uF509" }, + }; + } +} diff --git a/Wino.Mail/Controls/ImagePreviewControl.cs b/Wino.Mail/Controls/ImagePreviewControl.cs new file mode 100644 index 00000000..9c0fa0db --- /dev/null +++ b/Wino.Mail/Controls/ImagePreviewControl.cs @@ -0,0 +1,145 @@ +using System; +using System.Text.RegularExpressions; +using Fernandezja.ColorHashSharp; +using Windows.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Media.Imaging; +using Windows.UI.Xaml.Shapes; +using Wino.Core.Services; + +namespace Wino.Controls +{ + public class ImagePreviewControl : Control + { + private const string PART_EllipseInitialsGrid = "EllipseInitialsGrid"; + private const string PART_InitialsTextBlock = "InitialsTextBlock"; + private const string PART_KnownHostImage = "KnownHostImage"; + private const string PART_Ellipse = "Ellipse"; + + #region Dependency Properties + + public static readonly DependencyProperty FromNameProperty = DependencyProperty.Register(nameof(FromName), typeof(string), typeof(ImagePreviewControl), new PropertyMetadata(string.Empty, OnAddressInformationChanged)); + public static readonly DependencyProperty FromAddressProperty = DependencyProperty.Register(nameof(FromAddress), typeof(string), typeof(ImagePreviewControl), new PropertyMetadata(string.Empty, OnAddressInformationChanged)); + public static readonly DependencyProperty IsKnownProperty = DependencyProperty.Register(nameof(IsKnown), typeof(bool), typeof(ImagePreviewControl), new PropertyMetadata(false)); + + public string FromName + { + get { return (string)GetValue(FromNameProperty); } + set { SetValue(FromNameProperty, value); } + } + + public string FromAddress + { + get { return (string)GetValue(FromAddressProperty); } + set { SetValue(FromAddressProperty, value); } + } + + public bool IsKnown + { + get { return (bool)GetValue(IsKnownProperty); } + set { SetValue(IsKnownProperty, value); } + } + + + + #endregion + + private Ellipse Ellipse; + private Grid InitialsGrid; + private TextBlock InitialsTextblock; + private Image KnownHostImage; + + public ImagePreviewControl() + { + DefaultStyleKey = nameof(ImagePreviewControl); + } + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + InitialsGrid = GetTemplateChild(PART_EllipseInitialsGrid) as Grid; + InitialsTextblock = GetTemplateChild(PART_InitialsTextBlock) as TextBlock; + KnownHostImage = GetTemplateChild(PART_KnownHostImage) as Image; + Ellipse = GetTemplateChild(PART_Ellipse) as Ellipse; + + UpdateInformation(); + } + + private static void OnAddressInformationChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is ImagePreviewControl control) + control.UpdateInformation(); + } + + private void UpdateInformation() + { + if (KnownHostImage == null || InitialsGrid == null || InitialsTextblock == null || (string.IsNullOrEmpty(FromName) && string.IsNullOrEmpty(FromAddress))) + return; + + var host = ThumbnailService.GetHost(FromAddress); + + if (!string.IsNullOrEmpty(host)) + { + var tuple = ThumbnailService.CheckIsKnown(host); + + IsKnown = tuple.Item1; + host = tuple.Item2; + } + + if (IsKnown) + { + // Unrealize others. + + KnownHostImage.Visibility = Visibility.Visible; + InitialsGrid.Visibility = Visibility.Collapsed; + + // Apply company logo. + KnownHostImage.Source = new BitmapImage(new Uri(ThumbnailService.GetKnownHostImage(host))); + } + else + { + KnownHostImage.Visibility = Visibility.Collapsed; + InitialsGrid.Visibility = Visibility.Visible; + + var colorHash = new ColorHash(); + var rgb = colorHash.Rgb(FromAddress); + + Ellipse.Fill = new SolidColorBrush(Color.FromArgb(rgb.A, rgb.R, rgb.G, rgb.B)); + + InitialsTextblock.Text = ExtractInitialsFromName(FromName); + } + } + + public string ExtractInitialsFromName(string name) + { + // Change from name to from address in case of name doesn't exists. + if (string.IsNullOrEmpty(name)) + { + name = FromAddress; + } + + // first remove all: punctuation, separator chars, control chars, and numbers (unicode style regexes) + string initials = Regex.Replace(name, @"[\p{P}\p{S}\p{C}\p{N}]+", ""); + + // Replacing all possible whitespace/separator characters (unicode style), with a single, regular ascii space. + initials = Regex.Replace(initials, @"\p{Z}+", " "); + + // Remove all Sr, Jr, I, II, III, IV, V, VI, VII, VIII, IX at the end of names + initials = Regex.Replace(initials.Trim(), @"\s+(?:[JS]R|I{1,3}|I[VX]|VI{0,3})$", "", RegexOptions.IgnoreCase); + + // Extract up to 2 initials from the remaining cleaned name. + initials = Regex.Replace(initials, @"^(\p{L})[^\s]*(?:\s+(?:\p{L}+\s+(?=\p{L}))?(?:(\p{L})\p{L}*)?)?$", "$1$2").Trim(); + + if (initials.Length > 2) + { + // Worst case scenario, everything failed, just grab the first two letters of what we have left. + initials = initials.Substring(0, 2); + } + + return initials.ToUpperInvariant(); + } + } +} diff --git a/Wino.Mail/Controls/MailItemDisplayInformationControl.xaml b/Wino.Mail/Controls/MailItemDisplayInformationControl.xaml new file mode 100644 index 00000000..2e0e45b5 --- /dev/null +++ b/Wino.Mail/Controls/MailItemDisplayInformationControl.xaml @@ -0,0 +1,276 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Controls/MailItemDisplayInformationControl.xaml.cs b/Wino.Mail/Controls/MailItemDisplayInformationControl.xaml.cs new file mode 100644 index 00000000..4aee47b7 --- /dev/null +++ b/Wino.Mail/Controls/MailItemDisplayInformationControl.xaml.cs @@ -0,0 +1,282 @@ +using System; +using System.ComponentModel; +using System.Numerics; +using System.Windows.Input; +using Microsoft.UI.Xaml.Controls; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.MailItem; +using Wino.Extensions; +using Wino.Mail.ViewModels.Data; + +namespace Wino.Controls +{ + public sealed partial class MailItemDisplayInformationControl : UserControl, INotifyPropertyChanged + { + public ImagePreviewControl GetImagePreviewControl() => ContactImage; + + public static readonly DependencyProperty DisplayModeProperty = DependencyProperty.Register(nameof(DisplayMode), typeof(MailListDisplayMode), typeof(MailItemDisplayInformationControl), new PropertyMetadata(MailListDisplayMode.Spacious)); + public static readonly DependencyProperty ShowPreviewTextProperty = DependencyProperty.Register(nameof(ShowPreviewText), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(true)); + public static readonly DependencyProperty SnippetProperty = DependencyProperty.Register(nameof(Snippet), typeof(string), typeof(MailItemDisplayInformationControl), new PropertyMetadata(string.Empty)); + public static readonly DependencyProperty FromNameProperty = DependencyProperty.Register(nameof(FromName), typeof(string), typeof(MailItemDisplayInformationControl), new PropertyMetadata(string.Empty)); + public static readonly DependencyProperty SubjectProperty = DependencyProperty.Register(nameof(Subject), typeof(string), typeof(MailItemDisplayInformationControl), new PropertyMetadata("(no-subject)")); + public static readonly DependencyProperty IsReadProperty = DependencyProperty.Register(nameof(IsRead), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(false)); + public static readonly DependencyProperty IsFlaggedProperty = DependencyProperty.Register(nameof(IsFlagged), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(false)); + public static readonly DependencyProperty FromAddressProperty = DependencyProperty.Register(nameof(FromAddress), typeof(string), typeof(MailItemDisplayInformationControl), new PropertyMetadata(string.Empty)); + public static readonly DependencyProperty HasAttachmentsProperty = DependencyProperty.Register(nameof(HasAttachments), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(false)); + public static readonly DependencyProperty IsCustomFocusedProperty = DependencyProperty.Register(nameof(IsCustomFocused), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(false)); + public static readonly DependencyProperty ReceivedDateProperty = DependencyProperty.Register(nameof(ReceivedDate), typeof(DateTime), typeof(MailItemDisplayInformationControl), new PropertyMetadata(default(DateTime))); + public static readonly DependencyProperty IsDraftProperty = DependencyProperty.Register(nameof(IsDraft), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(false)); + public static readonly DependencyProperty IsAvatarVisibleProperty = DependencyProperty.Register(nameof(IsAvatarVisible), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(true)); + public static readonly DependencyProperty IsSubjectVisibleProperty = DependencyProperty.Register(nameof(IsSubjectVisible), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(true)); + public static readonly DependencyProperty ConnectedExpanderProperty = DependencyProperty.Register(nameof(ConnectedExpander), typeof(Expander), typeof(MailItemDisplayInformationControl), new PropertyMetadata(null)); + public static readonly DependencyProperty LeftHoverActionProperty = DependencyProperty.Register(nameof(LeftHoverAction), typeof(MailOperation), typeof(MailItemDisplayInformationControl), new PropertyMetadata(MailOperation.None)); + public static readonly DependencyProperty CenterHoverActionProperty = DependencyProperty.Register(nameof(CenterHoverAction), typeof(MailOperation), typeof(MailItemDisplayInformationControl), new PropertyMetadata(MailOperation.None)); + public static readonly DependencyProperty RightHoverActionProperty = DependencyProperty.Register(nameof(RightHoverAction), typeof(MailOperation), typeof(MailItemDisplayInformationControl), new PropertyMetadata(MailOperation.None)); + public static readonly DependencyProperty HoverActionExecutedCommandProperty = DependencyProperty.Register(nameof(HoverActionExecutedCommand), typeof(ICommand), typeof(MailItemDisplayInformationControl), new PropertyMetadata(null)); + public static readonly DependencyProperty MailItemProperty = DependencyProperty.Register(nameof(MailItem), typeof(IMailItem), typeof(MailItemDisplayInformationControl), new PropertyMetadata(null)); + public static readonly DependencyProperty IsHoverActionsEnabledProperty = DependencyProperty.Register(nameof(IsHoverActionsEnabled), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(true)); + public static readonly DependencyProperty Prefer24HourTimeFormatProperty = DependencyProperty.Register(nameof(Prefer24HourTimeFormat), typeof(bool), typeof(MailItemDisplayInformationControl), new PropertyMetadata(false)); + + public bool Prefer24HourTimeFormat + { + get { return (bool)GetValue(Prefer24HourTimeFormatProperty); } + set { SetValue(Prefer24HourTimeFormatProperty, value); } + } + + public bool IsHoverActionsEnabled + { + get { return (bool)GetValue(IsHoverActionsEnabledProperty); } + set { SetValue(IsHoverActionsEnabledProperty, value); } + } + + public IMailItem MailItem + { + get { return (IMailItem)GetValue(MailItemProperty); } + set { SetValue(MailItemProperty, value); } + } + + public ICommand HoverActionExecutedCommand + { + get { return (ICommand)GetValue(HoverActionExecutedCommandProperty); } + set { SetValue(HoverActionExecutedCommandProperty, value); } + } + + public MailOperation LeftHoverAction + { + get { return (MailOperation)GetValue(LeftHoverActionProperty); } + set { SetValue(LeftHoverActionProperty, value); } + } + + public MailOperation CenterHoverAction + { + get { return (MailOperation)GetValue(CenterHoverActionProperty); } + set { SetValue(CenterHoverActionProperty, value); } + } + + public MailOperation RightHoverAction + { + get { return (MailOperation)GetValue(RightHoverActionProperty); } + set { SetValue(RightHoverActionProperty, value); } + } + + + public event PropertyChangedEventHandler PropertyChanged; + + public Expander ConnectedExpander + { + get { return (Expander)GetValue(ConnectedExpanderProperty); } + set { SetValue(ConnectedExpanderProperty, value); } + } + + public bool IsSubjectVisible + { + get { return (bool)GetValue(IsSubjectVisibleProperty); } + set { SetValue(IsSubjectVisibleProperty, value); } + } + + public bool IsAvatarVisible + { + get { return (bool)GetValue(IsAvatarVisibleProperty); } + set { SetValue(IsAvatarVisibleProperty, value); } + } + + public bool IsDraft + { + get { return (bool)GetValue(IsDraftProperty); } + set { SetValue(IsDraftProperty, value); } + } + + public DateTime ReceivedDate + { + get { return (DateTime)GetValue(ReceivedDateProperty); } + set { SetValue(ReceivedDateProperty, value); } + } + public bool IsCustomFocused + { + get { return (bool)GetValue(IsCustomFocusedProperty); } + set { SetValue(IsCustomFocusedProperty, value); } + } + + public bool HasAttachments + { + get { return (bool)GetValue(HasAttachmentsProperty); } + set { SetValue(HasAttachmentsProperty, value); } + } + + public bool IsRead + { + get { return (bool)GetValue(IsReadProperty); } + set { SetValue(IsReadProperty, value); } + } + + public bool IsFlagged + { + get { return (bool)GetValue(IsFlaggedProperty); } + set { SetValue(IsFlaggedProperty, value); } + } + + public string FromAddress + { + get { return (string)GetValue(FromAddressProperty); } + set + { + SetValue(FromAddressProperty, value); + + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DisplayName))); + } + } + + public string DisplayName + { + get + { + if (string.IsNullOrEmpty(FromName)) + return FromAddress; + + return FromName; + } + } + public string FromName + { + get => (string)GetValue(FromNameProperty); + set + { + SetValue(FromNameProperty, value); + + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DisplayName))); + } + } + + public string Subject + { + get { return (string)GetValue(SubjectProperty); } + set { SetValue(SubjectProperty, value); } + } + + public string Snippet + { + get { return (string)GetValue(SnippetProperty); } + set { SetValue(SnippetProperty, value); } + } + + public bool ShowPreviewText + { + get { return (bool)GetValue(ShowPreviewTextProperty); } + set { SetValue(ShowPreviewTextProperty, value); } + } + + public MailListDisplayMode DisplayMode + { + get { return (MailListDisplayMode)GetValue(DisplayModeProperty); } + set { SetValue(DisplayModeProperty, value); } + } + + private bool tappedHandlingFlag = false; + + public MailItemDisplayInformationControl() + { + this.InitializeComponent(); + + var compositor = this.Visual().Compositor; + + var leftBackgroundVisual = compositor.CreateSpriteVisual(); + RootContainerVisualWrapper.SetChildVisual(leftBackgroundVisual); + MainContentContainer.EnableImplicitAnimation(VisualPropertyType.Offset, 400); + + RootContainer.EnableImplicitAnimation(VisualPropertyType.Offset, 400); + ContentGrid.EnableImplicitAnimation(VisualPropertyType.Offset, 400); + ContentStackpanel.EnableImplicitAnimation(VisualPropertyType.Offset, 400); + IconsContainer.EnableImplicitAnimation(VisualPropertyType.Offset, 400); + + RootContainerVisualWrapper.SizeChanged += (s, e) => leftBackgroundVisual.Size = e.NewSize.ToVector2(); + } + + private void ControlPointerEntered(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e) + { + if (IsHoverActionsEnabled) + { + HoverActionButtons.Visibility = Visibility.Visible; + } + } + + private void ControlPointerExited(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e) + { + if (IsHoverActionsEnabled) + { + HoverActionButtons.Visibility = Visibility.Collapsed; + } + } + + private void ExecuteHoverAction(MailOperation operation) + { + MailOperationPreperationRequest package = null; + + if (MailItem is MailItemViewModel mailItemViewModel) + package = new MailOperationPreperationRequest(operation, mailItemViewModel.MailCopy, toggleExecution: true); + else if (MailItem is ThreadMailItemViewModel threadMailItemViewModel) + package = new MailOperationPreperationRequest(operation, threadMailItemViewModel.GetMailCopies(), toggleExecution: true); + + if (package == null) return; + + tappedHandlingFlag = true; + + HoverActionExecutedCommand?.Execute(package); + } + + private void ThreadHeaderTapped(object sender, TappedRoutedEventArgs e) + { + // Due to CanDrag=True, outer expander doesn't get the click event and it doesn't expand. We expand here manually. + // Also hover action button clicks will be delegated here after the execution runs. + // We should not expand the thread if the reason we are here is for hover actions. + + if (tappedHandlingFlag) + { + tappedHandlingFlag = false; + e.Handled = true; + return; + } + + if (ConnectedExpander == null) return; + + ConnectedExpander.IsExpanded = !ConnectedExpander.IsExpanded; + } + + private void FirstActionClicked(object sender, RoutedEventArgs e) + { + ExecuteHoverAction(LeftHoverAction); + } + + private void SecondActionClicked(object sender, RoutedEventArgs e) + { + ExecuteHoverAction(CenterHoverAction); + } + + private void ThirdActionClicked(object sender, RoutedEventArgs e) + { + ExecuteHoverAction(RightHoverAction); + } + } +} diff --git a/Wino.Mail/Controls/RendererCommandBar.cs b/Wino.Mail/Controls/RendererCommandBar.cs new file mode 100644 index 00000000..693a7597 --- /dev/null +++ b/Wino.Mail/Controls/RendererCommandBar.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Windows.Input; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Menus; +using Wino.Helpers; +using Wino.MenuFlyouts; + +namespace Wino.Controls +{ + public class RendererCommandBar : CommandBar, IDisposable + { + public static readonly DependencyProperty MenuItemsProperty = DependencyProperty.Register(nameof(MenuItemsProperty), typeof(ObservableCollection), typeof(RendererCommandBar), new PropertyMetadata(null, OnMenuItemsChanged)); + public static readonly DependencyProperty OperationClickedCommandProperty = DependencyProperty.Register(nameof(OperationClickedCommand), typeof(ICommand), typeof(RendererCommandBar), new PropertyMetadata(null)); + + public ICommand OperationClickedCommand + { + get { return (ICommand)GetValue(OperationClickedCommandProperty); } + set { SetValue(OperationClickedCommandProperty, value); } + } + + public ObservableCollection MenuItems + { + get { return (ObservableCollection)GetValue(MenuItemsProperty); } + set { SetValue(MenuItemsProperty, value); } + } + + public RendererCommandBar() + { + this.DefaultStyleKey = typeof(CommandBar); + } + + private static void OnMenuItemsChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is RendererCommandBar commandBar) + { + if (args.OldValue != null) + commandBar.UnregisterButtonCollection(args.NewValue as ObservableCollection); + + if (args.NewValue != null) + commandBar.RegisterButtonCollection(args.NewValue as ObservableCollection); + } + } + + private void RegisterButtonCollection(ObservableCollection collection) + { + collection.CollectionChanged -= ButtonCollectionChanged; + collection.CollectionChanged += ButtonCollectionChanged; + + InitItems(collection); + } + + private void UnregisterButtonCollection(ObservableCollection collection) + { + collection.CollectionChanged -= ButtonCollectionChanged; + } + + // One time initializer on registration. + private void InitItems(IEnumerable items) + { + foreach (var item in items) + { + var operationText = XamlHelpers.GetOperationString(item.Operation); + + var operationAppBarItem = new AppBarButton() + { + Label = operationText + }; + + ToolTipService.SetToolTip(operationAppBarItem, operationText); + + PrimaryCommands.Add(operationAppBarItem); + } + } + + private void ButtonCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset) + { + PrimaryCommands.Clear(); + SecondaryCommands.Clear(); + } + + if (e.NewItems != null) + { + foreach (var newItem in e.NewItems) + { + if (newItem is MailOperationMenuItem item) + { + var button = new RendererCommandBarItem(item.Operation, Clicked) + { + IsEnabled = item.IsEnabled + }; + + if (!item.IsSecondaryMenuPreferred) + PrimaryCommands.Add(button); + else + SecondaryCommands.Add(button); + } + } + } + } + + private void Clicked(MailOperation operation) + { + OperationClickedCommand?.Execute(operation); + } + + private void DisposeMenuItems() + { + foreach (var item in this.PrimaryCommands) + { + if (item is RendererCommandBarItem rendererCommandBarItem) + { + rendererCommandBarItem.Dispose(); + } + } + + foreach (var item in this.SecondaryCommands) + { + if (item is RendererCommandBarItem rendererCommandBarItem) + { + rendererCommandBarItem.Dispose(); + } + } + } + + public void Dispose() + { + DisposeMenuItems(); + } + } +} diff --git a/Wino.Mail/Controls/SettingsMenuItemControl.cs b/Wino.Mail/Controls/SettingsMenuItemControl.cs new file mode 100644 index 00000000..b37b45f7 --- /dev/null +++ b/Wino.Mail/Controls/SettingsMenuItemControl.cs @@ -0,0 +1,73 @@ +using System; +using System.Windows.Input; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace Wino.Controls +{ + /// + /// Templated button for each setting in Settings Dialog. + /// + public class SettingsMenuItemControl : Control + { + public string Title + { + get { return (string)GetValue(TitleProperty); } + set { SetValue(TitleProperty, value); } + } + + public string Description + { + get { return (string)GetValue(DescriptionProperty); } + set { SetValue(DescriptionProperty, value); } + } + + public FrameworkElement Icon + { + get { return (FrameworkElement)GetValue(IconProperty); } + set { SetValue(IconProperty, value); } + } + + + public ICommand Command + { + get { return (ICommand)GetValue(CommandProperty); } + set { SetValue(CommandProperty, value); } + } + + + + public object CommandParameter + { + get { return (object)GetValue(CommandParameterProperty); } + set { SetValue(CommandParameterProperty, value); } + } + + public bool IsClickable + { + get { return (bool)GetValue(IsClickableProperty); } + set { SetValue(IsClickableProperty, value); } + } + + public bool IsNavigateIconVisible + { + get { return (bool)GetValue(IsNavigateIconVisibleProperty); } + set { SetValue(IsNavigateIconVisibleProperty, value); } + } + + public FrameworkElement SideContent + { + get { return (FrameworkElement)GetValue(SideContentProperty); } + set { SetValue(SideContentProperty, value); } + } + + public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(nameof(CommandParameter), typeof(object), typeof(SettingsMenuItemControl), new PropertyMetadata(null)); + public static readonly DependencyProperty SideContentProperty = DependencyProperty.Register(nameof(SideContent), typeof(FrameworkElement), typeof(SettingsMenuItemControl), new PropertyMetadata(null)); + public static readonly DependencyProperty IsClickableProperty = DependencyProperty.Register(nameof(IsClickable), typeof(bool), typeof(SettingsMenuItemControl), new PropertyMetadata(true)); + public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(nameof(Command), typeof(ICommand), typeof(SettingsMenuItemControl), new PropertyMetadata(null)); + public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), typeof(FrameworkElement), typeof(SettingsMenuItemControl), new PropertyMetadata(null)); + public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(nameof(Description), typeof(string), typeof(SettingsMenuItemControl), new PropertyMetadata(string.Empty)); + public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(SettingsMenuItemControl), new PropertyMetadata(string.Empty)); + public static readonly DependencyProperty IsNavigateIconVisibleProperty = DependencyProperty.Register(nameof(IsNavigateIconVisible), typeof(bool), typeof(SettingsMenuItemControl), new PropertyMetadata(true)); + } +} diff --git a/Wino.Mail/Controls/WinoFontIcon.cs b/Wino.Mail/Controls/WinoFontIcon.cs new file mode 100644 index 00000000..ba5c9cc3 --- /dev/null +++ b/Wino.Mail/Controls/WinoFontIcon.cs @@ -0,0 +1,96 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace Wino.Controls +{ + public enum WinoIconGlyph + { + None, + NewMail, + Google, + Microsoft, + CustomServer, + Archive, + UnArchive, + Reply, + ReplyAll, + LightEditor, + DarkEditor, + Delete, + Move, + Mail, + Draft, + Flag, + ClearFlag, + Folder, + Forward, + Inbox, + MarkRead, + MarkUnread, + Send, + Save, + Sync, + MultiSelect, + Zoom, + Pin, + UnPin, + Ignore, + Star, + CreateFolder, + Junk, + More, + Find, + SpecialFolderInbox, + SpecialFolderStarred, + SpecialFolderImportant, + SpecialFolderSent, + SpecialFolderDraft, + SpecialFolderArchive, + SpecialFolderDeleted, + SpecialFolderJunk, + SpecialFolderChat, + SpecialFolderCategory, + SpecialFolderUnread, + SpecialFolderForums, + SpecialFolderUpdated, + SpecialFolderPersonal, + SpecialFolderPromotions, + SpecialFolderSocial, + SpecialFolderOther, + SpecialFolderMore, + TurnOfNotifications, + EmptyFolder, + Rename, + DontSync + } + + public class WinoFontIcon : FontIcon + { + public WinoIconGlyph Icon + { + get { return (WinoIconGlyph)GetValue(IconProperty); } + set { SetValue(IconProperty, value); } + } + + public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), typeof(WinoIconGlyph), typeof(WinoFontIcon), new PropertyMetadata(WinoIconGlyph.Flag, OnIconChanged)); + + public WinoFontIcon() + { + FontFamily = new Windows.UI.Xaml.Media.FontFamily("ms-appx:///Assets/WinoIcons.ttf#WinoIcons"); + FontSize = 32; + } + + private static void OnIconChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is WinoFontIcon fontIcon) + { + fontIcon.UpdateGlyph(); + } + } + + private void UpdateGlyph() + { + Glyph = ControlConstants.WinoIconFontDictionary[Icon]; + } + } +} diff --git a/Wino.Mail/Controls/WinoFontIconSource.cs b/Wino.Mail/Controls/WinoFontIconSource.cs new file mode 100644 index 00000000..150bf2d8 --- /dev/null +++ b/Wino.Mail/Controls/WinoFontIconSource.cs @@ -0,0 +1,34 @@ +using Windows.UI.Xaml; + +namespace Wino.Controls +{ + public class WinoFontIconSource : Microsoft.UI.Xaml.Controls.FontIconSource + { + public WinoIconGlyph Icon + { + get { return (WinoIconGlyph)GetValue(IconProperty); } + set { SetValue(IconProperty, value); } + } + + public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), typeof(WinoIconGlyph), typeof(WinoFontIconSource), new PropertyMetadata(WinoIconGlyph.Flag, OnIconChanged)); + + public WinoFontIconSource() + { + FontFamily = new Windows.UI.Xaml.Media.FontFamily("ms-appx:///Assets/WinoIcons.ttf#WinoIcons"); + FontSize = 32; + } + + private static void OnIconChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is WinoFontIconSource fontIcon) + { + fontIcon.UpdateGlyph(); + } + } + + private void UpdateGlyph() + { + Glyph = ControlConstants.WinoIconFontDictionary[Icon]; + } + } +} diff --git a/Wino.Mail/Controls/WinoInfoBar.cs b/Wino.Mail/Controls/WinoInfoBar.cs new file mode 100644 index 00000000..6f2e288e --- /dev/null +++ b/Wino.Mail/Controls/WinoInfoBar.cs @@ -0,0 +1,91 @@ +using System; +using System.Numerics; +using CommunityToolkit.WinUI.Animations; +using Microsoft.UI.Xaml.Controls; +using Windows.UI.Xaml; +using Wino.Core.Domain.Enums; + +namespace Wino.Controls +{ + public class WinoInfoBar : InfoBar + { + public static readonly DependencyProperty AnimationTypeProperty = DependencyProperty.Register(nameof(AnimationType), typeof(InfoBarAnimationType), typeof(WinoInfoBar), new PropertyMetadata(InfoBarAnimationType.SlideFromRightToLeft)); + public static readonly DependencyProperty DismissIntervalProperty = DependencyProperty.Register(nameof(DismissInterval), typeof(int), typeof(WinoInfoBar), new PropertyMetadata(5, new PropertyChangedCallback(OnDismissIntervalChanged))); + + public InfoBarAnimationType AnimationType + { + get { return (InfoBarAnimationType)GetValue(AnimationTypeProperty); } + set { SetValue(AnimationTypeProperty, value); } + } + + public int DismissInterval + { + get { return (int)GetValue(DismissIntervalProperty); } + set { SetValue(DismissIntervalProperty, value); } + } + + private readonly DispatcherTimer _dispatcherTimer = new DispatcherTimer(); + + public WinoInfoBar() + { + RegisterPropertyChangedCallback(IsOpenProperty, IsOpenChanged); + + _dispatcherTimer.Interval = TimeSpan.FromSeconds(DismissInterval); + } + + private static void OnDismissIntervalChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is WinoInfoBar bar) + { + bar.UpdateInterval(bar.DismissInterval); + } + } + + private void UpdateInterval(int seconds) => _dispatcherTimer.Interval = TimeSpan.FromSeconds(seconds); + + private async void IsOpenChanged(DependencyObject sender, DependencyProperty dp) + { + if (sender is WinoInfoBar control) + { + // Message shown. + if (!control.IsOpen) return; + + Opacity = 1; + + _dispatcherTimer.Stop(); + + _dispatcherTimer.Tick -= TimerTick; + _dispatcherTimer.Tick += TimerTick; + + _dispatcherTimer.Start(); + + // Slide from right. + if (AnimationType == InfoBarAnimationType.SlideFromRightToLeft) + { + await AnimationBuilder.Create().Translation(new Vector3(0, 0, 0), new Vector3(150, 0, 0), null, TimeSpan.FromSeconds(0.5)).StartAsync(this); + } + else if (AnimationType == InfoBarAnimationType.SlideFromBottomToTop) + { + await AnimationBuilder.Create().Translation(new Vector3(0, 0, 0), new Vector3(0, 50, 0), null, TimeSpan.FromSeconds(0.5)).StartAsync(this); + } + } + } + + private async void TimerTick(object sender, object e) + { + _dispatcherTimer.Stop(); + _dispatcherTimer.Tick -= TimerTick; + + if (AnimationType == InfoBarAnimationType.SlideFromRightToLeft) + { + await AnimationBuilder.Create().Translation(new Vector3((float)ActualWidth, 0, 0), new Vector3(0, 0, 0), null, TimeSpan.FromSeconds(0.5)).StartAsync(this); + } + else if (AnimationType == InfoBarAnimationType.SlideFromBottomToTop) + { + await AnimationBuilder.Create().Translation(new Vector3(0, (float)ActualHeight, 0), new Vector3(0, 0, 0), null, TimeSpan.FromSeconds(0.5)).StartAsync(this); + } + + IsOpen = false; + } + } +} diff --git a/Wino.Mail/Controls/WinoNavigationViewItem.cs b/Wino.Mail/Controls/WinoNavigationViewItem.cs new file mode 100644 index 00000000..e28070b8 --- /dev/null +++ b/Wino.Mail/Controls/WinoNavigationViewItem.cs @@ -0,0 +1,47 @@ +using System.Numerics; +using Microsoft.UI.Xaml.Controls; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Hosting; + +namespace Wino.Controls +{ + public class WinoNavigationViewItem : NavigationViewItem + { + public bool IsDraggingItemOver + { + get { return (bool)GetValue(IsDraggingItemOverProperty); } + set { SetValue(IsDraggingItemOverProperty, value); } + } + + public static readonly DependencyProperty IsDraggingItemOverProperty = DependencyProperty.Register(nameof(IsDraggingItemOver), typeof(bool), typeof(WinoNavigationViewItem), new PropertyMetadata(false, OnIsDraggingItemOverChanged)); + + private static void OnIsDraggingItemOverChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is WinoNavigationViewItem control) + control.UpdateDragEnterState(); + } + + private void UpdateDragEnterState() + { + // TODO: Add animation. Maybe after overriding DragUI in shell? + + //if (IsDraggingItemOver) + //{ + // ScaleAnimation(new System.Numerics.Vector3(1.2f, 1.2f, 1.2f)); + //} + //else + //{ + // ScaleAnimation(new System.Numerics.Vector3(1f, 1f, 1f)); + //} + } + + private void ScaleAnimation(Vector3 vector) + { + if (this.Content is UIElement content) + { + var visual = ElementCompositionPreview.GetElementVisual(content); + visual.Scale = vector; + } + } + } +} diff --git a/Wino.Mail/Controls/WinoPivotControl.xaml b/Wino.Mail/Controls/WinoPivotControl.xaml new file mode 100644 index 00000000..a524e6d3 --- /dev/null +++ b/Wino.Mail/Controls/WinoPivotControl.xaml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Controls/WinoPivotControl.xaml.cs b/Wino.Mail/Controls/WinoPivotControl.xaml.cs new file mode 100644 index 00000000..975871bc --- /dev/null +++ b/Wino.Mail/Controls/WinoPivotControl.xaml.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Numerics; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading.Tasks; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI; +using Windows.UI.Composition; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; +using Wino.Extensions; + +namespace Wino.Controls +{ + // TODO: Memory leak with FolderPivot bindings. + public sealed partial class WinoPivotControl : UserControl + { + private Compositor _compositor; + private ShapeVisual _shapeVisual; + private CompositionSpriteShape _spriteShape; + private CompositionRoundedRectangleGeometry _roundedRectangle; + + public event EventHandler SelectionChanged; + + public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(nameof(SelectedItem), typeof(object), typeof(WinoPivotControl), new PropertyMetadata(null)); + public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(nameof(ItemsSource), typeof(object), typeof(WinoPivotControl), new PropertyMetadata(null)); + public static readonly DependencyProperty SelectorPipeColorProperty = DependencyProperty.Register(nameof(SelectorPipeColor), typeof(SolidColorBrush), typeof(WinoPivotControl), new PropertyMetadata(Colors.Transparent, OnSelectorPipeColorChanged)); + public static readonly DependencyProperty DataTemplateProperty = DependencyProperty.Register(nameof(DataTemplate), typeof(DataTemplate), typeof(WinoPivotControl), new PropertyMetadata(null)); + + public DataTemplate DataTemplate + { + get { return (DataTemplate)GetValue(DataTemplateProperty); } + set { SetValue(DataTemplateProperty, value); } + } + + public SolidColorBrush SelectorPipeColor + { + get { return (SolidColorBrush)GetValue(SelectorPipeColorProperty); } + set { SetValue(SelectorPipeColorProperty, value); } + } + + public object SelectedItem + { + get { return (object)GetValue(SelectedItemProperty); } + set { SetValue(SelectedItemProperty, value); } + } + + public object ItemsSource + { + get { return (object)GetValue(ItemsSourceProperty); } + set { SetValue(ItemsSourceProperty, value); } + } + + private static void OnSelectorPipeColorChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is WinoPivotControl control) + { + control.UpdateSelectorPipeColor(); + } + } + + private void UpdateSelectorPipeColor() + { + if (_spriteShape != null && _compositor != null) + { + _spriteShape.FillBrush = _compositor.CreateColorBrush(SelectorPipeColor.Color); + } + } + + private void CreateSelectorVisuals() + { + _compositor = this.Visual().Compositor; + + _roundedRectangle = _compositor.CreateRoundedRectangleGeometry(); + _roundedRectangle.CornerRadius = new Vector2(3, 3); + + _spriteShape = _compositor.CreateSpriteShape(_roundedRectangle); + _spriteShape.CenterPoint = new Vector2(100, 100); + + _shapeVisual = _compositor.CreateShapeVisual(); + + _shapeVisual.Shapes.Clear(); + _shapeVisual.Shapes.Add(_spriteShape); + + SelectorPipe.SetChildVisual(_shapeVisual); + + _shapeVisual.EnableImplicitAnimation(VisualPropertyType.Size, 400); + } + + public WinoPivotControl() + { + this.InitializeComponent(); + + CreateSelectorVisuals(); + } + + private bool IsContainerPresent() + { + return SelectedItem != null && PivotHeaders.ContainerFromItem(SelectedItem) != null; + } + + private void PivotHeaders_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + UpdateVisuals(); + + SelectionChanged?.Invoke(sender, e); + } + + private void UpdateVisuals() + { + MoveSelector(); + } + + private void UpdateSelectorVisibility() + { + SelectorPipe.Visibility = IsContainerPresent() ? Visibility.Visible : Visibility.Collapsed; + } + + private async void MoveSelector() + { + if (PivotHeaders.SelectedItem != null) + { + // Get selected item container position + // TODO: It's bad... + while(PivotHeaders.ContainerFromItem(PivotHeaders.SelectedItem) == null) + { + await Task.Delay(100); + } + + UpdateSelectorVisibility(); + + var container = PivotHeaders.ContainerFromItem(PivotHeaders.SelectedItem) as FrameworkElement; + + if (container != null) + { + var transformToVisual = container.TransformToVisual(this); + Point screenCoords = transformToVisual.TransformPoint(new Point(0, 0)); + + float actualWidth = 0, leftMargin = 0, translateX = 0; + + leftMargin = (float)(screenCoords.X); + + if (PivotHeaders.Items.Count > 1) + { + // Multiple items, pipe is centered. + + actualWidth = (float)(container.ActualWidth + 12) / 2; + translateX = leftMargin - 10 + (actualWidth / 2); + } + else + { + actualWidth = (float)(container.ActualWidth) - 12; + translateX = leftMargin + 4; + } + + SelectorPipe.Width = actualWidth; + SelectorPipe.Translation = new Vector3(translateX, 0, 0); + } + else + { + Debug.WriteLine("Container null"); + } + } + } + + private void SelectorPipeSizeChanged(object sender, SizeChangedEventArgs e) + { + _roundedRectangle.Size = e.NewSize.ToVector2(); + _shapeVisual.Size = e.NewSize.ToVector2(); + } + + private void ControlUnloaded(object sender, RoutedEventArgs e) + { + //PivotHeaders.SelectionChanged -= PivotHeaders_SelectionChanged; + //PivotHeaders.SelectedItem = null; + + //SelectedItem = null; + //ItemsSource = null; + } + + private void ControlLoaded(object sender, RoutedEventArgs e) + { + // Bindings.Update(); + } + } +} diff --git a/Wino.Mail/Controls/WinoSwipeControlItems.cs b/Wino.Mail/Controls/WinoSwipeControlItems.cs new file mode 100644 index 00000000..eb5cef74 --- /dev/null +++ b/Wino.Mail/Controls/WinoSwipeControlItems.cs @@ -0,0 +1,82 @@ +using System.Linq; +using Microsoft.UI.Xaml.Controls; +using Windows.UI.Xaml; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.MailItem; +using Wino.Helpers; +using Wino.Mail.ViewModels.Data; + +namespace Wino.Controls +{ + public class WinoSwipeControlItems : SwipeItems + { + public static readonly DependencyProperty SwipeOperationProperty = DependencyProperty.Register(nameof(SwipeOperation), typeof(MailOperation), typeof(WinoSwipeControlItems), new PropertyMetadata(default(MailOperation), new PropertyChangedCallback(OnItemsChanged))); + public static readonly DependencyProperty MailItemProperty = DependencyProperty.Register(nameof(MailItem), typeof(IMailItem), typeof(WinoSwipeControlItems), new PropertyMetadata(null)); + + public IMailItem MailItem + { + get { return (IMailItem)GetValue(MailItemProperty); } + set { SetValue(MailItemProperty, value); } + } + + + public MailOperation SwipeOperation + { + get { return (MailOperation)GetValue(SwipeOperationProperty); } + set { SetValue(SwipeOperationProperty, value); } + } + + private static void OnItemsChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is WinoSwipeControlItems control) + { + control.BuildSwipeItems(); + } + } + + private void BuildSwipeItems() + { + this.Clear(); + + var swipeItem = GetSwipeItem(SwipeOperation); + + this.Add(swipeItem); + } + + private SwipeItem GetSwipeItem(MailOperation operation) + { + if (MailItem == null) return null; + + var finalOperation = operation; + + bool isSingleItem = MailItem is MailItemViewModel; + + if (isSingleItem) + { + var singleItem = MailItem as MailItemViewModel; + + if (operation == MailOperation.MarkAsRead && singleItem.IsRead) + finalOperation = MailOperation.MarkAsUnread; + else if (operation == MailOperation.MarkAsUnread && !singleItem.IsRead) + finalOperation = MailOperation.MarkAsRead; + } + else + { + var threadItem = MailItem as ThreadMailItemViewModel; + + if (operation == MailOperation.MarkAsRead && threadItem.ThreadItems.All(a => a.IsRead)) + finalOperation = MailOperation.MarkAsUnread; + else if (operation == MailOperation.MarkAsUnread && threadItem.ThreadItems.All(a => !a.IsRead)) + finalOperation = MailOperation.MarkAsRead; + } + + var item = new SwipeItem() + { + IconSource = new WinoFontIconSource() { Icon = XamlHelpers.GetWinoIconGlyph(finalOperation) }, + Text = XamlHelpers.GetOperationString(finalOperation), + }; + + return item; + } + } +} diff --git a/Wino.Mail/Converters/ReverseBooleanConverter.cs b/Wino.Mail/Converters/ReverseBooleanConverter.cs new file mode 100644 index 00000000..d706e0a5 --- /dev/null +++ b/Wino.Mail/Converters/ReverseBooleanConverter.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Xaml.Data; + +namespace Wino.Converters +{ + public class ReverseBooleanConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is bool boolval) + return !boolval; + + return false; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/Wino.Mail/Converters/ReverseBooleanToVisibilityConverter.cs b/Wino.Mail/Converters/ReverseBooleanToVisibilityConverter.cs new file mode 100644 index 00000000..04eaf98d --- /dev/null +++ b/Wino.Mail/Converters/ReverseBooleanToVisibilityConverter.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Data; + +namespace Wino.Converters +{ + public class ReverseBooleanToVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + return ((bool)value) ? Visibility.Collapsed : Visibility.Visible; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/Wino.Mail/Dialogs/AccountCreationDialog.xaml b/Wino.Mail/Dialogs/AccountCreationDialog.xaml new file mode 100644 index 00000000..98cac4d6 --- /dev/null +++ b/Wino.Mail/Dialogs/AccountCreationDialog.xaml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Dialogs/AccountCreationDialog.xaml.cs b/Wino.Mail/Dialogs/AccountCreationDialog.xaml.cs new file mode 100644 index 00000000..d100a983 --- /dev/null +++ b/Wino.Mail/Dialogs/AccountCreationDialog.xaml.cs @@ -0,0 +1,33 @@ +using Wino.Core.Domain.Enums; +using Wino.Helpers; + +namespace Wino.Dialogs +{ + public sealed partial class AccountCreationDialog : BaseAccountCreationDialog + { + public AccountCreationDialog() + { + InitializeComponent(); + } + + public override void UpdateState() + { + switch (State) + { + case AccountCreationDialogState.SigningIn: + StatusText.Text = "Account information is being saved."; + DialogIcon.Data = XamlHelpers.GetPathIcon("SavingAccountPathIcon"); + break; + case AccountCreationDialogState.PreparingFolders: + StatusText.Text = "We are getting folder information at the moment."; + DialogIcon.Data = XamlHelpers.GetPathIcon("PreparingFoldersPathIcon"); + break; + case AccountCreationDialogState.Completed: + StatusText.Text = "All done."; + break; + default: + break; + } + } + } +} diff --git a/Wino.Mail/Dialogs/AccountEditDialog.xaml b/Wino.Mail/Dialogs/AccountEditDialog.xaml new file mode 100644 index 00000000..34cba8f8 --- /dev/null +++ b/Wino.Mail/Dialogs/AccountEditDialog.xaml @@ -0,0 +1,19 @@ + + + + + + diff --git a/Wino.Mail/Dialogs/AccountEditDialog.xaml.cs b/Wino.Mail/Dialogs/AccountEditDialog.xaml.cs new file mode 100644 index 00000000..e158b8b3 --- /dev/null +++ b/Wino.Mail/Dialogs/AccountEditDialog.xaml.cs @@ -0,0 +1,22 @@ +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain.Entities; + +namespace Wino.Dialogs +{ + public sealed partial class AccountEditDialog : ContentDialog + { + public MailAccount Account { get; private set; } + public bool IsSaved { get; set; } + + public AccountEditDialog(MailAccount account) + { + InitializeComponent(); + Account = account; + } + + private void SaveClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + IsSaved = true; + } + } +} diff --git a/Wino.Mail/Dialogs/AccountPickerDialog.xaml b/Wino.Mail/Dialogs/AccountPickerDialog.xaml new file mode 100644 index 00000000..f06da6d2 --- /dev/null +++ b/Wino.Mail/Dialogs/AccountPickerDialog.xaml @@ -0,0 +1,19 @@ + + + + diff --git a/Wino.Mail/Dialogs/AccountPickerDialog.xaml.cs b/Wino.Mail/Dialogs/AccountPickerDialog.xaml.cs new file mode 100644 index 00000000..9b4c46c4 --- /dev/null +++ b/Wino.Mail/Dialogs/AccountPickerDialog.xaml.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain.Entities; + +namespace Wino.Dialogs +{ + public sealed partial class AccountPickerDialog : ContentDialog + { + public MailAccount PickedAccount { get; set; } + + public List AvailableAccounts { get; set; } + + public AccountPickerDialog(List availableAccounts) + { + AvailableAccounts = availableAccounts; + + InitializeComponent(); + } + + private void AccountClicked(object sender, ItemClickEventArgs e) + { + PickedAccount = e.ClickedItem as MailAccount; + + Hide(); + } + } +} diff --git a/Wino.Mail/Dialogs/BaseAccountCreationDialog.cs b/Wino.Mail/Dialogs/BaseAccountCreationDialog.cs new file mode 100644 index 00000000..9c928793 --- /dev/null +++ b/Wino.Mail/Dialogs/BaseAccountCreationDialog.cs @@ -0,0 +1,52 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Dialogs +{ + public abstract class BaseAccountCreationDialog : ContentDialog, IAccountCreationDialog + { + public AccountCreationDialogState State + { + get { return (AccountCreationDialogState)GetValue(StateProperty); } + set { SetValue(StateProperty, value); } + } + + public static readonly DependencyProperty StateProperty = DependencyProperty.Register(nameof(State), typeof(AccountCreationDialogState), typeof(BaseAccountCreationDialog), new PropertyMetadata(AccountCreationDialogState.Idle, OnStateChanged)); + + // Prevent users from dismissing it by ESC key. + private void DialogClosing(ContentDialog sender, ContentDialogClosingEventArgs args) + { + if (args.Result == ContentDialogResult.None) + { + args.Cancel = true; + } + } + + public void ShowDialog() + { + _ = ShowAsync(); + } + + public void Complete() + { + State = AccountCreationDialogState.Completed; + + // Unregister from closing event. + Closing -= DialogClosing; + + Hide(); + } + + private static void OnStateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is BaseAccountCreationDialog dialog) + { + dialog.UpdateState(); + } + } + + public abstract void UpdateState(); + } +} diff --git a/Wino.Mail/Dialogs/ConfirmationDialog.xaml b/Wino.Mail/Dialogs/ConfirmationDialog.xaml new file mode 100644 index 00000000..5f7c5070 --- /dev/null +++ b/Wino.Mail/Dialogs/ConfirmationDialog.xaml @@ -0,0 +1,29 @@ + + + + 250 + 500 + 200 + 756 + + + + diff --git a/Wino.Mail/Dialogs/ConfirmationDialog.xaml.cs b/Wino.Mail/Dialogs/ConfirmationDialog.xaml.cs new file mode 100644 index 00000000..a2257157 --- /dev/null +++ b/Wino.Mail/Dialogs/ConfirmationDialog.xaml.cs @@ -0,0 +1,80 @@ +using System.Threading.Tasks; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Dialogs +{ + public sealed partial class ConfirmationDialog : ContentDialog, IConfirmationDialog + { + private TaskCompletionSource _completionSource; + + #region Dependency Properties + + public string DialogTitle + { + get { return (string)GetValue(DialogTitleProperty); } + set { SetValue(DialogTitleProperty, value); } + } + + public static readonly DependencyProperty DialogTitleProperty = DependencyProperty.Register(nameof(DialogTitle), typeof(string), typeof(ConfirmationDialog), new PropertyMetadata(string.Empty)); + + public string Message + { + get { return (string)GetValue(MessageProperty); } + set { SetValue(MessageProperty, value); } + } + + public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(nameof(Message), typeof(string), typeof(ConfirmationDialog), new PropertyMetadata(string.Empty)); + + public string ApproveButtonTitle + { + get { return (string)GetValue(ApproveButtonTitleProperty); } + set { SetValue(ApproveButtonTitleProperty, value); } + } + + public static readonly DependencyProperty ApproveButtonTitleProperty = DependencyProperty.Register(nameof(ApproveButtonTitle), typeof(string), typeof(ConfirmationDialog), new PropertyMetadata(string.Empty)); + + #endregion + + private bool _isApproved; + public ConfirmationDialog() + { + InitializeComponent(); + } + + public async Task ShowDialogAsync(string title, string message, string approveButtonTitle) + { + _completionSource = new TaskCompletionSource(); + + DialogTitle = title; + Message = message; + ApproveButtonTitle = approveButtonTitle; + +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + ShowAsync(); +#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + + return await _completionSource.Task; + } + + private void DialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args) + { + _completionSource.TrySetResult(_isApproved); + } + + private void ApproveClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + _isApproved = true; + + Hide(); + } + + private void CancelClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + _isApproved = false; + + Hide(); + } + } +} diff --git a/Wino.Mail/Dialogs/CustomThemeBuilderDialog.xaml b/Wino.Mail/Dialogs/CustomThemeBuilderDialog.xaml new file mode 100644 index 00000000..89e46480 --- /dev/null +++ b/Wino.Mail/Dialogs/CustomThemeBuilderDialog.xaml @@ -0,0 +1,85 @@ + + + + 600 + 900 + 200 + 756 + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Dialogs/CustomThemeBuilderDialog.xaml.cs b/Wino.Mail/Dialogs/CustomThemeBuilderDialog.xaml.cs new file mode 100644 index 00000000..633fce37 --- /dev/null +++ b/Wino.Mail/Dialogs/CustomThemeBuilderDialog.xaml.cs @@ -0,0 +1,65 @@ +using System; +using CommunityToolkit.WinUI.Helpers; +using Microsoft.Extensions.DependencyInjection; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; +using Wino.Core.Domain.Interfaces; +using Wino.Services; + +namespace Wino.Dialogs +{ + public sealed partial class CustomThemeBuilderDialog : ContentDialog + { + public byte[] WallpaperData { get; private set; } + public string AccentColor { get; private set; } + + private IThemeService _themeService; + + public CustomThemeBuilderDialog() + { + InitializeComponent(); + + _themeService = App.Current.Services.GetService(); + } + + private async void ApplyClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + if (Array.Empty() == WallpaperData) + return; + + var deferal = args.GetDeferral(); + + try + { + await _themeService.CreateNewCustomThemeAsync(ThemeNameBox.Text, AccentColor, WallpaperData); + } + catch (Exception exception) + { + ErrorTextBlock.Text = exception.Message; + } + finally + { + deferal.Complete(); + } + } + + private async void BrowseWallpaperClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e) + { + var dialogService = App.Current.Services.GetService(); + + var pickedFileData = await dialogService.PickWindowsFileContentAsync(".jpg", ".png"); + + if (pickedFileData == Array.Empty()) return; + + IsPrimaryButtonEnabled = true; + + WallpaperData = pickedFileData; + } + + private void PickerColorChanged(Microsoft.UI.Xaml.Controls.ColorPicker sender, Microsoft.UI.Xaml.Controls.ColorChangedEventArgs args) + { + PreviewAccentColorGrid.Background = new SolidColorBrush(args.NewColor); + AccentColor = args.NewColor.ToHex(); + } + } +} diff --git a/Wino.Mail/Dialogs/MoveMailDialog.xaml b/Wino.Mail/Dialogs/MoveMailDialog.xaml new file mode 100644 index 00000000..970b300d --- /dev/null +++ b/Wino.Mail/Dialogs/MoveMailDialog.xaml @@ -0,0 +1,59 @@ + + + + 600 + 600 + 756 + 756 + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Dialogs/MoveMailDialog.xaml.cs b/Wino.Mail/Dialogs/MoveMailDialog.xaml.cs new file mode 100644 index 00000000..4c0d92a6 --- /dev/null +++ b/Wino.Mail/Dialogs/MoveMailDialog.xaml.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain; +using Wino.Core.Domain.Models.Folders; + +namespace Wino.Dialogs +{ + public sealed partial class MoveMailDialog : ContentDialog + { + public IMailItemFolder SelectedFolder + { + get { return (IMailItemFolder)GetValue(SelectedFolderProperty); } + set { SetValue(SelectedFolderProperty, value); } + } + + public static readonly DependencyProperty SelectedFolderProperty = DependencyProperty.Register(nameof(SelectedFolder), typeof(IMailItemFolder), typeof(MoveMailDialog), new PropertyMetadata(null, OnSelectedFolderChanged)); + + + public List FolderList { get; set; } + + public MoveMailDialog(List allFolders) + { + InitializeComponent(); + + if (allFolders == null) return; + + FolderList = allFolders; + } + + private static void OnSelectedFolderChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is MoveMailDialog dialog) + { + dialog.VerifySelection(); + } + } + + private void VerifySelection() + { + if (SelectedFolder != null) + { + // Don't select non-move capable folders like Categories or More. + + if (!SelectedFolder.IsMoveTarget) + { + // Warn users for only proper mail folders. Not ghost folders. + InvalidFolderText.Visibility = Visibility.Visible; + InvalidFolderText.Text = string.Format(Translator.MoveMailDialog_InvalidFolderMessage, SelectedFolder.FolderName); + + if (FolderTreeView.SelectedItem != null) + { + // Toggle the expansion for the selected container if available. + // I don't like the expand arrow touch area. It's better this way. + + if (FolderTreeView.ContainerFromItem(FolderTreeView.SelectedItem) is Microsoft.UI.Xaml.Controls.TreeViewItem container) + { + container.IsExpanded = !container.IsExpanded; + } + } + SelectedFolder = null; + } + else + { + Hide(); + } + } + } + + private void CancelClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + Hide(); + } + } +} diff --git a/Wino.Mail/Dialogs/NewAccountDialog.xaml b/Wino.Mail/Dialogs/NewAccountDialog.xaml new file mode 100644 index 00000000..332c1447 --- /dev/null +++ b/Wino.Mail/Dialogs/NewAccountDialog.xaml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Dialogs/NewAccountDialog.xaml.cs b/Wino.Mail/Dialogs/NewAccountDialog.xaml.cs new file mode 100644 index 00000000..7d36d300 --- /dev/null +++ b/Wino.Mail/Dialogs/NewAccountDialog.xaml.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Accounts; + +namespace Wino.Dialogs +{ + public sealed partial class NewAccountDialog : ContentDialog + { + /// + /// Gets or sets current selected mail provider in the dialog. + /// + public ProviderDetail SelectedMailProvider + { + get { return (ProviderDetail)GetValue(SelectedMailProviderProperty); } + set { SetValue(SelectedMailProviderProperty, value); } + } + + public static readonly DependencyProperty SelectedMailProviderProperty = DependencyProperty.Register(nameof(SelectedMailProvider), typeof(ProviderDetail), typeof(NewAccountDialog), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedProviderChanged))); + + // List of available mail providers for now. + + public List Providers { get; set; } + + public Tuple AccountInformationTuple = null; + + public NewAccountDialog() + { + InitializeComponent(); + + // AccountColorPicker.Color = Colors.Blue; + } + + private static void OnSelectedProviderChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is NewAccountDialog dialog) + dialog.ValidateCreateButton(); + } + + private void CancelClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + Hide(); + } + + private void CreateClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + ValidateCreateButton(); + + if (IsSecondaryButtonEnabled) + { + AccountInformationTuple = new Tuple(AccountNameTextbox.Text.Trim(), SelectedMailProvider.Type); + Hide(); + } + } + + private void AccountNameChanged(object sender, TextChangedEventArgs e) + { + ValidateCreateButton(); + } + + // Returns whether we can create account or not. + private void ValidateCreateButton() + { + bool shouldEnable = SelectedMailProvider != null + && SelectedMailProvider.IsSupported + && !string.IsNullOrEmpty(AccountNameTextbox.Text); + + IsPrimaryButtonEnabled = shouldEnable; + } + + private void DialogOpened(ContentDialog sender, ContentDialogOpenedEventArgs args) + { + ValidateCreateButton(); + } + } +} diff --git a/Wino.Mail/Dialogs/NewImapSetupDialog.xaml b/Wino.Mail/Dialogs/NewImapSetupDialog.xaml new file mode 100644 index 00000000..09ffaba7 --- /dev/null +++ b/Wino.Mail/Dialogs/NewImapSetupDialog.xaml @@ -0,0 +1,25 @@ + + + + 0,0,0,0 + + 1920 + + + + + diff --git a/Wino.Mail/Dialogs/NewImapSetupDialog.xaml.cs b/Wino.Mail/Dialogs/NewImapSetupDialog.xaml.cs new file mode 100644 index 00000000..e2e04581 --- /dev/null +++ b/Wino.Mail/Dialogs/NewImapSetupDialog.xaml.cs @@ -0,0 +1,79 @@ +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.Messaging; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media.Animation; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Messages.Mails; +using Wino.Views.ImapSetup; + +namespace Wino.Dialogs +{ + public enum ImapSetupState + { + Welcome, + AutoDiscovery, + TestingConnection, + PreparingFolder + } + + public sealed partial class NewImapSetupDialog : ContentDialog, + IRecipient, + IRecipient, + IRecipient, + ICustomServerAccountCreationDialog + { + private TaskCompletionSource _getServerInfoTaskCompletionSource = new TaskCompletionSource(); + + private bool isDismissRequested = false; + + public NewImapSetupDialog() + { + InitializeComponent(); + + ImapFrame.Navigate(typeof(WelcomeImapSetupPage), null, new DrillInNavigationTransitionInfo()); + } + + // Not used for now. + public AccountCreationDialogState State { get; set; } + + public void Complete() + { + if (!_getServerInfoTaskCompletionSource.Task.IsCompleted) + _getServerInfoTaskCompletionSource.TrySetResult(null); + + isDismissRequested = true; + + Hide(); + } + + public Task GetCustomServerInformationAsync() => _getServerInfoTaskCompletionSource.Task; + + public void Receive(ImapSetupBackNavigationRequested message) + { + ImapFrame.Navigate(message.PageType, message.Parameter, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromLeft }); + } + + public void Receive(ImapSetupNavigationRequested message) + { + ImapFrame.Navigate(message.PageType, message.Parameter, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight }); + } + + public void Receive(ImapSetupDismissRequested message) => _getServerInfoTaskCompletionSource.TrySetResult(message.CompletedServerInformation); + + public void ShowDialog() => _ = ShowAsync(); + + public void ShowPreparingFolders() + { + ImapFrame.Navigate(typeof(PreparingImapFoldersPage), new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromLeft }); + } + + private void ImapSetupDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args) => WeakReferenceMessenger.Default.UnregisterAll(this); + + private void ImapSetupDialogOpened(ContentDialog sender, ContentDialogOpenedEventArgs args) => WeakReferenceMessenger.Default.RegisterAll(this); + + // Don't hide the dialog unless dismiss is requested from the inner pages specifically. + private void OnDialogClosing(ContentDialog sender, ContentDialogClosingEventArgs args) => args.Cancel = !isDismissRequested; + } +} diff --git a/Wino.Mail/Dialogs/StoreRatingDialog.xaml b/Wino.Mail/Dialogs/StoreRatingDialog.xaml new file mode 100644 index 00000000..be839312 --- /dev/null +++ b/Wino.Mail/Dialogs/StoreRatingDialog.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + diff --git a/Wino.Mail/Dialogs/StoreRatingDialog.xaml.cs b/Wino.Mail/Dialogs/StoreRatingDialog.xaml.cs new file mode 100644 index 00000000..eb6a771b --- /dev/null +++ b/Wino.Mail/Dialogs/StoreRatingDialog.xaml.cs @@ -0,0 +1,21 @@ +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Dialogs +{ + public sealed partial class StoreRatingDialog : ContentDialog, IStoreRatingDialog + { + public bool DontAskAgain { get; set; } + public bool RateWinoClicked { get; set; } + + public StoreRatingDialog() + { + this.InitializeComponent(); + } + + private void RateClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + RateWinoClicked = true; + } + } +} diff --git a/Wino.Mail/Dialogs/SystemFolderConfigurationDialog.xaml b/Wino.Mail/Dialogs/SystemFolderConfigurationDialog.xaml new file mode 100644 index 00000000..1e634077 --- /dev/null +++ b/Wino.Mail/Dialogs/SystemFolderConfigurationDialog.xaml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Dialogs/SystemFolderConfigurationDialog.xaml.cs b/Wino.Mail/Dialogs/SystemFolderConfigurationDialog.xaml.cs new file mode 100644 index 00000000..70aee9d5 --- /dev/null +++ b/Wino.Mail/Dialogs/SystemFolderConfigurationDialog.xaml.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Exceptions; + +namespace Wino.Dialogs +{ + public sealed partial class SystemFolderConfigurationDialog : ContentDialog + { + private bool canDismissDialog = false; + + public SystemFolderConfiguration Configuration { get; set; } + public List AvailableFolders { get; } + + public MailItemFolder Sent { get; set; } + public MailItemFolder Draft { get; set; } + public MailItemFolder Archive { get; set; } + public MailItemFolder Junk { get; set; } + public MailItemFolder Trash { get; set; } + + public SystemFolderConfigurationDialog(List availableFolders) + { + InitializeComponent(); + + AvailableFolders = availableFolders; + + Sent = AvailableFolders.Find(a => a.SpecialFolderType == Core.Domain.Enums.SpecialFolderType.Sent); + Draft = AvailableFolders.Find(a => a.SpecialFolderType == Core.Domain.Enums.SpecialFolderType.Draft); + Archive = AvailableFolders.Find(a => a.SpecialFolderType == Core.Domain.Enums.SpecialFolderType.Archive); + Junk = AvailableFolders.Find(a => a.SpecialFolderType == Core.Domain.Enums.SpecialFolderType.Junk); + Trash = AvailableFolders.Find(a => a.SpecialFolderType == Core.Domain.Enums.SpecialFolderType.Deleted); + } + + private void DialogClosing(ContentDialog sender, ContentDialogClosingEventArgs args) + { + args.Cancel = !canDismissDialog; + } + + private void CancelClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args) + => canDismissDialog = true; + + private void SaveClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + ValidationErrorTextBlock.Text = string.Empty; + + var allSpecialFolders = new List() + { + Sent, Draft, Archive, Trash, Junk + }; + + if (allSpecialFolders.Any(a => a != null && a.SpecialFolderType == SpecialFolderType.Inbox)) + ValidationErrorTextBlock.Text = Translator.SystemFolderConfigDialogValidation_InboxSelected; + + if (new HashSet(allSpecialFolders.Where(a => a != null).Select(x => x.Id)).Count != allSpecialFolders.Where(a => a != null).Count()) + ValidationErrorTextBlock.Text = Translator.SystemFolderConfigDialogValidation_DuplicateSystemFolders; + + // Check if we can save. + if (string.IsNullOrEmpty(ValidationErrorTextBlock.Text)) + { + var configuration = new SystemFolderConfiguration(Sent, Draft, Archive, Trash, Junk); + + canDismissDialog = true; + Configuration = configuration; + } + } + } +} diff --git a/Wino.Mail/Dialogs/TextInputDialog.xaml b/Wino.Mail/Dialogs/TextInputDialog.xaml new file mode 100644 index 00000000..ec60be81 --- /dev/null +++ b/Wino.Mail/Dialogs/TextInputDialog.xaml @@ -0,0 +1,29 @@ + + + + 400 + 400 + 200 + 756 + + + + + + + diff --git a/Wino.Mail/Dialogs/TextInputDialog.xaml.cs b/Wino.Mail/Dialogs/TextInputDialog.xaml.cs new file mode 100644 index 00000000..38c841ae --- /dev/null +++ b/Wino.Mail/Dialogs/TextInputDialog.xaml.cs @@ -0,0 +1,40 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace Wino.Dialogs +{ + public sealed partial class TextInputDialog : ContentDialog + { + public bool? HasInput { get; set; } + + public string CurrentInput + { + get { return (string)GetValue(CurrentInputProperty); } + set { SetValue(CurrentInputProperty, value); } + } + + public static readonly DependencyProperty CurrentInputProperty = DependencyProperty.Register(nameof(CurrentInput), typeof(string), typeof(TextInputDialog), new PropertyMetadata(string.Empty)); + + public TextInputDialog() + { + InitializeComponent(); + } + + public void SetDescription(string description) + { + DialogDescription.Text = description; + } + + private void CancelClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + Hide(); + } + + private void UpdateOrCreateClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + HasInput = true; + + Hide(); + } + } +} diff --git a/Wino.Mail/Dialogs/WinoMessageDialog.xaml b/Wino.Mail/Dialogs/WinoMessageDialog.xaml new file mode 100644 index 00000000..d61fc010 --- /dev/null +++ b/Wino.Mail/Dialogs/WinoMessageDialog.xaml @@ -0,0 +1,25 @@ + + + + 250 + 900 + 200 + 756 + + + + diff --git a/Wino.Mail/Dialogs/WinoMessageDialog.xaml.cs b/Wino.Mail/Dialogs/WinoMessageDialog.xaml.cs new file mode 100644 index 00000000..4e30b9b9 --- /dev/null +++ b/Wino.Mail/Dialogs/WinoMessageDialog.xaml.cs @@ -0,0 +1,60 @@ +using System.Threading.Tasks; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace Wino.Dialogs +{ + public sealed partial class WinoMessageDialog : ContentDialog + { + private TaskCompletionSource _completionSource; + + #region Dependency Properties + + public string DialogTitle + { + get { return (string)GetValue(DialogTitleProperty); } + set { SetValue(DialogTitleProperty, value); } + } + + public static readonly DependencyProperty DialogTitleProperty = DependencyProperty.Register(nameof(DialogTitle), typeof(string), typeof(ConfirmationDialog), new PropertyMetadata(string.Empty)); + + public string Message + { + get { return (string)GetValue(MessageProperty); } + set { SetValue(MessageProperty, value); } + } + + public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(nameof(Message), typeof(string), typeof(ConfirmationDialog), new PropertyMetadata(string.Empty)); + + #endregion + + public WinoMessageDialog() + { + InitializeComponent(); + } + + public async Task ShowDialogAsync(string title, string message) + { + _completionSource = new TaskCompletionSource(); + + DialogTitle = title; + Message = message; + +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + ShowAsync(); +#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + + return await _completionSource.Task; + } + + private void ApproveClicked(object sender, RoutedEventArgs e) + { + Hide(); + } + + private void DialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args) + { + _completionSource.TrySetResult(true); + } + } +} diff --git a/Wino.Mail/Extensions/AnimationExtensions.cs b/Wino.Mail/Extensions/AnimationExtensions.cs new file mode 100644 index 00000000..6ad7cb8a --- /dev/null +++ b/Wino.Mail/Extensions/AnimationExtensions.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Composition; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Media.Animation; + +namespace Wino.Extensions +{ + public static class AnimationExtensions + { + #region Composition + + public static ScalarKeyFrameAnimation CreateScalarKeyFrameAnimation(this Compositor compositor, float? from, float to, + double duration, double delay, CompositionEasingFunction easing, AnimationIterationBehavior iterationBehavior) + { + var animation = compositor.CreateScalarKeyFrameAnimation(); + + animation.Duration = TimeSpan.FromMilliseconds(duration); + if (!delay.Equals(0)) animation.DelayTime = TimeSpan.FromMilliseconds(delay); + if (from.HasValue) animation.InsertKeyFrame(0.0f, from.Value, easing); + animation.InsertKeyFrame(1.0f, to, easing); + animation.IterationBehavior = iterationBehavior; + + return animation; + } + + public static Vector2KeyFrameAnimation CreateVector2KeyFrameAnimation(this Compositor compositor, Vector2? from, Vector2 to, + double duration, double delay, CompositionEasingFunction easing, AnimationIterationBehavior iterationBehavior) + { + var animation = compositor.CreateVector2KeyFrameAnimation(); + + animation.Duration = TimeSpan.FromMilliseconds(duration); + animation.DelayTime = TimeSpan.FromMilliseconds(delay); + if (from.HasValue) animation.InsertKeyFrame(0.0f, from.Value, easing); + animation.InsertKeyFrame(1.0f, to, easing); + animation.IterationBehavior = iterationBehavior; + + return animation; + } + + public static Vector3KeyFrameAnimation CreateVector3KeyFrameAnimation(this Compositor compositor, Vector2? from, Vector2 to, + double duration, double delay, CompositionEasingFunction easing, AnimationIterationBehavior iterationBehavior) + { + var animation = compositor.CreateVector3KeyFrameAnimation(); + + animation.Duration = TimeSpan.FromMilliseconds(duration); + animation.DelayTime = TimeSpan.FromMilliseconds(delay); + if (from.HasValue) animation.InsertKeyFrame(0.0f, new Vector3(from.Value, 1.0f), easing); + animation.InsertKeyFrame(1.0f, new Vector3(to, 1.0f), easing); + animation.IterationBehavior = iterationBehavior; + + return animation; + } + + public static Vector3KeyFrameAnimation CreateVector3KeyFrameAnimation(this Compositor compositor, Vector3? from, Vector3 to, + double duration, double delay, CompositionEasingFunction easing, AnimationIterationBehavior iterationBehavior) + { + var animation = compositor.CreateVector3KeyFrameAnimation(); + + animation.Duration = TimeSpan.FromMilliseconds(duration); + animation.DelayTime = TimeSpan.FromMilliseconds(delay); + if (from.HasValue) animation.InsertKeyFrame(0.0f, from.Value, easing); + animation.InsertKeyFrame(1.0f, to, easing); + animation.IterationBehavior = iterationBehavior; + + return animation; + } + + #endregion + + #region Xaml Storyboard + + public static void Animate(this DependencyObject target, double? from, double to, + string propertyPath, int duration = 400, int startTime = 0, + EasingFunctionBase easing = null, Action completed = null, bool enableDependentAnimation = false) + { + if (easing == null) + { + easing = new ExponentialEase(); + } + + var db = new DoubleAnimation + { + EnableDependentAnimation = enableDependentAnimation, + To = to, + From = from, + EasingFunction = easing, + Duration = TimeSpan.FromMilliseconds(duration) + }; + Storyboard.SetTarget(db, target); + Storyboard.SetTargetProperty(db, propertyPath); + + var sb = new Storyboard + { + BeginTime = TimeSpan.FromMilliseconds(startTime) + }; + + if (completed != null) + { + sb.Completed += (s, e) => + { + completed(); + }; + } + + sb.Children.Add(db); + sb.Begin(); + } + + #endregion + } +} diff --git a/Wino.Mail/Extensions/CompositionEnums.cs b/Wino.Mail/Extensions/CompositionEnums.cs new file mode 100644 index 00000000..4ac01d05 --- /dev/null +++ b/Wino.Mail/Extensions/CompositionEnums.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wino.Extensions +{ + public enum TransitionDirection + { + TopToBottom, + BottomToTop, + LeftToRight, + RightToLeft + } + + public enum ClipAnimationDirection + { + Top, + Bottom, + Left, + Right + } + + public enum AnimationAxis + { + X, + Y, + Z + } + + public enum AnimationType + { + KeyFrame, + Expression + } + + public enum FlickDirection + { + None, + Up, + Down, + Left, + Right + } + + public enum ViewState + { + Empty, + Small, + Big, + Full + } + + public enum Gesture + { + Initial, + Tap, + Swipe + } + + [Flags] + public enum VisualPropertyType + { + None = 0, + Opacity = 1 << 0, + Offset = 1 << 1, + Scale = 1 << 2, + Size = 1 << 3, + RotationAngleInDegrees = 1 << 4, + All = ~0 + } +} diff --git a/Wino.Mail/Extensions/CompositionExtensions.Implicit.cs b/Wino.Mail/Extensions/CompositionExtensions.Implicit.cs new file mode 100644 index 00000000..d043fd45 --- /dev/null +++ b/Wino.Mail/Extensions/CompositionExtensions.Implicit.cs @@ -0,0 +1,200 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Composition; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Hosting; + +namespace Wino.Extensions +{ + public static partial class CompositionExtensions + { + public static void EnableFluidVisibilityAnimation(this UIElement element, AnimationAxis? axis = null, + float showFromOffset = 0.0f, float hideToOffset = 0.0f, Vector3? centerPoint = null, + float showFromScale = 1.0f, float hideToScale = 1.0f, float showDuration = 800.0f, float hideDuration = 800.0f, + int showDelay = 0, int hideDelay = 0, bool animateOpacity = true) + { + var elementVisual = element.Visual(); + var compositor = elementVisual.Compositor; + ElementCompositionPreview.SetIsTranslationEnabled(element, true); + + ScalarKeyFrameAnimation hideOpacityAnimation = null; + ScalarKeyFrameAnimation showOpacityAnimation = null; + ScalarKeyFrameAnimation hideOffsetAnimation = null; + ScalarKeyFrameAnimation showOffsetAnimation = null; + Vector2KeyFrameAnimation hideScaleAnimation = null; + Vector2KeyFrameAnimation showeScaleAnimation = null; + + if (animateOpacity) + { + hideOpacityAnimation = compositor.CreateScalarKeyFrameAnimation(); + hideOpacityAnimation.InsertKeyFrame(1.0f, 0.0f); + hideOpacityAnimation.Duration = TimeSpan.FromMilliseconds(hideDuration); + hideOpacityAnimation.DelayTime = TimeSpan.FromMilliseconds(hideDelay); + hideOpacityAnimation.Target = "Opacity"; + } + + if (!hideToOffset.Equals(0.0f)) + { + hideOffsetAnimation = compositor.CreateScalarKeyFrameAnimation(); + hideOffsetAnimation.InsertKeyFrame(1.0f, hideToOffset); + hideOffsetAnimation.Duration = TimeSpan.FromMilliseconds(hideDuration); + hideOffsetAnimation.DelayTime = TimeSpan.FromMilliseconds(hideDelay); + hideOffsetAnimation.Target = $"Translation.{axis}"; + } + + if (centerPoint.HasValue) + { + elementVisual.CenterPoint = centerPoint.Value; + } + + if (!hideToScale.Equals(1.0f)) + { + hideScaleAnimation = compositor.CreateVector2KeyFrameAnimation(); + hideScaleAnimation.InsertKeyFrame(1.0f, new Vector2(hideToScale)); + hideScaleAnimation.Duration = TimeSpan.FromMilliseconds(hideDuration); + hideScaleAnimation.DelayTime = TimeSpan.FromMilliseconds(hideDelay); + hideScaleAnimation.Target = "Scale.XY"; + } + + var hideAnimationGroup = compositor.CreateAnimationGroup(); + if (hideOpacityAnimation != null) + { + hideAnimationGroup.Add(hideOpacityAnimation); + } + if (hideOffsetAnimation != null) + { + hideAnimationGroup.Add(hideOffsetAnimation); + } + if (hideScaleAnimation != null) + { + hideAnimationGroup.Add(hideScaleAnimation); + } + + ElementCompositionPreview.SetImplicitHideAnimation(element, hideAnimationGroup); + + if (animateOpacity) + { + showOpacityAnimation = compositor.CreateScalarKeyFrameAnimation(); + showOpacityAnimation.InsertKeyFrame(1.0f, 1.0f); + showOpacityAnimation.Duration = TimeSpan.FromMilliseconds(showDuration); + showOpacityAnimation.DelayTime = TimeSpan.FromMilliseconds(showDelay); + showOpacityAnimation.Target = "Opacity"; + } + + if (!showFromOffset.Equals(0.0f)) + { + showOffsetAnimation = compositor.CreateScalarKeyFrameAnimation(); + showOffsetAnimation.InsertKeyFrame(0.0f, showFromOffset); + showOffsetAnimation.InsertKeyFrame(1.0f, 0.0f); + showOffsetAnimation.Duration = TimeSpan.FromMilliseconds(showDuration); + showOffsetAnimation.DelayTime = TimeSpan.FromMilliseconds(showDelay); + showOffsetAnimation.Target = $"Translation.{axis}"; + } + + if (!showFromScale.Equals(1.0f)) + { + showeScaleAnimation = compositor.CreateVector2KeyFrameAnimation(); + showeScaleAnimation.InsertKeyFrame(0.0f, new Vector2(showFromScale)); + showeScaleAnimation.InsertKeyFrame(1.0f, Vector2.One); + showeScaleAnimation.Duration = TimeSpan.FromMilliseconds(showDuration); + showeScaleAnimation.DelayTime = TimeSpan.FromMilliseconds(showDelay); + showeScaleAnimation.Target = "Scale.XY"; + } + + var showAnimationGroup = compositor.CreateAnimationGroup(); + if (showOpacityAnimation != null) + { + showAnimationGroup.Add(showOpacityAnimation); + } + if (showOffsetAnimation != null) + { + showAnimationGroup.Add(showOffsetAnimation); + } + if (showeScaleAnimation != null) + { + showAnimationGroup.Add(showeScaleAnimation); + } + + ElementCompositionPreview.SetImplicitShowAnimation(element, showAnimationGroup); + } + + public static void EnableImplicitAnimation(this UIElement element, VisualPropertyType typeToAnimate, + double duration = 800, double delay = 0, CompositionEasingFunction easing = null) + { + var visual = element.Visual(); + var compositor = visual.Compositor; + + var animationCollection = compositor.CreateImplicitAnimationCollection(); + + foreach (var type in UtilExtensions.GetValues()) + { + if (!typeToAnimate.HasFlag(type)) continue; + + var animation = CreateAnimationByType(compositor, type, duration, delay, easing); + + if (animation != null) + { + animationCollection[type.ToString()] = animation; + } + } + + visual.ImplicitAnimations = animationCollection; + } + + public static void EnableImplicitAnimation(this Visual visual, VisualPropertyType typeToAnimate, + double duration = 800, double delay = 0, CompositionEasingFunction easing = null) + { + var compositor = visual.Compositor; + + var animationCollection = compositor.CreateImplicitAnimationCollection(); + + foreach (var type in UtilExtensions.GetValues()) + { + if (!typeToAnimate.HasFlag(type)) continue; + + var animation = CreateAnimationByType(compositor, type, duration, delay, easing); + + if (animation != null) + { + animationCollection[type.ToString()] = animation; + } + } + + visual.ImplicitAnimations = animationCollection; + } + + private static KeyFrameAnimation CreateAnimationByType(Compositor compositor, VisualPropertyType type, + double duration = 800, double delay = 0, CompositionEasingFunction easing = null) + { + KeyFrameAnimation animation; + + switch (type) + { + case VisualPropertyType.Offset: + case VisualPropertyType.Scale: + animation = compositor.CreateVector3KeyFrameAnimation(); + break; + case VisualPropertyType.Size: + animation = compositor.CreateVector2KeyFrameAnimation(); + break; + case VisualPropertyType.Opacity: + case VisualPropertyType.RotationAngleInDegrees: + animation = compositor.CreateScalarKeyFrameAnimation(); + break; + default: + return null; + } + + animation.InsertExpressionKeyFrame(1.0f, "this.FinalValue", easing); + animation.Duration = TimeSpan.FromMilliseconds(duration); + animation.DelayTime = TimeSpan.FromMilliseconds(delay); + animation.Target = type.ToString(); + + return animation; + } + } +} diff --git a/Wino.Mail/Extensions/CompositionExtensions.Size.cs b/Wino.Mail/Extensions/CompositionExtensions.Size.cs new file mode 100644 index 00000000..42adf0af --- /dev/null +++ b/Wino.Mail/Extensions/CompositionExtensions.Size.cs @@ -0,0 +1,127 @@ +using System; +using System.Numerics; +using System.Threading.Tasks; +using Windows.UI.Composition; +using Windows.UI.Xaml; + +namespace Wino.Extensions +{ + public static partial class CompositionExtensions + { + public static void StartSizeAnimation(this UIElement element, Vector2? from = null, Vector2? to = null, + double duration = 800, int delay = 0, CompositionEasingFunction easing = null, Action completed = null, + AnimationIterationBehavior iterationBehavior = AnimationIterationBehavior.Count) + { + CompositionScopedBatch batch = null; + + var visual = element.Visual(); + var compositor = visual.Compositor; + + if (completed != null) + { + batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation); + batch.Completed += (s, e) => completed(); + } + + if (to == null) + { + to = Vector2.One; + } + + visual.StartAnimation("Size", + compositor.CreateVector2KeyFrameAnimation(from, to.Value, duration, delay, easing, iterationBehavior)); + + batch?.End(); + } + + public static void StartSizeAnimation(this Visual visual, Vector2? from = null, Vector2? to = null, + double duration = 800, int delay = 0, CompositionEasingFunction easing = null, Action completed = null, + AnimationIterationBehavior iterationBehavior = AnimationIterationBehavior.Count) + { + CompositionScopedBatch batch = null; + var compositor = visual.Compositor; + + if (completed != null) + { + batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation); + batch.Completed += (s, e) => completed(); + } + + if (to == null) + { + to = Vector2.One; + } + + visual.StartAnimation("Size", + compositor.CreateVector2KeyFrameAnimation(from, to.Value, duration, delay, easing, iterationBehavior)); + + batch?.End(); + } + + public static Task StartSizeAnimationAsync(this UIElement element, Vector2? from = null, Vector2? to = null, + double duration = 800, int delay = 0, CompositionEasingFunction easing = null, + AnimationIterationBehavior iterationBehavior = AnimationIterationBehavior.Count) + { + CompositionScopedBatch batch; + + var visual = element.Visual(); + var compositor = visual.Compositor; + + var taskSource = new TaskCompletionSource(); + + void Completed(object o, CompositionBatchCompletedEventArgs e) + { + batch.Completed -= Completed; + taskSource.SetResult(true); + } + + batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation); + batch.Completed += Completed; + + if (to == null) + { + to = Vector2.One; + } + + visual.StartAnimation("Size", + compositor.CreateVector2KeyFrameAnimation(from, to.Value, duration, delay, easing, iterationBehavior)); + + batch.End(); + + return taskSource.Task; + } + + public static Task StartSizeAnimationAsync(this Visual visual, Vector2? from = null, Vector2? to = null, + double duration = 800, int delay = 0, CompositionEasingFunction easing = null, + AnimationIterationBehavior iterationBehavior = AnimationIterationBehavior.Count) + { + CompositionScopedBatch batch; + + var compositor = visual.Compositor; + + var taskSource = new TaskCompletionSource(); + + void Completed(object o, CompositionBatchCompletedEventArgs e) + { + batch.Completed -= Completed; + taskSource.SetResult(true); + } + + batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation); + batch.Completed += Completed; + + if (to == null) + { + to = Vector2.One; + } + + visual.StartAnimation("Size", + compositor.CreateVector2KeyFrameAnimation(from, to.Value, duration, delay, easing, iterationBehavior)); + + batch.End(); + + return taskSource.Task; + } + + } +} diff --git a/Wino.Mail/Extensions/EnumerableExtensions.cs b/Wino.Mail/Extensions/EnumerableExtensions.cs new file mode 100644 index 00000000..4d94a9be --- /dev/null +++ b/Wino.Mail/Extensions/EnumerableExtensions.cs @@ -0,0 +1,18 @@ +using System.Collections; + +namespace Wino.Extensions +{ + public static class EnumerableExtensions + { + public static IEnumerable OfType(this IEnumerable source) + { + foreach (object item in source) + { + if (item is T1 || item is T2) + { + yield return item; + } + } + } + } +} diff --git a/Wino.Mail/Extensions/MimeKitExtensions.cs b/Wino.Mail/Extensions/MimeKitExtensions.cs new file mode 100644 index 00000000..4b776fce --- /dev/null +++ b/Wino.Mail/Extensions/MimeKitExtensions.cs @@ -0,0 +1,19 @@ +using System.IO; +using System.Threading.Tasks; +using Windows.Storage; +using Wino.Mail.ViewModels.Data; + +namespace Wino.Extensions +{ + public static class MimeKitExtensions + { + public static async Task ToAttachmentViewModelAsync(this StorageFile storageFile) + { + if (storageFile == null) return null; + + var bytes = await File.ReadAllBytesAsync(storageFile.Name); + + return new MailAttachmentViewModel(storageFile.Name, bytes); + } + } +} diff --git a/Wino.Mail/Extensions/UIExtensions.cs b/Wino.Mail/Extensions/UIExtensions.cs new file mode 100644 index 00000000..65a4723b --- /dev/null +++ b/Wino.Mail/Extensions/UIExtensions.cs @@ -0,0 +1,20 @@ +using Microsoft.UI.Xaml.Controls; +using Wino.Core.Domain.Enums; + +namespace Wino.Extensions +{ + public static class UIExtensions + { + public static InfoBarSeverity AsMUXCInfoBarSeverity(this InfoBarMessageType messageType) + { + return messageType switch + { + InfoBarMessageType.Error => InfoBarSeverity.Error, + InfoBarMessageType.Warning => InfoBarSeverity.Warning, + InfoBarMessageType.Information => InfoBarSeverity.Informational, + InfoBarMessageType.Success => InfoBarSeverity.Success, + _ => InfoBarSeverity.Informational + }; + } + } +} diff --git a/Wino.Mail/Extensions/UtilExtensions.cs b/Wino.Mail/Extensions/UtilExtensions.cs new file mode 100644 index 00000000..bfbea266 --- /dev/null +++ b/Wino.Mail/Extensions/UtilExtensions.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Windows.Foundation; +using Windows.UI.Composition; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Hosting; +using Windows.UI.Xaml.Media; + +namespace Wino.Extensions +{ + public static class UtilExtensions + { + public static float ToFloat(this double value) => (float)value; + + public static List Children(this DependencyObject parent) + { + var list = new List(); + + for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) + { + var child = VisualTreeHelper.GetChild(parent, i); + + if (child is FrameworkElement) + { + list.Add(child as FrameworkElement); + } + + list.AddRange(Children(child)); + } + + return list; + } + + public static T GetChildByName(this DependencyObject parent, string name) + { + var childControls = Children(parent); + var controls = childControls.OfType(); + + if (controls == null) + { + return default(T); + } + + var control = controls + .Where(x => x.Name.Equals(name)) + .Cast() + .First(); + + return control; + } + + public static Visual Visual(this UIElement element) => + ElementCompositionPreview.GetElementVisual(element); + + public static void SetChildVisual(this UIElement element, Visual childVisual) => + ElementCompositionPreview.SetElementChildVisual(element, childVisual); + + public static Point RelativePosition(this UIElement element, UIElement other) => + element.TransformToVisual(other).TransformPoint(new Point(0, 0)); + + public static bool IsFullyVisibile(this FrameworkElement element, FrameworkElement parent) + { + if (element == null || parent == null) + return false; + + if (element.Visibility != Visibility.Visible) + return false; + + var elementBounds = element.TransformToVisual(parent).TransformBounds(new Windows.Foundation.Rect(0, 0, element.ActualWidth, element.ActualHeight)); + var containerBounds = new Windows.Foundation.Rect(0, 0, parent.ActualWidth, parent.ActualHeight); + + var originalElementWidth = elementBounds.Width; + var originalElementHeight = elementBounds.Height; + + elementBounds.Intersect(containerBounds); + + var newElementWidth = elementBounds.Width; + var newElementHeight = elementBounds.Height; + + return originalElementWidth.Equals(newElementWidth) && originalElementHeight.Equals(newElementHeight); + } + + public static void ScrollToElement(this ScrollViewer scrollViewer, FrameworkElement element, + bool isVerticalScrolling = true, bool smoothScrolling = true, float? zoomFactor = null, bool bringToTopOrLeft = true) + { + if (!bringToTopOrLeft && element.IsFullyVisibile(scrollViewer)) + return; + + var contentArea = (FrameworkElement)scrollViewer.Content; + var position = element.RelativePosition(contentArea); + + if (isVerticalScrolling) + { + scrollViewer.ChangeView(null, position.Y, zoomFactor, !smoothScrolling); + } + else + { + scrollViewer.ChangeView(position.X, null, zoomFactor, !smoothScrolling); + } + } + + + public static IEnumerable GetValues() => Enum.GetValues(typeof(T)).Cast(); + } +} diff --git a/Wino.Mail/GlobalSuppressions.cs b/Wino.Mail/GlobalSuppressions.cs new file mode 100644 index 00000000..7d5444aa --- /dev/null +++ b/Wino.Mail/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Minor Code Smell", "S2094:Classes should not be empty", Justification = "", Scope = "type", Target = "~T:Wino.PubSub.BackBreadcrumNavigationRequest")] diff --git a/Wino.Mail/Helpers/JsonHelpers.cs b/Wino.Mail/Helpers/JsonHelpers.cs new file mode 100644 index 00000000..c04ecfcc --- /dev/null +++ b/Wino.Mail/Helpers/JsonHelpers.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; + +using Newtonsoft.Json; + +namespace Wino.Helpers +{ + public static class JsonHelpers + { + public static async Task ToObjectAsync(string value) + { + return await Task.Run(() => + { + return JsonConvert.DeserializeObject(value); + }); + } + + public static async Task StringifyAsync(object value) + { + return await Task.Run(() => + { + return JsonConvert.SerializeObject(value); + }); + } + } +} diff --git a/Wino.Mail/Helpers/SettingsStorageExtensions.cs b/Wino.Mail/Helpers/SettingsStorageExtensions.cs new file mode 100644 index 00000000..a566e891 --- /dev/null +++ b/Wino.Mail/Helpers/SettingsStorageExtensions.cs @@ -0,0 +1,119 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +using Windows.Storage; +using Windows.Storage.Streams; + +namespace Wino.Helpers +{ + // Use these extension methods to store and retrieve local and roaming app data + // More details regarding storing and retrieving app data at https://docs.microsoft.com/windows/uwp/app-settings/store-and-retrieve-app-data + public static class SettingsStorageExtensions + { + private const string FileExtension = ".json"; + + public static bool IsRoamingStorageAvailable(this ApplicationData appData) + { + return appData.RoamingStorageQuota == 0; + } + + public static async Task SaveAsync(this StorageFolder folder, string name, T content) + { + var file = await folder.CreateFileAsync(GetFileName(name), CreationCollisionOption.ReplaceExisting); + var fileContent = await JsonHelpers.StringifyAsync(content); + + await FileIO.WriteTextAsync(file, fileContent); + } + + public static async Task ReadAsync(this StorageFolder folder, string name) + { + if (!File.Exists(Path.Combine(folder.Path, GetFileName(name)))) + { + return default; + } + + var file = await folder.GetFileAsync($"{name}.json"); + var fileContent = await FileIO.ReadTextAsync(file); + + return await JsonHelpers.ToObjectAsync(fileContent); + } + + public static async Task SaveAsync(this ApplicationDataContainer settings, string key, T value) + { + settings.SaveString(key, await JsonHelpers.StringifyAsync(value)); + } + + public static void SaveString(this ApplicationDataContainer settings, string key, string value) + { + settings.Values[key] = value; + } + + public static async Task ReadAsync(this ApplicationDataContainer settings, string key) + { + object obj = null; + + if (settings.Values.TryGetValue(key, out obj)) + { + return await JsonHelpers.ToObjectAsync((string)obj); + } + + return default; + } + + public static async Task SaveFileAsync(this StorageFolder folder, byte[] content, string fileName, CreationCollisionOption options = CreationCollisionOption.ReplaceExisting) + { + if (content == null) + { + throw new ArgumentNullException(nameof(content)); + } + + if (string.IsNullOrEmpty(fileName)) + { + throw new ArgumentException("File name is null or empty. Specify a valid file name", nameof(fileName)); + } + + var storageFile = await folder.CreateFileAsync(fileName, options); + await FileIO.WriteBytesAsync(storageFile, content); + return storageFile; + } + + public static async Task ReadFileAsync(this StorageFolder folder, string fileName) + { + var item = await folder.TryGetItemAsync(fileName).AsTask().ConfigureAwait(false); + + if ((item != null) && item.IsOfType(StorageItemTypes.File)) + { + var storageFile = await folder.GetFileAsync(fileName); + byte[] content = await storageFile.ReadBytesAsync(); + return content; + } + + return null; + } + + public static async Task ReadBytesAsync(this StorageFile file) + { + if (file != null) + { + using (IRandomAccessStream stream = await file.OpenReadAsync()) + { + using (var reader = new DataReader(stream.GetInputStreamAt(0))) + { + await reader.LoadAsync((uint)stream.Size); + var bytes = new byte[stream.Size]; + reader.ReadBytes(bytes); + return bytes; + } + } + } + + return null; + } + + private static string GetFileName(string name) + { + return string.Concat(name, FileExtension); + } + } +} diff --git a/Wino.Mail/Helpers/WinoVisualTreeHelper.cs b/Wino.Mail/Helpers/WinoVisualTreeHelper.cs new file mode 100644 index 00000000..ded3482e --- /dev/null +++ b/Wino.Mail/Helpers/WinoVisualTreeHelper.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Media; + +namespace Wino.Helpers +{ + public static class WinoVisualTreeHelper + { + public static T GetChildObject(DependencyObject obj, string name) where T : FrameworkElement + { + DependencyObject child = null; + T grandChild = null; + + for (int i = 0; i <= VisualTreeHelper.GetChildrenCount(obj) - 1; i++) + { + child = VisualTreeHelper.GetChild(obj, i); + + if (child is T && (((T)child).Name == name | string.IsNullOrEmpty(name))) + { + return (T)child; + } + else + { + grandChild = GetChildObject(child, name); + } + if (grandChild != null) + { + return grandChild; + } + } + return null; + } + } +} diff --git a/Wino.Mail/Helpers/XamlHelpers.cs b/Wino.Mail/Helpers/XamlHelpers.cs new file mode 100644 index 00000000..7bef3d83 --- /dev/null +++ b/Wino.Mail/Helpers/XamlHelpers.cs @@ -0,0 +1,399 @@ +using System; +using System.Linq; +using Microsoft.Toolkit.Uwp.Helpers; +using Microsoft.UI.Xaml.Controls; +using Windows.UI; +using Windows.UI.Text; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Markup; +using Windows.UI.Xaml.Media; +using Wino.Controls; +using Wino.Core.Domain; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Reader; + +namespace Wino.Helpers +{ + public static class XamlHelpers + { + #region Converters + + public static Visibility ReverseBoolToVisibilityConverter(bool value) => value ? Visibility.Collapsed : Visibility.Visible; + public static Visibility ReverseVisibilityConverter(Visibility visibility) => visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible; + public static bool ReverseBoolConverter(bool value) => !value; + public static bool ShouldDisplayPreview(string text) => text.Any(x => char.IsLetter(x)); + public static bool CountToBooleanConverter(int value) => value > 0; + public static bool ObjectEquals(object obj1, object obj2) => object.Equals(obj1, obj2); + public static Visibility CountToVisibilityConverter(int value) => value > 0 ? Visibility.Visible : Visibility.Collapsed; + public static InfoBarSeverity InfoBarSeverityConverter(InfoBarMessageType messageType) + { + switch (messageType) + { + case InfoBarMessageType.Information: + return InfoBarSeverity.Informational; + case InfoBarMessageType.Success: + return InfoBarSeverity.Success; + case InfoBarMessageType.Warning: + return InfoBarSeverity.Warning; + case InfoBarMessageType.Error: + return InfoBarSeverity.Error; + default: + return InfoBarSeverity.Informational; + } + } + public static SolidColorBrush GetSolidColorBrushFromHex(string colorHex) => string.IsNullOrEmpty(colorHex) ? new SolidColorBrush(Colors.Transparent) : new SolidColorBrush(colorHex.ToColor()); + public static Visibility IsSelectionModeMultiple(ListViewSelectionMode mode) => mode == ListViewSelectionMode.Multiple ? Visibility.Visible : Visibility.Collapsed; + public static FontWeight GetFontWeightBySyncState(bool isSyncing) => isSyncing ? FontWeights.SemiBold : FontWeights.Normal; + public static FontWeight GetFontWeightByChildSelectedState(bool isChildSelected) => isChildSelected ? FontWeights.SemiBold : FontWeights.Normal; + public static Geometry GetPathIcon(string resourceName) => GetPathGeometry(Application.Current.Resources[$"{resourceName}"] as string); + public static GridLength GetGridLength(double width) => new GridLength(width, GridUnitType.Pixel); + public static double MailListAdaptivityConverter(double mailListPaneLength) => mailListPaneLength + 300d; + public static string GetMailItemDisplaySummaryForListing(bool isDraft, DateTime receivedDate, bool prefer24HourTime) + { + if (isDraft) + return Translator.Draft; + else + { + var localTime = receivedDate.ToLocalTime(); + + return prefer24HourTime ? localTime.ToString("HH:mm") : localTime.ToString("hh:mm tt"); + } + } + + public static string GetCreationDateString(DateTime date) + { + var localTime = date.ToLocalTime(); + return $"{localTime.ToLongDateString()} {localTime.ToLongTimeString()}"; + } + + public static string GetMailGroupDateString(object groupObject) + { + if (groupObject is string stringObject) + return stringObject; + + object dateObject = null; + + // From regular mail header template + if (groupObject is DateTime groupedDate) + dateObject = groupedDate; + else if (groupObject is IGrouping groupKey) + { + // From semantic group header. + dateObject = groupKey.Key; + } + + if (dateObject != null) + { + if (dateObject is DateTime dateTimeValue) + { + if (dateTimeValue == DateTime.Today) + return Translator.Today; + else if (dateTimeValue == DateTime.Today.AddDays(-1)) + return Translator.Yesterday; + else + return dateTimeValue.ToLongDateString(); + } + else + return dateObject.ToString(); + } + + return Translator.UnknownDateHeader; + } + + #endregion + + #region Wino Font Icon Transformation + + public static WinoIconGlyph GetWinoIconGlyph(FilterOptionType type) + { + switch (type) + { + case FilterOptionType.All: + return WinoIconGlyph.SpecialFolderCategory; + case FilterOptionType.Unread: + return WinoIconGlyph.MarkUnread; + case FilterOptionType.Flagged: + return WinoIconGlyph.Flag; + case FilterOptionType.Mentions: + return WinoIconGlyph.NewMail; + default: + return WinoIconGlyph.None; + } + } + + public static WinoIconGlyph GetWinoIconGlyph(MailOperation operation) + { + switch (operation) + { + case MailOperation.None: + return WinoIconGlyph.None; + case MailOperation.Archive: + return WinoIconGlyph.Archive; + case MailOperation.UnArchive: + return WinoIconGlyph.UnArchive; + case MailOperation.SoftDelete: + case MailOperation.HardDelete: + return WinoIconGlyph.Delete; + case MailOperation.Move: + return WinoIconGlyph.Forward; + case MailOperation.MoveToJunk: + return WinoIconGlyph.Junk; + case MailOperation.MoveToFocused: + break; + case MailOperation.MoveToOther: + break; + case MailOperation.AlwaysMoveToOther: + break; + case MailOperation.AlwaysMoveToFocused: + break; + case MailOperation.SetFlag: + return WinoIconGlyph.Flag; + case MailOperation.ClearFlag: + return WinoIconGlyph.ClearFlag; + case MailOperation.MarkAsRead: + return WinoIconGlyph.MarkRead; + case MailOperation.MarkAsUnread: + return WinoIconGlyph.MarkUnread; + case MailOperation.MarkAsNotJunk: + return WinoIconGlyph.Junk; + case MailOperation.Ignore: + return WinoIconGlyph.Ignore; + case MailOperation.Reply: + return WinoIconGlyph.Reply; + case MailOperation.ReplyAll: + return WinoIconGlyph.ReplyAll; + case MailOperation.Zoom: + return WinoIconGlyph.Zoom; + case MailOperation.SaveAs: + return WinoIconGlyph.Save; + case MailOperation.Find: + return WinoIconGlyph.Find; + case MailOperation.Forward: + return WinoIconGlyph.Forward; + case MailOperation.DarkEditor: + return WinoIconGlyph.DarkEditor; + case MailOperation.LightEditor: + return WinoIconGlyph.LightEditor; + } + + return WinoIconGlyph.None; + } + + public static WinoIconGlyph GetPathGeometry(FolderOperation operation) + { + switch (operation) + { + case FolderOperation.None: + return WinoIconGlyph.None; + case FolderOperation.Pin: + return WinoIconGlyph.Pin; + case FolderOperation.Unpin: + return WinoIconGlyph.UnPin; + case FolderOperation.MarkAllAsRead: + return WinoIconGlyph.MarkRead; + case FolderOperation.DontSync: + return WinoIconGlyph.DontSync; + case FolderOperation.Empty: + return WinoIconGlyph.EmptyFolder; + case FolderOperation.Rename: + return WinoIconGlyph.Rename; + case FolderOperation.Delete: + return WinoIconGlyph.Delete; + case FolderOperation.Move: + return WinoIconGlyph.Forward; + case FolderOperation.TurnOffNotifications: + return WinoIconGlyph.TurnOfNotifications; + case FolderOperation.CreateSubFolder: + return WinoIconGlyph.CreateFolder; + } + + return WinoIconGlyph.None; + } + + public static WinoIconGlyph GetSpecialFolderPathIconGeometry(SpecialFolderType specialFolderType) + { + switch (specialFolderType) + { + case SpecialFolderType.Inbox: + return WinoIconGlyph.SpecialFolderInbox; + case SpecialFolderType.Starred: + return WinoIconGlyph.SpecialFolderStarred; + case SpecialFolderType.Important: + return WinoIconGlyph.SpecialFolderImportant; + case SpecialFolderType.Sent: + return WinoIconGlyph.SpecialFolderSent; + case SpecialFolderType.Draft: + return WinoIconGlyph.SpecialFolderDraft; + case SpecialFolderType.Archive: + return WinoIconGlyph.SpecialFolderArchive; + case SpecialFolderType.Deleted: + return WinoIconGlyph.SpecialFolderDeleted; + case SpecialFolderType.Junk: + return WinoIconGlyph.SpecialFolderJunk; + case SpecialFolderType.Chat: + return WinoIconGlyph.SpecialFolderChat; + case SpecialFolderType.Category: + return WinoIconGlyph.SpecialFolderCategory; + case SpecialFolderType.Unread: + return WinoIconGlyph.SpecialFolderUnread; + case SpecialFolderType.Forums: + return WinoIconGlyph.SpecialFolderForums; + case SpecialFolderType.Updates: + return WinoIconGlyph.SpecialFolderUpdated; + case SpecialFolderType.Personal: + return WinoIconGlyph.SpecialFolderPersonal; + case SpecialFolderType.Promotions: + return WinoIconGlyph.SpecialFolderPromotions; + case SpecialFolderType.Social: + return WinoIconGlyph.SpecialFolderSocial; + case SpecialFolderType.Other: + return WinoIconGlyph.SpecialFolderOther; + case SpecialFolderType.More: + return WinoIconGlyph.SpecialFolderMore; + } + + return WinoIconGlyph.None; + } + + public static WinoIconGlyph GetProviderIcon(MailProviderType providerType) + { + switch (providerType) + { + case MailProviderType.Outlook: + return WinoIconGlyph.Microsoft; + case MailProviderType.Gmail: + return WinoIconGlyph.Google; + case MailProviderType.Office365: + return WinoIconGlyph.Microsoft; + case MailProviderType.IMAP4: + return WinoIconGlyph.Mail; + } + + return WinoIconGlyph.None; + } + + public static Geometry GetPathGeometry(string pathMarkup) + { + string xaml = + "" + + "" + pathMarkup + ""; + var path = XamlReader.Load(xaml) as Windows.UI.Xaml.Shapes.Path; + + Geometry geometry = path.Data; + path.Data = null; + return geometry; + } + + #endregion + + #region Toolbar Section Initialization + + public static Visibility IsFormatSection(EditorToolbarSection section) => section?.SectionType == EditorToolbarSectionType.Format ? Visibility.Visible : Visibility.Collapsed; + public static Visibility IsInsertSection(EditorToolbarSection section) => section?.SectionType == EditorToolbarSectionType.Insert ? Visibility.Visible : Visibility.Collapsed; + public static Visibility IsDrawSection(EditorToolbarSection section) => section?.SectionType == EditorToolbarSectionType.Draw ? Visibility.Visible : Visibility.Collapsed; + public static Visibility IsOptionsSection(EditorToolbarSection section) => section?.SectionType == EditorToolbarSectionType.Options ? Visibility.Visible : Visibility.Collapsed; + + #endregion + + #region Internationalization + + public static string GetOperationString(MailOperation operation) + { + switch (operation) + { + case MailOperation.None: + return "unknown"; + case MailOperation.Archive: + return Translator.MailOperation_Archive; + case MailOperation.UnArchive: + return Translator.MailOperation_Unarchive; + case MailOperation.SoftDelete: + return Translator.MailOperation_Delete; + case MailOperation.HardDelete: + return Translator.MailOperation_Delete; + case MailOperation.Move: + return Translator.MailOperation_Move; + case MailOperation.MoveToJunk: + return Translator.MailOperation_MoveJunk; + case MailOperation.MoveToFocused: + return Translator.MailOperation_MoveFocused; + case MailOperation.MoveToOther: + return Translator.MailOperation_MoveOther; + case MailOperation.AlwaysMoveToOther: + return Translator.MailOperation_AlwaysMoveOther; + case MailOperation.AlwaysMoveToFocused: + return Translator.MailOperation_AlwaysMoveFocused; + case MailOperation.SetFlag: + return Translator.MailOperation_SetFlag; + case MailOperation.ClearFlag: + return Translator.MailOperation_ClearFlag; + case MailOperation.MarkAsRead: + return Translator.MailOperation_MarkAsRead; + case MailOperation.MarkAsUnread: + return Translator.MailOperation_MarkAsUnread; + case MailOperation.MarkAsNotJunk: + return Translator.MailOperation_MarkNotJunk; + case MailOperation.Seperator: + return string.Empty; + case MailOperation.Ignore: + return Translator.MailOperation_Ignore; + case MailOperation.Reply: + return Translator.MailOperation_Reply; + case MailOperation.ReplyAll: + return Translator.MailOperation_ReplyAll; + case MailOperation.Zoom: + return Translator.MailOperation_Zoom; + case MailOperation.SaveAs: + return Translator.MailOperation_SaveAs; + case MailOperation.Find: + return Translator.MailOperation_Find; + case MailOperation.Forward: + return Translator.MailOperation_Forward; + case MailOperation.DarkEditor: + return string.Empty; + case MailOperation.LightEditor: + return string.Empty; + case MailOperation.Print: + return Translator.MailOperation_Print; + case MailOperation.Navigate: + return Translator.MailOperation_Navigate; + default: + return "unknown"; + } + } + + public static string GetOperationString(FolderOperation operation) + { + switch (operation) + { + case FolderOperation.None: + break; + case FolderOperation.Pin: + return Translator.FolderOperation_Pin; + case FolderOperation.Unpin: + return Translator.FolderOperation_Unpin; + case FolderOperation.MarkAllAsRead: + return Translator.FolderOperation_MarkAllAsRead; + case FolderOperation.DontSync: + return Translator.FolderOperation_DontSync; + case FolderOperation.Empty: + return Translator.FolderOperation_Empty; + case FolderOperation.Rename: + return Translator.FolderOperation_Rename; + case FolderOperation.Delete: + return Translator.FolderOperation_Delete; + case FolderOperation.Move: + return Translator.FolderOperation_Move; + case FolderOperation.CreateSubFolder: + return Translator.FolderOperation_CreateSubFolder; + } + + return string.Empty; + } + + #endregion + } +} diff --git a/Wino.Mail/JS/Quill/Editor/full.html b/Wino.Mail/JS/Quill/Editor/full.html new file mode 100644 index 00000000..ca55e9d8 --- /dev/null +++ b/Wino.Mail/JS/Quill/Editor/full.html @@ -0,0 +1,278 @@ + + + + + + + + + + + + + + + + + +
+ + +
+ +
+

Sent from Wino Mail for Windows +
+ + + + + + + + + + + + + + + diff --git a/Wino.Mail/JS/Quill/darkreader.js b/Wino.Mail/JS/Quill/darkreader.js new file mode 100644 index 00000000..6e45c600 --- /dev/null +++ b/Wino.Mail/JS/Quill/darkreader.js @@ -0,0 +1,3187 @@ +/** + * Dark Reader v4.9.1 + * https://darkreader.org/ + */ + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = global || self, factory(global.DarkReader = {})); +}(this, (function (exports) { 'use strict'; + + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use + this file except in compliance with the License. You may obtain a copy of the + License at http://www.apache.org/licenses/LICENSE-2.0 + + THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED + WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, + MERCHANTABLITY OR NON-INFRINGEMENT. + + See the Apache Version 2.0 License for specific language governing permissions + and limitations under the License. + ***************************************************************************** */ + + var __assign = function() { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); + }; + + function __awaiter(thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + } + + function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } + } + + function isFirefox() { + return navigator.userAgent.includes('Firefox'); + } + function isMacOS() { + return navigator.platform.toLowerCase().startsWith('mac'); + } + function isDeepSelectorSupported() { + try { + document.querySelector('x /deep/ x'); + return true; + } + catch (err) { + return false; + } + } + function isHostSelectorSupported() { + try { + document.querySelector(':host x'); + return true; + } + catch (err) { + return false; + } + } + function isDefinedSelectorSupported() { + try { + document.querySelector(':defined'); + return true; + } + catch (err) { + return false; + } + } + + function getOKResponse(url, mimeType) { + return __awaiter(this, void 0, void 0, function () { + var response; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4, fetch(url, { + cache: 'force-cache', + credentials: 'omit', + })]; + case 1: + response = _a.sent(); + if (isFirefox() && mimeType === 'text/css' && url.startsWith('moz-extension://') && url.endsWith('.css')) { + return [2, response]; + } + if (mimeType && !response.headers.get('Content-Type').startsWith(mimeType)) { + throw new Error("Mime type mismatch when loading " + url); + } + if (!response.ok) { + throw new Error("Unable to load " + url + " " + response.status + " " + response.statusText); + } + return [2, response]; + } + }); + }); + } + function loadAsDataURL(url, mimeType) { + return __awaiter(this, void 0, void 0, function () { + var response; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4, getOKResponse(url, mimeType)]; + case 1: + response = _a.sent(); + return [4, readResponseAsDataURL(response)]; + case 2: return [2, _a.sent()]; + } + }); + }); + } + function readResponseAsDataURL(response) { + return __awaiter(this, void 0, void 0, function () { + var blob, dataURL; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4, response.blob()]; + case 1: + blob = _a.sent(); + return [4, (new Promise(function (resolve) { + var reader = new FileReader(); + reader.onloadend = function () { return resolve(reader.result); }; + reader.readAsDataURL(blob); + }))]; + case 2: + dataURL = _a.sent(); + return [2, dataURL]; + } + }); + }); + } + + var throwCORSError = function (url) { return __awaiter(void 0, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2, Promise.reject(new Error([ + 'Embedded Dark Reader cannot access a cross-origin resource', + url, + 'Overview your URLs and CORS policies or use', + '`DarkReader.setFetchMethod(fetch: (url) => Promise))`.', + 'See if using `DarkReader.setFetchMethod(window.fetch)`', + 'before `DarkReader.enable()` works.' + ].join(' ')))]; + }); + }); }; + var fetcher = throwCORSError; + function setFetchMethod(fetch) { + if (fetch) { + fetcher = fetch; + } + else { + fetcher = throwCORSError; + } + } + function callFetchMethod(url) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4, fetcher(url)]; + case 1: return [2, _a.sent()]; + } + }); + }); + } + + if (!window.chrome) { + window.chrome = {}; + } + if (!chrome.runtime) { + chrome.runtime = {}; + } + var messageListeners = new Set(); + function sendMessage() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return __awaiter(this, void 0, void 0, function () { + var id_1, _a, url, responseType, response, text_1, error_1; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!(args[0] && args[0].type === 'fetch')) return [3, 8]; + id_1 = args[0].id; + _b.label = 1; + case 1: + _b.trys.push([1, 7, , 8]); + _a = args[0].data, url = _a.url, responseType = _a.responseType; + return [4, callFetchMethod(url)]; + case 2: + response = _b.sent(); + if (!(responseType === 'data-url')) return [3, 4]; + return [4, readResponseAsDataURL(response)]; + case 3: + text_1 = _b.sent(); + return [3, 6]; + case 4: return [4, response.text()]; + case 5: + text_1 = _b.sent(); + _b.label = 6; + case 6: + messageListeners.forEach(function (cb) { return cb({ type: 'fetch-response', data: text_1, error: null, id: id_1 }); }); + return [3, 8]; + case 7: + error_1 = _b.sent(); + console.error(error_1); + messageListeners.forEach(function (cb) { return cb({ type: 'fetch-response', data: null, error: error_1, id: id_1 }); }); + return [3, 8]; + case 8: return [2]; + } + }); + }); + } + function addMessageListener(callback) { + messageListeners.add(callback); + } + if (typeof chrome.runtime.sendMessage === 'function') { + var nativeSendMessage_1 = chrome.runtime.sendMessage; + chrome.runtime.sendMessage = function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + sendMessage.apply(void 0, args); + nativeSendMessage_1.apply(chrome.runtime, args); + }; + } + else { + chrome.runtime.sendMessage = sendMessage; + } + if (!chrome.runtime.onMessage) { + chrome.runtime.onMessage = {}; + } + if (typeof chrome.runtime.onMessage.addListener === 'function') { + var nativeAddListener_1 = chrome.runtime.onMessage.addListener; + chrome.runtime.onMessage.addListener = function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + addMessageListener.apply(void 0, args); + nativeAddListener_1.apply(chrome.runtime.onMessage, args); + }; + } + else { + chrome.runtime.onMessage.addListener = addMessageListener; + } + + var ThemeEngines = { + cssFilter: 'cssFilter', + svgFilter: 'svgFilter', + staticTheme: 'staticTheme', + dynamicTheme: 'dynamicTheme', + }; + + function parseURL(url) { + var a = document.createElement('a'); + a.href = url; + return a; + } + function getAbsoluteURL($base, $relative) { + if ($relative.match(/^.*?\/\//) || $relative.match(/^data\:/)) { + if ($relative.startsWith('//')) { + return "" + location.protocol + $relative; + } + return $relative; + } + var b = parseURL($base); + if ($relative.startsWith('/')) { + var u_1 = parseURL(b.protocol + "//" + b.host + $relative); + return u_1.href; + } + var pathParts = b.pathname.split('/').concat($relative.split('/')).filter(function (p) { return p; }); + var backwardIndex; + while ((backwardIndex = pathParts.indexOf('..')) > 0) { + pathParts.splice(backwardIndex - 1, 2); + } + var u = parseURL(b.protocol + "//" + b.host + "/" + pathParts.join('/')); + return u.href; + } + + function logInfo() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + } + function logWarn() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + } + + function iterateCSSRules(rules, iterate) { + Array.from(rules) + .forEach(function (rule) { + if (rule instanceof CSSMediaRule) { + var media = Array.from(rule.media); + if (media.includes('screen') || media.includes('all') || !(media.includes('print') || media.includes('speech'))) { + iterateCSSRules(rule.cssRules, iterate); + } + } + else if (rule instanceof CSSStyleRule) { + iterate(rule); + } + else if (rule instanceof CSSImportRule) { + try { + iterateCSSRules(rule.styleSheet.cssRules, iterate); + } + catch (err) { + logWarn(err); + } + } + else { + logWarn("CSSRule type not supported", rule); + } + }); + } + function iterateCSSDeclarations(style, iterate) { + Array.from(style).forEach(function (property) { + var value = style.getPropertyValue(property).trim(); + if (!value) { + return; + } + iterate(property, value); + }); + } + function isCSSVariable(property) { + return property.startsWith('--') && !property.startsWith('--darkreader'); + } + function getCSSVariables(rules) { + var variables = new Map(); + rules && iterateCSSRules(rules, function (rule) { + rule.style && iterateCSSDeclarations(rule.style, function (property, value) { + if (isCSSVariable(property)) { + variables.set(property, value); + } + }); + }); + return variables; + } + function getElementCSSVariables(element) { + var variables = new Map(); + iterateCSSDeclarations(element.style, function (property, value) { + if (isCSSVariable(property)) { + variables.set(property, value); + } + }); + return variables; + } + var cssURLRegex = /url\((('.+?')|(".+?")|([^\)]*?))\)/g; + var cssImportRegex = /@import (url\()?(('.+?')|(".+?")|([^\)]*?))\)?;?/g; + function getCSSURLValue(cssURL) { + return cssURL.replace(/^url\((.*)\)$/, '$1').replace(/^"(.*)"$/, '$1').replace(/^'(.*)'$/, '$1'); + } + function getCSSBaseBath(url) { + var cssURL = parseURL(url); + return cssURL.protocol + "//" + cssURL.host + cssURL.pathname.replace(/\?.*$/, '').replace(/(\/)([^\/]+)$/i, '$1'); + } + function replaceCSSRelativeURLsWithAbsolute($css, cssBasePath) { + return $css.replace(cssURLRegex, function (match) { + var pathValue = getCSSURLValue(match); + return "url(\"" + getAbsoluteURL(cssBasePath, pathValue) + "\")"; + }); + } + var cssCommentsRegex = /\/\*[\s\S]*?\*\//g; + function removeCSSComments($css) { + return $css.replace(cssCommentsRegex, ''); + } + var fontFaceRegex = /@font-face\s*{[^}]*}/g; + function replaceCSSFontFace($css) { + return $css.replace(fontFaceRegex, ''); + } + var varRegex = /var\((--[^\s,]+),?\s*([^\(\)]*(\([^\(\)]*\)[^\(\)]*)*\s*)\)/g; + function replaceCSSVariables(value, variables) { + var missing = false; + var result = value.replace(varRegex, function (match, name, fallback) { + if (variables.has(name)) { + return variables.get(name); + } + else if (fallback) { + return fallback; + } + else { + logWarn("Variable " + name + " not found"); + missing = true; + } + return match; + }); + if (missing) { + return result; + } + if (result.match(varRegex)) { + return replaceCSSVariables(result, variables); + } + return result; + } + + function throttle(callback) { + var pending = false; + var frameId = null; + var lastArgs; + var throttled = (function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + lastArgs = args; + if (frameId) { + pending = true; + } + else { + callback.apply(void 0, lastArgs); + frameId = requestAnimationFrame(function () { + frameId = null; + if (pending) { + callback.apply(void 0, lastArgs); + pending = false; + } + }); + } + }); + var cancel = function () { + cancelAnimationFrame(frameId); + pending = false; + frameId = null; + }; + return Object.assign(throttled, { cancel: cancel }); + } + function createAsyncTasksQueue() { + var tasks = []; + var frameId = null; + function runTasks() { + var task; + while (task = tasks.shift()) { + task(); + } + frameId = null; + } + function add(task) { + tasks.push(task); + if (!frameId) { + frameId = requestAnimationFrame(runTasks); + } + } + function cancel() { + tasks.splice(0); + cancelAnimationFrame(frameId); + frameId = null; + } + return { add: add, cancel: cancel }; + } + + function getDuration(time) { + var duration = 0; + if (time.seconds) { + duration += time.seconds * 1000; + } + if (time.minutes) { + duration += time.minutes * 60 * 1000; + } + if (time.hours) { + duration += time.hours * 60 * 60 * 1000; + } + if (time.days) { + duration += time.days * 24 * 60 * 60 * 1000; + } + return duration; + } + + function removeNode(node) { + node && node.parentNode && node.parentNode.removeChild(node); + } + function watchForNodePosition(node, _a) { + var _b = _a.onRestore, onRestore = _b === void 0 ? Function.prototype : _b, _c = _a.watchParent, watchParent = _c === void 0 ? true : _c, _d = _a.watchSibling, watchSibling = _d === void 0 ? false : _d; + var MAX_ATTEMPTS_COUNT = 10; + var ATTEMPTS_INTERVAL = getDuration({ seconds: 10 }); + var prevSibling = node.previousSibling; + var parent = node.parentNode; + if (!parent) { + logWarn('Unable to watch for node position: parent element not found', node, prevSibling); + return { stop: Function.prototype }; + } + var attempts = 0; + var start = null; + var restore = throttle(function () { + attempts++; + var now = Date.now(); + if (start == null) { + start = now; + } + else if (attempts >= MAX_ATTEMPTS_COUNT) { + if (now - start < ATTEMPTS_INTERVAL) { + logWarn('Node position watcher stopped: some script conflicts with Dark Reader and can cause high CPU usage', node, prevSibling); + stop(); + return; + } + start = now; + attempts = 1; + } + if (prevSibling && prevSibling.parentNode !== parent) { + logWarn('Unable to restore node position: sibling was removed', node, prevSibling, parent); + stop(); + return; + } + logWarn('Node was removed, restoring it\'s position', node, prevSibling, parent); + parent.insertBefore(node, prevSibling ? prevSibling.nextSibling : parent.firstChild); + onRestore && onRestore(); + }); + var observer = new MutationObserver(function () { + if ((watchParent && !node.parentNode) || + (watchSibling && node.previousSibling !== prevSibling)) { + restore(); + observer.takeRecords(); + } + }); + var run = function () { + observer.observe(parent, { childList: true }); + }; + var stop = function () { + observer.disconnect(); + }; + run(); + return { run: run, stop: stop }; + } + function iterateShadowNodes(root, iterator) { + var walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, { + acceptNode: function (node) { + return node.shadowRoot == null ? NodeFilter.FILTER_SKIP : NodeFilter.FILTER_ACCEPT; + } + }, false); + for (var node = (root.shadowRoot ? walker.currentNode : walker.nextNode()); node != null; node = walker.nextNode()) { + iterator(node); + iterateShadowNodes(node.shadowRoot, iterator); + } + } + + function hslToRGB(_a) { + var h = _a.h, s = _a.s, l = _a.l, _b = _a.a, a = _b === void 0 ? 1 : _b; + if (s === 0) { + var _c = [l, l, l].map(function (x) { return Math.round(x * 255); }), r_1 = _c[0], b_1 = _c[1], g_1 = _c[2]; + return { r: r_1, g: g_1, b: b_1, a: a }; + } + var c = (1 - Math.abs(2 * l - 1)) * s; + var x = c * (1 - Math.abs((h / 60) % 2 - 1)); + var m = l - c / 2; + var _d = (h < 60 ? [c, x, 0] : + h < 120 ? [x, c, 0] : + h < 180 ? [0, c, x] : + h < 240 ? [0, x, c] : + h < 300 ? [x, 0, c] : + [c, 0, x]).map(function (n) { return Math.round((n + m) * 255); }), r = _d[0], g = _d[1], b = _d[2]; + return { r: r, g: g, b: b, a: a }; + } + function rgbToHSL(_a) { + var r255 = _a.r, g255 = _a.g, b255 = _a.b, _b = _a.a, a = _b === void 0 ? 1 : _b; + var r = r255 / 255; + var g = g255 / 255; + var b = b255 / 255; + var max = Math.max(r, g, b); + var min = Math.min(r, g, b); + var c = max - min; + var l = (max + min) / 2; + if (c === 0) { + return { h: 0, s: 0, l: l, a: a }; + } + var h = (max === r ? (((g - b) / c) % 6) : + max === g ? ((b - r) / c + 2) : + ((r - g) / c + 4)) * 60; + if (h < 0) { + h += 360; + } + var s = c / (1 - Math.abs(2 * l - 1)); + return { h: h, s: s, l: l, a: a }; + } + function toFixed(n, digits) { + if (digits === void 0) { digits = 0; } + var fixed = n.toFixed(digits); + if (digits === 0) { + return fixed; + } + var dot = fixed.indexOf('.'); + if (dot >= 0) { + var zerosMatch = fixed.match(/0+$/); + if (zerosMatch) { + if (zerosMatch.index === dot + 1) { + return fixed.substring(0, dot); + } + return fixed.substring(0, zerosMatch.index); + } + } + return fixed; + } + function rgbToString(rgb) { + var r = rgb.r, g = rgb.g, b = rgb.b, a = rgb.a; + if (a != null && a < 1) { + return "rgba(" + toFixed(r) + ", " + toFixed(g) + ", " + toFixed(b) + ", " + toFixed(a, 2) + ")"; + } + return "rgb(" + toFixed(r) + ", " + toFixed(g) + ", " + toFixed(b) + ")"; + } + function rgbToHexString(_a) { + var r = _a.r, g = _a.g, b = _a.b, a = _a.a; + return "#" + (a != null && a < 1 ? [r, g, b, Math.round(a * 255)] : [r, g, b]).map(function (x) { + return "" + (x < 16 ? '0' : '') + x.toString(16); + }).join(''); + } + var rgbMatch = /^rgba?\([^\(\)]+\)$/; + var hslMatch = /^hsla?\([^\(\)]+\)$/; + var hexMatch = /^#[0-9a-f]+$/i; + function parse($color) { + var c = $color.trim().toLowerCase(); + if (c.match(rgbMatch)) { + return parseRGB(c); + } + if (c.match(hslMatch)) { + return parseHSL(c); + } + if (c.match(hexMatch)) { + return parseHex(c); + } + if (knownColors.has(c)) { + return getColorByName(c); + } + if (systemColors.has(c)) { + return getSystemColor(c); + } + if ($color === 'transparent') { + return { r: 0, g: 0, b: 0, a: 0 }; + } + throw new Error("Unable to parse " + $color); + } + function getNumbersFromString(str, splitter, range, units) { + var raw = str.split(splitter).filter(function (x) { return x; }); + var unitsList = Object.entries(units); + var numbers = raw.map(function (r) { return r.trim(); }).map(function (r, i) { + var n; + var unit = unitsList.find(function (_a) { + var u = _a[0]; + return r.endsWith(u); + }); + if (unit) { + n = parseFloat(r.substring(0, r.length - unit[0].length)) / unit[1] * range[i]; + } + else { + n = parseFloat(r); + } + if (range[i] > 1) { + return Math.round(n); + } + return n; + }); + return numbers; + } + var rgbSplitter = /rgba?|\(|\)|\/|,|\s/ig; + var rgbRange = [255, 255, 255, 1]; + var rgbUnits = { '%': 100 }; + function parseRGB($rgb) { + var _a = getNumbersFromString($rgb, rgbSplitter, rgbRange, rgbUnits), r = _a[0], g = _a[1], b = _a[2], _b = _a[3], a = _b === void 0 ? 1 : _b; + return { r: r, g: g, b: b, a: a }; + } + var hslSplitter = /hsla?|\(|\)|\/|,|\s/ig; + var hslRange = [360, 1, 1, 1]; + var hslUnits = { '%': 100, 'deg': 360, 'rad': 2 * Math.PI, 'turn': 1 }; + function parseHSL($hsl) { + var _a = getNumbersFromString($hsl, hslSplitter, hslRange, hslUnits), h = _a[0], s = _a[1], l = _a[2], _b = _a[3], a = _b === void 0 ? 1 : _b; + return hslToRGB({ h: h, s: s, l: l, a: a }); + } + function parseHex($hex) { + var h = $hex.substring(1); + switch (h.length) { + case 3: + case 4: { + var _a = [0, 1, 2].map(function (i) { return parseInt("" + h[i] + h[i], 16); }), r = _a[0], g = _a[1], b = _a[2]; + var a = h.length === 3 ? 1 : (parseInt("" + h[3] + h[3], 16) / 255); + return { r: r, g: g, b: b, a: a }; + } + case 6: + case 8: { + var _b = [0, 2, 4].map(function (i) { return parseInt(h.substring(i, i + 2), 16); }), r = _b[0], g = _b[1], b = _b[2]; + var a = h.length === 6 ? 1 : (parseInt(h.substring(6, 8), 16) / 255); + return { r: r, g: g, b: b, a: a }; + } + } + throw new Error("Unable to parse " + $hex); + } + function getColorByName($color) { + var n = knownColors.get($color); + return { + r: (n >> 16) & 255, + g: (n >> 8) & 255, + b: (n >> 0) & 255, + a: 1 + }; + } + function getSystemColor($color) { + var n = systemColors.get($color); + return { + r: (n >> 16) & 255, + g: (n >> 8) & 255, + b: (n >> 0) & 255, + a: 1 + }; + } + var knownColors = new Map(Object.entries({ + aliceblue: 0xf0f8ff, + antiquewhite: 0xfaebd7, + aqua: 0x00ffff, + aquamarine: 0x7fffd4, + azure: 0xf0ffff, + beige: 0xf5f5dc, + bisque: 0xffe4c4, + black: 0x000000, + blanchedalmond: 0xffebcd, + blue: 0x0000ff, + blueviolet: 0x8a2be2, + brown: 0xa52a2a, + burlywood: 0xdeb887, + cadetblue: 0x5f9ea0, + chartreuse: 0x7fff00, + chocolate: 0xd2691e, + coral: 0xff7f50, + cornflowerblue: 0x6495ed, + cornsilk: 0xfff8dc, + crimson: 0xdc143c, + cyan: 0x00ffff, + darkblue: 0x00008b, + darkcyan: 0x008b8b, + darkgoldenrod: 0xb8860b, + darkgray: 0xa9a9a9, + darkgrey: 0xa9a9a9, + darkgreen: 0x006400, + darkkhaki: 0xbdb76b, + darkmagenta: 0x8b008b, + darkolivegreen: 0x556b2f, + darkorange: 0xff8c00, + darkorchid: 0x9932cc, + darkred: 0x8b0000, + darksalmon: 0xe9967a, + darkseagreen: 0x8fbc8f, + darkslateblue: 0x483d8b, + darkslategray: 0x2f4f4f, + darkslategrey: 0x2f4f4f, + darkturquoise: 0x00ced1, + darkviolet: 0x9400d3, + deeppink: 0xff1493, + deepskyblue: 0x00bfff, + dimgray: 0x696969, + dimgrey: 0x696969, + dodgerblue: 0x1e90ff, + firebrick: 0xb22222, + floralwhite: 0xfffaf0, + forestgreen: 0x228b22, + fuchsia: 0xff00ff, + gainsboro: 0xdcdcdc, + ghostwhite: 0xf8f8ff, + gold: 0xffd700, + goldenrod: 0xdaa520, + gray: 0x808080, + grey: 0x808080, + green: 0x008000, + greenyellow: 0xadff2f, + honeydew: 0xf0fff0, + hotpink: 0xff69b4, + indianred: 0xcd5c5c, + indigo: 0x4b0082, + ivory: 0xfffff0, + khaki: 0xf0e68c, + lavender: 0xe6e6fa, + lavenderblush: 0xfff0f5, + lawngreen: 0x7cfc00, + lemonchiffon: 0xfffacd, + lightblue: 0xadd8e6, + lightcoral: 0xf08080, + lightcyan: 0xe0ffff, + lightgoldenrodyellow: 0xfafad2, + lightgray: 0xd3d3d3, + lightgrey: 0xd3d3d3, + lightgreen: 0x90ee90, + lightpink: 0xffb6c1, + lightsalmon: 0xffa07a, + lightseagreen: 0x20b2aa, + lightskyblue: 0x87cefa, + lightslategray: 0x778899, + lightslategrey: 0x778899, + lightsteelblue: 0xb0c4de, + lightyellow: 0xffffe0, + lime: 0x00ff00, + limegreen: 0x32cd32, + linen: 0xfaf0e6, + magenta: 0xff00ff, + maroon: 0x800000, + mediumaquamarine: 0x66cdaa, + mediumblue: 0x0000cd, + mediumorchid: 0xba55d3, + mediumpurple: 0x9370db, + mediumseagreen: 0x3cb371, + mediumslateblue: 0x7b68ee, + mediumspringgreen: 0x00fa9a, + mediumturquoise: 0x48d1cc, + mediumvioletred: 0xc71585, + midnightblue: 0x191970, + mintcream: 0xf5fffa, + mistyrose: 0xffe4e1, + moccasin: 0xffe4b5, + navajowhite: 0xffdead, + navy: 0x000080, + oldlace: 0xfdf5e6, + olive: 0x808000, + olivedrab: 0x6b8e23, + orange: 0xffa500, + orangered: 0xff4500, + orchid: 0xda70d6, + palegoldenrod: 0xeee8aa, + palegreen: 0x98fb98, + paleturquoise: 0xafeeee, + palevioletred: 0xdb7093, + papayawhip: 0xffefd5, + peachpuff: 0xffdab9, + peru: 0xcd853f, + pink: 0xffc0cb, + plum: 0xdda0dd, + powderblue: 0xb0e0e6, + purple: 0x800080, + rebeccapurple: 0x663399, + red: 0xff0000, + rosybrown: 0xbc8f8f, + royalblue: 0x4169e1, + saddlebrown: 0x8b4513, + salmon: 0xfa8072, + sandybrown: 0xf4a460, + seagreen: 0x2e8b57, + seashell: 0xfff5ee, + sienna: 0xa0522d, + silver: 0xc0c0c0, + skyblue: 0x87ceeb, + slateblue: 0x6a5acd, + slategray: 0x708090, + slategrey: 0x708090, + snow: 0xfffafa, + springgreen: 0x00ff7f, + steelblue: 0x4682b4, + tan: 0xd2b48c, + teal: 0x008080, + thistle: 0xd8bfd8, + tomato: 0xff6347, + turquoise: 0x40e0d0, + violet: 0xee82ee, + wheat: 0xf5deb3, + white: 0xffffff, + whitesmoke: 0xf5f5f5, + yellow: 0xffff00, + yellowgreen: 0x9acd32, + })); + var systemColors = new Map(Object.entries({ + ActiveBorder: 0x3b99fc, + ActiveCaption: 0x000000, + AppWorkspace: 0xaaaaaa, + Background: 0x6363ce, + ButtonFace: 0xffffff, + ButtonHighlight: 0xe9e9e9, + ButtonShadow: 0x9fa09f, + ButtonText: 0x000000, + CaptionText: 0x000000, + GrayText: 0x7f7f7f, + Highlight: 0xb2d7ff, + HighlightText: 0x000000, + InactiveBorder: 0xffffff, + InactiveCaption: 0xffffff, + InactiveCaptionText: 0x000000, + InfoBackground: 0xfbfcc5, + InfoText: 0x000000, + Menu: 0xf6f6f6, + MenuText: 0xffffff, + Scrollbar: 0xaaaaaa, + ThreeDDarkShadow: 0x000000, + ThreeDFace: 0xc0c0c0, + ThreeDHighlight: 0xffffff, + ThreeDLightShadow: 0xffffff, + ThreeDShadow: 0x000000, + Window: 0xececec, + WindowFrame: 0xaaaaaa, + WindowText: 0x000000, + '-webkit-focus-ring-color': 0xe59700 + }).map(function (_a) { + var key = _a[0], value = _a[1]; + return [key.toLowerCase(), value]; + })); + + function scale(x, inLow, inHigh, outLow, outHigh) { + return (x - inLow) * (outHigh - outLow) / (inHigh - inLow) + outLow; + } + function clamp(x, min, max) { + return Math.min(max, Math.max(min, x)); + } + function multiplyMatrices(m1, m2) { + var result = []; + for (var i = 0; i < m1.length; i++) { + result[i] = []; + for (var j = 0; j < m2[0].length; j++) { + var sum = 0; + for (var k = 0; k < m1[0].length; k++) { + sum += m1[i][k] * m2[k][j]; + } + result[i][j] = sum; + } + } + return result; + } + + function getMatches(regex, input, group) { + if (group === void 0) { group = 0; } + var matches = []; + var m; + while (m = regex.exec(input)) { + matches.push(m[group]); + } + return matches; + } + + function createFilterMatrix(config) { + var m = Matrix.identity(); + if (config.sepia !== 0) { + m = multiplyMatrices(m, Matrix.sepia(config.sepia / 100)); + } + if (config.grayscale !== 0) { + m = multiplyMatrices(m, Matrix.grayscale(config.grayscale / 100)); + } + if (config.contrast !== 100) { + m = multiplyMatrices(m, Matrix.contrast(config.contrast / 100)); + } + if (config.brightness !== 100) { + m = multiplyMatrices(m, Matrix.brightness(config.brightness / 100)); + } + if (config.mode === 1) { + m = multiplyMatrices(m, Matrix.invertNHue()); + } + return m; + } + function applyColorMatrix(_a, matrix) { + var r = _a[0], g = _a[1], b = _a[2]; + var rgb = [[r / 255], [g / 255], [b / 255], [1], [1]]; + var result = multiplyMatrices(matrix, rgb); + return [0, 1, 2].map(function (i) { return clamp(Math.round(result[i][0] * 255), 0, 255); }); + } + var Matrix = { + identity: function () { + return [ + [1, 0, 0, 0, 0], + [0, 1, 0, 0, 0], + [0, 0, 1, 0, 0], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + }, + invertNHue: function () { + return [ + [0.333, -0.667, -0.667, 0, 1], + [-0.667, 0.333, -0.667, 0, 1], + [-0.667, -0.667, 0.333, 0, 1], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + }, + brightness: function (v) { + return [ + [v, 0, 0, 0, 0], + [0, v, 0, 0, 0], + [0, 0, v, 0, 0], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + }, + contrast: function (v) { + var t = (1 - v) / 2; + return [ + [v, 0, 0, 0, t], + [0, v, 0, 0, t], + [0, 0, v, 0, t], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + }, + sepia: function (v) { + return [ + [(0.393 + 0.607 * (1 - v)), (0.769 - 0.769 * (1 - v)), (0.189 - 0.189 * (1 - v)), 0, 0], + [(0.349 - 0.349 * (1 - v)), (0.686 + 0.314 * (1 - v)), (0.168 - 0.168 * (1 - v)), 0, 0], + [(0.272 - 0.272 * (1 - v)), (0.534 - 0.534 * (1 - v)), (0.131 + 0.869 * (1 - v)), 0, 0], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + }, + grayscale: function (v) { + return [ + [(0.2126 + 0.7874 * (1 - v)), (0.7152 - 0.7152 * (1 - v)), (0.0722 - 0.0722 * (1 - v)), 0, 0], + [(0.2126 - 0.2126 * (1 - v)), (0.7152 + 0.2848 * (1 - v)), (0.0722 - 0.0722 * (1 - v)), 0, 0], + [(0.2126 - 0.2126 * (1 - v)), (0.7152 - 0.7152 * (1 - v)), (0.0722 + 0.9278 * (1 - v)), 0, 0], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + }, + }; + + var colorModificationCache = new Map(); + function clearColorModificationCache() { + colorModificationCache.clear(); + } + function modifyColorWithCache(rgb, filter, modifyHSL) { + var fnCache; + if (colorModificationCache.has(modifyHSL)) { + fnCache = colorModificationCache.get(modifyHSL); + } + else { + fnCache = new Map(); + colorModificationCache.set(modifyHSL, fnCache); + } + var id = Object.entries(rgb) + .concat(Object.entries(filter).filter(function (_a) { + var key = _a[0]; + return ['mode', 'brightness', 'contrast', 'grayscale', 'sepia'].indexOf(key) >= 0; + })) + .map(function (_a) { + var key = _a[0], value = _a[1]; + return key + ":" + value; + }) + .join(';'); + if (fnCache.has(id)) { + return fnCache.get(id); + } + var hsl = rgbToHSL(rgb); + var modified = modifyHSL(hsl); + var _a = hslToRGB(modified), r = _a.r, g = _a.g, b = _a.b, a = _a.a; + var matrix = createFilterMatrix(filter); + var _b = applyColorMatrix([r, g, b], matrix), rf = _b[0], gf = _b[1], bf = _b[2]; + var color = (a === 1 ? + rgbToHexString({ r: rf, g: gf, b: bf }) : + rgbToString({ r: rf, g: gf, b: bf, a: a })); + fnCache.set(id, color); + return color; + } + function noopHSL(hsl) { + return hsl; + } + function modifyColor(rgb, theme) { + return modifyColorWithCache(rgb, theme, noopHSL); + } + function modifyLightModeHSL(_a) { + var h = _a.h, s = _a.s, l = _a.l, a = _a.a; + var lMin = 0; + var lMid = 0.4; + var lMax = 0.9; + var sNeutralLim = 0.36; + var lNeutralDark = 0.2; + var lNeutralLight = 0.8; + var sColored = 0.16; + var hColoredL0 = 205; + var hColoredL1 = 40; + var lx = scale(l, 0, 1, lMin, lMax); + var hx = h; + var sx = s; + var isNeutral = l < lNeutralDark || l > lNeutralLight || s < sNeutralLim; + if (isNeutral) { + sx = (l < lMid ? + scale(l, 0, lMid, sColored, 0) : + scale(l, lMid, 1, 0, sColored)); + hx = (l < lMid ? hColoredL0 : hColoredL1); + } + return { h: hx, s: sx, l: lx, a: a }; + } + function modifyBgHSL(_a) { + var h = _a.h, s = _a.s, l = _a.l, a = _a.a; + var lMin = 0.1; + var lMaxS0 = 0.25; + var lMaxS1 = 0.4; + var sNeutralLim = 0.12; + var lNeutralLight = 0.8; + var sColored = 0.05; + var hColored = 205; + var hBlue0 = 200; + var hBlue1 = 280; + var lMax = scale(s, 0, 1, lMaxS0, lMaxS1); + var lx = (l < lMax ? + l : + l < 0.5 ? + lMax : + scale(l, 0.5, 1, lMax, lMin)); + var isNeutral = (l >= lNeutralLight && h > hBlue0 && h < hBlue1) || s < sNeutralLim; + var hx = h; + var sx = s; + if (isNeutral) { + sx = sColored; + hx = hColored; + } + return { h: hx, s: sx, l: lx, a: a }; + } + function modifyBackgroundColor(rgb, filter) { + if (filter.mode === 0) { + return modifyColorWithCache(rgb, filter, modifyLightModeHSL); + } + return modifyColorWithCache(rgb, __assign(__assign({}, filter), { mode: 0 }), modifyBgHSL); + } + function modifyFgHSL(_a) { + var h = _a.h, s = _a.s, l = _a.l, a = _a.a; + var lMax = 0.9; + var lMinS0 = 0.7; + var lMinS1 = 0.6; + var sNeutralLim = 0.24; + var lNeutralDark = 0.2; + var sColored = 0.10; + var hColored = 40; + var hBlue0 = 205; + var hBlue1 = 245; + var hBlueMax = 220; + var lBlueMin = 0.7; + var isBlue = h > hBlue0 && h <= hBlue1; + var lMin = scale(s, 0, 1, isBlue ? scale(h, hBlue0, hBlue1, lMinS0, lBlueMin) : lMinS0, lMinS1); + var lx = (l < 0.5 ? + scale(l, 0, 0.5, lMax, lMin) : + l < lMin ? + lMin : + l); + var hx = h; + var sx = s; + if (isBlue) { + hx = scale(hx, hBlue0, hBlue1, hBlue0, hBlueMax); + } + var isNeutral = l < lNeutralDark || s < sNeutralLim; + if (isNeutral) { + sx = sColored; + hx = hColored; + } + return { h: hx, s: sx, l: lx, a: a }; + } + function modifyForegroundColor(rgb, filter) { + if (filter.mode === 0) { + return modifyColorWithCache(rgb, filter, modifyLightModeHSL); + } + return modifyColorWithCache(rgb, __assign(__assign({}, filter), { mode: 0 }), modifyFgHSL); + } + function modifyBorderHSL(_a) { + var h = _a.h, s = _a.s, l = _a.l, a = _a.a; + var lMinS0 = 0.2; + var lMinS1 = 0.3; + var lMaxS0 = 0.4; + var lMaxS1 = 0.5; + var lMin = scale(s, 0, 1, lMinS0, lMinS1); + var lMax = scale(s, 0, 1, lMaxS0, lMaxS1); + var lx = scale(l, 0, 1, lMax, lMin); + return { h: h, s: s, l: lx, a: a }; + } + function modifyBorderColor(rgb, filter) { + if (filter.mode === 0) { + return modifyColorWithCache(rgb, filter, modifyLightModeHSL); + } + return modifyColorWithCache(rgb, __assign(__assign({}, filter), { mode: 0 }), modifyBorderHSL); + } + function modifyShadowColor(rgb, filter) { + return modifyBackgroundColor(rgb, filter); + } + function modifyGradientColor(rgb, filter) { + return modifyBackgroundColor(rgb, filter); + } + + function getURLHost(url) { + return url.match(/^(.*?\/{2,3})?(.+?)(\/|$)/)[2]; + } + + function createTextStyle(config) { + var lines = []; + lines.push('* {'); + if (config.useFont && config.fontFamily) { + lines.push(" font-family: " + config.fontFamily + " !important;"); + } + if (config.textStroke > 0) { + lines.push(" -webkit-text-stroke: " + config.textStroke + "px !important;"); + lines.push(" text-stroke: " + config.textStroke + "px !important;"); + } + lines.push('}'); + return lines.join('\n'); + } + + var FilterMode; + (function (FilterMode) { + FilterMode[FilterMode["light"] = 0] = "light"; + FilterMode[FilterMode["dark"] = 1] = "dark"; + })(FilterMode || (FilterMode = {})); + function getCSSFilterValue(config) { + var filters = []; + if (config.mode === FilterMode.dark) { + filters.push('invert(100%) hue-rotate(180deg)'); + } + if (config.brightness !== 100) { + filters.push("brightness(" + config.brightness + "%)"); + } + if (config.contrast !== 100) { + filters.push("contrast(" + config.contrast + "%)"); + } + if (config.grayscale !== 0) { + filters.push("grayscale(" + config.grayscale + "%)"); + } + if (config.sepia !== 0) { + filters.push("sepia(" + config.sepia + "%)"); + } + if (filters.length === 0) { + return null; + } + return filters.join(' '); + } + + function toSVGMatrix(matrix) { + return matrix.slice(0, 4).map(function (m) { return m.map(function (m) { return m.toFixed(3); }).join(' '); }).join(' '); + } + function getSVGFilterMatrixValue(config) { + return toSVGMatrix(createFilterMatrix(config)); + } + + var counter = 0; + var resolvers = new Map(); + var rejectors = new Map(); + function bgFetch(request) { + return new Promise(function (resolve, reject) { + var id = ++counter; + resolvers.set(id, resolve); + rejectors.set(id, reject); + chrome.runtime.sendMessage({ type: 'fetch', data: request, id: id }); + }); + } + chrome.runtime.onMessage.addListener(function (_a) { + var type = _a.type, data = _a.data, error = _a.error, id = _a.id; + if (type === 'fetch-response') { + var resolve = resolvers.get(id); + var reject = rejectors.get(id); + resolvers.delete(id); + rejectors.delete(id); + if (error) { + reject && reject(error); + } + else { + resolve && resolve(data); + } + } + }); + + function getImageDetails(url) { + return __awaiter(this, void 0, void 0, function () { + var dataURL, image, info; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!url.startsWith('data:')) return [3, 1]; + dataURL = url; + return [3, 3]; + case 1: return [4, getImageDataURL(url)]; + case 2: + dataURL = _a.sent(); + _a.label = 3; + case 3: return [4, urlToImage(dataURL)]; + case 4: + image = _a.sent(); + info = analyzeImage(image); + return [2, __assign({ src: url, dataURL: dataURL, width: image.naturalWidth, height: image.naturalHeight }, info)]; + } + }); + }); + } + function getImageDataURL(url) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!(getURLHost(url) === location.host)) return [3, 2]; + return [4, loadAsDataURL(url)]; + case 1: return [2, _a.sent()]; + case 2: return [4, bgFetch({ url: url, responseType: 'data-url' })]; + case 3: return [2, _a.sent()]; + } + }); + }); + } + function urlToImage(url) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2, new Promise(function (resolve, reject) { + var image = new Image(); + image.onload = function () { return resolve(image); }; + image.onerror = function () { return reject("Unable to load image " + url); }; + image.src = url; + })]; + }); + }); + } + function analyzeImage(image) { + var MAX_ANALIZE_PIXELS_COUNT = 32 * 32; + var naturalPixelsCount = image.naturalWidth * image.naturalHeight; + var k = Math.min(1, Math.sqrt(MAX_ANALIZE_PIXELS_COUNT / naturalPixelsCount)); + var width = Math.max(1, Math.round(image.naturalWidth * k)); + var height = Math.max(1, Math.round(image.naturalHeight * k)); + var canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + var context = canvas.getContext('2d'); + context.imageSmoothingEnabled = false; + context.drawImage(image, 0, 0, width, height); + var imageData = context.getImageData(0, 0, width, height); + var d = imageData.data; + var TRANSPARENT_ALPHA_THRESHOLD = 0.05; + var DARK_LIGHTNESS_THRESHOLD = 0.4; + var LIGHT_LIGHTNESS_THRESHOLD = 0.7; + var transparentPixelsCount = 0; + var darkPixelsCount = 0; + var lightPixelsCount = 0; + var i, x, y; + var r, g, b, a; + var l, min, max; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + i = 4 * (y * width + x); + r = d[i + 0] / 255; + g = d[i + 1] / 255; + b = d[i + 2] / 255; + a = d[i + 3] / 255; + if (a < TRANSPARENT_ALPHA_THRESHOLD) { + transparentPixelsCount++; + } + else { + min = Math.min(r, g, b); + max = Math.max(r, g, b); + l = (max + min) / 2; + if (l < DARK_LIGHTNESS_THRESHOLD) { + darkPixelsCount++; + } + if (l > LIGHT_LIGHTNESS_THRESHOLD) { + lightPixelsCount++; + } + } + } + } + var totalPixelsCount = width * height; + var opaquePixelsCount = totalPixelsCount - transparentPixelsCount; + var DARK_IMAGE_THRESHOLD = 0.7; + var LIGHT_IMAGE_THRESHOLD = 0.7; + var TRANSPARENT_IMAGE_THRESHOLD = 0.1; + var LARGE_IMAGE_PIXELS_COUNT = 800 * 600; + return { + isDark: ((darkPixelsCount / opaquePixelsCount) >= DARK_IMAGE_THRESHOLD), + isLight: ((lightPixelsCount / opaquePixelsCount) >= LIGHT_IMAGE_THRESHOLD), + isTransparent: ((transparentPixelsCount / totalPixelsCount) >= TRANSPARENT_IMAGE_THRESHOLD), + isLarge: (naturalPixelsCount >= LARGE_IMAGE_PIXELS_COUNT), + }; + } + function getFilteredImageDataURL(_a, filter) { + var dataURL = _a.dataURL, width = _a.width, height = _a.height; + var matrix = getSVGFilterMatrixValue(filter); + var svg = [ + "", + '', + '', + "", + '', + '', + "", + '', + ].join(''); + var bytes = new Uint8Array(svg.length); + for (var i = 0; i < svg.length; i++) { + bytes[i] = svg.charCodeAt(i); + } + var blob = new Blob([bytes], { type: 'image/svg+xml' }); + var objectURL = URL.createObjectURL(blob); + return objectURL; + } + + function getModifiableCSSDeclaration(property, value, rule, isCancelled) { + var important = Boolean(rule && rule.style && rule.style.getPropertyPriority(property)); + var sourceValue = value; + if (property.startsWith('--')) { + return null; + } + else if ((property.indexOf('color') >= 0 && property !== '-webkit-print-color-adjust') || + property === 'fill' || + property === 'stroke') { + var modifier = getColorModifier(property, value); + if (modifier) { + return { property: property, value: modifier, important: important, sourceValue: sourceValue }; + } + } + else if (property === 'background-image') { + var modifier = getBgImageModifier(property, value, rule, isCancelled); + if (modifier) { + return { property: property, value: modifier, important: important, sourceValue: sourceValue }; + } + } + else if (property.indexOf('shadow') >= 0) { + var modifier = getShadowModifier(property, value); + if (modifier) { + return { property: property, value: modifier, important: important, sourceValue: sourceValue }; + } + } + return null; + } + function getModifiedUserAgentStyle(filter, isIFrame) { + var lines = []; + if (!isIFrame) { + lines.push('html {'); + lines.push(" background-color: " + modifyBackgroundColor({ r: 255, g: 255, b: 255 }, filter) + " !important;"); + lines.push('}'); + } + lines.push((isIFrame ? '' : 'html, body, ') + "input, textarea, select, button {"); + lines.push(" background-color: " + modifyBackgroundColor({ r: 255, g: 255, b: 255 }, filter) + ";"); + lines.push('}'); + lines.push('html, body, input, textarea, select, button {'); + lines.push(" border-color: " + modifyBorderColor({ r: 76, g: 76, b: 76 }, filter) + ";"); + lines.push(" color: " + modifyForegroundColor({ r: 0, g: 0, b: 0 }, filter) + ";"); + lines.push('}'); + lines.push('a {'); + lines.push(" color: " + modifyForegroundColor({ r: 0, g: 64, b: 255 }, filter) + ";"); + lines.push('}'); + lines.push('table {'); + lines.push(" border-color: " + modifyBorderColor({ r: 128, g: 128, b: 128 }, filter) + ";"); + lines.push('}'); + lines.push('::placeholder {'); + lines.push(" color: " + modifyForegroundColor({ r: 169, g: 169, b: 169 }, filter) + ";"); + lines.push('}'); + ['::selection', '::-moz-selection'].forEach(function (selection) { + lines.push(selection + " {"); + lines.push(" background-color: " + modifyBackgroundColor({ r: 0, g: 96, b: 212 }, filter) + ";"); + lines.push(" color: " + modifyForegroundColor({ r: 255, g: 255, b: 255 }, filter) + ";"); + lines.push('}'); + }); + lines.push('input:-webkit-autofill,'); + lines.push('textarea:-webkit-autofill,'); + lines.push('select:-webkit-autofill {'); + lines.push(" background-color: " + modifyBackgroundColor({ r: 250, g: 255, b: 189 }, filter) + " !important;"); + lines.push(" color: " + modifyForegroundColor({ r: 0, g: 0, b: 0 }, filter) + " !important;"); + lines.push('}'); + if (!isMacOS()) { + lines.push('::-webkit-scrollbar {'); + lines.push(" background-color: " + modifyBackgroundColor({ r: 241, g: 241, b: 241 }, filter) + ";"); + lines.push(" color: " + modifyForegroundColor({ r: 96, g: 96, b: 96 }, filter) + ";"); + lines.push('}'); + lines.push('::-webkit-scrollbar-thumb {'); + lines.push(" background-color: " + modifyBackgroundColor({ r: 193, g: 193, b: 193 }, filter) + ";"); + lines.push('}'); + lines.push('::-webkit-scrollbar-thumb:hover {'); + lines.push(" background-color: " + modifyBackgroundColor({ r: 166, g: 166, b: 166 }, filter) + ";"); + lines.push('}'); + lines.push('::-webkit-scrollbar-thumb:active {'); + lines.push(" background-color: " + modifyBackgroundColor({ r: 96, g: 96, b: 96 }, filter) + ";"); + lines.push('}'); + lines.push('::-webkit-scrollbar-corner {'); + lines.push(" background-color: " + modifyBackgroundColor({ r: 255, g: 255, b: 255 }, filter) + ";"); + lines.push('}'); + lines.push('* {'); + lines.push(" scrollbar-color: " + modifyBackgroundColor({ r: 193, g: 193, b: 193 }, filter) + " " + modifyBackgroundColor({ r: 241, g: 241, b: 241 }, filter) + ";"); + lines.push('}'); + } + return lines.join('\n'); + } + function getModifiedFallbackStyle(filter, _a) { + var strict = _a.strict; + var lines = []; + lines.push("html, body, " + (strict ? 'body *' : 'body > *') + " {"); + lines.push(" background-color: " + modifyBackgroundColor({ r: 255, g: 255, b: 255 }, filter) + " !important;"); + lines.push(" border-color: " + modifyBorderColor({ r: 64, g: 64, b: 64 }, filter) + " !important;"); + lines.push(" color: " + modifyForegroundColor({ r: 0, g: 0, b: 0 }, filter) + " !important;"); + lines.push('}'); + return lines.join('\n'); + } + var unparsableColors = new Set([ + 'inherit', + 'transparent', + 'initial', + 'currentcolor', + 'none', + ]); + var colorParseCache = new Map(); + function parseColorWithCache($color) { + $color = $color.trim(); + if (colorParseCache.has($color)) { + return colorParseCache.get($color); + } + var color = parse($color); + colorParseCache.set($color, color); + return color; + } + function tryParseColor($color) { + try { + return parseColorWithCache($color); + } + catch (err) { + return null; + } + } + function getColorModifier(prop, value) { + if (unparsableColors.has(value.toLowerCase())) { + return value; + } + try { + var rgb_1 = parseColorWithCache(value); + if (prop.indexOf('background') >= 0) { + return function (filter) { return modifyBackgroundColor(rgb_1, filter); }; + } + if (prop.indexOf('border') >= 0 || prop.indexOf('outline') >= 0) { + return function (filter) { return modifyBorderColor(rgb_1, filter); }; + } + return function (filter) { return modifyForegroundColor(rgb_1, filter); }; + } + catch (err) { + logWarn('Color parse error', err); + return null; + } + } + var gradientRegex = /[\-a-z]+gradient\(([^\(\)]*(\(([^\(\)]*(\(.*?\)))*[^\(\)]*\))){0,15}[^\(\)]*\)/g; + var imageDetailsCache = new Map(); + var awaitingForImageLoading = new Map(); + function getBgImageModifier(prop, value, rule, isCancelled) { + var _this = this; + try { + var gradients = getMatches(gradientRegex, value); + var urls = getMatches(cssURLRegex, value); + if (urls.length === 0 && gradients.length === 0) { + return value; + } + var getIndices = function (matches) { + var index = 0; + return matches.map(function (match) { + var valueIndex = value.indexOf(match, index); + index = valueIndex + match.length; + return { match: match, index: valueIndex }; + }); + }; + var matches_1 = getIndices(urls).map(function (i) { return (__assign({ type: 'url' }, i)); }) + .concat(getIndices(gradients).map(function (i) { return (__assign({ type: 'gradient' }, i)); })) + .sort(function (a, b) { return a.index - b.index; }); + var getGradientModifier_1 = function (gradient) { + var match = gradient.match(/^(.*-gradient)\((.*)\)$/); + var type = match[1]; + var content = match[2]; + var partsRegex = /([^\(\),]+(\([^\(\)]*(\([^\(\)]*\)*[^\(\)]*)?\))?[^\(\),]*),?/g; + var colorStopRegex = /^(from|color-stop|to)\(([^\(\)]*?,\s*)?(.*?)\)$/; + var parts = getMatches(partsRegex, content, 1).map(function (part) { + part = part.trim(); + var rgb = tryParseColor(part); + if (rgb) { + return function (filter) { return modifyGradientColor(rgb, filter); }; + } + var space = part.lastIndexOf(' '); + rgb = tryParseColor(part.substring(0, space)); + if (rgb) { + return function (filter) { return modifyGradientColor(rgb, filter) + " " + part.substring(space + 1); }; + } + var colorStopMatch = part.match(colorStopRegex); + if (colorStopMatch) { + rgb = tryParseColor(colorStopMatch[3]); + if (rgb) { + return function (filter) { return colorStopMatch[1] + "(" + (colorStopMatch[2] ? colorStopMatch[2] + ", " : '') + modifyGradientColor(rgb, filter) + ")"; }; + } + } + return function () { return part; }; + }); + return function (filter) { + return type + "(" + parts.map(function (modify) { return modify(filter); }).join(', ') + ")"; + }; + }; + var getURLModifier_1 = function (urlValue) { + var url = getCSSURLValue(urlValue); + if (rule.parentStyleSheet.href) { + var basePath = getCSSBaseBath(rule.parentStyleSheet.href); + url = getAbsoluteURL(basePath, url); + } + else if (rule.parentStyleSheet.ownerNode && rule.parentStyleSheet.ownerNode.baseURI) { + url = getAbsoluteURL(rule.parentStyleSheet.ownerNode.baseURI, url); + } + else { + url = getAbsoluteURL(location.origin, url); + } + var absoluteValue = "url(\"" + url + "\")"; + return function (filter) { return __awaiter(_this, void 0, void 0, function () { + var imageDetails, awaiters_1, err_1, bgImageValue; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!imageDetailsCache.has(url)) return [3, 1]; + imageDetails = imageDetailsCache.get(url); + return [3, 7]; + case 1: + _a.trys.push([1, 6, , 7]); + if (!awaitingForImageLoading.has(url)) return [3, 3]; + awaiters_1 = awaitingForImageLoading.get(url); + return [4, new Promise(function (resolve) { return awaiters_1.push(resolve); })]; + case 2: + imageDetails = _a.sent(); + if (!imageDetails) { + return [2, null]; + } + return [3, 5]; + case 3: + awaitingForImageLoading.set(url, []); + return [4, getImageDetails(url)]; + case 4: + imageDetails = _a.sent(); + imageDetailsCache.set(url, imageDetails); + awaitingForImageLoading.get(url).forEach(function (resolve) { return resolve(imageDetails); }); + awaitingForImageLoading.delete(url); + _a.label = 5; + case 5: + if (isCancelled()) { + return [2, null]; + } + return [3, 7]; + case 6: + err_1 = _a.sent(); + logWarn(err_1); + if (awaitingForImageLoading.has(url)) { + awaitingForImageLoading.get(url).forEach(function (resolve) { return resolve(null); }); + awaitingForImageLoading.delete(url); + } + return [2, absoluteValue]; + case 7: + bgImageValue = getBgImageValue_1(imageDetails, filter) || absoluteValue; + return [2, bgImageValue]; + } + }); + }); }; + }; + var getBgImageValue_1 = function (imageDetails, filter) { + var isDark = imageDetails.isDark, isLight = imageDetails.isLight, isTransparent = imageDetails.isTransparent, isLarge = imageDetails.isLarge, width = imageDetails.width; + var result; + if (isDark && isTransparent && filter.mode === 1 && !isLarge && width > 2) { + logInfo("Inverting dark image " + imageDetails.src); + var inverted = getFilteredImageDataURL(imageDetails, __assign(__assign({}, filter), { sepia: clamp(filter.sepia + 10, 0, 100) })); + result = "url(\"" + inverted + "\")"; + } + else if (isLight && !isTransparent && filter.mode === 1) { + if (isLarge) { + result = 'none'; + } + else { + logInfo("Dimming light image " + imageDetails.src); + var dimmed = getFilteredImageDataURL(imageDetails, filter); + result = "url(\"" + dimmed + "\")"; + } + } + else if (filter.mode === 0 && isLight && !isLarge) { + logInfo("Applying filter to image " + imageDetails.src); + var filtered = getFilteredImageDataURL(imageDetails, __assign(__assign({}, filter), { brightness: clamp(filter.brightness - 10, 5, 200), sepia: clamp(filter.sepia + 10, 0, 100) })); + result = "url(\"" + filtered + "\")"; + } + else { + result = null; + } + return result; + }; + var modifiers_1 = []; + var index_1 = 0; + matches_1.forEach(function (_a, i) { + var match = _a.match, type = _a.type, matchStart = _a.index; + var prefixStart = index_1; + var matchEnd = matchStart + match.length; + index_1 = matchEnd; + modifiers_1.push(function () { return value.substring(prefixStart, matchStart); }); + modifiers_1.push(type === 'url' ? getURLModifier_1(match) : getGradientModifier_1(match)); + if (i === matches_1.length - 1) { + modifiers_1.push(function () { return value.substring(matchEnd); }); + } + }); + return function (filter) { + var results = modifiers_1.map(function (modify) { return modify(filter); }); + if (results.some(function (r) { return r instanceof Promise; })) { + return Promise.all(results) + .then(function (asyncResults) { + return asyncResults.join(''); + }); + } + return results.join(''); + }; + } + catch (err) { + logWarn("Unable to parse gradient " + value, err); + return null; + } + } + function getShadowModifier(prop, value) { + try { + var index_2 = 0; + var colorMatches_1 = getMatches(/(^|\s)([a-z]+\(.+?\)|#[0-9a-f]+|[a-z]+)(.*?(inset|outset)?($|,))/ig, value, 2); + var modifiers_2 = colorMatches_1.map(function (match, i) { + var prefixIndex = index_2; + var matchIndex = value.indexOf(match, index_2); + var matchEnd = matchIndex + match.length; + index_2 = matchEnd; + var rgb = tryParseColor(match); + if (!rgb) { + return function () { return value.substring(prefixIndex, matchEnd); }; + } + return function (filter) { return "" + value.substring(prefixIndex, matchIndex) + modifyShadowColor(rgb, filter) + (i === colorMatches_1.length - 1 ? value.substring(matchEnd) : ''); }; + }); + return function (filter) { return modifiers_2.map(function (modify) { return modify(filter); }).join(''); }; + } + catch (err) { + logWarn("Unable to parse shadow " + value, err); + return null; + } + } + function cleanModificationCache() { + colorParseCache.clear(); + clearColorModificationCache(); + imageDetailsCache.clear(); + awaitingForImageLoading.clear(); + } + + var overrides = { + 'background-color': { + customProp: '--darkreader-inline-bgcolor', + cssProp: 'background-color', + dataAttr: 'data-darkreader-inline-bgcolor', + store: new WeakSet(), + }, + 'background-image': { + customProp: '--darkreader-inline-bgimage', + cssProp: 'background-image', + dataAttr: 'data-darkreader-inline-bgimage', + store: new WeakSet(), + }, + 'border-color': { + customProp: '--darkreader-inline-border', + cssProp: 'border-color', + dataAttr: 'data-darkreader-inline-border', + store: new WeakSet(), + }, + 'border-bottom-color': { + customProp: '--darkreader-inline-border-bottom', + cssProp: 'border-bottom-color', + dataAttr: 'data-darkreader-inline-border-bottom', + store: new WeakSet(), + }, + 'border-left-color': { + customProp: '--darkreader-inline-border-left', + cssProp: 'border-left-color', + dataAttr: 'data-darkreader-inline-border-left', + store: new WeakSet(), + }, + 'border-right-color': { + customProp: '--darkreader-inline-border-right', + cssProp: 'border-right-color', + dataAttr: 'data-darkreader-inline-border-right', + store: new WeakSet(), + }, + 'border-top-color': { + customProp: '--darkreader-inline-border-top', + cssProp: 'border-top-color', + dataAttr: 'data-darkreader-inline-border-top', + store: new WeakSet(), + }, + 'box-shadow': { + customProp: '--darkreader-inline-boxshadow', + cssProp: 'box-shadow', + dataAttr: 'data-darkreader-inline-boxshadow', + store: new WeakSet(), + }, + 'color': { + customProp: '--darkreader-inline-color', + cssProp: 'color', + dataAttr: 'data-darkreader-inline-color', + store: new WeakSet(), + }, + 'fill': { + customProp: '--darkreader-inline-fill', + cssProp: 'fill', + dataAttr: 'data-darkreader-inline-fill', + store: new WeakSet(), + }, + 'stroke': { + customProp: '--darkreader-inline-stroke', + cssProp: 'stroke', + dataAttr: 'data-darkreader-inline-stroke', + store: new WeakSet(), + }, + 'outline-color': { + customProp: '--darkreader-inline-outline', + cssProp: 'outline-color', + dataAttr: 'data-darkreader-inline-outline', + store: new WeakSet(), + }, + }; + var overridesList = Object.values(overrides); + var INLINE_STYLE_ATTRS = ['style', 'fill', 'stroke', 'bgcolor', 'color']; + var INLINE_STYLE_SELECTOR = INLINE_STYLE_ATTRS.map(function (attr) { return "[" + attr + "]"; }).join(', '); + function getInlineOverrideStyle() { + return overridesList.map(function (_a) { + var dataAttr = _a.dataAttr, customProp = _a.customProp, cssProp = _a.cssProp; + return [ + "[" + dataAttr + "] {", + " " + cssProp + ": var(" + customProp + ") !important;", + '}', + ].join('\n'); + }).join('\n'); + } + function expand(nodes, selector) { + var results = []; + nodes.forEach(function (n) { + if (n instanceof Element) { + if (n.matches(selector)) { + results.push(n); + } + results.push.apply(results, Array.from(n.querySelectorAll(selector))); + } + }); + return results; + } + var observers = new Map(); + function watchForInlineStyles(elementStyleDidChange, shadowRootDiscovered) { + deepWatchForInlineStyles(document.documentElement, elementStyleDidChange, shadowRootDiscovered); + iterateShadowNodes(document.documentElement, function (node) { + deepWatchForInlineStyles(node.shadowRoot, elementStyleDidChange, shadowRootDiscovered); + }); + } + function deepWatchForInlineStyles(root, elementStyleDidChange, shadowRootDiscovered) { + if (observers.has(root)) { + observers.get(root).disconnect(); + } + var observer = new MutationObserver(function (mutations) { + mutations.forEach(function (m) { + var createdInlineStyles = expand(Array.from(m.addedNodes), INLINE_STYLE_SELECTOR); + if (createdInlineStyles.length > 0) { + createdInlineStyles.forEach(function (el) { return elementStyleDidChange(el); }); + } + if (m.type === 'attributes') { + if (INLINE_STYLE_ATTRS.includes(m.attributeName)) { + elementStyleDidChange(m.target); + } + overridesList + .filter(function (_a) { + var store = _a.store, dataAttr = _a.dataAttr; + return store.has(m.target) && !m.target.hasAttribute(dataAttr); + }) + .forEach(function (_a) { + var dataAttr = _a.dataAttr; + return m.target.setAttribute(dataAttr, ''); + }); + } + }); + mutations.forEach(function (m) { + m.addedNodes.forEach(function (added) { + if (added.isConnected) { + iterateShadowNodes(added, function (n) { + shadowRootDiscovered(n.shadowRoot); + deepWatchForInlineStyles(n.shadowRoot, elementStyleDidChange, shadowRootDiscovered); + }); + } + }); + }); + }); + observer.observe(root, { + childList: true, + subtree: true, + attributes: true, + attributeFilter: INLINE_STYLE_ATTRS.concat(overridesList.map(function (_a) { + var dataAttr = _a.dataAttr; + return dataAttr; + })), + }); + observers.set(root, observer); + } + function stopWatchingForInlineStyles() { + observers.forEach(function (o) { return o.disconnect(); }); + observers.clear(); + } + var inlineStyleCache = new WeakMap(); + var filterProps = ['brightness', 'contrast', 'grayscale', 'sepia', 'mode']; + function getInlineStyleCacheKey(el, theme) { + return INLINE_STYLE_ATTRS + .map(function (attr) { return attr + "=\"" + el.getAttribute(attr) + "\""; }) + .concat(filterProps.map(function (prop) { return prop + "=\"" + theme[prop] + "\""; })) + .join(' '); + } + function overrideInlineStyle(element, theme) { + var cacheKey = getInlineStyleCacheKey(element, theme); + if (cacheKey === inlineStyleCache.get(element)) { + return; + } + var unsetProps = new Set(Object.keys(overrides)); + function setCustomProp(targetCSSProp, modifierCSSProp, cssVal) { + var _a = overrides[targetCSSProp], customProp = _a.customProp, dataAttr = _a.dataAttr; + var mod = getModifiableCSSDeclaration(modifierCSSProp, cssVal, null, null); + if (!mod) { + return; + } + var value = mod.value; + if (typeof value === 'function') { + value = value(theme); + } + element.style.setProperty(customProp, value); + if (!element.hasAttribute(dataAttr)) { + element.setAttribute(dataAttr, ''); + } + unsetProps.delete(targetCSSProp); + } + if (element.hasAttribute('bgcolor')) { + var value = element.getAttribute('bgcolor'); + if (value.match(/^[0-9a-f]{3}$/i) || value.match(/^[0-9a-f]{6}$/i)) { + value = "#" + value; + } + setCustomProp('background-color', 'background-color', value); + } + if (element.hasAttribute('color')) { + var value = element.getAttribute('color'); + if (value.match(/^[0-9a-f]{3}$/i) || value.match(/^[0-9a-f]{6}$/i)) { + value = "#" + value; + } + setCustomProp('color', 'color', value); + } + if (element.hasAttribute('fill') && element instanceof SVGElement) { + var SMALL_SVG_LIMIT = 32; + var value = element.getAttribute('fill'); + var isBg = false; + if (!(element instanceof SVGTextElement)) { + var _a = element.getBoundingClientRect(), width = _a.width, height = _a.height; + isBg = (width > SMALL_SVG_LIMIT || height > SMALL_SVG_LIMIT); + } + setCustomProp('fill', isBg ? 'background-color' : 'color', value); + } + if (element.hasAttribute('stroke')) { + var value = element.getAttribute('stroke'); + setCustomProp('stroke', element instanceof SVGLineElement || element instanceof SVGTextElement ? 'border-color' : 'color', value); + } + element.style && iterateCSSDeclarations(element.style, function (property, value) { + if (property === 'background-image' && value.indexOf('url') >= 0) { + return; + } + if (overrides.hasOwnProperty(property)) { + setCustomProp(property, property, value); + } + }); + if (element.style && element instanceof SVGTextElement && element.style.fill) { + setCustomProp('fill', 'color', element.style.getPropertyValue('fill')); + } + Array.from(unsetProps).forEach(function (cssProp) { + var _a = overrides[cssProp], store = _a.store, dataAttr = _a.dataAttr; + store.delete(element); + element.removeAttribute(dataAttr); + }); + inlineStyleCache.set(element, getInlineStyleCacheKey(element, theme)); + } + + var metaThemeColorName = 'theme-color'; + var metaThemeColorSelector = "meta[name=\"" + metaThemeColorName + "\"]"; + var srcMetaThemeColor = null; + var observer = null; + function changeMetaThemeColor(meta, theme) { + srcMetaThemeColor = srcMetaThemeColor || meta.content; + try { + var color = parse(srcMetaThemeColor); + meta.content = modifyBackgroundColor(color, theme); + } + catch (err) { + logWarn(err); + } + } + function changeMetaThemeColorWhenAvailable(theme) { + var meta = document.querySelector(metaThemeColorSelector); + if (meta) { + changeMetaThemeColor(meta, theme); + } + else { + if (observer) { + observer.disconnect(); + } + observer = new MutationObserver(function (mutations) { + loop: for (var _i = 0, mutations_1 = mutations; _i < mutations_1.length; _i++) { + var m = mutations_1[_i]; + for (var _a = 0, _b = Array.from(m.addedNodes); _a < _b.length; _a++) { + var node = _b[_a]; + if (node instanceof HTMLMetaElement && node.name === metaThemeColorName) { + observer.disconnect(); + observer = null; + changeMetaThemeColor(node, theme); + break loop; + } + } + } + }); + observer.observe(document.head, { childList: true }); + } + } + function restoreMetaThemeColor() { + if (observer) { + observer.disconnect(); + observer = null; + } + var meta = document.querySelector(metaThemeColorSelector); + if (meta && srcMetaThemeColor) { + meta.content = srcMetaThemeColor; + } + } + + var STYLE_SELECTOR = (function () { + var selectors = [ + 'html /deep/ link[rel*="stylesheet" i]:not([disabled])', + 'html /deep/ style', + ':host /deep/ link[rel*="stylesheet" i]:not([disabled])', + ':host /deep/ style', + ':host link[rel*="stylesheet" i]:not([disabled])', + ':host style', + ]; + if (!isDeepSelectorSupported()) { + selectors = selectors.map(function (s) { return s.replace('/deep/ ', ''); }); + } + if (!isHostSelectorSupported()) { + selectors = selectors.filter(function (s) { return s.startsWith(':host'); }); + } + return selectors.join(', '); + })(); + function shouldManageStyle(element) { + return (((element instanceof HTMLStyleElement) || + (element instanceof SVGStyleElement) || + (element instanceof HTMLLinkElement && + element.rel && + element.rel.toLowerCase().includes('stylesheet') && + !element.disabled)) && + !element.classList.contains('darkreader') && + element.media !== 'print'); + } + var asyncQueue = createAsyncTasksQueue(); + function manageStyle(element, _a) { + var update = _a.update, loadingStart = _a.loadingStart, loadingEnd = _a.loadingEnd; + var prevStyles = []; + var next = element; + while ((next = next.nextElementSibling) && next.matches('.darkreader')) { + prevStyles.push(next); + } + var corsCopy = prevStyles.find(function (el) { return el.matches('.darkreader--cors'); }) || null; + var syncStyle = prevStyles.find(function (el) { return el.matches('.darkreader--sync'); }) || null; + var corsCopyPositionWatcher = null; + var syncStylePositionWatcher = null; + var cancelAsyncOperations = false; + function isCancelled() { + return cancelAsyncOperations; + } + var observer = new MutationObserver(function () { + update(); + }); + var observerOptions = { attributes: true, childList: true, characterData: true }; + function containsCSSImport() { + return element instanceof HTMLStyleElement && element.textContent.trim().match(cssImportRegex); + } + function getRulesSync() { + if (corsCopy) { + return corsCopy.sheet.cssRules; + } + if (element.sheet == null) { + return null; + } + if (element instanceof HTMLLinkElement) { + try { + return element.sheet.cssRules; + } + catch (err) { + logWarn(err); + return null; + } + } + if (containsCSSImport()) { + return null; + } + return safeGetSheetRules(); + } + function insertStyle() { + if (corsCopy) { + if (element.nextSibling !== corsCopy) { + element.parentNode.insertBefore(corsCopy, element.nextSibling); + } + if (corsCopy.nextSibling !== syncStyle) { + element.parentNode.insertBefore(syncStyle, corsCopy.nextSibling); + } + } + else if (element.nextSibling !== syncStyle) { + element.parentNode.insertBefore(syncStyle, element.nextSibling); + } + } + function createSyncStyle() { + syncStyle = element instanceof SVGStyleElement ? + document.createElementNS('http://www.w3.org/2000/svg', 'style') : + document.createElement('style'); + syncStyle.classList.add('darkreader'); + syncStyle.classList.add('darkreader--sync'); + syncStyle.media = 'screen'; + } + var isLoadingRules = false; + var wasLoadingError = false; + function getRulesAsync() { + return __awaiter(this, void 0, void 0, function () { + var cssText, cssBasePath, err_1, fullCSSText, err_2; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!(element instanceof HTMLLinkElement)) return [3, 6]; + if (!(element.sheet == null)) return [3, 4]; + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4, linkLoading(element)]; + case 2: + _a.sent(); + if (cancelAsyncOperations) { + return [2, null]; + } + return [3, 4]; + case 3: + err_1 = _a.sent(); + logWarn(err_1); + wasLoadingError = true; + return [2, null]; + case 4: + try { + if (element.sheet.cssRules != null) { + return [2, element.sheet.cssRules]; + } + } + catch (err) { + logWarn(err); + } + return [4, loadText(element.href)]; + case 5: + cssText = _a.sent(); + cssBasePath = getCSSBaseBath(element.href); + if (cancelAsyncOperations) { + return [2, null]; + } + return [3, 7]; + case 6: + if (containsCSSImport()) { + cssText = element.textContent.trim(); + cssBasePath = getCSSBaseBath(location.href); + } + else { + return [2, null]; + } + _a.label = 7; + case 7: + if (!cssText) return [3, 12]; + _a.label = 8; + case 8: + _a.trys.push([8, 10, , 11]); + return [4, replaceCSSImports(cssText, cssBasePath)]; + case 9: + fullCSSText = _a.sent(); + corsCopy = createCORSCopy(element, fullCSSText); + return [3, 11]; + case 10: + err_2 = _a.sent(); + logWarn(err_2); + return [3, 11]; + case 11: + if (corsCopy) { + corsCopyPositionWatcher = watchForNodePosition(corsCopy, { watchParent: true, watchSibling: true }); + return [2, corsCopy.sheet.cssRules]; + } + _a.label = 12; + case 12: return [2, null]; + } + }); + }); + } + function details() { + var rules = getRulesSync(); + if (!rules) { + if (isLoadingRules || wasLoadingError) { + return null; + } + isLoadingRules = true; + loadingStart(); + getRulesAsync().then(function (results) { + isLoadingRules = false; + loadingEnd(); + if (results) { + update(); + } + }).catch(function (err) { + logWarn(err); + isLoadingRules = false; + loadingEnd(); + }); + return null; + } + var variables = getCSSVariables(rules); + return { variables: variables }; + } + function getFilterKey(filter) { + return ['mode', 'brightness', 'contrast', 'grayscale', 'sepia'].map(function (p) { return p + ":" + filter[p]; }).join(';'); + } + var renderId = 0; + var rulesTextCache = new Map(); + var rulesModCache = new Map(); + var prevFilterKey = null; + var forceRestore = false; + function render(filter, variables) { + var rules = getRulesSync(); + if (!rules) { + return; + } + cancelAsyncOperations = false; + var rulesChanged = (rulesModCache.size === 0); + var notFoundCacheKeys = new Set(rulesModCache.keys()); + var filterKey = getFilterKey(filter); + var filterChanged = (filterKey !== prevFilterKey); + var modRules = []; + iterateCSSRules(rules, function (rule) { + var cssText = rule.cssText; + var textDiffersFromPrev = false; + notFoundCacheKeys.delete(cssText); + if (!rulesTextCache.has(cssText)) { + rulesTextCache.set(cssText, cssText); + textDiffersFromPrev = true; + } + var vars = null; + var varsRule = null; + if (variables.size > 0 || cssText.includes('var(')) { + var cssTextWithVariables = replaceCSSVariables(cssText, variables); + if (rulesTextCache.get(cssText) !== cssTextWithVariables) { + rulesTextCache.set(cssText, cssTextWithVariables); + textDiffersFromPrev = true; + vars = document.createElement('style'); + vars.classList.add('darkreader'); + vars.classList.add('darkreader--vars'); + vars.media = 'screen'; + vars.textContent = cssTextWithVariables; + element.parentNode.insertBefore(vars, element.nextSibling); + varsRule = vars.sheet.cssRules[0]; + } + } + if (textDiffersFromPrev) { + rulesChanged = true; + } + else { + modRules.push(rulesModCache.get(cssText)); + return; + } + var modDecs = []; + var targetRule = varsRule || rule; + targetRule && targetRule.style && iterateCSSDeclarations(targetRule.style, function (property, value) { + var mod = getModifiableCSSDeclaration(property, value, rule, isCancelled); + if (mod) { + modDecs.push(mod); + } + }); + var modRule = null; + if (modDecs.length > 0) { + modRule = { selector: rule.selectorText, declarations: modDecs }; + if (rule.parentRule instanceof CSSMediaRule) { + modRule.media = rule.parentRule.media.mediaText; + } + modRules.push(modRule); + } + rulesModCache.set(cssText, modRule); + removeNode(vars); + }); + notFoundCacheKeys.forEach(function (key) { + rulesTextCache.delete(key); + rulesModCache.delete(key); + }); + prevFilterKey = filterKey; + if (!forceRestore && !rulesChanged && !filterChanged) { + return; + } + renderId++; + forceRestore = false; + function setRule(target, index, declarations) { + var selector = declarations[0].selector; + target.insertRule(selector + " {}", index); + var style = target.cssRules.item(index).style; + declarations.forEach(function (_a) { + var property = _a.property, value = _a.value, important = _a.important, sourceValue = _a.sourceValue; + style.setProperty(property, value == null ? sourceValue : value, important ? 'important' : ''); + }); + } + var readyDeclarations = []; + var asyncDeclarations = new Map(); + var asyncDeclarationCounter = 0; + function buildStyleSheet() { + var groups = []; + readyDeclarations.forEach(function (decl, i) { + var mediaGroup; + var selectorGroup; + var prev = i === 0 ? null : readyDeclarations[i - 1]; + var isSameMedia = prev && prev.media === decl.media; + var isSameMediaAndSelector = prev && isSameMedia && prev.selector === decl.selector; + if (isSameMedia) { + mediaGroup = groups[groups.length - 1]; + } + else { + mediaGroup = []; + groups.push(mediaGroup); + } + if (isSameMediaAndSelector) { + selectorGroup = mediaGroup[mediaGroup.length - 1]; + } + else { + selectorGroup = []; + mediaGroup.push(selectorGroup); + } + selectorGroup.push(decl); + }); + if (!syncStyle) { + createSyncStyle(); + } + syncStylePositionWatcher && syncStylePositionWatcher.stop(); + insertStyle(); + var sheet = syncStyle.sheet; + for (var i = sheet.cssRules.length - 1; i >= 0; i--) { + sheet.deleteRule(i); + } + groups.forEach(function (mediaGroup) { + var media = mediaGroup[0][0].media; + var target; + if (media) { + sheet.insertRule("@media " + media + " {}", sheet.cssRules.length); + target = sheet.cssRules[sheet.cssRules.length - 1]; + } + else { + target = sheet; + } + mediaGroup.forEach(function (selectorGroup) { + var asyncItems = selectorGroup.filter(function (_a) { + var value = _a.value; + return value == null; + }); + if (asyncItems.length > 0) { + asyncItems.forEach(function (_a) { + var asyncKey = _a.asyncKey; + return asyncDeclarations.set(asyncKey, { declarations: selectorGroup, target: target, index: target.cssRules.length }); + }); + } + setRule(target, target.cssRules.length, selectorGroup); + }); + }); + if (syncStylePositionWatcher) { + syncStylePositionWatcher.run(); + } + else { + syncStylePositionWatcher = watchForNodePosition(syncStyle, { onRestore: buildStyleSheet, watchSibling: true, watchParent: true }); + } + } + function rebuildAsyncRule(key) { + var _a = asyncDeclarations.get(key), declarations = _a.declarations, target = _a.target, index = _a.index; + target.deleteRule(index); + setRule(target, index, declarations); + asyncDeclarations.delete(key); + } + modRules.filter(function (r) { return r; }).forEach(function (_a) { + var selector = _a.selector, declarations = _a.declarations, media = _a.media; + declarations.forEach(function (_a) { + var property = _a.property, value = _a.value, important = _a.important, sourceValue = _a.sourceValue; + if (typeof value === 'function') { + var modified = value(filter); + if (modified instanceof Promise) { + var index_1 = readyDeclarations.length; + var asyncKey_1 = asyncDeclarationCounter++; + readyDeclarations.push({ media: media, selector: selector, property: property, value: null, important: important, asyncKey: asyncKey_1, sourceValue: sourceValue }); + var promise = modified; + var currentRenderId_1 = renderId; + promise.then(function (asyncValue) { + if (!asyncValue || cancelAsyncOperations || currentRenderId_1 !== renderId) { + return; + } + readyDeclarations[index_1].value = asyncValue; + asyncQueue.add(function () { + if (cancelAsyncOperations || currentRenderId_1 !== renderId) { + return; + } + rebuildAsyncRule(asyncKey_1); + }); + }); + } + else { + readyDeclarations.push({ media: media, selector: selector, property: property, value: modified, important: important, sourceValue: sourceValue }); + } + } + else { + readyDeclarations.push({ media: media, selector: selector, property: property, value: value, important: important, sourceValue: sourceValue }); + } + }); + }); + buildStyleSheet(); + } + var rulesChangeKey = null; + var rulesCheckFrameId = null; + function safeGetSheetRules() { + try { + if (element.sheet == null) { + return null; + } + return element.sheet.cssRules; + } + catch (err) { + logWarn(err); + return null; + } + } + function updateRulesChangeKey() { + var rules = safeGetSheetRules(); + if (rules) { + rulesChangeKey = rules.length; + } + } + function didRulesKeyChange() { + var rules = safeGetSheetRules(); + return rules && rules.length !== rulesChangeKey; + } + function subscribeToSheetChanges() { + updateRulesChangeKey(); + unsubscribeFromSheetChanges(); + var checkForUpdate = function () { + if (didRulesKeyChange()) { + updateRulesChangeKey(); + update(); + } + rulesCheckFrameId = requestAnimationFrame(checkForUpdate); + }; + checkForUpdate(); + } + function unsubscribeFromSheetChanges() { + cancelAnimationFrame(rulesCheckFrameId); + } + function pause() { + observer.disconnect(); + cancelAsyncOperations = true; + corsCopyPositionWatcher && corsCopyPositionWatcher.stop(); + syncStylePositionWatcher && syncStylePositionWatcher.stop(); + unsubscribeFromSheetChanges(); + } + function destroy() { + pause(); + removeNode(corsCopy); + removeNode(syncStyle); + } + function watch() { + observer.observe(element, observerOptions); + if (element instanceof HTMLStyleElement) { + subscribeToSheetChanges(); + } + } + var maxMoveCount = 10; + var moveCount = 0; + function restore() { + if (!syncStyle) { + return; + } + moveCount++; + if (moveCount > maxMoveCount) { + logWarn('Style sheet was moved multiple times', element); + return; + } + logWarn('Restore style', syncStyle, element); + var shouldRestore = syncStyle.sheet == null || syncStyle.sheet.cssRules.length > 0; + insertStyle(); + if (shouldRestore) { + forceRestore = true; + updateRulesChangeKey(); + update(); + } + } + return { + details: details, + render: render, + pause: pause, + destroy: destroy, + watch: watch, + restore: restore, + }; + } + function linkLoading(link) { + return new Promise(function (resolve, reject) { + var cleanUp = function () { + link.removeEventListener('load', onLoad); + link.removeEventListener('error', onError); + }; + var onLoad = function () { + cleanUp(); + resolve(); + }; + var onError = function () { + cleanUp(); + reject("Link loading failed " + link.href); + }; + link.addEventListener('load', onLoad); + link.addEventListener('error', onError); + }); + } + function getCSSImportURL(importDeclaration) { + return getCSSURLValue(importDeclaration.substring(8).replace(/;$/, '')); + } + function loadText(url) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!url.startsWith('data:')) return [3, 3]; + return [4, fetch(url)]; + case 1: return [4, (_a.sent()).text()]; + case 2: return [2, _a.sent()]; + case 3: return [4, bgFetch({ url: url, responseType: 'text', mimeType: 'text/css' })]; + case 4: return [2, _a.sent()]; + } + }); + }); + } + function replaceCSSImports(cssText, basePath) { + return __awaiter(this, void 0, void 0, function () { + var importMatches, _i, importMatches_1, match, importURL, absoluteURL, importedCSS, err_3; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + cssText = removeCSSComments(cssText); + cssText = replaceCSSFontFace(cssText); + cssText = replaceCSSRelativeURLsWithAbsolute(cssText, basePath); + importMatches = getMatches(cssImportRegex, cssText); + _i = 0, importMatches_1 = importMatches; + _a.label = 1; + case 1: + if (!(_i < importMatches_1.length)) return [3, 8]; + match = importMatches_1[_i]; + importURL = getCSSImportURL(match); + absoluteURL = getAbsoluteURL(basePath, importURL); + importedCSS = void 0; + _a.label = 2; + case 2: + _a.trys.push([2, 5, , 6]); + return [4, loadText(absoluteURL)]; + case 3: + importedCSS = _a.sent(); + return [4, replaceCSSImports(importedCSS, getCSSBaseBath(absoluteURL))]; + case 4: + importedCSS = _a.sent(); + return [3, 6]; + case 5: + err_3 = _a.sent(); + logWarn(err_3); + importedCSS = ''; + return [3, 6]; + case 6: + cssText = cssText.split(match).join(importedCSS); + _a.label = 7; + case 7: + _i++; + return [3, 1]; + case 8: + cssText = cssText.trim(); + return [2, cssText]; + } + }); + }); + } + function createCORSCopy(srcElement, cssText) { + if (!cssText) { + return null; + } + var cors = document.createElement('style'); + cors.classList.add('darkreader'); + cors.classList.add('darkreader--cors'); + cors.media = 'screen'; + cors.textContent = cssText; + srcElement.parentNode.insertBefore(cors, srcElement.nextSibling); + cors.sheet.disabled = true; + return cors; + } + + var observer$1 = null; + function getAllManageableStyles(nodes) { + var results = []; + Array.from(nodes).forEach(function (node) { + if (node instanceof Element) { + if (shouldManageStyle(node)) { + results.push(node); + } + } + if (node instanceof Element || node instanceof ShadowRoot) { + results.push.apply(results, Array.from(node.querySelectorAll(STYLE_SELECTOR)).filter(shouldManageStyle)); + } + }); + return results; + } + var undefinedGroups = new Map(); + var elementsDefinitionCallback; + function collectUndefinedElements(root) { + if (!isDefinedSelectorSupported()) { + return; + } + root.querySelectorAll(':not(:defined)') + .forEach(function (el) { + var tag = el.tagName.toLowerCase(); + if (!undefinedGroups.has(tag)) { + undefinedGroups.set(tag, new Set()); + customElementsWhenDefined(tag).then(function () { + if (elementsDefinitionCallback) { + var elements = undefinedGroups.get(tag); + undefinedGroups.delete(tag); + elementsDefinitionCallback(Array.from(elements)); + } + }); + } + undefinedGroups.get(tag).add(el); + }); + } + function customElementsWhenDefined(tag) { + return new Promise(function (resolve) { + if (window.customElements && typeof window.customElements.whenDefined === 'function') { + customElements.whenDefined(tag).then(resolve); + } + else { + var checkIfDefined_1 = function () { + var elements = undefinedGroups.get(tag); + if (elements && elements.size > 0) { + if (elements.values().next().value.matches(':defined')) { + resolve(); + } + else { + requestAnimationFrame(checkIfDefined_1); + } + } + }; + requestAnimationFrame(checkIfDefined_1); + } + }); + } + function watchWhenCustomElementsDefined(callback) { + elementsDefinitionCallback = callback; + } + function unsubscribeFromDefineCustomElements() { + elementsDefinitionCallback = null; + undefinedGroups.clear(); + } + var shadowObservers = new Set(); + var nodesShadowObservers = new WeakMap(); + function unsubscribeFromShadowRootChanges() { + shadowObservers.forEach(function (o) { return o.disconnect(); }); + shadowObservers.clear(); + nodesShadowObservers = new WeakMap(); + } + function watchForStyleChanges(update) { + if (observer$1) { + observer$1.disconnect(); + shadowObservers.forEach(function (o) { return o.disconnect(); }); + shadowObservers.clear(); + nodesShadowObservers = new WeakMap(); + } + function handleMutations(mutations) { + var createdStyles = new Set(); + var updatedStyles = new Set(); + var removedStyles = new Set(); + var movedStyles = new Set(); + var additions = new Set(); + var deletions = new Set(); + var styleUpdates = new Set(); + mutations.forEach(function (m) { + m.addedNodes.forEach(function (n) { return additions.add(n); }); + m.removedNodes.forEach(function (n) { return deletions.add(n); }); + if (m.type === 'attributes' && shouldManageStyle(m.target)) { + styleUpdates.add(m.target); + } + }); + var styleAdditions = getAllManageableStyles(additions); + var styleDeletions = getAllManageableStyles(deletions); + additions.forEach(function (n) { + iterateShadowNodes(n, function (host) { + var shadowStyles = getAllManageableStyles(host.shadowRoot.children); + if (shadowStyles.length > 0) { + styleAdditions.push.apply(styleAdditions, shadowStyles); + } + }); + }); + deletions.forEach(function (n) { + iterateShadowNodes(n, function (host) { + var shadowStyles = getAllManageableStyles(host.shadowRoot.children); + if (shadowStyles.length > 0) { + styleDeletions.push.apply(styleDeletions, shadowStyles); + } + }); + }); + styleDeletions.forEach(function (style) { + if (style.isConnected) { + movedStyles.add(style); + } + else { + removedStyles.add(style); + } + }); + styleUpdates.forEach(function (style) { + if (!removedStyles.has(style)) { + updatedStyles.add(style); + } + }); + styleAdditions.forEach(function (style) { + if (!(removedStyles.has(style) || movedStyles.has(style) || updatedStyles.has(style))) { + createdStyles.add(style); + } + }); + if (createdStyles.size + removedStyles.size + updatedStyles.size > 0) { + update({ + created: Array.from(createdStyles), + updated: Array.from(updatedStyles), + removed: Array.from(removedStyles), + moved: Array.from(movedStyles), + }); + } + additions.forEach(function (n) { + if (n.isConnected) { + iterateShadowNodes(n, subscribeForShadowRootChanges); + if (n instanceof Element) { + collectUndefinedElements(n); + } + } + }); + } + function subscribeForShadowRootChanges(node) { + if (nodesShadowObservers.has(node)) { + return; + } + var shadowObserver = new MutationObserver(handleMutations); + shadowObserver.observe(node.shadowRoot, mutationObserverOptions); + shadowObservers.add(shadowObserver); + nodesShadowObservers.set(node, shadowObserver); + } + var mutationObserverOptions = { childList: true, subtree: true, attributes: true, attributeFilter: ['rel', 'disabled'] }; + observer$1 = new MutationObserver(handleMutations); + observer$1.observe(document.documentElement, mutationObserverOptions); + iterateShadowNodes(document.documentElement, subscribeForShadowRootChanges); + watchWhenCustomElementsDefined(function (hosts) { + var newStyles = getAllManageableStyles(hosts.map(function (h) { return h.shadowRoot; })); + update({ created: newStyles, updated: [], removed: [], moved: [] }); + hosts.forEach(function (h) { return subscribeForShadowRootChanges(h); }); + }); + collectUndefinedElements(document); + } + function stopWatchingForStyleChanges() { + if (observer$1) { + observer$1.disconnect(); + observer$1 = null; + unsubscribeFromShadowRootChanges(); + unsubscribeFromDefineCustomElements(); + } + } + + var styleManagers = new Map(); + var variables = new Map(); + var filter = null; + var fixes = null; + var isIFrame = null; + function createOrUpdateStyle(className, root) { + if (root === void 0) { root = document.head || document; } + var style = root.querySelector("." + className); + if (!style) { + style = document.createElement('style'); + style.classList.add('darkreader'); + style.classList.add(className); + style.media = 'screen'; + } + return style; + } + var stylePositionWatchers = new Map(); + function setupStylePositionWatcher(node, alias) { + stylePositionWatchers.has(alias) && stylePositionWatchers.get(alias).stop(); + stylePositionWatchers.set(alias, watchForNodePosition(node, { watchParent: true, watchSibling: false })); + } + function stopStylePositionWatchers() { + Array.from(stylePositionWatchers.values()).forEach(function (watcher) { return watcher.stop(); }); + stylePositionWatchers.clear(); + } + function createStaticStyleOverrides() { + var fallbackStyle = createOrUpdateStyle('darkreader--fallback'); + document.head.insertBefore(fallbackStyle, document.head.firstChild); + fallbackStyle.textContent = getModifiedFallbackStyle(filter, { strict: true }); + setupStylePositionWatcher(fallbackStyle, 'fallback'); + var userAgentStyle = createOrUpdateStyle('darkreader--user-agent'); + document.head.insertBefore(userAgentStyle, fallbackStyle.nextSibling); + userAgentStyle.textContent = getModifiedUserAgentStyle(filter, isIFrame); + setupStylePositionWatcher(userAgentStyle, 'user-agent'); + var textStyle = createOrUpdateStyle('darkreader--text'); + document.head.insertBefore(textStyle, fallbackStyle.nextSibling); + if (filter.useFont || filter.textStroke > 0) { + textStyle.textContent = createTextStyle(filter); + } + else { + textStyle.textContent = ''; + } + setupStylePositionWatcher(textStyle, 'text'); + var invertStyle = createOrUpdateStyle('darkreader--invert'); + document.head.insertBefore(invertStyle, textStyle.nextSibling); + if (fixes && Array.isArray(fixes.invert) && fixes.invert.length > 0) { + invertStyle.textContent = [ + fixes.invert.join(', ') + " {", + " filter: " + getCSSFilterValue(__assign(__assign({}, filter), { contrast: filter.mode === 0 ? filter.contrast : clamp(filter.contrast - 10, 0, 100) })) + " !important;", + '}', + ].join('\n'); + } + else { + invertStyle.textContent = ''; + } + setupStylePositionWatcher(invertStyle, 'invert'); + var inlineStyle = createOrUpdateStyle('darkreader--inline'); + document.head.insertBefore(inlineStyle, invertStyle.nextSibling); + inlineStyle.textContent = getInlineOverrideStyle(); + setupStylePositionWatcher(inlineStyle, 'inline'); + var overrideStyle = createOrUpdateStyle('darkreader--override'); + document.head.appendChild(overrideStyle); + overrideStyle.textContent = fixes && fixes.css ? replaceCSSTemplates(fixes.css) : ''; + setupStylePositionWatcher(overrideStyle, 'override'); + } + var shadowRootsWithOverrides = new Set(); + function createShadowStaticStyleOverrides(root) { + var inlineStyle = createOrUpdateStyle('darkreader--inline', root); + root.insertBefore(inlineStyle, root.firstChild); + inlineStyle.textContent = getInlineOverrideStyle(); + shadowRootsWithOverrides.add(root); + } + function replaceCSSTemplates($cssText) { + return $cssText.replace(/\${(.+?)}/g, function (m0, $color) { + try { + var color = parseColorWithCache($color); + return modifyColor(color, filter); + } + catch (err) { + logWarn(err); + return $color; + } + }); + } + function cleanFallbackStyle() { + var fallback = document.head.querySelector('.darkreader--fallback'); + if (fallback) { + fallback.textContent = ''; + } + } + function createDynamicStyleOverrides() { + cancelRendering(); + updateVariables(getElementCSSVariables(document.documentElement)); + var allStyles = Array.from(document.querySelectorAll(STYLE_SELECTOR)); + iterateShadowNodes(document.documentElement, function (node) { + var shadowStyles = node.shadowRoot.querySelectorAll(STYLE_SELECTOR); + if (shadowStyles.length > 0) { + allStyles.push.apply(allStyles, Array.from(shadowStyles)); + } + }); + var newManagers = Array.from(allStyles) + .filter(function (style) { return !styleManagers.has(style) && shouldManageStyle(style); }) + .map(function (style) { return createManager(style); }); + var newVariables = newManagers + .map(function (manager) { return manager.details(); }) + .filter(function (details) { return details && details.variables.size > 0; }) + .map(function (_a) { + var variables = _a.variables; + return variables; + }); + if (newVariables.length === 0) { + styleManagers.forEach(function (manager) { return manager.render(filter, variables); }); + if (loadingStyles.size === 0) { + cleanFallbackStyle(); + } + } + else { + newVariables.forEach(function (variables) { return updateVariables(variables); }); + throttledRenderAllStyles(function () { + if (loadingStyles.size === 0) { + cleanFallbackStyle(); + } + }); + } + newManagers.forEach(function (manager) { return manager.watch(); }); + var inlineStyleElements = Array.from(document.querySelectorAll(INLINE_STYLE_SELECTOR)); + iterateShadowNodes(document.documentElement, function (node) { + var elements = node.shadowRoot.querySelectorAll(INLINE_STYLE_SELECTOR); + if (elements.length > 0) { + createShadowStaticStyleOverrides(node.shadowRoot); + inlineStyleElements.push.apply(inlineStyleElements, Array.from(elements)); + } + }); + inlineStyleElements.forEach(function (el) { return overrideInlineStyle(el, filter); }); + } + var loadingStylesCounter = 0; + var loadingStyles = new Set(); + function createManager(element) { + if (styleManagers.has(element)) { + return; + } + var loadingStyleId = ++loadingStylesCounter; + function loadingStart() { + if (!isPageLoaded() || !didDocumentShowUp) { + loadingStyles.add(loadingStyleId); + var fallbackStyle = document.querySelector('.darkreader--fallback'); + if (!fallbackStyle.textContent) { + fallbackStyle.textContent = getModifiedFallbackStyle(filter, { strict: false }); + } + } + } + function loadingEnd() { + loadingStyles.delete(loadingStyleId); + if (loadingStyles.size === 0 && isPageLoaded()) { + cleanFallbackStyle(); + } + } + function update() { + var details = manager.details(); + if (!details) { + return; + } + if (details.variables.size === 0) { + manager.render(filter, variables); + } + else { + updateVariables(details.variables); + throttledRenderAllStyles(); + } + } + var manager = manageStyle(element, { update: update, loadingStart: loadingStart, loadingEnd: loadingEnd }); + styleManagers.set(element, manager); + return manager; + } + function updateVariables(newVars) { + if (newVars.size === 0) { + return; + } + newVars.forEach(function (value, key) { return variables.set(key, value); }); + variables.forEach(function (value, key) { return variables.set(key, replaceCSSVariables(value, variables)); }); + } + function removeManager(element) { + var manager = styleManagers.get(element); + if (manager) { + manager.destroy(); + styleManagers.delete(element); + } + } + var throttledRenderAllStyles = throttle(function (callback) { + styleManagers.forEach(function (manager) { return manager.render(filter, variables); }); + callback && callback(); + }); + var cancelRendering = function () { + throttledRenderAllStyles.cancel(); + }; + function isPageLoaded() { + return document.readyState === 'complete' || document.readyState === 'interactive'; + } + function onReadyStateChange() { + if (!isPageLoaded()) { + return; + } + document.removeEventListener('readystatechange', onReadyStateChange); + if (loadingStyles.size === 0) { + cleanFallbackStyle(); + } + } + var documentVisibilityListener = null; + var didDocumentShowUp = !document.hidden; + function watchForDocumentVisibility(callback) { + var alreadyWatching = Boolean(documentVisibilityListener); + documentVisibilityListener = function () { + if (!document.hidden) { + stopWatchingForDocumentVisibility(); + callback(); + didDocumentShowUp = true; + } + }; + if (!alreadyWatching) { + document.addEventListener('visibilitychange', documentVisibilityListener); + } + } + function stopWatchingForDocumentVisibility() { + document.removeEventListener('visibilitychange', documentVisibilityListener); + documentVisibilityListener = null; + } + function createThemeAndWatchForUpdates() { + createStaticStyleOverrides(); + function runDynamicStyle() { + createDynamicStyleOverrides(); + watchForUpdates(); + } + if (document.hidden) { + watchForDocumentVisibility(runDynamicStyle); + } + else { + runDynamicStyle(); + } + changeMetaThemeColorWhenAvailable(filter); + } + function watchForUpdates() { + watchForStyleChanges(function (_a) { + var created = _a.created, updated = _a.updated, removed = _a.removed, moved = _a.moved; + var stylesToRemove = removed; + var stylesToManage = created.concat(updated).concat(moved) + .filter(function (style) { return !styleManagers.has(style); }); + var stylesToRestore = moved + .filter(function (style) { return styleManagers.has(style); }); + stylesToRemove.forEach(function (style) { return removeManager(style); }); + var newManagers = stylesToManage + .map(function (style) { return createManager(style); }); + var newVariables = newManagers + .map(function (manager) { return manager.details(); }) + .filter(function (details) { return details && details.variables.size > 0; }) + .map(function (_a) { + var variables = _a.variables; + return variables; + }); + if (newVariables.length === 0) { + newManagers.forEach(function (manager) { return manager.render(filter, variables); }); + } + else { + newVariables.forEach(function (variables) { return updateVariables(variables); }); + throttledRenderAllStyles(); + } + newManagers.forEach(function (manager) { return manager.watch(); }); + stylesToRestore.forEach(function (style) { return styleManagers.get(style).restore(); }); + }); + watchForInlineStyles(function (element) { + overrideInlineStyle(element, filter); + if (element === document.documentElement) { + var rootVariables = getElementCSSVariables(document.documentElement); + if (rootVariables.size > 0) { + updateVariables(rootVariables); + throttledRenderAllStyles(); + } + } + }, function (root) { + var inlineStyleElements = root.querySelectorAll(INLINE_STYLE_SELECTOR); + if (inlineStyleElements.length > 0) { + createShadowStaticStyleOverrides(root); + inlineStyleElements.forEach(function (el) { return overrideInlineStyle(el, filter); }); + } + }); + document.addEventListener('readystatechange', onReadyStateChange); + } + function stopWatchingForUpdates() { + styleManagers.forEach(function (manager) { return manager.pause(); }); + stopStylePositionWatchers(); + stopWatchingForStyleChanges(); + stopWatchingForInlineStyles(); + document.removeEventListener('readystatechange', onReadyStateChange); + } + function createOrUpdateDynamicTheme(filterConfig, dynamicThemeFixes, iframe) { + filter = filterConfig; + fixes = dynamicThemeFixes; + isIFrame = iframe; + if (document.head) { + createThemeAndWatchForUpdates(); + } + else { + if (!isFirefox()) { + var fallbackStyle = createOrUpdateStyle('darkreader--fallback'); + document.documentElement.appendChild(fallbackStyle); + fallbackStyle.textContent = getModifiedFallbackStyle(filter, { strict: true }); + } + var headObserver_1 = new MutationObserver(function () { + if (document.head) { + headObserver_1.disconnect(); + createThemeAndWatchForUpdates(); + } + }); + headObserver_1.observe(document, { childList: true, subtree: true }); + } + } + function removeDynamicTheme() { + cleanDynamicThemeCache(); + removeNode(document.querySelector('.darkreader--fallback')); + if (document.head) { + restoreMetaThemeColor(); + removeNode(document.head.querySelector('.darkreader--user-agent')); + removeNode(document.head.querySelector('.darkreader--text')); + removeNode(document.head.querySelector('.darkreader--invert')); + removeNode(document.head.querySelector('.darkreader--inline')); + removeNode(document.head.querySelector('.darkreader--override')); + } + shadowRootsWithOverrides.forEach(function (root) { + removeNode(root.querySelector('.darkreader--inline')); + }); + shadowRootsWithOverrides.clear(); + Array.from(styleManagers.keys()).forEach(function (el) { return removeManager(el); }); + Array.from(document.querySelectorAll('.darkreader')).forEach(removeNode); + } + function cleanDynamicThemeCache() { + stopWatchingForDocumentVisibility(); + cancelRendering(); + stopWatchingForUpdates(); + cleanModificationCache(); + } + + var defaultTheme = { + mode: 1, + brightness: 100, + contrast: 100, + grayscale: 0, + sepia: 0, + useFont: false, + fontFamily: '', + textStroke: 0, + engine: ThemeEngines.dynamicTheme, + stylesheet: '', + }; + var isIFrame$1 = (function () { + try { + return window.self !== window.top; + } + catch (err) { + console.warn(err); + return true; + } + })(); + function enable(themeOptions, fixes) { + if (themeOptions === void 0) { themeOptions = {}; } + if (fixes === void 0) { fixes = null; } + var theme = __assign(__assign({}, defaultTheme), themeOptions); + if (theme.engine !== ThemeEngines.dynamicTheme) { + throw new Error('Theme engine is not supported'); + } + createOrUpdateDynamicTheme(theme, fixes, isIFrame$1); + } + function disable() { + removeDynamicTheme(); + } + var darkScheme = matchMedia('(prefers-color-scheme: dark)'); + var store = { + themeOptions: null, + fixes: null, + }; + function handleColorScheme() { + if (darkScheme.matches) { + enable(store.themeOptions, store.fixes); + } + else { + disable(); + } + } + function auto(themeOptions, fixes) { + if (themeOptions === void 0) { themeOptions = {}; } + if (fixes === void 0) { fixes = null; } + if (themeOptions) { + store = { themeOptions: themeOptions, fixes: fixes }; + handleColorScheme(); + darkScheme.addListener(handleColorScheme); + } + else { + darkScheme.removeListener(handleColorScheme); + disable(); + } + } + var setFetchMethod$1 = setFetchMethod; + + exports.auto = auto; + exports.disable = disable; + exports.enable = enable; + exports.setFetchMethod = setFetchMethod$1; + + Object.defineProperty(exports, '__esModule', { value: true }); + +}))); diff --git a/Wino.Mail/JS/Quill/full.html b/Wino.Mail/JS/Quill/full.html new file mode 100644 index 00000000..7aa371b9 --- /dev/null +++ b/Wino.Mail/JS/Quill/full.html @@ -0,0 +1,287 @@ + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+ +
+ + + + + + + + + + + + + + + + diff --git a/Wino.Mail/JS/Quill/highlight.min.js b/Wino.Mail/JS/Quill/highlight.min.js new file mode 100644 index 00000000..f30a334c --- /dev/null +++ b/Wino.Mail/JS/Quill/highlight.min.js @@ -0,0 +1,3 @@ +/*! highlight.js v9.12.0 | BSD3 License | git.io/hljslicense */ +!function(e){var t="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):t&&(t.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return t.hljs}))}(function(e){function t(e){return e.replace(/&/g,"&").replace(//g,">")}function r(e){return e.nodeName.toLowerCase()}function a(e,t){var r=e&&e.exec(t);return r&&0===r.index}function n(e){return E.test(e)}function i(e){var t,r,a,i,s=e.className+" ";if(s+=e.parentNode?e.parentNode.className:"",r=M.exec(s))return w(r[1])?r[1]:"no-highlight";for(s=s.split(/\s+/),t=0,a=s.length;a>t;t++)if(i=s[t],n(i)||w(i))return i}function s(e){var t,r={},a=Array.prototype.slice.call(arguments,1);for(t in e)r[t]=e[t];return a.forEach(function(e){for(t in e)r[t]=e[t]}),r}function c(e){var t=[];return function a(e,n){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?n+=i.nodeValue.length:1===i.nodeType&&(t.push({event:"start",offset:n,node:i}),n=a(i,n),r(i).match(/br|hr|img|input/)||t.push({event:"stop",offset:n,node:i}));return n}(e,0),t}function o(e,a,n){function i(){return e.length&&a.length?e[0].offset!==a[0].offset?e[0].offset"}function c(e){u+=""}function o(e){("start"===e.event?s:c)(e.node)}for(var l=0,u="",d=[];e.length||a.length;){var b=i();if(u+=t(n.substring(l,b[0].offset)),l=b[0].offset,b===e){d.reverse().forEach(c);do o(b.splice(0,1)[0]),b=i();while(b===e&&b.length&&b[0].offset===l);d.reverse().forEach(s)}else"start"===b[0].event?d.push(b[0].node):d.pop(),o(b.splice(0,1)[0])}return u+t(n.substr(l))}function l(e){return e.v&&!e.cached_variants&&(e.cached_variants=e.v.map(function(t){return s(e,{v:null},t)})),e.cached_variants||e.eW&&[s(e)]||[e]}function u(e){function t(e){return e&&e.source||e}function r(r,a){return new RegExp(t(r),"m"+(e.cI?"i":"")+(a?"g":""))}function a(n,i){if(!n.compiled){if(n.compiled=!0,n.k=n.k||n.bK,n.k){var s={},c=function(t,r){e.cI&&(r=r.toLowerCase()),r.split(" ").forEach(function(e){var r=e.split("|");s[r[0]]=[t,r[1]?Number(r[1]):1]})};"string"==typeof n.k?c("keyword",n.k):k(n.k).forEach(function(e){c(e,n.k[e])}),n.k=s}n.lR=r(n.l||/\w+/,!0),i&&(n.bK&&(n.b="\\b("+n.bK.split(" ").join("|")+")\\b"),n.b||(n.b=/\B|\b/),n.bR=r(n.b),n.e||n.eW||(n.e=/\B|\b/),n.e&&(n.eR=r(n.e)),n.tE=t(n.e)||"",n.eW&&i.tE&&(n.tE+=(n.e?"|":"")+i.tE)),n.i&&(n.iR=r(n.i)),null==n.r&&(n.r=1),n.c||(n.c=[]),n.c=Array.prototype.concat.apply([],n.c.map(function(e){return l("self"===e?n:e)})),n.c.forEach(function(e){a(e,n)}),n.starts&&a(n.starts,i);var o=n.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([n.tE,n.i]).map(t).filter(Boolean);n.t=o.length?r(o.join("|"),!0):{exec:function(){return null}}}}a(e)}function d(e,r,n,i){function s(e,t){var r,n;for(r=0,n=t.c.length;n>r;r++)if(a(t.c[r].bR,e))return t.c[r]}function c(e,t){if(a(e.eR,t)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?c(e.parent,t):void 0}function o(e,t){return!n&&a(t.iR,e)}function l(e,t){var r=v.cI?t[0].toLowerCase():t[0];return e.k.hasOwnProperty(r)&&e.k[r]}function p(e,t,r,a){var n=a?"":L.classPrefix,i='',i+t+s}function m(){var e,r,a,n;if(!N.k)return t(E);for(n="",r=0,N.lR.lastIndex=0,a=N.lR.exec(E);a;)n+=t(E.substring(r,a.index)),e=l(N,a),e?(M+=e[1],n+=p(e[0],t(a[0]))):n+=t(a[0]),r=N.lR.lastIndex,a=N.lR.exec(E);return n+t(E.substr(r))}function f(){var e="string"==typeof N.sL;if(e&&!x[N.sL])return t(E);var r=e?d(N.sL,E,!0,k[N.sL]):b(E,N.sL.length?N.sL:void 0);return N.r>0&&(M+=r.r),e&&(k[N.sL]=r.top),p(r.language,r.value,!1,!0)}function g(){C+=null!=N.sL?f():m(),E=""}function _(e){C+=e.cN?p(e.cN,"",!0):"",N=Object.create(e,{parent:{value:N}})}function h(e,t){if(E+=e,null==t)return g(),0;var r=s(t,N);if(r)return r.skip?E+=t:(r.eB&&(E+=t),g(),r.rB||r.eB||(E=t)),_(r,t),r.rB?0:t.length;var a=c(N,t);if(a){var n=N;n.skip?E+=t:(n.rE||n.eE||(E+=t),g(),n.eE&&(E=t));do N.cN&&(C+=R),N.skip||(M+=N.r),N=N.parent;while(N!==a.parent);return a.starts&&_(a.starts,""),n.rE?0:t.length}if(o(t,N))throw new Error('Illegal lexeme "'+t+'" for mode "'+(N.cN||"")+'"');return E+=t,t.length||1}var v=w(e);if(!v)throw new Error('Unknown language: "'+e+'"');u(v);var y,N=i||v,k={},C="";for(y=N;y!==v;y=y.parent)y.cN&&(C=p(y.cN,"",!0)+C);var E="",M=0;try{for(var B,S,$=0;;){if(N.t.lastIndex=$,B=N.t.exec(r),!B)break;S=h(r.substring($,B.index),B[0]),$=B.index+S}for(h(r.substr($)),y=N;y.parent;y=y.parent)y.cN&&(C+=R);return{r:M,value:C,language:e,top:N}}catch(A){if(A.message&&-1!==A.message.indexOf("Illegal"))return{r:0,value:t(r)};throw A}}function b(e,r){r=r||L.languages||k(x);var a={r:0,value:t(e)},n=a;return r.filter(w).forEach(function(t){var r=d(t,e,!1);r.language=t,r.r>n.r&&(n=r),r.r>a.r&&(n=a,a=r)}),n.language&&(a.second_best=n),a}function p(e){return L.tabReplace||L.useBR?e.replace(B,function(e,t){return L.useBR&&"\n"===e?"
":L.tabReplace?t.replace(/\t/g,L.tabReplace):""}):e}function m(e,t,r){var a=t?C[t]:r,n=[e.trim()];return e.match(/\bhljs\b/)||n.push("hljs"),-1===e.indexOf(a)&&n.push(a),n.join(" ").trim()}function f(e){var t,r,a,s,l,u=i(e);n(u)||(L.useBR?(t=document.createElementNS("http://www.w3.org/1999/xhtml","div"),t.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):t=e,l=t.textContent,a=u?d(u,l,!0):b(l),r=c(t),r.length&&(s=document.createElementNS("http://www.w3.org/1999/xhtml","div"),s.innerHTML=a.value,a.value=o(r,c(s),l)),a.value=p(a.value),e.innerHTML=a.value,e.className=m(e.className,u,a.language),e.result={language:a.language,re:a.r},a.second_best&&(e.second_best={language:a.second_best.language,re:a.second_best.r}))}function g(e){L=s(L,e)}function _(){if(!_.called){_.called=!0;var e=document.querySelectorAll("pre code");N.forEach.call(e,f)}}function h(){addEventListener("DOMContentLoaded",_,!1),addEventListener("load",_,!1)}function v(t,r){var a=x[t]=r(e);a.aliases&&a.aliases.forEach(function(e){C[e]=t})}function y(){return k(x)}function w(e){return e=(e||"").toLowerCase(),x[e]||x[C[e]]}var N=[],k=Object.keys,x={},C={},E=/^(no-?highlight|plain|text)$/i,M=/\blang(?:uage)?-([\w-]+)\b/i,B=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,R="
",L={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};return e.highlight=d,e.highlightAuto=b,e.fixMarkup=p,e.highlightBlock=f,e.configure=g,e.initHighlighting=_,e.initHighlightingOnLoad=h,e.registerLanguage=v,e.listLanguages=y,e.getLanguage=w,e.inherit=s,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},e.C=function(t,r,a){var n=e.inherit({cN:"comment",b:t,e:r,c:[]},a||{});return n.c.push(e.PWM),n.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),n},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e.registerLanguage("apache",function(e){var t={cN:"number",b:"[\\$%]\\d+"};return{aliases:["apacheconf"],cI:!0,c:[e.HCM,{cN:"section",b:""},{cN:"attribute",b:/\w+/,r:0,k:{nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{e:/$/,r:0,k:{literal:"on off all"},c:[{cN:"meta",b:"\\s\\[",e:"\\]$"},{cN:"variable",b:"[\\$%]\\{",e:"\\}",c:["self",t]},t,e.QSM]}}],i:/\S/}}),e.registerLanguage("bash",function(e){var t={cN:"variable",v:[{b:/\$[\w\d#@][\w\d_]*/},{b:/\$\{(.*?)}/}]},r={cN:"string",b:/"/,e:/"/,c:[e.BE,t,{cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]}]},a={cN:"string",b:/'/,e:/'/};return{aliases:["sh","zsh"],l:/\b-?[a-z\._]+\b/,k:{keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},c:[{cN:"meta",b:/^#![^\n]+sh\s*$/,r:10},{cN:"function",b:/\w[\w\d_]*\s*\(\s*\)\s*\{/,rB:!0,c:[e.inherit(e.TM,{b:/\w[\w\d_]*/})],r:0},e.HCM,r,a,t]}}),e.registerLanguage("coffeescript",function(e){var t={keyword:"in if for while finally new do return else break catch instanceof throw try this switch continue typeof delete debugger super yield import export from as default await then unless until loop of by when and or is isnt not",literal:"true false null undefined yes no on off",built_in:"npm require console print module global window document"},r="[A-Za-z$_][0-9A-Za-z$_]*",a={cN:"subst",b:/#\{/,e:/}/,k:t},n=[e.BNM,e.inherit(e.CNM,{starts:{e:"(\\s*/)?",r:0}}),{cN:"string",v:[{b:/'''/,e:/'''/,c:[e.BE]},{b:/'/,e:/'/,c:[e.BE]},{b:/"""/,e:/"""/,c:[e.BE,a]},{b:/"/,e:/"/,c:[e.BE,a]}]},{cN:"regexp",v:[{b:"///",e:"///",c:[a,e.HCM]},{b:"//[gim]*",r:0},{b:/\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)/}]},{b:"@"+r},{sL:"javascript",eB:!0,eE:!0,v:[{b:"```",e:"```"},{b:"`",e:"`"}]}];a.c=n;var i=e.inherit(e.TM,{b:r}),s="(\\(.*\\))?\\s*\\B[-=]>",c={cN:"params",b:"\\([^\\(]",rB:!0,c:[{b:/\(/,e:/\)/,k:t,c:["self"].concat(n)}]};return{aliases:["coffee","cson","iced"],k:t,i:/\/\*/,c:n.concat([e.C("###","###"),e.HCM,{cN:"function",b:"^\\s*"+r+"\\s*=\\s*"+s,e:"[-=]>",rB:!0,c:[i,c]},{b:/[:\(,=]\s*/,r:0,c:[{cN:"function",b:s,e:"[-=]>",rB:!0,c:[c]}]},{cN:"class",bK:"class",e:"$",i:/[:="\[\]]/,c:[{bK:"extends",eW:!0,i:/[:="\[\]]/,c:[i]},i]},{b:r+":",e:":",rB:!0,rE:!0,r:0}])}}),e.registerLanguage("cpp",function(e){var t={cN:"keyword",b:"\\b[a-z\\d_]*_t\\b"},r={cN:"string",v:[{b:'(u8?|U)?L?"',e:'"',i:"\\n",c:[e.BE]},{b:'(u8?|U)?R"',e:'"',c:[e.BE]},{b:"'\\\\?.",e:"'",i:"."}]},a={cN:"number",v:[{b:"\\b(0b[01']+)"},{b:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{b:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],r:0},n={cN:"meta",b:/#\s*[a-z]+\b/,e:/$/,k:{"meta-keyword":"if else elif endif define undef warning error line pragma ifdef ifndef include"},c:[{b:/\\\n/,r:0},e.inherit(r,{cN:"meta-string"}),{cN:"meta-string",b:/<[^\n>]*>/,e:/$/,i:"\\n"},e.CLCM,e.CBCM]},i=e.IR+"\\s*\\(",s={keyword:"int float while private char catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignof constexpr decltype noexcept static_assert thread_local restrict _Bool complex _Complex _Imaginary atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and or not",built_in:"std string cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr",literal:"true false nullptr NULL"},c=[t,e.CLCM,e.CBCM,a,r];return{aliases:["c","cc","h","c++","h++","hpp"],k:s,i:"",k:s,c:["self",t]},{b:e.IR+"::",k:s},{v:[{b:/=/,e:/;/},{b:/\(/,e:/\)/},{bK:"new throw return else",e:/;/}],k:s,c:c.concat([{b:/\(/,e:/\)/,k:s,c:c.concat(["self"]),r:0}]),r:0},{cN:"function",b:"("+e.IR+"[\\*&\\s]+)+"+i,rB:!0,e:/[{;=]/,eE:!0,k:s,i:/[^\w\s\*&]/,c:[{b:i,rB:!0,c:[e.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:s,r:0,c:[e.CLCM,e.CBCM,r,a,t]},e.CLCM,e.CBCM,n]},{cN:"class",bK:"class struct",e:/[{;:]/,c:[{b://,c:["self"]},e.TM]}]),exports:{preprocessor:n,strings:r,k:s}}}),e.registerLanguage("cs",function(e){var t={keyword:"abstract as base bool break byte case catch char checked const continue decimal default delegate do double enum event explicit extern finally fixed float for foreach goto if implicit in int interface internal is lock long nameof object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this try typeof uint ulong unchecked unsafe ushort using virtual void volatile while add alias ascending async await by descending dynamic equals from get global group into join let on orderby partial remove select set value var where yield",literal:"null false true"},r={cN:"string",b:'@"',e:'"',c:[{b:'""'}]},a=e.inherit(r,{i:/\n/}),n={cN:"subst",b:"{",e:"}",k:t},i=e.inherit(n,{i:/\n/}),s={cN:"string",b:/\$"/,e:'"',i:/\n/,c:[{b:"{{"},{b:"}}"},e.BE,i]},c={cN:"string",b:/\$@"/,e:'"',c:[{b:"{{"},{b:"}}"},{b:'""'},n]},o=e.inherit(c,{i:/\n/,c:[{b:"{{"},{b:"}}"},{b:'""'},i]});n.c=[c,s,r,e.ASM,e.QSM,e.CNM,e.CBCM],i.c=[o,s,a,e.ASM,e.QSM,e.CNM,e.inherit(e.CBCM,{i:/\n/})];var l={v:[c,s,r,e.ASM,e.QSM]},u=e.IR+"(<"+e.IR+"(\\s*,\\s*"+e.IR+")*>)?(\\[\\])?";return{aliases:["csharp"],k:t,i:/::/,c:[e.C("///","$",{rB:!0,c:[{cN:"doctag",v:[{b:"///",r:0},{b:""},{b:""}]}]}),e.CLCM,e.CBCM,{cN:"meta",b:"#",e:"$",k:{"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum"}},l,e.CNM,{bK:"class interface",e:/[{;=]/,i:/[^\s:]/,c:[e.TM,e.CLCM,e.CBCM]},{bK:"namespace",e:/[{;=]/,i:/[^\s:]/,c:[e.inherit(e.TM,{b:"[a-zA-Z](\\.?\\w)*"}),e.CLCM,e.CBCM]},{cN:"meta",b:"^\\s*\\[",eB:!0,e:"\\]",eE:!0,c:[{cN:"meta-string",b:/"/,e:/"/}]},{bK:"new return throw await else",r:0},{cN:"function",b:"("+u+"\\s+)+"+e.IR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:t,c:[{b:e.IR+"\\s*\\(",rB:!0,c:[e.TM],r:0},{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,k:t,r:0,c:[l,e.CNM,e.CBCM]},e.CLCM,e.CBCM]}]}}),e.registerLanguage("css",function(e){var t="[a-zA-Z-][a-zA-Z0-9_-]*",r={b:/[A-Z\_\.\-]+\s*:/,rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:/\S/,e:":",eE:!0,starts:{eW:!0,eE:!0,c:[{b:/[\w-]+\(/,rB:!0,c:[{cN:"built_in",b:/[\w-]+/},{b:/\(/,e:/\)/,c:[e.ASM,e.QSM]}]},e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"number",b:"#[0-9A-Fa-f]+"},{cN:"meta",b:"!important"}]}}]};return{cI:!0,i:/[=\/|'\$]/,c:[e.CBCM,{cN:"selector-id",b:/#[A-Za-z0-9_-]+/},{cN:"selector-class",b:/\.[A-Za-z0-9_-]+/},{cN:"selector-attr",b:/\[/,e:/\]/,i:"$"},{cN:"selector-pseudo",b:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{b:"@",e:"[{;]",i:/:/,c:[{cN:"keyword",b:/\w+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[e.ASM,e.QSM,e.CSSNM]}]},{cN:"selector-tag",b:t,r:0},{b:"{",e:"}",i:/\S/,c:[e.CBCM,r]}]}}),e.registerLanguage("diff",function(e){return{aliases:["patch"],c:[{cN:"meta",r:10,v:[{b:/^@@ +\-\d+,\d+ +\+\d+,\d+ +@@$/},{b:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{b:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{cN:"comment",v:[{b:/Index: /,e:/$/},{b:/={3,}/,e:/$/},{b:/^\-{3}/,e:/$/},{b:/^\*{3} /,e:/$/},{b:/^\+{3}/,e:/$/},{b:/\*{5}/,e:/\*{5}$/}]},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"addition",b:"^\\!",e:"$"}]}}),e.registerLanguage("http",function(e){var t="HTTP/[0-9\\.]+";return{aliases:["https"],i:"\\S",c:[{b:"^"+t,e:"$",c:[{cN:"number",b:"\\b\\d{3}\\b"}]},{b:"^[A-Z]+ (.*?) "+t+"$",rB:!0,e:"$",c:[{cN:"string",b:" ",e:" ",eB:!0,eE:!0},{b:t},{cN:"keyword",b:"[A-Z]+"}]},{cN:"attribute",b:"^\\w",e:": ",eE:!0,i:"\\n|\\s|=",starts:{e:"$",r:0}},{b:"\\n\\n",starts:{sL:[],eW:!0}}]}}),e.registerLanguage("ini",function(e){var t={cN:"string",c:[e.BE],v:[{b:"'''",e:"'''",r:10},{b:'"""',e:'"""',r:10},{b:'"',e:'"'},{b:"'",e:"'"}]};return{aliases:["toml"],cI:!0,i:/\S/,c:[e.C(";","$"),e.HCM,{cN:"section",b:/^\s*\[+/,e:/\]+/},{b:/^[a-z0-9\[\]_-]+\s*=\s*/,e:"$",rB:!0,c:[{cN:"attr",b:/[a-z0-9\[\]_-]+/},{b:/=/,eW:!0,r:0,c:[{cN:"literal",b:/\bon|off|true|false|yes|no\b/},{cN:"variable",v:[{b:/\$[\w\d"][\w\d_]*/},{b:/\$\{(.*?)}/}]},t,{cN:"number",b:/([\+\-]+)?[\d]+_[\d_]+/},e.NM]}]}]}}),e.registerLanguage("java",function(e){var t="[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",r=t+"(<"+t+"(\\s*,\\s*"+t+")*>)?",a="false synchronized int abstract float private char boolean static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",n="\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",i={cN:"number",b:n,r:0};return{aliases:["jsp"],k:a,i:/<\/|#/,c:[e.C("/\\*\\*","\\*/",{r:0,c:[{b:/\w+@/,r:0},{cN:"doctag",b:"@[A-Za-z]+"}]}),e.CLCM,e.CBCM,e.ASM,e.QSM,{cN:"class",bK:"class interface",e:/[{;=]/,eE:!0,k:"class interface",i:/[:"\[\]]/,c:[{bK:"extends implements"},e.UTM]},{bK:"new throw return else",r:0},{cN:"function",b:"("+r+"\\s+)+"+e.UIR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:a,c:[{b:e.UIR+"\\s*\\(",rB:!0,r:0,c:[e.UTM]},{cN:"params",b:/\(/,e:/\)/,k:a,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]},i,{cN:"meta",b:"@[A-Za-z]+"}]}}),e.registerLanguage("javascript",function(e){var t="[A-Za-z$_][0-9A-Za-z$_]*",r={keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await static import from as",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},a={cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},n={cN:"subst",b:"\\$\\{",e:"\\}",k:r,c:[]},i={cN:"string",b:"`",e:"`",c:[e.BE,n]};n.c=[e.ASM,e.QSM,i,a,e.RM];var s=n.c.concat([e.CBCM,e.CLCM]);return{aliases:["js","jsx"],k:r,c:[{cN:"meta",r:10,b:/^\s*['"]use (strict|asm)['"]/},{cN:"meta",b:/^#!/,e:/$/},e.ASM,e.QSM,i,e.CLCM,e.CBCM,a,{b:/[{,]\s*/,r:0,c:[{b:t+"\\s*:",rB:!0,r:0,c:[{cN:"attr",b:t,r:0}]}]},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{cN:"function",b:"(\\(.*?\\)|"+t+")\\s*=>",rB:!0,e:"\\s*=>",c:[{cN:"params",v:[{b:t},{b:/\(\s*\)/},{b:/\(/,e:/\)/,eB:!0,eE:!0,k:r,c:s}]}]},{b://,sL:"xml",c:[{b:/<\w+\s*\/>/,skip:!0},{b:/<\w+/,e:/(\/\w+|\w+\/)>/,skip:!0,c:[{b:/<\w+\s*\/>/,skip:!0},"self"]}]}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:t}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:s}],i:/\[|%/},{b:/\$[(.]/},e.METHOD_GUARD,{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]},{bK:"constructor",e:/\{/,eE:!0}],i:/#(?!!)/}}),e.registerLanguage("json",function(e){var t={literal:"true false null"},r=[e.QSM,e.CNM],a={e:",",eW:!0,eE:!0,c:r,k:t},n={b:"{",e:"}",c:[{cN:"attr",b:/"/,e:/"/,c:[e.BE],i:"\\n"},e.inherit(a,{b:/:/})],i:"\\S"},i={b:"\\[",e:"\\]",c:[e.inherit(a)],i:"\\S"};return r.splice(r.length,0,n,i),{c:r,k:t,i:"\\S"}}),e.registerLanguage("makefile",function(e){var t={cN:"variable",v:[{b:"\\$\\("+e.UIR+"\\)",c:[e.BE]},{b:/\$[@%`]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist"],cI:!0,c:[{cN:"meta",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},e.C("",{r:10}),{b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{b:/<\?(php)?/,e:/\?>/,sL:"php",c:[{b:"/\\*",e:"\\*/",skip:!0}]},{cN:"tag",b:"|$)",e:">",k:{name:"style"},c:[r],starts:{e:"",rE:!0,sL:["css","xml"]}},{cN:"tag",b:"|$)",e:">",k:{name:"script"},c:[r],starts:{e:"",rE:!0,sL:["actionscript","javascript","handlebars","xml"]}},{cN:"meta",v:[{b:/<\?xml/,e:/\?>/,r:10},{b:/<\?\w+/,e:/\?>/}]},{cN:"tag",b:"",c:[{cN:"name",b:/[^\/><\s]+/,r:0},r]}]}}),e.registerLanguage("markdown",function(e){return{aliases:["md","mkdown","mkd"],c:[{cN:"section",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"quote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"^```w*s*$",e:"^```s*$"},{b:"`.+?`"},{b:"^( {4}| )",e:"$",r:0}]},{b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:!0,c:[{cN:"string",b:"\\[",e:"\\]",eB:!0,rE:!0,r:0},{cN:"link",b:"\\]\\(",e:"\\)",eB:!0,eE:!0},{cN:"symbol",b:"\\]\\[",e:"\\]",eB:!0,eE:!0}],r:10},{b:/^\[[^\n]+\]:/,rB:!0,c:[{cN:"symbol",b:/\[/,e:/\]/,eB:!0,eE:!0},{cN:"link",b:/:\s*/,e:/$/,eB:!0}]}]}}),e.registerLanguage("nginx",function(e){var t={cN:"variable",v:[{b:/\$\d+/},{b:/\$\{/,e:/}/},{b:"[\\$\\@]"+e.UIR}]},r={eW:!0,l:"[a-z/_]+",k:{literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},r:0,i:"=>",c:[e.HCM,{cN:"string",c:[e.BE,t],v:[{b:/"/,e:/"/},{b:/'/,e:/'/}]},{b:"([a-z]+):/",e:"\\s",eW:!0,eE:!0,c:[t]},{cN:"regexp",c:[e.BE,t],v:[{b:"\\s\\^",e:"\\s|{|;",rE:!0},{b:"~\\*?\\s+",e:"\\s|{|;",rE:!0},{b:"\\*(\\.[a-z\\-]+)+"},{b:"([a-z\\-]+\\.)+\\*"}]},{cN:"number",b:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{cN:"number",b:"\\b\\d+[kKmMgGdshdwy]*\\b",r:0},t]};return{aliases:["nginxconf"],c:[e.HCM,{b:e.UIR+"\\s+{",rB:!0,e:"{",c:[{cN:"section",b:e.UIR}],r:0},{b:e.UIR+"\\s",e:";|{",rB:!0,c:[{cN:"attribute",b:e.UIR,starts:r}],r:0}],i:"[^\\s\\}]"}}),e.registerLanguage("objectivec",function(e){var t={cN:"built_in",b:"\\b(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)\\w+"},r={keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},a=/[a-zA-Z@][a-zA-Z0-9_]*/,n="@interface @class @protocol @implementation";return{aliases:["mm","objc","obj-c"],k:r,l:a,i:""}]}]},{cN:"class",b:"("+n.split(" ").join("|")+")\\b",e:"({|$)",eE:!0,k:n,l:a,c:[e.UTM]},{b:"\\."+e.UIR,r:0}]}}),e.registerLanguage("perl",function(e){var t="getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qqfileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmgetsub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedirioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when",r={cN:"subst",b:"[$@]\\{",e:"\\}",k:t},a={b:"->{",e:"}"},n={v:[{b:/\$\d/},{b:/[\$%@](\^\w\b|#\w+(::\w+)*|{\w+}|\w+(::\w*)*)/},{b:/[\$%@][^\s\w{]/,r:0}]},i=[e.BE,r,n],s=[n,e.HCM,e.C("^\\=\\w","\\=cut",{eW:!0}),a,{cN:"string",c:i,v:[{b:"q[qwxr]?\\s*\\(",e:"\\)",r:5},{b:"q[qwxr]?\\s*\\[",e:"\\]",r:5},{b:"q[qwxr]?\\s*\\{",e:"\\}",r:5},{b:"q[qwxr]?\\s*\\|",e:"\\|",r:5},{b:"q[qwxr]?\\s*\\<",e:"\\>",r:5},{b:"qw\\s+q",e:"q",r:5},{b:"'",e:"'",c:[e.BE]},{b:'"',e:'"'},{b:"`",e:"`",c:[e.BE]},{b:"{\\w+}",c:[],r:0},{b:"-?\\w+\\s*\\=\\>",c:[],r:0}]},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\/\\/|"+e.RSR+"|\\b(split|return|print|reverse|grep)\\b)\\s*",k:"split return print reverse grep",r:0,c:[e.HCM,{cN:"regexp",b:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",r:10},{cN:"regexp",b:"(m|qr)?/",e:"/[a-z]*",c:[e.BE],r:0}]},{cN:"function",bK:"sub",e:"(\\s*\\(.*?\\))?[;{]",eE:!0,r:5,c:[e.TM]},{b:"-\\w\\b",r:0},{b:"^__DATA__$",e:"^__END__$",sL:"mojolicious",c:[{b:"^@@.*",e:"$",cN:"comment"}]}];return r.c=s,a.c=s,{aliases:["pl","pm"],l:/[\w\.]+/,k:t,c:s}}),e.registerLanguage("php",function(e){var t={b:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},r={cN:"meta",b:/<\?(php)?|\?>/},a={cN:"string",c:[e.BE,r],v:[{b:'b"',e:'"'},{b:"b'",e:"'"},e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},n={v:[e.BNM,e.CNM]};return{aliases:["php3","php4","php5","php6"],cI:!0,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception default die require __FUNCTION__ enddeclare final try switch continue endfor endif declare unset true false trait goto instanceof insteadof __DIR__ __NAMESPACE__ yield finally",c:[e.HCM,e.C("//","$",{c:[r]}),e.C("/\\*","\\*/",{c:[{cN:"doctag",b:"@[A-Za-z]+"}]}),e.C("__halt_compiler.+?;",!1,{eW:!0,k:"__halt_compiler",l:e.UIR}),{cN:"string",b:/<<<['"]?\w+['"]?$/,e:/^\w+;?$/,c:[e.BE,{cN:"subst",v:[{b:/\$\w+/},{b:/\{\$/,e:/\}/}]}]},r,{cN:"keyword",b:/\$this\b/},t,{b:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{cN:"function",bK:"function",e:/[;{]/,eE:!0,i:"\\$|\\[|%",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)",c:["self",t,e.CBCM,a,n]}]},{cN:"class",bK:"class interface",e:"{",eE:!0,i:/[:\(\$"]/,c:[{bK:"extends implements"},e.UTM]},{bK:"namespace",e:";",i:/[\.']/,c:[e.UTM]},{bK:"use",e:";",c:[e.UTM]},{b:"=>"},a,n]}}),e.registerLanguage("python",function(e){var t={keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda async await nonlocal|10 None True False",built_in:"Ellipsis NotImplemented"},r={cN:"meta",b:/^(>>>|\.\.\.) /},a={cN:"subst",b:/\{/,e:/\}/,k:t,i:/#/},n={cN:"string",c:[e.BE],v:[{b:/(u|b)?r?'''/,e:/'''/,c:[r],r:10},{b:/(u|b)?r?"""/,e:/"""/,c:[r],r:10},{b:/(fr|rf|f)'''/,e:/'''/,c:[r,a]},{b:/(fr|rf|f)"""/,e:/"""/,c:[r,a]},{b:/(u|r|ur)'/,e:/'/,r:10},{b:/(u|r|ur)"/,e:/"/,r:10},{b:/(b|br)'/,e:/'/},{b:/(b|br)"/,e:/"/},{b:/(fr|rf|f)'/,e:/'/,c:[a]},{b:/(fr|rf|f)"/,e:/"/,c:[a]},e.ASM,e.QSM]},i={cN:"number",r:0,v:[{b:e.BNR+"[lLjJ]?"},{b:"\\b(0o[0-7]+)[lLjJ]?"},{b:e.CNR+"[lLjJ]?"}]},s={cN:"params",b:/\(/,e:/\)/,c:["self",r,i,n]};return a.c=[n,i,r],{aliases:["py","gyp"],k:t,i:/(<\/|->|\?)|=>/,c:[r,i,n,e.HCM,{v:[{cN:"function",bK:"def"},{cN:"class",bK:"class"}],e:/:/,i:/[${=;\n,]/,c:[e.UTM,s,{b:/->/,eW:!0,k:"None"}]},{cN:"meta",b:/^[\t ]*@/,e:/$/},{b:/\b(print|exec)\(/}]}}),e.registerLanguage("ruby",function(e){ +var t="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",r={keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",literal:"true false nil"},a={cN:"doctag",b:"@[A-Za-z]+"},n={b:"#<",e:">"},i=[e.C("#","$",{c:[a]}),e.C("^\\=begin","^\\=end",{c:[a],r:10}),e.C("^__END__","\\n$")],s={cN:"subst",b:"#\\{",e:"}",k:r},c={cN:"string",c:[e.BE,s],v:[{b:/'/,e:/'/},{b:/"/,e:/"/},{b:/`/,e:/`/},{b:"%[qQwWx]?\\(",e:"\\)"},{b:"%[qQwWx]?\\[",e:"\\]"},{b:"%[qQwWx]?{",e:"}"},{b:"%[qQwWx]?<",e:">"},{b:"%[qQwWx]?/",e:"/"},{b:"%[qQwWx]?%",e:"%"},{b:"%[qQwWx]?-",e:"-"},{b:"%[qQwWx]?\\|",e:"\\|"},{b:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{b:/<<(-?)\w+$/,e:/^\s*\w+$/}]},o={cN:"params",b:"\\(",e:"\\)",endsParent:!0,k:r},l=[c,n,{cN:"class",bK:"class module",e:"$|;",i:/=/,c:[e.inherit(e.TM,{b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{b:"<\\s*",c:[{b:"("+e.IR+"::)?"+e.IR}]}].concat(i)},{cN:"function",bK:"def",e:"$|;",c:[e.inherit(e.TM,{b:t}),o].concat(i)},{b:e.IR+"::"},{cN:"symbol",b:e.UIR+"(\\!|\\?)?:",r:0},{cN:"symbol",b:":(?!\\s)",c:[c,{b:t}],r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{cN:"params",b:/\|/,e:/\|/,k:r},{b:"("+e.RSR+"|unless)\\s*",k:"unless",c:[n,{cN:"regexp",c:[e.BE,s],i:/\n/,v:[{b:"/",e:"/[a-z]*"},{b:"%r{",e:"}[a-z]*"},{b:"%r\\(",e:"\\)[a-z]*"},{b:"%r!",e:"![a-z]*"},{b:"%r\\[",e:"\\][a-z]*"}]}].concat(i),r:0}].concat(i);s.c=l,o.c=l;var u="[>?]>",d="[\\w#]+\\(\\w+\\):\\d+:\\d+>",b="(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>",p=[{b:/^\s*=>/,starts:{e:"$",c:l}},{cN:"meta",b:"^("+u+"|"+d+"|"+b+")",starts:{e:"$",c:l}}];return{aliases:["rb","gemspec","podspec","thor","irb"],k:r,i:/\/\*/,c:i.concat(p).concat(l)}}),e.registerLanguage("shell",function(e){return{aliases:["console"],c:[{cN:"meta",b:"^\\s{0,3}[\\w\\d\\[\\]()@-]*[>%$#]",starts:{e:"$",sL:"bash"}}]}}),e.registerLanguage("sql",function(e){var t=e.C("--","$");return{cI:!0,i:/[<>{}*#]/,c:[{bK:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke comment",e:/;/,eW:!0,l:/[\w\.]+/,k:{keyword:"abort abs absolute acc acce accep accept access accessed accessible account acos action activate add addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias allocate allow alter always analyze ancillary and any anydata anydataset anyschema anytype apply archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base char_length character_length characters characterset charindex charset charsetform charsetid check checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation collect colu colum column column_value columns columns_updated comment commit compact compatibility compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection consider consistent constant constraint constraints constructor container content contents context contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime customdatum cycle data database databases datafile datafiles datalength date_add date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor deterministic diagnostics difference dimension direct_load directory disable disable_all disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div do document domain dotnet double downgrade drop dumpfile duplicate duration each edition editionable editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding execu execut execute exempt exists exit exp expire explain export export_set extended extent external external_1 external_2 externally extract failed failed_login_attempts failover failure far fast feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final finish first first_value fixed flash_cache flashback floor flush following follows for forall force form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ftp full function general generated get get_format get_lock getdate getutcdate global global_name globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex hierarchy high high_priority hosts hour http id ident_current ident_incr ident_seed identified identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile initial initialized initially initrans inmemory inner innodb input insert install instance instantiable instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists keep keep_duplicates key keys kill language large last last_day last_insert_id last_value lax lcase lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call logoff logon logs long loop low low_priority lower lpad lrtrim ltrim main make_set makedate maketime managed management manual map mapping mask master master_pos_wait match matched materialized max maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans md5 measures median medium member memcompress memory merge microsecond mid migration min minextents minimum mining minus minute minvalue missing mod mode model modification modify module monitoring month months mount move movement multiset mutex name name_const names nan national native natural nav nchar nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck noswitch not nothing notice notrim novalidate now nowait nth_value nullif nulls num numb numbe nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary out outer outfile outline output over overflow overriding package pad parallel parallel_enable parameters parent parse partial partition partitions pascal passing password password_grace_time password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction prediction_cost prediction_details prediction_probability prediction_set prepare present preserve prior priority private private_sga privileges procedural procedure procedure_analyze processlist profiles project prompt protection public publishingservername purge quarter query quick quiesce quota quotename radians raise rand range rank raw read reads readsize rebuild record records recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename repair repeat replace replicate replication required reset resetlogs resize resource respect restore restricted result result_cache resumable resume retention return returning returns reuse reverse revoke right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll sdo_georaster sdo_topo_geometry search sec_to_time second section securefile security seed segment select self sequence sequential serializable server servererror session session_user sessions_per_user set sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone standby start starting startup statement static statistics stats_binomial_test stats_crosstab stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime table tables tablespace tan tdo template temporary terminated tertiary_weights test than then thread through tier ties time time_format time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unpivot unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear wellformed when whene whenev wheneve whenever where while whitespace with within without work wrapped xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek",literal:"true false null",built_in:"array bigint binary bit blob boolean char character date dec decimal float int int8 integer interval number numeric real record serial serial8 smallint text varchar varying void"},c:[{cN:"string",b:"'",e:"'",c:[e.BE,{b:"''"}]},{cN:"string",b:'"',e:'"',c:[e.BE,{b:'""'}]},{cN:"string",b:"`",e:"`",c:[e.BE]},e.CNM,e.CBCM,t]},e.CBCM,t]}}),e}); \ No newline at end of file diff --git a/Wino.Mail/JS/Quill/image-resize.min.js b/Wino.Mail/JS/Quill/image-resize.min.js new file mode 100644 index 00000000..804145d5 --- /dev/null +++ b/Wino.Mail/JS/Quill/image-resize.min.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ImageResize=e():t.ImageResize=e()}(this,function(){return function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,o){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:o})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=38)}([function(t,e){function n(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)}t.exports=n},function(t,e,n){var o=n(22),r="object"==typeof self&&self&&self.Object===Object&&self,i=o||r||Function("return this")();t.exports=i},function(t,e){function n(t){return null!=t&&"object"==typeof t}t.exports=n},function(t,e,n){function o(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e-1&&t%1==0&&t-1&&t%1==0&&t<=o}var o=9007199254740991;t.exports=n},function(t,e,n){var o=n(49),r=n(54),i=n(86),a=i&&i.isTypedArray,s=a?r(a):o;t.exports=s},function(t,e,n){function o(t){return a(t)?r(t,!0):i(t)}var r=n(43),i=n(50),a=n(12);t.exports=o},function(t,e,n){"use strict";e.a={modules:["DisplaySize","Toolbar","Resize"],overlayStyles:{position:"absolute",boxSizing:"border-box",border:"1px dashed #444"},handleStyles:{position:"absolute",height:"12px",width:"12px",backgroundColor:"white",border:"1px solid #777",boxSizing:"border-box",opacity:"0.80"},displayStyles:{position:"absolute",font:"12px/1.0 Arial, Helvetica, sans-serif",padding:"4px 8px",textAlign:"center",backgroundColor:"white",color:"#333",border:"1px solid #777",boxSizing:"border-box",opacity:"0.80",cursor:"default"},toolbarStyles:{position:"absolute",top:"-12px",right:"0",left:"0",height:"0",minWidth:"100px",font:"12px/1.0 Arial, Helvetica, sans-serif",textAlign:"center",color:"#333",boxSizing:"border-box",cursor:"default"},toolbarButtonStyles:{display:"inline-block",width:"24px",height:"24px",background:"white",border:"1px solid #999",verticalAlign:"middle"},toolbarButtonSvgStyles:{fill:"#444",stroke:"#444",strokeWidth:"2"}}},function(t,e,n){"use strict";function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function r(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var a=n(9);n.d(e,"a",function(){return s});var s=function(t){function e(){var t,n,i,a;o(this,e);for(var s=arguments.length,u=Array(s),c=0;c120&&t[1]>30)Object.assign(i.display.style,{right:"4px",bottom:"4px",left:"auto"});else if("right"==i.img.style.float){var e=i.display.getBoundingClientRect();Object.assign(i.display.style,{right:"auto",bottom:"-"+(e.height+4)+"px",left:"-"+(e.width+4)+"px"})}else{var n=i.display.getBoundingClientRect();Object.assign(i.display.style,{right:"-"+(n.width+4)+"px",bottom:"-"+(n.height+4)+"px",left:"auto"})}}},i.getCurrentSize=function(){return[i.img.width,Math.round(i.img.width/i.img.naturalWidth*i.img.naturalHeight)]},a=n,r(i,a)}return i(e,t),e}(a.a)},function(t,e,n){"use strict";function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function r(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var a=n(9);n.d(e,"a",function(){return s});var s=function(t){function e(){var t,n,i,a;o(this,e);for(var s=arguments.length,u=Array(s),c=0;c0&&(o.style.borderLeftWidth="0"),Object.assign(o.children[0].style,i.options.toolbarButtonSvgStyles),e.isApplied()&&i._selectButton(o),i.toolbar.appendChild(o)})},i._selectButton=function(t){t.style.filter="invert(20%)"},a=n,r(i,a)}return i(e,t),e}(p.a)},function(t,e,n){var o=n(17),r=n(20),i=n(63),a=n(101),s=r(function(t){return t.push(void 0,i),o(a,void 0,t)});t.exports=s},function(t,e,n){"use strict";function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var r=n(37),i=n.n(r),a=n(33),s=n(34),u=n(36),c=n(35),l={DisplaySize:s.a,Toolbar:u.a,Resize:c.a},f=function t(e){var n=this,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};o(this,t),this.initializeModules=function(){n.removeModules(),n.modules=n.moduleClasses.map(function(t){return new(l[t]||t)(n)}),n.modules.forEach(function(t){t.onCreate()}),n.onUpdate()},this.onUpdate=function(){n.repositionElements(),n.modules.forEach(function(t){t.onUpdate()})},this.removeModules=function(){n.modules.forEach(function(t){t.onDestroy()}),n.modules=[]},this.handleClick=function(t){if(t.target&&t.target.tagName&&"IMG"===t.target.tagName.toUpperCase()){if(n.img===t.target)return;n.img&&n.hide(),n.show(t.target)}else n.img&&n.hide()},this.show=function(t){n.img=t,n.showOverlay(),n.initializeModules()},this.showOverlay=function(){n.overlay&&n.hideOverlay(),n.quill.setSelection(null),n.setUserSelect("none"),document.addEventListener("keyup",n.checkImage,!0),n.quill.root.addEventListener("input",n.checkImage,!0),n.overlay=document.createElement("div"),Object.assign(n.overlay.style,n.options.overlayStyles),n.quill.root.parentNode.appendChild(n.overlay),n.repositionElements()},this.hideOverlay=function(){n.overlay&&(n.quill.root.parentNode.removeChild(n.overlay),n.overlay=void 0,document.removeEventListener("keyup",n.checkImage),n.quill.root.removeEventListener("input",n.checkImage),n.setUserSelect(""))},this.repositionElements=function(){if(n.overlay&&n.img){var t=n.quill.root.parentNode,e=n.img.getBoundingClientRect(),o=t.getBoundingClientRect();Object.assign(n.overlay.style,{left:e.left-o.left-1+t.scrollLeft+"px",top:e.top-o.top+t.scrollTop+"px",width:e.width+"px",height:e.height+"px"})}},this.hide=function(){n.hideOverlay(),n.removeModules(),n.img=void 0},this.setUserSelect=function(t){["userSelect","mozUserSelect","webkitUserSelect","msUserSelect"].forEach(function(e){n.quill.root.style[e]=t,document.documentElement.style[e]=t})},this.checkImage=function(t){n.img&&(46!=t.keyCode&&8!=t.keyCode||window.Quill.find(n.img).deleteAt(0),n.hide())},this.quill=e;var s=!1;r.modules&&(s=r.modules.slice()),this.options=i()({},r,a.a),s!==!1&&(this.options.modules=s),document.execCommand("enableObjectResizing",!1,"false"),this.quill.root.addEventListener("click",this.handleClick,!1),this.quill.root.parentNode.style.position=this.quill.root.parentNode.style.position||"relative",this.moduleClasses=this.options.modules,console.log("this.options.modules",this.options.modules),this.modules=[]};e.default=f,window.Quill&&window.Quill.register("modules/imageResize",f)},function(t,e,n){function o(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e1?n[r-1]:void 0,s=r>2?n[2]:void 0;for(a=t.length>3&&"function"==typeof a?(r--,a):void 0,s&&i(n[0],n[1],s)&&(a=r<3?void 0:a,r=1),e=Object(e);++o-1}var r=n(4);t.exports=o},function(t,e,n){function o(t,e){var n=this.__data__,o=r(n,t);return o<0?(++this.size,n.push([t,e])):n[o][1]=e,this}var r=n(4);t.exports=o},function(t,e,n){function o(){this.size=0,this.__data__={hash:new r,map:new(a||i),string:new r}}var r=n(39),i=n(3),a=n(15);t.exports=o},function(t,e,n){function o(t){var e=r(this,t).delete(t);return this.size-=e?1:0,e}var r=n(6);t.exports=o},function(t,e,n){function o(t){return r(this,t).get(t)}var r=n(6);t.exports=o},function(t,e,n){function o(t){return r(this,t).has(t)}var r=n(6);t.exports=o},function(t,e,n){function o(t,e){var n=r(this,t),o=n.size;return n.set(t,e),this.size+=n.size==o?0:1,this}var r=n(6);t.exports=o},function(t,e){function n(t){var e=[];if(null!=t)for(var n in Object(t))e.push(n);return e}t.exports=n},function(t,e,n){(function(t){var o=n(22),r="object"==typeof e&&e&&!e.nodeType&&e,i=r&&"object"==typeof t&&t&&!t.nodeType&&t,a=i&&i.exports===r,s=a&&o.process,u=function(){try{return s&&s.binding&&s.binding("util")}catch(t){}}();t.exports=u}).call(e,n(14)(t))},function(t,e){function n(t){return r.call(t)}var o=Object.prototype,r=o.toString;t.exports=n},function(t,e){function n(t,e){return function(n){return t(e(n))}}t.exports=n},function(t,e,n){function o(t,e,n){return e=i(void 0===e?t.length-1:e,0),function(){for(var o=arguments,a=-1,s=i(o.length-e,0),u=Array(s);++a0){if(++e>=o)return arguments[0]}else e=0;return t.apply(void 0,arguments)}}var o=800,r=16,i=Date.now;t.exports=n},function(t,e,n){function o(){this.__data__=new r,this.size=0}var r=n(3);t.exports=o},function(t,e){function n(t){var e=this.__data__,n=e.delete(t);return this.size=e.size,n}t.exports=n},function(t,e){function n(t){return this.__data__.get(t)}t.exports=n},function(t,e){function n(t){return this.__data__.has(t)}t.exports=n},function(t,e,n){function o(t,e){var n=this.__data__;if(n instanceof r){var o=n.__data__;if(!i||o.length\n \n \n \n'},function(t,e){t.exports='\n \n \n \n'},function(t,e){t.exports='\n \n \n \n'},function(t,e){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(t){"object"==typeof window&&(n=window)}t.exports=n}])}); \ No newline at end of file diff --git a/Wino.Mail/JS/Quill/katex.min.css b/Wino.Mail/JS/Quill/katex.min.css new file mode 100644 index 00000000..d6fb8374 --- /dev/null +++ b/Wino.Mail/JS/Quill/katex.min.css @@ -0,0 +1 @@ +@font-face{font-family:KaTeX_AMS;src:url(fonts/KaTeX_AMS-Regular.eot);src:url(fonts/KaTeX_AMS-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_AMS-Regular.woff2) format('woff2'),url(fonts/KaTeX_AMS-Regular.woff) format('woff'),url(fonts/KaTeX_AMS-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Caligraphic;src:url(fonts/KaTeX_Caligraphic-Bold.eot);src:url(fonts/KaTeX_Caligraphic-Bold.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Caligraphic-Bold.woff2) format('woff2'),url(fonts/KaTeX_Caligraphic-Bold.woff) format('woff'),url(fonts/KaTeX_Caligraphic-Bold.ttf) format('truetype');font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Caligraphic;src:url(fonts/KaTeX_Caligraphic-Regular.eot);src:url(fonts/KaTeX_Caligraphic-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Caligraphic-Regular.woff2) format('woff2'),url(fonts/KaTeX_Caligraphic-Regular.woff) format('woff'),url(fonts/KaTeX_Caligraphic-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Fraktur;src:url(fonts/KaTeX_Fraktur-Bold.eot);src:url(fonts/KaTeX_Fraktur-Bold.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Fraktur-Bold.woff2) format('woff2'),url(fonts/KaTeX_Fraktur-Bold.woff) format('woff'),url(fonts/KaTeX_Fraktur-Bold.ttf) format('truetype');font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Fraktur;src:url(fonts/KaTeX_Fraktur-Regular.eot);src:url(fonts/KaTeX_Fraktur-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Fraktur-Regular.woff2) format('woff2'),url(fonts/KaTeX_Fraktur-Regular.woff) format('woff'),url(fonts/KaTeX_Fraktur-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-Bold.eot);src:url(fonts/KaTeX_Main-Bold.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Main-Bold.woff2) format('woff2'),url(fonts/KaTeX_Main-Bold.woff) format('woff'),url(fonts/KaTeX_Main-Bold.ttf) format('truetype');font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-Italic.eot);src:url(fonts/KaTeX_Main-Italic.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Main-Italic.woff2) format('woff2'),url(fonts/KaTeX_Main-Italic.woff) format('woff'),url(fonts/KaTeX_Main-Italic.ttf) format('truetype');font-weight:400;font-style:italic}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-Regular.eot);src:url(fonts/KaTeX_Main-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Main-Regular.woff2) format('woff2'),url(fonts/KaTeX_Main-Regular.woff) format('woff'),url(fonts/KaTeX_Main-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Math;src:url(fonts/KaTeX_Math-Italic.eot);src:url(fonts/KaTeX_Math-Italic.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Math-Italic.woff2) format('woff2'),url(fonts/KaTeX_Math-Italic.woff) format('woff'),url(fonts/KaTeX_Math-Italic.ttf) format('truetype');font-weight:400;font-style:italic}@font-face{font-family:KaTeX_SansSerif;src:url(fonts/KaTeX_SansSerif-Regular.eot);src:url(fonts/KaTeX_SansSerif-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_SansSerif-Regular.woff2) format('woff2'),url(fonts/KaTeX_SansSerif-Regular.woff) format('woff'),url(fonts/KaTeX_SansSerif-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Script;src:url(fonts/KaTeX_Script-Regular.eot);src:url(fonts/KaTeX_Script-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Script-Regular.woff2) format('woff2'),url(fonts/KaTeX_Script-Regular.woff) format('woff'),url(fonts/KaTeX_Script-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size1;src:url(fonts/KaTeX_Size1-Regular.eot);src:url(fonts/KaTeX_Size1-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Size1-Regular.woff2) format('woff2'),url(fonts/KaTeX_Size1-Regular.woff) format('woff'),url(fonts/KaTeX_Size1-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size2;src:url(fonts/KaTeX_Size2-Regular.eot);src:url(fonts/KaTeX_Size2-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Size2-Regular.woff2) format('woff2'),url(fonts/KaTeX_Size2-Regular.woff) format('woff'),url(fonts/KaTeX_Size2-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size3;src:url(fonts/KaTeX_Size3-Regular.eot);src:url(fonts/KaTeX_Size3-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Size3-Regular.woff2) format('woff2'),url(fonts/KaTeX_Size3-Regular.woff) format('woff'),url(fonts/KaTeX_Size3-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size4;src:url(fonts/KaTeX_Size4-Regular.eot);src:url(fonts/KaTeX_Size4-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Size4-Regular.woff2) format('woff2'),url(fonts/KaTeX_Size4-Regular.woff) format('woff'),url(fonts/KaTeX_Size4-Regular.ttf) format('truetype');font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Typewriter;src:url(fonts/KaTeX_Typewriter-Regular.eot);src:url(fonts/KaTeX_Typewriter-Regular.eot#iefix) format('embedded-opentype'),url(fonts/KaTeX_Typewriter-Regular.woff2) format('woff2'),url(fonts/KaTeX_Typewriter-Regular.woff) format('woff'),url(fonts/KaTeX_Typewriter-Regular.ttf) format('truetype');font-weight:400;font-style:normal}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:inline-block;text-align:initial}.katex{font:400 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;white-space:nowrap;text-indent:0}.katex .katex-html{display:inline-block}.katex .katex-mathml{position:absolute;clip:rect(1px,1px,1px,1px);padding:0;border:0;height:1px;width:1px;overflow:hidden}.katex .base,.katex .strut{display:inline-block}.katex .mathrm{font-style:normal}.katex .textit{font-style:italic}.katex .mathit{font-family:KaTeX_Math;font-style:italic}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .amsrm,.katex .mathbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak{font-family:KaTeX_Fraktur}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr{font-family:KaTeX_Script}.katex .mathsf{font-family:KaTeX_SansSerif}.katex .mainit{font-family:KaTeX_Main;font-style:italic}.katex .mord+.mop{margin-left:.16667em}.katex .mord+.mbin{margin-left:.22222em}.katex .mord+.mrel{margin-left:.27778em}.katex .mop+.mop,.katex .mop+.mord,.katex .mord+.minner{margin-left:.16667em}.katex .mop+.mrel{margin-left:.27778em}.katex .mop+.minner{margin-left:.16667em}.katex .mbin+.minner,.katex .mbin+.mop,.katex .mbin+.mopen,.katex .mbin+.mord{margin-left:.22222em}.katex .mrel+.minner,.katex .mrel+.mop,.katex .mrel+.mopen,.katex .mrel+.mord{margin-left:.27778em}.katex .mclose+.mop{margin-left:.16667em}.katex .mclose+.mbin{margin-left:.22222em}.katex .mclose+.mrel{margin-left:.27778em}.katex .mclose+.minner,.katex .minner+.mop,.katex .minner+.mord,.katex .mpunct+.mclose,.katex .mpunct+.minner,.katex .mpunct+.mop,.katex .mpunct+.mopen,.katex .mpunct+.mord,.katex .mpunct+.mpunct,.katex .mpunct+.mrel{margin-left:.16667em}.katex .minner+.mbin{margin-left:.22222em}.katex .minner+.mrel{margin-left:.27778em}.katex .minner+.minner,.katex .minner+.mopen,.katex .minner+.mpunct{margin-left:.16667em}.katex .mbin.mtight,.katex .mclose.mtight,.katex .minner.mtight,.katex .mop.mtight,.katex .mopen.mtight,.katex .mord.mtight,.katex .mpunct.mtight,.katex .mrel.mtight{margin-left:0}.katex .mclose+.mop.mtight,.katex .minner+.mop.mtight,.katex .mop+.mop.mtight,.katex .mop+.mord.mtight,.katex .mord+.mop.mtight{margin-left:.16667em}.katex .reset-textstyle.textstyle{font-size:1em}.katex .reset-textstyle.scriptstyle{font-size:.7em}.katex .reset-textstyle.scriptscriptstyle{font-size:.5em}.katex .reset-scriptstyle.textstyle{font-size:1.42857em}.katex .reset-scriptstyle.scriptstyle{font-size:1em}.katex .reset-scriptstyle.scriptscriptstyle{font-size:.71429em}.katex .reset-scriptscriptstyle.textstyle{font-size:2em}.katex .reset-scriptscriptstyle.scriptstyle{font-size:1.4em}.katex .reset-scriptscriptstyle.scriptscriptstyle{font-size:1em}.katex .style-wrap{position:relative}.katex .vlist{display:inline-block}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist .baseline-fix{display:inline-table;table-layout:fixed}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{width:100%}.katex .mfrac .frac-line:before{border-bottom-style:solid;border-bottom-width:1px;content:"";display:block}.katex .mfrac .frac-line:after{border-bottom-style:solid;border-bottom-width:.04em;content:"";display:block;margin-top:-1px}.katex .mspace{display:inline-block}.katex .mspace.negativethinspace{margin-left:-.16667em}.katex .mspace.thinspace{width:.16667em}.katex .mspace.negativemediumspace{margin-left:-.22222em}.katex .mspace.mediumspace{width:.22222em}.katex .mspace.thickspace{width:.27778em}.katex .mspace.sixmuspace{width:.333333em}.katex .mspace.eightmuspace{width:.444444em}.katex .mspace.enspace{width:.5em}.katex .mspace.twelvemuspace{width:.666667em}.katex .mspace.quad{width:1em}.katex .mspace.qquad{width:2em}.katex .llap,.katex .rlap{width:0;position:relative}.katex .llap>.inner,.katex .rlap>.inner{position:absolute}.katex .llap>.fix,.katex .rlap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .rlap>.inner{left:0}.katex .katex-logo .a{font-size:.75em;margin-left:-.32em;position:relative;top:-.2em}.katex .katex-logo .t{margin-left:-.23em}.katex .katex-logo .e{margin-left:-.1667em;position:relative;top:.2155em}.katex .katex-logo .x{margin-left:-.125em}.katex .rule{display:inline-block;border:0 solid;position:relative}.katex .overline .overline-line,.katex .underline .underline-line{width:100%}.katex .overline .overline-line:before,.katex .underline .underline-line:before{border-bottom-style:solid;border-bottom-width:1px;content:"";display:block}.katex .overline .overline-line:after,.katex .underline .underline-line:after{border-bottom-style:solid;border-bottom-width:.04em;content:"";display:block;margin-top:-1px}.katex .sqrt>.sqrt-sign{position:relative}.katex .sqrt .sqrt-line{width:100%}.katex .sqrt .sqrt-line:before{border-bottom-style:solid;border-bottom-width:1px;content:"";display:block}.katex .sqrt .sqrt-line:after{border-bottom-style:solid;border-bottom-width:.04em;content:"";display:block;margin-top:-1px}.katex .sqrt>.root{margin-left:.27777778em;margin-right:-.55555556em}.katex .fontsize-ensurer,.katex .sizing{display:inline-block}.katex .fontsize-ensurer.reset-size1.size1,.katex .sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .sizing.reset-size1.size2{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size3,.katex .sizing.reset-size1.size3{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size4,.katex .sizing.reset-size1.size4{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size5,.katex .sizing.reset-size1.size5{font-size:2em}.katex .fontsize-ensurer.reset-size1.size6,.katex .sizing.reset-size1.size6{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size7,.katex .sizing.reset-size1.size7{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size8,.katex .sizing.reset-size1.size8{font-size:3.46em}.katex .fontsize-ensurer.reset-size1.size9,.katex .sizing.reset-size1.size9{font-size:4.14em}.katex .fontsize-ensurer.reset-size1.size10,.katex .sizing.reset-size1.size10{font-size:4.98em}.katex .fontsize-ensurer.reset-size2.size1,.katex .sizing.reset-size2.size1{font-size:.71428571em}.katex .fontsize-ensurer.reset-size2.size2,.katex .sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .sizing.reset-size2.size3{font-size:1.14285714em}.katex .fontsize-ensurer.reset-size2.size4,.katex .sizing.reset-size2.size4{font-size:1.28571429em}.katex .fontsize-ensurer.reset-size2.size5,.katex .sizing.reset-size2.size5{font-size:1.42857143em}.katex .fontsize-ensurer.reset-size2.size6,.katex .sizing.reset-size2.size6{font-size:1.71428571em}.katex .fontsize-ensurer.reset-size2.size7,.katex .sizing.reset-size2.size7{font-size:2.05714286em}.katex .fontsize-ensurer.reset-size2.size8,.katex .sizing.reset-size2.size8{font-size:2.47142857em}.katex .fontsize-ensurer.reset-size2.size9,.katex .sizing.reset-size2.size9{font-size:2.95714286em}.katex .fontsize-ensurer.reset-size2.size10,.katex .sizing.reset-size2.size10{font-size:3.55714286em}.katex .fontsize-ensurer.reset-size3.size1,.katex .sizing.reset-size3.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size3.size2,.katex .sizing.reset-size3.size2{font-size:.875em}.katex .fontsize-ensurer.reset-size3.size3,.katex .sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .sizing.reset-size3.size4{font-size:1.125em}.katex .fontsize-ensurer.reset-size3.size5,.katex .sizing.reset-size3.size5{font-size:1.25em}.katex .fontsize-ensurer.reset-size3.size6,.katex .sizing.reset-size3.size6{font-size:1.5em}.katex .fontsize-ensurer.reset-size3.size7,.katex .sizing.reset-size3.size7{font-size:1.8em}.katex .fontsize-ensurer.reset-size3.size8,.katex .sizing.reset-size3.size8{font-size:2.1625em}.katex .fontsize-ensurer.reset-size3.size9,.katex .sizing.reset-size3.size9{font-size:2.5875em}.katex .fontsize-ensurer.reset-size3.size10,.katex .sizing.reset-size3.size10{font-size:3.1125em}.katex .fontsize-ensurer.reset-size4.size1,.katex .sizing.reset-size4.size1{font-size:.55555556em}.katex .fontsize-ensurer.reset-size4.size2,.katex .sizing.reset-size4.size2{font-size:.77777778em}.katex .fontsize-ensurer.reset-size4.size3,.katex .sizing.reset-size4.size3{font-size:.88888889em}.katex .fontsize-ensurer.reset-size4.size4,.katex .sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .sizing.reset-size4.size5{font-size:1.11111111em}.katex .fontsize-ensurer.reset-size4.size6,.katex .sizing.reset-size4.size6{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size4.size7,.katex .sizing.reset-size4.size7{font-size:1.6em}.katex .fontsize-ensurer.reset-size4.size8,.katex .sizing.reset-size4.size8{font-size:1.92222222em}.katex .fontsize-ensurer.reset-size4.size9,.katex .sizing.reset-size4.size9{font-size:2.3em}.katex .fontsize-ensurer.reset-size4.size10,.katex .sizing.reset-size4.size10{font-size:2.76666667em}.katex .fontsize-ensurer.reset-size5.size1,.katex .sizing.reset-size5.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size5.size2,.katex .sizing.reset-size5.size2{font-size:.7em}.katex .fontsize-ensurer.reset-size5.size3,.katex .sizing.reset-size5.size3{font-size:.8em}.katex .fontsize-ensurer.reset-size5.size4,.katex .sizing.reset-size5.size4{font-size:.9em}.katex .fontsize-ensurer.reset-size5.size5,.katex .sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .sizing.reset-size5.size6{font-size:1.2em}.katex .fontsize-ensurer.reset-size5.size7,.katex .sizing.reset-size5.size7{font-size:1.44em}.katex .fontsize-ensurer.reset-size5.size8,.katex .sizing.reset-size5.size8{font-size:1.73em}.katex .fontsize-ensurer.reset-size5.size9,.katex .sizing.reset-size5.size9{font-size:2.07em}.katex .fontsize-ensurer.reset-size5.size10,.katex .sizing.reset-size5.size10{font-size:2.49em}.katex .fontsize-ensurer.reset-size6.size1,.katex .sizing.reset-size6.size1{font-size:.41666667em}.katex .fontsize-ensurer.reset-size6.size2,.katex .sizing.reset-size6.size2{font-size:.58333333em}.katex .fontsize-ensurer.reset-size6.size3,.katex .sizing.reset-size6.size3{font-size:.66666667em}.katex .fontsize-ensurer.reset-size6.size4,.katex .sizing.reset-size6.size4{font-size:.75em}.katex .fontsize-ensurer.reset-size6.size5,.katex .sizing.reset-size6.size5{font-size:.83333333em}.katex .fontsize-ensurer.reset-size6.size6,.katex .sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .sizing.reset-size6.size8{font-size:1.44166667em}.katex .fontsize-ensurer.reset-size6.size9,.katex .sizing.reset-size6.size9{font-size:1.725em}.katex .fontsize-ensurer.reset-size6.size10,.katex .sizing.reset-size6.size10{font-size:2.075em}.katex .fontsize-ensurer.reset-size7.size1,.katex .sizing.reset-size7.size1{font-size:.34722222em}.katex .fontsize-ensurer.reset-size7.size2,.katex .sizing.reset-size7.size2{font-size:.48611111em}.katex .fontsize-ensurer.reset-size7.size3,.katex .sizing.reset-size7.size3{font-size:.55555556em}.katex .fontsize-ensurer.reset-size7.size4,.katex .sizing.reset-size7.size4{font-size:.625em}.katex .fontsize-ensurer.reset-size7.size5,.katex .sizing.reset-size7.size5{font-size:.69444444em}.katex .fontsize-ensurer.reset-size7.size6,.katex .sizing.reset-size7.size6{font-size:.83333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .sizing.reset-size7.size8{font-size:1.20138889em}.katex .fontsize-ensurer.reset-size7.size9,.katex .sizing.reset-size7.size9{font-size:1.4375em}.katex .fontsize-ensurer.reset-size7.size10,.katex .sizing.reset-size7.size10{font-size:1.72916667em}.katex .fontsize-ensurer.reset-size8.size1,.katex .sizing.reset-size8.size1{font-size:.28901734em}.katex .fontsize-ensurer.reset-size8.size2,.katex .sizing.reset-size8.size2{font-size:.40462428em}.katex .fontsize-ensurer.reset-size8.size3,.katex .sizing.reset-size8.size3{font-size:.46242775em}.katex .fontsize-ensurer.reset-size8.size4,.katex .sizing.reset-size8.size4{font-size:.52023121em}.katex .fontsize-ensurer.reset-size8.size5,.katex .sizing.reset-size8.size5{font-size:.57803468em}.katex .fontsize-ensurer.reset-size8.size6,.katex .sizing.reset-size8.size6{font-size:.69364162em}.katex .fontsize-ensurer.reset-size8.size7,.katex .sizing.reset-size8.size7{font-size:.83236994em}.katex .fontsize-ensurer.reset-size8.size8,.katex .sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .sizing.reset-size8.size9{font-size:1.19653179em}.katex .fontsize-ensurer.reset-size8.size10,.katex .sizing.reset-size8.size10{font-size:1.43930636em}.katex .fontsize-ensurer.reset-size9.size1,.katex .sizing.reset-size9.size1{font-size:.24154589em}.katex .fontsize-ensurer.reset-size9.size2,.katex .sizing.reset-size9.size2{font-size:.33816425em}.katex .fontsize-ensurer.reset-size9.size3,.katex .sizing.reset-size9.size3{font-size:.38647343em}.katex .fontsize-ensurer.reset-size9.size4,.katex .sizing.reset-size9.size4{font-size:.43478261em}.katex .fontsize-ensurer.reset-size9.size5,.katex .sizing.reset-size9.size5{font-size:.48309179em}.katex .fontsize-ensurer.reset-size9.size6,.katex .sizing.reset-size9.size6{font-size:.57971014em}.katex .fontsize-ensurer.reset-size9.size7,.katex .sizing.reset-size9.size7{font-size:.69565217em}.katex .fontsize-ensurer.reset-size9.size8,.katex .sizing.reset-size9.size8{font-size:.83574879em}.katex .fontsize-ensurer.reset-size9.size9,.katex .sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .sizing.reset-size9.size10{font-size:1.20289855em}.katex .fontsize-ensurer.reset-size10.size1,.katex .sizing.reset-size10.size1{font-size:.20080321em}.katex .fontsize-ensurer.reset-size10.size2,.katex .sizing.reset-size10.size2{font-size:.2811245em}.katex .fontsize-ensurer.reset-size10.size3,.katex .sizing.reset-size10.size3{font-size:.32128514em}.katex .fontsize-ensurer.reset-size10.size4,.katex .sizing.reset-size10.size4{font-size:.36144578em}.katex .fontsize-ensurer.reset-size10.size5,.katex .sizing.reset-size10.size5{font-size:.40160643em}.katex .fontsize-ensurer.reset-size10.size6,.katex .sizing.reset-size10.size6{font-size:.48192771em}.katex .fontsize-ensurer.reset-size10.size7,.katex .sizing.reset-size10.size7{font-size:.57831325em}.katex .fontsize-ensurer.reset-size10.size8,.katex .sizing.reset-size10.size8{font-size:.69477912em}.katex .fontsize-ensurer.reset-size10.size9,.katex .sizing.reset-size10.size9{font-size:.8313253em}.katex .fontsize-ensurer.reset-size10.size10,.katex .sizing.reset-size10.size10{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .accent>.vlist>span,.katex .op-limits>.vlist>span{text-align:center}.katex .accent .accent-body>span{width:0}.katex .accent .accent-body.accent-vec>span{position:relative;left:.326em}.katex .mtable .vertical-separator{display:inline-block;margin:0 -.025em;border-right:.05em solid #000}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist{text-align:center}.katex .mtable .col-align-l>.vlist{text-align:left}.katex .mtable .col-align-r>.vlist{text-align:right} \ No newline at end of file diff --git a/Wino.Mail/JS/Quill/katex.min.js b/Wino.Mail/JS/Quill/katex.min.js new file mode 100644 index 00000000..66c08216 --- /dev/null +++ b/Wino.Mail/JS/Quill/katex.min.js @@ -0,0 +1,4 @@ +(function(e){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=e()}else if(typeof define==="function"&&define.amd){define([],e)}else{var t;if(typeof window!=="undefined"){t=window}else if(typeof global!=="undefined"){t=global}else if(typeof self!=="undefined"){t=self}else{t=this}t.katex=e()}})(function(){var e,t,r;return function a(e,t,r){function i(s,l){if(!t[s]){if(!e[s]){var o=typeof require=="function"&&require;if(!l&&o)return o(s,!0);if(n)return n(s,!0);var u=new Error("Cannot find module '"+s+"'");throw u.code="MODULE_NOT_FOUND",u}var p=t[s]={exports:{}};e[s][0].call(p.exports,function(t){var r=e[s][1][t];return i(r?r:t)},p,p.exports,a,e,t,r)}return t[s].exports}var n=typeof require=="function"&&require;for(var s=0;s15){o="\u2026"+s.slice(i-15,i)}else{o=s.slice(0,i)}var u;if(n+15v){return this.parseFunction(i)}else{throw new p("Got function '"+i.result+"' with no arguments "+"as "+e,t)}}else{return i.result}};h.prototype.handleUnsupportedCmd=function(){var e=this.nextToken.text;var t=[];for(var r=0;ri){c=this.parseFunction(h)}else{throw new p("Got function '"+h.result+"' as "+"argument to '"+e+"'",o)}}else{c=h.result}s.push(c);n.push(this.pos)}s.push(n);return s};h.prototype.parseGroupOfType=function(e,t){var r=this.mode;if(e==="original"){e=r}if(e==="color"){return this.parseColorGroup(t)}if(e==="size"){return this.parseSizeGroup(t)}this.switchMode(e);if(e==="text"){while(this.nextToken.text===" "){this.consume()}}var a=this.parseGroup(t);this.switchMode(r);return a};h.prototype.parseStringGroup=function(e,t){if(t&&this.nextToken.text!=="["){return null}var r=this.mode;this.mode="text";this.expect(t?"[":"{");var a="";var i=this.nextToken;var n=i;while(this.nextToken.text!==(t?"]":"}")){if(this.nextToken.text==="EOF"){throw new p("Unexpected end of input in "+e,i.range(this.nextToken,a))}n=this.nextToken;a+=n.text;this.consume()}this.mode=r;this.expect(t?"]":"}");return i.range(n,a)};h.prototype.parseRegexGroup=function(e,t){var r=this.mode;this.mode="text";var a=this.nextToken;var i=a;var n="";while(this.nextToken.text!=="EOF"&&e.test(n+this.nextToken.text)){i=this.nextToken;n+=i.text;this.consume()}if(n===""){throw new p("Invalid "+t+": '"+a.text+"'",a)}this.mode=r;return a.range(i,n)};h.prototype.parseColorGroup=function(e){var t=this.parseStringGroup("color",e);if(!t){return null}var r=/^(#[a-z0-9]+|[a-z]+)$/i.exec(t.text);if(!r){throw new p("Invalid color: '"+t.text+"'",t)}return new m(new c("color",r[0],this.mode),false)};h.prototype.parseSizeGroup=function(e){var t;if(!e&&this.nextToken.text!=="{"){t=this.parseRegexGroup(/^[-+]? *(?:$|\d+|\d+\.\d*|\.\d*) *[a-z]{0,2}$/,"size")}else{t=this.parseStringGroup("size",e)}if(!t){return null}var r=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(t.text);if(!r){throw new p("Invalid size: '"+t.text+"'",t)}var a={number:+(r[1]+r[2]),unit:r[3]};if(a.unit!=="em"&&a.unit!=="ex"&&a.unit!=="mu"){throw new p("Invalid unit: '"+a.unit+"'",t)}return new m(new c("color",a,this.mode),false)};h.prototype.parseGroup=function(e){var t=this.nextToken;if(this.nextToken.text===(e?"[":"{")){this.consume();var r=this.parseExpression(false,e?"]":null);var a=this.nextToken;this.expect(e?"]":"}");if(this.mode==="text"){this.formLigatures(r)}return new m(new c("ordgroup",r,this.mode,t,a),false)}else{return e?null:this.parseSymbol()}};h.prototype.formLigatures=function(e){var t;var r=e.length-1;for(t=0;t0?t-1:0]}l.prototype.sup=function(){return y[x[this.id]]};l.prototype.sub=function(){return y[b[this.id]]};l.prototype.fracNum=function(){return y[w[this.id]]};l.prototype.fracDen=function(){return y[k[this.id]]};l.prototype.cramp=function(){return y[z[this.id]]};l.prototype.cls=function(){return d[this.size]+(this.cramped?" cramped":" uncramped")};l.prototype.reset=function(){return g[this.size]};l.prototype.isTight=function(){return this.size>=2};var o=0;var u=1;var p=2;var h=3;var c=4;var m=5;var f=6;var v=7;var d=["displaystyle textstyle","textstyle","scriptstyle","scriptscriptstyle"];var g=["reset-textstyle","reset-textstyle","reset-scriptstyle","reset-scriptscriptstyle"];var y=[new l(o,0,1,false),new l(u,0,1,true),new l(p,1,1,false),new l(h,1,1,true),new l(c,2,.7,false),new l(m,2,.7,true),new l(f,3,.5,false),new l(v,3,.5,true)];var x=[c,m,c,m,f,v,f,v];var b=[m,m,m,m,v,v,v,v];var w=[p,h,c,m,f,v,f,v];var k=[h,h,m,m,v,v,v,v];var z=[u,u,h,h,m,m,v,v];t.exports={DISPLAY:y[o],TEXT:y[p],SCRIPT:y[c],SCRIPTSCRIPT:y[f]}},{"./fontMetrics.js":17}],10:[function(e,t,r){var a=e("./domTree");var i=e("./fontMetrics");var n=e("./symbols");var s=e("./utils");var l=["\\Gamma","\\Delta","\\Theta","\\Lambda","\\Xi","\\Pi","\\Sigma","\\Upsilon","\\Phi","\\Psi","\\Omega"];var o=["\u0131","\u0237","\xa3"];var u=function(e,t,r,s,l){if(n[r][e]&&n[r][e].replace){e=n[r][e].replace}var o=i.getCharacterMetrics(e,t);var u;if(o){var p=o.italic;if(r==="text"){p=0}u=new a.symbolNode(e,o.height,o.depth,p,o.skew,l)}else{typeof console!=="undefined"&&console.warn("No character metrics for '"+e+"' in style '"+t+"'");u=new a.symbolNode(e,0,0,0,0,l)}if(s){if(s.style.isTight()){u.classes.push("mtight")}if(s.getColor()){u.style.color=s.getColor()}}return u};var p=function(e,t,r,a){if(e==="\\"||n[t][e].font==="main"){return u(e,"Main-Regular",t,r,a)}else{return u(e,"AMS-Regular",t,r,a.concat(["amsrm"]))}};var h=function(e,t,r,a,i){if(i==="mathord"){return c(e,t,r,a)}else if(i==="textord"){return u(e,"Main-Regular",t,r,a.concat(["mathrm"]))}else{throw new Error("unexpected type: "+i+" in mathDefault")}};var c=function(e,t,r,a){if(/[0-9]/.test(e.charAt(0))||s.contains(o,e)||s.contains(l,e)){return u(e,"Main-Italic",t,r,a.concat(["mainit"]))}else{return u(e,"Math-Italic",t,r,a.concat(["mathit"]))}};var m=function(e,t,r){var a=e.mode;var l=e.value;if(n[a][l]&&n[a][l].replace){l=n[a][l].replace}var p=["mord"];var m=t.font;if(m){if(m==="mathit"||s.contains(o,l)){return c(l,a,t,p)}else{var f=k[m].fontName;if(i.getCharacterMetrics(l,f)){return u(l,f,a,t,p.concat([m]))}else{return h(l,a,t,p,r)}}}else{return h(l,a,t,p,r)}};var f=function(e){var t=0;var r=0;var a=0;if(e.children){for(var i=0;it){t=e.children[i].height}if(e.children[i].depth>r){r=e.children[i].depth}if(e.children[i].maxFontSize>a){a=e.children[i].maxFontSize}}}e.height=t;e.depth=r;e.maxFontSize=a};var v=function(e,t,r){var i=new a.span(e,t,r);f(i);return i};var d=function(e,t){e.children=t.concat(e.children);f(e)};var g=function(e){var t=new a.documentFragment(e);f(t);return t};var y=function(e,t){var r=v([],[new a.symbolNode("\u200b")]);r.style.fontSize=t/e.style.sizeMultiplier+"em";var i=v(["fontsize-ensurer","reset-"+e.size,"size5"],[r]);return i};var x=function(e,t,r,i){var n;var s;var l;if(t==="individualShift"){var o=e;e=[o[0]];n=-o[0].shift-o[0].elem.depth;s=n;for(l=1;l0){f+=T;v-=T}}S=n.makeVList([{type:"elem",elem:s,shift:v},{type:"elem",elem:a,shift:-f}],"individualShift",null,t);if(r instanceof l.symbolNode){S.children[0].style.marginLeft=-r.italic+"em"}S.children[0].style.marginRight=k;S.children[1].style.marginRight=k}var A=d(r)||"mord";return p([A],[r,p(["msupsub"],[S])],t)};w.genfrac=function(e,t){var r=t.style;if(e.value.size==="display"){r=i.DISPLAY}else if(e.value.size==="text"){r=i.TEXT}var a=r.fracNum();var l=r.fracDen();var u;u=t.withStyle(a);var h=z(e.value.numer,u);var c=p([r.reset(),a.cls()],[h],u);u=t.withStyle(l);var m=z(e.value.denom,u);var f=p([r.reset(),l.cls()],[m],u);var v;if(e.value.hasBarLine){v=o.metrics.defaultRuleThickness/t.style.sizeMultiplier}else{v=0}var d;var g;var y;if(r.size===i.DISPLAY.size){d=r.metrics.num1;if(v>0){g=3*v}else{g=7*o.metrics.defaultRuleThickness}y=r.metrics.denom1}else{if(v>0){d=r.metrics.num2;g=v}else{d=r.metrics.num3;g=3*o.metrics.defaultRuleThickness}y=r.metrics.denom2}var x;if(v===0){var w=d-h.depth-(m.height-y);if(w0){N+=x;if(M=l){continue}var I;if(i>0||e.value.hskipBeforeAndAfter){I=u.deflt(O.pregap,f);if(I!==0){C=p(["arraycolsep"],[]);C.style.width=I+"em";E.push(C)}}var L=[];for(r=0;ra.height+a.depth+c){c=(c+d-a.height-a.depth)/2}var g=-(a.height+c+l)+v.height;v.style.top=g+"em";v.height-=g;v.depth+=g;var y;if(a.height===0&&a.depth===0){y=p()}else{y=n.makeVList([{type:"elem",elem:a},{type:"kern",size:c},{type:"elem",elem:u},{type:"kern",size:l}],"firstBaseline",null,t)}if(!e.value.index){return p(["mord","sqrt"],[v,y],t)}else{var x=t.withStyle(i.SCRIPTSCRIPT);var b=z(e.value.index,x);var w=p([r.reset(),i.SCRIPTSCRIPT.cls()],[b],x);var k=Math.max(v.height,y.height);var S=Math.max(v.depth,y.depth);var M=.6*(k-S);var T=n.makeVList([{type:"elem",elem:w}],"shift",-M,t);var A=p(["root"],[T]);return p(["mord","sqrt"],[A,v,y],t)}};w.sizing=function(e,t){var r=v(e.value.value,t.withSize(e.value.size),false);var a=t.style;var i=n.sizingMultiplier[e.value.size];i=i*a.sizeMultiplier;for(var s=0;s","\\langle","\\rangle","/","\\backslash","\\lt","\\gt"];var b=[0,1.2,1.8,2.4,3];var w=function(e,t,r,i,n){if(e==="<"||e==="\\lt"){e="\\langle"}else if(e===">"||e==="\\gt"){e="\\rangle"}if(o.contains(g,e)||o.contains(x,e)){return f(e,t,false,r,i,n)}else if(o.contains(y,e)){return d(e,b[t],false,r,i,n)}else{throw new a("Illegal delimiter: '"+e+"'")}};var k=[{type:"small",style:i.SCRIPTSCRIPT},{type:"small",style:i.SCRIPT},{type:"small",style:i.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4}];var z=[{type:"small",style:i.SCRIPTSCRIPT},{type:"small",style:i.SCRIPT},{type:"small",style:i.TEXT},{type:"stack"}];var S=[{type:"small",style:i.SCRIPTSCRIPT},{type:"small",style:i.SCRIPT},{type:"small",style:i.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4},{type:"stack"}];var M=function(e){if(e.type==="small"){return"Main-Regular"}else if(e.type==="large"){return"Size"+e.size+"-Regular"}else if(e.type==="stack"){return"Size4-Regular"}};var T=function(e,t,r,a){var i=Math.min(2,3-a.style.size);for(var n=i;nt){return r[n]}}return r[r.length-1]};var A=function(e,t,r,a,i,n){if(e==="<"||e==="\\lt"){e="\\langle"}else if(e===">"||e==="\\gt"){e="\\rangle"}var s;if(o.contains(x,e)){s=k}else if(o.contains(g,e)){s=S}else{s=z}var l=T(e,t,s,a);if(l.type==="small"){return m(e,l.style,r,a,i,n)}else if(l.type==="large"){return f(e,l.size,r,a,i,n)}else if(l.type==="stack"){return d(e,t,r,a,i,n)}};var N=function(e,t,r,a,i,n){var l=a.style.metrics.axisHeight*a.style.sizeMultiplier;var o=901;var u=5/s.metrics.ptPerEm;var p=Math.max(t-l,r+l);var h=Math.max(p/500*o,2*p-u);return A(e,h,true,a,i,n)};t.exports={sizedDelim:w,customSizedDelim:A,leftRightDelim:N}},{"./ParseError":6,"./Style":9,"./buildCommon":10,"./fontMetrics":17,"./symbols":23,"./utils":25}],15:[function(e,t,r){var a=e("./unicodeRegexes");var i=e("./utils");var n=function(e){e=e.slice();for(var t=e.length-1;t>=0;t--){if(!e[t]){e.splice(t,1)}}return e.join(" ")};function s(e,t,r){this.classes=e||[];this.children=t||[];this.height=0;this.depth=0;this.maxFontSize=0;this.style={};this.attributes={};if(r){if(r.style.isTight()){this.classes.push("mtight")}if(r.getColor()){this.style.color=r.getColor()}}}s.prototype.setAttribute=function(e,t){this.attributes[e]=t};s.prototype.tryCombine=function(e){return false};s.prototype.toNode=function(){var e=document.createElement("span");e.className=n(this.classes);for(var t in this.style){if(Object.prototype.hasOwnProperty.call(this.style,t)){e.style[t]=this.style[t]}}for(var r in this.attributes){if(Object.prototype.hasOwnProperty.call(this.attributes,r)){e.setAttribute(r,this.attributes[r])}}for(var a=0;a";return e};function l(e){this.children=e||[];this.height=0;this.depth=0;this.maxFontSize=0}l.prototype.toNode=function(){var e=document.createDocumentFragment();for(var t=0;t0||n(this.classes)!==n(e.classes)||this.skew!==e.skew||this.maxFontSize!==e.maxFontSize){return false}for(var t in this.style){if(this.style.hasOwnProperty(t)&&this.style[t]!==e.style[t]){return false}}for(t in e.style){if(e.style.hasOwnProperty(t)&&this.style[t]!==e.style[t]){return false}}this.value+=e.value;this.height=Math.max(this.height,e.height);this.depth=Math.max(this.depth,e.depth);this.italic=e.italic;return true};u.prototype.toNode=function(){var e=document.createTextNode(this.value);var t=null;if(this.italic>0){t=document.createElement("span");t.style.marginRight=this.italic+"em"}if(this.classes.length>0){t=t||document.createElement("span");t.className=n(this.classes)}for(var r in this.style){if(this.style.hasOwnProperty(r)){t=t||document.createElement("span");t.style[r]=this.style[r]}}if(t){t.appendChild(e);return t}else{return e}};u.prototype.toMarkup=function(){var e=false;var t="0){r+="margin-right:"+this.italic+"em;"}for(var a in this.style){if(this.style.hasOwnProperty(a)){r+=i.hyphenate(a)+":"+this.style[a]+";"}}if(r){e=true;t+=' style="'+i.escape(r)+'"'}var s=i.escape(this.value);if(e){t+=">";t+=s;t+="";return t}else{return s}};t.exports={span:s,documentFragment:l,symbolNode:u}},{"./unicodeRegexes":24,"./utils":25}],16:[function(e,t,r){var a=e("./parseData");var i=e("./ParseError");var n=e("./Style");var s=a.ParseNode;function l(e,t){var r=[];var a=[r];var n=[];while(true){var l=e.parseExpression(false,null);r.push(new s("ordgroup",l,e.mode));var o=e.nextToken.text;if(o==="&"){e.consume()}else if(o==="\\end"){break}else if(o==="\\\\"||o==="\\cr"){var u=e.parseFunction();n.push(u.value.size);r=[];a.push(r)}else{throw new i("Expected & or \\\\ or \\end",e.nextToken)}}t.body=a;t.rowGaps=n;return new s(t.type,t,e.mode)}function o(e,r,a){if(typeof e==="string"){e=[e]}if(typeof r==="number"){r={numArgs:r}}var i={numArgs:r.numArgs||0,argTypes:r.argTypes,greediness:1,allowedInText:!!r.allowedInText,numOptionalArgs:r.numOptionalArgs||0,handler:a};for(var n=0;n0){o=2}t.value.cols[i]={type:"align",align:n,pregap:o,postgap:0}}return t})},{"./ParseError":6,"./Style":9,"./parseData":21}],17:[function(e,t,r){var a=e("./Style");var i=e("./unicodeRegexes").cjkRegex;var n={slant:[.25,.25,.25],space:[0,0,0],stretch:[0,0,0],shrink:[0,0,0],xHeight:[.431,.431,.431],quad:[1,1.171,1.472],extraSpace:[0,0,0],num1:[.677,.732,.925],num2:[.394,.384,.387],num3:[.444,.471,.504],denom1:[.686,.752,1.025],denom2:[.345,.344,.532],sup1:[.413,.503,.504],sup2:[.363,.431,.404],sup3:[.289,.286,.294],sub1:[.15,.143,.2],sub2:[.247,.286,.4],supDrop:[.386,.353,.494],subDrop:[.05,.071,.1],delim1:[2.39,1.7,1.98],delim2:[1.01,1.157,1.42],axisHeight:[.25,.25,.25]};var s=0;var l=0;var o=0;var u=0;var p=.431;var h=1;var c=0;var m=.04;var f=.111;var v=.166;var d=.2;var g=.6;var y=.1;var x=10;var b=2/x;var w={defaultRuleThickness:m,bigOpSpacing1:f,bigOpSpacing2:v,bigOpSpacing3:d,bigOpSpacing4:g,bigOpSpacing5:y,ptPerEm:x,doubleRuleSep:b};var k=e("./fontMetricsData");var z={"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xc6":"A","\xc7":"C","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xcc":"I","\xcd":"I","\xce":"I","\xcf":"I","\xd0":"D","\xd1":"N","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xdd":"Y","\xde":"o","\xdf":"B","\xe0":"a","\xe1":"a","\xe2":"a","\xe3":"a","\xe4":"a","\xe5":"a","\xe6":"a","\xe7":"c","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xf0":"d","\xf1":"n","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xfd":"y","\xfe":"o","\xff":"y","\u0410":"A","\u0411":"B","\u0412":"B","\u0413":"F","\u0414":"A","\u0415":"E","\u0416":"K","\u0417":"3","\u0418":"N","\u0419":"N","\u041a":"K","\u041b":"N","\u041c":"M","\u041d":"H","\u041e":"O","\u041f":"N","\u0420":"P","\u0421":"C","\u0422":"T","\u0423":"y","\u0424":"O","\u0425":"X","\u0426":"U","\u0427":"h","\u0428":"W","\u0429":"W","\u042a":"B","\u042b":"X","\u042c":"B","\u042d":"3","\u042e":"X","\u042f":"R","\u0430":"a","\u0431":"b","\u0432":"a","\u0433":"r","\u0434":"y","\u0435":"e","\u0436":"m","\u0437":"e","\u0438":"n","\u0439":"n","\u043a":"n","\u043b":"n","\u043c":"m","\u043d":"n","\u043e":"o","\u043f":"n","\u0440":"p","\u0441":"c","\u0442":"o","\u0443":"y","\u0444":"b","\u0445":"x","\u0446":"n","\u0447":"n","\u0448":"w","\u0449":"w","\u044a":"a","\u044b":"m","\u044c":"a","\u044d":"e","\u044e":"m","\u044f":"r"};var S=function(e,t){var r=e.charCodeAt(0);if(e[0]in z){r=z[e[0]].charCodeAt(0)}else if(i.test(e[0])){r="M".charCodeAt(0)}var a=k[t][r];if(a){return{depth:a[0],height:a[1],italic:a[2],skew:a[3],width:a[4]}}};t.exports={metrics:w,sigmas:n,getCharacterMetrics:S}},{"./Style":9,"./fontMetricsData":18,"./unicodeRegexes":24}],18:[function(e,t,r){t.exports={"AMS-Regular":{65:[0,.68889,0,0],66:[0,.68889,0,0],67:[0,.68889,0,0],68:[0,.68889,0,0],69:[0,.68889,0,0],70:[0,.68889,0,0],71:[0,.68889,0,0],72:[0,.68889,0,0],73:[0,.68889,0,0],74:[.16667,.68889,0,0],75:[0,.68889,0,0],76:[0,.68889,0,0],77:[0,.68889,0,0],78:[0,.68889,0,0],79:[.16667,.68889,0,0],80:[0,.68889,0,0],81:[.16667,.68889,0,0],82:[0,.68889,0,0],83:[0,.68889,0,0],84:[0,.68889,0,0],85:[0,.68889,0,0],86:[0,.68889,0,0],87:[0,.68889,0,0],88:[0,.68889,0,0],89:[0,.68889,0,0],90:[0,.68889,0,0],107:[0,.68889,0,0],165:[0,.675,.025,0],174:[.15559,.69224,0,0],240:[0,.68889,0,0],295:[0,.68889,0,0],710:[0,.825,0,0],732:[0,.9,0,0],770:[0,.825,0,0],771:[0,.9,0,0],989:[.08167,.58167,0,0],1008:[0,.43056,.04028,0],8245:[0,.54986,0,0],8463:[0,.68889,0,0],8487:[0,.68889,0,0],8498:[0,.68889,0,0],8502:[0,.68889,0,0],8503:[0,.68889,0,0],8504:[0,.68889,0,0],8513:[0,.68889,0,0],8592:[-.03598,.46402,0,0],8594:[-.03598,.46402,0,0],8602:[-.13313,.36687,0,0],8603:[-.13313,.36687,0,0],8606:[.01354,.52239,0,0],8608:[.01354,.52239,0,0],8610:[.01354,.52239,0,0],8611:[.01354,.52239,0,0],8619:[0,.54986,0,0],8620:[0,.54986,0,0],8621:[-.13313,.37788,0,0],8622:[-.13313,.36687,0,0],8624:[0,.69224,0,0],8625:[0,.69224,0,0],8630:[0,.43056,0,0],8631:[0,.43056,0,0],8634:[.08198,.58198,0,0],8635:[.08198,.58198,0,0],8638:[.19444,.69224,0,0],8639:[.19444,.69224,0,0],8642:[.19444,.69224,0,0],8643:[.19444,.69224,0,0],8644:[.1808,.675,0,0],8646:[.1808,.675,0,0],8647:[.1808,.675,0,0],8648:[.19444,.69224,0,0],8649:[.1808,.675,0,0],8650:[.19444,.69224,0,0],8651:[.01354,.52239,0,0],8652:[.01354,.52239,0,0],8653:[-.13313,.36687,0,0],8654:[-.13313,.36687,0,0],8655:[-.13313,.36687,0,0],8666:[.13667,.63667,0,0],8667:[.13667,.63667,0,0],8669:[-.13313,.37788,0,0],8672:[-.064,.437,0,0],8674:[-.064,.437,0,0],8705:[0,.825,0,0],8708:[0,.68889,0,0],8709:[.08167,.58167,0,0],8717:[0,.43056,0,0],8722:[-.03598,.46402,0,0],8724:[.08198,.69224,0,0],8726:[.08167,.58167,0,0],8733:[0,.69224,0,0],8736:[0,.69224,0,0],8737:[0,.69224,0,0],8738:[.03517,.52239,0,0],8739:[.08167,.58167,0,0],8740:[.25142,.74111,0,0],8741:[.08167,.58167,0,0],8742:[.25142,.74111,0,0],8756:[0,.69224,0,0],8757:[0,.69224,0,0],8764:[-.13313,.36687,0,0],8765:[-.13313,.37788,0,0],8769:[-.13313,.36687,0,0],8770:[-.03625,.46375,0,0],8774:[.30274,.79383,0,0],8776:[-.01688,.48312,0,0],8778:[.08167,.58167,0,0],8782:[.06062,.54986,0,0],8783:[.06062,.54986,0,0],8785:[.08198,.58198,0,0],8786:[.08198,.58198,0,0],8787:[.08198,.58198,0,0],8790:[0,.69224,0,0],8791:[.22958,.72958,0,0],8796:[.08198,.91667,0,0],8806:[.25583,.75583,0,0], +8807:[.25583,.75583,0,0],8808:[.25142,.75726,0,0],8809:[.25142,.75726,0,0],8812:[.25583,.75583,0,0],8814:[.20576,.70576,0,0],8815:[.20576,.70576,0,0],8816:[.30274,.79383,0,0],8817:[.30274,.79383,0,0],8818:[.22958,.72958,0,0],8819:[.22958,.72958,0,0],8822:[.1808,.675,0,0],8823:[.1808,.675,0,0],8828:[.13667,.63667,0,0],8829:[.13667,.63667,0,0],8830:[.22958,.72958,0,0],8831:[.22958,.72958,0,0],8832:[.20576,.70576,0,0],8833:[.20576,.70576,0,0],8840:[.30274,.79383,0,0],8841:[.30274,.79383,0,0],8842:[.13597,.63597,0,0],8843:[.13597,.63597,0,0],8847:[.03517,.54986,0,0],8848:[.03517,.54986,0,0],8858:[.08198,.58198,0,0],8859:[.08198,.58198,0,0],8861:[.08198,.58198,0,0],8862:[0,.675,0,0],8863:[0,.675,0,0],8864:[0,.675,0,0],8865:[0,.675,0,0],8872:[0,.69224,0,0],8873:[0,.69224,0,0],8874:[0,.69224,0,0],8876:[0,.68889,0,0],8877:[0,.68889,0,0],8878:[0,.68889,0,0],8879:[0,.68889,0,0],8882:[.03517,.54986,0,0],8883:[.03517,.54986,0,0],8884:[.13667,.63667,0,0],8885:[.13667,.63667,0,0],8888:[0,.54986,0,0],8890:[.19444,.43056,0,0],8891:[.19444,.69224,0,0],8892:[.19444,.69224,0,0],8901:[0,.54986,0,0],8903:[.08167,.58167,0,0],8905:[.08167,.58167,0,0],8906:[.08167,.58167,0,0],8907:[0,.69224,0,0],8908:[0,.69224,0,0],8909:[-.03598,.46402,0,0],8910:[0,.54986,0,0],8911:[0,.54986,0,0],8912:[.03517,.54986,0,0],8913:[.03517,.54986,0,0],8914:[0,.54986,0,0],8915:[0,.54986,0,0],8916:[0,.69224,0,0],8918:[.0391,.5391,0,0],8919:[.0391,.5391,0,0],8920:[.03517,.54986,0,0],8921:[.03517,.54986,0,0],8922:[.38569,.88569,0,0],8923:[.38569,.88569,0,0],8926:[.13667,.63667,0,0],8927:[.13667,.63667,0,0],8928:[.30274,.79383,0,0],8929:[.30274,.79383,0,0],8934:[.23222,.74111,0,0],8935:[.23222,.74111,0,0],8936:[.23222,.74111,0,0],8937:[.23222,.74111,0,0],8938:[.20576,.70576,0,0],8939:[.20576,.70576,0,0],8940:[.30274,.79383,0,0],8941:[.30274,.79383,0,0],8994:[.19444,.69224,0,0],8995:[.19444,.69224,0,0],9416:[.15559,.69224,0,0],9484:[0,.69224,0,0],9488:[0,.69224,0,0],9492:[0,.37788,0,0],9496:[0,.37788,0,0],9585:[.19444,.68889,0,0],9586:[.19444,.74111,0,0],9632:[0,.675,0,0],9633:[0,.675,0,0],9650:[0,.54986,0,0],9651:[0,.54986,0,0],9654:[.03517,.54986,0,0],9660:[0,.54986,0,0],9661:[0,.54986,0,0],9664:[.03517,.54986,0,0],9674:[.11111,.69224,0,0],9733:[.19444,.69224,0,0],10003:[0,.69224,0,0],10016:[0,.69224,0,0],10731:[.11111,.69224,0,0],10846:[.19444,.75583,0,0],10877:[.13667,.63667,0,0],10878:[.13667,.63667,0,0],10885:[.25583,.75583,0,0],10886:[.25583,.75583,0,0],10887:[.13597,.63597,0,0],10888:[.13597,.63597,0,0],10889:[.26167,.75726,0,0],10890:[.26167,.75726,0,0],10891:[.48256,.98256,0,0],10892:[.48256,.98256,0,0],10901:[.13667,.63667,0,0],10902:[.13667,.63667,0,0],10933:[.25142,.75726,0,0],10934:[.25142,.75726,0,0],10935:[.26167,.75726,0,0],10936:[.26167,.75726,0,0],10937:[.26167,.75726,0,0],10938:[.26167,.75726,0,0],10949:[.25583,.75583,0,0],10950:[.25583,.75583,0,0],10955:[.28481,.79383,0,0],10956:[.28481,.79383,0,0],57350:[.08167,.58167,0,0],57351:[.08167,.58167,0,0],57352:[.08167,.58167,0,0],57353:[0,.43056,.04028,0],57356:[.25142,.75726,0,0],57357:[.25142,.75726,0,0],57358:[.41951,.91951,0,0],57359:[.30274,.79383,0,0],57360:[.30274,.79383,0,0],57361:[.41951,.91951,0,0],57366:[.25142,.75726,0,0],57367:[.25142,.75726,0,0],57368:[.25142,.75726,0,0],57369:[.25142,.75726,0,0],57370:[.13597,.63597,0,0],57371:[.13597,.63597,0,0]},"Caligraphic-Regular":{48:[0,.43056,0,0],49:[0,.43056,0,0],50:[0,.43056,0,0],51:[.19444,.43056,0,0],52:[.19444,.43056,0,0],53:[.19444,.43056,0,0],54:[0,.64444,0,0],55:[.19444,.43056,0,0],56:[0,.64444,0,0],57:[.19444,.43056,0,0],65:[0,.68333,0,.19445],66:[0,.68333,.03041,.13889],67:[0,.68333,.05834,.13889],68:[0,.68333,.02778,.08334],69:[0,.68333,.08944,.11111],70:[0,.68333,.09931,.11111],71:[.09722,.68333,.0593,.11111],72:[0,.68333,.00965,.11111],73:[0,.68333,.07382,0],74:[.09722,.68333,.18472,.16667],75:[0,.68333,.01445,.05556],76:[0,.68333,0,.13889],77:[0,.68333,0,.13889],78:[0,.68333,.14736,.08334],79:[0,.68333,.02778,.11111],80:[0,.68333,.08222,.08334],81:[.09722,.68333,0,.11111],82:[0,.68333,0,.08334],83:[0,.68333,.075,.13889],84:[0,.68333,.25417,0],85:[0,.68333,.09931,.08334],86:[0,.68333,.08222,0],87:[0,.68333,.08222,.08334],88:[0,.68333,.14643,.13889],89:[.09722,.68333,.08222,.08334],90:[0,.68333,.07944,.13889]},"Fraktur-Regular":{33:[0,.69141,0,0],34:[0,.69141,0,0],38:[0,.69141,0,0],39:[0,.69141,0,0],40:[.24982,.74947,0,0],41:[.24982,.74947,0,0],42:[0,.62119,0,0],43:[.08319,.58283,0,0],44:[0,.10803,0,0],45:[.08319,.58283,0,0],46:[0,.10803,0,0],47:[.24982,.74947,0,0],48:[0,.47534,0,0],49:[0,.47534,0,0],50:[0,.47534,0,0],51:[.18906,.47534,0,0],52:[.18906,.47534,0,0],53:[.18906,.47534,0,0],54:[0,.69141,0,0],55:[.18906,.47534,0,0],56:[0,.69141,0,0],57:[.18906,.47534,0,0],58:[0,.47534,0,0],59:[.12604,.47534,0,0],61:[-.13099,.36866,0,0],63:[0,.69141,0,0],65:[0,.69141,0,0],66:[0,.69141,0,0],67:[0,.69141,0,0],68:[0,.69141,0,0],69:[0,.69141,0,0],70:[.12604,.69141,0,0],71:[0,.69141,0,0],72:[.06302,.69141,0,0],73:[0,.69141,0,0],74:[.12604,.69141,0,0],75:[0,.69141,0,0],76:[0,.69141,0,0],77:[0,.69141,0,0],78:[0,.69141,0,0],79:[0,.69141,0,0],80:[.18906,.69141,0,0],81:[.03781,.69141,0,0],82:[0,.69141,0,0],83:[0,.69141,0,0],84:[0,.69141,0,0],85:[0,.69141,0,0],86:[0,.69141,0,0],87:[0,.69141,0,0],88:[0,.69141,0,0],89:[.18906,.69141,0,0],90:[.12604,.69141,0,0],91:[.24982,.74947,0,0],93:[.24982,.74947,0,0],94:[0,.69141,0,0],97:[0,.47534,0,0],98:[0,.69141,0,0],99:[0,.47534,0,0],100:[0,.62119,0,0],101:[0,.47534,0,0],102:[.18906,.69141,0,0],103:[.18906,.47534,0,0],104:[.18906,.69141,0,0],105:[0,.69141,0,0],106:[0,.69141,0,0],107:[0,.69141,0,0],108:[0,.69141,0,0],109:[0,.47534,0,0],110:[0,.47534,0,0],111:[0,.47534,0,0],112:[.18906,.52396,0,0],113:[.18906,.47534,0,0],114:[0,.47534,0,0],115:[0,.47534,0,0],116:[0,.62119,0,0],117:[0,.47534,0,0],118:[0,.52396,0,0],119:[0,.52396,0,0],120:[.18906,.47534,0,0],121:[.18906,.47534,0,0],122:[.18906,.47534,0,0],8216:[0,.69141,0,0],8217:[0,.69141,0,0],58112:[0,.62119,0,0],58113:[0,.62119,0,0],58114:[.18906,.69141,0,0],58115:[.18906,.69141,0,0],58116:[.18906,.47534,0,0],58117:[0,.69141,0,0],58118:[0,.62119,0,0],58119:[0,.47534,0,0]},"Main-Bold":{33:[0,.69444,0,0],34:[0,.69444,0,0],35:[.19444,.69444,0,0],36:[.05556,.75,0,0],37:[.05556,.75,0,0],38:[0,.69444,0,0],39:[0,.69444,0,0],40:[.25,.75,0,0],41:[.25,.75,0,0],42:[0,.75,0,0],43:[.13333,.63333,0,0],44:[.19444,.15556,0,0],45:[0,.44444,0,0],46:[0,.15556,0,0],47:[.25,.75,0,0],48:[0,.64444,0,0],49:[0,.64444,0,0],50:[0,.64444,0,0],51:[0,.64444,0,0],52:[0,.64444,0,0],53:[0,.64444,0,0],54:[0,.64444,0,0],55:[0,.64444,0,0],56:[0,.64444,0,0],57:[0,.64444,0,0],58:[0,.44444,0,0],59:[.19444,.44444,0,0],60:[.08556,.58556,0,0],61:[-.10889,.39111,0,0],62:[.08556,.58556,0,0],63:[0,.69444,0,0],64:[0,.69444,0,0],65:[0,.68611,0,0],66:[0,.68611,0,0],67:[0,.68611,0,0],68:[0,.68611,0,0],69:[0,.68611,0,0],70:[0,.68611,0,0],71:[0,.68611,0,0],72:[0,.68611,0,0],73:[0,.68611,0,0],74:[0,.68611,0,0],75:[0,.68611,0,0],76:[0,.68611,0,0],77:[0,.68611,0,0],78:[0,.68611,0,0],79:[0,.68611,0,0],80:[0,.68611,0,0],81:[.19444,.68611,0,0],82:[0,.68611,0,0],83:[0,.68611,0,0],84:[0,.68611,0,0],85:[0,.68611,0,0],86:[0,.68611,.01597,0],87:[0,.68611,.01597,0],88:[0,.68611,0,0],89:[0,.68611,.02875,0],90:[0,.68611,0,0],91:[.25,.75,0,0],92:[.25,.75,0,0],93:[.25,.75,0,0],94:[0,.69444,0,0],95:[.31,.13444,.03194,0],96:[0,.69444,0,0],97:[0,.44444,0,0],98:[0,.69444,0,0],99:[0,.44444,0,0],100:[0,.69444,0,0],101:[0,.44444,0,0],102:[0,.69444,.10903,0],103:[.19444,.44444,.01597,0],104:[0,.69444,0,0],105:[0,.69444,0,0],106:[.19444,.69444,0,0],107:[0,.69444,0,0],108:[0,.69444,0,0],109:[0,.44444,0,0],110:[0,.44444,0,0],111:[0,.44444,0,0],112:[.19444,.44444,0,0],113:[.19444,.44444,0,0],114:[0,.44444,0,0],115:[0,.44444,0,0],116:[0,.63492,0,0],117:[0,.44444,0,0],118:[0,.44444,.01597,0],119:[0,.44444,.01597,0],120:[0,.44444,0,0],121:[.19444,.44444,.01597,0],122:[0,.44444,0,0],123:[.25,.75,0,0],124:[.25,.75,0,0],125:[.25,.75,0,0],126:[.35,.34444,0,0],168:[0,.69444,0,0],172:[0,.44444,0,0],175:[0,.59611,0,0],176:[0,.69444,0,0],177:[.13333,.63333,0,0],180:[0,.69444,0,0],215:[.13333,.63333,0,0],247:[.13333,.63333,0,0],305:[0,.44444,0,0],567:[.19444,.44444,0,0],710:[0,.69444,0,0],711:[0,.63194,0,0],713:[0,.59611,0,0],714:[0,.69444,0,0],715:[0,.69444,0,0],728:[0,.69444,0,0],729:[0,.69444,0,0],730:[0,.69444,0,0],732:[0,.69444,0,0],768:[0,.69444,0,0],769:[0,.69444,0,0],770:[0,.69444,0,0],771:[0,.69444,0,0],772:[0,.59611,0,0],774:[0,.69444,0,0],775:[0,.69444,0,0],776:[0,.69444,0,0],778:[0,.69444,0,0],779:[0,.69444,0,0],780:[0,.63194,0,0],824:[.19444,.69444,0,0],915:[0,.68611,0,0],916:[0,.68611,0,0],920:[0,.68611,0,0],923:[0,.68611,0,0],926:[0,.68611,0,0],928:[0,.68611,0,0],931:[0,.68611,0,0],933:[0,.68611,0,0],934:[0,.68611,0,0],936:[0,.68611,0,0],937:[0,.68611,0,0],8211:[0,.44444,.03194,0],8212:[0,.44444,.03194,0],8216:[0,.69444,0,0],8217:[0,.69444,0,0],8220:[0,.69444,0,0],8221:[0,.69444,0,0],8224:[.19444,.69444,0,0],8225:[.19444,.69444,0,0],8242:[0,.55556,0,0],8407:[0,.72444,.15486,0],8463:[0,.69444,0,0],8465:[0,.69444,0,0],8467:[0,.69444,0,0],8472:[.19444,.44444,0,0],8476:[0,.69444,0,0],8501:[0,.69444,0,0],8592:[-.10889,.39111,0,0],8593:[.19444,.69444,0,0],8594:[-.10889,.39111,0,0],8595:[.19444,.69444,0,0],8596:[-.10889,.39111,0,0],8597:[.25,.75,0,0],8598:[.19444,.69444,0,0],8599:[.19444,.69444,0,0],8600:[.19444,.69444,0,0],8601:[.19444,.69444,0,0],8636:[-.10889,.39111,0,0],8637:[-.10889,.39111,0,0],8640:[-.10889,.39111,0,0],8641:[-.10889,.39111,0,0],8656:[-.10889,.39111,0,0],8657:[.19444,.69444,0,0],8658:[-.10889,.39111,0,0],8659:[.19444,.69444,0,0],8660:[-.10889,.39111,0,0],8661:[.25,.75,0,0],8704:[0,.69444,0,0],8706:[0,.69444,.06389,0],8707:[0,.69444,0,0],8709:[.05556,.75,0,0],8711:[0,.68611,0,0],8712:[.08556,.58556,0,0],8715:[.08556,.58556,0,0],8722:[.13333,.63333,0,0],8723:[.13333,.63333,0,0],8725:[.25,.75,0,0],8726:[.25,.75,0,0],8727:[-.02778,.47222,0,0],8728:[-.02639,.47361,0,0],8729:[-.02639,.47361,0,0],8730:[.18,.82,0,0],8733:[0,.44444,0,0],8734:[0,.44444,0,0],8736:[0,.69224,0,0],8739:[.25,.75,0,0],8741:[.25,.75,0,0],8743:[0,.55556,0,0],8744:[0,.55556,0,0],8745:[0,.55556,0,0],8746:[0,.55556,0,0],8747:[.19444,.69444,.12778,0],8764:[-.10889,.39111,0,0],8768:[.19444,.69444,0,0],8771:[.00222,.50222,0,0],8776:[.02444,.52444,0,0],8781:[.00222,.50222,0,0],8801:[.00222,.50222,0,0],8804:[.19667,.69667,0,0],8805:[.19667,.69667,0,0],8810:[.08556,.58556,0,0],8811:[.08556,.58556,0,0],8826:[.08556,.58556,0,0],8827:[.08556,.58556,0,0],8834:[.08556,.58556,0,0],8835:[.08556,.58556,0,0],8838:[.19667,.69667,0,0],8839:[.19667,.69667,0,0],8846:[0,.55556,0,0],8849:[.19667,.69667,0,0],8850:[.19667,.69667,0,0],8851:[0,.55556,0,0],8852:[0,.55556,0,0],8853:[.13333,.63333,0,0],8854:[.13333,.63333,0,0],8855:[.13333,.63333,0,0],8856:[.13333,.63333,0,0],8857:[.13333,.63333,0,0],8866:[0,.69444,0,0],8867:[0,.69444,0,0],8868:[0,.69444,0,0],8869:[0,.69444,0,0],8900:[-.02639,.47361,0,0],8901:[-.02639,.47361,0,0],8902:[-.02778,.47222,0,0],8968:[.25,.75,0,0],8969:[.25,.75,0,0],8970:[.25,.75,0,0],8971:[.25,.75,0,0],8994:[-.13889,.36111,0,0],8995:[-.13889,.36111,0,0],9651:[.19444,.69444,0,0],9657:[-.02778,.47222,0,0],9661:[.19444,.69444,0,0],9667:[-.02778,.47222,0,0],9711:[.19444,.69444,0,0],9824:[.12963,.69444,0,0],9825:[.12963,.69444,0,0],9826:[.12963,.69444,0,0],9827:[.12963,.69444,0,0],9837:[0,.75,0,0],9838:[.19444,.69444,0,0],9839:[.19444,.69444,0,0],10216:[.25,.75,0,0],10217:[.25,.75,0,0],10815:[0,.68611,0,0],10927:[.19667,.69667,0,0],10928:[.19667,.69667,0,0]},"Main-Italic":{33:[0,.69444,.12417,0],34:[0,.69444,.06961,0],35:[.19444,.69444,.06616,0],37:[.05556,.75,.13639,0],38:[0,.69444,.09694,0],39:[0,.69444,.12417,0],40:[.25,.75,.16194,0],41:[.25,.75,.03694,0],42:[0,.75,.14917,0],43:[.05667,.56167,.03694,0],44:[.19444,.10556,0,0],45:[0,.43056,.02826,0],46:[0,.10556,0,0],47:[.25,.75,.16194,0],48:[0,.64444,.13556,0],49:[0,.64444,.13556,0],50:[0,.64444,.13556,0],51:[0,.64444,.13556,0],52:[.19444,.64444,.13556,0],53:[0,.64444,.13556,0],54:[0,.64444,.13556,0],55:[.19444,.64444,.13556,0],56:[0,.64444,.13556,0],57:[0,.64444,.13556,0],58:[0,.43056,.0582,0],59:[.19444,.43056,.0582,0],61:[-.13313,.36687,.06616,0],63:[0,.69444,.1225,0],64:[0,.69444,.09597,0],65:[0,.68333,0,0],66:[0,.68333,.10257,0],67:[0,.68333,.14528,0],68:[0,.68333,.09403,0],69:[0,.68333,.12028,0],70:[0,.68333,.13305,0],71:[0,.68333,.08722,0],72:[0,.68333,.16389,0],73:[0,.68333,.15806,0],74:[0,.68333,.14028,0],75:[0,.68333,.14528,0],76:[0,.68333,0,0],77:[0,.68333,.16389,0],78:[0,.68333,.16389,0],79:[0,.68333,.09403,0],80:[0,.68333,.10257,0],81:[.19444,.68333,.09403,0],82:[0,.68333,.03868,0],83:[0,.68333,.11972,0],84:[0,.68333,.13305,0],85:[0,.68333,.16389,0],86:[0,.68333,.18361,0],87:[0,.68333,.18361,0],88:[0,.68333,.15806,0],89:[0,.68333,.19383,0],90:[0,.68333,.14528,0],91:[.25,.75,.1875,0],93:[.25,.75,.10528,0],94:[0,.69444,.06646,0],95:[.31,.12056,.09208,0],97:[0,.43056,.07671,0],98:[0,.69444,.06312,0],99:[0,.43056,.05653,0],100:[0,.69444,.10333,0],101:[0,.43056,.07514,0],102:[.19444,.69444,.21194,0],103:[.19444,.43056,.08847,0],104:[0,.69444,.07671,0],105:[0,.65536,.1019,0],106:[.19444,.65536,.14467,0],107:[0,.69444,.10764,0],108:[0,.69444,.10333,0],109:[0,.43056,.07671,0],110:[0,.43056,.07671,0],111:[0,.43056,.06312,0],112:[.19444,.43056,.06312,0],113:[.19444,.43056,.08847,0],114:[0,.43056,.10764,0],115:[0,.43056,.08208,0],116:[0,.61508,.09486,0],117:[0,.43056,.07671,0],118:[0,.43056,.10764,0],119:[0,.43056,.10764,0],120:[0,.43056,.12042,0],121:[.19444,.43056,.08847,0],122:[0,.43056,.12292,0],126:[.35,.31786,.11585,0],163:[0,.69444,0,0],305:[0,.43056,0,.02778],567:[.19444,.43056,0,.08334],768:[0,.69444,0,0],769:[0,.69444,.09694,0],770:[0,.69444,.06646,0],771:[0,.66786,.11585,0],772:[0,.56167,.10333,0],774:[0,.69444,.10806,0],775:[0,.66786,.11752,0],776:[0,.66786,.10474,0],778:[0,.69444,0,0],779:[0,.69444,.1225,0],780:[0,.62847,.08295,0],915:[0,.68333,.13305,0],916:[0,.68333,0,0],920:[0,.68333,.09403,0],923:[0,.68333,0,0],926:[0,.68333,.15294,0],928:[0,.68333,.16389,0],931:[0,.68333,.12028,0],933:[0,.68333,.11111,0],934:[0,.68333,.05986,0],936:[0,.68333,.11111,0],937:[0,.68333,.10257,0],8211:[0,.43056,.09208,0],8212:[0,.43056,.09208,0],8216:[0,.69444,.12417,0],8217:[0,.69444,.12417,0],8220:[0,.69444,.1685,0],8221:[0,.69444,.06961,0],8463:[0,.68889,0,0]},"Main-Regular":{32:[0,0,0,0],33:[0,.69444,0,0],34:[0,.69444,0,0],35:[.19444,.69444,0,0],36:[.05556,.75,0,0],37:[.05556,.75,0,0],38:[0,.69444,0,0],39:[0,.69444,0,0],40:[.25,.75,0,0],41:[.25,.75,0,0],42:[0,.75,0,0],43:[.08333,.58333,0,0],44:[.19444,.10556,0,0],45:[0,.43056,0,0],46:[0,.10556,0,0],47:[.25,.75,0,0],48:[0,.64444,0,0],49:[0,.64444,0,0],50:[0,.64444,0,0],51:[0,.64444,0,0],52:[0,.64444,0,0],53:[0,.64444,0,0],54:[0,.64444,0,0],55:[0,.64444,0,0],56:[0,.64444,0,0],57:[0,.64444,0,0],58:[0,.43056,0,0],59:[.19444,.43056,0,0],60:[.0391,.5391,0,0],61:[-.13313,.36687,0,0],62:[.0391,.5391,0,0],63:[0,.69444,0,0],64:[0,.69444,0,0],65:[0,.68333,0,0],66:[0,.68333,0,0],67:[0,.68333,0,0],68:[0,.68333,0,0],69:[0,.68333,0,0],70:[0,.68333,0,0],71:[0,.68333,0,0],72:[0,.68333,0,0],73:[0,.68333,0,0],74:[0,.68333,0,0],75:[0,.68333,0,0],76:[0,.68333,0,0],77:[0,.68333,0,0],78:[0,.68333,0,0],79:[0,.68333,0,0],80:[0,.68333,0,0],81:[.19444,.68333,0,0],82:[0,.68333,0,0],83:[0,.68333,0,0],84:[0,.68333,0,0],85:[0,.68333,0,0],86:[0,.68333,.01389,0],87:[0,.68333,.01389,0],88:[0,.68333,0,0],89:[0,.68333,.025,0],90:[0,.68333,0,0],91:[.25,.75,0,0],92:[.25,.75,0,0],93:[.25,.75,0,0],94:[0,.69444,0,0],95:[.31,.12056,.02778,0],96:[0,.69444,0,0],97:[0,.43056,0,0],98:[0,.69444,0,0],99:[0,.43056,0,0],100:[0,.69444,0,0],101:[0,.43056,0,0],102:[0,.69444,.07778,0],103:[.19444,.43056,.01389,0],104:[0,.69444,0,0],105:[0,.66786,0,0],106:[.19444,.66786,0,0],107:[0,.69444,0,0],108:[0,.69444,0,0],109:[0,.43056,0,0],110:[0,.43056,0,0],111:[0,.43056,0,0],112:[.19444,.43056,0,0],113:[.19444,.43056,0,0],114:[0,.43056,0,0],115:[0,.43056,0,0],116:[0,.61508,0,0],117:[0,.43056,0,0],118:[0,.43056,.01389,0],119:[0,.43056,.01389,0],120:[0,.43056,0,0],121:[.19444,.43056,.01389,0],122:[0,.43056,0,0],123:[.25,.75,0,0],124:[.25,.75,0,0],125:[.25,.75,0,0],126:[.35,.31786,0,0],160:[0,0,0,0],168:[0,.66786,0,0],172:[0,.43056,0,0],175:[0,.56778,0,0],176:[0,.69444,0,0],177:[.08333,.58333,0,0],180:[0,.69444,0,0],215:[.08333,.58333,0,0],247:[.08333,.58333,0,0],305:[0,.43056,0,0],567:[.19444,.43056,0,0],710:[0,.69444,0,0],711:[0,.62847,0,0],713:[0,.56778,0,0],714:[0,.69444,0,0],715:[0,.69444,0,0],728:[0,.69444,0,0],729:[0,.66786,0,0],730:[0,.69444,0,0],732:[0,.66786,0,0],768:[0,.69444,0,0],769:[0,.69444,0,0],770:[0,.69444,0,0],771:[0,.66786,0,0],772:[0,.56778,0,0],774:[0,.69444,0,0],775:[0,.66786,0,0],776:[0,.66786,0,0],778:[0,.69444,0,0],779:[0,.69444,0,0],780:[0,.62847,0,0],824:[.19444,.69444,0,0],915:[0,.68333,0,0],916:[0,.68333,0,0],920:[0,.68333,0,0],923:[0,.68333,0,0],926:[0,.68333,0,0],928:[0,.68333,0,0],931:[0,.68333,0,0],933:[0,.68333,0,0],934:[0,.68333,0,0],936:[0,.68333,0,0],937:[0,.68333,0,0],8211:[0,.43056,.02778,0],8212:[0,.43056,.02778,0],8216:[0,.69444,0,0],8217:[0,.69444,0,0],8220:[0,.69444,0,0],8221:[0,.69444,0,0],8224:[.19444,.69444,0,0],8225:[.19444,.69444,0,0],8230:[0,.12,0,0],8242:[0,.55556,0,0],8407:[0,.71444,.15382,0],8463:[0,.68889,0,0],8465:[0,.69444,0,0],8467:[0,.69444,0,.11111],8472:[.19444,.43056,0,.11111],8476:[0,.69444,0,0],8501:[0,.69444,0,0],8592:[-.13313,.36687,0,0],8593:[.19444,.69444,0,0],8594:[-.13313,.36687,0,0],8595:[.19444,.69444,0,0],8596:[-.13313,.36687,0,0],8597:[.25,.75,0,0],8598:[.19444,.69444,0,0],8599:[.19444,.69444,0,0],8600:[.19444,.69444,0,0],8601:[.19444,.69444,0,0],8614:[.011,.511,0,0],8617:[.011,.511,0,0],8618:[.011,.511,0,0],8636:[-.13313,.36687,0,0],8637:[-.13313,.36687,0,0],8640:[-.13313,.36687,0,0],8641:[-.13313,.36687,0,0],8652:[.011,.671,0,0],8656:[-.13313,.36687,0,0],8657:[.19444,.69444,0,0],8658:[-.13313,.36687,0,0],8659:[.19444,.69444,0,0],8660:[-.13313,.36687,0,0],8661:[.25,.75,0,0],8704:[0,.69444,0,0],8706:[0,.69444,.05556,.08334],8707:[0,.69444,0,0],8709:[.05556,.75,0,0],8711:[0,.68333,0,0],8712:[.0391,.5391,0,0],8715:[.0391,.5391,0,0],8722:[.08333,.58333,0,0],8723:[.08333,.58333,0,0],8725:[.25,.75,0,0],8726:[.25,.75,0,0],8727:[-.03472,.46528,0,0],8728:[-.05555,.44445,0,0],8729:[-.05555,.44445,0,0],8730:[.2,.8,0,0],8733:[0,.43056,0,0],8734:[0,.43056,0,0],8736:[0,.69224,0,0],8739:[.25,.75,0,0],8741:[.25,.75,0,0],8743:[0,.55556,0,0],8744:[0,.55556,0,0],8745:[0,.55556,0,0],8746:[0,.55556,0,0],8747:[.19444,.69444,.11111,0],8764:[-.13313,.36687,0,0],8768:[.19444,.69444,0,0],8771:[-.03625,.46375,0,0],8773:[-.022,.589,0,0],8776:[-.01688,.48312,0,0],8781:[-.03625,.46375,0,0],8784:[-.133,.67,0,0],8800:[.215,.716,0,0],8801:[-.03625,.46375,0,0],8804:[.13597,.63597,0,0],8805:[.13597,.63597,0,0],8810:[.0391,.5391,0,0],8811:[.0391,.5391,0,0],8826:[.0391,.5391,0,0],8827:[.0391,.5391,0,0],8834:[.0391,.5391,0,0],8835:[.0391,.5391,0,0],8838:[.13597,.63597,0,0],8839:[.13597,.63597,0,0],8846:[0,.55556,0,0],8849:[.13597,.63597,0,0],8850:[.13597,.63597,0,0],8851:[0,.55556,0,0],8852:[0,.55556,0,0],8853:[.08333,.58333,0,0],8854:[.08333,.58333,0,0],8855:[.08333,.58333,0,0],8856:[.08333,.58333,0,0],8857:[.08333,.58333,0,0],8866:[0,.69444,0,0],8867:[0,.69444,0,0],8868:[0,.69444,0,0],8869:[0,.69444,0,0],8872:[.249,.75,0,0],8900:[-.05555,.44445,0,0],8901:[-.05555,.44445,0,0],8902:[-.03472,.46528,0,0],8904:[.005,.505,0,0],8942:[.03,.9,0,0],8943:[-.19,.31,0,0],8945:[-.1,.82,0,0],8968:[.25,.75,0,0],8969:[.25,.75,0,0],8970:[.25,.75,0,0],8971:[.25,.75,0,0],8994:[-.14236,.35764,0,0],8995:[-.14236,.35764,0,0],9136:[.244,.744,0,0],9137:[.244,.744,0,0],9651:[.19444,.69444,0,0],9657:[-.03472,.46528,0,0],9661:[.19444,.69444,0,0],9667:[-.03472,.46528,0,0],9711:[.19444,.69444,0,0],9824:[.12963,.69444,0,0],9825:[.12963,.69444,0,0],9826:[.12963,.69444,0,0],9827:[.12963,.69444,0,0],9837:[0,.75,0,0],9838:[.19444,.69444,0,0],9839:[.19444,.69444,0,0],10216:[.25,.75,0,0],10217:[.25,.75,0,0],10222:[.244,.744,0,0],10223:[.244,.744,0,0],10229:[.011,.511,0,0],10230:[.011,.511,0,0],10231:[.011,.511,0,0],10232:[.024,.525,0,0],10233:[.024,.525,0,0],10234:[.024,.525,0,0],10236:[.011,.511,0,0],10815:[0,.68333,0,0],10927:[.13597,.63597,0,0],10928:[.13597,.63597,0,0]},"Math-BoldItalic":{47:[.19444,.69444,0,0],65:[0,.68611,0,0],66:[0,.68611,.04835,0],67:[0,.68611,.06979,0],68:[0,.68611,.03194,0],69:[0,.68611,.05451,0],70:[0,.68611,.15972,0],71:[0,.68611,0,0],72:[0,.68611,.08229,0],73:[0,.68611,.07778,0],74:[0,.68611,.10069,0],75:[0,.68611,.06979,0],76:[0,.68611,0,0],77:[0,.68611,.11424,0],78:[0,.68611,.11424,0],79:[0,.68611,.03194,0],80:[0,.68611,.15972,0],81:[.19444,.68611,0,0],82:[0,.68611,.00421,0],83:[0,.68611,.05382,0],84:[0,.68611,.15972,0],85:[0,.68611,.11424,0],86:[0,.68611,.25555,0],87:[0,.68611,.15972,0],88:[0,.68611,.07778,0],89:[0,.68611,.25555,0],90:[0,.68611,.06979,0],97:[0,.44444,0,0],98:[0,.69444,0,0],99:[0,.44444,0,0],100:[0,.69444,0,0],101:[0,.44444,0,0],102:[.19444,.69444,.11042,0],103:[.19444,.44444,.03704,0],104:[0,.69444,0,0],105:[0,.69326,0,0],106:[.19444,.69326,.0622,0],107:[0,.69444,.01852,0],108:[0,.69444,.0088,0],109:[0,.44444,0,0],110:[0,.44444,0,0],111:[0,.44444,0,0],112:[.19444,.44444,0,0],113:[.19444,.44444,.03704,0],114:[0,.44444,.03194,0],115:[0,.44444,0,0],116:[0,.63492,0,0],117:[0,.44444,0,0],118:[0,.44444,.03704,0],119:[0,.44444,.02778,0],120:[0,.44444,0,0],121:[.19444,.44444,.03704,0],122:[0,.44444,.04213,0],915:[0,.68611,.15972,0],916:[0,.68611,0,0],920:[0,.68611,.03194,0],923:[0,.68611,0,0],926:[0,.68611,.07458,0],928:[0,.68611,.08229,0],931:[0,.68611,.05451,0],933:[0,.68611,.15972,0],934:[0,.68611,0,0],936:[0,.68611,.11653,0],937:[0,.68611,.04835,0],945:[0,.44444,0,0],946:[.19444,.69444,.03403,0],947:[.19444,.44444,.06389,0],948:[0,.69444,.03819,0],949:[0,.44444,0,0],950:[.19444,.69444,.06215,0],951:[.19444,.44444,.03704,0],952:[0,.69444,.03194,0],953:[0,.44444,0,0],954:[0,.44444,0,0],955:[0,.69444,0,0],956:[.19444,.44444,0,0],957:[0,.44444,.06898,0],958:[.19444,.69444,.03021,0],959:[0,.44444,0,0],960:[0,.44444,.03704,0],961:[.19444,.44444,0,0],962:[.09722,.44444,.07917,0],963:[0,.44444,.03704,0],964:[0,.44444,.13472,0],965:[0,.44444,.03704,0],966:[.19444,.44444,0,0],967:[.19444,.44444,0,0],968:[.19444,.69444,.03704,0],969:[0,.44444,.03704,0],977:[0,.69444,0,0],981:[.19444,.69444,0,0],982:[0,.44444,.03194,0],1009:[.19444,.44444,0,0],1013:[0,.44444,0,0]},"Math-Italic":{47:[.19444,.69444,0,0],65:[0,.68333,0,.13889],66:[0,.68333,.05017,.08334],67:[0,.68333,.07153,.08334],68:[0,.68333,.02778,.05556],69:[0,.68333,.05764,.08334],70:[0,.68333,.13889,.08334],71:[0,.68333,0,.08334],72:[0,.68333,.08125,.05556],73:[0,.68333,.07847,.11111],74:[0,.68333,.09618,.16667],75:[0,.68333,.07153,.05556],76:[0,.68333,0,.02778],77:[0,.68333,.10903,.08334],78:[0,.68333,.10903,.08334],79:[0,.68333,.02778,.08334],80:[0,.68333,.13889,.08334],81:[.19444,.68333,0,.08334],82:[0,.68333,.00773,.08334],83:[0,.68333,.05764,.08334],84:[0,.68333,.13889,.08334],85:[0,.68333,.10903,.02778],86:[0,.68333,.22222,0],87:[0,.68333,.13889,0],88:[0,.68333,.07847,.08334],89:[0,.68333,.22222,0],90:[0,.68333,.07153,.08334],97:[0,.43056,0,0],98:[0,.69444,0,0],99:[0,.43056,0,.05556],100:[0,.69444,0,.16667],101:[0,.43056,0,.05556],102:[.19444,.69444,.10764,.16667],103:[.19444,.43056,.03588,.02778],104:[0,.69444,0,0],105:[0,.65952,0,0],106:[.19444,.65952,.05724,0],107:[0,.69444,.03148,0],108:[0,.69444,.01968,.08334],109:[0,.43056,0,0],110:[0,.43056,0,0],111:[0,.43056,0,.05556],112:[.19444,.43056,0,.08334],113:[.19444,.43056,.03588,.08334],114:[0,.43056,.02778,.05556],115:[0,.43056,0,.05556],116:[0,.61508,0,.08334],117:[0,.43056,0,.02778],118:[0,.43056,.03588,.02778],119:[0,.43056,.02691,.08334],120:[0,.43056,0,.02778],121:[.19444,.43056,.03588,.05556],122:[0,.43056,.04398,.05556],915:[0,.68333,.13889,.08334],916:[0,.68333,0,.16667],920:[0,.68333,.02778,.08334],923:[0,.68333,0,.16667],926:[0,.68333,.07569,.08334],928:[0,.68333,.08125,.05556],931:[0,.68333,.05764,.08334],933:[0,.68333,.13889,.05556],934:[0,.68333,0,.08334],936:[0,.68333,.11,.05556],937:[0,.68333,.05017,.08334],945:[0,.43056,.0037,.02778],946:[.19444,.69444,.05278,.08334],947:[.19444,.43056,.05556,0],948:[0,.69444,.03785,.05556],949:[0,.43056,0,.08334],950:[.19444,.69444,.07378,.08334],951:[.19444,.43056,.03588,.05556],952:[0,.69444,.02778,.08334],953:[0,.43056,0,.05556],954:[0,.43056,0,0],955:[0,.69444,0,0],956:[.19444,.43056,0,.02778],957:[0,.43056,.06366,.02778],958:[.19444,.69444,.04601,.11111],959:[0,.43056,0,.05556],960:[0,.43056,.03588,0],961:[.19444,.43056,0,.08334],962:[.09722,.43056,.07986,.08334],963:[0,.43056,.03588,0],964:[0,.43056,.1132,.02778],965:[0,.43056,.03588,.02778],966:[.19444,.43056,0,.08334],967:[.19444,.43056,0,.05556],968:[.19444,.69444,.03588,.11111],969:[0,.43056,.03588,0],977:[0,.69444,0,.08334],981:[.19444,.69444,0,.08334],982:[0,.43056,.02778,0],1009:[.19444,.43056,0,.08334],1013:[0,.43056,0,.05556]},"Math-Regular":{65:[0,.68333,0,.13889],66:[0,.68333,.05017,.08334],67:[0,.68333,.07153,.08334],68:[0,.68333,.02778,.05556],69:[0,.68333,.05764,.08334],70:[0,.68333,.13889,.08334],71:[0,.68333,0,.08334],72:[0,.68333,.08125,.05556],73:[0,.68333,.07847,.11111],74:[0,.68333,.09618,.16667],75:[0,.68333,.07153,.05556],76:[0,.68333,0,.02778],77:[0,.68333,.10903,.08334],78:[0,.68333,.10903,.08334],79:[0,.68333,.02778,.08334],80:[0,.68333,.13889,.08334],81:[.19444,.68333,0,.08334],82:[0,.68333,.00773,.08334],83:[0,.68333,.05764,.08334],84:[0,.68333,.13889,.08334],85:[0,.68333,.10903,.02778],86:[0,.68333,.22222,0],87:[0,.68333,.13889,0],88:[0,.68333,.07847,.08334],89:[0,.68333,.22222,0],90:[0,.68333,.07153,.08334],97:[0,.43056,0,0],98:[0,.69444,0,0],99:[0,.43056,0,.05556],100:[0,.69444,0,.16667],101:[0,.43056,0,.05556],102:[.19444,.69444,.10764,.16667],103:[.19444,.43056,.03588,.02778],104:[0,.69444,0,0],105:[0,.65952,0,0],106:[.19444,.65952,.05724,0],107:[0,.69444,.03148,0],108:[0,.69444,.01968,.08334],109:[0,.43056,0,0],110:[0,.43056,0,0],111:[0,.43056,0,.05556],112:[.19444,.43056,0,.08334],113:[.19444,.43056,.03588,.08334],114:[0,.43056,.02778,.05556],115:[0,.43056,0,.05556],116:[0,.61508,0,.08334],117:[0,.43056,0,.02778],118:[0,.43056,.03588,.02778],119:[0,.43056,.02691,.08334],120:[0,.43056,0,.02778],121:[.19444,.43056,.03588,.05556],122:[0,.43056,.04398,.05556],915:[0,.68333,.13889,.08334],916:[0,.68333,0,.16667],920:[0,.68333,.02778,.08334],923:[0,.68333,0,.16667],926:[0,.68333,.07569,.08334],928:[0,.68333,.08125,.05556],931:[0,.68333,.05764,.08334],933:[0,.68333,.13889,.05556],934:[0,.68333,0,.08334],936:[0,.68333,.11,.05556],937:[0,.68333,.05017,.08334],945:[0,.43056,.0037,.02778],946:[.19444,.69444,.05278,.08334],947:[.19444,.43056,.05556,0],948:[0,.69444,.03785,.05556],949:[0,.43056,0,.08334],950:[.19444,.69444,.07378,.08334],951:[.19444,.43056,.03588,.05556],952:[0,.69444,.02778,.08334],953:[0,.43056,0,.05556],954:[0,.43056,0,0],955:[0,.69444,0,0],956:[.19444,.43056,0,.02778],957:[0,.43056,.06366,.02778],958:[.19444,.69444,.04601,.11111],959:[0,.43056,0,.05556],960:[0,.43056,.03588,0],961:[.19444,.43056,0,.08334],962:[.09722,.43056,.07986,.08334],963:[0,.43056,.03588,0],964:[0,.43056,.1132,.02778],965:[0,.43056,.03588,.02778],966:[.19444,.43056,0,.08334],967:[.19444,.43056,0,.05556],968:[.19444,.69444,.03588,.11111],969:[0,.43056,.03588,0],977:[0,.69444,0,.08334],981:[.19444,.69444,0,.08334],982:[0,.43056,.02778,0],1009:[.19444,.43056,0,.08334],1013:[0,.43056,0,.05556]},"SansSerif-Regular":{33:[0,.69444,0,0],34:[0,.69444,0,0],35:[.19444,.69444,0,0],36:[.05556,.75,0,0],37:[.05556,.75,0,0],38:[0,.69444,0,0],39:[0,.69444,0,0],40:[.25,.75,0,0],41:[.25,.75,0,0],42:[0,.75,0,0],43:[.08333,.58333,0,0],44:[.125,.08333,0,0],45:[0,.44444,0,0],46:[0,.08333,0,0],47:[.25,.75,0,0],48:[0,.65556,0,0],49:[0,.65556,0,0],50:[0,.65556,0,0],51:[0,.65556,0,0],52:[0,.65556,0,0],53:[0,.65556,0,0],54:[0,.65556,0,0],55:[0,.65556,0,0],56:[0,.65556,0,0],57:[0,.65556,0,0],58:[0,.44444,0,0],59:[.125,.44444,0,0],61:[-.13,.37,0,0],63:[0,.69444,0,0],64:[0,.69444,0,0],65:[0,.69444,0,0],66:[0,.69444,0,0],67:[0,.69444,0,0],68:[0,.69444,0,0],69:[0,.69444,0,0],70:[0,.69444,0,0],71:[0,.69444,0,0],72:[0,.69444,0,0],73:[0,.69444,0,0],74:[0,.69444,0,0],75:[0,.69444,0,0],76:[0,.69444,0,0],77:[0,.69444,0,0],78:[0,.69444,0,0],79:[0,.69444,0,0],80:[0,.69444,0,0],81:[.125,.69444,0,0],82:[0,.69444,0,0],83:[0,.69444,0,0],84:[0,.69444,0,0],85:[0,.69444,0,0],86:[0,.69444,.01389,0],87:[0,.69444,.01389,0],88:[0,.69444,0,0],89:[0,.69444,.025,0],90:[0,.69444,0,0],91:[.25,.75,0,0],93:[.25,.75,0,0],94:[0,.69444,0,0],95:[.35,.09444,.02778,0],97:[0,.44444,0,0],98:[0,.69444,0,0],99:[0,.44444,0,0],100:[0,.69444,0,0],101:[0,.44444,0,0],102:[0,.69444,.06944,0],103:[.19444,.44444,.01389,0],104:[0,.69444,0,0],105:[0,.67937,0,0],106:[.19444,.67937,0,0],107:[0,.69444,0,0],108:[0,.69444,0,0],109:[0,.44444,0,0],110:[0,.44444,0,0],111:[0,.44444,0,0],112:[.19444,.44444,0,0],113:[.19444,.44444,0,0],114:[0,.44444,.01389,0],115:[0,.44444,0,0],116:[0,.57143,0,0],117:[0,.44444,0,0],118:[0,.44444,.01389,0],119:[0,.44444,.01389,0],120:[0,.44444,0,0],121:[.19444,.44444,.01389,0],122:[0,.44444,0,0],126:[.35,.32659,0,0],305:[0,.44444,0,0],567:[.19444,.44444,0,0],768:[0,.69444,0,0],769:[0,.69444,0,0],770:[0,.69444,0,0],771:[0,.67659,0,0],772:[0,.60889,0,0],774:[0,.69444,0,0],775:[0,.67937,0,0],776:[0,.67937,0,0],778:[0,.69444,0,0],779:[0,.69444,0,0],780:[0,.63194,0,0],915:[0,.69444,0,0],916:[0,.69444,0,0],920:[0,.69444,0,0],923:[0,.69444,0,0],926:[0,.69444,0,0],928:[0,.69444,0,0],931:[0,.69444,0,0],933:[0,.69444,0,0],934:[0,.69444,0,0],936:[0,.69444,0,0],937:[0,.69444,0,0],8211:[0,.44444,.02778,0],8212:[0,.44444,.02778,0],8216:[0,.69444,0,0],8217:[0,.69444,0,0],8220:[0,.69444,0,0],8221:[0,.69444,0,0]},"Script-Regular":{65:[0,.7,.22925,0],66:[0,.7,.04087,0],67:[0,.7,.1689,0],68:[0,.7,.09371,0],69:[0,.7,.18583,0],70:[0,.7,.13634,0],71:[0,.7,.17322,0],72:[0,.7,.29694,0],73:[0,.7,.19189,0],74:[.27778,.7,.19189,0],75:[0,.7,.31259,0],76:[0,.7,.19189,0],77:[0,.7,.15981,0],78:[0,.7,.3525,0],79:[0,.7,.08078,0],80:[0,.7,.08078,0],81:[0,.7,.03305,0],82:[0,.7,.06259,0],83:[0,.7,.19189,0],84:[0,.7,.29087,0],85:[0,.7,.25815,0],86:[0,.7,.27523,0],87:[0,.7,.27523,0],88:[0,.7,.26006,0],89:[0,.7,.2939,0],90:[0,.7,.24037,0]},"Size1-Regular":{40:[.35001,.85,0,0],41:[.35001,.85,0,0],47:[.35001,.85,0,0],91:[.35001,.85,0,0],92:[.35001,.85,0,0],93:[.35001,.85,0,0],123:[.35001,.85,0,0],125:[.35001,.85,0,0],710:[0,.72222,0,0],732:[0,.72222,0,0],770:[0,.72222,0,0],771:[0,.72222,0,0],8214:[-99e-5,.601,0,0],8593:[1e-5,.6,0,0],8595:[1e-5,.6,0,0],8657:[1e-5,.6,0,0],8659:[1e-5,.6,0,0],8719:[.25001,.75,0,0],8720:[.25001,.75,0,0],8721:[.25001,.75,0,0],8730:[.35001,.85,0,0],8739:[-.00599,.606,0,0],8741:[-.00599,.606,0,0],8747:[.30612,.805,.19445,0],8748:[.306,.805,.19445,0],8749:[.306,.805,.19445,0],8750:[.30612,.805,.19445,0],8896:[.25001,.75,0,0],8897:[.25001,.75,0,0],8898:[.25001,.75,0,0],8899:[.25001,.75,0,0],8968:[.35001,.85,0,0],8969:[.35001,.85,0,0],8970:[.35001,.85,0,0],8971:[.35001,.85,0,0],9168:[-99e-5,.601,0,0],10216:[.35001,.85,0,0],10217:[.35001,.85,0,0],10752:[.25001,.75,0,0],10753:[.25001,.75,0,0],10754:[.25001,.75,0,0],10756:[.25001,.75,0,0],10758:[.25001,.75,0,0]},"Size2-Regular":{40:[.65002,1.15,0,0],41:[.65002,1.15,0,0],47:[.65002,1.15,0,0],91:[.65002,1.15,0,0],92:[.65002,1.15,0,0],93:[.65002,1.15,0,0],123:[.65002,1.15,0,0],125:[.65002,1.15,0,0],710:[0,.75,0,0],732:[0,.75,0,0],770:[0,.75,0,0],771:[0,.75,0,0],8719:[.55001,1.05,0,0],8720:[.55001,1.05,0,0],8721:[.55001,1.05,0,0],8730:[.65002,1.15,0,0],8747:[.86225,1.36,.44445,0],8748:[.862,1.36,.44445,0],8749:[.862,1.36,.44445,0],8750:[.86225,1.36,.44445,0],8896:[.55001,1.05,0,0],8897:[.55001,1.05,0,0],8898:[.55001,1.05,0,0],8899:[.55001,1.05,0,0],8968:[.65002,1.15,0,0],8969:[.65002,1.15,0,0],8970:[.65002,1.15,0,0],8971:[.65002,1.15,0,0],10216:[.65002,1.15,0,0],10217:[.65002,1.15,0,0],10752:[.55001,1.05,0,0],10753:[.55001,1.05,0,0],10754:[.55001,1.05,0,0], +10756:[.55001,1.05,0,0],10758:[.55001,1.05,0,0]},"Size3-Regular":{40:[.95003,1.45,0,0],41:[.95003,1.45,0,0],47:[.95003,1.45,0,0],91:[.95003,1.45,0,0],92:[.95003,1.45,0,0],93:[.95003,1.45,0,0],123:[.95003,1.45,0,0],125:[.95003,1.45,0,0],710:[0,.75,0,0],732:[0,.75,0,0],770:[0,.75,0,0],771:[0,.75,0,0],8730:[.95003,1.45,0,0],8968:[.95003,1.45,0,0],8969:[.95003,1.45,0,0],8970:[.95003,1.45,0,0],8971:[.95003,1.45,0,0],10216:[.95003,1.45,0,0],10217:[.95003,1.45,0,0]},"Size4-Regular":{40:[1.25003,1.75,0,0],41:[1.25003,1.75,0,0],47:[1.25003,1.75,0,0],91:[1.25003,1.75,0,0],92:[1.25003,1.75,0,0],93:[1.25003,1.75,0,0],123:[1.25003,1.75,0,0],125:[1.25003,1.75,0,0],710:[0,.825,0,0],732:[0,.825,0,0],770:[0,.825,0,0],771:[0,.825,0,0],8730:[1.25003,1.75,0,0],8968:[1.25003,1.75,0,0],8969:[1.25003,1.75,0,0],8970:[1.25003,1.75,0,0],8971:[1.25003,1.75,0,0],9115:[.64502,1.155,0,0],9116:[1e-5,.6,0,0],9117:[.64502,1.155,0,0],9118:[.64502,1.155,0,0],9119:[1e-5,.6,0,0],9120:[.64502,1.155,0,0],9121:[.64502,1.155,0,0],9122:[-99e-5,.601,0,0],9123:[.64502,1.155,0,0],9124:[.64502,1.155,0,0],9125:[-99e-5,.601,0,0],9126:[.64502,1.155,0,0],9127:[1e-5,.9,0,0],9128:[.65002,1.15,0,0],9129:[.90001,0,0,0],9130:[0,.3,0,0],9131:[1e-5,.9,0,0],9132:[.65002,1.15,0,0],9133:[.90001,0,0,0],9143:[.88502,.915,0,0],10216:[1.25003,1.75,0,0],10217:[1.25003,1.75,0,0],57344:[-.00499,.605,0,0],57345:[-.00499,.605,0,0],57680:[0,.12,0,0],57681:[0,.12,0,0],57682:[0,.12,0,0],57683:[0,.12,0,0]},"Typewriter-Regular":{33:[0,.61111,0,0],34:[0,.61111,0,0],35:[0,.61111,0,0],36:[.08333,.69444,0,0],37:[.08333,.69444,0,0],38:[0,.61111,0,0],39:[0,.61111,0,0],40:[.08333,.69444,0,0],41:[.08333,.69444,0,0],42:[0,.52083,0,0],43:[-.08056,.53055,0,0],44:[.13889,.125,0,0],45:[-.08056,.53055,0,0],46:[0,.125,0,0],47:[.08333,.69444,0,0],48:[0,.61111,0,0],49:[0,.61111,0,0],50:[0,.61111,0,0],51:[0,.61111,0,0],52:[0,.61111,0,0],53:[0,.61111,0,0],54:[0,.61111,0,0],55:[0,.61111,0,0],56:[0,.61111,0,0],57:[0,.61111,0,0],58:[0,.43056,0,0],59:[.13889,.43056,0,0],60:[-.05556,.55556,0,0],61:[-.19549,.41562,0,0],62:[-.05556,.55556,0,0],63:[0,.61111,0,0],64:[0,.61111,0,0],65:[0,.61111,0,0],66:[0,.61111,0,0],67:[0,.61111,0,0],68:[0,.61111,0,0],69:[0,.61111,0,0],70:[0,.61111,0,0],71:[0,.61111,0,0],72:[0,.61111,0,0],73:[0,.61111,0,0],74:[0,.61111,0,0],75:[0,.61111,0,0],76:[0,.61111,0,0],77:[0,.61111,0,0],78:[0,.61111,0,0],79:[0,.61111,0,0],80:[0,.61111,0,0],81:[.13889,.61111,0,0],82:[0,.61111,0,0],83:[0,.61111,0,0],84:[0,.61111,0,0],85:[0,.61111,0,0],86:[0,.61111,0,0],87:[0,.61111,0,0],88:[0,.61111,0,0],89:[0,.61111,0,0],90:[0,.61111,0,0],91:[.08333,.69444,0,0],92:[.08333,.69444,0,0],93:[.08333,.69444,0,0],94:[0,.61111,0,0],95:[.09514,0,0,0],96:[0,.61111,0,0],97:[0,.43056,0,0],98:[0,.61111,0,0],99:[0,.43056,0,0],100:[0,.61111,0,0],101:[0,.43056,0,0],102:[0,.61111,0,0],103:[.22222,.43056,0,0],104:[0,.61111,0,0],105:[0,.61111,0,0],106:[.22222,.61111,0,0],107:[0,.61111,0,0],108:[0,.61111,0,0],109:[0,.43056,0,0],110:[0,.43056,0,0],111:[0,.43056,0,0],112:[.22222,.43056,0,0],113:[.22222,.43056,0,0],114:[0,.43056,0,0],115:[0,.43056,0,0],116:[0,.55358,0,0],117:[0,.43056,0,0],118:[0,.43056,0,0],119:[0,.43056,0,0],120:[0,.43056,0,0],121:[.22222,.43056,0,0],122:[0,.43056,0,0],123:[.08333,.69444,0,0],124:[.08333,.69444,0,0],125:[.08333,.69444,0,0],126:[0,.61111,0,0],127:[0,.61111,0,0],305:[0,.43056,0,0],567:[.22222,.43056,0,0],768:[0,.61111,0,0],769:[0,.61111,0,0],770:[0,.61111,0,0],771:[0,.61111,0,0],772:[0,.56555,0,0],774:[0,.61111,0,0],776:[0,.61111,0,0],778:[0,.61111,0,0],780:[0,.56597,0,0],915:[0,.61111,0,0],916:[0,.61111,0,0],920:[0,.61111,0,0],923:[0,.61111,0,0],926:[0,.61111,0,0],928:[0,.61111,0,0],931:[0,.61111,0,0],933:[0,.61111,0,0],934:[0,.61111,0,0],936:[0,.61111,0,0],937:[0,.61111,0,0],2018:[0,.61111,0,0],2019:[0,.61111,0,0],8242:[0,.61111,0,0]}}},{}],19:[function(e,t,r){var a=e("./utils");var i=e("./ParseError");var n=e("./parseData");var s=n.ParseNode;function l(e,r,a){if(typeof e==="string"){e=[e]}if(typeof r==="number"){r={numArgs:r}}var i={numArgs:r.numArgs,argTypes:r.argTypes,greediness:r.greediness===undefined?1:r.greediness,allowedInText:!!r.allowedInText,numOptionalArgs:r.numOptionalArgs||0,infix:!!r.infix,handler:a};for(var n=0;n","\\langle","\\rangle","\\lt","\\gt","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\\lmoustache","\\rmoustache","/","\\backslash","|","\\vert","\\|","\\Vert","\\uparrow","\\Uparrow","\\downarrow","\\Downarrow","\\updownarrow","\\Updownarrow","."];var c={"\\Bbb":"\\mathbb","\\bold":"\\mathbf","\\frak":"\\mathfrak"};l(["\\blue","\\orange","\\pink","\\red","\\green","\\gray","\\purple","\\blueA","\\blueB","\\blueC","\\blueD","\\blueE","\\tealA","\\tealB","\\tealC","\\tealD","\\tealE","\\greenA","\\greenB","\\greenC","\\greenD","\\greenE","\\goldA","\\goldB","\\goldC","\\goldD","\\goldE","\\redA","\\redB","\\redC","\\redD","\\redE","\\maroonA","\\maroonB","\\maroonC","\\maroonD","\\maroonE","\\purpleA","\\purpleB","\\purpleC","\\purpleD","\\purpleE","\\mintA","\\mintB","\\mintC","\\grayA","\\grayB","\\grayC","\\grayD","\\grayE","\\grayF","\\grayG","\\grayH","\\grayI","\\kaBlue","\\kaGreen"],{numArgs:1,allowedInText:true,greediness:3},function(e,t){var r=t[0];return{type:"color",color:"katex-"+e.funcName.slice(1),value:o(r)}});l(["\\arcsin","\\arccos","\\arctan","\\arg","\\cos","\\cosh","\\cot","\\coth","\\csc","\\deg","\\dim","\\exp","\\hom","\\ker","\\lg","\\ln","\\log","\\sec","\\sin","\\sinh","\\tan","\\tanh"],{numArgs:0},function(e){return{type:"op",limits:false,symbol:false,body:e.funcName}});l(["\\det","\\gcd","\\inf","\\lim","\\liminf","\\limsup","\\max","\\min","\\Pr","\\sup"],{numArgs:0},function(e){return{type:"op",limits:true,symbol:false,body:e.funcName}});l(["\\int","\\iint","\\iiint","\\oint"],{numArgs:0},function(e){return{type:"op",limits:false,symbol:true,body:e.funcName}});l(["\\coprod","\\bigvee","\\bigwedge","\\biguplus","\\bigcap","\\bigcup","\\intop","\\prod","\\sum","\\bigotimes","\\bigoplus","\\bigodot","\\bigsqcup","\\smallint"],{numArgs:0},function(e){return{type:"op",limits:true,symbol:true,body:e.funcName}});l("\\mathop",{numArgs:1},function(e,t){var r=t[0];return{type:"op",limits:false,symbol:false,value:o(r)}});l(["\\dfrac","\\frac","\\tfrac","\\dbinom","\\binom","\\tbinom","\\\\atopfrac"],{numArgs:2,greediness:2},function(e,t){var r=t[0];var a=t[1];var i;var n=null;var s=null;var l="auto";switch(e.funcName){case"\\dfrac":case"\\frac":case"\\tfrac":i=true;break;case"\\\\atopfrac":i=false;break;case"\\dbinom":case"\\binom":case"\\tbinom":i=false;n="(";s=")";break;default:throw new Error("Unrecognized genfrac command")}switch(e.funcName){case"\\dfrac":case"\\dbinom":l="display";break;case"\\tfrac":case"\\tbinom":l="text";break}return{type:"genfrac",numer:r,denom:a,hasBarLine:i,leftDelim:n,rightDelim:s,size:l}});l(["\\llap","\\rlap"],{numArgs:1,allowedInText:true},function(e,t){var r=t[0];return{type:e.funcName.slice(1),body:r}});var m=function(e,t){if(a.contains(h,e.value)){return e}else{throw new i("Invalid delimiter: '"+e.value+"' after '"+t.funcName+"'",e)}};l(["\\bigl","\\Bigl","\\biggl","\\Biggl","\\bigr","\\Bigr","\\biggr","\\Biggr","\\bigm","\\Bigm","\\biggm","\\Biggm","\\big","\\Big","\\bigg","\\Bigg"],{numArgs:1},function(e,t){var r=m(t[0],e);return{type:"delimsizing",size:p[e.funcName].size,mclass:p[e.funcName].mclass,value:r.value}});l(["\\left","\\right"],{numArgs:1},function(e,t){var r=m(t[0],e);return{type:"leftright",value:r.value}});l("\\middle",{numArgs:1},function(e,t){var r=m(t[0],e);if(!e.parser.leftrightDepth){throw new i("\\middle without preceding \\left",r)}return{type:"middle",value:r.value}});l(["\\tiny","\\scriptsize","\\footnotesize","\\small","\\normalsize","\\large","\\Large","\\LARGE","\\huge","\\Huge"],0,null);l(["\\displaystyle","\\textstyle","\\scriptstyle","\\scriptscriptstyle"],0,null);l(["\\mathrm","\\mathit","\\mathbf","\\mathbb","\\mathcal","\\mathfrak","\\mathscr","\\mathsf","\\mathtt","\\Bbb","\\bold","\\frak"],{numArgs:1,greediness:2},function(e,t){var r=t[0];var a=e.funcName;if(a in c){a=c[a]}return{type:"font",font:a.slice(1),body:r}});l(["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot"],{numArgs:1},function(e,t){var r=t[0];return{type:"accent",accent:e.funcName,base:r}});l(["\\over","\\choose","\\atop"],{numArgs:0,infix:true},function(e){var t;switch(e.funcName){case"\\over":t="\\frac";break;case"\\choose":t="\\binom";break;case"\\atop":t="\\\\atopfrac";break;default:throw new Error("Unrecognized infix genfrac command")}return{type:"infix",replaceWith:t,token:e.token}});l(["\\\\","\\cr"],{numArgs:0,numOptionalArgs:1,argTypes:["size"]},function(e,t){var r=t[0];return{type:"cr",size:r}});l(["\\begin","\\end"],{numArgs:1,argTypes:["text"]},function(e,t){var r=t[0];if(r.type!=="ordgroup"){throw new i("Invalid environment name",r)}var a="";for(var n=0;n";return e};function n(e){this.text=e}n.prototype.toNode=function(){return document.createTextNode(this.text)};n.prototype.toMarkup=function(){return a.escape(this.text)};t.exports={MathNode:i,TextNode:n}},{"./utils":25}],21:[function(e,t,r){function a(e,t,r,a,i){this.type=e;this.value=t;this.mode=r;if(a&&(!i||i.lexer===a.lexer)){this.lexer=a.lexer;this.start=a.start;this.end=(i||a).end}}t.exports={ParseNode:a}},{}],22:[function(e,t,r){var a=e("./Parser");var i=function(e,t){if(!(typeof e==="string"||e instanceof String)){throw new TypeError("KaTeX can only parse string typed expression")}var r=new a(e,t);return r.parse()};t.exports=i},{"./Parser":7}],23:[function(e,t,r){t.exports={math:{},text:{}};function a(e,r,a,i,n){t.exports[e][n]={font:r,group:a,replace:i}}var i="math";var n="text";var s="main";var l="ams";var o="accent";var u="bin";var p="close";var h="inner";var c="mathord";var m="op";var f="open";var v="punct";var d="rel";var g="spacing";var y="textord";a(i,s,d,"\u2261","\\equiv");a(i,s,d,"\u227a","\\prec");a(i,s,d,"\u227b","\\succ");a(i,s,d,"\u223c","\\sim");a(i,s,d,"\u22a5","\\perp");a(i,s,d,"\u2aaf","\\preceq");a(i,s,d,"\u2ab0","\\succeq");a(i,s,d,"\u2243","\\simeq");a(i,s,d,"\u2223","\\mid");a(i,s,d,"\u226a","\\ll");a(i,s,d,"\u226b","\\gg");a(i,s,d,"\u224d","\\asymp");a(i,s,d,"\u2225","\\parallel");a(i,s,d,"\u22c8","\\bowtie");a(i,s,d,"\u2323","\\smile");a(i,s,d,"\u2291","\\sqsubseteq");a(i,s,d,"\u2292","\\sqsupseteq");a(i,s,d,"\u2250","\\doteq");a(i,s,d,"\u2322","\\frown");a(i,s,d,"\u220b","\\ni");a(i,s,d,"\u221d","\\propto");a(i,s,d,"\u22a2","\\vdash");a(i,s,d,"\u22a3","\\dashv");a(i,s,d,"\u220b","\\owns");a(i,s,v,".","\\ldotp");a(i,s,v,"\u22c5","\\cdotp");a(i,s,y,"#","\\#");a(n,s,y,"#","\\#");a(i,s,y,"&","\\&");a(n,s,y,"&","\\&");a(i,s,y,"\u2135","\\aleph");a(i,s,y,"\u2200","\\forall");a(i,s,y,"\u210f","\\hbar");a(i,s,y,"\u2203","\\exists");a(i,s,y,"\u2207","\\nabla");a(i,s,y,"\u266d","\\flat");a(i,s,y,"\u2113","\\ell");a(i,s,y,"\u266e","\\natural");a(i,s,y,"\u2663","\\clubsuit");a(i,s,y,"\u2118","\\wp");a(i,s,y,"\u266f","\\sharp");a(i,s,y,"\u2662","\\diamondsuit");a(i,s,y,"\u211c","\\Re");a(i,s,y,"\u2661","\\heartsuit");a(i,s,y,"\u2111","\\Im");a(i,s,y,"\u2660","\\spadesuit");a(i,s,y,"\u2020","\\dag");a(i,s,y,"\u2021","\\ddag");a(i,s,p,"\u23b1","\\rmoustache");a(i,s,f,"\u23b0","\\lmoustache");a(i,s,p,"\u27ef","\\rgroup");a(i,s,f,"\u27ee","\\lgroup");a(i,s,u,"\u2213","\\mp");a(i,s,u,"\u2296","\\ominus");a(i,s,u,"\u228e","\\uplus");a(i,s,u,"\u2293","\\sqcap");a(i,s,u,"\u2217","\\ast");a(i,s,u,"\u2294","\\sqcup");a(i,s,u,"\u25ef","\\bigcirc");a(i,s,u,"\u2219","\\bullet");a(i,s,u,"\u2021","\\ddagger");a(i,s,u,"\u2240","\\wr");a(i,s,u,"\u2a3f","\\amalg");a(i,s,d,"\u27f5","\\longleftarrow");a(i,s,d,"\u21d0","\\Leftarrow");a(i,s,d,"\u27f8","\\Longleftarrow");a(i,s,d,"\u27f6","\\longrightarrow");a(i,s,d,"\u21d2","\\Rightarrow");a(i,s,d,"\u27f9","\\Longrightarrow");a(i,s,d,"\u2194","\\leftrightarrow");a(i,s,d,"\u27f7","\\longleftrightarrow");a(i,s,d,"\u21d4","\\Leftrightarrow");a(i,s,d,"\u27fa","\\Longleftrightarrow");a(i,s,d,"\u21a6","\\mapsto");a(i,s,d,"\u27fc","\\longmapsto");a(i,s,d,"\u2197","\\nearrow");a(i,s,d,"\u21a9","\\hookleftarrow");a(i,s,d,"\u21aa","\\hookrightarrow");a(i,s,d,"\u2198","\\searrow");a(i,s,d,"\u21bc","\\leftharpoonup");a(i,s,d,"\u21c0","\\rightharpoonup");a(i,s,d,"\u2199","\\swarrow");a(i,s,d,"\u21bd","\\leftharpoondown");a(i,s,d,"\u21c1","\\rightharpoondown");a(i,s,d,"\u2196","\\nwarrow");a(i,s,d,"\u21cc","\\rightleftharpoons");a(i,l,d,"\u226e","\\nless");a(i,l,d,"\ue010","\\nleqslant");a(i,l,d,"\ue011","\\nleqq");a(i,l,d,"\u2a87","\\lneq");a(i,l,d,"\u2268","\\lneqq");a(i,l,d,"\ue00c","\\lvertneqq");a(i,l,d,"\u22e6","\\lnsim");a(i,l,d,"\u2a89","\\lnapprox");a(i,l,d,"\u2280","\\nprec");a(i,l,d,"\u22e0","\\npreceq");a(i,l,d,"\u22e8","\\precnsim");a(i,l,d,"\u2ab9","\\precnapprox");a(i,l,d,"\u2241","\\nsim");a(i,l,d,"\ue006","\\nshortmid");a(i,l,d,"\u2224","\\nmid");a(i,l,d,"\u22ac","\\nvdash");a(i,l,d,"\u22ad","\\nvDash");a(i,l,d,"\u22ea","\\ntriangleleft");a(i,l,d,"\u22ec","\\ntrianglelefteq");a(i,l,d,"\u228a","\\subsetneq");a(i,l,d,"\ue01a","\\varsubsetneq");a(i,l,d,"\u2acb","\\subsetneqq");a(i,l,d,"\ue017","\\varsubsetneqq");a(i,l,d,"\u226f","\\ngtr");a(i,l,d,"\ue00f","\\ngeqslant");a(i,l,d,"\ue00e","\\ngeqq");a(i,l,d,"\u2a88","\\gneq");a(i,l,d,"\u2269","\\gneqq");a(i,l,d,"\ue00d","\\gvertneqq");a(i,l,d,"\u22e7","\\gnsim");a(i,l,d,"\u2a8a","\\gnapprox");a(i,l,d,"\u2281","\\nsucc");a(i,l,d,"\u22e1","\\nsucceq");a(i,l,d,"\u22e9","\\succnsim");a(i,l,d,"\u2aba","\\succnapprox");a(i,l,d,"\u2246","\\ncong");a(i,l,d,"\ue007","\\nshortparallel");a(i,l,d,"\u2226","\\nparallel");a(i,l,d,"\u22af","\\nVDash");a(i,l,d,"\u22eb","\\ntriangleright");a(i,l,d,"\u22ed","\\ntrianglerighteq");a(i,l,d,"\ue018","\\nsupseteqq");a(i,l,d,"\u228b","\\supsetneq");a(i,l,d,"\ue01b","\\varsupsetneq");a(i,l,d,"\u2acc","\\supsetneqq");a(i,l,d,"\ue019","\\varsupsetneqq");a(i,l,d,"\u22ae","\\nVdash");a(i,l,d,"\u2ab5","\\precneqq");a(i,l,d,"\u2ab6","\\succneqq");a(i,l,d,"\ue016","\\nsubseteqq");a(i,l,u,"\u22b4","\\unlhd");a(i,l,u,"\u22b5","\\unrhd");a(i,l,d,"\u219a","\\nleftarrow");a(i,l,d,"\u219b","\\nrightarrow");a(i,l,d,"\u21cd","\\nLeftarrow");a(i,l,d,"\u21cf","\\nRightarrow");a(i,l,d,"\u21ae","\\nleftrightarrow");a(i,l,d,"\u21ce","\\nLeftrightarrow");a(i,l,d,"\u25b3","\\vartriangle");a(i,l,y,"\u210f","\\hslash");a(i,l,y,"\u25bd","\\triangledown");a(i,l,y,"\u25ca","\\lozenge");a(i,l,y,"\u24c8","\\circledS");a(i,l,y,"\xae","\\circledR");a(i,l,y,"\u2221","\\measuredangle");a(i,l,y,"\u2204","\\nexists");a(i,l,y,"\u2127","\\mho");a(i,l,y,"\u2132","\\Finv");a(i,l,y,"\u2141","\\Game");a(i,l,y,"k","\\Bbbk");a(i,l,y,"\u2035","\\backprime");a(i,l,y,"\u25b2","\\blacktriangle");a(i,l,y,"\u25bc","\\blacktriangledown");a(i,l,y,"\u25a0","\\blacksquare");a(i,l,y,"\u29eb","\\blacklozenge");a(i,l,y,"\u2605","\\bigstar");a(i,l,y,"\u2222","\\sphericalangle");a(i,l,y,"\u2201","\\complement");a(i,l,y,"\xf0","\\eth");a(i,l,y,"\u2571","\\diagup");a(i,l,y,"\u2572","\\diagdown");a(i,l,y,"\u25a1","\\square");a(i,l,y,"\u25a1","\\Box");a(i,l,y,"\u25ca","\\Diamond");a(i,l,y,"\xa5","\\yen");a(i,l,y,"\u2713","\\checkmark");a(i,l,y,"\u2136","\\beth");a(i,l,y,"\u2138","\\daleth");a(i,l,y,"\u2137","\\gimel");a(i,l,y,"\u03dd","\\digamma");a(i,l,y,"\u03f0","\\varkappa");a(i,l,f,"\u250c","\\ulcorner");a(i,l,p,"\u2510","\\urcorner");a(i,l,f,"\u2514","\\llcorner");a(i,l,p,"\u2518","\\lrcorner");a(i,l,d,"\u2266","\\leqq");a(i,l,d,"\u2a7d","\\leqslant");a(i,l,d,"\u2a95","\\eqslantless");a(i,l,d,"\u2272","\\lesssim");a(i,l,d,"\u2a85","\\lessapprox");a(i,l,d,"\u224a","\\approxeq");a(i,l,u,"\u22d6","\\lessdot");a(i,l,d,"\u22d8","\\lll");a(i,l,d,"\u2276","\\lessgtr");a(i,l,d,"\u22da","\\lesseqgtr");a(i,l,d,"\u2a8b","\\lesseqqgtr");a(i,l,d,"\u2251","\\doteqdot");a(i,l,d,"\u2253","\\risingdotseq");a(i,l,d,"\u2252","\\fallingdotseq");a(i,l,d,"\u223d","\\backsim");a(i,l,d,"\u22cd","\\backsimeq");a(i,l,d,"\u2ac5","\\subseteqq");a(i,l,d,"\u22d0","\\Subset");a(i,l,d,"\u228f","\\sqsubset");a(i,l,d,"\u227c","\\preccurlyeq");a(i,l,d,"\u22de","\\curlyeqprec");a(i,l,d,"\u227e","\\precsim");a(i,l,d,"\u2ab7","\\precapprox");a(i,l,d,"\u22b2","\\vartriangleleft");a(i,l,d,"\u22b4","\\trianglelefteq");a(i,l,d,"\u22a8","\\vDash");a(i,l,d,"\u22aa","\\Vvdash");a(i,l,d,"\u2323","\\smallsmile");a(i,l,d,"\u2322","\\smallfrown");a(i,l,d,"\u224f","\\bumpeq");a(i,l,d,"\u224e","\\Bumpeq");a(i,l,d,"\u2267","\\geqq");a(i,l,d,"\u2a7e","\\geqslant");a(i,l,d,"\u2a96","\\eqslantgtr");a(i,l,d,"\u2273","\\gtrsim");a(i,l,d,"\u2a86","\\gtrapprox");a(i,l,u,"\u22d7","\\gtrdot");a(i,l,d,"\u22d9","\\ggg");a(i,l,d,"\u2277","\\gtrless");a(i,l,d,"\u22db","\\gtreqless");a(i,l,d,"\u2a8c","\\gtreqqless");a(i,l,d,"\u2256","\\eqcirc");a(i,l,d,"\u2257","\\circeq");a(i,l,d,"\u225c","\\triangleq");a(i,l,d,"\u223c","\\thicksim");a(i,l,d,"\u2248","\\thickapprox");a(i,l,d,"\u2ac6","\\supseteqq");a(i,l,d,"\u22d1","\\Supset");a(i,l,d,"\u2290","\\sqsupset");a(i,l,d,"\u227d","\\succcurlyeq");a(i,l,d,"\u22df","\\curlyeqsucc");a(i,l,d,"\u227f","\\succsim");a(i,l,d,"\u2ab8","\\succapprox");a(i,l,d,"\u22b3","\\vartriangleright");a(i,l,d,"\u22b5","\\trianglerighteq");a(i,l,d,"\u22a9","\\Vdash");a(i,l,d,"\u2223","\\shortmid");a(i,l,d,"\u2225","\\shortparallel");a(i,l,d,"\u226c","\\between");a(i,l,d,"\u22d4","\\pitchfork");a(i,l,d,"\u221d","\\varpropto");a(i,l,d,"\u25c0","\\blacktriangleleft");a(i,l,d,"\u2234","\\therefore");a(i,l,d,"\u220d","\\backepsilon");a(i,l,d,"\u25b6","\\blacktriangleright");a(i,l,d,"\u2235","\\because");a(i,l,d,"\u22d8","\\llless");a(i,l,d,"\u22d9","\\gggtr");a(i,l,u,"\u22b2","\\lhd");a(i,l,u,"\u22b3","\\rhd");a(i,l,d,"\u2242","\\eqsim");a(i,s,d,"\u22c8","\\Join");a(i,l,d,"\u2251","\\Doteq");a(i,l,u,"\u2214","\\dotplus");a(i,l,u,"\u2216","\\smallsetminus");a(i,l,u,"\u22d2","\\Cap");a(i,l,u,"\u22d3","\\Cup");a(i,l,u,"\u2a5e","\\doublebarwedge");a(i,l,u,"\u229f","\\boxminus");a(i,l,u,"\u229e","\\boxplus");a(i,l,u,"\u22c7","\\divideontimes");a(i,l,u,"\u22c9","\\ltimes");a(i,l,u,"\u22ca","\\rtimes");a(i,l,u,"\u22cb","\\leftthreetimes");a(i,l,u,"\u22cc","\\rightthreetimes");a(i,l,u,"\u22cf","\\curlywedge");a(i,l,u,"\u22ce","\\curlyvee");a(i,l,u,"\u229d","\\circleddash");a(i,l,u,"\u229b","\\circledast");a(i,l,u,"\u22c5","\\centerdot");a(i,l,u,"\u22ba","\\intercal");a(i,l,u,"\u22d2","\\doublecap");a(i,l,u,"\u22d3","\\doublecup");a(i,l,u,"\u22a0","\\boxtimes");a(i,l,d,"\u21e2","\\dashrightarrow");a(i,l,d,"\u21e0","\\dashleftarrow");a(i,l,d,"\u21c7","\\leftleftarrows");a(i,l,d,"\u21c6","\\leftrightarrows");a(i,l,d,"\u21da","\\Lleftarrow");a(i,l,d,"\u219e","\\twoheadleftarrow");a(i,l,d,"\u21a2","\\leftarrowtail");a(i,l,d,"\u21ab","\\looparrowleft");a(i,l,d,"\u21cb","\\leftrightharpoons");a(i,l,d,"\u21b6","\\curvearrowleft");a(i,l,d,"\u21ba","\\circlearrowleft");a(i,l,d,"\u21b0","\\Lsh");a(i,l,d,"\u21c8","\\upuparrows");a(i,l,d,"\u21bf","\\upharpoonleft");a(i,l,d,"\u21c3","\\downharpoonleft");a(i,l,d,"\u22b8","\\multimap");a(i,l,d,"\u21ad","\\leftrightsquigarrow");a(i,l,d,"\u21c9","\\rightrightarrows");a(i,l,d,"\u21c4","\\rightleftarrows");a(i,l,d,"\u21a0","\\twoheadrightarrow");a(i,l,d,"\u21a3","\\rightarrowtail");a(i,l,d,"\u21ac","\\looparrowright");a(i,l,d,"\u21b7","\\curvearrowright");a(i,l,d,"\u21bb","\\circlearrowright");a(i,l,d,"\u21b1","\\Rsh");a(i,l,d,"\u21ca","\\downdownarrows");a(i,l,d,"\u21be","\\upharpoonright");a(i,l,d,"\u21c2","\\downharpoonright");a(i,l,d,"\u21dd","\\rightsquigarrow");a(i,l,d,"\u21dd","\\leadsto");a(i,l,d,"\u21db","\\Rrightarrow");a(i,l,d,"\u21be","\\restriction");a(i,s,y,"\u2018","`");a(i,s,y,"$","\\$");a(n,s,y,"$","\\$");a(i,s,y,"%","\\%");a(n,s,y,"%","\\%");a(i,s,y,"_","\\_");a(n,s,y,"_","\\_");a(i,s,y,"\u2220","\\angle");a(i,s,y,"\u221e","\\infty");a(i,s,y,"\u2032","\\prime");a(i,s,y,"\u25b3","\\triangle");a(i,s,y,"\u0393","\\Gamma");a(i,s,y,"\u0394","\\Delta");a(i,s,y,"\u0398","\\Theta");a(i,s,y,"\u039b","\\Lambda");a(i,s,y,"\u039e","\\Xi");a(i,s,y,"\u03a0","\\Pi");a(i,s,y,"\u03a3","\\Sigma");a(i,s,y,"\u03a5","\\Upsilon");a(i,s,y,"\u03a6","\\Phi");a(i,s,y,"\u03a8","\\Psi");a(i,s,y,"\u03a9","\\Omega");a(i,s,y,"\xac","\\neg");a(i,s,y,"\xac","\\lnot");a(i,s,y,"\u22a4","\\top");a(i,s,y,"\u22a5","\\bot");a(i,s,y,"\u2205","\\emptyset");a(i,l,y,"\u2205","\\varnothing");a(i,s,c,"\u03b1","\\alpha");a(i,s,c,"\u03b2","\\beta");a(i,s,c,"\u03b3","\\gamma");a(i,s,c,"\u03b4","\\delta");a(i,s,c,"\u03f5","\\epsilon");a(i,s,c,"\u03b6","\\zeta");a(i,s,c,"\u03b7","\\eta");a(i,s,c,"\u03b8","\\theta");a(i,s,c,"\u03b9","\\iota");a(i,s,c,"\u03ba","\\kappa");a(i,s,c,"\u03bb","\\lambda");a(i,s,c,"\u03bc","\\mu");a(i,s,c,"\u03bd","\\nu");a(i,s,c,"\u03be","\\xi");a(i,s,c,"o","\\omicron");a(i,s,c,"\u03c0","\\pi");a(i,s,c,"\u03c1","\\rho");a(i,s,c,"\u03c3","\\sigma");a(i,s,c,"\u03c4","\\tau");a(i,s,c,"\u03c5","\\upsilon");a(i,s,c,"\u03d5","\\phi");a(i,s,c,"\u03c7","\\chi");a(i,s,c,"\u03c8","\\psi");a(i,s,c,"\u03c9","\\omega");a(i,s,c,"\u03b5","\\varepsilon");a(i,s,c,"\u03d1","\\vartheta");a(i,s,c,"\u03d6","\\varpi");a(i,s,c,"\u03f1","\\varrho");a(i,s,c,"\u03c2","\\varsigma");a(i,s,c,"\u03c6","\\varphi");a(i,s,u,"\u2217","*");a(i,s,u,"+","+");a(i,s,u,"\u2212","-");a(i,s,u,"\u22c5","\\cdot");a(i,s,u,"\u2218","\\circ");a(i,s,u,"\xf7","\\div");a(i,s,u,"\xb1","\\pm");a(i,s,u,"\xd7","\\times");a(i,s,u,"\u2229","\\cap");a(i,s,u,"\u222a","\\cup");a(i,s,u,"\u2216","\\setminus");a(i,s,u,"\u2227","\\land");a(i,s,u,"\u2228","\\lor");a(i,s,u,"\u2227","\\wedge");a(i,s,u,"\u2228","\\vee");a(i,s,y,"\u221a","\\surd");a(i,s,f,"(","(");a(i,s,f,"[","[");a(i,s,f,"\u27e8","\\langle");a(i,s,f,"\u2223","\\lvert");a(i,s,f,"\u2225","\\lVert");a(i,s,p,")",")");a(i,s,p,"]","]");a(i,s,p,"?","?");a(i,s,p,"!","!");a(i,s,p,"\u27e9","\\rangle");a(i,s,p,"\u2223","\\rvert");a(i,s,p,"\u2225","\\rVert");a(i,s,d,"=","=");a(i,s,d,"<","<");a(i,s,d,">",">");a(i,s,d,":",":");a(i,s,d,"\u2248","\\approx");a(i,s,d,"\u2245","\\cong");a(i,s,d,"\u2265","\\ge");a(i,s,d,"\u2265","\\geq");a(i,s,d,"\u2190","\\gets");a(i,s,d,">","\\gt");a(i,s,d,"\u2208","\\in");a(i,s,d,"\u2209","\\notin");a(i,s,d,"\u2282","\\subset");a(i,s,d,"\u2283","\\supset");a(i,s,d,"\u2286","\\subseteq");a(i,s,d,"\u2287","\\supseteq");a(i,l,d,"\u2288","\\nsubseteq");a(i,l,d,"\u2289","\\nsupseteq");a(i,s,d,"\u22a8","\\models");a(i,s,d,"\u2190","\\leftarrow");a(i,s,d,"\u2264","\\le");a(i,s,d,"\u2264","\\leq");a(i,s,d,"<","\\lt");a(i,s,d,"\u2260","\\ne");a(i,s,d,"\u2260","\\neq");a(i,s,d,"\u2192","\\rightarrow");a(i,s,d,"\u2192","\\to");a(i,l,d,"\u2271","\\ngeq");a(i,l,d,"\u2270","\\nleq");a(i,s,g,null,"\\!");a(i,s,g,"\xa0","\\ ");a(i,s,g,"\xa0","~");a(i,s,g,null,"\\,");a(i,s,g,null,"\\:");a(i,s,g,null,"\\;");a(i,s,g,null,"\\enspace");a(i,s,g,null,"\\qquad");a(i,s,g,null,"\\quad");a(i,s,g,"\xa0","\\space");a(i,s,v,",",",");a(i,s,v,";",";");a(i,s,v,":","\\colon");a(i,l,u,"\u22bc","\\barwedge");a(i,l,u,"\u22bb","\\veebar");a(i,s,u,"\u2299","\\odot");a(i,s,u,"\u2295","\\oplus");a(i,s,u,"\u2297","\\otimes");a(i,s,y,"\u2202","\\partial");a(i,s,u,"\u2298","\\oslash");a(i,l,u,"\u229a","\\circledcirc");a(i,l,u,"\u22a1","\\boxdot");a(i,s,u,"\u25b3","\\bigtriangleup");a(i,s,u,"\u25bd","\\bigtriangledown");a(i,s,u,"\u2020","\\dagger");a(i,s,u,"\u22c4","\\diamond");a(i,s,u,"\u22c6","\\star");a(i,s,u,"\u25c3","\\triangleleft");a(i,s,u,"\u25b9","\\triangleright");a(i,s,f,"{","\\{");a(n,s,y,"{","\\{");a(i,s,p,"}","\\}");a(n,s,y,"}","\\}");a(i,s,f,"{","\\lbrace");a(i,s,p,"}","\\rbrace");a(i,s,f,"[","\\lbrack");a(i,s,p,"]","\\rbrack");a(i,s,f,"\u230a","\\lfloor");a(i,s,p,"\u230b","\\rfloor");a(i,s,f,"\u2308","\\lceil");a(i,s,p,"\u2309","\\rceil");a(i,s,y,"\\","\\backslash");a(i,s,y,"\u2223","|");a(i,s,y,"\u2223","\\vert");a(i,s,y,"\u2225","\\|");a(i,s,y,"\u2225","\\Vert");a(i,s,d,"\u2191","\\uparrow");a(i,s,d,"\u21d1","\\Uparrow");a(i,s,d,"\u2193","\\downarrow");a(i,s,d,"\u21d3","\\Downarrow");a(i,s,d,"\u2195","\\updownarrow");a(i,s,d,"\u21d5","\\Updownarrow");a(i,i,m,"\u2210","\\coprod");a(i,i,m,"\u22c1","\\bigvee");a(i,i,m,"\u22c0","\\bigwedge");a(i,i,m,"\u2a04","\\biguplus");a(i,i,m,"\u22c2","\\bigcap");a(i,i,m,"\u22c3","\\bigcup");a(i,i,m,"\u222b","\\int");a(i,i,m,"\u222b","\\intop");a(i,i,m,"\u222c","\\iint");a(i,i,m,"\u222d","\\iiint");a(i,i,m,"\u220f","\\prod");a(i,i,m,"\u2211","\\sum");a(i,i,m,"\u2a02","\\bigotimes");a(i,i,m,"\u2a01","\\bigoplus");a(i,i,m,"\u2a00","\\bigodot");a(i,i,m,"\u222e","\\oint");a(i,i,m,"\u2a06","\\bigsqcup");a(i,i,m,"\u222b","\\smallint");a(n,s,h,"\u2026","\\textellipsis");a(i,s,h,"\u2026","\\mathellipsis");a(n,s,h,"\u2026","\\ldots");a(i,s,h,"\u2026","\\ldots");a(i,s,h,"\u22ef","\\cdots");a(i,s,h,"\u22f1","\\ddots");a(i,s,y,"\u22ee","\\vdots");a(i,s,o,"\xb4","\\acute");a(i,s,o,"`","\\grave");a(i,s,o,"\xa8","\\ddot");a(i,s,o,"~","\\tilde");a(i,s,o,"\xaf","\\bar");a(i,s,o,"\u02d8","\\breve");a(i,s,o,"\u02c7","\\check");a(i,s,o,"^","\\hat");a(i,s,o,"\u20d7","\\vec");a(i,s,o,"\u02d9","\\dot");a(i,s,c,"\u0131","\\imath");a(i,s,c,"\u0237","\\jmath");a(n,s,y,"\u2013","--");a(n,s,y,"\u2014","---");a(n,s,y,"\u2018","`");a(n,s,y,"\u2019","'");a(n,s,y,"\u201c","``");a(n,s,y,"\u201d","''");a(i,s,y,"\xb0","\\degree");a(n,s,y,"\xb0","\\degree");a(i,s,c,"\xa3","\\pounds");a(i,l,y,"\u2720","\\maltese");a(n,l,y,"\u2720","\\maltese");a(n,s,g,"\xa0","\\ ");a(n,s,g,"\xa0"," ");a(n,s,g,"\xa0","~");var x;var b;var w='0123456789/@."';for(x=0;x":">","<":"<",'"':""","'":"'"};var p=/[&><"']/g;function h(e){return u[e]}function c(e){return(""+e).replace(p,h)}var m;if(typeof document!=="undefined"){var f=document.createElement("span");if("textContent"in f){m=function(e,t){e.textContent=t}}else{m=function(e,t){e.innerText=t}}}function v(e){m(e,"")}t.exports={contains:n,deflt:s,escape:c,hyphenate:o,indexOf:i,setTextContent:m,clearNode:v}},{}]},{},[1])(1)}); diff --git a/Wino.Mail/JS/Quill/monokai-sublime.min.css b/Wino.Mail/JS/Quill/monokai-sublime.min.css new file mode 100644 index 00000000..589eff91 --- /dev/null +++ b/Wino.Mail/JS/Quill/monokai-sublime.min.css @@ -0,0 +1 @@ +.hljs{display:block;overflow-x:auto;padding:0.5em;background:#23241f}.hljs,.hljs-tag,.hljs-subst{color:#f8f8f2}.hljs-strong,.hljs-emphasis{color:#a8a8a2}.hljs-bullet,.hljs-quote,.hljs-number,.hljs-regexp,.hljs-literal,.hljs-link{color:#ae81ff}.hljs-code,.hljs-title,.hljs-section,.hljs-selector-class{color:#a6e22e}.hljs-strong{font-weight:bold}.hljs-emphasis{font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-name,.hljs-attr{color:#f92672}.hljs-symbol,.hljs-attribute{color:#66d9ef}.hljs-params,.hljs-class .hljs-title{color:#f8f8f2}.hljs-string,.hljs-type,.hljs-built_in,.hljs-builtin-name,.hljs-selector-id,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-addition,.hljs-variable,.hljs-template-variable{color:#e6db74}.hljs-comment,.hljs-deletion,.hljs-meta{color:#75715e} \ No newline at end of file diff --git a/Wino.Mail/JS/Quill/quill.core.css b/Wino.Mail/JS/Quill/quill.core.css new file mode 100644 index 00000000..4217e4e5 --- /dev/null +++ b/Wino.Mail/JS/Quill/quill.core.css @@ -0,0 +1,397 @@ +/*! + * Quill Editor v1.3.6 + * https://quilljs.com/ + * Copyright (c) 2014, Jason Chen + * Copyright (c) 2013, salesforce.com + */ +.ql-container { + box-sizing: border-box; + font-family: Helvetica, Arial, sans-serif; + font-size: 13px; + height: 100%; + margin: 0px; + position: relative; +} +.ql-container.ql-disabled .ql-tooltip { + visibility: hidden; +} +.ql-container.ql-disabled .ql-editor ul[data-checked] > li::before { + pointer-events: none; +} +.ql-clipboard { + left: -100000px; + height: 1px; + overflow-y: hidden; + position: absolute; + top: 50%; +} +.ql-clipboard p { + margin: 0; + padding: 0; +} +.ql-editor { + box-sizing: border-box; + line-height: 1.42; + height: 100%; + outline: none; + overflow-y: auto; + padding: 12px 15px; + tab-size: 4; + -moz-tab-size: 4; + text-align: left; + white-space: pre-wrap; + word-wrap: break-word; +} +.ql-editor > * { + cursor: text; +} +.ql-editor p, +.ql-editor ol, +.ql-editor ul, +.ql-editor pre, +.ql-editor blockquote, +.ql-editor h1, +.ql-editor h2, +.ql-editor h3, +.ql-editor h4, +.ql-editor h5, +.ql-editor h6 { + margin: 0; + padding: 0; + counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9; +} +.ql-editor ol, +.ql-editor ul { + padding-left: 1.5em; +} +.ql-editor ol > li, +.ql-editor ul > li { + list-style-type: none; +} +.ql-editor ul > li::before { + content: '\2022'; +} +.ql-editor ul[data-checked=true], +.ql-editor ul[data-checked=false] { + pointer-events: none; +} +.ql-editor ul[data-checked=true] > li *, +.ql-editor ul[data-checked=false] > li * { + pointer-events: all; +} +.ql-editor ul[data-checked=true] > li::before, +.ql-editor ul[data-checked=false] > li::before { + color: #777; + cursor: pointer; + pointer-events: all; +} +.ql-editor ul[data-checked=true] > li::before { + content: '\2611'; +} +.ql-editor ul[data-checked=false] > li::before { + content: '\2610'; +} +.ql-editor li::before { + display: inline-block; + white-space: nowrap; + width: 1.2em; +} +.ql-editor li:not(.ql-direction-rtl)::before { + margin-left: -1.5em; + margin-right: 0.3em; + text-align: right; +} +.ql-editor li.ql-direction-rtl::before { + margin-left: 0.3em; + margin-right: -1.5em; +} +.ql-editor ol li:not(.ql-direction-rtl), +.ql-editor ul li:not(.ql-direction-rtl) { + padding-left: 1.5em; +} +.ql-editor ol li.ql-direction-rtl, +.ql-editor ul li.ql-direction-rtl { + padding-right: 1.5em; +} +.ql-editor ol li { + counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9; + counter-increment: list-0; +} +.ql-editor ol li:before { + content: counter(list-0, decimal) '. '; +} +.ql-editor ol li.ql-indent-1 { + counter-increment: list-1; +} +.ql-editor ol li.ql-indent-1:before { + content: counter(list-1, lower-alpha) '. '; +} +.ql-editor ol li.ql-indent-1 { + counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9; +} +.ql-editor ol li.ql-indent-2 { + counter-increment: list-2; +} +.ql-editor ol li.ql-indent-2:before { + content: counter(list-2, lower-roman) '. '; +} +.ql-editor ol li.ql-indent-2 { + counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9; +} +.ql-editor ol li.ql-indent-3 { + counter-increment: list-3; +} +.ql-editor ol li.ql-indent-3:before { + content: counter(list-3, decimal) '. '; +} +.ql-editor ol li.ql-indent-3 { + counter-reset: list-4 list-5 list-6 list-7 list-8 list-9; +} +.ql-editor ol li.ql-indent-4 { + counter-increment: list-4; +} +.ql-editor ol li.ql-indent-4:before { + content: counter(list-4, lower-alpha) '. '; +} +.ql-editor ol li.ql-indent-4 { + counter-reset: list-5 list-6 list-7 list-8 list-9; +} +.ql-editor ol li.ql-indent-5 { + counter-increment: list-5; +} +.ql-editor ol li.ql-indent-5:before { + content: counter(list-5, lower-roman) '. '; +} +.ql-editor ol li.ql-indent-5 { + counter-reset: list-6 list-7 list-8 list-9; +} +.ql-editor ol li.ql-indent-6 { + counter-increment: list-6; +} +.ql-editor ol li.ql-indent-6:before { + content: counter(list-6, decimal) '. '; +} +.ql-editor ol li.ql-indent-6 { + counter-reset: list-7 list-8 list-9; +} +.ql-editor ol li.ql-indent-7 { + counter-increment: list-7; +} +.ql-editor ol li.ql-indent-7:before { + content: counter(list-7, lower-alpha) '. '; +} +.ql-editor ol li.ql-indent-7 { + counter-reset: list-8 list-9; +} +.ql-editor ol li.ql-indent-8 { + counter-increment: list-8; +} +.ql-editor ol li.ql-indent-8:before { + content: counter(list-8, lower-roman) '. '; +} +.ql-editor ol li.ql-indent-8 { + counter-reset: list-9; +} +.ql-editor ol li.ql-indent-9 { + counter-increment: list-9; +} +.ql-editor ol li.ql-indent-9:before { + content: counter(list-9, decimal) '. '; +} +.ql-editor .ql-indent-1:not(.ql-direction-rtl) { + padding-left: 3em; +} +.ql-editor li.ql-indent-1:not(.ql-direction-rtl) { + padding-left: 4.5em; +} +.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right { + padding-right: 3em; +} +.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right { + padding-right: 4.5em; +} +.ql-editor .ql-indent-2:not(.ql-direction-rtl) { + padding-left: 6em; +} +.ql-editor li.ql-indent-2:not(.ql-direction-rtl) { + padding-left: 7.5em; +} +.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right { + padding-right: 6em; +} +.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right { + padding-right: 7.5em; +} +.ql-editor .ql-indent-3:not(.ql-direction-rtl) { + padding-left: 9em; +} +.ql-editor li.ql-indent-3:not(.ql-direction-rtl) { + padding-left: 10.5em; +} +.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right { + padding-right: 9em; +} +.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right { + padding-right: 10.5em; +} +.ql-editor .ql-indent-4:not(.ql-direction-rtl) { + padding-left: 12em; +} +.ql-editor li.ql-indent-4:not(.ql-direction-rtl) { + padding-left: 13.5em; +} +.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right { + padding-right: 12em; +} +.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right { + padding-right: 13.5em; +} +.ql-editor .ql-indent-5:not(.ql-direction-rtl) { + padding-left: 15em; +} +.ql-editor li.ql-indent-5:not(.ql-direction-rtl) { + padding-left: 16.5em; +} +.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right { + padding-right: 15em; +} +.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right { + padding-right: 16.5em; +} +.ql-editor .ql-indent-6:not(.ql-direction-rtl) { + padding-left: 18em; +} +.ql-editor li.ql-indent-6:not(.ql-direction-rtl) { + padding-left: 19.5em; +} +.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right { + padding-right: 18em; +} +.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right { + padding-right: 19.5em; +} +.ql-editor .ql-indent-7:not(.ql-direction-rtl) { + padding-left: 21em; +} +.ql-editor li.ql-indent-7:not(.ql-direction-rtl) { + padding-left: 22.5em; +} +.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right { + padding-right: 21em; +} +.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right { + padding-right: 22.5em; +} +.ql-editor .ql-indent-8:not(.ql-direction-rtl) { + padding-left: 24em; +} +.ql-editor li.ql-indent-8:not(.ql-direction-rtl) { + padding-left: 25.5em; +} +.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right { + padding-right: 24em; +} +.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right { + padding-right: 25.5em; +} +.ql-editor .ql-indent-9:not(.ql-direction-rtl) { + padding-left: 27em; +} +.ql-editor li.ql-indent-9:not(.ql-direction-rtl) { + padding-left: 28.5em; +} +.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right { + padding-right: 27em; +} +.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right { + padding-right: 28.5em; +} +.ql-editor .ql-video { + display: block; + max-width: 100%; +} +.ql-editor .ql-video.ql-align-center { + margin: 0 auto; +} +.ql-editor .ql-video.ql-align-right { + margin: 0 0 0 auto; +} +.ql-editor .ql-bg-black { + background-color: #000; +} +.ql-editor .ql-bg-red { + background-color: #e60000; +} +.ql-editor .ql-bg-orange { + background-color: #f90; +} +.ql-editor .ql-bg-yellow { + background-color: #ff0; +} +.ql-editor .ql-bg-green { + background-color: #008a00; +} +.ql-editor .ql-bg-blue { + background-color: #06c; +} +.ql-editor .ql-bg-purple { + background-color: #93f; +} +.ql-editor .ql-color-white { + color: #fff; +} +.ql-editor .ql-color-red { + color: #e60000; +} +.ql-editor .ql-color-orange { + color: #f90; +} +.ql-editor .ql-color-yellow { + color: #ff0; +} +.ql-editor .ql-color-green { + color: #008a00; +} +.ql-editor .ql-color-blue { + color: #06c; +} +.ql-editor .ql-color-purple { + color: #93f; +} +.ql-editor .ql-font-serif { + font-family: Georgia, Times New Roman, serif; +} +.ql-editor .ql-font-monospace { + font-family: Monaco, Courier New, monospace; +} +.ql-editor .ql-size-small { + font-size: 0.75em; +} +.ql-editor .ql-size-large { + font-size: 1.5em; +} +.ql-editor .ql-size-huge { + font-size: 2.5em; +} +.ql-editor .ql-direction-rtl { + direction: rtl; + text-align: inherit; +} +.ql-editor .ql-align-center { + text-align: center; +} +.ql-editor .ql-align-justify { + text-align: justify; +} +.ql-editor .ql-align-right { + text-align: right; +} +.ql-editor.ql-blank::before { + color: rgba(0,0,0,0.6); + content: attr(data-placeholder); + font-style: italic; + left: 15px; + pointer-events: none; + position: absolute; + right: 15px; +} diff --git a/Wino.Mail/JS/Quill/quill.core.js b/Wino.Mail/JS/Quill/quill.core.js new file mode 100644 index 00000000..a51d15c8 --- /dev/null +++ b/Wino.Mail/JS/Quill/quill.core.js @@ -0,0 +1,8522 @@ +/*! + * Quill Editor v1.3.6 + * https://quilljs.com/ + * Copyright (c) 2014, Jason Chen + * Copyright (c) 2013, salesforce.com + */ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["Quill"] = factory(); + else + root["Quill"] = factory(); +})(typeof self !== 'undefined' ? self : this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 110); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var container_1 = __webpack_require__(17); +var format_1 = __webpack_require__(18); +var leaf_1 = __webpack_require__(19); +var scroll_1 = __webpack_require__(45); +var inline_1 = __webpack_require__(46); +var block_1 = __webpack_require__(47); +var embed_1 = __webpack_require__(48); +var text_1 = __webpack_require__(49); +var attributor_1 = __webpack_require__(12); +var class_1 = __webpack_require__(32); +var style_1 = __webpack_require__(33); +var store_1 = __webpack_require__(31); +var Registry = __webpack_require__(1); +var Parchment = { + Scope: Registry.Scope, + create: Registry.create, + find: Registry.find, + query: Registry.query, + register: Registry.register, + Container: container_1.default, + Format: format_1.default, + Leaf: leaf_1.default, + Embed: embed_1.default, + Scroll: scroll_1.default, + Block: block_1.default, + Inline: inline_1.default, + Text: text_1.default, + Attributor: { + Attribute: attributor_1.default, + Class: class_1.default, + Style: style_1.default, + Store: store_1.default, + }, +}; +exports.default = Parchment; + + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ParchmentError = /** @class */ (function (_super) { + __extends(ParchmentError, _super); + function ParchmentError(message) { + var _this = this; + message = '[Parchment] ' + message; + _this = _super.call(this, message) || this; + _this.message = message; + _this.name = _this.constructor.name; + return _this; + } + return ParchmentError; +}(Error)); +exports.ParchmentError = ParchmentError; +var attributes = {}; +var classes = {}; +var tags = {}; +var types = {}; +exports.DATA_KEY = '__blot'; +var Scope; +(function (Scope) { + Scope[Scope["TYPE"] = 3] = "TYPE"; + Scope[Scope["LEVEL"] = 12] = "LEVEL"; + Scope[Scope["ATTRIBUTE"] = 13] = "ATTRIBUTE"; + Scope[Scope["BLOT"] = 14] = "BLOT"; + Scope[Scope["INLINE"] = 7] = "INLINE"; + Scope[Scope["BLOCK"] = 11] = "BLOCK"; + Scope[Scope["BLOCK_BLOT"] = 10] = "BLOCK_BLOT"; + Scope[Scope["INLINE_BLOT"] = 6] = "INLINE_BLOT"; + Scope[Scope["BLOCK_ATTRIBUTE"] = 9] = "BLOCK_ATTRIBUTE"; + Scope[Scope["INLINE_ATTRIBUTE"] = 5] = "INLINE_ATTRIBUTE"; + Scope[Scope["ANY"] = 15] = "ANY"; +})(Scope = exports.Scope || (exports.Scope = {})); +function create(input, value) { + var match = query(input); + if (match == null) { + throw new ParchmentError("Unable to create " + input + " blot"); + } + var BlotClass = match; + var node = + // @ts-ignore + input instanceof Node || input['nodeType'] === Node.TEXT_NODE ? input : BlotClass.create(value); + return new BlotClass(node, value); +} +exports.create = create; +function find(node, bubble) { + if (bubble === void 0) { bubble = false; } + if (node == null) + return null; + // @ts-ignore + if (node[exports.DATA_KEY] != null) + return node[exports.DATA_KEY].blot; + if (bubble) + return find(node.parentNode, bubble); + return null; +} +exports.find = find; +function query(query, scope) { + if (scope === void 0) { scope = Scope.ANY; } + var match; + if (typeof query === 'string') { + match = types[query] || attributes[query]; + // @ts-ignore + } + else if (query instanceof Text || query['nodeType'] === Node.TEXT_NODE) { + match = types['text']; + } + else if (typeof query === 'number') { + if (query & Scope.LEVEL & Scope.BLOCK) { + match = types['block']; + } + else if (query & Scope.LEVEL & Scope.INLINE) { + match = types['inline']; + } + } + else if (query instanceof HTMLElement) { + var names = (query.getAttribute('class') || '').split(/\s+/); + for (var i in names) { + match = classes[names[i]]; + if (match) + break; + } + match = match || tags[query.tagName]; + } + if (match == null) + return null; + // @ts-ignore + if (scope & Scope.LEVEL & match.scope && scope & Scope.TYPE & match.scope) + return match; + return null; +} +exports.query = query; +function register() { + var Definitions = []; + for (var _i = 0; _i < arguments.length; _i++) { + Definitions[_i] = arguments[_i]; + } + if (Definitions.length > 1) { + return Definitions.map(function (d) { + return register(d); + }); + } + var Definition = Definitions[0]; + if (typeof Definition.blotName !== 'string' && typeof Definition.attrName !== 'string') { + throw new ParchmentError('Invalid definition'); + } + else if (Definition.blotName === 'abstract') { + throw new ParchmentError('Cannot register abstract class'); + } + types[Definition.blotName || Definition.attrName] = Definition; + if (typeof Definition.keyName === 'string') { + attributes[Definition.keyName] = Definition; + } + else { + if (Definition.className != null) { + classes[Definition.className] = Definition; + } + if (Definition.tagName != null) { + if (Array.isArray(Definition.tagName)) { + Definition.tagName = Definition.tagName.map(function (tagName) { + return tagName.toUpperCase(); + }); + } + else { + Definition.tagName = Definition.tagName.toUpperCase(); + } + var tagNames = Array.isArray(Definition.tagName) ? Definition.tagName : [Definition.tagName]; + tagNames.forEach(function (tag) { + if (tags[tag] == null || Definition.className == null) { + tags[tag] = Definition; + } + }); + } + } + return Definition; +} +exports.register = register; + + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + +var diff = __webpack_require__(51); +var equal = __webpack_require__(11); +var extend = __webpack_require__(3); +var op = __webpack_require__(20); + + +var NULL_CHARACTER = String.fromCharCode(0); // Placeholder char for embed in diff() + + +var Delta = function (ops) { + // Assume we are given a well formed ops + if (Array.isArray(ops)) { + this.ops = ops; + } else if (ops != null && Array.isArray(ops.ops)) { + this.ops = ops.ops; + } else { + this.ops = []; + } +}; + + +Delta.prototype.insert = function (text, attributes) { + var newOp = {}; + if (text.length === 0) return this; + newOp.insert = text; + if (attributes != null && typeof attributes === 'object' && Object.keys(attributes).length > 0) { + newOp.attributes = attributes; + } + return this.push(newOp); +}; + +Delta.prototype['delete'] = function (length) { + if (length <= 0) return this; + return this.push({ 'delete': length }); +}; + +Delta.prototype.retain = function (length, attributes) { + if (length <= 0) return this; + var newOp = { retain: length }; + if (attributes != null && typeof attributes === 'object' && Object.keys(attributes).length > 0) { + newOp.attributes = attributes; + } + return this.push(newOp); +}; + +Delta.prototype.push = function (newOp) { + var index = this.ops.length; + var lastOp = this.ops[index - 1]; + newOp = extend(true, {}, newOp); + if (typeof lastOp === 'object') { + if (typeof newOp['delete'] === 'number' && typeof lastOp['delete'] === 'number') { + this.ops[index - 1] = { 'delete': lastOp['delete'] + newOp['delete'] }; + return this; + } + // Since it does not matter if we insert before or after deleting at the same index, + // always prefer to insert first + if (typeof lastOp['delete'] === 'number' && newOp.insert != null) { + index -= 1; + lastOp = this.ops[index - 1]; + if (typeof lastOp !== 'object') { + this.ops.unshift(newOp); + return this; + } + } + if (equal(newOp.attributes, lastOp.attributes)) { + if (typeof newOp.insert === 'string' && typeof lastOp.insert === 'string') { + this.ops[index - 1] = { insert: lastOp.insert + newOp.insert }; + if (typeof newOp.attributes === 'object') this.ops[index - 1].attributes = newOp.attributes + return this; + } else if (typeof newOp.retain === 'number' && typeof lastOp.retain === 'number') { + this.ops[index - 1] = { retain: lastOp.retain + newOp.retain }; + if (typeof newOp.attributes === 'object') this.ops[index - 1].attributes = newOp.attributes + return this; + } + } + } + if (index === this.ops.length) { + this.ops.push(newOp); + } else { + this.ops.splice(index, 0, newOp); + } + return this; +}; + +Delta.prototype.chop = function () { + var lastOp = this.ops[this.ops.length - 1]; + if (lastOp && lastOp.retain && !lastOp.attributes) { + this.ops.pop(); + } + return this; +}; + +Delta.prototype.filter = function (predicate) { + return this.ops.filter(predicate); +}; + +Delta.prototype.forEach = function (predicate) { + this.ops.forEach(predicate); +}; + +Delta.prototype.map = function (predicate) { + return this.ops.map(predicate); +}; + +Delta.prototype.partition = function (predicate) { + var passed = [], failed = []; + this.forEach(function(op) { + var target = predicate(op) ? passed : failed; + target.push(op); + }); + return [passed, failed]; +}; + +Delta.prototype.reduce = function (predicate, initial) { + return this.ops.reduce(predicate, initial); +}; + +Delta.prototype.changeLength = function () { + return this.reduce(function (length, elem) { + if (elem.insert) { + return length + op.length(elem); + } else if (elem.delete) { + return length - elem.delete; + } + return length; + }, 0); +}; + +Delta.prototype.length = function () { + return this.reduce(function (length, elem) { + return length + op.length(elem); + }, 0); +}; + +Delta.prototype.slice = function (start, end) { + start = start || 0; + if (typeof end !== 'number') end = Infinity; + var ops = []; + var iter = op.iterator(this.ops); + var index = 0; + while (index < end && iter.hasNext()) { + var nextOp; + if (index < start) { + nextOp = iter.next(start - index); + } else { + nextOp = iter.next(end - index); + ops.push(nextOp); + } + index += op.length(nextOp); + } + return new Delta(ops); +}; + + +Delta.prototype.compose = function (other) { + var thisIter = op.iterator(this.ops); + var otherIter = op.iterator(other.ops); + var delta = new Delta(); + while (thisIter.hasNext() || otherIter.hasNext()) { + if (otherIter.peekType() === 'insert') { + delta.push(otherIter.next()); + } else if (thisIter.peekType() === 'delete') { + delta.push(thisIter.next()); + } else { + var length = Math.min(thisIter.peekLength(), otherIter.peekLength()); + var thisOp = thisIter.next(length); + var otherOp = otherIter.next(length); + if (typeof otherOp.retain === 'number') { + var newOp = {}; + if (typeof thisOp.retain === 'number') { + newOp.retain = length; + } else { + newOp.insert = thisOp.insert; + } + // Preserve null when composing with a retain, otherwise remove it for inserts + var attributes = op.attributes.compose(thisOp.attributes, otherOp.attributes, typeof thisOp.retain === 'number'); + if (attributes) newOp.attributes = attributes; + delta.push(newOp); + // Other op should be delete, we could be an insert or retain + // Insert + delete cancels out + } else if (typeof otherOp['delete'] === 'number' && typeof thisOp.retain === 'number') { + delta.push(otherOp); + } + } + } + return delta.chop(); +}; + +Delta.prototype.concat = function (other) { + var delta = new Delta(this.ops.slice()); + if (other.ops.length > 0) { + delta.push(other.ops[0]); + delta.ops = delta.ops.concat(other.ops.slice(1)); + } + return delta; +}; + +Delta.prototype.diff = function (other, index) { + if (this.ops === other.ops) { + return new Delta(); + } + var strings = [this, other].map(function (delta) { + return delta.map(function (op) { + if (op.insert != null) { + return typeof op.insert === 'string' ? op.insert : NULL_CHARACTER; + } + var prep = (delta === other) ? 'on' : 'with'; + throw new Error('diff() called ' + prep + ' non-document'); + }).join(''); + }); + var delta = new Delta(); + var diffResult = diff(strings[0], strings[1], index); + var thisIter = op.iterator(this.ops); + var otherIter = op.iterator(other.ops); + diffResult.forEach(function (component) { + var length = component[1].length; + while (length > 0) { + var opLength = 0; + switch (component[0]) { + case diff.INSERT: + opLength = Math.min(otherIter.peekLength(), length); + delta.push(otherIter.next(opLength)); + break; + case diff.DELETE: + opLength = Math.min(length, thisIter.peekLength()); + thisIter.next(opLength); + delta['delete'](opLength); + break; + case diff.EQUAL: + opLength = Math.min(thisIter.peekLength(), otherIter.peekLength(), length); + var thisOp = thisIter.next(opLength); + var otherOp = otherIter.next(opLength); + if (equal(thisOp.insert, otherOp.insert)) { + delta.retain(opLength, op.attributes.diff(thisOp.attributes, otherOp.attributes)); + } else { + delta.push(otherOp)['delete'](opLength); + } + break; + } + length -= opLength; + } + }); + return delta.chop(); +}; + +Delta.prototype.eachLine = function (predicate, newline) { + newline = newline || '\n'; + var iter = op.iterator(this.ops); + var line = new Delta(); + var i = 0; + while (iter.hasNext()) { + if (iter.peekType() !== 'insert') return; + var thisOp = iter.peek(); + var start = op.length(thisOp) - iter.peekLength(); + var index = typeof thisOp.insert === 'string' ? + thisOp.insert.indexOf(newline, start) - start : -1; + if (index < 0) { + line.push(iter.next()); + } else if (index > 0) { + line.push(iter.next(index)); + } else { + if (predicate(line, iter.next(1).attributes || {}, i) === false) { + return; + } + i += 1; + line = new Delta(); + } + } + if (line.length() > 0) { + predicate(line, {}, i); + } +}; + +Delta.prototype.transform = function (other, priority) { + priority = !!priority; + if (typeof other === 'number') { + return this.transformPosition(other, priority); + } + var thisIter = op.iterator(this.ops); + var otherIter = op.iterator(other.ops); + var delta = new Delta(); + while (thisIter.hasNext() || otherIter.hasNext()) { + if (thisIter.peekType() === 'insert' && (priority || otherIter.peekType() !== 'insert')) { + delta.retain(op.length(thisIter.next())); + } else if (otherIter.peekType() === 'insert') { + delta.push(otherIter.next()); + } else { + var length = Math.min(thisIter.peekLength(), otherIter.peekLength()); + var thisOp = thisIter.next(length); + var otherOp = otherIter.next(length); + if (thisOp['delete']) { + // Our delete either makes their delete redundant or removes their retain + continue; + } else if (otherOp['delete']) { + delta.push(otherOp); + } else { + // We retain either their retain or insert + delta.retain(length, op.attributes.transform(thisOp.attributes, otherOp.attributes, priority)); + } + } + } + return delta.chop(); +}; + +Delta.prototype.transformPosition = function (index, priority) { + priority = !!priority; + var thisIter = op.iterator(this.ops); + var offset = 0; + while (thisIter.hasNext() && offset <= index) { + var length = thisIter.peekLength(); + var nextType = thisIter.peekType(); + thisIter.next(); + if (nextType === 'delete') { + index -= Math.min(length, index - offset); + continue; + } else if (nextType === 'insert' && (offset < index || !priority)) { + index += length; + } + offset += length; + } + return index; +}; + + +module.exports = Delta; + + +/***/ }), +/* 3 */ +/***/ (function(module, exports) { + +'use strict'; + +var hasOwn = Object.prototype.hasOwnProperty; +var toStr = Object.prototype.toString; + +var isArray = function isArray(arr) { + if (typeof Array.isArray === 'function') { + return Array.isArray(arr); + } + + return toStr.call(arr) === '[object Array]'; +}; + +var isPlainObject = function isPlainObject(obj) { + if (!obj || toStr.call(obj) !== '[object Object]') { + return false; + } + + var hasOwnConstructor = hasOwn.call(obj, 'constructor'); + var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf'); + // Not own constructor property must be Object + if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + var key; + for (key in obj) { /**/ } + + return typeof key === 'undefined' || hasOwn.call(obj, key); +}; + +module.exports = function extend() { + var options, name, src, copy, copyIsArray, clone; + var target = arguments[0]; + var i = 1; + var length = arguments.length; + var deep = false; + + // Handle a deep copy situation + if (typeof target === 'boolean') { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + if (target == null || (typeof target !== 'object' && typeof target !== 'function')) { + target = {}; + } + + for (; i < length; ++i) { + options = arguments[i]; + // Only deal with non-null/undefined values + if (options != null) { + // Extend the base object + for (name in options) { + src = target[name]; + copy = options[name]; + + // Prevent never-ending loop + if (target !== copy) { + // Recurse if we're merging plain objects or arrays + if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) { + if (copyIsArray) { + copyIsArray = false; + clone = src && isArray(src) ? src : []; + } else { + clone = src && isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[name] = extend(deep, clone, copy); + + // Don't bring in undefined values + } else if (typeof copy !== 'undefined') { + target[name] = copy; + } + } + } + } + } + + // Return the modified object + return target; +}; + + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.BlockEmbed = exports.bubbleFormats = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _break = __webpack_require__(16); + +var _break2 = _interopRequireDefault(_break); + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var NEWLINE_LENGTH = 1; + +var BlockEmbed = function (_Parchment$Embed) { + _inherits(BlockEmbed, _Parchment$Embed); + + function BlockEmbed() { + _classCallCheck(this, BlockEmbed); + + return _possibleConstructorReturn(this, (BlockEmbed.__proto__ || Object.getPrototypeOf(BlockEmbed)).apply(this, arguments)); + } + + _createClass(BlockEmbed, [{ + key: 'attach', + value: function attach() { + _get(BlockEmbed.prototype.__proto__ || Object.getPrototypeOf(BlockEmbed.prototype), 'attach', this).call(this); + this.attributes = new _parchment2.default.Attributor.Store(this.domNode); + } + }, { + key: 'delta', + value: function delta() { + return new _quillDelta2.default().insert(this.value(), (0, _extend2.default)(this.formats(), this.attributes.values())); + } + }, { + key: 'format', + value: function format(name, value) { + var attribute = _parchment2.default.query(name, _parchment2.default.Scope.BLOCK_ATTRIBUTE); + if (attribute != null) { + this.attributes.attribute(attribute, value); + } + } + }, { + key: 'formatAt', + value: function formatAt(index, length, name, value) { + this.format(name, value); + } + }, { + key: 'insertAt', + value: function insertAt(index, value, def) { + if (typeof value === 'string' && value.endsWith('\n')) { + var block = _parchment2.default.create(Block.blotName); + this.parent.insertBefore(block, index === 0 ? this : this.next); + block.insertAt(0, value.slice(0, -1)); + } else { + _get(BlockEmbed.prototype.__proto__ || Object.getPrototypeOf(BlockEmbed.prototype), 'insertAt', this).call(this, index, value, def); + } + } + }]); + + return BlockEmbed; +}(_parchment2.default.Embed); + +BlockEmbed.scope = _parchment2.default.Scope.BLOCK_BLOT; +// It is important for cursor behavior BlockEmbeds use tags that are block level elements + + +var Block = function (_Parchment$Block) { + _inherits(Block, _Parchment$Block); + + function Block(domNode) { + _classCallCheck(this, Block); + + var _this2 = _possibleConstructorReturn(this, (Block.__proto__ || Object.getPrototypeOf(Block)).call(this, domNode)); + + _this2.cache = {}; + return _this2; + } + + _createClass(Block, [{ + key: 'delta', + value: function delta() { + if (this.cache.delta == null) { + this.cache.delta = this.descendants(_parchment2.default.Leaf).reduce(function (delta, leaf) { + if (leaf.length() === 0) { + return delta; + } else { + return delta.insert(leaf.value(), bubbleFormats(leaf)); + } + }, new _quillDelta2.default()).insert('\n', bubbleFormats(this)); + } + return this.cache.delta; + } + }, { + key: 'deleteAt', + value: function deleteAt(index, length) { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'deleteAt', this).call(this, index, length); + this.cache = {}; + } + }, { + key: 'formatAt', + value: function formatAt(index, length, name, value) { + if (length <= 0) return; + if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK)) { + if (index + length === this.length()) { + this.format(name, value); + } + } else { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'formatAt', this).call(this, index, Math.min(length, this.length() - index - 1), name, value); + } + this.cache = {}; + } + }, { + key: 'insertAt', + value: function insertAt(index, value, def) { + if (def != null) return _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertAt', this).call(this, index, value, def); + if (value.length === 0) return; + var lines = value.split('\n'); + var text = lines.shift(); + if (text.length > 0) { + if (index < this.length() - 1 || this.children.tail == null) { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertAt', this).call(this, Math.min(index, this.length() - 1), text); + } else { + this.children.tail.insertAt(this.children.tail.length(), text); + } + this.cache = {}; + } + var block = this; + lines.reduce(function (index, line) { + block = block.split(index, true); + block.insertAt(0, line); + return line.length; + }, index + text.length); + } + }, { + key: 'insertBefore', + value: function insertBefore(blot, ref) { + var head = this.children.head; + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertBefore', this).call(this, blot, ref); + if (head instanceof _break2.default) { + head.remove(); + } + this.cache = {}; + } + }, { + key: 'length', + value: function length() { + if (this.cache.length == null) { + this.cache.length = _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'length', this).call(this) + NEWLINE_LENGTH; + } + return this.cache.length; + } + }, { + key: 'moveChildren', + value: function moveChildren(target, ref) { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'moveChildren', this).call(this, target, ref); + this.cache = {}; + } + }, { + key: 'optimize', + value: function optimize(context) { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'optimize', this).call(this, context); + this.cache = {}; + } + }, { + key: 'path', + value: function path(index) { + return _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'path', this).call(this, index, true); + } + }, { + key: 'removeChild', + value: function removeChild(child) { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'removeChild', this).call(this, child); + this.cache = {}; + } + }, { + key: 'split', + value: function split(index) { + var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + if (force && (index === 0 || index >= this.length() - NEWLINE_LENGTH)) { + var clone = this.clone(); + if (index === 0) { + this.parent.insertBefore(clone, this); + return this; + } else { + this.parent.insertBefore(clone, this.next); + return clone; + } + } else { + var next = _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'split', this).call(this, index, force); + this.cache = {}; + return next; + } + } + }]); + + return Block; +}(_parchment2.default.Block); + +Block.blotName = 'block'; +Block.tagName = 'P'; +Block.defaultChild = 'break'; +Block.allowedChildren = [_inline2.default, _parchment2.default.Embed, _text2.default]; + +function bubbleFormats(blot) { + var formats = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + if (blot == null) return formats; + if (typeof blot.formats === 'function') { + formats = (0, _extend2.default)(formats, blot.formats()); + } + if (blot.parent == null || blot.parent.blotName == 'scroll' || blot.parent.statics.scope !== blot.statics.scope) { + return formats; + } + return bubbleFormats(blot.parent, formats); +} + +exports.bubbleFormats = bubbleFormats; +exports.BlockEmbed = BlockEmbed; +exports.default = Block; + +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.overload = exports.expandConfig = undefined; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +__webpack_require__(50); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _editor = __webpack_require__(14); + +var _editor2 = _interopRequireDefault(_editor); + +var _emitter3 = __webpack_require__(8); + +var _emitter4 = _interopRequireDefault(_emitter3); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _selection = __webpack_require__(15); + +var _selection2 = _interopRequireDefault(_selection); + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +var _logger = __webpack_require__(10); + +var _logger2 = _interopRequireDefault(_logger); + +var _theme = __webpack_require__(34); + +var _theme2 = _interopRequireDefault(_theme); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var debug = (0, _logger2.default)('quill'); + +var Quill = function () { + _createClass(Quill, null, [{ + key: 'debug', + value: function debug(limit) { + if (limit === true) { + limit = 'log'; + } + _logger2.default.level(limit); + } + }, { + key: 'find', + value: function find(node) { + return node.__quill || _parchment2.default.find(node); + } + }, { + key: 'import', + value: function _import(name) { + if (this.imports[name] == null) { + debug.error('Cannot import ' + name + '. Are you sure it was registered?'); + } + return this.imports[name]; + } + }, { + key: 'register', + value: function register(path, target) { + var _this = this; + + var overwrite = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + + if (typeof path !== 'string') { + var name = path.attrName || path.blotName; + if (typeof name === 'string') { + // register(Blot | Attributor, overwrite) + this.register('formats/' + name, path, target); + } else { + Object.keys(path).forEach(function (key) { + _this.register(key, path[key], target); + }); + } + } else { + if (this.imports[path] != null && !overwrite) { + debug.warn('Overwriting ' + path + ' with', target); + } + this.imports[path] = target; + if ((path.startsWith('blots/') || path.startsWith('formats/')) && target.blotName !== 'abstract') { + _parchment2.default.register(target); + } else if (path.startsWith('modules') && typeof target.register === 'function') { + target.register(); + } + } + } + }]); + + function Quill(container) { + var _this2 = this; + + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + _classCallCheck(this, Quill); + + this.options = expandConfig(container, options); + this.container = this.options.container; + if (this.container == null) { + return debug.error('Invalid Quill container', container); + } + if (this.options.debug) { + Quill.debug(this.options.debug); + } + var html = this.container.innerHTML.trim(); + this.container.classList.add('ql-container'); + this.container.innerHTML = ''; + this.container.__quill = this; + this.root = this.addContainer('ql-editor'); + this.root.classList.add('ql-blank'); + this.root.setAttribute('data-gramm', false); + this.scrollingContainer = this.options.scrollingContainer || this.root; + this.emitter = new _emitter4.default(); + this.scroll = _parchment2.default.create(this.root, { + emitter: this.emitter, + whitelist: this.options.formats + }); + this.editor = new _editor2.default(this.scroll); + this.selection = new _selection2.default(this.scroll, this.emitter); + this.theme = new this.options.theme(this, this.options); + this.keyboard = this.theme.addModule('keyboard'); + this.clipboard = this.theme.addModule('clipboard'); + this.history = this.theme.addModule('history'); + this.theme.init(); + this.emitter.on(_emitter4.default.events.EDITOR_CHANGE, function (type) { + if (type === _emitter4.default.events.TEXT_CHANGE) { + _this2.root.classList.toggle('ql-blank', _this2.editor.isBlank()); + } + }); + this.emitter.on(_emitter4.default.events.SCROLL_UPDATE, function (source, mutations) { + var range = _this2.selection.lastRange; + var index = range && range.length === 0 ? range.index : undefined; + modify.call(_this2, function () { + return _this2.editor.update(null, mutations, index); + }, source); + }); + var contents = this.clipboard.convert('
' + html + '


'); + this.setContents(contents); + this.history.clear(); + if (this.options.placeholder) { + this.root.setAttribute('data-placeholder', this.options.placeholder); + } + if (this.options.readOnly) { + this.disable(); + } + } + + _createClass(Quill, [{ + key: 'addContainer', + value: function addContainer(container) { + var refNode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; + + if (typeof container === 'string') { + var className = container; + container = document.createElement('div'); + container.classList.add(className); + } + this.container.insertBefore(container, refNode); + return container; + } + }, { + key: 'blur', + value: function blur() { + this.selection.setRange(null); + } + }, { + key: 'deleteText', + value: function deleteText(index, length, source) { + var _this3 = this; + + var _overload = overload(index, length, source); + + var _overload2 = _slicedToArray(_overload, 4); + + index = _overload2[0]; + length = _overload2[1]; + source = _overload2[3]; + + return modify.call(this, function () { + return _this3.editor.deleteText(index, length); + }, source, index, -1 * length); + } + }, { + key: 'disable', + value: function disable() { + this.enable(false); + } + }, { + key: 'enable', + value: function enable() { + var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; + + this.scroll.enable(enabled); + this.container.classList.toggle('ql-disabled', !enabled); + } + }, { + key: 'focus', + value: function focus() { + var scrollTop = this.scrollingContainer.scrollTop; + this.selection.focus(); + this.scrollingContainer.scrollTop = scrollTop; + this.scrollIntoView(); + } + }, { + key: 'format', + value: function format(name, value) { + var _this4 = this; + + var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _emitter4.default.sources.API; + + return modify.call(this, function () { + var range = _this4.getSelection(true); + var change = new _quillDelta2.default(); + if (range == null) { + return change; + } else if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK)) { + change = _this4.editor.formatLine(range.index, range.length, _defineProperty({}, name, value)); + } else if (range.length === 0) { + _this4.selection.format(name, value); + return change; + } else { + change = _this4.editor.formatText(range.index, range.length, _defineProperty({}, name, value)); + } + _this4.setSelection(range, _emitter4.default.sources.SILENT); + return change; + }, source); + } + }, { + key: 'formatLine', + value: function formatLine(index, length, name, value, source) { + var _this5 = this; + + var formats = void 0; + + var _overload3 = overload(index, length, name, value, source); + + var _overload4 = _slicedToArray(_overload3, 4); + + index = _overload4[0]; + length = _overload4[1]; + formats = _overload4[2]; + source = _overload4[3]; + + return modify.call(this, function () { + return _this5.editor.formatLine(index, length, formats); + }, source, index, 0); + } + }, { + key: 'formatText', + value: function formatText(index, length, name, value, source) { + var _this6 = this; + + var formats = void 0; + + var _overload5 = overload(index, length, name, value, source); + + var _overload6 = _slicedToArray(_overload5, 4); + + index = _overload6[0]; + length = _overload6[1]; + formats = _overload6[2]; + source = _overload6[3]; + + return modify.call(this, function () { + return _this6.editor.formatText(index, length, formats); + }, source, index, 0); + } + }, { + key: 'getBounds', + value: function getBounds(index) { + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + + var bounds = void 0; + if (typeof index === 'number') { + bounds = this.selection.getBounds(index, length); + } else { + bounds = this.selection.getBounds(index.index, index.length); + } + var containerBounds = this.container.getBoundingClientRect(); + return { + bottom: bounds.bottom - containerBounds.top, + height: bounds.height, + left: bounds.left - containerBounds.left, + right: bounds.right - containerBounds.left, + top: bounds.top - containerBounds.top, + width: bounds.width + }; + } + }, { + key: 'getContents', + value: function getContents() { + var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.getLength() - index; + + var _overload7 = overload(index, length); + + var _overload8 = _slicedToArray(_overload7, 2); + + index = _overload8[0]; + length = _overload8[1]; + + return this.editor.getContents(index, length); + } + }, { + key: 'getFormat', + value: function getFormat() { + var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.getSelection(true); + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + + if (typeof index === 'number') { + return this.editor.getFormat(index, length); + } else { + return this.editor.getFormat(index.index, index.length); + } + } + }, { + key: 'getIndex', + value: function getIndex(blot) { + return blot.offset(this.scroll); + } + }, { + key: 'getLength', + value: function getLength() { + return this.scroll.length(); + } + }, { + key: 'getLeaf', + value: function getLeaf(index) { + return this.scroll.leaf(index); + } + }, { + key: 'getLine', + value: function getLine(index) { + return this.scroll.line(index); + } + }, { + key: 'getLines', + value: function getLines() { + var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Number.MAX_VALUE; + + if (typeof index !== 'number') { + return this.scroll.lines(index.index, index.length); + } else { + return this.scroll.lines(index, length); + } + } + }, { + key: 'getModule', + value: function getModule(name) { + return this.theme.modules[name]; + } + }, { + key: 'getSelection', + value: function getSelection() { + var focus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + + if (focus) this.focus(); + this.update(); // Make sure we access getRange with editor in consistent state + return this.selection.getRange()[0]; + } + }, { + key: 'getText', + value: function getText() { + var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.getLength() - index; + + var _overload9 = overload(index, length); + + var _overload10 = _slicedToArray(_overload9, 2); + + index = _overload10[0]; + length = _overload10[1]; + + return this.editor.getText(index, length); + } + }, { + key: 'hasFocus', + value: function hasFocus() { + return this.selection.hasFocus(); + } + }, { + key: 'insertEmbed', + value: function insertEmbed(index, embed, value) { + var _this7 = this; + + var source = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : Quill.sources.API; + + return modify.call(this, function () { + return _this7.editor.insertEmbed(index, embed, value); + }, source, index); + } + }, { + key: 'insertText', + value: function insertText(index, text, name, value, source) { + var _this8 = this; + + var formats = void 0; + + var _overload11 = overload(index, 0, name, value, source); + + var _overload12 = _slicedToArray(_overload11, 4); + + index = _overload12[0]; + formats = _overload12[2]; + source = _overload12[3]; + + return modify.call(this, function () { + return _this8.editor.insertText(index, text, formats); + }, source, index, text.length); + } + }, { + key: 'isEnabled', + value: function isEnabled() { + return !this.container.classList.contains('ql-disabled'); + } + }, { + key: 'off', + value: function off() { + return this.emitter.off.apply(this.emitter, arguments); + } + }, { + key: 'on', + value: function on() { + return this.emitter.on.apply(this.emitter, arguments); + } + }, { + key: 'once', + value: function once() { + return this.emitter.once.apply(this.emitter, arguments); + } + }, { + key: 'pasteHTML', + value: function pasteHTML(index, html, source) { + this.clipboard.dangerouslyPasteHTML(index, html, source); + } + }, { + key: 'removeFormat', + value: function removeFormat(index, length, source) { + var _this9 = this; + + var _overload13 = overload(index, length, source); + + var _overload14 = _slicedToArray(_overload13, 4); + + index = _overload14[0]; + length = _overload14[1]; + source = _overload14[3]; + + return modify.call(this, function () { + return _this9.editor.removeFormat(index, length); + }, source, index); + } + }, { + key: 'scrollIntoView', + value: function scrollIntoView() { + this.selection.scrollIntoView(this.scrollingContainer); + } + }, { + key: 'setContents', + value: function setContents(delta) { + var _this10 = this; + + var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API; + + return modify.call(this, function () { + delta = new _quillDelta2.default(delta); + var length = _this10.getLength(); + var deleted = _this10.editor.deleteText(0, length); + var applied = _this10.editor.applyDelta(delta); + var lastOp = applied.ops[applied.ops.length - 1]; + if (lastOp != null && typeof lastOp.insert === 'string' && lastOp.insert[lastOp.insert.length - 1] === '\n') { + _this10.editor.deleteText(_this10.getLength() - 1, 1); + applied.delete(1); + } + var ret = deleted.compose(applied); + return ret; + }, source); + } + }, { + key: 'setSelection', + value: function setSelection(index, length, source) { + if (index == null) { + this.selection.setRange(null, length || Quill.sources.API); + } else { + var _overload15 = overload(index, length, source); + + var _overload16 = _slicedToArray(_overload15, 4); + + index = _overload16[0]; + length = _overload16[1]; + source = _overload16[3]; + + this.selection.setRange(new _selection.Range(index, length), source); + if (source !== _emitter4.default.sources.SILENT) { + this.selection.scrollIntoView(this.scrollingContainer); + } + } + } + }, { + key: 'setText', + value: function setText(text) { + var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API; + + var delta = new _quillDelta2.default().insert(text); + return this.setContents(delta, source); + } + }, { + key: 'update', + value: function update() { + var source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _emitter4.default.sources.USER; + + var change = this.scroll.update(source); // Will update selection before selection.update() does if text changes + this.selection.update(source); + return change; + } + }, { + key: 'updateContents', + value: function updateContents(delta) { + var _this11 = this; + + var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API; + + return modify.call(this, function () { + delta = new _quillDelta2.default(delta); + return _this11.editor.applyDelta(delta, source); + }, source, true); + } + }]); + + return Quill; +}(); + +Quill.DEFAULTS = { + bounds: null, + formats: null, + modules: {}, + placeholder: '', + readOnly: false, + scrollingContainer: null, + strict: true, + theme: 'default' +}; +Quill.events = _emitter4.default.events; +Quill.sources = _emitter4.default.sources; +// eslint-disable-next-line no-undef +Quill.version = false ? 'dev' : "1.3.6"; + +Quill.imports = { + 'delta': _quillDelta2.default, + 'parchment': _parchment2.default, + 'core/module': _module2.default, + 'core/theme': _theme2.default +}; + +function expandConfig(container, userConfig) { + userConfig = (0, _extend2.default)(true, { + container: container, + modules: { + clipboard: true, + keyboard: true, + history: true + } + }, userConfig); + if (!userConfig.theme || userConfig.theme === Quill.DEFAULTS.theme) { + userConfig.theme = _theme2.default; + } else { + userConfig.theme = Quill.import('themes/' + userConfig.theme); + if (userConfig.theme == null) { + throw new Error('Invalid theme ' + userConfig.theme + '. Did you register it?'); + } + } + var themeConfig = (0, _extend2.default)(true, {}, userConfig.theme.DEFAULTS); + [themeConfig, userConfig].forEach(function (config) { + config.modules = config.modules || {}; + Object.keys(config.modules).forEach(function (module) { + if (config.modules[module] === true) { + config.modules[module] = {}; + } + }); + }); + var moduleNames = Object.keys(themeConfig.modules).concat(Object.keys(userConfig.modules)); + var moduleConfig = moduleNames.reduce(function (config, name) { + var moduleClass = Quill.import('modules/' + name); + if (moduleClass == null) { + debug.error('Cannot load ' + name + ' module. Are you sure you registered it?'); + } else { + config[name] = moduleClass.DEFAULTS || {}; + } + return config; + }, {}); + // Special case toolbar shorthand + if (userConfig.modules != null && userConfig.modules.toolbar && userConfig.modules.toolbar.constructor !== Object) { + userConfig.modules.toolbar = { + container: userConfig.modules.toolbar + }; + } + userConfig = (0, _extend2.default)(true, {}, Quill.DEFAULTS, { modules: moduleConfig }, themeConfig, userConfig); + ['bounds', 'container', 'scrollingContainer'].forEach(function (key) { + if (typeof userConfig[key] === 'string') { + userConfig[key] = document.querySelector(userConfig[key]); + } + }); + userConfig.modules = Object.keys(userConfig.modules).reduce(function (config, name) { + if (userConfig.modules[name]) { + config[name] = userConfig.modules[name]; + } + return config; + }, {}); + return userConfig; +} + +// Handle selection preservation and TEXT_CHANGE emission +// common to modification APIs +function modify(modifier, source, index, shift) { + if (this.options.strict && !this.isEnabled() && source === _emitter4.default.sources.USER) { + return new _quillDelta2.default(); + } + var range = index == null ? null : this.getSelection(); + var oldDelta = this.editor.delta; + var change = modifier(); + if (range != null) { + if (index === true) index = range.index; + if (shift == null) { + range = shiftRange(range, change, source); + } else if (shift !== 0) { + range = shiftRange(range, index, shift, source); + } + this.setSelection(range, _emitter4.default.sources.SILENT); + } + if (change.length() > 0) { + var _emitter; + + var args = [_emitter4.default.events.TEXT_CHANGE, change, oldDelta, source]; + (_emitter = this.emitter).emit.apply(_emitter, [_emitter4.default.events.EDITOR_CHANGE].concat(args)); + if (source !== _emitter4.default.sources.SILENT) { + var _emitter2; + + (_emitter2 = this.emitter).emit.apply(_emitter2, args); + } + } + return change; +} + +function overload(index, length, name, value, source) { + var formats = {}; + if (typeof index.index === 'number' && typeof index.length === 'number') { + // Allow for throwaway end (used by insertText/insertEmbed) + if (typeof length !== 'number') { + source = value, value = name, name = length, length = index.length, index = index.index; + } else { + length = index.length, index = index.index; + } + } else if (typeof length !== 'number') { + source = value, value = name, name = length, length = 0; + } + // Handle format being object, two format name/value strings or excluded + if ((typeof name === 'undefined' ? 'undefined' : _typeof(name)) === 'object') { + formats = name; + source = value; + } else if (typeof name === 'string') { + if (value != null) { + formats[name] = value; + } else { + source = name; + } + } + // Handle optional source + source = source || _emitter4.default.sources.API; + return [index, length, formats, source]; +} + +function shiftRange(range, index, length, source) { + if (range == null) return null; + var start = void 0, + end = void 0; + if (index instanceof _quillDelta2.default) { + var _map = [range.index, range.index + range.length].map(function (pos) { + return index.transformPosition(pos, source !== _emitter4.default.sources.USER); + }); + + var _map2 = _slicedToArray(_map, 2); + + start = _map2[0]; + end = _map2[1]; + } else { + var _map3 = [range.index, range.index + range.length].map(function (pos) { + if (pos < index || pos === index && source === _emitter4.default.sources.USER) return pos; + if (length >= 0) { + return pos + length; + } else { + return Math.max(index, pos + length); + } + }); + + var _map4 = _slicedToArray(_map3, 2); + + start = _map4[0]; + end = _map4[1]; + } + return new _selection.Range(start, end - start); +} + +exports.expandConfig = expandConfig; +exports.overload = overload; +exports.default = Quill; + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Inline = function (_Parchment$Inline) { + _inherits(Inline, _Parchment$Inline); + + function Inline() { + _classCallCheck(this, Inline); + + return _possibleConstructorReturn(this, (Inline.__proto__ || Object.getPrototypeOf(Inline)).apply(this, arguments)); + } + + _createClass(Inline, [{ + key: 'formatAt', + value: function formatAt(index, length, name, value) { + if (Inline.compare(this.statics.blotName, name) < 0 && _parchment2.default.query(name, _parchment2.default.Scope.BLOT)) { + var blot = this.isolate(index, length); + if (value) { + blot.wrap(name, value); + } + } else { + _get(Inline.prototype.__proto__ || Object.getPrototypeOf(Inline.prototype), 'formatAt', this).call(this, index, length, name, value); + } + } + }, { + key: 'optimize', + value: function optimize(context) { + _get(Inline.prototype.__proto__ || Object.getPrototypeOf(Inline.prototype), 'optimize', this).call(this, context); + if (this.parent instanceof Inline && Inline.compare(this.statics.blotName, this.parent.statics.blotName) > 0) { + var parent = this.parent.isolate(this.offset(), this.length()); + this.moveChildren(parent); + parent.wrap(this); + } + } + }], [{ + key: 'compare', + value: function compare(self, other) { + var selfIndex = Inline.order.indexOf(self); + var otherIndex = Inline.order.indexOf(other); + if (selfIndex >= 0 || otherIndex >= 0) { + return selfIndex - otherIndex; + } else if (self === other) { + return 0; + } else if (self < other) { + return -1; + } else { + return 1; + } + } + }]); + + return Inline; +}(_parchment2.default.Inline); + +Inline.allowedChildren = [Inline, _parchment2.default.Embed, _text2.default]; +// Lower index means deeper in the DOM tree, since not found (-1) is for embeds +Inline.order = ['cursor', 'inline', // Must be lower +'underline', 'strike', 'italic', 'bold', 'script', 'link', 'code' // Must be higher +]; + +exports.default = Inline; + +/***/ }), +/* 7 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var TextBlot = function (_Parchment$Text) { + _inherits(TextBlot, _Parchment$Text); + + function TextBlot() { + _classCallCheck(this, TextBlot); + + return _possibleConstructorReturn(this, (TextBlot.__proto__ || Object.getPrototypeOf(TextBlot)).apply(this, arguments)); + } + + return TextBlot; +}(_parchment2.default.Text); + +exports.default = TextBlot; + +/***/ }), +/* 8 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _eventemitter = __webpack_require__(54); + +var _eventemitter2 = _interopRequireDefault(_eventemitter); + +var _logger = __webpack_require__(10); + +var _logger2 = _interopRequireDefault(_logger); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var debug = (0, _logger2.default)('quill:events'); + +var EVENTS = ['selectionchange', 'mousedown', 'mouseup', 'click']; + +EVENTS.forEach(function (eventName) { + document.addEventListener(eventName, function () { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + [].slice.call(document.querySelectorAll('.ql-container')).forEach(function (node) { + // TODO use WeakMap + if (node.__quill && node.__quill.emitter) { + var _node$__quill$emitter; + + (_node$__quill$emitter = node.__quill.emitter).handleDOM.apply(_node$__quill$emitter, args); + } + }); + }); +}); + +var Emitter = function (_EventEmitter) { + _inherits(Emitter, _EventEmitter); + + function Emitter() { + _classCallCheck(this, Emitter); + + var _this = _possibleConstructorReturn(this, (Emitter.__proto__ || Object.getPrototypeOf(Emitter)).call(this)); + + _this.listeners = {}; + _this.on('error', debug.error); + return _this; + } + + _createClass(Emitter, [{ + key: 'emit', + value: function emit() { + debug.log.apply(debug, arguments); + _get(Emitter.prototype.__proto__ || Object.getPrototypeOf(Emitter.prototype), 'emit', this).apply(this, arguments); + } + }, { + key: 'handleDOM', + value: function handleDOM(event) { + for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + args[_key2 - 1] = arguments[_key2]; + } + + (this.listeners[event.type] || []).forEach(function (_ref) { + var node = _ref.node, + handler = _ref.handler; + + if (event.target === node || node.contains(event.target)) { + handler.apply(undefined, [event].concat(args)); + } + }); + } + }, { + key: 'listenDOM', + value: function listenDOM(eventName, node, handler) { + if (!this.listeners[eventName]) { + this.listeners[eventName] = []; + } + this.listeners[eventName].push({ node: node, handler: handler }); + } + }]); + + return Emitter; +}(_eventemitter2.default); + +Emitter.events = { + EDITOR_CHANGE: 'editor-change', + SCROLL_BEFORE_UPDATE: 'scroll-before-update', + SCROLL_OPTIMIZE: 'scroll-optimize', + SCROLL_UPDATE: 'scroll-update', + SELECTION_CHANGE: 'selection-change', + TEXT_CHANGE: 'text-change' +}; +Emitter.sources = { + API: 'api', + SILENT: 'silent', + USER: 'user' +}; + +exports.default = Emitter; + +/***/ }), +/* 9 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Module = function Module(quill) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + _classCallCheck(this, Module); + + this.quill = quill; + this.options = options; +}; + +Module.DEFAULTS = {}; + +exports.default = Module; + +/***/ }), +/* 10 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +var levels = ['error', 'warn', 'log', 'info']; +var level = 'warn'; + +function debug(method) { + if (levels.indexOf(method) <= levels.indexOf(level)) { + var _console; + + for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + (_console = console)[method].apply(_console, args); // eslint-disable-line no-console + } +} + +function namespace(ns) { + return levels.reduce(function (logger, method) { + logger[method] = debug.bind(console, method, ns); + return logger; + }, {}); +} + +debug.level = namespace.level = function (newLevel) { + level = newLevel; +}; + +exports.default = namespace; + +/***/ }), +/* 11 */ +/***/ (function(module, exports, __webpack_require__) { + +var pSlice = Array.prototype.slice; +var objectKeys = __webpack_require__(52); +var isArguments = __webpack_require__(53); + +var deepEqual = module.exports = function (actual, expected, opts) { + if (!opts) opts = {}; + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + + } else if (actual instanceof Date && expected instanceof Date) { + return actual.getTime() === expected.getTime(); + + // 7.3. Other pairs that do not both pass typeof value == 'object', + // equivalence is determined by ==. + } else if (!actual || !expected || typeof actual != 'object' && typeof expected != 'object') { + return opts.strict ? actual === expected : actual == expected; + + // 7.4. For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected, opts); + } +} + +function isUndefinedOrNull(value) { + return value === null || value === undefined; +} + +function isBuffer (x) { + if (!x || typeof x !== 'object' || typeof x.length !== 'number') return false; + if (typeof x.copy !== 'function' || typeof x.slice !== 'function') { + return false; + } + if (x.length > 0 && typeof x[0] !== 'number') return false; + return true; +} + +function objEquiv(a, b, opts) { + var i, key; + if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) + return false; + // an identical 'prototype' property. + if (a.prototype !== b.prototype) return false; + //~~~I've managed to break Object.keys through screwy arguments passing. + // Converting to array solves the problem. + if (isArguments(a)) { + if (!isArguments(b)) { + return false; + } + a = pSlice.call(a); + b = pSlice.call(b); + return deepEqual(a, b, opts); + } + if (isBuffer(a)) { + if (!isBuffer(b)) { + return false; + } + if (a.length !== b.length) return false; + for (i = 0; i < a.length; i++) { + if (a[i] !== b[i]) return false; + } + return true; + } + try { + var ka = objectKeys(a), + kb = objectKeys(b); + } catch (e) {//happens when one is a string literal and the other isn't + return false; + } + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!deepEqual(a[key], b[key], opts)) return false; + } + return typeof a === typeof b; +} + + +/***/ }), +/* 12 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var Registry = __webpack_require__(1); +var Attributor = /** @class */ (function () { + function Attributor(attrName, keyName, options) { + if (options === void 0) { options = {}; } + this.attrName = attrName; + this.keyName = keyName; + var attributeBit = Registry.Scope.TYPE & Registry.Scope.ATTRIBUTE; + if (options.scope != null) { + // Ignore type bits, force attribute bit + this.scope = (options.scope & Registry.Scope.LEVEL) | attributeBit; + } + else { + this.scope = Registry.Scope.ATTRIBUTE; + } + if (options.whitelist != null) + this.whitelist = options.whitelist; + } + Attributor.keys = function (node) { + return [].map.call(node.attributes, function (item) { + return item.name; + }); + }; + Attributor.prototype.add = function (node, value) { + if (!this.canAdd(node, value)) + return false; + node.setAttribute(this.keyName, value); + return true; + }; + Attributor.prototype.canAdd = function (node, value) { + var match = Registry.query(node, Registry.Scope.BLOT & (this.scope | Registry.Scope.TYPE)); + if (match == null) + return false; + if (this.whitelist == null) + return true; + if (typeof value === 'string') { + return this.whitelist.indexOf(value.replace(/["']/g, '')) > -1; + } + else { + return this.whitelist.indexOf(value) > -1; + } + }; + Attributor.prototype.remove = function (node) { + node.removeAttribute(this.keyName); + }; + Attributor.prototype.value = function (node) { + var value = node.getAttribute(this.keyName); + if (this.canAdd(node, value) && value) { + return value; + } + return ''; + }; + return Attributor; +}()); +exports.default = Attributor; + + +/***/ }), +/* 13 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.Code = undefined; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Code = function (_Inline) { + _inherits(Code, _Inline); + + function Code() { + _classCallCheck(this, Code); + + return _possibleConstructorReturn(this, (Code.__proto__ || Object.getPrototypeOf(Code)).apply(this, arguments)); + } + + return Code; +}(_inline2.default); + +Code.blotName = 'code'; +Code.tagName = 'CODE'; + +var CodeBlock = function (_Block) { + _inherits(CodeBlock, _Block); + + function CodeBlock() { + _classCallCheck(this, CodeBlock); + + return _possibleConstructorReturn(this, (CodeBlock.__proto__ || Object.getPrototypeOf(CodeBlock)).apply(this, arguments)); + } + + _createClass(CodeBlock, [{ + key: 'delta', + value: function delta() { + var _this3 = this; + + var text = this.domNode.textContent; + if (text.endsWith('\n')) { + // Should always be true + text = text.slice(0, -1); + } + return text.split('\n').reduce(function (delta, frag) { + return delta.insert(frag).insert('\n', _this3.formats()); + }, new _quillDelta2.default()); + } + }, { + key: 'format', + value: function format(name, value) { + if (name === this.statics.blotName && value) return; + + var _descendant = this.descendant(_text2.default, this.length() - 1), + _descendant2 = _slicedToArray(_descendant, 1), + text = _descendant2[0]; + + if (text != null) { + text.deleteAt(text.length() - 1, 1); + } + _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'format', this).call(this, name, value); + } + }, { + key: 'formatAt', + value: function formatAt(index, length, name, value) { + if (length === 0) return; + if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK) == null || name === this.statics.blotName && value === this.statics.formats(this.domNode)) { + return; + } + var nextNewline = this.newlineIndex(index); + if (nextNewline < 0 || nextNewline >= index + length) return; + var prevNewline = this.newlineIndex(index, true) + 1; + var isolateLength = nextNewline - prevNewline + 1; + var blot = this.isolate(prevNewline, isolateLength); + var next = blot.next; + blot.format(name, value); + if (next instanceof CodeBlock) { + next.formatAt(0, index - prevNewline + length - isolateLength, name, value); + } + } + }, { + key: 'insertAt', + value: function insertAt(index, value, def) { + if (def != null) return; + + var _descendant3 = this.descendant(_text2.default, index), + _descendant4 = _slicedToArray(_descendant3, 2), + text = _descendant4[0], + offset = _descendant4[1]; + + text.insertAt(offset, value); + } + }, { + key: 'length', + value: function length() { + var length = this.domNode.textContent.length; + if (!this.domNode.textContent.endsWith('\n')) { + return length + 1; + } + return length; + } + }, { + key: 'newlineIndex', + value: function newlineIndex(searchIndex) { + var reverse = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + if (!reverse) { + var offset = this.domNode.textContent.slice(searchIndex).indexOf('\n'); + return offset > -1 ? searchIndex + offset : -1; + } else { + return this.domNode.textContent.slice(0, searchIndex).lastIndexOf('\n'); + } + } + }, { + key: 'optimize', + value: function optimize(context) { + if (!this.domNode.textContent.endsWith('\n')) { + this.appendChild(_parchment2.default.create('text', '\n')); + } + _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'optimize', this).call(this, context); + var next = this.next; + if (next != null && next.prev === this && next.statics.blotName === this.statics.blotName && this.statics.formats(this.domNode) === next.statics.formats(next.domNode)) { + next.optimize(context); + next.moveChildren(this); + next.remove(); + } + } + }, { + key: 'replace', + value: function replace(target) { + _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'replace', this).call(this, target); + [].slice.call(this.domNode.querySelectorAll('*')).forEach(function (node) { + var blot = _parchment2.default.find(node); + if (blot == null) { + node.parentNode.removeChild(node); + } else if (blot instanceof _parchment2.default.Embed) { + blot.remove(); + } else { + blot.unwrap(); + } + }); + } + }], [{ + key: 'create', + value: function create(value) { + var domNode = _get(CodeBlock.__proto__ || Object.getPrototypeOf(CodeBlock), 'create', this).call(this, value); + domNode.setAttribute('spellcheck', false); + return domNode; + } + }, { + key: 'formats', + value: function formats() { + return true; + } + }]); + + return CodeBlock; +}(_block2.default); + +CodeBlock.blotName = 'code-block'; +CodeBlock.tagName = 'PRE'; +CodeBlock.TAB = ' '; + +exports.Code = Code; +exports.default = CodeBlock; + +/***/ }), +/* 14 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _op = __webpack_require__(20); + +var _op2 = _interopRequireDefault(_op); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _code = __webpack_require__(13); + +var _code2 = _interopRequireDefault(_code); + +var _cursor = __webpack_require__(24); + +var _cursor2 = _interopRequireDefault(_cursor); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +var _break = __webpack_require__(16); + +var _break2 = _interopRequireDefault(_break); + +var _clone = __webpack_require__(21); + +var _clone2 = _interopRequireDefault(_clone); + +var _deepEqual = __webpack_require__(11); + +var _deepEqual2 = _interopRequireDefault(_deepEqual); + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var ASCII = /^[ -~]*$/; + +var Editor = function () { + function Editor(scroll) { + _classCallCheck(this, Editor); + + this.scroll = scroll; + this.delta = this.getDelta(); + } + + _createClass(Editor, [{ + key: 'applyDelta', + value: function applyDelta(delta) { + var _this = this; + + var consumeNextNewline = false; + this.scroll.update(); + var scrollLength = this.scroll.length(); + this.scroll.batchStart(); + delta = normalizeDelta(delta); + delta.reduce(function (index, op) { + var length = op.retain || op.delete || op.insert.length || 1; + var attributes = op.attributes || {}; + if (op.insert != null) { + if (typeof op.insert === 'string') { + var text = op.insert; + if (text.endsWith('\n') && consumeNextNewline) { + consumeNextNewline = false; + text = text.slice(0, -1); + } + if (index >= scrollLength && !text.endsWith('\n')) { + consumeNextNewline = true; + } + _this.scroll.insertAt(index, text); + + var _scroll$line = _this.scroll.line(index), + _scroll$line2 = _slicedToArray(_scroll$line, 2), + line = _scroll$line2[0], + offset = _scroll$line2[1]; + + var formats = (0, _extend2.default)({}, (0, _block.bubbleFormats)(line)); + if (line instanceof _block2.default) { + var _line$descendant = line.descendant(_parchment2.default.Leaf, offset), + _line$descendant2 = _slicedToArray(_line$descendant, 1), + leaf = _line$descendant2[0]; + + formats = (0, _extend2.default)(formats, (0, _block.bubbleFormats)(leaf)); + } + attributes = _op2.default.attributes.diff(formats, attributes) || {}; + } else if (_typeof(op.insert) === 'object') { + var key = Object.keys(op.insert)[0]; // There should only be one key + if (key == null) return index; + _this.scroll.insertAt(index, key, op.insert[key]); + } + scrollLength += length; + } + Object.keys(attributes).forEach(function (name) { + _this.scroll.formatAt(index, length, name, attributes[name]); + }); + return index + length; + }, 0); + delta.reduce(function (index, op) { + if (typeof op.delete === 'number') { + _this.scroll.deleteAt(index, op.delete); + return index; + } + return index + (op.retain || op.insert.length || 1); + }, 0); + this.scroll.batchEnd(); + return this.update(delta); + } + }, { + key: 'deleteText', + value: function deleteText(index, length) { + this.scroll.deleteAt(index, length); + return this.update(new _quillDelta2.default().retain(index).delete(length)); + } + }, { + key: 'formatLine', + value: function formatLine(index, length) { + var _this2 = this; + + var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + this.scroll.update(); + Object.keys(formats).forEach(function (format) { + if (_this2.scroll.whitelist != null && !_this2.scroll.whitelist[format]) return; + var lines = _this2.scroll.lines(index, Math.max(length, 1)); + var lengthRemaining = length; + lines.forEach(function (line) { + var lineLength = line.length(); + if (!(line instanceof _code2.default)) { + line.format(format, formats[format]); + } else { + var codeIndex = index - line.offset(_this2.scroll); + var codeLength = line.newlineIndex(codeIndex + lengthRemaining) - codeIndex + 1; + line.formatAt(codeIndex, codeLength, format, formats[format]); + } + lengthRemaining -= lineLength; + }); + }); + this.scroll.optimize(); + return this.update(new _quillDelta2.default().retain(index).retain(length, (0, _clone2.default)(formats))); + } + }, { + key: 'formatText', + value: function formatText(index, length) { + var _this3 = this; + + var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + Object.keys(formats).forEach(function (format) { + _this3.scroll.formatAt(index, length, format, formats[format]); + }); + return this.update(new _quillDelta2.default().retain(index).retain(length, (0, _clone2.default)(formats))); + } + }, { + key: 'getContents', + value: function getContents(index, length) { + return this.delta.slice(index, index + length); + } + }, { + key: 'getDelta', + value: function getDelta() { + return this.scroll.lines().reduce(function (delta, line) { + return delta.concat(line.delta()); + }, new _quillDelta2.default()); + } + }, { + key: 'getFormat', + value: function getFormat(index) { + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + + var lines = [], + leaves = []; + if (length === 0) { + this.scroll.path(index).forEach(function (path) { + var _path = _slicedToArray(path, 1), + blot = _path[0]; + + if (blot instanceof _block2.default) { + lines.push(blot); + } else if (blot instanceof _parchment2.default.Leaf) { + leaves.push(blot); + } + }); + } else { + lines = this.scroll.lines(index, length); + leaves = this.scroll.descendants(_parchment2.default.Leaf, index, length); + } + var formatsArr = [lines, leaves].map(function (blots) { + if (blots.length === 0) return {}; + var formats = (0, _block.bubbleFormats)(blots.shift()); + while (Object.keys(formats).length > 0) { + var blot = blots.shift(); + if (blot == null) return formats; + formats = combineFormats((0, _block.bubbleFormats)(blot), formats); + } + return formats; + }); + return _extend2.default.apply(_extend2.default, formatsArr); + } + }, { + key: 'getText', + value: function getText(index, length) { + return this.getContents(index, length).filter(function (op) { + return typeof op.insert === 'string'; + }).map(function (op) { + return op.insert; + }).join(''); + } + }, { + key: 'insertEmbed', + value: function insertEmbed(index, embed, value) { + this.scroll.insertAt(index, embed, value); + return this.update(new _quillDelta2.default().retain(index).insert(_defineProperty({}, embed, value))); + } + }, { + key: 'insertText', + value: function insertText(index, text) { + var _this4 = this; + + var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + text = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + this.scroll.insertAt(index, text); + Object.keys(formats).forEach(function (format) { + _this4.scroll.formatAt(index, text.length, format, formats[format]); + }); + return this.update(new _quillDelta2.default().retain(index).insert(text, (0, _clone2.default)(formats))); + } + }, { + key: 'isBlank', + value: function isBlank() { + if (this.scroll.children.length == 0) return true; + if (this.scroll.children.length > 1) return false; + var block = this.scroll.children.head; + if (block.statics.blotName !== _block2.default.blotName) return false; + if (block.children.length > 1) return false; + return block.children.head instanceof _break2.default; + } + }, { + key: 'removeFormat', + value: function removeFormat(index, length) { + var text = this.getText(index, length); + + var _scroll$line3 = this.scroll.line(index + length), + _scroll$line4 = _slicedToArray(_scroll$line3, 2), + line = _scroll$line4[0], + offset = _scroll$line4[1]; + + var suffixLength = 0, + suffix = new _quillDelta2.default(); + if (line != null) { + if (!(line instanceof _code2.default)) { + suffixLength = line.length() - offset; + } else { + suffixLength = line.newlineIndex(offset) - offset + 1; + } + suffix = line.delta().slice(offset, offset + suffixLength - 1).insert('\n'); + } + var contents = this.getContents(index, length + suffixLength); + var diff = contents.diff(new _quillDelta2.default().insert(text).concat(suffix)); + var delta = new _quillDelta2.default().retain(index).concat(diff); + return this.applyDelta(delta); + } + }, { + key: 'update', + value: function update(change) { + var mutations = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; + var cursorIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined; + + var oldDelta = this.delta; + if (mutations.length === 1 && mutations[0].type === 'characterData' && mutations[0].target.data.match(ASCII) && _parchment2.default.find(mutations[0].target)) { + // Optimization for character changes + var textBlot = _parchment2.default.find(mutations[0].target); + var formats = (0, _block.bubbleFormats)(textBlot); + var index = textBlot.offset(this.scroll); + var oldValue = mutations[0].oldValue.replace(_cursor2.default.CONTENTS, ''); + var oldText = new _quillDelta2.default().insert(oldValue); + var newText = new _quillDelta2.default().insert(textBlot.value()); + var diffDelta = new _quillDelta2.default().retain(index).concat(oldText.diff(newText, cursorIndex)); + change = diffDelta.reduce(function (delta, op) { + if (op.insert) { + return delta.insert(op.insert, formats); + } else { + return delta.push(op); + } + }, new _quillDelta2.default()); + this.delta = oldDelta.compose(change); + } else { + this.delta = this.getDelta(); + if (!change || !(0, _deepEqual2.default)(oldDelta.compose(change), this.delta)) { + change = oldDelta.diff(this.delta, cursorIndex); + } + } + return change; + } + }]); + + return Editor; +}(); + +function combineFormats(formats, combined) { + return Object.keys(combined).reduce(function (merged, name) { + if (formats[name] == null) return merged; + if (combined[name] === formats[name]) { + merged[name] = combined[name]; + } else if (Array.isArray(combined[name])) { + if (combined[name].indexOf(formats[name]) < 0) { + merged[name] = combined[name].concat([formats[name]]); + } + } else { + merged[name] = [combined[name], formats[name]]; + } + return merged; + }, {}); +} + +function normalizeDelta(delta) { + return delta.reduce(function (delta, op) { + if (op.insert === 1) { + var attributes = (0, _clone2.default)(op.attributes); + delete attributes['image']; + return delta.insert({ image: op.attributes.image }, attributes); + } + if (op.attributes != null && (op.attributes.list === true || op.attributes.bullet === true)) { + op = (0, _clone2.default)(op); + if (op.attributes.list) { + op.attributes.list = 'ordered'; + } else { + op.attributes.list = 'bullet'; + delete op.attributes.bullet; + } + } + if (typeof op.insert === 'string') { + var text = op.insert.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + return delta.insert(text, op.attributes); + } + return delta.push(op); + }, new _quillDelta2.default()); +} + +exports.default = Editor; + +/***/ }), +/* 15 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.Range = undefined; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _clone = __webpack_require__(21); + +var _clone2 = _interopRequireDefault(_clone); + +var _deepEqual = __webpack_require__(11); + +var _deepEqual2 = _interopRequireDefault(_deepEqual); + +var _emitter3 = __webpack_require__(8); + +var _emitter4 = _interopRequireDefault(_emitter3); + +var _logger = __webpack_require__(10); + +var _logger2 = _interopRequireDefault(_logger); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var debug = (0, _logger2.default)('quill:selection'); + +var Range = function Range(index) { + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + + _classCallCheck(this, Range); + + this.index = index; + this.length = length; +}; + +var Selection = function () { + function Selection(scroll, emitter) { + var _this = this; + + _classCallCheck(this, Selection); + + this.emitter = emitter; + this.scroll = scroll; + this.composing = false; + this.mouseDown = false; + this.root = this.scroll.domNode; + this.cursor = _parchment2.default.create('cursor', this); + // savedRange is last non-null range + this.lastRange = this.savedRange = new Range(0, 0); + this.handleComposition(); + this.handleDragging(); + this.emitter.listenDOM('selectionchange', document, function () { + if (!_this.mouseDown) { + setTimeout(_this.update.bind(_this, _emitter4.default.sources.USER), 1); + } + }); + this.emitter.on(_emitter4.default.events.EDITOR_CHANGE, function (type, delta) { + if (type === _emitter4.default.events.TEXT_CHANGE && delta.length() > 0) { + _this.update(_emitter4.default.sources.SILENT); + } + }); + this.emitter.on(_emitter4.default.events.SCROLL_BEFORE_UPDATE, function () { + if (!_this.hasFocus()) return; + var native = _this.getNativeRange(); + if (native == null) return; + if (native.start.node === _this.cursor.textNode) return; // cursor.restore() will handle + // TODO unclear if this has negative side effects + _this.emitter.once(_emitter4.default.events.SCROLL_UPDATE, function () { + try { + _this.setNativeRange(native.start.node, native.start.offset, native.end.node, native.end.offset); + } catch (ignored) {} + }); + }); + this.emitter.on(_emitter4.default.events.SCROLL_OPTIMIZE, function (mutations, context) { + if (context.range) { + var _context$range = context.range, + startNode = _context$range.startNode, + startOffset = _context$range.startOffset, + endNode = _context$range.endNode, + endOffset = _context$range.endOffset; + + _this.setNativeRange(startNode, startOffset, endNode, endOffset); + } + }); + this.update(_emitter4.default.sources.SILENT); + } + + _createClass(Selection, [{ + key: 'handleComposition', + value: function handleComposition() { + var _this2 = this; + + this.root.addEventListener('compositionstart', function () { + _this2.composing = true; + }); + this.root.addEventListener('compositionend', function () { + _this2.composing = false; + if (_this2.cursor.parent) { + var range = _this2.cursor.restore(); + if (!range) return; + setTimeout(function () { + _this2.setNativeRange(range.startNode, range.startOffset, range.endNode, range.endOffset); + }, 1); + } + }); + } + }, { + key: 'handleDragging', + value: function handleDragging() { + var _this3 = this; + + this.emitter.listenDOM('mousedown', document.body, function () { + _this3.mouseDown = true; + }); + this.emitter.listenDOM('mouseup', document.body, function () { + _this3.mouseDown = false; + _this3.update(_emitter4.default.sources.USER); + }); + } + }, { + key: 'focus', + value: function focus() { + if (this.hasFocus()) return; + this.root.focus(); + this.setRange(this.savedRange); + } + }, { + key: 'format', + value: function format(_format, value) { + if (this.scroll.whitelist != null && !this.scroll.whitelist[_format]) return; + this.scroll.update(); + var nativeRange = this.getNativeRange(); + if (nativeRange == null || !nativeRange.native.collapsed || _parchment2.default.query(_format, _parchment2.default.Scope.BLOCK)) return; + if (nativeRange.start.node !== this.cursor.textNode) { + var blot = _parchment2.default.find(nativeRange.start.node, false); + if (blot == null) return; + // TODO Give blot ability to not split + if (blot instanceof _parchment2.default.Leaf) { + var after = blot.split(nativeRange.start.offset); + blot.parent.insertBefore(this.cursor, after); + } else { + blot.insertBefore(this.cursor, nativeRange.start.node); // Should never happen + } + this.cursor.attach(); + } + this.cursor.format(_format, value); + this.scroll.optimize(); + this.setNativeRange(this.cursor.textNode, this.cursor.textNode.data.length); + this.update(); + } + }, { + key: 'getBounds', + value: function getBounds(index) { + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + + var scrollLength = this.scroll.length(); + index = Math.min(index, scrollLength - 1); + length = Math.min(index + length, scrollLength - 1) - index; + var node = void 0, + _scroll$leaf = this.scroll.leaf(index), + _scroll$leaf2 = _slicedToArray(_scroll$leaf, 2), + leaf = _scroll$leaf2[0], + offset = _scroll$leaf2[1]; + if (leaf == null) return null; + + var _leaf$position = leaf.position(offset, true); + + var _leaf$position2 = _slicedToArray(_leaf$position, 2); + + node = _leaf$position2[0]; + offset = _leaf$position2[1]; + + var range = document.createRange(); + if (length > 0) { + range.setStart(node, offset); + + var _scroll$leaf3 = this.scroll.leaf(index + length); + + var _scroll$leaf4 = _slicedToArray(_scroll$leaf3, 2); + + leaf = _scroll$leaf4[0]; + offset = _scroll$leaf4[1]; + + if (leaf == null) return null; + + var _leaf$position3 = leaf.position(offset, true); + + var _leaf$position4 = _slicedToArray(_leaf$position3, 2); + + node = _leaf$position4[0]; + offset = _leaf$position4[1]; + + range.setEnd(node, offset); + return range.getBoundingClientRect(); + } else { + var side = 'left'; + var rect = void 0; + if (node instanceof Text) { + if (offset < node.data.length) { + range.setStart(node, offset); + range.setEnd(node, offset + 1); + } else { + range.setStart(node, offset - 1); + range.setEnd(node, offset); + side = 'right'; + } + rect = range.getBoundingClientRect(); + } else { + rect = leaf.domNode.getBoundingClientRect(); + if (offset > 0) side = 'right'; + } + return { + bottom: rect.top + rect.height, + height: rect.height, + left: rect[side], + right: rect[side], + top: rect.top, + width: 0 + }; + } + } + }, { + key: 'getNativeRange', + value: function getNativeRange() { + var selection = document.getSelection(); + if (selection == null || selection.rangeCount <= 0) return null; + var nativeRange = selection.getRangeAt(0); + if (nativeRange == null) return null; + var range = this.normalizeNative(nativeRange); + debug.info('getNativeRange', range); + return range; + } + }, { + key: 'getRange', + value: function getRange() { + var normalized = this.getNativeRange(); + if (normalized == null) return [null, null]; + var range = this.normalizedToRange(normalized); + return [range, normalized]; + } + }, { + key: 'hasFocus', + value: function hasFocus() { + return document.activeElement === this.root; + } + }, { + key: 'normalizedToRange', + value: function normalizedToRange(range) { + var _this4 = this; + + var positions = [[range.start.node, range.start.offset]]; + if (!range.native.collapsed) { + positions.push([range.end.node, range.end.offset]); + } + var indexes = positions.map(function (position) { + var _position = _slicedToArray(position, 2), + node = _position[0], + offset = _position[1]; + + var blot = _parchment2.default.find(node, true); + var index = blot.offset(_this4.scroll); + if (offset === 0) { + return index; + } else if (blot instanceof _parchment2.default.Container) { + return index + blot.length(); + } else { + return index + blot.index(node, offset); + } + }); + var end = Math.min(Math.max.apply(Math, _toConsumableArray(indexes)), this.scroll.length() - 1); + var start = Math.min.apply(Math, [end].concat(_toConsumableArray(indexes))); + return new Range(start, end - start); + } + }, { + key: 'normalizeNative', + value: function normalizeNative(nativeRange) { + if (!contains(this.root, nativeRange.startContainer) || !nativeRange.collapsed && !contains(this.root, nativeRange.endContainer)) { + return null; + } + var range = { + start: { node: nativeRange.startContainer, offset: nativeRange.startOffset }, + end: { node: nativeRange.endContainer, offset: nativeRange.endOffset }, + native: nativeRange + }; + [range.start, range.end].forEach(function (position) { + var node = position.node, + offset = position.offset; + while (!(node instanceof Text) && node.childNodes.length > 0) { + if (node.childNodes.length > offset) { + node = node.childNodes[offset]; + offset = 0; + } else if (node.childNodes.length === offset) { + node = node.lastChild; + offset = node instanceof Text ? node.data.length : node.childNodes.length + 1; + } else { + break; + } + } + position.node = node, position.offset = offset; + }); + return range; + } + }, { + key: 'rangeToNative', + value: function rangeToNative(range) { + var _this5 = this; + + var indexes = range.collapsed ? [range.index] : [range.index, range.index + range.length]; + var args = []; + var scrollLength = this.scroll.length(); + indexes.forEach(function (index, i) { + index = Math.min(scrollLength - 1, index); + var node = void 0, + _scroll$leaf5 = _this5.scroll.leaf(index), + _scroll$leaf6 = _slicedToArray(_scroll$leaf5, 2), + leaf = _scroll$leaf6[0], + offset = _scroll$leaf6[1]; + var _leaf$position5 = leaf.position(offset, i !== 0); + + var _leaf$position6 = _slicedToArray(_leaf$position5, 2); + + node = _leaf$position6[0]; + offset = _leaf$position6[1]; + + args.push(node, offset); + }); + if (args.length < 2) { + args = args.concat(args); + } + return args; + } + }, { + key: 'scrollIntoView', + value: function scrollIntoView(scrollingContainer) { + var range = this.lastRange; + if (range == null) return; + var bounds = this.getBounds(range.index, range.length); + if (bounds == null) return; + var limit = this.scroll.length() - 1; + + var _scroll$line = this.scroll.line(Math.min(range.index, limit)), + _scroll$line2 = _slicedToArray(_scroll$line, 1), + first = _scroll$line2[0]; + + var last = first; + if (range.length > 0) { + var _scroll$line3 = this.scroll.line(Math.min(range.index + range.length, limit)); + + var _scroll$line4 = _slicedToArray(_scroll$line3, 1); + + last = _scroll$line4[0]; + } + if (first == null || last == null) return; + var scrollBounds = scrollingContainer.getBoundingClientRect(); + if (bounds.top < scrollBounds.top) { + scrollingContainer.scrollTop -= scrollBounds.top - bounds.top; + } else if (bounds.bottom > scrollBounds.bottom) { + scrollingContainer.scrollTop += bounds.bottom - scrollBounds.bottom; + } + } + }, { + key: 'setNativeRange', + value: function setNativeRange(startNode, startOffset) { + var endNode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : startNode; + var endOffset = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : startOffset; + var force = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; + + debug.info('setNativeRange', startNode, startOffset, endNode, endOffset); + if (startNode != null && (this.root.parentNode == null || startNode.parentNode == null || endNode.parentNode == null)) { + return; + } + var selection = document.getSelection(); + if (selection == null) return; + if (startNode != null) { + if (!this.hasFocus()) this.root.focus(); + var native = (this.getNativeRange() || {}).native; + if (native == null || force || startNode !== native.startContainer || startOffset !== native.startOffset || endNode !== native.endContainer || endOffset !== native.endOffset) { + + if (startNode.tagName == "BR") { + startOffset = [].indexOf.call(startNode.parentNode.childNodes, startNode); + startNode = startNode.parentNode; + } + if (endNode.tagName == "BR") { + endOffset = [].indexOf.call(endNode.parentNode.childNodes, endNode); + endNode = endNode.parentNode; + } + var range = document.createRange(); + range.setStart(startNode, startOffset); + range.setEnd(endNode, endOffset); + selection.removeAllRanges(); + selection.addRange(range); + } + } else { + selection.removeAllRanges(); + this.root.blur(); + document.body.focus(); // root.blur() not enough on IE11+Travis+SauceLabs (but not local VMs) + } + } + }, { + key: 'setRange', + value: function setRange(range) { + var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _emitter4.default.sources.API; + + if (typeof force === 'string') { + source = force; + force = false; + } + debug.info('setRange', range); + if (range != null) { + var args = this.rangeToNative(range); + this.setNativeRange.apply(this, _toConsumableArray(args).concat([force])); + } else { + this.setNativeRange(null); + } + this.update(source); + } + }, { + key: 'update', + value: function update() { + var source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _emitter4.default.sources.USER; + + var oldRange = this.lastRange; + + var _getRange = this.getRange(), + _getRange2 = _slicedToArray(_getRange, 2), + lastRange = _getRange2[0], + nativeRange = _getRange2[1]; + + this.lastRange = lastRange; + if (this.lastRange != null) { + this.savedRange = this.lastRange; + } + if (!(0, _deepEqual2.default)(oldRange, this.lastRange)) { + var _emitter; + + if (!this.composing && nativeRange != null && nativeRange.native.collapsed && nativeRange.start.node !== this.cursor.textNode) { + this.cursor.restore(); + } + var args = [_emitter4.default.events.SELECTION_CHANGE, (0, _clone2.default)(this.lastRange), (0, _clone2.default)(oldRange), source]; + (_emitter = this.emitter).emit.apply(_emitter, [_emitter4.default.events.EDITOR_CHANGE].concat(args)); + if (source !== _emitter4.default.sources.SILENT) { + var _emitter2; + + (_emitter2 = this.emitter).emit.apply(_emitter2, args); + } + } + } + }]); + + return Selection; +}(); + +function contains(parent, descendant) { + try { + // Firefox inserts inaccessible nodes around video elements + descendant.parentNode; + } catch (e) { + return false; + } + // IE11 has bug with Text nodes + // https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect + if (descendant instanceof Text) { + descendant = descendant.parentNode; + } + return parent.contains(descendant); +} + +exports.Range = Range; +exports.default = Selection; + +/***/ }), +/* 16 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Break = function (_Parchment$Embed) { + _inherits(Break, _Parchment$Embed); + + function Break() { + _classCallCheck(this, Break); + + return _possibleConstructorReturn(this, (Break.__proto__ || Object.getPrototypeOf(Break)).apply(this, arguments)); + } + + _createClass(Break, [{ + key: 'insertInto', + value: function insertInto(parent, ref) { + if (parent.children.length === 0) { + _get(Break.prototype.__proto__ || Object.getPrototypeOf(Break.prototype), 'insertInto', this).call(this, parent, ref); + } else { + this.remove(); + } + } + }, { + key: 'length', + value: function length() { + return 0; + } + }, { + key: 'value', + value: function value() { + return ''; + } + }], [{ + key: 'value', + value: function value() { + return undefined; + } + }]); + + return Break; +}(_parchment2.default.Embed); + +Break.blotName = 'break'; +Break.tagName = 'BR'; + +exports.default = Break; + +/***/ }), +/* 17 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var linked_list_1 = __webpack_require__(44); +var shadow_1 = __webpack_require__(30); +var Registry = __webpack_require__(1); +var ContainerBlot = /** @class */ (function (_super) { + __extends(ContainerBlot, _super); + function ContainerBlot(domNode) { + var _this = _super.call(this, domNode) || this; + _this.build(); + return _this; + } + ContainerBlot.prototype.appendChild = function (other) { + this.insertBefore(other); + }; + ContainerBlot.prototype.attach = function () { + _super.prototype.attach.call(this); + this.children.forEach(function (child) { + child.attach(); + }); + }; + ContainerBlot.prototype.build = function () { + var _this = this; + this.children = new linked_list_1.default(); + // Need to be reversed for if DOM nodes already in order + [].slice + .call(this.domNode.childNodes) + .reverse() + .forEach(function (node) { + try { + var child = makeBlot(node); + _this.insertBefore(child, _this.children.head || undefined); + } + catch (err) { + if (err instanceof Registry.ParchmentError) + return; + else + throw err; + } + }); + }; + ContainerBlot.prototype.deleteAt = function (index, length) { + if (index === 0 && length === this.length()) { + return this.remove(); + } + this.children.forEachAt(index, length, function (child, offset, length) { + child.deleteAt(offset, length); + }); + }; + ContainerBlot.prototype.descendant = function (criteria, index) { + var _a = this.children.find(index), child = _a[0], offset = _a[1]; + if ((criteria.blotName == null && criteria(child)) || + (criteria.blotName != null && child instanceof criteria)) { + return [child, offset]; + } + else if (child instanceof ContainerBlot) { + return child.descendant(criteria, offset); + } + else { + return [null, -1]; + } + }; + ContainerBlot.prototype.descendants = function (criteria, index, length) { + if (index === void 0) { index = 0; } + if (length === void 0) { length = Number.MAX_VALUE; } + var descendants = []; + var lengthLeft = length; + this.children.forEachAt(index, length, function (child, index, length) { + if ((criteria.blotName == null && criteria(child)) || + (criteria.blotName != null && child instanceof criteria)) { + descendants.push(child); + } + if (child instanceof ContainerBlot) { + descendants = descendants.concat(child.descendants(criteria, index, lengthLeft)); + } + lengthLeft -= length; + }); + return descendants; + }; + ContainerBlot.prototype.detach = function () { + this.children.forEach(function (child) { + child.detach(); + }); + _super.prototype.detach.call(this); + }; + ContainerBlot.prototype.formatAt = function (index, length, name, value) { + this.children.forEachAt(index, length, function (child, offset, length) { + child.formatAt(offset, length, name, value); + }); + }; + ContainerBlot.prototype.insertAt = function (index, value, def) { + var _a = this.children.find(index), child = _a[0], offset = _a[1]; + if (child) { + child.insertAt(offset, value, def); + } + else { + var blot = def == null ? Registry.create('text', value) : Registry.create(value, def); + this.appendChild(blot); + } + }; + ContainerBlot.prototype.insertBefore = function (childBlot, refBlot) { + if (this.statics.allowedChildren != null && + !this.statics.allowedChildren.some(function (child) { + return childBlot instanceof child; + })) { + throw new Registry.ParchmentError("Cannot insert " + childBlot.statics.blotName + " into " + this.statics.blotName); + } + childBlot.insertInto(this, refBlot); + }; + ContainerBlot.prototype.length = function () { + return this.children.reduce(function (memo, child) { + return memo + child.length(); + }, 0); + }; + ContainerBlot.prototype.moveChildren = function (targetParent, refNode) { + this.children.forEach(function (child) { + targetParent.insertBefore(child, refNode); + }); + }; + ContainerBlot.prototype.optimize = function (context) { + _super.prototype.optimize.call(this, context); + if (this.children.length === 0) { + if (this.statics.defaultChild != null) { + var child = Registry.create(this.statics.defaultChild); + this.appendChild(child); + child.optimize(context); + } + else { + this.remove(); + } + } + }; + ContainerBlot.prototype.path = function (index, inclusive) { + if (inclusive === void 0) { inclusive = false; } + var _a = this.children.find(index, inclusive), child = _a[0], offset = _a[1]; + var position = [[this, index]]; + if (child instanceof ContainerBlot) { + return position.concat(child.path(offset, inclusive)); + } + else if (child != null) { + position.push([child, offset]); + } + return position; + }; + ContainerBlot.prototype.removeChild = function (child) { + this.children.remove(child); + }; + ContainerBlot.prototype.replace = function (target) { + if (target instanceof ContainerBlot) { + target.moveChildren(this); + } + _super.prototype.replace.call(this, target); + }; + ContainerBlot.prototype.split = function (index, force) { + if (force === void 0) { force = false; } + if (!force) { + if (index === 0) + return this; + if (index === this.length()) + return this.next; + } + var after = this.clone(); + this.parent.insertBefore(after, this.next); + this.children.forEachAt(index, this.length(), function (child, offset, length) { + child = child.split(offset, force); + after.appendChild(child); + }); + return after; + }; + ContainerBlot.prototype.unwrap = function () { + this.moveChildren(this.parent, this.next); + this.remove(); + }; + ContainerBlot.prototype.update = function (mutations, context) { + var _this = this; + var addedNodes = []; + var removedNodes = []; + mutations.forEach(function (mutation) { + if (mutation.target === _this.domNode && mutation.type === 'childList') { + addedNodes.push.apply(addedNodes, mutation.addedNodes); + removedNodes.push.apply(removedNodes, mutation.removedNodes); + } + }); + removedNodes.forEach(function (node) { + // Check node has actually been removed + // One exception is Chrome does not immediately remove IFRAMEs + // from DOM but MutationRecord is correct in its reported removal + if (node.parentNode != null && + // @ts-ignore + node.tagName !== 'IFRAME' && + document.body.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINED_BY) { + return; + } + var blot = Registry.find(node); + if (blot == null) + return; + if (blot.domNode.parentNode == null || blot.domNode.parentNode === _this.domNode) { + blot.detach(); + } + }); + addedNodes + .filter(function (node) { + return node.parentNode == _this.domNode; + }) + .sort(function (a, b) { + if (a === b) + return 0; + if (a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING) { + return 1; + } + return -1; + }) + .forEach(function (node) { + var refBlot = null; + if (node.nextSibling != null) { + refBlot = Registry.find(node.nextSibling); + } + var blot = makeBlot(node); + if (blot.next != refBlot || blot.next == null) { + if (blot.parent != null) { + blot.parent.removeChild(_this); + } + _this.insertBefore(blot, refBlot || undefined); + } + }); + }; + return ContainerBlot; +}(shadow_1.default)); +function makeBlot(node) { + var blot = Registry.find(node); + if (blot == null) { + try { + blot = Registry.create(node); + } + catch (e) { + blot = Registry.create(Registry.Scope.INLINE); + [].slice.call(node.childNodes).forEach(function (child) { + // @ts-ignore + blot.domNode.appendChild(child); + }); + if (node.parentNode) { + node.parentNode.replaceChild(blot.domNode, node); + } + blot.attach(); + } + } + return blot; +} +exports.default = ContainerBlot; + + +/***/ }), +/* 18 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var attributor_1 = __webpack_require__(12); +var store_1 = __webpack_require__(31); +var container_1 = __webpack_require__(17); +var Registry = __webpack_require__(1); +var FormatBlot = /** @class */ (function (_super) { + __extends(FormatBlot, _super); + function FormatBlot(domNode) { + var _this = _super.call(this, domNode) || this; + _this.attributes = new store_1.default(_this.domNode); + return _this; + } + FormatBlot.formats = function (domNode) { + if (typeof this.tagName === 'string') { + return true; + } + else if (Array.isArray(this.tagName)) { + return domNode.tagName.toLowerCase(); + } + return undefined; + }; + FormatBlot.prototype.format = function (name, value) { + var format = Registry.query(name); + if (format instanceof attributor_1.default) { + this.attributes.attribute(format, value); + } + else if (value) { + if (format != null && (name !== this.statics.blotName || this.formats()[name] !== value)) { + this.replaceWith(name, value); + } + } + }; + FormatBlot.prototype.formats = function () { + var formats = this.attributes.values(); + var format = this.statics.formats(this.domNode); + if (format != null) { + formats[this.statics.blotName] = format; + } + return formats; + }; + FormatBlot.prototype.replaceWith = function (name, value) { + var replacement = _super.prototype.replaceWith.call(this, name, value); + this.attributes.copy(replacement); + return replacement; + }; + FormatBlot.prototype.update = function (mutations, context) { + var _this = this; + _super.prototype.update.call(this, mutations, context); + if (mutations.some(function (mutation) { + return mutation.target === _this.domNode && mutation.type === 'attributes'; + })) { + this.attributes.build(); + } + }; + FormatBlot.prototype.wrap = function (name, value) { + var wrapper = _super.prototype.wrap.call(this, name, value); + if (wrapper instanceof FormatBlot && wrapper.statics.scope === this.statics.scope) { + this.attributes.move(wrapper); + } + return wrapper; + }; + return FormatBlot; +}(container_1.default)); +exports.default = FormatBlot; + + +/***/ }), +/* 19 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var shadow_1 = __webpack_require__(30); +var Registry = __webpack_require__(1); +var LeafBlot = /** @class */ (function (_super) { + __extends(LeafBlot, _super); + function LeafBlot() { + return _super !== null && _super.apply(this, arguments) || this; + } + LeafBlot.value = function (domNode) { + return true; + }; + LeafBlot.prototype.index = function (node, offset) { + if (this.domNode === node || + this.domNode.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINED_BY) { + return Math.min(offset, 1); + } + return -1; + }; + LeafBlot.prototype.position = function (index, inclusive) { + var offset = [].indexOf.call(this.parent.domNode.childNodes, this.domNode); + if (index > 0) + offset += 1; + return [this.parent.domNode, offset]; + }; + LeafBlot.prototype.value = function () { + return _a = {}, _a[this.statics.blotName] = this.statics.value(this.domNode) || true, _a; + var _a; + }; + LeafBlot.scope = Registry.Scope.INLINE_BLOT; + return LeafBlot; +}(shadow_1.default)); +exports.default = LeafBlot; + + +/***/ }), +/* 20 */ +/***/ (function(module, exports, __webpack_require__) { + +var equal = __webpack_require__(11); +var extend = __webpack_require__(3); + + +var lib = { + attributes: { + compose: function (a, b, keepNull) { + if (typeof a !== 'object') a = {}; + if (typeof b !== 'object') b = {}; + var attributes = extend(true, {}, b); + if (!keepNull) { + attributes = Object.keys(attributes).reduce(function (copy, key) { + if (attributes[key] != null) { + copy[key] = attributes[key]; + } + return copy; + }, {}); + } + for (var key in a) { + if (a[key] !== undefined && b[key] === undefined) { + attributes[key] = a[key]; + } + } + return Object.keys(attributes).length > 0 ? attributes : undefined; + }, + + diff: function(a, b) { + if (typeof a !== 'object') a = {}; + if (typeof b !== 'object') b = {}; + var attributes = Object.keys(a).concat(Object.keys(b)).reduce(function (attributes, key) { + if (!equal(a[key], b[key])) { + attributes[key] = b[key] === undefined ? null : b[key]; + } + return attributes; + }, {}); + return Object.keys(attributes).length > 0 ? attributes : undefined; + }, + + transform: function (a, b, priority) { + if (typeof a !== 'object') return b; + if (typeof b !== 'object') return undefined; + if (!priority) return b; // b simply overwrites us without priority + var attributes = Object.keys(b).reduce(function (attributes, key) { + if (a[key] === undefined) attributes[key] = b[key]; // null is a valid value + return attributes; + }, {}); + return Object.keys(attributes).length > 0 ? attributes : undefined; + } + }, + + iterator: function (ops) { + return new Iterator(ops); + }, + + length: function (op) { + if (typeof op['delete'] === 'number') { + return op['delete']; + } else if (typeof op.retain === 'number') { + return op.retain; + } else { + return typeof op.insert === 'string' ? op.insert.length : 1; + } + } +}; + + +function Iterator(ops) { + this.ops = ops; + this.index = 0; + this.offset = 0; +}; + +Iterator.prototype.hasNext = function () { + return this.peekLength() < Infinity; +}; + +Iterator.prototype.next = function (length) { + if (!length) length = Infinity; + var nextOp = this.ops[this.index]; + if (nextOp) { + var offset = this.offset; + var opLength = lib.length(nextOp) + if (length >= opLength - offset) { + length = opLength - offset; + this.index += 1; + this.offset = 0; + } else { + this.offset += length; + } + if (typeof nextOp['delete'] === 'number') { + return { 'delete': length }; + } else { + var retOp = {}; + if (nextOp.attributes) { + retOp.attributes = nextOp.attributes; + } + if (typeof nextOp.retain === 'number') { + retOp.retain = length; + } else if (typeof nextOp.insert === 'string') { + retOp.insert = nextOp.insert.substr(offset, length); + } else { + // offset should === 0, length should === 1 + retOp.insert = nextOp.insert; + } + return retOp; + } + } else { + return { retain: Infinity }; + } +}; + +Iterator.prototype.peek = function () { + return this.ops[this.index]; +}; + +Iterator.prototype.peekLength = function () { + if (this.ops[this.index]) { + // Should never return 0 if our index is being managed correctly + return lib.length(this.ops[this.index]) - this.offset; + } else { + return Infinity; + } +}; + +Iterator.prototype.peekType = function () { + if (this.ops[this.index]) { + if (typeof this.ops[this.index]['delete'] === 'number') { + return 'delete'; + } else if (typeof this.ops[this.index].retain === 'number') { + return 'retain'; + } else { + return 'insert'; + } + } + return 'retain'; +}; + + +module.exports = lib; + + +/***/ }), +/* 21 */ +/***/ (function(module, exports) { + +var clone = (function() { +'use strict'; + +function _instanceof(obj, type) { + return type != null && obj instanceof type; +} + +var nativeMap; +try { + nativeMap = Map; +} catch(_) { + // maybe a reference error because no `Map`. Give it a dummy value that no + // value will ever be an instanceof. + nativeMap = function() {}; +} + +var nativeSet; +try { + nativeSet = Set; +} catch(_) { + nativeSet = function() {}; +} + +var nativePromise; +try { + nativePromise = Promise; +} catch(_) { + nativePromise = function() {}; +} + +/** + * Clones (copies) an Object using deep copying. + * + * This function supports circular references by default, but if you are certain + * there are no circular references in your object, you can save some CPU time + * by calling clone(obj, false). + * + * Caution: if `circular` is false and `parent` contains circular references, + * your program may enter an infinite loop and crash. + * + * @param `parent` - the object to be cloned + * @param `circular` - set to true if the object to be cloned may contain + * circular references. (optional - true by default) + * @param `depth` - set to a number if the object is only to be cloned to + * a particular depth. (optional - defaults to Infinity) + * @param `prototype` - sets the prototype to be used when cloning an object. + * (optional - defaults to parent prototype). + * @param `includeNonEnumerable` - set to true if the non-enumerable properties + * should be cloned as well. Non-enumerable properties on the prototype + * chain will be ignored. (optional - false by default) +*/ +function clone(parent, circular, depth, prototype, includeNonEnumerable) { + if (typeof circular === 'object') { + depth = circular.depth; + prototype = circular.prototype; + includeNonEnumerable = circular.includeNonEnumerable; + circular = circular.circular; + } + // maintain two arrays for circular references, where corresponding parents + // and children have the same index + var allParents = []; + var allChildren = []; + + var useBuffer = typeof Buffer != 'undefined'; + + if (typeof circular == 'undefined') + circular = true; + + if (typeof depth == 'undefined') + depth = Infinity; + + // recurse this function so we don't reset allParents and allChildren + function _clone(parent, depth) { + // cloning null always returns null + if (parent === null) + return null; + + if (depth === 0) + return parent; + + var child; + var proto; + if (typeof parent != 'object') { + return parent; + } + + if (_instanceof(parent, nativeMap)) { + child = new nativeMap(); + } else if (_instanceof(parent, nativeSet)) { + child = new nativeSet(); + } else if (_instanceof(parent, nativePromise)) { + child = new nativePromise(function (resolve, reject) { + parent.then(function(value) { + resolve(_clone(value, depth - 1)); + }, function(err) { + reject(_clone(err, depth - 1)); + }); + }); + } else if (clone.__isArray(parent)) { + child = []; + } else if (clone.__isRegExp(parent)) { + child = new RegExp(parent.source, __getRegExpFlags(parent)); + if (parent.lastIndex) child.lastIndex = parent.lastIndex; + } else if (clone.__isDate(parent)) { + child = new Date(parent.getTime()); + } else if (useBuffer && Buffer.isBuffer(parent)) { + child = new Buffer(parent.length); + parent.copy(child); + return child; + } else if (_instanceof(parent, Error)) { + child = Object.create(parent); + } else { + if (typeof prototype == 'undefined') { + proto = Object.getPrototypeOf(parent); + child = Object.create(proto); + } + else { + child = Object.create(prototype); + proto = prototype; + } + } + + if (circular) { + var index = allParents.indexOf(parent); + + if (index != -1) { + return allChildren[index]; + } + allParents.push(parent); + allChildren.push(child); + } + + if (_instanceof(parent, nativeMap)) { + parent.forEach(function(value, key) { + var keyChild = _clone(key, depth - 1); + var valueChild = _clone(value, depth - 1); + child.set(keyChild, valueChild); + }); + } + if (_instanceof(parent, nativeSet)) { + parent.forEach(function(value) { + var entryChild = _clone(value, depth - 1); + child.add(entryChild); + }); + } + + for (var i in parent) { + var attrs; + if (proto) { + attrs = Object.getOwnPropertyDescriptor(proto, i); + } + + if (attrs && attrs.set == null) { + continue; + } + child[i] = _clone(parent[i], depth - 1); + } + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(parent); + for (var i = 0; i < symbols.length; i++) { + // Don't need to worry about cloning a symbol because it is a primitive, + // like a number or string. + var symbol = symbols[i]; + var descriptor = Object.getOwnPropertyDescriptor(parent, symbol); + if (descriptor && !descriptor.enumerable && !includeNonEnumerable) { + continue; + } + child[symbol] = _clone(parent[symbol], depth - 1); + if (!descriptor.enumerable) { + Object.defineProperty(child, symbol, { + enumerable: false + }); + } + } + } + + if (includeNonEnumerable) { + var allPropertyNames = Object.getOwnPropertyNames(parent); + for (var i = 0; i < allPropertyNames.length; i++) { + var propertyName = allPropertyNames[i]; + var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName); + if (descriptor && descriptor.enumerable) { + continue; + } + child[propertyName] = _clone(parent[propertyName], depth - 1); + Object.defineProperty(child, propertyName, { + enumerable: false + }); + } + } + + return child; + } + + return _clone(parent, depth); +} + +/** + * Simple flat clone using prototype, accepts only objects, usefull for property + * override on FLAT configuration object (no nested props). + * + * USE WITH CAUTION! This may not behave as you wish if you do not know how this + * works. + */ +clone.clonePrototype = function clonePrototype(parent) { + if (parent === null) + return null; + + var c = function () {}; + c.prototype = parent; + return new c(); +}; + +// private utility functions + +function __objToStr(o) { + return Object.prototype.toString.call(o); +} +clone.__objToStr = __objToStr; + +function __isDate(o) { + return typeof o === 'object' && __objToStr(o) === '[object Date]'; +} +clone.__isDate = __isDate; + +function __isArray(o) { + return typeof o === 'object' && __objToStr(o) === '[object Array]'; +} +clone.__isArray = __isArray; + +function __isRegExp(o) { + return typeof o === 'object' && __objToStr(o) === '[object RegExp]'; +} +clone.__isRegExp = __isRegExp; + +function __getRegExpFlags(re) { + var flags = ''; + if (re.global) flags += 'g'; + if (re.ignoreCase) flags += 'i'; + if (re.multiline) flags += 'm'; + return flags; +} +clone.__getRegExpFlags = __getRegExpFlags; + +return clone; +})(); + +if (typeof module === 'object' && module.exports) { + module.exports = clone; +} + + +/***/ }), +/* 22 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _emitter = __webpack_require__(8); + +var _emitter2 = _interopRequireDefault(_emitter); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +var _break = __webpack_require__(16); + +var _break2 = _interopRequireDefault(_break); + +var _code = __webpack_require__(13); + +var _code2 = _interopRequireDefault(_code); + +var _container = __webpack_require__(25); + +var _container2 = _interopRequireDefault(_container); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +function isLine(blot) { + return blot instanceof _block2.default || blot instanceof _block.BlockEmbed; +} + +var Scroll = function (_Parchment$Scroll) { + _inherits(Scroll, _Parchment$Scroll); + + function Scroll(domNode, config) { + _classCallCheck(this, Scroll); + + var _this = _possibleConstructorReturn(this, (Scroll.__proto__ || Object.getPrototypeOf(Scroll)).call(this, domNode)); + + _this.emitter = config.emitter; + if (Array.isArray(config.whitelist)) { + _this.whitelist = config.whitelist.reduce(function (whitelist, format) { + whitelist[format] = true; + return whitelist; + }, {}); + } + // Some reason fixes composition issues with character languages in Windows/Chrome, Safari + _this.domNode.addEventListener('DOMNodeInserted', function () {}); + _this.optimize(); + _this.enable(); + return _this; + } + + _createClass(Scroll, [{ + key: 'batchStart', + value: function batchStart() { + this.batch = true; + } + }, { + key: 'batchEnd', + value: function batchEnd() { + this.batch = false; + this.optimize(); + } + }, { + key: 'deleteAt', + value: function deleteAt(index, length) { + var _line = this.line(index), + _line2 = _slicedToArray(_line, 2), + first = _line2[0], + offset = _line2[1]; + + var _line3 = this.line(index + length), + _line4 = _slicedToArray(_line3, 1), + last = _line4[0]; + + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'deleteAt', this).call(this, index, length); + if (last != null && first !== last && offset > 0) { + if (first instanceof _block.BlockEmbed || last instanceof _block.BlockEmbed) { + this.optimize(); + return; + } + if (first instanceof _code2.default) { + var newlineIndex = first.newlineIndex(first.length(), true); + if (newlineIndex > -1) { + first = first.split(newlineIndex + 1); + if (first === last) { + this.optimize(); + return; + } + } + } else if (last instanceof _code2.default) { + var _newlineIndex = last.newlineIndex(0); + if (_newlineIndex > -1) { + last.split(_newlineIndex + 1); + } + } + var ref = last.children.head instanceof _break2.default ? null : last.children.head; + first.moveChildren(last, ref); + first.remove(); + } + this.optimize(); + } + }, { + key: 'enable', + value: function enable() { + var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; + + this.domNode.setAttribute('contenteditable', enabled); + } + }, { + key: 'formatAt', + value: function formatAt(index, length, format, value) { + if (this.whitelist != null && !this.whitelist[format]) return; + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'formatAt', this).call(this, index, length, format, value); + this.optimize(); + } + }, { + key: 'insertAt', + value: function insertAt(index, value, def) { + if (def != null && this.whitelist != null && !this.whitelist[value]) return; + if (index >= this.length()) { + if (def == null || _parchment2.default.query(value, _parchment2.default.Scope.BLOCK) == null) { + var blot = _parchment2.default.create(this.statics.defaultChild); + this.appendChild(blot); + if (def == null && value.endsWith('\n')) { + value = value.slice(0, -1); + } + blot.insertAt(0, value, def); + } else { + var embed = _parchment2.default.create(value, def); + this.appendChild(embed); + } + } else { + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'insertAt', this).call(this, index, value, def); + } + this.optimize(); + } + }, { + key: 'insertBefore', + value: function insertBefore(blot, ref) { + if (blot.statics.scope === _parchment2.default.Scope.INLINE_BLOT) { + var wrapper = _parchment2.default.create(this.statics.defaultChild); + wrapper.appendChild(blot); + blot = wrapper; + } + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'insertBefore', this).call(this, blot, ref); + } + }, { + key: 'leaf', + value: function leaf(index) { + return this.path(index).pop() || [null, -1]; + } + }, { + key: 'line', + value: function line(index) { + if (index === this.length()) { + return this.line(index - 1); + } + return this.descendant(isLine, index); + } + }, { + key: 'lines', + value: function lines() { + var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Number.MAX_VALUE; + + var getLines = function getLines(blot, index, length) { + var lines = [], + lengthLeft = length; + blot.children.forEachAt(index, length, function (child, index, length) { + if (isLine(child)) { + lines.push(child); + } else if (child instanceof _parchment2.default.Container) { + lines = lines.concat(getLines(child, index, lengthLeft)); + } + lengthLeft -= length; + }); + return lines; + }; + return getLines(this, index, length); + } + }, { + key: 'optimize', + value: function optimize() { + var mutations = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + if (this.batch === true) return; + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'optimize', this).call(this, mutations, context); + if (mutations.length > 0) { + this.emitter.emit(_emitter2.default.events.SCROLL_OPTIMIZE, mutations, context); + } + } + }, { + key: 'path', + value: function path(index) { + return _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'path', this).call(this, index).slice(1); // Exclude self + } + }, { + key: 'update', + value: function update(mutations) { + if (this.batch === true) return; + var source = _emitter2.default.sources.USER; + if (typeof mutations === 'string') { + source = mutations; + } + if (!Array.isArray(mutations)) { + mutations = this.observer.takeRecords(); + } + if (mutations.length > 0) { + this.emitter.emit(_emitter2.default.events.SCROLL_BEFORE_UPDATE, source, mutations); + } + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'update', this).call(this, mutations.concat([])); // pass copy + if (mutations.length > 0) { + this.emitter.emit(_emitter2.default.events.SCROLL_UPDATE, source, mutations); + } + } + }]); + + return Scroll; +}(_parchment2.default.Scroll); + +Scroll.blotName = 'scroll'; +Scroll.className = 'ql-editor'; +Scroll.tagName = 'DIV'; +Scroll.defaultChild = 'block'; +Scroll.allowedChildren = [_block2.default, _block.BlockEmbed, _container2.default]; + +exports.default = Scroll; + +/***/ }), +/* 23 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.SHORTKEY = exports.default = undefined; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _clone = __webpack_require__(21); + +var _clone2 = _interopRequireDefault(_clone); + +var _deepEqual = __webpack_require__(11); + +var _deepEqual2 = _interopRequireDefault(_deepEqual); + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _op = __webpack_require__(20); + +var _op2 = _interopRequireDefault(_op); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _logger = __webpack_require__(10); + +var _logger2 = _interopRequireDefault(_logger); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var debug = (0, _logger2.default)('quill:keyboard'); + +var SHORTKEY = /Mac/i.test(navigator.platform) ? 'metaKey' : 'ctrlKey'; + +var Keyboard = function (_Module) { + _inherits(Keyboard, _Module); + + _createClass(Keyboard, null, [{ + key: 'match', + value: function match(evt, binding) { + binding = normalize(binding); + if (['altKey', 'ctrlKey', 'metaKey', 'shiftKey'].some(function (key) { + return !!binding[key] !== evt[key] && binding[key] !== null; + })) { + return false; + } + return binding.key === (evt.which || evt.keyCode); + } + }]); + + function Keyboard(quill, options) { + _classCallCheck(this, Keyboard); + + var _this = _possibleConstructorReturn(this, (Keyboard.__proto__ || Object.getPrototypeOf(Keyboard)).call(this, quill, options)); + + _this.bindings = {}; + Object.keys(_this.options.bindings).forEach(function (name) { + if (name === 'list autofill' && quill.scroll.whitelist != null && !quill.scroll.whitelist['list']) { + return; + } + if (_this.options.bindings[name]) { + _this.addBinding(_this.options.bindings[name]); + } + }); + _this.addBinding({ key: Keyboard.keys.ENTER, shiftKey: null }, handleEnter); + _this.addBinding({ key: Keyboard.keys.ENTER, metaKey: null, ctrlKey: null, altKey: null }, function () {}); + if (/Firefox/i.test(navigator.userAgent)) { + // Need to handle delete and backspace for Firefox in the general case #1171 + _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true }, handleBackspace); + _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true }, handleDelete); + } else { + _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true, prefix: /^.?$/ }, handleBackspace); + _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true, suffix: /^.?$/ }, handleDelete); + } + _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: false }, handleDeleteRange); + _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: false }, handleDeleteRange); + _this.addBinding({ key: Keyboard.keys.BACKSPACE, altKey: null, ctrlKey: null, metaKey: null, shiftKey: null }, { collapsed: true, offset: 0 }, handleBackspace); + _this.listen(); + return _this; + } + + _createClass(Keyboard, [{ + key: 'addBinding', + value: function addBinding(key) { + var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var handler = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + var binding = normalize(key); + if (binding == null || binding.key == null) { + return debug.warn('Attempted to add invalid keyboard binding', binding); + } + if (typeof context === 'function') { + context = { handler: context }; + } + if (typeof handler === 'function') { + handler = { handler: handler }; + } + binding = (0, _extend2.default)(binding, context, handler); + this.bindings[binding.key] = this.bindings[binding.key] || []; + this.bindings[binding.key].push(binding); + } + }, { + key: 'listen', + value: function listen() { + var _this2 = this; + + this.quill.root.addEventListener('keydown', function (evt) { + if (evt.defaultPrevented) return; + var which = evt.which || evt.keyCode; + var bindings = (_this2.bindings[which] || []).filter(function (binding) { + return Keyboard.match(evt, binding); + }); + if (bindings.length === 0) return; + var range = _this2.quill.getSelection(); + if (range == null || !_this2.quill.hasFocus()) return; + + var _quill$getLine = _this2.quill.getLine(range.index), + _quill$getLine2 = _slicedToArray(_quill$getLine, 2), + line = _quill$getLine2[0], + offset = _quill$getLine2[1]; + + var _quill$getLeaf = _this2.quill.getLeaf(range.index), + _quill$getLeaf2 = _slicedToArray(_quill$getLeaf, 2), + leafStart = _quill$getLeaf2[0], + offsetStart = _quill$getLeaf2[1]; + + var _ref = range.length === 0 ? [leafStart, offsetStart] : _this2.quill.getLeaf(range.index + range.length), + _ref2 = _slicedToArray(_ref, 2), + leafEnd = _ref2[0], + offsetEnd = _ref2[1]; + + var prefixText = leafStart instanceof _parchment2.default.Text ? leafStart.value().slice(0, offsetStart) : ''; + var suffixText = leafEnd instanceof _parchment2.default.Text ? leafEnd.value().slice(offsetEnd) : ''; + var curContext = { + collapsed: range.length === 0, + empty: range.length === 0 && line.length() <= 1, + format: _this2.quill.getFormat(range), + offset: offset, + prefix: prefixText, + suffix: suffixText + }; + var prevented = bindings.some(function (binding) { + if (binding.collapsed != null && binding.collapsed !== curContext.collapsed) return false; + if (binding.empty != null && binding.empty !== curContext.empty) return false; + if (binding.offset != null && binding.offset !== curContext.offset) return false; + if (Array.isArray(binding.format)) { + // any format is present + if (binding.format.every(function (name) { + return curContext.format[name] == null; + })) { + return false; + } + } else if (_typeof(binding.format) === 'object') { + // all formats must match + if (!Object.keys(binding.format).every(function (name) { + if (binding.format[name] === true) return curContext.format[name] != null; + if (binding.format[name] === false) return curContext.format[name] == null; + return (0, _deepEqual2.default)(binding.format[name], curContext.format[name]); + })) { + return false; + } + } + if (binding.prefix != null && !binding.prefix.test(curContext.prefix)) return false; + if (binding.suffix != null && !binding.suffix.test(curContext.suffix)) return false; + return binding.handler.call(_this2, range, curContext) !== true; + }); + if (prevented) { + evt.preventDefault(); + } + }); + } + }]); + + return Keyboard; +}(_module2.default); + +Keyboard.keys = { + BACKSPACE: 8, + TAB: 9, + ENTER: 13, + ESCAPE: 27, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + DELETE: 46 +}; + +Keyboard.DEFAULTS = { + bindings: { + 'bold': makeFormatHandler('bold'), + 'italic': makeFormatHandler('italic'), + 'underline': makeFormatHandler('underline'), + 'indent': { + // highlight tab or tab at beginning of list, indent or blockquote + key: Keyboard.keys.TAB, + format: ['blockquote', 'indent', 'list'], + handler: function handler(range, context) { + if (context.collapsed && context.offset !== 0) return true; + this.quill.format('indent', '+1', _quill2.default.sources.USER); + } + }, + 'outdent': { + key: Keyboard.keys.TAB, + shiftKey: true, + format: ['blockquote', 'indent', 'list'], + // highlight tab or tab at beginning of list, indent or blockquote + handler: function handler(range, context) { + if (context.collapsed && context.offset !== 0) return true; + this.quill.format('indent', '-1', _quill2.default.sources.USER); + } + }, + 'outdent backspace': { + key: Keyboard.keys.BACKSPACE, + collapsed: true, + shiftKey: null, + metaKey: null, + ctrlKey: null, + altKey: null, + format: ['indent', 'list'], + offset: 0, + handler: function handler(range, context) { + if (context.format.indent != null) { + this.quill.format('indent', '-1', _quill2.default.sources.USER); + } else if (context.format.list != null) { + this.quill.format('list', false, _quill2.default.sources.USER); + } + } + }, + 'indent code-block': makeCodeBlockHandler(true), + 'outdent code-block': makeCodeBlockHandler(false), + 'remove tab': { + key: Keyboard.keys.TAB, + shiftKey: true, + collapsed: true, + prefix: /\t$/, + handler: function handler(range) { + this.quill.deleteText(range.index - 1, 1, _quill2.default.sources.USER); + } + }, + 'tab': { + key: Keyboard.keys.TAB, + handler: function handler(range) { + this.quill.history.cutoff(); + var delta = new _quillDelta2.default().retain(range.index).delete(range.length).insert('\t'); + this.quill.updateContents(delta, _quill2.default.sources.USER); + this.quill.history.cutoff(); + this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT); + } + }, + 'list empty enter': { + key: Keyboard.keys.ENTER, + collapsed: true, + format: ['list'], + empty: true, + handler: function handler(range, context) { + this.quill.format('list', false, _quill2.default.sources.USER); + if (context.format.indent) { + this.quill.format('indent', false, _quill2.default.sources.USER); + } + } + }, + 'checklist enter': { + key: Keyboard.keys.ENTER, + collapsed: true, + format: { list: 'checked' }, + handler: function handler(range) { + var _quill$getLine3 = this.quill.getLine(range.index), + _quill$getLine4 = _slicedToArray(_quill$getLine3, 2), + line = _quill$getLine4[0], + offset = _quill$getLine4[1]; + + var formats = (0, _extend2.default)({}, line.formats(), { list: 'checked' }); + var delta = new _quillDelta2.default().retain(range.index).insert('\n', formats).retain(line.length() - offset - 1).retain(1, { list: 'unchecked' }); + this.quill.updateContents(delta, _quill2.default.sources.USER); + this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT); + this.quill.scrollIntoView(); + } + }, + 'header enter': { + key: Keyboard.keys.ENTER, + collapsed: true, + format: ['header'], + suffix: /^$/, + handler: function handler(range, context) { + var _quill$getLine5 = this.quill.getLine(range.index), + _quill$getLine6 = _slicedToArray(_quill$getLine5, 2), + line = _quill$getLine6[0], + offset = _quill$getLine6[1]; + + var delta = new _quillDelta2.default().retain(range.index).insert('\n', context.format).retain(line.length() - offset - 1).retain(1, { header: null }); + this.quill.updateContents(delta, _quill2.default.sources.USER); + this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT); + this.quill.scrollIntoView(); + } + }, + 'list autofill': { + key: ' ', + collapsed: true, + format: { list: false }, + prefix: /^\s*?(\d+\.|-|\*|\[ ?\]|\[x\])$/, + handler: function handler(range, context) { + var length = context.prefix.length; + + var _quill$getLine7 = this.quill.getLine(range.index), + _quill$getLine8 = _slicedToArray(_quill$getLine7, 2), + line = _quill$getLine8[0], + offset = _quill$getLine8[1]; + + if (offset > length) return true; + var value = void 0; + switch (context.prefix.trim()) { + case '[]':case '[ ]': + value = 'unchecked'; + break; + case '[x]': + value = 'checked'; + break; + case '-':case '*': + value = 'bullet'; + break; + default: + value = 'ordered'; + } + this.quill.insertText(range.index, ' ', _quill2.default.sources.USER); + this.quill.history.cutoff(); + var delta = new _quillDelta2.default().retain(range.index - offset).delete(length + 1).retain(line.length() - 2 - offset).retain(1, { list: value }); + this.quill.updateContents(delta, _quill2.default.sources.USER); + this.quill.history.cutoff(); + this.quill.setSelection(range.index - length, _quill2.default.sources.SILENT); + } + }, + 'code exit': { + key: Keyboard.keys.ENTER, + collapsed: true, + format: ['code-block'], + prefix: /\n\n$/, + suffix: /^\s+$/, + handler: function handler(range) { + var _quill$getLine9 = this.quill.getLine(range.index), + _quill$getLine10 = _slicedToArray(_quill$getLine9, 2), + line = _quill$getLine10[0], + offset = _quill$getLine10[1]; + + var delta = new _quillDelta2.default().retain(range.index + line.length() - offset - 2).retain(1, { 'code-block': null }).delete(1); + this.quill.updateContents(delta, _quill2.default.sources.USER); + } + }, + 'embed left': makeEmbedArrowHandler(Keyboard.keys.LEFT, false), + 'embed left shift': makeEmbedArrowHandler(Keyboard.keys.LEFT, true), + 'embed right': makeEmbedArrowHandler(Keyboard.keys.RIGHT, false), + 'embed right shift': makeEmbedArrowHandler(Keyboard.keys.RIGHT, true) + } +}; + +function makeEmbedArrowHandler(key, shiftKey) { + var _ref3; + + var where = key === Keyboard.keys.LEFT ? 'prefix' : 'suffix'; + return _ref3 = { + key: key, + shiftKey: shiftKey, + altKey: null + }, _defineProperty(_ref3, where, /^$/), _defineProperty(_ref3, 'handler', function handler(range) { + var index = range.index; + if (key === Keyboard.keys.RIGHT) { + index += range.length + 1; + } + + var _quill$getLeaf3 = this.quill.getLeaf(index), + _quill$getLeaf4 = _slicedToArray(_quill$getLeaf3, 1), + leaf = _quill$getLeaf4[0]; + + if (!(leaf instanceof _parchment2.default.Embed)) return true; + if (key === Keyboard.keys.LEFT) { + if (shiftKey) { + this.quill.setSelection(range.index - 1, range.length + 1, _quill2.default.sources.USER); + } else { + this.quill.setSelection(range.index - 1, _quill2.default.sources.USER); + } + } else { + if (shiftKey) { + this.quill.setSelection(range.index, range.length + 1, _quill2.default.sources.USER); + } else { + this.quill.setSelection(range.index + range.length + 1, _quill2.default.sources.USER); + } + } + return false; + }), _ref3; +} + +function handleBackspace(range, context) { + if (range.index === 0 || this.quill.getLength() <= 1) return; + + var _quill$getLine11 = this.quill.getLine(range.index), + _quill$getLine12 = _slicedToArray(_quill$getLine11, 1), + line = _quill$getLine12[0]; + + var formats = {}; + if (context.offset === 0) { + var _quill$getLine13 = this.quill.getLine(range.index - 1), + _quill$getLine14 = _slicedToArray(_quill$getLine13, 1), + prev = _quill$getLine14[0]; + + if (prev != null && prev.length() > 1) { + var curFormats = line.formats(); + var prevFormats = this.quill.getFormat(range.index - 1, 1); + formats = _op2.default.attributes.diff(curFormats, prevFormats) || {}; + } + } + // Check for astral symbols + var length = /[\uD800-\uDBFF][\uDC00-\uDFFF]$/.test(context.prefix) ? 2 : 1; + this.quill.deleteText(range.index - length, length, _quill2.default.sources.USER); + if (Object.keys(formats).length > 0) { + this.quill.formatLine(range.index - length, length, formats, _quill2.default.sources.USER); + } + this.quill.focus(); +} + +function handleDelete(range, context) { + // Check for astral symbols + var length = /^[\uD800-\uDBFF][\uDC00-\uDFFF]/.test(context.suffix) ? 2 : 1; + if (range.index >= this.quill.getLength() - length) return; + var formats = {}, + nextLength = 0; + + var _quill$getLine15 = this.quill.getLine(range.index), + _quill$getLine16 = _slicedToArray(_quill$getLine15, 1), + line = _quill$getLine16[0]; + + if (context.offset >= line.length() - 1) { + var _quill$getLine17 = this.quill.getLine(range.index + 1), + _quill$getLine18 = _slicedToArray(_quill$getLine17, 1), + next = _quill$getLine18[0]; + + if (next) { + var curFormats = line.formats(); + var nextFormats = this.quill.getFormat(range.index, 1); + formats = _op2.default.attributes.diff(curFormats, nextFormats) || {}; + nextLength = next.length(); + } + } + this.quill.deleteText(range.index, length, _quill2.default.sources.USER); + if (Object.keys(formats).length > 0) { + this.quill.formatLine(range.index + nextLength - 1, length, formats, _quill2.default.sources.USER); + } +} + +function handleDeleteRange(range) { + var lines = this.quill.getLines(range); + var formats = {}; + if (lines.length > 1) { + var firstFormats = lines[0].formats(); + var lastFormats = lines[lines.length - 1].formats(); + formats = _op2.default.attributes.diff(lastFormats, firstFormats) || {}; + } + this.quill.deleteText(range, _quill2.default.sources.USER); + if (Object.keys(formats).length > 0) { + this.quill.formatLine(range.index, 1, formats, _quill2.default.sources.USER); + } + this.quill.setSelection(range.index, _quill2.default.sources.SILENT); + this.quill.focus(); +} + +function handleEnter(range, context) { + var _this3 = this; + + if (range.length > 0) { + this.quill.scroll.deleteAt(range.index, range.length); // So we do not trigger text-change + } + var lineFormats = Object.keys(context.format).reduce(function (lineFormats, format) { + if (_parchment2.default.query(format, _parchment2.default.Scope.BLOCK) && !Array.isArray(context.format[format])) { + lineFormats[format] = context.format[format]; + } + return lineFormats; + }, {}); + this.quill.insertText(range.index, '\n', lineFormats, _quill2.default.sources.USER); + // Earlier scroll.deleteAt might have messed up our selection, + // so insertText's built in selection preservation is not reliable + this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT); + this.quill.focus(); + Object.keys(context.format).forEach(function (name) { + if (lineFormats[name] != null) return; + if (Array.isArray(context.format[name])) return; + if (name === 'link') return; + _this3.quill.format(name, context.format[name], _quill2.default.sources.USER); + }); +} + +function makeCodeBlockHandler(indent) { + return { + key: Keyboard.keys.TAB, + shiftKey: !indent, + format: { 'code-block': true }, + handler: function handler(range) { + var CodeBlock = _parchment2.default.query('code-block'); + var index = range.index, + length = range.length; + + var _quill$scroll$descend = this.quill.scroll.descendant(CodeBlock, index), + _quill$scroll$descend2 = _slicedToArray(_quill$scroll$descend, 2), + block = _quill$scroll$descend2[0], + offset = _quill$scroll$descend2[1]; + + if (block == null) return; + var scrollIndex = this.quill.getIndex(block); + var start = block.newlineIndex(offset, true) + 1; + var end = block.newlineIndex(scrollIndex + offset + length); + var lines = block.domNode.textContent.slice(start, end).split('\n'); + offset = 0; + lines.forEach(function (line, i) { + if (indent) { + block.insertAt(start + offset, CodeBlock.TAB); + offset += CodeBlock.TAB.length; + if (i === 0) { + index += CodeBlock.TAB.length; + } else { + length += CodeBlock.TAB.length; + } + } else if (line.startsWith(CodeBlock.TAB)) { + block.deleteAt(start + offset, CodeBlock.TAB.length); + offset -= CodeBlock.TAB.length; + if (i === 0) { + index -= CodeBlock.TAB.length; + } else { + length -= CodeBlock.TAB.length; + } + } + offset += line.length + 1; + }); + this.quill.update(_quill2.default.sources.USER); + this.quill.setSelection(index, length, _quill2.default.sources.SILENT); + } + }; +} + +function makeFormatHandler(format) { + return { + key: format[0].toUpperCase(), + shortKey: true, + handler: function handler(range, context) { + this.quill.format(format, !context.format[format], _quill2.default.sources.USER); + } + }; +} + +function normalize(binding) { + if (typeof binding === 'string' || typeof binding === 'number') { + return normalize({ key: binding }); + } + if ((typeof binding === 'undefined' ? 'undefined' : _typeof(binding)) === 'object') { + binding = (0, _clone2.default)(binding, false); + } + if (typeof binding.key === 'string') { + if (Keyboard.keys[binding.key.toUpperCase()] != null) { + binding.key = Keyboard.keys[binding.key.toUpperCase()]; + } else if (binding.key.length === 1) { + binding.key = binding.key.toUpperCase().charCodeAt(0); + } else { + return null; + } + } + if (binding.shortKey) { + binding[SHORTKEY] = binding.shortKey; + delete binding.shortKey; + } + return binding; +} + +exports.default = Keyboard; +exports.SHORTKEY = SHORTKEY; + +/***/ }), +/* 24 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Cursor = function (_Parchment$Embed) { + _inherits(Cursor, _Parchment$Embed); + + _createClass(Cursor, null, [{ + key: 'value', + value: function value() { + return undefined; + } + }]); + + function Cursor(domNode, selection) { + _classCallCheck(this, Cursor); + + var _this = _possibleConstructorReturn(this, (Cursor.__proto__ || Object.getPrototypeOf(Cursor)).call(this, domNode)); + + _this.selection = selection; + _this.textNode = document.createTextNode(Cursor.CONTENTS); + _this.domNode.appendChild(_this.textNode); + _this._length = 0; + return _this; + } + + _createClass(Cursor, [{ + key: 'detach', + value: function detach() { + // super.detach() will also clear domNode.__blot + if (this.parent != null) this.parent.removeChild(this); + } + }, { + key: 'format', + value: function format(name, value) { + if (this._length !== 0) { + return _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'format', this).call(this, name, value); + } + var target = this, + index = 0; + while (target != null && target.statics.scope !== _parchment2.default.Scope.BLOCK_BLOT) { + index += target.offset(target.parent); + target = target.parent; + } + if (target != null) { + this._length = Cursor.CONTENTS.length; + target.optimize(); + target.formatAt(index, Cursor.CONTENTS.length, name, value); + this._length = 0; + } + } + }, { + key: 'index', + value: function index(node, offset) { + if (node === this.textNode) return 0; + return _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'index', this).call(this, node, offset); + } + }, { + key: 'length', + value: function length() { + return this._length; + } + }, { + key: 'position', + value: function position() { + return [this.textNode, this.textNode.data.length]; + } + }, { + key: 'remove', + value: function remove() { + _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'remove', this).call(this); + this.parent = null; + } + }, { + key: 'restore', + value: function restore() { + if (this.selection.composing || this.parent == null) return; + var textNode = this.textNode; + var range = this.selection.getNativeRange(); + var restoreText = void 0, + start = void 0, + end = void 0; + if (range != null && range.start.node === textNode && range.end.node === textNode) { + var _ref = [textNode, range.start.offset, range.end.offset]; + restoreText = _ref[0]; + start = _ref[1]; + end = _ref[2]; + } + // Link format will insert text outside of anchor tag + while (this.domNode.lastChild != null && this.domNode.lastChild !== this.textNode) { + this.domNode.parentNode.insertBefore(this.domNode.lastChild, this.domNode); + } + if (this.textNode.data !== Cursor.CONTENTS) { + var text = this.textNode.data.split(Cursor.CONTENTS).join(''); + if (this.next instanceof _text2.default) { + restoreText = this.next.domNode; + this.next.insertAt(0, text); + this.textNode.data = Cursor.CONTENTS; + } else { + this.textNode.data = text; + this.parent.insertBefore(_parchment2.default.create(this.textNode), this); + this.textNode = document.createTextNode(Cursor.CONTENTS); + this.domNode.appendChild(this.textNode); + } + } + this.remove(); + if (start != null) { + var _map = [start, end].map(function (offset) { + return Math.max(0, Math.min(restoreText.data.length, offset - 1)); + }); + + var _map2 = _slicedToArray(_map, 2); + + start = _map2[0]; + end = _map2[1]; + + return { + startNode: restoreText, + startOffset: start, + endNode: restoreText, + endOffset: end + }; + } + } + }, { + key: 'update', + value: function update(mutations, context) { + var _this2 = this; + + if (mutations.some(function (mutation) { + return mutation.type === 'characterData' && mutation.target === _this2.textNode; + })) { + var range = this.restore(); + if (range) context.range = range; + } + } + }, { + key: 'value', + value: function value() { + return ''; + } + }]); + + return Cursor; +}(_parchment2.default.Embed); + +Cursor.blotName = 'cursor'; +Cursor.className = 'ql-cursor'; +Cursor.tagName = 'span'; +Cursor.CONTENTS = '\uFEFF'; // Zero width no break space + + +exports.default = Cursor; + +/***/ }), +/* 25 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Container = function (_Parchment$Container) { + _inherits(Container, _Parchment$Container); + + function Container() { + _classCallCheck(this, Container); + + return _possibleConstructorReturn(this, (Container.__proto__ || Object.getPrototypeOf(Container)).apply(this, arguments)); + } + + return Container; +}(_parchment2.default.Container); + +Container.allowedChildren = [_block2.default, _block.BlockEmbed, Container]; + +exports.default = Container; + +/***/ }), +/* 26 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ColorStyle = exports.ColorClass = exports.ColorAttributor = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var ColorAttributor = function (_Parchment$Attributor) { + _inherits(ColorAttributor, _Parchment$Attributor); + + function ColorAttributor() { + _classCallCheck(this, ColorAttributor); + + return _possibleConstructorReturn(this, (ColorAttributor.__proto__ || Object.getPrototypeOf(ColorAttributor)).apply(this, arguments)); + } + + _createClass(ColorAttributor, [{ + key: 'value', + value: function value(domNode) { + var value = _get(ColorAttributor.prototype.__proto__ || Object.getPrototypeOf(ColorAttributor.prototype), 'value', this).call(this, domNode); + if (!value.startsWith('rgb(')) return value; + value = value.replace(/^[^\d]+/, '').replace(/[^\d]+$/, ''); + return '#' + value.split(',').map(function (component) { + return ('00' + parseInt(component).toString(16)).slice(-2); + }).join(''); + } + }]); + + return ColorAttributor; +}(_parchment2.default.Attributor.Style); + +var ColorClass = new _parchment2.default.Attributor.Class('color', 'ql-color', { + scope: _parchment2.default.Scope.INLINE +}); +var ColorStyle = new ColorAttributor('color', 'color', { + scope: _parchment2.default.Scope.INLINE +}); + +exports.ColorAttributor = ColorAttributor; +exports.ColorClass = ColorClass; +exports.ColorStyle = ColorStyle; + +/***/ }), +/* 27 */, +/* 28 */, +/* 29 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +var _break = __webpack_require__(16); + +var _break2 = _interopRequireDefault(_break); + +var _container = __webpack_require__(25); + +var _container2 = _interopRequireDefault(_container); + +var _cursor = __webpack_require__(24); + +var _cursor2 = _interopRequireDefault(_cursor); + +var _embed = __webpack_require__(35); + +var _embed2 = _interopRequireDefault(_embed); + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +var _scroll = __webpack_require__(22); + +var _scroll2 = _interopRequireDefault(_scroll); + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +var _clipboard = __webpack_require__(55); + +var _clipboard2 = _interopRequireDefault(_clipboard); + +var _history = __webpack_require__(42); + +var _history2 = _interopRequireDefault(_history); + +var _keyboard = __webpack_require__(23); + +var _keyboard2 = _interopRequireDefault(_keyboard); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +_quill2.default.register({ + 'blots/block': _block2.default, + 'blots/block/embed': _block.BlockEmbed, + 'blots/break': _break2.default, + 'blots/container': _container2.default, + 'blots/cursor': _cursor2.default, + 'blots/embed': _embed2.default, + 'blots/inline': _inline2.default, + 'blots/scroll': _scroll2.default, + 'blots/text': _text2.default, + + 'modules/clipboard': _clipboard2.default, + 'modules/history': _history2.default, + 'modules/keyboard': _keyboard2.default +}); + +_parchment2.default.register(_block2.default, _break2.default, _cursor2.default, _inline2.default, _scroll2.default, _text2.default); + +exports.default = _quill2.default; + +/***/ }), +/* 30 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var Registry = __webpack_require__(1); +var ShadowBlot = /** @class */ (function () { + function ShadowBlot(domNode) { + this.domNode = domNode; + // @ts-ignore + this.domNode[Registry.DATA_KEY] = { blot: this }; + } + Object.defineProperty(ShadowBlot.prototype, "statics", { + // Hack for accessing inherited static methods + get: function () { + return this.constructor; + }, + enumerable: true, + configurable: true + }); + ShadowBlot.create = function (value) { + if (this.tagName == null) { + throw new Registry.ParchmentError('Blot definition missing tagName'); + } + var node; + if (Array.isArray(this.tagName)) { + if (typeof value === 'string') { + value = value.toUpperCase(); + if (parseInt(value).toString() === value) { + value = parseInt(value); + } + } + if (typeof value === 'number') { + node = document.createElement(this.tagName[value - 1]); + } + else if (this.tagName.indexOf(value) > -1) { + node = document.createElement(value); + } + else { + node = document.createElement(this.tagName[0]); + } + } + else { + node = document.createElement(this.tagName); + } + if (this.className) { + node.classList.add(this.className); + } + return node; + }; + ShadowBlot.prototype.attach = function () { + if (this.parent != null) { + this.scroll = this.parent.scroll; + } + }; + ShadowBlot.prototype.clone = function () { + var domNode = this.domNode.cloneNode(false); + return Registry.create(domNode); + }; + ShadowBlot.prototype.detach = function () { + if (this.parent != null) + this.parent.removeChild(this); + // @ts-ignore + delete this.domNode[Registry.DATA_KEY]; + }; + ShadowBlot.prototype.deleteAt = function (index, length) { + var blot = this.isolate(index, length); + blot.remove(); + }; + ShadowBlot.prototype.formatAt = function (index, length, name, value) { + var blot = this.isolate(index, length); + if (Registry.query(name, Registry.Scope.BLOT) != null && value) { + blot.wrap(name, value); + } + else if (Registry.query(name, Registry.Scope.ATTRIBUTE) != null) { + var parent = Registry.create(this.statics.scope); + blot.wrap(parent); + parent.format(name, value); + } + }; + ShadowBlot.prototype.insertAt = function (index, value, def) { + var blot = def == null ? Registry.create('text', value) : Registry.create(value, def); + var ref = this.split(index); + this.parent.insertBefore(blot, ref); + }; + ShadowBlot.prototype.insertInto = function (parentBlot, refBlot) { + if (refBlot === void 0) { refBlot = null; } + if (this.parent != null) { + this.parent.children.remove(this); + } + var refDomNode = null; + parentBlot.children.insertBefore(this, refBlot); + if (refBlot != null) { + refDomNode = refBlot.domNode; + } + if (this.domNode.parentNode != parentBlot.domNode || + this.domNode.nextSibling != refDomNode) { + parentBlot.domNode.insertBefore(this.domNode, refDomNode); + } + this.parent = parentBlot; + this.attach(); + }; + ShadowBlot.prototype.isolate = function (index, length) { + var target = this.split(index); + target.split(length); + return target; + }; + ShadowBlot.prototype.length = function () { + return 1; + }; + ShadowBlot.prototype.offset = function (root) { + if (root === void 0) { root = this.parent; } + if (this.parent == null || this == root) + return 0; + return this.parent.children.offset(this) + this.parent.offset(root); + }; + ShadowBlot.prototype.optimize = function (context) { + // TODO clean up once we use WeakMap + // @ts-ignore + if (this.domNode[Registry.DATA_KEY] != null) { + // @ts-ignore + delete this.domNode[Registry.DATA_KEY].mutations; + } + }; + ShadowBlot.prototype.remove = function () { + if (this.domNode.parentNode != null) { + this.domNode.parentNode.removeChild(this.domNode); + } + this.detach(); + }; + ShadowBlot.prototype.replace = function (target) { + if (target.parent == null) + return; + target.parent.insertBefore(this, target.next); + target.remove(); + }; + ShadowBlot.prototype.replaceWith = function (name, value) { + var replacement = typeof name === 'string' ? Registry.create(name, value) : name; + replacement.replace(this); + return replacement; + }; + ShadowBlot.prototype.split = function (index, force) { + return index === 0 ? this : this.next; + }; + ShadowBlot.prototype.update = function (mutations, context) { + // Nothing to do by default + }; + ShadowBlot.prototype.wrap = function (name, value) { + var wrapper = typeof name === 'string' ? Registry.create(name, value) : name; + if (this.parent != null) { + this.parent.insertBefore(wrapper, this.next); + } + wrapper.appendChild(this); + return wrapper; + }; + ShadowBlot.blotName = 'abstract'; + return ShadowBlot; +}()); +exports.default = ShadowBlot; + + +/***/ }), +/* 31 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var attributor_1 = __webpack_require__(12); +var class_1 = __webpack_require__(32); +var style_1 = __webpack_require__(33); +var Registry = __webpack_require__(1); +var AttributorStore = /** @class */ (function () { + function AttributorStore(domNode) { + this.attributes = {}; + this.domNode = domNode; + this.build(); + } + AttributorStore.prototype.attribute = function (attribute, value) { + // verb + if (value) { + if (attribute.add(this.domNode, value)) { + if (attribute.value(this.domNode) != null) { + this.attributes[attribute.attrName] = attribute; + } + else { + delete this.attributes[attribute.attrName]; + } + } + } + else { + attribute.remove(this.domNode); + delete this.attributes[attribute.attrName]; + } + }; + AttributorStore.prototype.build = function () { + var _this = this; + this.attributes = {}; + var attributes = attributor_1.default.keys(this.domNode); + var classes = class_1.default.keys(this.domNode); + var styles = style_1.default.keys(this.domNode); + attributes + .concat(classes) + .concat(styles) + .forEach(function (name) { + var attr = Registry.query(name, Registry.Scope.ATTRIBUTE); + if (attr instanceof attributor_1.default) { + _this.attributes[attr.attrName] = attr; + } + }); + }; + AttributorStore.prototype.copy = function (target) { + var _this = this; + Object.keys(this.attributes).forEach(function (key) { + var value = _this.attributes[key].value(_this.domNode); + target.format(key, value); + }); + }; + AttributorStore.prototype.move = function (target) { + var _this = this; + this.copy(target); + Object.keys(this.attributes).forEach(function (key) { + _this.attributes[key].remove(_this.domNode); + }); + this.attributes = {}; + }; + AttributorStore.prototype.values = function () { + var _this = this; + return Object.keys(this.attributes).reduce(function (attributes, name) { + attributes[name] = _this.attributes[name].value(_this.domNode); + return attributes; + }, {}); + }; + return AttributorStore; +}()); +exports.default = AttributorStore; + + +/***/ }), +/* 32 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var attributor_1 = __webpack_require__(12); +function match(node, prefix) { + var className = node.getAttribute('class') || ''; + return className.split(/\s+/).filter(function (name) { + return name.indexOf(prefix + "-") === 0; + }); +} +var ClassAttributor = /** @class */ (function (_super) { + __extends(ClassAttributor, _super); + function ClassAttributor() { + return _super !== null && _super.apply(this, arguments) || this; + } + ClassAttributor.keys = function (node) { + return (node.getAttribute('class') || '').split(/\s+/).map(function (name) { + return name + .split('-') + .slice(0, -1) + .join('-'); + }); + }; + ClassAttributor.prototype.add = function (node, value) { + if (!this.canAdd(node, value)) + return false; + this.remove(node); + node.classList.add(this.keyName + "-" + value); + return true; + }; + ClassAttributor.prototype.remove = function (node) { + var matches = match(node, this.keyName); + matches.forEach(function (name) { + node.classList.remove(name); + }); + if (node.classList.length === 0) { + node.removeAttribute('class'); + } + }; + ClassAttributor.prototype.value = function (node) { + var result = match(node, this.keyName)[0] || ''; + var value = result.slice(this.keyName.length + 1); // +1 for hyphen + return this.canAdd(node, value) ? value : ''; + }; + return ClassAttributor; +}(attributor_1.default)); +exports.default = ClassAttributor; + + +/***/ }), +/* 33 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var attributor_1 = __webpack_require__(12); +function camelize(name) { + var parts = name.split('-'); + var rest = parts + .slice(1) + .map(function (part) { + return part[0].toUpperCase() + part.slice(1); + }) + .join(''); + return parts[0] + rest; +} +var StyleAttributor = /** @class */ (function (_super) { + __extends(StyleAttributor, _super); + function StyleAttributor() { + return _super !== null && _super.apply(this, arguments) || this; + } + StyleAttributor.keys = function (node) { + return (node.getAttribute('style') || '').split(';').map(function (value) { + var arr = value.split(':'); + return arr[0].trim(); + }); + }; + StyleAttributor.prototype.add = function (node, value) { + if (!this.canAdd(node, value)) + return false; + // @ts-ignore + node.style[camelize(this.keyName)] = value; + return true; + }; + StyleAttributor.prototype.remove = function (node) { + // @ts-ignore + node.style[camelize(this.keyName)] = ''; + if (!node.getAttribute('style')) { + node.removeAttribute('style'); + } + }; + StyleAttributor.prototype.value = function (node) { + // @ts-ignore + var value = node.style[camelize(this.keyName)]; + return this.canAdd(node, value) ? value : ''; + }; + return StyleAttributor; +}(attributor_1.default)); +exports.default = StyleAttributor; + + +/***/ }), +/* 34 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Theme = function () { + function Theme(quill, options) { + _classCallCheck(this, Theme); + + this.quill = quill; + this.options = options; + this.modules = {}; + } + + _createClass(Theme, [{ + key: 'init', + value: function init() { + var _this = this; + + Object.keys(this.options.modules).forEach(function (name) { + if (_this.modules[name] == null) { + _this.addModule(name); + } + }); + } + }, { + key: 'addModule', + value: function addModule(name) { + var moduleClass = this.quill.constructor.import('modules/' + name); + this.modules[name] = new moduleClass(this.quill, this.options.modules[name] || {}); + return this.modules[name]; + } + }]); + + return Theme; +}(); + +Theme.DEFAULTS = { + modules: {} +}; +Theme.themes = { + 'default': Theme +}; + +exports.default = Theme; + +/***/ }), +/* 35 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var GUARD_TEXT = '\uFEFF'; + +var Embed = function (_Parchment$Embed) { + _inherits(Embed, _Parchment$Embed); + + function Embed(node) { + _classCallCheck(this, Embed); + + var _this = _possibleConstructorReturn(this, (Embed.__proto__ || Object.getPrototypeOf(Embed)).call(this, node)); + + _this.contentNode = document.createElement('span'); + _this.contentNode.setAttribute('contenteditable', false); + [].slice.call(_this.domNode.childNodes).forEach(function (childNode) { + _this.contentNode.appendChild(childNode); + }); + _this.leftGuard = document.createTextNode(GUARD_TEXT); + _this.rightGuard = document.createTextNode(GUARD_TEXT); + _this.domNode.appendChild(_this.leftGuard); + _this.domNode.appendChild(_this.contentNode); + _this.domNode.appendChild(_this.rightGuard); + return _this; + } + + _createClass(Embed, [{ + key: 'index', + value: function index(node, offset) { + if (node === this.leftGuard) return 0; + if (node === this.rightGuard) return 1; + return _get(Embed.prototype.__proto__ || Object.getPrototypeOf(Embed.prototype), 'index', this).call(this, node, offset); + } + }, { + key: 'restore', + value: function restore(node) { + var range = void 0, + textNode = void 0; + var text = node.data.split(GUARD_TEXT).join(''); + if (node === this.leftGuard) { + if (this.prev instanceof _text2.default) { + var prevLength = this.prev.length(); + this.prev.insertAt(prevLength, text); + range = { + startNode: this.prev.domNode, + startOffset: prevLength + text.length + }; + } else { + textNode = document.createTextNode(text); + this.parent.insertBefore(_parchment2.default.create(textNode), this); + range = { + startNode: textNode, + startOffset: text.length + }; + } + } else if (node === this.rightGuard) { + if (this.next instanceof _text2.default) { + this.next.insertAt(0, text); + range = { + startNode: this.next.domNode, + startOffset: text.length + }; + } else { + textNode = document.createTextNode(text); + this.parent.insertBefore(_parchment2.default.create(textNode), this.next); + range = { + startNode: textNode, + startOffset: text.length + }; + } + } + node.data = GUARD_TEXT; + return range; + } + }, { + key: 'update', + value: function update(mutations, context) { + var _this2 = this; + + mutations.forEach(function (mutation) { + if (mutation.type === 'characterData' && (mutation.target === _this2.leftGuard || mutation.target === _this2.rightGuard)) { + var range = _this2.restore(mutation.target); + if (range) context.range = range; + } + }); + } + }]); + + return Embed; +}(_parchment2.default.Embed); + +exports.default = Embed; + +/***/ }), +/* 36 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.AlignStyle = exports.AlignClass = exports.AlignAttribute = undefined; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var config = { + scope: _parchment2.default.Scope.BLOCK, + whitelist: ['right', 'center', 'justify'] +}; + +var AlignAttribute = new _parchment2.default.Attributor.Attribute('align', 'align', config); +var AlignClass = new _parchment2.default.Attributor.Class('align', 'ql-align', config); +var AlignStyle = new _parchment2.default.Attributor.Style('align', 'text-align', config); + +exports.AlignAttribute = AlignAttribute; +exports.AlignClass = AlignClass; +exports.AlignStyle = AlignStyle; + +/***/ }), +/* 37 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.BackgroundStyle = exports.BackgroundClass = undefined; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _color = __webpack_require__(26); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var BackgroundClass = new _parchment2.default.Attributor.Class('background', 'ql-bg', { + scope: _parchment2.default.Scope.INLINE +}); +var BackgroundStyle = new _color.ColorAttributor('background', 'background-color', { + scope: _parchment2.default.Scope.INLINE +}); + +exports.BackgroundClass = BackgroundClass; +exports.BackgroundStyle = BackgroundStyle; + +/***/ }), +/* 38 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DirectionStyle = exports.DirectionClass = exports.DirectionAttribute = undefined; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var config = { + scope: _parchment2.default.Scope.BLOCK, + whitelist: ['rtl'] +}; + +var DirectionAttribute = new _parchment2.default.Attributor.Attribute('direction', 'dir', config); +var DirectionClass = new _parchment2.default.Attributor.Class('direction', 'ql-direction', config); +var DirectionStyle = new _parchment2.default.Attributor.Style('direction', 'direction', config); + +exports.DirectionAttribute = DirectionAttribute; +exports.DirectionClass = DirectionClass; +exports.DirectionStyle = DirectionStyle; + +/***/ }), +/* 39 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.FontClass = exports.FontStyle = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var config = { + scope: _parchment2.default.Scope.INLINE, + whitelist: ['serif', 'monospace'] +}; + +var FontClass = new _parchment2.default.Attributor.Class('font', 'ql-font', config); + +var FontStyleAttributor = function (_Parchment$Attributor) { + _inherits(FontStyleAttributor, _Parchment$Attributor); + + function FontStyleAttributor() { + _classCallCheck(this, FontStyleAttributor); + + return _possibleConstructorReturn(this, (FontStyleAttributor.__proto__ || Object.getPrototypeOf(FontStyleAttributor)).apply(this, arguments)); + } + + _createClass(FontStyleAttributor, [{ + key: 'value', + value: function value(node) { + return _get(FontStyleAttributor.prototype.__proto__ || Object.getPrototypeOf(FontStyleAttributor.prototype), 'value', this).call(this, node).replace(/["']/g, ''); + } + }]); + + return FontStyleAttributor; +}(_parchment2.default.Attributor.Style); + +var FontStyle = new FontStyleAttributor('font', 'font-family', config); + +exports.FontStyle = FontStyle; +exports.FontClass = FontClass; + +/***/ }), +/* 40 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.SizeStyle = exports.SizeClass = undefined; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var SizeClass = new _parchment2.default.Attributor.Class('size', 'ql-size', { + scope: _parchment2.default.Scope.INLINE, + whitelist: ['small', 'large', 'huge'] +}); +var SizeStyle = new _parchment2.default.Attributor.Style('size', 'font-size', { + scope: _parchment2.default.Scope.INLINE, + whitelist: ['10px', '18px', '32px'] +}); + +exports.SizeClass = SizeClass; +exports.SizeStyle = SizeStyle; + +/***/ }), +/* 41 */, +/* 42 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getLastChangeIndex = exports.default = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var History = function (_Module) { + _inherits(History, _Module); + + function History(quill, options) { + _classCallCheck(this, History); + + var _this = _possibleConstructorReturn(this, (History.__proto__ || Object.getPrototypeOf(History)).call(this, quill, options)); + + _this.lastRecorded = 0; + _this.ignoreChange = false; + _this.clear(); + _this.quill.on(_quill2.default.events.EDITOR_CHANGE, function (eventName, delta, oldDelta, source) { + if (eventName !== _quill2.default.events.TEXT_CHANGE || _this.ignoreChange) return; + if (!_this.options.userOnly || source === _quill2.default.sources.USER) { + _this.record(delta, oldDelta); + } else { + _this.transform(delta); + } + }); + _this.quill.keyboard.addBinding({ key: 'Z', shortKey: true }, _this.undo.bind(_this)); + _this.quill.keyboard.addBinding({ key: 'Z', shortKey: true, shiftKey: true }, _this.redo.bind(_this)); + if (/Win/i.test(navigator.platform)) { + _this.quill.keyboard.addBinding({ key: 'Y', shortKey: true }, _this.redo.bind(_this)); + } + return _this; + } + + _createClass(History, [{ + key: 'change', + value: function change(source, dest) { + if (this.stack[source].length === 0) return; + var delta = this.stack[source].pop(); + this.stack[dest].push(delta); + this.lastRecorded = 0; + this.ignoreChange = true; + this.quill.updateContents(delta[source], _quill2.default.sources.USER); + this.ignoreChange = false; + var index = getLastChangeIndex(delta[source]); + this.quill.setSelection(index); + } + }, { + key: 'clear', + value: function clear() { + this.stack = { undo: [], redo: [] }; + } + }, { + key: 'cutoff', + value: function cutoff() { + this.lastRecorded = 0; + } + }, { + key: 'record', + value: function record(changeDelta, oldDelta) { + if (changeDelta.ops.length === 0) return; + this.stack.redo = []; + var undoDelta = this.quill.getContents().diff(oldDelta); + var timestamp = Date.now(); + if (this.lastRecorded + this.options.delay > timestamp && this.stack.undo.length > 0) { + var delta = this.stack.undo.pop(); + undoDelta = undoDelta.compose(delta.undo); + changeDelta = delta.redo.compose(changeDelta); + } else { + this.lastRecorded = timestamp; + } + this.stack.undo.push({ + redo: changeDelta, + undo: undoDelta + }); + if (this.stack.undo.length > this.options.maxStack) { + this.stack.undo.shift(); + } + } + }, { + key: 'redo', + value: function redo() { + this.change('redo', 'undo'); + } + }, { + key: 'transform', + value: function transform(delta) { + this.stack.undo.forEach(function (change) { + change.undo = delta.transform(change.undo, true); + change.redo = delta.transform(change.redo, true); + }); + this.stack.redo.forEach(function (change) { + change.undo = delta.transform(change.undo, true); + change.redo = delta.transform(change.redo, true); + }); + } + }, { + key: 'undo', + value: function undo() { + this.change('undo', 'redo'); + } + }]); + + return History; +}(_module2.default); + +History.DEFAULTS = { + delay: 1000, + maxStack: 100, + userOnly: false +}; + +function endsWithNewlineChange(delta) { + var lastOp = delta.ops[delta.ops.length - 1]; + if (lastOp == null) return false; + if (lastOp.insert != null) { + return typeof lastOp.insert === 'string' && lastOp.insert.endsWith('\n'); + } + if (lastOp.attributes != null) { + return Object.keys(lastOp.attributes).some(function (attr) { + return _parchment2.default.query(attr, _parchment2.default.Scope.BLOCK) != null; + }); + } + return false; +} + +function getLastChangeIndex(delta) { + var deleteLength = delta.reduce(function (length, op) { + length += op.delete || 0; + return length; + }, 0); + var changeIndex = delta.length() - deleteLength; + if (endsWithNewlineChange(delta)) { + changeIndex -= 1; + } + return changeIndex; +} + +exports.default = History; +exports.getLastChangeIndex = getLastChangeIndex; + +/***/ }), +/* 43 */, +/* 44 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var LinkedList = /** @class */ (function () { + function LinkedList() { + this.head = this.tail = null; + this.length = 0; + } + LinkedList.prototype.append = function () { + var nodes = []; + for (var _i = 0; _i < arguments.length; _i++) { + nodes[_i] = arguments[_i]; + } + this.insertBefore(nodes[0], null); + if (nodes.length > 1) { + this.append.apply(this, nodes.slice(1)); + } + }; + LinkedList.prototype.contains = function (node) { + var cur, next = this.iterator(); + while ((cur = next())) { + if (cur === node) + return true; + } + return false; + }; + LinkedList.prototype.insertBefore = function (node, refNode) { + if (!node) + return; + node.next = refNode; + if (refNode != null) { + node.prev = refNode.prev; + if (refNode.prev != null) { + refNode.prev.next = node; + } + refNode.prev = node; + if (refNode === this.head) { + this.head = node; + } + } + else if (this.tail != null) { + this.tail.next = node; + node.prev = this.tail; + this.tail = node; + } + else { + node.prev = null; + this.head = this.tail = node; + } + this.length += 1; + }; + LinkedList.prototype.offset = function (target) { + var index = 0, cur = this.head; + while (cur != null) { + if (cur === target) + return index; + index += cur.length(); + cur = cur.next; + } + return -1; + }; + LinkedList.prototype.remove = function (node) { + if (!this.contains(node)) + return; + if (node.prev != null) + node.prev.next = node.next; + if (node.next != null) + node.next.prev = node.prev; + if (node === this.head) + this.head = node.next; + if (node === this.tail) + this.tail = node.prev; + this.length -= 1; + }; + LinkedList.prototype.iterator = function (curNode) { + if (curNode === void 0) { curNode = this.head; } + // TODO use yield when we can + return function () { + var ret = curNode; + if (curNode != null) + curNode = curNode.next; + return ret; + }; + }; + LinkedList.prototype.find = function (index, inclusive) { + if (inclusive === void 0) { inclusive = false; } + var cur, next = this.iterator(); + while ((cur = next())) { + var length = cur.length(); + if (index < length || + (inclusive && index === length && (cur.next == null || cur.next.length() !== 0))) { + return [cur, index]; + } + index -= length; + } + return [null, 0]; + }; + LinkedList.prototype.forEach = function (callback) { + var cur, next = this.iterator(); + while ((cur = next())) { + callback(cur); + } + }; + LinkedList.prototype.forEachAt = function (index, length, callback) { + if (length <= 0) + return; + var _a = this.find(index), startNode = _a[0], offset = _a[1]; + var cur, curIndex = index - offset, next = this.iterator(startNode); + while ((cur = next()) && curIndex < index + length) { + var curLength = cur.length(); + if (index > curIndex) { + callback(cur, index - curIndex, Math.min(length, curIndex + curLength - index)); + } + else { + callback(cur, 0, Math.min(curLength, index + length - curIndex)); + } + curIndex += curLength; + } + }; + LinkedList.prototype.map = function (callback) { + return this.reduce(function (memo, cur) { + memo.push(callback(cur)); + return memo; + }, []); + }; + LinkedList.prototype.reduce = function (callback, memo) { + var cur, next = this.iterator(); + while ((cur = next())) { + memo = callback(memo, cur); + } + return memo; + }; + return LinkedList; +}()); +exports.default = LinkedList; + + +/***/ }), +/* 45 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var container_1 = __webpack_require__(17); +var Registry = __webpack_require__(1); +var OBSERVER_CONFIG = { + attributes: true, + characterData: true, + characterDataOldValue: true, + childList: true, + subtree: true, +}; +var MAX_OPTIMIZE_ITERATIONS = 100; +var ScrollBlot = /** @class */ (function (_super) { + __extends(ScrollBlot, _super); + function ScrollBlot(node) { + var _this = _super.call(this, node) || this; + _this.scroll = _this; + _this.observer = new MutationObserver(function (mutations) { + _this.update(mutations); + }); + _this.observer.observe(_this.domNode, OBSERVER_CONFIG); + _this.attach(); + return _this; + } + ScrollBlot.prototype.detach = function () { + _super.prototype.detach.call(this); + this.observer.disconnect(); + }; + ScrollBlot.prototype.deleteAt = function (index, length) { + this.update(); + if (index === 0 && length === this.length()) { + this.children.forEach(function (child) { + child.remove(); + }); + } + else { + _super.prototype.deleteAt.call(this, index, length); + } + }; + ScrollBlot.prototype.formatAt = function (index, length, name, value) { + this.update(); + _super.prototype.formatAt.call(this, index, length, name, value); + }; + ScrollBlot.prototype.insertAt = function (index, value, def) { + this.update(); + _super.prototype.insertAt.call(this, index, value, def); + }; + ScrollBlot.prototype.optimize = function (mutations, context) { + var _this = this; + if (mutations === void 0) { mutations = []; } + if (context === void 0) { context = {}; } + _super.prototype.optimize.call(this, context); + // We must modify mutations directly, cannot make copy and then modify + var records = [].slice.call(this.observer.takeRecords()); + // Array.push currently seems to be implemented by a non-tail recursive function + // so we cannot just mutations.push.apply(mutations, this.observer.takeRecords()); + while (records.length > 0) + mutations.push(records.pop()); + // TODO use WeakMap + var mark = function (blot, markParent) { + if (markParent === void 0) { markParent = true; } + if (blot == null || blot === _this) + return; + if (blot.domNode.parentNode == null) + return; + // @ts-ignore + if (blot.domNode[Registry.DATA_KEY].mutations == null) { + // @ts-ignore + blot.domNode[Registry.DATA_KEY].mutations = []; + } + if (markParent) + mark(blot.parent); + }; + var optimize = function (blot) { + // Post-order traversal + if ( + // @ts-ignore + blot.domNode[Registry.DATA_KEY] == null || + // @ts-ignore + blot.domNode[Registry.DATA_KEY].mutations == null) { + return; + } + if (blot instanceof container_1.default) { + blot.children.forEach(optimize); + } + blot.optimize(context); + }; + var remaining = mutations; + for (var i = 0; remaining.length > 0; i += 1) { + if (i >= MAX_OPTIMIZE_ITERATIONS) { + throw new Error('[Parchment] Maximum optimize iterations reached'); + } + remaining.forEach(function (mutation) { + var blot = Registry.find(mutation.target, true); + if (blot == null) + return; + if (blot.domNode === mutation.target) { + if (mutation.type === 'childList') { + mark(Registry.find(mutation.previousSibling, false)); + [].forEach.call(mutation.addedNodes, function (node) { + var child = Registry.find(node, false); + mark(child, false); + if (child instanceof container_1.default) { + child.children.forEach(function (grandChild) { + mark(grandChild, false); + }); + } + }); + } + else if (mutation.type === 'attributes') { + mark(blot.prev); + } + } + mark(blot); + }); + this.children.forEach(optimize); + remaining = [].slice.call(this.observer.takeRecords()); + records = remaining.slice(); + while (records.length > 0) + mutations.push(records.pop()); + } + }; + ScrollBlot.prototype.update = function (mutations, context) { + var _this = this; + if (context === void 0) { context = {}; } + mutations = mutations || this.observer.takeRecords(); + // TODO use WeakMap + mutations + .map(function (mutation) { + var blot = Registry.find(mutation.target, true); + if (blot == null) + return null; + // @ts-ignore + if (blot.domNode[Registry.DATA_KEY].mutations == null) { + // @ts-ignore + blot.domNode[Registry.DATA_KEY].mutations = [mutation]; + return blot; + } + else { + // @ts-ignore + blot.domNode[Registry.DATA_KEY].mutations.push(mutation); + return null; + } + }) + .forEach(function (blot) { + if (blot == null || + blot === _this || + //@ts-ignore + blot.domNode[Registry.DATA_KEY] == null) + return; + // @ts-ignore + blot.update(blot.domNode[Registry.DATA_KEY].mutations || [], context); + }); + // @ts-ignore + if (this.domNode[Registry.DATA_KEY].mutations != null) { + // @ts-ignore + _super.prototype.update.call(this, this.domNode[Registry.DATA_KEY].mutations, context); + } + this.optimize(mutations, context); + }; + ScrollBlot.blotName = 'scroll'; + ScrollBlot.defaultChild = 'block'; + ScrollBlot.scope = Registry.Scope.BLOCK_BLOT; + ScrollBlot.tagName = 'DIV'; + return ScrollBlot; +}(container_1.default)); +exports.default = ScrollBlot; + + +/***/ }), +/* 46 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var format_1 = __webpack_require__(18); +var Registry = __webpack_require__(1); +// Shallow object comparison +function isEqual(obj1, obj2) { + if (Object.keys(obj1).length !== Object.keys(obj2).length) + return false; + // @ts-ignore + for (var prop in obj1) { + // @ts-ignore + if (obj1[prop] !== obj2[prop]) + return false; + } + return true; +} +var InlineBlot = /** @class */ (function (_super) { + __extends(InlineBlot, _super); + function InlineBlot() { + return _super !== null && _super.apply(this, arguments) || this; + } + InlineBlot.formats = function (domNode) { + if (domNode.tagName === InlineBlot.tagName) + return undefined; + return _super.formats.call(this, domNode); + }; + InlineBlot.prototype.format = function (name, value) { + var _this = this; + if (name === this.statics.blotName && !value) { + this.children.forEach(function (child) { + if (!(child instanceof format_1.default)) { + child = child.wrap(InlineBlot.blotName, true); + } + _this.attributes.copy(child); + }); + this.unwrap(); + } + else { + _super.prototype.format.call(this, name, value); + } + }; + InlineBlot.prototype.formatAt = function (index, length, name, value) { + if (this.formats()[name] != null || Registry.query(name, Registry.Scope.ATTRIBUTE)) { + var blot = this.isolate(index, length); + blot.format(name, value); + } + else { + _super.prototype.formatAt.call(this, index, length, name, value); + } + }; + InlineBlot.prototype.optimize = function (context) { + _super.prototype.optimize.call(this, context); + var formats = this.formats(); + if (Object.keys(formats).length === 0) { + return this.unwrap(); // unformatted span + } + var next = this.next; + if (next instanceof InlineBlot && next.prev === this && isEqual(formats, next.formats())) { + next.moveChildren(this); + next.remove(); + } + }; + InlineBlot.blotName = 'inline'; + InlineBlot.scope = Registry.Scope.INLINE_BLOT; + InlineBlot.tagName = 'SPAN'; + return InlineBlot; +}(format_1.default)); +exports.default = InlineBlot; + + +/***/ }), +/* 47 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var format_1 = __webpack_require__(18); +var Registry = __webpack_require__(1); +var BlockBlot = /** @class */ (function (_super) { + __extends(BlockBlot, _super); + function BlockBlot() { + return _super !== null && _super.apply(this, arguments) || this; + } + BlockBlot.formats = function (domNode) { + var tagName = Registry.query(BlockBlot.blotName).tagName; + if (domNode.tagName === tagName) + return undefined; + return _super.formats.call(this, domNode); + }; + BlockBlot.prototype.format = function (name, value) { + if (Registry.query(name, Registry.Scope.BLOCK) == null) { + return; + } + else if (name === this.statics.blotName && !value) { + this.replaceWith(BlockBlot.blotName); + } + else { + _super.prototype.format.call(this, name, value); + } + }; + BlockBlot.prototype.formatAt = function (index, length, name, value) { + if (Registry.query(name, Registry.Scope.BLOCK) != null) { + this.format(name, value); + } + else { + _super.prototype.formatAt.call(this, index, length, name, value); + } + }; + BlockBlot.prototype.insertAt = function (index, value, def) { + if (def == null || Registry.query(value, Registry.Scope.INLINE) != null) { + // Insert text or inline + _super.prototype.insertAt.call(this, index, value, def); + } + else { + var after = this.split(index); + var blot = Registry.create(value, def); + after.parent.insertBefore(blot, after); + } + }; + BlockBlot.prototype.update = function (mutations, context) { + if (navigator.userAgent.match(/Trident/)) { + this.build(); + } + else { + _super.prototype.update.call(this, mutations, context); + } + }; + BlockBlot.blotName = 'block'; + BlockBlot.scope = Registry.Scope.BLOCK_BLOT; + BlockBlot.tagName = 'P'; + return BlockBlot; +}(format_1.default)); +exports.default = BlockBlot; + + +/***/ }), +/* 48 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var leaf_1 = __webpack_require__(19); +var EmbedBlot = /** @class */ (function (_super) { + __extends(EmbedBlot, _super); + function EmbedBlot() { + return _super !== null && _super.apply(this, arguments) || this; + } + EmbedBlot.formats = function (domNode) { + return undefined; + }; + EmbedBlot.prototype.format = function (name, value) { + // super.formatAt wraps, which is what we want in general, + // but this allows subclasses to overwrite for formats + // that just apply to particular embeds + _super.prototype.formatAt.call(this, 0, this.length(), name, value); + }; + EmbedBlot.prototype.formatAt = function (index, length, name, value) { + if (index === 0 && length === this.length()) { + this.format(name, value); + } + else { + _super.prototype.formatAt.call(this, index, length, name, value); + } + }; + EmbedBlot.prototype.formats = function () { + return this.statics.formats(this.domNode); + }; + return EmbedBlot; +}(leaf_1.default)); +exports.default = EmbedBlot; + + +/***/ }), +/* 49 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var leaf_1 = __webpack_require__(19); +var Registry = __webpack_require__(1); +var TextBlot = /** @class */ (function (_super) { + __extends(TextBlot, _super); + function TextBlot(node) { + var _this = _super.call(this, node) || this; + _this.text = _this.statics.value(_this.domNode); + return _this; + } + TextBlot.create = function (value) { + return document.createTextNode(value); + }; + TextBlot.value = function (domNode) { + var text = domNode.data; + // @ts-ignore + if (text['normalize']) + text = text['normalize'](); + return text; + }; + TextBlot.prototype.deleteAt = function (index, length) { + this.domNode.data = this.text = this.text.slice(0, index) + this.text.slice(index + length); + }; + TextBlot.prototype.index = function (node, offset) { + if (this.domNode === node) { + return offset; + } + return -1; + }; + TextBlot.prototype.insertAt = function (index, value, def) { + if (def == null) { + this.text = this.text.slice(0, index) + value + this.text.slice(index); + this.domNode.data = this.text; + } + else { + _super.prototype.insertAt.call(this, index, value, def); + } + }; + TextBlot.prototype.length = function () { + return this.text.length; + }; + TextBlot.prototype.optimize = function (context) { + _super.prototype.optimize.call(this, context); + this.text = this.statics.value(this.domNode); + if (this.text.length === 0) { + this.remove(); + } + else if (this.next instanceof TextBlot && this.next.prev === this) { + this.insertAt(this.length(), this.next.value()); + this.next.remove(); + } + }; + TextBlot.prototype.position = function (index, inclusive) { + if (inclusive === void 0) { inclusive = false; } + return [this.domNode, index]; + }; + TextBlot.prototype.split = function (index, force) { + if (force === void 0) { force = false; } + if (!force) { + if (index === 0) + return this; + if (index === this.length()) + return this.next; + } + var after = Registry.create(this.domNode.splitText(index)); + this.parent.insertBefore(after, this.next); + this.text = this.statics.value(this.domNode); + return after; + }; + TextBlot.prototype.update = function (mutations, context) { + var _this = this; + if (mutations.some(function (mutation) { + return mutation.type === 'characterData' && mutation.target === _this.domNode; + })) { + this.text = this.statics.value(this.domNode); + } + }; + TextBlot.prototype.value = function () { + return this.text; + }; + TextBlot.blotName = 'text'; + TextBlot.scope = Registry.Scope.INLINE_BLOT; + return TextBlot; +}(leaf_1.default)); +exports.default = TextBlot; + + +/***/ }), +/* 50 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var elem = document.createElement('div'); +elem.classList.toggle('test-class', false); +if (elem.classList.contains('test-class')) { + var _toggle = DOMTokenList.prototype.toggle; + DOMTokenList.prototype.toggle = function (token, force) { + if (arguments.length > 1 && !this.contains(token) === !force) { + return force; + } else { + return _toggle.call(this, token); + } + }; +} + +if (!String.prototype.startsWith) { + String.prototype.startsWith = function (searchString, position) { + position = position || 0; + return this.substr(position, searchString.length) === searchString; + }; +} + +if (!String.prototype.endsWith) { + String.prototype.endsWith = function (searchString, position) { + var subjectString = this.toString(); + if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { + position = subjectString.length; + } + position -= searchString.length; + var lastIndex = subjectString.indexOf(searchString, position); + return lastIndex !== -1 && lastIndex === position; + }; +} + +if (!Array.prototype.find) { + Object.defineProperty(Array.prototype, "find", { + value: function value(predicate) { + if (this === null) { + throw new TypeError('Array.prototype.find called on null or undefined'); + } + if (typeof predicate !== 'function') { + throw new TypeError('predicate must be a function'); + } + var list = Object(this); + var length = list.length >>> 0; + var thisArg = arguments[1]; + var value; + + for (var i = 0; i < length; i++) { + value = list[i]; + if (predicate.call(thisArg, value, i, list)) { + return value; + } + } + return undefined; + } + }); +} + +document.addEventListener("DOMContentLoaded", function () { + // Disable resizing in Firefox + document.execCommand("enableObjectResizing", false, false); + // Disable automatic linkifying in IE11 + document.execCommand("autoUrlDetect", false, false); +}); + +/***/ }), +/* 51 */ +/***/ (function(module, exports) { + +/** + * This library modifies the diff-patch-match library by Neil Fraser + * by removing the patch and match functionality and certain advanced + * options in the diff function. The original license is as follows: + * + * === + * + * Diff Match and Patch + * + * Copyright 2006 Google Inc. + * http://code.google.com/p/google-diff-match-patch/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * The data structure representing a diff is an array of tuples: + * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] + * which means: delete 'Hello', add 'Goodbye' and keep ' world.' + */ +var DIFF_DELETE = -1; +var DIFF_INSERT = 1; +var DIFF_EQUAL = 0; + + +/** + * Find the differences between two texts. Simplifies the problem by stripping + * any common prefix or suffix off the texts before diffing. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {Int} cursor_pos Expected edit position in text1 (optional) + * @return {Array} Array of diff tuples. + */ +function diff_main(text1, text2, cursor_pos) { + // Check for equality (speedup). + if (text1 == text2) { + if (text1) { + return [[DIFF_EQUAL, text1]]; + } + return []; + } + + // Check cursor_pos within bounds + if (cursor_pos < 0 || text1.length < cursor_pos) { + cursor_pos = null; + } + + // Trim off common prefix (speedup). + var commonlength = diff_commonPrefix(text1, text2); + var commonprefix = text1.substring(0, commonlength); + text1 = text1.substring(commonlength); + text2 = text2.substring(commonlength); + + // Trim off common suffix (speedup). + commonlength = diff_commonSuffix(text1, text2); + var commonsuffix = text1.substring(text1.length - commonlength); + text1 = text1.substring(0, text1.length - commonlength); + text2 = text2.substring(0, text2.length - commonlength); + + // Compute the diff on the middle block. + var diffs = diff_compute_(text1, text2); + + // Restore the prefix and suffix. + if (commonprefix) { + diffs.unshift([DIFF_EQUAL, commonprefix]); + } + if (commonsuffix) { + diffs.push([DIFF_EQUAL, commonsuffix]); + } + diff_cleanupMerge(diffs); + if (cursor_pos != null) { + diffs = fix_cursor(diffs, cursor_pos); + } + diffs = fix_emoji(diffs); + return diffs; +}; + + +/** + * Find the differences between two texts. Assumes that the texts do not + * have any common prefix or suffix. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @return {Array} Array of diff tuples. + */ +function diff_compute_(text1, text2) { + var diffs; + + if (!text1) { + // Just add some text (speedup). + return [[DIFF_INSERT, text2]]; + } + + if (!text2) { + // Just delete some text (speedup). + return [[DIFF_DELETE, text1]]; + } + + var longtext = text1.length > text2.length ? text1 : text2; + var shorttext = text1.length > text2.length ? text2 : text1; + var i = longtext.indexOf(shorttext); + if (i != -1) { + // Shorter text is inside the longer text (speedup). + diffs = [[DIFF_INSERT, longtext.substring(0, i)], + [DIFF_EQUAL, shorttext], + [DIFF_INSERT, longtext.substring(i + shorttext.length)]]; + // Swap insertions for deletions if diff is reversed. + if (text1.length > text2.length) { + diffs[0][0] = diffs[2][0] = DIFF_DELETE; + } + return diffs; + } + + if (shorttext.length == 1) { + // Single character string. + // After the previous speedup, the character can't be an equality. + return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; + } + + // Check to see if the problem can be split in two. + var hm = diff_halfMatch_(text1, text2); + if (hm) { + // A half-match was found, sort out the return data. + var text1_a = hm[0]; + var text1_b = hm[1]; + var text2_a = hm[2]; + var text2_b = hm[3]; + var mid_common = hm[4]; + // Send both pairs off for separate processing. + var diffs_a = diff_main(text1_a, text2_a); + var diffs_b = diff_main(text1_b, text2_b); + // Merge the results. + return diffs_a.concat([[DIFF_EQUAL, mid_common]], diffs_b); + } + + return diff_bisect_(text1, text2); +}; + + +/** + * Find the 'middle snake' of a diff, split the problem in two + * and return the recursively constructed diff. + * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @return {Array} Array of diff tuples. + * @private + */ +function diff_bisect_(text1, text2) { + // Cache the text lengths to prevent multiple calls. + var text1_length = text1.length; + var text2_length = text2.length; + var max_d = Math.ceil((text1_length + text2_length) / 2); + var v_offset = max_d; + var v_length = 2 * max_d; + var v1 = new Array(v_length); + var v2 = new Array(v_length); + // Setting all elements to -1 is faster in Chrome & Firefox than mixing + // integers and undefined. + for (var x = 0; x < v_length; x++) { + v1[x] = -1; + v2[x] = -1; + } + v1[v_offset + 1] = 0; + v2[v_offset + 1] = 0; + var delta = text1_length - text2_length; + // If the total number of characters is odd, then the front path will collide + // with the reverse path. + var front = (delta % 2 != 0); + // Offsets for start and end of k loop. + // Prevents mapping of space beyond the grid. + var k1start = 0; + var k1end = 0; + var k2start = 0; + var k2end = 0; + for (var d = 0; d < max_d; d++) { + // Walk the front path one step. + for (var k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { + var k1_offset = v_offset + k1; + var x1; + if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) { + x1 = v1[k1_offset + 1]; + } else { + x1 = v1[k1_offset - 1] + 1; + } + var y1 = x1 - k1; + while (x1 < text1_length && y1 < text2_length && + text1.charAt(x1) == text2.charAt(y1)) { + x1++; + y1++; + } + v1[k1_offset] = x1; + if (x1 > text1_length) { + // Ran off the right of the graph. + k1end += 2; + } else if (y1 > text2_length) { + // Ran off the bottom of the graph. + k1start += 2; + } else if (front) { + var k2_offset = v_offset + delta - k1; + if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) { + // Mirror x2 onto top-left coordinate system. + var x2 = text1_length - v2[k2_offset]; + if (x1 >= x2) { + // Overlap detected. + return diff_bisectSplit_(text1, text2, x1, y1); + } + } + } + } + + // Walk the reverse path one step. + for (var k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { + var k2_offset = v_offset + k2; + var x2; + if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) { + x2 = v2[k2_offset + 1]; + } else { + x2 = v2[k2_offset - 1] + 1; + } + var y2 = x2 - k2; + while (x2 < text1_length && y2 < text2_length && + text1.charAt(text1_length - x2 - 1) == + text2.charAt(text2_length - y2 - 1)) { + x2++; + y2++; + } + v2[k2_offset] = x2; + if (x2 > text1_length) { + // Ran off the left of the graph. + k2end += 2; + } else if (y2 > text2_length) { + // Ran off the top of the graph. + k2start += 2; + } else if (!front) { + var k1_offset = v_offset + delta - k2; + if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) { + var x1 = v1[k1_offset]; + var y1 = v_offset + x1 - k1_offset; + // Mirror x2 onto top-left coordinate system. + x2 = text1_length - x2; + if (x1 >= x2) { + // Overlap detected. + return diff_bisectSplit_(text1, text2, x1, y1); + } + } + } + } + } + // Diff took too long and hit the deadline or + // number of diffs equals number of characters, no commonality at all. + return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; +}; + + +/** + * Given the location of the 'middle snake', split the diff in two parts + * and recurse. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} x Index of split point in text1. + * @param {number} y Index of split point in text2. + * @return {Array} Array of diff tuples. + */ +function diff_bisectSplit_(text1, text2, x, y) { + var text1a = text1.substring(0, x); + var text2a = text2.substring(0, y); + var text1b = text1.substring(x); + var text2b = text2.substring(y); + + // Compute both diffs serially. + var diffs = diff_main(text1a, text2a); + var diffsb = diff_main(text1b, text2b); + + return diffs.concat(diffsb); +}; + + +/** + * Determine the common prefix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the start of each + * string. + */ +function diff_commonPrefix(text1, text2) { + // Quick check for common null cases. + if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) { + return 0; + } + // Binary search. + // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + var pointermin = 0; + var pointermax = Math.min(text1.length, text2.length); + var pointermid = pointermax; + var pointerstart = 0; + while (pointermin < pointermid) { + if (text1.substring(pointerstart, pointermid) == + text2.substring(pointerstart, pointermid)) { + pointermin = pointermid; + pointerstart = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; +}; + + +/** + * Determine the common suffix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of each string. + */ +function diff_commonSuffix(text1, text2) { + // Quick check for common null cases. + if (!text1 || !text2 || + text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) { + return 0; + } + // Binary search. + // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + var pointermin = 0; + var pointermax = Math.min(text1.length, text2.length); + var pointermid = pointermax; + var pointerend = 0; + while (pointermin < pointermid) { + if (text1.substring(text1.length - pointermid, text1.length - pointerend) == + text2.substring(text2.length - pointermid, text2.length - pointerend)) { + pointermin = pointermid; + pointerend = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; +}; + + +/** + * Do the two texts share a substring which is at least half the length of the + * longer text? + * This speedup can produce non-minimal diffs. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {Array.} Five element Array, containing the prefix of + * text1, the suffix of text1, the prefix of text2, the suffix of + * text2 and the common middle. Or null if there was no match. + */ +function diff_halfMatch_(text1, text2) { + var longtext = text1.length > text2.length ? text1 : text2; + var shorttext = text1.length > text2.length ? text2 : text1; + if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { + return null; // Pointless. + } + + /** + * Does a substring of shorttext exist within longtext such that the substring + * is at least half the length of longtext? + * Closure, but does not reference any external variables. + * @param {string} longtext Longer string. + * @param {string} shorttext Shorter string. + * @param {number} i Start index of quarter length substring within longtext. + * @return {Array.} Five element Array, containing the prefix of + * longtext, the suffix of longtext, the prefix of shorttext, the suffix + * of shorttext and the common middle. Or null if there was no match. + * @private + */ + function diff_halfMatchI_(longtext, shorttext, i) { + // Start with a 1/4 length substring at position i as a seed. + var seed = longtext.substring(i, i + Math.floor(longtext.length / 4)); + var j = -1; + var best_common = ''; + var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b; + while ((j = shorttext.indexOf(seed, j + 1)) != -1) { + var prefixLength = diff_commonPrefix(longtext.substring(i), + shorttext.substring(j)); + var suffixLength = diff_commonSuffix(longtext.substring(0, i), + shorttext.substring(0, j)); + if (best_common.length < suffixLength + prefixLength) { + best_common = shorttext.substring(j - suffixLength, j) + + shorttext.substring(j, j + prefixLength); + best_longtext_a = longtext.substring(0, i - suffixLength); + best_longtext_b = longtext.substring(i + prefixLength); + best_shorttext_a = shorttext.substring(0, j - suffixLength); + best_shorttext_b = shorttext.substring(j + prefixLength); + } + } + if (best_common.length * 2 >= longtext.length) { + return [best_longtext_a, best_longtext_b, + best_shorttext_a, best_shorttext_b, best_common]; + } else { + return null; + } + } + + // First check if the second quarter is the seed for a half-match. + var hm1 = diff_halfMatchI_(longtext, shorttext, + Math.ceil(longtext.length / 4)); + // Check again based on the third quarter. + var hm2 = diff_halfMatchI_(longtext, shorttext, + Math.ceil(longtext.length / 2)); + var hm; + if (!hm1 && !hm2) { + return null; + } else if (!hm2) { + hm = hm1; + } else if (!hm1) { + hm = hm2; + } else { + // Both matched. Select the longest. + hm = hm1[4].length > hm2[4].length ? hm1 : hm2; + } + + // A half-match was found, sort out the return data. + var text1_a, text1_b, text2_a, text2_b; + if (text1.length > text2.length) { + text1_a = hm[0]; + text1_b = hm[1]; + text2_a = hm[2]; + text2_b = hm[3]; + } else { + text2_a = hm[0]; + text2_b = hm[1]; + text1_a = hm[2]; + text1_b = hm[3]; + } + var mid_common = hm[4]; + return [text1_a, text1_b, text2_a, text2_b, mid_common]; +}; + + +/** + * Reorder and merge like edit sections. Merge equalities. + * Any edit section can move as long as it doesn't cross an equality. + * @param {Array} diffs Array of diff tuples. + */ +function diff_cleanupMerge(diffs) { + diffs.push([DIFF_EQUAL, '']); // Add a dummy entry at the end. + var pointer = 0; + var count_delete = 0; + var count_insert = 0; + var text_delete = ''; + var text_insert = ''; + var commonlength; + while (pointer < diffs.length) { + switch (diffs[pointer][0]) { + case DIFF_INSERT: + count_insert++; + text_insert += diffs[pointer][1]; + pointer++; + break; + case DIFF_DELETE: + count_delete++; + text_delete += diffs[pointer][1]; + pointer++; + break; + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (count_delete + count_insert > 1) { + if (count_delete !== 0 && count_insert !== 0) { + // Factor out any common prefixies. + commonlength = diff_commonPrefix(text_insert, text_delete); + if (commonlength !== 0) { + if ((pointer - count_delete - count_insert) > 0 && + diffs[pointer - count_delete - count_insert - 1][0] == + DIFF_EQUAL) { + diffs[pointer - count_delete - count_insert - 1][1] += + text_insert.substring(0, commonlength); + } else { + diffs.splice(0, 0, [DIFF_EQUAL, + text_insert.substring(0, commonlength)]); + pointer++; + } + text_insert = text_insert.substring(commonlength); + text_delete = text_delete.substring(commonlength); + } + // Factor out any common suffixies. + commonlength = diff_commonSuffix(text_insert, text_delete); + if (commonlength !== 0) { + diffs[pointer][1] = text_insert.substring(text_insert.length - + commonlength) + diffs[pointer][1]; + text_insert = text_insert.substring(0, text_insert.length - + commonlength); + text_delete = text_delete.substring(0, text_delete.length - + commonlength); + } + } + // Delete the offending records and add the merged ones. + if (count_delete === 0) { + diffs.splice(pointer - count_insert, + count_delete + count_insert, [DIFF_INSERT, text_insert]); + } else if (count_insert === 0) { + diffs.splice(pointer - count_delete, + count_delete + count_insert, [DIFF_DELETE, text_delete]); + } else { + diffs.splice(pointer - count_delete - count_insert, + count_delete + count_insert, [DIFF_DELETE, text_delete], + [DIFF_INSERT, text_insert]); + } + pointer = pointer - count_delete - count_insert + + (count_delete ? 1 : 0) + (count_insert ? 1 : 0) + 1; + } else if (pointer !== 0 && diffs[pointer - 1][0] == DIFF_EQUAL) { + // Merge this equality with the previous one. + diffs[pointer - 1][1] += diffs[pointer][1]; + diffs.splice(pointer, 1); + } else { + pointer++; + } + count_insert = 0; + count_delete = 0; + text_delete = ''; + text_insert = ''; + break; + } + } + if (diffs[diffs.length - 1][1] === '') { + diffs.pop(); // Remove the dummy entry at the end. + } + + // Second pass: look for single edits surrounded on both sides by equalities + // which can be shifted sideways to eliminate an equality. + // e.g: ABAC -> ABAC + var changes = false; + pointer = 1; + // Intentionally ignore the first and last element (don't need checking). + while (pointer < diffs.length - 1) { + if (diffs[pointer - 1][0] == DIFF_EQUAL && + diffs[pointer + 1][0] == DIFF_EQUAL) { + // This is a single edit surrounded by equalities. + if (diffs[pointer][1].substring(diffs[pointer][1].length - + diffs[pointer - 1][1].length) == diffs[pointer - 1][1]) { + // Shift the edit over the previous equality. + diffs[pointer][1] = diffs[pointer - 1][1] + + diffs[pointer][1].substring(0, diffs[pointer][1].length - + diffs[pointer - 1][1].length); + diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; + diffs.splice(pointer - 1, 1); + changes = true; + } else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) == + diffs[pointer + 1][1]) { + // Shift the edit over the next equality. + diffs[pointer - 1][1] += diffs[pointer + 1][1]; + diffs[pointer][1] = + diffs[pointer][1].substring(diffs[pointer + 1][1].length) + + diffs[pointer + 1][1]; + diffs.splice(pointer + 1, 1); + changes = true; + } + } + pointer++; + } + // If shifts were made, the diff needs reordering and another shift sweep. + if (changes) { + diff_cleanupMerge(diffs); + } +}; + + +var diff = diff_main; +diff.INSERT = DIFF_INSERT; +diff.DELETE = DIFF_DELETE; +diff.EQUAL = DIFF_EQUAL; + +module.exports = diff; + +/* + * Modify a diff such that the cursor position points to the start of a change: + * E.g. + * cursor_normalize_diff([[DIFF_EQUAL, 'abc']], 1) + * => [1, [[DIFF_EQUAL, 'a'], [DIFF_EQUAL, 'bc']]] + * cursor_normalize_diff([[DIFF_INSERT, 'new'], [DIFF_DELETE, 'xyz']], 2) + * => [2, [[DIFF_INSERT, 'new'], [DIFF_DELETE, 'xy'], [DIFF_DELETE, 'z']]] + * + * @param {Array} diffs Array of diff tuples + * @param {Int} cursor_pos Suggested edit position. Must not be out of bounds! + * @return {Array} A tuple [cursor location in the modified diff, modified diff] + */ +function cursor_normalize_diff (diffs, cursor_pos) { + if (cursor_pos === 0) { + return [DIFF_EQUAL, diffs]; + } + for (var current_pos = 0, i = 0; i < diffs.length; i++) { + var d = diffs[i]; + if (d[0] === DIFF_DELETE || d[0] === DIFF_EQUAL) { + var next_pos = current_pos + d[1].length; + if (cursor_pos === next_pos) { + return [i + 1, diffs]; + } else if (cursor_pos < next_pos) { + // copy to prevent side effects + diffs = diffs.slice(); + // split d into two diff changes + var split_pos = cursor_pos - current_pos; + var d_left = [d[0], d[1].slice(0, split_pos)]; + var d_right = [d[0], d[1].slice(split_pos)]; + diffs.splice(i, 1, d_left, d_right); + return [i + 1, diffs]; + } else { + current_pos = next_pos; + } + } + } + throw new Error('cursor_pos is out of bounds!') +} + +/* + * Modify a diff such that the edit position is "shifted" to the proposed edit location (cursor_position). + * + * Case 1) + * Check if a naive shift is possible: + * [0, X], [ 1, Y] -> [ 1, Y], [0, X] (if X + Y === Y + X) + * [0, X], [-1, Y] -> [-1, Y], [0, X] (if X + Y === Y + X) - holds same result + * Case 2) + * Check if the following shifts are possible: + * [0, 'pre'], [ 1, 'prefix'] -> [ 1, 'pre'], [0, 'pre'], [ 1, 'fix'] + * [0, 'pre'], [-1, 'prefix'] -> [-1, 'pre'], [0, 'pre'], [-1, 'fix'] + * ^ ^ + * d d_next + * + * @param {Array} diffs Array of diff tuples + * @param {Int} cursor_pos Suggested edit position. Must not be out of bounds! + * @return {Array} Array of diff tuples + */ +function fix_cursor (diffs, cursor_pos) { + var norm = cursor_normalize_diff(diffs, cursor_pos); + var ndiffs = norm[1]; + var cursor_pointer = norm[0]; + var d = ndiffs[cursor_pointer]; + var d_next = ndiffs[cursor_pointer + 1]; + + if (d == null) { + // Text was deleted from end of original string, + // cursor is now out of bounds in new string + return diffs; + } else if (d[0] !== DIFF_EQUAL) { + // A modification happened at the cursor location. + // This is the expected outcome, so we can return the original diff. + return diffs; + } else { + if (d_next != null && d[1] + d_next[1] === d_next[1] + d[1]) { + // Case 1) + // It is possible to perform a naive shift + ndiffs.splice(cursor_pointer, 2, d_next, d) + return merge_tuples(ndiffs, cursor_pointer, 2) + } else if (d_next != null && d_next[1].indexOf(d[1]) === 0) { + // Case 2) + // d[1] is a prefix of d_next[1] + // We can assume that d_next[0] !== 0, since d[0] === 0 + // Shift edit locations.. + ndiffs.splice(cursor_pointer, 2, [d_next[0], d[1]], [0, d[1]]); + var suffix = d_next[1].slice(d[1].length); + if (suffix.length > 0) { + ndiffs.splice(cursor_pointer + 2, 0, [d_next[0], suffix]); + } + return merge_tuples(ndiffs, cursor_pointer, 3) + } else { + // Not possible to perform any modification + return diffs; + } + } +} + +/* + * Check diff did not split surrogate pairs. + * Ex. [0, '\uD83D'], [-1, '\uDC36'], [1, '\uDC2F'] -> [-1, '\uD83D\uDC36'], [1, '\uD83D\uDC2F'] + * '\uD83D\uDC36' === '🐶', '\uD83D\uDC2F' === '🐯' + * + * @param {Array} diffs Array of diff tuples + * @return {Array} Array of diff tuples + */ +function fix_emoji (diffs) { + var compact = false; + var starts_with_pair_end = function(str) { + return str.charCodeAt(0) >= 0xDC00 && str.charCodeAt(0) <= 0xDFFF; + } + var ends_with_pair_start = function(str) { + return str.charCodeAt(str.length-1) >= 0xD800 && str.charCodeAt(str.length-1) <= 0xDBFF; + } + for (var i = 2; i < diffs.length; i += 1) { + if (diffs[i-2][0] === DIFF_EQUAL && ends_with_pair_start(diffs[i-2][1]) && + diffs[i-1][0] === DIFF_DELETE && starts_with_pair_end(diffs[i-1][1]) && + diffs[i][0] === DIFF_INSERT && starts_with_pair_end(diffs[i][1])) { + compact = true; + + diffs[i-1][1] = diffs[i-2][1].slice(-1) + diffs[i-1][1]; + diffs[i][1] = diffs[i-2][1].slice(-1) + diffs[i][1]; + + diffs[i-2][1] = diffs[i-2][1].slice(0, -1); + } + } + if (!compact) { + return diffs; + } + var fixed_diffs = []; + for (var i = 0; i < diffs.length; i += 1) { + if (diffs[i][1].length > 0) { + fixed_diffs.push(diffs[i]); + } + } + return fixed_diffs; +} + +/* + * Try to merge tuples with their neigbors in a given range. + * E.g. [0, 'a'], [0, 'b'] -> [0, 'ab'] + * + * @param {Array} diffs Array of diff tuples. + * @param {Int} start Position of the first element to merge (diffs[start] is also merged with diffs[start - 1]). + * @param {Int} length Number of consecutive elements to check. + * @return {Array} Array of merged diff tuples. + */ +function merge_tuples (diffs, start, length) { + // Check from (start-1) to (start+length). + for (var i = start + length - 1; i >= 0 && i >= start - 1; i--) { + if (i + 1 < diffs.length) { + var left_d = diffs[i]; + var right_d = diffs[i+1]; + if (left_d[0] === right_d[1]) { + diffs.splice(i, 2, [left_d[0], left_d[1] + right_d[1]]); + } + } + } + return diffs; +} + + +/***/ }), +/* 52 */ +/***/ (function(module, exports) { + +exports = module.exports = typeof Object.keys === 'function' + ? Object.keys : shim; + +exports.shim = shim; +function shim (obj) { + var keys = []; + for (var key in obj) keys.push(key); + return keys; +} + + +/***/ }), +/* 53 */ +/***/ (function(module, exports) { + +var supportsArgumentsClass = (function(){ + return Object.prototype.toString.call(arguments) +})() == '[object Arguments]'; + +exports = module.exports = supportsArgumentsClass ? supported : unsupported; + +exports.supported = supported; +function supported(object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +}; + +exports.unsupported = unsupported; +function unsupported(object){ + return object && + typeof object == 'object' && + typeof object.length == 'number' && + Object.prototype.hasOwnProperty.call(object, 'callee') && + !Object.prototype.propertyIsEnumerable.call(object, 'callee') || + false; +}; + + +/***/ }), +/* 54 */ +/***/ (function(module, exports) { + +'use strict'; + +var has = Object.prototype.hasOwnProperty + , prefix = '~'; + +/** + * Constructor to create a storage for our `EE` objects. + * An `Events` instance is a plain object whose properties are event names. + * + * @constructor + * @api private + */ +function Events() {} + +// +// We try to not inherit from `Object.prototype`. In some engines creating an +// instance in this way is faster than calling `Object.create(null)` directly. +// If `Object.create(null)` is not supported we prefix the event names with a +// character to make sure that the built-in object properties are not +// overridden or used as an attack vector. +// +if (Object.create) { + Events.prototype = Object.create(null); + + // + // This hack is needed because the `__proto__` property is still inherited in + // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5. + // + if (!new Events().__proto__) prefix = false; +} + +/** + * Representation of a single event listener. + * + * @param {Function} fn The listener function. + * @param {Mixed} context The context to invoke the listener with. + * @param {Boolean} [once=false] Specify if the listener is a one-time listener. + * @constructor + * @api private + */ +function EE(fn, context, once) { + this.fn = fn; + this.context = context; + this.once = once || false; +} + +/** + * Minimal `EventEmitter` interface that is molded against the Node.js + * `EventEmitter` interface. + * + * @constructor + * @api public + */ +function EventEmitter() { + this._events = new Events(); + this._eventsCount = 0; +} + +/** + * Return an array listing the events for which the emitter has registered + * listeners. + * + * @returns {Array} + * @api public + */ +EventEmitter.prototype.eventNames = function eventNames() { + var names = [] + , events + , name; + + if (this._eventsCount === 0) return names; + + for (name in (events = this._events)) { + if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); + } + + if (Object.getOwnPropertySymbols) { + return names.concat(Object.getOwnPropertySymbols(events)); + } + + return names; +}; + +/** + * Return the listeners registered for a given event. + * + * @param {String|Symbol} event The event name. + * @param {Boolean} exists Only check if there are listeners. + * @returns {Array|Boolean} + * @api public + */ +EventEmitter.prototype.listeners = function listeners(event, exists) { + var evt = prefix ? prefix + event : event + , available = this._events[evt]; + + if (exists) return !!available; + if (!available) return []; + if (available.fn) return [available.fn]; + + for (var i = 0, l = available.length, ee = new Array(l); i < l; i++) { + ee[i] = available[i].fn; + } + + return ee; +}; + +/** + * Calls each of the listeners registered for a given event. + * + * @param {String|Symbol} event The event name. + * @returns {Boolean} `true` if the event had listeners, else `false`. + * @api public + */ +EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { + var evt = prefix ? prefix + event : event; + + if (!this._events[evt]) return false; + + var listeners = this._events[evt] + , len = arguments.length + , args + , i; + + if (listeners.fn) { + if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); + + switch (len) { + case 1: return listeners.fn.call(listeners.context), true; + case 2: return listeners.fn.call(listeners.context, a1), true; + case 3: return listeners.fn.call(listeners.context, a1, a2), true; + case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; + case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; + case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; + } + + for (i = 1, args = new Array(len -1); i < len; i++) { + args[i - 1] = arguments[i]; + } + + listeners.fn.apply(listeners.context, args); + } else { + var length = listeners.length + , j; + + for (i = 0; i < length; i++) { + if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); + + switch (len) { + case 1: listeners[i].fn.call(listeners[i].context); break; + case 2: listeners[i].fn.call(listeners[i].context, a1); break; + case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; + case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; + default: + if (!args) for (j = 1, args = new Array(len -1); j < len; j++) { + args[j - 1] = arguments[j]; + } + + listeners[i].fn.apply(listeners[i].context, args); + } + } + } + + return true; +}; + +/** + * Add a listener for a given event. + * + * @param {String|Symbol} event The event name. + * @param {Function} fn The listener function. + * @param {Mixed} [context=this] The context to invoke the listener with. + * @returns {EventEmitter} `this`. + * @api public + */ +EventEmitter.prototype.on = function on(event, fn, context) { + var listener = new EE(fn, context || this) + , evt = prefix ? prefix + event : event; + + if (!this._events[evt]) this._events[evt] = listener, this._eventsCount++; + else if (!this._events[evt].fn) this._events[evt].push(listener); + else this._events[evt] = [this._events[evt], listener]; + + return this; +}; + +/** + * Add a one-time listener for a given event. + * + * @param {String|Symbol} event The event name. + * @param {Function} fn The listener function. + * @param {Mixed} [context=this] The context to invoke the listener with. + * @returns {EventEmitter} `this`. + * @api public + */ +EventEmitter.prototype.once = function once(event, fn, context) { + var listener = new EE(fn, context || this, true) + , evt = prefix ? prefix + event : event; + + if (!this._events[evt]) this._events[evt] = listener, this._eventsCount++; + else if (!this._events[evt].fn) this._events[evt].push(listener); + else this._events[evt] = [this._events[evt], listener]; + + return this; +}; + +/** + * Remove the listeners of a given event. + * + * @param {String|Symbol} event The event name. + * @param {Function} fn Only remove the listeners that match this function. + * @param {Mixed} context Only remove the listeners that have this context. + * @param {Boolean} once Only remove one-time listeners. + * @returns {EventEmitter} `this`. + * @api public + */ +EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { + var evt = prefix ? prefix + event : event; + + if (!this._events[evt]) return this; + if (!fn) { + if (--this._eventsCount === 0) this._events = new Events(); + else delete this._events[evt]; + return this; + } + + var listeners = this._events[evt]; + + if (listeners.fn) { + if ( + listeners.fn === fn + && (!once || listeners.once) + && (!context || listeners.context === context) + ) { + if (--this._eventsCount === 0) this._events = new Events(); + else delete this._events[evt]; + } + } else { + for (var i = 0, events = [], length = listeners.length; i < length; i++) { + if ( + listeners[i].fn !== fn + || (once && !listeners[i].once) + || (context && listeners[i].context !== context) + ) { + events.push(listeners[i]); + } + } + + // + // Reset the array, or remove it completely if we have no more listeners. + // + if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; + else if (--this._eventsCount === 0) this._events = new Events(); + else delete this._events[evt]; + } + + return this; +}; + +/** + * Remove all listeners, or those of the specified event. + * + * @param {String|Symbol} [event] The event name. + * @returns {EventEmitter} `this`. + * @api public + */ +EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { + var evt; + + if (event) { + evt = prefix ? prefix + event : event; + if (this._events[evt]) { + if (--this._eventsCount === 0) this._events = new Events(); + else delete this._events[evt]; + } + } else { + this._events = new Events(); + this._eventsCount = 0; + } + + return this; +}; + +// +// Alias methods names because people roll like that. +// +EventEmitter.prototype.off = EventEmitter.prototype.removeListener; +EventEmitter.prototype.addListener = EventEmitter.prototype.on; + +// +// This function doesn't apply anymore. +// +EventEmitter.prototype.setMaxListeners = function setMaxListeners() { + return this; +}; + +// +// Expose the prefix. +// +EventEmitter.prefixed = prefix; + +// +// Allow `EventEmitter` to be imported as module namespace. +// +EventEmitter.EventEmitter = EventEmitter; + +// +// Expose the module. +// +if ('undefined' !== typeof module) { + module.exports = EventEmitter; +} + + +/***/ }), +/* 55 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.matchText = exports.matchSpacing = exports.matchNewline = exports.matchBlot = exports.matchAttributor = exports.default = undefined; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _extend2 = __webpack_require__(3); + +var _extend3 = _interopRequireDefault(_extend2); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _logger = __webpack_require__(10); + +var _logger2 = _interopRequireDefault(_logger); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +var _align = __webpack_require__(36); + +var _background = __webpack_require__(37); + +var _code = __webpack_require__(13); + +var _code2 = _interopRequireDefault(_code); + +var _color = __webpack_require__(26); + +var _direction = __webpack_require__(38); + +var _font = __webpack_require__(39); + +var _size = __webpack_require__(40); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var debug = (0, _logger2.default)('quill:clipboard'); + +var DOM_KEY = '__ql-matcher'; + +var CLIPBOARD_CONFIG = [[Node.TEXT_NODE, matchText], [Node.TEXT_NODE, matchNewline], ['br', matchBreak], [Node.ELEMENT_NODE, matchNewline], [Node.ELEMENT_NODE, matchBlot], [Node.ELEMENT_NODE, matchSpacing], [Node.ELEMENT_NODE, matchAttributor], [Node.ELEMENT_NODE, matchStyles], ['li', matchIndent], ['b', matchAlias.bind(matchAlias, 'bold')], ['i', matchAlias.bind(matchAlias, 'italic')], ['style', matchIgnore]]; + +var ATTRIBUTE_ATTRIBUTORS = [_align.AlignAttribute, _direction.DirectionAttribute].reduce(function (memo, attr) { + memo[attr.keyName] = attr; + return memo; +}, {}); + +var STYLE_ATTRIBUTORS = [_align.AlignStyle, _background.BackgroundStyle, _color.ColorStyle, _direction.DirectionStyle, _font.FontStyle, _size.SizeStyle].reduce(function (memo, attr) { + memo[attr.keyName] = attr; + return memo; +}, {}); + +var Clipboard = function (_Module) { + _inherits(Clipboard, _Module); + + function Clipboard(quill, options) { + _classCallCheck(this, Clipboard); + + var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this, quill, options)); + + _this.quill.root.addEventListener('paste', _this.onPaste.bind(_this)); + _this.container = _this.quill.addContainer('ql-clipboard'); + _this.container.setAttribute('contenteditable', true); + _this.container.setAttribute('tabindex', -1); + _this.matchers = []; + CLIPBOARD_CONFIG.concat(_this.options.matchers).forEach(function (_ref) { + var _ref2 = _slicedToArray(_ref, 2), + selector = _ref2[0], + matcher = _ref2[1]; + + if (!options.matchVisual && matcher === matchSpacing) return; + _this.addMatcher(selector, matcher); + }); + return _this; + } + + _createClass(Clipboard, [{ + key: 'addMatcher', + value: function addMatcher(selector, matcher) { + this.matchers.push([selector, matcher]); + } + }, { + key: 'convert', + value: function convert(html) { + if (typeof html === 'string') { + this.container.innerHTML = html.replace(/\>\r?\n +\<'); // Remove spaces between tags + return this.convert(); + } + var formats = this.quill.getFormat(this.quill.selection.savedRange.index); + if (formats[_code2.default.blotName]) { + var text = this.container.innerText; + this.container.innerHTML = ''; + return new _quillDelta2.default().insert(text, _defineProperty({}, _code2.default.blotName, formats[_code2.default.blotName])); + } + + var _prepareMatching = this.prepareMatching(), + _prepareMatching2 = _slicedToArray(_prepareMatching, 2), + elementMatchers = _prepareMatching2[0], + textMatchers = _prepareMatching2[1]; + + var delta = traverse(this.container, elementMatchers, textMatchers); + // Remove trailing newline + if (deltaEndsWith(delta, '\n') && delta.ops[delta.ops.length - 1].attributes == null) { + delta = delta.compose(new _quillDelta2.default().retain(delta.length() - 1).delete(1)); + } + debug.log('convert', this.container.innerHTML, delta); + this.container.innerHTML = ''; + return delta; + } + }, { + key: 'dangerouslyPasteHTML', + value: function dangerouslyPasteHTML(index, html) { + var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _quill2.default.sources.API; + + if (typeof index === 'string') { + this.quill.setContents(this.convert(index), html); + this.quill.setSelection(0, _quill2.default.sources.SILENT); + } else { + var paste = this.convert(html); + this.quill.updateContents(new _quillDelta2.default().retain(index).concat(paste), source); + this.quill.setSelection(index + paste.length(), _quill2.default.sources.SILENT); + } + } + }, { + key: 'onPaste', + value: function onPaste(e) { + var _this2 = this; + + if (e.defaultPrevented || !this.quill.isEnabled()) return; + var range = this.quill.getSelection(); + var delta = new _quillDelta2.default().retain(range.index); + var scrollTop = this.quill.scrollingContainer.scrollTop; + this.container.focus(); + this.quill.selection.update(_quill2.default.sources.SILENT); + setTimeout(function () { + delta = delta.concat(_this2.convert()).delete(range.length); + _this2.quill.updateContents(delta, _quill2.default.sources.USER); + // range.length contributes to delta.length() + _this2.quill.setSelection(delta.length() - range.length, _quill2.default.sources.SILENT); + _this2.quill.scrollingContainer.scrollTop = scrollTop; + _this2.quill.focus(); + }, 1); + } + }, { + key: 'prepareMatching', + value: function prepareMatching() { + var _this3 = this; + + var elementMatchers = [], + textMatchers = []; + this.matchers.forEach(function (pair) { + var _pair = _slicedToArray(pair, 2), + selector = _pair[0], + matcher = _pair[1]; + + switch (selector) { + case Node.TEXT_NODE: + textMatchers.push(matcher); + break; + case Node.ELEMENT_NODE: + elementMatchers.push(matcher); + break; + default: + [].forEach.call(_this3.container.querySelectorAll(selector), function (node) { + // TODO use weakmap + node[DOM_KEY] = node[DOM_KEY] || []; + node[DOM_KEY].push(matcher); + }); + break; + } + }); + return [elementMatchers, textMatchers]; + } + }]); + + return Clipboard; +}(_module2.default); + +Clipboard.DEFAULTS = { + matchers: [], + matchVisual: true +}; + +function applyFormat(delta, format, value) { + if ((typeof format === 'undefined' ? 'undefined' : _typeof(format)) === 'object') { + return Object.keys(format).reduce(function (delta, key) { + return applyFormat(delta, key, format[key]); + }, delta); + } else { + return delta.reduce(function (delta, op) { + if (op.attributes && op.attributes[format]) { + return delta.push(op); + } else { + return delta.insert(op.insert, (0, _extend3.default)({}, _defineProperty({}, format, value), op.attributes)); + } + }, new _quillDelta2.default()); + } +} + +function computeStyle(node) { + if (node.nodeType !== Node.ELEMENT_NODE) return {}; + var DOM_KEY = '__ql-computed-style'; + return node[DOM_KEY] || (node[DOM_KEY] = window.getComputedStyle(node)); +} + +function deltaEndsWith(delta, text) { + var endText = ""; + for (var i = delta.ops.length - 1; i >= 0 && endText.length < text.length; --i) { + var op = delta.ops[i]; + if (typeof op.insert !== 'string') break; + endText = op.insert + endText; + } + return endText.slice(-1 * text.length) === text; +} + +function isLine(node) { + if (node.childNodes.length === 0) return false; // Exclude embed blocks + var style = computeStyle(node); + return ['block', 'list-item'].indexOf(style.display) > -1; +} + +function traverse(node, elementMatchers, textMatchers) { + // Post-order + if (node.nodeType === node.TEXT_NODE) { + return textMatchers.reduce(function (delta, matcher) { + return matcher(node, delta); + }, new _quillDelta2.default()); + } else if (node.nodeType === node.ELEMENT_NODE) { + return [].reduce.call(node.childNodes || [], function (delta, childNode) { + var childrenDelta = traverse(childNode, elementMatchers, textMatchers); + if (childNode.nodeType === node.ELEMENT_NODE) { + childrenDelta = elementMatchers.reduce(function (childrenDelta, matcher) { + return matcher(childNode, childrenDelta); + }, childrenDelta); + childrenDelta = (childNode[DOM_KEY] || []).reduce(function (childrenDelta, matcher) { + return matcher(childNode, childrenDelta); + }, childrenDelta); + } + return delta.concat(childrenDelta); + }, new _quillDelta2.default()); + } else { + return new _quillDelta2.default(); + } +} + +function matchAlias(format, node, delta) { + return applyFormat(delta, format, true); +} + +function matchAttributor(node, delta) { + var attributes = _parchment2.default.Attributor.Attribute.keys(node); + var classes = _parchment2.default.Attributor.Class.keys(node); + var styles = _parchment2.default.Attributor.Style.keys(node); + var formats = {}; + attributes.concat(classes).concat(styles).forEach(function (name) { + var attr = _parchment2.default.query(name, _parchment2.default.Scope.ATTRIBUTE); + if (attr != null) { + formats[attr.attrName] = attr.value(node); + if (formats[attr.attrName]) return; + } + attr = ATTRIBUTE_ATTRIBUTORS[name]; + if (attr != null && (attr.attrName === name || attr.keyName === name)) { + formats[attr.attrName] = attr.value(node) || undefined; + } + attr = STYLE_ATTRIBUTORS[name]; + if (attr != null && (attr.attrName === name || attr.keyName === name)) { + attr = STYLE_ATTRIBUTORS[name]; + formats[attr.attrName] = attr.value(node) || undefined; + } + }); + if (Object.keys(formats).length > 0) { + delta = applyFormat(delta, formats); + } + return delta; +} + +function matchBlot(node, delta) { + var match = _parchment2.default.query(node); + if (match == null) return delta; + if (match.prototype instanceof _parchment2.default.Embed) { + var embed = {}; + var value = match.value(node); + if (value != null) { + embed[match.blotName] = value; + delta = new _quillDelta2.default().insert(embed, match.formats(node)); + } + } else if (typeof match.formats === 'function') { + delta = applyFormat(delta, match.blotName, match.formats(node)); + } + return delta; +} + +function matchBreak(node, delta) { + if (!deltaEndsWith(delta, '\n')) { + delta.insert('\n'); + } + return delta; +} + +function matchIgnore() { + return new _quillDelta2.default(); +} + +function matchIndent(node, delta) { + var match = _parchment2.default.query(node); + if (match == null || match.blotName !== 'list-item' || !deltaEndsWith(delta, '\n')) { + return delta; + } + var indent = -1, + parent = node.parentNode; + while (!parent.classList.contains('ql-clipboard')) { + if ((_parchment2.default.query(parent) || {}).blotName === 'list') { + indent += 1; + } + parent = parent.parentNode; + } + if (indent <= 0) return delta; + return delta.compose(new _quillDelta2.default().retain(delta.length() - 1).retain(1, { indent: indent })); +} + +function matchNewline(node, delta) { + if (!deltaEndsWith(delta, '\n')) { + if (isLine(node) || delta.length() > 0 && node.nextSibling && isLine(node.nextSibling)) { + delta.insert('\n'); + } + } + return delta; +} + +function matchSpacing(node, delta) { + if (isLine(node) && node.nextElementSibling != null && !deltaEndsWith(delta, '\n\n')) { + var nodeHeight = node.offsetHeight + parseFloat(computeStyle(node).marginTop) + parseFloat(computeStyle(node).marginBottom); + if (node.nextElementSibling.offsetTop > node.offsetTop + nodeHeight * 1.5) { + delta.insert('\n'); + } + } + return delta; +} + +function matchStyles(node, delta) { + var formats = {}; + var style = node.style || {}; + if (style.fontStyle && computeStyle(node).fontStyle === 'italic') { + formats.italic = true; + } + if (style.fontWeight && (computeStyle(node).fontWeight.startsWith('bold') || parseInt(computeStyle(node).fontWeight) >= 700)) { + formats.bold = true; + } + if (Object.keys(formats).length > 0) { + delta = applyFormat(delta, formats); + } + if (parseFloat(style.textIndent || 0) > 0) { + // Could be 0.5in + delta = new _quillDelta2.default().insert('\t').concat(delta); + } + return delta; +} + +function matchText(node, delta) { + var text = node.data; + // Word represents empty line with   + if (node.parentNode.tagName === 'O:P') { + return delta.insert(text.trim()); + } + if (text.trim().length === 0 && node.parentNode.classList.contains('ql-clipboard')) { + return delta; + } + if (!computeStyle(node.parentNode).whiteSpace.startsWith('pre')) { + // eslint-disable-next-line func-style + var replacer = function replacer(collapse, match) { + match = match.replace(/[^\u00a0]/g, ''); // \u00a0 is nbsp; + return match.length < 1 && collapse ? ' ' : match; + }; + text = text.replace(/\r\n/g, ' ').replace(/\n/g, ' '); + text = text.replace(/\s\s+/g, replacer.bind(replacer, true)); // collapse whitespace + if (node.previousSibling == null && isLine(node.parentNode) || node.previousSibling != null && isLine(node.previousSibling)) { + text = text.replace(/^\s+/, replacer.bind(replacer, false)); + } + if (node.nextSibling == null && isLine(node.parentNode) || node.nextSibling != null && isLine(node.nextSibling)) { + text = text.replace(/\s+$/, replacer.bind(replacer, false)); + } + } + return delta.insert(text); +} + +exports.default = Clipboard; +exports.matchAttributor = matchAttributor; +exports.matchBlot = matchBlot; +exports.matchNewline = matchNewline; +exports.matchSpacing = matchSpacing; +exports.matchText = matchText; + +/***/ }), +/* 56 */, +/* 57 */, +/* 58 */, +/* 59 */, +/* 60 */, +/* 61 */, +/* 62 */, +/* 63 */, +/* 64 */, +/* 65 */, +/* 66 */, +/* 67 */, +/* 68 */, +/* 69 */, +/* 70 */, +/* 71 */, +/* 72 */, +/* 73 */, +/* 74 */, +/* 75 */, +/* 76 */, +/* 77 */, +/* 78 */, +/* 79 */, +/* 80 */, +/* 81 */, +/* 82 */, +/* 83 */, +/* 84 */, +/* 85 */, +/* 86 */, +/* 87 */, +/* 88 */, +/* 89 */, +/* 90 */, +/* 91 */, +/* 92 */, +/* 93 */, +/* 94 */, +/* 95 */, +/* 96 */, +/* 97 */, +/* 98 */, +/* 99 */, +/* 100 */, +/* 101 */, +/* 102 */, +/* 103 */, +/* 104 */, +/* 105 */, +/* 106 */, +/* 107 */, +/* 108 */, +/* 109 */, +/* 110 */ +/***/ (function(module, exports, __webpack_require__) { + +module.exports = __webpack_require__(29); + + +/***/ }) +/******/ ])["default"]; +}); \ No newline at end of file diff --git a/Wino.Mail/JS/Quill/quill.js b/Wino.Mail/JS/Quill/quill.js new file mode 100644 index 00000000..5dc8fbe3 --- /dev/null +++ b/Wino.Mail/JS/Quill/quill.js @@ -0,0 +1,11489 @@ +/*! + * Quill Editor v1.3.6 + * https://quilljs.com/ + * Copyright (c) 2014, Jason Chen + * Copyright (c) 2013, salesforce.com + */ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["Quill"] = factory(); + else + root["Quill"] = factory(); +})(typeof self !== 'undefined' ? self : this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 109); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var container_1 = __webpack_require__(17); +var format_1 = __webpack_require__(18); +var leaf_1 = __webpack_require__(19); +var scroll_1 = __webpack_require__(45); +var inline_1 = __webpack_require__(46); +var block_1 = __webpack_require__(47); +var embed_1 = __webpack_require__(48); +var text_1 = __webpack_require__(49); +var attributor_1 = __webpack_require__(12); +var class_1 = __webpack_require__(32); +var style_1 = __webpack_require__(33); +var store_1 = __webpack_require__(31); +var Registry = __webpack_require__(1); +var Parchment = { + Scope: Registry.Scope, + create: Registry.create, + find: Registry.find, + query: Registry.query, + register: Registry.register, + Container: container_1.default, + Format: format_1.default, + Leaf: leaf_1.default, + Embed: embed_1.default, + Scroll: scroll_1.default, + Block: block_1.default, + Inline: inline_1.default, + Text: text_1.default, + Attributor: { + Attribute: attributor_1.default, + Class: class_1.default, + Style: style_1.default, + Store: store_1.default, + }, +}; +exports.default = Parchment; + + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ParchmentError = /** @class */ (function (_super) { + __extends(ParchmentError, _super); + function ParchmentError(message) { + var _this = this; + message = '[Parchment] ' + message; + _this = _super.call(this, message) || this; + _this.message = message; + _this.name = _this.constructor.name; + return _this; + } + return ParchmentError; +}(Error)); +exports.ParchmentError = ParchmentError; +var attributes = {}; +var classes = {}; +var tags = {}; +var types = {}; +exports.DATA_KEY = '__blot'; +var Scope; +(function (Scope) { + Scope[Scope["TYPE"] = 3] = "TYPE"; + Scope[Scope["LEVEL"] = 12] = "LEVEL"; + Scope[Scope["ATTRIBUTE"] = 13] = "ATTRIBUTE"; + Scope[Scope["BLOT"] = 14] = "BLOT"; + Scope[Scope["INLINE"] = 7] = "INLINE"; + Scope[Scope["BLOCK"] = 11] = "BLOCK"; + Scope[Scope["BLOCK_BLOT"] = 10] = "BLOCK_BLOT"; + Scope[Scope["INLINE_BLOT"] = 6] = "INLINE_BLOT"; + Scope[Scope["BLOCK_ATTRIBUTE"] = 9] = "BLOCK_ATTRIBUTE"; + Scope[Scope["INLINE_ATTRIBUTE"] = 5] = "INLINE_ATTRIBUTE"; + Scope[Scope["ANY"] = 15] = "ANY"; +})(Scope = exports.Scope || (exports.Scope = {})); +function create(input, value) { + var match = query(input); + if (match == null) { + throw new ParchmentError("Unable to create " + input + " blot"); + } + var BlotClass = match; + var node = + // @ts-ignore + input instanceof Node || input['nodeType'] === Node.TEXT_NODE ? input : BlotClass.create(value); + return new BlotClass(node, value); +} +exports.create = create; +function find(node, bubble) { + if (bubble === void 0) { bubble = false; } + if (node == null) + return null; + // @ts-ignore + if (node[exports.DATA_KEY] != null) + return node[exports.DATA_KEY].blot; + if (bubble) + return find(node.parentNode, bubble); + return null; +} +exports.find = find; +function query(query, scope) { + if (scope === void 0) { scope = Scope.ANY; } + var match; + if (typeof query === 'string') { + match = types[query] || attributes[query]; + // @ts-ignore + } + else if (query instanceof Text || query['nodeType'] === Node.TEXT_NODE) { + match = types['text']; + } + else if (typeof query === 'number') { + if (query & Scope.LEVEL & Scope.BLOCK) { + match = types['block']; + } + else if (query & Scope.LEVEL & Scope.INLINE) { + match = types['inline']; + } + } + else if (query instanceof HTMLElement) { + var names = (query.getAttribute('class') || '').split(/\s+/); + for (var i in names) { + match = classes[names[i]]; + if (match) + break; + } + match = match || tags[query.tagName]; + } + if (match == null) + return null; + // @ts-ignore + if (scope & Scope.LEVEL & match.scope && scope & Scope.TYPE & match.scope) + return match; + return null; +} +exports.query = query; +function register() { + var Definitions = []; + for (var _i = 0; _i < arguments.length; _i++) { + Definitions[_i] = arguments[_i]; + } + if (Definitions.length > 1) { + return Definitions.map(function (d) { + return register(d); + }); + } + var Definition = Definitions[0]; + if (typeof Definition.blotName !== 'string' && typeof Definition.attrName !== 'string') { + throw new ParchmentError('Invalid definition'); + } + else if (Definition.blotName === 'abstract') { + throw new ParchmentError('Cannot register abstract class'); + } + types[Definition.blotName || Definition.attrName] = Definition; + if (typeof Definition.keyName === 'string') { + attributes[Definition.keyName] = Definition; + } + else { + if (Definition.className != null) { + classes[Definition.className] = Definition; + } + if (Definition.tagName != null) { + if (Array.isArray(Definition.tagName)) { + Definition.tagName = Definition.tagName.map(function (tagName) { + return tagName.toUpperCase(); + }); + } + else { + Definition.tagName = Definition.tagName.toUpperCase(); + } + var tagNames = Array.isArray(Definition.tagName) ? Definition.tagName : [Definition.tagName]; + tagNames.forEach(function (tag) { + if (tags[tag] == null || Definition.className == null) { + tags[tag] = Definition; + } + }); + } + } + return Definition; +} +exports.register = register; + + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + +var diff = __webpack_require__(51); +var equal = __webpack_require__(11); +var extend = __webpack_require__(3); +var op = __webpack_require__(20); + + +var NULL_CHARACTER = String.fromCharCode(0); // Placeholder char for embed in diff() + + +var Delta = function (ops) { + // Assume we are given a well formed ops + if (Array.isArray(ops)) { + this.ops = ops; + } else if (ops != null && Array.isArray(ops.ops)) { + this.ops = ops.ops; + } else { + this.ops = []; + } +}; + + +Delta.prototype.insert = function (text, attributes) { + var newOp = {}; + if (text.length === 0) return this; + newOp.insert = text; + if (attributes != null && typeof attributes === 'object' && Object.keys(attributes).length > 0) { + newOp.attributes = attributes; + } + return this.push(newOp); +}; + +Delta.prototype['delete'] = function (length) { + if (length <= 0) return this; + return this.push({ 'delete': length }); +}; + +Delta.prototype.retain = function (length, attributes) { + if (length <= 0) return this; + var newOp = { retain: length }; + if (attributes != null && typeof attributes === 'object' && Object.keys(attributes).length > 0) { + newOp.attributes = attributes; + } + return this.push(newOp); +}; + +Delta.prototype.push = function (newOp) { + var index = this.ops.length; + var lastOp = this.ops[index - 1]; + newOp = extend(true, {}, newOp); + if (typeof lastOp === 'object') { + if (typeof newOp['delete'] === 'number' && typeof lastOp['delete'] === 'number') { + this.ops[index - 1] = { 'delete': lastOp['delete'] + newOp['delete'] }; + return this; + } + // Since it does not matter if we insert before or after deleting at the same index, + // always prefer to insert first + if (typeof lastOp['delete'] === 'number' && newOp.insert != null) { + index -= 1; + lastOp = this.ops[index - 1]; + if (typeof lastOp !== 'object') { + this.ops.unshift(newOp); + return this; + } + } + if (equal(newOp.attributes, lastOp.attributes)) { + if (typeof newOp.insert === 'string' && typeof lastOp.insert === 'string') { + this.ops[index - 1] = { insert: lastOp.insert + newOp.insert }; + if (typeof newOp.attributes === 'object') this.ops[index - 1].attributes = newOp.attributes + return this; + } else if (typeof newOp.retain === 'number' && typeof lastOp.retain === 'number') { + this.ops[index - 1] = { retain: lastOp.retain + newOp.retain }; + if (typeof newOp.attributes === 'object') this.ops[index - 1].attributes = newOp.attributes + return this; + } + } + } + if (index === this.ops.length) { + this.ops.push(newOp); + } else { + this.ops.splice(index, 0, newOp); + } + return this; +}; + +Delta.prototype.chop = function () { + var lastOp = this.ops[this.ops.length - 1]; + if (lastOp && lastOp.retain && !lastOp.attributes) { + this.ops.pop(); + } + return this; +}; + +Delta.prototype.filter = function (predicate) { + return this.ops.filter(predicate); +}; + +Delta.prototype.forEach = function (predicate) { + this.ops.forEach(predicate); +}; + +Delta.prototype.map = function (predicate) { + return this.ops.map(predicate); +}; + +Delta.prototype.partition = function (predicate) { + var passed = [], failed = []; + this.forEach(function(op) { + var target = predicate(op) ? passed : failed; + target.push(op); + }); + return [passed, failed]; +}; + +Delta.prototype.reduce = function (predicate, initial) { + return this.ops.reduce(predicate, initial); +}; + +Delta.prototype.changeLength = function () { + return this.reduce(function (length, elem) { + if (elem.insert) { + return length + op.length(elem); + } else if (elem.delete) { + return length - elem.delete; + } + return length; + }, 0); +}; + +Delta.prototype.length = function () { + return this.reduce(function (length, elem) { + return length + op.length(elem); + }, 0); +}; + +Delta.prototype.slice = function (start, end) { + start = start || 0; + if (typeof end !== 'number') end = Infinity; + var ops = []; + var iter = op.iterator(this.ops); + var index = 0; + while (index < end && iter.hasNext()) { + var nextOp; + if (index < start) { + nextOp = iter.next(start - index); + } else { + nextOp = iter.next(end - index); + ops.push(nextOp); + } + index += op.length(nextOp); + } + return new Delta(ops); +}; + + +Delta.prototype.compose = function (other) { + var thisIter = op.iterator(this.ops); + var otherIter = op.iterator(other.ops); + var delta = new Delta(); + while (thisIter.hasNext() || otherIter.hasNext()) { + if (otherIter.peekType() === 'insert') { + delta.push(otherIter.next()); + } else if (thisIter.peekType() === 'delete') { + delta.push(thisIter.next()); + } else { + var length = Math.min(thisIter.peekLength(), otherIter.peekLength()); + var thisOp = thisIter.next(length); + var otherOp = otherIter.next(length); + if (typeof otherOp.retain === 'number') { + var newOp = {}; + if (typeof thisOp.retain === 'number') { + newOp.retain = length; + } else { + newOp.insert = thisOp.insert; + } + // Preserve null when composing with a retain, otherwise remove it for inserts + var attributes = op.attributes.compose(thisOp.attributes, otherOp.attributes, typeof thisOp.retain === 'number'); + if (attributes) newOp.attributes = attributes; + delta.push(newOp); + // Other op should be delete, we could be an insert or retain + // Insert + delete cancels out + } else if (typeof otherOp['delete'] === 'number' && typeof thisOp.retain === 'number') { + delta.push(otherOp); + } + } + } + return delta.chop(); +}; + +Delta.prototype.concat = function (other) { + var delta = new Delta(this.ops.slice()); + if (other.ops.length > 0) { + delta.push(other.ops[0]); + delta.ops = delta.ops.concat(other.ops.slice(1)); + } + return delta; +}; + +Delta.prototype.diff = function (other, index) { + if (this.ops === other.ops) { + return new Delta(); + } + var strings = [this, other].map(function (delta) { + return delta.map(function (op) { + if (op.insert != null) { + return typeof op.insert === 'string' ? op.insert : NULL_CHARACTER; + } + var prep = (delta === other) ? 'on' : 'with'; + throw new Error('diff() called ' + prep + ' non-document'); + }).join(''); + }); + var delta = new Delta(); + var diffResult = diff(strings[0], strings[1], index); + var thisIter = op.iterator(this.ops); + var otherIter = op.iterator(other.ops); + diffResult.forEach(function (component) { + var length = component[1].length; + while (length > 0) { + var opLength = 0; + switch (component[0]) { + case diff.INSERT: + opLength = Math.min(otherIter.peekLength(), length); + delta.push(otherIter.next(opLength)); + break; + case diff.DELETE: + opLength = Math.min(length, thisIter.peekLength()); + thisIter.next(opLength); + delta['delete'](opLength); + break; + case diff.EQUAL: + opLength = Math.min(thisIter.peekLength(), otherIter.peekLength(), length); + var thisOp = thisIter.next(opLength); + var otherOp = otherIter.next(opLength); + if (equal(thisOp.insert, otherOp.insert)) { + delta.retain(opLength, op.attributes.diff(thisOp.attributes, otherOp.attributes)); + } else { + delta.push(otherOp)['delete'](opLength); + } + break; + } + length -= opLength; + } + }); + return delta.chop(); +}; + +Delta.prototype.eachLine = function (predicate, newline) { + newline = newline || '\n'; + var iter = op.iterator(this.ops); + var line = new Delta(); + var i = 0; + while (iter.hasNext()) { + if (iter.peekType() !== 'insert') return; + var thisOp = iter.peek(); + var start = op.length(thisOp) - iter.peekLength(); + var index = typeof thisOp.insert === 'string' ? + thisOp.insert.indexOf(newline, start) - start : -1; + if (index < 0) { + line.push(iter.next()); + } else if (index > 0) { + line.push(iter.next(index)); + } else { + if (predicate(line, iter.next(1).attributes || {}, i) === false) { + return; + } + i += 1; + line = new Delta(); + } + } + if (line.length() > 0) { + predicate(line, {}, i); + } +}; + +Delta.prototype.transform = function (other, priority) { + priority = !!priority; + if (typeof other === 'number') { + return this.transformPosition(other, priority); + } + var thisIter = op.iterator(this.ops); + var otherIter = op.iterator(other.ops); + var delta = new Delta(); + while (thisIter.hasNext() || otherIter.hasNext()) { + if (thisIter.peekType() === 'insert' && (priority || otherIter.peekType() !== 'insert')) { + delta.retain(op.length(thisIter.next())); + } else if (otherIter.peekType() === 'insert') { + delta.push(otherIter.next()); + } else { + var length = Math.min(thisIter.peekLength(), otherIter.peekLength()); + var thisOp = thisIter.next(length); + var otherOp = otherIter.next(length); + if (thisOp['delete']) { + // Our delete either makes their delete redundant or removes their retain + continue; + } else if (otherOp['delete']) { + delta.push(otherOp); + } else { + // We retain either their retain or insert + delta.retain(length, op.attributes.transform(thisOp.attributes, otherOp.attributes, priority)); + } + } + } + return delta.chop(); +}; + +Delta.prototype.transformPosition = function (index, priority) { + priority = !!priority; + var thisIter = op.iterator(this.ops); + var offset = 0; + while (thisIter.hasNext() && offset <= index) { + var length = thisIter.peekLength(); + var nextType = thisIter.peekType(); + thisIter.next(); + if (nextType === 'delete') { + index -= Math.min(length, index - offset); + continue; + } else if (nextType === 'insert' && (offset < index || !priority)) { + index += length; + } + offset += length; + } + return index; +}; + + +module.exports = Delta; + + +/***/ }), +/* 3 */ +/***/ (function(module, exports) { + +'use strict'; + +var hasOwn = Object.prototype.hasOwnProperty; +var toStr = Object.prototype.toString; + +var isArray = function isArray(arr) { + if (typeof Array.isArray === 'function') { + return Array.isArray(arr); + } + + return toStr.call(arr) === '[object Array]'; +}; + +var isPlainObject = function isPlainObject(obj) { + if (!obj || toStr.call(obj) !== '[object Object]') { + return false; + } + + var hasOwnConstructor = hasOwn.call(obj, 'constructor'); + var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf'); + // Not own constructor property must be Object + if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + var key; + for (key in obj) { /**/ } + + return typeof key === 'undefined' || hasOwn.call(obj, key); +}; + +module.exports = function extend() { + var options, name, src, copy, copyIsArray, clone; + var target = arguments[0]; + var i = 1; + var length = arguments.length; + var deep = false; + + // Handle a deep copy situation + if (typeof target === 'boolean') { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + if (target == null || (typeof target !== 'object' && typeof target !== 'function')) { + target = {}; + } + + for (; i < length; ++i) { + options = arguments[i]; + // Only deal with non-null/undefined values + if (options != null) { + // Extend the base object + for (name in options) { + src = target[name]; + copy = options[name]; + + // Prevent never-ending loop + if (target !== copy) { + // Recurse if we're merging plain objects or arrays + if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) { + if (copyIsArray) { + copyIsArray = false; + clone = src && isArray(src) ? src : []; + } else { + clone = src && isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[name] = extend(deep, clone, copy); + + // Don't bring in undefined values + } else if (typeof copy !== 'undefined') { + target[name] = copy; + } + } + } + } + } + + // Return the modified object + return target; +}; + + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.BlockEmbed = exports.bubbleFormats = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _break = __webpack_require__(16); + +var _break2 = _interopRequireDefault(_break); + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var NEWLINE_LENGTH = 1; + +var BlockEmbed = function (_Parchment$Embed) { + _inherits(BlockEmbed, _Parchment$Embed); + + function BlockEmbed() { + _classCallCheck(this, BlockEmbed); + + return _possibleConstructorReturn(this, (BlockEmbed.__proto__ || Object.getPrototypeOf(BlockEmbed)).apply(this, arguments)); + } + + _createClass(BlockEmbed, [{ + key: 'attach', + value: function attach() { + _get(BlockEmbed.prototype.__proto__ || Object.getPrototypeOf(BlockEmbed.prototype), 'attach', this).call(this); + this.attributes = new _parchment2.default.Attributor.Store(this.domNode); + } + }, { + key: 'delta', + value: function delta() { + return new _quillDelta2.default().insert(this.value(), (0, _extend2.default)(this.formats(), this.attributes.values())); + } + }, { + key: 'format', + value: function format(name, value) { + var attribute = _parchment2.default.query(name, _parchment2.default.Scope.BLOCK_ATTRIBUTE); + if (attribute != null) { + this.attributes.attribute(attribute, value); + } + } + }, { + key: 'formatAt', + value: function formatAt(index, length, name, value) { + this.format(name, value); + } + }, { + key: 'insertAt', + value: function insertAt(index, value, def) { + if (typeof value === 'string' && value.endsWith('\n')) { + var block = _parchment2.default.create(Block.blotName); + this.parent.insertBefore(block, index === 0 ? this : this.next); + block.insertAt(0, value.slice(0, -1)); + } else { + _get(BlockEmbed.prototype.__proto__ || Object.getPrototypeOf(BlockEmbed.prototype), 'insertAt', this).call(this, index, value, def); + } + } + }]); + + return BlockEmbed; +}(_parchment2.default.Embed); + +BlockEmbed.scope = _parchment2.default.Scope.BLOCK_BLOT; +// It is important for cursor behavior BlockEmbeds use tags that are block level elements + + +var Block = function (_Parchment$Block) { + _inherits(Block, _Parchment$Block); + + function Block(domNode) { + _classCallCheck(this, Block); + + var _this2 = _possibleConstructorReturn(this, (Block.__proto__ || Object.getPrototypeOf(Block)).call(this, domNode)); + + _this2.cache = {}; + return _this2; + } + + _createClass(Block, [{ + key: 'delta', + value: function delta() { + if (this.cache.delta == null) { + this.cache.delta = this.descendants(_parchment2.default.Leaf).reduce(function (delta, leaf) { + if (leaf.length() === 0) { + return delta; + } else { + return delta.insert(leaf.value(), bubbleFormats(leaf)); + } + }, new _quillDelta2.default()).insert('\n', bubbleFormats(this)); + } + return this.cache.delta; + } + }, { + key: 'deleteAt', + value: function deleteAt(index, length) { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'deleteAt', this).call(this, index, length); + this.cache = {}; + } + }, { + key: 'formatAt', + value: function formatAt(index, length, name, value) { + if (length <= 0) return; + if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK)) { + if (index + length === this.length()) { + this.format(name, value); + } + } else { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'formatAt', this).call(this, index, Math.min(length, this.length() - index - 1), name, value); + } + this.cache = {}; + } + }, { + key: 'insertAt', + value: function insertAt(index, value, def) { + if (def != null) return _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertAt', this).call(this, index, value, def); + if (value.length === 0) return; + var lines = value.split('\n'); + var text = lines.shift(); + if (text.length > 0) { + if (index < this.length() - 1 || this.children.tail == null) { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertAt', this).call(this, Math.min(index, this.length() - 1), text); + } else { + this.children.tail.insertAt(this.children.tail.length(), text); + } + this.cache = {}; + } + var block = this; + lines.reduce(function (index, line) { + block = block.split(index, true); + block.insertAt(0, line); + return line.length; + }, index + text.length); + } + }, { + key: 'insertBefore', + value: function insertBefore(blot, ref) { + var head = this.children.head; + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertBefore', this).call(this, blot, ref); + if (head instanceof _break2.default) { + head.remove(); + } + this.cache = {}; + } + }, { + key: 'length', + value: function length() { + if (this.cache.length == null) { + this.cache.length = _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'length', this).call(this) + NEWLINE_LENGTH; + } + return this.cache.length; + } + }, { + key: 'moveChildren', + value: function moveChildren(target, ref) { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'moveChildren', this).call(this, target, ref); + this.cache = {}; + } + }, { + key: 'optimize', + value: function optimize(context) { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'optimize', this).call(this, context); + this.cache = {}; + } + }, { + key: 'path', + value: function path(index) { + return _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'path', this).call(this, index, true); + } + }, { + key: 'removeChild', + value: function removeChild(child) { + _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'removeChild', this).call(this, child); + this.cache = {}; + } + }, { + key: 'split', + value: function split(index) { + var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + if (force && (index === 0 || index >= this.length() - NEWLINE_LENGTH)) { + var clone = this.clone(); + if (index === 0) { + this.parent.insertBefore(clone, this); + return this; + } else { + this.parent.insertBefore(clone, this.next); + return clone; + } + } else { + var next = _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'split', this).call(this, index, force); + this.cache = {}; + return next; + } + } + }]); + + return Block; +}(_parchment2.default.Block); + +Block.blotName = 'block'; +Block.tagName = 'P'; +Block.defaultChild = 'break'; +Block.allowedChildren = [_inline2.default, _parchment2.default.Embed, _text2.default]; + +function bubbleFormats(blot) { + var formats = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + if (blot == null) return formats; + if (typeof blot.formats === 'function') { + formats = (0, _extend2.default)(formats, blot.formats()); + } + if (blot.parent == null || blot.parent.blotName == 'scroll' || blot.parent.statics.scope !== blot.statics.scope) { + return formats; + } + return bubbleFormats(blot.parent, formats); +} + +exports.bubbleFormats = bubbleFormats; +exports.BlockEmbed = BlockEmbed; +exports.default = Block; + +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.overload = exports.expandConfig = undefined; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +__webpack_require__(50); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _editor = __webpack_require__(14); + +var _editor2 = _interopRequireDefault(_editor); + +var _emitter3 = __webpack_require__(8); + +var _emitter4 = _interopRequireDefault(_emitter3); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _selection = __webpack_require__(15); + +var _selection2 = _interopRequireDefault(_selection); + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +var _logger = __webpack_require__(10); + +var _logger2 = _interopRequireDefault(_logger); + +var _theme = __webpack_require__(34); + +var _theme2 = _interopRequireDefault(_theme); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var debug = (0, _logger2.default)('quill'); + +var Quill = function () { + _createClass(Quill, null, [{ + key: 'debug', + value: function debug(limit) { + if (limit === true) { + limit = 'log'; + } + _logger2.default.level(limit); + } + }, { + key: 'find', + value: function find(node) { + return node.__quill || _parchment2.default.find(node); + } + }, { + key: 'import', + value: function _import(name) { + if (this.imports[name] == null) { + debug.error('Cannot import ' + name + '. Are you sure it was registered?'); + } + return this.imports[name]; + } + }, { + key: 'register', + value: function register(path, target) { + var _this = this; + + var overwrite = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + + if (typeof path !== 'string') { + var name = path.attrName || path.blotName; + if (typeof name === 'string') { + // register(Blot | Attributor, overwrite) + this.register('formats/' + name, path, target); + } else { + Object.keys(path).forEach(function (key) { + _this.register(key, path[key], target); + }); + } + } else { + if (this.imports[path] != null && !overwrite) { + debug.warn('Overwriting ' + path + ' with', target); + } + this.imports[path] = target; + if ((path.startsWith('blots/') || path.startsWith('formats/')) && target.blotName !== 'abstract') { + _parchment2.default.register(target); + } else if (path.startsWith('modules') && typeof target.register === 'function') { + target.register(); + } + } + } + }]); + + function Quill(container) { + var _this2 = this; + + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + _classCallCheck(this, Quill); + + this.options = expandConfig(container, options); + this.container = this.options.container; + if (this.container == null) { + return debug.error('Invalid Quill container', container); + } + if (this.options.debug) { + Quill.debug(this.options.debug); + } + var html = this.container.innerHTML.trim(); + this.container.classList.add('ql-container'); + this.container.innerHTML = ''; + this.container.__quill = this; + this.root = this.addContainer('ql-editor'); + this.root.classList.add('ql-blank'); + this.root.setAttribute('data-gramm', false); + this.scrollingContainer = this.options.scrollingContainer || this.root; + this.emitter = new _emitter4.default(); + this.scroll = _parchment2.default.create(this.root, { + emitter: this.emitter, + whitelist: this.options.formats + }); + this.editor = new _editor2.default(this.scroll); + this.selection = new _selection2.default(this.scroll, this.emitter); + this.theme = new this.options.theme(this, this.options); + this.keyboard = this.theme.addModule('keyboard'); + this.clipboard = this.theme.addModule('clipboard'); + this.history = this.theme.addModule('history'); + this.theme.init(); + this.emitter.on(_emitter4.default.events.EDITOR_CHANGE, function (type) { + if (type === _emitter4.default.events.TEXT_CHANGE) { + _this2.root.classList.toggle('ql-blank', _this2.editor.isBlank()); + } + }); + this.emitter.on(_emitter4.default.events.SCROLL_UPDATE, function (source, mutations) { + var range = _this2.selection.lastRange; + var index = range && range.length === 0 ? range.index : undefined; + modify.call(_this2, function () { + return _this2.editor.update(null, mutations, index); + }, source); + }); + var contents = this.clipboard.convert('
' + html + '


'); + this.setContents(contents); + this.history.clear(); + if (this.options.placeholder) { + this.root.setAttribute('data-placeholder', this.options.placeholder); + } + if (this.options.readOnly) { + this.disable(); + } + } + + _createClass(Quill, [{ + key: 'addContainer', + value: function addContainer(container) { + var refNode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; + + if (typeof container === 'string') { + var className = container; + container = document.createElement('div'); + container.classList.add(className); + } + this.container.insertBefore(container, refNode); + return container; + } + }, { + key: 'blur', + value: function blur() { + this.selection.setRange(null); + } + }, { + key: 'deleteText', + value: function deleteText(index, length, source) { + var _this3 = this; + + var _overload = overload(index, length, source); + + var _overload2 = _slicedToArray(_overload, 4); + + index = _overload2[0]; + length = _overload2[1]; + source = _overload2[3]; + + return modify.call(this, function () { + return _this3.editor.deleteText(index, length); + }, source, index, -1 * length); + } + }, { + key: 'disable', + value: function disable() { + this.enable(false); + } + }, { + key: 'enable', + value: function enable() { + var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; + + this.scroll.enable(enabled); + this.container.classList.toggle('ql-disabled', !enabled); + } + }, { + key: 'focus', + value: function focus() { + var scrollTop = this.scrollingContainer.scrollTop; + this.selection.focus(); + this.scrollingContainer.scrollTop = scrollTop; + this.scrollIntoView(); + } + }, { + key: 'format', + value: function format(name, value) { + var _this4 = this; + + var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _emitter4.default.sources.API; + + return modify.call(this, function () { + var range = _this4.getSelection(true); + var change = new _quillDelta2.default(); + if (range == null) { + return change; + } else if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK)) { + change = _this4.editor.formatLine(range.index, range.length, _defineProperty({}, name, value)); + } else if (range.length === 0) { + _this4.selection.format(name, value); + return change; + } else { + change = _this4.editor.formatText(range.index, range.length, _defineProperty({}, name, value)); + } + _this4.setSelection(range, _emitter4.default.sources.SILENT); + return change; + }, source); + } + }, { + key: 'formatLine', + value: function formatLine(index, length, name, value, source) { + var _this5 = this; + + var formats = void 0; + + var _overload3 = overload(index, length, name, value, source); + + var _overload4 = _slicedToArray(_overload3, 4); + + index = _overload4[0]; + length = _overload4[1]; + formats = _overload4[2]; + source = _overload4[3]; + + return modify.call(this, function () { + return _this5.editor.formatLine(index, length, formats); + }, source, index, 0); + } + }, { + key: 'formatText', + value: function formatText(index, length, name, value, source) { + var _this6 = this; + + var formats = void 0; + + var _overload5 = overload(index, length, name, value, source); + + var _overload6 = _slicedToArray(_overload5, 4); + + index = _overload6[0]; + length = _overload6[1]; + formats = _overload6[2]; + source = _overload6[3]; + + return modify.call(this, function () { + return _this6.editor.formatText(index, length, formats); + }, source, index, 0); + } + }, { + key: 'getBounds', + value: function getBounds(index) { + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + + var bounds = void 0; + if (typeof index === 'number') { + bounds = this.selection.getBounds(index, length); + } else { + bounds = this.selection.getBounds(index.index, index.length); + } + var containerBounds = this.container.getBoundingClientRect(); + return { + bottom: bounds.bottom - containerBounds.top, + height: bounds.height, + left: bounds.left - containerBounds.left, + right: bounds.right - containerBounds.left, + top: bounds.top - containerBounds.top, + width: bounds.width + }; + } + }, { + key: 'getContents', + value: function getContents() { + var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.getLength() - index; + + var _overload7 = overload(index, length); + + var _overload8 = _slicedToArray(_overload7, 2); + + index = _overload8[0]; + length = _overload8[1]; + + return this.editor.getContents(index, length); + } + }, { + key: 'getFormat', + value: function getFormat() { + var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.getSelection(true); + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + + if (typeof index === 'number') { + return this.editor.getFormat(index, length); + } else { + return this.editor.getFormat(index.index, index.length); + } + } + }, { + key: 'getIndex', + value: function getIndex(blot) { + return blot.offset(this.scroll); + } + }, { + key: 'getLength', + value: function getLength() { + return this.scroll.length(); + } + }, { + key: 'getLeaf', + value: function getLeaf(index) { + return this.scroll.leaf(index); + } + }, { + key: 'getLine', + value: function getLine(index) { + return this.scroll.line(index); + } + }, { + key: 'getLines', + value: function getLines() { + var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Number.MAX_VALUE; + + if (typeof index !== 'number') { + return this.scroll.lines(index.index, index.length); + } else { + return this.scroll.lines(index, length); + } + } + }, { + key: 'getModule', + value: function getModule(name) { + return this.theme.modules[name]; + } + }, { + key: 'getSelection', + value: function getSelection() { + var focus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + + if (focus) this.focus(); + this.update(); // Make sure we access getRange with editor in consistent state + return this.selection.getRange()[0]; + } + }, { + key: 'getText', + value: function getText() { + var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.getLength() - index; + + var _overload9 = overload(index, length); + + var _overload10 = _slicedToArray(_overload9, 2); + + index = _overload10[0]; + length = _overload10[1]; + + return this.editor.getText(index, length); + } + }, { + key: 'hasFocus', + value: function hasFocus() { + return this.selection.hasFocus(); + } + }, { + key: 'insertEmbed', + value: function insertEmbed(index, embed, value) { + var _this7 = this; + + var source = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : Quill.sources.API; + + return modify.call(this, function () { + return _this7.editor.insertEmbed(index, embed, value); + }, source, index); + } + }, { + key: 'insertText', + value: function insertText(index, text, name, value, source) { + var _this8 = this; + + var formats = void 0; + + var _overload11 = overload(index, 0, name, value, source); + + var _overload12 = _slicedToArray(_overload11, 4); + + index = _overload12[0]; + formats = _overload12[2]; + source = _overload12[3]; + + return modify.call(this, function () { + return _this8.editor.insertText(index, text, formats); + }, source, index, text.length); + } + }, { + key: 'isEnabled', + value: function isEnabled() { + return !this.container.classList.contains('ql-disabled'); + } + }, { + key: 'off', + value: function off() { + return this.emitter.off.apply(this.emitter, arguments); + } + }, { + key: 'on', + value: function on() { + return this.emitter.on.apply(this.emitter, arguments); + } + }, { + key: 'once', + value: function once() { + return this.emitter.once.apply(this.emitter, arguments); + } + }, { + key: 'pasteHTML', + value: function pasteHTML(index, html, source) { + this.clipboard.dangerouslyPasteHTML(index, html, source); + } + }, { + key: 'removeFormat', + value: function removeFormat(index, length, source) { + var _this9 = this; + + var _overload13 = overload(index, length, source); + + var _overload14 = _slicedToArray(_overload13, 4); + + index = _overload14[0]; + length = _overload14[1]; + source = _overload14[3]; + + return modify.call(this, function () { + return _this9.editor.removeFormat(index, length); + }, source, index); + } + }, { + key: 'scrollIntoView', + value: function scrollIntoView() { + this.selection.scrollIntoView(this.scrollingContainer); + } + }, { + key: 'setContents', + value: function setContents(delta) { + var _this10 = this; + + var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API; + + return modify.call(this, function () { + delta = new _quillDelta2.default(delta); + var length = _this10.getLength(); + var deleted = _this10.editor.deleteText(0, length); + var applied = _this10.editor.applyDelta(delta); + var lastOp = applied.ops[applied.ops.length - 1]; + if (lastOp != null && typeof lastOp.insert === 'string' && lastOp.insert[lastOp.insert.length - 1] === '\n') { + _this10.editor.deleteText(_this10.getLength() - 1, 1); + applied.delete(1); + } + var ret = deleted.compose(applied); + return ret; + }, source); + } + }, { + key: 'setSelection', + value: function setSelection(index, length, source) { + if (index == null) { + this.selection.setRange(null, length || Quill.sources.API); + } else { + var _overload15 = overload(index, length, source); + + var _overload16 = _slicedToArray(_overload15, 4); + + index = _overload16[0]; + length = _overload16[1]; + source = _overload16[3]; + + this.selection.setRange(new _selection.Range(index, length), source); + if (source !== _emitter4.default.sources.SILENT) { + this.selection.scrollIntoView(this.scrollingContainer); + } + } + } + }, { + key: 'setText', + value: function setText(text) { + var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API; + + var delta = new _quillDelta2.default().insert(text); + return this.setContents(delta, source); + } + }, { + key: 'update', + value: function update() { + var source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _emitter4.default.sources.USER; + + var change = this.scroll.update(source); // Will update selection before selection.update() does if text changes + this.selection.update(source); + return change; + } + }, { + key: 'updateContents', + value: function updateContents(delta) { + var _this11 = this; + + var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API; + + return modify.call(this, function () { + delta = new _quillDelta2.default(delta); + return _this11.editor.applyDelta(delta, source); + }, source, true); + } + }]); + + return Quill; +}(); + +Quill.DEFAULTS = { + bounds: null, + formats: null, + modules: {}, + placeholder: '', + readOnly: false, + scrollingContainer: null, + strict: true, + theme: 'default' +}; +Quill.events = _emitter4.default.events; +Quill.sources = _emitter4.default.sources; +// eslint-disable-next-line no-undef +Quill.version = false ? 'dev' : "1.3.6"; + +Quill.imports = { + 'delta': _quillDelta2.default, + 'parchment': _parchment2.default, + 'core/module': _module2.default, + 'core/theme': _theme2.default +}; + +function expandConfig(container, userConfig) { + userConfig = (0, _extend2.default)(true, { + container: container, + modules: { + clipboard: true, + keyboard: true, + history: true + } + }, userConfig); + if (!userConfig.theme || userConfig.theme === Quill.DEFAULTS.theme) { + userConfig.theme = _theme2.default; + } else { + userConfig.theme = Quill.import('themes/' + userConfig.theme); + if (userConfig.theme == null) { + throw new Error('Invalid theme ' + userConfig.theme + '. Did you register it?'); + } + } + var themeConfig = (0, _extend2.default)(true, {}, userConfig.theme.DEFAULTS); + [themeConfig, userConfig].forEach(function (config) { + config.modules = config.modules || {}; + Object.keys(config.modules).forEach(function (module) { + if (config.modules[module] === true) { + config.modules[module] = {}; + } + }); + }); + var moduleNames = Object.keys(themeConfig.modules).concat(Object.keys(userConfig.modules)); + var moduleConfig = moduleNames.reduce(function (config, name) { + var moduleClass = Quill.import('modules/' + name); + if (moduleClass == null) { + debug.error('Cannot load ' + name + ' module. Are you sure you registered it?'); + } else { + config[name] = moduleClass.DEFAULTS || {}; + } + return config; + }, {}); + // Special case toolbar shorthand + if (userConfig.modules != null && userConfig.modules.toolbar && userConfig.modules.toolbar.constructor !== Object) { + userConfig.modules.toolbar = { + container: userConfig.modules.toolbar + }; + } + userConfig = (0, _extend2.default)(true, {}, Quill.DEFAULTS, { modules: moduleConfig }, themeConfig, userConfig); + ['bounds', 'container', 'scrollingContainer'].forEach(function (key) { + if (typeof userConfig[key] === 'string') { + userConfig[key] = document.querySelector(userConfig[key]); + } + }); + userConfig.modules = Object.keys(userConfig.modules).reduce(function (config, name) { + if (userConfig.modules[name]) { + config[name] = userConfig.modules[name]; + } + return config; + }, {}); + return userConfig; +} + +// Handle selection preservation and TEXT_CHANGE emission +// common to modification APIs +function modify(modifier, source, index, shift) { + if (this.options.strict && !this.isEnabled() && source === _emitter4.default.sources.USER) { + return new _quillDelta2.default(); + } + var range = index == null ? null : this.getSelection(); + var oldDelta = this.editor.delta; + var change = modifier(); + if (range != null) { + if (index === true) index = range.index; + if (shift == null) { + range = shiftRange(range, change, source); + } else if (shift !== 0) { + range = shiftRange(range, index, shift, source); + } + this.setSelection(range, _emitter4.default.sources.SILENT); + } + if (change.length() > 0) { + var _emitter; + + var args = [_emitter4.default.events.TEXT_CHANGE, change, oldDelta, source]; + (_emitter = this.emitter).emit.apply(_emitter, [_emitter4.default.events.EDITOR_CHANGE].concat(args)); + if (source !== _emitter4.default.sources.SILENT) { + var _emitter2; + + (_emitter2 = this.emitter).emit.apply(_emitter2, args); + } + } + return change; +} + +function overload(index, length, name, value, source) { + var formats = {}; + if (typeof index.index === 'number' && typeof index.length === 'number') { + // Allow for throwaway end (used by insertText/insertEmbed) + if (typeof length !== 'number') { + source = value, value = name, name = length, length = index.length, index = index.index; + } else { + length = index.length, index = index.index; + } + } else if (typeof length !== 'number') { + source = value, value = name, name = length, length = 0; + } + // Handle format being object, two format name/value strings or excluded + if ((typeof name === 'undefined' ? 'undefined' : _typeof(name)) === 'object') { + formats = name; + source = value; + } else if (typeof name === 'string') { + if (value != null) { + formats[name] = value; + } else { + source = name; + } + } + // Handle optional source + source = source || _emitter4.default.sources.API; + return [index, length, formats, source]; +} + +function shiftRange(range, index, length, source) { + if (range == null) return null; + var start = void 0, + end = void 0; + if (index instanceof _quillDelta2.default) { + var _map = [range.index, range.index + range.length].map(function (pos) { + return index.transformPosition(pos, source !== _emitter4.default.sources.USER); + }); + + var _map2 = _slicedToArray(_map, 2); + + start = _map2[0]; + end = _map2[1]; + } else { + var _map3 = [range.index, range.index + range.length].map(function (pos) { + if (pos < index || pos === index && source === _emitter4.default.sources.USER) return pos; + if (length >= 0) { + return pos + length; + } else { + return Math.max(index, pos + length); + } + }); + + var _map4 = _slicedToArray(_map3, 2); + + start = _map4[0]; + end = _map4[1]; + } + return new _selection.Range(start, end - start); +} + +exports.expandConfig = expandConfig; +exports.overload = overload; +exports.default = Quill; + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Inline = function (_Parchment$Inline) { + _inherits(Inline, _Parchment$Inline); + + function Inline() { + _classCallCheck(this, Inline); + + return _possibleConstructorReturn(this, (Inline.__proto__ || Object.getPrototypeOf(Inline)).apply(this, arguments)); + } + + _createClass(Inline, [{ + key: 'formatAt', + value: function formatAt(index, length, name, value) { + if (Inline.compare(this.statics.blotName, name) < 0 && _parchment2.default.query(name, _parchment2.default.Scope.BLOT)) { + var blot = this.isolate(index, length); + if (value) { + blot.wrap(name, value); + } + } else { + _get(Inline.prototype.__proto__ || Object.getPrototypeOf(Inline.prototype), 'formatAt', this).call(this, index, length, name, value); + } + } + }, { + key: 'optimize', + value: function optimize(context) { + _get(Inline.prototype.__proto__ || Object.getPrototypeOf(Inline.prototype), 'optimize', this).call(this, context); + if (this.parent instanceof Inline && Inline.compare(this.statics.blotName, this.parent.statics.blotName) > 0) { + var parent = this.parent.isolate(this.offset(), this.length()); + this.moveChildren(parent); + parent.wrap(this); + } + } + }], [{ + key: 'compare', + value: function compare(self, other) { + var selfIndex = Inline.order.indexOf(self); + var otherIndex = Inline.order.indexOf(other); + if (selfIndex >= 0 || otherIndex >= 0) { + return selfIndex - otherIndex; + } else if (self === other) { + return 0; + } else if (self < other) { + return -1; + } else { + return 1; + } + } + }]); + + return Inline; +}(_parchment2.default.Inline); + +Inline.allowedChildren = [Inline, _parchment2.default.Embed, _text2.default]; +// Lower index means deeper in the DOM tree, since not found (-1) is for embeds +Inline.order = ['cursor', 'inline', // Must be lower +'underline', 'strike', 'italic', 'bold', 'script', 'link', 'code' // Must be higher +]; + +exports.default = Inline; + +/***/ }), +/* 7 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var TextBlot = function (_Parchment$Text) { + _inherits(TextBlot, _Parchment$Text); + + function TextBlot() { + _classCallCheck(this, TextBlot); + + return _possibleConstructorReturn(this, (TextBlot.__proto__ || Object.getPrototypeOf(TextBlot)).apply(this, arguments)); + } + + return TextBlot; +}(_parchment2.default.Text); + +exports.default = TextBlot; + +/***/ }), +/* 8 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _eventemitter = __webpack_require__(54); + +var _eventemitter2 = _interopRequireDefault(_eventemitter); + +var _logger = __webpack_require__(10); + +var _logger2 = _interopRequireDefault(_logger); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var debug = (0, _logger2.default)('quill:events'); + +var EVENTS = ['selectionchange', 'mousedown', 'mouseup', 'click']; + +EVENTS.forEach(function (eventName) { + document.addEventListener(eventName, function () { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + [].slice.call(document.querySelectorAll('.ql-container')).forEach(function (node) { + // TODO use WeakMap + if (node.__quill && node.__quill.emitter) { + var _node$__quill$emitter; + + (_node$__quill$emitter = node.__quill.emitter).handleDOM.apply(_node$__quill$emitter, args); + } + }); + }); +}); + +var Emitter = function (_EventEmitter) { + _inherits(Emitter, _EventEmitter); + + function Emitter() { + _classCallCheck(this, Emitter); + + var _this = _possibleConstructorReturn(this, (Emitter.__proto__ || Object.getPrototypeOf(Emitter)).call(this)); + + _this.listeners = {}; + _this.on('error', debug.error); + return _this; + } + + _createClass(Emitter, [{ + key: 'emit', + value: function emit() { + debug.log.apply(debug, arguments); + _get(Emitter.prototype.__proto__ || Object.getPrototypeOf(Emitter.prototype), 'emit', this).apply(this, arguments); + } + }, { + key: 'handleDOM', + value: function handleDOM(event) { + for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + args[_key2 - 1] = arguments[_key2]; + } + + (this.listeners[event.type] || []).forEach(function (_ref) { + var node = _ref.node, + handler = _ref.handler; + + if (event.target === node || node.contains(event.target)) { + handler.apply(undefined, [event].concat(args)); + } + }); + } + }, { + key: 'listenDOM', + value: function listenDOM(eventName, node, handler) { + if (!this.listeners[eventName]) { + this.listeners[eventName] = []; + } + this.listeners[eventName].push({ node: node, handler: handler }); + } + }]); + + return Emitter; +}(_eventemitter2.default); + +Emitter.events = { + EDITOR_CHANGE: 'editor-change', + SCROLL_BEFORE_UPDATE: 'scroll-before-update', + SCROLL_OPTIMIZE: 'scroll-optimize', + SCROLL_UPDATE: 'scroll-update', + SELECTION_CHANGE: 'selection-change', + TEXT_CHANGE: 'text-change' +}; +Emitter.sources = { + API: 'api', + SILENT: 'silent', + USER: 'user' +}; + +exports.default = Emitter; + +/***/ }), +/* 9 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Module = function Module(quill) { + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + _classCallCheck(this, Module); + + this.quill = quill; + this.options = options; +}; + +Module.DEFAULTS = {}; + +exports.default = Module; + +/***/ }), +/* 10 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +var levels = ['error', 'warn', 'log', 'info']; +var level = 'warn'; + +function debug(method) { + if (levels.indexOf(method) <= levels.indexOf(level)) { + var _console; + + for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + (_console = console)[method].apply(_console, args); // eslint-disable-line no-console + } +} + +function namespace(ns) { + return levels.reduce(function (logger, method) { + logger[method] = debug.bind(console, method, ns); + return logger; + }, {}); +} + +debug.level = namespace.level = function (newLevel) { + level = newLevel; +}; + +exports.default = namespace; + +/***/ }), +/* 11 */ +/***/ (function(module, exports, __webpack_require__) { + +var pSlice = Array.prototype.slice; +var objectKeys = __webpack_require__(52); +var isArguments = __webpack_require__(53); + +var deepEqual = module.exports = function (actual, expected, opts) { + if (!opts) opts = {}; + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + + } else if (actual instanceof Date && expected instanceof Date) { + return actual.getTime() === expected.getTime(); + + // 7.3. Other pairs that do not both pass typeof value == 'object', + // equivalence is determined by ==. + } else if (!actual || !expected || typeof actual != 'object' && typeof expected != 'object') { + return opts.strict ? actual === expected : actual == expected; + + // 7.4. For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected, opts); + } +} + +function isUndefinedOrNull(value) { + return value === null || value === undefined; +} + +function isBuffer (x) { + if (!x || typeof x !== 'object' || typeof x.length !== 'number') return false; + if (typeof x.copy !== 'function' || typeof x.slice !== 'function') { + return false; + } + if (x.length > 0 && typeof x[0] !== 'number') return false; + return true; +} + +function objEquiv(a, b, opts) { + var i, key; + if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) + return false; + // an identical 'prototype' property. + if (a.prototype !== b.prototype) return false; + //~~~I've managed to break Object.keys through screwy arguments passing. + // Converting to array solves the problem. + if (isArguments(a)) { + if (!isArguments(b)) { + return false; + } + a = pSlice.call(a); + b = pSlice.call(b); + return deepEqual(a, b, opts); + } + if (isBuffer(a)) { + if (!isBuffer(b)) { + return false; + } + if (a.length !== b.length) return false; + for (i = 0; i < a.length; i++) { + if (a[i] !== b[i]) return false; + } + return true; + } + try { + var ka = objectKeys(a), + kb = objectKeys(b); + } catch (e) {//happens when one is a string literal and the other isn't + return false; + } + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!deepEqual(a[key], b[key], opts)) return false; + } + return typeof a === typeof b; +} + + +/***/ }), +/* 12 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var Registry = __webpack_require__(1); +var Attributor = /** @class */ (function () { + function Attributor(attrName, keyName, options) { + if (options === void 0) { options = {}; } + this.attrName = attrName; + this.keyName = keyName; + var attributeBit = Registry.Scope.TYPE & Registry.Scope.ATTRIBUTE; + if (options.scope != null) { + // Ignore type bits, force attribute bit + this.scope = (options.scope & Registry.Scope.LEVEL) | attributeBit; + } + else { + this.scope = Registry.Scope.ATTRIBUTE; + } + if (options.whitelist != null) + this.whitelist = options.whitelist; + } + Attributor.keys = function (node) { + return [].map.call(node.attributes, function (item) { + return item.name; + }); + }; + Attributor.prototype.add = function (node, value) { + if (!this.canAdd(node, value)) + return false; + node.setAttribute(this.keyName, value); + return true; + }; + Attributor.prototype.canAdd = function (node, value) { + var match = Registry.query(node, Registry.Scope.BLOT & (this.scope | Registry.Scope.TYPE)); + if (match == null) + return false; + if (this.whitelist == null) + return true; + if (typeof value === 'string') { + return this.whitelist.indexOf(value.replace(/["']/g, '')) > -1; + } + else { + return this.whitelist.indexOf(value) > -1; + } + }; + Attributor.prototype.remove = function (node) { + node.removeAttribute(this.keyName); + }; + Attributor.prototype.value = function (node) { + var value = node.getAttribute(this.keyName); + if (this.canAdd(node, value) && value) { + return value; + } + return ''; + }; + return Attributor; +}()); +exports.default = Attributor; + + +/***/ }), +/* 13 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.Code = undefined; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Code = function (_Inline) { + _inherits(Code, _Inline); + + function Code() { + _classCallCheck(this, Code); + + return _possibleConstructorReturn(this, (Code.__proto__ || Object.getPrototypeOf(Code)).apply(this, arguments)); + } + + return Code; +}(_inline2.default); + +Code.blotName = 'code'; +Code.tagName = 'CODE'; + +var CodeBlock = function (_Block) { + _inherits(CodeBlock, _Block); + + function CodeBlock() { + _classCallCheck(this, CodeBlock); + + return _possibleConstructorReturn(this, (CodeBlock.__proto__ || Object.getPrototypeOf(CodeBlock)).apply(this, arguments)); + } + + _createClass(CodeBlock, [{ + key: 'delta', + value: function delta() { + var _this3 = this; + + var text = this.domNode.textContent; + if (text.endsWith('\n')) { + // Should always be true + text = text.slice(0, -1); + } + return text.split('\n').reduce(function (delta, frag) { + return delta.insert(frag).insert('\n', _this3.formats()); + }, new _quillDelta2.default()); + } + }, { + key: 'format', + value: function format(name, value) { + if (name === this.statics.blotName && value) return; + + var _descendant = this.descendant(_text2.default, this.length() - 1), + _descendant2 = _slicedToArray(_descendant, 1), + text = _descendant2[0]; + + if (text != null) { + text.deleteAt(text.length() - 1, 1); + } + _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'format', this).call(this, name, value); + } + }, { + key: 'formatAt', + value: function formatAt(index, length, name, value) { + if (length === 0) return; + if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK) == null || name === this.statics.blotName && value === this.statics.formats(this.domNode)) { + return; + } + var nextNewline = this.newlineIndex(index); + if (nextNewline < 0 || nextNewline >= index + length) return; + var prevNewline = this.newlineIndex(index, true) + 1; + var isolateLength = nextNewline - prevNewline + 1; + var blot = this.isolate(prevNewline, isolateLength); + var next = blot.next; + blot.format(name, value); + if (next instanceof CodeBlock) { + next.formatAt(0, index - prevNewline + length - isolateLength, name, value); + } + } + }, { + key: 'insertAt', + value: function insertAt(index, value, def) { + if (def != null) return; + + var _descendant3 = this.descendant(_text2.default, index), + _descendant4 = _slicedToArray(_descendant3, 2), + text = _descendant4[0], + offset = _descendant4[1]; + + text.insertAt(offset, value); + } + }, { + key: 'length', + value: function length() { + var length = this.domNode.textContent.length; + if (!this.domNode.textContent.endsWith('\n')) { + return length + 1; + } + return length; + } + }, { + key: 'newlineIndex', + value: function newlineIndex(searchIndex) { + var reverse = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + if (!reverse) { + var offset = this.domNode.textContent.slice(searchIndex).indexOf('\n'); + return offset > -1 ? searchIndex + offset : -1; + } else { + return this.domNode.textContent.slice(0, searchIndex).lastIndexOf('\n'); + } + } + }, { + key: 'optimize', + value: function optimize(context) { + if (!this.domNode.textContent.endsWith('\n')) { + this.appendChild(_parchment2.default.create('text', '\n')); + } + _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'optimize', this).call(this, context); + var next = this.next; + if (next != null && next.prev === this && next.statics.blotName === this.statics.blotName && this.statics.formats(this.domNode) === next.statics.formats(next.domNode)) { + next.optimize(context); + next.moveChildren(this); + next.remove(); + } + } + }, { + key: 'replace', + value: function replace(target) { + _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'replace', this).call(this, target); + [].slice.call(this.domNode.querySelectorAll('*')).forEach(function (node) { + var blot = _parchment2.default.find(node); + if (blot == null) { + node.parentNode.removeChild(node); + } else if (blot instanceof _parchment2.default.Embed) { + blot.remove(); + } else { + blot.unwrap(); + } + }); + } + }], [{ + key: 'create', + value: function create(value) { + var domNode = _get(CodeBlock.__proto__ || Object.getPrototypeOf(CodeBlock), 'create', this).call(this, value); + domNode.setAttribute('spellcheck', false); + return domNode; + } + }, { + key: 'formats', + value: function formats() { + return true; + } + }]); + + return CodeBlock; +}(_block2.default); + +CodeBlock.blotName = 'code-block'; +CodeBlock.tagName = 'PRE'; +CodeBlock.TAB = ' '; + +exports.Code = Code; +exports.default = CodeBlock; + +/***/ }), +/* 14 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _op = __webpack_require__(20); + +var _op2 = _interopRequireDefault(_op); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _code = __webpack_require__(13); + +var _code2 = _interopRequireDefault(_code); + +var _cursor = __webpack_require__(24); + +var _cursor2 = _interopRequireDefault(_cursor); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +var _break = __webpack_require__(16); + +var _break2 = _interopRequireDefault(_break); + +var _clone = __webpack_require__(21); + +var _clone2 = _interopRequireDefault(_clone); + +var _deepEqual = __webpack_require__(11); + +var _deepEqual2 = _interopRequireDefault(_deepEqual); + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var ASCII = /^[ -~]*$/; + +var Editor = function () { + function Editor(scroll) { + _classCallCheck(this, Editor); + + this.scroll = scroll; + this.delta = this.getDelta(); + } + + _createClass(Editor, [{ + key: 'applyDelta', + value: function applyDelta(delta) { + var _this = this; + + var consumeNextNewline = false; + this.scroll.update(); + var scrollLength = this.scroll.length(); + this.scroll.batchStart(); + delta = normalizeDelta(delta); + delta.reduce(function (index, op) { + var length = op.retain || op.delete || op.insert.length || 1; + var attributes = op.attributes || {}; + if (op.insert != null) { + if (typeof op.insert === 'string') { + var text = op.insert; + if (text.endsWith('\n') && consumeNextNewline) { + consumeNextNewline = false; + text = text.slice(0, -1); + } + if (index >= scrollLength && !text.endsWith('\n')) { + consumeNextNewline = true; + } + _this.scroll.insertAt(index, text); + + var _scroll$line = _this.scroll.line(index), + _scroll$line2 = _slicedToArray(_scroll$line, 2), + line = _scroll$line2[0], + offset = _scroll$line2[1]; + + var formats = (0, _extend2.default)({}, (0, _block.bubbleFormats)(line)); + if (line instanceof _block2.default) { + var _line$descendant = line.descendant(_parchment2.default.Leaf, offset), + _line$descendant2 = _slicedToArray(_line$descendant, 1), + leaf = _line$descendant2[0]; + + formats = (0, _extend2.default)(formats, (0, _block.bubbleFormats)(leaf)); + } + attributes = _op2.default.attributes.diff(formats, attributes) || {}; + } else if (_typeof(op.insert) === 'object') { + var key = Object.keys(op.insert)[0]; // There should only be one key + if (key == null) return index; + _this.scroll.insertAt(index, key, op.insert[key]); + } + scrollLength += length; + } + Object.keys(attributes).forEach(function (name) { + _this.scroll.formatAt(index, length, name, attributes[name]); + }); + return index + length; + }, 0); + delta.reduce(function (index, op) { + if (typeof op.delete === 'number') { + _this.scroll.deleteAt(index, op.delete); + return index; + } + return index + (op.retain || op.insert.length || 1); + }, 0); + this.scroll.batchEnd(); + return this.update(delta); + } + }, { + key: 'deleteText', + value: function deleteText(index, length) { + this.scroll.deleteAt(index, length); + return this.update(new _quillDelta2.default().retain(index).delete(length)); + } + }, { + key: 'formatLine', + value: function formatLine(index, length) { + var _this2 = this; + + var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + this.scroll.update(); + Object.keys(formats).forEach(function (format) { + if (_this2.scroll.whitelist != null && !_this2.scroll.whitelist[format]) return; + var lines = _this2.scroll.lines(index, Math.max(length, 1)); + var lengthRemaining = length; + lines.forEach(function (line) { + var lineLength = line.length(); + if (!(line instanceof _code2.default)) { + line.format(format, formats[format]); + } else { + var codeIndex = index - line.offset(_this2.scroll); + var codeLength = line.newlineIndex(codeIndex + lengthRemaining) - codeIndex + 1; + line.formatAt(codeIndex, codeLength, format, formats[format]); + } + lengthRemaining -= lineLength; + }); + }); + this.scroll.optimize(); + return this.update(new _quillDelta2.default().retain(index).retain(length, (0, _clone2.default)(formats))); + } + }, { + key: 'formatText', + value: function formatText(index, length) { + var _this3 = this; + + var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + Object.keys(formats).forEach(function (format) { + _this3.scroll.formatAt(index, length, format, formats[format]); + }); + return this.update(new _quillDelta2.default().retain(index).retain(length, (0, _clone2.default)(formats))); + } + }, { + key: 'getContents', + value: function getContents(index, length) { + return this.delta.slice(index, index + length); + } + }, { + key: 'getDelta', + value: function getDelta() { + return this.scroll.lines().reduce(function (delta, line) { + return delta.concat(line.delta()); + }, new _quillDelta2.default()); + } + }, { + key: 'getFormat', + value: function getFormat(index) { + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + + var lines = [], + leaves = []; + if (length === 0) { + this.scroll.path(index).forEach(function (path) { + var _path = _slicedToArray(path, 1), + blot = _path[0]; + + if (blot instanceof _block2.default) { + lines.push(blot); + } else if (blot instanceof _parchment2.default.Leaf) { + leaves.push(blot); + } + }); + } else { + lines = this.scroll.lines(index, length); + leaves = this.scroll.descendants(_parchment2.default.Leaf, index, length); + } + var formatsArr = [lines, leaves].map(function (blots) { + if (blots.length === 0) return {}; + var formats = (0, _block.bubbleFormats)(blots.shift()); + while (Object.keys(formats).length > 0) { + var blot = blots.shift(); + if (blot == null) return formats; + formats = combineFormats((0, _block.bubbleFormats)(blot), formats); + } + return formats; + }); + return _extend2.default.apply(_extend2.default, formatsArr); + } + }, { + key: 'getText', + value: function getText(index, length) { + return this.getContents(index, length).filter(function (op) { + return typeof op.insert === 'string'; + }).map(function (op) { + return op.insert; + }).join(''); + } + }, { + key: 'insertEmbed', + value: function insertEmbed(index, embed, value) { + this.scroll.insertAt(index, embed, value); + return this.update(new _quillDelta2.default().retain(index).insert(_defineProperty({}, embed, value))); + } + }, { + key: 'insertText', + value: function insertText(index, text) { + var _this4 = this; + + var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + text = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + this.scroll.insertAt(index, text); + Object.keys(formats).forEach(function (format) { + _this4.scroll.formatAt(index, text.length, format, formats[format]); + }); + return this.update(new _quillDelta2.default().retain(index).insert(text, (0, _clone2.default)(formats))); + } + }, { + key: 'isBlank', + value: function isBlank() { + if (this.scroll.children.length == 0) return true; + if (this.scroll.children.length > 1) return false; + var block = this.scroll.children.head; + if (block.statics.blotName !== _block2.default.blotName) return false; + if (block.children.length > 1) return false; + return block.children.head instanceof _break2.default; + } + }, { + key: 'removeFormat', + value: function removeFormat(index, length) { + var text = this.getText(index, length); + + var _scroll$line3 = this.scroll.line(index + length), + _scroll$line4 = _slicedToArray(_scroll$line3, 2), + line = _scroll$line4[0], + offset = _scroll$line4[1]; + + var suffixLength = 0, + suffix = new _quillDelta2.default(); + if (line != null) { + if (!(line instanceof _code2.default)) { + suffixLength = line.length() - offset; + } else { + suffixLength = line.newlineIndex(offset) - offset + 1; + } + suffix = line.delta().slice(offset, offset + suffixLength - 1).insert('\n'); + } + var contents = this.getContents(index, length + suffixLength); + var diff = contents.diff(new _quillDelta2.default().insert(text).concat(suffix)); + var delta = new _quillDelta2.default().retain(index).concat(diff); + return this.applyDelta(delta); + } + }, { + key: 'update', + value: function update(change) { + var mutations = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; + var cursorIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined; + + var oldDelta = this.delta; + if (mutations.length === 1 && mutations[0].type === 'characterData' && mutations[0].target.data.match(ASCII) && _parchment2.default.find(mutations[0].target)) { + // Optimization for character changes + var textBlot = _parchment2.default.find(mutations[0].target); + var formats = (0, _block.bubbleFormats)(textBlot); + var index = textBlot.offset(this.scroll); + var oldValue = mutations[0].oldValue.replace(_cursor2.default.CONTENTS, ''); + var oldText = new _quillDelta2.default().insert(oldValue); + var newText = new _quillDelta2.default().insert(textBlot.value()); + var diffDelta = new _quillDelta2.default().retain(index).concat(oldText.diff(newText, cursorIndex)); + change = diffDelta.reduce(function (delta, op) { + if (op.insert) { + return delta.insert(op.insert, formats); + } else { + return delta.push(op); + } + }, new _quillDelta2.default()); + this.delta = oldDelta.compose(change); + } else { + this.delta = this.getDelta(); + if (!change || !(0, _deepEqual2.default)(oldDelta.compose(change), this.delta)) { + change = oldDelta.diff(this.delta, cursorIndex); + } + } + return change; + } + }]); + + return Editor; +}(); + +function combineFormats(formats, combined) { + return Object.keys(combined).reduce(function (merged, name) { + if (formats[name] == null) return merged; + if (combined[name] === formats[name]) { + merged[name] = combined[name]; + } else if (Array.isArray(combined[name])) { + if (combined[name].indexOf(formats[name]) < 0) { + merged[name] = combined[name].concat([formats[name]]); + } + } else { + merged[name] = [combined[name], formats[name]]; + } + return merged; + }, {}); +} + +function normalizeDelta(delta) { + return delta.reduce(function (delta, op) { + if (op.insert === 1) { + var attributes = (0, _clone2.default)(op.attributes); + delete attributes['image']; + return delta.insert({ image: op.attributes.image }, attributes); + } + if (op.attributes != null && (op.attributes.list === true || op.attributes.bullet === true)) { + op = (0, _clone2.default)(op); + if (op.attributes.list) { + op.attributes.list = 'ordered'; + } else { + op.attributes.list = 'bullet'; + delete op.attributes.bullet; + } + } + if (typeof op.insert === 'string') { + var text = op.insert.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + return delta.insert(text, op.attributes); + } + return delta.push(op); + }, new _quillDelta2.default()); +} + +exports.default = Editor; + +/***/ }), +/* 15 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.Range = undefined; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _clone = __webpack_require__(21); + +var _clone2 = _interopRequireDefault(_clone); + +var _deepEqual = __webpack_require__(11); + +var _deepEqual2 = _interopRequireDefault(_deepEqual); + +var _emitter3 = __webpack_require__(8); + +var _emitter4 = _interopRequireDefault(_emitter3); + +var _logger = __webpack_require__(10); + +var _logger2 = _interopRequireDefault(_logger); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var debug = (0, _logger2.default)('quill:selection'); + +var Range = function Range(index) { + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + + _classCallCheck(this, Range); + + this.index = index; + this.length = length; +}; + +var Selection = function () { + function Selection(scroll, emitter) { + var _this = this; + + _classCallCheck(this, Selection); + + this.emitter = emitter; + this.scroll = scroll; + this.composing = false; + this.mouseDown = false; + this.root = this.scroll.domNode; + this.cursor = _parchment2.default.create('cursor', this); + // savedRange is last non-null range + this.lastRange = this.savedRange = new Range(0, 0); + this.handleComposition(); + this.handleDragging(); + this.emitter.listenDOM('selectionchange', document, function () { + if (!_this.mouseDown) { + setTimeout(_this.update.bind(_this, _emitter4.default.sources.USER), 1); + } + }); + this.emitter.on(_emitter4.default.events.EDITOR_CHANGE, function (type, delta) { + if (type === _emitter4.default.events.TEXT_CHANGE && delta.length() > 0) { + _this.update(_emitter4.default.sources.SILENT); + } + }); + this.emitter.on(_emitter4.default.events.SCROLL_BEFORE_UPDATE, function () { + if (!_this.hasFocus()) return; + var native = _this.getNativeRange(); + if (native == null) return; + if (native.start.node === _this.cursor.textNode) return; // cursor.restore() will handle + // TODO unclear if this has negative side effects + _this.emitter.once(_emitter4.default.events.SCROLL_UPDATE, function () { + try { + _this.setNativeRange(native.start.node, native.start.offset, native.end.node, native.end.offset); + } catch (ignored) {} + }); + }); + this.emitter.on(_emitter4.default.events.SCROLL_OPTIMIZE, function (mutations, context) { + if (context.range) { + var _context$range = context.range, + startNode = _context$range.startNode, + startOffset = _context$range.startOffset, + endNode = _context$range.endNode, + endOffset = _context$range.endOffset; + + _this.setNativeRange(startNode, startOffset, endNode, endOffset); + } + }); + this.update(_emitter4.default.sources.SILENT); + } + + _createClass(Selection, [{ + key: 'handleComposition', + value: function handleComposition() { + var _this2 = this; + + this.root.addEventListener('compositionstart', function () { + _this2.composing = true; + }); + this.root.addEventListener('compositionend', function () { + _this2.composing = false; + if (_this2.cursor.parent) { + var range = _this2.cursor.restore(); + if (!range) return; + setTimeout(function () { + _this2.setNativeRange(range.startNode, range.startOffset, range.endNode, range.endOffset); + }, 1); + } + }); + } + }, { + key: 'handleDragging', + value: function handleDragging() { + var _this3 = this; + + this.emitter.listenDOM('mousedown', document.body, function () { + _this3.mouseDown = true; + }); + this.emitter.listenDOM('mouseup', document.body, function () { + _this3.mouseDown = false; + _this3.update(_emitter4.default.sources.USER); + }); + } + }, { + key: 'focus', + value: function focus() { + if (this.hasFocus()) return; + this.root.focus(); + this.setRange(this.savedRange); + } + }, { + key: 'format', + value: function format(_format, value) { + if (this.scroll.whitelist != null && !this.scroll.whitelist[_format]) return; + this.scroll.update(); + var nativeRange = this.getNativeRange(); + if (nativeRange == null || !nativeRange.native.collapsed || _parchment2.default.query(_format, _parchment2.default.Scope.BLOCK)) return; + if (nativeRange.start.node !== this.cursor.textNode) { + var blot = _parchment2.default.find(nativeRange.start.node, false); + if (blot == null) return; + // TODO Give blot ability to not split + if (blot instanceof _parchment2.default.Leaf) { + var after = blot.split(nativeRange.start.offset); + blot.parent.insertBefore(this.cursor, after); + } else { + blot.insertBefore(this.cursor, nativeRange.start.node); // Should never happen + } + this.cursor.attach(); + } + this.cursor.format(_format, value); + this.scroll.optimize(); + this.setNativeRange(this.cursor.textNode, this.cursor.textNode.data.length); + this.update(); + } + }, { + key: 'getBounds', + value: function getBounds(index) { + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + + var scrollLength = this.scroll.length(); + index = Math.min(index, scrollLength - 1); + length = Math.min(index + length, scrollLength - 1) - index; + var node = void 0, + _scroll$leaf = this.scroll.leaf(index), + _scroll$leaf2 = _slicedToArray(_scroll$leaf, 2), + leaf = _scroll$leaf2[0], + offset = _scroll$leaf2[1]; + if (leaf == null) return null; + + var _leaf$position = leaf.position(offset, true); + + var _leaf$position2 = _slicedToArray(_leaf$position, 2); + + node = _leaf$position2[0]; + offset = _leaf$position2[1]; + + var range = document.createRange(); + if (length > 0) { + range.setStart(node, offset); + + var _scroll$leaf3 = this.scroll.leaf(index + length); + + var _scroll$leaf4 = _slicedToArray(_scroll$leaf3, 2); + + leaf = _scroll$leaf4[0]; + offset = _scroll$leaf4[1]; + + if (leaf == null) return null; + + var _leaf$position3 = leaf.position(offset, true); + + var _leaf$position4 = _slicedToArray(_leaf$position3, 2); + + node = _leaf$position4[0]; + offset = _leaf$position4[1]; + + range.setEnd(node, offset); + return range.getBoundingClientRect(); + } else { + var side = 'left'; + var rect = void 0; + if (node instanceof Text) { + if (offset < node.data.length) { + range.setStart(node, offset); + range.setEnd(node, offset + 1); + } else { + range.setStart(node, offset - 1); + range.setEnd(node, offset); + side = 'right'; + } + rect = range.getBoundingClientRect(); + } else { + rect = leaf.domNode.getBoundingClientRect(); + if (offset > 0) side = 'right'; + } + return { + bottom: rect.top + rect.height, + height: rect.height, + left: rect[side], + right: rect[side], + top: rect.top, + width: 0 + }; + } + } + }, { + key: 'getNativeRange', + value: function getNativeRange() { + var selection = document.getSelection(); + if (selection == null || selection.rangeCount <= 0) return null; + var nativeRange = selection.getRangeAt(0); + if (nativeRange == null) return null; + var range = this.normalizeNative(nativeRange); + debug.info('getNativeRange', range); + return range; + } + }, { + key: 'getRange', + value: function getRange() { + var normalized = this.getNativeRange(); + if (normalized == null) return [null, null]; + var range = this.normalizedToRange(normalized); + return [range, normalized]; + } + }, { + key: 'hasFocus', + value: function hasFocus() { + return document.activeElement === this.root; + } + }, { + key: 'normalizedToRange', + value: function normalizedToRange(range) { + var _this4 = this; + + var positions = [[range.start.node, range.start.offset]]; + if (!range.native.collapsed) { + positions.push([range.end.node, range.end.offset]); + } + var indexes = positions.map(function (position) { + var _position = _slicedToArray(position, 2), + node = _position[0], + offset = _position[1]; + + var blot = _parchment2.default.find(node, true); + var index = blot.offset(_this4.scroll); + if (offset === 0) { + return index; + } else if (blot instanceof _parchment2.default.Container) { + return index + blot.length(); + } else { + return index + blot.index(node, offset); + } + }); + var end = Math.min(Math.max.apply(Math, _toConsumableArray(indexes)), this.scroll.length() - 1); + var start = Math.min.apply(Math, [end].concat(_toConsumableArray(indexes))); + return new Range(start, end - start); + } + }, { + key: 'normalizeNative', + value: function normalizeNative(nativeRange) { + if (!contains(this.root, nativeRange.startContainer) || !nativeRange.collapsed && !contains(this.root, nativeRange.endContainer)) { + return null; + } + var range = { + start: { node: nativeRange.startContainer, offset: nativeRange.startOffset }, + end: { node: nativeRange.endContainer, offset: nativeRange.endOffset }, + native: nativeRange + }; + [range.start, range.end].forEach(function (position) { + var node = position.node, + offset = position.offset; + while (!(node instanceof Text) && node.childNodes.length > 0) { + if (node.childNodes.length > offset) { + node = node.childNodes[offset]; + offset = 0; + } else if (node.childNodes.length === offset) { + node = node.lastChild; + offset = node instanceof Text ? node.data.length : node.childNodes.length + 1; + } else { + break; + } + } + position.node = node, position.offset = offset; + }); + return range; + } + }, { + key: 'rangeToNative', + value: function rangeToNative(range) { + var _this5 = this; + + var indexes = range.collapsed ? [range.index] : [range.index, range.index + range.length]; + var args = []; + var scrollLength = this.scroll.length(); + indexes.forEach(function (index, i) { + index = Math.min(scrollLength - 1, index); + var node = void 0, + _scroll$leaf5 = _this5.scroll.leaf(index), + _scroll$leaf6 = _slicedToArray(_scroll$leaf5, 2), + leaf = _scroll$leaf6[0], + offset = _scroll$leaf6[1]; + var _leaf$position5 = leaf.position(offset, i !== 0); + + var _leaf$position6 = _slicedToArray(_leaf$position5, 2); + + node = _leaf$position6[0]; + offset = _leaf$position6[1]; + + args.push(node, offset); + }); + if (args.length < 2) { + args = args.concat(args); + } + return args; + } + }, { + key: 'scrollIntoView', + value: function scrollIntoView(scrollingContainer) { + var range = this.lastRange; + if (range == null) return; + var bounds = this.getBounds(range.index, range.length); + if (bounds == null) return; + var limit = this.scroll.length() - 1; + + var _scroll$line = this.scroll.line(Math.min(range.index, limit)), + _scroll$line2 = _slicedToArray(_scroll$line, 1), + first = _scroll$line2[0]; + + var last = first; + if (range.length > 0) { + var _scroll$line3 = this.scroll.line(Math.min(range.index + range.length, limit)); + + var _scroll$line4 = _slicedToArray(_scroll$line3, 1); + + last = _scroll$line4[0]; + } + if (first == null || last == null) return; + var scrollBounds = scrollingContainer.getBoundingClientRect(); + if (bounds.top < scrollBounds.top) { + scrollingContainer.scrollTop -= scrollBounds.top - bounds.top; + } else if (bounds.bottom > scrollBounds.bottom) { + scrollingContainer.scrollTop += bounds.bottom - scrollBounds.bottom; + } + } + }, { + key: 'setNativeRange', + value: function setNativeRange(startNode, startOffset) { + var endNode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : startNode; + var endOffset = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : startOffset; + var force = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; + + debug.info('setNativeRange', startNode, startOffset, endNode, endOffset); + if (startNode != null && (this.root.parentNode == null || startNode.parentNode == null || endNode.parentNode == null)) { + return; + } + var selection = document.getSelection(); + if (selection == null) return; + if (startNode != null) { + if (!this.hasFocus()) this.root.focus(); + var native = (this.getNativeRange() || {}).native; + if (native == null || force || startNode !== native.startContainer || startOffset !== native.startOffset || endNode !== native.endContainer || endOffset !== native.endOffset) { + + if (startNode.tagName == "BR") { + startOffset = [].indexOf.call(startNode.parentNode.childNodes, startNode); + startNode = startNode.parentNode; + } + if (endNode.tagName == "BR") { + endOffset = [].indexOf.call(endNode.parentNode.childNodes, endNode); + endNode = endNode.parentNode; + } + var range = document.createRange(); + range.setStart(startNode, startOffset); + range.setEnd(endNode, endOffset); + selection.removeAllRanges(); + selection.addRange(range); + } + } else { + selection.removeAllRanges(); + this.root.blur(); + document.body.focus(); // root.blur() not enough on IE11+Travis+SauceLabs (but not local VMs) + } + } + }, { + key: 'setRange', + value: function setRange(range) { + var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _emitter4.default.sources.API; + + if (typeof force === 'string') { + source = force; + force = false; + } + debug.info('setRange', range); + if (range != null) { + var args = this.rangeToNative(range); + this.setNativeRange.apply(this, _toConsumableArray(args).concat([force])); + } else { + this.setNativeRange(null); + } + this.update(source); + } + }, { + key: 'update', + value: function update() { + var source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _emitter4.default.sources.USER; + + var oldRange = this.lastRange; + + var _getRange = this.getRange(), + _getRange2 = _slicedToArray(_getRange, 2), + lastRange = _getRange2[0], + nativeRange = _getRange2[1]; + + this.lastRange = lastRange; + if (this.lastRange != null) { + this.savedRange = this.lastRange; + } + if (!(0, _deepEqual2.default)(oldRange, this.lastRange)) { + var _emitter; + + if (!this.composing && nativeRange != null && nativeRange.native.collapsed && nativeRange.start.node !== this.cursor.textNode) { + this.cursor.restore(); + } + var args = [_emitter4.default.events.SELECTION_CHANGE, (0, _clone2.default)(this.lastRange), (0, _clone2.default)(oldRange), source]; + (_emitter = this.emitter).emit.apply(_emitter, [_emitter4.default.events.EDITOR_CHANGE].concat(args)); + if (source !== _emitter4.default.sources.SILENT) { + var _emitter2; + + (_emitter2 = this.emitter).emit.apply(_emitter2, args); + } + } + } + }]); + + return Selection; +}(); + +function contains(parent, descendant) { + try { + // Firefox inserts inaccessible nodes around video elements + descendant.parentNode; + } catch (e) { + return false; + } + // IE11 has bug with Text nodes + // https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect + if (descendant instanceof Text) { + descendant = descendant.parentNode; + } + return parent.contains(descendant); +} + +exports.Range = Range; +exports.default = Selection; + +/***/ }), +/* 16 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Break = function (_Parchment$Embed) { + _inherits(Break, _Parchment$Embed); + + function Break() { + _classCallCheck(this, Break); + + return _possibleConstructorReturn(this, (Break.__proto__ || Object.getPrototypeOf(Break)).apply(this, arguments)); + } + + _createClass(Break, [{ + key: 'insertInto', + value: function insertInto(parent, ref) { + if (parent.children.length === 0) { + _get(Break.prototype.__proto__ || Object.getPrototypeOf(Break.prototype), 'insertInto', this).call(this, parent, ref); + } else { + this.remove(); + } + } + }, { + key: 'length', + value: function length() { + return 0; + } + }, { + key: 'value', + value: function value() { + return ''; + } + }], [{ + key: 'value', + value: function value() { + return undefined; + } + }]); + + return Break; +}(_parchment2.default.Embed); + +Break.blotName = 'break'; +Break.tagName = 'BR'; + +exports.default = Break; + +/***/ }), +/* 17 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var linked_list_1 = __webpack_require__(44); +var shadow_1 = __webpack_require__(30); +var Registry = __webpack_require__(1); +var ContainerBlot = /** @class */ (function (_super) { + __extends(ContainerBlot, _super); + function ContainerBlot(domNode) { + var _this = _super.call(this, domNode) || this; + _this.build(); + return _this; + } + ContainerBlot.prototype.appendChild = function (other) { + this.insertBefore(other); + }; + ContainerBlot.prototype.attach = function () { + _super.prototype.attach.call(this); + this.children.forEach(function (child) { + child.attach(); + }); + }; + ContainerBlot.prototype.build = function () { + var _this = this; + this.children = new linked_list_1.default(); + // Need to be reversed for if DOM nodes already in order + [].slice + .call(this.domNode.childNodes) + .reverse() + .forEach(function (node) { + try { + var child = makeBlot(node); + _this.insertBefore(child, _this.children.head || undefined); + } + catch (err) { + if (err instanceof Registry.ParchmentError) + return; + else + throw err; + } + }); + }; + ContainerBlot.prototype.deleteAt = function (index, length) { + if (index === 0 && length === this.length()) { + return this.remove(); + } + this.children.forEachAt(index, length, function (child, offset, length) { + child.deleteAt(offset, length); + }); + }; + ContainerBlot.prototype.descendant = function (criteria, index) { + var _a = this.children.find(index), child = _a[0], offset = _a[1]; + if ((criteria.blotName == null && criteria(child)) || + (criteria.blotName != null && child instanceof criteria)) { + return [child, offset]; + } + else if (child instanceof ContainerBlot) { + return child.descendant(criteria, offset); + } + else { + return [null, -1]; + } + }; + ContainerBlot.prototype.descendants = function (criteria, index, length) { + if (index === void 0) { index = 0; } + if (length === void 0) { length = Number.MAX_VALUE; } + var descendants = []; + var lengthLeft = length; + this.children.forEachAt(index, length, function (child, index, length) { + if ((criteria.blotName == null && criteria(child)) || + (criteria.blotName != null && child instanceof criteria)) { + descendants.push(child); + } + if (child instanceof ContainerBlot) { + descendants = descendants.concat(child.descendants(criteria, index, lengthLeft)); + } + lengthLeft -= length; + }); + return descendants; + }; + ContainerBlot.prototype.detach = function () { + this.children.forEach(function (child) { + child.detach(); + }); + _super.prototype.detach.call(this); + }; + ContainerBlot.prototype.formatAt = function (index, length, name, value) { + this.children.forEachAt(index, length, function (child, offset, length) { + child.formatAt(offset, length, name, value); + }); + }; + ContainerBlot.prototype.insertAt = function (index, value, def) { + var _a = this.children.find(index), child = _a[0], offset = _a[1]; + if (child) { + child.insertAt(offset, value, def); + } + else { + var blot = def == null ? Registry.create('text', value) : Registry.create(value, def); + this.appendChild(blot); + } + }; + ContainerBlot.prototype.insertBefore = function (childBlot, refBlot) { + if (this.statics.allowedChildren != null && + !this.statics.allowedChildren.some(function (child) { + return childBlot instanceof child; + })) { + throw new Registry.ParchmentError("Cannot insert " + childBlot.statics.blotName + " into " + this.statics.blotName); + } + childBlot.insertInto(this, refBlot); + }; + ContainerBlot.prototype.length = function () { + return this.children.reduce(function (memo, child) { + return memo + child.length(); + }, 0); + }; + ContainerBlot.prototype.moveChildren = function (targetParent, refNode) { + this.children.forEach(function (child) { + targetParent.insertBefore(child, refNode); + }); + }; + ContainerBlot.prototype.optimize = function (context) { + _super.prototype.optimize.call(this, context); + if (this.children.length === 0) { + if (this.statics.defaultChild != null) { + var child = Registry.create(this.statics.defaultChild); + this.appendChild(child); + child.optimize(context); + } + else { + this.remove(); + } + } + }; + ContainerBlot.prototype.path = function (index, inclusive) { + if (inclusive === void 0) { inclusive = false; } + var _a = this.children.find(index, inclusive), child = _a[0], offset = _a[1]; + var position = [[this, index]]; + if (child instanceof ContainerBlot) { + return position.concat(child.path(offset, inclusive)); + } + else if (child != null) { + position.push([child, offset]); + } + return position; + }; + ContainerBlot.prototype.removeChild = function (child) { + this.children.remove(child); + }; + ContainerBlot.prototype.replace = function (target) { + if (target instanceof ContainerBlot) { + target.moveChildren(this); + } + _super.prototype.replace.call(this, target); + }; + ContainerBlot.prototype.split = function (index, force) { + if (force === void 0) { force = false; } + if (!force) { + if (index === 0) + return this; + if (index === this.length()) + return this.next; + } + var after = this.clone(); + this.parent.insertBefore(after, this.next); + this.children.forEachAt(index, this.length(), function (child, offset, length) { + child = child.split(offset, force); + after.appendChild(child); + }); + return after; + }; + ContainerBlot.prototype.unwrap = function () { + this.moveChildren(this.parent, this.next); + this.remove(); + }; + ContainerBlot.prototype.update = function (mutations, context) { + var _this = this; + var addedNodes = []; + var removedNodes = []; + mutations.forEach(function (mutation) { + if (mutation.target === _this.domNode && mutation.type === 'childList') { + addedNodes.push.apply(addedNodes, mutation.addedNodes); + removedNodes.push.apply(removedNodes, mutation.removedNodes); + } + }); + removedNodes.forEach(function (node) { + // Check node has actually been removed + // One exception is Chrome does not immediately remove IFRAMEs + // from DOM but MutationRecord is correct in its reported removal + if (node.parentNode != null && + // @ts-ignore + node.tagName !== 'IFRAME' && + document.body.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINED_BY) { + return; + } + var blot = Registry.find(node); + if (blot == null) + return; + if (blot.domNode.parentNode == null || blot.domNode.parentNode === _this.domNode) { + blot.detach(); + } + }); + addedNodes + .filter(function (node) { + return node.parentNode == _this.domNode; + }) + .sort(function (a, b) { + if (a === b) + return 0; + if (a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING) { + return 1; + } + return -1; + }) + .forEach(function (node) { + var refBlot = null; + if (node.nextSibling != null) { + refBlot = Registry.find(node.nextSibling); + } + var blot = makeBlot(node); + if (blot.next != refBlot || blot.next == null) { + if (blot.parent != null) { + blot.parent.removeChild(_this); + } + _this.insertBefore(blot, refBlot || undefined); + } + }); + }; + return ContainerBlot; +}(shadow_1.default)); +function makeBlot(node) { + var blot = Registry.find(node); + if (blot == null) { + try { + blot = Registry.create(node); + } + catch (e) { + blot = Registry.create(Registry.Scope.INLINE); + [].slice.call(node.childNodes).forEach(function (child) { + // @ts-ignore + blot.domNode.appendChild(child); + }); + if (node.parentNode) { + node.parentNode.replaceChild(blot.domNode, node); + } + blot.attach(); + } + } + return blot; +} +exports.default = ContainerBlot; + + +/***/ }), +/* 18 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var attributor_1 = __webpack_require__(12); +var store_1 = __webpack_require__(31); +var container_1 = __webpack_require__(17); +var Registry = __webpack_require__(1); +var FormatBlot = /** @class */ (function (_super) { + __extends(FormatBlot, _super); + function FormatBlot(domNode) { + var _this = _super.call(this, domNode) || this; + _this.attributes = new store_1.default(_this.domNode); + return _this; + } + FormatBlot.formats = function (domNode) { + if (typeof this.tagName === 'string') { + return true; + } + else if (Array.isArray(this.tagName)) { + return domNode.tagName.toLowerCase(); + } + return undefined; + }; + FormatBlot.prototype.format = function (name, value) { + var format = Registry.query(name); + if (format instanceof attributor_1.default) { + this.attributes.attribute(format, value); + } + else if (value) { + if (format != null && (name !== this.statics.blotName || this.formats()[name] !== value)) { + this.replaceWith(name, value); + } + } + }; + FormatBlot.prototype.formats = function () { + var formats = this.attributes.values(); + var format = this.statics.formats(this.domNode); + if (format != null) { + formats[this.statics.blotName] = format; + } + return formats; + }; + FormatBlot.prototype.replaceWith = function (name, value) { + var replacement = _super.prototype.replaceWith.call(this, name, value); + this.attributes.copy(replacement); + return replacement; + }; + FormatBlot.prototype.update = function (mutations, context) { + var _this = this; + _super.prototype.update.call(this, mutations, context); + if (mutations.some(function (mutation) { + return mutation.target === _this.domNode && mutation.type === 'attributes'; + })) { + this.attributes.build(); + } + }; + FormatBlot.prototype.wrap = function (name, value) { + var wrapper = _super.prototype.wrap.call(this, name, value); + if (wrapper instanceof FormatBlot && wrapper.statics.scope === this.statics.scope) { + this.attributes.move(wrapper); + } + return wrapper; + }; + return FormatBlot; +}(container_1.default)); +exports.default = FormatBlot; + + +/***/ }), +/* 19 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var shadow_1 = __webpack_require__(30); +var Registry = __webpack_require__(1); +var LeafBlot = /** @class */ (function (_super) { + __extends(LeafBlot, _super); + function LeafBlot() { + return _super !== null && _super.apply(this, arguments) || this; + } + LeafBlot.value = function (domNode) { + return true; + }; + LeafBlot.prototype.index = function (node, offset) { + if (this.domNode === node || + this.domNode.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINED_BY) { + return Math.min(offset, 1); + } + return -1; + }; + LeafBlot.prototype.position = function (index, inclusive) { + var offset = [].indexOf.call(this.parent.domNode.childNodes, this.domNode); + if (index > 0) + offset += 1; + return [this.parent.domNode, offset]; + }; + LeafBlot.prototype.value = function () { + return _a = {}, _a[this.statics.blotName] = this.statics.value(this.domNode) || true, _a; + var _a; + }; + LeafBlot.scope = Registry.Scope.INLINE_BLOT; + return LeafBlot; +}(shadow_1.default)); +exports.default = LeafBlot; + + +/***/ }), +/* 20 */ +/***/ (function(module, exports, __webpack_require__) { + +var equal = __webpack_require__(11); +var extend = __webpack_require__(3); + + +var lib = { + attributes: { + compose: function (a, b, keepNull) { + if (typeof a !== 'object') a = {}; + if (typeof b !== 'object') b = {}; + var attributes = extend(true, {}, b); + if (!keepNull) { + attributes = Object.keys(attributes).reduce(function (copy, key) { + if (attributes[key] != null) { + copy[key] = attributes[key]; + } + return copy; + }, {}); + } + for (var key in a) { + if (a[key] !== undefined && b[key] === undefined) { + attributes[key] = a[key]; + } + } + return Object.keys(attributes).length > 0 ? attributes : undefined; + }, + + diff: function(a, b) { + if (typeof a !== 'object') a = {}; + if (typeof b !== 'object') b = {}; + var attributes = Object.keys(a).concat(Object.keys(b)).reduce(function (attributes, key) { + if (!equal(a[key], b[key])) { + attributes[key] = b[key] === undefined ? null : b[key]; + } + return attributes; + }, {}); + return Object.keys(attributes).length > 0 ? attributes : undefined; + }, + + transform: function (a, b, priority) { + if (typeof a !== 'object') return b; + if (typeof b !== 'object') return undefined; + if (!priority) return b; // b simply overwrites us without priority + var attributes = Object.keys(b).reduce(function (attributes, key) { + if (a[key] === undefined) attributes[key] = b[key]; // null is a valid value + return attributes; + }, {}); + return Object.keys(attributes).length > 0 ? attributes : undefined; + } + }, + + iterator: function (ops) { + return new Iterator(ops); + }, + + length: function (op) { + if (typeof op['delete'] === 'number') { + return op['delete']; + } else if (typeof op.retain === 'number') { + return op.retain; + } else { + return typeof op.insert === 'string' ? op.insert.length : 1; + } + } +}; + + +function Iterator(ops) { + this.ops = ops; + this.index = 0; + this.offset = 0; +}; + +Iterator.prototype.hasNext = function () { + return this.peekLength() < Infinity; +}; + +Iterator.prototype.next = function (length) { + if (!length) length = Infinity; + var nextOp = this.ops[this.index]; + if (nextOp) { + var offset = this.offset; + var opLength = lib.length(nextOp) + if (length >= opLength - offset) { + length = opLength - offset; + this.index += 1; + this.offset = 0; + } else { + this.offset += length; + } + if (typeof nextOp['delete'] === 'number') { + return { 'delete': length }; + } else { + var retOp = {}; + if (nextOp.attributes) { + retOp.attributes = nextOp.attributes; + } + if (typeof nextOp.retain === 'number') { + retOp.retain = length; + } else if (typeof nextOp.insert === 'string') { + retOp.insert = nextOp.insert.substr(offset, length); + } else { + // offset should === 0, length should === 1 + retOp.insert = nextOp.insert; + } + return retOp; + } + } else { + return { retain: Infinity }; + } +}; + +Iterator.prototype.peek = function () { + return this.ops[this.index]; +}; + +Iterator.prototype.peekLength = function () { + if (this.ops[this.index]) { + // Should never return 0 if our index is being managed correctly + return lib.length(this.ops[this.index]) - this.offset; + } else { + return Infinity; + } +}; + +Iterator.prototype.peekType = function () { + if (this.ops[this.index]) { + if (typeof this.ops[this.index]['delete'] === 'number') { + return 'delete'; + } else if (typeof this.ops[this.index].retain === 'number') { + return 'retain'; + } else { + return 'insert'; + } + } + return 'retain'; +}; + + +module.exports = lib; + + +/***/ }), +/* 21 */ +/***/ (function(module, exports) { + +var clone = (function() { +'use strict'; + +function _instanceof(obj, type) { + return type != null && obj instanceof type; +} + +var nativeMap; +try { + nativeMap = Map; +} catch(_) { + // maybe a reference error because no `Map`. Give it a dummy value that no + // value will ever be an instanceof. + nativeMap = function() {}; +} + +var nativeSet; +try { + nativeSet = Set; +} catch(_) { + nativeSet = function() {}; +} + +var nativePromise; +try { + nativePromise = Promise; +} catch(_) { + nativePromise = function() {}; +} + +/** + * Clones (copies) an Object using deep copying. + * + * This function supports circular references by default, but if you are certain + * there are no circular references in your object, you can save some CPU time + * by calling clone(obj, false). + * + * Caution: if `circular` is false and `parent` contains circular references, + * your program may enter an infinite loop and crash. + * + * @param `parent` - the object to be cloned + * @param `circular` - set to true if the object to be cloned may contain + * circular references. (optional - true by default) + * @param `depth` - set to a number if the object is only to be cloned to + * a particular depth. (optional - defaults to Infinity) + * @param `prototype` - sets the prototype to be used when cloning an object. + * (optional - defaults to parent prototype). + * @param `includeNonEnumerable` - set to true if the non-enumerable properties + * should be cloned as well. Non-enumerable properties on the prototype + * chain will be ignored. (optional - false by default) +*/ +function clone(parent, circular, depth, prototype, includeNonEnumerable) { + if (typeof circular === 'object') { + depth = circular.depth; + prototype = circular.prototype; + includeNonEnumerable = circular.includeNonEnumerable; + circular = circular.circular; + } + // maintain two arrays for circular references, where corresponding parents + // and children have the same index + var allParents = []; + var allChildren = []; + + var useBuffer = typeof Buffer != 'undefined'; + + if (typeof circular == 'undefined') + circular = true; + + if (typeof depth == 'undefined') + depth = Infinity; + + // recurse this function so we don't reset allParents and allChildren + function _clone(parent, depth) { + // cloning null always returns null + if (parent === null) + return null; + + if (depth === 0) + return parent; + + var child; + var proto; + if (typeof parent != 'object') { + return parent; + } + + if (_instanceof(parent, nativeMap)) { + child = new nativeMap(); + } else if (_instanceof(parent, nativeSet)) { + child = new nativeSet(); + } else if (_instanceof(parent, nativePromise)) { + child = new nativePromise(function (resolve, reject) { + parent.then(function(value) { + resolve(_clone(value, depth - 1)); + }, function(err) { + reject(_clone(err, depth - 1)); + }); + }); + } else if (clone.__isArray(parent)) { + child = []; + } else if (clone.__isRegExp(parent)) { + child = new RegExp(parent.source, __getRegExpFlags(parent)); + if (parent.lastIndex) child.lastIndex = parent.lastIndex; + } else if (clone.__isDate(parent)) { + child = new Date(parent.getTime()); + } else if (useBuffer && Buffer.isBuffer(parent)) { + child = new Buffer(parent.length); + parent.copy(child); + return child; + } else if (_instanceof(parent, Error)) { + child = Object.create(parent); + } else { + if (typeof prototype == 'undefined') { + proto = Object.getPrototypeOf(parent); + child = Object.create(proto); + } + else { + child = Object.create(prototype); + proto = prototype; + } + } + + if (circular) { + var index = allParents.indexOf(parent); + + if (index != -1) { + return allChildren[index]; + } + allParents.push(parent); + allChildren.push(child); + } + + if (_instanceof(parent, nativeMap)) { + parent.forEach(function(value, key) { + var keyChild = _clone(key, depth - 1); + var valueChild = _clone(value, depth - 1); + child.set(keyChild, valueChild); + }); + } + if (_instanceof(parent, nativeSet)) { + parent.forEach(function(value) { + var entryChild = _clone(value, depth - 1); + child.add(entryChild); + }); + } + + for (var i in parent) { + var attrs; + if (proto) { + attrs = Object.getOwnPropertyDescriptor(proto, i); + } + + if (attrs && attrs.set == null) { + continue; + } + child[i] = _clone(parent[i], depth - 1); + } + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(parent); + for (var i = 0; i < symbols.length; i++) { + // Don't need to worry about cloning a symbol because it is a primitive, + // like a number or string. + var symbol = symbols[i]; + var descriptor = Object.getOwnPropertyDescriptor(parent, symbol); + if (descriptor && !descriptor.enumerable && !includeNonEnumerable) { + continue; + } + child[symbol] = _clone(parent[symbol], depth - 1); + if (!descriptor.enumerable) { + Object.defineProperty(child, symbol, { + enumerable: false + }); + } + } + } + + if (includeNonEnumerable) { + var allPropertyNames = Object.getOwnPropertyNames(parent); + for (var i = 0; i < allPropertyNames.length; i++) { + var propertyName = allPropertyNames[i]; + var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName); + if (descriptor && descriptor.enumerable) { + continue; + } + child[propertyName] = _clone(parent[propertyName], depth - 1); + Object.defineProperty(child, propertyName, { + enumerable: false + }); + } + } + + return child; + } + + return _clone(parent, depth); +} + +/** + * Simple flat clone using prototype, accepts only objects, usefull for property + * override on FLAT configuration object (no nested props). + * + * USE WITH CAUTION! This may not behave as you wish if you do not know how this + * works. + */ +clone.clonePrototype = function clonePrototype(parent) { + if (parent === null) + return null; + + var c = function () {}; + c.prototype = parent; + return new c(); +}; + +// private utility functions + +function __objToStr(o) { + return Object.prototype.toString.call(o); +} +clone.__objToStr = __objToStr; + +function __isDate(o) { + return typeof o === 'object' && __objToStr(o) === '[object Date]'; +} +clone.__isDate = __isDate; + +function __isArray(o) { + return typeof o === 'object' && __objToStr(o) === '[object Array]'; +} +clone.__isArray = __isArray; + +function __isRegExp(o) { + return typeof o === 'object' && __objToStr(o) === '[object RegExp]'; +} +clone.__isRegExp = __isRegExp; + +function __getRegExpFlags(re) { + var flags = ''; + if (re.global) flags += 'g'; + if (re.ignoreCase) flags += 'i'; + if (re.multiline) flags += 'm'; + return flags; +} +clone.__getRegExpFlags = __getRegExpFlags; + +return clone; +})(); + +if (typeof module === 'object' && module.exports) { + module.exports = clone; +} + + +/***/ }), +/* 22 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _emitter = __webpack_require__(8); + +var _emitter2 = _interopRequireDefault(_emitter); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +var _break = __webpack_require__(16); + +var _break2 = _interopRequireDefault(_break); + +var _code = __webpack_require__(13); + +var _code2 = _interopRequireDefault(_code); + +var _container = __webpack_require__(25); + +var _container2 = _interopRequireDefault(_container); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +function isLine(blot) { + return blot instanceof _block2.default || blot instanceof _block.BlockEmbed; +} + +var Scroll = function (_Parchment$Scroll) { + _inherits(Scroll, _Parchment$Scroll); + + function Scroll(domNode, config) { + _classCallCheck(this, Scroll); + + var _this = _possibleConstructorReturn(this, (Scroll.__proto__ || Object.getPrototypeOf(Scroll)).call(this, domNode)); + + _this.emitter = config.emitter; + if (Array.isArray(config.whitelist)) { + _this.whitelist = config.whitelist.reduce(function (whitelist, format) { + whitelist[format] = true; + return whitelist; + }, {}); + } + // Some reason fixes composition issues with character languages in Windows/Chrome, Safari + _this.domNode.addEventListener('DOMNodeInserted', function () {}); + _this.optimize(); + _this.enable(); + return _this; + } + + _createClass(Scroll, [{ + key: 'batchStart', + value: function batchStart() { + this.batch = true; + } + }, { + key: 'batchEnd', + value: function batchEnd() { + this.batch = false; + this.optimize(); + } + }, { + key: 'deleteAt', + value: function deleteAt(index, length) { + var _line = this.line(index), + _line2 = _slicedToArray(_line, 2), + first = _line2[0], + offset = _line2[1]; + + var _line3 = this.line(index + length), + _line4 = _slicedToArray(_line3, 1), + last = _line4[0]; + + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'deleteAt', this).call(this, index, length); + if (last != null && first !== last && offset > 0) { + if (first instanceof _block.BlockEmbed || last instanceof _block.BlockEmbed) { + this.optimize(); + return; + } + if (first instanceof _code2.default) { + var newlineIndex = first.newlineIndex(first.length(), true); + if (newlineIndex > -1) { + first = first.split(newlineIndex + 1); + if (first === last) { + this.optimize(); + return; + } + } + } else if (last instanceof _code2.default) { + var _newlineIndex = last.newlineIndex(0); + if (_newlineIndex > -1) { + last.split(_newlineIndex + 1); + } + } + var ref = last.children.head instanceof _break2.default ? null : last.children.head; + first.moveChildren(last, ref); + first.remove(); + } + this.optimize(); + } + }, { + key: 'enable', + value: function enable() { + var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; + + this.domNode.setAttribute('contenteditable', enabled); + } + }, { + key: 'formatAt', + value: function formatAt(index, length, format, value) { + if (this.whitelist != null && !this.whitelist[format]) return; + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'formatAt', this).call(this, index, length, format, value); + this.optimize(); + } + }, { + key: 'insertAt', + value: function insertAt(index, value, def) { + if (def != null && this.whitelist != null && !this.whitelist[value]) return; + if (index >= this.length()) { + if (def == null || _parchment2.default.query(value, _parchment2.default.Scope.BLOCK) == null) { + var blot = _parchment2.default.create(this.statics.defaultChild); + this.appendChild(blot); + if (def == null && value.endsWith('\n')) { + value = value.slice(0, -1); + } + blot.insertAt(0, value, def); + } else { + var embed = _parchment2.default.create(value, def); + this.appendChild(embed); + } + } else { + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'insertAt', this).call(this, index, value, def); + } + this.optimize(); + } + }, { + key: 'insertBefore', + value: function insertBefore(blot, ref) { + if (blot.statics.scope === _parchment2.default.Scope.INLINE_BLOT) { + var wrapper = _parchment2.default.create(this.statics.defaultChild); + wrapper.appendChild(blot); + blot = wrapper; + } + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'insertBefore', this).call(this, blot, ref); + } + }, { + key: 'leaf', + value: function leaf(index) { + return this.path(index).pop() || [null, -1]; + } + }, { + key: 'line', + value: function line(index) { + if (index === this.length()) { + return this.line(index - 1); + } + return this.descendant(isLine, index); + } + }, { + key: 'lines', + value: function lines() { + var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Number.MAX_VALUE; + + var getLines = function getLines(blot, index, length) { + var lines = [], + lengthLeft = length; + blot.children.forEachAt(index, length, function (child, index, length) { + if (isLine(child)) { + lines.push(child); + } else if (child instanceof _parchment2.default.Container) { + lines = lines.concat(getLines(child, index, lengthLeft)); + } + lengthLeft -= length; + }); + return lines; + }; + return getLines(this, index, length); + } + }, { + key: 'optimize', + value: function optimize() { + var mutations = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + if (this.batch === true) return; + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'optimize', this).call(this, mutations, context); + if (mutations.length > 0) { + this.emitter.emit(_emitter2.default.events.SCROLL_OPTIMIZE, mutations, context); + } + } + }, { + key: 'path', + value: function path(index) { + return _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'path', this).call(this, index).slice(1); // Exclude self + } + }, { + key: 'update', + value: function update(mutations) { + if (this.batch === true) return; + var source = _emitter2.default.sources.USER; + if (typeof mutations === 'string') { + source = mutations; + } + if (!Array.isArray(mutations)) { + mutations = this.observer.takeRecords(); + } + if (mutations.length > 0) { + this.emitter.emit(_emitter2.default.events.SCROLL_BEFORE_UPDATE, source, mutations); + } + _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'update', this).call(this, mutations.concat([])); // pass copy + if (mutations.length > 0) { + this.emitter.emit(_emitter2.default.events.SCROLL_UPDATE, source, mutations); + } + } + }]); + + return Scroll; +}(_parchment2.default.Scroll); + +Scroll.blotName = 'scroll'; +Scroll.className = 'ql-editor'; +Scroll.tagName = 'DIV'; +Scroll.defaultChild = 'block'; +Scroll.allowedChildren = [_block2.default, _block.BlockEmbed, _container2.default]; + +exports.default = Scroll; + +/***/ }), +/* 23 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.SHORTKEY = exports.default = undefined; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _clone = __webpack_require__(21); + +var _clone2 = _interopRequireDefault(_clone); + +var _deepEqual = __webpack_require__(11); + +var _deepEqual2 = _interopRequireDefault(_deepEqual); + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _op = __webpack_require__(20); + +var _op2 = _interopRequireDefault(_op); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _logger = __webpack_require__(10); + +var _logger2 = _interopRequireDefault(_logger); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var debug = (0, _logger2.default)('quill:keyboard'); + +var SHORTKEY = /Mac/i.test(navigator.platform) ? 'metaKey' : 'ctrlKey'; + +var Keyboard = function (_Module) { + _inherits(Keyboard, _Module); + + _createClass(Keyboard, null, [{ + key: 'match', + value: function match(evt, binding) { + binding = normalize(binding); + if (['altKey', 'ctrlKey', 'metaKey', 'shiftKey'].some(function (key) { + return !!binding[key] !== evt[key] && binding[key] !== null; + })) { + return false; + } + return binding.key === (evt.which || evt.keyCode); + } + }]); + + function Keyboard(quill, options) { + _classCallCheck(this, Keyboard); + + var _this = _possibleConstructorReturn(this, (Keyboard.__proto__ || Object.getPrototypeOf(Keyboard)).call(this, quill, options)); + + _this.bindings = {}; + Object.keys(_this.options.bindings).forEach(function (name) { + if (name === 'list autofill' && quill.scroll.whitelist != null && !quill.scroll.whitelist['list']) { + return; + } + if (_this.options.bindings[name]) { + _this.addBinding(_this.options.bindings[name]); + } + }); + _this.addBinding({ key: Keyboard.keys.ENTER, shiftKey: null }, handleEnter); + _this.addBinding({ key: Keyboard.keys.ENTER, metaKey: null, ctrlKey: null, altKey: null }, function () {}); + if (/Firefox/i.test(navigator.userAgent)) { + // Need to handle delete and backspace for Firefox in the general case #1171 + _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true }, handleBackspace); + _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true }, handleDelete); + } else { + _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true, prefix: /^.?$/ }, handleBackspace); + _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true, suffix: /^.?$/ }, handleDelete); + } + _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: false }, handleDeleteRange); + _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: false }, handleDeleteRange); + _this.addBinding({ key: Keyboard.keys.BACKSPACE, altKey: null, ctrlKey: null, metaKey: null, shiftKey: null }, { collapsed: true, offset: 0 }, handleBackspace); + _this.listen(); + return _this; + } + + _createClass(Keyboard, [{ + key: 'addBinding', + value: function addBinding(key) { + var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var handler = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + + var binding = normalize(key); + if (binding == null || binding.key == null) { + return debug.warn('Attempted to add invalid keyboard binding', binding); + } + if (typeof context === 'function') { + context = { handler: context }; + } + if (typeof handler === 'function') { + handler = { handler: handler }; + } + binding = (0, _extend2.default)(binding, context, handler); + this.bindings[binding.key] = this.bindings[binding.key] || []; + this.bindings[binding.key].push(binding); + } + }, { + key: 'listen', + value: function listen() { + var _this2 = this; + + this.quill.root.addEventListener('keydown', function (evt) { + if (evt.defaultPrevented) return; + var which = evt.which || evt.keyCode; + var bindings = (_this2.bindings[which] || []).filter(function (binding) { + return Keyboard.match(evt, binding); + }); + if (bindings.length === 0) return; + var range = _this2.quill.getSelection(); + if (range == null || !_this2.quill.hasFocus()) return; + + var _quill$getLine = _this2.quill.getLine(range.index), + _quill$getLine2 = _slicedToArray(_quill$getLine, 2), + line = _quill$getLine2[0], + offset = _quill$getLine2[1]; + + var _quill$getLeaf = _this2.quill.getLeaf(range.index), + _quill$getLeaf2 = _slicedToArray(_quill$getLeaf, 2), + leafStart = _quill$getLeaf2[0], + offsetStart = _quill$getLeaf2[1]; + + var _ref = range.length === 0 ? [leafStart, offsetStart] : _this2.quill.getLeaf(range.index + range.length), + _ref2 = _slicedToArray(_ref, 2), + leafEnd = _ref2[0], + offsetEnd = _ref2[1]; + + var prefixText = leafStart instanceof _parchment2.default.Text ? leafStart.value().slice(0, offsetStart) : ''; + var suffixText = leafEnd instanceof _parchment2.default.Text ? leafEnd.value().slice(offsetEnd) : ''; + var curContext = { + collapsed: range.length === 0, + empty: range.length === 0 && line.length() <= 1, + format: _this2.quill.getFormat(range), + offset: offset, + prefix: prefixText, + suffix: suffixText + }; + var prevented = bindings.some(function (binding) { + if (binding.collapsed != null && binding.collapsed !== curContext.collapsed) return false; + if (binding.empty != null && binding.empty !== curContext.empty) return false; + if (binding.offset != null && binding.offset !== curContext.offset) return false; + if (Array.isArray(binding.format)) { + // any format is present + if (binding.format.every(function (name) { + return curContext.format[name] == null; + })) { + return false; + } + } else if (_typeof(binding.format) === 'object') { + // all formats must match + if (!Object.keys(binding.format).every(function (name) { + if (binding.format[name] === true) return curContext.format[name] != null; + if (binding.format[name] === false) return curContext.format[name] == null; + return (0, _deepEqual2.default)(binding.format[name], curContext.format[name]); + })) { + return false; + } + } + if (binding.prefix != null && !binding.prefix.test(curContext.prefix)) return false; + if (binding.suffix != null && !binding.suffix.test(curContext.suffix)) return false; + return binding.handler.call(_this2, range, curContext) !== true; + }); + if (prevented) { + evt.preventDefault(); + } + }); + } + }]); + + return Keyboard; +}(_module2.default); + +Keyboard.keys = { + BACKSPACE: 8, + TAB: 9, + ENTER: 13, + ESCAPE: 27, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + DELETE: 46 +}; + +Keyboard.DEFAULTS = { + bindings: { + 'bold': makeFormatHandler('bold'), + 'italic': makeFormatHandler('italic'), + 'underline': makeFormatHandler('underline'), + 'indent': { + // highlight tab or tab at beginning of list, indent or blockquote + key: Keyboard.keys.TAB, + format: ['blockquote', 'indent', 'list'], + handler: function handler(range, context) { + if (context.collapsed && context.offset !== 0) return true; + this.quill.format('indent', '+1', _quill2.default.sources.USER); + } + }, + 'outdent': { + key: Keyboard.keys.TAB, + shiftKey: true, + format: ['blockquote', 'indent', 'list'], + // highlight tab or tab at beginning of list, indent or blockquote + handler: function handler(range, context) { + if (context.collapsed && context.offset !== 0) return true; + this.quill.format('indent', '-1', _quill2.default.sources.USER); + } + }, + 'outdent backspace': { + key: Keyboard.keys.BACKSPACE, + collapsed: true, + shiftKey: null, + metaKey: null, + ctrlKey: null, + altKey: null, + format: ['indent', 'list'], + offset: 0, + handler: function handler(range, context) { + if (context.format.indent != null) { + this.quill.format('indent', '-1', _quill2.default.sources.USER); + } else if (context.format.list != null) { + this.quill.format('list', false, _quill2.default.sources.USER); + } + } + }, + 'indent code-block': makeCodeBlockHandler(true), + 'outdent code-block': makeCodeBlockHandler(false), + 'remove tab': { + key: Keyboard.keys.TAB, + shiftKey: true, + collapsed: true, + prefix: /\t$/, + handler: function handler(range) { + this.quill.deleteText(range.index - 1, 1, _quill2.default.sources.USER); + } + }, + 'tab': { + key: Keyboard.keys.TAB, + handler: function handler(range) { + this.quill.history.cutoff(); + var delta = new _quillDelta2.default().retain(range.index).delete(range.length).insert('\t'); + this.quill.updateContents(delta, _quill2.default.sources.USER); + this.quill.history.cutoff(); + this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT); + } + }, + 'list empty enter': { + key: Keyboard.keys.ENTER, + collapsed: true, + format: ['list'], + empty: true, + handler: function handler(range, context) { + this.quill.format('list', false, _quill2.default.sources.USER); + if (context.format.indent) { + this.quill.format('indent', false, _quill2.default.sources.USER); + } + } + }, + 'checklist enter': { + key: Keyboard.keys.ENTER, + collapsed: true, + format: { list: 'checked' }, + handler: function handler(range) { + var _quill$getLine3 = this.quill.getLine(range.index), + _quill$getLine4 = _slicedToArray(_quill$getLine3, 2), + line = _quill$getLine4[0], + offset = _quill$getLine4[1]; + + var formats = (0, _extend2.default)({}, line.formats(), { list: 'checked' }); + var delta = new _quillDelta2.default().retain(range.index).insert('\n', formats).retain(line.length() - offset - 1).retain(1, { list: 'unchecked' }); + this.quill.updateContents(delta, _quill2.default.sources.USER); + this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT); + this.quill.scrollIntoView(); + } + }, + 'header enter': { + key: Keyboard.keys.ENTER, + collapsed: true, + format: ['header'], + suffix: /^$/, + handler: function handler(range, context) { + var _quill$getLine5 = this.quill.getLine(range.index), + _quill$getLine6 = _slicedToArray(_quill$getLine5, 2), + line = _quill$getLine6[0], + offset = _quill$getLine6[1]; + + var delta = new _quillDelta2.default().retain(range.index).insert('\n', context.format).retain(line.length() - offset - 1).retain(1, { header: null }); + this.quill.updateContents(delta, _quill2.default.sources.USER); + this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT); + this.quill.scrollIntoView(); + } + }, + 'list autofill': { + key: ' ', + collapsed: true, + format: { list: false }, + prefix: /^\s*?(\d+\.|-|\*|\[ ?\]|\[x\])$/, + handler: function handler(range, context) { + var length = context.prefix.length; + + var _quill$getLine7 = this.quill.getLine(range.index), + _quill$getLine8 = _slicedToArray(_quill$getLine7, 2), + line = _quill$getLine8[0], + offset = _quill$getLine8[1]; + + if (offset > length) return true; + var value = void 0; + switch (context.prefix.trim()) { + case '[]':case '[ ]': + value = 'unchecked'; + break; + case '[x]': + value = 'checked'; + break; + case '-':case '*': + value = 'bullet'; + break; + default: + value = 'ordered'; + } + this.quill.insertText(range.index, ' ', _quill2.default.sources.USER); + this.quill.history.cutoff(); + var delta = new _quillDelta2.default().retain(range.index - offset).delete(length + 1).retain(line.length() - 2 - offset).retain(1, { list: value }); + this.quill.updateContents(delta, _quill2.default.sources.USER); + this.quill.history.cutoff(); + this.quill.setSelection(range.index - length, _quill2.default.sources.SILENT); + } + }, + 'code exit': { + key: Keyboard.keys.ENTER, + collapsed: true, + format: ['code-block'], + prefix: /\n\n$/, + suffix: /^\s+$/, + handler: function handler(range) { + var _quill$getLine9 = this.quill.getLine(range.index), + _quill$getLine10 = _slicedToArray(_quill$getLine9, 2), + line = _quill$getLine10[0], + offset = _quill$getLine10[1]; + + var delta = new _quillDelta2.default().retain(range.index + line.length() - offset - 2).retain(1, { 'code-block': null }).delete(1); + this.quill.updateContents(delta, _quill2.default.sources.USER); + } + }, + 'embed left': makeEmbedArrowHandler(Keyboard.keys.LEFT, false), + 'embed left shift': makeEmbedArrowHandler(Keyboard.keys.LEFT, true), + 'embed right': makeEmbedArrowHandler(Keyboard.keys.RIGHT, false), + 'embed right shift': makeEmbedArrowHandler(Keyboard.keys.RIGHT, true) + } +}; + +function makeEmbedArrowHandler(key, shiftKey) { + var _ref3; + + var where = key === Keyboard.keys.LEFT ? 'prefix' : 'suffix'; + return _ref3 = { + key: key, + shiftKey: shiftKey, + altKey: null + }, _defineProperty(_ref3, where, /^$/), _defineProperty(_ref3, 'handler', function handler(range) { + var index = range.index; + if (key === Keyboard.keys.RIGHT) { + index += range.length + 1; + } + + var _quill$getLeaf3 = this.quill.getLeaf(index), + _quill$getLeaf4 = _slicedToArray(_quill$getLeaf3, 1), + leaf = _quill$getLeaf4[0]; + + if (!(leaf instanceof _parchment2.default.Embed)) return true; + if (key === Keyboard.keys.LEFT) { + if (shiftKey) { + this.quill.setSelection(range.index - 1, range.length + 1, _quill2.default.sources.USER); + } else { + this.quill.setSelection(range.index - 1, _quill2.default.sources.USER); + } + } else { + if (shiftKey) { + this.quill.setSelection(range.index, range.length + 1, _quill2.default.sources.USER); + } else { + this.quill.setSelection(range.index + range.length + 1, _quill2.default.sources.USER); + } + } + return false; + }), _ref3; +} + +function handleBackspace(range, context) { + if (range.index === 0 || this.quill.getLength() <= 1) return; + + var _quill$getLine11 = this.quill.getLine(range.index), + _quill$getLine12 = _slicedToArray(_quill$getLine11, 1), + line = _quill$getLine12[0]; + + var formats = {}; + if (context.offset === 0) { + var _quill$getLine13 = this.quill.getLine(range.index - 1), + _quill$getLine14 = _slicedToArray(_quill$getLine13, 1), + prev = _quill$getLine14[0]; + + if (prev != null && prev.length() > 1) { + var curFormats = line.formats(); + var prevFormats = this.quill.getFormat(range.index - 1, 1); + formats = _op2.default.attributes.diff(curFormats, prevFormats) || {}; + } + } + // Check for astral symbols + var length = /[\uD800-\uDBFF][\uDC00-\uDFFF]$/.test(context.prefix) ? 2 : 1; + this.quill.deleteText(range.index - length, length, _quill2.default.sources.USER); + if (Object.keys(formats).length > 0) { + this.quill.formatLine(range.index - length, length, formats, _quill2.default.sources.USER); + } + this.quill.focus(); +} + +function handleDelete(range, context) { + // Check for astral symbols + var length = /^[\uD800-\uDBFF][\uDC00-\uDFFF]/.test(context.suffix) ? 2 : 1; + if (range.index >= this.quill.getLength() - length) return; + var formats = {}, + nextLength = 0; + + var _quill$getLine15 = this.quill.getLine(range.index), + _quill$getLine16 = _slicedToArray(_quill$getLine15, 1), + line = _quill$getLine16[0]; + + if (context.offset >= line.length() - 1) { + var _quill$getLine17 = this.quill.getLine(range.index + 1), + _quill$getLine18 = _slicedToArray(_quill$getLine17, 1), + next = _quill$getLine18[0]; + + if (next) { + var curFormats = line.formats(); + var nextFormats = this.quill.getFormat(range.index, 1); + formats = _op2.default.attributes.diff(curFormats, nextFormats) || {}; + nextLength = next.length(); + } + } + this.quill.deleteText(range.index, length, _quill2.default.sources.USER); + if (Object.keys(formats).length > 0) { + this.quill.formatLine(range.index + nextLength - 1, length, formats, _quill2.default.sources.USER); + } +} + +function handleDeleteRange(range) { + var lines = this.quill.getLines(range); + var formats = {}; + if (lines.length > 1) { + var firstFormats = lines[0].formats(); + var lastFormats = lines[lines.length - 1].formats(); + formats = _op2.default.attributes.diff(lastFormats, firstFormats) || {}; + } + this.quill.deleteText(range, _quill2.default.sources.USER); + if (Object.keys(formats).length > 0) { + this.quill.formatLine(range.index, 1, formats, _quill2.default.sources.USER); + } + this.quill.setSelection(range.index, _quill2.default.sources.SILENT); + this.quill.focus(); +} + +function handleEnter(range, context) { + var _this3 = this; + + if (range.length > 0) { + this.quill.scroll.deleteAt(range.index, range.length); // So we do not trigger text-change + } + var lineFormats = Object.keys(context.format).reduce(function (lineFormats, format) { + if (_parchment2.default.query(format, _parchment2.default.Scope.BLOCK) && !Array.isArray(context.format[format])) { + lineFormats[format] = context.format[format]; + } + return lineFormats; + }, {}); + this.quill.insertText(range.index, '\n', lineFormats, _quill2.default.sources.USER); + // Earlier scroll.deleteAt might have messed up our selection, + // so insertText's built in selection preservation is not reliable + this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT); + this.quill.focus(); + Object.keys(context.format).forEach(function (name) { + if (lineFormats[name] != null) return; + if (Array.isArray(context.format[name])) return; + if (name === 'link') return; + _this3.quill.format(name, context.format[name], _quill2.default.sources.USER); + }); +} + +function makeCodeBlockHandler(indent) { + return { + key: Keyboard.keys.TAB, + shiftKey: !indent, + format: { 'code-block': true }, + handler: function handler(range) { + var CodeBlock = _parchment2.default.query('code-block'); + var index = range.index, + length = range.length; + + var _quill$scroll$descend = this.quill.scroll.descendant(CodeBlock, index), + _quill$scroll$descend2 = _slicedToArray(_quill$scroll$descend, 2), + block = _quill$scroll$descend2[0], + offset = _quill$scroll$descend2[1]; + + if (block == null) return; + var scrollIndex = this.quill.getIndex(block); + var start = block.newlineIndex(offset, true) + 1; + var end = block.newlineIndex(scrollIndex + offset + length); + var lines = block.domNode.textContent.slice(start, end).split('\n'); + offset = 0; + lines.forEach(function (line, i) { + if (indent) { + block.insertAt(start + offset, CodeBlock.TAB); + offset += CodeBlock.TAB.length; + if (i === 0) { + index += CodeBlock.TAB.length; + } else { + length += CodeBlock.TAB.length; + } + } else if (line.startsWith(CodeBlock.TAB)) { + block.deleteAt(start + offset, CodeBlock.TAB.length); + offset -= CodeBlock.TAB.length; + if (i === 0) { + index -= CodeBlock.TAB.length; + } else { + length -= CodeBlock.TAB.length; + } + } + offset += line.length + 1; + }); + this.quill.update(_quill2.default.sources.USER); + this.quill.setSelection(index, length, _quill2.default.sources.SILENT); + } + }; +} + +function makeFormatHandler(format) { + return { + key: format[0].toUpperCase(), + shortKey: true, + handler: function handler(range, context) { + this.quill.format(format, !context.format[format], _quill2.default.sources.USER); + } + }; +} + +function normalize(binding) { + if (typeof binding === 'string' || typeof binding === 'number') { + return normalize({ key: binding }); + } + if ((typeof binding === 'undefined' ? 'undefined' : _typeof(binding)) === 'object') { + binding = (0, _clone2.default)(binding, false); + } + if (typeof binding.key === 'string') { + if (Keyboard.keys[binding.key.toUpperCase()] != null) { + binding.key = Keyboard.keys[binding.key.toUpperCase()]; + } else if (binding.key.length === 1) { + binding.key = binding.key.toUpperCase().charCodeAt(0); + } else { + return null; + } + } + if (binding.shortKey) { + binding[SHORTKEY] = binding.shortKey; + delete binding.shortKey; + } + return binding; +} + +exports.default = Keyboard; +exports.SHORTKEY = SHORTKEY; + +/***/ }), +/* 24 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Cursor = function (_Parchment$Embed) { + _inherits(Cursor, _Parchment$Embed); + + _createClass(Cursor, null, [{ + key: 'value', + value: function value() { + return undefined; + } + }]); + + function Cursor(domNode, selection) { + _classCallCheck(this, Cursor); + + var _this = _possibleConstructorReturn(this, (Cursor.__proto__ || Object.getPrototypeOf(Cursor)).call(this, domNode)); + + _this.selection = selection; + _this.textNode = document.createTextNode(Cursor.CONTENTS); + _this.domNode.appendChild(_this.textNode); + _this._length = 0; + return _this; + } + + _createClass(Cursor, [{ + key: 'detach', + value: function detach() { + // super.detach() will also clear domNode.__blot + if (this.parent != null) this.parent.removeChild(this); + } + }, { + key: 'format', + value: function format(name, value) { + if (this._length !== 0) { + return _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'format', this).call(this, name, value); + } + var target = this, + index = 0; + while (target != null && target.statics.scope !== _parchment2.default.Scope.BLOCK_BLOT) { + index += target.offset(target.parent); + target = target.parent; + } + if (target != null) { + this._length = Cursor.CONTENTS.length; + target.optimize(); + target.formatAt(index, Cursor.CONTENTS.length, name, value); + this._length = 0; + } + } + }, { + key: 'index', + value: function index(node, offset) { + if (node === this.textNode) return 0; + return _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'index', this).call(this, node, offset); + } + }, { + key: 'length', + value: function length() { + return this._length; + } + }, { + key: 'position', + value: function position() { + return [this.textNode, this.textNode.data.length]; + } + }, { + key: 'remove', + value: function remove() { + _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'remove', this).call(this); + this.parent = null; + } + }, { + key: 'restore', + value: function restore() { + if (this.selection.composing || this.parent == null) return; + var textNode = this.textNode; + var range = this.selection.getNativeRange(); + var restoreText = void 0, + start = void 0, + end = void 0; + if (range != null && range.start.node === textNode && range.end.node === textNode) { + var _ref = [textNode, range.start.offset, range.end.offset]; + restoreText = _ref[0]; + start = _ref[1]; + end = _ref[2]; + } + // Link format will insert text outside of anchor tag + while (this.domNode.lastChild != null && this.domNode.lastChild !== this.textNode) { + this.domNode.parentNode.insertBefore(this.domNode.lastChild, this.domNode); + } + if (this.textNode.data !== Cursor.CONTENTS) { + var text = this.textNode.data.split(Cursor.CONTENTS).join(''); + if (this.next instanceof _text2.default) { + restoreText = this.next.domNode; + this.next.insertAt(0, text); + this.textNode.data = Cursor.CONTENTS; + } else { + this.textNode.data = text; + this.parent.insertBefore(_parchment2.default.create(this.textNode), this); + this.textNode = document.createTextNode(Cursor.CONTENTS); + this.domNode.appendChild(this.textNode); + } + } + this.remove(); + if (start != null) { + var _map = [start, end].map(function (offset) { + return Math.max(0, Math.min(restoreText.data.length, offset - 1)); + }); + + var _map2 = _slicedToArray(_map, 2); + + start = _map2[0]; + end = _map2[1]; + + return { + startNode: restoreText, + startOffset: start, + endNode: restoreText, + endOffset: end + }; + } + } + }, { + key: 'update', + value: function update(mutations, context) { + var _this2 = this; + + if (mutations.some(function (mutation) { + return mutation.type === 'characterData' && mutation.target === _this2.textNode; + })) { + var range = this.restore(); + if (range) context.range = range; + } + } + }, { + key: 'value', + value: function value() { + return ''; + } + }]); + + return Cursor; +}(_parchment2.default.Embed); + +Cursor.blotName = 'cursor'; +Cursor.className = 'ql-cursor'; +Cursor.tagName = 'span'; +Cursor.CONTENTS = '\uFEFF'; // Zero width no break space + + +exports.default = Cursor; + +/***/ }), +/* 25 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Container = function (_Parchment$Container) { + _inherits(Container, _Parchment$Container); + + function Container() { + _classCallCheck(this, Container); + + return _possibleConstructorReturn(this, (Container.__proto__ || Object.getPrototypeOf(Container)).apply(this, arguments)); + } + + return Container; +}(_parchment2.default.Container); + +Container.allowedChildren = [_block2.default, _block.BlockEmbed, Container]; + +exports.default = Container; + +/***/ }), +/* 26 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ColorStyle = exports.ColorClass = exports.ColorAttributor = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var ColorAttributor = function (_Parchment$Attributor) { + _inherits(ColorAttributor, _Parchment$Attributor); + + function ColorAttributor() { + _classCallCheck(this, ColorAttributor); + + return _possibleConstructorReturn(this, (ColorAttributor.__proto__ || Object.getPrototypeOf(ColorAttributor)).apply(this, arguments)); + } + + _createClass(ColorAttributor, [{ + key: 'value', + value: function value(domNode) { + var value = _get(ColorAttributor.prototype.__proto__ || Object.getPrototypeOf(ColorAttributor.prototype), 'value', this).call(this, domNode); + if (!value.startsWith('rgb(')) return value; + value = value.replace(/^[^\d]+/, '').replace(/[^\d]+$/, ''); + return '#' + value.split(',').map(function (component) { + return ('00' + parseInt(component).toString(16)).slice(-2); + }).join(''); + } + }]); + + return ColorAttributor; +}(_parchment2.default.Attributor.Style); + +var ColorClass = new _parchment2.default.Attributor.Class('color', 'ql-color', { + scope: _parchment2.default.Scope.INLINE +}); +var ColorStyle = new ColorAttributor('color', 'color', { + scope: _parchment2.default.Scope.INLINE +}); + +exports.ColorAttributor = ColorAttributor; +exports.ColorClass = ColorClass; +exports.ColorStyle = ColorStyle; + +/***/ }), +/* 27 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.sanitize = exports.default = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Link = function (_Inline) { + _inherits(Link, _Inline); + + function Link() { + _classCallCheck(this, Link); + + return _possibleConstructorReturn(this, (Link.__proto__ || Object.getPrototypeOf(Link)).apply(this, arguments)); + } + + _createClass(Link, [{ + key: 'format', + value: function format(name, value) { + if (name !== this.statics.blotName || !value) return _get(Link.prototype.__proto__ || Object.getPrototypeOf(Link.prototype), 'format', this).call(this, name, value); + value = this.constructor.sanitize(value); + this.domNode.setAttribute('href', value); + } + }], [{ + key: 'create', + value: function create(value) { + var node = _get(Link.__proto__ || Object.getPrototypeOf(Link), 'create', this).call(this, value); + value = this.sanitize(value); + node.setAttribute('href', value); + node.setAttribute('target', '_blank'); + return node; + } + }, { + key: 'formats', + value: function formats(domNode) { + return domNode.getAttribute('href'); + } + }, { + key: 'sanitize', + value: function sanitize(url) { + return _sanitize(url, this.PROTOCOL_WHITELIST) ? url : this.SANITIZED_URL; + } + }]); + + return Link; +}(_inline2.default); + +Link.blotName = 'link'; +Link.tagName = 'A'; +Link.SANITIZED_URL = 'about:blank'; +Link.PROTOCOL_WHITELIST = ['http', 'https', 'mailto', 'tel']; + +function _sanitize(url, protocols) { + var anchor = document.createElement('a'); + anchor.href = url; + var protocol = anchor.href.slice(0, anchor.href.indexOf(':')); + return protocols.indexOf(protocol) > -1; +} + +exports.default = Link; +exports.sanitize = _sanitize; + +/***/ }), +/* 28 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _keyboard = __webpack_require__(23); + +var _keyboard2 = _interopRequireDefault(_keyboard); + +var _dropdown = __webpack_require__(107); + +var _dropdown2 = _interopRequireDefault(_dropdown); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var optionsCounter = 0; + +function toggleAriaAttribute(element, attribute) { + element.setAttribute(attribute, !(element.getAttribute(attribute) === 'true')); +} + +var Picker = function () { + function Picker(select) { + var _this = this; + + _classCallCheck(this, Picker); + + this.select = select; + this.container = document.createElement('span'); + this.buildPicker(); + this.select.style.display = 'none'; + this.select.parentNode.insertBefore(this.container, this.select); + + this.label.addEventListener('mousedown', function () { + _this.togglePicker(); + }); + this.label.addEventListener('keydown', function (event) { + switch (event.keyCode) { + // Allows the "Enter" key to open the picker + case _keyboard2.default.keys.ENTER: + _this.togglePicker(); + break; + + // Allows the "Escape" key to close the picker + case _keyboard2.default.keys.ESCAPE: + _this.escape(); + event.preventDefault(); + break; + default: + } + }); + this.select.addEventListener('change', this.update.bind(this)); + } + + _createClass(Picker, [{ + key: 'togglePicker', + value: function togglePicker() { + this.container.classList.toggle('ql-expanded'); + // Toggle aria-expanded and aria-hidden to make the picker accessible + toggleAriaAttribute(this.label, 'aria-expanded'); + toggleAriaAttribute(this.options, 'aria-hidden'); + } + }, { + key: 'buildItem', + value: function buildItem(option) { + var _this2 = this; + + var item = document.createElement('span'); + item.tabIndex = '0'; + item.setAttribute('role', 'button'); + + item.classList.add('ql-picker-item'); + if (option.hasAttribute('value')) { + item.setAttribute('data-value', option.getAttribute('value')); + } + if (option.textContent) { + item.setAttribute('data-label', option.textContent); + } + item.addEventListener('click', function () { + _this2.selectItem(item, true); + }); + item.addEventListener('keydown', function (event) { + switch (event.keyCode) { + // Allows the "Enter" key to select an item + case _keyboard2.default.keys.ENTER: + _this2.selectItem(item, true); + event.preventDefault(); + break; + + // Allows the "Escape" key to close the picker + case _keyboard2.default.keys.ESCAPE: + _this2.escape(); + event.preventDefault(); + break; + default: + } + }); + + return item; + } + }, { + key: 'buildLabel', + value: function buildLabel() { + var label = document.createElement('span'); + label.classList.add('ql-picker-label'); + label.innerHTML = _dropdown2.default; + label.tabIndex = '0'; + label.setAttribute('role', 'button'); + label.setAttribute('aria-expanded', 'false'); + this.container.appendChild(label); + return label; + } + }, { + key: 'buildOptions', + value: function buildOptions() { + var _this3 = this; + + var options = document.createElement('span'); + options.classList.add('ql-picker-options'); + + // Don't want screen readers to read this until options are visible + options.setAttribute('aria-hidden', 'true'); + options.tabIndex = '-1'; + + // Need a unique id for aria-controls + options.id = 'ql-picker-options-' + optionsCounter; + optionsCounter += 1; + this.label.setAttribute('aria-controls', options.id); + + this.options = options; + + [].slice.call(this.select.options).forEach(function (option) { + var item = _this3.buildItem(option); + options.appendChild(item); + if (option.selected === true) { + _this3.selectItem(item); + } + }); + this.container.appendChild(options); + } + }, { + key: 'buildPicker', + value: function buildPicker() { + var _this4 = this; + + [].slice.call(this.select.attributes).forEach(function (item) { + _this4.container.setAttribute(item.name, item.value); + }); + this.container.classList.add('ql-picker'); + this.label = this.buildLabel(); + this.buildOptions(); + } + }, { + key: 'escape', + value: function escape() { + var _this5 = this; + + // Close menu and return focus to trigger label + this.close(); + // Need setTimeout for accessibility to ensure that the browser executes + // focus on the next process thread and after any DOM content changes + setTimeout(function () { + return _this5.label.focus(); + }, 1); + } + }, { + key: 'close', + value: function close() { + this.container.classList.remove('ql-expanded'); + this.label.setAttribute('aria-expanded', 'false'); + this.options.setAttribute('aria-hidden', 'true'); + } + }, { + key: 'selectItem', + value: function selectItem(item) { + var trigger = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + var selected = this.container.querySelector('.ql-selected'); + if (item === selected) return; + if (selected != null) { + selected.classList.remove('ql-selected'); + } + if (item == null) return; + item.classList.add('ql-selected'); + this.select.selectedIndex = [].indexOf.call(item.parentNode.children, item); + if (item.hasAttribute('data-value')) { + this.label.setAttribute('data-value', item.getAttribute('data-value')); + } else { + this.label.removeAttribute('data-value'); + } + if (item.hasAttribute('data-label')) { + this.label.setAttribute('data-label', item.getAttribute('data-label')); + } else { + this.label.removeAttribute('data-label'); + } + if (trigger) { + if (typeof Event === 'function') { + this.select.dispatchEvent(new Event('change')); + } else if ((typeof Event === 'undefined' ? 'undefined' : _typeof(Event)) === 'object') { + // IE11 + var event = document.createEvent('Event'); + event.initEvent('change', true, true); + this.select.dispatchEvent(event); + } + this.close(); + } + } + }, { + key: 'update', + value: function update() { + var option = void 0; + if (this.select.selectedIndex > -1) { + var item = this.container.querySelector('.ql-picker-options').children[this.select.selectedIndex]; + option = this.select.options[this.select.selectedIndex]; + this.selectItem(item); + } else { + this.selectItem(null); + } + var isActive = option != null && option !== this.select.querySelector('option[selected]'); + this.label.classList.toggle('ql-active', isActive); + } + }]); + + return Picker; +}(); + +exports.default = Picker; + +/***/ }), +/* 29 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +var _break = __webpack_require__(16); + +var _break2 = _interopRequireDefault(_break); + +var _container = __webpack_require__(25); + +var _container2 = _interopRequireDefault(_container); + +var _cursor = __webpack_require__(24); + +var _cursor2 = _interopRequireDefault(_cursor); + +var _embed = __webpack_require__(35); + +var _embed2 = _interopRequireDefault(_embed); + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +var _scroll = __webpack_require__(22); + +var _scroll2 = _interopRequireDefault(_scroll); + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +var _clipboard = __webpack_require__(55); + +var _clipboard2 = _interopRequireDefault(_clipboard); + +var _history = __webpack_require__(42); + +var _history2 = _interopRequireDefault(_history); + +var _keyboard = __webpack_require__(23); + +var _keyboard2 = _interopRequireDefault(_keyboard); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +_quill2.default.register({ + 'blots/block': _block2.default, + 'blots/block/embed': _block.BlockEmbed, + 'blots/break': _break2.default, + 'blots/container': _container2.default, + 'blots/cursor': _cursor2.default, + 'blots/embed': _embed2.default, + 'blots/inline': _inline2.default, + 'blots/scroll': _scroll2.default, + 'blots/text': _text2.default, + + 'modules/clipboard': _clipboard2.default, + 'modules/history': _history2.default, + 'modules/keyboard': _keyboard2.default +}); + +_parchment2.default.register(_block2.default, _break2.default, _cursor2.default, _inline2.default, _scroll2.default, _text2.default); + +exports.default = _quill2.default; + +/***/ }), +/* 30 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var Registry = __webpack_require__(1); +var ShadowBlot = /** @class */ (function () { + function ShadowBlot(domNode) { + this.domNode = domNode; + // @ts-ignore + this.domNode[Registry.DATA_KEY] = { blot: this }; + } + Object.defineProperty(ShadowBlot.prototype, "statics", { + // Hack for accessing inherited static methods + get: function () { + return this.constructor; + }, + enumerable: true, + configurable: true + }); + ShadowBlot.create = function (value) { + if (this.tagName == null) { + throw new Registry.ParchmentError('Blot definition missing tagName'); + } + var node; + if (Array.isArray(this.tagName)) { + if (typeof value === 'string') { + value = value.toUpperCase(); + if (parseInt(value).toString() === value) { + value = parseInt(value); + } + } + if (typeof value === 'number') { + node = document.createElement(this.tagName[value - 1]); + } + else if (this.tagName.indexOf(value) > -1) { + node = document.createElement(value); + } + else { + node = document.createElement(this.tagName[0]); + } + } + else { + node = document.createElement(this.tagName); + } + if (this.className) { + node.classList.add(this.className); + } + return node; + }; + ShadowBlot.prototype.attach = function () { + if (this.parent != null) { + this.scroll = this.parent.scroll; + } + }; + ShadowBlot.prototype.clone = function () { + var domNode = this.domNode.cloneNode(false); + return Registry.create(domNode); + }; + ShadowBlot.prototype.detach = function () { + if (this.parent != null) + this.parent.removeChild(this); + // @ts-ignore + delete this.domNode[Registry.DATA_KEY]; + }; + ShadowBlot.prototype.deleteAt = function (index, length) { + var blot = this.isolate(index, length); + blot.remove(); + }; + ShadowBlot.prototype.formatAt = function (index, length, name, value) { + var blot = this.isolate(index, length); + if (Registry.query(name, Registry.Scope.BLOT) != null && value) { + blot.wrap(name, value); + } + else if (Registry.query(name, Registry.Scope.ATTRIBUTE) != null) { + var parent = Registry.create(this.statics.scope); + blot.wrap(parent); + parent.format(name, value); + } + }; + ShadowBlot.prototype.insertAt = function (index, value, def) { + var blot = def == null ? Registry.create('text', value) : Registry.create(value, def); + var ref = this.split(index); + this.parent.insertBefore(blot, ref); + }; + ShadowBlot.prototype.insertInto = function (parentBlot, refBlot) { + if (refBlot === void 0) { refBlot = null; } + if (this.parent != null) { + this.parent.children.remove(this); + } + var refDomNode = null; + parentBlot.children.insertBefore(this, refBlot); + if (refBlot != null) { + refDomNode = refBlot.domNode; + } + if (this.domNode.parentNode != parentBlot.domNode || + this.domNode.nextSibling != refDomNode) { + parentBlot.domNode.insertBefore(this.domNode, refDomNode); + } + this.parent = parentBlot; + this.attach(); + }; + ShadowBlot.prototype.isolate = function (index, length) { + var target = this.split(index); + target.split(length); + return target; + }; + ShadowBlot.prototype.length = function () { + return 1; + }; + ShadowBlot.prototype.offset = function (root) { + if (root === void 0) { root = this.parent; } + if (this.parent == null || this == root) + return 0; + return this.parent.children.offset(this) + this.parent.offset(root); + }; + ShadowBlot.prototype.optimize = function (context) { + // TODO clean up once we use WeakMap + // @ts-ignore + if (this.domNode[Registry.DATA_KEY] != null) { + // @ts-ignore + delete this.domNode[Registry.DATA_KEY].mutations; + } + }; + ShadowBlot.prototype.remove = function () { + if (this.domNode.parentNode != null) { + this.domNode.parentNode.removeChild(this.domNode); + } + this.detach(); + }; + ShadowBlot.prototype.replace = function (target) { + if (target.parent == null) + return; + target.parent.insertBefore(this, target.next); + target.remove(); + }; + ShadowBlot.prototype.replaceWith = function (name, value) { + var replacement = typeof name === 'string' ? Registry.create(name, value) : name; + replacement.replace(this); + return replacement; + }; + ShadowBlot.prototype.split = function (index, force) { + return index === 0 ? this : this.next; + }; + ShadowBlot.prototype.update = function (mutations, context) { + // Nothing to do by default + }; + ShadowBlot.prototype.wrap = function (name, value) { + var wrapper = typeof name === 'string' ? Registry.create(name, value) : name; + if (this.parent != null) { + this.parent.insertBefore(wrapper, this.next); + } + wrapper.appendChild(this); + return wrapper; + }; + ShadowBlot.blotName = 'abstract'; + return ShadowBlot; +}()); +exports.default = ShadowBlot; + + +/***/ }), +/* 31 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var attributor_1 = __webpack_require__(12); +var class_1 = __webpack_require__(32); +var style_1 = __webpack_require__(33); +var Registry = __webpack_require__(1); +var AttributorStore = /** @class */ (function () { + function AttributorStore(domNode) { + this.attributes = {}; + this.domNode = domNode; + this.build(); + } + AttributorStore.prototype.attribute = function (attribute, value) { + // verb + if (value) { + if (attribute.add(this.domNode, value)) { + if (attribute.value(this.domNode) != null) { + this.attributes[attribute.attrName] = attribute; + } + else { + delete this.attributes[attribute.attrName]; + } + } + } + else { + attribute.remove(this.domNode); + delete this.attributes[attribute.attrName]; + } + }; + AttributorStore.prototype.build = function () { + var _this = this; + this.attributes = {}; + var attributes = attributor_1.default.keys(this.domNode); + var classes = class_1.default.keys(this.domNode); + var styles = style_1.default.keys(this.domNode); + attributes + .concat(classes) + .concat(styles) + .forEach(function (name) { + var attr = Registry.query(name, Registry.Scope.ATTRIBUTE); + if (attr instanceof attributor_1.default) { + _this.attributes[attr.attrName] = attr; + } + }); + }; + AttributorStore.prototype.copy = function (target) { + var _this = this; + Object.keys(this.attributes).forEach(function (key) { + var value = _this.attributes[key].value(_this.domNode); + target.format(key, value); + }); + }; + AttributorStore.prototype.move = function (target) { + var _this = this; + this.copy(target); + Object.keys(this.attributes).forEach(function (key) { + _this.attributes[key].remove(_this.domNode); + }); + this.attributes = {}; + }; + AttributorStore.prototype.values = function () { + var _this = this; + return Object.keys(this.attributes).reduce(function (attributes, name) { + attributes[name] = _this.attributes[name].value(_this.domNode); + return attributes; + }, {}); + }; + return AttributorStore; +}()); +exports.default = AttributorStore; + + +/***/ }), +/* 32 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var attributor_1 = __webpack_require__(12); +function match(node, prefix) { + var className = node.getAttribute('class') || ''; + return className.split(/\s+/).filter(function (name) { + return name.indexOf(prefix + "-") === 0; + }); +} +var ClassAttributor = /** @class */ (function (_super) { + __extends(ClassAttributor, _super); + function ClassAttributor() { + return _super !== null && _super.apply(this, arguments) || this; + } + ClassAttributor.keys = function (node) { + return (node.getAttribute('class') || '').split(/\s+/).map(function (name) { + return name + .split('-') + .slice(0, -1) + .join('-'); + }); + }; + ClassAttributor.prototype.add = function (node, value) { + if (!this.canAdd(node, value)) + return false; + this.remove(node); + node.classList.add(this.keyName + "-" + value); + return true; + }; + ClassAttributor.prototype.remove = function (node) { + var matches = match(node, this.keyName); + matches.forEach(function (name) { + node.classList.remove(name); + }); + if (node.classList.length === 0) { + node.removeAttribute('class'); + } + }; + ClassAttributor.prototype.value = function (node) { + var result = match(node, this.keyName)[0] || ''; + var value = result.slice(this.keyName.length + 1); // +1 for hyphen + return this.canAdd(node, value) ? value : ''; + }; + return ClassAttributor; +}(attributor_1.default)); +exports.default = ClassAttributor; + + +/***/ }), +/* 33 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var attributor_1 = __webpack_require__(12); +function camelize(name) { + var parts = name.split('-'); + var rest = parts + .slice(1) + .map(function (part) { + return part[0].toUpperCase() + part.slice(1); + }) + .join(''); + return parts[0] + rest; +} +var StyleAttributor = /** @class */ (function (_super) { + __extends(StyleAttributor, _super); + function StyleAttributor() { + return _super !== null && _super.apply(this, arguments) || this; + } + StyleAttributor.keys = function (node) { + return (node.getAttribute('style') || '').split(';').map(function (value) { + var arr = value.split(':'); + return arr[0].trim(); + }); + }; + StyleAttributor.prototype.add = function (node, value) { + if (!this.canAdd(node, value)) + return false; + // @ts-ignore + node.style[camelize(this.keyName)] = value; + return true; + }; + StyleAttributor.prototype.remove = function (node) { + // @ts-ignore + node.style[camelize(this.keyName)] = ''; + if (!node.getAttribute('style')) { + node.removeAttribute('style'); + } + }; + StyleAttributor.prototype.value = function (node) { + // @ts-ignore + var value = node.style[camelize(this.keyName)]; + return this.canAdd(node, value) ? value : ''; + }; + return StyleAttributor; +}(attributor_1.default)); +exports.default = StyleAttributor; + + +/***/ }), +/* 34 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Theme = function () { + function Theme(quill, options) { + _classCallCheck(this, Theme); + + this.quill = quill; + this.options = options; + this.modules = {}; + } + + _createClass(Theme, [{ + key: 'init', + value: function init() { + var _this = this; + + Object.keys(this.options.modules).forEach(function (name) { + if (_this.modules[name] == null) { + _this.addModule(name); + } + }); + } + }, { + key: 'addModule', + value: function addModule(name) { + var moduleClass = this.quill.constructor.import('modules/' + name); + this.modules[name] = new moduleClass(this.quill, this.options.modules[name] || {}); + return this.modules[name]; + } + }]); + + return Theme; +}(); + +Theme.DEFAULTS = { + modules: {} +}; +Theme.themes = { + 'default': Theme +}; + +exports.default = Theme; + +/***/ }), +/* 35 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _text = __webpack_require__(7); + +var _text2 = _interopRequireDefault(_text); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var GUARD_TEXT = '\uFEFF'; + +var Embed = function (_Parchment$Embed) { + _inherits(Embed, _Parchment$Embed); + + function Embed(node) { + _classCallCheck(this, Embed); + + var _this = _possibleConstructorReturn(this, (Embed.__proto__ || Object.getPrototypeOf(Embed)).call(this, node)); + + _this.contentNode = document.createElement('span'); + _this.contentNode.setAttribute('contenteditable', false); + [].slice.call(_this.domNode.childNodes).forEach(function (childNode) { + _this.contentNode.appendChild(childNode); + }); + _this.leftGuard = document.createTextNode(GUARD_TEXT); + _this.rightGuard = document.createTextNode(GUARD_TEXT); + _this.domNode.appendChild(_this.leftGuard); + _this.domNode.appendChild(_this.contentNode); + _this.domNode.appendChild(_this.rightGuard); + return _this; + } + + _createClass(Embed, [{ + key: 'index', + value: function index(node, offset) { + if (node === this.leftGuard) return 0; + if (node === this.rightGuard) return 1; + return _get(Embed.prototype.__proto__ || Object.getPrototypeOf(Embed.prototype), 'index', this).call(this, node, offset); + } + }, { + key: 'restore', + value: function restore(node) { + var range = void 0, + textNode = void 0; + var text = node.data.split(GUARD_TEXT).join(''); + if (node === this.leftGuard) { + if (this.prev instanceof _text2.default) { + var prevLength = this.prev.length(); + this.prev.insertAt(prevLength, text); + range = { + startNode: this.prev.domNode, + startOffset: prevLength + text.length + }; + } else { + textNode = document.createTextNode(text); + this.parent.insertBefore(_parchment2.default.create(textNode), this); + range = { + startNode: textNode, + startOffset: text.length + }; + } + } else if (node === this.rightGuard) { + if (this.next instanceof _text2.default) { + this.next.insertAt(0, text); + range = { + startNode: this.next.domNode, + startOffset: text.length + }; + } else { + textNode = document.createTextNode(text); + this.parent.insertBefore(_parchment2.default.create(textNode), this.next); + range = { + startNode: textNode, + startOffset: text.length + }; + } + } + node.data = GUARD_TEXT; + return range; + } + }, { + key: 'update', + value: function update(mutations, context) { + var _this2 = this; + + mutations.forEach(function (mutation) { + if (mutation.type === 'characterData' && (mutation.target === _this2.leftGuard || mutation.target === _this2.rightGuard)) { + var range = _this2.restore(mutation.target); + if (range) context.range = range; + } + }); + } + }]); + + return Embed; +}(_parchment2.default.Embed); + +exports.default = Embed; + +/***/ }), +/* 36 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.AlignStyle = exports.AlignClass = exports.AlignAttribute = undefined; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var config = { + scope: _parchment2.default.Scope.BLOCK, + whitelist: ['right', 'center', 'justify'] +}; + +var AlignAttribute = new _parchment2.default.Attributor.Attribute('align', 'align', config); +var AlignClass = new _parchment2.default.Attributor.Class('align', 'ql-align', config); +var AlignStyle = new _parchment2.default.Attributor.Style('align', 'text-align', config); + +exports.AlignAttribute = AlignAttribute; +exports.AlignClass = AlignClass; +exports.AlignStyle = AlignStyle; + +/***/ }), +/* 37 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.BackgroundStyle = exports.BackgroundClass = undefined; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _color = __webpack_require__(26); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var BackgroundClass = new _parchment2.default.Attributor.Class('background', 'ql-bg', { + scope: _parchment2.default.Scope.INLINE +}); +var BackgroundStyle = new _color.ColorAttributor('background', 'background-color', { + scope: _parchment2.default.Scope.INLINE +}); + +exports.BackgroundClass = BackgroundClass; +exports.BackgroundStyle = BackgroundStyle; + +/***/ }), +/* 38 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DirectionStyle = exports.DirectionClass = exports.DirectionAttribute = undefined; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var config = { + scope: _parchment2.default.Scope.BLOCK, + whitelist: ['rtl'] +}; + +var DirectionAttribute = new _parchment2.default.Attributor.Attribute('direction', 'dir', config); +var DirectionClass = new _parchment2.default.Attributor.Class('direction', 'ql-direction', config); +var DirectionStyle = new _parchment2.default.Attributor.Style('direction', 'direction', config); + +exports.DirectionAttribute = DirectionAttribute; +exports.DirectionClass = DirectionClass; +exports.DirectionStyle = DirectionStyle; + +/***/ }), +/* 39 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.FontClass = exports.FontStyle = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var config = { + scope: _parchment2.default.Scope.INLINE, + whitelist: ['serif', 'monospace'] +}; + +var FontClass = new _parchment2.default.Attributor.Class('font', 'ql-font', config); + +var FontStyleAttributor = function (_Parchment$Attributor) { + _inherits(FontStyleAttributor, _Parchment$Attributor); + + function FontStyleAttributor() { + _classCallCheck(this, FontStyleAttributor); + + return _possibleConstructorReturn(this, (FontStyleAttributor.__proto__ || Object.getPrototypeOf(FontStyleAttributor)).apply(this, arguments)); + } + + _createClass(FontStyleAttributor, [{ + key: 'value', + value: function value(node) { + return _get(FontStyleAttributor.prototype.__proto__ || Object.getPrototypeOf(FontStyleAttributor.prototype), 'value', this).call(this, node).replace(/["']/g, ''); + } + }]); + + return FontStyleAttributor; +}(_parchment2.default.Attributor.Style); + +var FontStyle = new FontStyleAttributor('font', 'font-family', config); + +exports.FontStyle = FontStyle; +exports.FontClass = FontClass; + +/***/ }), +/* 40 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.SizeStyle = exports.SizeClass = undefined; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var SizeClass = new _parchment2.default.Attributor.Class('size', 'ql-size', { + scope: _parchment2.default.Scope.INLINE, + whitelist: ['small', 'large', 'huge'] +}); +var SizeStyle = new _parchment2.default.Attributor.Style('size', 'font-size', { + scope: _parchment2.default.Scope.INLINE, + whitelist: ['10px', '18px', '32px'] +}); + +exports.SizeClass = SizeClass; +exports.SizeStyle = SizeStyle; + +/***/ }), +/* 41 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +module.exports = { + 'align': { + '': __webpack_require__(76), + 'center': __webpack_require__(77), + 'right': __webpack_require__(78), + 'justify': __webpack_require__(79) + }, + 'background': __webpack_require__(80), + 'blockquote': __webpack_require__(81), + 'bold': __webpack_require__(82), + 'clean': __webpack_require__(83), + 'code': __webpack_require__(58), + 'code-block': __webpack_require__(58), + 'color': __webpack_require__(84), + 'direction': { + '': __webpack_require__(85), + 'rtl': __webpack_require__(86) + }, + 'float': { + 'center': __webpack_require__(87), + 'full': __webpack_require__(88), + 'left': __webpack_require__(89), + 'right': __webpack_require__(90) + }, + 'formula': __webpack_require__(91), + 'header': { + '1': __webpack_require__(92), + '2': __webpack_require__(93) + }, + 'italic': __webpack_require__(94), + 'image': __webpack_require__(95), + 'indent': { + '+1': __webpack_require__(96), + '-1': __webpack_require__(97) + }, + 'link': __webpack_require__(98), + 'list': { + 'ordered': __webpack_require__(99), + 'bullet': __webpack_require__(100), + 'check': __webpack_require__(101) + }, + 'script': { + 'sub': __webpack_require__(102), + 'super': __webpack_require__(103) + }, + 'strike': __webpack_require__(104), + 'underline': __webpack_require__(105), + 'video': __webpack_require__(106) +}; + +/***/ }), +/* 42 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getLastChangeIndex = exports.default = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var History = function (_Module) { + _inherits(History, _Module); + + function History(quill, options) { + _classCallCheck(this, History); + + var _this = _possibleConstructorReturn(this, (History.__proto__ || Object.getPrototypeOf(History)).call(this, quill, options)); + + _this.lastRecorded = 0; + _this.ignoreChange = false; + _this.clear(); + _this.quill.on(_quill2.default.events.EDITOR_CHANGE, function (eventName, delta, oldDelta, source) { + if (eventName !== _quill2.default.events.TEXT_CHANGE || _this.ignoreChange) return; + if (!_this.options.userOnly || source === _quill2.default.sources.USER) { + _this.record(delta, oldDelta); + } else { + _this.transform(delta); + } + }); + _this.quill.keyboard.addBinding({ key: 'Z', shortKey: true }, _this.undo.bind(_this)); + _this.quill.keyboard.addBinding({ key: 'Z', shortKey: true, shiftKey: true }, _this.redo.bind(_this)); + if (/Win/i.test(navigator.platform)) { + _this.quill.keyboard.addBinding({ key: 'Y', shortKey: true }, _this.redo.bind(_this)); + } + return _this; + } + + _createClass(History, [{ + key: 'change', + value: function change(source, dest) { + if (this.stack[source].length === 0) return; + var delta = this.stack[source].pop(); + this.stack[dest].push(delta); + this.lastRecorded = 0; + this.ignoreChange = true; + this.quill.updateContents(delta[source], _quill2.default.sources.USER); + this.ignoreChange = false; + var index = getLastChangeIndex(delta[source]); + this.quill.setSelection(index); + } + }, { + key: 'clear', + value: function clear() { + this.stack = { undo: [], redo: [] }; + } + }, { + key: 'cutoff', + value: function cutoff() { + this.lastRecorded = 0; + } + }, { + key: 'record', + value: function record(changeDelta, oldDelta) { + if (changeDelta.ops.length === 0) return; + this.stack.redo = []; + var undoDelta = this.quill.getContents().diff(oldDelta); + var timestamp = Date.now(); + if (this.lastRecorded + this.options.delay > timestamp && this.stack.undo.length > 0) { + var delta = this.stack.undo.pop(); + undoDelta = undoDelta.compose(delta.undo); + changeDelta = delta.redo.compose(changeDelta); + } else { + this.lastRecorded = timestamp; + } + this.stack.undo.push({ + redo: changeDelta, + undo: undoDelta + }); + if (this.stack.undo.length > this.options.maxStack) { + this.stack.undo.shift(); + } + } + }, { + key: 'redo', + value: function redo() { + this.change('redo', 'undo'); + } + }, { + key: 'transform', + value: function transform(delta) { + this.stack.undo.forEach(function (change) { + change.undo = delta.transform(change.undo, true); + change.redo = delta.transform(change.redo, true); + }); + this.stack.redo.forEach(function (change) { + change.undo = delta.transform(change.undo, true); + change.redo = delta.transform(change.redo, true); + }); + } + }, { + key: 'undo', + value: function undo() { + this.change('undo', 'redo'); + } + }]); + + return History; +}(_module2.default); + +History.DEFAULTS = { + delay: 1000, + maxStack: 100, + userOnly: false +}; + +function endsWithNewlineChange(delta) { + var lastOp = delta.ops[delta.ops.length - 1]; + if (lastOp == null) return false; + if (lastOp.insert != null) { + return typeof lastOp.insert === 'string' && lastOp.insert.endsWith('\n'); + } + if (lastOp.attributes != null) { + return Object.keys(lastOp.attributes).some(function (attr) { + return _parchment2.default.query(attr, _parchment2.default.Scope.BLOCK) != null; + }); + } + return false; +} + +function getLastChangeIndex(delta) { + var deleteLength = delta.reduce(function (length, op) { + length += op.delete || 0; + return length; + }, 0); + var changeIndex = delta.length() - deleteLength; + if (endsWithNewlineChange(delta)) { + changeIndex -= 1; + } + return changeIndex; +} + +exports.default = History; +exports.getLastChangeIndex = getLastChangeIndex; + +/***/ }), +/* 43 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.BaseTooltip = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _emitter = __webpack_require__(8); + +var _emitter2 = _interopRequireDefault(_emitter); + +var _keyboard = __webpack_require__(23); + +var _keyboard2 = _interopRequireDefault(_keyboard); + +var _theme = __webpack_require__(34); + +var _theme2 = _interopRequireDefault(_theme); + +var _colorPicker = __webpack_require__(59); + +var _colorPicker2 = _interopRequireDefault(_colorPicker); + +var _iconPicker = __webpack_require__(60); + +var _iconPicker2 = _interopRequireDefault(_iconPicker); + +var _picker = __webpack_require__(28); + +var _picker2 = _interopRequireDefault(_picker); + +var _tooltip = __webpack_require__(61); + +var _tooltip2 = _interopRequireDefault(_tooltip); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var ALIGNS = [false, 'center', 'right', 'justify']; + +var COLORS = ["#000000", "#e60000", "#ff9900", "#ffff00", "#008a00", "#0066cc", "#9933ff", "#ffffff", "#facccc", "#ffebcc", "#ffffcc", "#cce8cc", "#cce0f5", "#ebd6ff", "#bbbbbb", "#f06666", "#ffc266", "#ffff66", "#66b966", "#66a3e0", "#c285ff", "#888888", "#a10000", "#b26b00", "#b2b200", "#006100", "#0047b2", "#6b24b2", "#444444", "#5c0000", "#663d00", "#666600", "#003700", "#002966", "#3d1466"]; + +var FONTS = [false, 'serif', 'monospace']; + +var HEADERS = ['1', '2', '3', false]; + +var SIZES = ['small', false, 'large', 'huge']; + +var BaseTheme = function (_Theme) { + _inherits(BaseTheme, _Theme); + + function BaseTheme(quill, options) { + _classCallCheck(this, BaseTheme); + + var _this = _possibleConstructorReturn(this, (BaseTheme.__proto__ || Object.getPrototypeOf(BaseTheme)).call(this, quill, options)); + + var listener = function listener(e) { + if (!document.body.contains(quill.root)) { + return document.body.removeEventListener('click', listener); + } + if (_this.tooltip != null && !_this.tooltip.root.contains(e.target) && document.activeElement !== _this.tooltip.textbox && !_this.quill.hasFocus()) { + _this.tooltip.hide(); + } + if (_this.pickers != null) { + _this.pickers.forEach(function (picker) { + if (!picker.container.contains(e.target)) { + picker.close(); + } + }); + } + }; + quill.emitter.listenDOM('click', document.body, listener); + return _this; + } + + _createClass(BaseTheme, [{ + key: 'addModule', + value: function addModule(name) { + var module = _get(BaseTheme.prototype.__proto__ || Object.getPrototypeOf(BaseTheme.prototype), 'addModule', this).call(this, name); + if (name === 'toolbar') { + this.extendToolbar(module); + } + return module; + } + }, { + key: 'buildButtons', + value: function buildButtons(buttons, icons) { + buttons.forEach(function (button) { + var className = button.getAttribute('class') || ''; + className.split(/\s+/).forEach(function (name) { + if (!name.startsWith('ql-')) return; + name = name.slice('ql-'.length); + if (icons[name] == null) return; + if (name === 'direction') { + button.innerHTML = icons[name][''] + icons[name]['rtl']; + } else if (typeof icons[name] === 'string') { + button.innerHTML = icons[name]; + } else { + var value = button.value || ''; + if (value != null && icons[name][value]) { + button.innerHTML = icons[name][value]; + } + } + }); + }); + } + }, { + key: 'buildPickers', + value: function buildPickers(selects, icons) { + var _this2 = this; + + this.pickers = selects.map(function (select) { + if (select.classList.contains('ql-align')) { + if (select.querySelector('option') == null) { + fillSelect(select, ALIGNS); + } + return new _iconPicker2.default(select, icons.align); + } else if (select.classList.contains('ql-background') || select.classList.contains('ql-color')) { + var format = select.classList.contains('ql-background') ? 'background' : 'color'; + if (select.querySelector('option') == null) { + fillSelect(select, COLORS, format === 'background' ? '#ffffff' : '#000000'); + } + return new _colorPicker2.default(select, icons[format]); + } else { + if (select.querySelector('option') == null) { + if (select.classList.contains('ql-font')) { + fillSelect(select, FONTS); + } else if (select.classList.contains('ql-header')) { + fillSelect(select, HEADERS); + } else if (select.classList.contains('ql-size')) { + fillSelect(select, SIZES); + } + } + return new _picker2.default(select); + } + }); + var update = function update() { + _this2.pickers.forEach(function (picker) { + picker.update(); + }); + }; + this.quill.on(_emitter2.default.events.EDITOR_CHANGE, update); + } + }]); + + return BaseTheme; +}(_theme2.default); + +BaseTheme.DEFAULTS = (0, _extend2.default)(true, {}, _theme2.default.DEFAULTS, { + modules: { + toolbar: { + handlers: { + formula: function formula() { + this.quill.theme.tooltip.edit('formula'); + }, + image: function image() { + var _this3 = this; + + var fileInput = this.container.querySelector('input.ql-image[type=file]'); + if (fileInput == null) { + fileInput = document.createElement('input'); + fileInput.setAttribute('type', 'file'); + fileInput.setAttribute('accept', 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon'); + fileInput.classList.add('ql-image'); + fileInput.addEventListener('change', function () { + if (fileInput.files != null && fileInput.files[0] != null) { + var reader = new FileReader(); + reader.onload = function (e) { + var range = _this3.quill.getSelection(true); + _this3.quill.updateContents(new _quillDelta2.default().retain(range.index).delete(range.length).insert({ image: e.target.result }), _emitter2.default.sources.USER); + _this3.quill.setSelection(range.index + 1, _emitter2.default.sources.SILENT); + fileInput.value = ""; + }; + reader.readAsDataURL(fileInput.files[0]); + } + }); + this.container.appendChild(fileInput); + } + fileInput.click(); + }, + video: function video() { + this.quill.theme.tooltip.edit('video'); + } + } + } + } +}); + +var BaseTooltip = function (_Tooltip) { + _inherits(BaseTooltip, _Tooltip); + + function BaseTooltip(quill, boundsContainer) { + _classCallCheck(this, BaseTooltip); + + var _this4 = _possibleConstructorReturn(this, (BaseTooltip.__proto__ || Object.getPrototypeOf(BaseTooltip)).call(this, quill, boundsContainer)); + + _this4.textbox = _this4.root.querySelector('input[type="text"]'); + _this4.listen(); + return _this4; + } + + _createClass(BaseTooltip, [{ + key: 'listen', + value: function listen() { + var _this5 = this; + + this.textbox.addEventListener('keydown', function (event) { + if (_keyboard2.default.match(event, 'enter')) { + _this5.save(); + event.preventDefault(); + } else if (_keyboard2.default.match(event, 'escape')) { + _this5.cancel(); + event.preventDefault(); + } + }); + } + }, { + key: 'cancel', + value: function cancel() { + this.hide(); + } + }, { + key: 'edit', + value: function edit() { + var mode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'link'; + var preview = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; + + this.root.classList.remove('ql-hidden'); + this.root.classList.add('ql-editing'); + if (preview != null) { + this.textbox.value = preview; + } else if (mode !== this.root.getAttribute('data-mode')) { + this.textbox.value = ''; + } + this.position(this.quill.getBounds(this.quill.selection.savedRange)); + this.textbox.select(); + this.textbox.setAttribute('placeholder', this.textbox.getAttribute('data-' + mode) || ''); + this.root.setAttribute('data-mode', mode); + } + }, { + key: 'restoreFocus', + value: function restoreFocus() { + var scrollTop = this.quill.scrollingContainer.scrollTop; + this.quill.focus(); + this.quill.scrollingContainer.scrollTop = scrollTop; + } + }, { + key: 'save', + value: function save() { + var value = this.textbox.value; + switch (this.root.getAttribute('data-mode')) { + case 'link': + { + var scrollTop = this.quill.root.scrollTop; + if (this.linkRange) { + this.quill.formatText(this.linkRange, 'link', value, _emitter2.default.sources.USER); + delete this.linkRange; + } else { + this.restoreFocus(); + this.quill.format('link', value, _emitter2.default.sources.USER); + } + this.quill.root.scrollTop = scrollTop; + break; + } + case 'video': + { + value = extractVideoUrl(value); + } // eslint-disable-next-line no-fallthrough + case 'formula': + { + if (!value) break; + var range = this.quill.getSelection(true); + if (range != null) { + var index = range.index + range.length; + this.quill.insertEmbed(index, this.root.getAttribute('data-mode'), value, _emitter2.default.sources.USER); + if (this.root.getAttribute('data-mode') === 'formula') { + this.quill.insertText(index + 1, ' ', _emitter2.default.sources.USER); + } + this.quill.setSelection(index + 2, _emitter2.default.sources.USER); + } + break; + } + default: + } + this.textbox.value = ''; + this.hide(); + } + }]); + + return BaseTooltip; +}(_tooltip2.default); + +function extractVideoUrl(url) { + var match = url.match(/^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtube\.com\/watch.*v=([a-zA-Z0-9_-]+)/) || url.match(/^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtu\.be\/([a-zA-Z0-9_-]+)/); + if (match) { + return (match[1] || 'https') + '://www.youtube.com/embed/' + match[2] + '?showinfo=0'; + } + if (match = url.match(/^(?:(https?):\/\/)?(?:www\.)?vimeo\.com\/(\d+)/)) { + // eslint-disable-line no-cond-assign + return (match[1] || 'https') + '://player.vimeo.com/video/' + match[2] + '/'; + } + return url; +} + +function fillSelect(select, values) { + var defaultValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + + values.forEach(function (value) { + var option = document.createElement('option'); + if (value === defaultValue) { + option.setAttribute('selected', 'selected'); + } else { + option.setAttribute('value', value); + } + select.appendChild(option); + }); +} + +exports.BaseTooltip = BaseTooltip; +exports.default = BaseTheme; + +/***/ }), +/* 44 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var LinkedList = /** @class */ (function () { + function LinkedList() { + this.head = this.tail = null; + this.length = 0; + } + LinkedList.prototype.append = function () { + var nodes = []; + for (var _i = 0; _i < arguments.length; _i++) { + nodes[_i] = arguments[_i]; + } + this.insertBefore(nodes[0], null); + if (nodes.length > 1) { + this.append.apply(this, nodes.slice(1)); + } + }; + LinkedList.prototype.contains = function (node) { + var cur, next = this.iterator(); + while ((cur = next())) { + if (cur === node) + return true; + } + return false; + }; + LinkedList.prototype.insertBefore = function (node, refNode) { + if (!node) + return; + node.next = refNode; + if (refNode != null) { + node.prev = refNode.prev; + if (refNode.prev != null) { + refNode.prev.next = node; + } + refNode.prev = node; + if (refNode === this.head) { + this.head = node; + } + } + else if (this.tail != null) { + this.tail.next = node; + node.prev = this.tail; + this.tail = node; + } + else { + node.prev = null; + this.head = this.tail = node; + } + this.length += 1; + }; + LinkedList.prototype.offset = function (target) { + var index = 0, cur = this.head; + while (cur != null) { + if (cur === target) + return index; + index += cur.length(); + cur = cur.next; + } + return -1; + }; + LinkedList.prototype.remove = function (node) { + if (!this.contains(node)) + return; + if (node.prev != null) + node.prev.next = node.next; + if (node.next != null) + node.next.prev = node.prev; + if (node === this.head) + this.head = node.next; + if (node === this.tail) + this.tail = node.prev; + this.length -= 1; + }; + LinkedList.prototype.iterator = function (curNode) { + if (curNode === void 0) { curNode = this.head; } + // TODO use yield when we can + return function () { + var ret = curNode; + if (curNode != null) + curNode = curNode.next; + return ret; + }; + }; + LinkedList.prototype.find = function (index, inclusive) { + if (inclusive === void 0) { inclusive = false; } + var cur, next = this.iterator(); + while ((cur = next())) { + var length = cur.length(); + if (index < length || + (inclusive && index === length && (cur.next == null || cur.next.length() !== 0))) { + return [cur, index]; + } + index -= length; + } + return [null, 0]; + }; + LinkedList.prototype.forEach = function (callback) { + var cur, next = this.iterator(); + while ((cur = next())) { + callback(cur); + } + }; + LinkedList.prototype.forEachAt = function (index, length, callback) { + if (length <= 0) + return; + var _a = this.find(index), startNode = _a[0], offset = _a[1]; + var cur, curIndex = index - offset, next = this.iterator(startNode); + while ((cur = next()) && curIndex < index + length) { + var curLength = cur.length(); + if (index > curIndex) { + callback(cur, index - curIndex, Math.min(length, curIndex + curLength - index)); + } + else { + callback(cur, 0, Math.min(curLength, index + length - curIndex)); + } + curIndex += curLength; + } + }; + LinkedList.prototype.map = function (callback) { + return this.reduce(function (memo, cur) { + memo.push(callback(cur)); + return memo; + }, []); + }; + LinkedList.prototype.reduce = function (callback, memo) { + var cur, next = this.iterator(); + while ((cur = next())) { + memo = callback(memo, cur); + } + return memo; + }; + return LinkedList; +}()); +exports.default = LinkedList; + + +/***/ }), +/* 45 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var container_1 = __webpack_require__(17); +var Registry = __webpack_require__(1); +var OBSERVER_CONFIG = { + attributes: true, + characterData: true, + characterDataOldValue: true, + childList: true, + subtree: true, +}; +var MAX_OPTIMIZE_ITERATIONS = 100; +var ScrollBlot = /** @class */ (function (_super) { + __extends(ScrollBlot, _super); + function ScrollBlot(node) { + var _this = _super.call(this, node) || this; + _this.scroll = _this; + _this.observer = new MutationObserver(function (mutations) { + _this.update(mutations); + }); + _this.observer.observe(_this.domNode, OBSERVER_CONFIG); + _this.attach(); + return _this; + } + ScrollBlot.prototype.detach = function () { + _super.prototype.detach.call(this); + this.observer.disconnect(); + }; + ScrollBlot.prototype.deleteAt = function (index, length) { + this.update(); + if (index === 0 && length === this.length()) { + this.children.forEach(function (child) { + child.remove(); + }); + } + else { + _super.prototype.deleteAt.call(this, index, length); + } + }; + ScrollBlot.prototype.formatAt = function (index, length, name, value) { + this.update(); + _super.prototype.formatAt.call(this, index, length, name, value); + }; + ScrollBlot.prototype.insertAt = function (index, value, def) { + this.update(); + _super.prototype.insertAt.call(this, index, value, def); + }; + ScrollBlot.prototype.optimize = function (mutations, context) { + var _this = this; + if (mutations === void 0) { mutations = []; } + if (context === void 0) { context = {}; } + _super.prototype.optimize.call(this, context); + // We must modify mutations directly, cannot make copy and then modify + var records = [].slice.call(this.observer.takeRecords()); + // Array.push currently seems to be implemented by a non-tail recursive function + // so we cannot just mutations.push.apply(mutations, this.observer.takeRecords()); + while (records.length > 0) + mutations.push(records.pop()); + // TODO use WeakMap + var mark = function (blot, markParent) { + if (markParent === void 0) { markParent = true; } + if (blot == null || blot === _this) + return; + if (blot.domNode.parentNode == null) + return; + // @ts-ignore + if (blot.domNode[Registry.DATA_KEY].mutations == null) { + // @ts-ignore + blot.domNode[Registry.DATA_KEY].mutations = []; + } + if (markParent) + mark(blot.parent); + }; + var optimize = function (blot) { + // Post-order traversal + if ( + // @ts-ignore + blot.domNode[Registry.DATA_KEY] == null || + // @ts-ignore + blot.domNode[Registry.DATA_KEY].mutations == null) { + return; + } + if (blot instanceof container_1.default) { + blot.children.forEach(optimize); + } + blot.optimize(context); + }; + var remaining = mutations; + for (var i = 0; remaining.length > 0; i += 1) { + if (i >= MAX_OPTIMIZE_ITERATIONS) { + throw new Error('[Parchment] Maximum optimize iterations reached'); + } + remaining.forEach(function (mutation) { + var blot = Registry.find(mutation.target, true); + if (blot == null) + return; + if (blot.domNode === mutation.target) { + if (mutation.type === 'childList') { + mark(Registry.find(mutation.previousSibling, false)); + [].forEach.call(mutation.addedNodes, function (node) { + var child = Registry.find(node, false); + mark(child, false); + if (child instanceof container_1.default) { + child.children.forEach(function (grandChild) { + mark(grandChild, false); + }); + } + }); + } + else if (mutation.type === 'attributes') { + mark(blot.prev); + } + } + mark(blot); + }); + this.children.forEach(optimize); + remaining = [].slice.call(this.observer.takeRecords()); + records = remaining.slice(); + while (records.length > 0) + mutations.push(records.pop()); + } + }; + ScrollBlot.prototype.update = function (mutations, context) { + var _this = this; + if (context === void 0) { context = {}; } + mutations = mutations || this.observer.takeRecords(); + // TODO use WeakMap + mutations + .map(function (mutation) { + var blot = Registry.find(mutation.target, true); + if (blot == null) + return null; + // @ts-ignore + if (blot.domNode[Registry.DATA_KEY].mutations == null) { + // @ts-ignore + blot.domNode[Registry.DATA_KEY].mutations = [mutation]; + return blot; + } + else { + // @ts-ignore + blot.domNode[Registry.DATA_KEY].mutations.push(mutation); + return null; + } + }) + .forEach(function (blot) { + if (blot == null || + blot === _this || + //@ts-ignore + blot.domNode[Registry.DATA_KEY] == null) + return; + // @ts-ignore + blot.update(blot.domNode[Registry.DATA_KEY].mutations || [], context); + }); + // @ts-ignore + if (this.domNode[Registry.DATA_KEY].mutations != null) { + // @ts-ignore + _super.prototype.update.call(this, this.domNode[Registry.DATA_KEY].mutations, context); + } + this.optimize(mutations, context); + }; + ScrollBlot.blotName = 'scroll'; + ScrollBlot.defaultChild = 'block'; + ScrollBlot.scope = Registry.Scope.BLOCK_BLOT; + ScrollBlot.tagName = 'DIV'; + return ScrollBlot; +}(container_1.default)); +exports.default = ScrollBlot; + + +/***/ }), +/* 46 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var format_1 = __webpack_require__(18); +var Registry = __webpack_require__(1); +// Shallow object comparison +function isEqual(obj1, obj2) { + if (Object.keys(obj1).length !== Object.keys(obj2).length) + return false; + // @ts-ignore + for (var prop in obj1) { + // @ts-ignore + if (obj1[prop] !== obj2[prop]) + return false; + } + return true; +} +var InlineBlot = /** @class */ (function (_super) { + __extends(InlineBlot, _super); + function InlineBlot() { + return _super !== null && _super.apply(this, arguments) || this; + } + InlineBlot.formats = function (domNode) { + if (domNode.tagName === InlineBlot.tagName) + return undefined; + return _super.formats.call(this, domNode); + }; + InlineBlot.prototype.format = function (name, value) { + var _this = this; + if (name === this.statics.blotName && !value) { + this.children.forEach(function (child) { + if (!(child instanceof format_1.default)) { + child = child.wrap(InlineBlot.blotName, true); + } + _this.attributes.copy(child); + }); + this.unwrap(); + } + else { + _super.prototype.format.call(this, name, value); + } + }; + InlineBlot.prototype.formatAt = function (index, length, name, value) { + if (this.formats()[name] != null || Registry.query(name, Registry.Scope.ATTRIBUTE)) { + var blot = this.isolate(index, length); + blot.format(name, value); + } + else { + _super.prototype.formatAt.call(this, index, length, name, value); + } + }; + InlineBlot.prototype.optimize = function (context) { + _super.prototype.optimize.call(this, context); + var formats = this.formats(); + if (Object.keys(formats).length === 0) { + return this.unwrap(); // unformatted span + } + var next = this.next; + if (next instanceof InlineBlot && next.prev === this && isEqual(formats, next.formats())) { + next.moveChildren(this); + next.remove(); + } + }; + InlineBlot.blotName = 'inline'; + InlineBlot.scope = Registry.Scope.INLINE_BLOT; + InlineBlot.tagName = 'SPAN'; + return InlineBlot; +}(format_1.default)); +exports.default = InlineBlot; + + +/***/ }), +/* 47 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var format_1 = __webpack_require__(18); +var Registry = __webpack_require__(1); +var BlockBlot = /** @class */ (function (_super) { + __extends(BlockBlot, _super); + function BlockBlot() { + return _super !== null && _super.apply(this, arguments) || this; + } + BlockBlot.formats = function (domNode) { + var tagName = Registry.query(BlockBlot.blotName).tagName; + if (domNode.tagName === tagName) + return undefined; + return _super.formats.call(this, domNode); + }; + BlockBlot.prototype.format = function (name, value) { + if (Registry.query(name, Registry.Scope.BLOCK) == null) { + return; + } + else if (name === this.statics.blotName && !value) { + this.replaceWith(BlockBlot.blotName); + } + else { + _super.prototype.format.call(this, name, value); + } + }; + BlockBlot.prototype.formatAt = function (index, length, name, value) { + if (Registry.query(name, Registry.Scope.BLOCK) != null) { + this.format(name, value); + } + else { + _super.prototype.formatAt.call(this, index, length, name, value); + } + }; + BlockBlot.prototype.insertAt = function (index, value, def) { + if (def == null || Registry.query(value, Registry.Scope.INLINE) != null) { + // Insert text or inline + _super.prototype.insertAt.call(this, index, value, def); + } + else { + var after = this.split(index); + var blot = Registry.create(value, def); + after.parent.insertBefore(blot, after); + } + }; + BlockBlot.prototype.update = function (mutations, context) { + if (navigator.userAgent.match(/Trident/)) { + this.build(); + } + else { + _super.prototype.update.call(this, mutations, context); + } + }; + BlockBlot.blotName = 'block'; + BlockBlot.scope = Registry.Scope.BLOCK_BLOT; + BlockBlot.tagName = 'P'; + return BlockBlot; +}(format_1.default)); +exports.default = BlockBlot; + + +/***/ }), +/* 48 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var leaf_1 = __webpack_require__(19); +var EmbedBlot = /** @class */ (function (_super) { + __extends(EmbedBlot, _super); + function EmbedBlot() { + return _super !== null && _super.apply(this, arguments) || this; + } + EmbedBlot.formats = function (domNode) { + return undefined; + }; + EmbedBlot.prototype.format = function (name, value) { + // super.formatAt wraps, which is what we want in general, + // but this allows subclasses to overwrite for formats + // that just apply to particular embeds + _super.prototype.formatAt.call(this, 0, this.length(), name, value); + }; + EmbedBlot.prototype.formatAt = function (index, length, name, value) { + if (index === 0 && length === this.length()) { + this.format(name, value); + } + else { + _super.prototype.formatAt.call(this, index, length, name, value); + } + }; + EmbedBlot.prototype.formats = function () { + return this.statics.formats(this.domNode); + }; + return EmbedBlot; +}(leaf_1.default)); +exports.default = EmbedBlot; + + +/***/ }), +/* 49 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var leaf_1 = __webpack_require__(19); +var Registry = __webpack_require__(1); +var TextBlot = /** @class */ (function (_super) { + __extends(TextBlot, _super); + function TextBlot(node) { + var _this = _super.call(this, node) || this; + _this.text = _this.statics.value(_this.domNode); + return _this; + } + TextBlot.create = function (value) { + return document.createTextNode(value); + }; + TextBlot.value = function (domNode) { + var text = domNode.data; + // @ts-ignore + if (text['normalize']) + text = text['normalize'](); + return text; + }; + TextBlot.prototype.deleteAt = function (index, length) { + this.domNode.data = this.text = this.text.slice(0, index) + this.text.slice(index + length); + }; + TextBlot.prototype.index = function (node, offset) { + if (this.domNode === node) { + return offset; + } + return -1; + }; + TextBlot.prototype.insertAt = function (index, value, def) { + if (def == null) { + this.text = this.text.slice(0, index) + value + this.text.slice(index); + this.domNode.data = this.text; + } + else { + _super.prototype.insertAt.call(this, index, value, def); + } + }; + TextBlot.prototype.length = function () { + return this.text.length; + }; + TextBlot.prototype.optimize = function (context) { + _super.prototype.optimize.call(this, context); + this.text = this.statics.value(this.domNode); + if (this.text.length === 0) { + this.remove(); + } + else if (this.next instanceof TextBlot && this.next.prev === this) { + this.insertAt(this.length(), this.next.value()); + this.next.remove(); + } + }; + TextBlot.prototype.position = function (index, inclusive) { + if (inclusive === void 0) { inclusive = false; } + return [this.domNode, index]; + }; + TextBlot.prototype.split = function (index, force) { + if (force === void 0) { force = false; } + if (!force) { + if (index === 0) + return this; + if (index === this.length()) + return this.next; + } + var after = Registry.create(this.domNode.splitText(index)); + this.parent.insertBefore(after, this.next); + this.text = this.statics.value(this.domNode); + return after; + }; + TextBlot.prototype.update = function (mutations, context) { + var _this = this; + if (mutations.some(function (mutation) { + return mutation.type === 'characterData' && mutation.target === _this.domNode; + })) { + this.text = this.statics.value(this.domNode); + } + }; + TextBlot.prototype.value = function () { + return this.text; + }; + TextBlot.blotName = 'text'; + TextBlot.scope = Registry.Scope.INLINE_BLOT; + return TextBlot; +}(leaf_1.default)); +exports.default = TextBlot; + + +/***/ }), +/* 50 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var elem = document.createElement('div'); +elem.classList.toggle('test-class', false); +if (elem.classList.contains('test-class')) { + var _toggle = DOMTokenList.prototype.toggle; + DOMTokenList.prototype.toggle = function (token, force) { + if (arguments.length > 1 && !this.contains(token) === !force) { + return force; + } else { + return _toggle.call(this, token); + } + }; +} + +if (!String.prototype.startsWith) { + String.prototype.startsWith = function (searchString, position) { + position = position || 0; + return this.substr(position, searchString.length) === searchString; + }; +} + +if (!String.prototype.endsWith) { + String.prototype.endsWith = function (searchString, position) { + var subjectString = this.toString(); + if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { + position = subjectString.length; + } + position -= searchString.length; + var lastIndex = subjectString.indexOf(searchString, position); + return lastIndex !== -1 && lastIndex === position; + }; +} + +if (!Array.prototype.find) { + Object.defineProperty(Array.prototype, "find", { + value: function value(predicate) { + if (this === null) { + throw new TypeError('Array.prototype.find called on null or undefined'); + } + if (typeof predicate !== 'function') { + throw new TypeError('predicate must be a function'); + } + var list = Object(this); + var length = list.length >>> 0; + var thisArg = arguments[1]; + var value; + + for (var i = 0; i < length; i++) { + value = list[i]; + if (predicate.call(thisArg, value, i, list)) { + return value; + } + } + return undefined; + } + }); +} + +document.addEventListener("DOMContentLoaded", function () { + // Disable resizing in Firefox + document.execCommand("enableObjectResizing", false, false); + // Disable automatic linkifying in IE11 + document.execCommand("autoUrlDetect", false, false); +}); + +/***/ }), +/* 51 */ +/***/ (function(module, exports) { + +/** + * This library modifies the diff-patch-match library by Neil Fraser + * by removing the patch and match functionality and certain advanced + * options in the diff function. The original license is as follows: + * + * === + * + * Diff Match and Patch + * + * Copyright 2006 Google Inc. + * http://code.google.com/p/google-diff-match-patch/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * The data structure representing a diff is an array of tuples: + * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] + * which means: delete 'Hello', add 'Goodbye' and keep ' world.' + */ +var DIFF_DELETE = -1; +var DIFF_INSERT = 1; +var DIFF_EQUAL = 0; + + +/** + * Find the differences between two texts. Simplifies the problem by stripping + * any common prefix or suffix off the texts before diffing. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {Int} cursor_pos Expected edit position in text1 (optional) + * @return {Array} Array of diff tuples. + */ +function diff_main(text1, text2, cursor_pos) { + // Check for equality (speedup). + if (text1 == text2) { + if (text1) { + return [[DIFF_EQUAL, text1]]; + } + return []; + } + + // Check cursor_pos within bounds + if (cursor_pos < 0 || text1.length < cursor_pos) { + cursor_pos = null; + } + + // Trim off common prefix (speedup). + var commonlength = diff_commonPrefix(text1, text2); + var commonprefix = text1.substring(0, commonlength); + text1 = text1.substring(commonlength); + text2 = text2.substring(commonlength); + + // Trim off common suffix (speedup). + commonlength = diff_commonSuffix(text1, text2); + var commonsuffix = text1.substring(text1.length - commonlength); + text1 = text1.substring(0, text1.length - commonlength); + text2 = text2.substring(0, text2.length - commonlength); + + // Compute the diff on the middle block. + var diffs = diff_compute_(text1, text2); + + // Restore the prefix and suffix. + if (commonprefix) { + diffs.unshift([DIFF_EQUAL, commonprefix]); + } + if (commonsuffix) { + diffs.push([DIFF_EQUAL, commonsuffix]); + } + diff_cleanupMerge(diffs); + if (cursor_pos != null) { + diffs = fix_cursor(diffs, cursor_pos); + } + diffs = fix_emoji(diffs); + return diffs; +}; + + +/** + * Find the differences between two texts. Assumes that the texts do not + * have any common prefix or suffix. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @return {Array} Array of diff tuples. + */ +function diff_compute_(text1, text2) { + var diffs; + + if (!text1) { + // Just add some text (speedup). + return [[DIFF_INSERT, text2]]; + } + + if (!text2) { + // Just delete some text (speedup). + return [[DIFF_DELETE, text1]]; + } + + var longtext = text1.length > text2.length ? text1 : text2; + var shorttext = text1.length > text2.length ? text2 : text1; + var i = longtext.indexOf(shorttext); + if (i != -1) { + // Shorter text is inside the longer text (speedup). + diffs = [[DIFF_INSERT, longtext.substring(0, i)], + [DIFF_EQUAL, shorttext], + [DIFF_INSERT, longtext.substring(i + shorttext.length)]]; + // Swap insertions for deletions if diff is reversed. + if (text1.length > text2.length) { + diffs[0][0] = diffs[2][0] = DIFF_DELETE; + } + return diffs; + } + + if (shorttext.length == 1) { + // Single character string. + // After the previous speedup, the character can't be an equality. + return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; + } + + // Check to see if the problem can be split in two. + var hm = diff_halfMatch_(text1, text2); + if (hm) { + // A half-match was found, sort out the return data. + var text1_a = hm[0]; + var text1_b = hm[1]; + var text2_a = hm[2]; + var text2_b = hm[3]; + var mid_common = hm[4]; + // Send both pairs off for separate processing. + var diffs_a = diff_main(text1_a, text2_a); + var diffs_b = diff_main(text1_b, text2_b); + // Merge the results. + return diffs_a.concat([[DIFF_EQUAL, mid_common]], diffs_b); + } + + return diff_bisect_(text1, text2); +}; + + +/** + * Find the 'middle snake' of a diff, split the problem in two + * and return the recursively constructed diff. + * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @return {Array} Array of diff tuples. + * @private + */ +function diff_bisect_(text1, text2) { + // Cache the text lengths to prevent multiple calls. + var text1_length = text1.length; + var text2_length = text2.length; + var max_d = Math.ceil((text1_length + text2_length) / 2); + var v_offset = max_d; + var v_length = 2 * max_d; + var v1 = new Array(v_length); + var v2 = new Array(v_length); + // Setting all elements to -1 is faster in Chrome & Firefox than mixing + // integers and undefined. + for (var x = 0; x < v_length; x++) { + v1[x] = -1; + v2[x] = -1; + } + v1[v_offset + 1] = 0; + v2[v_offset + 1] = 0; + var delta = text1_length - text2_length; + // If the total number of characters is odd, then the front path will collide + // with the reverse path. + var front = (delta % 2 != 0); + // Offsets for start and end of k loop. + // Prevents mapping of space beyond the grid. + var k1start = 0; + var k1end = 0; + var k2start = 0; + var k2end = 0; + for (var d = 0; d < max_d; d++) { + // Walk the front path one step. + for (var k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { + var k1_offset = v_offset + k1; + var x1; + if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) { + x1 = v1[k1_offset + 1]; + } else { + x1 = v1[k1_offset - 1] + 1; + } + var y1 = x1 - k1; + while (x1 < text1_length && y1 < text2_length && + text1.charAt(x1) == text2.charAt(y1)) { + x1++; + y1++; + } + v1[k1_offset] = x1; + if (x1 > text1_length) { + // Ran off the right of the graph. + k1end += 2; + } else if (y1 > text2_length) { + // Ran off the bottom of the graph. + k1start += 2; + } else if (front) { + var k2_offset = v_offset + delta - k1; + if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) { + // Mirror x2 onto top-left coordinate system. + var x2 = text1_length - v2[k2_offset]; + if (x1 >= x2) { + // Overlap detected. + return diff_bisectSplit_(text1, text2, x1, y1); + } + } + } + } + + // Walk the reverse path one step. + for (var k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { + var k2_offset = v_offset + k2; + var x2; + if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) { + x2 = v2[k2_offset + 1]; + } else { + x2 = v2[k2_offset - 1] + 1; + } + var y2 = x2 - k2; + while (x2 < text1_length && y2 < text2_length && + text1.charAt(text1_length - x2 - 1) == + text2.charAt(text2_length - y2 - 1)) { + x2++; + y2++; + } + v2[k2_offset] = x2; + if (x2 > text1_length) { + // Ran off the left of the graph. + k2end += 2; + } else if (y2 > text2_length) { + // Ran off the top of the graph. + k2start += 2; + } else if (!front) { + var k1_offset = v_offset + delta - k2; + if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) { + var x1 = v1[k1_offset]; + var y1 = v_offset + x1 - k1_offset; + // Mirror x2 onto top-left coordinate system. + x2 = text1_length - x2; + if (x1 >= x2) { + // Overlap detected. + return diff_bisectSplit_(text1, text2, x1, y1); + } + } + } + } + } + // Diff took too long and hit the deadline or + // number of diffs equals number of characters, no commonality at all. + return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; +}; + + +/** + * Given the location of the 'middle snake', split the diff in two parts + * and recurse. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} x Index of split point in text1. + * @param {number} y Index of split point in text2. + * @return {Array} Array of diff tuples. + */ +function diff_bisectSplit_(text1, text2, x, y) { + var text1a = text1.substring(0, x); + var text2a = text2.substring(0, y); + var text1b = text1.substring(x); + var text2b = text2.substring(y); + + // Compute both diffs serially. + var diffs = diff_main(text1a, text2a); + var diffsb = diff_main(text1b, text2b); + + return diffs.concat(diffsb); +}; + + +/** + * Determine the common prefix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the start of each + * string. + */ +function diff_commonPrefix(text1, text2) { + // Quick check for common null cases. + if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) { + return 0; + } + // Binary search. + // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + var pointermin = 0; + var pointermax = Math.min(text1.length, text2.length); + var pointermid = pointermax; + var pointerstart = 0; + while (pointermin < pointermid) { + if (text1.substring(pointerstart, pointermid) == + text2.substring(pointerstart, pointermid)) { + pointermin = pointermid; + pointerstart = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; +}; + + +/** + * Determine the common suffix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of each string. + */ +function diff_commonSuffix(text1, text2) { + // Quick check for common null cases. + if (!text1 || !text2 || + text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) { + return 0; + } + // Binary search. + // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + var pointermin = 0; + var pointermax = Math.min(text1.length, text2.length); + var pointermid = pointermax; + var pointerend = 0; + while (pointermin < pointermid) { + if (text1.substring(text1.length - pointermid, text1.length - pointerend) == + text2.substring(text2.length - pointermid, text2.length - pointerend)) { + pointermin = pointermid; + pointerend = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; +}; + + +/** + * Do the two texts share a substring which is at least half the length of the + * longer text? + * This speedup can produce non-minimal diffs. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {Array.} Five element Array, containing the prefix of + * text1, the suffix of text1, the prefix of text2, the suffix of + * text2 and the common middle. Or null if there was no match. + */ +function diff_halfMatch_(text1, text2) { + var longtext = text1.length > text2.length ? text1 : text2; + var shorttext = text1.length > text2.length ? text2 : text1; + if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { + return null; // Pointless. + } + + /** + * Does a substring of shorttext exist within longtext such that the substring + * is at least half the length of longtext? + * Closure, but does not reference any external variables. + * @param {string} longtext Longer string. + * @param {string} shorttext Shorter string. + * @param {number} i Start index of quarter length substring within longtext. + * @return {Array.} Five element Array, containing the prefix of + * longtext, the suffix of longtext, the prefix of shorttext, the suffix + * of shorttext and the common middle. Or null if there was no match. + * @private + */ + function diff_halfMatchI_(longtext, shorttext, i) { + // Start with a 1/4 length substring at position i as a seed. + var seed = longtext.substring(i, i + Math.floor(longtext.length / 4)); + var j = -1; + var best_common = ''; + var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b; + while ((j = shorttext.indexOf(seed, j + 1)) != -1) { + var prefixLength = diff_commonPrefix(longtext.substring(i), + shorttext.substring(j)); + var suffixLength = diff_commonSuffix(longtext.substring(0, i), + shorttext.substring(0, j)); + if (best_common.length < suffixLength + prefixLength) { + best_common = shorttext.substring(j - suffixLength, j) + + shorttext.substring(j, j + prefixLength); + best_longtext_a = longtext.substring(0, i - suffixLength); + best_longtext_b = longtext.substring(i + prefixLength); + best_shorttext_a = shorttext.substring(0, j - suffixLength); + best_shorttext_b = shorttext.substring(j + prefixLength); + } + } + if (best_common.length * 2 >= longtext.length) { + return [best_longtext_a, best_longtext_b, + best_shorttext_a, best_shorttext_b, best_common]; + } else { + return null; + } + } + + // First check if the second quarter is the seed for a half-match. + var hm1 = diff_halfMatchI_(longtext, shorttext, + Math.ceil(longtext.length / 4)); + // Check again based on the third quarter. + var hm2 = diff_halfMatchI_(longtext, shorttext, + Math.ceil(longtext.length / 2)); + var hm; + if (!hm1 && !hm2) { + return null; + } else if (!hm2) { + hm = hm1; + } else if (!hm1) { + hm = hm2; + } else { + // Both matched. Select the longest. + hm = hm1[4].length > hm2[4].length ? hm1 : hm2; + } + + // A half-match was found, sort out the return data. + var text1_a, text1_b, text2_a, text2_b; + if (text1.length > text2.length) { + text1_a = hm[0]; + text1_b = hm[1]; + text2_a = hm[2]; + text2_b = hm[3]; + } else { + text2_a = hm[0]; + text2_b = hm[1]; + text1_a = hm[2]; + text1_b = hm[3]; + } + var mid_common = hm[4]; + return [text1_a, text1_b, text2_a, text2_b, mid_common]; +}; + + +/** + * Reorder and merge like edit sections. Merge equalities. + * Any edit section can move as long as it doesn't cross an equality. + * @param {Array} diffs Array of diff tuples. + */ +function diff_cleanupMerge(diffs) { + diffs.push([DIFF_EQUAL, '']); // Add a dummy entry at the end. + var pointer = 0; + var count_delete = 0; + var count_insert = 0; + var text_delete = ''; + var text_insert = ''; + var commonlength; + while (pointer < diffs.length) { + switch (diffs[pointer][0]) { + case DIFF_INSERT: + count_insert++; + text_insert += diffs[pointer][1]; + pointer++; + break; + case DIFF_DELETE: + count_delete++; + text_delete += diffs[pointer][1]; + pointer++; + break; + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (count_delete + count_insert > 1) { + if (count_delete !== 0 && count_insert !== 0) { + // Factor out any common prefixies. + commonlength = diff_commonPrefix(text_insert, text_delete); + if (commonlength !== 0) { + if ((pointer - count_delete - count_insert) > 0 && + diffs[pointer - count_delete - count_insert - 1][0] == + DIFF_EQUAL) { + diffs[pointer - count_delete - count_insert - 1][1] += + text_insert.substring(0, commonlength); + } else { + diffs.splice(0, 0, [DIFF_EQUAL, + text_insert.substring(0, commonlength)]); + pointer++; + } + text_insert = text_insert.substring(commonlength); + text_delete = text_delete.substring(commonlength); + } + // Factor out any common suffixies. + commonlength = diff_commonSuffix(text_insert, text_delete); + if (commonlength !== 0) { + diffs[pointer][1] = text_insert.substring(text_insert.length - + commonlength) + diffs[pointer][1]; + text_insert = text_insert.substring(0, text_insert.length - + commonlength); + text_delete = text_delete.substring(0, text_delete.length - + commonlength); + } + } + // Delete the offending records and add the merged ones. + if (count_delete === 0) { + diffs.splice(pointer - count_insert, + count_delete + count_insert, [DIFF_INSERT, text_insert]); + } else if (count_insert === 0) { + diffs.splice(pointer - count_delete, + count_delete + count_insert, [DIFF_DELETE, text_delete]); + } else { + diffs.splice(pointer - count_delete - count_insert, + count_delete + count_insert, [DIFF_DELETE, text_delete], + [DIFF_INSERT, text_insert]); + } + pointer = pointer - count_delete - count_insert + + (count_delete ? 1 : 0) + (count_insert ? 1 : 0) + 1; + } else if (pointer !== 0 && diffs[pointer - 1][0] == DIFF_EQUAL) { + // Merge this equality with the previous one. + diffs[pointer - 1][1] += diffs[pointer][1]; + diffs.splice(pointer, 1); + } else { + pointer++; + } + count_insert = 0; + count_delete = 0; + text_delete = ''; + text_insert = ''; + break; + } + } + if (diffs[diffs.length - 1][1] === '') { + diffs.pop(); // Remove the dummy entry at the end. + } + + // Second pass: look for single edits surrounded on both sides by equalities + // which can be shifted sideways to eliminate an equality. + // e.g: ABAC -> ABAC + var changes = false; + pointer = 1; + // Intentionally ignore the first and last element (don't need checking). + while (pointer < diffs.length - 1) { + if (diffs[pointer - 1][0] == DIFF_EQUAL && + diffs[pointer + 1][0] == DIFF_EQUAL) { + // This is a single edit surrounded by equalities. + if (diffs[pointer][1].substring(diffs[pointer][1].length - + diffs[pointer - 1][1].length) == diffs[pointer - 1][1]) { + // Shift the edit over the previous equality. + diffs[pointer][1] = diffs[pointer - 1][1] + + diffs[pointer][1].substring(0, diffs[pointer][1].length - + diffs[pointer - 1][1].length); + diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; + diffs.splice(pointer - 1, 1); + changes = true; + } else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) == + diffs[pointer + 1][1]) { + // Shift the edit over the next equality. + diffs[pointer - 1][1] += diffs[pointer + 1][1]; + diffs[pointer][1] = + diffs[pointer][1].substring(diffs[pointer + 1][1].length) + + diffs[pointer + 1][1]; + diffs.splice(pointer + 1, 1); + changes = true; + } + } + pointer++; + } + // If shifts were made, the diff needs reordering and another shift sweep. + if (changes) { + diff_cleanupMerge(diffs); + } +}; + + +var diff = diff_main; +diff.INSERT = DIFF_INSERT; +diff.DELETE = DIFF_DELETE; +diff.EQUAL = DIFF_EQUAL; + +module.exports = diff; + +/* + * Modify a diff such that the cursor position points to the start of a change: + * E.g. + * cursor_normalize_diff([[DIFF_EQUAL, 'abc']], 1) + * => [1, [[DIFF_EQUAL, 'a'], [DIFF_EQUAL, 'bc']]] + * cursor_normalize_diff([[DIFF_INSERT, 'new'], [DIFF_DELETE, 'xyz']], 2) + * => [2, [[DIFF_INSERT, 'new'], [DIFF_DELETE, 'xy'], [DIFF_DELETE, 'z']]] + * + * @param {Array} diffs Array of diff tuples + * @param {Int} cursor_pos Suggested edit position. Must not be out of bounds! + * @return {Array} A tuple [cursor location in the modified diff, modified diff] + */ +function cursor_normalize_diff (diffs, cursor_pos) { + if (cursor_pos === 0) { + return [DIFF_EQUAL, diffs]; + } + for (var current_pos = 0, i = 0; i < diffs.length; i++) { + var d = diffs[i]; + if (d[0] === DIFF_DELETE || d[0] === DIFF_EQUAL) { + var next_pos = current_pos + d[1].length; + if (cursor_pos === next_pos) { + return [i + 1, diffs]; + } else if (cursor_pos < next_pos) { + // copy to prevent side effects + diffs = diffs.slice(); + // split d into two diff changes + var split_pos = cursor_pos - current_pos; + var d_left = [d[0], d[1].slice(0, split_pos)]; + var d_right = [d[0], d[1].slice(split_pos)]; + diffs.splice(i, 1, d_left, d_right); + return [i + 1, diffs]; + } else { + current_pos = next_pos; + } + } + } + throw new Error('cursor_pos is out of bounds!') +} + +/* + * Modify a diff such that the edit position is "shifted" to the proposed edit location (cursor_position). + * + * Case 1) + * Check if a naive shift is possible: + * [0, X], [ 1, Y] -> [ 1, Y], [0, X] (if X + Y === Y + X) + * [0, X], [-1, Y] -> [-1, Y], [0, X] (if X + Y === Y + X) - holds same result + * Case 2) + * Check if the following shifts are possible: + * [0, 'pre'], [ 1, 'prefix'] -> [ 1, 'pre'], [0, 'pre'], [ 1, 'fix'] + * [0, 'pre'], [-1, 'prefix'] -> [-1, 'pre'], [0, 'pre'], [-1, 'fix'] + * ^ ^ + * d d_next + * + * @param {Array} diffs Array of diff tuples + * @param {Int} cursor_pos Suggested edit position. Must not be out of bounds! + * @return {Array} Array of diff tuples + */ +function fix_cursor (diffs, cursor_pos) { + var norm = cursor_normalize_diff(diffs, cursor_pos); + var ndiffs = norm[1]; + var cursor_pointer = norm[0]; + var d = ndiffs[cursor_pointer]; + var d_next = ndiffs[cursor_pointer + 1]; + + if (d == null) { + // Text was deleted from end of original string, + // cursor is now out of bounds in new string + return diffs; + } else if (d[0] !== DIFF_EQUAL) { + // A modification happened at the cursor location. + // This is the expected outcome, so we can return the original diff. + return diffs; + } else { + if (d_next != null && d[1] + d_next[1] === d_next[1] + d[1]) { + // Case 1) + // It is possible to perform a naive shift + ndiffs.splice(cursor_pointer, 2, d_next, d) + return merge_tuples(ndiffs, cursor_pointer, 2) + } else if (d_next != null && d_next[1].indexOf(d[1]) === 0) { + // Case 2) + // d[1] is a prefix of d_next[1] + // We can assume that d_next[0] !== 0, since d[0] === 0 + // Shift edit locations.. + ndiffs.splice(cursor_pointer, 2, [d_next[0], d[1]], [0, d[1]]); + var suffix = d_next[1].slice(d[1].length); + if (suffix.length > 0) { + ndiffs.splice(cursor_pointer + 2, 0, [d_next[0], suffix]); + } + return merge_tuples(ndiffs, cursor_pointer, 3) + } else { + // Not possible to perform any modification + return diffs; + } + } +} + +/* + * Check diff did not split surrogate pairs. + * Ex. [0, '\uD83D'], [-1, '\uDC36'], [1, '\uDC2F'] -> [-1, '\uD83D\uDC36'], [1, '\uD83D\uDC2F'] + * '\uD83D\uDC36' === '🐶', '\uD83D\uDC2F' === '🐯' + * + * @param {Array} diffs Array of diff tuples + * @return {Array} Array of diff tuples + */ +function fix_emoji (diffs) { + var compact = false; + var starts_with_pair_end = function(str) { + return str.charCodeAt(0) >= 0xDC00 && str.charCodeAt(0) <= 0xDFFF; + } + var ends_with_pair_start = function(str) { + return str.charCodeAt(str.length-1) >= 0xD800 && str.charCodeAt(str.length-1) <= 0xDBFF; + } + for (var i = 2; i < diffs.length; i += 1) { + if (diffs[i-2][0] === DIFF_EQUAL && ends_with_pair_start(diffs[i-2][1]) && + diffs[i-1][0] === DIFF_DELETE && starts_with_pair_end(diffs[i-1][1]) && + diffs[i][0] === DIFF_INSERT && starts_with_pair_end(diffs[i][1])) { + compact = true; + + diffs[i-1][1] = diffs[i-2][1].slice(-1) + diffs[i-1][1]; + diffs[i][1] = diffs[i-2][1].slice(-1) + diffs[i][1]; + + diffs[i-2][1] = diffs[i-2][1].slice(0, -1); + } + } + if (!compact) { + return diffs; + } + var fixed_diffs = []; + for (var i = 0; i < diffs.length; i += 1) { + if (diffs[i][1].length > 0) { + fixed_diffs.push(diffs[i]); + } + } + return fixed_diffs; +} + +/* + * Try to merge tuples with their neigbors in a given range. + * E.g. [0, 'a'], [0, 'b'] -> [0, 'ab'] + * + * @param {Array} diffs Array of diff tuples. + * @param {Int} start Position of the first element to merge (diffs[start] is also merged with diffs[start - 1]). + * @param {Int} length Number of consecutive elements to check. + * @return {Array} Array of merged diff tuples. + */ +function merge_tuples (diffs, start, length) { + // Check from (start-1) to (start+length). + for (var i = start + length - 1; i >= 0 && i >= start - 1; i--) { + if (i + 1 < diffs.length) { + var left_d = diffs[i]; + var right_d = diffs[i+1]; + if (left_d[0] === right_d[1]) { + diffs.splice(i, 2, [left_d[0], left_d[1] + right_d[1]]); + } + } + } + return diffs; +} + + +/***/ }), +/* 52 */ +/***/ (function(module, exports) { + +exports = module.exports = typeof Object.keys === 'function' + ? Object.keys : shim; + +exports.shim = shim; +function shim (obj) { + var keys = []; + for (var key in obj) keys.push(key); + return keys; +} + + +/***/ }), +/* 53 */ +/***/ (function(module, exports) { + +var supportsArgumentsClass = (function(){ + return Object.prototype.toString.call(arguments) +})() == '[object Arguments]'; + +exports = module.exports = supportsArgumentsClass ? supported : unsupported; + +exports.supported = supported; +function supported(object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +}; + +exports.unsupported = unsupported; +function unsupported(object){ + return object && + typeof object == 'object' && + typeof object.length == 'number' && + Object.prototype.hasOwnProperty.call(object, 'callee') && + !Object.prototype.propertyIsEnumerable.call(object, 'callee') || + false; +}; + + +/***/ }), +/* 54 */ +/***/ (function(module, exports) { + +'use strict'; + +var has = Object.prototype.hasOwnProperty + , prefix = '~'; + +/** + * Constructor to create a storage for our `EE` objects. + * An `Events` instance is a plain object whose properties are event names. + * + * @constructor + * @api private + */ +function Events() {} + +// +// We try to not inherit from `Object.prototype`. In some engines creating an +// instance in this way is faster than calling `Object.create(null)` directly. +// If `Object.create(null)` is not supported we prefix the event names with a +// character to make sure that the built-in object properties are not +// overridden or used as an attack vector. +// +if (Object.create) { + Events.prototype = Object.create(null); + + // + // This hack is needed because the `__proto__` property is still inherited in + // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5. + // + if (!new Events().__proto__) prefix = false; +} + +/** + * Representation of a single event listener. + * + * @param {Function} fn The listener function. + * @param {Mixed} context The context to invoke the listener with. + * @param {Boolean} [once=false] Specify if the listener is a one-time listener. + * @constructor + * @api private + */ +function EE(fn, context, once) { + this.fn = fn; + this.context = context; + this.once = once || false; +} + +/** + * Minimal `EventEmitter` interface that is molded against the Node.js + * `EventEmitter` interface. + * + * @constructor + * @api public + */ +function EventEmitter() { + this._events = new Events(); + this._eventsCount = 0; +} + +/** + * Return an array listing the events for which the emitter has registered + * listeners. + * + * @returns {Array} + * @api public + */ +EventEmitter.prototype.eventNames = function eventNames() { + var names = [] + , events + , name; + + if (this._eventsCount === 0) return names; + + for (name in (events = this._events)) { + if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); + } + + if (Object.getOwnPropertySymbols) { + return names.concat(Object.getOwnPropertySymbols(events)); + } + + return names; +}; + +/** + * Return the listeners registered for a given event. + * + * @param {String|Symbol} event The event name. + * @param {Boolean} exists Only check if there are listeners. + * @returns {Array|Boolean} + * @api public + */ +EventEmitter.prototype.listeners = function listeners(event, exists) { + var evt = prefix ? prefix + event : event + , available = this._events[evt]; + + if (exists) return !!available; + if (!available) return []; + if (available.fn) return [available.fn]; + + for (var i = 0, l = available.length, ee = new Array(l); i < l; i++) { + ee[i] = available[i].fn; + } + + return ee; +}; + +/** + * Calls each of the listeners registered for a given event. + * + * @param {String|Symbol} event The event name. + * @returns {Boolean} `true` if the event had listeners, else `false`. + * @api public + */ +EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { + var evt = prefix ? prefix + event : event; + + if (!this._events[evt]) return false; + + var listeners = this._events[evt] + , len = arguments.length + , args + , i; + + if (listeners.fn) { + if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); + + switch (len) { + case 1: return listeners.fn.call(listeners.context), true; + case 2: return listeners.fn.call(listeners.context, a1), true; + case 3: return listeners.fn.call(listeners.context, a1, a2), true; + case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; + case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; + case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; + } + + for (i = 1, args = new Array(len -1); i < len; i++) { + args[i - 1] = arguments[i]; + } + + listeners.fn.apply(listeners.context, args); + } else { + var length = listeners.length + , j; + + for (i = 0; i < length; i++) { + if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); + + switch (len) { + case 1: listeners[i].fn.call(listeners[i].context); break; + case 2: listeners[i].fn.call(listeners[i].context, a1); break; + case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; + case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; + default: + if (!args) for (j = 1, args = new Array(len -1); j < len; j++) { + args[j - 1] = arguments[j]; + } + + listeners[i].fn.apply(listeners[i].context, args); + } + } + } + + return true; +}; + +/** + * Add a listener for a given event. + * + * @param {String|Symbol} event The event name. + * @param {Function} fn The listener function. + * @param {Mixed} [context=this] The context to invoke the listener with. + * @returns {EventEmitter} `this`. + * @api public + */ +EventEmitter.prototype.on = function on(event, fn, context) { + var listener = new EE(fn, context || this) + , evt = prefix ? prefix + event : event; + + if (!this._events[evt]) this._events[evt] = listener, this._eventsCount++; + else if (!this._events[evt].fn) this._events[evt].push(listener); + else this._events[evt] = [this._events[evt], listener]; + + return this; +}; + +/** + * Add a one-time listener for a given event. + * + * @param {String|Symbol} event The event name. + * @param {Function} fn The listener function. + * @param {Mixed} [context=this] The context to invoke the listener with. + * @returns {EventEmitter} `this`. + * @api public + */ +EventEmitter.prototype.once = function once(event, fn, context) { + var listener = new EE(fn, context || this, true) + , evt = prefix ? prefix + event : event; + + if (!this._events[evt]) this._events[evt] = listener, this._eventsCount++; + else if (!this._events[evt].fn) this._events[evt].push(listener); + else this._events[evt] = [this._events[evt], listener]; + + return this; +}; + +/** + * Remove the listeners of a given event. + * + * @param {String|Symbol} event The event name. + * @param {Function} fn Only remove the listeners that match this function. + * @param {Mixed} context Only remove the listeners that have this context. + * @param {Boolean} once Only remove one-time listeners. + * @returns {EventEmitter} `this`. + * @api public + */ +EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { + var evt = prefix ? prefix + event : event; + + if (!this._events[evt]) return this; + if (!fn) { + if (--this._eventsCount === 0) this._events = new Events(); + else delete this._events[evt]; + return this; + } + + var listeners = this._events[evt]; + + if (listeners.fn) { + if ( + listeners.fn === fn + && (!once || listeners.once) + && (!context || listeners.context === context) + ) { + if (--this._eventsCount === 0) this._events = new Events(); + else delete this._events[evt]; + } + } else { + for (var i = 0, events = [], length = listeners.length; i < length; i++) { + if ( + listeners[i].fn !== fn + || (once && !listeners[i].once) + || (context && listeners[i].context !== context) + ) { + events.push(listeners[i]); + } + } + + // + // Reset the array, or remove it completely if we have no more listeners. + // + if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; + else if (--this._eventsCount === 0) this._events = new Events(); + else delete this._events[evt]; + } + + return this; +}; + +/** + * Remove all listeners, or those of the specified event. + * + * @param {String|Symbol} [event] The event name. + * @returns {EventEmitter} `this`. + * @api public + */ +EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { + var evt; + + if (event) { + evt = prefix ? prefix + event : event; + if (this._events[evt]) { + if (--this._eventsCount === 0) this._events = new Events(); + else delete this._events[evt]; + } + } else { + this._events = new Events(); + this._eventsCount = 0; + } + + return this; +}; + +// +// Alias methods names because people roll like that. +// +EventEmitter.prototype.off = EventEmitter.prototype.removeListener; +EventEmitter.prototype.addListener = EventEmitter.prototype.on; + +// +// This function doesn't apply anymore. +// +EventEmitter.prototype.setMaxListeners = function setMaxListeners() { + return this; +}; + +// +// Expose the prefix. +// +EventEmitter.prefixed = prefix; + +// +// Allow `EventEmitter` to be imported as module namespace. +// +EventEmitter.EventEmitter = EventEmitter; + +// +// Expose the module. +// +if ('undefined' !== typeof module) { + module.exports = EventEmitter; +} + + +/***/ }), +/* 55 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.matchText = exports.matchSpacing = exports.matchNewline = exports.matchBlot = exports.matchAttributor = exports.default = undefined; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _extend2 = __webpack_require__(3); + +var _extend3 = _interopRequireDefault(_extend2); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _logger = __webpack_require__(10); + +var _logger2 = _interopRequireDefault(_logger); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +var _align = __webpack_require__(36); + +var _background = __webpack_require__(37); + +var _code = __webpack_require__(13); + +var _code2 = _interopRequireDefault(_code); + +var _color = __webpack_require__(26); + +var _direction = __webpack_require__(38); + +var _font = __webpack_require__(39); + +var _size = __webpack_require__(40); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var debug = (0, _logger2.default)('quill:clipboard'); + +var DOM_KEY = '__ql-matcher'; + +var CLIPBOARD_CONFIG = [[Node.TEXT_NODE, matchText], [Node.TEXT_NODE, matchNewline], ['br', matchBreak], [Node.ELEMENT_NODE, matchNewline], [Node.ELEMENT_NODE, matchBlot], [Node.ELEMENT_NODE, matchSpacing], [Node.ELEMENT_NODE, matchAttributor], [Node.ELEMENT_NODE, matchStyles], ['li', matchIndent], ['b', matchAlias.bind(matchAlias, 'bold')], ['i', matchAlias.bind(matchAlias, 'italic')], ['style', matchIgnore]]; + +var ATTRIBUTE_ATTRIBUTORS = [_align.AlignAttribute, _direction.DirectionAttribute].reduce(function (memo, attr) { + memo[attr.keyName] = attr; + return memo; +}, {}); + +var STYLE_ATTRIBUTORS = [_align.AlignStyle, _background.BackgroundStyle, _color.ColorStyle, _direction.DirectionStyle, _font.FontStyle, _size.SizeStyle].reduce(function (memo, attr) { + memo[attr.keyName] = attr; + return memo; +}, {}); + +var Clipboard = function (_Module) { + _inherits(Clipboard, _Module); + + function Clipboard(quill, options) { + _classCallCheck(this, Clipboard); + + var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this, quill, options)); + + _this.quill.root.addEventListener('paste', _this.onPaste.bind(_this)); + _this.container = _this.quill.addContainer('ql-clipboard'); + _this.container.setAttribute('contenteditable', true); + _this.container.setAttribute('tabindex', -1); + _this.matchers = []; + CLIPBOARD_CONFIG.concat(_this.options.matchers).forEach(function (_ref) { + var _ref2 = _slicedToArray(_ref, 2), + selector = _ref2[0], + matcher = _ref2[1]; + + if (!options.matchVisual && matcher === matchSpacing) return; + _this.addMatcher(selector, matcher); + }); + return _this; + } + + _createClass(Clipboard, [{ + key: 'addMatcher', + value: function addMatcher(selector, matcher) { + this.matchers.push([selector, matcher]); + } + }, { + key: 'convert', + value: function convert(html) { + if (typeof html === 'string') { + this.container.innerHTML = html.replace(/\>\r?\n +\<'); // Remove spaces between tags + return this.convert(); + } + var formats = this.quill.getFormat(this.quill.selection.savedRange.index); + if (formats[_code2.default.blotName]) { + var text = this.container.innerText; + this.container.innerHTML = ''; + return new _quillDelta2.default().insert(text, _defineProperty({}, _code2.default.blotName, formats[_code2.default.blotName])); + } + + var _prepareMatching = this.prepareMatching(), + _prepareMatching2 = _slicedToArray(_prepareMatching, 2), + elementMatchers = _prepareMatching2[0], + textMatchers = _prepareMatching2[1]; + + var delta = traverse(this.container, elementMatchers, textMatchers); + // Remove trailing newline + if (deltaEndsWith(delta, '\n') && delta.ops[delta.ops.length - 1].attributes == null) { + delta = delta.compose(new _quillDelta2.default().retain(delta.length() - 1).delete(1)); + } + debug.log('convert', this.container.innerHTML, delta); + this.container.innerHTML = ''; + return delta; + } + }, { + key: 'dangerouslyPasteHTML', + value: function dangerouslyPasteHTML(index, html) { + var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _quill2.default.sources.API; + + if (typeof index === 'string') { + this.quill.setContents(this.convert(index), html); + this.quill.setSelection(0, _quill2.default.sources.SILENT); + } else { + var paste = this.convert(html); + this.quill.updateContents(new _quillDelta2.default().retain(index).concat(paste), source); + this.quill.setSelection(index + paste.length(), _quill2.default.sources.SILENT); + } + } + }, { + key: 'onPaste', + value: function onPaste(e) { + var _this2 = this; + + if (e.defaultPrevented || !this.quill.isEnabled()) return; + var range = this.quill.getSelection(); + var delta = new _quillDelta2.default().retain(range.index); + var scrollTop = this.quill.scrollingContainer.scrollTop; + this.container.focus(); + this.quill.selection.update(_quill2.default.sources.SILENT); + setTimeout(function () { + delta = delta.concat(_this2.convert()).delete(range.length); + _this2.quill.updateContents(delta, _quill2.default.sources.USER); + // range.length contributes to delta.length() + _this2.quill.setSelection(delta.length() - range.length, _quill2.default.sources.SILENT); + _this2.quill.scrollingContainer.scrollTop = scrollTop; + _this2.quill.focus(); + }, 1); + } + }, { + key: 'prepareMatching', + value: function prepareMatching() { + var _this3 = this; + + var elementMatchers = [], + textMatchers = []; + this.matchers.forEach(function (pair) { + var _pair = _slicedToArray(pair, 2), + selector = _pair[0], + matcher = _pair[1]; + + switch (selector) { + case Node.TEXT_NODE: + textMatchers.push(matcher); + break; + case Node.ELEMENT_NODE: + elementMatchers.push(matcher); + break; + default: + [].forEach.call(_this3.container.querySelectorAll(selector), function (node) { + // TODO use weakmap + node[DOM_KEY] = node[DOM_KEY] || []; + node[DOM_KEY].push(matcher); + }); + break; + } + }); + return [elementMatchers, textMatchers]; + } + }]); + + return Clipboard; +}(_module2.default); + +Clipboard.DEFAULTS = { + matchers: [], + matchVisual: true +}; + +function applyFormat(delta, format, value) { + if ((typeof format === 'undefined' ? 'undefined' : _typeof(format)) === 'object') { + return Object.keys(format).reduce(function (delta, key) { + return applyFormat(delta, key, format[key]); + }, delta); + } else { + return delta.reduce(function (delta, op) { + if (op.attributes && op.attributes[format]) { + return delta.push(op); + } else { + return delta.insert(op.insert, (0, _extend3.default)({}, _defineProperty({}, format, value), op.attributes)); + } + }, new _quillDelta2.default()); + } +} + +function computeStyle(node) { + if (node.nodeType !== Node.ELEMENT_NODE) return {}; + var DOM_KEY = '__ql-computed-style'; + return node[DOM_KEY] || (node[DOM_KEY] = window.getComputedStyle(node)); +} + +function deltaEndsWith(delta, text) { + var endText = ""; + for (var i = delta.ops.length - 1; i >= 0 && endText.length < text.length; --i) { + var op = delta.ops[i]; + if (typeof op.insert !== 'string') break; + endText = op.insert + endText; + } + return endText.slice(-1 * text.length) === text; +} + +function isLine(node) { + if (node.childNodes.length === 0) return false; // Exclude embed blocks + var style = computeStyle(node); + return ['block', 'list-item'].indexOf(style.display) > -1; +} + +function traverse(node, elementMatchers, textMatchers) { + // Post-order + if (node.nodeType === node.TEXT_NODE) { + return textMatchers.reduce(function (delta, matcher) { + return matcher(node, delta); + }, new _quillDelta2.default()); + } else if (node.nodeType === node.ELEMENT_NODE) { + return [].reduce.call(node.childNodes || [], function (delta, childNode) { + var childrenDelta = traverse(childNode, elementMatchers, textMatchers); + if (childNode.nodeType === node.ELEMENT_NODE) { + childrenDelta = elementMatchers.reduce(function (childrenDelta, matcher) { + return matcher(childNode, childrenDelta); + }, childrenDelta); + childrenDelta = (childNode[DOM_KEY] || []).reduce(function (childrenDelta, matcher) { + return matcher(childNode, childrenDelta); + }, childrenDelta); + } + return delta.concat(childrenDelta); + }, new _quillDelta2.default()); + } else { + return new _quillDelta2.default(); + } +} + +function matchAlias(format, node, delta) { + return applyFormat(delta, format, true); +} + +function matchAttributor(node, delta) { + var attributes = _parchment2.default.Attributor.Attribute.keys(node); + var classes = _parchment2.default.Attributor.Class.keys(node); + var styles = _parchment2.default.Attributor.Style.keys(node); + var formats = {}; + attributes.concat(classes).concat(styles).forEach(function (name) { + var attr = _parchment2.default.query(name, _parchment2.default.Scope.ATTRIBUTE); + if (attr != null) { + formats[attr.attrName] = attr.value(node); + if (formats[attr.attrName]) return; + } + attr = ATTRIBUTE_ATTRIBUTORS[name]; + if (attr != null && (attr.attrName === name || attr.keyName === name)) { + formats[attr.attrName] = attr.value(node) || undefined; + } + attr = STYLE_ATTRIBUTORS[name]; + if (attr != null && (attr.attrName === name || attr.keyName === name)) { + attr = STYLE_ATTRIBUTORS[name]; + formats[attr.attrName] = attr.value(node) || undefined; + } + }); + if (Object.keys(formats).length > 0) { + delta = applyFormat(delta, formats); + } + return delta; +} + +function matchBlot(node, delta) { + var match = _parchment2.default.query(node); + if (match == null) return delta; + if (match.prototype instanceof _parchment2.default.Embed) { + var embed = {}; + var value = match.value(node); + if (value != null) { + embed[match.blotName] = value; + delta = new _quillDelta2.default().insert(embed, match.formats(node)); + } + } else if (typeof match.formats === 'function') { + delta = applyFormat(delta, match.blotName, match.formats(node)); + } + return delta; +} + +function matchBreak(node, delta) { + if (!deltaEndsWith(delta, '\n')) { + delta.insert('\n'); + } + return delta; +} + +function matchIgnore() { + return new _quillDelta2.default(); +} + +function matchIndent(node, delta) { + var match = _parchment2.default.query(node); + if (match == null || match.blotName !== 'list-item' || !deltaEndsWith(delta, '\n')) { + return delta; + } + var indent = -1, + parent = node.parentNode; + while (!parent.classList.contains('ql-clipboard')) { + if ((_parchment2.default.query(parent) || {}).blotName === 'list') { + indent += 1; + } + parent = parent.parentNode; + } + if (indent <= 0) return delta; + return delta.compose(new _quillDelta2.default().retain(delta.length() - 1).retain(1, { indent: indent })); +} + +function matchNewline(node, delta) { + if (!deltaEndsWith(delta, '\n')) { + if (isLine(node) || delta.length() > 0 && node.nextSibling && isLine(node.nextSibling)) { + delta.insert('\n'); + } + } + return delta; +} + +function matchSpacing(node, delta) { + if (isLine(node) && node.nextElementSibling != null && !deltaEndsWith(delta, '\n\n')) { + var nodeHeight = node.offsetHeight + parseFloat(computeStyle(node).marginTop) + parseFloat(computeStyle(node).marginBottom); + if (node.nextElementSibling.offsetTop > node.offsetTop + nodeHeight * 1.5) { + delta.insert('\n'); + } + } + return delta; +} + +function matchStyles(node, delta) { + var formats = {}; + var style = node.style || {}; + if (style.fontStyle && computeStyle(node).fontStyle === 'italic') { + formats.italic = true; + } + if (style.fontWeight && (computeStyle(node).fontWeight.startsWith('bold') || parseInt(computeStyle(node).fontWeight) >= 700)) { + formats.bold = true; + } + if (Object.keys(formats).length > 0) { + delta = applyFormat(delta, formats); + } + if (parseFloat(style.textIndent || 0) > 0) { + // Could be 0.5in + delta = new _quillDelta2.default().insert('\t').concat(delta); + } + return delta; +} + +function matchText(node, delta) { + var text = node.data; + // Word represents empty line with   + if (node.parentNode.tagName === 'O:P') { + return delta.insert(text.trim()); + } + if (text.trim().length === 0 && node.parentNode.classList.contains('ql-clipboard')) { + return delta; + } + if (!computeStyle(node.parentNode).whiteSpace.startsWith('pre')) { + // eslint-disable-next-line func-style + var replacer = function replacer(collapse, match) { + match = match.replace(/[^\u00a0]/g, ''); // \u00a0 is nbsp; + return match.length < 1 && collapse ? ' ' : match; + }; + text = text.replace(/\r\n/g, ' ').replace(/\n/g, ' '); + text = text.replace(/\s\s+/g, replacer.bind(replacer, true)); // collapse whitespace + if (node.previousSibling == null && isLine(node.parentNode) || node.previousSibling != null && isLine(node.previousSibling)) { + text = text.replace(/^\s+/, replacer.bind(replacer, false)); + } + if (node.nextSibling == null && isLine(node.parentNode) || node.nextSibling != null && isLine(node.nextSibling)) { + text = text.replace(/\s+$/, replacer.bind(replacer, false)); + } + } + return delta.insert(text); +} + +exports.default = Clipboard; +exports.matchAttributor = matchAttributor; +exports.matchBlot = matchBlot; +exports.matchNewline = matchNewline; +exports.matchSpacing = matchSpacing; +exports.matchText = matchText; + +/***/ }), +/* 56 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Bold = function (_Inline) { + _inherits(Bold, _Inline); + + function Bold() { + _classCallCheck(this, Bold); + + return _possibleConstructorReturn(this, (Bold.__proto__ || Object.getPrototypeOf(Bold)).apply(this, arguments)); + } + + _createClass(Bold, [{ + key: 'optimize', + value: function optimize(context) { + _get(Bold.prototype.__proto__ || Object.getPrototypeOf(Bold.prototype), 'optimize', this).call(this, context); + if (this.domNode.tagName !== this.statics.tagName[0]) { + this.replaceWith(this.statics.blotName); + } + } + }], [{ + key: 'create', + value: function create() { + return _get(Bold.__proto__ || Object.getPrototypeOf(Bold), 'create', this).call(this); + } + }, { + key: 'formats', + value: function formats() { + return true; + } + }]); + + return Bold; +}(_inline2.default); + +Bold.blotName = 'bold'; +Bold.tagName = ['STRONG', 'B']; + +exports.default = Bold; + +/***/ }), +/* 57 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.addControls = exports.default = undefined; + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _quillDelta = __webpack_require__(2); + +var _quillDelta2 = _interopRequireDefault(_quillDelta); + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _logger = __webpack_require__(10); + +var _logger2 = _interopRequireDefault(_logger); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var debug = (0, _logger2.default)('quill:toolbar'); + +var Toolbar = function (_Module) { + _inherits(Toolbar, _Module); + + function Toolbar(quill, options) { + _classCallCheck(this, Toolbar); + + var _this = _possibleConstructorReturn(this, (Toolbar.__proto__ || Object.getPrototypeOf(Toolbar)).call(this, quill, options)); + + if (Array.isArray(_this.options.container)) { + var container = document.createElement('div'); + addControls(container, _this.options.container); + quill.container.parentNode.insertBefore(container, quill.container); + _this.container = container; + } else if (typeof _this.options.container === 'string') { + _this.container = document.querySelector(_this.options.container); + } else { + _this.container = _this.options.container; + } + if (!(_this.container instanceof HTMLElement)) { + var _ret; + + return _ret = debug.error('Container required for toolbar', _this.options), _possibleConstructorReturn(_this, _ret); + } + _this.container.classList.add('ql-toolbar'); + _this.controls = []; + _this.handlers = {}; + Object.keys(_this.options.handlers).forEach(function (format) { + _this.addHandler(format, _this.options.handlers[format]); + }); + [].forEach.call(_this.container.querySelectorAll('button, select'), function (input) { + _this.attach(input); + }); + _this.quill.on(_quill2.default.events.EDITOR_CHANGE, function (type, range) { + if (type === _quill2.default.events.SELECTION_CHANGE) { + _this.update(range); + } + }); + _this.quill.on(_quill2.default.events.SCROLL_OPTIMIZE, function () { + var _this$quill$selection = _this.quill.selection.getRange(), + _this$quill$selection2 = _slicedToArray(_this$quill$selection, 1), + range = _this$quill$selection2[0]; // quill.getSelection triggers update + + + _this.update(range); + }); + return _this; + } + + _createClass(Toolbar, [{ + key: 'addHandler', + value: function addHandler(format, handler) { + this.handlers[format] = handler; + } + }, { + key: 'attach', + value: function attach(input) { + var _this2 = this; + + var format = [].find.call(input.classList, function (className) { + return className.indexOf('ql-') === 0; + }); + if (!format) return; + format = format.slice('ql-'.length); + if (input.tagName === 'BUTTON') { + input.setAttribute('type', 'button'); + } + if (this.handlers[format] == null) { + if (this.quill.scroll.whitelist != null && this.quill.scroll.whitelist[format] == null) { + debug.warn('ignoring attaching to disabled format', format, input); + return; + } + if (_parchment2.default.query(format) == null) { + debug.warn('ignoring attaching to nonexistent format', format, input); + return; + } + } + var eventName = input.tagName === 'SELECT' ? 'change' : 'click'; + input.addEventListener(eventName, function (e) { + var value = void 0; + if (input.tagName === 'SELECT') { + if (input.selectedIndex < 0) return; + var selected = input.options[input.selectedIndex]; + if (selected.hasAttribute('selected')) { + value = false; + } else { + value = selected.value || false; + } + } else { + if (input.classList.contains('ql-active')) { + value = false; + } else { + value = input.value || !input.hasAttribute('value'); + } + e.preventDefault(); + } + _this2.quill.focus(); + + var _quill$selection$getR = _this2.quill.selection.getRange(), + _quill$selection$getR2 = _slicedToArray(_quill$selection$getR, 1), + range = _quill$selection$getR2[0]; + + if (_this2.handlers[format] != null) { + _this2.handlers[format].call(_this2, value); + } else if (_parchment2.default.query(format).prototype instanceof _parchment2.default.Embed) { + value = prompt('Enter ' + format); + if (!value) return; + _this2.quill.updateContents(new _quillDelta2.default().retain(range.index).delete(range.length).insert(_defineProperty({}, format, value)), _quill2.default.sources.USER); + } else { + _this2.quill.format(format, value, _quill2.default.sources.USER); + } + _this2.update(range); + }); + // TODO use weakmap + this.controls.push([format, input]); + } + }, { + key: 'update', + value: function update(range) { + var formats = range == null ? {} : this.quill.getFormat(range); + this.controls.forEach(function (pair) { + var _pair = _slicedToArray(pair, 2), + format = _pair[0], + input = _pair[1]; + + if (input.tagName === 'SELECT') { + var option = void 0; + if (range == null) { + option = null; + } else if (formats[format] == null) { + option = input.querySelector('option[selected]'); + } else if (!Array.isArray(formats[format])) { + var value = formats[format]; + if (typeof value === 'string') { + value = value.replace(/\"/g, '\\"'); + } + option = input.querySelector('option[value="' + value + '"]'); + } + if (option == null) { + input.value = ''; // TODO make configurable? + input.selectedIndex = -1; + } else { + option.selected = true; + } + } else { + if (range == null) { + input.classList.remove('ql-active'); + } else if (input.hasAttribute('value')) { + // both being null should match (default values) + // '1' should match with 1 (headers) + var isActive = formats[format] === input.getAttribute('value') || formats[format] != null && formats[format].toString() === input.getAttribute('value') || formats[format] == null && !input.getAttribute('value'); + input.classList.toggle('ql-active', isActive); + } else { + input.classList.toggle('ql-active', formats[format] != null); + } + } + }); + } + }]); + + return Toolbar; +}(_module2.default); + +Toolbar.DEFAULTS = {}; + +function addButton(container, format, value) { + var input = document.createElement('button'); + input.setAttribute('type', 'button'); + input.classList.add('ql-' + format); + if (value != null) { + input.value = value; + } + container.appendChild(input); +} + +function addControls(container, groups) { + if (!Array.isArray(groups[0])) { + groups = [groups]; + } + groups.forEach(function (controls) { + var group = document.createElement('span'); + group.classList.add('ql-formats'); + controls.forEach(function (control) { + if (typeof control === 'string') { + addButton(group, control); + } else { + var format = Object.keys(control)[0]; + var value = control[format]; + if (Array.isArray(value)) { + addSelect(group, format, value); + } else { + addButton(group, format, value); + } + } + }); + container.appendChild(group); + }); +} + +function addSelect(container, format, values) { + var input = document.createElement('select'); + input.classList.add('ql-' + format); + values.forEach(function (value) { + var option = document.createElement('option'); + if (value !== false) { + option.setAttribute('value', value); + } else { + option.setAttribute('selected', 'selected'); + } + input.appendChild(option); + }); + container.appendChild(input); +} + +Toolbar.DEFAULTS = { + container: null, + handlers: { + clean: function clean() { + var _this3 = this; + + var range = this.quill.getSelection(); + if (range == null) return; + if (range.length == 0) { + var formats = this.quill.getFormat(); + Object.keys(formats).forEach(function (name) { + // Clean functionality in existing apps only clean inline formats + if (_parchment2.default.query(name, _parchment2.default.Scope.INLINE) != null) { + _this3.quill.format(name, false); + } + }); + } else { + this.quill.removeFormat(range, _quill2.default.sources.USER); + } + }, + direction: function direction(value) { + var align = this.quill.getFormat()['align']; + if (value === 'rtl' && align == null) { + this.quill.format('align', 'right', _quill2.default.sources.USER); + } else if (!value && align === 'right') { + this.quill.format('align', false, _quill2.default.sources.USER); + } + this.quill.format('direction', value, _quill2.default.sources.USER); + }, + indent: function indent(value) { + var range = this.quill.getSelection(); + var formats = this.quill.getFormat(range); + var indent = parseInt(formats.indent || 0); + if (value === '+1' || value === '-1') { + var modifier = value === '+1' ? 1 : -1; + if (formats.direction === 'rtl') modifier *= -1; + this.quill.format('indent', indent + modifier, _quill2.default.sources.USER); + } + }, + link: function link(value) { + if (value === true) { + value = prompt('Enter link URL:'); + } + this.quill.format('link', value, _quill2.default.sources.USER); + }, + list: function list(value) { + var range = this.quill.getSelection(); + var formats = this.quill.getFormat(range); + if (value === 'check') { + if (formats['list'] === 'checked' || formats['list'] === 'unchecked') { + this.quill.format('list', false, _quill2.default.sources.USER); + } else { + this.quill.format('list', 'unchecked', _quill2.default.sources.USER); + } + } else { + this.quill.format('list', value, _quill2.default.sources.USER); + } + } + } +}; + +exports.default = Toolbar; +exports.addControls = addControls; + +/***/ }), +/* 58 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 59 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _picker = __webpack_require__(28); + +var _picker2 = _interopRequireDefault(_picker); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var ColorPicker = function (_Picker) { + _inherits(ColorPicker, _Picker); + + function ColorPicker(select, label) { + _classCallCheck(this, ColorPicker); + + var _this = _possibleConstructorReturn(this, (ColorPicker.__proto__ || Object.getPrototypeOf(ColorPicker)).call(this, select)); + + _this.label.innerHTML = label; + _this.container.classList.add('ql-color-picker'); + [].slice.call(_this.container.querySelectorAll('.ql-picker-item'), 0, 7).forEach(function (item) { + item.classList.add('ql-primary'); + }); + return _this; + } + + _createClass(ColorPicker, [{ + key: 'buildItem', + value: function buildItem(option) { + var item = _get(ColorPicker.prototype.__proto__ || Object.getPrototypeOf(ColorPicker.prototype), 'buildItem', this).call(this, option); + item.style.backgroundColor = option.getAttribute('value') || ''; + return item; + } + }, { + key: 'selectItem', + value: function selectItem(item, trigger) { + _get(ColorPicker.prototype.__proto__ || Object.getPrototypeOf(ColorPicker.prototype), 'selectItem', this).call(this, item, trigger); + var colorLabel = this.label.querySelector('.ql-color-label'); + var value = item ? item.getAttribute('data-value') || '' : ''; + if (colorLabel) { + if (colorLabel.tagName === 'line') { + colorLabel.style.stroke = value; + } else { + colorLabel.style.fill = value; + } + } + } + }]); + + return ColorPicker; +}(_picker2.default); + +exports.default = ColorPicker; + +/***/ }), +/* 60 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _picker = __webpack_require__(28); + +var _picker2 = _interopRequireDefault(_picker); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var IconPicker = function (_Picker) { + _inherits(IconPicker, _Picker); + + function IconPicker(select, icons) { + _classCallCheck(this, IconPicker); + + var _this = _possibleConstructorReturn(this, (IconPicker.__proto__ || Object.getPrototypeOf(IconPicker)).call(this, select)); + + _this.container.classList.add('ql-icon-picker'); + [].forEach.call(_this.container.querySelectorAll('.ql-picker-item'), function (item) { + item.innerHTML = icons[item.getAttribute('data-value') || '']; + }); + _this.defaultItem = _this.container.querySelector('.ql-selected'); + _this.selectItem(_this.defaultItem); + return _this; + } + + _createClass(IconPicker, [{ + key: 'selectItem', + value: function selectItem(item, trigger) { + _get(IconPicker.prototype.__proto__ || Object.getPrototypeOf(IconPicker.prototype), 'selectItem', this).call(this, item, trigger); + item = item || this.defaultItem; + this.label.innerHTML = item.innerHTML; + } + }]); + + return IconPicker; +}(_picker2.default); + +exports.default = IconPicker; + +/***/ }), +/* 61 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Tooltip = function () { + function Tooltip(quill, boundsContainer) { + var _this = this; + + _classCallCheck(this, Tooltip); + + this.quill = quill; + this.boundsContainer = boundsContainer || document.body; + this.root = quill.addContainer('ql-tooltip'); + this.root.innerHTML = this.constructor.TEMPLATE; + if (this.quill.root === this.quill.scrollingContainer) { + this.quill.root.addEventListener('scroll', function () { + _this.root.style.marginTop = -1 * _this.quill.root.scrollTop + 'px'; + }); + } + this.hide(); + } + + _createClass(Tooltip, [{ + key: 'hide', + value: function hide() { + this.root.classList.add('ql-hidden'); + } + }, { + key: 'position', + value: function position(reference) { + var left = reference.left + reference.width / 2 - this.root.offsetWidth / 2; + // root.scrollTop should be 0 if scrollContainer !== root + var top = reference.bottom + this.quill.root.scrollTop; + this.root.style.left = left + 'px'; + this.root.style.top = top + 'px'; + this.root.classList.remove('ql-flip'); + var containerBounds = this.boundsContainer.getBoundingClientRect(); + var rootBounds = this.root.getBoundingClientRect(); + var shift = 0; + if (rootBounds.right > containerBounds.right) { + shift = containerBounds.right - rootBounds.right; + this.root.style.left = left + shift + 'px'; + } + if (rootBounds.left < containerBounds.left) { + shift = containerBounds.left - rootBounds.left; + this.root.style.left = left + shift + 'px'; + } + if (rootBounds.bottom > containerBounds.bottom) { + var height = rootBounds.bottom - rootBounds.top; + var verticalShift = reference.bottom - reference.top + height; + this.root.style.top = top - verticalShift + 'px'; + this.root.classList.add('ql-flip'); + } + return shift; + } + }, { + key: 'show', + value: function show() { + this.root.classList.remove('ql-editing'); + this.root.classList.remove('ql-hidden'); + } + }]); + + return Tooltip; +}(); + +exports.default = Tooltip; + +/***/ }), +/* 62 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +var _emitter = __webpack_require__(8); + +var _emitter2 = _interopRequireDefault(_emitter); + +var _base = __webpack_require__(43); + +var _base2 = _interopRequireDefault(_base); + +var _link = __webpack_require__(27); + +var _link2 = _interopRequireDefault(_link); + +var _selection = __webpack_require__(15); + +var _icons = __webpack_require__(41); + +var _icons2 = _interopRequireDefault(_icons); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var TOOLBAR_CONFIG = [[{ header: ['1', '2', '3', false] }], ['bold', 'italic', 'underline', 'link'], [{ list: 'ordered' }, { list: 'bullet' }], ['clean']]; + +var SnowTheme = function (_BaseTheme) { + _inherits(SnowTheme, _BaseTheme); + + function SnowTheme(quill, options) { + _classCallCheck(this, SnowTheme); + + if (options.modules.toolbar != null && options.modules.toolbar.container == null) { + options.modules.toolbar.container = TOOLBAR_CONFIG; + } + + var _this = _possibleConstructorReturn(this, (SnowTheme.__proto__ || Object.getPrototypeOf(SnowTheme)).call(this, quill, options)); + + _this.quill.container.classList.add('ql-snow'); + return _this; + } + + _createClass(SnowTheme, [{ + key: 'extendToolbar', + value: function extendToolbar(toolbar) { + toolbar.container.classList.add('ql-snow'); + this.buildButtons([].slice.call(toolbar.container.querySelectorAll('button')), _icons2.default); + this.buildPickers([].slice.call(toolbar.container.querySelectorAll('select')), _icons2.default); + this.tooltip = new SnowTooltip(this.quill, this.options.bounds); + if (toolbar.container.querySelector('.ql-link')) { + this.quill.keyboard.addBinding({ key: 'K', shortKey: true }, function (range, context) { + toolbar.handlers['link'].call(toolbar, !context.format.link); + }); + } + } + }]); + + return SnowTheme; +}(_base2.default); + +SnowTheme.DEFAULTS = (0, _extend2.default)(true, {}, _base2.default.DEFAULTS, { + modules: { + toolbar: { + handlers: { + link: function link(value) { + if (value) { + var range = this.quill.getSelection(); + if (range == null || range.length == 0) return; + var preview = this.quill.getText(range); + if (/^\S+@\S+\.\S+$/.test(preview) && preview.indexOf('mailto:') !== 0) { + preview = 'mailto:' + preview; + } + var tooltip = this.quill.theme.tooltip; + tooltip.edit('link', preview); + } else { + this.quill.format('link', false); + } + } + } + } + } +}); + +var SnowTooltip = function (_BaseTooltip) { + _inherits(SnowTooltip, _BaseTooltip); + + function SnowTooltip(quill, bounds) { + _classCallCheck(this, SnowTooltip); + + var _this2 = _possibleConstructorReturn(this, (SnowTooltip.__proto__ || Object.getPrototypeOf(SnowTooltip)).call(this, quill, bounds)); + + _this2.preview = _this2.root.querySelector('a.ql-preview'); + return _this2; + } + + _createClass(SnowTooltip, [{ + key: 'listen', + value: function listen() { + var _this3 = this; + + _get(SnowTooltip.prototype.__proto__ || Object.getPrototypeOf(SnowTooltip.prototype), 'listen', this).call(this); + this.root.querySelector('a.ql-action').addEventListener('click', function (event) { + if (_this3.root.classList.contains('ql-editing')) { + _this3.save(); + } else { + _this3.edit('link', _this3.preview.textContent); + } + event.preventDefault(); + }); + this.root.querySelector('a.ql-remove').addEventListener('click', function (event) { + if (_this3.linkRange != null) { + var range = _this3.linkRange; + _this3.restoreFocus(); + _this3.quill.formatText(range, 'link', false, _emitter2.default.sources.USER); + delete _this3.linkRange; + } + event.preventDefault(); + _this3.hide(); + }); + this.quill.on(_emitter2.default.events.SELECTION_CHANGE, function (range, oldRange, source) { + if (range == null) return; + if (range.length === 0 && source === _emitter2.default.sources.USER) { + var _quill$scroll$descend = _this3.quill.scroll.descendant(_link2.default, range.index), + _quill$scroll$descend2 = _slicedToArray(_quill$scroll$descend, 2), + link = _quill$scroll$descend2[0], + offset = _quill$scroll$descend2[1]; + + if (link != null) { + _this3.linkRange = new _selection.Range(range.index - offset, link.length()); + var preview = _link2.default.formats(link.domNode); + _this3.preview.textContent = preview; + _this3.preview.setAttribute('href', preview); + _this3.show(); + _this3.position(_this3.quill.getBounds(_this3.linkRange)); + return; + } + } else { + delete _this3.linkRange; + } + _this3.hide(); + }); + } + }, { + key: 'show', + value: function show() { + _get(SnowTooltip.prototype.__proto__ || Object.getPrototypeOf(SnowTooltip.prototype), 'show', this).call(this); + this.root.removeAttribute('data-mode'); + } + }]); + + return SnowTooltip; +}(_base.BaseTooltip); + +SnowTooltip.TEMPLATE = ['', '', '', ''].join(''); + +exports.default = SnowTheme; + +/***/ }), +/* 63 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _core = __webpack_require__(29); + +var _core2 = _interopRequireDefault(_core); + +var _align = __webpack_require__(36); + +var _direction = __webpack_require__(38); + +var _indent = __webpack_require__(64); + +var _blockquote = __webpack_require__(65); + +var _blockquote2 = _interopRequireDefault(_blockquote); + +var _header = __webpack_require__(66); + +var _header2 = _interopRequireDefault(_header); + +var _list = __webpack_require__(67); + +var _list2 = _interopRequireDefault(_list); + +var _background = __webpack_require__(37); + +var _color = __webpack_require__(26); + +var _font = __webpack_require__(39); + +var _size = __webpack_require__(40); + +var _bold = __webpack_require__(56); + +var _bold2 = _interopRequireDefault(_bold); + +var _italic = __webpack_require__(68); + +var _italic2 = _interopRequireDefault(_italic); + +var _link = __webpack_require__(27); + +var _link2 = _interopRequireDefault(_link); + +var _script = __webpack_require__(69); + +var _script2 = _interopRequireDefault(_script); + +var _strike = __webpack_require__(70); + +var _strike2 = _interopRequireDefault(_strike); + +var _underline = __webpack_require__(71); + +var _underline2 = _interopRequireDefault(_underline); + +var _image = __webpack_require__(72); + +var _image2 = _interopRequireDefault(_image); + +var _video = __webpack_require__(73); + +var _video2 = _interopRequireDefault(_video); + +var _code = __webpack_require__(13); + +var _code2 = _interopRequireDefault(_code); + +var _formula = __webpack_require__(74); + +var _formula2 = _interopRequireDefault(_formula); + +var _syntax = __webpack_require__(75); + +var _syntax2 = _interopRequireDefault(_syntax); + +var _toolbar = __webpack_require__(57); + +var _toolbar2 = _interopRequireDefault(_toolbar); + +var _icons = __webpack_require__(41); + +var _icons2 = _interopRequireDefault(_icons); + +var _picker = __webpack_require__(28); + +var _picker2 = _interopRequireDefault(_picker); + +var _colorPicker = __webpack_require__(59); + +var _colorPicker2 = _interopRequireDefault(_colorPicker); + +var _iconPicker = __webpack_require__(60); + +var _iconPicker2 = _interopRequireDefault(_iconPicker); + +var _tooltip = __webpack_require__(61); + +var _tooltip2 = _interopRequireDefault(_tooltip); + +var _bubble = __webpack_require__(108); + +var _bubble2 = _interopRequireDefault(_bubble); + +var _snow = __webpack_require__(62); + +var _snow2 = _interopRequireDefault(_snow); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +_core2.default.register({ + 'attributors/attribute/direction': _direction.DirectionAttribute, + + 'attributors/class/align': _align.AlignClass, + 'attributors/class/background': _background.BackgroundClass, + 'attributors/class/color': _color.ColorClass, + 'attributors/class/direction': _direction.DirectionClass, + 'attributors/class/font': _font.FontClass, + 'attributors/class/size': _size.SizeClass, + + 'attributors/style/align': _align.AlignStyle, + 'attributors/style/background': _background.BackgroundStyle, + 'attributors/style/color': _color.ColorStyle, + 'attributors/style/direction': _direction.DirectionStyle, + 'attributors/style/font': _font.FontStyle, + 'attributors/style/size': _size.SizeStyle +}, true); + +_core2.default.register({ + 'formats/align': _align.AlignClass, + 'formats/direction': _direction.DirectionClass, + 'formats/indent': _indent.IndentClass, + + 'formats/background': _background.BackgroundStyle, + 'formats/color': _color.ColorStyle, + 'formats/font': _font.FontClass, + 'formats/size': _size.SizeClass, + + 'formats/blockquote': _blockquote2.default, + 'formats/code-block': _code2.default, + 'formats/header': _header2.default, + 'formats/list': _list2.default, + + 'formats/bold': _bold2.default, + 'formats/code': _code.Code, + 'formats/italic': _italic2.default, + 'formats/link': _link2.default, + 'formats/script': _script2.default, + 'formats/strike': _strike2.default, + 'formats/underline': _underline2.default, + + 'formats/image': _image2.default, + 'formats/video': _video2.default, + + 'formats/list/item': _list.ListItem, + + 'modules/formula': _formula2.default, + 'modules/syntax': _syntax2.default, + 'modules/toolbar': _toolbar2.default, + + 'themes/bubble': _bubble2.default, + 'themes/snow': _snow2.default, + + 'ui/icons': _icons2.default, + 'ui/picker': _picker2.default, + 'ui/icon-picker': _iconPicker2.default, + 'ui/color-picker': _colorPicker2.default, + 'ui/tooltip': _tooltip2.default +}, true); + +exports.default = _core2.default; + +/***/ }), +/* 64 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.IndentClass = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var IdentAttributor = function (_Parchment$Attributor) { + _inherits(IdentAttributor, _Parchment$Attributor); + + function IdentAttributor() { + _classCallCheck(this, IdentAttributor); + + return _possibleConstructorReturn(this, (IdentAttributor.__proto__ || Object.getPrototypeOf(IdentAttributor)).apply(this, arguments)); + } + + _createClass(IdentAttributor, [{ + key: 'add', + value: function add(node, value) { + if (value === '+1' || value === '-1') { + var indent = this.value(node) || 0; + value = value === '+1' ? indent + 1 : indent - 1; + } + if (value === 0) { + this.remove(node); + return true; + } else { + return _get(IdentAttributor.prototype.__proto__ || Object.getPrototypeOf(IdentAttributor.prototype), 'add', this).call(this, node, value); + } + } + }, { + key: 'canAdd', + value: function canAdd(node, value) { + return _get(IdentAttributor.prototype.__proto__ || Object.getPrototypeOf(IdentAttributor.prototype), 'canAdd', this).call(this, node, value) || _get(IdentAttributor.prototype.__proto__ || Object.getPrototypeOf(IdentAttributor.prototype), 'canAdd', this).call(this, node, parseInt(value)); + } + }, { + key: 'value', + value: function value(node) { + return parseInt(_get(IdentAttributor.prototype.__proto__ || Object.getPrototypeOf(IdentAttributor.prototype), 'value', this).call(this, node)) || undefined; // Don't return NaN + } + }]); + + return IdentAttributor; +}(_parchment2.default.Attributor.Class); + +var IndentClass = new IdentAttributor('indent', 'ql-indent', { + scope: _parchment2.default.Scope.BLOCK, + whitelist: [1, 2, 3, 4, 5, 6, 7, 8] +}); + +exports.IndentClass = IndentClass; + +/***/ }), +/* 65 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Blockquote = function (_Block) { + _inherits(Blockquote, _Block); + + function Blockquote() { + _classCallCheck(this, Blockquote); + + return _possibleConstructorReturn(this, (Blockquote.__proto__ || Object.getPrototypeOf(Blockquote)).apply(this, arguments)); + } + + return Blockquote; +}(_block2.default); + +Blockquote.blotName = 'blockquote'; +Blockquote.tagName = 'blockquote'; + +exports.default = Blockquote; + +/***/ }), +/* 66 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Header = function (_Block) { + _inherits(Header, _Block); + + function Header() { + _classCallCheck(this, Header); + + return _possibleConstructorReturn(this, (Header.__proto__ || Object.getPrototypeOf(Header)).apply(this, arguments)); + } + + _createClass(Header, null, [{ + key: 'formats', + value: function formats(domNode) { + return this.tagName.indexOf(domNode.tagName) + 1; + } + }]); + + return Header; +}(_block2.default); + +Header.blotName = 'header'; +Header.tagName = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6']; + +exports.default = Header; + +/***/ }), +/* 67 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.ListItem = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _block = __webpack_require__(4); + +var _block2 = _interopRequireDefault(_block); + +var _container = __webpack_require__(25); + +var _container2 = _interopRequireDefault(_container); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var ListItem = function (_Block) { + _inherits(ListItem, _Block); + + function ListItem() { + _classCallCheck(this, ListItem); + + return _possibleConstructorReturn(this, (ListItem.__proto__ || Object.getPrototypeOf(ListItem)).apply(this, arguments)); + } + + _createClass(ListItem, [{ + key: 'format', + value: function format(name, value) { + if (name === List.blotName && !value) { + this.replaceWith(_parchment2.default.create(this.statics.scope)); + } else { + _get(ListItem.prototype.__proto__ || Object.getPrototypeOf(ListItem.prototype), 'format', this).call(this, name, value); + } + } + }, { + key: 'remove', + value: function remove() { + if (this.prev == null && this.next == null) { + this.parent.remove(); + } else { + _get(ListItem.prototype.__proto__ || Object.getPrototypeOf(ListItem.prototype), 'remove', this).call(this); + } + } + }, { + key: 'replaceWith', + value: function replaceWith(name, value) { + this.parent.isolate(this.offset(this.parent), this.length()); + if (name === this.parent.statics.blotName) { + this.parent.replaceWith(name, value); + return this; + } else { + this.parent.unwrap(); + return _get(ListItem.prototype.__proto__ || Object.getPrototypeOf(ListItem.prototype), 'replaceWith', this).call(this, name, value); + } + } + }], [{ + key: 'formats', + value: function formats(domNode) { + return domNode.tagName === this.tagName ? undefined : _get(ListItem.__proto__ || Object.getPrototypeOf(ListItem), 'formats', this).call(this, domNode); + } + }]); + + return ListItem; +}(_block2.default); + +ListItem.blotName = 'list-item'; +ListItem.tagName = 'LI'; + +var List = function (_Container) { + _inherits(List, _Container); + + _createClass(List, null, [{ + key: 'create', + value: function create(value) { + var tagName = value === 'ordered' ? 'OL' : 'UL'; + var node = _get(List.__proto__ || Object.getPrototypeOf(List), 'create', this).call(this, tagName); + if (value === 'checked' || value === 'unchecked') { + node.setAttribute('data-checked', value === 'checked'); + } + return node; + } + }, { + key: 'formats', + value: function formats(domNode) { + if (domNode.tagName === 'OL') return 'ordered'; + if (domNode.tagName === 'UL') { + if (domNode.hasAttribute('data-checked')) { + return domNode.getAttribute('data-checked') === 'true' ? 'checked' : 'unchecked'; + } else { + return 'bullet'; + } + } + return undefined; + } + }]); + + function List(domNode) { + _classCallCheck(this, List); + + var _this2 = _possibleConstructorReturn(this, (List.__proto__ || Object.getPrototypeOf(List)).call(this, domNode)); + + var listEventHandler = function listEventHandler(e) { + if (e.target.parentNode !== domNode) return; + var format = _this2.statics.formats(domNode); + var blot = _parchment2.default.find(e.target); + if (format === 'checked') { + blot.format('list', 'unchecked'); + } else if (format === 'unchecked') { + blot.format('list', 'checked'); + } + }; + + domNode.addEventListener('touchstart', listEventHandler); + domNode.addEventListener('mousedown', listEventHandler); + return _this2; + } + + _createClass(List, [{ + key: 'format', + value: function format(name, value) { + if (this.children.length > 0) { + this.children.tail.format(name, value); + } + } + }, { + key: 'formats', + value: function formats() { + // We don't inherit from FormatBlot + return _defineProperty({}, this.statics.blotName, this.statics.formats(this.domNode)); + } + }, { + key: 'insertBefore', + value: function insertBefore(blot, ref) { + if (blot instanceof ListItem) { + _get(List.prototype.__proto__ || Object.getPrototypeOf(List.prototype), 'insertBefore', this).call(this, blot, ref); + } else { + var index = ref == null ? this.length() : ref.offset(this); + var after = this.split(index); + after.parent.insertBefore(blot, after); + } + } + }, { + key: 'optimize', + value: function optimize(context) { + _get(List.prototype.__proto__ || Object.getPrototypeOf(List.prototype), 'optimize', this).call(this, context); + var next = this.next; + if (next != null && next.prev === this && next.statics.blotName === this.statics.blotName && next.domNode.tagName === this.domNode.tagName && next.domNode.getAttribute('data-checked') === this.domNode.getAttribute('data-checked')) { + next.moveChildren(this); + next.remove(); + } + } + }, { + key: 'replace', + value: function replace(target) { + if (target.statics.blotName !== this.statics.blotName) { + var item = _parchment2.default.create(this.statics.defaultChild); + target.moveChildren(item); + this.appendChild(item); + } + _get(List.prototype.__proto__ || Object.getPrototypeOf(List.prototype), 'replace', this).call(this, target); + } + }]); + + return List; +}(_container2.default); + +List.blotName = 'list'; +List.scope = _parchment2.default.Scope.BLOCK_BLOT; +List.tagName = ['OL', 'UL']; +List.defaultChild = 'list-item'; +List.allowedChildren = [ListItem]; + +exports.ListItem = ListItem; +exports.default = List; + +/***/ }), +/* 68 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _bold = __webpack_require__(56); + +var _bold2 = _interopRequireDefault(_bold); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Italic = function (_Bold) { + _inherits(Italic, _Bold); + + function Italic() { + _classCallCheck(this, Italic); + + return _possibleConstructorReturn(this, (Italic.__proto__ || Object.getPrototypeOf(Italic)).apply(this, arguments)); + } + + return Italic; +}(_bold2.default); + +Italic.blotName = 'italic'; +Italic.tagName = ['EM', 'I']; + +exports.default = Italic; + +/***/ }), +/* 69 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Script = function (_Inline) { + _inherits(Script, _Inline); + + function Script() { + _classCallCheck(this, Script); + + return _possibleConstructorReturn(this, (Script.__proto__ || Object.getPrototypeOf(Script)).apply(this, arguments)); + } + + _createClass(Script, null, [{ + key: 'create', + value: function create(value) { + if (value === 'super') { + return document.createElement('sup'); + } else if (value === 'sub') { + return document.createElement('sub'); + } else { + return _get(Script.__proto__ || Object.getPrototypeOf(Script), 'create', this).call(this, value); + } + } + }, { + key: 'formats', + value: function formats(domNode) { + if (domNode.tagName === 'SUB') return 'sub'; + if (domNode.tagName === 'SUP') return 'super'; + return undefined; + } + }]); + + return Script; +}(_inline2.default); + +Script.blotName = 'script'; +Script.tagName = ['SUB', 'SUP']; + +exports.default = Script; + +/***/ }), +/* 70 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Strike = function (_Inline) { + _inherits(Strike, _Inline); + + function Strike() { + _classCallCheck(this, Strike); + + return _possibleConstructorReturn(this, (Strike.__proto__ || Object.getPrototypeOf(Strike)).apply(this, arguments)); + } + + return Strike; +}(_inline2.default); + +Strike.blotName = 'strike'; +Strike.tagName = 'S'; + +exports.default = Strike; + +/***/ }), +/* 71 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _inline = __webpack_require__(6); + +var _inline2 = _interopRequireDefault(_inline); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var Underline = function (_Inline) { + _inherits(Underline, _Inline); + + function Underline() { + _classCallCheck(this, Underline); + + return _possibleConstructorReturn(this, (Underline.__proto__ || Object.getPrototypeOf(Underline)).apply(this, arguments)); + } + + return Underline; +}(_inline2.default); + +Underline.blotName = 'underline'; +Underline.tagName = 'U'; + +exports.default = Underline; + +/***/ }), +/* 72 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _link = __webpack_require__(27); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var ATTRIBUTES = ['alt', 'height', 'width']; + +var Image = function (_Parchment$Embed) { + _inherits(Image, _Parchment$Embed); + + function Image() { + _classCallCheck(this, Image); + + return _possibleConstructorReturn(this, (Image.__proto__ || Object.getPrototypeOf(Image)).apply(this, arguments)); + } + + _createClass(Image, [{ + key: 'format', + value: function format(name, value) { + if (ATTRIBUTES.indexOf(name) > -1) { + if (value) { + this.domNode.setAttribute(name, value); + } else { + this.domNode.removeAttribute(name); + } + } else { + _get(Image.prototype.__proto__ || Object.getPrototypeOf(Image.prototype), 'format', this).call(this, name, value); + } + } + }], [{ + key: 'create', + value: function create(value) { + var node = _get(Image.__proto__ || Object.getPrototypeOf(Image), 'create', this).call(this, value); + if (typeof value === 'string') { + node.setAttribute('src', this.sanitize(value)); + } + return node; + } + }, { + key: 'formats', + value: function formats(domNode) { + return ATTRIBUTES.reduce(function (formats, attribute) { + if (domNode.hasAttribute(attribute)) { + formats[attribute] = domNode.getAttribute(attribute); + } + return formats; + }, {}); + } + }, { + key: 'match', + value: function match(url) { + return (/\.(jpe?g|gif|png)$/.test(url) || /^data:image\/.+;base64/.test(url) + ); + } + }, { + key: 'sanitize', + value: function sanitize(url) { + return (0, _link.sanitize)(url, ['http', 'https', 'data']) ? url : '//:0'; + } + }, { + key: 'value', + value: function value(domNode) { + return domNode.getAttribute('src'); + } + }]); + + return Image; +}(_parchment2.default.Embed); + +Image.blotName = 'image'; +Image.tagName = 'IMG'; + +exports.default = Image; + +/***/ }), +/* 73 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _block = __webpack_require__(4); + +var _link = __webpack_require__(27); + +var _link2 = _interopRequireDefault(_link); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var ATTRIBUTES = ['height', 'width']; + +var Video = function (_BlockEmbed) { + _inherits(Video, _BlockEmbed); + + function Video() { + _classCallCheck(this, Video); + + return _possibleConstructorReturn(this, (Video.__proto__ || Object.getPrototypeOf(Video)).apply(this, arguments)); + } + + _createClass(Video, [{ + key: 'format', + value: function format(name, value) { + if (ATTRIBUTES.indexOf(name) > -1) { + if (value) { + this.domNode.setAttribute(name, value); + } else { + this.domNode.removeAttribute(name); + } + } else { + _get(Video.prototype.__proto__ || Object.getPrototypeOf(Video.prototype), 'format', this).call(this, name, value); + } + } + }], [{ + key: 'create', + value: function create(value) { + var node = _get(Video.__proto__ || Object.getPrototypeOf(Video), 'create', this).call(this, value); + node.setAttribute('frameborder', '0'); + node.setAttribute('allowfullscreen', true); + node.setAttribute('src', this.sanitize(value)); + return node; + } + }, { + key: 'formats', + value: function formats(domNode) { + return ATTRIBUTES.reduce(function (formats, attribute) { + if (domNode.hasAttribute(attribute)) { + formats[attribute] = domNode.getAttribute(attribute); + } + return formats; + }, {}); + } + }, { + key: 'sanitize', + value: function sanitize(url) { + return _link2.default.sanitize(url); + } + }, { + key: 'value', + value: function value(domNode) { + return domNode.getAttribute('src'); + } + }]); + + return Video; +}(_block.BlockEmbed); + +Video.blotName = 'video'; +Video.className = 'ql-video'; +Video.tagName = 'IFRAME'; + +exports.default = Video; + +/***/ }), +/* 74 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.FormulaBlot = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _embed = __webpack_require__(35); + +var _embed2 = _interopRequireDefault(_embed); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var FormulaBlot = function (_Embed) { + _inherits(FormulaBlot, _Embed); + + function FormulaBlot() { + _classCallCheck(this, FormulaBlot); + + return _possibleConstructorReturn(this, (FormulaBlot.__proto__ || Object.getPrototypeOf(FormulaBlot)).apply(this, arguments)); + } + + _createClass(FormulaBlot, null, [{ + key: 'create', + value: function create(value) { + var node = _get(FormulaBlot.__proto__ || Object.getPrototypeOf(FormulaBlot), 'create', this).call(this, value); + if (typeof value === 'string') { + window.katex.render(value, node, { + throwOnError: false, + errorColor: '#f00' + }); + node.setAttribute('data-value', value); + } + return node; + } + }, { + key: 'value', + value: function value(domNode) { + return domNode.getAttribute('data-value'); + } + }]); + + return FormulaBlot; +}(_embed2.default); + +FormulaBlot.blotName = 'formula'; +FormulaBlot.className = 'ql-formula'; +FormulaBlot.tagName = 'SPAN'; + +var Formula = function (_Module) { + _inherits(Formula, _Module); + + _createClass(Formula, null, [{ + key: 'register', + value: function register() { + _quill2.default.register(FormulaBlot, true); + } + }]); + + function Formula() { + _classCallCheck(this, Formula); + + var _this2 = _possibleConstructorReturn(this, (Formula.__proto__ || Object.getPrototypeOf(Formula)).call(this)); + + if (window.katex == null) { + throw new Error('Formula module requires KaTeX.'); + } + return _this2; + } + + return Formula; +}(_module2.default); + +exports.FormulaBlot = FormulaBlot; +exports.default = Formula; + +/***/ }), +/* 75 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.CodeToken = exports.CodeBlock = undefined; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _parchment = __webpack_require__(0); + +var _parchment2 = _interopRequireDefault(_parchment); + +var _quill = __webpack_require__(5); + +var _quill2 = _interopRequireDefault(_quill); + +var _module = __webpack_require__(9); + +var _module2 = _interopRequireDefault(_module); + +var _code = __webpack_require__(13); + +var _code2 = _interopRequireDefault(_code); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var SyntaxCodeBlock = function (_CodeBlock) { + _inherits(SyntaxCodeBlock, _CodeBlock); + + function SyntaxCodeBlock() { + _classCallCheck(this, SyntaxCodeBlock); + + return _possibleConstructorReturn(this, (SyntaxCodeBlock.__proto__ || Object.getPrototypeOf(SyntaxCodeBlock)).apply(this, arguments)); + } + + _createClass(SyntaxCodeBlock, [{ + key: 'replaceWith', + value: function replaceWith(block) { + this.domNode.textContent = this.domNode.textContent; + this.attach(); + _get(SyntaxCodeBlock.prototype.__proto__ || Object.getPrototypeOf(SyntaxCodeBlock.prototype), 'replaceWith', this).call(this, block); + } + }, { + key: 'highlight', + value: function highlight(_highlight) { + var text = this.domNode.textContent; + if (this.cachedText !== text) { + if (text.trim().length > 0 || this.cachedText == null) { + this.domNode.innerHTML = _highlight(text); + this.domNode.normalize(); + this.attach(); + } + this.cachedText = text; + } + } + }]); + + return SyntaxCodeBlock; +}(_code2.default); + +SyntaxCodeBlock.className = 'ql-syntax'; + +var CodeToken = new _parchment2.default.Attributor.Class('token', 'hljs', { + scope: _parchment2.default.Scope.INLINE +}); + +var Syntax = function (_Module) { + _inherits(Syntax, _Module); + + _createClass(Syntax, null, [{ + key: 'register', + value: function register() { + _quill2.default.register(CodeToken, true); + _quill2.default.register(SyntaxCodeBlock, true); + } + }]); + + function Syntax(quill, options) { + _classCallCheck(this, Syntax); + + var _this2 = _possibleConstructorReturn(this, (Syntax.__proto__ || Object.getPrototypeOf(Syntax)).call(this, quill, options)); + + if (typeof _this2.options.highlight !== 'function') { + throw new Error('Syntax module requires highlight.js. Please include the library on the page before Quill.'); + } + var timer = null; + _this2.quill.on(_quill2.default.events.SCROLL_OPTIMIZE, function () { + clearTimeout(timer); + timer = setTimeout(function () { + _this2.highlight(); + timer = null; + }, _this2.options.interval); + }); + _this2.highlight(); + return _this2; + } + + _createClass(Syntax, [{ + key: 'highlight', + value: function highlight() { + var _this3 = this; + + if (this.quill.selection.composing) return; + this.quill.update(_quill2.default.sources.USER); + var range = this.quill.getSelection(); + this.quill.scroll.descendants(SyntaxCodeBlock).forEach(function (code) { + code.highlight(_this3.options.highlight); + }); + this.quill.update(_quill2.default.sources.SILENT); + if (range != null) { + this.quill.setSelection(range, _quill2.default.sources.SILENT); + } + } + }]); + + return Syntax; +}(_module2.default); + +Syntax.DEFAULTS = { + highlight: function () { + if (window.hljs == null) return null; + return function (text) { + var result = window.hljs.highlightAuto(text); + return result.value; + }; + }(), + interval: 1000 +}; + +exports.CodeBlock = SyntaxCodeBlock; +exports.CodeToken = CodeToken; +exports.default = Syntax; + +/***/ }), +/* 76 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 77 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 78 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 79 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 80 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 81 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 82 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 83 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 84 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 85 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 86 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 87 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 88 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 89 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 90 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 91 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 92 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 93 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 94 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 95 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 96 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 97 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 98 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 99 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 100 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 101 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 102 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 103 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 104 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 105 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 106 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 107 */ +/***/ (function(module, exports) { + +module.exports = " "; + +/***/ }), +/* 108 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.BubbleTooltip = undefined; + +var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _extend = __webpack_require__(3); + +var _extend2 = _interopRequireDefault(_extend); + +var _emitter = __webpack_require__(8); + +var _emitter2 = _interopRequireDefault(_emitter); + +var _base = __webpack_require__(43); + +var _base2 = _interopRequireDefault(_base); + +var _selection = __webpack_require__(15); + +var _icons = __webpack_require__(41); + +var _icons2 = _interopRequireDefault(_icons); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var TOOLBAR_CONFIG = [['bold', 'italic', 'link'], [{ header: 1 }, { header: 2 }, 'blockquote']]; + +var BubbleTheme = function (_BaseTheme) { + _inherits(BubbleTheme, _BaseTheme); + + function BubbleTheme(quill, options) { + _classCallCheck(this, BubbleTheme); + + if (options.modules.toolbar != null && options.modules.toolbar.container == null) { + options.modules.toolbar.container = TOOLBAR_CONFIG; + } + + var _this = _possibleConstructorReturn(this, (BubbleTheme.__proto__ || Object.getPrototypeOf(BubbleTheme)).call(this, quill, options)); + + _this.quill.container.classList.add('ql-bubble'); + return _this; + } + + _createClass(BubbleTheme, [{ + key: 'extendToolbar', + value: function extendToolbar(toolbar) { + this.tooltip = new BubbleTooltip(this.quill, this.options.bounds); + this.tooltip.root.appendChild(toolbar.container); + this.buildButtons([].slice.call(toolbar.container.querySelectorAll('button')), _icons2.default); + this.buildPickers([].slice.call(toolbar.container.querySelectorAll('select')), _icons2.default); + } + }]); + + return BubbleTheme; +}(_base2.default); + +BubbleTheme.DEFAULTS = (0, _extend2.default)(true, {}, _base2.default.DEFAULTS, { + modules: { + toolbar: { + handlers: { + link: function link(value) { + if (!value) { + this.quill.format('link', false); + } else { + this.quill.theme.tooltip.edit(); + } + } + } + } + } +}); + +var BubbleTooltip = function (_BaseTooltip) { + _inherits(BubbleTooltip, _BaseTooltip); + + function BubbleTooltip(quill, bounds) { + _classCallCheck(this, BubbleTooltip); + + var _this2 = _possibleConstructorReturn(this, (BubbleTooltip.__proto__ || Object.getPrototypeOf(BubbleTooltip)).call(this, quill, bounds)); + + _this2.quill.on(_emitter2.default.events.EDITOR_CHANGE, function (type, range, oldRange, source) { + if (type !== _emitter2.default.events.SELECTION_CHANGE) return; + if (range != null && range.length > 0 && source === _emitter2.default.sources.USER) { + _this2.show(); + // Lock our width so we will expand beyond our offsetParent boundaries + _this2.root.style.left = '0px'; + _this2.root.style.width = ''; + _this2.root.style.width = _this2.root.offsetWidth + 'px'; + var lines = _this2.quill.getLines(range.index, range.length); + if (lines.length === 1) { + _this2.position(_this2.quill.getBounds(range)); + } else { + var lastLine = lines[lines.length - 1]; + var index = _this2.quill.getIndex(lastLine); + var length = Math.min(lastLine.length() - 1, range.index + range.length - index); + var _bounds = _this2.quill.getBounds(new _selection.Range(index, length)); + _this2.position(_bounds); + } + } else if (document.activeElement !== _this2.textbox && _this2.quill.hasFocus()) { + _this2.hide(); + } + }); + return _this2; + } + + _createClass(BubbleTooltip, [{ + key: 'listen', + value: function listen() { + var _this3 = this; + + _get(BubbleTooltip.prototype.__proto__ || Object.getPrototypeOf(BubbleTooltip.prototype), 'listen', this).call(this); + this.root.querySelector('.ql-close').addEventListener('click', function () { + _this3.root.classList.remove('ql-editing'); + }); + this.quill.on(_emitter2.default.events.SCROLL_OPTIMIZE, function () { + // Let selection be restored by toolbar handlers before repositioning + setTimeout(function () { + if (_this3.root.classList.contains('ql-hidden')) return; + var range = _this3.quill.getSelection(); + if (range != null) { + _this3.position(_this3.quill.getBounds(range)); + } + }, 1); + }); + } + }, { + key: 'cancel', + value: function cancel() { + this.show(); + } + }, { + key: 'position', + value: function position(reference) { + var shift = _get(BubbleTooltip.prototype.__proto__ || Object.getPrototypeOf(BubbleTooltip.prototype), 'position', this).call(this, reference); + var arrow = this.root.querySelector('.ql-tooltip-arrow'); + arrow.style.marginLeft = ''; + if (shift === 0) return shift; + arrow.style.marginLeft = -1 * shift - arrow.offsetWidth / 2 + 'px'; + } + }]); + + return BubbleTooltip; +}(_base.BaseTooltip); + +BubbleTooltip.TEMPLATE = ['', '
', '', '', '
'].join(''); + +exports.BubbleTooltip = BubbleTooltip; +exports.default = BubbleTheme; + +/***/ }), +/* 109 */ +/***/ (function(module, exports, __webpack_require__) { + +module.exports = __webpack_require__(63); + + +/***/ }) +/******/ ])["default"]; +}); \ No newline at end of file diff --git a/Wino.Mail/JS/Quill/quill.min.js b/Wino.Mail/JS/Quill/quill.min.js new file mode 100644 index 00000000..ff30fd90 --- /dev/null +++ b/Wino.Mail/JS/Quill/quill.min.js @@ -0,0 +1,8 @@ +/*! + * Quill Editor v1.3.6 + * https://quilljs.com/ + * Copyright (c) 2014, Jason Chen + * Copyright (c) 2013, salesforce.com + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Quill=e():t.Quill=e()}("undefined"!=typeof self?self:this,function(){return function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}var n={};return e.m=t,e.c=n,e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=45)}([function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(17),o=n(18),i=n(19),l=n(48),a=n(49),s=n(50),u=n(51),c=n(52),f=n(11),h=n(29),p=n(30),d=n(28),y=n(1),v={Scope:y.Scope,create:y.create,find:y.find,query:y.query,register:y.register,Container:r.default,Format:o.default,Leaf:i.default,Embed:u.default,Scroll:l.default,Block:s.default,Inline:a.default,Text:c.default,Attributor:{Attribute:f.default,Class:h.default,Style:p.default,Store:d.default}};e.default=v},function(t,e,n){"use strict";function r(t,e){var n=i(t);if(null==n)throw new s("Unable to create "+t+" blot");var r=n;return new r(t instanceof Node||t.nodeType===Node.TEXT_NODE?t:r.create(e),e)}function o(t,n){return void 0===n&&(n=!1),null==t?null:null!=t[e.DATA_KEY]?t[e.DATA_KEY].blot:n?o(t.parentNode,n):null}function i(t,e){void 0===e&&(e=p.ANY);var n;if("string"==typeof t)n=h[t]||u[t];else if(t instanceof Text||t.nodeType===Node.TEXT_NODE)n=h.text;else if("number"==typeof t)t&p.LEVEL&p.BLOCK?n=h.block:t&p.LEVEL&p.INLINE&&(n=h.inline);else if(t instanceof HTMLElement){var r=(t.getAttribute("class")||"").split(/\s+/);for(var o in r)if(n=c[r[o]])break;n=n||f[t.tagName]}return null==n?null:e&p.LEVEL&n.scope&&e&p.TYPE&n.scope?n:null}function l(){for(var t=[],e=0;e1)return t.map(function(t){return l(t)});var n=t[0];if("string"!=typeof n.blotName&&"string"!=typeof n.attrName)throw new s("Invalid definition");if("abstract"===n.blotName)throw new s("Cannot register abstract class");if(h[n.blotName||n.attrName]=n,"string"==typeof n.keyName)u[n.keyName]=n;else if(null!=n.className&&(c[n.className]=n),null!=n.tagName){Array.isArray(n.tagName)?n.tagName=n.tagName.map(function(t){return t.toUpperCase()}):n.tagName=n.tagName.toUpperCase();var r=Array.isArray(n.tagName)?n.tagName:[n.tagName];r.forEach(function(t){null!=f[t]&&null!=n.className||(f[t]=n)})}return n}var a=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var s=function(t){function e(e){var n=this;return e="[Parchment] "+e,n=t.call(this,e)||this,n.message=e,n.name=n.constructor.name,n}return a(e,t),e}(Error);e.ParchmentError=s;var u={},c={},f={},h={};e.DATA_KEY="__blot";var p;!function(t){t[t.TYPE=3]="TYPE",t[t.LEVEL=12]="LEVEL",t[t.ATTRIBUTE=13]="ATTRIBUTE",t[t.BLOT=14]="BLOT",t[t.INLINE=7]="INLINE",t[t.BLOCK=11]="BLOCK",t[t.BLOCK_BLOT=10]="BLOCK_BLOT",t[t.INLINE_BLOT=6]="INLINE_BLOT",t[t.BLOCK_ATTRIBUTE=9]="BLOCK_ATTRIBUTE",t[t.INLINE_ATTRIBUTE=5]="INLINE_ATTRIBUTE",t[t.ANY=15]="ANY"}(p=e.Scope||(e.Scope={})),e.create=r,e.find=o,e.query=i,e.register=l},function(t,e){"use strict";var n=Object.prototype.hasOwnProperty,r=Object.prototype.toString,o=function(t){return"function"==typeof Array.isArray?Array.isArray(t):"[object Array]"===r.call(t)},i=function(t){if(!t||"[object Object]"!==r.call(t))return!1;var e=n.call(t,"constructor"),o=t.constructor&&t.constructor.prototype&&n.call(t.constructor.prototype,"isPrototypeOf");if(t.constructor&&!e&&!o)return!1;var i;for(i in t);return void 0===i||n.call(t,i)};t.exports=function t(){var e,n,r,l,a,s,u=arguments[0],c=1,f=arguments.length,h=!1;for("boolean"==typeof u&&(h=u,u=arguments[1]||{},c=2),(null==u||"object"!=typeof u&&"function"!=typeof u)&&(u={});c1&&void 0!==arguments[1]?arguments[1]:{};return null==t?e:("function"==typeof t.formats&&(e=(0,f.default)(e,t.formats())),null==t.parent||"scroll"==t.parent.blotName||t.parent.statics.scope!==t.statics.scope?e:a(t.parent,e))}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.BlockEmbed=e.bubbleFormats=void 0;var s=function(){function t(t,e){for(var n=0;n0&&(t1&&void 0!==arguments[1]&&arguments[1];if(n&&(0===t||t>=this.length()-1)){var r=this.clone();return 0===t?(this.parent.insertBefore(r,this),this):(this.parent.insertBefore(r,this.next),r)}var o=u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"split",this).call(this,t,n);return this.cache={},o}}]),e}(y.default.Block);x.blotName="block",x.tagName="P",x.defaultChild="break",x.allowedChildren=[m.default,y.default.Embed,O.default],e.bubbleFormats=a,e.BlockEmbed=w,e.default=x},function(t,e,n){var r=n(54),o=n(12),i=n(2),l=n(20),a=String.fromCharCode(0),s=function(t){Array.isArray(t)?this.ops=t:null!=t&&Array.isArray(t.ops)?this.ops=t.ops:this.ops=[]};s.prototype.insert=function(t,e){var n={};return 0===t.length?this:(n.insert=t,null!=e&&"object"==typeof e&&Object.keys(e).length>0&&(n.attributes=e),this.push(n))},s.prototype.delete=function(t){return t<=0?this:this.push({delete:t})},s.prototype.retain=function(t,e){if(t<=0)return this;var n={retain:t};return null!=e&&"object"==typeof e&&Object.keys(e).length>0&&(n.attributes=e),this.push(n)},s.prototype.push=function(t){var e=this.ops.length,n=this.ops[e-1];if(t=i(!0,{},t),"object"==typeof n){if("number"==typeof t.delete&&"number"==typeof n.delete)return this.ops[e-1]={delete:n.delete+t.delete},this;if("number"==typeof n.delete&&null!=t.insert&&(e-=1,"object"!=typeof(n=this.ops[e-1])))return this.ops.unshift(t),this;if(o(t.attributes,n.attributes)){if("string"==typeof t.insert&&"string"==typeof n.insert)return this.ops[e-1]={insert:n.insert+t.insert},"object"==typeof t.attributes&&(this.ops[e-1].attributes=t.attributes),this;if("number"==typeof t.retain&&"number"==typeof n.retain)return this.ops[e-1]={retain:n.retain+t.retain},"object"==typeof t.attributes&&(this.ops[e-1].attributes=t.attributes),this}}return e===this.ops.length?this.ops.push(t):this.ops.splice(e,0,t),this},s.prototype.chop=function(){var t=this.ops[this.ops.length-1];return t&&t.retain&&!t.attributes&&this.ops.pop(),this},s.prototype.filter=function(t){return this.ops.filter(t)},s.prototype.forEach=function(t){this.ops.forEach(t)},s.prototype.map=function(t){return this.ops.map(t)},s.prototype.partition=function(t){var e=[],n=[];return this.forEach(function(r){(t(r)?e:n).push(r)}),[e,n]},s.prototype.reduce=function(t,e){return this.ops.reduce(t,e)},s.prototype.changeLength=function(){return this.reduce(function(t,e){return e.insert?t+l.length(e):e.delete?t-e.delete:t},0)},s.prototype.length=function(){return this.reduce(function(t,e){return t+l.length(e)},0)},s.prototype.slice=function(t,e){t=t||0,"number"!=typeof e&&(e=1/0);for(var n=[],r=l.iterator(this.ops),o=0;o0&&(e.push(t.ops[0]),e.ops=e.ops.concat(t.ops.slice(1))),e},s.prototype.diff=function(t,e){if(this.ops===t.ops)return new s;var n=[this,t].map(function(e){return e.map(function(n){if(null!=n.insert)return"string"==typeof n.insert?n.insert:a;var r=e===t?"on":"with";throw new Error("diff() called "+r+" non-document")}).join("")}),i=new s,u=r(n[0],n[1],e),c=l.iterator(this.ops),f=l.iterator(t.ops);return u.forEach(function(t){for(var e=t[1].length;e>0;){var n=0;switch(t[0]){case r.INSERT:n=Math.min(f.peekLength(),e),i.push(f.next(n));break;case r.DELETE:n=Math.min(e,c.peekLength()),c.next(n),i.delete(n);break;case r.EQUAL:n=Math.min(c.peekLength(),f.peekLength(),e);var a=c.next(n),s=f.next(n);o(a.insert,s.insert)?i.retain(n,l.attributes.diff(a.attributes,s.attributes)):i.push(s).delete(n)}e-=n}}),i.chop()},s.prototype.eachLine=function(t,e){e=e||"\n";for(var n=l.iterator(this.ops),r=new s,o=0;n.hasNext();){if("insert"!==n.peekType())return;var i=n.peek(),a=l.length(i)-n.peekLength(),u="string"==typeof i.insert?i.insert.indexOf(e,a)-a:-1;if(u<0)r.push(n.next());else if(u>0)r.push(n.next(u));else{if(!1===t(r,n.next(1).attributes||{},o))return;o+=1,r=new s}}r.length()>0&&t(r,{},o)},s.prototype.transform=function(t,e){if(e=!!e,"number"==typeof t)return this.transformPosition(t,e);for(var n=l.iterator(this.ops),r=l.iterator(t.ops),o=new s;n.hasNext()||r.hasNext();)if("insert"!==n.peekType()||!e&&"insert"===r.peekType())if("insert"===r.peekType())o.push(r.next());else{var i=Math.min(n.peekLength(),r.peekLength()),a=n.next(i),u=r.next(i);if(a.delete)continue;u.delete?o.push(u):o.retain(i,l.attributes.transform(a.attributes,u.attributes,e))}else o.retain(l.length(n.next()));return o.chop()},s.prototype.transformPosition=function(t,e){e=!!e;for(var n=l.iterator(this.ops),r=0;n.hasNext()&&r<=t;){var o=n.peekLength(),i=n.peekType();n.next(),"delete"!==i?("insert"===i&&(r0){var n=this.parent.isolate(this.offset(),this.length());this.moveChildren(n),n.wrap(this)}}}],[{key:"compare",value:function(t,n){var r=e.order.indexOf(t),o=e.order.indexOf(n);return r>=0||o>=0?r-o:t===n?0:t0){var a,s=[g.default.events.TEXT_CHANGE,l,i,e];if((a=this.emitter).emit.apply(a,[g.default.events.EDITOR_CHANGE].concat(s)),e!==g.default.sources.SILENT){var c;(c=this.emitter).emit.apply(c,s)}}return l}function s(t,e,n,r,o){var i={};return"number"==typeof t.index&&"number"==typeof t.length?"number"!=typeof e?(o=r,r=n,n=e,e=t.length,t=t.index):(e=t.length,t=t.index):"number"!=typeof e&&(o=r,r=n,n=e,e=0),"object"===(void 0===n?"undefined":c(n))?(i=n,o=r):"string"==typeof n&&(null!=r?i[n]=r:o=n),o=o||g.default.sources.API,[t,e,i,o]}function u(t,e,n,r){if(null==t)return null;var o=void 0,i=void 0;if(e instanceof d.default){var l=[t.index,t.index+t.length].map(function(t){return e.transformPosition(t,r!==g.default.sources.USER)}),a=f(l,2);o=a[0],i=a[1]}else{var s=[t.index,t.index+t.length].map(function(t){return t=0?t+n:Math.max(e,t+n)}),u=f(s,2);o=u[0],i=u[1]}return new x.Range(o,i-o)}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.overload=e.expandConfig=void 0;var c="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},f=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),h=function(){function t(t,e){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:{};if(i(this,t),this.options=l(e,r),this.container=this.options.container,null==this.container)return P.error("Invalid Quill container",e);this.options.debug&&t.debug(this.options.debug);var o=this.container.innerHTML.trim();this.container.classList.add("ql-container"),this.container.innerHTML="",this.container.__quill=this,this.root=this.addContainer("ql-editor"),this.root.classList.add("ql-blank"),this.root.setAttribute("data-gramm",!1),this.scrollingContainer=this.options.scrollingContainer||this.root,this.emitter=new g.default,this.scroll=w.default.create(this.root,{emitter:this.emitter,whitelist:this.options.formats}),this.editor=new v.default(this.scroll),this.selection=new k.default(this.scroll,this.emitter),this.theme=new this.options.theme(this,this.options),this.keyboard=this.theme.addModule("keyboard"),this.clipboard=this.theme.addModule("clipboard"),this.history=this.theme.addModule("history"),this.theme.init(),this.emitter.on(g.default.events.EDITOR_CHANGE,function(t){t===g.default.events.TEXT_CHANGE&&n.root.classList.toggle("ql-blank",n.editor.isBlank())}),this.emitter.on(g.default.events.SCROLL_UPDATE,function(t,e){var r=n.selection.lastRange,o=r&&0===r.length?r.index:void 0;a.call(n,function(){return n.editor.update(null,e,o)},t)});var s=this.clipboard.convert("
"+o+"


");this.setContents(s),this.history.clear(),this.options.placeholder&&this.root.setAttribute("data-placeholder",this.options.placeholder),this.options.readOnly&&this.disable()}return h(t,null,[{key:"debug",value:function(t){!0===t&&(t="log"),A.default.level(t)}},{key:"find",value:function(t){return t.__quill||w.default.find(t)}},{key:"import",value:function(t){return null==this.imports[t]&&P.error("Cannot import "+t+". Are you sure it was registered?"),this.imports[t]}},{key:"register",value:function(t,e){var n=this,r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if("string"!=typeof t){var o=t.attrName||t.blotName;"string"==typeof o?this.register("formats/"+o,t,e):Object.keys(t).forEach(function(r){n.register(r,t[r],e)})}else null==this.imports[t]||r||P.warn("Overwriting "+t+" with",e),this.imports[t]=e,(t.startsWith("blots/")||t.startsWith("formats/"))&&"abstract"!==e.blotName?w.default.register(e):t.startsWith("modules")&&"function"==typeof e.register&&e.register()}}]),h(t,[{key:"addContainer",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;if("string"==typeof t){var n=t;t=document.createElement("div"),t.classList.add(n)}return this.container.insertBefore(t,e),t}},{key:"blur",value:function(){this.selection.setRange(null)}},{key:"deleteText",value:function(t,e,n){var r=this,o=s(t,e,n),i=f(o,4);return t=i[0],e=i[1],n=i[3],a.call(this,function(){return r.editor.deleteText(t,e)},n,t,-1*e)}},{key:"disable",value:function(){this.enable(!1)}},{key:"enable",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.scroll.enable(t),this.container.classList.toggle("ql-disabled",!t)}},{key:"focus",value:function(){var t=this.scrollingContainer.scrollTop;this.selection.focus(),this.scrollingContainer.scrollTop=t,this.scrollIntoView()}},{key:"format",value:function(t,e){var n=this,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:g.default.sources.API;return a.call(this,function(){var r=n.getSelection(!0),i=new d.default;if(null==r)return i;if(w.default.query(t,w.default.Scope.BLOCK))i=n.editor.formatLine(r.index,r.length,o({},t,e));else{if(0===r.length)return n.selection.format(t,e),i;i=n.editor.formatText(r.index,r.length,o({},t,e))}return n.setSelection(r,g.default.sources.SILENT),i},r)}},{key:"formatLine",value:function(t,e,n,r,o){var i=this,l=void 0,u=s(t,e,n,r,o),c=f(u,4);return t=c[0],e=c[1],l=c[2],o=c[3],a.call(this,function(){return i.editor.formatLine(t,e,l)},o,t,0)}},{key:"formatText",value:function(t,e,n,r,o){var i=this,l=void 0,u=s(t,e,n,r,o),c=f(u,4);return t=c[0],e=c[1],l=c[2],o=c[3],a.call(this,function(){return i.editor.formatText(t,e,l)},o,t,0)}},{key:"getBounds",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=void 0;n="number"==typeof t?this.selection.getBounds(t,e):this.selection.getBounds(t.index,t.length);var r=this.container.getBoundingClientRect();return{bottom:n.bottom-r.top,height:n.height,left:n.left-r.left,right:n.right-r.left,top:n.top-r.top,width:n.width}}},{key:"getContents",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.getLength()-t,n=s(t,e),r=f(n,2);return t=r[0],e=r[1],this.editor.getContents(t,e)}},{key:"getFormat",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.getSelection(!0),e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return"number"==typeof t?this.editor.getFormat(t,e):this.editor.getFormat(t.index,t.length)}},{key:"getIndex",value:function(t){return t.offset(this.scroll)}},{key:"getLength",value:function(){return this.scroll.length()}},{key:"getLeaf",value:function(t){return this.scroll.leaf(t)}},{key:"getLine",value:function(t){return this.scroll.line(t)}},{key:"getLines",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:Number.MAX_VALUE;return"number"!=typeof t?this.scroll.lines(t.index,t.length):this.scroll.lines(t,e)}},{key:"getModule",value:function(t){return this.theme.modules[t]}},{key:"getSelection",value:function(){return arguments.length>0&&void 0!==arguments[0]&&arguments[0]&&this.focus(),this.update(),this.selection.getRange()[0]}},{key:"getText",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.getLength()-t,n=s(t,e),r=f(n,2);return t=r[0],e=r[1],this.editor.getText(t,e)}},{key:"hasFocus",value:function(){return this.selection.hasFocus()}},{key:"insertEmbed",value:function(e,n,r){var o=this,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:t.sources.API;return a.call(this,function(){return o.editor.insertEmbed(e,n,r)},i,e)}},{key:"insertText",value:function(t,e,n,r,o){var i=this,l=void 0,u=s(t,0,n,r,o),c=f(u,4);return t=c[0],l=c[2],o=c[3],a.call(this,function(){return i.editor.insertText(t,e,l)},o,t,e.length)}},{key:"isEnabled",value:function(){return!this.container.classList.contains("ql-disabled")}},{key:"off",value:function(){return this.emitter.off.apply(this.emitter,arguments)}},{key:"on",value:function(){return this.emitter.on.apply(this.emitter,arguments)}},{key:"once",value:function(){return this.emitter.once.apply(this.emitter,arguments)}},{key:"pasteHTML",value:function(t,e,n){this.clipboard.dangerouslyPasteHTML(t,e,n)}},{key:"removeFormat",value:function(t,e,n){var r=this,o=s(t,e,n),i=f(o,4);return t=i[0],e=i[1],n=i[3],a.call(this,function(){return r.editor.removeFormat(t,e)},n,t)}},{key:"scrollIntoView",value:function(){this.selection.scrollIntoView(this.scrollingContainer)}},{key:"setContents",value:function(t){var e=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:g.default.sources.API;return a.call(this,function(){t=new d.default(t);var n=e.getLength(),r=e.editor.deleteText(0,n),o=e.editor.applyDelta(t),i=o.ops[o.ops.length-1];return null!=i&&"string"==typeof i.insert&&"\n"===i.insert[i.insert.length-1]&&(e.editor.deleteText(e.getLength()-1,1),o.delete(1)),r.compose(o)},n)}},{key:"setSelection",value:function(e,n,r){if(null==e)this.selection.setRange(null,n||t.sources.API);else{var o=s(e,n,r),i=f(o,4);e=i[0],n=i[1],r=i[3],this.selection.setRange(new x.Range(e,n),r),r!==g.default.sources.SILENT&&this.selection.scrollIntoView(this.scrollingContainer)}}},{key:"setText",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:g.default.sources.API,n=(new d.default).insert(t);return this.setContents(n,e)}},{key:"update",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:g.default.sources.USER,e=this.scroll.update(t);return this.selection.update(t),e}},{key:"updateContents",value:function(t){var e=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:g.default.sources.API;return a.call(this,function(){return t=new d.default(t),e.editor.applyDelta(t,n)},n,!0)}}]),t}();S.DEFAULTS={bounds:null,formats:null,modules:{},placeholder:"",readOnly:!1,scrollingContainer:null,strict:!0,theme:"default"},S.events=g.default.events,S.sources=g.default.sources,S.version="1.3.6",S.imports={delta:d.default,parchment:w.default,"core/module":_.default,"core/theme":T.default},e.expandConfig=l,e.overload=s,e.default=S},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var o=function t(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};r(this,t),this.quill=e,this.options=n};o.DEFAULTS={},e.default=o},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=n(0),a=function(t){return t&&t.__esModule?t:{default:t}}(l),s=function(t){function e(){return r(this,e),o(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return i(e,t),e}(a.default.Text);e.default=s},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){for(var n=0;n1?e-1:0),r=1;r1?n-1:0),o=1;o-1:this.whitelist.indexOf(e)>-1))},t.prototype.remove=function(t){t.removeAttribute(this.keyName)},t.prototype.value=function(t){var e=t.getAttribute(this.keyName);return this.canAdd(t,e)&&e?e:""},t}();e.default=o},function(t,e,n){function r(t){return null===t||void 0===t}function o(t){return!(!t||"object"!=typeof t||"number"!=typeof t.length)&&("function"==typeof t.copy&&"function"==typeof t.slice&&!(t.length>0&&"number"!=typeof t[0]))}function i(t,e,n){var i,c;if(r(t)||r(e))return!1;if(t.prototype!==e.prototype)return!1;if(s(t))return!!s(e)&&(t=l.call(t),e=l.call(e),u(t,e,n));if(o(t)){if(!o(e))return!1;if(t.length!==e.length)return!1;for(i=0;i=0;i--)if(f[i]!=h[i])return!1;for(i=f.length-1;i>=0;i--)if(c=f[i],!u(t[c],e[c],n))return!1;return typeof t==typeof e}var l=Array.prototype.slice,a=n(55),s=n(56),u=t.exports=function(t,e,n){return n||(n={}),t===e||(t instanceof Date&&e instanceof Date?t.getTime()===e.getTime():!t||!e||"object"!=typeof t&&"object"!=typeof e?n.strict?t===e:t==e:i(t,e,n))}},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.Code=void 0;var a=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),s=function(){function t(t,e){for(var n=0;n=t+n)){var l=this.newlineIndex(t,!0)+1,a=i-l+1,s=this.isolate(l,a),u=s.next;s.format(r,o),u instanceof e&&u.formatAt(0,t-l+n-a,r,o)}}}},{key:"insertAt",value:function(t,e,n){if(null==n){var r=this.descendant(m.default,t),o=a(r,2),i=o[0],l=o[1];i.insertAt(l,e)}}},{key:"length",value:function(){var t=this.domNode.textContent.length;return this.domNode.textContent.endsWith("\n")?t:t+1}},{key:"newlineIndex",value:function(t){if(arguments.length>1&&void 0!==arguments[1]&&arguments[1])return this.domNode.textContent.slice(0,t).lastIndexOf("\n");var e=this.domNode.textContent.slice(t).indexOf("\n");return e>-1?t+e:-1}},{key:"optimize",value:function(t){this.domNode.textContent.endsWith("\n")||this.appendChild(p.default.create("text","\n")),u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"optimize",this).call(this,t);var n=this.next;null!=n&&n.prev===this&&n.statics.blotName===this.statics.blotName&&this.statics.formats(this.domNode)===n.statics.formats(n.domNode)&&(n.optimize(t),n.moveChildren(this),n.remove())}},{key:"replace",value:function(t){u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"replace",this).call(this,t),[].slice.call(this.domNode.querySelectorAll("*")).forEach(function(t){var e=p.default.find(t);null==e?t.parentNode.removeChild(t):e instanceof p.default.Embed?e.remove():e.unwrap()})}}],[{key:"create",value:function(t){var n=u(e.__proto__||Object.getPrototypeOf(e),"create",this).call(this,t);return n.setAttribute("spellcheck",!1),n}},{key:"formats",value:function(){return!0}}]),e}(y.default);O.blotName="code-block",O.tagName="PRE",O.TAB=" ",e.Code=_,e.default=O},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=function(){function t(t,e){for(var n=0;n-1}Object.defineProperty(e,"__esModule",{value:!0}),e.sanitize=e.default=void 0;var a=function(){function t(t,e){for(var n=0;n1&&void 0!==arguments[1]&&arguments[1],n=this.container.querySelector(".ql-selected");if(t!==n&&(null!=n&&n.classList.remove("ql-selected"),null!=t&&(t.classList.add("ql-selected"),this.select.selectedIndex=[].indexOf.call(t.parentNode.children,t),t.hasAttribute("data-value")?this.label.setAttribute("data-value",t.getAttribute("data-value")):this.label.removeAttribute("data-value"),t.hasAttribute("data-label")?this.label.setAttribute("data-label",t.getAttribute("data-label")):this.label.removeAttribute("data-label"),e))){if("function"==typeof Event)this.select.dispatchEvent(new Event("change"));else if("object"===("undefined"==typeof Event?"undefined":l(Event))){var r=document.createEvent("Event");r.initEvent("change",!0,!0),this.select.dispatchEvent(r)}this.close()}}},{key:"update",value:function(){var t=void 0;if(this.select.selectedIndex>-1){var e=this.container.querySelector(".ql-picker-options").children[this.select.selectedIndex];t=this.select.options[this.select.selectedIndex],this.selectItem(e)}else this.selectItem(null);var n=null!=t&&t!==this.select.querySelector("option[selected]");this.label.classList.toggle("ql-active",n)}}]),t}();e.default=p},function(t,e,n){"use strict";function r(t){var e=a.find(t);if(null==e)try{e=a.create(t)}catch(n){e=a.create(a.Scope.INLINE),[].slice.call(t.childNodes).forEach(function(t){e.domNode.appendChild(t)}),t.parentNode&&t.parentNode.replaceChild(e.domNode,t),e.attach()}return e}var o=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var i=n(47),l=n(27),a=n(1),s=function(t){function e(e){var n=t.call(this,e)||this;return n.build(),n}return o(e,t),e.prototype.appendChild=function(t){this.insertBefore(t)},e.prototype.attach=function(){t.prototype.attach.call(this),this.children.forEach(function(t){t.attach()})},e.prototype.build=function(){var t=this;this.children=new i.default,[].slice.call(this.domNode.childNodes).reverse().forEach(function(e){try{var n=r(e);t.insertBefore(n,t.children.head||void 0)}catch(t){if(t instanceof a.ParchmentError)return;throw t}})},e.prototype.deleteAt=function(t,e){if(0===t&&e===this.length())return this.remove();this.children.forEachAt(t,e,function(t,e,n){t.deleteAt(e,n)})},e.prototype.descendant=function(t,n){var r=this.children.find(n),o=r[0],i=r[1];return null==t.blotName&&t(o)||null!=t.blotName&&o instanceof t?[o,i]:o instanceof e?o.descendant(t,i):[null,-1]},e.prototype.descendants=function(t,n,r){void 0===n&&(n=0),void 0===r&&(r=Number.MAX_VALUE);var o=[],i=r;return this.children.forEachAt(n,r,function(n,r,l){(null==t.blotName&&t(n)||null!=t.blotName&&n instanceof t)&&o.push(n),n instanceof e&&(o=o.concat(n.descendants(t,r,i))),i-=l}),o},e.prototype.detach=function(){this.children.forEach(function(t){t.detach()}),t.prototype.detach.call(this)},e.prototype.formatAt=function(t,e,n,r){this.children.forEachAt(t,e,function(t,e,o){t.formatAt(e,o,n,r)})},e.prototype.insertAt=function(t,e,n){var r=this.children.find(t),o=r[0],i=r[1];if(o)o.insertAt(i,e,n);else{var l=null==n?a.create("text",e):a.create(e,n);this.appendChild(l)}},e.prototype.insertBefore=function(t,e){if(null!=this.statics.allowedChildren&&!this.statics.allowedChildren.some(function(e){return t instanceof e}))throw new a.ParchmentError("Cannot insert "+t.statics.blotName+" into "+this.statics.blotName);t.insertInto(this,e)},e.prototype.length=function(){return this.children.reduce(function(t,e){return t+e.length()},0)},e.prototype.moveChildren=function(t,e){this.children.forEach(function(n){t.insertBefore(n,e)})},e.prototype.optimize=function(e){if(t.prototype.optimize.call(this,e),0===this.children.length)if(null!=this.statics.defaultChild){var n=a.create(this.statics.defaultChild);this.appendChild(n),n.optimize(e)}else this.remove()},e.prototype.path=function(t,n){void 0===n&&(n=!1);var r=this.children.find(t,n),o=r[0],i=r[1],l=[[this,t]];return o instanceof e?l.concat(o.path(i,n)):(null!=o&&l.push([o,i]),l)},e.prototype.removeChild=function(t){this.children.remove(t)},e.prototype.replace=function(n){n instanceof e&&n.moveChildren(this),t.prototype.replace.call(this,n)},e.prototype.split=function(t,e){if(void 0===e&&(e=!1),!e){if(0===t)return this;if(t===this.length())return this.next}var n=this.clone();return this.parent.insertBefore(n,this.next),this.children.forEachAt(t,this.length(),function(t,r,o){t=t.split(r,e),n.appendChild(t)}),n},e.prototype.unwrap=function(){this.moveChildren(this.parent,this.next),this.remove()},e.prototype.update=function(t,e){var n=this,o=[],i=[];t.forEach(function(t){t.target===n.domNode&&"childList"===t.type&&(o.push.apply(o,t.addedNodes),i.push.apply(i,t.removedNodes))}),i.forEach(function(t){if(!(null!=t.parentNode&&"IFRAME"!==t.tagName&&document.body.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_CONTAINED_BY)){var e=a.find(t);null!=e&&(null!=e.domNode.parentNode&&e.domNode.parentNode!==n.domNode||e.detach())}}),o.filter(function(t){return t.parentNode==n.domNode}).sort(function(t,e){return t===e?0:t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_FOLLOWING?1:-1}).forEach(function(t){var e=null;null!=t.nextSibling&&(e=a.find(t.nextSibling));var o=r(t);o.next==e&&null!=o.next||(null!=o.parent&&o.parent.removeChild(n),n.insertBefore(o,e||void 0))})},e}(l.default);e.default=s},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(11),i=n(28),l=n(17),a=n(1),s=function(t){function e(e){var n=t.call(this,e)||this;return n.attributes=new i.default(n.domNode),n}return r(e,t),e.formats=function(t){return"string"==typeof this.tagName||(Array.isArray(this.tagName)?t.tagName.toLowerCase():void 0)},e.prototype.format=function(t,e){var n=a.query(t);n instanceof o.default?this.attributes.attribute(n,e):e&&(null==n||t===this.statics.blotName&&this.formats()[t]===e||this.replaceWith(t,e))},e.prototype.formats=function(){var t=this.attributes.values(),e=this.statics.formats(this.domNode);return null!=e&&(t[this.statics.blotName]=e),t},e.prototype.replaceWith=function(e,n){var r=t.prototype.replaceWith.call(this,e,n);return this.attributes.copy(r),r},e.prototype.update=function(e,n){var r=this;t.prototype.update.call(this,e,n),e.some(function(t){return t.target===r.domNode&&"attributes"===t.type})&&this.attributes.build()},e.prototype.wrap=function(n,r){var o=t.prototype.wrap.call(this,n,r);return o instanceof e&&o.statics.scope===this.statics.scope&&this.attributes.move(o),o},e}(l.default);e.default=s},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(27),i=n(1),l=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r(e,t),e.value=function(t){return!0},e.prototype.index=function(t,e){return this.domNode===t||this.domNode.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_CONTAINED_BY?Math.min(e,1):-1},e.prototype.position=function(t,e){var n=[].indexOf.call(this.parent.domNode.childNodes,this.domNode);return t>0&&(n+=1),[this.parent.domNode,n]},e.prototype.value=function(){return t={},t[this.statics.blotName]=this.statics.value(this.domNode)||!0,t;var t},e.scope=i.Scope.INLINE_BLOT,e}(o.default);e.default=l},function(t,e,n){function r(t){this.ops=t,this.index=0,this.offset=0}var o=n(12),i=n(2),l={attributes:{compose:function(t,e,n){"object"!=typeof t&&(t={}),"object"!=typeof e&&(e={});var r=i(!0,{},e);n||(r=Object.keys(r).reduce(function(t,e){return null!=r[e]&&(t[e]=r[e]),t},{}));for(var o in t)void 0!==t[o]&&void 0===e[o]&&(r[o]=t[o]);return Object.keys(r).length>0?r:void 0},diff:function(t,e){"object"!=typeof t&&(t={}),"object"!=typeof e&&(e={});var n=Object.keys(t).concat(Object.keys(e)).reduce(function(n,r){return o(t[r],e[r])||(n[r]=void 0===e[r]?null:e[r]),n},{});return Object.keys(n).length>0?n:void 0},transform:function(t,e,n){if("object"!=typeof t)return e;if("object"==typeof e){if(!n)return e;var r=Object.keys(e).reduce(function(n,r){return void 0===t[r]&&(n[r]=e[r]),n},{});return Object.keys(r).length>0?r:void 0}}},iterator:function(t){return new r(t)},length:function(t){return"number"==typeof t.delete?t.delete:"number"==typeof t.retain?t.retain:"string"==typeof t.insert?t.insert.length:1}};r.prototype.hasNext=function(){return this.peekLength()<1/0},r.prototype.next=function(t){t||(t=1/0);var e=this.ops[this.index];if(e){var n=this.offset,r=l.length(e);if(t>=r-n?(t=r-n,this.index+=1,this.offset=0):this.offset+=t,"number"==typeof e.delete)return{delete:t};var o={};return e.attributes&&(o.attributes=e.attributes),"number"==typeof e.retain?o.retain=t:"string"==typeof e.insert?o.insert=e.insert.substr(n,t):o.insert=e.insert,o}return{retain:1/0}},r.prototype.peek=function(){return this.ops[this.index]},r.prototype.peekLength=function(){return this.ops[this.index]?l.length(this.ops[this.index])-this.offset:1/0},r.prototype.peekType=function(){return this.ops[this.index]?"number"==typeof this.ops[this.index].delete?"delete":"number"==typeof this.ops[this.index].retain?"retain":"insert":"retain"},t.exports=l},function(t,e){var n=function(){"use strict";function t(t,e){return null!=e&&t instanceof e}function e(n,r,o,i,c){function f(n,o){if(null===n)return null;if(0===o)return n;var y,v;if("object"!=typeof n)return n;if(t(n,a))y=new a;else if(t(n,s))y=new s;else if(t(n,u))y=new u(function(t,e){n.then(function(e){t(f(e,o-1))},function(t){e(f(t,o-1))})});else if(e.__isArray(n))y=[];else if(e.__isRegExp(n))y=new RegExp(n.source,l(n)),n.lastIndex&&(y.lastIndex=n.lastIndex);else if(e.__isDate(n))y=new Date(n.getTime());else{if(d&&Buffer.isBuffer(n))return y=new Buffer(n.length),n.copy(y),y;t(n,Error)?y=Object.create(n):void 0===i?(v=Object.getPrototypeOf(n),y=Object.create(v)):(y=Object.create(i),v=i)}if(r){var b=h.indexOf(n);if(-1!=b)return p[b];h.push(n),p.push(y)}t(n,a)&&n.forEach(function(t,e){var n=f(e,o-1),r=f(t,o-1);y.set(n,r)}),t(n,s)&&n.forEach(function(t){var e=f(t,o-1);y.add(e)});for(var g in n){var m;v&&(m=Object.getOwnPropertyDescriptor(v,g)),m&&null==m.set||(y[g]=f(n[g],o-1))}if(Object.getOwnPropertySymbols)for(var _=Object.getOwnPropertySymbols(n),g=0;g<_.length;g++){var O=_[g],w=Object.getOwnPropertyDescriptor(n,O);(!w||w.enumerable||c)&&(y[O]=f(n[O],o-1),w.enumerable||Object.defineProperty(y,O,{enumerable:!1}))}if(c)for(var x=Object.getOwnPropertyNames(n),g=0;g1&&void 0!==arguments[1]?arguments[1]:0;i(this,t),this.index=e,this.length=n},O=function(){function t(e,n){var r=this;i(this,t),this.emitter=n,this.scroll=e,this.composing=!1,this.mouseDown=!1,this.root=this.scroll.domNode,this.cursor=c.default.create("cursor",this),this.lastRange=this.savedRange=new _(0,0),this.handleComposition(),this.handleDragging(),this.emitter.listenDOM("selectionchange",document,function(){r.mouseDown||setTimeout(r.update.bind(r,v.default.sources.USER),1)}),this.emitter.on(v.default.events.EDITOR_CHANGE,function(t,e){t===v.default.events.TEXT_CHANGE&&e.length()>0&&r.update(v.default.sources.SILENT)}),this.emitter.on(v.default.events.SCROLL_BEFORE_UPDATE,function(){if(r.hasFocus()){var t=r.getNativeRange();null!=t&&t.start.node!==r.cursor.textNode&&r.emitter.once(v.default.events.SCROLL_UPDATE,function(){try{r.setNativeRange(t.start.node,t.start.offset,t.end.node,t.end.offset)}catch(t){}})}}),this.emitter.on(v.default.events.SCROLL_OPTIMIZE,function(t,e){if(e.range){var n=e.range,o=n.startNode,i=n.startOffset,l=n.endNode,a=n.endOffset;r.setNativeRange(o,i,l,a)}}),this.update(v.default.sources.SILENT)}return s(t,[{key:"handleComposition",value:function(){var t=this;this.root.addEventListener("compositionstart",function(){t.composing=!0}),this.root.addEventListener("compositionend",function(){if(t.composing=!1,t.cursor.parent){var e=t.cursor.restore();if(!e)return;setTimeout(function(){t.setNativeRange(e.startNode,e.startOffset,e.endNode,e.endOffset)},1)}})}},{key:"handleDragging",value:function(){var t=this;this.emitter.listenDOM("mousedown",document.body,function(){t.mouseDown=!0}),this.emitter.listenDOM("mouseup",document.body,function(){t.mouseDown=!1,t.update(v.default.sources.USER)})}},{key:"focus",value:function(){this.hasFocus()||(this.root.focus(),this.setRange(this.savedRange))}},{key:"format",value:function(t,e){if(null==this.scroll.whitelist||this.scroll.whitelist[t]){this.scroll.update();var n=this.getNativeRange();if(null!=n&&n.native.collapsed&&!c.default.query(t,c.default.Scope.BLOCK)){if(n.start.node!==this.cursor.textNode){var r=c.default.find(n.start.node,!1);if(null==r)return;if(r instanceof c.default.Leaf){var o=r.split(n.start.offset);r.parent.insertBefore(this.cursor,o)}else r.insertBefore(this.cursor,n.start.node);this.cursor.attach()}this.cursor.format(t,e),this.scroll.optimize(),this.setNativeRange(this.cursor.textNode,this.cursor.textNode.data.length),this.update()}}}},{key:"getBounds",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=this.scroll.length();t=Math.min(t,n-1),e=Math.min(t+e,n-1)-t;var r=void 0,o=this.scroll.leaf(t),i=a(o,2),l=i[0],s=i[1];if(null==l)return null;var u=l.position(s,!0),c=a(u,2);r=c[0],s=c[1];var f=document.createRange();if(e>0){f.setStart(r,s);var h=this.scroll.leaf(t+e),p=a(h,2);if(l=p[0],s=p[1],null==l)return null;var d=l.position(s,!0),y=a(d,2);return r=y[0],s=y[1],f.setEnd(r,s),f.getBoundingClientRect()}var v="left",b=void 0;return r instanceof Text?(s0&&(v="right")),{bottom:b.top+b.height,height:b.height,left:b[v],right:b[v],top:b.top,width:0}}},{key:"getNativeRange",value:function(){var t=document.getSelection();if(null==t||t.rangeCount<=0)return null;var e=t.getRangeAt(0);if(null==e)return null;var n=this.normalizeNative(e);return m.info("getNativeRange",n),n}},{key:"getRange",value:function(){var t=this.getNativeRange();return null==t?[null,null]:[this.normalizedToRange(t),t]}},{key:"hasFocus",value:function(){return document.activeElement===this.root}},{key:"normalizedToRange",value:function(t){var e=this,n=[[t.start.node,t.start.offset]];t.native.collapsed||n.push([t.end.node,t.end.offset]);var r=n.map(function(t){var n=a(t,2),r=n[0],o=n[1],i=c.default.find(r,!0),l=i.offset(e.scroll);return 0===o?l:i instanceof c.default.Container?l+i.length():l+i.index(r,o)}),i=Math.min(Math.max.apply(Math,o(r)),this.scroll.length()-1),l=Math.min.apply(Math,[i].concat(o(r)));return new _(l,i-l)}},{key:"normalizeNative",value:function(t){if(!l(this.root,t.startContainer)||!t.collapsed&&!l(this.root,t.endContainer))return null;var e={start:{node:t.startContainer,offset:t.startOffset},end:{node:t.endContainer,offset:t.endOffset},native:t};return[e.start,e.end].forEach(function(t){for(var e=t.node,n=t.offset;!(e instanceof Text)&&e.childNodes.length>0;)if(e.childNodes.length>n)e=e.childNodes[n],n=0;else{if(e.childNodes.length!==n)break;e=e.lastChild,n=e instanceof Text?e.data.length:e.childNodes.length+1}t.node=e,t.offset=n}),e}},{key:"rangeToNative",value:function(t){var e=this,n=t.collapsed?[t.index]:[t.index,t.index+t.length],r=[],o=this.scroll.length();return n.forEach(function(t,n){t=Math.min(o-1,t);var i=void 0,l=e.scroll.leaf(t),s=a(l,2),u=s[0],c=s[1],f=u.position(c,0!==n),h=a(f,2);i=h[0],c=h[1],r.push(i,c)}),r.length<2&&(r=r.concat(r)),r}},{key:"scrollIntoView",value:function(t){var e=this.lastRange;if(null!=e){var n=this.getBounds(e.index,e.length);if(null!=n){var r=this.scroll.length()-1,o=this.scroll.line(Math.min(e.index,r)),i=a(o,1),l=i[0],s=l;if(e.length>0){var u=this.scroll.line(Math.min(e.index+e.length,r));s=a(u,1)[0]}if(null!=l&&null!=s){var c=t.getBoundingClientRect();n.topc.bottom&&(t.scrollTop+=n.bottom-c.bottom)}}}}},{key:"setNativeRange",value:function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:t,r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:e,o=arguments.length>4&&void 0!==arguments[4]&&arguments[4];if(m.info("setNativeRange",t,e,n,r),null==t||null!=this.root.parentNode&&null!=t.parentNode&&null!=n.parentNode){var i=document.getSelection();if(null!=i)if(null!=t){this.hasFocus()||this.root.focus();var l=(this.getNativeRange()||{}).native;if(null==l||o||t!==l.startContainer||e!==l.startOffset||n!==l.endContainer||r!==l.endOffset){"BR"==t.tagName&&(e=[].indexOf.call(t.parentNode.childNodes,t),t=t.parentNode),"BR"==n.tagName&&(r=[].indexOf.call(n.parentNode.childNodes,n),n=n.parentNode);var a=document.createRange();a.setStart(t,e),a.setEnd(n,r),i.removeAllRanges(),i.addRange(a)}}else i.removeAllRanges(),this.root.blur(),document.body.focus()}}},{key:"setRange",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:v.default.sources.API;if("string"==typeof e&&(n=e,e=!1),m.info("setRange",t),null!=t){var r=this.rangeToNative(t);this.setNativeRange.apply(this,o(r).concat([e]))}else this.setNativeRange(null);this.update(n)}},{key:"update",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:v.default.sources.USER,e=this.lastRange,n=this.getRange(),r=a(n,2),o=r[0],i=r[1];if(this.lastRange=o,null!=this.lastRange&&(this.savedRange=this.lastRange),!(0,d.default)(e,this.lastRange)){var l;!this.composing&&null!=i&&i.native.collapsed&&i.start.node!==this.cursor.textNode&&this.cursor.restore();var s=[v.default.events.SELECTION_CHANGE,(0,h.default)(this.lastRange),(0,h.default)(e),t];if((l=this.emitter).emit.apply(l,[v.default.events.EDITOR_CHANGE].concat(s)),t!==v.default.sources.SILENT){var u;(u=this.emitter).emit.apply(u,s)}}}}]),t}();e.Range=_,e.default=O},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var a=n(0),s=r(a),u=n(3),c=r(u),f=function(t){function e(){return o(this,e),i(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return l(e,t),e}(s.default.Container);f.allowedChildren=[c.default,u.BlockEmbed,f],e.default=f},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0}),e.ColorStyle=e.ColorClass=e.ColorAttributor=void 0;var l=function(){function t(t,e){for(var n=0;n1){var u=o.formats(),c=this.quill.getFormat(t.index-1,1);i=A.default.attributes.diff(u,c)||{}}}var f=/[\uD800-\uDBFF][\uDC00-\uDFFF]$/.test(e.prefix)?2:1;this.quill.deleteText(t.index-f,f,S.default.sources.USER),Object.keys(i).length>0&&this.quill.formatLine(t.index-f,f,i,S.default.sources.USER),this.quill.focus()}}function c(t,e){var n=/^[\uD800-\uDBFF][\uDC00-\uDFFF]/.test(e.suffix)?2:1;if(!(t.index>=this.quill.getLength()-n)){var r={},o=0,i=this.quill.getLine(t.index),l=b(i,1),a=l[0];if(e.offset>=a.length()-1){var s=this.quill.getLine(t.index+1),u=b(s,1),c=u[0];if(c){var f=a.formats(),h=this.quill.getFormat(t.index,1);r=A.default.attributes.diff(f,h)||{},o=c.length()}}this.quill.deleteText(t.index,n,S.default.sources.USER),Object.keys(r).length>0&&this.quill.formatLine(t.index+o-1,n,r,S.default.sources.USER)}}function f(t){var e=this.quill.getLines(t),n={};if(e.length>1){var r=e[0].formats(),o=e[e.length-1].formats();n=A.default.attributes.diff(o,r)||{}}this.quill.deleteText(t,S.default.sources.USER),Object.keys(n).length>0&&this.quill.formatLine(t.index,1,n,S.default.sources.USER),this.quill.setSelection(t.index,S.default.sources.SILENT),this.quill.focus()}function h(t,e){var n=this;t.length>0&&this.quill.scroll.deleteAt(t.index,t.length);var r=Object.keys(e.format).reduce(function(t,n){return T.default.query(n,T.default.Scope.BLOCK)&&!Array.isArray(e.format[n])&&(t[n]=e.format[n]),t},{});this.quill.insertText(t.index,"\n",r,S.default.sources.USER),this.quill.setSelection(t.index+1,S.default.sources.SILENT),this.quill.focus(),Object.keys(e.format).forEach(function(t){null==r[t]&&(Array.isArray(e.format[t])||"link"!==t&&n.quill.format(t,e.format[t],S.default.sources.USER))})}function p(t){return{key:D.keys.TAB,shiftKey:!t,format:{"code-block":!0},handler:function(e){var n=T.default.query("code-block"),r=e.index,o=e.length,i=this.quill.scroll.descendant(n,r),l=b(i,2),a=l[0],s=l[1];if(null!=a){var u=this.quill.getIndex(a),c=a.newlineIndex(s,!0)+1,f=a.newlineIndex(u+s+o),h=a.domNode.textContent.slice(c,f).split("\n");s=0,h.forEach(function(e,i){t?(a.insertAt(c+s,n.TAB),s+=n.TAB.length,0===i?r+=n.TAB.length:o+=n.TAB.length):e.startsWith(n.TAB)&&(a.deleteAt(c+s,n.TAB.length),s-=n.TAB.length,0===i?r-=n.TAB.length:o-=n.TAB.length),s+=e.length+1}),this.quill.update(S.default.sources.USER),this.quill.setSelection(r,o,S.default.sources.SILENT)}}}}function d(t){return{key:t[0].toUpperCase(),shortKey:!0,handler:function(e,n){this.quill.format(t,!n.format[t],S.default.sources.USER)}}}function y(t){if("string"==typeof t||"number"==typeof t)return y({key:t});if("object"===(void 0===t?"undefined":v(t))&&(t=(0,_.default)(t,!1)),"string"==typeof t.key)if(null!=D.keys[t.key.toUpperCase()])t.key=D.keys[t.key.toUpperCase()];else{if(1!==t.key.length)return null;t.key=t.key.toUpperCase().charCodeAt(0)}return t.shortKey&&(t[B]=t.shortKey,delete t.shortKey),t}Object.defineProperty(e,"__esModule",{value:!0}),e.SHORTKEY=e.default=void 0;var v="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},b=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),g=function(){function t(t,e){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=y(t);if(null==r||null==r.key)return I.warn("Attempted to add invalid keyboard binding",r);"function"==typeof e&&(e={handler:e}),"function"==typeof n&&(n={handler:n}),r=(0,k.default)(r,e,n),this.bindings[r.key]=this.bindings[r.key]||[],this.bindings[r.key].push(r)}},{key:"listen",value:function(){var t=this;this.quill.root.addEventListener("keydown",function(n){if(!n.defaultPrevented){var r=n.which||n.keyCode,o=(t.bindings[r]||[]).filter(function(t){return e.match(n,t)});if(0!==o.length){var i=t.quill.getSelection();if(null!=i&&t.quill.hasFocus()){var l=t.quill.getLine(i.index),a=b(l,2),s=a[0],u=a[1],c=t.quill.getLeaf(i.index),f=b(c,2),h=f[0],p=f[1],d=0===i.length?[h,p]:t.quill.getLeaf(i.index+i.length),y=b(d,2),g=y[0],m=y[1],_=h instanceof T.default.Text?h.value().slice(0,p):"",O=g instanceof T.default.Text?g.value().slice(m):"",x={collapsed:0===i.length,empty:0===i.length&&s.length()<=1,format:t.quill.getFormat(i),offset:u,prefix:_,suffix:O};o.some(function(e){if(null!=e.collapsed&&e.collapsed!==x.collapsed)return!1;if(null!=e.empty&&e.empty!==x.empty)return!1;if(null!=e.offset&&e.offset!==x.offset)return!1;if(Array.isArray(e.format)){if(e.format.every(function(t){return null==x.format[t]}))return!1}else if("object"===v(e.format)&&!Object.keys(e.format).every(function(t){return!0===e.format[t]?null!=x.format[t]:!1===e.format[t]?null==x.format[t]:(0,w.default)(e.format[t],x.format[t])}))return!1;return!(null!=e.prefix&&!e.prefix.test(x.prefix))&&(!(null!=e.suffix&&!e.suffix.test(x.suffix))&&!0!==e.handler.call(t,i,x))})&&n.preventDefault()}}}})}}]),e}(R.default);D.keys={BACKSPACE:8,TAB:9,ENTER:13,ESCAPE:27,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46},D.DEFAULTS={bindings:{bold:d("bold"),italic:d("italic"),underline:d("underline"),indent:{key:D.keys.TAB,format:["blockquote","indent","list"],handler:function(t,e){if(e.collapsed&&0!==e.offset)return!0;this.quill.format("indent","+1",S.default.sources.USER)}},outdent:{key:D.keys.TAB,shiftKey:!0,format:["blockquote","indent","list"],handler:function(t,e){if(e.collapsed&&0!==e.offset)return!0;this.quill.format("indent","-1",S.default.sources.USER)}},"outdent backspace":{key:D.keys.BACKSPACE,collapsed:!0,shiftKey:null,metaKey:null,ctrlKey:null,altKey:null,format:["indent","list"],offset:0,handler:function(t,e){null!=e.format.indent?this.quill.format("indent","-1",S.default.sources.USER):null!=e.format.list&&this.quill.format("list",!1,S.default.sources.USER)}},"indent code-block":p(!0),"outdent code-block":p(!1),"remove tab":{key:D.keys.TAB,shiftKey:!0,collapsed:!0,prefix:/\t$/,handler:function(t){this.quill.deleteText(t.index-1,1,S.default.sources.USER)}},tab:{key:D.keys.TAB,handler:function(t){this.quill.history.cutoff();var e=(new N.default).retain(t.index).delete(t.length).insert("\t");this.quill.updateContents(e,S.default.sources.USER),this.quill.history.cutoff(),this.quill.setSelection(t.index+1,S.default.sources.SILENT)}},"list empty enter":{key:D.keys.ENTER,collapsed:!0,format:["list"],empty:!0,handler:function(t,e){this.quill.format("list",!1,S.default.sources.USER),e.format.indent&&this.quill.format("indent",!1,S.default.sources.USER)}},"checklist enter":{key:D.keys.ENTER,collapsed:!0,format:{list:"checked"},handler:function(t){var e=this.quill.getLine(t.index),n=b(e,2),r=n[0],o=n[1],i=(0,k.default)({},r.formats(),{list:"checked"}),l=(new N.default).retain(t.index).insert("\n",i).retain(r.length()-o-1).retain(1,{list:"unchecked"});this.quill.updateContents(l,S.default.sources.USER),this.quill.setSelection(t.index+1,S.default.sources.SILENT),this.quill.scrollIntoView()}},"header enter":{key:D.keys.ENTER,collapsed:!0,format:["header"],suffix:/^$/,handler:function(t,e){var n=this.quill.getLine(t.index),r=b(n,2),o=r[0],i=r[1],l=(new N.default).retain(t.index).insert("\n",e.format).retain(o.length()-i-1).retain(1,{header:null});this.quill.updateContents(l,S.default.sources.USER),this.quill.setSelection(t.index+1,S.default.sources.SILENT),this.quill.scrollIntoView()}},"list autofill":{key:" ",collapsed:!0,format:{list:!1},prefix:/^\s*?(\d+\.|-|\*|\[ ?\]|\[x\])$/,handler:function(t,e){var n=e.prefix.length,r=this.quill.getLine(t.index),o=b(r,2),i=o[0],l=o[1];if(l>n)return!0;var a=void 0;switch(e.prefix.trim()){case"[]":case"[ ]":a="unchecked";break;case"[x]":a="checked";break;case"-":case"*":a="bullet";break;default:a="ordered"}this.quill.insertText(t.index," ",S.default.sources.USER),this.quill.history.cutoff();var s=(new N.default).retain(t.index-l).delete(n+1).retain(i.length()-2-l).retain(1,{list:a});this.quill.updateContents(s,S.default.sources.USER),this.quill.history.cutoff(),this.quill.setSelection(t.index-n,S.default.sources.SILENT)}},"code exit":{key:D.keys.ENTER,collapsed:!0,format:["code-block"],prefix:/\n\n$/,suffix:/^\s+$/,handler:function(t){var e=this.quill.getLine(t.index),n=b(e,2),r=n[0],o=n[1],i=(new N.default).retain(t.index+r.length()-o-2).retain(1,{"code-block":null}).delete(1);this.quill.updateContents(i,S.default.sources.USER)}},"embed left":s(D.keys.LEFT,!1),"embed left shift":s(D.keys.LEFT,!0),"embed right":s(D.keys.RIGHT,!1),"embed right shift":s(D.keys.RIGHT,!0)}},e.default=D,e.SHORTKEY=B},function(t,e,n){"use strict";t.exports={align:{"":n(75),center:n(76),right:n(77),justify:n(78)},background:n(79),blockquote:n(80),bold:n(81),clean:n(82),code:n(40),"code-block":n(40),color:n(83),direction:{"":n(84),rtl:n(85)},float:{center:n(86),full:n(87),left:n(88),right:n(89)},formula:n(90),header:{1:n(91),2:n(92)},italic:n(93),image:n(94),indent:{"+1":n(95),"-1":n(96)},link:n(97),list:{ordered:n(98),bullet:n(99),check:n(100)},script:{sub:n(101),super:n(102)},strike:n(103),underline:n(104),video:n(105)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(1),o=function(){function t(t){this.domNode=t,this.domNode[r.DATA_KEY]={blot:this}}return Object.defineProperty(t.prototype,"statics",{get:function(){return this.constructor},enumerable:!0,configurable:!0}),t.create=function(t){if(null==this.tagName)throw new r.ParchmentError("Blot definition missing tagName");var e;return Array.isArray(this.tagName)?("string"==typeof t&&(t=t.toUpperCase(),parseInt(t).toString()===t&&(t=parseInt(t))),e="number"==typeof t?document.createElement(this.tagName[t-1]):this.tagName.indexOf(t)>-1?document.createElement(t):document.createElement(this.tagName[0])):e=document.createElement(this.tagName),this.className&&e.classList.add(this.className),e},t.prototype.attach=function(){null!=this.parent&&(this.scroll=this.parent.scroll)},t.prototype.clone=function(){var t=this.domNode.cloneNode(!1);return r.create(t)},t.prototype.detach=function(){null!=this.parent&&this.parent.removeChild(this),delete this.domNode[r.DATA_KEY]},t.prototype.deleteAt=function(t,e){this.isolate(t,e).remove()},t.prototype.formatAt=function(t,e,n,o){var i=this.isolate(t,e);if(null!=r.query(n,r.Scope.BLOT)&&o)i.wrap(n,o);else if(null!=r.query(n,r.Scope.ATTRIBUTE)){var l=r.create(this.statics.scope);i.wrap(l),l.format(n,o)}},t.prototype.insertAt=function(t,e,n){var o=null==n?r.create("text",e):r.create(e,n),i=this.split(t);this.parent.insertBefore(o,i)},t.prototype.insertInto=function(t,e){void 0===e&&(e=null),null!=this.parent&&this.parent.children.remove(this);var n=null;t.children.insertBefore(this,e),null!=e&&(n=e.domNode),this.domNode.parentNode==t.domNode&&this.domNode.nextSibling==n||t.domNode.insertBefore(this.domNode,n),this.parent=t,this.attach()},t.prototype.isolate=function(t,e){var n=this.split(t);return n.split(e),n},t.prototype.length=function(){return 1},t.prototype.offset=function(t){return void 0===t&&(t=this.parent),null==this.parent||this==t?0:this.parent.children.offset(this)+this.parent.offset(t)},t.prototype.optimize=function(t){null!=this.domNode[r.DATA_KEY]&&delete this.domNode[r.DATA_KEY].mutations},t.prototype.remove=function(){null!=this.domNode.parentNode&&this.domNode.parentNode.removeChild(this.domNode),this.detach()},t.prototype.replace=function(t){null!=t.parent&&(t.parent.insertBefore(this,t.next),t.remove())},t.prototype.replaceWith=function(t,e){var n="string"==typeof t?r.create(t,e):t;return n.replace(this),n},t.prototype.split=function(t,e){return 0===t?this:this.next},t.prototype.update=function(t,e){},t.prototype.wrap=function(t,e){var n="string"==typeof t?r.create(t,e):t;return null!=this.parent&&this.parent.insertBefore(n,this.next),n.appendChild(this),n},t.blotName="abstract",t}();e.default=o},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(11),o=n(29),i=n(30),l=n(1),a=function(){function t(t){this.attributes={},this.domNode=t,this.build()}return t.prototype.attribute=function(t,e){e?t.add(this.domNode,e)&&(null!=t.value(this.domNode)?this.attributes[t.attrName]=t:delete this.attributes[t.attrName]):(t.remove(this.domNode),delete this.attributes[t.attrName])},t.prototype.build=function(){var t=this;this.attributes={};var e=r.default.keys(this.domNode),n=o.default.keys(this.domNode),a=i.default.keys(this.domNode);e.concat(n).concat(a).forEach(function(e){var n=l.query(e,l.Scope.ATTRIBUTE);n instanceof r.default&&(t.attributes[n.attrName]=n)})},t.prototype.copy=function(t){var e=this;Object.keys(this.attributes).forEach(function(n){var r=e.attributes[n].value(e.domNode);t.format(n,r)})},t.prototype.move=function(t){var e=this;this.copy(t),Object.keys(this.attributes).forEach(function(t){e.attributes[t].remove(e.domNode)}),this.attributes={}},t.prototype.values=function(){var t=this;return Object.keys(this.attributes).reduce(function(e,n){return e[n]=t.attributes[n].value(t.domNode),e},{})},t}();e.default=a},function(t,e,n){"use strict";function r(t,e){return(t.getAttribute("class")||"").split(/\s+/).filter(function(t){return 0===t.indexOf(e+"-")})}var o=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var i=n(11),l=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return o(e,t),e.keys=function(t){return(t.getAttribute("class")||"").split(/\s+/).map(function(t){return t.split("-").slice(0,-1).join("-")})},e.prototype.add=function(t,e){return!!this.canAdd(t,e)&&(this.remove(t),t.classList.add(this.keyName+"-"+e),!0)},e.prototype.remove=function(t){r(t,this.keyName).forEach(function(e){t.classList.remove(e)}),0===t.classList.length&&t.removeAttribute("class")},e.prototype.value=function(t){var e=r(t,this.keyName)[0]||"",n=e.slice(this.keyName.length+1);return this.canAdd(t,n)?n:""},e}(i.default);e.default=l},function(t,e,n){"use strict";function r(t){var e=t.split("-"),n=e.slice(1).map(function(t){return t[0].toUpperCase()+t.slice(1)}).join("");return e[0]+n}var o=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var i=n(11),l=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return o(e,t),e.keys=function(t){return(t.getAttribute("style")||"").split(";").map(function(t){return t.split(":")[0].trim()})},e.prototype.add=function(t,e){return!!this.canAdd(t,e)&&(t.style[r(this.keyName)]=e,!0)},e.prototype.remove=function(t){t.style[r(this.keyName)]="",t.getAttribute("style")||t.removeAttribute("style")},e.prototype.value=function(t){var e=t.style[r(this.keyName)];return this.canAdd(t,e)?e:""},e}(i.default);e.default=l},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),s=function t(e,n,r){null===e&&(e=Function.prototype);var o=Object.getOwnPropertyDescriptor(e,n);if(void 0===o){var i=Object.getPrototypeOf(e);return null===i?void 0:t(i,n,r)}if("value"in o)return o.value;var l=o.get;if(void 0!==l)return l.call(r)},u=function(){function t(t,e){for(var n=0;n '},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=function(){function t(t,e){for(var n=0;nr.right&&(i=r.right-o.right,this.root.style.left=e+i+"px"),o.leftr.bottom){var l=o.bottom-o.top,a=t.bottom-t.top+l;this.root.style.top=n-a+"px",this.root.classList.add("ql-flip")}return i}},{key:"show",value:function(){this.root.classList.remove("ql-editing"),this.root.classList.remove("ql-hidden")}}]),t}();e.default=i},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function a(t){var e=t.match(/^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtube\.com\/watch.*v=([a-zA-Z0-9_-]+)/)||t.match(/^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtu\.be\/([a-zA-Z0-9_-]+)/);return e?(e[1]||"https")+"://www.youtube.com/embed/"+e[2]+"?showinfo=0":(e=t.match(/^(?:(https?):\/\/)?(?:www\.)?vimeo\.com\/(\d+)/))?(e[1]||"https")+"://player.vimeo.com/video/"+e[2]+"/":t}function s(t,e){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];e.forEach(function(e){var r=document.createElement("option");e===n?r.setAttribute("selected","selected"):r.setAttribute("value",e),t.appendChild(r)})}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.BaseTooltip=void 0;var u=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:"link",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;this.root.classList.remove("ql-hidden"),this.root.classList.add("ql-editing"),null!=e?this.textbox.value=e:t!==this.root.getAttribute("data-mode")&&(this.textbox.value=""),this.position(this.quill.getBounds(this.quill.selection.savedRange)),this.textbox.select(),this.textbox.setAttribute("placeholder",this.textbox.getAttribute("data-"+t)||""),this.root.setAttribute("data-mode",t)}},{key:"restoreFocus",value:function(){var t=this.quill.scrollingContainer.scrollTop;this.quill.focus(),this.quill.scrollingContainer.scrollTop=t}},{key:"save",value:function(){var t=this.textbox.value;switch(this.root.getAttribute("data-mode")){case"link":var e=this.quill.root.scrollTop;this.linkRange?(this.quill.formatText(this.linkRange,"link",t,v.default.sources.USER),delete this.linkRange):(this.restoreFocus(),this.quill.format("link",t,v.default.sources.USER)),this.quill.root.scrollTop=e;break;case"video":t=a(t);case"formula":if(!t)break;var n=this.quill.getSelection(!0);if(null!=n){var r=n.index+n.length;this.quill.insertEmbed(r,this.root.getAttribute("data-mode"),t,v.default.sources.USER),"formula"===this.root.getAttribute("data-mode")&&this.quill.insertText(r+1," ",v.default.sources.USER),this.quill.setSelection(r+2,v.default.sources.USER)}}this.textbox.value="",this.hide()}}]),e}(A.default);e.BaseTooltip=M,e.default=L},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(e,"__esModule",{value:!0});var o=n(46),i=r(o),l=n(34),a=n(36),s=n(62),u=n(63),c=r(u),f=n(64),h=r(f),p=n(65),d=r(p),y=n(35),v=n(24),b=n(37),g=n(38),m=n(39),_=r(m),O=n(66),w=r(O),x=n(15),k=r(x),E=n(67),N=r(E),j=n(68),A=r(j),q=n(69),T=r(q),P=n(70),S=r(P),C=n(71),L=r(C),M=n(13),R=r(M),I=n(72),B=r(I),D=n(73),U=r(D),F=n(74),H=r(F),K=n(26),z=r(K),Z=n(16),V=r(Z),W=n(41),G=r(W),Y=n(42),X=r(Y),$=n(43),Q=r($),J=n(107),tt=r(J),et=n(108),nt=r(et);i.default.register({"attributors/attribute/direction":a.DirectionAttribute,"attributors/class/align":l.AlignClass,"attributors/class/background":y.BackgroundClass,"attributors/class/color":v.ColorClass,"attributors/class/direction":a.DirectionClass,"attributors/class/font":b.FontClass,"attributors/class/size":g.SizeClass,"attributors/style/align":l.AlignStyle,"attributors/style/background":y.BackgroundStyle,"attributors/style/color":v.ColorStyle,"attributors/style/direction":a.DirectionStyle,"attributors/style/font":b.FontStyle,"attributors/style/size":g.SizeStyle},!0),i.default.register({"formats/align":l.AlignClass,"formats/direction":a.DirectionClass,"formats/indent":s.IndentClass,"formats/background":y.BackgroundStyle,"formats/color":v.ColorStyle,"formats/font":b.FontClass,"formats/size":g.SizeClass,"formats/blockquote":c.default,"formats/code-block":R.default,"formats/header":h.default,"formats/list":d.default,"formats/bold":_.default,"formats/code":M.Code,"formats/italic":w.default,"formats/link":k.default,"formats/script":N.default,"formats/strike":A.default,"formats/underline":T.default,"formats/image":S.default,"formats/video":L.default,"formats/list/item":p.ListItem,"modules/formula":B.default,"modules/syntax":U.default,"modules/toolbar":H.default,"themes/bubble":tt.default,"themes/snow":nt.default,"ui/icons":z.default,"ui/picker":V.default,"ui/icon-picker":X.default,"ui/color-picker":G.default,"ui/tooltip":Q.default},!0),e.default=i.default},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(e,"__esModule",{value:!0});var o=n(0),i=r(o),l=n(6),a=r(l),s=n(3),u=r(s),c=n(14),f=r(c),h=n(23),p=r(h),d=n(31),y=r(d),v=n(33),b=r(v),g=n(5),m=r(g),_=n(59),O=r(_),w=n(8),x=r(w),k=n(60),E=r(k),N=n(61),j=r(N),A=n(25),q=r(A);a.default.register({"blots/block":u.default,"blots/block/embed":s.BlockEmbed,"blots/break":f.default,"blots/container":p.default,"blots/cursor":y.default,"blots/embed":b.default,"blots/inline":m.default,"blots/scroll":O.default,"blots/text":x.default,"modules/clipboard":E.default,"modules/history":j.default,"modules/keyboard":q.default}),i.default.register(u.default,f.default,y.default,m.default,O.default,x.default),e.default=a.default},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function t(){this.head=this.tail=null,this.length=0}return t.prototype.append=function(){for(var t=[],e=0;e1&&this.append.apply(this,t.slice(1))},t.prototype.contains=function(t){for(var e,n=this.iterator();e=n();)if(e===t)return!0;return!1},t.prototype.insertBefore=function(t,e){t&&(t.next=e,null!=e?(t.prev=e.prev,null!=e.prev&&(e.prev.next=t),e.prev=t,e===this.head&&(this.head=t)):null!=this.tail?(this.tail.next=t,t.prev=this.tail,this.tail=t):(t.prev=null,this.head=this.tail=t),this.length+=1)},t.prototype.offset=function(t){for(var e=0,n=this.head;null!=n;){if(n===t)return e;e+=n.length(),n=n.next}return-1},t.prototype.remove=function(t){this.contains(t)&&(null!=t.prev&&(t.prev.next=t.next),null!=t.next&&(t.next.prev=t.prev),t===this.head&&(this.head=t.next),t===this.tail&&(this.tail=t.prev),this.length-=1)},t.prototype.iterator=function(t){return void 0===t&&(t=this.head),function(){var e=t;return null!=t&&(t=t.next),e}},t.prototype.find=function(t,e){void 0===e&&(e=!1);for(var n,r=this.iterator();n=r();){var o=n.length();if(ta?n(r,t-a,Math.min(e,a+u-t)):n(r,0,Math.min(u,t+e-a)),a+=u}},t.prototype.map=function(t){return this.reduce(function(e,n){return e.push(t(n)),e},[])},t.prototype.reduce=function(t,e){for(var n,r=this.iterator();n=r();)e=t(e,n);return e},t}();e.default=r},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(17),i=n(1),l={attributes:!0,characterData:!0,characterDataOldValue:!0,childList:!0,subtree:!0},a=function(t){function e(e){var n=t.call(this,e)||this;return n.scroll=n,n.observer=new MutationObserver(function(t){n.update(t)}),n.observer.observe(n.domNode,l),n.attach(),n}return r(e,t),e.prototype.detach=function(){t.prototype.detach.call(this),this.observer.disconnect()},e.prototype.deleteAt=function(e,n){this.update(),0===e&&n===this.length()?this.children.forEach(function(t){t.remove()}):t.prototype.deleteAt.call(this,e,n)},e.prototype.formatAt=function(e,n,r,o){this.update(),t.prototype.formatAt.call(this,e,n,r,o)},e.prototype.insertAt=function(e,n,r){this.update(),t.prototype.insertAt.call(this,e,n,r)},e.prototype.optimize=function(e,n){var r=this;void 0===e&&(e=[]),void 0===n&&(n={}),t.prototype.optimize.call(this,n);for(var l=[].slice.call(this.observer.takeRecords());l.length>0;)e.push(l.pop());for(var a=function(t,e){void 0===e&&(e=!0),null!=t&&t!==r&&null!=t.domNode.parentNode&&(null==t.domNode[i.DATA_KEY].mutations&&(t.domNode[i.DATA_KEY].mutations=[]),e&&a(t.parent))},s=function(t){null!=t.domNode[i.DATA_KEY]&&null!=t.domNode[i.DATA_KEY].mutations&&(t instanceof o.default&&t.children.forEach(s),t.optimize(n))},u=e,c=0;u.length>0;c+=1){if(c>=100)throw new Error("[Parchment] Maximum optimize iterations reached");for(u.forEach(function(t){var e=i.find(t.target,!0);null!=e&&(e.domNode===t.target&&("childList"===t.type?(a(i.find(t.previousSibling,!1)),[].forEach.call(t.addedNodes,function(t){var e=i.find(t,!1);a(e,!1),e instanceof o.default&&e.children.forEach(function(t){a(t,!1)})})):"attributes"===t.type&&a(e.prev)),a(e))}),this.children.forEach(s),u=[].slice.call(this.observer.takeRecords()),l=u.slice();l.length>0;)e.push(l.pop())}},e.prototype.update=function(e,n){var r=this;void 0===n&&(n={}),e=e||this.observer.takeRecords(),e.map(function(t){var e=i.find(t.target,!0);return null==e?null:null==e.domNode[i.DATA_KEY].mutations?(e.domNode[i.DATA_KEY].mutations=[t],e):(e.domNode[i.DATA_KEY].mutations.push(t),null)}).forEach(function(t){null!=t&&t!==r&&null!=t.domNode[i.DATA_KEY]&&t.update(t.domNode[i.DATA_KEY].mutations||[],n)}),null!=this.domNode[i.DATA_KEY].mutations&&t.prototype.update.call(this,this.domNode[i.DATA_KEY].mutations,n),this.optimize(e,n)},e.blotName="scroll",e.defaultChild="block",e.scope=i.Scope.BLOCK_BLOT,e.tagName="DIV",e}(o.default);e.default=a},function(t,e,n){"use strict";function r(t,e){if(Object.keys(t).length!==Object.keys(e).length)return!1;for(var n in t)if(t[n]!==e[n])return!1;return!0}var o=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var i=n(18),l=n(1),a=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return o(e,t),e.formats=function(n){if(n.tagName!==e.tagName)return t.formats.call(this,n)},e.prototype.format=function(n,r){var o=this;n!==this.statics.blotName||r?t.prototype.format.call(this,n,r):(this.children.forEach(function(t){t instanceof i.default||(t=t.wrap(e.blotName,!0)),o.attributes.copy(t)}),this.unwrap())},e.prototype.formatAt=function(e,n,r,o){if(null!=this.formats()[r]||l.query(r,l.Scope.ATTRIBUTE)){this.isolate(e,n).format(r,o)}else t.prototype.formatAt.call(this,e,n,r,o)},e.prototype.optimize=function(n){t.prototype.optimize.call(this,n);var o=this.formats();if(0===Object.keys(o).length)return this.unwrap();var i=this.next;i instanceof e&&i.prev===this&&r(o,i.formats())&&(i.moveChildren(this),i.remove())},e.blotName="inline",e.scope=l.Scope.INLINE_BLOT,e.tagName="SPAN",e}(i.default);e.default=a},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(18),i=n(1),l=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r(e,t),e.formats=function(n){var r=i.query(e.blotName).tagName;if(n.tagName!==r)return t.formats.call(this,n)},e.prototype.format=function(n,r){null!=i.query(n,i.Scope.BLOCK)&&(n!==this.statics.blotName||r?t.prototype.format.call(this,n,r):this.replaceWith(e.blotName))},e.prototype.formatAt=function(e,n,r,o){null!=i.query(r,i.Scope.BLOCK)?this.format(r,o):t.prototype.formatAt.call(this,e,n,r,o)},e.prototype.insertAt=function(e,n,r){if(null==r||null!=i.query(n,i.Scope.INLINE))t.prototype.insertAt.call(this,e,n,r);else{var o=this.split(e),l=i.create(n,r);o.parent.insertBefore(l,o)}},e.prototype.update=function(e,n){navigator.userAgent.match(/Trident/)?this.build():t.prototype.update.call(this,e,n)},e.blotName="block",e.scope=i.Scope.BLOCK_BLOT,e.tagName="P",e}(o.default);e.default=l},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(19),i=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r(e,t),e.formats=function(t){},e.prototype.format=function(e,n){t.prototype.formatAt.call(this,0,this.length(),e,n)},e.prototype.formatAt=function(e,n,r,o){0===e&&n===this.length()?this.format(r,o):t.prototype.formatAt.call(this,e,n,r,o)},e.prototype.formats=function(){return this.statics.formats(this.domNode)},e}(o.default);e.default=i},function(t,e,n){"use strict";var r=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function r(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}();Object.defineProperty(e,"__esModule",{value:!0});var o=n(19),i=n(1),l=function(t){function e(e){var n=t.call(this,e)||this;return n.text=n.statics.value(n.domNode),n}return r(e,t),e.create=function(t){return document.createTextNode(t)},e.value=function(t){var e=t.data;return e.normalize&&(e=e.normalize()),e},e.prototype.deleteAt=function(t,e){this.domNode.data=this.text=this.text.slice(0,t)+this.text.slice(t+e)},e.prototype.index=function(t,e){return this.domNode===t?e:-1},e.prototype.insertAt=function(e,n,r){null==r?(this.text=this.text.slice(0,e)+n+this.text.slice(e),this.domNode.data=this.text):t.prototype.insertAt.call(this,e,n,r)},e.prototype.length=function(){return this.text.length},e.prototype.optimize=function(n){t.prototype.optimize.call(this,n),this.text=this.statics.value(this.domNode),0===this.text.length?this.remove():this.next instanceof e&&this.next.prev===this&&(this.insertAt(this.length(),this.next.value()),this.next.remove())},e.prototype.position=function(t,e){return void 0===e&&(e=!1),[this.domNode,t]},e.prototype.split=function(t,e){if(void 0===e&&(e=!1),!e){if(0===t)return this;if(t===this.length())return this.next}var n=i.create(this.domNode.splitText(t));return this.parent.insertBefore(n,this.next),this.text=this.statics.value(this.domNode),n},e.prototype.update=function(t,e){var n=this;t.some(function(t){return"characterData"===t.type&&t.target===n.domNode})&&(this.text=this.statics.value(this.domNode))},e.prototype.value=function(){return this.text},e.blotName="text",e.scope=i.Scope.INLINE_BLOT,e}(o.default);e.default=l},function(t,e,n){"use strict";var r=document.createElement("div");if(r.classList.toggle("test-class",!1),r.classList.contains("test-class")){var o=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(t,e){return arguments.length>1&&!this.contains(t)==!e?e:o.call(this,t)}}String.prototype.startsWith||(String.prototype.startsWith=function(t,e){return e=e||0,this.substr(e,t.length)===t}),String.prototype.endsWith||(String.prototype.endsWith=function(t,e){var n=this.toString();("number"!=typeof e||!isFinite(e)||Math.floor(e)!==e||e>n.length)&&(e=n.length),e-=t.length;var r=n.indexOf(t,e);return-1!==r&&r===e}),Array.prototype.find||Object.defineProperty(Array.prototype,"find",{value:function(t){if(null===this)throw new TypeError("Array.prototype.find called on null or undefined");if("function"!=typeof t)throw new TypeError("predicate must be a function");for(var e,n=Object(this),r=n.length>>>0,o=arguments[1],i=0;ie.length?t:e,l=t.length>e.length?e:t,a=i.indexOf(l);if(-1!=a)return r=[[y,i.substring(0,a)],[v,l],[y,i.substring(a+l.length)]],t.length>e.length&&(r[0][0]=r[2][0]=d),r;if(1==l.length)return[[d,t],[y,e]];var u=s(t,e);if(u){var c=u[0],f=u[1],h=u[2],p=u[3],b=u[4],g=n(c,h),m=n(f,p);return g.concat([[v,b]],m)}return o(t,e)}function o(t,e){for(var n=t.length,r=e.length,o=Math.ceil((n+r)/2),l=o,a=2*o,s=new Array(a),u=new Array(a),c=0;cn)v+=2;else if(x>r)p+=2;else if(h){var k=l+f-_;if(k>=0&&k=E)return i(t,e,O,x)}}}for(var N=-m+b;N<=m-g;N+=2){var E,k=l+N;E=N==-m||N!=m&&u[k-1]n)g+=2;else if(j>r)b+=2;else if(!h){var w=l+f-N;if(w>=0&&w=E)return i(t,e,O,x)}}}}return[[d,t],[y,e]]}function i(t,e,r,o){var i=t.substring(0,r),l=e.substring(0,o),a=t.substring(r),s=e.substring(o),u=n(i,l),c=n(a,s);return u.concat(c)}function l(t,e){if(!t||!e||t.charAt(0)!=e.charAt(0))return 0;for(var n=0,r=Math.min(t.length,e.length),o=r,i=0;n=t.length?[r,o,i,s,f]:null}var r=t.length>e.length?t:e,o=t.length>e.length?e:t;if(r.length<4||2*o.lengthu[4].length?s:u:s;var c,f,h,p;return t.length>e.length?(c=i[0],f=i[1],h=i[2],p=i[3]):(h=i[0],p=i[1],c=i[2],f=i[3]),[c,f,h,p,i[4]]}function u(t){t.push([v,""]);for(var e,n=0,r=0,o=0,i="",s="";n1?(0!==r&&0!==o&&(e=l(s,i),0!==e&&(n-r-o>0&&t[n-r-o-1][0]==v?t[n-r-o-1][1]+=s.substring(0,e):(t.splice(0,0,[v,s.substring(0,e)]),n++),s=s.substring(e),i=i.substring(e)),0!==(e=a(s,i))&&(t[n][1]=s.substring(s.length-e)+t[n][1],s=s.substring(0,s.length-e),i=i.substring(0,i.length-e))),0===r?t.splice(n-o,r+o,[y,s]):0===o?t.splice(n-r,r+o,[d,i]):t.splice(n-r-o,r+o,[d,i],[y,s]),n=n-r-o+(r?1:0)+(o?1:0)+1):0!==n&&t[n-1][0]==v?(t[n-1][1]+=t[n][1],t.splice(n,1)):n++,o=0,r=0,i="",s=""}""===t[t.length-1][1]&&t.pop();var c=!1;for(n=1;n0&&r.splice(o+2,0,[l[0],a]),p(r,o,3)}return t}function h(t){for(var e=!1,n=function(t){return t.charCodeAt(0)>=56320&&t.charCodeAt(0)<=57343},r=2;r=55296&&t.charCodeAt(t.length-1)<=56319}(t[r-2][1])&&t[r-1][0]===d&&n(t[r-1][1])&&t[r][0]===y&&n(t[r][1])&&(e=!0,t[r-1][1]=t[r-2][1].slice(-1)+t[r-1][1],t[r][1]=t[r-2][1].slice(-1)+t[r][1],t[r-2][1]=t[r-2][1].slice(0,-1));if(!e)return t;for(var o=[],r=0;r0&&o.push(t[r]);return o}function p(t,e,n){for(var r=e+n-1;r>=0&&r>=e-1;r--)if(r+1=r&&!a.endsWith("\n")&&(n=!0),e.scroll.insertAt(t,a);var c=e.scroll.line(t),f=u(c,2),h=f[0],p=f[1],y=(0,T.default)({},(0,O.bubbleFormats)(h));if(h instanceof w.default){var b=h.descendant(v.default.Leaf,p),g=u(b,1),m=g[0];y=(0,T.default)(y,(0,O.bubbleFormats)(m))}l=d.default.attributes.diff(y,l)||{}}else if("object"===s(o.insert)){var _=Object.keys(o.insert)[0];if(null==_)return t;e.scroll.insertAt(t,_,o.insert[_])}r+=i}return Object.keys(l).forEach(function(n){e.scroll.formatAt(t,i,n,l[n])}),t+i},0),t.reduce(function(t,n){return"number"==typeof n.delete?(e.scroll.deleteAt(t,n.delete),t):t+(n.retain||n.insert.length||1)},0),this.scroll.batchEnd(),this.update(t)}},{key:"deleteText",value:function(t,e){return this.scroll.deleteAt(t,e),this.update((new h.default).retain(t).delete(e))}},{key:"formatLine",value:function(t,e){var n=this,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return this.scroll.update(),Object.keys(r).forEach(function(o){if(null==n.scroll.whitelist||n.scroll.whitelist[o]){var i=n.scroll.lines(t,Math.max(e,1)),l=e;i.forEach(function(e){var i=e.length();if(e instanceof g.default){var a=t-e.offset(n.scroll),s=e.newlineIndex(a+l)-a+1;e.formatAt(a,s,o,r[o])}else e.format(o,r[o]);l-=i})}}),this.scroll.optimize(),this.update((new h.default).retain(t).retain(e,(0,N.default)(r)))}},{key:"formatText",value:function(t,e){var n=this,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return Object.keys(r).forEach(function(o){n.scroll.formatAt(t,e,o,r[o])}),this.update((new h.default).retain(t).retain(e,(0,N.default)(r)))}},{key:"getContents",value:function(t,e){return this.delta.slice(t,t+e)}},{key:"getDelta",value:function(){return this.scroll.lines().reduce(function(t,e){return t.concat(e.delta())},new h.default)}},{key:"getFormat",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=[],r=[];0===e?this.scroll.path(t).forEach(function(t){var e=u(t,1),o=e[0];o instanceof w.default?n.push(o):o instanceof v.default.Leaf&&r.push(o)}):(n=this.scroll.lines(t,e),r=this.scroll.descendants(v.default.Leaf,t,e));var o=[n,r].map(function(t){if(0===t.length)return{};for(var e=(0,O.bubbleFormats)(t.shift());Object.keys(e).length>0;){var n=t.shift();if(null==n)return e;e=l((0,O.bubbleFormats)(n),e)}return e});return T.default.apply(T.default,o)}},{key:"getText",value:function(t,e){return this.getContents(t,e).filter(function(t){return"string"==typeof t.insert}).map(function(t){return t.insert}).join("")}},{key:"insertEmbed",value:function(t,e,n){return this.scroll.insertAt(t,e,n),this.update((new h.default).retain(t).insert(o({},e,n)))}},{key:"insertText",value:function(t,e){var n=this,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return e=e.replace(/\r\n/g,"\n").replace(/\r/g,"\n"),this.scroll.insertAt(t,e),Object.keys(r).forEach(function(o){n.scroll.formatAt(t,e.length,o,r[o])}),this.update((new h.default).retain(t).insert(e,(0,N.default)(r)))}},{key:"isBlank",value:function(){if(0==this.scroll.children.length)return!0;if(this.scroll.children.length>1)return!1;var t=this.scroll.children.head;return t.statics.blotName===w.default.blotName&&(!(t.children.length>1)&&t.children.head instanceof k.default)}},{key:"removeFormat",value:function(t,e){var n=this.getText(t,e),r=this.scroll.line(t+e),o=u(r,2),i=o[0],l=o[1],a=0,s=new h.default;null!=i&&(a=i instanceof g.default?i.newlineIndex(l)-l+1:i.length()-l,s=i.delta().slice(l,l+a-1).insert("\n"));var c=this.getContents(t,e+a),f=c.diff((new h.default).insert(n).concat(s)),p=(new h.default).retain(t).concat(f);return this.applyDelta(p)}},{key:"update",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,r=this.delta;if(1===e.length&&"characterData"===e[0].type&&e[0].target.data.match(P)&&v.default.find(e[0].target)){var o=v.default.find(e[0].target),i=(0,O.bubbleFormats)(o),l=o.offset(this.scroll),a=e[0].oldValue.replace(_.default.CONTENTS,""),s=(new h.default).insert(a),u=(new h.default).insert(o.value());t=(new h.default).retain(l).concat(s.diff(u,n)).reduce(function(t,e){return e.insert?t.insert(e.insert,i):t.push(e)},new h.default),this.delta=r.compose(t)}else this.delta=this.getDelta(),t&&(0,A.default)(r.compose(t),this.delta)||(t=r.diff(this.delta,n));return t}}]),t}();e.default=S},function(t,e){"use strict";function n(){}function r(t,e,n){this.fn=t,this.context=e,this.once=n||!1}function o(){this._events=new n,this._eventsCount=0}var i=Object.prototype.hasOwnProperty,l="~";Object.create&&(n.prototype=Object.create(null),(new n).__proto__||(l=!1)),o.prototype.eventNames=function(){var t,e,n=[];if(0===this._eventsCount)return n;for(e in t=this._events)i.call(t,e)&&n.push(l?e.slice(1):e);return Object.getOwnPropertySymbols?n.concat(Object.getOwnPropertySymbols(t)):n},o.prototype.listeners=function(t,e){var n=l?l+t:t,r=this._events[n];if(e)return!!r;if(!r)return[];if(r.fn)return[r.fn];for(var o=0,i=r.length,a=new Array(i);o0){if(i instanceof y.BlockEmbed||f instanceof y.BlockEmbed)return void this.optimize();if(i instanceof _.default){var h=i.newlineIndex(i.length(),!0);if(h>-1&&(i=i.split(h+1))===f)return void this.optimize()}else if(f instanceof _.default){var p=f.newlineIndex(0);p>-1&&f.split(p+1)}var d=f.children.head instanceof g.default?null:f.children.head;i.moveChildren(f,d),i.remove()}this.optimize()}},{key:"enable",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.domNode.setAttribute("contenteditable",t)}},{key:"formatAt",value:function(t,n,r,o){(null==this.whitelist||this.whitelist[r])&&(c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"formatAt",this).call(this,t,n,r,o),this.optimize())}},{key:"insertAt",value:function(t,n,r){if(null==r||null==this.whitelist||this.whitelist[n]){if(t>=this.length())if(null==r||null==h.default.query(n,h.default.Scope.BLOCK)){var o=h.default.create(this.statics.defaultChild);this.appendChild(o),null==r&&n.endsWith("\n")&&(n=n.slice(0,-1)),o.insertAt(0,n,r)}else{var i=h.default.create(n,r);this.appendChild(i)}else c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"insertAt",this).call(this,t,n,r);this.optimize()}}},{key:"insertBefore",value:function(t,n){if(t.statics.scope===h.default.Scope.INLINE_BLOT){var r=h.default.create(this.statics.defaultChild);r.appendChild(t),t=r}c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"insertBefore",this).call(this,t,n)}},{key:"leaf",value:function(t){return this.path(t).pop()||[null,-1]}},{key:"line",value:function(t){return t===this.length()?this.line(t-1):this.descendant(a,t)}},{key:"lines",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:Number.MAX_VALUE;return function t(e,n,r){var o=[],i=r;return e.children.forEachAt(n,r,function(e,n,r){a(e)?o.push(e):e instanceof h.default.Container&&(o=o.concat(t(e,n,i))),i-=r}),o}(this,t,e)}},{key:"optimize",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};!0!==this.batch&&(c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"optimize",this).call(this,t,n),t.length>0&&this.emitter.emit(d.default.events.SCROLL_OPTIMIZE,t,n))}},{key:"path",value:function(t){return c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"path",this).call(this,t).slice(1)}},{key:"update",value:function(t){if(!0!==this.batch){var n=d.default.sources.USER;"string"==typeof t&&(n=t),Array.isArray(t)||(t=this.observer.takeRecords()),t.length>0&&this.emitter.emit(d.default.events.SCROLL_BEFORE_UPDATE,n,t),c(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"update",this).call(this,t.concat([])),t.length>0&&this.emitter.emit(d.default.events.SCROLL_UPDATE,n,t)}}}]),e}(h.default.Scroll);x.blotName="scroll",x.className="ql-editor",x.tagName="DIV",x.defaultChild="block",x.allowedChildren=[v.default,y.BlockEmbed,w.default],e.default=x},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function l(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function a(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function s(t,e,n){return"object"===(void 0===e?"undefined":x(e))?Object.keys(e).reduce(function(t,n){return s(t,n,e[n])},t):t.reduce(function(t,r){return r.attributes&&r.attributes[e]?t.push(r):t.insert(r.insert,(0,j.default)({},o({},e,n),r.attributes))},new q.default)}function u(t){if(t.nodeType!==Node.ELEMENT_NODE)return{};return t["__ql-computed-style"]||(t["__ql-computed-style"]=window.getComputedStyle(t))}function c(t,e){for(var n="",r=t.ops.length-1;r>=0&&n.length-1}function h(t,e,n){return t.nodeType===t.TEXT_NODE?n.reduce(function(e,n){return n(t,e)},new q.default):t.nodeType===t.ELEMENT_NODE?[].reduce.call(t.childNodes||[],function(r,o){var i=h(o,e,n);return o.nodeType===t.ELEMENT_NODE&&(i=e.reduce(function(t,e){return e(o,t)},i),i=(o[W]||[]).reduce(function(t,e){return e(o,t)},i)),r.concat(i)},new q.default):new q.default}function p(t,e,n){return s(n,t,!0)}function d(t,e){var n=P.default.Attributor.Attribute.keys(t),r=P.default.Attributor.Class.keys(t),o=P.default.Attributor.Style.keys(t),i={};return n.concat(r).concat(o).forEach(function(e){var n=P.default.query(e,P.default.Scope.ATTRIBUTE);null!=n&&(i[n.attrName]=n.value(t),i[n.attrName])||(n=Y[e],null==n||n.attrName!==e&&n.keyName!==e||(i[n.attrName]=n.value(t)||void 0),null==(n=X[e])||n.attrName!==e&&n.keyName!==e||(n=X[e],i[n.attrName]=n.value(t)||void 0))}),Object.keys(i).length>0&&(e=s(e,i)),e}function y(t,e){var n=P.default.query(t);if(null==n)return e;if(n.prototype instanceof P.default.Embed){var r={},o=n.value(t);null!=o&&(r[n.blotName]=o,e=(new q.default).insert(r,n.formats(t)))}else"function"==typeof n.formats&&(e=s(e,n.blotName,n.formats(t)));return e}function v(t,e){return c(e,"\n")||e.insert("\n"),e}function b(){return new q.default}function g(t,e){var n=P.default.query(t);if(null==n||"list-item"!==n.blotName||!c(e,"\n"))return e;for(var r=-1,o=t.parentNode;!o.classList.contains("ql-clipboard");)"list"===(P.default.query(o)||{}).blotName&&(r+=1),o=o.parentNode;return r<=0?e:e.compose((new q.default).retain(e.length()-1).retain(1,{indent:r}))}function m(t,e){return c(e,"\n")||(f(t)||e.length()>0&&t.nextSibling&&f(t.nextSibling))&&e.insert("\n"),e}function _(t,e){if(f(t)&&null!=t.nextElementSibling&&!c(e,"\n\n")){var n=t.offsetHeight+parseFloat(u(t).marginTop)+parseFloat(u(t).marginBottom);t.nextElementSibling.offsetTop>t.offsetTop+1.5*n&&e.insert("\n")}return e}function O(t,e){var n={},r=t.style||{};return r.fontStyle&&"italic"===u(t).fontStyle&&(n.italic=!0),r.fontWeight&&(u(t).fontWeight.startsWith("bold")||parseInt(u(t).fontWeight)>=700)&&(n.bold=!0),Object.keys(n).length>0&&(e=s(e,n)),parseFloat(r.textIndent||0)>0&&(e=(new q.default).insert("\t").concat(e)),e}function w(t,e){var n=t.data;if("O:P"===t.parentNode.tagName)return e.insert(n.trim());if(0===n.trim().length&&t.parentNode.classList.contains("ql-clipboard"))return e;if(!u(t.parentNode).whiteSpace.startsWith("pre")){var r=function(t,e){return e=e.replace(/[^\u00a0]/g,""),e.length<1&&t?" ":e};n=n.replace(/\r\n/g," ").replace(/\n/g," "),n=n.replace(/\s\s+/g,r.bind(r,!0)),(null==t.previousSibling&&f(t.parentNode)||null!=t.previousSibling&&f(t.previousSibling))&&(n=n.replace(/^\s+/,r.bind(r,!1))),(null==t.nextSibling&&f(t.parentNode)||null!=t.nextSibling&&f(t.nextSibling))&&(n=n.replace(/\s+$/,r.bind(r,!1)))}return e.insert(n)}Object.defineProperty(e,"__esModule",{value:!0}),e.matchText=e.matchSpacing=e.matchNewline=e.matchBlot=e.matchAttributor=e.default=void 0;var x="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},k=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),E=function(){function t(t,e){for(var n=0;n\r?\n +\<"),this.convert();var e=this.quill.getFormat(this.quill.selection.savedRange.index);if(e[F.default.blotName]){var n=this.container.innerText;return this.container.innerHTML="",(new q.default).insert(n,o({},F.default.blotName,e[F.default.blotName]))}var r=this.prepareMatching(),i=k(r,2),l=i[0],a=i[1],s=h(this.container,l,a);return c(s,"\n")&&null==s.ops[s.ops.length-1].attributes&&(s=s.compose((new q.default).retain(s.length()-1).delete(1))),V.log("convert",this.container.innerHTML,s),this.container.innerHTML="",s}},{key:"dangerouslyPasteHTML",value:function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:C.default.sources.API;if("string"==typeof t)this.quill.setContents(this.convert(t),e),this.quill.setSelection(0,C.default.sources.SILENT);else{var r=this.convert(e);this.quill.updateContents((new q.default).retain(t).concat(r),n),this.quill.setSelection(t+r.length(),C.default.sources.SILENT)}}},{key:"onPaste",value:function(t){var e=this;if(!t.defaultPrevented&&this.quill.isEnabled()){var n=this.quill.getSelection(),r=(new q.default).retain(n.index),o=this.quill.scrollingContainer.scrollTop;this.container.focus(),this.quill.selection.update(C.default.sources.SILENT),setTimeout(function(){r=r.concat(e.convert()).delete(n.length),e.quill.updateContents(r,C.default.sources.USER),e.quill.setSelection(r.length()-n.length,C.default.sources.SILENT),e.quill.scrollingContainer.scrollTop=o,e.quill.focus()},1)}}},{key:"prepareMatching",value:function(){var t=this,e=[],n=[];return this.matchers.forEach(function(r){var o=k(r,2),i=o[0],l=o[1];switch(i){case Node.TEXT_NODE:n.push(l);break;case Node.ELEMENT_NODE:e.push(l);break;default:[].forEach.call(t.container.querySelectorAll(i),function(t){t[W]=t[W]||[],t[W].push(l)})}}),[e,n]}}]),e}(I.default);$.DEFAULTS={matchers:[],matchVisual:!0},e.default=$,e.matchAttributor=d,e.matchBlot=y,e.matchNewline=m,e.matchSpacing=_,e.matchText=w},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function a(t){var e=t.ops[t.ops.length-1];return null!=e&&(null!=e.insert?"string"==typeof e.insert&&e.insert.endsWith("\n"):null!=e.attributes&&Object.keys(e.attributes).some(function(t){return null!=f.default.query(t,f.default.Scope.BLOCK)}))}function s(t){var e=t.reduce(function(t,e){return t+=e.delete||0},0),n=t.length()-e;return a(t)&&(n-=1),n}Object.defineProperty(e,"__esModule",{value:!0}),e.getLastChangeIndex=e.default=void 0;var u=function(){function t(t,e){for(var n=0;nr&&this.stack.undo.length>0){var o=this.stack.undo.pop();n=n.compose(o.undo),t=o.redo.compose(t)}else this.lastRecorded=r;this.stack.undo.push({redo:t,undo:n}),this.stack.undo.length>this.options.maxStack&&this.stack.undo.shift()}}},{key:"redo",value:function(){this.change("redo","undo")}},{key:"transform",value:function(t){this.stack.undo.forEach(function(e){e.undo=t.transform(e.undo,!0),e.redo=t.transform(e.redo,!0)}),this.stack.redo.forEach(function(e){e.undo=t.transform(e.undo,!0),e.redo=t.transform(e.redo,!0)})}},{key:"undo",value:function(){this.change("undo","redo")}}]),e}(y.default);v.DEFAULTS={delay:1e3,maxStack:100,userOnly:!1},e.default=v,e.getLastChangeIndex=s},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0}),e.IndentClass=void 0;var l=function(){function t(t,e){for(var n=0;n0&&this.children.tail.format(t,e)}},{key:"formats",value:function(){return o({},this.statics.blotName,this.statics.formats(this.domNode))}},{key:"insertBefore",value:function(t,n){if(t instanceof v)u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"insertBefore",this).call(this,t,n);else{var r=null==n?this.length():n.offset(this),o=this.split(r);o.parent.insertBefore(t,o)}}},{key:"optimize",value:function(t){u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"optimize",this).call(this,t);var n=this.next;null!=n&&n.prev===this&&n.statics.blotName===this.statics.blotName&&n.domNode.tagName===this.domNode.tagName&&n.domNode.getAttribute("data-checked")===this.domNode.getAttribute("data-checked")&&(n.moveChildren(this),n.remove())}},{key:"replace",value:function(t){if(t.statics.blotName!==this.statics.blotName){var n=f.default.create(this.statics.defaultChild);t.moveChildren(n),this.appendChild(n)}u(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"replace",this).call(this,t)}}]),e}(y.default);b.blotName="list",b.scope=f.default.Scope.BLOCK_BLOT,b.tagName=["OL","UL"],b.defaultChild="list-item",b.allowedChildren=[v],e.ListItem=v,e.default=b},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=n(39),a=function(t){return t&&t.__esModule?t:{default:t}}(l),s=function(t){function e(){return r(this,e),o(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return i(e,t),e}(a.default);s.blotName="italic",s.tagName=["EM","I"],e.default=s},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=function(){function t(t,e){for(var n=0;n-1?n?this.domNode.setAttribute(t,n):this.domNode.removeAttribute(t):a(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"format",this).call(this,t,n)}}],[{key:"create",value:function(t){var n=a(e.__proto__||Object.getPrototypeOf(e),"create",this).call(this,t);return"string"==typeof t&&n.setAttribute("src",this.sanitize(t)),n}},{key:"formats",value:function(t){return f.reduce(function(e,n){return t.hasAttribute(n)&&(e[n]=t.getAttribute(n)),e},{})}},{key:"match",value:function(t){return/\.(jpe?g|gif|png)$/.test(t)||/^data:image\/.+;base64/.test(t)}},{key:"sanitize",value:function(t){return(0,c.sanitize)(t,["http","https","data"])?t:"//:0"}},{key:"value",value:function(t){return t.getAttribute("src")}}]),e}(u.default.Embed);h.blotName="image",h.tagName="IMG",e.default=h},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=function(){function t(t,e){for(var n=0;n-1?n?this.domNode.setAttribute(t,n):this.domNode.removeAttribute(t):a(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"format",this).call(this,t,n)}}],[{key:"create",value:function(t){var n=a(e.__proto__||Object.getPrototypeOf(e),"create",this).call(this,t);return n.setAttribute("frameborder","0"),n.setAttribute("allowfullscreen",!0),n.setAttribute("src",this.sanitize(t)),n}},{key:"formats",value:function(t){return f.reduce(function(e,n){return t.hasAttribute(n)&&(e[n]=t.getAttribute(n)),e},{})}},{key:"sanitize",value:function(t){return c.default.sanitize(t)}},{key:"value",value:function(t){return t.getAttribute("src")}}]),e}(s.BlockEmbed);h.blotName="video",h.className="ql-video",h.tagName="IFRAME",e.default=h},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.FormulaBlot=void 0;var a=function(){function t(t,e){for(var n=0;n0||null==this.cachedText)&&(this.domNode.innerHTML=t(e),this.domNode.normalize(),this.attach()),this.cachedText=e)}}]),e}(v.default);b.className="ql-syntax";var g=new c.default.Attributor.Class("token","hljs",{scope:c.default.Scope.INLINE}),m=function(t){function e(t,n){o(this,e);var r=i(this,(e.__proto__||Object.getPrototypeOf(e)).call(this,t,n));if("function"!=typeof r.options.highlight)throw new Error("Syntax module requires highlight.js. Please include the library on the page before Quill.");var l=null;return r.quill.on(h.default.events.SCROLL_OPTIMIZE,function(){clearTimeout(l),l=setTimeout(function(){r.highlight(),l=null},r.options.interval)}),r.highlight(),r}return l(e,t),a(e,null,[{key:"register",value:function(){h.default.register(g,!0),h.default.register(b,!0)}}]),a(e,[{key:"highlight",value:function(){var t=this;if(!this.quill.selection.composing){this.quill.update(h.default.sources.USER);var e=this.quill.getSelection();this.quill.scroll.descendants(b).forEach(function(e){e.highlight(t.options.highlight)}),this.quill.update(h.default.sources.SILENT),null!=e&&this.quill.setSelection(e,h.default.sources.SILENT)}}}]),e}(d.default);m.DEFAULTS={highlight:function(){return null==window.hljs?null:function(t){return window.hljs.highlightAuto(t).value}}(),interval:1e3},e.CodeBlock=b,e.CodeToken=g,e.default=m},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function l(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function a(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function s(t,e,n){var r=document.createElement("button");r.setAttribute("type","button"),r.classList.add("ql-"+e),null!=n&&(r.value=n),t.appendChild(r)}function u(t,e){Array.isArray(e[0])||(e=[e]),e.forEach(function(e){var n=document.createElement("span");n.classList.add("ql-formats"),e.forEach(function(t){if("string"==typeof t)s(n,t);else{var e=Object.keys(t)[0],r=t[e];Array.isArray(r)?c(n,e,r):s(n,e,r)}}),t.appendChild(n)})}function c(t,e,n){var r=document.createElement("select");r.classList.add("ql-"+e),n.forEach(function(t){var e=document.createElement("option");!1!==t?e.setAttribute("value",t):e.setAttribute("selected","selected"),r.appendChild(e)}),t.appendChild(r)}Object.defineProperty(e,"__esModule",{value:!0}),e.addControls=e.default=void 0;var f=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),h=function(){function t(t,e){for(var n=0;n '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e){t.exports=' '},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.BubbleTooltip=void 0;var a=function t(e,n,r){null===e&&(e=Function.prototype);var o=Object.getOwnPropertyDescriptor(e,n);if(void 0===o){var i=Object.getPrototypeOf(e);return null===i?void 0:t(i,n,r)}if("value"in o)return o.value;var l=o.get;if(void 0!==l)return l.call(r)},s=function(){function t(t,e){for(var n=0;n0&&o===h.default.sources.USER){r.show(),r.root.style.left="0px",r.root.style.width="",r.root.style.width=r.root.offsetWidth+"px";var i=r.quill.getLines(e.index,e.length);if(1===i.length)r.position(r.quill.getBounds(e));else{var l=i[i.length-1],a=r.quill.getIndex(l),s=Math.min(l.length()-1,e.index+e.length-a),u=r.quill.getBounds(new y.Range(a,s));r.position(u)}}else document.activeElement!==r.textbox&&r.quill.hasFocus()&&r.hide()}),r}return l(e,t),s(e,[{key:"listen",value:function(){var t=this;a(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"listen",this).call(this),this.root.querySelector(".ql-close").addEventListener("click",function(){t.root.classList.remove("ql-editing")}),this.quill.on(h.default.events.SCROLL_OPTIMIZE,function(){setTimeout(function(){if(!t.root.classList.contains("ql-hidden")){var e=t.quill.getSelection();null!=e&&t.position(t.quill.getBounds(e))}},1)})}},{key:"cancel",value:function(){this.show()}},{key:"position",value:function(t){var n=a(e.prototype.__proto__||Object.getPrototypeOf(e.prototype),"position",this).call(this,t),r=this.root.querySelector(".ql-tooltip-arrow");if(r.style.marginLeft="",0===n)return n;r.style.marginLeft=-1*n-r.offsetWidth/2+"px"}}]),e}(p.BaseTooltip);_.TEMPLATE=['','
','','',"
"].join(""),e.BubbleTooltip=_,e.default=m},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var a=function(){function t(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var l,a=t[Symbol.iterator]();!(r=(l=a.next()).done)&&(n.push(l.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}return function(e,n){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),s=function t(e,n,r){null===e&&(e=Function.prototype);var o=Object.getOwnPropertyDescriptor(e,n);if(void 0===o){var i=Object.getPrototypeOf(e);return null===i?void 0:t(i,n,r)}if("value"in o)return o.value;var l=o.get;if(void 0!==l)return l.call(r)},u=function(){function t(t,e){for(var n=0;n','','',''].join(""),e.default=w}]).default}); +//# sourceMappingURL=quill.min.js.map \ No newline at end of file diff --git a/Wino.Mail/JS/Quill/quill.min.js.map b/Wino.Mail/JS/Quill/quill.min.js.map new file mode 100644 index 00000000..f2729907 --- /dev/null +++ b/Wino.Mail/JS/Quill/quill.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///quill.min.js","webpack:///webpack/bootstrap bc1e3bc04ac5f71cbeee","webpack:///./node_modules/parchment/src/parchment.ts","webpack:///./node_modules/parchment/src/registry.ts","webpack:///./node_modules/extend/index.js","webpack:///./blots/block.js","webpack:///./node_modules/quill-delta/lib/delta.js","webpack:///./blots/inline.js","webpack:///./core/quill.js","webpack:///./core/module.js","webpack:///./blots/text.js","webpack:///./core/emitter.js","webpack:///./core/logger.js","webpack:///./node_modules/parchment/src/attributor/attributor.ts","webpack:///./node_modules/deep-equal/index.js","webpack:///./formats/code.js","webpack:///./blots/break.js","webpack:///./formats/link.js","webpack:///./ui/picker.js","webpack:///./node_modules/parchment/src/blot/abstract/container.ts","webpack:///./node_modules/parchment/src/blot/abstract/format.ts","webpack:///./node_modules/parchment/src/blot/abstract/leaf.ts","webpack:///./node_modules/quill-delta/lib/op.js","webpack:///./node_modules/clone/clone.js","webpack:///./core/selection.js","webpack:///./blots/container.js","webpack:///./formats/color.js","webpack:///./modules/keyboard.js","webpack:///./ui/icons.js","webpack:///./node_modules/parchment/src/blot/abstract/shadow.ts","webpack:///./node_modules/parchment/src/attributor/store.ts","webpack:///./node_modules/parchment/src/attributor/class.ts","webpack:///./node_modules/parchment/src/attributor/style.ts","webpack:///./blots/cursor.js","webpack:///./core/theme.js","webpack:///./blots/embed.js","webpack:///./formats/align.js","webpack:///./formats/background.js","webpack:///./formats/direction.js","webpack:///./formats/font.js","webpack:///./formats/size.js","webpack:///./formats/bold.js","webpack:///./assets/icons/code.svg","webpack:///./ui/color-picker.js","webpack:///./ui/icon-picker.js","webpack:///./ui/tooltip.js","webpack:///./themes/base.js","webpack:///./quill.js","webpack:///./core.js","webpack:///./node_modules/parchment/src/collection/linked-list.ts","webpack:///./node_modules/parchment/src/blot/scroll.ts","webpack:///./node_modules/parchment/src/blot/inline.ts","webpack:///./node_modules/parchment/src/blot/block.ts","webpack:///./node_modules/parchment/src/blot/embed.ts","webpack:///./node_modules/parchment/src/blot/text.ts","webpack:///./core/polyfill.js","webpack:///./node_modules/fast-diff/diff.js","webpack:///./node_modules/deep-equal/lib/keys.js","webpack:///./node_modules/deep-equal/lib/is_arguments.js","webpack:///./core/editor.js","webpack:///./node_modules/eventemitter3/index.js","webpack:///./blots/scroll.js","webpack:///./modules/clipboard.js","webpack:///./modules/history.js","webpack:///./formats/indent.js","webpack:///./formats/blockquote.js","webpack:///./formats/header.js","webpack:///./formats/list.js","webpack:///./formats/italic.js","webpack:///./formats/script.js","webpack:///./formats/strike.js","webpack:///./formats/underline.js","webpack:///./formats/image.js","webpack:///./formats/video.js","webpack:///./modules/formula.js","webpack:///./modules/syntax.js","webpack:///./modules/toolbar.js","webpack:///./assets/icons/align-left.svg","webpack:///./assets/icons/align-center.svg","webpack:///./assets/icons/align-right.svg","webpack:///./assets/icons/align-justify.svg","webpack:///./assets/icons/background.svg","webpack:///./assets/icons/blockquote.svg","webpack:///./assets/icons/bold.svg","webpack:///./assets/icons/clean.svg","webpack:///./assets/icons/color.svg","webpack:///./assets/icons/direction-ltr.svg","webpack:///./assets/icons/direction-rtl.svg","webpack:///./assets/icons/float-center.svg","webpack:///./assets/icons/float-full.svg","webpack:///./assets/icons/float-left.svg","webpack:///./assets/icons/float-right.svg","webpack:///./assets/icons/formula.svg","webpack:///./assets/icons/header.svg","webpack:///./assets/icons/header-2.svg","webpack:///./assets/icons/italic.svg","webpack:///./assets/icons/image.svg","webpack:///./assets/icons/indent.svg","webpack:///./assets/icons/outdent.svg","webpack:///./assets/icons/link.svg","webpack:///./assets/icons/list-ordered.svg","webpack:///./assets/icons/list-bullet.svg","webpack:///./assets/icons/list-check.svg","webpack:///./assets/icons/subscript.svg","webpack:///./assets/icons/superscript.svg","webpack:///./assets/icons/strike.svg","webpack:///./assets/icons/underline.svg","webpack:///./assets/icons/video.svg","webpack:///./assets/icons/dropdown.svg","webpack:///./themes/bubble.js","webpack:///./themes/snow.js"],"names":["root","factory","exports","module","define","amd","self","this","modules","__webpack_require__","moduleId","installedModules","i","l","call","m","c","d","name","getter","o","Object","defineProperty","configurable","enumerable","get","n","__esModule","object","property","prototype","hasOwnProperty","p","s","value","container_1","format_1","leaf_1","scroll_1","inline_1","block_1","embed_1","text_1","attributor_1","class_1","style_1","store_1","Registry","Parchment","Scope","create","find","query","register","Container","default","Format","Leaf","Embed","Scroll","Block","Inline","Text","Attributor","Attribute","Class","Style","Store","input","match","ParchmentError","BlotClass","Node","TEXT_NODE","node","bubble","DATA_KEY","blot","parentNode","scope","ANY","types","attributes","LEVEL","BLOCK","INLINE","HTMLElement","names","getAttribute","split","classes","tags","tagName","TYPE","Definitions","_i","arguments","length","map","Definition","blotName","attrName","keyName","className","Array","isArray","toUpperCase","tagNames","forEach","tag","__extends","extendStatics","setPrototypeOf","__proto__","b","__","constructor","_super","message","_this","Error","hasOwn","toStr","toString","arr","isPlainObject","obj","hasOwnConstructor","hasIsPrototypeOf","key","extend","options","src","copy","copyIsArray","clone","target","deep","_interopRequireDefault","_classCallCheck","instance","Constructor","TypeError","_possibleConstructorReturn","ReferenceError","_inherits","subClass","superClass","writable","bubbleFormats","formats","undefined","_extend2","parent","statics","BlockEmbed","_createClass","defineProperties","props","descriptor","protoProps","staticProps","_get","receiver","Function","desc","getOwnPropertyDescriptor","getPrototypeOf","_extend","_quillDelta","_quillDelta2","_parchment","_parchment2","_break","_break2","_inline","_inline2","_text","_text2","_Parchment$Embed","apply","domNode","insert","values","attribute","BLOCK_ATTRIBUTE","index","format","def","endsWith","block","insertBefore","next","insertAt","slice","BLOCK_BLOT","_Parchment$Block","_this2","cache","delta","descendants","reduce","leaf","Math","min","lines","text","shift","children","tail","line","ref","head","remove","context","child","force","defaultChild","allowedChildren","diff","equal","op","NULL_CHARACTER","String","fromCharCode","Delta","ops","newOp","keys","push","delete","retain","lastOp","unshift","splice","chop","pop","filter","predicate","partition","passed","failed","initial","changeLength","elem","start","end","Infinity","iter","iterator","hasNext","nextOp","compose","other","thisIter","otherIter","peekType","peekLength","thisOp","otherOp","concat","strings","prep","join","diffResult","component","opLength","INSERT","DELETE","EQUAL","eachLine","newline","peek","indexOf","transform","priority","transformPosition","offset","nextType","_Parchment$Inline","compare","BLOT","isolate","wrap","moveChildren","selfIndex","order","otherIndex","_defineProperty","expandConfig","container","userConfig","clipboard","keyboard","history","theme","Quill","DEFAULTS","import","_theme2","themeConfig","config","moduleNames","moduleConfig","moduleClass","debug","error","toolbar","document","querySelector","modify","modifier","source","strict","isEnabled","_emitter4","sources","USER","range","getSelection","oldDelta","editor","change","shiftRange","setSelection","SILENT","_emitter","args","events","TEXT_CHANGE","emitter","emit","EDITOR_CHANGE","_emitter2","overload","_typeof","API","_map","pos","_map2","_slicedToArray","_map3","max","_map4","_selection","Range","Symbol","sliceIterator","_arr","_n","_d","_e","_s","done","err","_editor","_editor2","_emitter3","_module","_module2","_selection2","_logger","_logger2","_theme","html","innerHTML","trim","classList","add","__quill","addContainer","setAttribute","scrollingContainer","scroll","whitelist","selection","addModule","init","on","type","toggle","isBlank","SCROLL_UPDATE","mutations","lastRange","update","contents","convert","setContents","clear","placeholder","readOnly","disable","limit","level","imports","path","overwrite","warn","startsWith","refNode","createElement","setRange","_this3","_overload","_overload2","deleteText","enable","enabled","scrollTop","focus","scrollIntoView","_this4","formatLine","formatText","_this5","_overload3","_overload4","_this6","_overload5","_overload6","bounds","getBounds","containerBounds","getBoundingClientRect","bottom","top","height","left","right","width","getLength","_overload7","_overload8","getContents","getFormat","Number","MAX_VALUE","getRange","_overload9","_overload10","getText","hasFocus","embed","_this7","insertEmbed","_this8","_overload11","_overload12","insertText","contains","off","once","dangerouslyPasteHTML","_this9","_overload13","_overload14","removeFormat","_this10","deleted","applied","applyDelta","_overload15","_overload16","_this11","version","parchment","core/module","core/theme","Module","quill","TextBlot","_Parchment$Text","_eventemitter","_eventemitter2","eventName","addEventListener","_len","_key","querySelectorAll","_node$__quill$emitter","handleDOM","Emitter","_EventEmitter","listeners","log","event","_len2","_key2","_ref","handler","SCROLL_BEFORE_UPDATE","SCROLL_OPTIMIZE","SELECTION_CHANGE","method","levels","_console","console","namespace","ns","logger","bind","newLevel","attributeBit","ATTRIBUTE","item","canAdd","replace","removeAttribute","isUndefinedOrNull","isBuffer","x","objEquiv","a","opts","isArguments","pSlice","deepEqual","ka","objectKeys","kb","e","sort","actual","expected","Date","getTime","Code","_block","_block2","_Inline","CodeBlock","_Block","textContent","frag","_descendant","descendant","_descendant2","deleteAt","nextNewline","newlineIndex","prevNewline","isolateLength","formatAt","_descendant3","_descendant4","searchIndex","lastIndexOf","appendChild","prev","optimize","removeChild","unwrap","TAB","Break","sanitize","url","protocols","anchor","href","protocol","Link","PROTOCOL_WHITELIST","SANITIZED_URL","toggleAriaAttribute","element","_keyboard","_keyboard2","_dropdown","_dropdown2","optionsCounter","Picker","select","buildPicker","style","display","label","togglePicker","keyCode","ENTER","ESCAPE","escape","preventDefault","option","tabIndex","hasAttribute","selectItem","id","buildItem","selected","buildLabel","buildOptions","close","setTimeout","trigger","selectedIndex","Event","dispatchEvent","createEvent","initEvent","isActive","makeBlot","childNodes","replaceChild","attach","linked_list_1","shadow_1","ContainerBlot","build","reverse","forEachAt","criteria","_a","lengthLeft","detach","childBlot","refBlot","some","insertInto","memo","targetParent","inclusive","position","after","addedNodes","removedNodes","mutation","body","compareDocumentPosition","DOCUMENT_POSITION_CONTAINED_BY","DOCUMENT_POSITION_FOLLOWING","nextSibling","FormatBlot","toLowerCase","replaceWith","replacement","wrapper","move","LeafBlot","INLINE_BLOT","Iterator","lib","keepNull","retOp","substr","_instanceof","circular","depth","includeNonEnumerable","_clone","proto","nativeMap","nativeSet","nativePromise","resolve","reject","then","__isArray","__isRegExp","RegExp","__getRegExpFlags","lastIndex","__isDate","useBuffer","Buffer","allParents","allChildren","keyChild","valueChild","set","entryChild","attrs","getOwnPropertySymbols","symbols","symbol","allPropertyNames","getOwnPropertyNames","propertyName","__objToStr","re","flags","global","ignoreCase","multiline","Map","_","Set","Promise","clonePrototype","_toConsumableArray","arr2","from","_clone2","_deepEqual","_deepEqual2","Selection","composing","mouseDown","cursor","savedRange","handleComposition","handleDragging","listenDOM","native","getNativeRange","textNode","setNativeRange","ignored","_context$range","startNode","startOffset","endNode","endOffset","restore","nativeRange","collapsed","data","scrollLength","_scroll$leaf","_scroll$leaf2","_leaf$position","_leaf$position2","createRange","setStart","_scroll$leaf3","_scroll$leaf4","_leaf$position3","_leaf$position4","setEnd","side","rect","rangeCount","getRangeAt","normalizeNative","info","normalized","normalizedToRange","activeElement","positions","indexes","_position","startContainer","endContainer","lastChild","_scroll$leaf5","_scroll$leaf6","_leaf$position5","_leaf$position6","_scroll$line","_scroll$line2","first","last","_scroll$line3","scrollBounds","removeAllRanges","addRange","blur","rangeToNative","oldRange","_getRange","_getRange2","_Parchment$Container","ColorStyle","ColorClass","ColorAttributor","_Parchment$Attributor","parseInt","makeEmbedArrowHandler","shiftKey","_ref3","where","Keyboard","LEFT","altKey","RIGHT","_quill$getLeaf3","getLeaf","_quill2","handleBackspace","_quill$getLine11","getLine","_quill$getLine12","_quill$getLine13","_quill$getLine14","curFormats","prevFormats","_op2","test","prefix","handleDelete","suffix","nextLength","_quill$getLine15","_quill$getLine16","_quill$getLine17","_quill$getLine18","nextFormats","handleDeleteRange","getLines","firstFormats","lastFormats","handleEnter","lineFormats","makeCodeBlockHandler","indent","code-block","_quill$scroll$descend","_quill$scroll$descend2","scrollIndex","getIndex","makeFormatHandler","shortKey","normalize","binding","charCodeAt","SHORTKEY","_op","_quill","navigator","platform","_Module","bindings","addBinding","metaKey","ctrlKey","userAgent","BACKSPACE","listen","evt","which","defaultPrevented","_quill$getLine","_quill$getLine2","_quill$getLeaf","_quill$getLeaf2","leafStart","offsetStart","_ref2","leafEnd","offsetEnd","prefixText","suffixText","curContext","empty","every","UP","DOWN","bold","italic","underline","outdent","outdent backspace","list","indent code-block","outdent code-block","remove tab","tab","cutoff","updateContents","list empty enter","checklist enter","_quill$getLine3","_quill$getLine4","header enter","_quill$getLine5","_quill$getLine6","header","list autofill","_quill$getLine7","_quill$getLine8","code exit","_quill$getLine9","_quill$getLine10","embed left","embed left shift","embed right","embed right shift","align","","center","justify","background","blockquote","clean","code","color","direction","rtl","float","full","formula","1","2","image","+1","-1","link","ordered","bullet","check","script","sub","super","strike","video","ShadowBlot","cloneNode","parentBlot","refDomNode","AttributorStore","styles","attr","ClassAttributor","result","camelize","parts","rest","part","StyleAttributor","Cursor","createTextNode","CONTENTS","_length","restoreText","Theme","themes","GUARD_TEXT","contentNode","childNode","leftGuard","rightGuard","prevLength","AlignStyle","AlignClass","AlignAttribute","BackgroundStyle","BackgroundClass","_color","DirectionStyle","DirectionClass","DirectionAttribute","FontClass","FontStyle","FontStyleAttributor","SizeStyle","SizeClass","Bold","_picker","_picker2","ColorPicker","_Picker","backgroundColor","colorLabel","stroke","fill","IconPicker","icons","defaultItem","Tooltip","boundsContainer","TEMPLATE","marginTop","hide","reference","offsetWidth","rootBounds","verticalShift","extractVideoUrl","fillSelect","defaultValue","BaseTooltip","_colorPicker","_colorPicker2","_iconPicker","_iconPicker2","_tooltip","_tooltip2","ALIGNS","COLORS","FONTS","HEADERS","SIZES","BaseTheme","_Theme","listener","removeEventListener","tooltip","textbox","pickers","picker","extendToolbar","buttons","button","selects","handlers","edit","fileInput","files","reader","FileReader","onload","readAsDataURL","click","_Tooltip","save","cancel","mode","preview","linkRange","restoreFocus","_core","_core2","_align","_direction","_indent","_blockquote","_blockquote2","_header","_header2","_list","_list2","_background","_font","_size","_bold","_bold2","_italic","_italic2","_link","_link2","_script","_script2","_strike","_strike2","_underline","_underline2","_image","_image2","_video","_video2","_code","_code2","_formula","_formula2","_syntax","_syntax2","_toolbar","_toolbar2","_icons","_icons2","_bubble","_bubble2","_snow","_snow2","attributors/attribute/direction","attributors/class/align","attributors/class/background","attributors/class/color","attributors/class/direction","attributors/class/font","attributors/class/size","attributors/style/align","attributors/style/background","attributors/style/color","attributors/style/direction","attributors/style/font","attributors/style/size","formats/align","formats/direction","formats/indent","IndentClass","formats/background","formats/color","formats/font","formats/size","formats/blockquote","formats/code-block","formats/header","formats/list","formats/bold","formats/code","formats/italic","formats/link","formats/script","formats/strike","formats/underline","formats/image","formats/video","formats/list/item","ListItem","modules/formula","modules/syntax","modules/toolbar","themes/bubble","themes/snow","ui/icons","ui/picker","ui/icon-picker","ui/color-picker","ui/tooltip","_container","_container2","_cursor","_cursor2","_embed","_embed2","_scroll","_scroll2","_clipboard","_clipboard2","_history","_history2","blots/block","blots/block/embed","blots/break","blots/container","blots/cursor","blots/embed","blots/inline","blots/scroll","blots/text","modules/clipboard","modules/history","modules/keyboard","LinkedList","append","nodes","cur","curNode","ret","callback","curIndex","curLength","OBSERVER_CONFIG","characterData","characterDataOldValue","childList","subtree","ScrollBlot","observer","MutationObserver","observe","disconnect","records","takeRecords","mark","markParent","remaining","previousSibling","grandChild","isEqual","obj1","obj2","prop","InlineBlot","BlockBlot","EmbedBlot","splitText","_toggle","DOMTokenList","token","searchString","subjectString","isFinite","floor","thisArg","execCommand","diff_main","text1","text2","cursor_pos","DIFF_EQUAL","commonlength","diff_commonPrefix","commonprefix","substring","diff_commonSuffix","commonsuffix","diffs","diff_compute_","diff_cleanupMerge","fix_cursor","fix_emoji","DIFF_INSERT","DIFF_DELETE","longtext","shorttext","hm","diff_halfMatch_","text1_a","text1_b","text2_a","text2_b","mid_common","diffs_a","diffs_b","diff_bisect_","text1_length","text2_length","max_d","ceil","v_offset","v_length","v1","v2","front","k1start","k1end","k2start","k2end","k1","x1","k1_offset","y1","charAt","k2_offset","x2","diff_bisectSplit_","k2","y2","y","text1a","text2a","text1b","text2b","diffsb","pointermin","pointermax","pointermid","pointerstart","pointerend","diff_halfMatchI_","best_longtext_a","best_longtext_b","best_shorttext_a","best_shorttext_b","seed","j","best_common","prefixLength","suffixLength","hm1","hm2","pointer","count_delete","count_insert","text_delete","text_insert","changes","cursor_normalize_diff","current_pos","next_pos","split_pos","d_left","d_right","norm","ndiffs","cursor_pointer","d_next","merge_tuples","compact","starts_with_pair_end","str","fixed_diffs","left_d","right_d","shim","supported","unsupported","propertyIsEnumerable","supportsArgumentsClass","combineFormats","combined","merged","normalizeDelta","ASCII","Editor","getDelta","consumeNextNewline","batchStart","_line$descendant","_line$descendant2","batchEnd","lengthRemaining","lineLength","codeIndex","codeLength","leaves","_path","formatsArr","blots","_scroll$line4","cursorIndex","textBlot","oldValue","oldText","newText","Events","EE","fn","EventEmitter","_events","_eventsCount","has","eventNames","exists","available","ee","a1","a2","a3","a4","a5","len","removeListener","removeAllListeners","addListener","setMaxListeners","prefixed","isLine","_Parchment$Scroll","batch","_line","_line2","_line3","_line4","applyFormat","_extend3","computeStyle","nodeType","ELEMENT_NODE","window","getComputedStyle","deltaEndsWith","endText","traverse","elementMatchers","textMatchers","matcher","childrenDelta","DOM_KEY","matchAlias","matchAttributor","ATTRIBUTE_ATTRIBUTORS","STYLE_ATTRIBUTORS","matchBlot","matchBreak","matchIgnore","matchIndent","matchNewline","matchSpacing","nextElementSibling","nodeHeight","offsetHeight","parseFloat","marginBottom","offsetTop","matchStyles","fontStyle","fontWeight","textIndent","matchText","whiteSpace","replacer","collapse","CLIPBOARD_CONFIG","Clipboard","onPaste","matchers","selector","matchVisual","addMatcher","innerText","_prepareMatching","prepareMatching","_prepareMatching2","paste","pair","_pair","endsWithNewlineChange","getLastChangeIndex","deleteLength","changeIndex","History","lastRecorded","ignoreChange","userOnly","record","undo","redo","dest","stack","changeDelta","undoDelta","timestamp","now","delay","maxStack","IdentAttributor","Blockquote","Header","List","_Container","listEventHandler","Italic","_Bold","Script","Strike","Underline","ATTRIBUTES","Image","Video","_BlockEmbed","FormulaBlot","_Embed","katex","render","throwOnError","errorColor","Formula","CodeToken","SyntaxCodeBlock","_CodeBlock","highlight","cachedText","Syntax","timer","clearTimeout","interval","hljs","highlightAuto","addButton","addControls","groups","controls","group","control","addSelect","Toolbar","_ret","addHandler","_this$quill$selection","_this$quill$selection2","_quill$selection$getR","_quill$selection$getR2","prompt","BubbleTooltip","_base","_base2","TOOLBAR_CONFIG","BubbleTheme","_BaseTheme","buildButtons","buildPickers","_BaseTooltip","show","lastLine","arrow","marginLeft","SnowTheme","SnowTooltip"],"mappings":"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","file":"quill.min.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"Quill\"] = factory();\n\telse\n\t\troot[\"Quill\"] = factory();\n})(typeof self !== 'undefined' ? self : this, function() {\nreturn \n\n\n// WEBPACK FOOTER //\n// webpack/universalModuleDefinition","/*!\n * Quill Editor v1.3.6\n * https://quilljs.com/\n * Copyright (c) 2014, Jason Chen\n * Copyright (c) 2013, salesforce.com\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"Quill\"] = factory();\n\telse\n\t\troot[\"Quill\"] = factory();\n})(typeof self !== 'undefined' ? self : this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 45);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar container_1 = __webpack_require__(17);\nvar format_1 = __webpack_require__(18);\nvar leaf_1 = __webpack_require__(19);\nvar scroll_1 = __webpack_require__(48);\nvar inline_1 = __webpack_require__(49);\nvar block_1 = __webpack_require__(50);\nvar embed_1 = __webpack_require__(51);\nvar text_1 = __webpack_require__(52);\nvar attributor_1 = __webpack_require__(11);\nvar class_1 = __webpack_require__(29);\nvar style_1 = __webpack_require__(30);\nvar store_1 = __webpack_require__(28);\nvar Registry = __webpack_require__(1);\nvar Parchment = {\n Scope: Registry.Scope,\n create: Registry.create,\n find: Registry.find,\n query: Registry.query,\n register: Registry.register,\n Container: container_1.default,\n Format: format_1.default,\n Leaf: leaf_1.default,\n Embed: embed_1.default,\n Scroll: scroll_1.default,\n Block: block_1.default,\n Inline: inline_1.default,\n Text: text_1.default,\n Attributor: {\n Attribute: attributor_1.default,\n Class: class_1.default,\n Style: style_1.default,\n Store: store_1.default,\n },\n};\nexports.default = Parchment;\n\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar ParchmentError = /** @class */ (function (_super) {\n __extends(ParchmentError, _super);\n function ParchmentError(message) {\n var _this = this;\n message = '[Parchment] ' + message;\n _this = _super.call(this, message) || this;\n _this.message = message;\n _this.name = _this.constructor.name;\n return _this;\n }\n return ParchmentError;\n}(Error));\nexports.ParchmentError = ParchmentError;\nvar attributes = {};\nvar classes = {};\nvar tags = {};\nvar types = {};\nexports.DATA_KEY = '__blot';\nvar Scope;\n(function (Scope) {\n Scope[Scope[\"TYPE\"] = 3] = \"TYPE\";\n Scope[Scope[\"LEVEL\"] = 12] = \"LEVEL\";\n Scope[Scope[\"ATTRIBUTE\"] = 13] = \"ATTRIBUTE\";\n Scope[Scope[\"BLOT\"] = 14] = \"BLOT\";\n Scope[Scope[\"INLINE\"] = 7] = \"INLINE\";\n Scope[Scope[\"BLOCK\"] = 11] = \"BLOCK\";\n Scope[Scope[\"BLOCK_BLOT\"] = 10] = \"BLOCK_BLOT\";\n Scope[Scope[\"INLINE_BLOT\"] = 6] = \"INLINE_BLOT\";\n Scope[Scope[\"BLOCK_ATTRIBUTE\"] = 9] = \"BLOCK_ATTRIBUTE\";\n Scope[Scope[\"INLINE_ATTRIBUTE\"] = 5] = \"INLINE_ATTRIBUTE\";\n Scope[Scope[\"ANY\"] = 15] = \"ANY\";\n})(Scope = exports.Scope || (exports.Scope = {}));\nfunction create(input, value) {\n var match = query(input);\n if (match == null) {\n throw new ParchmentError(\"Unable to create \" + input + \" blot\");\n }\n var BlotClass = match;\n var node = \n // @ts-ignore\n input instanceof Node || input['nodeType'] === Node.TEXT_NODE ? input : BlotClass.create(value);\n return new BlotClass(node, value);\n}\nexports.create = create;\nfunction find(node, bubble) {\n if (bubble === void 0) { bubble = false; }\n if (node == null)\n return null;\n // @ts-ignore\n if (node[exports.DATA_KEY] != null)\n return node[exports.DATA_KEY].blot;\n if (bubble)\n return find(node.parentNode, bubble);\n return null;\n}\nexports.find = find;\nfunction query(query, scope) {\n if (scope === void 0) { scope = Scope.ANY; }\n var match;\n if (typeof query === 'string') {\n match = types[query] || attributes[query];\n // @ts-ignore\n }\n else if (query instanceof Text || query['nodeType'] === Node.TEXT_NODE) {\n match = types['text'];\n }\n else if (typeof query === 'number') {\n if (query & Scope.LEVEL & Scope.BLOCK) {\n match = types['block'];\n }\n else if (query & Scope.LEVEL & Scope.INLINE) {\n match = types['inline'];\n }\n }\n else if (query instanceof HTMLElement) {\n var names = (query.getAttribute('class') || '').split(/\\s+/);\n for (var i in names) {\n match = classes[names[i]];\n if (match)\n break;\n }\n match = match || tags[query.tagName];\n }\n if (match == null)\n return null;\n // @ts-ignore\n if (scope & Scope.LEVEL & match.scope && scope & Scope.TYPE & match.scope)\n return match;\n return null;\n}\nexports.query = query;\nfunction register() {\n var Definitions = [];\n for (var _i = 0; _i < arguments.length; _i++) {\n Definitions[_i] = arguments[_i];\n }\n if (Definitions.length > 1) {\n return Definitions.map(function (d) {\n return register(d);\n });\n }\n var Definition = Definitions[0];\n if (typeof Definition.blotName !== 'string' && typeof Definition.attrName !== 'string') {\n throw new ParchmentError('Invalid definition');\n }\n else if (Definition.blotName === 'abstract') {\n throw new ParchmentError('Cannot register abstract class');\n }\n types[Definition.blotName || Definition.attrName] = Definition;\n if (typeof Definition.keyName === 'string') {\n attributes[Definition.keyName] = Definition;\n }\n else {\n if (Definition.className != null) {\n classes[Definition.className] = Definition;\n }\n if (Definition.tagName != null) {\n if (Array.isArray(Definition.tagName)) {\n Definition.tagName = Definition.tagName.map(function (tagName) {\n return tagName.toUpperCase();\n });\n }\n else {\n Definition.tagName = Definition.tagName.toUpperCase();\n }\n var tagNames = Array.isArray(Definition.tagName) ? Definition.tagName : [Definition.tagName];\n tagNames.forEach(function (tag) {\n if (tags[tag] == null || Definition.className == null) {\n tags[tag] = Definition;\n }\n });\n }\n }\n return Definition;\n}\nexports.register = register;\n\n\n/***/ }),\n/* 2 */\n/***/ (function(module, exports) {\n\n'use strict';\n\nvar hasOwn = Object.prototype.hasOwnProperty;\nvar toStr = Object.prototype.toString;\n\nvar isArray = function isArray(arr) {\n\tif (typeof Array.isArray === 'function') {\n\t\treturn Array.isArray(arr);\n\t}\n\n\treturn toStr.call(arr) === '[object Array]';\n};\n\nvar isPlainObject = function isPlainObject(obj) {\n\tif (!obj || toStr.call(obj) !== '[object Object]') {\n\t\treturn false;\n\t}\n\n\tvar hasOwnConstructor = hasOwn.call(obj, 'constructor');\n\tvar hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');\n\t// Not own constructor property must be Object\n\tif (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {\n\t\treturn false;\n\t}\n\n\t// Own properties are enumerated firstly, so to speed up,\n\t// if last one is own, then all properties are own.\n\tvar key;\n\tfor (key in obj) { /**/ }\n\n\treturn typeof key === 'undefined' || hasOwn.call(obj, key);\n};\n\nmodule.exports = function extend() {\n\tvar options, name, src, copy, copyIsArray, clone;\n\tvar target = arguments[0];\n\tvar i = 1;\n\tvar length = arguments.length;\n\tvar deep = false;\n\n\t// Handle a deep copy situation\n\tif (typeof target === 'boolean') {\n\t\tdeep = target;\n\t\ttarget = arguments[1] || {};\n\t\t// skip the boolean and the target\n\t\ti = 2;\n\t}\n\tif (target == null || (typeof target !== 'object' && typeof target !== 'function')) {\n\t\ttarget = {};\n\t}\n\n\tfor (; i < length; ++i) {\n\t\toptions = arguments[i];\n\t\t// Only deal with non-null/undefined values\n\t\tif (options != null) {\n\t\t\t// Extend the base object\n\t\t\tfor (name in options) {\n\t\t\t\tsrc = target[name];\n\t\t\t\tcopy = options[name];\n\n\t\t\t\t// Prevent never-ending loop\n\t\t\t\tif (target !== copy) {\n\t\t\t\t\t// Recurse if we're merging plain objects or arrays\n\t\t\t\t\tif (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {\n\t\t\t\t\t\tif (copyIsArray) {\n\t\t\t\t\t\t\tcopyIsArray = false;\n\t\t\t\t\t\t\tclone = src && isArray(src) ? src : [];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tclone = src && isPlainObject(src) ? src : {};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Never move original objects, clone them\n\t\t\t\t\t\ttarget[name] = extend(deep, clone, copy);\n\n\t\t\t\t\t// Don't bring in undefined values\n\t\t\t\t\t} else if (typeof copy !== 'undefined') {\n\t\t\t\t\t\ttarget[name] = copy;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Return the modified object\n\treturn target;\n};\n\n\n/***/ }),\n/* 3 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = exports.BlockEmbed = exports.bubbleFormats = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _extend = __webpack_require__(2);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nvar _quillDelta = __webpack_require__(4);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _break = __webpack_require__(14);\n\nvar _break2 = _interopRequireDefault(_break);\n\nvar _inline = __webpack_require__(5);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nvar _text = __webpack_require__(8);\n\nvar _text2 = _interopRequireDefault(_text);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar NEWLINE_LENGTH = 1;\n\nvar BlockEmbed = function (_Parchment$Embed) {\n _inherits(BlockEmbed, _Parchment$Embed);\n\n function BlockEmbed() {\n _classCallCheck(this, BlockEmbed);\n\n return _possibleConstructorReturn(this, (BlockEmbed.__proto__ || Object.getPrototypeOf(BlockEmbed)).apply(this, arguments));\n }\n\n _createClass(BlockEmbed, [{\n key: 'attach',\n value: function attach() {\n _get(BlockEmbed.prototype.__proto__ || Object.getPrototypeOf(BlockEmbed.prototype), 'attach', this).call(this);\n this.attributes = new _parchment2.default.Attributor.Store(this.domNode);\n }\n }, {\n key: 'delta',\n value: function delta() {\n return new _quillDelta2.default().insert(this.value(), (0, _extend2.default)(this.formats(), this.attributes.values()));\n }\n }, {\n key: 'format',\n value: function format(name, value) {\n var attribute = _parchment2.default.query(name, _parchment2.default.Scope.BLOCK_ATTRIBUTE);\n if (attribute != null) {\n this.attributes.attribute(attribute, value);\n }\n }\n }, {\n key: 'formatAt',\n value: function formatAt(index, length, name, value) {\n this.format(name, value);\n }\n }, {\n key: 'insertAt',\n value: function insertAt(index, value, def) {\n if (typeof value === 'string' && value.endsWith('\\n')) {\n var block = _parchment2.default.create(Block.blotName);\n this.parent.insertBefore(block, index === 0 ? this : this.next);\n block.insertAt(0, value.slice(0, -1));\n } else {\n _get(BlockEmbed.prototype.__proto__ || Object.getPrototypeOf(BlockEmbed.prototype), 'insertAt', this).call(this, index, value, def);\n }\n }\n }]);\n\n return BlockEmbed;\n}(_parchment2.default.Embed);\n\nBlockEmbed.scope = _parchment2.default.Scope.BLOCK_BLOT;\n// It is important for cursor behavior BlockEmbeds use tags that are block level elements\n\n\nvar Block = function (_Parchment$Block) {\n _inherits(Block, _Parchment$Block);\n\n function Block(domNode) {\n _classCallCheck(this, Block);\n\n var _this2 = _possibleConstructorReturn(this, (Block.__proto__ || Object.getPrototypeOf(Block)).call(this, domNode));\n\n _this2.cache = {};\n return _this2;\n }\n\n _createClass(Block, [{\n key: 'delta',\n value: function delta() {\n if (this.cache.delta == null) {\n this.cache.delta = this.descendants(_parchment2.default.Leaf).reduce(function (delta, leaf) {\n if (leaf.length() === 0) {\n return delta;\n } else {\n return delta.insert(leaf.value(), bubbleFormats(leaf));\n }\n }, new _quillDelta2.default()).insert('\\n', bubbleFormats(this));\n }\n return this.cache.delta;\n }\n }, {\n key: 'deleteAt',\n value: function deleteAt(index, length) {\n _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'deleteAt', this).call(this, index, length);\n this.cache = {};\n }\n }, {\n key: 'formatAt',\n value: function formatAt(index, length, name, value) {\n if (length <= 0) return;\n if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK)) {\n if (index + length === this.length()) {\n this.format(name, value);\n }\n } else {\n _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'formatAt', this).call(this, index, Math.min(length, this.length() - index - 1), name, value);\n }\n this.cache = {};\n }\n }, {\n key: 'insertAt',\n value: function insertAt(index, value, def) {\n if (def != null) return _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertAt', this).call(this, index, value, def);\n if (value.length === 0) return;\n var lines = value.split('\\n');\n var text = lines.shift();\n if (text.length > 0) {\n if (index < this.length() - 1 || this.children.tail == null) {\n _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertAt', this).call(this, Math.min(index, this.length() - 1), text);\n } else {\n this.children.tail.insertAt(this.children.tail.length(), text);\n }\n this.cache = {};\n }\n var block = this;\n lines.reduce(function (index, line) {\n block = block.split(index, true);\n block.insertAt(0, line);\n return line.length;\n }, index + text.length);\n }\n }, {\n key: 'insertBefore',\n value: function insertBefore(blot, ref) {\n var head = this.children.head;\n _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'insertBefore', this).call(this, blot, ref);\n if (head instanceof _break2.default) {\n head.remove();\n }\n this.cache = {};\n }\n }, {\n key: 'length',\n value: function length() {\n if (this.cache.length == null) {\n this.cache.length = _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'length', this).call(this) + NEWLINE_LENGTH;\n }\n return this.cache.length;\n }\n }, {\n key: 'moveChildren',\n value: function moveChildren(target, ref) {\n _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'moveChildren', this).call(this, target, ref);\n this.cache = {};\n }\n }, {\n key: 'optimize',\n value: function optimize(context) {\n _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'optimize', this).call(this, context);\n this.cache = {};\n }\n }, {\n key: 'path',\n value: function path(index) {\n return _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'path', this).call(this, index, true);\n }\n }, {\n key: 'removeChild',\n value: function removeChild(child) {\n _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'removeChild', this).call(this, child);\n this.cache = {};\n }\n }, {\n key: 'split',\n value: function split(index) {\n var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n if (force && (index === 0 || index >= this.length() - NEWLINE_LENGTH)) {\n var clone = this.clone();\n if (index === 0) {\n this.parent.insertBefore(clone, this);\n return this;\n } else {\n this.parent.insertBefore(clone, this.next);\n return clone;\n }\n } else {\n var next = _get(Block.prototype.__proto__ || Object.getPrototypeOf(Block.prototype), 'split', this).call(this, index, force);\n this.cache = {};\n return next;\n }\n }\n }]);\n\n return Block;\n}(_parchment2.default.Block);\n\nBlock.blotName = 'block';\nBlock.tagName = 'P';\nBlock.defaultChild = 'break';\nBlock.allowedChildren = [_inline2.default, _parchment2.default.Embed, _text2.default];\n\nfunction bubbleFormats(blot) {\n var formats = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n if (blot == null) return formats;\n if (typeof blot.formats === 'function') {\n formats = (0, _extend2.default)(formats, blot.formats());\n }\n if (blot.parent == null || blot.parent.blotName == 'scroll' || blot.parent.statics.scope !== blot.statics.scope) {\n return formats;\n }\n return bubbleFormats(blot.parent, formats);\n}\n\nexports.bubbleFormats = bubbleFormats;\nexports.BlockEmbed = BlockEmbed;\nexports.default = Block;\n\n/***/ }),\n/* 4 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar diff = __webpack_require__(54);\nvar equal = __webpack_require__(12);\nvar extend = __webpack_require__(2);\nvar op = __webpack_require__(20);\n\n\nvar NULL_CHARACTER = String.fromCharCode(0); // Placeholder char for embed in diff()\n\n\nvar Delta = function (ops) {\n // Assume we are given a well formed ops\n if (Array.isArray(ops)) {\n this.ops = ops;\n } else if (ops != null && Array.isArray(ops.ops)) {\n this.ops = ops.ops;\n } else {\n this.ops = [];\n }\n};\n\n\nDelta.prototype.insert = function (text, attributes) {\n var newOp = {};\n if (text.length === 0) return this;\n newOp.insert = text;\n if (attributes != null && typeof attributes === 'object' && Object.keys(attributes).length > 0) {\n newOp.attributes = attributes;\n }\n return this.push(newOp);\n};\n\nDelta.prototype['delete'] = function (length) {\n if (length <= 0) return this;\n return this.push({ 'delete': length });\n};\n\nDelta.prototype.retain = function (length, attributes) {\n if (length <= 0) return this;\n var newOp = { retain: length };\n if (attributes != null && typeof attributes === 'object' && Object.keys(attributes).length > 0) {\n newOp.attributes = attributes;\n }\n return this.push(newOp);\n};\n\nDelta.prototype.push = function (newOp) {\n var index = this.ops.length;\n var lastOp = this.ops[index - 1];\n newOp = extend(true, {}, newOp);\n if (typeof lastOp === 'object') {\n if (typeof newOp['delete'] === 'number' && typeof lastOp['delete'] === 'number') {\n this.ops[index - 1] = { 'delete': lastOp['delete'] + newOp['delete'] };\n return this;\n }\n // Since it does not matter if we insert before or after deleting at the same index,\n // always prefer to insert first\n if (typeof lastOp['delete'] === 'number' && newOp.insert != null) {\n index -= 1;\n lastOp = this.ops[index - 1];\n if (typeof lastOp !== 'object') {\n this.ops.unshift(newOp);\n return this;\n }\n }\n if (equal(newOp.attributes, lastOp.attributes)) {\n if (typeof newOp.insert === 'string' && typeof lastOp.insert === 'string') {\n this.ops[index - 1] = { insert: lastOp.insert + newOp.insert };\n if (typeof newOp.attributes === 'object') this.ops[index - 1].attributes = newOp.attributes\n return this;\n } else if (typeof newOp.retain === 'number' && typeof lastOp.retain === 'number') {\n this.ops[index - 1] = { retain: lastOp.retain + newOp.retain };\n if (typeof newOp.attributes === 'object') this.ops[index - 1].attributes = newOp.attributes\n return this;\n }\n }\n }\n if (index === this.ops.length) {\n this.ops.push(newOp);\n } else {\n this.ops.splice(index, 0, newOp);\n }\n return this;\n};\n\nDelta.prototype.chop = function () {\n var lastOp = this.ops[this.ops.length - 1];\n if (lastOp && lastOp.retain && !lastOp.attributes) {\n this.ops.pop();\n }\n return this;\n};\n\nDelta.prototype.filter = function (predicate) {\n return this.ops.filter(predicate);\n};\n\nDelta.prototype.forEach = function (predicate) {\n this.ops.forEach(predicate);\n};\n\nDelta.prototype.map = function (predicate) {\n return this.ops.map(predicate);\n};\n\nDelta.prototype.partition = function (predicate) {\n var passed = [], failed = [];\n this.forEach(function(op) {\n var target = predicate(op) ? passed : failed;\n target.push(op);\n });\n return [passed, failed];\n};\n\nDelta.prototype.reduce = function (predicate, initial) {\n return this.ops.reduce(predicate, initial);\n};\n\nDelta.prototype.changeLength = function () {\n return this.reduce(function (length, elem) {\n if (elem.insert) {\n return length + op.length(elem);\n } else if (elem.delete) {\n return length - elem.delete;\n }\n return length;\n }, 0);\n};\n\nDelta.prototype.length = function () {\n return this.reduce(function (length, elem) {\n return length + op.length(elem);\n }, 0);\n};\n\nDelta.prototype.slice = function (start, end) {\n start = start || 0;\n if (typeof end !== 'number') end = Infinity;\n var ops = [];\n var iter = op.iterator(this.ops);\n var index = 0;\n while (index < end && iter.hasNext()) {\n var nextOp;\n if (index < start) {\n nextOp = iter.next(start - index);\n } else {\n nextOp = iter.next(end - index);\n ops.push(nextOp);\n }\n index += op.length(nextOp);\n }\n return new Delta(ops);\n};\n\n\nDelta.prototype.compose = function (other) {\n var thisIter = op.iterator(this.ops);\n var otherIter = op.iterator(other.ops);\n var delta = new Delta();\n while (thisIter.hasNext() || otherIter.hasNext()) {\n if (otherIter.peekType() === 'insert') {\n delta.push(otherIter.next());\n } else if (thisIter.peekType() === 'delete') {\n delta.push(thisIter.next());\n } else {\n var length = Math.min(thisIter.peekLength(), otherIter.peekLength());\n var thisOp = thisIter.next(length);\n var otherOp = otherIter.next(length);\n if (typeof otherOp.retain === 'number') {\n var newOp = {};\n if (typeof thisOp.retain === 'number') {\n newOp.retain = length;\n } else {\n newOp.insert = thisOp.insert;\n }\n // Preserve null when composing with a retain, otherwise remove it for inserts\n var attributes = op.attributes.compose(thisOp.attributes, otherOp.attributes, typeof thisOp.retain === 'number');\n if (attributes) newOp.attributes = attributes;\n delta.push(newOp);\n // Other op should be delete, we could be an insert or retain\n // Insert + delete cancels out\n } else if (typeof otherOp['delete'] === 'number' && typeof thisOp.retain === 'number') {\n delta.push(otherOp);\n }\n }\n }\n return delta.chop();\n};\n\nDelta.prototype.concat = function (other) {\n var delta = new Delta(this.ops.slice());\n if (other.ops.length > 0) {\n delta.push(other.ops[0]);\n delta.ops = delta.ops.concat(other.ops.slice(1));\n }\n return delta;\n};\n\nDelta.prototype.diff = function (other, index) {\n if (this.ops === other.ops) {\n return new Delta();\n }\n var strings = [this, other].map(function (delta) {\n return delta.map(function (op) {\n if (op.insert != null) {\n return typeof op.insert === 'string' ? op.insert : NULL_CHARACTER;\n }\n var prep = (delta === other) ? 'on' : 'with';\n throw new Error('diff() called ' + prep + ' non-document');\n }).join('');\n });\n var delta = new Delta();\n var diffResult = diff(strings[0], strings[1], index);\n var thisIter = op.iterator(this.ops);\n var otherIter = op.iterator(other.ops);\n diffResult.forEach(function (component) {\n var length = component[1].length;\n while (length > 0) {\n var opLength = 0;\n switch (component[0]) {\n case diff.INSERT:\n opLength = Math.min(otherIter.peekLength(), length);\n delta.push(otherIter.next(opLength));\n break;\n case diff.DELETE:\n opLength = Math.min(length, thisIter.peekLength());\n thisIter.next(opLength);\n delta['delete'](opLength);\n break;\n case diff.EQUAL:\n opLength = Math.min(thisIter.peekLength(), otherIter.peekLength(), length);\n var thisOp = thisIter.next(opLength);\n var otherOp = otherIter.next(opLength);\n if (equal(thisOp.insert, otherOp.insert)) {\n delta.retain(opLength, op.attributes.diff(thisOp.attributes, otherOp.attributes));\n } else {\n delta.push(otherOp)['delete'](opLength);\n }\n break;\n }\n length -= opLength;\n }\n });\n return delta.chop();\n};\n\nDelta.prototype.eachLine = function (predicate, newline) {\n newline = newline || '\\n';\n var iter = op.iterator(this.ops);\n var line = new Delta();\n var i = 0;\n while (iter.hasNext()) {\n if (iter.peekType() !== 'insert') return;\n var thisOp = iter.peek();\n var start = op.length(thisOp) - iter.peekLength();\n var index = typeof thisOp.insert === 'string' ?\n thisOp.insert.indexOf(newline, start) - start : -1;\n if (index < 0) {\n line.push(iter.next());\n } else if (index > 0) {\n line.push(iter.next(index));\n } else {\n if (predicate(line, iter.next(1).attributes || {}, i) === false) {\n return;\n }\n i += 1;\n line = new Delta();\n }\n }\n if (line.length() > 0) {\n predicate(line, {}, i);\n }\n};\n\nDelta.prototype.transform = function (other, priority) {\n priority = !!priority;\n if (typeof other === 'number') {\n return this.transformPosition(other, priority);\n }\n var thisIter = op.iterator(this.ops);\n var otherIter = op.iterator(other.ops);\n var delta = new Delta();\n while (thisIter.hasNext() || otherIter.hasNext()) {\n if (thisIter.peekType() === 'insert' && (priority || otherIter.peekType() !== 'insert')) {\n delta.retain(op.length(thisIter.next()));\n } else if (otherIter.peekType() === 'insert') {\n delta.push(otherIter.next());\n } else {\n var length = Math.min(thisIter.peekLength(), otherIter.peekLength());\n var thisOp = thisIter.next(length);\n var otherOp = otherIter.next(length);\n if (thisOp['delete']) {\n // Our delete either makes their delete redundant or removes their retain\n continue;\n } else if (otherOp['delete']) {\n delta.push(otherOp);\n } else {\n // We retain either their retain or insert\n delta.retain(length, op.attributes.transform(thisOp.attributes, otherOp.attributes, priority));\n }\n }\n }\n return delta.chop();\n};\n\nDelta.prototype.transformPosition = function (index, priority) {\n priority = !!priority;\n var thisIter = op.iterator(this.ops);\n var offset = 0;\n while (thisIter.hasNext() && offset <= index) {\n var length = thisIter.peekLength();\n var nextType = thisIter.peekType();\n thisIter.next();\n if (nextType === 'delete') {\n index -= Math.min(length, index - offset);\n continue;\n } else if (nextType === 'insert' && (offset < index || !priority)) {\n index += length;\n }\n offset += length;\n }\n return index;\n};\n\n\nmodule.exports = Delta;\n\n\n/***/ }),\n/* 5 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _text = __webpack_require__(8);\n\nvar _text2 = _interopRequireDefault(_text);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Inline = function (_Parchment$Inline) {\n _inherits(Inline, _Parchment$Inline);\n\n function Inline() {\n _classCallCheck(this, Inline);\n\n return _possibleConstructorReturn(this, (Inline.__proto__ || Object.getPrototypeOf(Inline)).apply(this, arguments));\n }\n\n _createClass(Inline, [{\n key: 'formatAt',\n value: function formatAt(index, length, name, value) {\n if (Inline.compare(this.statics.blotName, name) < 0 && _parchment2.default.query(name, _parchment2.default.Scope.BLOT)) {\n var blot = this.isolate(index, length);\n if (value) {\n blot.wrap(name, value);\n }\n } else {\n _get(Inline.prototype.__proto__ || Object.getPrototypeOf(Inline.prototype), 'formatAt', this).call(this, index, length, name, value);\n }\n }\n }, {\n key: 'optimize',\n value: function optimize(context) {\n _get(Inline.prototype.__proto__ || Object.getPrototypeOf(Inline.prototype), 'optimize', this).call(this, context);\n if (this.parent instanceof Inline && Inline.compare(this.statics.blotName, this.parent.statics.blotName) > 0) {\n var parent = this.parent.isolate(this.offset(), this.length());\n this.moveChildren(parent);\n parent.wrap(this);\n }\n }\n }], [{\n key: 'compare',\n value: function compare(self, other) {\n var selfIndex = Inline.order.indexOf(self);\n var otherIndex = Inline.order.indexOf(other);\n if (selfIndex >= 0 || otherIndex >= 0) {\n return selfIndex - otherIndex;\n } else if (self === other) {\n return 0;\n } else if (self < other) {\n return -1;\n } else {\n return 1;\n }\n }\n }]);\n\n return Inline;\n}(_parchment2.default.Inline);\n\nInline.allowedChildren = [Inline, _parchment2.default.Embed, _text2.default];\n// Lower index means deeper in the DOM tree, since not found (-1) is for embeds\nInline.order = ['cursor', 'inline', // Must be lower\n'underline', 'strike', 'italic', 'bold', 'script', 'link', 'code' // Must be higher\n];\n\nexports.default = Inline;\n\n/***/ }),\n/* 6 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = exports.overload = exports.expandConfig = undefined;\n\nvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\n__webpack_require__(53);\n\nvar _quillDelta = __webpack_require__(4);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _editor = __webpack_require__(57);\n\nvar _editor2 = _interopRequireDefault(_editor);\n\nvar _emitter3 = __webpack_require__(9);\n\nvar _emitter4 = _interopRequireDefault(_emitter3);\n\nvar _module = __webpack_require__(7);\n\nvar _module2 = _interopRequireDefault(_module);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _selection = __webpack_require__(22);\n\nvar _selection2 = _interopRequireDefault(_selection);\n\nvar _extend = __webpack_require__(2);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nvar _logger = __webpack_require__(10);\n\nvar _logger2 = _interopRequireDefault(_logger);\n\nvar _theme = __webpack_require__(32);\n\nvar _theme2 = _interopRequireDefault(_theme);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar debug = (0, _logger2.default)('quill');\n\nvar Quill = function () {\n _createClass(Quill, null, [{\n key: 'debug',\n value: function debug(limit) {\n if (limit === true) {\n limit = 'log';\n }\n _logger2.default.level(limit);\n }\n }, {\n key: 'find',\n value: function find(node) {\n return node.__quill || _parchment2.default.find(node);\n }\n }, {\n key: 'import',\n value: function _import(name) {\n if (this.imports[name] == null) {\n debug.error('Cannot import ' + name + '. Are you sure it was registered?');\n }\n return this.imports[name];\n }\n }, {\n key: 'register',\n value: function register(path, target) {\n var _this = this;\n\n var overwrite = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n\n if (typeof path !== 'string') {\n var name = path.attrName || path.blotName;\n if (typeof name === 'string') {\n // register(Blot | Attributor, overwrite)\n this.register('formats/' + name, path, target);\n } else {\n Object.keys(path).forEach(function (key) {\n _this.register(key, path[key], target);\n });\n }\n } else {\n if (this.imports[path] != null && !overwrite) {\n debug.warn('Overwriting ' + path + ' with', target);\n }\n this.imports[path] = target;\n if ((path.startsWith('blots/') || path.startsWith('formats/')) && target.blotName !== 'abstract') {\n _parchment2.default.register(target);\n } else if (path.startsWith('modules') && typeof target.register === 'function') {\n target.register();\n }\n }\n }\n }]);\n\n function Quill(container) {\n var _this2 = this;\n\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n _classCallCheck(this, Quill);\n\n this.options = expandConfig(container, options);\n this.container = this.options.container;\n if (this.container == null) {\n return debug.error('Invalid Quill container', container);\n }\n if (this.options.debug) {\n Quill.debug(this.options.debug);\n }\n var html = this.container.innerHTML.trim();\n this.container.classList.add('ql-container');\n this.container.innerHTML = '';\n this.container.__quill = this;\n this.root = this.addContainer('ql-editor');\n this.root.classList.add('ql-blank');\n this.root.setAttribute('data-gramm', false);\n this.scrollingContainer = this.options.scrollingContainer || this.root;\n this.emitter = new _emitter4.default();\n this.scroll = _parchment2.default.create(this.root, {\n emitter: this.emitter,\n whitelist: this.options.formats\n });\n this.editor = new _editor2.default(this.scroll);\n this.selection = new _selection2.default(this.scroll, this.emitter);\n this.theme = new this.options.theme(this, this.options);\n this.keyboard = this.theme.addModule('keyboard');\n this.clipboard = this.theme.addModule('clipboard');\n this.history = this.theme.addModule('history');\n this.theme.init();\n this.emitter.on(_emitter4.default.events.EDITOR_CHANGE, function (type) {\n if (type === _emitter4.default.events.TEXT_CHANGE) {\n _this2.root.classList.toggle('ql-blank', _this2.editor.isBlank());\n }\n });\n this.emitter.on(_emitter4.default.events.SCROLL_UPDATE, function (source, mutations) {\n var range = _this2.selection.lastRange;\n var index = range && range.length === 0 ? range.index : undefined;\n modify.call(_this2, function () {\n return _this2.editor.update(null, mutations, index);\n }, source);\n });\n var contents = this.clipboard.convert('
' + html + '


');\n this.setContents(contents);\n this.history.clear();\n if (this.options.placeholder) {\n this.root.setAttribute('data-placeholder', this.options.placeholder);\n }\n if (this.options.readOnly) {\n this.disable();\n }\n }\n\n _createClass(Quill, [{\n key: 'addContainer',\n value: function addContainer(container) {\n var refNode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n\n if (typeof container === 'string') {\n var className = container;\n container = document.createElement('div');\n container.classList.add(className);\n }\n this.container.insertBefore(container, refNode);\n return container;\n }\n }, {\n key: 'blur',\n value: function blur() {\n this.selection.setRange(null);\n }\n }, {\n key: 'deleteText',\n value: function deleteText(index, length, source) {\n var _this3 = this;\n\n var _overload = overload(index, length, source);\n\n var _overload2 = _slicedToArray(_overload, 4);\n\n index = _overload2[0];\n length = _overload2[1];\n source = _overload2[3];\n\n return modify.call(this, function () {\n return _this3.editor.deleteText(index, length);\n }, source, index, -1 * length);\n }\n }, {\n key: 'disable',\n value: function disable() {\n this.enable(false);\n }\n }, {\n key: 'enable',\n value: function enable() {\n var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;\n\n this.scroll.enable(enabled);\n this.container.classList.toggle('ql-disabled', !enabled);\n }\n }, {\n key: 'focus',\n value: function focus() {\n var scrollTop = this.scrollingContainer.scrollTop;\n this.selection.focus();\n this.scrollingContainer.scrollTop = scrollTop;\n this.scrollIntoView();\n }\n }, {\n key: 'format',\n value: function format(name, value) {\n var _this4 = this;\n\n var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _emitter4.default.sources.API;\n\n return modify.call(this, function () {\n var range = _this4.getSelection(true);\n var change = new _quillDelta2.default();\n if (range == null) {\n return change;\n } else if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK)) {\n change = _this4.editor.formatLine(range.index, range.length, _defineProperty({}, name, value));\n } else if (range.length === 0) {\n _this4.selection.format(name, value);\n return change;\n } else {\n change = _this4.editor.formatText(range.index, range.length, _defineProperty({}, name, value));\n }\n _this4.setSelection(range, _emitter4.default.sources.SILENT);\n return change;\n }, source);\n }\n }, {\n key: 'formatLine',\n value: function formatLine(index, length, name, value, source) {\n var _this5 = this;\n\n var formats = void 0;\n\n var _overload3 = overload(index, length, name, value, source);\n\n var _overload4 = _slicedToArray(_overload3, 4);\n\n index = _overload4[0];\n length = _overload4[1];\n formats = _overload4[2];\n source = _overload4[3];\n\n return modify.call(this, function () {\n return _this5.editor.formatLine(index, length, formats);\n }, source, index, 0);\n }\n }, {\n key: 'formatText',\n value: function formatText(index, length, name, value, source) {\n var _this6 = this;\n\n var formats = void 0;\n\n var _overload5 = overload(index, length, name, value, source);\n\n var _overload6 = _slicedToArray(_overload5, 4);\n\n index = _overload6[0];\n length = _overload6[1];\n formats = _overload6[2];\n source = _overload6[3];\n\n return modify.call(this, function () {\n return _this6.editor.formatText(index, length, formats);\n }, source, index, 0);\n }\n }, {\n key: 'getBounds',\n value: function getBounds(index) {\n var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n var bounds = void 0;\n if (typeof index === 'number') {\n bounds = this.selection.getBounds(index, length);\n } else {\n bounds = this.selection.getBounds(index.index, index.length);\n }\n var containerBounds = this.container.getBoundingClientRect();\n return {\n bottom: bounds.bottom - containerBounds.top,\n height: bounds.height,\n left: bounds.left - containerBounds.left,\n right: bounds.right - containerBounds.left,\n top: bounds.top - containerBounds.top,\n width: bounds.width\n };\n }\n }, {\n key: 'getContents',\n value: function getContents() {\n var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.getLength() - index;\n\n var _overload7 = overload(index, length);\n\n var _overload8 = _slicedToArray(_overload7, 2);\n\n index = _overload8[0];\n length = _overload8[1];\n\n return this.editor.getContents(index, length);\n }\n }, {\n key: 'getFormat',\n value: function getFormat() {\n var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.getSelection(true);\n var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n if (typeof index === 'number') {\n return this.editor.getFormat(index, length);\n } else {\n return this.editor.getFormat(index.index, index.length);\n }\n }\n }, {\n key: 'getIndex',\n value: function getIndex(blot) {\n return blot.offset(this.scroll);\n }\n }, {\n key: 'getLength',\n value: function getLength() {\n return this.scroll.length();\n }\n }, {\n key: 'getLeaf',\n value: function getLeaf(index) {\n return this.scroll.leaf(index);\n }\n }, {\n key: 'getLine',\n value: function getLine(index) {\n return this.scroll.line(index);\n }\n }, {\n key: 'getLines',\n value: function getLines() {\n var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Number.MAX_VALUE;\n\n if (typeof index !== 'number') {\n return this.scroll.lines(index.index, index.length);\n } else {\n return this.scroll.lines(index, length);\n }\n }\n }, {\n key: 'getModule',\n value: function getModule(name) {\n return this.theme.modules[name];\n }\n }, {\n key: 'getSelection',\n value: function getSelection() {\n var focus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n\n if (focus) this.focus();\n this.update(); // Make sure we access getRange with editor in consistent state\n return this.selection.getRange()[0];\n }\n }, {\n key: 'getText',\n value: function getText() {\n var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.getLength() - index;\n\n var _overload9 = overload(index, length);\n\n var _overload10 = _slicedToArray(_overload9, 2);\n\n index = _overload10[0];\n length = _overload10[1];\n\n return this.editor.getText(index, length);\n }\n }, {\n key: 'hasFocus',\n value: function hasFocus() {\n return this.selection.hasFocus();\n }\n }, {\n key: 'insertEmbed',\n value: function insertEmbed(index, embed, value) {\n var _this7 = this;\n\n var source = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : Quill.sources.API;\n\n return modify.call(this, function () {\n return _this7.editor.insertEmbed(index, embed, value);\n }, source, index);\n }\n }, {\n key: 'insertText',\n value: function insertText(index, text, name, value, source) {\n var _this8 = this;\n\n var formats = void 0;\n\n var _overload11 = overload(index, 0, name, value, source);\n\n var _overload12 = _slicedToArray(_overload11, 4);\n\n index = _overload12[0];\n formats = _overload12[2];\n source = _overload12[3];\n\n return modify.call(this, function () {\n return _this8.editor.insertText(index, text, formats);\n }, source, index, text.length);\n }\n }, {\n key: 'isEnabled',\n value: function isEnabled() {\n return !this.container.classList.contains('ql-disabled');\n }\n }, {\n key: 'off',\n value: function off() {\n return this.emitter.off.apply(this.emitter, arguments);\n }\n }, {\n key: 'on',\n value: function on() {\n return this.emitter.on.apply(this.emitter, arguments);\n }\n }, {\n key: 'once',\n value: function once() {\n return this.emitter.once.apply(this.emitter, arguments);\n }\n }, {\n key: 'pasteHTML',\n value: function pasteHTML(index, html, source) {\n this.clipboard.dangerouslyPasteHTML(index, html, source);\n }\n }, {\n key: 'removeFormat',\n value: function removeFormat(index, length, source) {\n var _this9 = this;\n\n var _overload13 = overload(index, length, source);\n\n var _overload14 = _slicedToArray(_overload13, 4);\n\n index = _overload14[0];\n length = _overload14[1];\n source = _overload14[3];\n\n return modify.call(this, function () {\n return _this9.editor.removeFormat(index, length);\n }, source, index);\n }\n }, {\n key: 'scrollIntoView',\n value: function scrollIntoView() {\n this.selection.scrollIntoView(this.scrollingContainer);\n }\n }, {\n key: 'setContents',\n value: function setContents(delta) {\n var _this10 = this;\n\n var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API;\n\n return modify.call(this, function () {\n delta = new _quillDelta2.default(delta);\n var length = _this10.getLength();\n var deleted = _this10.editor.deleteText(0, length);\n var applied = _this10.editor.applyDelta(delta);\n var lastOp = applied.ops[applied.ops.length - 1];\n if (lastOp != null && typeof lastOp.insert === 'string' && lastOp.insert[lastOp.insert.length - 1] === '\\n') {\n _this10.editor.deleteText(_this10.getLength() - 1, 1);\n applied.delete(1);\n }\n var ret = deleted.compose(applied);\n return ret;\n }, source);\n }\n }, {\n key: 'setSelection',\n value: function setSelection(index, length, source) {\n if (index == null) {\n this.selection.setRange(null, length || Quill.sources.API);\n } else {\n var _overload15 = overload(index, length, source);\n\n var _overload16 = _slicedToArray(_overload15, 4);\n\n index = _overload16[0];\n length = _overload16[1];\n source = _overload16[3];\n\n this.selection.setRange(new _selection.Range(index, length), source);\n if (source !== _emitter4.default.sources.SILENT) {\n this.selection.scrollIntoView(this.scrollingContainer);\n }\n }\n }\n }, {\n key: 'setText',\n value: function setText(text) {\n var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API;\n\n var delta = new _quillDelta2.default().insert(text);\n return this.setContents(delta, source);\n }\n }, {\n key: 'update',\n value: function update() {\n var source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _emitter4.default.sources.USER;\n\n var change = this.scroll.update(source); // Will update selection before selection.update() does if text changes\n this.selection.update(source);\n return change;\n }\n }, {\n key: 'updateContents',\n value: function updateContents(delta) {\n var _this11 = this;\n\n var source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _emitter4.default.sources.API;\n\n return modify.call(this, function () {\n delta = new _quillDelta2.default(delta);\n return _this11.editor.applyDelta(delta, source);\n }, source, true);\n }\n }]);\n\n return Quill;\n}();\n\nQuill.DEFAULTS = {\n bounds: null,\n formats: null,\n modules: {},\n placeholder: '',\n readOnly: false,\n scrollingContainer: null,\n strict: true,\n theme: 'default'\n};\nQuill.events = _emitter4.default.events;\nQuill.sources = _emitter4.default.sources;\n// eslint-disable-next-line no-undef\nQuill.version = false ? 'dev' : \"1.3.6\";\n\nQuill.imports = {\n 'delta': _quillDelta2.default,\n 'parchment': _parchment2.default,\n 'core/module': _module2.default,\n 'core/theme': _theme2.default\n};\n\nfunction expandConfig(container, userConfig) {\n userConfig = (0, _extend2.default)(true, {\n container: container,\n modules: {\n clipboard: true,\n keyboard: true,\n history: true\n }\n }, userConfig);\n if (!userConfig.theme || userConfig.theme === Quill.DEFAULTS.theme) {\n userConfig.theme = _theme2.default;\n } else {\n userConfig.theme = Quill.import('themes/' + userConfig.theme);\n if (userConfig.theme == null) {\n throw new Error('Invalid theme ' + userConfig.theme + '. Did you register it?');\n }\n }\n var themeConfig = (0, _extend2.default)(true, {}, userConfig.theme.DEFAULTS);\n [themeConfig, userConfig].forEach(function (config) {\n config.modules = config.modules || {};\n Object.keys(config.modules).forEach(function (module) {\n if (config.modules[module] === true) {\n config.modules[module] = {};\n }\n });\n });\n var moduleNames = Object.keys(themeConfig.modules).concat(Object.keys(userConfig.modules));\n var moduleConfig = moduleNames.reduce(function (config, name) {\n var moduleClass = Quill.import('modules/' + name);\n if (moduleClass == null) {\n debug.error('Cannot load ' + name + ' module. Are you sure you registered it?');\n } else {\n config[name] = moduleClass.DEFAULTS || {};\n }\n return config;\n }, {});\n // Special case toolbar shorthand\n if (userConfig.modules != null && userConfig.modules.toolbar && userConfig.modules.toolbar.constructor !== Object) {\n userConfig.modules.toolbar = {\n container: userConfig.modules.toolbar\n };\n }\n userConfig = (0, _extend2.default)(true, {}, Quill.DEFAULTS, { modules: moduleConfig }, themeConfig, userConfig);\n ['bounds', 'container', 'scrollingContainer'].forEach(function (key) {\n if (typeof userConfig[key] === 'string') {\n userConfig[key] = document.querySelector(userConfig[key]);\n }\n });\n userConfig.modules = Object.keys(userConfig.modules).reduce(function (config, name) {\n if (userConfig.modules[name]) {\n config[name] = userConfig.modules[name];\n }\n return config;\n }, {});\n return userConfig;\n}\n\n// Handle selection preservation and TEXT_CHANGE emission\n// common to modification APIs\nfunction modify(modifier, source, index, shift) {\n if (this.options.strict && !this.isEnabled() && source === _emitter4.default.sources.USER) {\n return new _quillDelta2.default();\n }\n var range = index == null ? null : this.getSelection();\n var oldDelta = this.editor.delta;\n var change = modifier();\n if (range != null) {\n if (index === true) index = range.index;\n if (shift == null) {\n range = shiftRange(range, change, source);\n } else if (shift !== 0) {\n range = shiftRange(range, index, shift, source);\n }\n this.setSelection(range, _emitter4.default.sources.SILENT);\n }\n if (change.length() > 0) {\n var _emitter;\n\n var args = [_emitter4.default.events.TEXT_CHANGE, change, oldDelta, source];\n (_emitter = this.emitter).emit.apply(_emitter, [_emitter4.default.events.EDITOR_CHANGE].concat(args));\n if (source !== _emitter4.default.sources.SILENT) {\n var _emitter2;\n\n (_emitter2 = this.emitter).emit.apply(_emitter2, args);\n }\n }\n return change;\n}\n\nfunction overload(index, length, name, value, source) {\n var formats = {};\n if (typeof index.index === 'number' && typeof index.length === 'number') {\n // Allow for throwaway end (used by insertText/insertEmbed)\n if (typeof length !== 'number') {\n source = value, value = name, name = length, length = index.length, index = index.index;\n } else {\n length = index.length, index = index.index;\n }\n } else if (typeof length !== 'number') {\n source = value, value = name, name = length, length = 0;\n }\n // Handle format being object, two format name/value strings or excluded\n if ((typeof name === 'undefined' ? 'undefined' : _typeof(name)) === 'object') {\n formats = name;\n source = value;\n } else if (typeof name === 'string') {\n if (value != null) {\n formats[name] = value;\n } else {\n source = name;\n }\n }\n // Handle optional source\n source = source || _emitter4.default.sources.API;\n return [index, length, formats, source];\n}\n\nfunction shiftRange(range, index, length, source) {\n if (range == null) return null;\n var start = void 0,\n end = void 0;\n if (index instanceof _quillDelta2.default) {\n var _map = [range.index, range.index + range.length].map(function (pos) {\n return index.transformPosition(pos, source !== _emitter4.default.sources.USER);\n });\n\n var _map2 = _slicedToArray(_map, 2);\n\n start = _map2[0];\n end = _map2[1];\n } else {\n var _map3 = [range.index, range.index + range.length].map(function (pos) {\n if (pos < index || pos === index && source === _emitter4.default.sources.USER) return pos;\n if (length >= 0) {\n return pos + length;\n } else {\n return Math.max(index, pos + length);\n }\n });\n\n var _map4 = _slicedToArray(_map3, 2);\n\n start = _map4[0];\n end = _map4[1];\n }\n return new _selection.Range(start, end - start);\n}\n\nexports.expandConfig = expandConfig;\nexports.overload = overload;\nexports.default = Quill;\n\n/***/ }),\n/* 7 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar Module = function Module(quill) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n _classCallCheck(this, Module);\n\n this.quill = quill;\n this.options = options;\n};\n\nModule.DEFAULTS = {};\n\nexports.default = Module;\n\n/***/ }),\n/* 8 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar TextBlot = function (_Parchment$Text) {\n _inherits(TextBlot, _Parchment$Text);\n\n function TextBlot() {\n _classCallCheck(this, TextBlot);\n\n return _possibleConstructorReturn(this, (TextBlot.__proto__ || Object.getPrototypeOf(TextBlot)).apply(this, arguments));\n }\n\n return TextBlot;\n}(_parchment2.default.Text);\n\nexports.default = TextBlot;\n\n/***/ }),\n/* 9 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _eventemitter = __webpack_require__(58);\n\nvar _eventemitter2 = _interopRequireDefault(_eventemitter);\n\nvar _logger = __webpack_require__(10);\n\nvar _logger2 = _interopRequireDefault(_logger);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar debug = (0, _logger2.default)('quill:events');\n\nvar EVENTS = ['selectionchange', 'mousedown', 'mouseup', 'click'];\n\nEVENTS.forEach(function (eventName) {\n document.addEventListener(eventName, function () {\n for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n [].slice.call(document.querySelectorAll('.ql-container')).forEach(function (node) {\n // TODO use WeakMap\n if (node.__quill && node.__quill.emitter) {\n var _node$__quill$emitter;\n\n (_node$__quill$emitter = node.__quill.emitter).handleDOM.apply(_node$__quill$emitter, args);\n }\n });\n });\n});\n\nvar Emitter = function (_EventEmitter) {\n _inherits(Emitter, _EventEmitter);\n\n function Emitter() {\n _classCallCheck(this, Emitter);\n\n var _this = _possibleConstructorReturn(this, (Emitter.__proto__ || Object.getPrototypeOf(Emitter)).call(this));\n\n _this.listeners = {};\n _this.on('error', debug.error);\n return _this;\n }\n\n _createClass(Emitter, [{\n key: 'emit',\n value: function emit() {\n debug.log.apply(debug, arguments);\n _get(Emitter.prototype.__proto__ || Object.getPrototypeOf(Emitter.prototype), 'emit', this).apply(this, arguments);\n }\n }, {\n key: 'handleDOM',\n value: function handleDOM(event) {\n for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {\n args[_key2 - 1] = arguments[_key2];\n }\n\n (this.listeners[event.type] || []).forEach(function (_ref) {\n var node = _ref.node,\n handler = _ref.handler;\n\n if (event.target === node || node.contains(event.target)) {\n handler.apply(undefined, [event].concat(args));\n }\n });\n }\n }, {\n key: 'listenDOM',\n value: function listenDOM(eventName, node, handler) {\n if (!this.listeners[eventName]) {\n this.listeners[eventName] = [];\n }\n this.listeners[eventName].push({ node: node, handler: handler });\n }\n }]);\n\n return Emitter;\n}(_eventemitter2.default);\n\nEmitter.events = {\n EDITOR_CHANGE: 'editor-change',\n SCROLL_BEFORE_UPDATE: 'scroll-before-update',\n SCROLL_OPTIMIZE: 'scroll-optimize',\n SCROLL_UPDATE: 'scroll-update',\n SELECTION_CHANGE: 'selection-change',\n TEXT_CHANGE: 'text-change'\n};\nEmitter.sources = {\n API: 'api',\n SILENT: 'silent',\n USER: 'user'\n};\n\nexports.default = Emitter;\n\n/***/ }),\n/* 10 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nvar levels = ['error', 'warn', 'log', 'info'];\nvar level = 'warn';\n\nfunction debug(method) {\n if (levels.indexOf(method) <= levels.indexOf(level)) {\n var _console;\n\n for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n args[_key - 1] = arguments[_key];\n }\n\n (_console = console)[method].apply(_console, args); // eslint-disable-line no-console\n }\n}\n\nfunction namespace(ns) {\n return levels.reduce(function (logger, method) {\n logger[method] = debug.bind(console, method, ns);\n return logger;\n }, {});\n}\n\ndebug.level = namespace.level = function (newLevel) {\n level = newLevel;\n};\n\nexports.default = namespace;\n\n/***/ }),\n/* 11 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar Registry = __webpack_require__(1);\nvar Attributor = /** @class */ (function () {\n function Attributor(attrName, keyName, options) {\n if (options === void 0) { options = {}; }\n this.attrName = attrName;\n this.keyName = keyName;\n var attributeBit = Registry.Scope.TYPE & Registry.Scope.ATTRIBUTE;\n if (options.scope != null) {\n // Ignore type bits, force attribute bit\n this.scope = (options.scope & Registry.Scope.LEVEL) | attributeBit;\n }\n else {\n this.scope = Registry.Scope.ATTRIBUTE;\n }\n if (options.whitelist != null)\n this.whitelist = options.whitelist;\n }\n Attributor.keys = function (node) {\n return [].map.call(node.attributes, function (item) {\n return item.name;\n });\n };\n Attributor.prototype.add = function (node, value) {\n if (!this.canAdd(node, value))\n return false;\n node.setAttribute(this.keyName, value);\n return true;\n };\n Attributor.prototype.canAdd = function (node, value) {\n var match = Registry.query(node, Registry.Scope.BLOT & (this.scope | Registry.Scope.TYPE));\n if (match == null)\n return false;\n if (this.whitelist == null)\n return true;\n if (typeof value === 'string') {\n return this.whitelist.indexOf(value.replace(/[\"']/g, '')) > -1;\n }\n else {\n return this.whitelist.indexOf(value) > -1;\n }\n };\n Attributor.prototype.remove = function (node) {\n node.removeAttribute(this.keyName);\n };\n Attributor.prototype.value = function (node) {\n var value = node.getAttribute(this.keyName);\n if (this.canAdd(node, value) && value) {\n return value;\n }\n return '';\n };\n return Attributor;\n}());\nexports.default = Attributor;\n\n\n/***/ }),\n/* 12 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar pSlice = Array.prototype.slice;\nvar objectKeys = __webpack_require__(55);\nvar isArguments = __webpack_require__(56);\n\nvar deepEqual = module.exports = function (actual, expected, opts) {\n if (!opts) opts = {};\n // 7.1. All identical values are equivalent, as determined by ===.\n if (actual === expected) {\n return true;\n\n } else if (actual instanceof Date && expected instanceof Date) {\n return actual.getTime() === expected.getTime();\n\n // 7.3. Other pairs that do not both pass typeof value == 'object',\n // equivalence is determined by ==.\n } else if (!actual || !expected || typeof actual != 'object' && typeof expected != 'object') {\n return opts.strict ? actual === expected : actual == expected;\n\n // 7.4. For all other Object pairs, including Array objects, equivalence is\n // determined by having the same number of owned properties (as verified\n // with Object.prototype.hasOwnProperty.call), the same set of keys\n // (although not necessarily the same order), equivalent values for every\n // corresponding key, and an identical 'prototype' property. Note: this\n // accounts for both named and indexed properties on Arrays.\n } else {\n return objEquiv(actual, expected, opts);\n }\n}\n\nfunction isUndefinedOrNull(value) {\n return value === null || value === undefined;\n}\n\nfunction isBuffer (x) {\n if (!x || typeof x !== 'object' || typeof x.length !== 'number') return false;\n if (typeof x.copy !== 'function' || typeof x.slice !== 'function') {\n return false;\n }\n if (x.length > 0 && typeof x[0] !== 'number') return false;\n return true;\n}\n\nfunction objEquiv(a, b, opts) {\n var i, key;\n if (isUndefinedOrNull(a) || isUndefinedOrNull(b))\n return false;\n // an identical 'prototype' property.\n if (a.prototype !== b.prototype) return false;\n //~~~I've managed to break Object.keys through screwy arguments passing.\n // Converting to array solves the problem.\n if (isArguments(a)) {\n if (!isArguments(b)) {\n return false;\n }\n a = pSlice.call(a);\n b = pSlice.call(b);\n return deepEqual(a, b, opts);\n }\n if (isBuffer(a)) {\n if (!isBuffer(b)) {\n return false;\n }\n if (a.length !== b.length) return false;\n for (i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n }\n try {\n var ka = objectKeys(a),\n kb = objectKeys(b);\n } catch (e) {//happens when one is a string literal and the other isn't\n return false;\n }\n // having the same number of owned properties (keys incorporates\n // hasOwnProperty)\n if (ka.length != kb.length)\n return false;\n //the same set of keys (although not necessarily the same order),\n ka.sort();\n kb.sort();\n //~~~cheap key test\n for (i = ka.length - 1; i >= 0; i--) {\n if (ka[i] != kb[i])\n return false;\n }\n //equivalent values for every corresponding key, and\n //~~~possibly expensive deep test\n for (i = ka.length - 1; i >= 0; i--) {\n key = ka[i];\n if (!deepEqual(a[key], b[key], opts)) return false;\n }\n return typeof a === typeof b;\n}\n\n\n/***/ }),\n/* 13 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = exports.Code = undefined;\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _quillDelta = __webpack_require__(4);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _block = __webpack_require__(3);\n\nvar _block2 = _interopRequireDefault(_block);\n\nvar _inline = __webpack_require__(5);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nvar _text = __webpack_require__(8);\n\nvar _text2 = _interopRequireDefault(_text);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Code = function (_Inline) {\n _inherits(Code, _Inline);\n\n function Code() {\n _classCallCheck(this, Code);\n\n return _possibleConstructorReturn(this, (Code.__proto__ || Object.getPrototypeOf(Code)).apply(this, arguments));\n }\n\n return Code;\n}(_inline2.default);\n\nCode.blotName = 'code';\nCode.tagName = 'CODE';\n\nvar CodeBlock = function (_Block) {\n _inherits(CodeBlock, _Block);\n\n function CodeBlock() {\n _classCallCheck(this, CodeBlock);\n\n return _possibleConstructorReturn(this, (CodeBlock.__proto__ || Object.getPrototypeOf(CodeBlock)).apply(this, arguments));\n }\n\n _createClass(CodeBlock, [{\n key: 'delta',\n value: function delta() {\n var _this3 = this;\n\n var text = this.domNode.textContent;\n if (text.endsWith('\\n')) {\n // Should always be true\n text = text.slice(0, -1);\n }\n return text.split('\\n').reduce(function (delta, frag) {\n return delta.insert(frag).insert('\\n', _this3.formats());\n }, new _quillDelta2.default());\n }\n }, {\n key: 'format',\n value: function format(name, value) {\n if (name === this.statics.blotName && value) return;\n\n var _descendant = this.descendant(_text2.default, this.length() - 1),\n _descendant2 = _slicedToArray(_descendant, 1),\n text = _descendant2[0];\n\n if (text != null) {\n text.deleteAt(text.length() - 1, 1);\n }\n _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'format', this).call(this, name, value);\n }\n }, {\n key: 'formatAt',\n value: function formatAt(index, length, name, value) {\n if (length === 0) return;\n if (_parchment2.default.query(name, _parchment2.default.Scope.BLOCK) == null || name === this.statics.blotName && value === this.statics.formats(this.domNode)) {\n return;\n }\n var nextNewline = this.newlineIndex(index);\n if (nextNewline < 0 || nextNewline >= index + length) return;\n var prevNewline = this.newlineIndex(index, true) + 1;\n var isolateLength = nextNewline - prevNewline + 1;\n var blot = this.isolate(prevNewline, isolateLength);\n var next = blot.next;\n blot.format(name, value);\n if (next instanceof CodeBlock) {\n next.formatAt(0, index - prevNewline + length - isolateLength, name, value);\n }\n }\n }, {\n key: 'insertAt',\n value: function insertAt(index, value, def) {\n if (def != null) return;\n\n var _descendant3 = this.descendant(_text2.default, index),\n _descendant4 = _slicedToArray(_descendant3, 2),\n text = _descendant4[0],\n offset = _descendant4[1];\n\n text.insertAt(offset, value);\n }\n }, {\n key: 'length',\n value: function length() {\n var length = this.domNode.textContent.length;\n if (!this.domNode.textContent.endsWith('\\n')) {\n return length + 1;\n }\n return length;\n }\n }, {\n key: 'newlineIndex',\n value: function newlineIndex(searchIndex) {\n var reverse = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n if (!reverse) {\n var offset = this.domNode.textContent.slice(searchIndex).indexOf('\\n');\n return offset > -1 ? searchIndex + offset : -1;\n } else {\n return this.domNode.textContent.slice(0, searchIndex).lastIndexOf('\\n');\n }\n }\n }, {\n key: 'optimize',\n value: function optimize(context) {\n if (!this.domNode.textContent.endsWith('\\n')) {\n this.appendChild(_parchment2.default.create('text', '\\n'));\n }\n _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'optimize', this).call(this, context);\n var next = this.next;\n if (next != null && next.prev === this && next.statics.blotName === this.statics.blotName && this.statics.formats(this.domNode) === next.statics.formats(next.domNode)) {\n next.optimize(context);\n next.moveChildren(this);\n next.remove();\n }\n }\n }, {\n key: 'replace',\n value: function replace(target) {\n _get(CodeBlock.prototype.__proto__ || Object.getPrototypeOf(CodeBlock.prototype), 'replace', this).call(this, target);\n [].slice.call(this.domNode.querySelectorAll('*')).forEach(function (node) {\n var blot = _parchment2.default.find(node);\n if (blot == null) {\n node.parentNode.removeChild(node);\n } else if (blot instanceof _parchment2.default.Embed) {\n blot.remove();\n } else {\n blot.unwrap();\n }\n });\n }\n }], [{\n key: 'create',\n value: function create(value) {\n var domNode = _get(CodeBlock.__proto__ || Object.getPrototypeOf(CodeBlock), 'create', this).call(this, value);\n domNode.setAttribute('spellcheck', false);\n return domNode;\n }\n }, {\n key: 'formats',\n value: function formats() {\n return true;\n }\n }]);\n\n return CodeBlock;\n}(_block2.default);\n\nCodeBlock.blotName = 'code-block';\nCodeBlock.tagName = 'PRE';\nCodeBlock.TAB = ' ';\n\nexports.Code = Code;\nexports.default = CodeBlock;\n\n/***/ }),\n/* 14 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Break = function (_Parchment$Embed) {\n _inherits(Break, _Parchment$Embed);\n\n function Break() {\n _classCallCheck(this, Break);\n\n return _possibleConstructorReturn(this, (Break.__proto__ || Object.getPrototypeOf(Break)).apply(this, arguments));\n }\n\n _createClass(Break, [{\n key: 'insertInto',\n value: function insertInto(parent, ref) {\n if (parent.children.length === 0) {\n _get(Break.prototype.__proto__ || Object.getPrototypeOf(Break.prototype), 'insertInto', this).call(this, parent, ref);\n } else {\n this.remove();\n }\n }\n }, {\n key: 'length',\n value: function length() {\n return 0;\n }\n }, {\n key: 'value',\n value: function value() {\n return '';\n }\n }], [{\n key: 'value',\n value: function value() {\n return undefined;\n }\n }]);\n\n return Break;\n}(_parchment2.default.Embed);\n\nBreak.blotName = 'break';\nBreak.tagName = 'BR';\n\nexports.default = Break;\n\n/***/ }),\n/* 15 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.sanitize = exports.default = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _inline = __webpack_require__(5);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Link = function (_Inline) {\n _inherits(Link, _Inline);\n\n function Link() {\n _classCallCheck(this, Link);\n\n return _possibleConstructorReturn(this, (Link.__proto__ || Object.getPrototypeOf(Link)).apply(this, arguments));\n }\n\n _createClass(Link, [{\n key: 'format',\n value: function format(name, value) {\n if (name !== this.statics.blotName || !value) return _get(Link.prototype.__proto__ || Object.getPrototypeOf(Link.prototype), 'format', this).call(this, name, value);\n value = this.constructor.sanitize(value);\n this.domNode.setAttribute('href', value);\n }\n }], [{\n key: 'create',\n value: function create(value) {\n var node = _get(Link.__proto__ || Object.getPrototypeOf(Link), 'create', this).call(this, value);\n value = this.sanitize(value);\n node.setAttribute('href', value);\n node.setAttribute('target', '_blank');\n return node;\n }\n }, {\n key: 'formats',\n value: function formats(domNode) {\n return domNode.getAttribute('href');\n }\n }, {\n key: 'sanitize',\n value: function sanitize(url) {\n return _sanitize(url, this.PROTOCOL_WHITELIST) ? url : this.SANITIZED_URL;\n }\n }]);\n\n return Link;\n}(_inline2.default);\n\nLink.blotName = 'link';\nLink.tagName = 'A';\nLink.SANITIZED_URL = 'about:blank';\nLink.PROTOCOL_WHITELIST = ['http', 'https', 'mailto', 'tel'];\n\nfunction _sanitize(url, protocols) {\n var anchor = document.createElement('a');\n anchor.href = url;\n var protocol = anchor.href.slice(0, anchor.href.indexOf(':'));\n return protocols.indexOf(protocol) > -1;\n}\n\nexports.default = Link;\nexports.sanitize = _sanitize;\n\n/***/ }),\n/* 16 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _keyboard = __webpack_require__(25);\n\nvar _keyboard2 = _interopRequireDefault(_keyboard);\n\nvar _dropdown = __webpack_require__(106);\n\nvar _dropdown2 = _interopRequireDefault(_dropdown);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar optionsCounter = 0;\n\nfunction toggleAriaAttribute(element, attribute) {\n element.setAttribute(attribute, !(element.getAttribute(attribute) === 'true'));\n}\n\nvar Picker = function () {\n function Picker(select) {\n var _this = this;\n\n _classCallCheck(this, Picker);\n\n this.select = select;\n this.container = document.createElement('span');\n this.buildPicker();\n this.select.style.display = 'none';\n this.select.parentNode.insertBefore(this.container, this.select);\n\n this.label.addEventListener('mousedown', function () {\n _this.togglePicker();\n });\n this.label.addEventListener('keydown', function (event) {\n switch (event.keyCode) {\n // Allows the \"Enter\" key to open the picker\n case _keyboard2.default.keys.ENTER:\n _this.togglePicker();\n break;\n\n // Allows the \"Escape\" key to close the picker\n case _keyboard2.default.keys.ESCAPE:\n _this.escape();\n event.preventDefault();\n break;\n default:\n }\n });\n this.select.addEventListener('change', this.update.bind(this));\n }\n\n _createClass(Picker, [{\n key: 'togglePicker',\n value: function togglePicker() {\n this.container.classList.toggle('ql-expanded');\n // Toggle aria-expanded and aria-hidden to make the picker accessible\n toggleAriaAttribute(this.label, 'aria-expanded');\n toggleAriaAttribute(this.options, 'aria-hidden');\n }\n }, {\n key: 'buildItem',\n value: function buildItem(option) {\n var _this2 = this;\n\n var item = document.createElement('span');\n item.tabIndex = '0';\n item.setAttribute('role', 'button');\n\n item.classList.add('ql-picker-item');\n if (option.hasAttribute('value')) {\n item.setAttribute('data-value', option.getAttribute('value'));\n }\n if (option.textContent) {\n item.setAttribute('data-label', option.textContent);\n }\n item.addEventListener('click', function () {\n _this2.selectItem(item, true);\n });\n item.addEventListener('keydown', function (event) {\n switch (event.keyCode) {\n // Allows the \"Enter\" key to select an item\n case _keyboard2.default.keys.ENTER:\n _this2.selectItem(item, true);\n event.preventDefault();\n break;\n\n // Allows the \"Escape\" key to close the picker\n case _keyboard2.default.keys.ESCAPE:\n _this2.escape();\n event.preventDefault();\n break;\n default:\n }\n });\n\n return item;\n }\n }, {\n key: 'buildLabel',\n value: function buildLabel() {\n var label = document.createElement('span');\n label.classList.add('ql-picker-label');\n label.innerHTML = _dropdown2.default;\n label.tabIndex = '0';\n label.setAttribute('role', 'button');\n label.setAttribute('aria-expanded', 'false');\n this.container.appendChild(label);\n return label;\n }\n }, {\n key: 'buildOptions',\n value: function buildOptions() {\n var _this3 = this;\n\n var options = document.createElement('span');\n options.classList.add('ql-picker-options');\n\n // Don't want screen readers to read this until options are visible\n options.setAttribute('aria-hidden', 'true');\n options.tabIndex = '-1';\n\n // Need a unique id for aria-controls\n options.id = 'ql-picker-options-' + optionsCounter;\n optionsCounter += 1;\n this.label.setAttribute('aria-controls', options.id);\n\n this.options = options;\n\n [].slice.call(this.select.options).forEach(function (option) {\n var item = _this3.buildItem(option);\n options.appendChild(item);\n if (option.selected === true) {\n _this3.selectItem(item);\n }\n });\n this.container.appendChild(options);\n }\n }, {\n key: 'buildPicker',\n value: function buildPicker() {\n var _this4 = this;\n\n [].slice.call(this.select.attributes).forEach(function (item) {\n _this4.container.setAttribute(item.name, item.value);\n });\n this.container.classList.add('ql-picker');\n this.label = this.buildLabel();\n this.buildOptions();\n }\n }, {\n key: 'escape',\n value: function escape() {\n var _this5 = this;\n\n // Close menu and return focus to trigger label\n this.close();\n // Need setTimeout for accessibility to ensure that the browser executes\n // focus on the next process thread and after any DOM content changes\n setTimeout(function () {\n return _this5.label.focus();\n }, 1);\n }\n }, {\n key: 'close',\n value: function close() {\n this.container.classList.remove('ql-expanded');\n this.label.setAttribute('aria-expanded', 'false');\n this.options.setAttribute('aria-hidden', 'true');\n }\n }, {\n key: 'selectItem',\n value: function selectItem(item) {\n var trigger = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n var selected = this.container.querySelector('.ql-selected');\n if (item === selected) return;\n if (selected != null) {\n selected.classList.remove('ql-selected');\n }\n if (item == null) return;\n item.classList.add('ql-selected');\n this.select.selectedIndex = [].indexOf.call(item.parentNode.children, item);\n if (item.hasAttribute('data-value')) {\n this.label.setAttribute('data-value', item.getAttribute('data-value'));\n } else {\n this.label.removeAttribute('data-value');\n }\n if (item.hasAttribute('data-label')) {\n this.label.setAttribute('data-label', item.getAttribute('data-label'));\n } else {\n this.label.removeAttribute('data-label');\n }\n if (trigger) {\n if (typeof Event === 'function') {\n this.select.dispatchEvent(new Event('change'));\n } else if ((typeof Event === 'undefined' ? 'undefined' : _typeof(Event)) === 'object') {\n // IE11\n var event = document.createEvent('Event');\n event.initEvent('change', true, true);\n this.select.dispatchEvent(event);\n }\n this.close();\n }\n }\n }, {\n key: 'update',\n value: function update() {\n var option = void 0;\n if (this.select.selectedIndex > -1) {\n var item = this.container.querySelector('.ql-picker-options').children[this.select.selectedIndex];\n option = this.select.options[this.select.selectedIndex];\n this.selectItem(item);\n } else {\n this.selectItem(null);\n }\n var isActive = option != null && option !== this.select.querySelector('option[selected]');\n this.label.classList.toggle('ql-active', isActive);\n }\n }]);\n\n return Picker;\n}();\n\nexports.default = Picker;\n\n/***/ }),\n/* 17 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar linked_list_1 = __webpack_require__(47);\nvar shadow_1 = __webpack_require__(27);\nvar Registry = __webpack_require__(1);\nvar ContainerBlot = /** @class */ (function (_super) {\n __extends(ContainerBlot, _super);\n function ContainerBlot(domNode) {\n var _this = _super.call(this, domNode) || this;\n _this.build();\n return _this;\n }\n ContainerBlot.prototype.appendChild = function (other) {\n this.insertBefore(other);\n };\n ContainerBlot.prototype.attach = function () {\n _super.prototype.attach.call(this);\n this.children.forEach(function (child) {\n child.attach();\n });\n };\n ContainerBlot.prototype.build = function () {\n var _this = this;\n this.children = new linked_list_1.default();\n // Need to be reversed for if DOM nodes already in order\n [].slice\n .call(this.domNode.childNodes)\n .reverse()\n .forEach(function (node) {\n try {\n var child = makeBlot(node);\n _this.insertBefore(child, _this.children.head || undefined);\n }\n catch (err) {\n if (err instanceof Registry.ParchmentError)\n return;\n else\n throw err;\n }\n });\n };\n ContainerBlot.prototype.deleteAt = function (index, length) {\n if (index === 0 && length === this.length()) {\n return this.remove();\n }\n this.children.forEachAt(index, length, function (child, offset, length) {\n child.deleteAt(offset, length);\n });\n };\n ContainerBlot.prototype.descendant = function (criteria, index) {\n var _a = this.children.find(index), child = _a[0], offset = _a[1];\n if ((criteria.blotName == null && criteria(child)) ||\n (criteria.blotName != null && child instanceof criteria)) {\n return [child, offset];\n }\n else if (child instanceof ContainerBlot) {\n return child.descendant(criteria, offset);\n }\n else {\n return [null, -1];\n }\n };\n ContainerBlot.prototype.descendants = function (criteria, index, length) {\n if (index === void 0) { index = 0; }\n if (length === void 0) { length = Number.MAX_VALUE; }\n var descendants = [];\n var lengthLeft = length;\n this.children.forEachAt(index, length, function (child, index, length) {\n if ((criteria.blotName == null && criteria(child)) ||\n (criteria.blotName != null && child instanceof criteria)) {\n descendants.push(child);\n }\n if (child instanceof ContainerBlot) {\n descendants = descendants.concat(child.descendants(criteria, index, lengthLeft));\n }\n lengthLeft -= length;\n });\n return descendants;\n };\n ContainerBlot.prototype.detach = function () {\n this.children.forEach(function (child) {\n child.detach();\n });\n _super.prototype.detach.call(this);\n };\n ContainerBlot.prototype.formatAt = function (index, length, name, value) {\n this.children.forEachAt(index, length, function (child, offset, length) {\n child.formatAt(offset, length, name, value);\n });\n };\n ContainerBlot.prototype.insertAt = function (index, value, def) {\n var _a = this.children.find(index), child = _a[0], offset = _a[1];\n if (child) {\n child.insertAt(offset, value, def);\n }\n else {\n var blot = def == null ? Registry.create('text', value) : Registry.create(value, def);\n this.appendChild(blot);\n }\n };\n ContainerBlot.prototype.insertBefore = function (childBlot, refBlot) {\n if (this.statics.allowedChildren != null &&\n !this.statics.allowedChildren.some(function (child) {\n return childBlot instanceof child;\n })) {\n throw new Registry.ParchmentError(\"Cannot insert \" + childBlot.statics.blotName + \" into \" + this.statics.blotName);\n }\n childBlot.insertInto(this, refBlot);\n };\n ContainerBlot.prototype.length = function () {\n return this.children.reduce(function (memo, child) {\n return memo + child.length();\n }, 0);\n };\n ContainerBlot.prototype.moveChildren = function (targetParent, refNode) {\n this.children.forEach(function (child) {\n targetParent.insertBefore(child, refNode);\n });\n };\n ContainerBlot.prototype.optimize = function (context) {\n _super.prototype.optimize.call(this, context);\n if (this.children.length === 0) {\n if (this.statics.defaultChild != null) {\n var child = Registry.create(this.statics.defaultChild);\n this.appendChild(child);\n child.optimize(context);\n }\n else {\n this.remove();\n }\n }\n };\n ContainerBlot.prototype.path = function (index, inclusive) {\n if (inclusive === void 0) { inclusive = false; }\n var _a = this.children.find(index, inclusive), child = _a[0], offset = _a[1];\n var position = [[this, index]];\n if (child instanceof ContainerBlot) {\n return position.concat(child.path(offset, inclusive));\n }\n else if (child != null) {\n position.push([child, offset]);\n }\n return position;\n };\n ContainerBlot.prototype.removeChild = function (child) {\n this.children.remove(child);\n };\n ContainerBlot.prototype.replace = function (target) {\n if (target instanceof ContainerBlot) {\n target.moveChildren(this);\n }\n _super.prototype.replace.call(this, target);\n };\n ContainerBlot.prototype.split = function (index, force) {\n if (force === void 0) { force = false; }\n if (!force) {\n if (index === 0)\n return this;\n if (index === this.length())\n return this.next;\n }\n var after = this.clone();\n this.parent.insertBefore(after, this.next);\n this.children.forEachAt(index, this.length(), function (child, offset, length) {\n child = child.split(offset, force);\n after.appendChild(child);\n });\n return after;\n };\n ContainerBlot.prototype.unwrap = function () {\n this.moveChildren(this.parent, this.next);\n this.remove();\n };\n ContainerBlot.prototype.update = function (mutations, context) {\n var _this = this;\n var addedNodes = [];\n var removedNodes = [];\n mutations.forEach(function (mutation) {\n if (mutation.target === _this.domNode && mutation.type === 'childList') {\n addedNodes.push.apply(addedNodes, mutation.addedNodes);\n removedNodes.push.apply(removedNodes, mutation.removedNodes);\n }\n });\n removedNodes.forEach(function (node) {\n // Check node has actually been removed\n // One exception is Chrome does not immediately remove IFRAMEs\n // from DOM but MutationRecord is correct in its reported removal\n if (node.parentNode != null &&\n // @ts-ignore\n node.tagName !== 'IFRAME' &&\n document.body.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINED_BY) {\n return;\n }\n var blot = Registry.find(node);\n if (blot == null)\n return;\n if (blot.domNode.parentNode == null || blot.domNode.parentNode === _this.domNode) {\n blot.detach();\n }\n });\n addedNodes\n .filter(function (node) {\n return node.parentNode == _this.domNode;\n })\n .sort(function (a, b) {\n if (a === b)\n return 0;\n if (a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING) {\n return 1;\n }\n return -1;\n })\n .forEach(function (node) {\n var refBlot = null;\n if (node.nextSibling != null) {\n refBlot = Registry.find(node.nextSibling);\n }\n var blot = makeBlot(node);\n if (blot.next != refBlot || blot.next == null) {\n if (blot.parent != null) {\n blot.parent.removeChild(_this);\n }\n _this.insertBefore(blot, refBlot || undefined);\n }\n });\n };\n return ContainerBlot;\n}(shadow_1.default));\nfunction makeBlot(node) {\n var blot = Registry.find(node);\n if (blot == null) {\n try {\n blot = Registry.create(node);\n }\n catch (e) {\n blot = Registry.create(Registry.Scope.INLINE);\n [].slice.call(node.childNodes).forEach(function (child) {\n // @ts-ignore\n blot.domNode.appendChild(child);\n });\n if (node.parentNode) {\n node.parentNode.replaceChild(blot.domNode, node);\n }\n blot.attach();\n }\n }\n return blot;\n}\nexports.default = ContainerBlot;\n\n\n/***/ }),\n/* 18 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar attributor_1 = __webpack_require__(11);\nvar store_1 = __webpack_require__(28);\nvar container_1 = __webpack_require__(17);\nvar Registry = __webpack_require__(1);\nvar FormatBlot = /** @class */ (function (_super) {\n __extends(FormatBlot, _super);\n function FormatBlot(domNode) {\n var _this = _super.call(this, domNode) || this;\n _this.attributes = new store_1.default(_this.domNode);\n return _this;\n }\n FormatBlot.formats = function (domNode) {\n if (typeof this.tagName === 'string') {\n return true;\n }\n else if (Array.isArray(this.tagName)) {\n return domNode.tagName.toLowerCase();\n }\n return undefined;\n };\n FormatBlot.prototype.format = function (name, value) {\n var format = Registry.query(name);\n if (format instanceof attributor_1.default) {\n this.attributes.attribute(format, value);\n }\n else if (value) {\n if (format != null && (name !== this.statics.blotName || this.formats()[name] !== value)) {\n this.replaceWith(name, value);\n }\n }\n };\n FormatBlot.prototype.formats = function () {\n var formats = this.attributes.values();\n var format = this.statics.formats(this.domNode);\n if (format != null) {\n formats[this.statics.blotName] = format;\n }\n return formats;\n };\n FormatBlot.prototype.replaceWith = function (name, value) {\n var replacement = _super.prototype.replaceWith.call(this, name, value);\n this.attributes.copy(replacement);\n return replacement;\n };\n FormatBlot.prototype.update = function (mutations, context) {\n var _this = this;\n _super.prototype.update.call(this, mutations, context);\n if (mutations.some(function (mutation) {\n return mutation.target === _this.domNode && mutation.type === 'attributes';\n })) {\n this.attributes.build();\n }\n };\n FormatBlot.prototype.wrap = function (name, value) {\n var wrapper = _super.prototype.wrap.call(this, name, value);\n if (wrapper instanceof FormatBlot && wrapper.statics.scope === this.statics.scope) {\n this.attributes.move(wrapper);\n }\n return wrapper;\n };\n return FormatBlot;\n}(container_1.default));\nexports.default = FormatBlot;\n\n\n/***/ }),\n/* 19 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar shadow_1 = __webpack_require__(27);\nvar Registry = __webpack_require__(1);\nvar LeafBlot = /** @class */ (function (_super) {\n __extends(LeafBlot, _super);\n function LeafBlot() {\n return _super !== null && _super.apply(this, arguments) || this;\n }\n LeafBlot.value = function (domNode) {\n return true;\n };\n LeafBlot.prototype.index = function (node, offset) {\n if (this.domNode === node ||\n this.domNode.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINED_BY) {\n return Math.min(offset, 1);\n }\n return -1;\n };\n LeafBlot.prototype.position = function (index, inclusive) {\n var offset = [].indexOf.call(this.parent.domNode.childNodes, this.domNode);\n if (index > 0)\n offset += 1;\n return [this.parent.domNode, offset];\n };\n LeafBlot.prototype.value = function () {\n return _a = {}, _a[this.statics.blotName] = this.statics.value(this.domNode) || true, _a;\n var _a;\n };\n LeafBlot.scope = Registry.Scope.INLINE_BLOT;\n return LeafBlot;\n}(shadow_1.default));\nexports.default = LeafBlot;\n\n\n/***/ }),\n/* 20 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar equal = __webpack_require__(12);\nvar extend = __webpack_require__(2);\n\n\nvar lib = {\n attributes: {\n compose: function (a, b, keepNull) {\n if (typeof a !== 'object') a = {};\n if (typeof b !== 'object') b = {};\n var attributes = extend(true, {}, b);\n if (!keepNull) {\n attributes = Object.keys(attributes).reduce(function (copy, key) {\n if (attributes[key] != null) {\n copy[key] = attributes[key];\n }\n return copy;\n }, {});\n }\n for (var key in a) {\n if (a[key] !== undefined && b[key] === undefined) {\n attributes[key] = a[key];\n }\n }\n return Object.keys(attributes).length > 0 ? attributes : undefined;\n },\n\n diff: function(a, b) {\n if (typeof a !== 'object') a = {};\n if (typeof b !== 'object') b = {};\n var attributes = Object.keys(a).concat(Object.keys(b)).reduce(function (attributes, key) {\n if (!equal(a[key], b[key])) {\n attributes[key] = b[key] === undefined ? null : b[key];\n }\n return attributes;\n }, {});\n return Object.keys(attributes).length > 0 ? attributes : undefined;\n },\n\n transform: function (a, b, priority) {\n if (typeof a !== 'object') return b;\n if (typeof b !== 'object') return undefined;\n if (!priority) return b; // b simply overwrites us without priority\n var attributes = Object.keys(b).reduce(function (attributes, key) {\n if (a[key] === undefined) attributes[key] = b[key]; // null is a valid value\n return attributes;\n }, {});\n return Object.keys(attributes).length > 0 ? attributes : undefined;\n }\n },\n\n iterator: function (ops) {\n return new Iterator(ops);\n },\n\n length: function (op) {\n if (typeof op['delete'] === 'number') {\n return op['delete'];\n } else if (typeof op.retain === 'number') {\n return op.retain;\n } else {\n return typeof op.insert === 'string' ? op.insert.length : 1;\n }\n }\n};\n\n\nfunction Iterator(ops) {\n this.ops = ops;\n this.index = 0;\n this.offset = 0;\n};\n\nIterator.prototype.hasNext = function () {\n return this.peekLength() < Infinity;\n};\n\nIterator.prototype.next = function (length) {\n if (!length) length = Infinity;\n var nextOp = this.ops[this.index];\n if (nextOp) {\n var offset = this.offset;\n var opLength = lib.length(nextOp)\n if (length >= opLength - offset) {\n length = opLength - offset;\n this.index += 1;\n this.offset = 0;\n } else {\n this.offset += length;\n }\n if (typeof nextOp['delete'] === 'number') {\n return { 'delete': length };\n } else {\n var retOp = {};\n if (nextOp.attributes) {\n retOp.attributes = nextOp.attributes;\n }\n if (typeof nextOp.retain === 'number') {\n retOp.retain = length;\n } else if (typeof nextOp.insert === 'string') {\n retOp.insert = nextOp.insert.substr(offset, length);\n } else {\n // offset should === 0, length should === 1\n retOp.insert = nextOp.insert;\n }\n return retOp;\n }\n } else {\n return { retain: Infinity };\n }\n};\n\nIterator.prototype.peek = function () {\n return this.ops[this.index];\n};\n\nIterator.prototype.peekLength = function () {\n if (this.ops[this.index]) {\n // Should never return 0 if our index is being managed correctly\n return lib.length(this.ops[this.index]) - this.offset;\n } else {\n return Infinity;\n }\n};\n\nIterator.prototype.peekType = function () {\n if (this.ops[this.index]) {\n if (typeof this.ops[this.index]['delete'] === 'number') {\n return 'delete';\n } else if (typeof this.ops[this.index].retain === 'number') {\n return 'retain';\n } else {\n return 'insert';\n }\n }\n return 'retain';\n};\n\n\nmodule.exports = lib;\n\n\n/***/ }),\n/* 21 */\n/***/ (function(module, exports) {\n\nvar clone = (function() {\n'use strict';\n\nfunction _instanceof(obj, type) {\n return type != null && obj instanceof type;\n}\n\nvar nativeMap;\ntry {\n nativeMap = Map;\n} catch(_) {\n // maybe a reference error because no `Map`. Give it a dummy value that no\n // value will ever be an instanceof.\n nativeMap = function() {};\n}\n\nvar nativeSet;\ntry {\n nativeSet = Set;\n} catch(_) {\n nativeSet = function() {};\n}\n\nvar nativePromise;\ntry {\n nativePromise = Promise;\n} catch(_) {\n nativePromise = function() {};\n}\n\n/**\n * Clones (copies) an Object using deep copying.\n *\n * This function supports circular references by default, but if you are certain\n * there are no circular references in your object, you can save some CPU time\n * by calling clone(obj, false).\n *\n * Caution: if `circular` is false and `parent` contains circular references,\n * your program may enter an infinite loop and crash.\n *\n * @param `parent` - the object to be cloned\n * @param `circular` - set to true if the object to be cloned may contain\n * circular references. (optional - true by default)\n * @param `depth` - set to a number if the object is only to be cloned to\n * a particular depth. (optional - defaults to Infinity)\n * @param `prototype` - sets the prototype to be used when cloning an object.\n * (optional - defaults to parent prototype).\n * @param `includeNonEnumerable` - set to true if the non-enumerable properties\n * should be cloned as well. Non-enumerable properties on the prototype\n * chain will be ignored. (optional - false by default)\n*/\nfunction clone(parent, circular, depth, prototype, includeNonEnumerable) {\n if (typeof circular === 'object') {\n depth = circular.depth;\n prototype = circular.prototype;\n includeNonEnumerable = circular.includeNonEnumerable;\n circular = circular.circular;\n }\n // maintain two arrays for circular references, where corresponding parents\n // and children have the same index\n var allParents = [];\n var allChildren = [];\n\n var useBuffer = typeof Buffer != 'undefined';\n\n if (typeof circular == 'undefined')\n circular = true;\n\n if (typeof depth == 'undefined')\n depth = Infinity;\n\n // recurse this function so we don't reset allParents and allChildren\n function _clone(parent, depth) {\n // cloning null always returns null\n if (parent === null)\n return null;\n\n if (depth === 0)\n return parent;\n\n var child;\n var proto;\n if (typeof parent != 'object') {\n return parent;\n }\n\n if (_instanceof(parent, nativeMap)) {\n child = new nativeMap();\n } else if (_instanceof(parent, nativeSet)) {\n child = new nativeSet();\n } else if (_instanceof(parent, nativePromise)) {\n child = new nativePromise(function (resolve, reject) {\n parent.then(function(value) {\n resolve(_clone(value, depth - 1));\n }, function(err) {\n reject(_clone(err, depth - 1));\n });\n });\n } else if (clone.__isArray(parent)) {\n child = [];\n } else if (clone.__isRegExp(parent)) {\n child = new RegExp(parent.source, __getRegExpFlags(parent));\n if (parent.lastIndex) child.lastIndex = parent.lastIndex;\n } else if (clone.__isDate(parent)) {\n child = new Date(parent.getTime());\n } else if (useBuffer && Buffer.isBuffer(parent)) {\n child = new Buffer(parent.length);\n parent.copy(child);\n return child;\n } else if (_instanceof(parent, Error)) {\n child = Object.create(parent);\n } else {\n if (typeof prototype == 'undefined') {\n proto = Object.getPrototypeOf(parent);\n child = Object.create(proto);\n }\n else {\n child = Object.create(prototype);\n proto = prototype;\n }\n }\n\n if (circular) {\n var index = allParents.indexOf(parent);\n\n if (index != -1) {\n return allChildren[index];\n }\n allParents.push(parent);\n allChildren.push(child);\n }\n\n if (_instanceof(parent, nativeMap)) {\n parent.forEach(function(value, key) {\n var keyChild = _clone(key, depth - 1);\n var valueChild = _clone(value, depth - 1);\n child.set(keyChild, valueChild);\n });\n }\n if (_instanceof(parent, nativeSet)) {\n parent.forEach(function(value) {\n var entryChild = _clone(value, depth - 1);\n child.add(entryChild);\n });\n }\n\n for (var i in parent) {\n var attrs;\n if (proto) {\n attrs = Object.getOwnPropertyDescriptor(proto, i);\n }\n\n if (attrs && attrs.set == null) {\n continue;\n }\n child[i] = _clone(parent[i], depth - 1);\n }\n\n if (Object.getOwnPropertySymbols) {\n var symbols = Object.getOwnPropertySymbols(parent);\n for (var i = 0; i < symbols.length; i++) {\n // Don't need to worry about cloning a symbol because it is a primitive,\n // like a number or string.\n var symbol = symbols[i];\n var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);\n if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {\n continue;\n }\n child[symbol] = _clone(parent[symbol], depth - 1);\n if (!descriptor.enumerable) {\n Object.defineProperty(child, symbol, {\n enumerable: false\n });\n }\n }\n }\n\n if (includeNonEnumerable) {\n var allPropertyNames = Object.getOwnPropertyNames(parent);\n for (var i = 0; i < allPropertyNames.length; i++) {\n var propertyName = allPropertyNames[i];\n var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);\n if (descriptor && descriptor.enumerable) {\n continue;\n }\n child[propertyName] = _clone(parent[propertyName], depth - 1);\n Object.defineProperty(child, propertyName, {\n enumerable: false\n });\n }\n }\n\n return child;\n }\n\n return _clone(parent, depth);\n}\n\n/**\n * Simple flat clone using prototype, accepts only objects, usefull for property\n * override on FLAT configuration object (no nested props).\n *\n * USE WITH CAUTION! This may not behave as you wish if you do not know how this\n * works.\n */\nclone.clonePrototype = function clonePrototype(parent) {\n if (parent === null)\n return null;\n\n var c = function () {};\n c.prototype = parent;\n return new c();\n};\n\n// private utility functions\n\nfunction __objToStr(o) {\n return Object.prototype.toString.call(o);\n}\nclone.__objToStr = __objToStr;\n\nfunction __isDate(o) {\n return typeof o === 'object' && __objToStr(o) === '[object Date]';\n}\nclone.__isDate = __isDate;\n\nfunction __isArray(o) {\n return typeof o === 'object' && __objToStr(o) === '[object Array]';\n}\nclone.__isArray = __isArray;\n\nfunction __isRegExp(o) {\n return typeof o === 'object' && __objToStr(o) === '[object RegExp]';\n}\nclone.__isRegExp = __isRegExp;\n\nfunction __getRegExpFlags(re) {\n var flags = '';\n if (re.global) flags += 'g';\n if (re.ignoreCase) flags += 'i';\n if (re.multiline) flags += 'm';\n return flags;\n}\nclone.__getRegExpFlags = __getRegExpFlags;\n\nreturn clone;\n})();\n\nif (typeof module === 'object' && module.exports) {\n module.exports = clone;\n}\n\n\n/***/ }),\n/* 22 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = exports.Range = undefined;\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _clone = __webpack_require__(21);\n\nvar _clone2 = _interopRequireDefault(_clone);\n\nvar _deepEqual = __webpack_require__(12);\n\nvar _deepEqual2 = _interopRequireDefault(_deepEqual);\n\nvar _emitter3 = __webpack_require__(9);\n\nvar _emitter4 = _interopRequireDefault(_emitter3);\n\nvar _logger = __webpack_require__(10);\n\nvar _logger2 = _interopRequireDefault(_logger);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar debug = (0, _logger2.default)('quill:selection');\n\nvar Range = function Range(index) {\n var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n _classCallCheck(this, Range);\n\n this.index = index;\n this.length = length;\n};\n\nvar Selection = function () {\n function Selection(scroll, emitter) {\n var _this = this;\n\n _classCallCheck(this, Selection);\n\n this.emitter = emitter;\n this.scroll = scroll;\n this.composing = false;\n this.mouseDown = false;\n this.root = this.scroll.domNode;\n this.cursor = _parchment2.default.create('cursor', this);\n // savedRange is last non-null range\n this.lastRange = this.savedRange = new Range(0, 0);\n this.handleComposition();\n this.handleDragging();\n this.emitter.listenDOM('selectionchange', document, function () {\n if (!_this.mouseDown) {\n setTimeout(_this.update.bind(_this, _emitter4.default.sources.USER), 1);\n }\n });\n this.emitter.on(_emitter4.default.events.EDITOR_CHANGE, function (type, delta) {\n if (type === _emitter4.default.events.TEXT_CHANGE && delta.length() > 0) {\n _this.update(_emitter4.default.sources.SILENT);\n }\n });\n this.emitter.on(_emitter4.default.events.SCROLL_BEFORE_UPDATE, function () {\n if (!_this.hasFocus()) return;\n var native = _this.getNativeRange();\n if (native == null) return;\n if (native.start.node === _this.cursor.textNode) return; // cursor.restore() will handle\n // TODO unclear if this has negative side effects\n _this.emitter.once(_emitter4.default.events.SCROLL_UPDATE, function () {\n try {\n _this.setNativeRange(native.start.node, native.start.offset, native.end.node, native.end.offset);\n } catch (ignored) {}\n });\n });\n this.emitter.on(_emitter4.default.events.SCROLL_OPTIMIZE, function (mutations, context) {\n if (context.range) {\n var _context$range = context.range,\n startNode = _context$range.startNode,\n startOffset = _context$range.startOffset,\n endNode = _context$range.endNode,\n endOffset = _context$range.endOffset;\n\n _this.setNativeRange(startNode, startOffset, endNode, endOffset);\n }\n });\n this.update(_emitter4.default.sources.SILENT);\n }\n\n _createClass(Selection, [{\n key: 'handleComposition',\n value: function handleComposition() {\n var _this2 = this;\n\n this.root.addEventListener('compositionstart', function () {\n _this2.composing = true;\n });\n this.root.addEventListener('compositionend', function () {\n _this2.composing = false;\n if (_this2.cursor.parent) {\n var range = _this2.cursor.restore();\n if (!range) return;\n setTimeout(function () {\n _this2.setNativeRange(range.startNode, range.startOffset, range.endNode, range.endOffset);\n }, 1);\n }\n });\n }\n }, {\n key: 'handleDragging',\n value: function handleDragging() {\n var _this3 = this;\n\n this.emitter.listenDOM('mousedown', document.body, function () {\n _this3.mouseDown = true;\n });\n this.emitter.listenDOM('mouseup', document.body, function () {\n _this3.mouseDown = false;\n _this3.update(_emitter4.default.sources.USER);\n });\n }\n }, {\n key: 'focus',\n value: function focus() {\n if (this.hasFocus()) return;\n this.root.focus();\n this.setRange(this.savedRange);\n }\n }, {\n key: 'format',\n value: function format(_format, value) {\n if (this.scroll.whitelist != null && !this.scroll.whitelist[_format]) return;\n this.scroll.update();\n var nativeRange = this.getNativeRange();\n if (nativeRange == null || !nativeRange.native.collapsed || _parchment2.default.query(_format, _parchment2.default.Scope.BLOCK)) return;\n if (nativeRange.start.node !== this.cursor.textNode) {\n var blot = _parchment2.default.find(nativeRange.start.node, false);\n if (blot == null) return;\n // TODO Give blot ability to not split\n if (blot instanceof _parchment2.default.Leaf) {\n var after = blot.split(nativeRange.start.offset);\n blot.parent.insertBefore(this.cursor, after);\n } else {\n blot.insertBefore(this.cursor, nativeRange.start.node); // Should never happen\n }\n this.cursor.attach();\n }\n this.cursor.format(_format, value);\n this.scroll.optimize();\n this.setNativeRange(this.cursor.textNode, this.cursor.textNode.data.length);\n this.update();\n }\n }, {\n key: 'getBounds',\n value: function getBounds(index) {\n var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n var scrollLength = this.scroll.length();\n index = Math.min(index, scrollLength - 1);\n length = Math.min(index + length, scrollLength - 1) - index;\n var node = void 0,\n _scroll$leaf = this.scroll.leaf(index),\n _scroll$leaf2 = _slicedToArray(_scroll$leaf, 2),\n leaf = _scroll$leaf2[0],\n offset = _scroll$leaf2[1];\n if (leaf == null) return null;\n\n var _leaf$position = leaf.position(offset, true);\n\n var _leaf$position2 = _slicedToArray(_leaf$position, 2);\n\n node = _leaf$position2[0];\n offset = _leaf$position2[1];\n\n var range = document.createRange();\n if (length > 0) {\n range.setStart(node, offset);\n\n var _scroll$leaf3 = this.scroll.leaf(index + length);\n\n var _scroll$leaf4 = _slicedToArray(_scroll$leaf3, 2);\n\n leaf = _scroll$leaf4[0];\n offset = _scroll$leaf4[1];\n\n if (leaf == null) return null;\n\n var _leaf$position3 = leaf.position(offset, true);\n\n var _leaf$position4 = _slicedToArray(_leaf$position3, 2);\n\n node = _leaf$position4[0];\n offset = _leaf$position4[1];\n\n range.setEnd(node, offset);\n return range.getBoundingClientRect();\n } else {\n var side = 'left';\n var rect = void 0;\n if (node instanceof Text) {\n if (offset < node.data.length) {\n range.setStart(node, offset);\n range.setEnd(node, offset + 1);\n } else {\n range.setStart(node, offset - 1);\n range.setEnd(node, offset);\n side = 'right';\n }\n rect = range.getBoundingClientRect();\n } else {\n rect = leaf.domNode.getBoundingClientRect();\n if (offset > 0) side = 'right';\n }\n return {\n bottom: rect.top + rect.height,\n height: rect.height,\n left: rect[side],\n right: rect[side],\n top: rect.top,\n width: 0\n };\n }\n }\n }, {\n key: 'getNativeRange',\n value: function getNativeRange() {\n var selection = document.getSelection();\n if (selection == null || selection.rangeCount <= 0) return null;\n var nativeRange = selection.getRangeAt(0);\n if (nativeRange == null) return null;\n var range = this.normalizeNative(nativeRange);\n debug.info('getNativeRange', range);\n return range;\n }\n }, {\n key: 'getRange',\n value: function getRange() {\n var normalized = this.getNativeRange();\n if (normalized == null) return [null, null];\n var range = this.normalizedToRange(normalized);\n return [range, normalized];\n }\n }, {\n key: 'hasFocus',\n value: function hasFocus() {\n return document.activeElement === this.root;\n }\n }, {\n key: 'normalizedToRange',\n value: function normalizedToRange(range) {\n var _this4 = this;\n\n var positions = [[range.start.node, range.start.offset]];\n if (!range.native.collapsed) {\n positions.push([range.end.node, range.end.offset]);\n }\n var indexes = positions.map(function (position) {\n var _position = _slicedToArray(position, 2),\n node = _position[0],\n offset = _position[1];\n\n var blot = _parchment2.default.find(node, true);\n var index = blot.offset(_this4.scroll);\n if (offset === 0) {\n return index;\n } else if (blot instanceof _parchment2.default.Container) {\n return index + blot.length();\n } else {\n return index + blot.index(node, offset);\n }\n });\n var end = Math.min(Math.max.apply(Math, _toConsumableArray(indexes)), this.scroll.length() - 1);\n var start = Math.min.apply(Math, [end].concat(_toConsumableArray(indexes)));\n return new Range(start, end - start);\n }\n }, {\n key: 'normalizeNative',\n value: function normalizeNative(nativeRange) {\n if (!contains(this.root, nativeRange.startContainer) || !nativeRange.collapsed && !contains(this.root, nativeRange.endContainer)) {\n return null;\n }\n var range = {\n start: { node: nativeRange.startContainer, offset: nativeRange.startOffset },\n end: { node: nativeRange.endContainer, offset: nativeRange.endOffset },\n native: nativeRange\n };\n [range.start, range.end].forEach(function (position) {\n var node = position.node,\n offset = position.offset;\n while (!(node instanceof Text) && node.childNodes.length > 0) {\n if (node.childNodes.length > offset) {\n node = node.childNodes[offset];\n offset = 0;\n } else if (node.childNodes.length === offset) {\n node = node.lastChild;\n offset = node instanceof Text ? node.data.length : node.childNodes.length + 1;\n } else {\n break;\n }\n }\n position.node = node, position.offset = offset;\n });\n return range;\n }\n }, {\n key: 'rangeToNative',\n value: function rangeToNative(range) {\n var _this5 = this;\n\n var indexes = range.collapsed ? [range.index] : [range.index, range.index + range.length];\n var args = [];\n var scrollLength = this.scroll.length();\n indexes.forEach(function (index, i) {\n index = Math.min(scrollLength - 1, index);\n var node = void 0,\n _scroll$leaf5 = _this5.scroll.leaf(index),\n _scroll$leaf6 = _slicedToArray(_scroll$leaf5, 2),\n leaf = _scroll$leaf6[0],\n offset = _scroll$leaf6[1];\n var _leaf$position5 = leaf.position(offset, i !== 0);\n\n var _leaf$position6 = _slicedToArray(_leaf$position5, 2);\n\n node = _leaf$position6[0];\n offset = _leaf$position6[1];\n\n args.push(node, offset);\n });\n if (args.length < 2) {\n args = args.concat(args);\n }\n return args;\n }\n }, {\n key: 'scrollIntoView',\n value: function scrollIntoView(scrollingContainer) {\n var range = this.lastRange;\n if (range == null) return;\n var bounds = this.getBounds(range.index, range.length);\n if (bounds == null) return;\n var limit = this.scroll.length() - 1;\n\n var _scroll$line = this.scroll.line(Math.min(range.index, limit)),\n _scroll$line2 = _slicedToArray(_scroll$line, 1),\n first = _scroll$line2[0];\n\n var last = first;\n if (range.length > 0) {\n var _scroll$line3 = this.scroll.line(Math.min(range.index + range.length, limit));\n\n var _scroll$line4 = _slicedToArray(_scroll$line3, 1);\n\n last = _scroll$line4[0];\n }\n if (first == null || last == null) return;\n var scrollBounds = scrollingContainer.getBoundingClientRect();\n if (bounds.top < scrollBounds.top) {\n scrollingContainer.scrollTop -= scrollBounds.top - bounds.top;\n } else if (bounds.bottom > scrollBounds.bottom) {\n scrollingContainer.scrollTop += bounds.bottom - scrollBounds.bottom;\n }\n }\n }, {\n key: 'setNativeRange',\n value: function setNativeRange(startNode, startOffset) {\n var endNode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : startNode;\n var endOffset = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : startOffset;\n var force = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;\n\n debug.info('setNativeRange', startNode, startOffset, endNode, endOffset);\n if (startNode != null && (this.root.parentNode == null || startNode.parentNode == null || endNode.parentNode == null)) {\n return;\n }\n var selection = document.getSelection();\n if (selection == null) return;\n if (startNode != null) {\n if (!this.hasFocus()) this.root.focus();\n var native = (this.getNativeRange() || {}).native;\n if (native == null || force || startNode !== native.startContainer || startOffset !== native.startOffset || endNode !== native.endContainer || endOffset !== native.endOffset) {\n\n if (startNode.tagName == \"BR\") {\n startOffset = [].indexOf.call(startNode.parentNode.childNodes, startNode);\n startNode = startNode.parentNode;\n }\n if (endNode.tagName == \"BR\") {\n endOffset = [].indexOf.call(endNode.parentNode.childNodes, endNode);\n endNode = endNode.parentNode;\n }\n var range = document.createRange();\n range.setStart(startNode, startOffset);\n range.setEnd(endNode, endOffset);\n selection.removeAllRanges();\n selection.addRange(range);\n }\n } else {\n selection.removeAllRanges();\n this.root.blur();\n document.body.focus(); // root.blur() not enough on IE11+Travis+SauceLabs (but not local VMs)\n }\n }\n }, {\n key: 'setRange',\n value: function setRange(range) {\n var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _emitter4.default.sources.API;\n\n if (typeof force === 'string') {\n source = force;\n force = false;\n }\n debug.info('setRange', range);\n if (range != null) {\n var args = this.rangeToNative(range);\n this.setNativeRange.apply(this, _toConsumableArray(args).concat([force]));\n } else {\n this.setNativeRange(null);\n }\n this.update(source);\n }\n }, {\n key: 'update',\n value: function update() {\n var source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _emitter4.default.sources.USER;\n\n var oldRange = this.lastRange;\n\n var _getRange = this.getRange(),\n _getRange2 = _slicedToArray(_getRange, 2),\n lastRange = _getRange2[0],\n nativeRange = _getRange2[1];\n\n this.lastRange = lastRange;\n if (this.lastRange != null) {\n this.savedRange = this.lastRange;\n }\n if (!(0, _deepEqual2.default)(oldRange, this.lastRange)) {\n var _emitter;\n\n if (!this.composing && nativeRange != null && nativeRange.native.collapsed && nativeRange.start.node !== this.cursor.textNode) {\n this.cursor.restore();\n }\n var args = [_emitter4.default.events.SELECTION_CHANGE, (0, _clone2.default)(this.lastRange), (0, _clone2.default)(oldRange), source];\n (_emitter = this.emitter).emit.apply(_emitter, [_emitter4.default.events.EDITOR_CHANGE].concat(args));\n if (source !== _emitter4.default.sources.SILENT) {\n var _emitter2;\n\n (_emitter2 = this.emitter).emit.apply(_emitter2, args);\n }\n }\n }\n }]);\n\n return Selection;\n}();\n\nfunction contains(parent, descendant) {\n try {\n // Firefox inserts inaccessible nodes around video elements\n descendant.parentNode;\n } catch (e) {\n return false;\n }\n // IE11 has bug with Text nodes\n // https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect\n if (descendant instanceof Text) {\n descendant = descendant.parentNode;\n }\n return parent.contains(descendant);\n}\n\nexports.Range = Range;\nexports.default = Selection;\n\n/***/ }),\n/* 23 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _block = __webpack_require__(3);\n\nvar _block2 = _interopRequireDefault(_block);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Container = function (_Parchment$Container) {\n _inherits(Container, _Parchment$Container);\n\n function Container() {\n _classCallCheck(this, Container);\n\n return _possibleConstructorReturn(this, (Container.__proto__ || Object.getPrototypeOf(Container)).apply(this, arguments));\n }\n\n return Container;\n}(_parchment2.default.Container);\n\nContainer.allowedChildren = [_block2.default, _block.BlockEmbed, Container];\n\nexports.default = Container;\n\n/***/ }),\n/* 24 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.ColorStyle = exports.ColorClass = exports.ColorAttributor = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar ColorAttributor = function (_Parchment$Attributor) {\n _inherits(ColorAttributor, _Parchment$Attributor);\n\n function ColorAttributor() {\n _classCallCheck(this, ColorAttributor);\n\n return _possibleConstructorReturn(this, (ColorAttributor.__proto__ || Object.getPrototypeOf(ColorAttributor)).apply(this, arguments));\n }\n\n _createClass(ColorAttributor, [{\n key: 'value',\n value: function value(domNode) {\n var value = _get(ColorAttributor.prototype.__proto__ || Object.getPrototypeOf(ColorAttributor.prototype), 'value', this).call(this, domNode);\n if (!value.startsWith('rgb(')) return value;\n value = value.replace(/^[^\\d]+/, '').replace(/[^\\d]+$/, '');\n return '#' + value.split(',').map(function (component) {\n return ('00' + parseInt(component).toString(16)).slice(-2);\n }).join('');\n }\n }]);\n\n return ColorAttributor;\n}(_parchment2.default.Attributor.Style);\n\nvar ColorClass = new _parchment2.default.Attributor.Class('color', 'ql-color', {\n scope: _parchment2.default.Scope.INLINE\n});\nvar ColorStyle = new ColorAttributor('color', 'color', {\n scope: _parchment2.default.Scope.INLINE\n});\n\nexports.ColorAttributor = ColorAttributor;\nexports.ColorClass = ColorClass;\nexports.ColorStyle = ColorStyle;\n\n/***/ }),\n/* 25 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.SHORTKEY = exports.default = undefined;\n\nvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _clone = __webpack_require__(21);\n\nvar _clone2 = _interopRequireDefault(_clone);\n\nvar _deepEqual = __webpack_require__(12);\n\nvar _deepEqual2 = _interopRequireDefault(_deepEqual);\n\nvar _extend = __webpack_require__(2);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nvar _quillDelta = __webpack_require__(4);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _op = __webpack_require__(20);\n\nvar _op2 = _interopRequireDefault(_op);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _quill = __webpack_require__(6);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _logger = __webpack_require__(10);\n\nvar _logger2 = _interopRequireDefault(_logger);\n\nvar _module = __webpack_require__(7);\n\nvar _module2 = _interopRequireDefault(_module);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar debug = (0, _logger2.default)('quill:keyboard');\n\nvar SHORTKEY = /Mac/i.test(navigator.platform) ? 'metaKey' : 'ctrlKey';\n\nvar Keyboard = function (_Module) {\n _inherits(Keyboard, _Module);\n\n _createClass(Keyboard, null, [{\n key: 'match',\n value: function match(evt, binding) {\n binding = normalize(binding);\n if (['altKey', 'ctrlKey', 'metaKey', 'shiftKey'].some(function (key) {\n return !!binding[key] !== evt[key] && binding[key] !== null;\n })) {\n return false;\n }\n return binding.key === (evt.which || evt.keyCode);\n }\n }]);\n\n function Keyboard(quill, options) {\n _classCallCheck(this, Keyboard);\n\n var _this = _possibleConstructorReturn(this, (Keyboard.__proto__ || Object.getPrototypeOf(Keyboard)).call(this, quill, options));\n\n _this.bindings = {};\n Object.keys(_this.options.bindings).forEach(function (name) {\n if (name === 'list autofill' && quill.scroll.whitelist != null && !quill.scroll.whitelist['list']) {\n return;\n }\n if (_this.options.bindings[name]) {\n _this.addBinding(_this.options.bindings[name]);\n }\n });\n _this.addBinding({ key: Keyboard.keys.ENTER, shiftKey: null }, handleEnter);\n _this.addBinding({ key: Keyboard.keys.ENTER, metaKey: null, ctrlKey: null, altKey: null }, function () {});\n if (/Firefox/i.test(navigator.userAgent)) {\n // Need to handle delete and backspace for Firefox in the general case #1171\n _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true }, handleBackspace);\n _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true }, handleDelete);\n } else {\n _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true, prefix: /^.?$/ }, handleBackspace);\n _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true, suffix: /^.?$/ }, handleDelete);\n }\n _this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: false }, handleDeleteRange);\n _this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: false }, handleDeleteRange);\n _this.addBinding({ key: Keyboard.keys.BACKSPACE, altKey: null, ctrlKey: null, metaKey: null, shiftKey: null }, { collapsed: true, offset: 0 }, handleBackspace);\n _this.listen();\n return _this;\n }\n\n _createClass(Keyboard, [{\n key: 'addBinding',\n value: function addBinding(key) {\n var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n var handler = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n var binding = normalize(key);\n if (binding == null || binding.key == null) {\n return debug.warn('Attempted to add invalid keyboard binding', binding);\n }\n if (typeof context === 'function') {\n context = { handler: context };\n }\n if (typeof handler === 'function') {\n handler = { handler: handler };\n }\n binding = (0, _extend2.default)(binding, context, handler);\n this.bindings[binding.key] = this.bindings[binding.key] || [];\n this.bindings[binding.key].push(binding);\n }\n }, {\n key: 'listen',\n value: function listen() {\n var _this2 = this;\n\n this.quill.root.addEventListener('keydown', function (evt) {\n if (evt.defaultPrevented) return;\n var which = evt.which || evt.keyCode;\n var bindings = (_this2.bindings[which] || []).filter(function (binding) {\n return Keyboard.match(evt, binding);\n });\n if (bindings.length === 0) return;\n var range = _this2.quill.getSelection();\n if (range == null || !_this2.quill.hasFocus()) return;\n\n var _quill$getLine = _this2.quill.getLine(range.index),\n _quill$getLine2 = _slicedToArray(_quill$getLine, 2),\n line = _quill$getLine2[0],\n offset = _quill$getLine2[1];\n\n var _quill$getLeaf = _this2.quill.getLeaf(range.index),\n _quill$getLeaf2 = _slicedToArray(_quill$getLeaf, 2),\n leafStart = _quill$getLeaf2[0],\n offsetStart = _quill$getLeaf2[1];\n\n var _ref = range.length === 0 ? [leafStart, offsetStart] : _this2.quill.getLeaf(range.index + range.length),\n _ref2 = _slicedToArray(_ref, 2),\n leafEnd = _ref2[0],\n offsetEnd = _ref2[1];\n\n var prefixText = leafStart instanceof _parchment2.default.Text ? leafStart.value().slice(0, offsetStart) : '';\n var suffixText = leafEnd instanceof _parchment2.default.Text ? leafEnd.value().slice(offsetEnd) : '';\n var curContext = {\n collapsed: range.length === 0,\n empty: range.length === 0 && line.length() <= 1,\n format: _this2.quill.getFormat(range),\n offset: offset,\n prefix: prefixText,\n suffix: suffixText\n };\n var prevented = bindings.some(function (binding) {\n if (binding.collapsed != null && binding.collapsed !== curContext.collapsed) return false;\n if (binding.empty != null && binding.empty !== curContext.empty) return false;\n if (binding.offset != null && binding.offset !== curContext.offset) return false;\n if (Array.isArray(binding.format)) {\n // any format is present\n if (binding.format.every(function (name) {\n return curContext.format[name] == null;\n })) {\n return false;\n }\n } else if (_typeof(binding.format) === 'object') {\n // all formats must match\n if (!Object.keys(binding.format).every(function (name) {\n if (binding.format[name] === true) return curContext.format[name] != null;\n if (binding.format[name] === false) return curContext.format[name] == null;\n return (0, _deepEqual2.default)(binding.format[name], curContext.format[name]);\n })) {\n return false;\n }\n }\n if (binding.prefix != null && !binding.prefix.test(curContext.prefix)) return false;\n if (binding.suffix != null && !binding.suffix.test(curContext.suffix)) return false;\n return binding.handler.call(_this2, range, curContext) !== true;\n });\n if (prevented) {\n evt.preventDefault();\n }\n });\n }\n }]);\n\n return Keyboard;\n}(_module2.default);\n\nKeyboard.keys = {\n BACKSPACE: 8,\n TAB: 9,\n ENTER: 13,\n ESCAPE: 27,\n LEFT: 37,\n UP: 38,\n RIGHT: 39,\n DOWN: 40,\n DELETE: 46\n};\n\nKeyboard.DEFAULTS = {\n bindings: {\n 'bold': makeFormatHandler('bold'),\n 'italic': makeFormatHandler('italic'),\n 'underline': makeFormatHandler('underline'),\n 'indent': {\n // highlight tab or tab at beginning of list, indent or blockquote\n key: Keyboard.keys.TAB,\n format: ['blockquote', 'indent', 'list'],\n handler: function handler(range, context) {\n if (context.collapsed && context.offset !== 0) return true;\n this.quill.format('indent', '+1', _quill2.default.sources.USER);\n }\n },\n 'outdent': {\n key: Keyboard.keys.TAB,\n shiftKey: true,\n format: ['blockquote', 'indent', 'list'],\n // highlight tab or tab at beginning of list, indent or blockquote\n handler: function handler(range, context) {\n if (context.collapsed && context.offset !== 0) return true;\n this.quill.format('indent', '-1', _quill2.default.sources.USER);\n }\n },\n 'outdent backspace': {\n key: Keyboard.keys.BACKSPACE,\n collapsed: true,\n shiftKey: null,\n metaKey: null,\n ctrlKey: null,\n altKey: null,\n format: ['indent', 'list'],\n offset: 0,\n handler: function handler(range, context) {\n if (context.format.indent != null) {\n this.quill.format('indent', '-1', _quill2.default.sources.USER);\n } else if (context.format.list != null) {\n this.quill.format('list', false, _quill2.default.sources.USER);\n }\n }\n },\n 'indent code-block': makeCodeBlockHandler(true),\n 'outdent code-block': makeCodeBlockHandler(false),\n 'remove tab': {\n key: Keyboard.keys.TAB,\n shiftKey: true,\n collapsed: true,\n prefix: /\\t$/,\n handler: function handler(range) {\n this.quill.deleteText(range.index - 1, 1, _quill2.default.sources.USER);\n }\n },\n 'tab': {\n key: Keyboard.keys.TAB,\n handler: function handler(range) {\n this.quill.history.cutoff();\n var delta = new _quillDelta2.default().retain(range.index).delete(range.length).insert('\\t');\n this.quill.updateContents(delta, _quill2.default.sources.USER);\n this.quill.history.cutoff();\n this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT);\n }\n },\n 'list empty enter': {\n key: Keyboard.keys.ENTER,\n collapsed: true,\n format: ['list'],\n empty: true,\n handler: function handler(range, context) {\n this.quill.format('list', false, _quill2.default.sources.USER);\n if (context.format.indent) {\n this.quill.format('indent', false, _quill2.default.sources.USER);\n }\n }\n },\n 'checklist enter': {\n key: Keyboard.keys.ENTER,\n collapsed: true,\n format: { list: 'checked' },\n handler: function handler(range) {\n var _quill$getLine3 = this.quill.getLine(range.index),\n _quill$getLine4 = _slicedToArray(_quill$getLine3, 2),\n line = _quill$getLine4[0],\n offset = _quill$getLine4[1];\n\n var formats = (0, _extend2.default)({}, line.formats(), { list: 'checked' });\n var delta = new _quillDelta2.default().retain(range.index).insert('\\n', formats).retain(line.length() - offset - 1).retain(1, { list: 'unchecked' });\n this.quill.updateContents(delta, _quill2.default.sources.USER);\n this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT);\n this.quill.scrollIntoView();\n }\n },\n 'header enter': {\n key: Keyboard.keys.ENTER,\n collapsed: true,\n format: ['header'],\n suffix: /^$/,\n handler: function handler(range, context) {\n var _quill$getLine5 = this.quill.getLine(range.index),\n _quill$getLine6 = _slicedToArray(_quill$getLine5, 2),\n line = _quill$getLine6[0],\n offset = _quill$getLine6[1];\n\n var delta = new _quillDelta2.default().retain(range.index).insert('\\n', context.format).retain(line.length() - offset - 1).retain(1, { header: null });\n this.quill.updateContents(delta, _quill2.default.sources.USER);\n this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT);\n this.quill.scrollIntoView();\n }\n },\n 'list autofill': {\n key: ' ',\n collapsed: true,\n format: { list: false },\n prefix: /^\\s*?(\\d+\\.|-|\\*|\\[ ?\\]|\\[x\\])$/,\n handler: function handler(range, context) {\n var length = context.prefix.length;\n\n var _quill$getLine7 = this.quill.getLine(range.index),\n _quill$getLine8 = _slicedToArray(_quill$getLine7, 2),\n line = _quill$getLine8[0],\n offset = _quill$getLine8[1];\n\n if (offset > length) return true;\n var value = void 0;\n switch (context.prefix.trim()) {\n case '[]':case '[ ]':\n value = 'unchecked';\n break;\n case '[x]':\n value = 'checked';\n break;\n case '-':case '*':\n value = 'bullet';\n break;\n default:\n value = 'ordered';\n }\n this.quill.insertText(range.index, ' ', _quill2.default.sources.USER);\n this.quill.history.cutoff();\n var delta = new _quillDelta2.default().retain(range.index - offset).delete(length + 1).retain(line.length() - 2 - offset).retain(1, { list: value });\n this.quill.updateContents(delta, _quill2.default.sources.USER);\n this.quill.history.cutoff();\n this.quill.setSelection(range.index - length, _quill2.default.sources.SILENT);\n }\n },\n 'code exit': {\n key: Keyboard.keys.ENTER,\n collapsed: true,\n format: ['code-block'],\n prefix: /\\n\\n$/,\n suffix: /^\\s+$/,\n handler: function handler(range) {\n var _quill$getLine9 = this.quill.getLine(range.index),\n _quill$getLine10 = _slicedToArray(_quill$getLine9, 2),\n line = _quill$getLine10[0],\n offset = _quill$getLine10[1];\n\n var delta = new _quillDelta2.default().retain(range.index + line.length() - offset - 2).retain(1, { 'code-block': null }).delete(1);\n this.quill.updateContents(delta, _quill2.default.sources.USER);\n }\n },\n 'embed left': makeEmbedArrowHandler(Keyboard.keys.LEFT, false),\n 'embed left shift': makeEmbedArrowHandler(Keyboard.keys.LEFT, true),\n 'embed right': makeEmbedArrowHandler(Keyboard.keys.RIGHT, false),\n 'embed right shift': makeEmbedArrowHandler(Keyboard.keys.RIGHT, true)\n }\n};\n\nfunction makeEmbedArrowHandler(key, shiftKey) {\n var _ref3;\n\n var where = key === Keyboard.keys.LEFT ? 'prefix' : 'suffix';\n return _ref3 = {\n key: key,\n shiftKey: shiftKey,\n altKey: null\n }, _defineProperty(_ref3, where, /^$/), _defineProperty(_ref3, 'handler', function handler(range) {\n var index = range.index;\n if (key === Keyboard.keys.RIGHT) {\n index += range.length + 1;\n }\n\n var _quill$getLeaf3 = this.quill.getLeaf(index),\n _quill$getLeaf4 = _slicedToArray(_quill$getLeaf3, 1),\n leaf = _quill$getLeaf4[0];\n\n if (!(leaf instanceof _parchment2.default.Embed)) return true;\n if (key === Keyboard.keys.LEFT) {\n if (shiftKey) {\n this.quill.setSelection(range.index - 1, range.length + 1, _quill2.default.sources.USER);\n } else {\n this.quill.setSelection(range.index - 1, _quill2.default.sources.USER);\n }\n } else {\n if (shiftKey) {\n this.quill.setSelection(range.index, range.length + 1, _quill2.default.sources.USER);\n } else {\n this.quill.setSelection(range.index + range.length + 1, _quill2.default.sources.USER);\n }\n }\n return false;\n }), _ref3;\n}\n\nfunction handleBackspace(range, context) {\n if (range.index === 0 || this.quill.getLength() <= 1) return;\n\n var _quill$getLine11 = this.quill.getLine(range.index),\n _quill$getLine12 = _slicedToArray(_quill$getLine11, 1),\n line = _quill$getLine12[0];\n\n var formats = {};\n if (context.offset === 0) {\n var _quill$getLine13 = this.quill.getLine(range.index - 1),\n _quill$getLine14 = _slicedToArray(_quill$getLine13, 1),\n prev = _quill$getLine14[0];\n\n if (prev != null && prev.length() > 1) {\n var curFormats = line.formats();\n var prevFormats = this.quill.getFormat(range.index - 1, 1);\n formats = _op2.default.attributes.diff(curFormats, prevFormats) || {};\n }\n }\n // Check for astral symbols\n var length = /[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]$/.test(context.prefix) ? 2 : 1;\n this.quill.deleteText(range.index - length, length, _quill2.default.sources.USER);\n if (Object.keys(formats).length > 0) {\n this.quill.formatLine(range.index - length, length, formats, _quill2.default.sources.USER);\n }\n this.quill.focus();\n}\n\nfunction handleDelete(range, context) {\n // Check for astral symbols\n var length = /^[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/.test(context.suffix) ? 2 : 1;\n if (range.index >= this.quill.getLength() - length) return;\n var formats = {},\n nextLength = 0;\n\n var _quill$getLine15 = this.quill.getLine(range.index),\n _quill$getLine16 = _slicedToArray(_quill$getLine15, 1),\n line = _quill$getLine16[0];\n\n if (context.offset >= line.length() - 1) {\n var _quill$getLine17 = this.quill.getLine(range.index + 1),\n _quill$getLine18 = _slicedToArray(_quill$getLine17, 1),\n next = _quill$getLine18[0];\n\n if (next) {\n var curFormats = line.formats();\n var nextFormats = this.quill.getFormat(range.index, 1);\n formats = _op2.default.attributes.diff(curFormats, nextFormats) || {};\n nextLength = next.length();\n }\n }\n this.quill.deleteText(range.index, length, _quill2.default.sources.USER);\n if (Object.keys(formats).length > 0) {\n this.quill.formatLine(range.index + nextLength - 1, length, formats, _quill2.default.sources.USER);\n }\n}\n\nfunction handleDeleteRange(range) {\n var lines = this.quill.getLines(range);\n var formats = {};\n if (lines.length > 1) {\n var firstFormats = lines[0].formats();\n var lastFormats = lines[lines.length - 1].formats();\n formats = _op2.default.attributes.diff(lastFormats, firstFormats) || {};\n }\n this.quill.deleteText(range, _quill2.default.sources.USER);\n if (Object.keys(formats).length > 0) {\n this.quill.formatLine(range.index, 1, formats, _quill2.default.sources.USER);\n }\n this.quill.setSelection(range.index, _quill2.default.sources.SILENT);\n this.quill.focus();\n}\n\nfunction handleEnter(range, context) {\n var _this3 = this;\n\n if (range.length > 0) {\n this.quill.scroll.deleteAt(range.index, range.length); // So we do not trigger text-change\n }\n var lineFormats = Object.keys(context.format).reduce(function (lineFormats, format) {\n if (_parchment2.default.query(format, _parchment2.default.Scope.BLOCK) && !Array.isArray(context.format[format])) {\n lineFormats[format] = context.format[format];\n }\n return lineFormats;\n }, {});\n this.quill.insertText(range.index, '\\n', lineFormats, _quill2.default.sources.USER);\n // Earlier scroll.deleteAt might have messed up our selection,\n // so insertText's built in selection preservation is not reliable\n this.quill.setSelection(range.index + 1, _quill2.default.sources.SILENT);\n this.quill.focus();\n Object.keys(context.format).forEach(function (name) {\n if (lineFormats[name] != null) return;\n if (Array.isArray(context.format[name])) return;\n if (name === 'link') return;\n _this3.quill.format(name, context.format[name], _quill2.default.sources.USER);\n });\n}\n\nfunction makeCodeBlockHandler(indent) {\n return {\n key: Keyboard.keys.TAB,\n shiftKey: !indent,\n format: { 'code-block': true },\n handler: function handler(range) {\n var CodeBlock = _parchment2.default.query('code-block');\n var index = range.index,\n length = range.length;\n\n var _quill$scroll$descend = this.quill.scroll.descendant(CodeBlock, index),\n _quill$scroll$descend2 = _slicedToArray(_quill$scroll$descend, 2),\n block = _quill$scroll$descend2[0],\n offset = _quill$scroll$descend2[1];\n\n if (block == null) return;\n var scrollIndex = this.quill.getIndex(block);\n var start = block.newlineIndex(offset, true) + 1;\n var end = block.newlineIndex(scrollIndex + offset + length);\n var lines = block.domNode.textContent.slice(start, end).split('\\n');\n offset = 0;\n lines.forEach(function (line, i) {\n if (indent) {\n block.insertAt(start + offset, CodeBlock.TAB);\n offset += CodeBlock.TAB.length;\n if (i === 0) {\n index += CodeBlock.TAB.length;\n } else {\n length += CodeBlock.TAB.length;\n }\n } else if (line.startsWith(CodeBlock.TAB)) {\n block.deleteAt(start + offset, CodeBlock.TAB.length);\n offset -= CodeBlock.TAB.length;\n if (i === 0) {\n index -= CodeBlock.TAB.length;\n } else {\n length -= CodeBlock.TAB.length;\n }\n }\n offset += line.length + 1;\n });\n this.quill.update(_quill2.default.sources.USER);\n this.quill.setSelection(index, length, _quill2.default.sources.SILENT);\n }\n };\n}\n\nfunction makeFormatHandler(format) {\n return {\n key: format[0].toUpperCase(),\n shortKey: true,\n handler: function handler(range, context) {\n this.quill.format(format, !context.format[format], _quill2.default.sources.USER);\n }\n };\n}\n\nfunction normalize(binding) {\n if (typeof binding === 'string' || typeof binding === 'number') {\n return normalize({ key: binding });\n }\n if ((typeof binding === 'undefined' ? 'undefined' : _typeof(binding)) === 'object') {\n binding = (0, _clone2.default)(binding, false);\n }\n if (typeof binding.key === 'string') {\n if (Keyboard.keys[binding.key.toUpperCase()] != null) {\n binding.key = Keyboard.keys[binding.key.toUpperCase()];\n } else if (binding.key.length === 1) {\n binding.key = binding.key.toUpperCase().charCodeAt(0);\n } else {\n return null;\n }\n }\n if (binding.shortKey) {\n binding[SHORTKEY] = binding.shortKey;\n delete binding.shortKey;\n }\n return binding;\n}\n\nexports.default = Keyboard;\nexports.SHORTKEY = SHORTKEY;\n\n/***/ }),\n/* 26 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = {\n 'align': {\n '': __webpack_require__(75),\n 'center': __webpack_require__(76),\n 'right': __webpack_require__(77),\n 'justify': __webpack_require__(78)\n },\n 'background': __webpack_require__(79),\n 'blockquote': __webpack_require__(80),\n 'bold': __webpack_require__(81),\n 'clean': __webpack_require__(82),\n 'code': __webpack_require__(40),\n 'code-block': __webpack_require__(40),\n 'color': __webpack_require__(83),\n 'direction': {\n '': __webpack_require__(84),\n 'rtl': __webpack_require__(85)\n },\n 'float': {\n 'center': __webpack_require__(86),\n 'full': __webpack_require__(87),\n 'left': __webpack_require__(88),\n 'right': __webpack_require__(89)\n },\n 'formula': __webpack_require__(90),\n 'header': {\n '1': __webpack_require__(91),\n '2': __webpack_require__(92)\n },\n 'italic': __webpack_require__(93),\n 'image': __webpack_require__(94),\n 'indent': {\n '+1': __webpack_require__(95),\n '-1': __webpack_require__(96)\n },\n 'link': __webpack_require__(97),\n 'list': {\n 'ordered': __webpack_require__(98),\n 'bullet': __webpack_require__(99),\n 'check': __webpack_require__(100)\n },\n 'script': {\n 'sub': __webpack_require__(101),\n 'super': __webpack_require__(102)\n },\n 'strike': __webpack_require__(103),\n 'underline': __webpack_require__(104),\n 'video': __webpack_require__(105)\n};\n\n/***/ }),\n/* 27 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar Registry = __webpack_require__(1);\nvar ShadowBlot = /** @class */ (function () {\n function ShadowBlot(domNode) {\n this.domNode = domNode;\n // @ts-ignore\n this.domNode[Registry.DATA_KEY] = { blot: this };\n }\n Object.defineProperty(ShadowBlot.prototype, \"statics\", {\n // Hack for accessing inherited static methods\n get: function () {\n return this.constructor;\n },\n enumerable: true,\n configurable: true\n });\n ShadowBlot.create = function (value) {\n if (this.tagName == null) {\n throw new Registry.ParchmentError('Blot definition missing tagName');\n }\n var node;\n if (Array.isArray(this.tagName)) {\n if (typeof value === 'string') {\n value = value.toUpperCase();\n if (parseInt(value).toString() === value) {\n value = parseInt(value);\n }\n }\n if (typeof value === 'number') {\n node = document.createElement(this.tagName[value - 1]);\n }\n else if (this.tagName.indexOf(value) > -1) {\n node = document.createElement(value);\n }\n else {\n node = document.createElement(this.tagName[0]);\n }\n }\n else {\n node = document.createElement(this.tagName);\n }\n if (this.className) {\n node.classList.add(this.className);\n }\n return node;\n };\n ShadowBlot.prototype.attach = function () {\n if (this.parent != null) {\n this.scroll = this.parent.scroll;\n }\n };\n ShadowBlot.prototype.clone = function () {\n var domNode = this.domNode.cloneNode(false);\n return Registry.create(domNode);\n };\n ShadowBlot.prototype.detach = function () {\n if (this.parent != null)\n this.parent.removeChild(this);\n // @ts-ignore\n delete this.domNode[Registry.DATA_KEY];\n };\n ShadowBlot.prototype.deleteAt = function (index, length) {\n var blot = this.isolate(index, length);\n blot.remove();\n };\n ShadowBlot.prototype.formatAt = function (index, length, name, value) {\n var blot = this.isolate(index, length);\n if (Registry.query(name, Registry.Scope.BLOT) != null && value) {\n blot.wrap(name, value);\n }\n else if (Registry.query(name, Registry.Scope.ATTRIBUTE) != null) {\n var parent = Registry.create(this.statics.scope);\n blot.wrap(parent);\n parent.format(name, value);\n }\n };\n ShadowBlot.prototype.insertAt = function (index, value, def) {\n var blot = def == null ? Registry.create('text', value) : Registry.create(value, def);\n var ref = this.split(index);\n this.parent.insertBefore(blot, ref);\n };\n ShadowBlot.prototype.insertInto = function (parentBlot, refBlot) {\n if (refBlot === void 0) { refBlot = null; }\n if (this.parent != null) {\n this.parent.children.remove(this);\n }\n var refDomNode = null;\n parentBlot.children.insertBefore(this, refBlot);\n if (refBlot != null) {\n refDomNode = refBlot.domNode;\n }\n if (this.domNode.parentNode != parentBlot.domNode ||\n this.domNode.nextSibling != refDomNode) {\n parentBlot.domNode.insertBefore(this.domNode, refDomNode);\n }\n this.parent = parentBlot;\n this.attach();\n };\n ShadowBlot.prototype.isolate = function (index, length) {\n var target = this.split(index);\n target.split(length);\n return target;\n };\n ShadowBlot.prototype.length = function () {\n return 1;\n };\n ShadowBlot.prototype.offset = function (root) {\n if (root === void 0) { root = this.parent; }\n if (this.parent == null || this == root)\n return 0;\n return this.parent.children.offset(this) + this.parent.offset(root);\n };\n ShadowBlot.prototype.optimize = function (context) {\n // TODO clean up once we use WeakMap\n // @ts-ignore\n if (this.domNode[Registry.DATA_KEY] != null) {\n // @ts-ignore\n delete this.domNode[Registry.DATA_KEY].mutations;\n }\n };\n ShadowBlot.prototype.remove = function () {\n if (this.domNode.parentNode != null) {\n this.domNode.parentNode.removeChild(this.domNode);\n }\n this.detach();\n };\n ShadowBlot.prototype.replace = function (target) {\n if (target.parent == null)\n return;\n target.parent.insertBefore(this, target.next);\n target.remove();\n };\n ShadowBlot.prototype.replaceWith = function (name, value) {\n var replacement = typeof name === 'string' ? Registry.create(name, value) : name;\n replacement.replace(this);\n return replacement;\n };\n ShadowBlot.prototype.split = function (index, force) {\n return index === 0 ? this : this.next;\n };\n ShadowBlot.prototype.update = function (mutations, context) {\n // Nothing to do by default\n };\n ShadowBlot.prototype.wrap = function (name, value) {\n var wrapper = typeof name === 'string' ? Registry.create(name, value) : name;\n if (this.parent != null) {\n this.parent.insertBefore(wrapper, this.next);\n }\n wrapper.appendChild(this);\n return wrapper;\n };\n ShadowBlot.blotName = 'abstract';\n return ShadowBlot;\n}());\nexports.default = ShadowBlot;\n\n\n/***/ }),\n/* 28 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar attributor_1 = __webpack_require__(11);\nvar class_1 = __webpack_require__(29);\nvar style_1 = __webpack_require__(30);\nvar Registry = __webpack_require__(1);\nvar AttributorStore = /** @class */ (function () {\n function AttributorStore(domNode) {\n this.attributes = {};\n this.domNode = domNode;\n this.build();\n }\n AttributorStore.prototype.attribute = function (attribute, value) {\n // verb\n if (value) {\n if (attribute.add(this.domNode, value)) {\n if (attribute.value(this.domNode) != null) {\n this.attributes[attribute.attrName] = attribute;\n }\n else {\n delete this.attributes[attribute.attrName];\n }\n }\n }\n else {\n attribute.remove(this.domNode);\n delete this.attributes[attribute.attrName];\n }\n };\n AttributorStore.prototype.build = function () {\n var _this = this;\n this.attributes = {};\n var attributes = attributor_1.default.keys(this.domNode);\n var classes = class_1.default.keys(this.domNode);\n var styles = style_1.default.keys(this.domNode);\n attributes\n .concat(classes)\n .concat(styles)\n .forEach(function (name) {\n var attr = Registry.query(name, Registry.Scope.ATTRIBUTE);\n if (attr instanceof attributor_1.default) {\n _this.attributes[attr.attrName] = attr;\n }\n });\n };\n AttributorStore.prototype.copy = function (target) {\n var _this = this;\n Object.keys(this.attributes).forEach(function (key) {\n var value = _this.attributes[key].value(_this.domNode);\n target.format(key, value);\n });\n };\n AttributorStore.prototype.move = function (target) {\n var _this = this;\n this.copy(target);\n Object.keys(this.attributes).forEach(function (key) {\n _this.attributes[key].remove(_this.domNode);\n });\n this.attributes = {};\n };\n AttributorStore.prototype.values = function () {\n var _this = this;\n return Object.keys(this.attributes).reduce(function (attributes, name) {\n attributes[name] = _this.attributes[name].value(_this.domNode);\n return attributes;\n }, {});\n };\n return AttributorStore;\n}());\nexports.default = AttributorStore;\n\n\n/***/ }),\n/* 29 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar attributor_1 = __webpack_require__(11);\nfunction match(node, prefix) {\n var className = node.getAttribute('class') || '';\n return className.split(/\\s+/).filter(function (name) {\n return name.indexOf(prefix + \"-\") === 0;\n });\n}\nvar ClassAttributor = /** @class */ (function (_super) {\n __extends(ClassAttributor, _super);\n function ClassAttributor() {\n return _super !== null && _super.apply(this, arguments) || this;\n }\n ClassAttributor.keys = function (node) {\n return (node.getAttribute('class') || '').split(/\\s+/).map(function (name) {\n return name\n .split('-')\n .slice(0, -1)\n .join('-');\n });\n };\n ClassAttributor.prototype.add = function (node, value) {\n if (!this.canAdd(node, value))\n return false;\n this.remove(node);\n node.classList.add(this.keyName + \"-\" + value);\n return true;\n };\n ClassAttributor.prototype.remove = function (node) {\n var matches = match(node, this.keyName);\n matches.forEach(function (name) {\n node.classList.remove(name);\n });\n if (node.classList.length === 0) {\n node.removeAttribute('class');\n }\n };\n ClassAttributor.prototype.value = function (node) {\n var result = match(node, this.keyName)[0] || '';\n var value = result.slice(this.keyName.length + 1); // +1 for hyphen\n return this.canAdd(node, value) ? value : '';\n };\n return ClassAttributor;\n}(attributor_1.default));\nexports.default = ClassAttributor;\n\n\n/***/ }),\n/* 30 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar attributor_1 = __webpack_require__(11);\nfunction camelize(name) {\n var parts = name.split('-');\n var rest = parts\n .slice(1)\n .map(function (part) {\n return part[0].toUpperCase() + part.slice(1);\n })\n .join('');\n return parts[0] + rest;\n}\nvar StyleAttributor = /** @class */ (function (_super) {\n __extends(StyleAttributor, _super);\n function StyleAttributor() {\n return _super !== null && _super.apply(this, arguments) || this;\n }\n StyleAttributor.keys = function (node) {\n return (node.getAttribute('style') || '').split(';').map(function (value) {\n var arr = value.split(':');\n return arr[0].trim();\n });\n };\n StyleAttributor.prototype.add = function (node, value) {\n if (!this.canAdd(node, value))\n return false;\n // @ts-ignore\n node.style[camelize(this.keyName)] = value;\n return true;\n };\n StyleAttributor.prototype.remove = function (node) {\n // @ts-ignore\n node.style[camelize(this.keyName)] = '';\n if (!node.getAttribute('style')) {\n node.removeAttribute('style');\n }\n };\n StyleAttributor.prototype.value = function (node) {\n // @ts-ignore\n var value = node.style[camelize(this.keyName)];\n return this.canAdd(node, value) ? value : '';\n };\n return StyleAttributor;\n}(attributor_1.default));\nexports.default = StyleAttributor;\n\n\n/***/ }),\n/* 31 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _text = __webpack_require__(8);\n\nvar _text2 = _interopRequireDefault(_text);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Cursor = function (_Parchment$Embed) {\n _inherits(Cursor, _Parchment$Embed);\n\n _createClass(Cursor, null, [{\n key: 'value',\n value: function value() {\n return undefined;\n }\n }]);\n\n function Cursor(domNode, selection) {\n _classCallCheck(this, Cursor);\n\n var _this = _possibleConstructorReturn(this, (Cursor.__proto__ || Object.getPrototypeOf(Cursor)).call(this, domNode));\n\n _this.selection = selection;\n _this.textNode = document.createTextNode(Cursor.CONTENTS);\n _this.domNode.appendChild(_this.textNode);\n _this._length = 0;\n return _this;\n }\n\n _createClass(Cursor, [{\n key: 'detach',\n value: function detach() {\n // super.detach() will also clear domNode.__blot\n if (this.parent != null) this.parent.removeChild(this);\n }\n }, {\n key: 'format',\n value: function format(name, value) {\n if (this._length !== 0) {\n return _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'format', this).call(this, name, value);\n }\n var target = this,\n index = 0;\n while (target != null && target.statics.scope !== _parchment2.default.Scope.BLOCK_BLOT) {\n index += target.offset(target.parent);\n target = target.parent;\n }\n if (target != null) {\n this._length = Cursor.CONTENTS.length;\n target.optimize();\n target.formatAt(index, Cursor.CONTENTS.length, name, value);\n this._length = 0;\n }\n }\n }, {\n key: 'index',\n value: function index(node, offset) {\n if (node === this.textNode) return 0;\n return _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'index', this).call(this, node, offset);\n }\n }, {\n key: 'length',\n value: function length() {\n return this._length;\n }\n }, {\n key: 'position',\n value: function position() {\n return [this.textNode, this.textNode.data.length];\n }\n }, {\n key: 'remove',\n value: function remove() {\n _get(Cursor.prototype.__proto__ || Object.getPrototypeOf(Cursor.prototype), 'remove', this).call(this);\n this.parent = null;\n }\n }, {\n key: 'restore',\n value: function restore() {\n if (this.selection.composing || this.parent == null) return;\n var textNode = this.textNode;\n var range = this.selection.getNativeRange();\n var restoreText = void 0,\n start = void 0,\n end = void 0;\n if (range != null && range.start.node === textNode && range.end.node === textNode) {\n var _ref = [textNode, range.start.offset, range.end.offset];\n restoreText = _ref[0];\n start = _ref[1];\n end = _ref[2];\n }\n // Link format will insert text outside of anchor tag\n while (this.domNode.lastChild != null && this.domNode.lastChild !== this.textNode) {\n this.domNode.parentNode.insertBefore(this.domNode.lastChild, this.domNode);\n }\n if (this.textNode.data !== Cursor.CONTENTS) {\n var text = this.textNode.data.split(Cursor.CONTENTS).join('');\n if (this.next instanceof _text2.default) {\n restoreText = this.next.domNode;\n this.next.insertAt(0, text);\n this.textNode.data = Cursor.CONTENTS;\n } else {\n this.textNode.data = text;\n this.parent.insertBefore(_parchment2.default.create(this.textNode), this);\n this.textNode = document.createTextNode(Cursor.CONTENTS);\n this.domNode.appendChild(this.textNode);\n }\n }\n this.remove();\n if (start != null) {\n var _map = [start, end].map(function (offset) {\n return Math.max(0, Math.min(restoreText.data.length, offset - 1));\n });\n\n var _map2 = _slicedToArray(_map, 2);\n\n start = _map2[0];\n end = _map2[1];\n\n return {\n startNode: restoreText,\n startOffset: start,\n endNode: restoreText,\n endOffset: end\n };\n }\n }\n }, {\n key: 'update',\n value: function update(mutations, context) {\n var _this2 = this;\n\n if (mutations.some(function (mutation) {\n return mutation.type === 'characterData' && mutation.target === _this2.textNode;\n })) {\n var range = this.restore();\n if (range) context.range = range;\n }\n }\n }, {\n key: 'value',\n value: function value() {\n return '';\n }\n }]);\n\n return Cursor;\n}(_parchment2.default.Embed);\n\nCursor.blotName = 'cursor';\nCursor.className = 'ql-cursor';\nCursor.tagName = 'span';\nCursor.CONTENTS = '\\uFEFF'; // Zero width no break space\n\n\nexports.default = Cursor;\n\n/***/ }),\n/* 32 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar Theme = function () {\n function Theme(quill, options) {\n _classCallCheck(this, Theme);\n\n this.quill = quill;\n this.options = options;\n this.modules = {};\n }\n\n _createClass(Theme, [{\n key: 'init',\n value: function init() {\n var _this = this;\n\n Object.keys(this.options.modules).forEach(function (name) {\n if (_this.modules[name] == null) {\n _this.addModule(name);\n }\n });\n }\n }, {\n key: 'addModule',\n value: function addModule(name) {\n var moduleClass = this.quill.constructor.import('modules/' + name);\n this.modules[name] = new moduleClass(this.quill, this.options.modules[name] || {});\n return this.modules[name];\n }\n }]);\n\n return Theme;\n}();\n\nTheme.DEFAULTS = {\n modules: {}\n};\nTheme.themes = {\n 'default': Theme\n};\n\nexports.default = Theme;\n\n/***/ }),\n/* 33 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _text = __webpack_require__(8);\n\nvar _text2 = _interopRequireDefault(_text);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar GUARD_TEXT = '\\uFEFF';\n\nvar Embed = function (_Parchment$Embed) {\n _inherits(Embed, _Parchment$Embed);\n\n function Embed(node) {\n _classCallCheck(this, Embed);\n\n var _this = _possibleConstructorReturn(this, (Embed.__proto__ || Object.getPrototypeOf(Embed)).call(this, node));\n\n _this.contentNode = document.createElement('span');\n _this.contentNode.setAttribute('contenteditable', false);\n [].slice.call(_this.domNode.childNodes).forEach(function (childNode) {\n _this.contentNode.appendChild(childNode);\n });\n _this.leftGuard = document.createTextNode(GUARD_TEXT);\n _this.rightGuard = document.createTextNode(GUARD_TEXT);\n _this.domNode.appendChild(_this.leftGuard);\n _this.domNode.appendChild(_this.contentNode);\n _this.domNode.appendChild(_this.rightGuard);\n return _this;\n }\n\n _createClass(Embed, [{\n key: 'index',\n value: function index(node, offset) {\n if (node === this.leftGuard) return 0;\n if (node === this.rightGuard) return 1;\n return _get(Embed.prototype.__proto__ || Object.getPrototypeOf(Embed.prototype), 'index', this).call(this, node, offset);\n }\n }, {\n key: 'restore',\n value: function restore(node) {\n var range = void 0,\n textNode = void 0;\n var text = node.data.split(GUARD_TEXT).join('');\n if (node === this.leftGuard) {\n if (this.prev instanceof _text2.default) {\n var prevLength = this.prev.length();\n this.prev.insertAt(prevLength, text);\n range = {\n startNode: this.prev.domNode,\n startOffset: prevLength + text.length\n };\n } else {\n textNode = document.createTextNode(text);\n this.parent.insertBefore(_parchment2.default.create(textNode), this);\n range = {\n startNode: textNode,\n startOffset: text.length\n };\n }\n } else if (node === this.rightGuard) {\n if (this.next instanceof _text2.default) {\n this.next.insertAt(0, text);\n range = {\n startNode: this.next.domNode,\n startOffset: text.length\n };\n } else {\n textNode = document.createTextNode(text);\n this.parent.insertBefore(_parchment2.default.create(textNode), this.next);\n range = {\n startNode: textNode,\n startOffset: text.length\n };\n }\n }\n node.data = GUARD_TEXT;\n return range;\n }\n }, {\n key: 'update',\n value: function update(mutations, context) {\n var _this2 = this;\n\n mutations.forEach(function (mutation) {\n if (mutation.type === 'characterData' && (mutation.target === _this2.leftGuard || mutation.target === _this2.rightGuard)) {\n var range = _this2.restore(mutation.target);\n if (range) context.range = range;\n }\n });\n }\n }]);\n\n return Embed;\n}(_parchment2.default.Embed);\n\nexports.default = Embed;\n\n/***/ }),\n/* 34 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.AlignStyle = exports.AlignClass = exports.AlignAttribute = undefined;\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar config = {\n scope: _parchment2.default.Scope.BLOCK,\n whitelist: ['right', 'center', 'justify']\n};\n\nvar AlignAttribute = new _parchment2.default.Attributor.Attribute('align', 'align', config);\nvar AlignClass = new _parchment2.default.Attributor.Class('align', 'ql-align', config);\nvar AlignStyle = new _parchment2.default.Attributor.Style('align', 'text-align', config);\n\nexports.AlignAttribute = AlignAttribute;\nexports.AlignClass = AlignClass;\nexports.AlignStyle = AlignStyle;\n\n/***/ }),\n/* 35 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.BackgroundStyle = exports.BackgroundClass = undefined;\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _color = __webpack_require__(24);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar BackgroundClass = new _parchment2.default.Attributor.Class('background', 'ql-bg', {\n scope: _parchment2.default.Scope.INLINE\n});\nvar BackgroundStyle = new _color.ColorAttributor('background', 'background-color', {\n scope: _parchment2.default.Scope.INLINE\n});\n\nexports.BackgroundClass = BackgroundClass;\nexports.BackgroundStyle = BackgroundStyle;\n\n/***/ }),\n/* 36 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.DirectionStyle = exports.DirectionClass = exports.DirectionAttribute = undefined;\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar config = {\n scope: _parchment2.default.Scope.BLOCK,\n whitelist: ['rtl']\n};\n\nvar DirectionAttribute = new _parchment2.default.Attributor.Attribute('direction', 'dir', config);\nvar DirectionClass = new _parchment2.default.Attributor.Class('direction', 'ql-direction', config);\nvar DirectionStyle = new _parchment2.default.Attributor.Style('direction', 'direction', config);\n\nexports.DirectionAttribute = DirectionAttribute;\nexports.DirectionClass = DirectionClass;\nexports.DirectionStyle = DirectionStyle;\n\n/***/ }),\n/* 37 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.FontClass = exports.FontStyle = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar config = {\n scope: _parchment2.default.Scope.INLINE,\n whitelist: ['serif', 'monospace']\n};\n\nvar FontClass = new _parchment2.default.Attributor.Class('font', 'ql-font', config);\n\nvar FontStyleAttributor = function (_Parchment$Attributor) {\n _inherits(FontStyleAttributor, _Parchment$Attributor);\n\n function FontStyleAttributor() {\n _classCallCheck(this, FontStyleAttributor);\n\n return _possibleConstructorReturn(this, (FontStyleAttributor.__proto__ || Object.getPrototypeOf(FontStyleAttributor)).apply(this, arguments));\n }\n\n _createClass(FontStyleAttributor, [{\n key: 'value',\n value: function value(node) {\n return _get(FontStyleAttributor.prototype.__proto__ || Object.getPrototypeOf(FontStyleAttributor.prototype), 'value', this).call(this, node).replace(/[\"']/g, '');\n }\n }]);\n\n return FontStyleAttributor;\n}(_parchment2.default.Attributor.Style);\n\nvar FontStyle = new FontStyleAttributor('font', 'font-family', config);\n\nexports.FontStyle = FontStyle;\nexports.FontClass = FontClass;\n\n/***/ }),\n/* 38 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.SizeStyle = exports.SizeClass = undefined;\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar SizeClass = new _parchment2.default.Attributor.Class('size', 'ql-size', {\n scope: _parchment2.default.Scope.INLINE,\n whitelist: ['small', 'large', 'huge']\n});\nvar SizeStyle = new _parchment2.default.Attributor.Style('size', 'font-size', {\n scope: _parchment2.default.Scope.INLINE,\n whitelist: ['10px', '18px', '32px']\n});\n\nexports.SizeClass = SizeClass;\nexports.SizeStyle = SizeStyle;\n\n/***/ }),\n/* 39 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _inline = __webpack_require__(5);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Bold = function (_Inline) {\n _inherits(Bold, _Inline);\n\n function Bold() {\n _classCallCheck(this, Bold);\n\n return _possibleConstructorReturn(this, (Bold.__proto__ || Object.getPrototypeOf(Bold)).apply(this, arguments));\n }\n\n _createClass(Bold, [{\n key: 'optimize',\n value: function optimize(context) {\n _get(Bold.prototype.__proto__ || Object.getPrototypeOf(Bold.prototype), 'optimize', this).call(this, context);\n if (this.domNode.tagName !== this.statics.tagName[0]) {\n this.replaceWith(this.statics.blotName);\n }\n }\n }], [{\n key: 'create',\n value: function create() {\n return _get(Bold.__proto__ || Object.getPrototypeOf(Bold), 'create', this).call(this);\n }\n }, {\n key: 'formats',\n value: function formats() {\n return true;\n }\n }]);\n\n return Bold;\n}(_inline2.default);\n\nBold.blotName = 'bold';\nBold.tagName = ['STRONG', 'B'];\n\nexports.default = Bold;\n\n/***/ }),\n/* 40 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 41 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _picker = __webpack_require__(16);\n\nvar _picker2 = _interopRequireDefault(_picker);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar ColorPicker = function (_Picker) {\n _inherits(ColorPicker, _Picker);\n\n function ColorPicker(select, label) {\n _classCallCheck(this, ColorPicker);\n\n var _this = _possibleConstructorReturn(this, (ColorPicker.__proto__ || Object.getPrototypeOf(ColorPicker)).call(this, select));\n\n _this.label.innerHTML = label;\n _this.container.classList.add('ql-color-picker');\n [].slice.call(_this.container.querySelectorAll('.ql-picker-item'), 0, 7).forEach(function (item) {\n item.classList.add('ql-primary');\n });\n return _this;\n }\n\n _createClass(ColorPicker, [{\n key: 'buildItem',\n value: function buildItem(option) {\n var item = _get(ColorPicker.prototype.__proto__ || Object.getPrototypeOf(ColorPicker.prototype), 'buildItem', this).call(this, option);\n item.style.backgroundColor = option.getAttribute('value') || '';\n return item;\n }\n }, {\n key: 'selectItem',\n value: function selectItem(item, trigger) {\n _get(ColorPicker.prototype.__proto__ || Object.getPrototypeOf(ColorPicker.prototype), 'selectItem', this).call(this, item, trigger);\n var colorLabel = this.label.querySelector('.ql-color-label');\n var value = item ? item.getAttribute('data-value') || '' : '';\n if (colorLabel) {\n if (colorLabel.tagName === 'line') {\n colorLabel.style.stroke = value;\n } else {\n colorLabel.style.fill = value;\n }\n }\n }\n }]);\n\n return ColorPicker;\n}(_picker2.default);\n\nexports.default = ColorPicker;\n\n/***/ }),\n/* 42 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _picker = __webpack_require__(16);\n\nvar _picker2 = _interopRequireDefault(_picker);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar IconPicker = function (_Picker) {\n _inherits(IconPicker, _Picker);\n\n function IconPicker(select, icons) {\n _classCallCheck(this, IconPicker);\n\n var _this = _possibleConstructorReturn(this, (IconPicker.__proto__ || Object.getPrototypeOf(IconPicker)).call(this, select));\n\n _this.container.classList.add('ql-icon-picker');\n [].forEach.call(_this.container.querySelectorAll('.ql-picker-item'), function (item) {\n item.innerHTML = icons[item.getAttribute('data-value') || ''];\n });\n _this.defaultItem = _this.container.querySelector('.ql-selected');\n _this.selectItem(_this.defaultItem);\n return _this;\n }\n\n _createClass(IconPicker, [{\n key: 'selectItem',\n value: function selectItem(item, trigger) {\n _get(IconPicker.prototype.__proto__ || Object.getPrototypeOf(IconPicker.prototype), 'selectItem', this).call(this, item, trigger);\n item = item || this.defaultItem;\n this.label.innerHTML = item.innerHTML;\n }\n }]);\n\n return IconPicker;\n}(_picker2.default);\n\nexports.default = IconPicker;\n\n/***/ }),\n/* 43 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar Tooltip = function () {\n function Tooltip(quill, boundsContainer) {\n var _this = this;\n\n _classCallCheck(this, Tooltip);\n\n this.quill = quill;\n this.boundsContainer = boundsContainer || document.body;\n this.root = quill.addContainer('ql-tooltip');\n this.root.innerHTML = this.constructor.TEMPLATE;\n if (this.quill.root === this.quill.scrollingContainer) {\n this.quill.root.addEventListener('scroll', function () {\n _this.root.style.marginTop = -1 * _this.quill.root.scrollTop + 'px';\n });\n }\n this.hide();\n }\n\n _createClass(Tooltip, [{\n key: 'hide',\n value: function hide() {\n this.root.classList.add('ql-hidden');\n }\n }, {\n key: 'position',\n value: function position(reference) {\n var left = reference.left + reference.width / 2 - this.root.offsetWidth / 2;\n // root.scrollTop should be 0 if scrollContainer !== root\n var top = reference.bottom + this.quill.root.scrollTop;\n this.root.style.left = left + 'px';\n this.root.style.top = top + 'px';\n this.root.classList.remove('ql-flip');\n var containerBounds = this.boundsContainer.getBoundingClientRect();\n var rootBounds = this.root.getBoundingClientRect();\n var shift = 0;\n if (rootBounds.right > containerBounds.right) {\n shift = containerBounds.right - rootBounds.right;\n this.root.style.left = left + shift + 'px';\n }\n if (rootBounds.left < containerBounds.left) {\n shift = containerBounds.left - rootBounds.left;\n this.root.style.left = left + shift + 'px';\n }\n if (rootBounds.bottom > containerBounds.bottom) {\n var height = rootBounds.bottom - rootBounds.top;\n var verticalShift = reference.bottom - reference.top + height;\n this.root.style.top = top - verticalShift + 'px';\n this.root.classList.add('ql-flip');\n }\n return shift;\n }\n }, {\n key: 'show',\n value: function show() {\n this.root.classList.remove('ql-editing');\n this.root.classList.remove('ql-hidden');\n }\n }]);\n\n return Tooltip;\n}();\n\nexports.default = Tooltip;\n\n/***/ }),\n/* 44 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = exports.BaseTooltip = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _extend = __webpack_require__(2);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nvar _quillDelta = __webpack_require__(4);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _emitter = __webpack_require__(9);\n\nvar _emitter2 = _interopRequireDefault(_emitter);\n\nvar _keyboard = __webpack_require__(25);\n\nvar _keyboard2 = _interopRequireDefault(_keyboard);\n\nvar _theme = __webpack_require__(32);\n\nvar _theme2 = _interopRequireDefault(_theme);\n\nvar _colorPicker = __webpack_require__(41);\n\nvar _colorPicker2 = _interopRequireDefault(_colorPicker);\n\nvar _iconPicker = __webpack_require__(42);\n\nvar _iconPicker2 = _interopRequireDefault(_iconPicker);\n\nvar _picker = __webpack_require__(16);\n\nvar _picker2 = _interopRequireDefault(_picker);\n\nvar _tooltip = __webpack_require__(43);\n\nvar _tooltip2 = _interopRequireDefault(_tooltip);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar ALIGNS = [false, 'center', 'right', 'justify'];\n\nvar COLORS = [\"#000000\", \"#e60000\", \"#ff9900\", \"#ffff00\", \"#008a00\", \"#0066cc\", \"#9933ff\", \"#ffffff\", \"#facccc\", \"#ffebcc\", \"#ffffcc\", \"#cce8cc\", \"#cce0f5\", \"#ebd6ff\", \"#bbbbbb\", \"#f06666\", \"#ffc266\", \"#ffff66\", \"#66b966\", \"#66a3e0\", \"#c285ff\", \"#888888\", \"#a10000\", \"#b26b00\", \"#b2b200\", \"#006100\", \"#0047b2\", \"#6b24b2\", \"#444444\", \"#5c0000\", \"#663d00\", \"#666600\", \"#003700\", \"#002966\", \"#3d1466\"];\n\nvar FONTS = [false, 'serif', 'monospace'];\n\nvar HEADERS = ['1', '2', '3', false];\n\nvar SIZES = ['small', false, 'large', 'huge'];\n\nvar BaseTheme = function (_Theme) {\n _inherits(BaseTheme, _Theme);\n\n function BaseTheme(quill, options) {\n _classCallCheck(this, BaseTheme);\n\n var _this = _possibleConstructorReturn(this, (BaseTheme.__proto__ || Object.getPrototypeOf(BaseTheme)).call(this, quill, options));\n\n var listener = function listener(e) {\n if (!document.body.contains(quill.root)) {\n return document.body.removeEventListener('click', listener);\n }\n if (_this.tooltip != null && !_this.tooltip.root.contains(e.target) && document.activeElement !== _this.tooltip.textbox && !_this.quill.hasFocus()) {\n _this.tooltip.hide();\n }\n if (_this.pickers != null) {\n _this.pickers.forEach(function (picker) {\n if (!picker.container.contains(e.target)) {\n picker.close();\n }\n });\n }\n };\n quill.emitter.listenDOM('click', document.body, listener);\n return _this;\n }\n\n _createClass(BaseTheme, [{\n key: 'addModule',\n value: function addModule(name) {\n var module = _get(BaseTheme.prototype.__proto__ || Object.getPrototypeOf(BaseTheme.prototype), 'addModule', this).call(this, name);\n if (name === 'toolbar') {\n this.extendToolbar(module);\n }\n return module;\n }\n }, {\n key: 'buildButtons',\n value: function buildButtons(buttons, icons) {\n buttons.forEach(function (button) {\n var className = button.getAttribute('class') || '';\n className.split(/\\s+/).forEach(function (name) {\n if (!name.startsWith('ql-')) return;\n name = name.slice('ql-'.length);\n if (icons[name] == null) return;\n if (name === 'direction') {\n button.innerHTML = icons[name][''] + icons[name]['rtl'];\n } else if (typeof icons[name] === 'string') {\n button.innerHTML = icons[name];\n } else {\n var value = button.value || '';\n if (value != null && icons[name][value]) {\n button.innerHTML = icons[name][value];\n }\n }\n });\n });\n }\n }, {\n key: 'buildPickers',\n value: function buildPickers(selects, icons) {\n var _this2 = this;\n\n this.pickers = selects.map(function (select) {\n if (select.classList.contains('ql-align')) {\n if (select.querySelector('option') == null) {\n fillSelect(select, ALIGNS);\n }\n return new _iconPicker2.default(select, icons.align);\n } else if (select.classList.contains('ql-background') || select.classList.contains('ql-color')) {\n var format = select.classList.contains('ql-background') ? 'background' : 'color';\n if (select.querySelector('option') == null) {\n fillSelect(select, COLORS, format === 'background' ? '#ffffff' : '#000000');\n }\n return new _colorPicker2.default(select, icons[format]);\n } else {\n if (select.querySelector('option') == null) {\n if (select.classList.contains('ql-font')) {\n fillSelect(select, FONTS);\n } else if (select.classList.contains('ql-header')) {\n fillSelect(select, HEADERS);\n } else if (select.classList.contains('ql-size')) {\n fillSelect(select, SIZES);\n }\n }\n return new _picker2.default(select);\n }\n });\n var update = function update() {\n _this2.pickers.forEach(function (picker) {\n picker.update();\n });\n };\n this.quill.on(_emitter2.default.events.EDITOR_CHANGE, update);\n }\n }]);\n\n return BaseTheme;\n}(_theme2.default);\n\nBaseTheme.DEFAULTS = (0, _extend2.default)(true, {}, _theme2.default.DEFAULTS, {\n modules: {\n toolbar: {\n handlers: {\n formula: function formula() {\n this.quill.theme.tooltip.edit('formula');\n },\n image: function image() {\n var _this3 = this;\n\n var fileInput = this.container.querySelector('input.ql-image[type=file]');\n if (fileInput == null) {\n fileInput = document.createElement('input');\n fileInput.setAttribute('type', 'file');\n fileInput.setAttribute('accept', 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon');\n fileInput.classList.add('ql-image');\n fileInput.addEventListener('change', function () {\n if (fileInput.files != null && fileInput.files[0] != null) {\n var reader = new FileReader();\n reader.onload = function (e) {\n var range = _this3.quill.getSelection(true);\n _this3.quill.updateContents(new _quillDelta2.default().retain(range.index).delete(range.length).insert({ image: e.target.result }), _emitter2.default.sources.USER);\n _this3.quill.setSelection(range.index + 1, _emitter2.default.sources.SILENT);\n fileInput.value = \"\";\n };\n reader.readAsDataURL(fileInput.files[0]);\n }\n });\n this.container.appendChild(fileInput);\n }\n fileInput.click();\n },\n video: function video() {\n this.quill.theme.tooltip.edit('video');\n }\n }\n }\n }\n});\n\nvar BaseTooltip = function (_Tooltip) {\n _inherits(BaseTooltip, _Tooltip);\n\n function BaseTooltip(quill, boundsContainer) {\n _classCallCheck(this, BaseTooltip);\n\n var _this4 = _possibleConstructorReturn(this, (BaseTooltip.__proto__ || Object.getPrototypeOf(BaseTooltip)).call(this, quill, boundsContainer));\n\n _this4.textbox = _this4.root.querySelector('input[type=\"text\"]');\n _this4.listen();\n return _this4;\n }\n\n _createClass(BaseTooltip, [{\n key: 'listen',\n value: function listen() {\n var _this5 = this;\n\n this.textbox.addEventListener('keydown', function (event) {\n if (_keyboard2.default.match(event, 'enter')) {\n _this5.save();\n event.preventDefault();\n } else if (_keyboard2.default.match(event, 'escape')) {\n _this5.cancel();\n event.preventDefault();\n }\n });\n }\n }, {\n key: 'cancel',\n value: function cancel() {\n this.hide();\n }\n }, {\n key: 'edit',\n value: function edit() {\n var mode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'link';\n var preview = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n\n this.root.classList.remove('ql-hidden');\n this.root.classList.add('ql-editing');\n if (preview != null) {\n this.textbox.value = preview;\n } else if (mode !== this.root.getAttribute('data-mode')) {\n this.textbox.value = '';\n }\n this.position(this.quill.getBounds(this.quill.selection.savedRange));\n this.textbox.select();\n this.textbox.setAttribute('placeholder', this.textbox.getAttribute('data-' + mode) || '');\n this.root.setAttribute('data-mode', mode);\n }\n }, {\n key: 'restoreFocus',\n value: function restoreFocus() {\n var scrollTop = this.quill.scrollingContainer.scrollTop;\n this.quill.focus();\n this.quill.scrollingContainer.scrollTop = scrollTop;\n }\n }, {\n key: 'save',\n value: function save() {\n var value = this.textbox.value;\n switch (this.root.getAttribute('data-mode')) {\n case 'link':\n {\n var scrollTop = this.quill.root.scrollTop;\n if (this.linkRange) {\n this.quill.formatText(this.linkRange, 'link', value, _emitter2.default.sources.USER);\n delete this.linkRange;\n } else {\n this.restoreFocus();\n this.quill.format('link', value, _emitter2.default.sources.USER);\n }\n this.quill.root.scrollTop = scrollTop;\n break;\n }\n case 'video':\n {\n value = extractVideoUrl(value);\n } // eslint-disable-next-line no-fallthrough\n case 'formula':\n {\n if (!value) break;\n var range = this.quill.getSelection(true);\n if (range != null) {\n var index = range.index + range.length;\n this.quill.insertEmbed(index, this.root.getAttribute('data-mode'), value, _emitter2.default.sources.USER);\n if (this.root.getAttribute('data-mode') === 'formula') {\n this.quill.insertText(index + 1, ' ', _emitter2.default.sources.USER);\n }\n this.quill.setSelection(index + 2, _emitter2.default.sources.USER);\n }\n break;\n }\n default:\n }\n this.textbox.value = '';\n this.hide();\n }\n }]);\n\n return BaseTooltip;\n}(_tooltip2.default);\n\nfunction extractVideoUrl(url) {\n var match = url.match(/^(?:(https?):\\/\\/)?(?:(?:www|m)\\.)?youtube\\.com\\/watch.*v=([a-zA-Z0-9_-]+)/) || url.match(/^(?:(https?):\\/\\/)?(?:(?:www|m)\\.)?youtu\\.be\\/([a-zA-Z0-9_-]+)/);\n if (match) {\n return (match[1] || 'https') + '://www.youtube.com/embed/' + match[2] + '?showinfo=0';\n }\n if (match = url.match(/^(?:(https?):\\/\\/)?(?:www\\.)?vimeo\\.com\\/(\\d+)/)) {\n // eslint-disable-line no-cond-assign\n return (match[1] || 'https') + '://player.vimeo.com/video/' + match[2] + '/';\n }\n return url;\n}\n\nfunction fillSelect(select, values) {\n var defaultValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n\n values.forEach(function (value) {\n var option = document.createElement('option');\n if (value === defaultValue) {\n option.setAttribute('selected', 'selected');\n } else {\n option.setAttribute('value', value);\n }\n select.appendChild(option);\n });\n}\n\nexports.BaseTooltip = BaseTooltip;\nexports.default = BaseTheme;\n\n/***/ }),\n/* 45 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _core = __webpack_require__(46);\n\nvar _core2 = _interopRequireDefault(_core);\n\nvar _align = __webpack_require__(34);\n\nvar _direction = __webpack_require__(36);\n\nvar _indent = __webpack_require__(62);\n\nvar _blockquote = __webpack_require__(63);\n\nvar _blockquote2 = _interopRequireDefault(_blockquote);\n\nvar _header = __webpack_require__(64);\n\nvar _header2 = _interopRequireDefault(_header);\n\nvar _list = __webpack_require__(65);\n\nvar _list2 = _interopRequireDefault(_list);\n\nvar _background = __webpack_require__(35);\n\nvar _color = __webpack_require__(24);\n\nvar _font = __webpack_require__(37);\n\nvar _size = __webpack_require__(38);\n\nvar _bold = __webpack_require__(39);\n\nvar _bold2 = _interopRequireDefault(_bold);\n\nvar _italic = __webpack_require__(66);\n\nvar _italic2 = _interopRequireDefault(_italic);\n\nvar _link = __webpack_require__(15);\n\nvar _link2 = _interopRequireDefault(_link);\n\nvar _script = __webpack_require__(67);\n\nvar _script2 = _interopRequireDefault(_script);\n\nvar _strike = __webpack_require__(68);\n\nvar _strike2 = _interopRequireDefault(_strike);\n\nvar _underline = __webpack_require__(69);\n\nvar _underline2 = _interopRequireDefault(_underline);\n\nvar _image = __webpack_require__(70);\n\nvar _image2 = _interopRequireDefault(_image);\n\nvar _video = __webpack_require__(71);\n\nvar _video2 = _interopRequireDefault(_video);\n\nvar _code = __webpack_require__(13);\n\nvar _code2 = _interopRequireDefault(_code);\n\nvar _formula = __webpack_require__(72);\n\nvar _formula2 = _interopRequireDefault(_formula);\n\nvar _syntax = __webpack_require__(73);\n\nvar _syntax2 = _interopRequireDefault(_syntax);\n\nvar _toolbar = __webpack_require__(74);\n\nvar _toolbar2 = _interopRequireDefault(_toolbar);\n\nvar _icons = __webpack_require__(26);\n\nvar _icons2 = _interopRequireDefault(_icons);\n\nvar _picker = __webpack_require__(16);\n\nvar _picker2 = _interopRequireDefault(_picker);\n\nvar _colorPicker = __webpack_require__(41);\n\nvar _colorPicker2 = _interopRequireDefault(_colorPicker);\n\nvar _iconPicker = __webpack_require__(42);\n\nvar _iconPicker2 = _interopRequireDefault(_iconPicker);\n\nvar _tooltip = __webpack_require__(43);\n\nvar _tooltip2 = _interopRequireDefault(_tooltip);\n\nvar _bubble = __webpack_require__(107);\n\nvar _bubble2 = _interopRequireDefault(_bubble);\n\nvar _snow = __webpack_require__(108);\n\nvar _snow2 = _interopRequireDefault(_snow);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n_core2.default.register({\n 'attributors/attribute/direction': _direction.DirectionAttribute,\n\n 'attributors/class/align': _align.AlignClass,\n 'attributors/class/background': _background.BackgroundClass,\n 'attributors/class/color': _color.ColorClass,\n 'attributors/class/direction': _direction.DirectionClass,\n 'attributors/class/font': _font.FontClass,\n 'attributors/class/size': _size.SizeClass,\n\n 'attributors/style/align': _align.AlignStyle,\n 'attributors/style/background': _background.BackgroundStyle,\n 'attributors/style/color': _color.ColorStyle,\n 'attributors/style/direction': _direction.DirectionStyle,\n 'attributors/style/font': _font.FontStyle,\n 'attributors/style/size': _size.SizeStyle\n}, true);\n\n_core2.default.register({\n 'formats/align': _align.AlignClass,\n 'formats/direction': _direction.DirectionClass,\n 'formats/indent': _indent.IndentClass,\n\n 'formats/background': _background.BackgroundStyle,\n 'formats/color': _color.ColorStyle,\n 'formats/font': _font.FontClass,\n 'formats/size': _size.SizeClass,\n\n 'formats/blockquote': _blockquote2.default,\n 'formats/code-block': _code2.default,\n 'formats/header': _header2.default,\n 'formats/list': _list2.default,\n\n 'formats/bold': _bold2.default,\n 'formats/code': _code.Code,\n 'formats/italic': _italic2.default,\n 'formats/link': _link2.default,\n 'formats/script': _script2.default,\n 'formats/strike': _strike2.default,\n 'formats/underline': _underline2.default,\n\n 'formats/image': _image2.default,\n 'formats/video': _video2.default,\n\n 'formats/list/item': _list.ListItem,\n\n 'modules/formula': _formula2.default,\n 'modules/syntax': _syntax2.default,\n 'modules/toolbar': _toolbar2.default,\n\n 'themes/bubble': _bubble2.default,\n 'themes/snow': _snow2.default,\n\n 'ui/icons': _icons2.default,\n 'ui/picker': _picker2.default,\n 'ui/icon-picker': _iconPicker2.default,\n 'ui/color-picker': _colorPicker2.default,\n 'ui/tooltip': _tooltip2.default\n}, true);\n\nexports.default = _core2.default;\n\n/***/ }),\n/* 46 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _quill = __webpack_require__(6);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _block = __webpack_require__(3);\n\nvar _block2 = _interopRequireDefault(_block);\n\nvar _break = __webpack_require__(14);\n\nvar _break2 = _interopRequireDefault(_break);\n\nvar _container = __webpack_require__(23);\n\nvar _container2 = _interopRequireDefault(_container);\n\nvar _cursor = __webpack_require__(31);\n\nvar _cursor2 = _interopRequireDefault(_cursor);\n\nvar _embed = __webpack_require__(33);\n\nvar _embed2 = _interopRequireDefault(_embed);\n\nvar _inline = __webpack_require__(5);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nvar _scroll = __webpack_require__(59);\n\nvar _scroll2 = _interopRequireDefault(_scroll);\n\nvar _text = __webpack_require__(8);\n\nvar _text2 = _interopRequireDefault(_text);\n\nvar _clipboard = __webpack_require__(60);\n\nvar _clipboard2 = _interopRequireDefault(_clipboard);\n\nvar _history = __webpack_require__(61);\n\nvar _history2 = _interopRequireDefault(_history);\n\nvar _keyboard = __webpack_require__(25);\n\nvar _keyboard2 = _interopRequireDefault(_keyboard);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n_quill2.default.register({\n 'blots/block': _block2.default,\n 'blots/block/embed': _block.BlockEmbed,\n 'blots/break': _break2.default,\n 'blots/container': _container2.default,\n 'blots/cursor': _cursor2.default,\n 'blots/embed': _embed2.default,\n 'blots/inline': _inline2.default,\n 'blots/scroll': _scroll2.default,\n 'blots/text': _text2.default,\n\n 'modules/clipboard': _clipboard2.default,\n 'modules/history': _history2.default,\n 'modules/keyboard': _keyboard2.default\n});\n\n_parchment2.default.register(_block2.default, _break2.default, _cursor2.default, _inline2.default, _scroll2.default, _text2.default);\n\nexports.default = _quill2.default;\n\n/***/ }),\n/* 47 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar LinkedList = /** @class */ (function () {\n function LinkedList() {\n this.head = this.tail = null;\n this.length = 0;\n }\n LinkedList.prototype.append = function () {\n var nodes = [];\n for (var _i = 0; _i < arguments.length; _i++) {\n nodes[_i] = arguments[_i];\n }\n this.insertBefore(nodes[0], null);\n if (nodes.length > 1) {\n this.append.apply(this, nodes.slice(1));\n }\n };\n LinkedList.prototype.contains = function (node) {\n var cur, next = this.iterator();\n while ((cur = next())) {\n if (cur === node)\n return true;\n }\n return false;\n };\n LinkedList.prototype.insertBefore = function (node, refNode) {\n if (!node)\n return;\n node.next = refNode;\n if (refNode != null) {\n node.prev = refNode.prev;\n if (refNode.prev != null) {\n refNode.prev.next = node;\n }\n refNode.prev = node;\n if (refNode === this.head) {\n this.head = node;\n }\n }\n else if (this.tail != null) {\n this.tail.next = node;\n node.prev = this.tail;\n this.tail = node;\n }\n else {\n node.prev = null;\n this.head = this.tail = node;\n }\n this.length += 1;\n };\n LinkedList.prototype.offset = function (target) {\n var index = 0, cur = this.head;\n while (cur != null) {\n if (cur === target)\n return index;\n index += cur.length();\n cur = cur.next;\n }\n return -1;\n };\n LinkedList.prototype.remove = function (node) {\n if (!this.contains(node))\n return;\n if (node.prev != null)\n node.prev.next = node.next;\n if (node.next != null)\n node.next.prev = node.prev;\n if (node === this.head)\n this.head = node.next;\n if (node === this.tail)\n this.tail = node.prev;\n this.length -= 1;\n };\n LinkedList.prototype.iterator = function (curNode) {\n if (curNode === void 0) { curNode = this.head; }\n // TODO use yield when we can\n return function () {\n var ret = curNode;\n if (curNode != null)\n curNode = curNode.next;\n return ret;\n };\n };\n LinkedList.prototype.find = function (index, inclusive) {\n if (inclusive === void 0) { inclusive = false; }\n var cur, next = this.iterator();\n while ((cur = next())) {\n var length = cur.length();\n if (index < length ||\n (inclusive && index === length && (cur.next == null || cur.next.length() !== 0))) {\n return [cur, index];\n }\n index -= length;\n }\n return [null, 0];\n };\n LinkedList.prototype.forEach = function (callback) {\n var cur, next = this.iterator();\n while ((cur = next())) {\n callback(cur);\n }\n };\n LinkedList.prototype.forEachAt = function (index, length, callback) {\n if (length <= 0)\n return;\n var _a = this.find(index), startNode = _a[0], offset = _a[1];\n var cur, curIndex = index - offset, next = this.iterator(startNode);\n while ((cur = next()) && curIndex < index + length) {\n var curLength = cur.length();\n if (index > curIndex) {\n callback(cur, index - curIndex, Math.min(length, curIndex + curLength - index));\n }\n else {\n callback(cur, 0, Math.min(curLength, index + length - curIndex));\n }\n curIndex += curLength;\n }\n };\n LinkedList.prototype.map = function (callback) {\n return this.reduce(function (memo, cur) {\n memo.push(callback(cur));\n return memo;\n }, []);\n };\n LinkedList.prototype.reduce = function (callback, memo) {\n var cur, next = this.iterator();\n while ((cur = next())) {\n memo = callback(memo, cur);\n }\n return memo;\n };\n return LinkedList;\n}());\nexports.default = LinkedList;\n\n\n/***/ }),\n/* 48 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar container_1 = __webpack_require__(17);\nvar Registry = __webpack_require__(1);\nvar OBSERVER_CONFIG = {\n attributes: true,\n characterData: true,\n characterDataOldValue: true,\n childList: true,\n subtree: true,\n};\nvar MAX_OPTIMIZE_ITERATIONS = 100;\nvar ScrollBlot = /** @class */ (function (_super) {\n __extends(ScrollBlot, _super);\n function ScrollBlot(node) {\n var _this = _super.call(this, node) || this;\n _this.scroll = _this;\n _this.observer = new MutationObserver(function (mutations) {\n _this.update(mutations);\n });\n _this.observer.observe(_this.domNode, OBSERVER_CONFIG);\n _this.attach();\n return _this;\n }\n ScrollBlot.prototype.detach = function () {\n _super.prototype.detach.call(this);\n this.observer.disconnect();\n };\n ScrollBlot.prototype.deleteAt = function (index, length) {\n this.update();\n if (index === 0 && length === this.length()) {\n this.children.forEach(function (child) {\n child.remove();\n });\n }\n else {\n _super.prototype.deleteAt.call(this, index, length);\n }\n };\n ScrollBlot.prototype.formatAt = function (index, length, name, value) {\n this.update();\n _super.prototype.formatAt.call(this, index, length, name, value);\n };\n ScrollBlot.prototype.insertAt = function (index, value, def) {\n this.update();\n _super.prototype.insertAt.call(this, index, value, def);\n };\n ScrollBlot.prototype.optimize = function (mutations, context) {\n var _this = this;\n if (mutations === void 0) { mutations = []; }\n if (context === void 0) { context = {}; }\n _super.prototype.optimize.call(this, context);\n // We must modify mutations directly, cannot make copy and then modify\n var records = [].slice.call(this.observer.takeRecords());\n // Array.push currently seems to be implemented by a non-tail recursive function\n // so we cannot just mutations.push.apply(mutations, this.observer.takeRecords());\n while (records.length > 0)\n mutations.push(records.pop());\n // TODO use WeakMap\n var mark = function (blot, markParent) {\n if (markParent === void 0) { markParent = true; }\n if (blot == null || blot === _this)\n return;\n if (blot.domNode.parentNode == null)\n return;\n // @ts-ignore\n if (blot.domNode[Registry.DATA_KEY].mutations == null) {\n // @ts-ignore\n blot.domNode[Registry.DATA_KEY].mutations = [];\n }\n if (markParent)\n mark(blot.parent);\n };\n var optimize = function (blot) {\n // Post-order traversal\n if (\n // @ts-ignore\n blot.domNode[Registry.DATA_KEY] == null ||\n // @ts-ignore\n blot.domNode[Registry.DATA_KEY].mutations == null) {\n return;\n }\n if (blot instanceof container_1.default) {\n blot.children.forEach(optimize);\n }\n blot.optimize(context);\n };\n var remaining = mutations;\n for (var i = 0; remaining.length > 0; i += 1) {\n if (i >= MAX_OPTIMIZE_ITERATIONS) {\n throw new Error('[Parchment] Maximum optimize iterations reached');\n }\n remaining.forEach(function (mutation) {\n var blot = Registry.find(mutation.target, true);\n if (blot == null)\n return;\n if (blot.domNode === mutation.target) {\n if (mutation.type === 'childList') {\n mark(Registry.find(mutation.previousSibling, false));\n [].forEach.call(mutation.addedNodes, function (node) {\n var child = Registry.find(node, false);\n mark(child, false);\n if (child instanceof container_1.default) {\n child.children.forEach(function (grandChild) {\n mark(grandChild, false);\n });\n }\n });\n }\n else if (mutation.type === 'attributes') {\n mark(blot.prev);\n }\n }\n mark(blot);\n });\n this.children.forEach(optimize);\n remaining = [].slice.call(this.observer.takeRecords());\n records = remaining.slice();\n while (records.length > 0)\n mutations.push(records.pop());\n }\n };\n ScrollBlot.prototype.update = function (mutations, context) {\n var _this = this;\n if (context === void 0) { context = {}; }\n mutations = mutations || this.observer.takeRecords();\n // TODO use WeakMap\n mutations\n .map(function (mutation) {\n var blot = Registry.find(mutation.target, true);\n if (blot == null)\n return null;\n // @ts-ignore\n if (blot.domNode[Registry.DATA_KEY].mutations == null) {\n // @ts-ignore\n blot.domNode[Registry.DATA_KEY].mutations = [mutation];\n return blot;\n }\n else {\n // @ts-ignore\n blot.domNode[Registry.DATA_KEY].mutations.push(mutation);\n return null;\n }\n })\n .forEach(function (blot) {\n if (blot == null ||\n blot === _this ||\n //@ts-ignore\n blot.domNode[Registry.DATA_KEY] == null)\n return;\n // @ts-ignore\n blot.update(blot.domNode[Registry.DATA_KEY].mutations || [], context);\n });\n // @ts-ignore\n if (this.domNode[Registry.DATA_KEY].mutations != null) {\n // @ts-ignore\n _super.prototype.update.call(this, this.domNode[Registry.DATA_KEY].mutations, context);\n }\n this.optimize(mutations, context);\n };\n ScrollBlot.blotName = 'scroll';\n ScrollBlot.defaultChild = 'block';\n ScrollBlot.scope = Registry.Scope.BLOCK_BLOT;\n ScrollBlot.tagName = 'DIV';\n return ScrollBlot;\n}(container_1.default));\nexports.default = ScrollBlot;\n\n\n/***/ }),\n/* 49 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar format_1 = __webpack_require__(18);\nvar Registry = __webpack_require__(1);\n// Shallow object comparison\nfunction isEqual(obj1, obj2) {\n if (Object.keys(obj1).length !== Object.keys(obj2).length)\n return false;\n // @ts-ignore\n for (var prop in obj1) {\n // @ts-ignore\n if (obj1[prop] !== obj2[prop])\n return false;\n }\n return true;\n}\nvar InlineBlot = /** @class */ (function (_super) {\n __extends(InlineBlot, _super);\n function InlineBlot() {\n return _super !== null && _super.apply(this, arguments) || this;\n }\n InlineBlot.formats = function (domNode) {\n if (domNode.tagName === InlineBlot.tagName)\n return undefined;\n return _super.formats.call(this, domNode);\n };\n InlineBlot.prototype.format = function (name, value) {\n var _this = this;\n if (name === this.statics.blotName && !value) {\n this.children.forEach(function (child) {\n if (!(child instanceof format_1.default)) {\n child = child.wrap(InlineBlot.blotName, true);\n }\n _this.attributes.copy(child);\n });\n this.unwrap();\n }\n else {\n _super.prototype.format.call(this, name, value);\n }\n };\n InlineBlot.prototype.formatAt = function (index, length, name, value) {\n if (this.formats()[name] != null || Registry.query(name, Registry.Scope.ATTRIBUTE)) {\n var blot = this.isolate(index, length);\n blot.format(name, value);\n }\n else {\n _super.prototype.formatAt.call(this, index, length, name, value);\n }\n };\n InlineBlot.prototype.optimize = function (context) {\n _super.prototype.optimize.call(this, context);\n var formats = this.formats();\n if (Object.keys(formats).length === 0) {\n return this.unwrap(); // unformatted span\n }\n var next = this.next;\n if (next instanceof InlineBlot && next.prev === this && isEqual(formats, next.formats())) {\n next.moveChildren(this);\n next.remove();\n }\n };\n InlineBlot.blotName = 'inline';\n InlineBlot.scope = Registry.Scope.INLINE_BLOT;\n InlineBlot.tagName = 'SPAN';\n return InlineBlot;\n}(format_1.default));\nexports.default = InlineBlot;\n\n\n/***/ }),\n/* 50 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar format_1 = __webpack_require__(18);\nvar Registry = __webpack_require__(1);\nvar BlockBlot = /** @class */ (function (_super) {\n __extends(BlockBlot, _super);\n function BlockBlot() {\n return _super !== null && _super.apply(this, arguments) || this;\n }\n BlockBlot.formats = function (domNode) {\n var tagName = Registry.query(BlockBlot.blotName).tagName;\n if (domNode.tagName === tagName)\n return undefined;\n return _super.formats.call(this, domNode);\n };\n BlockBlot.prototype.format = function (name, value) {\n if (Registry.query(name, Registry.Scope.BLOCK) == null) {\n return;\n }\n else if (name === this.statics.blotName && !value) {\n this.replaceWith(BlockBlot.blotName);\n }\n else {\n _super.prototype.format.call(this, name, value);\n }\n };\n BlockBlot.prototype.formatAt = function (index, length, name, value) {\n if (Registry.query(name, Registry.Scope.BLOCK) != null) {\n this.format(name, value);\n }\n else {\n _super.prototype.formatAt.call(this, index, length, name, value);\n }\n };\n BlockBlot.prototype.insertAt = function (index, value, def) {\n if (def == null || Registry.query(value, Registry.Scope.INLINE) != null) {\n // Insert text or inline\n _super.prototype.insertAt.call(this, index, value, def);\n }\n else {\n var after = this.split(index);\n var blot = Registry.create(value, def);\n after.parent.insertBefore(blot, after);\n }\n };\n BlockBlot.prototype.update = function (mutations, context) {\n if (navigator.userAgent.match(/Trident/)) {\n this.build();\n }\n else {\n _super.prototype.update.call(this, mutations, context);\n }\n };\n BlockBlot.blotName = 'block';\n BlockBlot.scope = Registry.Scope.BLOCK_BLOT;\n BlockBlot.tagName = 'P';\n return BlockBlot;\n}(format_1.default));\nexports.default = BlockBlot;\n\n\n/***/ }),\n/* 51 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar leaf_1 = __webpack_require__(19);\nvar EmbedBlot = /** @class */ (function (_super) {\n __extends(EmbedBlot, _super);\n function EmbedBlot() {\n return _super !== null && _super.apply(this, arguments) || this;\n }\n EmbedBlot.formats = function (domNode) {\n return undefined;\n };\n EmbedBlot.prototype.format = function (name, value) {\n // super.formatAt wraps, which is what we want in general,\n // but this allows subclasses to overwrite for formats\n // that just apply to particular embeds\n _super.prototype.formatAt.call(this, 0, this.length(), name, value);\n };\n EmbedBlot.prototype.formatAt = function (index, length, name, value) {\n if (index === 0 && length === this.length()) {\n this.format(name, value);\n }\n else {\n _super.prototype.formatAt.call(this, index, length, name, value);\n }\n };\n EmbedBlot.prototype.formats = function () {\n return this.statics.formats(this.domNode);\n };\n return EmbedBlot;\n}(leaf_1.default));\nexports.default = EmbedBlot;\n\n\n/***/ }),\n/* 52 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar leaf_1 = __webpack_require__(19);\nvar Registry = __webpack_require__(1);\nvar TextBlot = /** @class */ (function (_super) {\n __extends(TextBlot, _super);\n function TextBlot(node) {\n var _this = _super.call(this, node) || this;\n _this.text = _this.statics.value(_this.domNode);\n return _this;\n }\n TextBlot.create = function (value) {\n return document.createTextNode(value);\n };\n TextBlot.value = function (domNode) {\n var text = domNode.data;\n // @ts-ignore\n if (text['normalize'])\n text = text['normalize']();\n return text;\n };\n TextBlot.prototype.deleteAt = function (index, length) {\n this.domNode.data = this.text = this.text.slice(0, index) + this.text.slice(index + length);\n };\n TextBlot.prototype.index = function (node, offset) {\n if (this.domNode === node) {\n return offset;\n }\n return -1;\n };\n TextBlot.prototype.insertAt = function (index, value, def) {\n if (def == null) {\n this.text = this.text.slice(0, index) + value + this.text.slice(index);\n this.domNode.data = this.text;\n }\n else {\n _super.prototype.insertAt.call(this, index, value, def);\n }\n };\n TextBlot.prototype.length = function () {\n return this.text.length;\n };\n TextBlot.prototype.optimize = function (context) {\n _super.prototype.optimize.call(this, context);\n this.text = this.statics.value(this.domNode);\n if (this.text.length === 0) {\n this.remove();\n }\n else if (this.next instanceof TextBlot && this.next.prev === this) {\n this.insertAt(this.length(), this.next.value());\n this.next.remove();\n }\n };\n TextBlot.prototype.position = function (index, inclusive) {\n if (inclusive === void 0) { inclusive = false; }\n return [this.domNode, index];\n };\n TextBlot.prototype.split = function (index, force) {\n if (force === void 0) { force = false; }\n if (!force) {\n if (index === 0)\n return this;\n if (index === this.length())\n return this.next;\n }\n var after = Registry.create(this.domNode.splitText(index));\n this.parent.insertBefore(after, this.next);\n this.text = this.statics.value(this.domNode);\n return after;\n };\n TextBlot.prototype.update = function (mutations, context) {\n var _this = this;\n if (mutations.some(function (mutation) {\n return mutation.type === 'characterData' && mutation.target === _this.domNode;\n })) {\n this.text = this.statics.value(this.domNode);\n }\n };\n TextBlot.prototype.value = function () {\n return this.text;\n };\n TextBlot.blotName = 'text';\n TextBlot.scope = Registry.Scope.INLINE_BLOT;\n return TextBlot;\n}(leaf_1.default));\nexports.default = TextBlot;\n\n\n/***/ }),\n/* 53 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar elem = document.createElement('div');\nelem.classList.toggle('test-class', false);\nif (elem.classList.contains('test-class')) {\n var _toggle = DOMTokenList.prototype.toggle;\n DOMTokenList.prototype.toggle = function (token, force) {\n if (arguments.length > 1 && !this.contains(token) === !force) {\n return force;\n } else {\n return _toggle.call(this, token);\n }\n };\n}\n\nif (!String.prototype.startsWith) {\n String.prototype.startsWith = function (searchString, position) {\n position = position || 0;\n return this.substr(position, searchString.length) === searchString;\n };\n}\n\nif (!String.prototype.endsWith) {\n String.prototype.endsWith = function (searchString, position) {\n var subjectString = this.toString();\n if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {\n position = subjectString.length;\n }\n position -= searchString.length;\n var lastIndex = subjectString.indexOf(searchString, position);\n return lastIndex !== -1 && lastIndex === position;\n };\n}\n\nif (!Array.prototype.find) {\n Object.defineProperty(Array.prototype, \"find\", {\n value: function value(predicate) {\n if (this === null) {\n throw new TypeError('Array.prototype.find called on null or undefined');\n }\n if (typeof predicate !== 'function') {\n throw new TypeError('predicate must be a function');\n }\n var list = Object(this);\n var length = list.length >>> 0;\n var thisArg = arguments[1];\n var value;\n\n for (var i = 0; i < length; i++) {\n value = list[i];\n if (predicate.call(thisArg, value, i, list)) {\n return value;\n }\n }\n return undefined;\n }\n });\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", function () {\n // Disable resizing in Firefox\n document.execCommand(\"enableObjectResizing\", false, false);\n // Disable automatic linkifying in IE11\n document.execCommand(\"autoUrlDetect\", false, false);\n});\n\n/***/ }),\n/* 54 */\n/***/ (function(module, exports) {\n\n/**\n * This library modifies the diff-patch-match library by Neil Fraser\n * by removing the patch and match functionality and certain advanced\n * options in the diff function. The original license is as follows:\n *\n * ===\n *\n * Diff Match and Patch\n *\n * Copyright 2006 Google Inc.\n * http://code.google.com/p/google-diff-match-patch/\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\n/**\n * The data structure representing a diff is an array of tuples:\n * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]\n * which means: delete 'Hello', add 'Goodbye' and keep ' world.'\n */\nvar DIFF_DELETE = -1;\nvar DIFF_INSERT = 1;\nvar DIFF_EQUAL = 0;\n\n\n/**\n * Find the differences between two texts. Simplifies the problem by stripping\n * any common prefix or suffix off the texts before diffing.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @param {Int} cursor_pos Expected edit position in text1 (optional)\n * @return {Array} Array of diff tuples.\n */\nfunction diff_main(text1, text2, cursor_pos) {\n // Check for equality (speedup).\n if (text1 == text2) {\n if (text1) {\n return [[DIFF_EQUAL, text1]];\n }\n return [];\n }\n\n // Check cursor_pos within bounds\n if (cursor_pos < 0 || text1.length < cursor_pos) {\n cursor_pos = null;\n }\n\n // Trim off common prefix (speedup).\n var commonlength = diff_commonPrefix(text1, text2);\n var commonprefix = text1.substring(0, commonlength);\n text1 = text1.substring(commonlength);\n text2 = text2.substring(commonlength);\n\n // Trim off common suffix (speedup).\n commonlength = diff_commonSuffix(text1, text2);\n var commonsuffix = text1.substring(text1.length - commonlength);\n text1 = text1.substring(0, text1.length - commonlength);\n text2 = text2.substring(0, text2.length - commonlength);\n\n // Compute the diff on the middle block.\n var diffs = diff_compute_(text1, text2);\n\n // Restore the prefix and suffix.\n if (commonprefix) {\n diffs.unshift([DIFF_EQUAL, commonprefix]);\n }\n if (commonsuffix) {\n diffs.push([DIFF_EQUAL, commonsuffix]);\n }\n diff_cleanupMerge(diffs);\n if (cursor_pos != null) {\n diffs = fix_cursor(diffs, cursor_pos);\n }\n diffs = fix_emoji(diffs);\n return diffs;\n};\n\n\n/**\n * Find the differences between two texts. Assumes that the texts do not\n * have any common prefix or suffix.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @return {Array} Array of diff tuples.\n */\nfunction diff_compute_(text1, text2) {\n var diffs;\n\n if (!text1) {\n // Just add some text (speedup).\n return [[DIFF_INSERT, text2]];\n }\n\n if (!text2) {\n // Just delete some text (speedup).\n return [[DIFF_DELETE, text1]];\n }\n\n var longtext = text1.length > text2.length ? text1 : text2;\n var shorttext = text1.length > text2.length ? text2 : text1;\n var i = longtext.indexOf(shorttext);\n if (i != -1) {\n // Shorter text is inside the longer text (speedup).\n diffs = [[DIFF_INSERT, longtext.substring(0, i)],\n [DIFF_EQUAL, shorttext],\n [DIFF_INSERT, longtext.substring(i + shorttext.length)]];\n // Swap insertions for deletions if diff is reversed.\n if (text1.length > text2.length) {\n diffs[0][0] = diffs[2][0] = DIFF_DELETE;\n }\n return diffs;\n }\n\n if (shorttext.length == 1) {\n // Single character string.\n // After the previous speedup, the character can't be an equality.\n return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];\n }\n\n // Check to see if the problem can be split in two.\n var hm = diff_halfMatch_(text1, text2);\n if (hm) {\n // A half-match was found, sort out the return data.\n var text1_a = hm[0];\n var text1_b = hm[1];\n var text2_a = hm[2];\n var text2_b = hm[3];\n var mid_common = hm[4];\n // Send both pairs off for separate processing.\n var diffs_a = diff_main(text1_a, text2_a);\n var diffs_b = diff_main(text1_b, text2_b);\n // Merge the results.\n return diffs_a.concat([[DIFF_EQUAL, mid_common]], diffs_b);\n }\n\n return diff_bisect_(text1, text2);\n};\n\n\n/**\n * Find the 'middle snake' of a diff, split the problem in two\n * and return the recursively constructed diff.\n * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @return {Array} Array of diff tuples.\n * @private\n */\nfunction diff_bisect_(text1, text2) {\n // Cache the text lengths to prevent multiple calls.\n var text1_length = text1.length;\n var text2_length = text2.length;\n var max_d = Math.ceil((text1_length + text2_length) / 2);\n var v_offset = max_d;\n var v_length = 2 * max_d;\n var v1 = new Array(v_length);\n var v2 = new Array(v_length);\n // Setting all elements to -1 is faster in Chrome & Firefox than mixing\n // integers and undefined.\n for (var x = 0; x < v_length; x++) {\n v1[x] = -1;\n v2[x] = -1;\n }\n v1[v_offset + 1] = 0;\n v2[v_offset + 1] = 0;\n var delta = text1_length - text2_length;\n // If the total number of characters is odd, then the front path will collide\n // with the reverse path.\n var front = (delta % 2 != 0);\n // Offsets for start and end of k loop.\n // Prevents mapping of space beyond the grid.\n var k1start = 0;\n var k1end = 0;\n var k2start = 0;\n var k2end = 0;\n for (var d = 0; d < max_d; d++) {\n // Walk the front path one step.\n for (var k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {\n var k1_offset = v_offset + k1;\n var x1;\n if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) {\n x1 = v1[k1_offset + 1];\n } else {\n x1 = v1[k1_offset - 1] + 1;\n }\n var y1 = x1 - k1;\n while (x1 < text1_length && y1 < text2_length &&\n text1.charAt(x1) == text2.charAt(y1)) {\n x1++;\n y1++;\n }\n v1[k1_offset] = x1;\n if (x1 > text1_length) {\n // Ran off the right of the graph.\n k1end += 2;\n } else if (y1 > text2_length) {\n // Ran off the bottom of the graph.\n k1start += 2;\n } else if (front) {\n var k2_offset = v_offset + delta - k1;\n if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) {\n // Mirror x2 onto top-left coordinate system.\n var x2 = text1_length - v2[k2_offset];\n if (x1 >= x2) {\n // Overlap detected.\n return diff_bisectSplit_(text1, text2, x1, y1);\n }\n }\n }\n }\n\n // Walk the reverse path one step.\n for (var k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {\n var k2_offset = v_offset + k2;\n var x2;\n if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) {\n x2 = v2[k2_offset + 1];\n } else {\n x2 = v2[k2_offset - 1] + 1;\n }\n var y2 = x2 - k2;\n while (x2 < text1_length && y2 < text2_length &&\n text1.charAt(text1_length - x2 - 1) ==\n text2.charAt(text2_length - y2 - 1)) {\n x2++;\n y2++;\n }\n v2[k2_offset] = x2;\n if (x2 > text1_length) {\n // Ran off the left of the graph.\n k2end += 2;\n } else if (y2 > text2_length) {\n // Ran off the top of the graph.\n k2start += 2;\n } else if (!front) {\n var k1_offset = v_offset + delta - k2;\n if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) {\n var x1 = v1[k1_offset];\n var y1 = v_offset + x1 - k1_offset;\n // Mirror x2 onto top-left coordinate system.\n x2 = text1_length - x2;\n if (x1 >= x2) {\n // Overlap detected.\n return diff_bisectSplit_(text1, text2, x1, y1);\n }\n }\n }\n }\n }\n // Diff took too long and hit the deadline or\n // number of diffs equals number of characters, no commonality at all.\n return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];\n};\n\n\n/**\n * Given the location of the 'middle snake', split the diff in two parts\n * and recurse.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @param {number} x Index of split point in text1.\n * @param {number} y Index of split point in text2.\n * @return {Array} Array of diff tuples.\n */\nfunction diff_bisectSplit_(text1, text2, x, y) {\n var text1a = text1.substring(0, x);\n var text2a = text2.substring(0, y);\n var text1b = text1.substring(x);\n var text2b = text2.substring(y);\n\n // Compute both diffs serially.\n var diffs = diff_main(text1a, text2a);\n var diffsb = diff_main(text1b, text2b);\n\n return diffs.concat(diffsb);\n};\n\n\n/**\n * Determine the common prefix of two strings.\n * @param {string} text1 First string.\n * @param {string} text2 Second string.\n * @return {number} The number of characters common to the start of each\n * string.\n */\nfunction diff_commonPrefix(text1, text2) {\n // Quick check for common null cases.\n if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) {\n return 0;\n }\n // Binary search.\n // Performance analysis: http://neil.fraser.name/news/2007/10/09/\n var pointermin = 0;\n var pointermax = Math.min(text1.length, text2.length);\n var pointermid = pointermax;\n var pointerstart = 0;\n while (pointermin < pointermid) {\n if (text1.substring(pointerstart, pointermid) ==\n text2.substring(pointerstart, pointermid)) {\n pointermin = pointermid;\n pointerstart = pointermin;\n } else {\n pointermax = pointermid;\n }\n pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);\n }\n return pointermid;\n};\n\n\n/**\n * Determine the common suffix of two strings.\n * @param {string} text1 First string.\n * @param {string} text2 Second string.\n * @return {number} The number of characters common to the end of each string.\n */\nfunction diff_commonSuffix(text1, text2) {\n // Quick check for common null cases.\n if (!text1 || !text2 ||\n text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) {\n return 0;\n }\n // Binary search.\n // Performance analysis: http://neil.fraser.name/news/2007/10/09/\n var pointermin = 0;\n var pointermax = Math.min(text1.length, text2.length);\n var pointermid = pointermax;\n var pointerend = 0;\n while (pointermin < pointermid) {\n if (text1.substring(text1.length - pointermid, text1.length - pointerend) ==\n text2.substring(text2.length - pointermid, text2.length - pointerend)) {\n pointermin = pointermid;\n pointerend = pointermin;\n } else {\n pointermax = pointermid;\n }\n pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);\n }\n return pointermid;\n};\n\n\n/**\n * Do the two texts share a substring which is at least half the length of the\n * longer text?\n * This speedup can produce non-minimal diffs.\n * @param {string} text1 First string.\n * @param {string} text2 Second string.\n * @return {Array.} Five element Array, containing the prefix of\n * text1, the suffix of text1, the prefix of text2, the suffix of\n * text2 and the common middle. Or null if there was no match.\n */\nfunction diff_halfMatch_(text1, text2) {\n var longtext = text1.length > text2.length ? text1 : text2;\n var shorttext = text1.length > text2.length ? text2 : text1;\n if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {\n return null; // Pointless.\n }\n\n /**\n * Does a substring of shorttext exist within longtext such that the substring\n * is at least half the length of longtext?\n * Closure, but does not reference any external variables.\n * @param {string} longtext Longer string.\n * @param {string} shorttext Shorter string.\n * @param {number} i Start index of quarter length substring within longtext.\n * @return {Array.} Five element Array, containing the prefix of\n * longtext, the suffix of longtext, the prefix of shorttext, the suffix\n * of shorttext and the common middle. Or null if there was no match.\n * @private\n */\n function diff_halfMatchI_(longtext, shorttext, i) {\n // Start with a 1/4 length substring at position i as a seed.\n var seed = longtext.substring(i, i + Math.floor(longtext.length / 4));\n var j = -1;\n var best_common = '';\n var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b;\n while ((j = shorttext.indexOf(seed, j + 1)) != -1) {\n var prefixLength = diff_commonPrefix(longtext.substring(i),\n shorttext.substring(j));\n var suffixLength = diff_commonSuffix(longtext.substring(0, i),\n shorttext.substring(0, j));\n if (best_common.length < suffixLength + prefixLength) {\n best_common = shorttext.substring(j - suffixLength, j) +\n shorttext.substring(j, j + prefixLength);\n best_longtext_a = longtext.substring(0, i - suffixLength);\n best_longtext_b = longtext.substring(i + prefixLength);\n best_shorttext_a = shorttext.substring(0, j - suffixLength);\n best_shorttext_b = shorttext.substring(j + prefixLength);\n }\n }\n if (best_common.length * 2 >= longtext.length) {\n return [best_longtext_a, best_longtext_b,\n best_shorttext_a, best_shorttext_b, best_common];\n } else {\n return null;\n }\n }\n\n // First check if the second quarter is the seed for a half-match.\n var hm1 = diff_halfMatchI_(longtext, shorttext,\n Math.ceil(longtext.length / 4));\n // Check again based on the third quarter.\n var hm2 = diff_halfMatchI_(longtext, shorttext,\n Math.ceil(longtext.length / 2));\n var hm;\n if (!hm1 && !hm2) {\n return null;\n } else if (!hm2) {\n hm = hm1;\n } else if (!hm1) {\n hm = hm2;\n } else {\n // Both matched. Select the longest.\n hm = hm1[4].length > hm2[4].length ? hm1 : hm2;\n }\n\n // A half-match was found, sort out the return data.\n var text1_a, text1_b, text2_a, text2_b;\n if (text1.length > text2.length) {\n text1_a = hm[0];\n text1_b = hm[1];\n text2_a = hm[2];\n text2_b = hm[3];\n } else {\n text2_a = hm[0];\n text2_b = hm[1];\n text1_a = hm[2];\n text1_b = hm[3];\n }\n var mid_common = hm[4];\n return [text1_a, text1_b, text2_a, text2_b, mid_common];\n};\n\n\n/**\n * Reorder and merge like edit sections. Merge equalities.\n * Any edit section can move as long as it doesn't cross an equality.\n * @param {Array} diffs Array of diff tuples.\n */\nfunction diff_cleanupMerge(diffs) {\n diffs.push([DIFF_EQUAL, '']); // Add a dummy entry at the end.\n var pointer = 0;\n var count_delete = 0;\n var count_insert = 0;\n var text_delete = '';\n var text_insert = '';\n var commonlength;\n while (pointer < diffs.length) {\n switch (diffs[pointer][0]) {\n case DIFF_INSERT:\n count_insert++;\n text_insert += diffs[pointer][1];\n pointer++;\n break;\n case DIFF_DELETE:\n count_delete++;\n text_delete += diffs[pointer][1];\n pointer++;\n break;\n case DIFF_EQUAL:\n // Upon reaching an equality, check for prior redundancies.\n if (count_delete + count_insert > 1) {\n if (count_delete !== 0 && count_insert !== 0) {\n // Factor out any common prefixies.\n commonlength = diff_commonPrefix(text_insert, text_delete);\n if (commonlength !== 0) {\n if ((pointer - count_delete - count_insert) > 0 &&\n diffs[pointer - count_delete - count_insert - 1][0] ==\n DIFF_EQUAL) {\n diffs[pointer - count_delete - count_insert - 1][1] +=\n text_insert.substring(0, commonlength);\n } else {\n diffs.splice(0, 0, [DIFF_EQUAL,\n text_insert.substring(0, commonlength)]);\n pointer++;\n }\n text_insert = text_insert.substring(commonlength);\n text_delete = text_delete.substring(commonlength);\n }\n // Factor out any common suffixies.\n commonlength = diff_commonSuffix(text_insert, text_delete);\n if (commonlength !== 0) {\n diffs[pointer][1] = text_insert.substring(text_insert.length -\n commonlength) + diffs[pointer][1];\n text_insert = text_insert.substring(0, text_insert.length -\n commonlength);\n text_delete = text_delete.substring(0, text_delete.length -\n commonlength);\n }\n }\n // Delete the offending records and add the merged ones.\n if (count_delete === 0) {\n diffs.splice(pointer - count_insert,\n count_delete + count_insert, [DIFF_INSERT, text_insert]);\n } else if (count_insert === 0) {\n diffs.splice(pointer - count_delete,\n count_delete + count_insert, [DIFF_DELETE, text_delete]);\n } else {\n diffs.splice(pointer - count_delete - count_insert,\n count_delete + count_insert, [DIFF_DELETE, text_delete],\n [DIFF_INSERT, text_insert]);\n }\n pointer = pointer - count_delete - count_insert +\n (count_delete ? 1 : 0) + (count_insert ? 1 : 0) + 1;\n } else if (pointer !== 0 && diffs[pointer - 1][0] == DIFF_EQUAL) {\n // Merge this equality with the previous one.\n diffs[pointer - 1][1] += diffs[pointer][1];\n diffs.splice(pointer, 1);\n } else {\n pointer++;\n }\n count_insert = 0;\n count_delete = 0;\n text_delete = '';\n text_insert = '';\n break;\n }\n }\n if (diffs[diffs.length - 1][1] === '') {\n diffs.pop(); // Remove the dummy entry at the end.\n }\n\n // Second pass: look for single edits surrounded on both sides by equalities\n // which can be shifted sideways to eliminate an equality.\n // e.g: ABAC -> ABAC\n var changes = false;\n pointer = 1;\n // Intentionally ignore the first and last element (don't need checking).\n while (pointer < diffs.length - 1) {\n if (diffs[pointer - 1][0] == DIFF_EQUAL &&\n diffs[pointer + 1][0] == DIFF_EQUAL) {\n // This is a single edit surrounded by equalities.\n if (diffs[pointer][1].substring(diffs[pointer][1].length -\n diffs[pointer - 1][1].length) == diffs[pointer - 1][1]) {\n // Shift the edit over the previous equality.\n diffs[pointer][1] = diffs[pointer - 1][1] +\n diffs[pointer][1].substring(0, diffs[pointer][1].length -\n diffs[pointer - 1][1].length);\n diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];\n diffs.splice(pointer - 1, 1);\n changes = true;\n } else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) ==\n diffs[pointer + 1][1]) {\n // Shift the edit over the next equality.\n diffs[pointer - 1][1] += diffs[pointer + 1][1];\n diffs[pointer][1] =\n diffs[pointer][1].substring(diffs[pointer + 1][1].length) +\n diffs[pointer + 1][1];\n diffs.splice(pointer + 1, 1);\n changes = true;\n }\n }\n pointer++;\n }\n // If shifts were made, the diff needs reordering and another shift sweep.\n if (changes) {\n diff_cleanupMerge(diffs);\n }\n};\n\n\nvar diff = diff_main;\ndiff.INSERT = DIFF_INSERT;\ndiff.DELETE = DIFF_DELETE;\ndiff.EQUAL = DIFF_EQUAL;\n\nmodule.exports = diff;\n\n/*\n * Modify a diff such that the cursor position points to the start of a change:\n * E.g.\n * cursor_normalize_diff([[DIFF_EQUAL, 'abc']], 1)\n * => [1, [[DIFF_EQUAL, 'a'], [DIFF_EQUAL, 'bc']]]\n * cursor_normalize_diff([[DIFF_INSERT, 'new'], [DIFF_DELETE, 'xyz']], 2)\n * => [2, [[DIFF_INSERT, 'new'], [DIFF_DELETE, 'xy'], [DIFF_DELETE, 'z']]]\n *\n * @param {Array} diffs Array of diff tuples\n * @param {Int} cursor_pos Suggested edit position. Must not be out of bounds!\n * @return {Array} A tuple [cursor location in the modified diff, modified diff]\n */\nfunction cursor_normalize_diff (diffs, cursor_pos) {\n if (cursor_pos === 0) {\n return [DIFF_EQUAL, diffs];\n }\n for (var current_pos = 0, i = 0; i < diffs.length; i++) {\n var d = diffs[i];\n if (d[0] === DIFF_DELETE || d[0] === DIFF_EQUAL) {\n var next_pos = current_pos + d[1].length;\n if (cursor_pos === next_pos) {\n return [i + 1, diffs];\n } else if (cursor_pos < next_pos) {\n // copy to prevent side effects\n diffs = diffs.slice();\n // split d into two diff changes\n var split_pos = cursor_pos - current_pos;\n var d_left = [d[0], d[1].slice(0, split_pos)];\n var d_right = [d[0], d[1].slice(split_pos)];\n diffs.splice(i, 1, d_left, d_right);\n return [i + 1, diffs];\n } else {\n current_pos = next_pos;\n }\n }\n }\n throw new Error('cursor_pos is out of bounds!')\n}\n\n/*\n * Modify a diff such that the edit position is \"shifted\" to the proposed edit location (cursor_position).\n *\n * Case 1)\n * Check if a naive shift is possible:\n * [0, X], [ 1, Y] -> [ 1, Y], [0, X] (if X + Y === Y + X)\n * [0, X], [-1, Y] -> [-1, Y], [0, X] (if X + Y === Y + X) - holds same result\n * Case 2)\n * Check if the following shifts are possible:\n * [0, 'pre'], [ 1, 'prefix'] -> [ 1, 'pre'], [0, 'pre'], [ 1, 'fix']\n * [0, 'pre'], [-1, 'prefix'] -> [-1, 'pre'], [0, 'pre'], [-1, 'fix']\n * ^ ^\n * d d_next\n *\n * @param {Array} diffs Array of diff tuples\n * @param {Int} cursor_pos Suggested edit position. Must not be out of bounds!\n * @return {Array} Array of diff tuples\n */\nfunction fix_cursor (diffs, cursor_pos) {\n var norm = cursor_normalize_diff(diffs, cursor_pos);\n var ndiffs = norm[1];\n var cursor_pointer = norm[0];\n var d = ndiffs[cursor_pointer];\n var d_next = ndiffs[cursor_pointer + 1];\n\n if (d == null) {\n // Text was deleted from end of original string,\n // cursor is now out of bounds in new string\n return diffs;\n } else if (d[0] !== DIFF_EQUAL) {\n // A modification happened at the cursor location.\n // This is the expected outcome, so we can return the original diff.\n return diffs;\n } else {\n if (d_next != null && d[1] + d_next[1] === d_next[1] + d[1]) {\n // Case 1)\n // It is possible to perform a naive shift\n ndiffs.splice(cursor_pointer, 2, d_next, d)\n return merge_tuples(ndiffs, cursor_pointer, 2)\n } else if (d_next != null && d_next[1].indexOf(d[1]) === 0) {\n // Case 2)\n // d[1] is a prefix of d_next[1]\n // We can assume that d_next[0] !== 0, since d[0] === 0\n // Shift edit locations..\n ndiffs.splice(cursor_pointer, 2, [d_next[0], d[1]], [0, d[1]]);\n var suffix = d_next[1].slice(d[1].length);\n if (suffix.length > 0) {\n ndiffs.splice(cursor_pointer + 2, 0, [d_next[0], suffix]);\n }\n return merge_tuples(ndiffs, cursor_pointer, 3)\n } else {\n // Not possible to perform any modification\n return diffs;\n }\n }\n}\n\n/*\n * Check diff did not split surrogate pairs.\n * Ex. [0, '\\uD83D'], [-1, '\\uDC36'], [1, '\\uDC2F'] -> [-1, '\\uD83D\\uDC36'], [1, '\\uD83D\\uDC2F']\n * '\\uD83D\\uDC36' === '🐶', '\\uD83D\\uDC2F' === '🐯'\n *\n * @param {Array} diffs Array of diff tuples\n * @return {Array} Array of diff tuples\n */\nfunction fix_emoji (diffs) {\n var compact = false;\n var starts_with_pair_end = function(str) {\n return str.charCodeAt(0) >= 0xDC00 && str.charCodeAt(0) <= 0xDFFF;\n }\n var ends_with_pair_start = function(str) {\n return str.charCodeAt(str.length-1) >= 0xD800 && str.charCodeAt(str.length-1) <= 0xDBFF;\n }\n for (var i = 2; i < diffs.length; i += 1) {\n if (diffs[i-2][0] === DIFF_EQUAL && ends_with_pair_start(diffs[i-2][1]) &&\n diffs[i-1][0] === DIFF_DELETE && starts_with_pair_end(diffs[i-1][1]) &&\n diffs[i][0] === DIFF_INSERT && starts_with_pair_end(diffs[i][1])) {\n compact = true;\n\n diffs[i-1][1] = diffs[i-2][1].slice(-1) + diffs[i-1][1];\n diffs[i][1] = diffs[i-2][1].slice(-1) + diffs[i][1];\n\n diffs[i-2][1] = diffs[i-2][1].slice(0, -1);\n }\n }\n if (!compact) {\n return diffs;\n }\n var fixed_diffs = [];\n for (var i = 0; i < diffs.length; i += 1) {\n if (diffs[i][1].length > 0) {\n fixed_diffs.push(diffs[i]);\n }\n }\n return fixed_diffs;\n}\n\n/*\n * Try to merge tuples with their neigbors in a given range.\n * E.g. [0, 'a'], [0, 'b'] -> [0, 'ab']\n *\n * @param {Array} diffs Array of diff tuples.\n * @param {Int} start Position of the first element to merge (diffs[start] is also merged with diffs[start - 1]).\n * @param {Int} length Number of consecutive elements to check.\n * @return {Array} Array of merged diff tuples.\n */\nfunction merge_tuples (diffs, start, length) {\n // Check from (start-1) to (start+length).\n for (var i = start + length - 1; i >= 0 && i >= start - 1; i--) {\n if (i + 1 < diffs.length) {\n var left_d = diffs[i];\n var right_d = diffs[i+1];\n if (left_d[0] === right_d[1]) {\n diffs.splice(i, 2, [left_d[0], left_d[1] + right_d[1]]);\n }\n }\n }\n return diffs;\n}\n\n\n/***/ }),\n/* 55 */\n/***/ (function(module, exports) {\n\nexports = module.exports = typeof Object.keys === 'function'\n ? Object.keys : shim;\n\nexports.shim = shim;\nfunction shim (obj) {\n var keys = [];\n for (var key in obj) keys.push(key);\n return keys;\n}\n\n\n/***/ }),\n/* 56 */\n/***/ (function(module, exports) {\n\nvar supportsArgumentsClass = (function(){\n return Object.prototype.toString.call(arguments)\n})() == '[object Arguments]';\n\nexports = module.exports = supportsArgumentsClass ? supported : unsupported;\n\nexports.supported = supported;\nfunction supported(object) {\n return Object.prototype.toString.call(object) == '[object Arguments]';\n};\n\nexports.unsupported = unsupported;\nfunction unsupported(object){\n return object &&\n typeof object == 'object' &&\n typeof object.length == 'number' &&\n Object.prototype.hasOwnProperty.call(object, 'callee') &&\n !Object.prototype.propertyIsEnumerable.call(object, 'callee') ||\n false;\n};\n\n\n/***/ }),\n/* 57 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _quillDelta = __webpack_require__(4);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _op = __webpack_require__(20);\n\nvar _op2 = _interopRequireDefault(_op);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _code = __webpack_require__(13);\n\nvar _code2 = _interopRequireDefault(_code);\n\nvar _cursor = __webpack_require__(31);\n\nvar _cursor2 = _interopRequireDefault(_cursor);\n\nvar _block = __webpack_require__(3);\n\nvar _block2 = _interopRequireDefault(_block);\n\nvar _break = __webpack_require__(14);\n\nvar _break2 = _interopRequireDefault(_break);\n\nvar _clone = __webpack_require__(21);\n\nvar _clone2 = _interopRequireDefault(_clone);\n\nvar _deepEqual = __webpack_require__(12);\n\nvar _deepEqual2 = _interopRequireDefault(_deepEqual);\n\nvar _extend = __webpack_require__(2);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nvar ASCII = /^[ -~]*$/;\n\nvar Editor = function () {\n function Editor(scroll) {\n _classCallCheck(this, Editor);\n\n this.scroll = scroll;\n this.delta = this.getDelta();\n }\n\n _createClass(Editor, [{\n key: 'applyDelta',\n value: function applyDelta(delta) {\n var _this = this;\n\n var consumeNextNewline = false;\n this.scroll.update();\n var scrollLength = this.scroll.length();\n this.scroll.batchStart();\n delta = normalizeDelta(delta);\n delta.reduce(function (index, op) {\n var length = op.retain || op.delete || op.insert.length || 1;\n var attributes = op.attributes || {};\n if (op.insert != null) {\n if (typeof op.insert === 'string') {\n var text = op.insert;\n if (text.endsWith('\\n') && consumeNextNewline) {\n consumeNextNewline = false;\n text = text.slice(0, -1);\n }\n if (index >= scrollLength && !text.endsWith('\\n')) {\n consumeNextNewline = true;\n }\n _this.scroll.insertAt(index, text);\n\n var _scroll$line = _this.scroll.line(index),\n _scroll$line2 = _slicedToArray(_scroll$line, 2),\n line = _scroll$line2[0],\n offset = _scroll$line2[1];\n\n var formats = (0, _extend2.default)({}, (0, _block.bubbleFormats)(line));\n if (line instanceof _block2.default) {\n var _line$descendant = line.descendant(_parchment2.default.Leaf, offset),\n _line$descendant2 = _slicedToArray(_line$descendant, 1),\n leaf = _line$descendant2[0];\n\n formats = (0, _extend2.default)(formats, (0, _block.bubbleFormats)(leaf));\n }\n attributes = _op2.default.attributes.diff(formats, attributes) || {};\n } else if (_typeof(op.insert) === 'object') {\n var key = Object.keys(op.insert)[0]; // There should only be one key\n if (key == null) return index;\n _this.scroll.insertAt(index, key, op.insert[key]);\n }\n scrollLength += length;\n }\n Object.keys(attributes).forEach(function (name) {\n _this.scroll.formatAt(index, length, name, attributes[name]);\n });\n return index + length;\n }, 0);\n delta.reduce(function (index, op) {\n if (typeof op.delete === 'number') {\n _this.scroll.deleteAt(index, op.delete);\n return index;\n }\n return index + (op.retain || op.insert.length || 1);\n }, 0);\n this.scroll.batchEnd();\n return this.update(delta);\n }\n }, {\n key: 'deleteText',\n value: function deleteText(index, length) {\n this.scroll.deleteAt(index, length);\n return this.update(new _quillDelta2.default().retain(index).delete(length));\n }\n }, {\n key: 'formatLine',\n value: function formatLine(index, length) {\n var _this2 = this;\n\n var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n this.scroll.update();\n Object.keys(formats).forEach(function (format) {\n if (_this2.scroll.whitelist != null && !_this2.scroll.whitelist[format]) return;\n var lines = _this2.scroll.lines(index, Math.max(length, 1));\n var lengthRemaining = length;\n lines.forEach(function (line) {\n var lineLength = line.length();\n if (!(line instanceof _code2.default)) {\n line.format(format, formats[format]);\n } else {\n var codeIndex = index - line.offset(_this2.scroll);\n var codeLength = line.newlineIndex(codeIndex + lengthRemaining) - codeIndex + 1;\n line.formatAt(codeIndex, codeLength, format, formats[format]);\n }\n lengthRemaining -= lineLength;\n });\n });\n this.scroll.optimize();\n return this.update(new _quillDelta2.default().retain(index).retain(length, (0, _clone2.default)(formats)));\n }\n }, {\n key: 'formatText',\n value: function formatText(index, length) {\n var _this3 = this;\n\n var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n Object.keys(formats).forEach(function (format) {\n _this3.scroll.formatAt(index, length, format, formats[format]);\n });\n return this.update(new _quillDelta2.default().retain(index).retain(length, (0, _clone2.default)(formats)));\n }\n }, {\n key: 'getContents',\n value: function getContents(index, length) {\n return this.delta.slice(index, index + length);\n }\n }, {\n key: 'getDelta',\n value: function getDelta() {\n return this.scroll.lines().reduce(function (delta, line) {\n return delta.concat(line.delta());\n }, new _quillDelta2.default());\n }\n }, {\n key: 'getFormat',\n value: function getFormat(index) {\n var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n\n var lines = [],\n leaves = [];\n if (length === 0) {\n this.scroll.path(index).forEach(function (path) {\n var _path = _slicedToArray(path, 1),\n blot = _path[0];\n\n if (blot instanceof _block2.default) {\n lines.push(blot);\n } else if (blot instanceof _parchment2.default.Leaf) {\n leaves.push(blot);\n }\n });\n } else {\n lines = this.scroll.lines(index, length);\n leaves = this.scroll.descendants(_parchment2.default.Leaf, index, length);\n }\n var formatsArr = [lines, leaves].map(function (blots) {\n if (blots.length === 0) return {};\n var formats = (0, _block.bubbleFormats)(blots.shift());\n while (Object.keys(formats).length > 0) {\n var blot = blots.shift();\n if (blot == null) return formats;\n formats = combineFormats((0, _block.bubbleFormats)(blot), formats);\n }\n return formats;\n });\n return _extend2.default.apply(_extend2.default, formatsArr);\n }\n }, {\n key: 'getText',\n value: function getText(index, length) {\n return this.getContents(index, length).filter(function (op) {\n return typeof op.insert === 'string';\n }).map(function (op) {\n return op.insert;\n }).join('');\n }\n }, {\n key: 'insertEmbed',\n value: function insertEmbed(index, embed, value) {\n this.scroll.insertAt(index, embed, value);\n return this.update(new _quillDelta2.default().retain(index).insert(_defineProperty({}, embed, value)));\n }\n }, {\n key: 'insertText',\n value: function insertText(index, text) {\n var _this4 = this;\n\n var formats = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n\n text = text.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n this.scroll.insertAt(index, text);\n Object.keys(formats).forEach(function (format) {\n _this4.scroll.formatAt(index, text.length, format, formats[format]);\n });\n return this.update(new _quillDelta2.default().retain(index).insert(text, (0, _clone2.default)(formats)));\n }\n }, {\n key: 'isBlank',\n value: function isBlank() {\n if (this.scroll.children.length == 0) return true;\n if (this.scroll.children.length > 1) return false;\n var block = this.scroll.children.head;\n if (block.statics.blotName !== _block2.default.blotName) return false;\n if (block.children.length > 1) return false;\n return block.children.head instanceof _break2.default;\n }\n }, {\n key: 'removeFormat',\n value: function removeFormat(index, length) {\n var text = this.getText(index, length);\n\n var _scroll$line3 = this.scroll.line(index + length),\n _scroll$line4 = _slicedToArray(_scroll$line3, 2),\n line = _scroll$line4[0],\n offset = _scroll$line4[1];\n\n var suffixLength = 0,\n suffix = new _quillDelta2.default();\n if (line != null) {\n if (!(line instanceof _code2.default)) {\n suffixLength = line.length() - offset;\n } else {\n suffixLength = line.newlineIndex(offset) - offset + 1;\n }\n suffix = line.delta().slice(offset, offset + suffixLength - 1).insert('\\n');\n }\n var contents = this.getContents(index, length + suffixLength);\n var diff = contents.diff(new _quillDelta2.default().insert(text).concat(suffix));\n var delta = new _quillDelta2.default().retain(index).concat(diff);\n return this.applyDelta(delta);\n }\n }, {\n key: 'update',\n value: function update(change) {\n var mutations = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];\n var cursorIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;\n\n var oldDelta = this.delta;\n if (mutations.length === 1 && mutations[0].type === 'characterData' && mutations[0].target.data.match(ASCII) && _parchment2.default.find(mutations[0].target)) {\n // Optimization for character changes\n var textBlot = _parchment2.default.find(mutations[0].target);\n var formats = (0, _block.bubbleFormats)(textBlot);\n var index = textBlot.offset(this.scroll);\n var oldValue = mutations[0].oldValue.replace(_cursor2.default.CONTENTS, '');\n var oldText = new _quillDelta2.default().insert(oldValue);\n var newText = new _quillDelta2.default().insert(textBlot.value());\n var diffDelta = new _quillDelta2.default().retain(index).concat(oldText.diff(newText, cursorIndex));\n change = diffDelta.reduce(function (delta, op) {\n if (op.insert) {\n return delta.insert(op.insert, formats);\n } else {\n return delta.push(op);\n }\n }, new _quillDelta2.default());\n this.delta = oldDelta.compose(change);\n } else {\n this.delta = this.getDelta();\n if (!change || !(0, _deepEqual2.default)(oldDelta.compose(change), this.delta)) {\n change = oldDelta.diff(this.delta, cursorIndex);\n }\n }\n return change;\n }\n }]);\n\n return Editor;\n}();\n\nfunction combineFormats(formats, combined) {\n return Object.keys(combined).reduce(function (merged, name) {\n if (formats[name] == null) return merged;\n if (combined[name] === formats[name]) {\n merged[name] = combined[name];\n } else if (Array.isArray(combined[name])) {\n if (combined[name].indexOf(formats[name]) < 0) {\n merged[name] = combined[name].concat([formats[name]]);\n }\n } else {\n merged[name] = [combined[name], formats[name]];\n }\n return merged;\n }, {});\n}\n\nfunction normalizeDelta(delta) {\n return delta.reduce(function (delta, op) {\n if (op.insert === 1) {\n var attributes = (0, _clone2.default)(op.attributes);\n delete attributes['image'];\n return delta.insert({ image: op.attributes.image }, attributes);\n }\n if (op.attributes != null && (op.attributes.list === true || op.attributes.bullet === true)) {\n op = (0, _clone2.default)(op);\n if (op.attributes.list) {\n op.attributes.list = 'ordered';\n } else {\n op.attributes.list = 'bullet';\n delete op.attributes.bullet;\n }\n }\n if (typeof op.insert === 'string') {\n var text = op.insert.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n return delta.insert(text, op.attributes);\n }\n return delta.push(op);\n }, new _quillDelta2.default());\n}\n\nexports.default = Editor;\n\n/***/ }),\n/* 58 */\n/***/ (function(module, exports) {\n\n'use strict';\n\nvar has = Object.prototype.hasOwnProperty\n , prefix = '~';\n\n/**\n * Constructor to create a storage for our `EE` objects.\n * An `Events` instance is a plain object whose properties are event names.\n *\n * @constructor\n * @api private\n */\nfunction Events() {}\n\n//\n// We try to not inherit from `Object.prototype`. In some engines creating an\n// instance in this way is faster than calling `Object.create(null)` directly.\n// If `Object.create(null)` is not supported we prefix the event names with a\n// character to make sure that the built-in object properties are not\n// overridden or used as an attack vector.\n//\nif (Object.create) {\n Events.prototype = Object.create(null);\n\n //\n // This hack is needed because the `__proto__` property is still inherited in\n // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.\n //\n if (!new Events().__proto__) prefix = false;\n}\n\n/**\n * Representation of a single event listener.\n *\n * @param {Function} fn The listener function.\n * @param {Mixed} context The context to invoke the listener with.\n * @param {Boolean} [once=false] Specify if the listener is a one-time listener.\n * @constructor\n * @api private\n */\nfunction EE(fn, context, once) {\n this.fn = fn;\n this.context = context;\n this.once = once || false;\n}\n\n/**\n * Minimal `EventEmitter` interface that is molded against the Node.js\n * `EventEmitter` interface.\n *\n * @constructor\n * @api public\n */\nfunction EventEmitter() {\n this._events = new Events();\n this._eventsCount = 0;\n}\n\n/**\n * Return an array listing the events for which the emitter has registered\n * listeners.\n *\n * @returns {Array}\n * @api public\n */\nEventEmitter.prototype.eventNames = function eventNames() {\n var names = []\n , events\n , name;\n\n if (this._eventsCount === 0) return names;\n\n for (name in (events = this._events)) {\n if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);\n }\n\n if (Object.getOwnPropertySymbols) {\n return names.concat(Object.getOwnPropertySymbols(events));\n }\n\n return names;\n};\n\n/**\n * Return the listeners registered for a given event.\n *\n * @param {String|Symbol} event The event name.\n * @param {Boolean} exists Only check if there are listeners.\n * @returns {Array|Boolean}\n * @api public\n */\nEventEmitter.prototype.listeners = function listeners(event, exists) {\n var evt = prefix ? prefix + event : event\n , available = this._events[evt];\n\n if (exists) return !!available;\n if (!available) return [];\n if (available.fn) return [available.fn];\n\n for (var i = 0, l = available.length, ee = new Array(l); i < l; i++) {\n ee[i] = available[i].fn;\n }\n\n return ee;\n};\n\n/**\n * Calls each of the listeners registered for a given event.\n *\n * @param {String|Symbol} event The event name.\n * @returns {Boolean} `true` if the event had listeners, else `false`.\n * @api public\n */\nEventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {\n var evt = prefix ? prefix + event : event;\n\n if (!this._events[evt]) return false;\n\n var listeners = this._events[evt]\n , len = arguments.length\n , args\n , i;\n\n if (listeners.fn) {\n if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);\n\n switch (len) {\n case 1: return listeners.fn.call(listeners.context), true;\n case 2: return listeners.fn.call(listeners.context, a1), true;\n case 3: return listeners.fn.call(listeners.context, a1, a2), true;\n case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;\n case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;\n case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;\n }\n\n for (i = 1, args = new Array(len -1); i < len; i++) {\n args[i - 1] = arguments[i];\n }\n\n listeners.fn.apply(listeners.context, args);\n } else {\n var length = listeners.length\n , j;\n\n for (i = 0; i < length; i++) {\n if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);\n\n switch (len) {\n case 1: listeners[i].fn.call(listeners[i].context); break;\n case 2: listeners[i].fn.call(listeners[i].context, a1); break;\n case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;\n case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;\n default:\n if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {\n args[j - 1] = arguments[j];\n }\n\n listeners[i].fn.apply(listeners[i].context, args);\n }\n }\n }\n\n return true;\n};\n\n/**\n * Add a listener for a given event.\n *\n * @param {String|Symbol} event The event name.\n * @param {Function} fn The listener function.\n * @param {Mixed} [context=this] The context to invoke the listener with.\n * @returns {EventEmitter} `this`.\n * @api public\n */\nEventEmitter.prototype.on = function on(event, fn, context) {\n var listener = new EE(fn, context || this)\n , evt = prefix ? prefix + event : event;\n\n if (!this._events[evt]) this._events[evt] = listener, this._eventsCount++;\n else if (!this._events[evt].fn) this._events[evt].push(listener);\n else this._events[evt] = [this._events[evt], listener];\n\n return this;\n};\n\n/**\n * Add a one-time listener for a given event.\n *\n * @param {String|Symbol} event The event name.\n * @param {Function} fn The listener function.\n * @param {Mixed} [context=this] The context to invoke the listener with.\n * @returns {EventEmitter} `this`.\n * @api public\n */\nEventEmitter.prototype.once = function once(event, fn, context) {\n var listener = new EE(fn, context || this, true)\n , evt = prefix ? prefix + event : event;\n\n if (!this._events[evt]) this._events[evt] = listener, this._eventsCount++;\n else if (!this._events[evt].fn) this._events[evt].push(listener);\n else this._events[evt] = [this._events[evt], listener];\n\n return this;\n};\n\n/**\n * Remove the listeners of a given event.\n *\n * @param {String|Symbol} event The event name.\n * @param {Function} fn Only remove the listeners that match this function.\n * @param {Mixed} context Only remove the listeners that have this context.\n * @param {Boolean} once Only remove one-time listeners.\n * @returns {EventEmitter} `this`.\n * @api public\n */\nEventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {\n var evt = prefix ? prefix + event : event;\n\n if (!this._events[evt]) return this;\n if (!fn) {\n if (--this._eventsCount === 0) this._events = new Events();\n else delete this._events[evt];\n return this;\n }\n\n var listeners = this._events[evt];\n\n if (listeners.fn) {\n if (\n listeners.fn === fn\n && (!once || listeners.once)\n && (!context || listeners.context === context)\n ) {\n if (--this._eventsCount === 0) this._events = new Events();\n else delete this._events[evt];\n }\n } else {\n for (var i = 0, events = [], length = listeners.length; i < length; i++) {\n if (\n listeners[i].fn !== fn\n || (once && !listeners[i].once)\n || (context && listeners[i].context !== context)\n ) {\n events.push(listeners[i]);\n }\n }\n\n //\n // Reset the array, or remove it completely if we have no more listeners.\n //\n if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;\n else if (--this._eventsCount === 0) this._events = new Events();\n else delete this._events[evt];\n }\n\n return this;\n};\n\n/**\n * Remove all listeners, or those of the specified event.\n *\n * @param {String|Symbol} [event] The event name.\n * @returns {EventEmitter} `this`.\n * @api public\n */\nEventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {\n var evt;\n\n if (event) {\n evt = prefix ? prefix + event : event;\n if (this._events[evt]) {\n if (--this._eventsCount === 0) this._events = new Events();\n else delete this._events[evt];\n }\n } else {\n this._events = new Events();\n this._eventsCount = 0;\n }\n\n return this;\n};\n\n//\n// Alias methods names because people roll like that.\n//\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\nEventEmitter.prototype.addListener = EventEmitter.prototype.on;\n\n//\n// This function doesn't apply anymore.\n//\nEventEmitter.prototype.setMaxListeners = function setMaxListeners() {\n return this;\n};\n\n//\n// Expose the prefix.\n//\nEventEmitter.prefixed = prefix;\n\n//\n// Allow `EventEmitter` to be imported as module namespace.\n//\nEventEmitter.EventEmitter = EventEmitter;\n\n//\n// Expose the module.\n//\nif ('undefined' !== typeof module) {\n module.exports = EventEmitter;\n}\n\n\n/***/ }),\n/* 59 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _emitter = __webpack_require__(9);\n\nvar _emitter2 = _interopRequireDefault(_emitter);\n\nvar _block = __webpack_require__(3);\n\nvar _block2 = _interopRequireDefault(_block);\n\nvar _break = __webpack_require__(14);\n\nvar _break2 = _interopRequireDefault(_break);\n\nvar _code = __webpack_require__(13);\n\nvar _code2 = _interopRequireDefault(_code);\n\nvar _container = __webpack_require__(23);\n\nvar _container2 = _interopRequireDefault(_container);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nfunction isLine(blot) {\n return blot instanceof _block2.default || blot instanceof _block.BlockEmbed;\n}\n\nvar Scroll = function (_Parchment$Scroll) {\n _inherits(Scroll, _Parchment$Scroll);\n\n function Scroll(domNode, config) {\n _classCallCheck(this, Scroll);\n\n var _this = _possibleConstructorReturn(this, (Scroll.__proto__ || Object.getPrototypeOf(Scroll)).call(this, domNode));\n\n _this.emitter = config.emitter;\n if (Array.isArray(config.whitelist)) {\n _this.whitelist = config.whitelist.reduce(function (whitelist, format) {\n whitelist[format] = true;\n return whitelist;\n }, {});\n }\n // Some reason fixes composition issues with character languages in Windows/Chrome, Safari\n _this.domNode.addEventListener('DOMNodeInserted', function () {});\n _this.optimize();\n _this.enable();\n return _this;\n }\n\n _createClass(Scroll, [{\n key: 'batchStart',\n value: function batchStart() {\n this.batch = true;\n }\n }, {\n key: 'batchEnd',\n value: function batchEnd() {\n this.batch = false;\n this.optimize();\n }\n }, {\n key: 'deleteAt',\n value: function deleteAt(index, length) {\n var _line = this.line(index),\n _line2 = _slicedToArray(_line, 2),\n first = _line2[0],\n offset = _line2[1];\n\n var _line3 = this.line(index + length),\n _line4 = _slicedToArray(_line3, 1),\n last = _line4[0];\n\n _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'deleteAt', this).call(this, index, length);\n if (last != null && first !== last && offset > 0) {\n if (first instanceof _block.BlockEmbed || last instanceof _block.BlockEmbed) {\n this.optimize();\n return;\n }\n if (first instanceof _code2.default) {\n var newlineIndex = first.newlineIndex(first.length(), true);\n if (newlineIndex > -1) {\n first = first.split(newlineIndex + 1);\n if (first === last) {\n this.optimize();\n return;\n }\n }\n } else if (last instanceof _code2.default) {\n var _newlineIndex = last.newlineIndex(0);\n if (_newlineIndex > -1) {\n last.split(_newlineIndex + 1);\n }\n }\n var ref = last.children.head instanceof _break2.default ? null : last.children.head;\n first.moveChildren(last, ref);\n first.remove();\n }\n this.optimize();\n }\n }, {\n key: 'enable',\n value: function enable() {\n var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;\n\n this.domNode.setAttribute('contenteditable', enabled);\n }\n }, {\n key: 'formatAt',\n value: function formatAt(index, length, format, value) {\n if (this.whitelist != null && !this.whitelist[format]) return;\n _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'formatAt', this).call(this, index, length, format, value);\n this.optimize();\n }\n }, {\n key: 'insertAt',\n value: function insertAt(index, value, def) {\n if (def != null && this.whitelist != null && !this.whitelist[value]) return;\n if (index >= this.length()) {\n if (def == null || _parchment2.default.query(value, _parchment2.default.Scope.BLOCK) == null) {\n var blot = _parchment2.default.create(this.statics.defaultChild);\n this.appendChild(blot);\n if (def == null && value.endsWith('\\n')) {\n value = value.slice(0, -1);\n }\n blot.insertAt(0, value, def);\n } else {\n var embed = _parchment2.default.create(value, def);\n this.appendChild(embed);\n }\n } else {\n _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'insertAt', this).call(this, index, value, def);\n }\n this.optimize();\n }\n }, {\n key: 'insertBefore',\n value: function insertBefore(blot, ref) {\n if (blot.statics.scope === _parchment2.default.Scope.INLINE_BLOT) {\n var wrapper = _parchment2.default.create(this.statics.defaultChild);\n wrapper.appendChild(blot);\n blot = wrapper;\n }\n _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'insertBefore', this).call(this, blot, ref);\n }\n }, {\n key: 'leaf',\n value: function leaf(index) {\n return this.path(index).pop() || [null, -1];\n }\n }, {\n key: 'line',\n value: function line(index) {\n if (index === this.length()) {\n return this.line(index - 1);\n }\n return this.descendant(isLine, index);\n }\n }, {\n key: 'lines',\n value: function lines() {\n var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Number.MAX_VALUE;\n\n var getLines = function getLines(blot, index, length) {\n var lines = [],\n lengthLeft = length;\n blot.children.forEachAt(index, length, function (child, index, length) {\n if (isLine(child)) {\n lines.push(child);\n } else if (child instanceof _parchment2.default.Container) {\n lines = lines.concat(getLines(child, index, lengthLeft));\n }\n lengthLeft -= length;\n });\n return lines;\n };\n return getLines(this, index, length);\n }\n }, {\n key: 'optimize',\n value: function optimize() {\n var mutations = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n if (this.batch === true) return;\n _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'optimize', this).call(this, mutations, context);\n if (mutations.length > 0) {\n this.emitter.emit(_emitter2.default.events.SCROLL_OPTIMIZE, mutations, context);\n }\n }\n }, {\n key: 'path',\n value: function path(index) {\n return _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'path', this).call(this, index).slice(1); // Exclude self\n }\n }, {\n key: 'update',\n value: function update(mutations) {\n if (this.batch === true) return;\n var source = _emitter2.default.sources.USER;\n if (typeof mutations === 'string') {\n source = mutations;\n }\n if (!Array.isArray(mutations)) {\n mutations = this.observer.takeRecords();\n }\n if (mutations.length > 0) {\n this.emitter.emit(_emitter2.default.events.SCROLL_BEFORE_UPDATE, source, mutations);\n }\n _get(Scroll.prototype.__proto__ || Object.getPrototypeOf(Scroll.prototype), 'update', this).call(this, mutations.concat([])); // pass copy\n if (mutations.length > 0) {\n this.emitter.emit(_emitter2.default.events.SCROLL_UPDATE, source, mutations);\n }\n }\n }]);\n\n return Scroll;\n}(_parchment2.default.Scroll);\n\nScroll.blotName = 'scroll';\nScroll.className = 'ql-editor';\nScroll.tagName = 'DIV';\nScroll.defaultChild = 'block';\nScroll.allowedChildren = [_block2.default, _block.BlockEmbed, _container2.default];\n\nexports.default = Scroll;\n\n/***/ }),\n/* 60 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.matchText = exports.matchSpacing = exports.matchNewline = exports.matchBlot = exports.matchAttributor = exports.default = undefined;\n\nvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _extend2 = __webpack_require__(2);\n\nvar _extend3 = _interopRequireDefault(_extend2);\n\nvar _quillDelta = __webpack_require__(4);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _quill = __webpack_require__(6);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _logger = __webpack_require__(10);\n\nvar _logger2 = _interopRequireDefault(_logger);\n\nvar _module = __webpack_require__(7);\n\nvar _module2 = _interopRequireDefault(_module);\n\nvar _align = __webpack_require__(34);\n\nvar _background = __webpack_require__(35);\n\nvar _code = __webpack_require__(13);\n\nvar _code2 = _interopRequireDefault(_code);\n\nvar _color = __webpack_require__(24);\n\nvar _direction = __webpack_require__(36);\n\nvar _font = __webpack_require__(37);\n\nvar _size = __webpack_require__(38);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar debug = (0, _logger2.default)('quill:clipboard');\n\nvar DOM_KEY = '__ql-matcher';\n\nvar CLIPBOARD_CONFIG = [[Node.TEXT_NODE, matchText], [Node.TEXT_NODE, matchNewline], ['br', matchBreak], [Node.ELEMENT_NODE, matchNewline], [Node.ELEMENT_NODE, matchBlot], [Node.ELEMENT_NODE, matchSpacing], [Node.ELEMENT_NODE, matchAttributor], [Node.ELEMENT_NODE, matchStyles], ['li', matchIndent], ['b', matchAlias.bind(matchAlias, 'bold')], ['i', matchAlias.bind(matchAlias, 'italic')], ['style', matchIgnore]];\n\nvar ATTRIBUTE_ATTRIBUTORS = [_align.AlignAttribute, _direction.DirectionAttribute].reduce(function (memo, attr) {\n memo[attr.keyName] = attr;\n return memo;\n}, {});\n\nvar STYLE_ATTRIBUTORS = [_align.AlignStyle, _background.BackgroundStyle, _color.ColorStyle, _direction.DirectionStyle, _font.FontStyle, _size.SizeStyle].reduce(function (memo, attr) {\n memo[attr.keyName] = attr;\n return memo;\n}, {});\n\nvar Clipboard = function (_Module) {\n _inherits(Clipboard, _Module);\n\n function Clipboard(quill, options) {\n _classCallCheck(this, Clipboard);\n\n var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this, quill, options));\n\n _this.quill.root.addEventListener('paste', _this.onPaste.bind(_this));\n _this.container = _this.quill.addContainer('ql-clipboard');\n _this.container.setAttribute('contenteditable', true);\n _this.container.setAttribute('tabindex', -1);\n _this.matchers = [];\n CLIPBOARD_CONFIG.concat(_this.options.matchers).forEach(function (_ref) {\n var _ref2 = _slicedToArray(_ref, 2),\n selector = _ref2[0],\n matcher = _ref2[1];\n\n if (!options.matchVisual && matcher === matchSpacing) return;\n _this.addMatcher(selector, matcher);\n });\n return _this;\n }\n\n _createClass(Clipboard, [{\n key: 'addMatcher',\n value: function addMatcher(selector, matcher) {\n this.matchers.push([selector, matcher]);\n }\n }, {\n key: 'convert',\n value: function convert(html) {\n if (typeof html === 'string') {\n this.container.innerHTML = html.replace(/\\>\\r?\\n +\\<'); // Remove spaces between tags\n return this.convert();\n }\n var formats = this.quill.getFormat(this.quill.selection.savedRange.index);\n if (formats[_code2.default.blotName]) {\n var text = this.container.innerText;\n this.container.innerHTML = '';\n return new _quillDelta2.default().insert(text, _defineProperty({}, _code2.default.blotName, formats[_code2.default.blotName]));\n }\n\n var _prepareMatching = this.prepareMatching(),\n _prepareMatching2 = _slicedToArray(_prepareMatching, 2),\n elementMatchers = _prepareMatching2[0],\n textMatchers = _prepareMatching2[1];\n\n var delta = traverse(this.container, elementMatchers, textMatchers);\n // Remove trailing newline\n if (deltaEndsWith(delta, '\\n') && delta.ops[delta.ops.length - 1].attributes == null) {\n delta = delta.compose(new _quillDelta2.default().retain(delta.length() - 1).delete(1));\n }\n debug.log('convert', this.container.innerHTML, delta);\n this.container.innerHTML = '';\n return delta;\n }\n }, {\n key: 'dangerouslyPasteHTML',\n value: function dangerouslyPasteHTML(index, html) {\n var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _quill2.default.sources.API;\n\n if (typeof index === 'string') {\n this.quill.setContents(this.convert(index), html);\n this.quill.setSelection(0, _quill2.default.sources.SILENT);\n } else {\n var paste = this.convert(html);\n this.quill.updateContents(new _quillDelta2.default().retain(index).concat(paste), source);\n this.quill.setSelection(index + paste.length(), _quill2.default.sources.SILENT);\n }\n }\n }, {\n key: 'onPaste',\n value: function onPaste(e) {\n var _this2 = this;\n\n if (e.defaultPrevented || !this.quill.isEnabled()) return;\n var range = this.quill.getSelection();\n var delta = new _quillDelta2.default().retain(range.index);\n var scrollTop = this.quill.scrollingContainer.scrollTop;\n this.container.focus();\n this.quill.selection.update(_quill2.default.sources.SILENT);\n setTimeout(function () {\n delta = delta.concat(_this2.convert()).delete(range.length);\n _this2.quill.updateContents(delta, _quill2.default.sources.USER);\n // range.length contributes to delta.length()\n _this2.quill.setSelection(delta.length() - range.length, _quill2.default.sources.SILENT);\n _this2.quill.scrollingContainer.scrollTop = scrollTop;\n _this2.quill.focus();\n }, 1);\n }\n }, {\n key: 'prepareMatching',\n value: function prepareMatching() {\n var _this3 = this;\n\n var elementMatchers = [],\n textMatchers = [];\n this.matchers.forEach(function (pair) {\n var _pair = _slicedToArray(pair, 2),\n selector = _pair[0],\n matcher = _pair[1];\n\n switch (selector) {\n case Node.TEXT_NODE:\n textMatchers.push(matcher);\n break;\n case Node.ELEMENT_NODE:\n elementMatchers.push(matcher);\n break;\n default:\n [].forEach.call(_this3.container.querySelectorAll(selector), function (node) {\n // TODO use weakmap\n node[DOM_KEY] = node[DOM_KEY] || [];\n node[DOM_KEY].push(matcher);\n });\n break;\n }\n });\n return [elementMatchers, textMatchers];\n }\n }]);\n\n return Clipboard;\n}(_module2.default);\n\nClipboard.DEFAULTS = {\n matchers: [],\n matchVisual: true\n};\n\nfunction applyFormat(delta, format, value) {\n if ((typeof format === 'undefined' ? 'undefined' : _typeof(format)) === 'object') {\n return Object.keys(format).reduce(function (delta, key) {\n return applyFormat(delta, key, format[key]);\n }, delta);\n } else {\n return delta.reduce(function (delta, op) {\n if (op.attributes && op.attributes[format]) {\n return delta.push(op);\n } else {\n return delta.insert(op.insert, (0, _extend3.default)({}, _defineProperty({}, format, value), op.attributes));\n }\n }, new _quillDelta2.default());\n }\n}\n\nfunction computeStyle(node) {\n if (node.nodeType !== Node.ELEMENT_NODE) return {};\n var DOM_KEY = '__ql-computed-style';\n return node[DOM_KEY] || (node[DOM_KEY] = window.getComputedStyle(node));\n}\n\nfunction deltaEndsWith(delta, text) {\n var endText = \"\";\n for (var i = delta.ops.length - 1; i >= 0 && endText.length < text.length; --i) {\n var op = delta.ops[i];\n if (typeof op.insert !== 'string') break;\n endText = op.insert + endText;\n }\n return endText.slice(-1 * text.length) === text;\n}\n\nfunction isLine(node) {\n if (node.childNodes.length === 0) return false; // Exclude embed blocks\n var style = computeStyle(node);\n return ['block', 'list-item'].indexOf(style.display) > -1;\n}\n\nfunction traverse(node, elementMatchers, textMatchers) {\n // Post-order\n if (node.nodeType === node.TEXT_NODE) {\n return textMatchers.reduce(function (delta, matcher) {\n return matcher(node, delta);\n }, new _quillDelta2.default());\n } else if (node.nodeType === node.ELEMENT_NODE) {\n return [].reduce.call(node.childNodes || [], function (delta, childNode) {\n var childrenDelta = traverse(childNode, elementMatchers, textMatchers);\n if (childNode.nodeType === node.ELEMENT_NODE) {\n childrenDelta = elementMatchers.reduce(function (childrenDelta, matcher) {\n return matcher(childNode, childrenDelta);\n }, childrenDelta);\n childrenDelta = (childNode[DOM_KEY] || []).reduce(function (childrenDelta, matcher) {\n return matcher(childNode, childrenDelta);\n }, childrenDelta);\n }\n return delta.concat(childrenDelta);\n }, new _quillDelta2.default());\n } else {\n return new _quillDelta2.default();\n }\n}\n\nfunction matchAlias(format, node, delta) {\n return applyFormat(delta, format, true);\n}\n\nfunction matchAttributor(node, delta) {\n var attributes = _parchment2.default.Attributor.Attribute.keys(node);\n var classes = _parchment2.default.Attributor.Class.keys(node);\n var styles = _parchment2.default.Attributor.Style.keys(node);\n var formats = {};\n attributes.concat(classes).concat(styles).forEach(function (name) {\n var attr = _parchment2.default.query(name, _parchment2.default.Scope.ATTRIBUTE);\n if (attr != null) {\n formats[attr.attrName] = attr.value(node);\n if (formats[attr.attrName]) return;\n }\n attr = ATTRIBUTE_ATTRIBUTORS[name];\n if (attr != null && (attr.attrName === name || attr.keyName === name)) {\n formats[attr.attrName] = attr.value(node) || undefined;\n }\n attr = STYLE_ATTRIBUTORS[name];\n if (attr != null && (attr.attrName === name || attr.keyName === name)) {\n attr = STYLE_ATTRIBUTORS[name];\n formats[attr.attrName] = attr.value(node) || undefined;\n }\n });\n if (Object.keys(formats).length > 0) {\n delta = applyFormat(delta, formats);\n }\n return delta;\n}\n\nfunction matchBlot(node, delta) {\n var match = _parchment2.default.query(node);\n if (match == null) return delta;\n if (match.prototype instanceof _parchment2.default.Embed) {\n var embed = {};\n var value = match.value(node);\n if (value != null) {\n embed[match.blotName] = value;\n delta = new _quillDelta2.default().insert(embed, match.formats(node));\n }\n } else if (typeof match.formats === 'function') {\n delta = applyFormat(delta, match.blotName, match.formats(node));\n }\n return delta;\n}\n\nfunction matchBreak(node, delta) {\n if (!deltaEndsWith(delta, '\\n')) {\n delta.insert('\\n');\n }\n return delta;\n}\n\nfunction matchIgnore() {\n return new _quillDelta2.default();\n}\n\nfunction matchIndent(node, delta) {\n var match = _parchment2.default.query(node);\n if (match == null || match.blotName !== 'list-item' || !deltaEndsWith(delta, '\\n')) {\n return delta;\n }\n var indent = -1,\n parent = node.parentNode;\n while (!parent.classList.contains('ql-clipboard')) {\n if ((_parchment2.default.query(parent) || {}).blotName === 'list') {\n indent += 1;\n }\n parent = parent.parentNode;\n }\n if (indent <= 0) return delta;\n return delta.compose(new _quillDelta2.default().retain(delta.length() - 1).retain(1, { indent: indent }));\n}\n\nfunction matchNewline(node, delta) {\n if (!deltaEndsWith(delta, '\\n')) {\n if (isLine(node) || delta.length() > 0 && node.nextSibling && isLine(node.nextSibling)) {\n delta.insert('\\n');\n }\n }\n return delta;\n}\n\nfunction matchSpacing(node, delta) {\n if (isLine(node) && node.nextElementSibling != null && !deltaEndsWith(delta, '\\n\\n')) {\n var nodeHeight = node.offsetHeight + parseFloat(computeStyle(node).marginTop) + parseFloat(computeStyle(node).marginBottom);\n if (node.nextElementSibling.offsetTop > node.offsetTop + nodeHeight * 1.5) {\n delta.insert('\\n');\n }\n }\n return delta;\n}\n\nfunction matchStyles(node, delta) {\n var formats = {};\n var style = node.style || {};\n if (style.fontStyle && computeStyle(node).fontStyle === 'italic') {\n formats.italic = true;\n }\n if (style.fontWeight && (computeStyle(node).fontWeight.startsWith('bold') || parseInt(computeStyle(node).fontWeight) >= 700)) {\n formats.bold = true;\n }\n if (Object.keys(formats).length > 0) {\n delta = applyFormat(delta, formats);\n }\n if (parseFloat(style.textIndent || 0) > 0) {\n // Could be 0.5in\n delta = new _quillDelta2.default().insert('\\t').concat(delta);\n }\n return delta;\n}\n\nfunction matchText(node, delta) {\n var text = node.data;\n // Word represents empty line with  \n if (node.parentNode.tagName === 'O:P') {\n return delta.insert(text.trim());\n }\n if (text.trim().length === 0 && node.parentNode.classList.contains('ql-clipboard')) {\n return delta;\n }\n if (!computeStyle(node.parentNode).whiteSpace.startsWith('pre')) {\n // eslint-disable-next-line func-style\n var replacer = function replacer(collapse, match) {\n match = match.replace(/[^\\u00a0]/g, ''); // \\u00a0 is nbsp;\n return match.length < 1 && collapse ? ' ' : match;\n };\n text = text.replace(/\\r\\n/g, ' ').replace(/\\n/g, ' ');\n text = text.replace(/\\s\\s+/g, replacer.bind(replacer, true)); // collapse whitespace\n if (node.previousSibling == null && isLine(node.parentNode) || node.previousSibling != null && isLine(node.previousSibling)) {\n text = text.replace(/^\\s+/, replacer.bind(replacer, false));\n }\n if (node.nextSibling == null && isLine(node.parentNode) || node.nextSibling != null && isLine(node.nextSibling)) {\n text = text.replace(/\\s+$/, replacer.bind(replacer, false));\n }\n }\n return delta.insert(text);\n}\n\nexports.default = Clipboard;\nexports.matchAttributor = matchAttributor;\nexports.matchBlot = matchBlot;\nexports.matchNewline = matchNewline;\nexports.matchSpacing = matchSpacing;\nexports.matchText = matchText;\n\n/***/ }),\n/* 61 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.getLastChangeIndex = exports.default = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _quill = __webpack_require__(6);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _module = __webpack_require__(7);\n\nvar _module2 = _interopRequireDefault(_module);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar History = function (_Module) {\n _inherits(History, _Module);\n\n function History(quill, options) {\n _classCallCheck(this, History);\n\n var _this = _possibleConstructorReturn(this, (History.__proto__ || Object.getPrototypeOf(History)).call(this, quill, options));\n\n _this.lastRecorded = 0;\n _this.ignoreChange = false;\n _this.clear();\n _this.quill.on(_quill2.default.events.EDITOR_CHANGE, function (eventName, delta, oldDelta, source) {\n if (eventName !== _quill2.default.events.TEXT_CHANGE || _this.ignoreChange) return;\n if (!_this.options.userOnly || source === _quill2.default.sources.USER) {\n _this.record(delta, oldDelta);\n } else {\n _this.transform(delta);\n }\n });\n _this.quill.keyboard.addBinding({ key: 'Z', shortKey: true }, _this.undo.bind(_this));\n _this.quill.keyboard.addBinding({ key: 'Z', shortKey: true, shiftKey: true }, _this.redo.bind(_this));\n if (/Win/i.test(navigator.platform)) {\n _this.quill.keyboard.addBinding({ key: 'Y', shortKey: true }, _this.redo.bind(_this));\n }\n return _this;\n }\n\n _createClass(History, [{\n key: 'change',\n value: function change(source, dest) {\n if (this.stack[source].length === 0) return;\n var delta = this.stack[source].pop();\n this.stack[dest].push(delta);\n this.lastRecorded = 0;\n this.ignoreChange = true;\n this.quill.updateContents(delta[source], _quill2.default.sources.USER);\n this.ignoreChange = false;\n var index = getLastChangeIndex(delta[source]);\n this.quill.setSelection(index);\n }\n }, {\n key: 'clear',\n value: function clear() {\n this.stack = { undo: [], redo: [] };\n }\n }, {\n key: 'cutoff',\n value: function cutoff() {\n this.lastRecorded = 0;\n }\n }, {\n key: 'record',\n value: function record(changeDelta, oldDelta) {\n if (changeDelta.ops.length === 0) return;\n this.stack.redo = [];\n var undoDelta = this.quill.getContents().diff(oldDelta);\n var timestamp = Date.now();\n if (this.lastRecorded + this.options.delay > timestamp && this.stack.undo.length > 0) {\n var delta = this.stack.undo.pop();\n undoDelta = undoDelta.compose(delta.undo);\n changeDelta = delta.redo.compose(changeDelta);\n } else {\n this.lastRecorded = timestamp;\n }\n this.stack.undo.push({\n redo: changeDelta,\n undo: undoDelta\n });\n if (this.stack.undo.length > this.options.maxStack) {\n this.stack.undo.shift();\n }\n }\n }, {\n key: 'redo',\n value: function redo() {\n this.change('redo', 'undo');\n }\n }, {\n key: 'transform',\n value: function transform(delta) {\n this.stack.undo.forEach(function (change) {\n change.undo = delta.transform(change.undo, true);\n change.redo = delta.transform(change.redo, true);\n });\n this.stack.redo.forEach(function (change) {\n change.undo = delta.transform(change.undo, true);\n change.redo = delta.transform(change.redo, true);\n });\n }\n }, {\n key: 'undo',\n value: function undo() {\n this.change('undo', 'redo');\n }\n }]);\n\n return History;\n}(_module2.default);\n\nHistory.DEFAULTS = {\n delay: 1000,\n maxStack: 100,\n userOnly: false\n};\n\nfunction endsWithNewlineChange(delta) {\n var lastOp = delta.ops[delta.ops.length - 1];\n if (lastOp == null) return false;\n if (lastOp.insert != null) {\n return typeof lastOp.insert === 'string' && lastOp.insert.endsWith('\\n');\n }\n if (lastOp.attributes != null) {\n return Object.keys(lastOp.attributes).some(function (attr) {\n return _parchment2.default.query(attr, _parchment2.default.Scope.BLOCK) != null;\n });\n }\n return false;\n}\n\nfunction getLastChangeIndex(delta) {\n var deleteLength = delta.reduce(function (length, op) {\n length += op.delete || 0;\n return length;\n }, 0);\n var changeIndex = delta.length() - deleteLength;\n if (endsWithNewlineChange(delta)) {\n changeIndex -= 1;\n }\n return changeIndex;\n}\n\nexports.default = History;\nexports.getLastChangeIndex = getLastChangeIndex;\n\n/***/ }),\n/* 62 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.IndentClass = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar IdentAttributor = function (_Parchment$Attributor) {\n _inherits(IdentAttributor, _Parchment$Attributor);\n\n function IdentAttributor() {\n _classCallCheck(this, IdentAttributor);\n\n return _possibleConstructorReturn(this, (IdentAttributor.__proto__ || Object.getPrototypeOf(IdentAttributor)).apply(this, arguments));\n }\n\n _createClass(IdentAttributor, [{\n key: 'add',\n value: function add(node, value) {\n if (value === '+1' || value === '-1') {\n var indent = this.value(node) || 0;\n value = value === '+1' ? indent + 1 : indent - 1;\n }\n if (value === 0) {\n this.remove(node);\n return true;\n } else {\n return _get(IdentAttributor.prototype.__proto__ || Object.getPrototypeOf(IdentAttributor.prototype), 'add', this).call(this, node, value);\n }\n }\n }, {\n key: 'canAdd',\n value: function canAdd(node, value) {\n return _get(IdentAttributor.prototype.__proto__ || Object.getPrototypeOf(IdentAttributor.prototype), 'canAdd', this).call(this, node, value) || _get(IdentAttributor.prototype.__proto__ || Object.getPrototypeOf(IdentAttributor.prototype), 'canAdd', this).call(this, node, parseInt(value));\n }\n }, {\n key: 'value',\n value: function value(node) {\n return parseInt(_get(IdentAttributor.prototype.__proto__ || Object.getPrototypeOf(IdentAttributor.prototype), 'value', this).call(this, node)) || undefined; // Don't return NaN\n }\n }]);\n\n return IdentAttributor;\n}(_parchment2.default.Attributor.Class);\n\nvar IndentClass = new IdentAttributor('indent', 'ql-indent', {\n scope: _parchment2.default.Scope.BLOCK,\n whitelist: [1, 2, 3, 4, 5, 6, 7, 8]\n});\n\nexports.IndentClass = IndentClass;\n\n/***/ }),\n/* 63 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _block = __webpack_require__(3);\n\nvar _block2 = _interopRequireDefault(_block);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Blockquote = function (_Block) {\n _inherits(Blockquote, _Block);\n\n function Blockquote() {\n _classCallCheck(this, Blockquote);\n\n return _possibleConstructorReturn(this, (Blockquote.__proto__ || Object.getPrototypeOf(Blockquote)).apply(this, arguments));\n }\n\n return Blockquote;\n}(_block2.default);\n\nBlockquote.blotName = 'blockquote';\nBlockquote.tagName = 'blockquote';\n\nexports.default = Blockquote;\n\n/***/ }),\n/* 64 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _block = __webpack_require__(3);\n\nvar _block2 = _interopRequireDefault(_block);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Header = function (_Block) {\n _inherits(Header, _Block);\n\n function Header() {\n _classCallCheck(this, Header);\n\n return _possibleConstructorReturn(this, (Header.__proto__ || Object.getPrototypeOf(Header)).apply(this, arguments));\n }\n\n _createClass(Header, null, [{\n key: 'formats',\n value: function formats(domNode) {\n return this.tagName.indexOf(domNode.tagName) + 1;\n }\n }]);\n\n return Header;\n}(_block2.default);\n\nHeader.blotName = 'header';\nHeader.tagName = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'];\n\nexports.default = Header;\n\n/***/ }),\n/* 65 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = exports.ListItem = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _block = __webpack_require__(3);\n\nvar _block2 = _interopRequireDefault(_block);\n\nvar _container = __webpack_require__(23);\n\nvar _container2 = _interopRequireDefault(_container);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar ListItem = function (_Block) {\n _inherits(ListItem, _Block);\n\n function ListItem() {\n _classCallCheck(this, ListItem);\n\n return _possibleConstructorReturn(this, (ListItem.__proto__ || Object.getPrototypeOf(ListItem)).apply(this, arguments));\n }\n\n _createClass(ListItem, [{\n key: 'format',\n value: function format(name, value) {\n if (name === List.blotName && !value) {\n this.replaceWith(_parchment2.default.create(this.statics.scope));\n } else {\n _get(ListItem.prototype.__proto__ || Object.getPrototypeOf(ListItem.prototype), 'format', this).call(this, name, value);\n }\n }\n }, {\n key: 'remove',\n value: function remove() {\n if (this.prev == null && this.next == null) {\n this.parent.remove();\n } else {\n _get(ListItem.prototype.__proto__ || Object.getPrototypeOf(ListItem.prototype), 'remove', this).call(this);\n }\n }\n }, {\n key: 'replaceWith',\n value: function replaceWith(name, value) {\n this.parent.isolate(this.offset(this.parent), this.length());\n if (name === this.parent.statics.blotName) {\n this.parent.replaceWith(name, value);\n return this;\n } else {\n this.parent.unwrap();\n return _get(ListItem.prototype.__proto__ || Object.getPrototypeOf(ListItem.prototype), 'replaceWith', this).call(this, name, value);\n }\n }\n }], [{\n key: 'formats',\n value: function formats(domNode) {\n return domNode.tagName === this.tagName ? undefined : _get(ListItem.__proto__ || Object.getPrototypeOf(ListItem), 'formats', this).call(this, domNode);\n }\n }]);\n\n return ListItem;\n}(_block2.default);\n\nListItem.blotName = 'list-item';\nListItem.tagName = 'LI';\n\nvar List = function (_Container) {\n _inherits(List, _Container);\n\n _createClass(List, null, [{\n key: 'create',\n value: function create(value) {\n var tagName = value === 'ordered' ? 'OL' : 'UL';\n var node = _get(List.__proto__ || Object.getPrototypeOf(List), 'create', this).call(this, tagName);\n if (value === 'checked' || value === 'unchecked') {\n node.setAttribute('data-checked', value === 'checked');\n }\n return node;\n }\n }, {\n key: 'formats',\n value: function formats(domNode) {\n if (domNode.tagName === 'OL') return 'ordered';\n if (domNode.tagName === 'UL') {\n if (domNode.hasAttribute('data-checked')) {\n return domNode.getAttribute('data-checked') === 'true' ? 'checked' : 'unchecked';\n } else {\n return 'bullet';\n }\n }\n return undefined;\n }\n }]);\n\n function List(domNode) {\n _classCallCheck(this, List);\n\n var _this2 = _possibleConstructorReturn(this, (List.__proto__ || Object.getPrototypeOf(List)).call(this, domNode));\n\n var listEventHandler = function listEventHandler(e) {\n if (e.target.parentNode !== domNode) return;\n var format = _this2.statics.formats(domNode);\n var blot = _parchment2.default.find(e.target);\n if (format === 'checked') {\n blot.format('list', 'unchecked');\n } else if (format === 'unchecked') {\n blot.format('list', 'checked');\n }\n };\n\n domNode.addEventListener('touchstart', listEventHandler);\n domNode.addEventListener('mousedown', listEventHandler);\n return _this2;\n }\n\n _createClass(List, [{\n key: 'format',\n value: function format(name, value) {\n if (this.children.length > 0) {\n this.children.tail.format(name, value);\n }\n }\n }, {\n key: 'formats',\n value: function formats() {\n // We don't inherit from FormatBlot\n return _defineProperty({}, this.statics.blotName, this.statics.formats(this.domNode));\n }\n }, {\n key: 'insertBefore',\n value: function insertBefore(blot, ref) {\n if (blot instanceof ListItem) {\n _get(List.prototype.__proto__ || Object.getPrototypeOf(List.prototype), 'insertBefore', this).call(this, blot, ref);\n } else {\n var index = ref == null ? this.length() : ref.offset(this);\n var after = this.split(index);\n after.parent.insertBefore(blot, after);\n }\n }\n }, {\n key: 'optimize',\n value: function optimize(context) {\n _get(List.prototype.__proto__ || Object.getPrototypeOf(List.prototype), 'optimize', this).call(this, context);\n var next = this.next;\n if (next != null && next.prev === this && next.statics.blotName === this.statics.blotName && next.domNode.tagName === this.domNode.tagName && next.domNode.getAttribute('data-checked') === this.domNode.getAttribute('data-checked')) {\n next.moveChildren(this);\n next.remove();\n }\n }\n }, {\n key: 'replace',\n value: function replace(target) {\n if (target.statics.blotName !== this.statics.blotName) {\n var item = _parchment2.default.create(this.statics.defaultChild);\n target.moveChildren(item);\n this.appendChild(item);\n }\n _get(List.prototype.__proto__ || Object.getPrototypeOf(List.prototype), 'replace', this).call(this, target);\n }\n }]);\n\n return List;\n}(_container2.default);\n\nList.blotName = 'list';\nList.scope = _parchment2.default.Scope.BLOCK_BLOT;\nList.tagName = ['OL', 'UL'];\nList.defaultChild = 'list-item';\nList.allowedChildren = [ListItem];\n\nexports.ListItem = ListItem;\nexports.default = List;\n\n/***/ }),\n/* 66 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _bold = __webpack_require__(39);\n\nvar _bold2 = _interopRequireDefault(_bold);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Italic = function (_Bold) {\n _inherits(Italic, _Bold);\n\n function Italic() {\n _classCallCheck(this, Italic);\n\n return _possibleConstructorReturn(this, (Italic.__proto__ || Object.getPrototypeOf(Italic)).apply(this, arguments));\n }\n\n return Italic;\n}(_bold2.default);\n\nItalic.blotName = 'italic';\nItalic.tagName = ['EM', 'I'];\n\nexports.default = Italic;\n\n/***/ }),\n/* 67 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _inline = __webpack_require__(5);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Script = function (_Inline) {\n _inherits(Script, _Inline);\n\n function Script() {\n _classCallCheck(this, Script);\n\n return _possibleConstructorReturn(this, (Script.__proto__ || Object.getPrototypeOf(Script)).apply(this, arguments));\n }\n\n _createClass(Script, null, [{\n key: 'create',\n value: function create(value) {\n if (value === 'super') {\n return document.createElement('sup');\n } else if (value === 'sub') {\n return document.createElement('sub');\n } else {\n return _get(Script.__proto__ || Object.getPrototypeOf(Script), 'create', this).call(this, value);\n }\n }\n }, {\n key: 'formats',\n value: function formats(domNode) {\n if (domNode.tagName === 'SUB') return 'sub';\n if (domNode.tagName === 'SUP') return 'super';\n return undefined;\n }\n }]);\n\n return Script;\n}(_inline2.default);\n\nScript.blotName = 'script';\nScript.tagName = ['SUB', 'SUP'];\n\nexports.default = Script;\n\n/***/ }),\n/* 68 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _inline = __webpack_require__(5);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Strike = function (_Inline) {\n _inherits(Strike, _Inline);\n\n function Strike() {\n _classCallCheck(this, Strike);\n\n return _possibleConstructorReturn(this, (Strike.__proto__ || Object.getPrototypeOf(Strike)).apply(this, arguments));\n }\n\n return Strike;\n}(_inline2.default);\n\nStrike.blotName = 'strike';\nStrike.tagName = 'S';\n\nexports.default = Strike;\n\n/***/ }),\n/* 69 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _inline = __webpack_require__(5);\n\nvar _inline2 = _interopRequireDefault(_inline);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar Underline = function (_Inline) {\n _inherits(Underline, _Inline);\n\n function Underline() {\n _classCallCheck(this, Underline);\n\n return _possibleConstructorReturn(this, (Underline.__proto__ || Object.getPrototypeOf(Underline)).apply(this, arguments));\n }\n\n return Underline;\n}(_inline2.default);\n\nUnderline.blotName = 'underline';\nUnderline.tagName = 'U';\n\nexports.default = Underline;\n\n/***/ }),\n/* 70 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _link = __webpack_require__(15);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar ATTRIBUTES = ['alt', 'height', 'width'];\n\nvar Image = function (_Parchment$Embed) {\n _inherits(Image, _Parchment$Embed);\n\n function Image() {\n _classCallCheck(this, Image);\n\n return _possibleConstructorReturn(this, (Image.__proto__ || Object.getPrototypeOf(Image)).apply(this, arguments));\n }\n\n _createClass(Image, [{\n key: 'format',\n value: function format(name, value) {\n if (ATTRIBUTES.indexOf(name) > -1) {\n if (value) {\n this.domNode.setAttribute(name, value);\n } else {\n this.domNode.removeAttribute(name);\n }\n } else {\n _get(Image.prototype.__proto__ || Object.getPrototypeOf(Image.prototype), 'format', this).call(this, name, value);\n }\n }\n }], [{\n key: 'create',\n value: function create(value) {\n var node = _get(Image.__proto__ || Object.getPrototypeOf(Image), 'create', this).call(this, value);\n if (typeof value === 'string') {\n node.setAttribute('src', this.sanitize(value));\n }\n return node;\n }\n }, {\n key: 'formats',\n value: function formats(domNode) {\n return ATTRIBUTES.reduce(function (formats, attribute) {\n if (domNode.hasAttribute(attribute)) {\n formats[attribute] = domNode.getAttribute(attribute);\n }\n return formats;\n }, {});\n }\n }, {\n key: 'match',\n value: function match(url) {\n return (/\\.(jpe?g|gif|png)$/.test(url) || /^data:image\\/.+;base64/.test(url)\n );\n }\n }, {\n key: 'sanitize',\n value: function sanitize(url) {\n return (0, _link.sanitize)(url, ['http', 'https', 'data']) ? url : '//:0';\n }\n }, {\n key: 'value',\n value: function value(domNode) {\n return domNode.getAttribute('src');\n }\n }]);\n\n return Image;\n}(_parchment2.default.Embed);\n\nImage.blotName = 'image';\nImage.tagName = 'IMG';\n\nexports.default = Image;\n\n/***/ }),\n/* 71 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _block = __webpack_require__(3);\n\nvar _link = __webpack_require__(15);\n\nvar _link2 = _interopRequireDefault(_link);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar ATTRIBUTES = ['height', 'width'];\n\nvar Video = function (_BlockEmbed) {\n _inherits(Video, _BlockEmbed);\n\n function Video() {\n _classCallCheck(this, Video);\n\n return _possibleConstructorReturn(this, (Video.__proto__ || Object.getPrototypeOf(Video)).apply(this, arguments));\n }\n\n _createClass(Video, [{\n key: 'format',\n value: function format(name, value) {\n if (ATTRIBUTES.indexOf(name) > -1) {\n if (value) {\n this.domNode.setAttribute(name, value);\n } else {\n this.domNode.removeAttribute(name);\n }\n } else {\n _get(Video.prototype.__proto__ || Object.getPrototypeOf(Video.prototype), 'format', this).call(this, name, value);\n }\n }\n }], [{\n key: 'create',\n value: function create(value) {\n var node = _get(Video.__proto__ || Object.getPrototypeOf(Video), 'create', this).call(this, value);\n node.setAttribute('frameborder', '0');\n node.setAttribute('allowfullscreen', true);\n node.setAttribute('src', this.sanitize(value));\n return node;\n }\n }, {\n key: 'formats',\n value: function formats(domNode) {\n return ATTRIBUTES.reduce(function (formats, attribute) {\n if (domNode.hasAttribute(attribute)) {\n formats[attribute] = domNode.getAttribute(attribute);\n }\n return formats;\n }, {});\n }\n }, {\n key: 'sanitize',\n value: function sanitize(url) {\n return _link2.default.sanitize(url);\n }\n }, {\n key: 'value',\n value: function value(domNode) {\n return domNode.getAttribute('src');\n }\n }]);\n\n return Video;\n}(_block.BlockEmbed);\n\nVideo.blotName = 'video';\nVideo.className = 'ql-video';\nVideo.tagName = 'IFRAME';\n\nexports.default = Video;\n\n/***/ }),\n/* 72 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = exports.FormulaBlot = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _embed = __webpack_require__(33);\n\nvar _embed2 = _interopRequireDefault(_embed);\n\nvar _quill = __webpack_require__(6);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _module = __webpack_require__(7);\n\nvar _module2 = _interopRequireDefault(_module);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar FormulaBlot = function (_Embed) {\n _inherits(FormulaBlot, _Embed);\n\n function FormulaBlot() {\n _classCallCheck(this, FormulaBlot);\n\n return _possibleConstructorReturn(this, (FormulaBlot.__proto__ || Object.getPrototypeOf(FormulaBlot)).apply(this, arguments));\n }\n\n _createClass(FormulaBlot, null, [{\n key: 'create',\n value: function create(value) {\n var node = _get(FormulaBlot.__proto__ || Object.getPrototypeOf(FormulaBlot), 'create', this).call(this, value);\n if (typeof value === 'string') {\n window.katex.render(value, node, {\n throwOnError: false,\n errorColor: '#f00'\n });\n node.setAttribute('data-value', value);\n }\n return node;\n }\n }, {\n key: 'value',\n value: function value(domNode) {\n return domNode.getAttribute('data-value');\n }\n }]);\n\n return FormulaBlot;\n}(_embed2.default);\n\nFormulaBlot.blotName = 'formula';\nFormulaBlot.className = 'ql-formula';\nFormulaBlot.tagName = 'SPAN';\n\nvar Formula = function (_Module) {\n _inherits(Formula, _Module);\n\n _createClass(Formula, null, [{\n key: 'register',\n value: function register() {\n _quill2.default.register(FormulaBlot, true);\n }\n }]);\n\n function Formula() {\n _classCallCheck(this, Formula);\n\n var _this2 = _possibleConstructorReturn(this, (Formula.__proto__ || Object.getPrototypeOf(Formula)).call(this));\n\n if (window.katex == null) {\n throw new Error('Formula module requires KaTeX.');\n }\n return _this2;\n }\n\n return Formula;\n}(_module2.default);\n\nexports.FormulaBlot = FormulaBlot;\nexports.default = Formula;\n\n/***/ }),\n/* 73 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = exports.CodeToken = exports.CodeBlock = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _quill = __webpack_require__(6);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _module = __webpack_require__(7);\n\nvar _module2 = _interopRequireDefault(_module);\n\nvar _code = __webpack_require__(13);\n\nvar _code2 = _interopRequireDefault(_code);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar SyntaxCodeBlock = function (_CodeBlock) {\n _inherits(SyntaxCodeBlock, _CodeBlock);\n\n function SyntaxCodeBlock() {\n _classCallCheck(this, SyntaxCodeBlock);\n\n return _possibleConstructorReturn(this, (SyntaxCodeBlock.__proto__ || Object.getPrototypeOf(SyntaxCodeBlock)).apply(this, arguments));\n }\n\n _createClass(SyntaxCodeBlock, [{\n key: 'replaceWith',\n value: function replaceWith(block) {\n this.domNode.textContent = this.domNode.textContent;\n this.attach();\n _get(SyntaxCodeBlock.prototype.__proto__ || Object.getPrototypeOf(SyntaxCodeBlock.prototype), 'replaceWith', this).call(this, block);\n }\n }, {\n key: 'highlight',\n value: function highlight(_highlight) {\n var text = this.domNode.textContent;\n if (this.cachedText !== text) {\n if (text.trim().length > 0 || this.cachedText == null) {\n this.domNode.innerHTML = _highlight(text);\n this.domNode.normalize();\n this.attach();\n }\n this.cachedText = text;\n }\n }\n }]);\n\n return SyntaxCodeBlock;\n}(_code2.default);\n\nSyntaxCodeBlock.className = 'ql-syntax';\n\nvar CodeToken = new _parchment2.default.Attributor.Class('token', 'hljs', {\n scope: _parchment2.default.Scope.INLINE\n});\n\nvar Syntax = function (_Module) {\n _inherits(Syntax, _Module);\n\n _createClass(Syntax, null, [{\n key: 'register',\n value: function register() {\n _quill2.default.register(CodeToken, true);\n _quill2.default.register(SyntaxCodeBlock, true);\n }\n }]);\n\n function Syntax(quill, options) {\n _classCallCheck(this, Syntax);\n\n var _this2 = _possibleConstructorReturn(this, (Syntax.__proto__ || Object.getPrototypeOf(Syntax)).call(this, quill, options));\n\n if (typeof _this2.options.highlight !== 'function') {\n throw new Error('Syntax module requires highlight.js. Please include the library on the page before Quill.');\n }\n var timer = null;\n _this2.quill.on(_quill2.default.events.SCROLL_OPTIMIZE, function () {\n clearTimeout(timer);\n timer = setTimeout(function () {\n _this2.highlight();\n timer = null;\n }, _this2.options.interval);\n });\n _this2.highlight();\n return _this2;\n }\n\n _createClass(Syntax, [{\n key: 'highlight',\n value: function highlight() {\n var _this3 = this;\n\n if (this.quill.selection.composing) return;\n this.quill.update(_quill2.default.sources.USER);\n var range = this.quill.getSelection();\n this.quill.scroll.descendants(SyntaxCodeBlock).forEach(function (code) {\n code.highlight(_this3.options.highlight);\n });\n this.quill.update(_quill2.default.sources.SILENT);\n if (range != null) {\n this.quill.setSelection(range, _quill2.default.sources.SILENT);\n }\n }\n }]);\n\n return Syntax;\n}(_module2.default);\n\nSyntax.DEFAULTS = {\n highlight: function () {\n if (window.hljs == null) return null;\n return function (text) {\n var result = window.hljs.highlightAuto(text);\n return result.value;\n };\n }(),\n interval: 1000\n};\n\nexports.CodeBlock = SyntaxCodeBlock;\nexports.CodeToken = CodeToken;\nexports.default = Syntax;\n\n/***/ }),\n/* 74 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.addControls = exports.default = undefined;\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _quillDelta = __webpack_require__(4);\n\nvar _quillDelta2 = _interopRequireDefault(_quillDelta);\n\nvar _parchment = __webpack_require__(0);\n\nvar _parchment2 = _interopRequireDefault(_parchment);\n\nvar _quill = __webpack_require__(6);\n\nvar _quill2 = _interopRequireDefault(_quill);\n\nvar _logger = __webpack_require__(10);\n\nvar _logger2 = _interopRequireDefault(_logger);\n\nvar _module = __webpack_require__(7);\n\nvar _module2 = _interopRequireDefault(_module);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar debug = (0, _logger2.default)('quill:toolbar');\n\nvar Toolbar = function (_Module) {\n _inherits(Toolbar, _Module);\n\n function Toolbar(quill, options) {\n _classCallCheck(this, Toolbar);\n\n var _this = _possibleConstructorReturn(this, (Toolbar.__proto__ || Object.getPrototypeOf(Toolbar)).call(this, quill, options));\n\n if (Array.isArray(_this.options.container)) {\n var container = document.createElement('div');\n addControls(container, _this.options.container);\n quill.container.parentNode.insertBefore(container, quill.container);\n _this.container = container;\n } else if (typeof _this.options.container === 'string') {\n _this.container = document.querySelector(_this.options.container);\n } else {\n _this.container = _this.options.container;\n }\n if (!(_this.container instanceof HTMLElement)) {\n var _ret;\n\n return _ret = debug.error('Container required for toolbar', _this.options), _possibleConstructorReturn(_this, _ret);\n }\n _this.container.classList.add('ql-toolbar');\n _this.controls = [];\n _this.handlers = {};\n Object.keys(_this.options.handlers).forEach(function (format) {\n _this.addHandler(format, _this.options.handlers[format]);\n });\n [].forEach.call(_this.container.querySelectorAll('button, select'), function (input) {\n _this.attach(input);\n });\n _this.quill.on(_quill2.default.events.EDITOR_CHANGE, function (type, range) {\n if (type === _quill2.default.events.SELECTION_CHANGE) {\n _this.update(range);\n }\n });\n _this.quill.on(_quill2.default.events.SCROLL_OPTIMIZE, function () {\n var _this$quill$selection = _this.quill.selection.getRange(),\n _this$quill$selection2 = _slicedToArray(_this$quill$selection, 1),\n range = _this$quill$selection2[0]; // quill.getSelection triggers update\n\n\n _this.update(range);\n });\n return _this;\n }\n\n _createClass(Toolbar, [{\n key: 'addHandler',\n value: function addHandler(format, handler) {\n this.handlers[format] = handler;\n }\n }, {\n key: 'attach',\n value: function attach(input) {\n var _this2 = this;\n\n var format = [].find.call(input.classList, function (className) {\n return className.indexOf('ql-') === 0;\n });\n if (!format) return;\n format = format.slice('ql-'.length);\n if (input.tagName === 'BUTTON') {\n input.setAttribute('type', 'button');\n }\n if (this.handlers[format] == null) {\n if (this.quill.scroll.whitelist != null && this.quill.scroll.whitelist[format] == null) {\n debug.warn('ignoring attaching to disabled format', format, input);\n return;\n }\n if (_parchment2.default.query(format) == null) {\n debug.warn('ignoring attaching to nonexistent format', format, input);\n return;\n }\n }\n var eventName = input.tagName === 'SELECT' ? 'change' : 'click';\n input.addEventListener(eventName, function (e) {\n var value = void 0;\n if (input.tagName === 'SELECT') {\n if (input.selectedIndex < 0) return;\n var selected = input.options[input.selectedIndex];\n if (selected.hasAttribute('selected')) {\n value = false;\n } else {\n value = selected.value || false;\n }\n } else {\n if (input.classList.contains('ql-active')) {\n value = false;\n } else {\n value = input.value || !input.hasAttribute('value');\n }\n e.preventDefault();\n }\n _this2.quill.focus();\n\n var _quill$selection$getR = _this2.quill.selection.getRange(),\n _quill$selection$getR2 = _slicedToArray(_quill$selection$getR, 1),\n range = _quill$selection$getR2[0];\n\n if (_this2.handlers[format] != null) {\n _this2.handlers[format].call(_this2, value);\n } else if (_parchment2.default.query(format).prototype instanceof _parchment2.default.Embed) {\n value = prompt('Enter ' + format);\n if (!value) return;\n _this2.quill.updateContents(new _quillDelta2.default().retain(range.index).delete(range.length).insert(_defineProperty({}, format, value)), _quill2.default.sources.USER);\n } else {\n _this2.quill.format(format, value, _quill2.default.sources.USER);\n }\n _this2.update(range);\n });\n // TODO use weakmap\n this.controls.push([format, input]);\n }\n }, {\n key: 'update',\n value: function update(range) {\n var formats = range == null ? {} : this.quill.getFormat(range);\n this.controls.forEach(function (pair) {\n var _pair = _slicedToArray(pair, 2),\n format = _pair[0],\n input = _pair[1];\n\n if (input.tagName === 'SELECT') {\n var option = void 0;\n if (range == null) {\n option = null;\n } else if (formats[format] == null) {\n option = input.querySelector('option[selected]');\n } else if (!Array.isArray(formats[format])) {\n var value = formats[format];\n if (typeof value === 'string') {\n value = value.replace(/\\\"/g, '\\\\\"');\n }\n option = input.querySelector('option[value=\"' + value + '\"]');\n }\n if (option == null) {\n input.value = ''; // TODO make configurable?\n input.selectedIndex = -1;\n } else {\n option.selected = true;\n }\n } else {\n if (range == null) {\n input.classList.remove('ql-active');\n } else if (input.hasAttribute('value')) {\n // both being null should match (default values)\n // '1' should match with 1 (headers)\n var isActive = formats[format] === input.getAttribute('value') || formats[format] != null && formats[format].toString() === input.getAttribute('value') || formats[format] == null && !input.getAttribute('value');\n input.classList.toggle('ql-active', isActive);\n } else {\n input.classList.toggle('ql-active', formats[format] != null);\n }\n }\n });\n }\n }]);\n\n return Toolbar;\n}(_module2.default);\n\nToolbar.DEFAULTS = {};\n\nfunction addButton(container, format, value) {\n var input = document.createElement('button');\n input.setAttribute('type', 'button');\n input.classList.add('ql-' + format);\n if (value != null) {\n input.value = value;\n }\n container.appendChild(input);\n}\n\nfunction addControls(container, groups) {\n if (!Array.isArray(groups[0])) {\n groups = [groups];\n }\n groups.forEach(function (controls) {\n var group = document.createElement('span');\n group.classList.add('ql-formats');\n controls.forEach(function (control) {\n if (typeof control === 'string') {\n addButton(group, control);\n } else {\n var format = Object.keys(control)[0];\n var value = control[format];\n if (Array.isArray(value)) {\n addSelect(group, format, value);\n } else {\n addButton(group, format, value);\n }\n }\n });\n container.appendChild(group);\n });\n}\n\nfunction addSelect(container, format, values) {\n var input = document.createElement('select');\n input.classList.add('ql-' + format);\n values.forEach(function (value) {\n var option = document.createElement('option');\n if (value !== false) {\n option.setAttribute('value', value);\n } else {\n option.setAttribute('selected', 'selected');\n }\n input.appendChild(option);\n });\n container.appendChild(input);\n}\n\nToolbar.DEFAULTS = {\n container: null,\n handlers: {\n clean: function clean() {\n var _this3 = this;\n\n var range = this.quill.getSelection();\n if (range == null) return;\n if (range.length == 0) {\n var formats = this.quill.getFormat();\n Object.keys(formats).forEach(function (name) {\n // Clean functionality in existing apps only clean inline formats\n if (_parchment2.default.query(name, _parchment2.default.Scope.INLINE) != null) {\n _this3.quill.format(name, false);\n }\n });\n } else {\n this.quill.removeFormat(range, _quill2.default.sources.USER);\n }\n },\n direction: function direction(value) {\n var align = this.quill.getFormat()['align'];\n if (value === 'rtl' && align == null) {\n this.quill.format('align', 'right', _quill2.default.sources.USER);\n } else if (!value && align === 'right') {\n this.quill.format('align', false, _quill2.default.sources.USER);\n }\n this.quill.format('direction', value, _quill2.default.sources.USER);\n },\n indent: function indent(value) {\n var range = this.quill.getSelection();\n var formats = this.quill.getFormat(range);\n var indent = parseInt(formats.indent || 0);\n if (value === '+1' || value === '-1') {\n var modifier = value === '+1' ? 1 : -1;\n if (formats.direction === 'rtl') modifier *= -1;\n this.quill.format('indent', indent + modifier, _quill2.default.sources.USER);\n }\n },\n link: function link(value) {\n if (value === true) {\n value = prompt('Enter link URL:');\n }\n this.quill.format('link', value, _quill2.default.sources.USER);\n },\n list: function list(value) {\n var range = this.quill.getSelection();\n var formats = this.quill.getFormat(range);\n if (value === 'check') {\n if (formats['list'] === 'checked' || formats['list'] === 'unchecked') {\n this.quill.format('list', false, _quill2.default.sources.USER);\n } else {\n this.quill.format('list', 'unchecked', _quill2.default.sources.USER);\n }\n } else {\n this.quill.format('list', value, _quill2.default.sources.USER);\n }\n }\n }\n};\n\nexports.default = Toolbar;\nexports.addControls = addControls;\n\n/***/ }),\n/* 75 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 76 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 77 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 78 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 79 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 80 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 81 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 82 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 83 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 84 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 85 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 86 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 87 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 88 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 89 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 90 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 91 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 92 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 93 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 94 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 95 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 96 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 97 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 98 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 99 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 100 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 101 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 102 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 103 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 104 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 105 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 106 */\n/***/ (function(module, exports) {\n\nmodule.exports = \" \";\n\n/***/ }),\n/* 107 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = exports.BubbleTooltip = undefined;\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _extend = __webpack_require__(2);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nvar _emitter = __webpack_require__(9);\n\nvar _emitter2 = _interopRequireDefault(_emitter);\n\nvar _base = __webpack_require__(44);\n\nvar _base2 = _interopRequireDefault(_base);\n\nvar _selection = __webpack_require__(22);\n\nvar _icons = __webpack_require__(26);\n\nvar _icons2 = _interopRequireDefault(_icons);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar TOOLBAR_CONFIG = [['bold', 'italic', 'link'], [{ header: 1 }, { header: 2 }, 'blockquote']];\n\nvar BubbleTheme = function (_BaseTheme) {\n _inherits(BubbleTheme, _BaseTheme);\n\n function BubbleTheme(quill, options) {\n _classCallCheck(this, BubbleTheme);\n\n if (options.modules.toolbar != null && options.modules.toolbar.container == null) {\n options.modules.toolbar.container = TOOLBAR_CONFIG;\n }\n\n var _this = _possibleConstructorReturn(this, (BubbleTheme.__proto__ || Object.getPrototypeOf(BubbleTheme)).call(this, quill, options));\n\n _this.quill.container.classList.add('ql-bubble');\n return _this;\n }\n\n _createClass(BubbleTheme, [{\n key: 'extendToolbar',\n value: function extendToolbar(toolbar) {\n this.tooltip = new BubbleTooltip(this.quill, this.options.bounds);\n this.tooltip.root.appendChild(toolbar.container);\n this.buildButtons([].slice.call(toolbar.container.querySelectorAll('button')), _icons2.default);\n this.buildPickers([].slice.call(toolbar.container.querySelectorAll('select')), _icons2.default);\n }\n }]);\n\n return BubbleTheme;\n}(_base2.default);\n\nBubbleTheme.DEFAULTS = (0, _extend2.default)(true, {}, _base2.default.DEFAULTS, {\n modules: {\n toolbar: {\n handlers: {\n link: function link(value) {\n if (!value) {\n this.quill.format('link', false);\n } else {\n this.quill.theme.tooltip.edit();\n }\n }\n }\n }\n }\n});\n\nvar BubbleTooltip = function (_BaseTooltip) {\n _inherits(BubbleTooltip, _BaseTooltip);\n\n function BubbleTooltip(quill, bounds) {\n _classCallCheck(this, BubbleTooltip);\n\n var _this2 = _possibleConstructorReturn(this, (BubbleTooltip.__proto__ || Object.getPrototypeOf(BubbleTooltip)).call(this, quill, bounds));\n\n _this2.quill.on(_emitter2.default.events.EDITOR_CHANGE, function (type, range, oldRange, source) {\n if (type !== _emitter2.default.events.SELECTION_CHANGE) return;\n if (range != null && range.length > 0 && source === _emitter2.default.sources.USER) {\n _this2.show();\n // Lock our width so we will expand beyond our offsetParent boundaries\n _this2.root.style.left = '0px';\n _this2.root.style.width = '';\n _this2.root.style.width = _this2.root.offsetWidth + 'px';\n var lines = _this2.quill.getLines(range.index, range.length);\n if (lines.length === 1) {\n _this2.position(_this2.quill.getBounds(range));\n } else {\n var lastLine = lines[lines.length - 1];\n var index = _this2.quill.getIndex(lastLine);\n var length = Math.min(lastLine.length() - 1, range.index + range.length - index);\n var _bounds = _this2.quill.getBounds(new _selection.Range(index, length));\n _this2.position(_bounds);\n }\n } else if (document.activeElement !== _this2.textbox && _this2.quill.hasFocus()) {\n _this2.hide();\n }\n });\n return _this2;\n }\n\n _createClass(BubbleTooltip, [{\n key: 'listen',\n value: function listen() {\n var _this3 = this;\n\n _get(BubbleTooltip.prototype.__proto__ || Object.getPrototypeOf(BubbleTooltip.prototype), 'listen', this).call(this);\n this.root.querySelector('.ql-close').addEventListener('click', function () {\n _this3.root.classList.remove('ql-editing');\n });\n this.quill.on(_emitter2.default.events.SCROLL_OPTIMIZE, function () {\n // Let selection be restored by toolbar handlers before repositioning\n setTimeout(function () {\n if (_this3.root.classList.contains('ql-hidden')) return;\n var range = _this3.quill.getSelection();\n if (range != null) {\n _this3.position(_this3.quill.getBounds(range));\n }\n }, 1);\n });\n }\n }, {\n key: 'cancel',\n value: function cancel() {\n this.show();\n }\n }, {\n key: 'position',\n value: function position(reference) {\n var shift = _get(BubbleTooltip.prototype.__proto__ || Object.getPrototypeOf(BubbleTooltip.prototype), 'position', this).call(this, reference);\n var arrow = this.root.querySelector('.ql-tooltip-arrow');\n arrow.style.marginLeft = '';\n if (shift === 0) return shift;\n arrow.style.marginLeft = -1 * shift - arrow.offsetWidth / 2 + 'px';\n }\n }]);\n\n return BubbleTooltip;\n}(_base.BaseTooltip);\n\nBubbleTooltip.TEMPLATE = ['', '
', '', '', '
'].join('');\n\nexports.BubbleTooltip = BubbleTooltip;\nexports.default = BubbleTheme;\n\n/***/ }),\n/* 108 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if (\"value\" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _extend = __webpack_require__(2);\n\nvar _extend2 = _interopRequireDefault(_extend);\n\nvar _emitter = __webpack_require__(9);\n\nvar _emitter2 = _interopRequireDefault(_emitter);\n\nvar _base = __webpack_require__(44);\n\nvar _base2 = _interopRequireDefault(_base);\n\nvar _link = __webpack_require__(15);\n\nvar _link2 = _interopRequireDefault(_link);\n\nvar _selection = __webpack_require__(22);\n\nvar _icons = __webpack_require__(26);\n\nvar _icons2 = _interopRequireDefault(_icons);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\nvar TOOLBAR_CONFIG = [[{ header: ['1', '2', '3', false] }], ['bold', 'italic', 'underline', 'link'], [{ list: 'ordered' }, { list: 'bullet' }], ['clean']];\n\nvar SnowTheme = function (_BaseTheme) {\n _inherits(SnowTheme, _BaseTheme);\n\n function SnowTheme(quill, options) {\n _classCallCheck(this, SnowTheme);\n\n if (options.modules.toolbar != null && options.modules.toolbar.container == null) {\n options.modules.toolbar.container = TOOLBAR_CONFIG;\n }\n\n var _this = _possibleConstructorReturn(this, (SnowTheme.__proto__ || Object.getPrototypeOf(SnowTheme)).call(this, quill, options));\n\n _this.quill.container.classList.add('ql-snow');\n return _this;\n }\n\n _createClass(SnowTheme, [{\n key: 'extendToolbar',\n value: function extendToolbar(toolbar) {\n toolbar.container.classList.add('ql-snow');\n this.buildButtons([].slice.call(toolbar.container.querySelectorAll('button')), _icons2.default);\n this.buildPickers([].slice.call(toolbar.container.querySelectorAll('select')), _icons2.default);\n this.tooltip = new SnowTooltip(this.quill, this.options.bounds);\n if (toolbar.container.querySelector('.ql-link')) {\n this.quill.keyboard.addBinding({ key: 'K', shortKey: true }, function (range, context) {\n toolbar.handlers['link'].call(toolbar, !context.format.link);\n });\n }\n }\n }]);\n\n return SnowTheme;\n}(_base2.default);\n\nSnowTheme.DEFAULTS = (0, _extend2.default)(true, {}, _base2.default.DEFAULTS, {\n modules: {\n toolbar: {\n handlers: {\n link: function link(value) {\n if (value) {\n var range = this.quill.getSelection();\n if (range == null || range.length == 0) return;\n var preview = this.quill.getText(range);\n if (/^\\S+@\\S+\\.\\S+$/.test(preview) && preview.indexOf('mailto:') !== 0) {\n preview = 'mailto:' + preview;\n }\n var tooltip = this.quill.theme.tooltip;\n tooltip.edit('link', preview);\n } else {\n this.quill.format('link', false);\n }\n }\n }\n }\n }\n});\n\nvar SnowTooltip = function (_BaseTooltip) {\n _inherits(SnowTooltip, _BaseTooltip);\n\n function SnowTooltip(quill, bounds) {\n _classCallCheck(this, SnowTooltip);\n\n var _this2 = _possibleConstructorReturn(this, (SnowTooltip.__proto__ || Object.getPrototypeOf(SnowTooltip)).call(this, quill, bounds));\n\n _this2.preview = _this2.root.querySelector('a.ql-preview');\n return _this2;\n }\n\n _createClass(SnowTooltip, [{\n key: 'listen',\n value: function listen() {\n var _this3 = this;\n\n _get(SnowTooltip.prototype.__proto__ || Object.getPrototypeOf(SnowTooltip.prototype), 'listen', this).call(this);\n this.root.querySelector('a.ql-action').addEventListener('click', function (event) {\n if (_this3.root.classList.contains('ql-editing')) {\n _this3.save();\n } else {\n _this3.edit('link', _this3.preview.textContent);\n }\n event.preventDefault();\n });\n this.root.querySelector('a.ql-remove').addEventListener('click', function (event) {\n if (_this3.linkRange != null) {\n var range = _this3.linkRange;\n _this3.restoreFocus();\n _this3.quill.formatText(range, 'link', false, _emitter2.default.sources.USER);\n delete _this3.linkRange;\n }\n event.preventDefault();\n _this3.hide();\n });\n this.quill.on(_emitter2.default.events.SELECTION_CHANGE, function (range, oldRange, source) {\n if (range == null) return;\n if (range.length === 0 && source === _emitter2.default.sources.USER) {\n var _quill$scroll$descend = _this3.quill.scroll.descendant(_link2.default, range.index),\n _quill$scroll$descend2 = _slicedToArray(_quill$scroll$descend, 2),\n link = _quill$scroll$descend2[0],\n offset = _quill$scroll$descend2[1];\n\n if (link != null) {\n _this3.linkRange = new _selection.Range(range.index - offset, link.length());\n var preview = _link2.default.formats(link.domNode);\n _this3.preview.textContent = preview;\n _this3.preview.setAttribute('href', preview);\n _this3.show();\n _this3.position(_this3.quill.getBounds(_this3.linkRange));\n return;\n }\n } else {\n delete _this3.linkRange;\n }\n _this3.hide();\n });\n }\n }, {\n key: 'show',\n value: function show() {\n _get(SnowTooltip.prototype.__proto__ || Object.getPrototypeOf(SnowTooltip.prototype), 'show', this).call(this);\n this.root.removeAttribute('data-mode');\n }\n }]);\n\n return SnowTooltip;\n}(_base.BaseTooltip);\n\nSnowTooltip.TEMPLATE = ['', '', '', ''].join('');\n\nexports.default = SnowTheme;\n\n/***/ })\n/******/ ])[\"default\"];\n});\n\n\n// WEBPACK FOOTER //\n// quill.min.js"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 45);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap bc1e3bc04ac5f71cbeee","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar container_1 = require(\"./blot/abstract/container\");\nvar format_1 = require(\"./blot/abstract/format\");\nvar leaf_1 = require(\"./blot/abstract/leaf\");\nvar scroll_1 = require(\"./blot/scroll\");\nvar inline_1 = require(\"./blot/inline\");\nvar block_1 = require(\"./blot/block\");\nvar embed_1 = require(\"./blot/embed\");\nvar text_1 = require(\"./blot/text\");\nvar attributor_1 = require(\"./attributor/attributor\");\nvar class_1 = require(\"./attributor/class\");\nvar style_1 = require(\"./attributor/style\");\nvar store_1 = require(\"./attributor/store\");\nvar Registry = require(\"./registry\");\nvar Parchment = {\n Scope: Registry.Scope,\n create: Registry.create,\n find: Registry.find,\n query: Registry.query,\n register: Registry.register,\n Container: container_1.default,\n Format: format_1.default,\n Leaf: leaf_1.default,\n Embed: embed_1.default,\n Scroll: scroll_1.default,\n Block: block_1.default,\n Inline: inline_1.default,\n Text: text_1.default,\n Attributor: {\n Attribute: attributor_1.default,\n Class: class_1.default,\n Style: style_1.default,\n Store: store_1.default,\n },\n};\nexports.default = Parchment;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/parchment/src/parchment.ts\n// module id = 0\n// module chunks = 0","\"use strict\";\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar ParchmentError = /** @class */ (function (_super) {\n __extends(ParchmentError, _super);\n function ParchmentError(message) {\n var _this = this;\n message = '[Parchment] ' + message;\n _this = _super.call(this, message) || this;\n _this.message = message;\n _this.name = _this.constructor.name;\n return _this;\n }\n return ParchmentError;\n}(Error));\nexports.ParchmentError = ParchmentError;\nvar attributes = {};\nvar classes = {};\nvar tags = {};\nvar types = {};\nexports.DATA_KEY = '__blot';\nvar Scope;\n(function (Scope) {\n Scope[Scope[\"TYPE\"] = 3] = \"TYPE\";\n Scope[Scope[\"LEVEL\"] = 12] = \"LEVEL\";\n Scope[Scope[\"ATTRIBUTE\"] = 13] = \"ATTRIBUTE\";\n Scope[Scope[\"BLOT\"] = 14] = \"BLOT\";\n Scope[Scope[\"INLINE\"] = 7] = \"INLINE\";\n Scope[Scope[\"BLOCK\"] = 11] = \"BLOCK\";\n Scope[Scope[\"BLOCK_BLOT\"] = 10] = \"BLOCK_BLOT\";\n Scope[Scope[\"INLINE_BLOT\"] = 6] = \"INLINE_BLOT\";\n Scope[Scope[\"BLOCK_ATTRIBUTE\"] = 9] = \"BLOCK_ATTRIBUTE\";\n Scope[Scope[\"INLINE_ATTRIBUTE\"] = 5] = \"INLINE_ATTRIBUTE\";\n Scope[Scope[\"ANY\"] = 15] = \"ANY\";\n})(Scope = exports.Scope || (exports.Scope = {}));\nfunction create(input, value) {\n var match = query(input);\n if (match == null) {\n throw new ParchmentError(\"Unable to create \" + input + \" blot\");\n }\n var BlotClass = match;\n var node = \n // @ts-ignore\n input instanceof Node || input['nodeType'] === Node.TEXT_NODE ? input : BlotClass.create(value);\n return new BlotClass(node, value);\n}\nexports.create = create;\nfunction find(node, bubble) {\n if (bubble === void 0) { bubble = false; }\n if (node == null)\n return null;\n // @ts-ignore\n if (node[exports.DATA_KEY] != null)\n return node[exports.DATA_KEY].blot;\n if (bubble)\n return find(node.parentNode, bubble);\n return null;\n}\nexports.find = find;\nfunction query(query, scope) {\n if (scope === void 0) { scope = Scope.ANY; }\n var match;\n if (typeof query === 'string') {\n match = types[query] || attributes[query];\n // @ts-ignore\n }\n else if (query instanceof Text || query['nodeType'] === Node.TEXT_NODE) {\n match = types['text'];\n }\n else if (typeof query === 'number') {\n if (query & Scope.LEVEL & Scope.BLOCK) {\n match = types['block'];\n }\n else if (query & Scope.LEVEL & Scope.INLINE) {\n match = types['inline'];\n }\n }\n else if (query instanceof HTMLElement) {\n var names = (query.getAttribute('class') || '').split(/\\s+/);\n for (var i in names) {\n match = classes[names[i]];\n if (match)\n break;\n }\n match = match || tags[query.tagName];\n }\n if (match == null)\n return null;\n // @ts-ignore\n if (scope & Scope.LEVEL & match.scope && scope & Scope.TYPE & match.scope)\n return match;\n return null;\n}\nexports.query = query;\nfunction register() {\n var Definitions = [];\n for (var _i = 0; _i < arguments.length; _i++) {\n Definitions[_i] = arguments[_i];\n }\n if (Definitions.length > 1) {\n return Definitions.map(function (d) {\n return register(d);\n });\n }\n var Definition = Definitions[0];\n if (typeof Definition.blotName !== 'string' && typeof Definition.attrName !== 'string') {\n throw new ParchmentError('Invalid definition');\n }\n else if (Definition.blotName === 'abstract') {\n throw new ParchmentError('Cannot register abstract class');\n }\n types[Definition.blotName || Definition.attrName] = Definition;\n if (typeof Definition.keyName === 'string') {\n attributes[Definition.keyName] = Definition;\n }\n else {\n if (Definition.className != null) {\n classes[Definition.className] = Definition;\n }\n if (Definition.tagName != null) {\n if (Array.isArray(Definition.tagName)) {\n Definition.tagName = Definition.tagName.map(function (tagName) {\n return tagName.toUpperCase();\n });\n }\n else {\n Definition.tagName = Definition.tagName.toUpperCase();\n }\n var tagNames = Array.isArray(Definition.tagName) ? Definition.tagName : [Definition.tagName];\n tagNames.forEach(function (tag) {\n if (tags[tag] == null || Definition.className == null) {\n tags[tag] = Definition;\n }\n });\n }\n }\n return Definition;\n}\nexports.register = register;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/parchment/src/registry.ts\n// module id = 1\n// module chunks = 0","'use strict';\n\nvar hasOwn = Object.prototype.hasOwnProperty;\nvar toStr = Object.prototype.toString;\n\nvar isArray = function isArray(arr) {\n\tif (typeof Array.isArray === 'function') {\n\t\treturn Array.isArray(arr);\n\t}\n\n\treturn toStr.call(arr) === '[object Array]';\n};\n\nvar isPlainObject = function isPlainObject(obj) {\n\tif (!obj || toStr.call(obj) !== '[object Object]') {\n\t\treturn false;\n\t}\n\n\tvar hasOwnConstructor = hasOwn.call(obj, 'constructor');\n\tvar hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');\n\t// Not own constructor property must be Object\n\tif (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {\n\t\treturn false;\n\t}\n\n\t// Own properties are enumerated firstly, so to speed up,\n\t// if last one is own, then all properties are own.\n\tvar key;\n\tfor (key in obj) { /**/ }\n\n\treturn typeof key === 'undefined' || hasOwn.call(obj, key);\n};\n\nmodule.exports = function extend() {\n\tvar options, name, src, copy, copyIsArray, clone;\n\tvar target = arguments[0];\n\tvar i = 1;\n\tvar length = arguments.length;\n\tvar deep = false;\n\n\t// Handle a deep copy situation\n\tif (typeof target === 'boolean') {\n\t\tdeep = target;\n\t\ttarget = arguments[1] || {};\n\t\t// skip the boolean and the target\n\t\ti = 2;\n\t}\n\tif (target == null || (typeof target !== 'object' && typeof target !== 'function')) {\n\t\ttarget = {};\n\t}\n\n\tfor (; i < length; ++i) {\n\t\toptions = arguments[i];\n\t\t// Only deal with non-null/undefined values\n\t\tif (options != null) {\n\t\t\t// Extend the base object\n\t\t\tfor (name in options) {\n\t\t\t\tsrc = target[name];\n\t\t\t\tcopy = options[name];\n\n\t\t\t\t// Prevent never-ending loop\n\t\t\t\tif (target !== copy) {\n\t\t\t\t\t// Recurse if we're merging plain objects or arrays\n\t\t\t\t\tif (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {\n\t\t\t\t\t\tif (copyIsArray) {\n\t\t\t\t\t\t\tcopyIsArray = false;\n\t\t\t\t\t\t\tclone = src && isArray(src) ? src : [];\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tclone = src && isPlainObject(src) ? src : {};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Never move original objects, clone them\n\t\t\t\t\t\ttarget[name] = extend(deep, clone, copy);\n\n\t\t\t\t\t// Don't bring in undefined values\n\t\t\t\t\t} else if (typeof copy !== 'undefined') {\n\t\t\t\t\t\ttarget[name] = copy;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Return the modified object\n\treturn target;\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/extend/index.js\n// module id = 2\n// module chunks = 0","import extend from 'extend';\nimport Delta from 'quill-delta';\nimport Parchment from 'parchment';\nimport Break from './break';\nimport Inline from './inline';\nimport TextBlot from './text';\n\n\nconst NEWLINE_LENGTH = 1;\n\n\nclass BlockEmbed extends Parchment.Embed {\n attach() {\n super.attach();\n this.attributes = new Parchment.Attributor.Store(this.domNode);\n }\n\n delta() {\n return new Delta().insert(this.value(), extend(this.formats(), this.attributes.values()));\n }\n\n format(name, value) {\n let attribute = Parchment.query(name, Parchment.Scope.BLOCK_ATTRIBUTE);\n if (attribute != null) {\n this.attributes.attribute(attribute, value);\n }\n }\n\n formatAt(index, length, name, value) {\n this.format(name, value);\n }\n\n insertAt(index, value, def) {\n if (typeof value === 'string' && value.endsWith('\\n')) {\n let block = Parchment.create(Block.blotName);\n this.parent.insertBefore(block, index === 0 ? this : this.next);\n block.insertAt(0, value.slice(0, -1));\n } else {\n super.insertAt(index, value, def);\n }\n }\n}\nBlockEmbed.scope = Parchment.Scope.BLOCK_BLOT;\n// It is important for cursor behavior BlockEmbeds use tags that are block level elements\n\n\nclass Block extends Parchment.Block {\n constructor(domNode) {\n super(domNode);\n this.cache = {};\n }\n\n delta() {\n if (this.cache.delta == null) {\n this.cache.delta = this.descendants(Parchment.Leaf).reduce((delta, leaf) => {\n if (leaf.length() === 0) {\n return delta;\n } else {\n return delta.insert(leaf.value(), bubbleFormats(leaf));\n }\n }, new Delta()).insert('\\n', bubbleFormats(this));\n }\n return this.cache.delta;\n }\n\n deleteAt(index, length) {\n super.deleteAt(index, length);\n this.cache = {};\n }\n\n formatAt(index, length, name, value) {\n if (length <= 0) return;\n if (Parchment.query(name, Parchment.Scope.BLOCK)) {\n if (index + length === this.length()) {\n this.format(name, value);\n }\n } else {\n super.formatAt(index, Math.min(length, this.length() - index - 1), name, value);\n }\n this.cache = {};\n }\n\n insertAt(index, value, def) {\n if (def != null) return super.insertAt(index, value, def);\n if (value.length === 0) return;\n let lines = value.split('\\n');\n let text = lines.shift();\n if (text.length > 0) {\n if (index < this.length() - 1 || this.children.tail == null) {\n super.insertAt(Math.min(index, this.length() - 1), text);\n } else {\n this.children.tail.insertAt(this.children.tail.length(), text);\n }\n this.cache = {};\n }\n let block = this;\n lines.reduce(function(index, line) {\n block = block.split(index, true);\n block.insertAt(0, line);\n return line.length;\n }, index + text.length);\n }\n\n insertBefore(blot, ref) {\n let head = this.children.head;\n super.insertBefore(blot, ref);\n if (head instanceof Break) {\n head.remove();\n }\n this.cache = {};\n }\n\n length() {\n if (this.cache.length == null) {\n this.cache.length = super.length() + NEWLINE_LENGTH;\n }\n return this.cache.length;\n }\n\n moveChildren(target, ref) {\n super.moveChildren(target, ref);\n this.cache = {};\n }\n\n optimize(context) {\n super.optimize(context);\n this.cache = {};\n }\n\n path(index) {\n return super.path(index, true);\n }\n\n removeChild(child) {\n super.removeChild(child);\n this.cache = {};\n }\n\n split(index, force = false) {\n if (force && (index === 0 || index >= this.length() - NEWLINE_LENGTH)) {\n let clone = this.clone();\n if (index === 0) {\n this.parent.insertBefore(clone, this);\n return this;\n } else {\n this.parent.insertBefore(clone, this.next);\n return clone;\n }\n } else {\n let next = super.split(index, force);\n this.cache = {};\n return next;\n }\n }\n}\nBlock.blotName = 'block';\nBlock.tagName = 'P';\nBlock.defaultChild = 'break';\nBlock.allowedChildren = [Inline, Parchment.Embed, TextBlot];\n\n\nfunction bubbleFormats(blot, formats = {}) {\n if (blot == null) return formats;\n if (typeof blot.formats === 'function') {\n formats = extend(formats, blot.formats());\n }\n if (blot.parent == null || blot.parent.blotName == 'scroll' || blot.parent.statics.scope !== blot.statics.scope) {\n return formats;\n }\n return bubbleFormats(blot.parent, formats);\n}\n\n\nexport { bubbleFormats, BlockEmbed, Block as default };\n\n\n\n// WEBPACK FOOTER //\n// ./blots/block.js","var diff = require('fast-diff');\nvar equal = require('deep-equal');\nvar extend = require('extend');\nvar op = require('./op');\n\n\nvar NULL_CHARACTER = String.fromCharCode(0); // Placeholder char for embed in diff()\n\n\nvar Delta = function (ops) {\n // Assume we are given a well formed ops\n if (Array.isArray(ops)) {\n this.ops = ops;\n } else if (ops != null && Array.isArray(ops.ops)) {\n this.ops = ops.ops;\n } else {\n this.ops = [];\n }\n};\n\n\nDelta.prototype.insert = function (text, attributes) {\n var newOp = {};\n if (text.length === 0) return this;\n newOp.insert = text;\n if (attributes != null && typeof attributes === 'object' && Object.keys(attributes).length > 0) {\n newOp.attributes = attributes;\n }\n return this.push(newOp);\n};\n\nDelta.prototype['delete'] = function (length) {\n if (length <= 0) return this;\n return this.push({ 'delete': length });\n};\n\nDelta.prototype.retain = function (length, attributes) {\n if (length <= 0) return this;\n var newOp = { retain: length };\n if (attributes != null && typeof attributes === 'object' && Object.keys(attributes).length > 0) {\n newOp.attributes = attributes;\n }\n return this.push(newOp);\n};\n\nDelta.prototype.push = function (newOp) {\n var index = this.ops.length;\n var lastOp = this.ops[index - 1];\n newOp = extend(true, {}, newOp);\n if (typeof lastOp === 'object') {\n if (typeof newOp['delete'] === 'number' && typeof lastOp['delete'] === 'number') {\n this.ops[index - 1] = { 'delete': lastOp['delete'] + newOp['delete'] };\n return this;\n }\n // Since it does not matter if we insert before or after deleting at the same index,\n // always prefer to insert first\n if (typeof lastOp['delete'] === 'number' && newOp.insert != null) {\n index -= 1;\n lastOp = this.ops[index - 1];\n if (typeof lastOp !== 'object') {\n this.ops.unshift(newOp);\n return this;\n }\n }\n if (equal(newOp.attributes, lastOp.attributes)) {\n if (typeof newOp.insert === 'string' && typeof lastOp.insert === 'string') {\n this.ops[index - 1] = { insert: lastOp.insert + newOp.insert };\n if (typeof newOp.attributes === 'object') this.ops[index - 1].attributes = newOp.attributes\n return this;\n } else if (typeof newOp.retain === 'number' && typeof lastOp.retain === 'number') {\n this.ops[index - 1] = { retain: lastOp.retain + newOp.retain };\n if (typeof newOp.attributes === 'object') this.ops[index - 1].attributes = newOp.attributes\n return this;\n }\n }\n }\n if (index === this.ops.length) {\n this.ops.push(newOp);\n } else {\n this.ops.splice(index, 0, newOp);\n }\n return this;\n};\n\nDelta.prototype.chop = function () {\n var lastOp = this.ops[this.ops.length - 1];\n if (lastOp && lastOp.retain && !lastOp.attributes) {\n this.ops.pop();\n }\n return this;\n};\n\nDelta.prototype.filter = function (predicate) {\n return this.ops.filter(predicate);\n};\n\nDelta.prototype.forEach = function (predicate) {\n this.ops.forEach(predicate);\n};\n\nDelta.prototype.map = function (predicate) {\n return this.ops.map(predicate);\n};\n\nDelta.prototype.partition = function (predicate) {\n var passed = [], failed = [];\n this.forEach(function(op) {\n var target = predicate(op) ? passed : failed;\n target.push(op);\n });\n return [passed, failed];\n};\n\nDelta.prototype.reduce = function (predicate, initial) {\n return this.ops.reduce(predicate, initial);\n};\n\nDelta.prototype.changeLength = function () {\n return this.reduce(function (length, elem) {\n if (elem.insert) {\n return length + op.length(elem);\n } else if (elem.delete) {\n return length - elem.delete;\n }\n return length;\n }, 0);\n};\n\nDelta.prototype.length = function () {\n return this.reduce(function (length, elem) {\n return length + op.length(elem);\n }, 0);\n};\n\nDelta.prototype.slice = function (start, end) {\n start = start || 0;\n if (typeof end !== 'number') end = Infinity;\n var ops = [];\n var iter = op.iterator(this.ops);\n var index = 0;\n while (index < end && iter.hasNext()) {\n var nextOp;\n if (index < start) {\n nextOp = iter.next(start - index);\n } else {\n nextOp = iter.next(end - index);\n ops.push(nextOp);\n }\n index += op.length(nextOp);\n }\n return new Delta(ops);\n};\n\n\nDelta.prototype.compose = function (other) {\n var thisIter = op.iterator(this.ops);\n var otherIter = op.iterator(other.ops);\n var delta = new Delta();\n while (thisIter.hasNext() || otherIter.hasNext()) {\n if (otherIter.peekType() === 'insert') {\n delta.push(otherIter.next());\n } else if (thisIter.peekType() === 'delete') {\n delta.push(thisIter.next());\n } else {\n var length = Math.min(thisIter.peekLength(), otherIter.peekLength());\n var thisOp = thisIter.next(length);\n var otherOp = otherIter.next(length);\n if (typeof otherOp.retain === 'number') {\n var newOp = {};\n if (typeof thisOp.retain === 'number') {\n newOp.retain = length;\n } else {\n newOp.insert = thisOp.insert;\n }\n // Preserve null when composing with a retain, otherwise remove it for inserts\n var attributes = op.attributes.compose(thisOp.attributes, otherOp.attributes, typeof thisOp.retain === 'number');\n if (attributes) newOp.attributes = attributes;\n delta.push(newOp);\n // Other op should be delete, we could be an insert or retain\n // Insert + delete cancels out\n } else if (typeof otherOp['delete'] === 'number' && typeof thisOp.retain === 'number') {\n delta.push(otherOp);\n }\n }\n }\n return delta.chop();\n};\n\nDelta.prototype.concat = function (other) {\n var delta = new Delta(this.ops.slice());\n if (other.ops.length > 0) {\n delta.push(other.ops[0]);\n delta.ops = delta.ops.concat(other.ops.slice(1));\n }\n return delta;\n};\n\nDelta.prototype.diff = function (other, index) {\n if (this.ops === other.ops) {\n return new Delta();\n }\n var strings = [this, other].map(function (delta) {\n return delta.map(function (op) {\n if (op.insert != null) {\n return typeof op.insert === 'string' ? op.insert : NULL_CHARACTER;\n }\n var prep = (delta === other) ? 'on' : 'with';\n throw new Error('diff() called ' + prep + ' non-document');\n }).join('');\n });\n var delta = new Delta();\n var diffResult = diff(strings[0], strings[1], index);\n var thisIter = op.iterator(this.ops);\n var otherIter = op.iterator(other.ops);\n diffResult.forEach(function (component) {\n var length = component[1].length;\n while (length > 0) {\n var opLength = 0;\n switch (component[0]) {\n case diff.INSERT:\n opLength = Math.min(otherIter.peekLength(), length);\n delta.push(otherIter.next(opLength));\n break;\n case diff.DELETE:\n opLength = Math.min(length, thisIter.peekLength());\n thisIter.next(opLength);\n delta['delete'](opLength);\n break;\n case diff.EQUAL:\n opLength = Math.min(thisIter.peekLength(), otherIter.peekLength(), length);\n var thisOp = thisIter.next(opLength);\n var otherOp = otherIter.next(opLength);\n if (equal(thisOp.insert, otherOp.insert)) {\n delta.retain(opLength, op.attributes.diff(thisOp.attributes, otherOp.attributes));\n } else {\n delta.push(otherOp)['delete'](opLength);\n }\n break;\n }\n length -= opLength;\n }\n });\n return delta.chop();\n};\n\nDelta.prototype.eachLine = function (predicate, newline) {\n newline = newline || '\\n';\n var iter = op.iterator(this.ops);\n var line = new Delta();\n var i = 0;\n while (iter.hasNext()) {\n if (iter.peekType() !== 'insert') return;\n var thisOp = iter.peek();\n var start = op.length(thisOp) - iter.peekLength();\n var index = typeof thisOp.insert === 'string' ?\n thisOp.insert.indexOf(newline, start) - start : -1;\n if (index < 0) {\n line.push(iter.next());\n } else if (index > 0) {\n line.push(iter.next(index));\n } else {\n if (predicate(line, iter.next(1).attributes || {}, i) === false) {\n return;\n }\n i += 1;\n line = new Delta();\n }\n }\n if (line.length() > 0) {\n predicate(line, {}, i);\n }\n};\n\nDelta.prototype.transform = function (other, priority) {\n priority = !!priority;\n if (typeof other === 'number') {\n return this.transformPosition(other, priority);\n }\n var thisIter = op.iterator(this.ops);\n var otherIter = op.iterator(other.ops);\n var delta = new Delta();\n while (thisIter.hasNext() || otherIter.hasNext()) {\n if (thisIter.peekType() === 'insert' && (priority || otherIter.peekType() !== 'insert')) {\n delta.retain(op.length(thisIter.next()));\n } else if (otherIter.peekType() === 'insert') {\n delta.push(otherIter.next());\n } else {\n var length = Math.min(thisIter.peekLength(), otherIter.peekLength());\n var thisOp = thisIter.next(length);\n var otherOp = otherIter.next(length);\n if (thisOp['delete']) {\n // Our delete either makes their delete redundant or removes their retain\n continue;\n } else if (otherOp['delete']) {\n delta.push(otherOp);\n } else {\n // We retain either their retain or insert\n delta.retain(length, op.attributes.transform(thisOp.attributes, otherOp.attributes, priority));\n }\n }\n }\n return delta.chop();\n};\n\nDelta.prototype.transformPosition = function (index, priority) {\n priority = !!priority;\n var thisIter = op.iterator(this.ops);\n var offset = 0;\n while (thisIter.hasNext() && offset <= index) {\n var length = thisIter.peekLength();\n var nextType = thisIter.peekType();\n thisIter.next();\n if (nextType === 'delete') {\n index -= Math.min(length, index - offset);\n continue;\n } else if (nextType === 'insert' && (offset < index || !priority)) {\n index += length;\n }\n offset += length;\n }\n return index;\n};\n\n\nmodule.exports = Delta;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/quill-delta/lib/delta.js\n// module id = 4\n// module chunks = 0","import Text from './text';\nimport Parchment from 'parchment';\n\n\nclass Inline extends Parchment.Inline {\n static compare(self, other) {\n let selfIndex = Inline.order.indexOf(self);\n let otherIndex = Inline.order.indexOf(other);\n if (selfIndex >= 0 || otherIndex >= 0) {\n return selfIndex - otherIndex;\n } else if (self === other) {\n return 0;\n } else if (self < other) {\n return -1;\n } else {\n return 1;\n }\n }\n\n formatAt(index, length, name, value) {\n if (Inline.compare(this.statics.blotName, name) < 0 && Parchment.query(name, Parchment.Scope.BLOT)) {\n let blot = this.isolate(index, length);\n if (value) {\n blot.wrap(name, value);\n }\n } else {\n super.formatAt(index, length, name, value);\n }\n }\n\n optimize(context) {\n super.optimize(context);\n if (this.parent instanceof Inline &&\n Inline.compare(this.statics.blotName, this.parent.statics.blotName) > 0) {\n let parent = this.parent.isolate(this.offset(), this.length());\n this.moveChildren(parent);\n parent.wrap(this);\n }\n }\n}\nInline.allowedChildren = [Inline, Parchment.Embed, Text];\n// Lower index means deeper in the DOM tree, since not found (-1) is for embeds\nInline.order = [\n 'cursor', 'inline', // Must be lower\n 'underline', 'strike', 'italic', 'bold', 'script',\n 'link', 'code' // Must be higher\n];\n\n\nexport default Inline;\n\n\n\n// WEBPACK FOOTER //\n// ./blots/inline.js","import './polyfill';\nimport Delta from 'quill-delta';\nimport Editor from './editor';\nimport Emitter from './emitter';\nimport Module from './module';\nimport Parchment from 'parchment';\nimport Selection, { Range } from './selection';\nimport extend from 'extend';\nimport logger from './logger';\nimport Theme from './theme';\n\nlet debug = logger('quill');\n\n\nclass Quill {\n static debug(limit) {\n if (limit === true) {\n limit = 'log';\n }\n logger.level(limit);\n }\n\n static find(node) {\n return node.__quill || Parchment.find(node);\n }\n\n static import(name) {\n if (this.imports[name] == null) {\n debug.error(`Cannot import ${name}. Are you sure it was registered?`);\n }\n return this.imports[name];\n }\n\n static register(path, target, overwrite = false) {\n if (typeof path !== 'string') {\n let name = path.attrName || path.blotName;\n if (typeof name === 'string') {\n // register(Blot | Attributor, overwrite)\n this.register('formats/' + name, path, target);\n } else {\n Object.keys(path).forEach((key) => {\n this.register(key, path[key], target);\n });\n }\n } else {\n if (this.imports[path] != null && !overwrite) {\n debug.warn(`Overwriting ${path} with`, target);\n }\n this.imports[path] = target;\n if ((path.startsWith('blots/') || path.startsWith('formats/')) &&\n target.blotName !== 'abstract') {\n Parchment.register(target);\n } else if (path.startsWith('modules') && typeof target.register === 'function') {\n target.register();\n }\n }\n }\n\n constructor(container, options = {}) {\n this.options = expandConfig(container, options);\n this.container = this.options.container;\n if (this.container == null) {\n return debug.error('Invalid Quill container', container);\n }\n if (this.options.debug) {\n Quill.debug(this.options.debug);\n }\n let html = this.container.innerHTML.trim();\n this.container.classList.add('ql-container');\n this.container.innerHTML = '';\n this.container.__quill = this;\n this.root = this.addContainer('ql-editor');\n this.root.classList.add('ql-blank');\n this.root.setAttribute('data-gramm', false);\n this.scrollingContainer = this.options.scrollingContainer || this.root;\n this.emitter = new Emitter();\n this.scroll = Parchment.create(this.root, {\n emitter: this.emitter,\n whitelist: this.options.formats\n });\n this.editor = new Editor(this.scroll);\n this.selection = new Selection(this.scroll, this.emitter);\n this.theme = new this.options.theme(this, this.options);\n this.keyboard = this.theme.addModule('keyboard');\n this.clipboard = this.theme.addModule('clipboard');\n this.history = this.theme.addModule('history');\n this.theme.init();\n this.emitter.on(Emitter.events.EDITOR_CHANGE, (type) => {\n if (type === Emitter.events.TEXT_CHANGE) {\n this.root.classList.toggle('ql-blank', this.editor.isBlank());\n }\n });\n this.emitter.on(Emitter.events.SCROLL_UPDATE, (source, mutations) => {\n let range = this.selection.lastRange;\n let index = range && range.length === 0 ? range.index : undefined;\n modify.call(this, () => {\n return this.editor.update(null, mutations, index);\n }, source);\n });\n let contents = this.clipboard.convert(`
${html}


`);\n this.setContents(contents);\n this.history.clear();\n if (this.options.placeholder) {\n this.root.setAttribute('data-placeholder', this.options.placeholder);\n }\n if (this.options.readOnly) {\n this.disable();\n }\n }\n\n addContainer(container, refNode = null) {\n if (typeof container === 'string') {\n let className = container;\n container = document.createElement('div');\n container.classList.add(className);\n }\n this.container.insertBefore(container, refNode);\n return container;\n }\n\n blur() {\n this.selection.setRange(null);\n }\n\n deleteText(index, length, source) {\n [index, length, , source] = overload(index, length, source);\n return modify.call(this, () => {\n return this.editor.deleteText(index, length);\n }, source, index, -1*length);\n }\n\n disable() {\n this.enable(false);\n }\n\n enable(enabled = true) {\n this.scroll.enable(enabled);\n this.container.classList.toggle('ql-disabled', !enabled);\n }\n\n focus() {\n let scrollTop = this.scrollingContainer.scrollTop;\n this.selection.focus();\n this.scrollingContainer.scrollTop = scrollTop;\n this.scrollIntoView();\n }\n\n format(name, value, source = Emitter.sources.API) {\n return modify.call(this, () => {\n let range = this.getSelection(true);\n let change = new Delta();\n if (range == null) {\n return change;\n } else if (Parchment.query(name, Parchment.Scope.BLOCK)) {\n change = this.editor.formatLine(range.index, range.length, { [name]: value });\n } else if (range.length === 0) {\n this.selection.format(name, value);\n return change;\n } else {\n change = this.editor.formatText(range.index, range.length, { [name]: value });\n }\n this.setSelection(range, Emitter.sources.SILENT);\n return change;\n }, source);\n }\n\n formatLine(index, length, name, value, source) {\n let formats;\n [index, length, formats, source] = overload(index, length, name, value, source);\n return modify.call(this, () => {\n return this.editor.formatLine(index, length, formats);\n }, source, index, 0);\n }\n\n formatText(index, length, name, value, source) {\n let formats;\n [index, length, formats, source] = overload(index, length, name, value, source);\n return modify.call(this, () => {\n return this.editor.formatText(index, length, formats);\n }, source, index, 0);\n }\n\n getBounds(index, length = 0) {\n let bounds;\n if (typeof index === 'number') {\n bounds = this.selection.getBounds(index, length);\n } else {\n bounds = this.selection.getBounds(index.index, index.length);\n }\n let containerBounds = this.container.getBoundingClientRect();\n return {\n bottom: bounds.bottom - containerBounds.top,\n height: bounds.height,\n left: bounds.left - containerBounds.left,\n right: bounds.right - containerBounds.left,\n top: bounds.top - containerBounds.top,\n width: bounds.width\n };\n }\n\n getContents(index = 0, length = this.getLength() - index) {\n [index, length] = overload(index, length);\n return this.editor.getContents(index, length);\n }\n\n getFormat(index = this.getSelection(true), length = 0) {\n if (typeof index === 'number') {\n return this.editor.getFormat(index, length);\n } else {\n return this.editor.getFormat(index.index, index.length);\n }\n }\n\n getIndex(blot) {\n return blot.offset(this.scroll);\n }\n\n getLength() {\n return this.scroll.length();\n }\n\n getLeaf(index) {\n return this.scroll.leaf(index);\n }\n\n getLine(index) {\n return this.scroll.line(index);\n }\n\n getLines(index = 0, length = Number.MAX_VALUE) {\n if (typeof index !== 'number') {\n return this.scroll.lines(index.index, index.length);\n } else {\n return this.scroll.lines(index, length);\n }\n }\n\n getModule(name) {\n return this.theme.modules[name];\n }\n\n getSelection(focus = false) {\n if (focus) this.focus();\n this.update(); // Make sure we access getRange with editor in consistent state\n return this.selection.getRange()[0];\n }\n\n getText(index = 0, length = this.getLength() - index) {\n [index, length] = overload(index, length);\n return this.editor.getText(index, length);\n }\n\n hasFocus() {\n return this.selection.hasFocus();\n }\n\n insertEmbed(index, embed, value, source = Quill.sources.API) {\n return modify.call(this, () => {\n return this.editor.insertEmbed(index, embed, value);\n }, source, index);\n }\n\n insertText(index, text, name, value, source) {\n let formats;\n [index, , formats, source] = overload(index, 0, name, value, source);\n return modify.call(this, () => {\n return this.editor.insertText(index, text, formats);\n }, source, index, text.length);\n }\n\n isEnabled() {\n return !this.container.classList.contains('ql-disabled');\n }\n\n off() {\n return this.emitter.off.apply(this.emitter, arguments);\n }\n\n on() {\n return this.emitter.on.apply(this.emitter, arguments);\n }\n\n once() {\n return this.emitter.once.apply(this.emitter, arguments);\n }\n\n pasteHTML(index, html, source) {\n this.clipboard.dangerouslyPasteHTML(index, html, source);\n }\n\n removeFormat(index, length, source) {\n [index, length, , source] = overload(index, length, source);\n return modify.call(this, () => {\n return this.editor.removeFormat(index, length);\n }, source, index);\n }\n\n scrollIntoView() {\n this.selection.scrollIntoView(this.scrollingContainer);\n }\n\n setContents(delta, source = Emitter.sources.API) {\n return modify.call(this, () => {\n delta = new Delta(delta);\n let length = this.getLength();\n let deleted = this.editor.deleteText(0, length);\n let applied = this.editor.applyDelta(delta);\n let lastOp = applied.ops[applied.ops.length - 1];\n if (lastOp != null && typeof(lastOp.insert) === 'string' && lastOp.insert[lastOp.insert.length-1] === '\\n') {\n this.editor.deleteText(this.getLength() - 1, 1);\n applied.delete(1);\n }\n let ret = deleted.compose(applied);\n return ret;\n }, source);\n }\n\n setSelection(index, length, source) {\n if (index == null) {\n this.selection.setRange(null, length || Quill.sources.API);\n } else {\n [index, length, , source] = overload(index, length, source);\n this.selection.setRange(new Range(index, length), source);\n if (source !== Emitter.sources.SILENT) {\n this.selection.scrollIntoView(this.scrollingContainer);\n }\n }\n }\n\n setText(text, source = Emitter.sources.API) {\n let delta = new Delta().insert(text);\n return this.setContents(delta, source);\n }\n\n update(source = Emitter.sources.USER) {\n let change = this.scroll.update(source); // Will update selection before selection.update() does if text changes\n this.selection.update(source);\n return change;\n }\n\n updateContents(delta, source = Emitter.sources.API) {\n return modify.call(this, () => {\n delta = new Delta(delta);\n return this.editor.applyDelta(delta, source);\n }, source, true);\n }\n}\nQuill.DEFAULTS = {\n bounds: null,\n formats: null,\n modules: {},\n placeholder: '',\n readOnly: false,\n scrollingContainer: null,\n strict: true,\n theme: 'default'\n};\nQuill.events = Emitter.events;\nQuill.sources = Emitter.sources;\n// eslint-disable-next-line no-undef\nQuill.version = typeof(QUILL_VERSION) === 'undefined' ? 'dev' : QUILL_VERSION;\n\nQuill.imports = {\n 'delta' : Delta,\n 'parchment' : Parchment,\n 'core/module' : Module,\n 'core/theme' : Theme\n};\n\n\nfunction expandConfig(container, userConfig) {\n userConfig = extend(true, {\n container: container,\n modules: {\n clipboard: true,\n keyboard: true,\n history: true\n }\n }, userConfig);\n if (!userConfig.theme || userConfig.theme === Quill.DEFAULTS.theme) {\n userConfig.theme = Theme;\n } else {\n userConfig.theme = Quill.import(`themes/${userConfig.theme}`);\n if (userConfig.theme == null) {\n throw new Error(`Invalid theme ${userConfig.theme}. Did you register it?`);\n }\n }\n let themeConfig = extend(true, {}, userConfig.theme.DEFAULTS);\n [themeConfig, userConfig].forEach(function(config) {\n config.modules = config.modules || {};\n Object.keys(config.modules).forEach(function(module) {\n if (config.modules[module] === true) {\n config.modules[module] = {};\n }\n });\n });\n let moduleNames = Object.keys(themeConfig.modules).concat(Object.keys(userConfig.modules));\n let moduleConfig = moduleNames.reduce(function(config, name) {\n let moduleClass = Quill.import(`modules/${name}`);\n if (moduleClass == null) {\n debug.error(`Cannot load ${name} module. Are you sure you registered it?`);\n } else {\n config[name] = moduleClass.DEFAULTS || {};\n }\n return config;\n }, {});\n // Special case toolbar shorthand\n if (userConfig.modules != null && userConfig.modules.toolbar &&\n userConfig.modules.toolbar.constructor !== Object) {\n userConfig.modules.toolbar = {\n container: userConfig.modules.toolbar\n };\n }\n userConfig = extend(true, {}, Quill.DEFAULTS, { modules: moduleConfig }, themeConfig, userConfig);\n ['bounds', 'container', 'scrollingContainer'].forEach(function(key) {\n if (typeof userConfig[key] === 'string') {\n userConfig[key] = document.querySelector(userConfig[key]);\n }\n });\n userConfig.modules = Object.keys(userConfig.modules).reduce(function(config, name) {\n if (userConfig.modules[name]) {\n config[name] = userConfig.modules[name];\n }\n return config;\n }, {});\n return userConfig;\n}\n\n// Handle selection preservation and TEXT_CHANGE emission\n// common to modification APIs\nfunction modify(modifier, source, index, shift) {\n if (this.options.strict && !this.isEnabled() && source === Emitter.sources.USER) {\n return new Delta();\n }\n let range = index == null ? null : this.getSelection();\n let oldDelta = this.editor.delta;\n let change = modifier();\n if (range != null) {\n if (index === true) index = range.index;\n if (shift == null) {\n range = shiftRange(range, change, source);\n } else if (shift !== 0) {\n range = shiftRange(range, index, shift, source);\n }\n this.setSelection(range, Emitter.sources.SILENT);\n }\n if (change.length() > 0) {\n let args = [Emitter.events.TEXT_CHANGE, change, oldDelta, source];\n this.emitter.emit(Emitter.events.EDITOR_CHANGE, ...args);\n if (source !== Emitter.sources.SILENT) {\n this.emitter.emit(...args);\n }\n }\n return change;\n}\n\nfunction overload(index, length, name, value, source) {\n let formats = {};\n if (typeof index.index === 'number' && typeof index.length === 'number') {\n // Allow for throwaway end (used by insertText/insertEmbed)\n if (typeof length !== 'number') {\n source = value, value = name, name = length, length = index.length, index = index.index;\n } else {\n length = index.length, index = index.index;\n }\n } else if (typeof length !== 'number') {\n source = value, value = name, name = length, length = 0;\n }\n // Handle format being object, two format name/value strings or excluded\n if (typeof name === 'object') {\n formats = name;\n source = value;\n } else if (typeof name === 'string') {\n if (value != null) {\n formats[name] = value;\n } else {\n source = name;\n }\n }\n // Handle optional source\n source = source || Emitter.sources.API;\n return [index, length, formats, source];\n}\n\nfunction shiftRange(range, index, length, source) {\n if (range == null) return null;\n let start, end;\n if (index instanceof Delta) {\n [start, end] = [range.index, range.index + range.length].map(function(pos) {\n return index.transformPosition(pos, source !== Emitter.sources.USER);\n });\n } else {\n [start, end] = [range.index, range.index + range.length].map(function(pos) {\n if (pos < index || (pos === index && source === Emitter.sources.USER)) return pos;\n if (length >= 0) {\n return pos + length;\n } else {\n return Math.max(index, pos + length);\n }\n });\n }\n return new Range(start, end - start);\n}\n\n\nexport { expandConfig, overload, Quill as default };\n\n\n\n// WEBPACK FOOTER //\n// ./core/quill.js","class Module {\n constructor(quill, options = {}) {\n this.quill = quill;\n this.options = options;\n }\n}\nModule.DEFAULTS = {};\n\n\nexport default Module;\n\n\n\n// WEBPACK FOOTER //\n// ./core/module.js","import Parchment from 'parchment';\n\nclass TextBlot extends Parchment.Text { }\n\nexport default TextBlot;\n\n\n\n// WEBPACK FOOTER //\n// ./blots/text.js","import EventEmitter from 'eventemitter3';\nimport logger from './logger';\n\nlet debug = logger('quill:events');\n\nconst EVENTS = ['selectionchange', 'mousedown', 'mouseup', 'click'];\n\nEVENTS.forEach(function(eventName) {\n document.addEventListener(eventName, (...args) => {\n [].slice.call(document.querySelectorAll('.ql-container')).forEach((node) => {\n // TODO use WeakMap\n if (node.__quill && node.__quill.emitter) {\n node.__quill.emitter.handleDOM(...args);\n }\n });\n });\n});\n\n\nclass Emitter extends EventEmitter {\n constructor() {\n super();\n this.listeners = {};\n this.on('error', debug.error);\n }\n\n emit() {\n debug.log.apply(debug, arguments);\n super.emit.apply(this, arguments);\n }\n\n handleDOM(event, ...args) {\n (this.listeners[event.type] || []).forEach(function({ node, handler }) {\n if (event.target === node || node.contains(event.target)) {\n handler(event, ...args);\n }\n });\n }\n\n listenDOM(eventName, node, handler) {\n if (!this.listeners[eventName]) {\n this.listeners[eventName] = [];\n }\n this.listeners[eventName].push({ node, handler })\n }\n}\n\nEmitter.events = {\n EDITOR_CHANGE : 'editor-change',\n SCROLL_BEFORE_UPDATE : 'scroll-before-update',\n SCROLL_OPTIMIZE : 'scroll-optimize',\n SCROLL_UPDATE : 'scroll-update',\n SELECTION_CHANGE : 'selection-change',\n TEXT_CHANGE : 'text-change'\n};\nEmitter.sources = {\n API : 'api',\n SILENT : 'silent',\n USER : 'user'\n};\n\n\nexport default Emitter;\n\n\n\n// WEBPACK FOOTER //\n// ./core/emitter.js","let levels = ['error', 'warn', 'log', 'info'];\nlet level = 'warn';\n\nfunction debug(method, ...args) {\n if (levels.indexOf(method) <= levels.indexOf(level)) {\n console[method](...args); // eslint-disable-line no-console\n }\n}\n\nfunction namespace(ns) {\n return levels.reduce(function(logger, method) {\n logger[method] = debug.bind(console, method, ns);\n return logger;\n }, {});\n}\n\ndebug.level = namespace.level = function(newLevel) {\n level = newLevel;\n};\n\n\nexport default namespace;\n\n\n\n// WEBPACK FOOTER //\n// ./core/logger.js","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar Registry = require(\"../registry\");\nvar Attributor = /** @class */ (function () {\n function Attributor(attrName, keyName, options) {\n if (options === void 0) { options = {}; }\n this.attrName = attrName;\n this.keyName = keyName;\n var attributeBit = Registry.Scope.TYPE & Registry.Scope.ATTRIBUTE;\n if (options.scope != null) {\n // Ignore type bits, force attribute bit\n this.scope = (options.scope & Registry.Scope.LEVEL) | attributeBit;\n }\n else {\n this.scope = Registry.Scope.ATTRIBUTE;\n }\n if (options.whitelist != null)\n this.whitelist = options.whitelist;\n }\n Attributor.keys = function (node) {\n return [].map.call(node.attributes, function (item) {\n return item.name;\n });\n };\n Attributor.prototype.add = function (node, value) {\n if (!this.canAdd(node, value))\n return false;\n node.setAttribute(this.keyName, value);\n return true;\n };\n Attributor.prototype.canAdd = function (node, value) {\n var match = Registry.query(node, Registry.Scope.BLOT & (this.scope | Registry.Scope.TYPE));\n if (match == null)\n return false;\n if (this.whitelist == null)\n return true;\n if (typeof value === 'string') {\n return this.whitelist.indexOf(value.replace(/[\"']/g, '')) > -1;\n }\n else {\n return this.whitelist.indexOf(value) > -1;\n }\n };\n Attributor.prototype.remove = function (node) {\n node.removeAttribute(this.keyName);\n };\n Attributor.prototype.value = function (node) {\n var value = node.getAttribute(this.keyName);\n if (this.canAdd(node, value) && value) {\n return value;\n }\n return '';\n };\n return Attributor;\n}());\nexports.default = Attributor;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/parchment/src/attributor/attributor.ts\n// module id = 11\n// module chunks = 0","var pSlice = Array.prototype.slice;\nvar objectKeys = require('./lib/keys.js');\nvar isArguments = require('./lib/is_arguments.js');\n\nvar deepEqual = module.exports = function (actual, expected, opts) {\n if (!opts) opts = {};\n // 7.1. All identical values are equivalent, as determined by ===.\n if (actual === expected) {\n return true;\n\n } else if (actual instanceof Date && expected instanceof Date) {\n return actual.getTime() === expected.getTime();\n\n // 7.3. Other pairs that do not both pass typeof value == 'object',\n // equivalence is determined by ==.\n } else if (!actual || !expected || typeof actual != 'object' && typeof expected != 'object') {\n return opts.strict ? actual === expected : actual == expected;\n\n // 7.4. For all other Object pairs, including Array objects, equivalence is\n // determined by having the same number of owned properties (as verified\n // with Object.prototype.hasOwnProperty.call), the same set of keys\n // (although not necessarily the same order), equivalent values for every\n // corresponding key, and an identical 'prototype' property. Note: this\n // accounts for both named and indexed properties on Arrays.\n } else {\n return objEquiv(actual, expected, opts);\n }\n}\n\nfunction isUndefinedOrNull(value) {\n return value === null || value === undefined;\n}\n\nfunction isBuffer (x) {\n if (!x || typeof x !== 'object' || typeof x.length !== 'number') return false;\n if (typeof x.copy !== 'function' || typeof x.slice !== 'function') {\n return false;\n }\n if (x.length > 0 && typeof x[0] !== 'number') return false;\n return true;\n}\n\nfunction objEquiv(a, b, opts) {\n var i, key;\n if (isUndefinedOrNull(a) || isUndefinedOrNull(b))\n return false;\n // an identical 'prototype' property.\n if (a.prototype !== b.prototype) return false;\n //~~~I've managed to break Object.keys through screwy arguments passing.\n // Converting to array solves the problem.\n if (isArguments(a)) {\n if (!isArguments(b)) {\n return false;\n }\n a = pSlice.call(a);\n b = pSlice.call(b);\n return deepEqual(a, b, opts);\n }\n if (isBuffer(a)) {\n if (!isBuffer(b)) {\n return false;\n }\n if (a.length !== b.length) return false;\n for (i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n }\n try {\n var ka = objectKeys(a),\n kb = objectKeys(b);\n } catch (e) {//happens when one is a string literal and the other isn't\n return false;\n }\n // having the same number of owned properties (keys incorporates\n // hasOwnProperty)\n if (ka.length != kb.length)\n return false;\n //the same set of keys (although not necessarily the same order),\n ka.sort();\n kb.sort();\n //~~~cheap key test\n for (i = ka.length - 1; i >= 0; i--) {\n if (ka[i] != kb[i])\n return false;\n }\n //equivalent values for every corresponding key, and\n //~~~possibly expensive deep test\n for (i = ka.length - 1; i >= 0; i--) {\n key = ka[i];\n if (!deepEqual(a[key], b[key], opts)) return false;\n }\n return typeof a === typeof b;\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/deep-equal/index.js\n// module id = 12\n// module chunks = 0","import Delta from 'quill-delta';\nimport Parchment from 'parchment';\nimport Block from '../blots/block';\nimport Inline from '../blots/inline';\nimport TextBlot from '../blots/text';\n\n\nclass Code extends Inline {}\nCode.blotName = 'code';\nCode.tagName = 'CODE';\n\n\nclass CodeBlock extends Block {\n static create(value) {\n let domNode = super.create(value);\n domNode.setAttribute('spellcheck', false);\n return domNode;\n }\n\n static formats() {\n return true;\n }\n\n delta() {\n let text = this.domNode.textContent;\n if (text.endsWith('\\n')) { // Should always be true\n text = text.slice(0, -1);\n }\n return text.split('\\n').reduce((delta, frag) => {\n return delta.insert(frag).insert('\\n', this.formats());\n }, new Delta());\n }\n\n format(name, value) {\n if (name === this.statics.blotName && value) return;\n let [text, ] = this.descendant(TextBlot, this.length() - 1);\n if (text != null) {\n text.deleteAt(text.length() - 1, 1);\n }\n super.format(name, value);\n }\n\n formatAt(index, length, name, value) {\n if (length === 0) return;\n if (Parchment.query(name, Parchment.Scope.BLOCK) == null ||\n (name === this.statics.blotName && value === this.statics.formats(this.domNode))) {\n return;\n }\n let nextNewline = this.newlineIndex(index);\n if (nextNewline < 0 || nextNewline >= index + length) return;\n let prevNewline = this.newlineIndex(index, true) + 1;\n let isolateLength = nextNewline - prevNewline + 1;\n let blot = this.isolate(prevNewline, isolateLength);\n let next = blot.next;\n blot.format(name, value);\n if (next instanceof CodeBlock) {\n next.formatAt(0, index - prevNewline + length - isolateLength, name, value);\n }\n }\n\n insertAt(index, value, def) {\n if (def != null) return;\n let [text, offset] = this.descendant(TextBlot, index);\n text.insertAt(offset, value);\n }\n\n length() {\n let length = this.domNode.textContent.length;\n if (!this.domNode.textContent.endsWith('\\n')) {\n return length + 1;\n }\n return length;\n }\n\n newlineIndex(searchIndex, reverse = false) {\n if (!reverse) {\n let offset = this.domNode.textContent.slice(searchIndex).indexOf('\\n');\n return offset > -1 ? searchIndex + offset : -1;\n } else {\n return this.domNode.textContent.slice(0, searchIndex).lastIndexOf('\\n');\n }\n }\n\n optimize(context) {\n if (!this.domNode.textContent.endsWith('\\n')) {\n this.appendChild(Parchment.create('text', '\\n'));\n }\n super.optimize(context);\n let next = this.next;\n if (next != null && next.prev === this &&\n next.statics.blotName === this.statics.blotName &&\n this.statics.formats(this.domNode) === next.statics.formats(next.domNode)) {\n next.optimize(context);\n next.moveChildren(this);\n next.remove();\n }\n }\n\n replace(target) {\n super.replace(target);\n [].slice.call(this.domNode.querySelectorAll('*')).forEach(function(node) {\n let blot = Parchment.find(node);\n if (blot == null) {\n node.parentNode.removeChild(node);\n } else if (blot instanceof Parchment.Embed) {\n blot.remove();\n } else {\n blot.unwrap();\n }\n });\n }\n}\nCodeBlock.blotName = 'code-block';\nCodeBlock.tagName = 'PRE';\nCodeBlock.TAB = ' ';\n\n\nexport { Code, CodeBlock as default };\n\n\n\n// WEBPACK FOOTER //\n// ./formats/code.js","import Parchment from 'parchment';\n\n\nclass Break extends Parchment.Embed {\n static value() {\n return undefined;\n }\n\n insertInto(parent, ref) {\n if (parent.children.length === 0) {\n super.insertInto(parent, ref);\n } else {\n this.remove();\n }\n }\n\n length() {\n return 0;\n }\n\n value() {\n return '';\n }\n}\nBreak.blotName = 'break';\nBreak.tagName = 'BR';\n\n\nexport default Break;\n\n\n\n// WEBPACK FOOTER //\n// ./blots/break.js","import Inline from '../blots/inline';\n\n\nclass Link extends Inline {\n static create(value) {\n let node = super.create(value);\n value = this.sanitize(value);\n node.setAttribute('href', value);\n node.setAttribute('target', '_blank');\n return node;\n }\n\n static formats(domNode) {\n return domNode.getAttribute('href');\n }\n\n static sanitize(url) {\n return sanitize(url, this.PROTOCOL_WHITELIST) ? url : this.SANITIZED_URL;\n }\n\n format(name, value) {\n if (name !== this.statics.blotName || !value) return super.format(name, value);\n value = this.constructor.sanitize(value);\n this.domNode.setAttribute('href', value);\n }\n}\nLink.blotName = 'link';\nLink.tagName = 'A';\nLink.SANITIZED_URL = 'about:blank';\nLink.PROTOCOL_WHITELIST = ['http', 'https', 'mailto', 'tel'];\n\n\nfunction sanitize(url, protocols) {\n let anchor = document.createElement('a');\n anchor.href = url;\n let protocol = anchor.href.slice(0, anchor.href.indexOf(':'));\n return protocols.indexOf(protocol) > -1;\n}\n\n\nexport { Link as default, sanitize };\n\n\n\n// WEBPACK FOOTER //\n// ./formats/link.js","import Keyboard from '../modules/keyboard';\nimport DropdownIcon from '../assets/icons/dropdown.svg';\n\nlet optionsCounter = 0;\n\nfunction toggleAriaAttribute(element, attribute) {\n element.setAttribute(attribute, !(element.getAttribute(attribute) === 'true'));\n}\n\nclass Picker {\n constructor(select) {\n this.select = select;\n this.container = document.createElement('span');\n this.buildPicker();\n this.select.style.display = 'none';\n this.select.parentNode.insertBefore(this.container, this.select);\n\n this.label.addEventListener('mousedown', () => {\n this.togglePicker();\n });\n this.label.addEventListener('keydown', (event) => {\n switch(event.keyCode) {\n // Allows the \"Enter\" key to open the picker\n case Keyboard.keys.ENTER:\n this.togglePicker();\n break;\n\n // Allows the \"Escape\" key to close the picker\n case Keyboard.keys.ESCAPE:\n this.escape();\n event.preventDefault();\n break;\n default:\n }\n });\n this.select.addEventListener('change', this.update.bind(this));\n }\n\n togglePicker() {\n this.container.classList.toggle('ql-expanded');\n // Toggle aria-expanded and aria-hidden to make the picker accessible\n toggleAriaAttribute(this.label, 'aria-expanded');\n toggleAriaAttribute(this.options, 'aria-hidden');\n }\n\n buildItem(option) {\n let item = document.createElement('span');\n item.tabIndex = '0';\n item.setAttribute('role', 'button');\n\n item.classList.add('ql-picker-item');\n if (option.hasAttribute('value')) {\n item.setAttribute('data-value', option.getAttribute('value'));\n }\n if (option.textContent) {\n item.setAttribute('data-label', option.textContent);\n }\n item.addEventListener('click', () => {\n this.selectItem(item, true);\n });\n item.addEventListener('keydown', (event) => {\n switch(event.keyCode) {\n // Allows the \"Enter\" key to select an item\n case Keyboard.keys.ENTER:\n this.selectItem(item, true);\n event.preventDefault();\n break;\n\n // Allows the \"Escape\" key to close the picker\n case Keyboard.keys.ESCAPE:\n this.escape();\n event.preventDefault();\n break;\n default:\n }\n });\n\n return item;\n }\n\n buildLabel() {\n let label = document.createElement('span');\n label.classList.add('ql-picker-label');\n label.innerHTML = DropdownIcon;\n label.tabIndex = '0';\n label.setAttribute('role', 'button');\n label.setAttribute('aria-expanded', 'false');\n this.container.appendChild(label);\n return label;\n }\n\n buildOptions() {\n let options = document.createElement('span');\n options.classList.add('ql-picker-options');\n\n // Don't want screen readers to read this until options are visible\n options.setAttribute('aria-hidden', 'true');\n options.tabIndex = '-1';\n\n // Need a unique id for aria-controls\n options.id = `ql-picker-options-${optionsCounter}`;\n optionsCounter += 1;\n this.label.setAttribute('aria-controls', options.id);\n\n this.options = options;\n\n [].slice.call(this.select.options).forEach((option) => {\n let item = this.buildItem(option);\n options.appendChild(item);\n if (option.selected === true) {\n this.selectItem(item);\n }\n });\n this.container.appendChild(options);\n }\n\n buildPicker() {\n [].slice.call(this.select.attributes).forEach((item) => {\n this.container.setAttribute(item.name, item.value);\n });\n this.container.classList.add('ql-picker');\n this.label = this.buildLabel();\n this.buildOptions();\n }\n\n escape() {\n // Close menu and return focus to trigger label\n this.close();\n // Need setTimeout for accessibility to ensure that the browser executes\n // focus on the next process thread and after any DOM content changes\n setTimeout(() => this.label.focus(), 1);\n }\n\n close() {\n this.container.classList.remove('ql-expanded');\n this.label.setAttribute('aria-expanded', 'false');\n this.options.setAttribute('aria-hidden', 'true');\n }\n\n selectItem(item, trigger = false) {\n let selected = this.container.querySelector('.ql-selected');\n if (item === selected) return;\n if (selected != null) {\n selected.classList.remove('ql-selected');\n }\n if (item == null) return;\n item.classList.add('ql-selected');\n this.select.selectedIndex = [].indexOf.call(item.parentNode.children, item);\n if (item.hasAttribute('data-value')) {\n this.label.setAttribute('data-value', item.getAttribute('data-value'));\n } else {\n this.label.removeAttribute('data-value');\n }\n if (item.hasAttribute('data-label')) {\n this.label.setAttribute('data-label', item.getAttribute('data-label'));\n } else {\n this.label.removeAttribute('data-label');\n }\n if (trigger) {\n if (typeof Event === 'function') {\n this.select.dispatchEvent(new Event('change'));\n } else if (typeof Event === 'object') { // IE11\n let event = document.createEvent('Event');\n event.initEvent('change', true, true);\n this.select.dispatchEvent(event);\n }\n this.close();\n }\n }\n\n update() {\n let option;\n if (this.select.selectedIndex > -1) {\n let item = this.container.querySelector('.ql-picker-options').children[this.select.selectedIndex];\n option = this.select.options[this.select.selectedIndex];\n this.selectItem(item);\n } else {\n this.selectItem(null);\n }\n let isActive = option != null && option !== this.select.querySelector('option[selected]');\n this.label.classList.toggle('ql-active', isActive);\n }\n}\n\n\nexport default Picker;\n\n\n\n// WEBPACK FOOTER //\n// ./ui/picker.js","\"use strict\";\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar linked_list_1 = require(\"../../collection/linked-list\");\nvar shadow_1 = require(\"./shadow\");\nvar Registry = require(\"../../registry\");\nvar ContainerBlot = /** @class */ (function (_super) {\n __extends(ContainerBlot, _super);\n function ContainerBlot(domNode) {\n var _this = _super.call(this, domNode) || this;\n _this.build();\n return _this;\n }\n ContainerBlot.prototype.appendChild = function (other) {\n this.insertBefore(other);\n };\n ContainerBlot.prototype.attach = function () {\n _super.prototype.attach.call(this);\n this.children.forEach(function (child) {\n child.attach();\n });\n };\n ContainerBlot.prototype.build = function () {\n var _this = this;\n this.children = new linked_list_1.default();\n // Need to be reversed for if DOM nodes already in order\n [].slice\n .call(this.domNode.childNodes)\n .reverse()\n .forEach(function (node) {\n try {\n var child = makeBlot(node);\n _this.insertBefore(child, _this.children.head || undefined);\n }\n catch (err) {\n if (err instanceof Registry.ParchmentError)\n return;\n else\n throw err;\n }\n });\n };\n ContainerBlot.prototype.deleteAt = function (index, length) {\n if (index === 0 && length === this.length()) {\n return this.remove();\n }\n this.children.forEachAt(index, length, function (child, offset, length) {\n child.deleteAt(offset, length);\n });\n };\n ContainerBlot.prototype.descendant = function (criteria, index) {\n var _a = this.children.find(index), child = _a[0], offset = _a[1];\n if ((criteria.blotName == null && criteria(child)) ||\n (criteria.blotName != null && child instanceof criteria)) {\n return [child, offset];\n }\n else if (child instanceof ContainerBlot) {\n return child.descendant(criteria, offset);\n }\n else {\n return [null, -1];\n }\n };\n ContainerBlot.prototype.descendants = function (criteria, index, length) {\n if (index === void 0) { index = 0; }\n if (length === void 0) { length = Number.MAX_VALUE; }\n var descendants = [];\n var lengthLeft = length;\n this.children.forEachAt(index, length, function (child, index, length) {\n if ((criteria.blotName == null && criteria(child)) ||\n (criteria.blotName != null && child instanceof criteria)) {\n descendants.push(child);\n }\n if (child instanceof ContainerBlot) {\n descendants = descendants.concat(child.descendants(criteria, index, lengthLeft));\n }\n lengthLeft -= length;\n });\n return descendants;\n };\n ContainerBlot.prototype.detach = function () {\n this.children.forEach(function (child) {\n child.detach();\n });\n _super.prototype.detach.call(this);\n };\n ContainerBlot.prototype.formatAt = function (index, length, name, value) {\n this.children.forEachAt(index, length, function (child, offset, length) {\n child.formatAt(offset, length, name, value);\n });\n };\n ContainerBlot.prototype.insertAt = function (index, value, def) {\n var _a = this.children.find(index), child = _a[0], offset = _a[1];\n if (child) {\n child.insertAt(offset, value, def);\n }\n else {\n var blot = def == null ? Registry.create('text', value) : Registry.create(value, def);\n this.appendChild(blot);\n }\n };\n ContainerBlot.prototype.insertBefore = function (childBlot, refBlot) {\n if (this.statics.allowedChildren != null &&\n !this.statics.allowedChildren.some(function (child) {\n return childBlot instanceof child;\n })) {\n throw new Registry.ParchmentError(\"Cannot insert \" + childBlot.statics.blotName + \" into \" + this.statics.blotName);\n }\n childBlot.insertInto(this, refBlot);\n };\n ContainerBlot.prototype.length = function () {\n return this.children.reduce(function (memo, child) {\n return memo + child.length();\n }, 0);\n };\n ContainerBlot.prototype.moveChildren = function (targetParent, refNode) {\n this.children.forEach(function (child) {\n targetParent.insertBefore(child, refNode);\n });\n };\n ContainerBlot.prototype.optimize = function (context) {\n _super.prototype.optimize.call(this, context);\n if (this.children.length === 0) {\n if (this.statics.defaultChild != null) {\n var child = Registry.create(this.statics.defaultChild);\n this.appendChild(child);\n child.optimize(context);\n }\n else {\n this.remove();\n }\n }\n };\n ContainerBlot.prototype.path = function (index, inclusive) {\n if (inclusive === void 0) { inclusive = false; }\n var _a = this.children.find(index, inclusive), child = _a[0], offset = _a[1];\n var position = [[this, index]];\n if (child instanceof ContainerBlot) {\n return position.concat(child.path(offset, inclusive));\n }\n else if (child != null) {\n position.push([child, offset]);\n }\n return position;\n };\n ContainerBlot.prototype.removeChild = function (child) {\n this.children.remove(child);\n };\n ContainerBlot.prototype.replace = function (target) {\n if (target instanceof ContainerBlot) {\n target.moveChildren(this);\n }\n _super.prototype.replace.call(this, target);\n };\n ContainerBlot.prototype.split = function (index, force) {\n if (force === void 0) { force = false; }\n if (!force) {\n if (index === 0)\n return this;\n if (index === this.length())\n return this.next;\n }\n var after = this.clone();\n this.parent.insertBefore(after, this.next);\n this.children.forEachAt(index, this.length(), function (child, offset, length) {\n child = child.split(offset, force);\n after.appendChild(child);\n });\n return after;\n };\n ContainerBlot.prototype.unwrap = function () {\n this.moveChildren(this.parent, this.next);\n this.remove();\n };\n ContainerBlot.prototype.update = function (mutations, context) {\n var _this = this;\n var addedNodes = [];\n var removedNodes = [];\n mutations.forEach(function (mutation) {\n if (mutation.target === _this.domNode && mutation.type === 'childList') {\n addedNodes.push.apply(addedNodes, mutation.addedNodes);\n removedNodes.push.apply(removedNodes, mutation.removedNodes);\n }\n });\n removedNodes.forEach(function (node) {\n // Check node has actually been removed\n // One exception is Chrome does not immediately remove IFRAMEs\n // from DOM but MutationRecord is correct in its reported removal\n if (node.parentNode != null &&\n // @ts-ignore\n node.tagName !== 'IFRAME' &&\n document.body.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINED_BY) {\n return;\n }\n var blot = Registry.find(node);\n if (blot == null)\n return;\n if (blot.domNode.parentNode == null || blot.domNode.parentNode === _this.domNode) {\n blot.detach();\n }\n });\n addedNodes\n .filter(function (node) {\n return node.parentNode == _this.domNode;\n })\n .sort(function (a, b) {\n if (a === b)\n return 0;\n if (a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING) {\n return 1;\n }\n return -1;\n })\n .forEach(function (node) {\n var refBlot = null;\n if (node.nextSibling != null) {\n refBlot = Registry.find(node.nextSibling);\n }\n var blot = makeBlot(node);\n if (blot.next != refBlot || blot.next == null) {\n if (blot.parent != null) {\n blot.parent.removeChild(_this);\n }\n _this.insertBefore(blot, refBlot || undefined);\n }\n });\n };\n return ContainerBlot;\n}(shadow_1.default));\nfunction makeBlot(node) {\n var blot = Registry.find(node);\n if (blot == null) {\n try {\n blot = Registry.create(node);\n }\n catch (e) {\n blot = Registry.create(Registry.Scope.INLINE);\n [].slice.call(node.childNodes).forEach(function (child) {\n // @ts-ignore\n blot.domNode.appendChild(child);\n });\n if (node.parentNode) {\n node.parentNode.replaceChild(blot.domNode, node);\n }\n blot.attach();\n }\n }\n return blot;\n}\nexports.default = ContainerBlot;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/parchment/src/blot/abstract/container.ts\n// module id = 17\n// module chunks = 0","\"use strict\";\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar attributor_1 = require(\"../../attributor/attributor\");\nvar store_1 = require(\"../../attributor/store\");\nvar container_1 = require(\"./container\");\nvar Registry = require(\"../../registry\");\nvar FormatBlot = /** @class */ (function (_super) {\n __extends(FormatBlot, _super);\n function FormatBlot(domNode) {\n var _this = _super.call(this, domNode) || this;\n _this.attributes = new store_1.default(_this.domNode);\n return _this;\n }\n FormatBlot.formats = function (domNode) {\n if (typeof this.tagName === 'string') {\n return true;\n }\n else if (Array.isArray(this.tagName)) {\n return domNode.tagName.toLowerCase();\n }\n return undefined;\n };\n FormatBlot.prototype.format = function (name, value) {\n var format = Registry.query(name);\n if (format instanceof attributor_1.default) {\n this.attributes.attribute(format, value);\n }\n else if (value) {\n if (format != null && (name !== this.statics.blotName || this.formats()[name] !== value)) {\n this.replaceWith(name, value);\n }\n }\n };\n FormatBlot.prototype.formats = function () {\n var formats = this.attributes.values();\n var format = this.statics.formats(this.domNode);\n if (format != null) {\n formats[this.statics.blotName] = format;\n }\n return formats;\n };\n FormatBlot.prototype.replaceWith = function (name, value) {\n var replacement = _super.prototype.replaceWith.call(this, name, value);\n this.attributes.copy(replacement);\n return replacement;\n };\n FormatBlot.prototype.update = function (mutations, context) {\n var _this = this;\n _super.prototype.update.call(this, mutations, context);\n if (mutations.some(function (mutation) {\n return mutation.target === _this.domNode && mutation.type === 'attributes';\n })) {\n this.attributes.build();\n }\n };\n FormatBlot.prototype.wrap = function (name, value) {\n var wrapper = _super.prototype.wrap.call(this, name, value);\n if (wrapper instanceof FormatBlot && wrapper.statics.scope === this.statics.scope) {\n this.attributes.move(wrapper);\n }\n return wrapper;\n };\n return FormatBlot;\n}(container_1.default));\nexports.default = FormatBlot;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/parchment/src/blot/abstract/format.ts\n// module id = 18\n// module chunks = 0","\"use strict\";\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar shadow_1 = require(\"./shadow\");\nvar Registry = require(\"../../registry\");\nvar LeafBlot = /** @class */ (function (_super) {\n __extends(LeafBlot, _super);\n function LeafBlot() {\n return _super !== null && _super.apply(this, arguments) || this;\n }\n LeafBlot.value = function (domNode) {\n return true;\n };\n LeafBlot.prototype.index = function (node, offset) {\n if (this.domNode === node ||\n this.domNode.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_CONTAINED_BY) {\n return Math.min(offset, 1);\n }\n return -1;\n };\n LeafBlot.prototype.position = function (index, inclusive) {\n var offset = [].indexOf.call(this.parent.domNode.childNodes, this.domNode);\n if (index > 0)\n offset += 1;\n return [this.parent.domNode, offset];\n };\n LeafBlot.prototype.value = function () {\n return _a = {}, _a[this.statics.blotName] = this.statics.value(this.domNode) || true, _a;\n var _a;\n };\n LeafBlot.scope = Registry.Scope.INLINE_BLOT;\n return LeafBlot;\n}(shadow_1.default));\nexports.default = LeafBlot;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/parchment/src/blot/abstract/leaf.ts\n// module id = 19\n// module chunks = 0","var equal = require('deep-equal');\nvar extend = require('extend');\n\n\nvar lib = {\n attributes: {\n compose: function (a, b, keepNull) {\n if (typeof a !== 'object') a = {};\n if (typeof b !== 'object') b = {};\n var attributes = extend(true, {}, b);\n if (!keepNull) {\n attributes = Object.keys(attributes).reduce(function (copy, key) {\n if (attributes[key] != null) {\n copy[key] = attributes[key];\n }\n return copy;\n }, {});\n }\n for (var key in a) {\n if (a[key] !== undefined && b[key] === undefined) {\n attributes[key] = a[key];\n }\n }\n return Object.keys(attributes).length > 0 ? attributes : undefined;\n },\n\n diff: function(a, b) {\n if (typeof a !== 'object') a = {};\n if (typeof b !== 'object') b = {};\n var attributes = Object.keys(a).concat(Object.keys(b)).reduce(function (attributes, key) {\n if (!equal(a[key], b[key])) {\n attributes[key] = b[key] === undefined ? null : b[key];\n }\n return attributes;\n }, {});\n return Object.keys(attributes).length > 0 ? attributes : undefined;\n },\n\n transform: function (a, b, priority) {\n if (typeof a !== 'object') return b;\n if (typeof b !== 'object') return undefined;\n if (!priority) return b; // b simply overwrites us without priority\n var attributes = Object.keys(b).reduce(function (attributes, key) {\n if (a[key] === undefined) attributes[key] = b[key]; // null is a valid value\n return attributes;\n }, {});\n return Object.keys(attributes).length > 0 ? attributes : undefined;\n }\n },\n\n iterator: function (ops) {\n return new Iterator(ops);\n },\n\n length: function (op) {\n if (typeof op['delete'] === 'number') {\n return op['delete'];\n } else if (typeof op.retain === 'number') {\n return op.retain;\n } else {\n return typeof op.insert === 'string' ? op.insert.length : 1;\n }\n }\n};\n\n\nfunction Iterator(ops) {\n this.ops = ops;\n this.index = 0;\n this.offset = 0;\n};\n\nIterator.prototype.hasNext = function () {\n return this.peekLength() < Infinity;\n};\n\nIterator.prototype.next = function (length) {\n if (!length) length = Infinity;\n var nextOp = this.ops[this.index];\n if (nextOp) {\n var offset = this.offset;\n var opLength = lib.length(nextOp)\n if (length >= opLength - offset) {\n length = opLength - offset;\n this.index += 1;\n this.offset = 0;\n } else {\n this.offset += length;\n }\n if (typeof nextOp['delete'] === 'number') {\n return { 'delete': length };\n } else {\n var retOp = {};\n if (nextOp.attributes) {\n retOp.attributes = nextOp.attributes;\n }\n if (typeof nextOp.retain === 'number') {\n retOp.retain = length;\n } else if (typeof nextOp.insert === 'string') {\n retOp.insert = nextOp.insert.substr(offset, length);\n } else {\n // offset should === 0, length should === 1\n retOp.insert = nextOp.insert;\n }\n return retOp;\n }\n } else {\n return { retain: Infinity };\n }\n};\n\nIterator.prototype.peek = function () {\n return this.ops[this.index];\n};\n\nIterator.prototype.peekLength = function () {\n if (this.ops[this.index]) {\n // Should never return 0 if our index is being managed correctly\n return lib.length(this.ops[this.index]) - this.offset;\n } else {\n return Infinity;\n }\n};\n\nIterator.prototype.peekType = function () {\n if (this.ops[this.index]) {\n if (typeof this.ops[this.index]['delete'] === 'number') {\n return 'delete';\n } else if (typeof this.ops[this.index].retain === 'number') {\n return 'retain';\n } else {\n return 'insert';\n }\n }\n return 'retain';\n};\n\n\nmodule.exports = lib;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/quill-delta/lib/op.js\n// module id = 20\n// module chunks = 0","var clone = (function() {\n'use strict';\n\nfunction _instanceof(obj, type) {\n return type != null && obj instanceof type;\n}\n\nvar nativeMap;\ntry {\n nativeMap = Map;\n} catch(_) {\n // maybe a reference error because no `Map`. Give it a dummy value that no\n // value will ever be an instanceof.\n nativeMap = function() {};\n}\n\nvar nativeSet;\ntry {\n nativeSet = Set;\n} catch(_) {\n nativeSet = function() {};\n}\n\nvar nativePromise;\ntry {\n nativePromise = Promise;\n} catch(_) {\n nativePromise = function() {};\n}\n\n/**\n * Clones (copies) an Object using deep copying.\n *\n * This function supports circular references by default, but if you are certain\n * there are no circular references in your object, you can save some CPU time\n * by calling clone(obj, false).\n *\n * Caution: if `circular` is false and `parent` contains circular references,\n * your program may enter an infinite loop and crash.\n *\n * @param `parent` - the object to be cloned\n * @param `circular` - set to true if the object to be cloned may contain\n * circular references. (optional - true by default)\n * @param `depth` - set to a number if the object is only to be cloned to\n * a particular depth. (optional - defaults to Infinity)\n * @param `prototype` - sets the prototype to be used when cloning an object.\n * (optional - defaults to parent prototype).\n * @param `includeNonEnumerable` - set to true if the non-enumerable properties\n * should be cloned as well. Non-enumerable properties on the prototype\n * chain will be ignored. (optional - false by default)\n*/\nfunction clone(parent, circular, depth, prototype, includeNonEnumerable) {\n if (typeof circular === 'object') {\n depth = circular.depth;\n prototype = circular.prototype;\n includeNonEnumerable = circular.includeNonEnumerable;\n circular = circular.circular;\n }\n // maintain two arrays for circular references, where corresponding parents\n // and children have the same index\n var allParents = [];\n var allChildren = [];\n\n var useBuffer = typeof Buffer != 'undefined';\n\n if (typeof circular == 'undefined')\n circular = true;\n\n if (typeof depth == 'undefined')\n depth = Infinity;\n\n // recurse this function so we don't reset allParents and allChildren\n function _clone(parent, depth) {\n // cloning null always returns null\n if (parent === null)\n return null;\n\n if (depth === 0)\n return parent;\n\n var child;\n var proto;\n if (typeof parent != 'object') {\n return parent;\n }\n\n if (_instanceof(parent, nativeMap)) {\n child = new nativeMap();\n } else if (_instanceof(parent, nativeSet)) {\n child = new nativeSet();\n } else if (_instanceof(parent, nativePromise)) {\n child = new nativePromise(function (resolve, reject) {\n parent.then(function(value) {\n resolve(_clone(value, depth - 1));\n }, function(err) {\n reject(_clone(err, depth - 1));\n });\n });\n } else if (clone.__isArray(parent)) {\n child = [];\n } else if (clone.__isRegExp(parent)) {\n child = new RegExp(parent.source, __getRegExpFlags(parent));\n if (parent.lastIndex) child.lastIndex = parent.lastIndex;\n } else if (clone.__isDate(parent)) {\n child = new Date(parent.getTime());\n } else if (useBuffer && Buffer.isBuffer(parent)) {\n child = new Buffer(parent.length);\n parent.copy(child);\n return child;\n } else if (_instanceof(parent, Error)) {\n child = Object.create(parent);\n } else {\n if (typeof prototype == 'undefined') {\n proto = Object.getPrototypeOf(parent);\n child = Object.create(proto);\n }\n else {\n child = Object.create(prototype);\n proto = prototype;\n }\n }\n\n if (circular) {\n var index = allParents.indexOf(parent);\n\n if (index != -1) {\n return allChildren[index];\n }\n allParents.push(parent);\n allChildren.push(child);\n }\n\n if (_instanceof(parent, nativeMap)) {\n parent.forEach(function(value, key) {\n var keyChild = _clone(key, depth - 1);\n var valueChild = _clone(value, depth - 1);\n child.set(keyChild, valueChild);\n });\n }\n if (_instanceof(parent, nativeSet)) {\n parent.forEach(function(value) {\n var entryChild = _clone(value, depth - 1);\n child.add(entryChild);\n });\n }\n\n for (var i in parent) {\n var attrs;\n if (proto) {\n attrs = Object.getOwnPropertyDescriptor(proto, i);\n }\n\n if (attrs && attrs.set == null) {\n continue;\n }\n child[i] = _clone(parent[i], depth - 1);\n }\n\n if (Object.getOwnPropertySymbols) {\n var symbols = Object.getOwnPropertySymbols(parent);\n for (var i = 0; i < symbols.length; i++) {\n // Don't need to worry about cloning a symbol because it is a primitive,\n // like a number or string.\n var symbol = symbols[i];\n var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);\n if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {\n continue;\n }\n child[symbol] = _clone(parent[symbol], depth - 1);\n if (!descriptor.enumerable) {\n Object.defineProperty(child, symbol, {\n enumerable: false\n });\n }\n }\n }\n\n if (includeNonEnumerable) {\n var allPropertyNames = Object.getOwnPropertyNames(parent);\n for (var i = 0; i < allPropertyNames.length; i++) {\n var propertyName = allPropertyNames[i];\n var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);\n if (descriptor && descriptor.enumerable) {\n continue;\n }\n child[propertyName] = _clone(parent[propertyName], depth - 1);\n Object.defineProperty(child, propertyName, {\n enumerable: false\n });\n }\n }\n\n return child;\n }\n\n return _clone(parent, depth);\n}\n\n/**\n * Simple flat clone using prototype, accepts only objects, usefull for property\n * override on FLAT configuration object (no nested props).\n *\n * USE WITH CAUTION! This may not behave as you wish if you do not know how this\n * works.\n */\nclone.clonePrototype = function clonePrototype(parent) {\n if (parent === null)\n return null;\n\n var c = function () {};\n c.prototype = parent;\n return new c();\n};\n\n// private utility functions\n\nfunction __objToStr(o) {\n return Object.prototype.toString.call(o);\n}\nclone.__objToStr = __objToStr;\n\nfunction __isDate(o) {\n return typeof o === 'object' && __objToStr(o) === '[object Date]';\n}\nclone.__isDate = __isDate;\n\nfunction __isArray(o) {\n return typeof o === 'object' && __objToStr(o) === '[object Array]';\n}\nclone.__isArray = __isArray;\n\nfunction __isRegExp(o) {\n return typeof o === 'object' && __objToStr(o) === '[object RegExp]';\n}\nclone.__isRegExp = __isRegExp;\n\nfunction __getRegExpFlags(re) {\n var flags = '';\n if (re.global) flags += 'g';\n if (re.ignoreCase) flags += 'i';\n if (re.multiline) flags += 'm';\n return flags;\n}\nclone.__getRegExpFlags = __getRegExpFlags;\n\nreturn clone;\n})();\n\nif (typeof module === 'object' && module.exports) {\n module.exports = clone;\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/clone/clone.js\n// module id = 21\n// module chunks = 0","import Parchment from 'parchment';\nimport clone from 'clone';\nimport equal from 'deep-equal';\nimport Emitter from './emitter';\nimport logger from './logger';\n\nlet debug = logger('quill:selection');\n\n\nclass Range {\n constructor(index, length = 0) {\n this.index = index;\n this.length = length;\n }\n}\n\n\nclass Selection {\n constructor(scroll, emitter) {\n this.emitter = emitter;\n this.scroll = scroll;\n this.composing = false;\n this.mouseDown = false;\n this.root = this.scroll.domNode;\n this.cursor = Parchment.create('cursor', this);\n // savedRange is last non-null range\n this.lastRange = this.savedRange = new Range(0, 0);\n this.handleComposition();\n this.handleDragging();\n this.emitter.listenDOM('selectionchange', document, () => {\n if (!this.mouseDown) {\n setTimeout(this.update.bind(this, Emitter.sources.USER), 1);\n }\n });\n this.emitter.on(Emitter.events.EDITOR_CHANGE, (type, delta) => {\n if (type === Emitter.events.TEXT_CHANGE && delta.length() > 0) {\n this.update(Emitter.sources.SILENT);\n }\n });\n this.emitter.on(Emitter.events.SCROLL_BEFORE_UPDATE, () => {\n if (!this.hasFocus()) return;\n let native = this.getNativeRange();\n if (native == null) return;\n if (native.start.node === this.cursor.textNode) return; // cursor.restore() will handle\n // TODO unclear if this has negative side effects\n this.emitter.once(Emitter.events.SCROLL_UPDATE, () => {\n try {\n this.setNativeRange(native.start.node, native.start.offset, native.end.node, native.end.offset);\n } catch (ignored) {}\n });\n });\n this.emitter.on(Emitter.events.SCROLL_OPTIMIZE, (mutations, context) => {\n if (context.range) {\n const { startNode, startOffset, endNode, endOffset } = context.range;\n this.setNativeRange(startNode, startOffset, endNode, endOffset);\n }\n });\n this.update(Emitter.sources.SILENT);\n }\n\n handleComposition() {\n this.root.addEventListener('compositionstart', () => {\n this.composing = true;\n });\n this.root.addEventListener('compositionend', () => {\n this.composing = false;\n if (this.cursor.parent) {\n const range = this.cursor.restore();\n if (!range) return;\n setTimeout(() => {\n this.setNativeRange(range.startNode, range.startOffset, range.endNode, range.endOffset);\n }, 1);\n }\n });\n }\n\n handleDragging() {\n this.emitter.listenDOM('mousedown', document.body, () => {\n this.mouseDown = true;\n });\n this.emitter.listenDOM('mouseup', document.body, () => {\n this.mouseDown = false;\n this.update(Emitter.sources.USER);\n });\n }\n\n focus() {\n if (this.hasFocus()) return;\n this.root.focus();\n this.setRange(this.savedRange);\n }\n\n format(format, value) {\n if (this.scroll.whitelist != null && !this.scroll.whitelist[format]) return;\n this.scroll.update();\n let nativeRange = this.getNativeRange();\n if (nativeRange == null || !nativeRange.native.collapsed || Parchment.query(format, Parchment.Scope.BLOCK)) return;\n if (nativeRange.start.node !== this.cursor.textNode) {\n let blot = Parchment.find(nativeRange.start.node, false);\n if (blot == null) return;\n // TODO Give blot ability to not split\n if (blot instanceof Parchment.Leaf) {\n let after = blot.split(nativeRange.start.offset);\n blot.parent.insertBefore(this.cursor, after);\n } else {\n blot.insertBefore(this.cursor, nativeRange.start.node); // Should never happen\n }\n this.cursor.attach();\n }\n this.cursor.format(format, value);\n this.scroll.optimize();\n this.setNativeRange(this.cursor.textNode, this.cursor.textNode.data.length);\n this.update();\n }\n\n getBounds(index, length = 0) {\n let scrollLength = this.scroll.length();\n index = Math.min(index, scrollLength - 1);\n length = Math.min(index + length, scrollLength - 1) - index;\n let node, [leaf, offset] = this.scroll.leaf(index);\n if (leaf == null) return null;\n [node, offset] = leaf.position(offset, true);\n let range = document.createRange();\n if (length > 0) {\n range.setStart(node, offset);\n [leaf, offset] = this.scroll.leaf(index + length);\n if (leaf == null) return null;\n [node, offset] = leaf.position(offset, true);\n range.setEnd(node, offset);\n return range.getBoundingClientRect();\n } else {\n let side = 'left';\n let rect;\n if (node instanceof Text) {\n if (offset < node.data.length) {\n range.setStart(node, offset);\n range.setEnd(node, offset + 1);\n } else {\n range.setStart(node, offset - 1);\n range.setEnd(node, offset);\n side = 'right';\n }\n rect = range.getBoundingClientRect();\n } else {\n rect = leaf.domNode.getBoundingClientRect();\n if (offset > 0) side = 'right';\n }\n return {\n bottom: rect.top + rect.height,\n height: rect.height,\n left: rect[side],\n right: rect[side],\n top: rect.top,\n width: 0\n };\n }\n }\n\n getNativeRange() {\n let selection = document.getSelection();\n if (selection == null || selection.rangeCount <= 0) return null;\n let nativeRange = selection.getRangeAt(0);\n if (nativeRange == null) return null;\n let range = this.normalizeNative(nativeRange);\n debug.info('getNativeRange', range);\n return range;\n }\n\n getRange() {\n let normalized = this.getNativeRange();\n if (normalized == null) return [null, null];\n let range = this.normalizedToRange(normalized);\n return [range, normalized];\n }\n\n hasFocus() {\n return document.activeElement === this.root;\n }\n\n normalizedToRange(range) {\n let positions = [[range.start.node, range.start.offset]];\n if (!range.native.collapsed) {\n positions.push([range.end.node, range.end.offset]);\n }\n let indexes = positions.map((position) => {\n let [node, offset] = position;\n let blot = Parchment.find(node, true);\n let index = blot.offset(this.scroll);\n if (offset === 0) {\n return index;\n } else if (blot instanceof Parchment.Container) {\n return index + blot.length();\n } else {\n return index + blot.index(node, offset);\n }\n });\n let end = Math.min(Math.max(...indexes), this.scroll.length() - 1);\n let start = Math.min(end, ...indexes);\n return new Range(start, end-start);\n }\n\n normalizeNative(nativeRange) {\n if (!contains(this.root, nativeRange.startContainer) ||\n (!nativeRange.collapsed && !contains(this.root, nativeRange.endContainer))) {\n return null;\n }\n let range = {\n start: { node: nativeRange.startContainer, offset: nativeRange.startOffset },\n end: { node: nativeRange.endContainer, offset: nativeRange.endOffset },\n native: nativeRange\n };\n [range.start, range.end].forEach(function(position) {\n let node = position.node, offset = position.offset;\n while (!(node instanceof Text) && node.childNodes.length > 0) {\n if (node.childNodes.length > offset) {\n node = node.childNodes[offset];\n offset = 0;\n } else if (node.childNodes.length === offset) {\n node = node.lastChild;\n offset = node instanceof Text ? node.data.length : node.childNodes.length + 1;\n } else {\n break;\n }\n }\n position.node = node, position.offset = offset;\n });\n return range;\n }\n\n rangeToNative(range) {\n let indexes = range.collapsed ? [range.index] : [range.index, range.index + range.length];\n let args = [];\n let scrollLength = this.scroll.length();\n indexes.forEach((index, i) => {\n index = Math.min(scrollLength - 1, index);\n let node, [leaf, offset] = this.scroll.leaf(index);\n [node, offset] = leaf.position(offset, i !== 0);\n args.push(node, offset);\n });\n if (args.length < 2) {\n args = args.concat(args);\n }\n return args;\n }\n\n scrollIntoView(scrollingContainer) {\n let range = this.lastRange;\n if (range == null) return;\n let bounds = this.getBounds(range.index, range.length);\n if (bounds == null) return;\n let limit = this.scroll.length()-1;\n let [first, ] = this.scroll.line(Math.min(range.index, limit));\n let last = first;\n if (range.length > 0) {\n [last, ] = this.scroll.line(Math.min(range.index + range.length, limit));\n }\n if (first == null || last == null) return;\n let scrollBounds = scrollingContainer.getBoundingClientRect();\n if (bounds.top < scrollBounds.top) {\n scrollingContainer.scrollTop -= (scrollBounds.top - bounds.top);\n } else if (bounds.bottom > scrollBounds.bottom) {\n scrollingContainer.scrollTop += (bounds.bottom - scrollBounds.bottom);\n }\n }\n\n setNativeRange(startNode, startOffset, endNode = startNode, endOffset = startOffset, force = false) {\n debug.info('setNativeRange', startNode, startOffset, endNode, endOffset);\n if (startNode != null && (this.root.parentNode == null || startNode.parentNode == null || endNode.parentNode == null)) {\n return;\n }\n let selection = document.getSelection();\n if (selection == null) return;\n if (startNode != null) {\n if (!this.hasFocus()) this.root.focus();\n let native = (this.getNativeRange() || {}).native;\n if (native == null || force ||\n startNode !== native.startContainer ||\n startOffset !== native.startOffset ||\n endNode !== native.endContainer ||\n endOffset !== native.endOffset) {\n\n if (startNode.tagName == \"BR\") {\n startOffset = [].indexOf.call(startNode.parentNode.childNodes, startNode);\n startNode = startNode.parentNode;\n }\n if (endNode.tagName == \"BR\") {\n endOffset = [].indexOf.call(endNode.parentNode.childNodes, endNode);\n endNode = endNode.parentNode;\n }\n let range = document.createRange();\n range.setStart(startNode, startOffset);\n range.setEnd(endNode, endOffset);\n selection.removeAllRanges();\n selection.addRange(range);\n }\n } else {\n selection.removeAllRanges();\n this.root.blur();\n document.body.focus(); // root.blur() not enough on IE11+Travis+SauceLabs (but not local VMs)\n }\n }\n\n setRange(range, force = false, source = Emitter.sources.API) {\n if (typeof force === 'string') {\n source = force;\n force = false;\n }\n debug.info('setRange', range);\n if (range != null) {\n let args = this.rangeToNative(range);\n this.setNativeRange(...args, force);\n } else {\n this.setNativeRange(null);\n }\n this.update(source);\n }\n\n update(source = Emitter.sources.USER) {\n let oldRange = this.lastRange;\n let [lastRange, nativeRange] = this.getRange();\n this.lastRange = lastRange;\n if (this.lastRange != null) {\n this.savedRange = this.lastRange;\n }\n if (!equal(oldRange, this.lastRange)) {\n if (!this.composing && nativeRange != null && nativeRange.native.collapsed && nativeRange.start.node !== this.cursor.textNode) {\n this.cursor.restore();\n }\n let args = [Emitter.events.SELECTION_CHANGE, clone(this.lastRange), clone(oldRange), source];\n this.emitter.emit(Emitter.events.EDITOR_CHANGE, ...args);\n if (source !== Emitter.sources.SILENT) {\n this.emitter.emit(...args);\n }\n }\n }\n}\n\n\nfunction contains(parent, descendant) {\n try {\n // Firefox inserts inaccessible nodes around video elements\n descendant.parentNode;\n } catch (e) {\n return false;\n }\n // IE11 has bug with Text nodes\n // https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect\n if (descendant instanceof Text) {\n descendant = descendant.parentNode;\n }\n return parent.contains(descendant);\n}\n\n\nexport { Range, Selection as default };\n\n\n\n// WEBPACK FOOTER //\n// ./core/selection.js","import Parchment from 'parchment';\nimport Block, { BlockEmbed } from './block';\n\n\nclass Container extends Parchment.Container { }\nContainer.allowedChildren = [Block, BlockEmbed, Container];\n\n\nexport default Container;\n\n\n\n// WEBPACK FOOTER //\n// ./blots/container.js","import Parchment from 'parchment';\n\nclass ColorAttributor extends Parchment.Attributor.Style {\n value(domNode) {\n let value = super.value(domNode);\n if (!value.startsWith('rgb(')) return value;\n value = value.replace(/^[^\\d]+/, '').replace(/[^\\d]+$/, '');\n return '#' + value.split(',').map(function(component) {\n return ('00' + parseInt(component).toString(16)).slice(-2);\n }).join('');\n }\n}\n\nlet ColorClass = new Parchment.Attributor.Class('color', 'ql-color', {\n scope: Parchment.Scope.INLINE\n});\nlet ColorStyle = new ColorAttributor('color', 'color', {\n scope: Parchment.Scope.INLINE\n});\n\nexport { ColorAttributor, ColorClass, ColorStyle };\n\n\n\n// WEBPACK FOOTER //\n// ./formats/color.js","import clone from 'clone';\nimport equal from 'deep-equal';\nimport extend from 'extend';\nimport Delta from 'quill-delta';\nimport DeltaOp from 'quill-delta/lib/op';\nimport Parchment from 'parchment';\nimport Quill from '../core/quill';\nimport logger from '../core/logger';\nimport Module from '../core/module';\n\nlet debug = logger('quill:keyboard');\n\nconst SHORTKEY = /Mac/i.test(navigator.platform) ? 'metaKey' : 'ctrlKey';\n\n\nclass Keyboard extends Module {\n static match(evt, binding) {\n binding = normalize(binding);\n if (['altKey', 'ctrlKey', 'metaKey', 'shiftKey'].some(function(key) {\n return (!!binding[key] !== evt[key] && binding[key] !== null);\n })) {\n return false;\n }\n return binding.key === (evt.which || evt.keyCode);\n }\n\n constructor(quill, options) {\n super(quill, options);\n this.bindings = {};\n Object.keys(this.options.bindings).forEach((name) => {\n if (name === 'list autofill' &&\n quill.scroll.whitelist != null &&\n !quill.scroll.whitelist['list']) {\n return;\n }\n if (this.options.bindings[name]) {\n this.addBinding(this.options.bindings[name]);\n }\n });\n this.addBinding({ key: Keyboard.keys.ENTER, shiftKey: null }, handleEnter);\n this.addBinding({ key: Keyboard.keys.ENTER, metaKey: null, ctrlKey: null, altKey: null }, function() {});\n if (/Firefox/i.test(navigator.userAgent)) {\n // Need to handle delete and backspace for Firefox in the general case #1171\n this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true }, handleBackspace);\n this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true }, handleDelete);\n } else {\n this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true, prefix: /^.?$/ }, handleBackspace);\n this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true, suffix: /^.?$/ }, handleDelete);\n }\n this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: false }, handleDeleteRange);\n this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: false }, handleDeleteRange);\n this.addBinding({ key: Keyboard.keys.BACKSPACE, altKey: null, ctrlKey: null, metaKey: null, shiftKey: null },\n { collapsed: true, offset: 0 },\n handleBackspace);\n this.listen();\n }\n\n addBinding(key, context = {}, handler = {}) {\n let binding = normalize(key);\n if (binding == null || binding.key == null) {\n return debug.warn('Attempted to add invalid keyboard binding', binding);\n }\n if (typeof context === 'function') {\n context = { handler: context };\n }\n if (typeof handler === 'function') {\n handler = { handler: handler };\n }\n binding = extend(binding, context, handler);\n this.bindings[binding.key] = this.bindings[binding.key] || [];\n this.bindings[binding.key].push(binding);\n }\n\n listen() {\n this.quill.root.addEventListener('keydown', (evt) => {\n if (evt.defaultPrevented) return;\n let which = evt.which || evt.keyCode;\n let bindings = (this.bindings[which] || []).filter(function(binding) {\n return Keyboard.match(evt, binding);\n });\n if (bindings.length === 0) return;\n let range = this.quill.getSelection();\n if (range == null || !this.quill.hasFocus()) return;\n let [line, offset] = this.quill.getLine(range.index);\n let [leafStart, offsetStart] = this.quill.getLeaf(range.index);\n let [leafEnd, offsetEnd] = range.length === 0 ? [leafStart, offsetStart] : this.quill.getLeaf(range.index + range.length);\n let prefixText = leafStart instanceof Parchment.Text ? leafStart.value().slice(0, offsetStart) : '';\n let suffixText = leafEnd instanceof Parchment.Text ? leafEnd.value().slice(offsetEnd) : '';\n let curContext = {\n collapsed: range.length === 0,\n empty: range.length === 0 && line.length() <= 1,\n format: this.quill.getFormat(range),\n offset: offset,\n prefix: prefixText,\n suffix: suffixText\n };\n let prevented = bindings.some((binding) => {\n if (binding.collapsed != null && binding.collapsed !== curContext.collapsed) return false;\n if (binding.empty != null && binding.empty !== curContext.empty) return false;\n if (binding.offset != null && binding.offset !== curContext.offset) return false;\n if (Array.isArray(binding.format)) {\n // any format is present\n if (binding.format.every(function(name) {\n return curContext.format[name] == null;\n })) {\n return false;\n }\n } else if (typeof binding.format === 'object') {\n // all formats must match\n if (!Object.keys(binding.format).every(function(name) {\n if (binding.format[name] === true) return curContext.format[name] != null;\n if (binding.format[name] === false) return curContext.format[name] == null;\n return equal(binding.format[name], curContext.format[name]);\n })) {\n return false;\n }\n }\n if (binding.prefix != null && !binding.prefix.test(curContext.prefix)) return false;\n if (binding.suffix != null && !binding.suffix.test(curContext.suffix)) return false;\n return binding.handler.call(this, range, curContext) !== true;\n });\n if (prevented) {\n evt.preventDefault();\n }\n });\n }\n}\n\nKeyboard.keys = {\n BACKSPACE: 8,\n TAB: 9,\n ENTER: 13,\n ESCAPE: 27,\n LEFT: 37,\n UP: 38,\n RIGHT: 39,\n DOWN: 40,\n DELETE: 46\n};\n\nKeyboard.DEFAULTS = {\n bindings: {\n 'bold' : makeFormatHandler('bold'),\n 'italic' : makeFormatHandler('italic'),\n 'underline' : makeFormatHandler('underline'),\n 'indent': {\n // highlight tab or tab at beginning of list, indent or blockquote\n key: Keyboard.keys.TAB,\n format: ['blockquote', 'indent', 'list'],\n handler: function(range, context) {\n if (context.collapsed && context.offset !== 0) return true;\n this.quill.format('indent', '+1', Quill.sources.USER);\n }\n },\n 'outdent': {\n key: Keyboard.keys.TAB,\n shiftKey: true,\n format: ['blockquote', 'indent', 'list'],\n // highlight tab or tab at beginning of list, indent or blockquote\n handler: function(range, context) {\n if (context.collapsed && context.offset !== 0) return true;\n this.quill.format('indent', '-1', Quill.sources.USER);\n }\n },\n 'outdent backspace': {\n key: Keyboard.keys.BACKSPACE,\n collapsed: true,\n shiftKey: null,\n metaKey: null,\n ctrlKey: null,\n altKey: null,\n format: ['indent', 'list'],\n offset: 0,\n handler: function(range, context) {\n if (context.format.indent != null) {\n this.quill.format('indent', '-1', Quill.sources.USER);\n } else if (context.format.list != null) {\n this.quill.format('list', false, Quill.sources.USER);\n }\n }\n },\n 'indent code-block': makeCodeBlockHandler(true),\n 'outdent code-block': makeCodeBlockHandler(false),\n 'remove tab': {\n key: Keyboard.keys.TAB,\n shiftKey: true,\n collapsed: true,\n prefix: /\\t$/,\n handler: function(range) {\n this.quill.deleteText(range.index - 1, 1, Quill.sources.USER);\n }\n },\n 'tab': {\n key: Keyboard.keys.TAB,\n handler: function(range) {\n this.quill.history.cutoff();\n let delta = new Delta().retain(range.index)\n .delete(range.length)\n .insert('\\t');\n this.quill.updateContents(delta, Quill.sources.USER);\n this.quill.history.cutoff();\n this.quill.setSelection(range.index + 1, Quill.sources.SILENT);\n }\n },\n 'list empty enter': {\n key: Keyboard.keys.ENTER,\n collapsed: true,\n format: ['list'],\n empty: true,\n handler: function(range, context) {\n this.quill.format('list', false, Quill.sources.USER);\n if (context.format.indent) {\n this.quill.format('indent', false, Quill.sources.USER);\n }\n }\n },\n 'checklist enter': {\n key: Keyboard.keys.ENTER,\n collapsed: true,\n format: { list: 'checked' },\n handler: function(range) {\n let [line, offset] = this.quill.getLine(range.index);\n let formats = extend({}, line.formats(), { list: 'checked' });\n let delta = new Delta().retain(range.index)\n .insert('\\n', formats)\n .retain(line.length() - offset - 1)\n .retain(1, { list: 'unchecked' });\n this.quill.updateContents(delta, Quill.sources.USER);\n this.quill.setSelection(range.index + 1, Quill.sources.SILENT);\n this.quill.scrollIntoView();\n }\n },\n 'header enter': {\n key: Keyboard.keys.ENTER,\n collapsed: true,\n format: ['header'],\n suffix: /^$/,\n handler: function(range, context) {\n let [line, offset] = this.quill.getLine(range.index);\n let delta = new Delta().retain(range.index)\n .insert('\\n', context.format)\n .retain(line.length() - offset - 1)\n .retain(1, { header: null });\n this.quill.updateContents(delta, Quill.sources.USER);\n this.quill.setSelection(range.index + 1, Quill.sources.SILENT);\n this.quill.scrollIntoView();\n }\n },\n 'list autofill': {\n key: ' ',\n collapsed: true,\n format: { list: false },\n prefix: /^\\s*?(\\d+\\.|-|\\*|\\[ ?\\]|\\[x\\])$/,\n handler: function(range, context) {\n let length = context.prefix.length;\n let [line, offset] = this.quill.getLine(range.index);\n if (offset > length) return true;\n let value;\n switch (context.prefix.trim()) {\n case '[]': case '[ ]':\n value = 'unchecked';\n break;\n case '[x]':\n value = 'checked';\n break;\n case '-': case '*':\n value = 'bullet';\n break;\n default:\n value = 'ordered';\n }\n this.quill.insertText(range.index, ' ', Quill.sources.USER);\n this.quill.history.cutoff();\n let delta = new Delta().retain(range.index - offset)\n .delete(length + 1)\n .retain(line.length() - 2 - offset)\n .retain(1, { list: value });\n this.quill.updateContents(delta, Quill.sources.USER);\n this.quill.history.cutoff();\n this.quill.setSelection(range.index - length, Quill.sources.SILENT);\n }\n },\n 'code exit': {\n key: Keyboard.keys.ENTER,\n collapsed: true,\n format: ['code-block'],\n prefix: /\\n\\n$/,\n suffix: /^\\s+$/,\n handler: function(range) {\n const [line, offset] = this.quill.getLine(range.index);\n const delta = new Delta()\n .retain(range.index + line.length() - offset - 2)\n .retain(1, { 'code-block': null })\n .delete(1);\n this.quill.updateContents(delta, Quill.sources.USER);\n }\n },\n 'embed left': makeEmbedArrowHandler(Keyboard.keys.LEFT, false),\n 'embed left shift': makeEmbedArrowHandler(Keyboard.keys.LEFT, true),\n 'embed right': makeEmbedArrowHandler(Keyboard.keys.RIGHT, false),\n 'embed right shift': makeEmbedArrowHandler(Keyboard.keys.RIGHT, true)\n }\n};\n\nfunction makeEmbedArrowHandler(key, shiftKey) {\n const where = key === Keyboard.keys.LEFT ? 'prefix' : 'suffix';\n return {\n key,\n shiftKey,\n altKey: null,\n [where]: /^$/,\n handler: function(range) {\n let index = range.index;\n if (key === Keyboard.keys.RIGHT) {\n index += (range.length + 1);\n }\n const [leaf, ] = this.quill.getLeaf(index);\n if (!(leaf instanceof Parchment.Embed)) return true;\n if (key === Keyboard.keys.LEFT) {\n if (shiftKey) {\n this.quill.setSelection(range.index - 1, range.length + 1, Quill.sources.USER);\n } else {\n this.quill.setSelection(range.index - 1, Quill.sources.USER);\n }\n } else {\n if (shiftKey) {\n this.quill.setSelection(range.index, range.length + 1, Quill.sources.USER);\n } else {\n this.quill.setSelection(range.index + range.length + 1, Quill.sources.USER);\n }\n }\n return false;\n }\n };\n}\n\n\nfunction handleBackspace(range, context) {\n if (range.index === 0 || this.quill.getLength() <= 1) return;\n let [line, ] = this.quill.getLine(range.index);\n let formats = {};\n if (context.offset === 0) {\n let [prev, ] = this.quill.getLine(range.index - 1);\n if (prev != null && prev.length() > 1) {\n let curFormats = line.formats();\n let prevFormats = this.quill.getFormat(range.index-1, 1);\n formats = DeltaOp.attributes.diff(curFormats, prevFormats) || {};\n }\n }\n // Check for astral symbols\n let length = /[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]$/.test(context.prefix) ? 2 : 1;\n this.quill.deleteText(range.index-length, length, Quill.sources.USER);\n if (Object.keys(formats).length > 0) {\n this.quill.formatLine(range.index-length, length, formats, Quill.sources.USER);\n }\n this.quill.focus();\n}\n\nfunction handleDelete(range, context) {\n // Check for astral symbols\n let length = /^[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/.test(context.suffix) ? 2 : 1;\n if (range.index >= this.quill.getLength() - length) return;\n let formats = {}, nextLength = 0;\n let [line, ] = this.quill.getLine(range.index);\n if (context.offset >= line.length() - 1) {\n let [next, ] = this.quill.getLine(range.index + 1);\n if (next) {\n let curFormats = line.formats();\n let nextFormats = this.quill.getFormat(range.index, 1);\n formats = DeltaOp.attributes.diff(curFormats, nextFormats) || {};\n nextLength = next.length();\n }\n }\n this.quill.deleteText(range.index, length, Quill.sources.USER);\n if (Object.keys(formats).length > 0) {\n this.quill.formatLine(range.index + nextLength - 1, length, formats, Quill.sources.USER);\n }\n}\n\nfunction handleDeleteRange(range) {\n let lines = this.quill.getLines(range);\n let formats = {};\n if (lines.length > 1) {\n let firstFormats = lines[0].formats();\n let lastFormats = lines[lines.length - 1].formats();\n formats = DeltaOp.attributes.diff(lastFormats, firstFormats) || {};\n }\n this.quill.deleteText(range, Quill.sources.USER);\n if (Object.keys(formats).length > 0) {\n this.quill.formatLine(range.index, 1, formats, Quill.sources.USER);\n }\n this.quill.setSelection(range.index, Quill.sources.SILENT);\n this.quill.focus();\n}\n\nfunction handleEnter(range, context) {\n if (range.length > 0) {\n this.quill.scroll.deleteAt(range.index, range.length); // So we do not trigger text-change\n }\n let lineFormats = Object.keys(context.format).reduce(function(lineFormats, format) {\n if (Parchment.query(format, Parchment.Scope.BLOCK) && !Array.isArray(context.format[format])) {\n lineFormats[format] = context.format[format];\n }\n return lineFormats;\n }, {});\n this.quill.insertText(range.index, '\\n', lineFormats, Quill.sources.USER);\n // Earlier scroll.deleteAt might have messed up our selection,\n // so insertText's built in selection preservation is not reliable\n this.quill.setSelection(range.index + 1, Quill.sources.SILENT);\n this.quill.focus();\n Object.keys(context.format).forEach((name) => {\n if (lineFormats[name] != null) return;\n if (Array.isArray(context.format[name])) return;\n if (name === 'link') return;\n this.quill.format(name, context.format[name], Quill.sources.USER);\n });\n}\n\nfunction makeCodeBlockHandler(indent) {\n return {\n key: Keyboard.keys.TAB,\n shiftKey: !indent,\n format: {'code-block': true },\n handler: function(range) {\n let CodeBlock = Parchment.query('code-block');\n let index = range.index, length = range.length;\n let [block, offset] = this.quill.scroll.descendant(CodeBlock, index);\n if (block == null) return;\n let scrollIndex = this.quill.getIndex(block);\n let start = block.newlineIndex(offset, true) + 1;\n let end = block.newlineIndex(scrollIndex + offset + length);\n let lines = block.domNode.textContent.slice(start, end).split('\\n');\n offset = 0;\n lines.forEach((line, i) => {\n if (indent) {\n block.insertAt(start + offset, CodeBlock.TAB);\n offset += CodeBlock.TAB.length;\n if (i === 0) {\n index += CodeBlock.TAB.length;\n } else {\n length += CodeBlock.TAB.length;\n }\n } else if (line.startsWith(CodeBlock.TAB)) {\n block.deleteAt(start + offset, CodeBlock.TAB.length);\n offset -= CodeBlock.TAB.length;\n if (i === 0) {\n index -= CodeBlock.TAB.length;\n } else {\n length -= CodeBlock.TAB.length;\n }\n }\n offset += line.length + 1;\n });\n this.quill.update(Quill.sources.USER);\n this.quill.setSelection(index, length, Quill.sources.SILENT);\n }\n };\n}\n\nfunction makeFormatHandler(format) {\n return {\n key: format[0].toUpperCase(),\n shortKey: true,\n handler: function(range, context) {\n this.quill.format(format, !context.format[format], Quill.sources.USER);\n }\n };\n}\n\nfunction normalize(binding) {\n if (typeof binding === 'string' || typeof binding === 'number') {\n return normalize({ key: binding });\n }\n if (typeof binding === 'object') {\n binding = clone(binding, false);\n }\n if (typeof binding.key === 'string') {\n if (Keyboard.keys[binding.key.toUpperCase()] != null) {\n binding.key = Keyboard.keys[binding.key.toUpperCase()];\n } else if (binding.key.length === 1) {\n binding.key = binding.key.toUpperCase().charCodeAt(0);\n } else {\n return null;\n }\n }\n if (binding.shortKey) {\n binding[SHORTKEY] = binding.shortKey;\n delete binding.shortKey;\n }\n return binding;\n}\n\n\nexport { Keyboard as default, SHORTKEY };\n\n\n\n// WEBPACK FOOTER //\n// ./modules/keyboard.js","module.exports = {\n 'align': {\n '' : require('../assets/icons/align-left.svg'),\n 'center' : require('../assets/icons/align-center.svg'),\n 'right' : require('../assets/icons/align-right.svg'),\n 'justify' : require('../assets/icons/align-justify.svg')\n },\n 'background': require('../assets/icons/background.svg'),\n 'blockquote': require('../assets/icons/blockquote.svg'),\n 'bold' : require('../assets/icons/bold.svg'),\n 'clean' : require('../assets/icons/clean.svg'),\n 'code' : require('../assets/icons/code.svg'),\n 'code-block': require('../assets/icons/code.svg'),\n 'color' : require('../assets/icons/color.svg'),\n 'direction' : {\n '' : require('../assets/icons/direction-ltr.svg'),\n 'rtl' : require('../assets/icons/direction-rtl.svg')\n },\n 'float': {\n 'center' : require('../assets/icons/float-center.svg'),\n 'full' : require('../assets/icons/float-full.svg'),\n 'left' : require('../assets/icons/float-left.svg'),\n 'right' : require('../assets/icons/float-right.svg')\n },\n 'formula' : require('../assets/icons/formula.svg'),\n 'header': {\n '1' : require('../assets/icons/header.svg'),\n '2' : require('../assets/icons/header-2.svg')\n },\n 'italic' : require('../assets/icons/italic.svg'),\n 'image' : require('../assets/icons/image.svg'),\n 'indent': {\n '+1' : require('../assets/icons/indent.svg'),\n '-1' : require('../assets/icons/outdent.svg')\n },\n 'link' : require('../assets/icons/link.svg'),\n 'list': {\n 'ordered' : require('../assets/icons/list-ordered.svg'),\n 'bullet' : require('../assets/icons/list-bullet.svg'),\n 'check' : require('../assets/icons/list-check.svg')\n },\n 'script': {\n 'sub' : require('../assets/icons/subscript.svg'),\n 'super' : require('../assets/icons/superscript.svg'),\n },\n 'strike' : require('../assets/icons/strike.svg'),\n 'underline' : require('../assets/icons/underline.svg'),\n 'video' : require('../assets/icons/video.svg')\n};\n\n\n\n// WEBPACK FOOTER //\n// ./ui/icons.js","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar Registry = require(\"../../registry\");\nvar ShadowBlot = /** @class */ (function () {\n function ShadowBlot(domNode) {\n this.domNode = domNode;\n // @ts-ignore\n this.domNode[Registry.DATA_KEY] = { blot: this };\n }\n Object.defineProperty(ShadowBlot.prototype, \"statics\", {\n // Hack for accessing inherited static methods\n get: function () {\n return this.constructor;\n },\n enumerable: true,\n configurable: true\n });\n ShadowBlot.create = function (value) {\n if (this.tagName == null) {\n throw new Registry.ParchmentError('Blot definition missing tagName');\n }\n var node;\n if (Array.isArray(this.tagName)) {\n if (typeof value === 'string') {\n value = value.toUpperCase();\n if (parseInt(value).toString() === value) {\n value = parseInt(value);\n }\n }\n if (typeof value === 'number') {\n node = document.createElement(this.tagName[value - 1]);\n }\n else if (this.tagName.indexOf(value) > -1) {\n node = document.createElement(value);\n }\n else {\n node = document.createElement(this.tagName[0]);\n }\n }\n else {\n node = document.createElement(this.tagName);\n }\n if (this.className) {\n node.classList.add(this.className);\n }\n return node;\n };\n ShadowBlot.prototype.attach = function () {\n if (this.parent != null) {\n this.scroll = this.parent.scroll;\n }\n };\n ShadowBlot.prototype.clone = function () {\n var domNode = this.domNode.cloneNode(false);\n return Registry.create(domNode);\n };\n ShadowBlot.prototype.detach = function () {\n if (this.parent != null)\n this.parent.removeChild(this);\n // @ts-ignore\n delete this.domNode[Registry.DATA_KEY];\n };\n ShadowBlot.prototype.deleteAt = function (index, length) {\n var blot = this.isolate(index, length);\n blot.remove();\n };\n ShadowBlot.prototype.formatAt = function (index, length, name, value) {\n var blot = this.isolate(index, length);\n if (Registry.query(name, Registry.Scope.BLOT) != null && value) {\n blot.wrap(name, value);\n }\n else if (Registry.query(name, Registry.Scope.ATTRIBUTE) != null) {\n var parent = Registry.create(this.statics.scope);\n blot.wrap(parent);\n parent.format(name, value);\n }\n };\n ShadowBlot.prototype.insertAt = function (index, value, def) {\n var blot = def == null ? Registry.create('text', value) : Registry.create(value, def);\n var ref = this.split(index);\n this.parent.insertBefore(blot, ref);\n };\n ShadowBlot.prototype.insertInto = function (parentBlot, refBlot) {\n if (refBlot === void 0) { refBlot = null; }\n if (this.parent != null) {\n this.parent.children.remove(this);\n }\n var refDomNode = null;\n parentBlot.children.insertBefore(this, refBlot);\n if (refBlot != null) {\n refDomNode = refBlot.domNode;\n }\n if (this.domNode.parentNode != parentBlot.domNode ||\n this.domNode.nextSibling != refDomNode) {\n parentBlot.domNode.insertBefore(this.domNode, refDomNode);\n }\n this.parent = parentBlot;\n this.attach();\n };\n ShadowBlot.prototype.isolate = function (index, length) {\n var target = this.split(index);\n target.split(length);\n return target;\n };\n ShadowBlot.prototype.length = function () {\n return 1;\n };\n ShadowBlot.prototype.offset = function (root) {\n if (root === void 0) { root = this.parent; }\n if (this.parent == null || this == root)\n return 0;\n return this.parent.children.offset(this) + this.parent.offset(root);\n };\n ShadowBlot.prototype.optimize = function (context) {\n // TODO clean up once we use WeakMap\n // @ts-ignore\n if (this.domNode[Registry.DATA_KEY] != null) {\n // @ts-ignore\n delete this.domNode[Registry.DATA_KEY].mutations;\n }\n };\n ShadowBlot.prototype.remove = function () {\n if (this.domNode.parentNode != null) {\n this.domNode.parentNode.removeChild(this.domNode);\n }\n this.detach();\n };\n ShadowBlot.prototype.replace = function (target) {\n if (target.parent == null)\n return;\n target.parent.insertBefore(this, target.next);\n target.remove();\n };\n ShadowBlot.prototype.replaceWith = function (name, value) {\n var replacement = typeof name === 'string' ? Registry.create(name, value) : name;\n replacement.replace(this);\n return replacement;\n };\n ShadowBlot.prototype.split = function (index, force) {\n return index === 0 ? this : this.next;\n };\n ShadowBlot.prototype.update = function (mutations, context) {\n // Nothing to do by default\n };\n ShadowBlot.prototype.wrap = function (name, value) {\n var wrapper = typeof name === 'string' ? Registry.create(name, value) : name;\n if (this.parent != null) {\n this.parent.insertBefore(wrapper, this.next);\n }\n wrapper.appendChild(this);\n return wrapper;\n };\n ShadowBlot.blotName = 'abstract';\n return ShadowBlot;\n}());\nexports.default = ShadowBlot;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/parchment/src/blot/abstract/shadow.ts\n// module id = 27\n// module chunks = 0","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar attributor_1 = require(\"./attributor\");\nvar class_1 = require(\"./class\");\nvar style_1 = require(\"./style\");\nvar Registry = require(\"../registry\");\nvar AttributorStore = /** @class */ (function () {\n function AttributorStore(domNode) {\n this.attributes = {};\n this.domNode = domNode;\n this.build();\n }\n AttributorStore.prototype.attribute = function (attribute, value) {\n // verb\n if (value) {\n if (attribute.add(this.domNode, value)) {\n if (attribute.value(this.domNode) != null) {\n this.attributes[attribute.attrName] = attribute;\n }\n else {\n delete this.attributes[attribute.attrName];\n }\n }\n }\n else {\n attribute.remove(this.domNode);\n delete this.attributes[attribute.attrName];\n }\n };\n AttributorStore.prototype.build = function () {\n var _this = this;\n this.attributes = {};\n var attributes = attributor_1.default.keys(this.domNode);\n var classes = class_1.default.keys(this.domNode);\n var styles = style_1.default.keys(this.domNode);\n attributes\n .concat(classes)\n .concat(styles)\n .forEach(function (name) {\n var attr = Registry.query(name, Registry.Scope.ATTRIBUTE);\n if (attr instanceof attributor_1.default) {\n _this.attributes[attr.attrName] = attr;\n }\n });\n };\n AttributorStore.prototype.copy = function (target) {\n var _this = this;\n Object.keys(this.attributes).forEach(function (key) {\n var value = _this.attributes[key].value(_this.domNode);\n target.format(key, value);\n });\n };\n AttributorStore.prototype.move = function (target) {\n var _this = this;\n this.copy(target);\n Object.keys(this.attributes).forEach(function (key) {\n _this.attributes[key].remove(_this.domNode);\n });\n this.attributes = {};\n };\n AttributorStore.prototype.values = function () {\n var _this = this;\n return Object.keys(this.attributes).reduce(function (attributes, name) {\n attributes[name] = _this.attributes[name].value(_this.domNode);\n return attributes;\n }, {});\n };\n return AttributorStore;\n}());\nexports.default = AttributorStore;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/parchment/src/attributor/store.ts\n// module id = 28\n// module chunks = 0","\"use strict\";\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar attributor_1 = require(\"./attributor\");\nfunction match(node, prefix) {\n var className = node.getAttribute('class') || '';\n return className.split(/\\s+/).filter(function (name) {\n return name.indexOf(prefix + \"-\") === 0;\n });\n}\nvar ClassAttributor = /** @class */ (function (_super) {\n __extends(ClassAttributor, _super);\n function ClassAttributor() {\n return _super !== null && _super.apply(this, arguments) || this;\n }\n ClassAttributor.keys = function (node) {\n return (node.getAttribute('class') || '').split(/\\s+/).map(function (name) {\n return name\n .split('-')\n .slice(0, -1)\n .join('-');\n });\n };\n ClassAttributor.prototype.add = function (node, value) {\n if (!this.canAdd(node, value))\n return false;\n this.remove(node);\n node.classList.add(this.keyName + \"-\" + value);\n return true;\n };\n ClassAttributor.prototype.remove = function (node) {\n var matches = match(node, this.keyName);\n matches.forEach(function (name) {\n node.classList.remove(name);\n });\n if (node.classList.length === 0) {\n node.removeAttribute('class');\n }\n };\n ClassAttributor.prototype.value = function (node) {\n var result = match(node, this.keyName)[0] || '';\n var value = result.slice(this.keyName.length + 1); // +1 for hyphen\n return this.canAdd(node, value) ? value : '';\n };\n return ClassAttributor;\n}(attributor_1.default));\nexports.default = ClassAttributor;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/parchment/src/attributor/class.ts\n// module id = 29\n// module chunks = 0","\"use strict\";\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar attributor_1 = require(\"./attributor\");\nfunction camelize(name) {\n var parts = name.split('-');\n var rest = parts\n .slice(1)\n .map(function (part) {\n return part[0].toUpperCase() + part.slice(1);\n })\n .join('');\n return parts[0] + rest;\n}\nvar StyleAttributor = /** @class */ (function (_super) {\n __extends(StyleAttributor, _super);\n function StyleAttributor() {\n return _super !== null && _super.apply(this, arguments) || this;\n }\n StyleAttributor.keys = function (node) {\n return (node.getAttribute('style') || '').split(';').map(function (value) {\n var arr = value.split(':');\n return arr[0].trim();\n });\n };\n StyleAttributor.prototype.add = function (node, value) {\n if (!this.canAdd(node, value))\n return false;\n // @ts-ignore\n node.style[camelize(this.keyName)] = value;\n return true;\n };\n StyleAttributor.prototype.remove = function (node) {\n // @ts-ignore\n node.style[camelize(this.keyName)] = '';\n if (!node.getAttribute('style')) {\n node.removeAttribute('style');\n }\n };\n StyleAttributor.prototype.value = function (node) {\n // @ts-ignore\n var value = node.style[camelize(this.keyName)];\n return this.canAdd(node, value) ? value : '';\n };\n return StyleAttributor;\n}(attributor_1.default));\nexports.default = StyleAttributor;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/parchment/src/attributor/style.ts\n// module id = 30\n// module chunks = 0","import Parchment from 'parchment';\nimport TextBlot from './text';\n\n\nclass Cursor extends Parchment.Embed {\n static value() {\n return undefined;\n }\n\n constructor(domNode, selection) {\n super(domNode);\n this.selection = selection;\n this.textNode = document.createTextNode(Cursor.CONTENTS);\n this.domNode.appendChild(this.textNode);\n this._length = 0;\n }\n\n detach() {\n // super.detach() will also clear domNode.__blot\n if (this.parent != null) this.parent.removeChild(this);\n }\n\n format(name, value) {\n if (this._length !== 0) {\n return super.format(name, value);\n }\n let target = this, index = 0;\n while (target != null && target.statics.scope !== Parchment.Scope.BLOCK_BLOT) {\n index += target.offset(target.parent);\n target = target.parent;\n }\n if (target != null) {\n this._length = Cursor.CONTENTS.length;\n target.optimize();\n target.formatAt(index, Cursor.CONTENTS.length, name, value);\n this._length = 0;\n }\n }\n\n index(node, offset) {\n if (node === this.textNode) return 0;\n return super.index(node, offset);\n }\n\n length() {\n return this._length;\n }\n\n position() {\n return [this.textNode, this.textNode.data.length];\n }\n\n remove() {\n super.remove();\n this.parent = null;\n }\n\n restore() {\n if (this.selection.composing || this.parent == null) return;\n let textNode = this.textNode;\n let range = this.selection.getNativeRange();\n let restoreText, start, end;\n if (range != null && range.start.node === textNode && range.end.node === textNode) {\n [restoreText, start, end] = [textNode, range.start.offset, range.end.offset];\n }\n // Link format will insert text outside of anchor tag\n while (this.domNode.lastChild != null && this.domNode.lastChild !== this.textNode) {\n this.domNode.parentNode.insertBefore(this.domNode.lastChild, this.domNode);\n }\n if (this.textNode.data !== Cursor.CONTENTS) {\n let text = this.textNode.data.split(Cursor.CONTENTS).join('');\n if (this.next instanceof TextBlot) {\n restoreText = this.next.domNode;\n this.next.insertAt(0, text);\n this.textNode.data = Cursor.CONTENTS;\n } else {\n this.textNode.data = text;\n this.parent.insertBefore(Parchment.create(this.textNode), this);\n this.textNode = document.createTextNode(Cursor.CONTENTS);\n this.domNode.appendChild(this.textNode);\n }\n }\n this.remove();\n if (start != null) {\n [start, end] = [start, end].map(function(offset) {\n return Math.max(0, Math.min(restoreText.data.length, offset - 1));\n });\n return {\n startNode: restoreText,\n startOffset: start,\n endNode: restoreText,\n endOffset: end\n };\n }\n }\n\n update(mutations, context) {\n if (mutations.some((mutation) => {\n return mutation.type === 'characterData' && mutation.target === this.textNode;\n })) {\n let range = this.restore();\n if (range) context.range = range;\n }\n }\n\n value() {\n return '';\n }\n}\nCursor.blotName = 'cursor';\nCursor.className = 'ql-cursor';\nCursor.tagName = 'span';\nCursor.CONTENTS = \"\\uFEFF\"; // Zero width no break space\n\n\nexport default Cursor;\n\n\n\n// WEBPACK FOOTER //\n// ./blots/cursor.js","class Theme {\n constructor(quill, options) {\n this.quill = quill;\n this.options = options;\n this.modules = {};\n }\n\n init() {\n Object.keys(this.options.modules).forEach((name) => {\n if (this.modules[name] == null) {\n this.addModule(name);\n }\n });\n }\n\n addModule(name) {\n let moduleClass = this.quill.constructor.import(`modules/${name}`);\n this.modules[name] = new moduleClass(this.quill, this.options.modules[name] || {});\n return this.modules[name];\n }\n}\nTheme.DEFAULTS = {\n modules: {}\n};\nTheme.themes = {\n 'default': Theme\n};\n\n\nexport default Theme;\n\n\n\n// WEBPACK FOOTER //\n// ./core/theme.js","import Parchment from 'parchment';\nimport TextBlot from './text';\n\nconst GUARD_TEXT = \"\\uFEFF\";\n\n\nclass Embed extends Parchment.Embed {\n constructor(node) {\n super(node);\n this.contentNode = document.createElement('span');\n this.contentNode.setAttribute('contenteditable', false);\n [].slice.call(this.domNode.childNodes).forEach((childNode) => {\n this.contentNode.appendChild(childNode);\n });\n this.leftGuard = document.createTextNode(GUARD_TEXT);\n this.rightGuard = document.createTextNode(GUARD_TEXT);\n this.domNode.appendChild(this.leftGuard);\n this.domNode.appendChild(this.contentNode);\n this.domNode.appendChild(this.rightGuard);\n }\n\n index(node, offset) {\n if (node === this.leftGuard) return 0;\n if (node === this.rightGuard) return 1;\n return super.index(node, offset);\n }\n\n restore(node) {\n let range, textNode;\n let text = node.data.split(GUARD_TEXT).join('');\n if (node === this.leftGuard) {\n if (this.prev instanceof TextBlot) {\n let prevLength = this.prev.length();\n this.prev.insertAt(prevLength, text);\n range = {\n startNode: this.prev.domNode,\n startOffset: prevLength + text.length\n };\n } else {\n textNode = document.createTextNode(text);\n this.parent.insertBefore(Parchment.create(textNode), this);\n range = {\n startNode: textNode,\n startOffset: text.length\n };\n }\n } else if (node === this.rightGuard) {\n if (this.next instanceof TextBlot) {\n this.next.insertAt(0, text);\n range = {\n startNode: this.next.domNode,\n startOffset: text.length\n }\n } else {\n textNode = document.createTextNode(text);\n this.parent.insertBefore(Parchment.create(textNode), this.next);\n range = {\n startNode: textNode,\n startOffset: text.length\n };\n }\n }\n node.data = GUARD_TEXT;\n return range;\n }\n\n update(mutations, context) {\n mutations.forEach((mutation) => {\n if (mutation.type === 'characterData' &&\n (mutation.target === this.leftGuard || mutation.target === this.rightGuard)) {\n let range = this.restore(mutation.target);\n if (range) context.range = range;\n }\n });\n }\n}\n\n\nexport default Embed;\n\n\n\n// WEBPACK FOOTER //\n// ./blots/embed.js","import Parchment from 'parchment';\n\nlet config = {\n scope: Parchment.Scope.BLOCK,\n whitelist: ['right', 'center', 'justify']\n};\n\nlet AlignAttribute = new Parchment.Attributor.Attribute('align', 'align', config);\nlet AlignClass = new Parchment.Attributor.Class('align', 'ql-align', config);\nlet AlignStyle = new Parchment.Attributor.Style('align', 'text-align', config);\n\nexport { AlignAttribute, AlignClass, AlignStyle };\n\n\n\n// WEBPACK FOOTER //\n// ./formats/align.js","import Parchment from 'parchment';\nimport { ColorAttributor } from './color';\n\nlet BackgroundClass = new Parchment.Attributor.Class('background', 'ql-bg', {\n scope: Parchment.Scope.INLINE\n});\nlet BackgroundStyle = new ColorAttributor('background', 'background-color', {\n scope: Parchment.Scope.INLINE\n});\n\nexport { BackgroundClass, BackgroundStyle };\n\n\n\n// WEBPACK FOOTER //\n// ./formats/background.js","import Parchment from 'parchment';\n\nlet config = {\n scope: Parchment.Scope.BLOCK,\n whitelist: ['rtl']\n};\n\nlet DirectionAttribute = new Parchment.Attributor.Attribute('direction', 'dir', config);\nlet DirectionClass = new Parchment.Attributor.Class('direction', 'ql-direction', config);\nlet DirectionStyle = new Parchment.Attributor.Style('direction', 'direction', config);\n\nexport { DirectionAttribute, DirectionClass, DirectionStyle };\n\n\n\n// WEBPACK FOOTER //\n// ./formats/direction.js","import Parchment from 'parchment';\n\nlet config = {\n scope: Parchment.Scope.INLINE,\n whitelist: ['serif', 'monospace']\n};\n\nlet FontClass = new Parchment.Attributor.Class('font', 'ql-font', config);\n\nclass FontStyleAttributor extends Parchment.Attributor.Style {\n value(node) {\n return super.value(node).replace(/[\"']/g, '');\n }\n}\n\nlet FontStyle = new FontStyleAttributor('font', 'font-family', config);\n\nexport { FontStyle, FontClass };\n\n\n\n// WEBPACK FOOTER //\n// ./formats/font.js","import Parchment from 'parchment';\n\nlet SizeClass = new Parchment.Attributor.Class('size', 'ql-size', {\n scope: Parchment.Scope.INLINE,\n whitelist: ['small', 'large', 'huge']\n});\nlet SizeStyle = new Parchment.Attributor.Style('size', 'font-size', {\n scope: Parchment.Scope.INLINE,\n whitelist: ['10px', '18px', '32px']\n});\n\nexport { SizeClass, SizeStyle };\n\n\n\n// WEBPACK FOOTER //\n// ./formats/size.js","import Inline from '../blots/inline';\n\nclass Bold extends Inline {\n static create() {\n return super.create();\n }\n\n static formats() {\n return true;\n }\n\n optimize(context) {\n super.optimize(context);\n if (this.domNode.tagName !== this.statics.tagName[0]) {\n this.replaceWith(this.statics.blotName);\n }\n }\n}\nBold.blotName = 'bold';\nBold.tagName = ['STRONG', 'B'];\n\nexport default Bold;\n\n\n\n// WEBPACK FOOTER //\n// ./formats/bold.js","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/code.svg\n// module id = 40\n// module chunks = 0","import Picker from './picker';\n\n\nclass ColorPicker extends Picker {\n constructor(select, label) {\n super(select);\n this.label.innerHTML = label;\n this.container.classList.add('ql-color-picker');\n [].slice.call(this.container.querySelectorAll('.ql-picker-item'), 0, 7).forEach(function(item) {\n item.classList.add('ql-primary');\n });\n }\n\n buildItem(option) {\n let item = super.buildItem(option);\n item.style.backgroundColor = option.getAttribute('value') || '';\n return item;\n }\n\n selectItem(item, trigger) {\n super.selectItem(item, trigger);\n let colorLabel = this.label.querySelector('.ql-color-label');\n let value = item ? item.getAttribute('data-value') || '' : '';\n if (colorLabel) {\n if (colorLabel.tagName === 'line') {\n colorLabel.style.stroke = value;\n } else {\n colorLabel.style.fill = value;\n }\n }\n }\n}\n\n\nexport default ColorPicker;\n\n\n\n// WEBPACK FOOTER //\n// ./ui/color-picker.js","import Picker from './picker';\n\n\nclass IconPicker extends Picker {\n constructor(select, icons) {\n super(select);\n this.container.classList.add('ql-icon-picker');\n [].forEach.call(this.container.querySelectorAll('.ql-picker-item'), (item) => {\n item.innerHTML = icons[item.getAttribute('data-value') || ''];\n });\n this.defaultItem = this.container.querySelector('.ql-selected');\n this.selectItem(this.defaultItem);\n }\n\n selectItem(item, trigger) {\n super.selectItem(item, trigger);\n item = item || this.defaultItem;\n this.label.innerHTML = item.innerHTML;\n }\n}\n\n\nexport default IconPicker;\n\n\n\n// WEBPACK FOOTER //\n// ./ui/icon-picker.js","class Tooltip {\n constructor(quill, boundsContainer) {\n this.quill = quill;\n this.boundsContainer = boundsContainer || document.body;\n this.root = quill.addContainer('ql-tooltip');\n this.root.innerHTML = this.constructor.TEMPLATE;\n if (this.quill.root === this.quill.scrollingContainer) {\n this.quill.root.addEventListener('scroll', () => {\n this.root.style.marginTop = (-1*this.quill.root.scrollTop) + 'px';\n });\n }\n this.hide();\n }\n\n hide() {\n this.root.classList.add('ql-hidden');\n }\n\n position(reference) {\n let left = reference.left + reference.width/2 - this.root.offsetWidth/2;\n // root.scrollTop should be 0 if scrollContainer !== root\n let top = reference.bottom + this.quill.root.scrollTop;\n this.root.style.left = left + 'px';\n this.root.style.top = top + 'px';\n this.root.classList.remove('ql-flip');\n let containerBounds = this.boundsContainer.getBoundingClientRect();\n let rootBounds = this.root.getBoundingClientRect();\n let shift = 0;\n if (rootBounds.right > containerBounds.right) {\n shift = containerBounds.right - rootBounds.right;\n this.root.style.left = (left + shift) + 'px';\n }\n if (rootBounds.left < containerBounds.left) {\n shift = containerBounds.left - rootBounds.left;\n this.root.style.left = (left + shift) + 'px';\n }\n if (rootBounds.bottom > containerBounds.bottom) {\n let height = rootBounds.bottom - rootBounds.top;\n let verticalShift = reference.bottom - reference.top + height;\n this.root.style.top = (top - verticalShift) + 'px';\n this.root.classList.add('ql-flip');\n }\n return shift;\n }\n\n show() {\n this.root.classList.remove('ql-editing');\n this.root.classList.remove('ql-hidden');\n }\n}\n\n\nexport default Tooltip;\n\n\n\n// WEBPACK FOOTER //\n// ./ui/tooltip.js","import extend from 'extend';\nimport Delta from 'quill-delta';\nimport Emitter from '../core/emitter';\nimport Keyboard from '../modules/keyboard';\nimport Theme from '../core/theme';\nimport ColorPicker from '../ui/color-picker';\nimport IconPicker from '../ui/icon-picker';\nimport Picker from '../ui/picker';\nimport Tooltip from '../ui/tooltip';\n\n\nconst ALIGNS = [ false, 'center', 'right', 'justify' ];\n\nconst COLORS = [\n \"#000000\", \"#e60000\", \"#ff9900\", \"#ffff00\", \"#008a00\", \"#0066cc\", \"#9933ff\",\n \"#ffffff\", \"#facccc\", \"#ffebcc\", \"#ffffcc\", \"#cce8cc\", \"#cce0f5\", \"#ebd6ff\",\n \"#bbbbbb\", \"#f06666\", \"#ffc266\", \"#ffff66\", \"#66b966\", \"#66a3e0\", \"#c285ff\",\n \"#888888\", \"#a10000\", \"#b26b00\", \"#b2b200\", \"#006100\", \"#0047b2\", \"#6b24b2\",\n \"#444444\", \"#5c0000\", \"#663d00\", \"#666600\", \"#003700\", \"#002966\", \"#3d1466\"\n];\n\nconst FONTS = [ false, 'serif', 'monospace' ];\n\nconst HEADERS = [ '1', '2', '3', false ];\n\nconst SIZES = [ 'small', false, 'large', 'huge' ];\n\n\nclass BaseTheme extends Theme {\n constructor(quill, options) {\n super(quill, options);\n let listener = (e) => {\n if (!document.body.contains(quill.root)) {\n return document.body.removeEventListener('click', listener);\n }\n if (this.tooltip != null && !this.tooltip.root.contains(e.target) &&\n document.activeElement !== this.tooltip.textbox && !this.quill.hasFocus()) {\n this.tooltip.hide();\n }\n if (this.pickers != null) {\n this.pickers.forEach(function(picker) {\n if (!picker.container.contains(e.target)) {\n picker.close();\n }\n });\n }\n };\n quill.emitter.listenDOM('click', document.body, listener);\n }\n\n addModule(name) {\n let module = super.addModule(name);\n if (name === 'toolbar') {\n this.extendToolbar(module);\n }\n return module;\n }\n\n buildButtons(buttons, icons) {\n buttons.forEach((button) => {\n let className = button.getAttribute('class') || '';\n className.split(/\\s+/).forEach((name) => {\n if (!name.startsWith('ql-')) return;\n name = name.slice('ql-'.length);\n if (icons[name] == null) return;\n if (name === 'direction') {\n button.innerHTML = icons[name][''] + icons[name]['rtl'];\n } else if (typeof icons[name] === 'string') {\n button.innerHTML = icons[name];\n } else {\n let value = button.value || '';\n if (value != null && icons[name][value]) {\n button.innerHTML = icons[name][value];\n }\n }\n });\n });\n }\n\n buildPickers(selects, icons) {\n this.pickers = selects.map((select) => {\n if (select.classList.contains('ql-align')) {\n if (select.querySelector('option') == null) {\n fillSelect(select, ALIGNS);\n }\n return new IconPicker(select, icons.align);\n } else if (select.classList.contains('ql-background') || select.classList.contains('ql-color')) {\n let format = select.classList.contains('ql-background') ? 'background' : 'color';\n if (select.querySelector('option') == null) {\n fillSelect(select, COLORS, format === 'background' ? '#ffffff' : '#000000');\n }\n return new ColorPicker(select, icons[format]);\n } else {\n if (select.querySelector('option') == null) {\n if (select.classList.contains('ql-font')) {\n fillSelect(select, FONTS);\n } else if (select.classList.contains('ql-header')) {\n fillSelect(select, HEADERS);\n } else if (select.classList.contains('ql-size')) {\n fillSelect(select, SIZES);\n }\n }\n return new Picker(select);\n }\n });\n let update = () => {\n this.pickers.forEach(function(picker) {\n picker.update();\n });\n };\n this.quill.on(Emitter.events.EDITOR_CHANGE, update);\n }\n}\nBaseTheme.DEFAULTS = extend(true, {}, Theme.DEFAULTS, {\n modules: {\n toolbar: {\n handlers: {\n formula: function() {\n this.quill.theme.tooltip.edit('formula');\n },\n image: function() {\n let fileInput = this.container.querySelector('input.ql-image[type=file]');\n if (fileInput == null) {\n fileInput = document.createElement('input');\n fileInput.setAttribute('type', 'file');\n fileInput.setAttribute('accept', 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon');\n fileInput.classList.add('ql-image');\n fileInput.addEventListener('change', () => {\n if (fileInput.files != null && fileInput.files[0] != null) {\n let reader = new FileReader();\n reader.onload = (e) => {\n let range = this.quill.getSelection(true);\n this.quill.updateContents(new Delta()\n .retain(range.index)\n .delete(range.length)\n .insert({ image: e.target.result })\n , Emitter.sources.USER);\n this.quill.setSelection(range.index + 1, Emitter.sources.SILENT);\n fileInput.value = \"\";\n }\n reader.readAsDataURL(fileInput.files[0]);\n }\n });\n this.container.appendChild(fileInput);\n }\n fileInput.click();\n },\n video: function() {\n this.quill.theme.tooltip.edit('video');\n }\n }\n }\n }\n});\n\n\nclass BaseTooltip extends Tooltip {\n constructor(quill, boundsContainer) {\n super(quill, boundsContainer);\n this.textbox = this.root.querySelector('input[type=\"text\"]');\n this.listen();\n }\n\n listen() {\n this.textbox.addEventListener('keydown', (event) => {\n if (Keyboard.match(event, 'enter')) {\n this.save();\n event.preventDefault();\n } else if (Keyboard.match(event, 'escape')) {\n this.cancel();\n event.preventDefault();\n }\n });\n }\n\n cancel() {\n this.hide();\n }\n\n edit(mode = 'link', preview = null) {\n this.root.classList.remove('ql-hidden');\n this.root.classList.add('ql-editing');\n if (preview != null) {\n this.textbox.value = preview;\n } else if (mode !== this.root.getAttribute('data-mode')) {\n this.textbox.value = '';\n }\n this.position(this.quill.getBounds(this.quill.selection.savedRange));\n this.textbox.select();\n this.textbox.setAttribute('placeholder', this.textbox.getAttribute(`data-${mode}`) || '');\n this.root.setAttribute('data-mode', mode);\n }\n\n restoreFocus() {\n let scrollTop = this.quill.scrollingContainer.scrollTop;\n this.quill.focus();\n this.quill.scrollingContainer.scrollTop = scrollTop;\n }\n\n save() {\n let value = this.textbox.value;\n switch(this.root.getAttribute('data-mode')) {\n case 'link': {\n let scrollTop = this.quill.root.scrollTop;\n if (this.linkRange) {\n this.quill.formatText(this.linkRange, 'link', value, Emitter.sources.USER);\n delete this.linkRange;\n } else {\n this.restoreFocus();\n this.quill.format('link', value, Emitter.sources.USER);\n }\n this.quill.root.scrollTop = scrollTop;\n break;\n }\n case 'video': {\n value = extractVideoUrl(value);\n } // eslint-disable-next-line no-fallthrough\n case 'formula': {\n if (!value) break;\n let range = this.quill.getSelection(true);\n if (range != null) {\n let index = range.index + range.length;\n this.quill.insertEmbed(index, this.root.getAttribute('data-mode'), value, Emitter.sources.USER);\n if (this.root.getAttribute('data-mode') === 'formula') {\n this.quill.insertText(index + 1, ' ', Emitter.sources.USER);\n }\n this.quill.setSelection(index + 2, Emitter.sources.USER);\n }\n break;\n }\n default:\n }\n this.textbox.value = '';\n this.hide();\n }\n}\n\n\nfunction extractVideoUrl(url) {\n let match = url.match(/^(?:(https?):\\/\\/)?(?:(?:www|m)\\.)?youtube\\.com\\/watch.*v=([a-zA-Z0-9_-]+)/) ||\n url.match(/^(?:(https?):\\/\\/)?(?:(?:www|m)\\.)?youtu\\.be\\/([a-zA-Z0-9_-]+)/);\n if (match) {\n return (match[1] || 'https') + '://www.youtube.com/embed/' + match[2] + '?showinfo=0';\n }\n if (match = url.match(/^(?:(https?):\\/\\/)?(?:www\\.)?vimeo\\.com\\/(\\d+)/)) { // eslint-disable-line no-cond-assign\n return (match[1] || 'https') + '://player.vimeo.com/video/' + match[2] + '/';\n }\n return url;\n}\n\nfunction fillSelect(select, values, defaultValue = false) {\n values.forEach(function(value) {\n let option = document.createElement('option');\n if (value === defaultValue) {\n option.setAttribute('selected', 'selected');\n } else {\n option.setAttribute('value', value);\n }\n select.appendChild(option);\n });\n}\n\n\nexport { BaseTooltip, BaseTheme as default };\n\n\n\n// WEBPACK FOOTER //\n// ./themes/base.js","import Quill from './core';\n\nimport { AlignClass, AlignStyle } from './formats/align';\nimport { DirectionAttribute, DirectionClass, DirectionStyle } from './formats/direction';\nimport { IndentClass as Indent } from './formats/indent';\n\nimport Blockquote from './formats/blockquote';\nimport Header from './formats/header';\nimport List, { ListItem } from './formats/list';\n\nimport { BackgroundClass, BackgroundStyle } from './formats/background';\nimport { ColorClass, ColorStyle } from './formats/color';\nimport { FontClass, FontStyle } from './formats/font';\nimport { SizeClass, SizeStyle } from './formats/size';\n\nimport Bold from './formats/bold';\nimport Italic from './formats/italic';\nimport Link from './formats/link';\nimport Script from './formats/script';\nimport Strike from './formats/strike';\nimport Underline from './formats/underline';\n\nimport Image from './formats/image';\nimport Video from './formats/video';\n\nimport CodeBlock, { Code as InlineCode } from './formats/code';\n\nimport Formula from './modules/formula';\nimport Syntax from './modules/syntax';\nimport Toolbar from './modules/toolbar';\n\nimport Icons from './ui/icons';\nimport Picker from './ui/picker';\nimport ColorPicker from './ui/color-picker';\nimport IconPicker from './ui/icon-picker';\nimport Tooltip from './ui/tooltip';\n\nimport BubbleTheme from './themes/bubble';\nimport SnowTheme from './themes/snow';\n\n\nQuill.register({\n 'attributors/attribute/direction': DirectionAttribute,\n\n 'attributors/class/align': AlignClass,\n 'attributors/class/background': BackgroundClass,\n 'attributors/class/color': ColorClass,\n 'attributors/class/direction': DirectionClass,\n 'attributors/class/font': FontClass,\n 'attributors/class/size': SizeClass,\n\n 'attributors/style/align': AlignStyle,\n 'attributors/style/background': BackgroundStyle,\n 'attributors/style/color': ColorStyle,\n 'attributors/style/direction': DirectionStyle,\n 'attributors/style/font': FontStyle,\n 'attributors/style/size': SizeStyle\n}, true);\n\n\nQuill.register({\n 'formats/align': AlignClass,\n 'formats/direction': DirectionClass,\n 'formats/indent': Indent,\n\n 'formats/background': BackgroundStyle,\n 'formats/color': ColorStyle,\n 'formats/font': FontClass,\n 'formats/size': SizeClass,\n\n 'formats/blockquote': Blockquote,\n 'formats/code-block': CodeBlock,\n 'formats/header': Header,\n 'formats/list': List,\n\n 'formats/bold': Bold,\n 'formats/code': InlineCode,\n 'formats/italic': Italic,\n 'formats/link': Link,\n 'formats/script': Script,\n 'formats/strike': Strike,\n 'formats/underline': Underline,\n\n 'formats/image': Image,\n 'formats/video': Video,\n\n 'formats/list/item': ListItem,\n\n 'modules/formula': Formula,\n 'modules/syntax': Syntax,\n 'modules/toolbar': Toolbar,\n\n 'themes/bubble': BubbleTheme,\n 'themes/snow': SnowTheme,\n\n 'ui/icons': Icons,\n 'ui/picker': Picker,\n 'ui/icon-picker': IconPicker,\n 'ui/color-picker': ColorPicker,\n 'ui/tooltip': Tooltip\n}, true);\n\n\nexport default Quill;\n\n\n\n// WEBPACK FOOTER //\n// ./quill.js","import Parchment from 'parchment';\nimport Quill from './core/quill';\n\nimport Block, { BlockEmbed } from './blots/block';\nimport Break from './blots/break';\nimport Container from './blots/container';\nimport Cursor from './blots/cursor';\nimport Embed from './blots/embed';\nimport Inline from './blots/inline';\nimport Scroll from './blots/scroll';\nimport TextBlot from './blots/text';\n\nimport Clipboard from './modules/clipboard';\nimport History from './modules/history';\nimport Keyboard from './modules/keyboard';\n\nQuill.register({\n 'blots/block' : Block,\n 'blots/block/embed' : BlockEmbed,\n 'blots/break' : Break,\n 'blots/container' : Container,\n 'blots/cursor' : Cursor,\n 'blots/embed' : Embed,\n 'blots/inline' : Inline,\n 'blots/scroll' : Scroll,\n 'blots/text' : TextBlot,\n\n 'modules/clipboard' : Clipboard,\n 'modules/history' : History,\n 'modules/keyboard' : Keyboard\n});\n\nParchment.register(Block, Break, Cursor, Inline, Scroll, TextBlot);\n\n\nexport default Quill;\n\n\n\n// WEBPACK FOOTER //\n// ./core.js","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar LinkedList = /** @class */ (function () {\n function LinkedList() {\n this.head = this.tail = null;\n this.length = 0;\n }\n LinkedList.prototype.append = function () {\n var nodes = [];\n for (var _i = 0; _i < arguments.length; _i++) {\n nodes[_i] = arguments[_i];\n }\n this.insertBefore(nodes[0], null);\n if (nodes.length > 1) {\n this.append.apply(this, nodes.slice(1));\n }\n };\n LinkedList.prototype.contains = function (node) {\n var cur, next = this.iterator();\n while ((cur = next())) {\n if (cur === node)\n return true;\n }\n return false;\n };\n LinkedList.prototype.insertBefore = function (node, refNode) {\n if (!node)\n return;\n node.next = refNode;\n if (refNode != null) {\n node.prev = refNode.prev;\n if (refNode.prev != null) {\n refNode.prev.next = node;\n }\n refNode.prev = node;\n if (refNode === this.head) {\n this.head = node;\n }\n }\n else if (this.tail != null) {\n this.tail.next = node;\n node.prev = this.tail;\n this.tail = node;\n }\n else {\n node.prev = null;\n this.head = this.tail = node;\n }\n this.length += 1;\n };\n LinkedList.prototype.offset = function (target) {\n var index = 0, cur = this.head;\n while (cur != null) {\n if (cur === target)\n return index;\n index += cur.length();\n cur = cur.next;\n }\n return -1;\n };\n LinkedList.prototype.remove = function (node) {\n if (!this.contains(node))\n return;\n if (node.prev != null)\n node.prev.next = node.next;\n if (node.next != null)\n node.next.prev = node.prev;\n if (node === this.head)\n this.head = node.next;\n if (node === this.tail)\n this.tail = node.prev;\n this.length -= 1;\n };\n LinkedList.prototype.iterator = function (curNode) {\n if (curNode === void 0) { curNode = this.head; }\n // TODO use yield when we can\n return function () {\n var ret = curNode;\n if (curNode != null)\n curNode = curNode.next;\n return ret;\n };\n };\n LinkedList.prototype.find = function (index, inclusive) {\n if (inclusive === void 0) { inclusive = false; }\n var cur, next = this.iterator();\n while ((cur = next())) {\n var length = cur.length();\n if (index < length ||\n (inclusive && index === length && (cur.next == null || cur.next.length() !== 0))) {\n return [cur, index];\n }\n index -= length;\n }\n return [null, 0];\n };\n LinkedList.prototype.forEach = function (callback) {\n var cur, next = this.iterator();\n while ((cur = next())) {\n callback(cur);\n }\n };\n LinkedList.prototype.forEachAt = function (index, length, callback) {\n if (length <= 0)\n return;\n var _a = this.find(index), startNode = _a[0], offset = _a[1];\n var cur, curIndex = index - offset, next = this.iterator(startNode);\n while ((cur = next()) && curIndex < index + length) {\n var curLength = cur.length();\n if (index > curIndex) {\n callback(cur, index - curIndex, Math.min(length, curIndex + curLength - index));\n }\n else {\n callback(cur, 0, Math.min(curLength, index + length - curIndex));\n }\n curIndex += curLength;\n }\n };\n LinkedList.prototype.map = function (callback) {\n return this.reduce(function (memo, cur) {\n memo.push(callback(cur));\n return memo;\n }, []);\n };\n LinkedList.prototype.reduce = function (callback, memo) {\n var cur, next = this.iterator();\n while ((cur = next())) {\n memo = callback(memo, cur);\n }\n return memo;\n };\n return LinkedList;\n}());\nexports.default = LinkedList;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/parchment/src/collection/linked-list.ts\n// module id = 47\n// module chunks = 0","\"use strict\";\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar container_1 = require(\"./abstract/container\");\nvar Registry = require(\"../registry\");\nvar OBSERVER_CONFIG = {\n attributes: true,\n characterData: true,\n characterDataOldValue: true,\n childList: true,\n subtree: true,\n};\nvar MAX_OPTIMIZE_ITERATIONS = 100;\nvar ScrollBlot = /** @class */ (function (_super) {\n __extends(ScrollBlot, _super);\n function ScrollBlot(node) {\n var _this = _super.call(this, node) || this;\n _this.scroll = _this;\n _this.observer = new MutationObserver(function (mutations) {\n _this.update(mutations);\n });\n _this.observer.observe(_this.domNode, OBSERVER_CONFIG);\n _this.attach();\n return _this;\n }\n ScrollBlot.prototype.detach = function () {\n _super.prototype.detach.call(this);\n this.observer.disconnect();\n };\n ScrollBlot.prototype.deleteAt = function (index, length) {\n this.update();\n if (index === 0 && length === this.length()) {\n this.children.forEach(function (child) {\n child.remove();\n });\n }\n else {\n _super.prototype.deleteAt.call(this, index, length);\n }\n };\n ScrollBlot.prototype.formatAt = function (index, length, name, value) {\n this.update();\n _super.prototype.formatAt.call(this, index, length, name, value);\n };\n ScrollBlot.prototype.insertAt = function (index, value, def) {\n this.update();\n _super.prototype.insertAt.call(this, index, value, def);\n };\n ScrollBlot.prototype.optimize = function (mutations, context) {\n var _this = this;\n if (mutations === void 0) { mutations = []; }\n if (context === void 0) { context = {}; }\n _super.prototype.optimize.call(this, context);\n // We must modify mutations directly, cannot make copy and then modify\n var records = [].slice.call(this.observer.takeRecords());\n // Array.push currently seems to be implemented by a non-tail recursive function\n // so we cannot just mutations.push.apply(mutations, this.observer.takeRecords());\n while (records.length > 0)\n mutations.push(records.pop());\n // TODO use WeakMap\n var mark = function (blot, markParent) {\n if (markParent === void 0) { markParent = true; }\n if (blot == null || blot === _this)\n return;\n if (blot.domNode.parentNode == null)\n return;\n // @ts-ignore\n if (blot.domNode[Registry.DATA_KEY].mutations == null) {\n // @ts-ignore\n blot.domNode[Registry.DATA_KEY].mutations = [];\n }\n if (markParent)\n mark(blot.parent);\n };\n var optimize = function (blot) {\n // Post-order traversal\n if (\n // @ts-ignore\n blot.domNode[Registry.DATA_KEY] == null ||\n // @ts-ignore\n blot.domNode[Registry.DATA_KEY].mutations == null) {\n return;\n }\n if (blot instanceof container_1.default) {\n blot.children.forEach(optimize);\n }\n blot.optimize(context);\n };\n var remaining = mutations;\n for (var i = 0; remaining.length > 0; i += 1) {\n if (i >= MAX_OPTIMIZE_ITERATIONS) {\n throw new Error('[Parchment] Maximum optimize iterations reached');\n }\n remaining.forEach(function (mutation) {\n var blot = Registry.find(mutation.target, true);\n if (blot == null)\n return;\n if (blot.domNode === mutation.target) {\n if (mutation.type === 'childList') {\n mark(Registry.find(mutation.previousSibling, false));\n [].forEach.call(mutation.addedNodes, function (node) {\n var child = Registry.find(node, false);\n mark(child, false);\n if (child instanceof container_1.default) {\n child.children.forEach(function (grandChild) {\n mark(grandChild, false);\n });\n }\n });\n }\n else if (mutation.type === 'attributes') {\n mark(blot.prev);\n }\n }\n mark(blot);\n });\n this.children.forEach(optimize);\n remaining = [].slice.call(this.observer.takeRecords());\n records = remaining.slice();\n while (records.length > 0)\n mutations.push(records.pop());\n }\n };\n ScrollBlot.prototype.update = function (mutations, context) {\n var _this = this;\n if (context === void 0) { context = {}; }\n mutations = mutations || this.observer.takeRecords();\n // TODO use WeakMap\n mutations\n .map(function (mutation) {\n var blot = Registry.find(mutation.target, true);\n if (blot == null)\n return null;\n // @ts-ignore\n if (blot.domNode[Registry.DATA_KEY].mutations == null) {\n // @ts-ignore\n blot.domNode[Registry.DATA_KEY].mutations = [mutation];\n return blot;\n }\n else {\n // @ts-ignore\n blot.domNode[Registry.DATA_KEY].mutations.push(mutation);\n return null;\n }\n })\n .forEach(function (blot) {\n if (blot == null ||\n blot === _this ||\n //@ts-ignore\n blot.domNode[Registry.DATA_KEY] == null)\n return;\n // @ts-ignore\n blot.update(blot.domNode[Registry.DATA_KEY].mutations || [], context);\n });\n // @ts-ignore\n if (this.domNode[Registry.DATA_KEY].mutations != null) {\n // @ts-ignore\n _super.prototype.update.call(this, this.domNode[Registry.DATA_KEY].mutations, context);\n }\n this.optimize(mutations, context);\n };\n ScrollBlot.blotName = 'scroll';\n ScrollBlot.defaultChild = 'block';\n ScrollBlot.scope = Registry.Scope.BLOCK_BLOT;\n ScrollBlot.tagName = 'DIV';\n return ScrollBlot;\n}(container_1.default));\nexports.default = ScrollBlot;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/parchment/src/blot/scroll.ts\n// module id = 48\n// module chunks = 0","\"use strict\";\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar format_1 = require(\"./abstract/format\");\nvar Registry = require(\"../registry\");\n// Shallow object comparison\nfunction isEqual(obj1, obj2) {\n if (Object.keys(obj1).length !== Object.keys(obj2).length)\n return false;\n // @ts-ignore\n for (var prop in obj1) {\n // @ts-ignore\n if (obj1[prop] !== obj2[prop])\n return false;\n }\n return true;\n}\nvar InlineBlot = /** @class */ (function (_super) {\n __extends(InlineBlot, _super);\n function InlineBlot() {\n return _super !== null && _super.apply(this, arguments) || this;\n }\n InlineBlot.formats = function (domNode) {\n if (domNode.tagName === InlineBlot.tagName)\n return undefined;\n return _super.formats.call(this, domNode);\n };\n InlineBlot.prototype.format = function (name, value) {\n var _this = this;\n if (name === this.statics.blotName && !value) {\n this.children.forEach(function (child) {\n if (!(child instanceof format_1.default)) {\n child = child.wrap(InlineBlot.blotName, true);\n }\n _this.attributes.copy(child);\n });\n this.unwrap();\n }\n else {\n _super.prototype.format.call(this, name, value);\n }\n };\n InlineBlot.prototype.formatAt = function (index, length, name, value) {\n if (this.formats()[name] != null || Registry.query(name, Registry.Scope.ATTRIBUTE)) {\n var blot = this.isolate(index, length);\n blot.format(name, value);\n }\n else {\n _super.prototype.formatAt.call(this, index, length, name, value);\n }\n };\n InlineBlot.prototype.optimize = function (context) {\n _super.prototype.optimize.call(this, context);\n var formats = this.formats();\n if (Object.keys(formats).length === 0) {\n return this.unwrap(); // unformatted span\n }\n var next = this.next;\n if (next instanceof InlineBlot && next.prev === this && isEqual(formats, next.formats())) {\n next.moveChildren(this);\n next.remove();\n }\n };\n InlineBlot.blotName = 'inline';\n InlineBlot.scope = Registry.Scope.INLINE_BLOT;\n InlineBlot.tagName = 'SPAN';\n return InlineBlot;\n}(format_1.default));\nexports.default = InlineBlot;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/parchment/src/blot/inline.ts\n// module id = 49\n// module chunks = 0","\"use strict\";\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar format_1 = require(\"./abstract/format\");\nvar Registry = require(\"../registry\");\nvar BlockBlot = /** @class */ (function (_super) {\n __extends(BlockBlot, _super);\n function BlockBlot() {\n return _super !== null && _super.apply(this, arguments) || this;\n }\n BlockBlot.formats = function (domNode) {\n var tagName = Registry.query(BlockBlot.blotName).tagName;\n if (domNode.tagName === tagName)\n return undefined;\n return _super.formats.call(this, domNode);\n };\n BlockBlot.prototype.format = function (name, value) {\n if (Registry.query(name, Registry.Scope.BLOCK) == null) {\n return;\n }\n else if (name === this.statics.blotName && !value) {\n this.replaceWith(BlockBlot.blotName);\n }\n else {\n _super.prototype.format.call(this, name, value);\n }\n };\n BlockBlot.prototype.formatAt = function (index, length, name, value) {\n if (Registry.query(name, Registry.Scope.BLOCK) != null) {\n this.format(name, value);\n }\n else {\n _super.prototype.formatAt.call(this, index, length, name, value);\n }\n };\n BlockBlot.prototype.insertAt = function (index, value, def) {\n if (def == null || Registry.query(value, Registry.Scope.INLINE) != null) {\n // Insert text or inline\n _super.prototype.insertAt.call(this, index, value, def);\n }\n else {\n var after = this.split(index);\n var blot = Registry.create(value, def);\n after.parent.insertBefore(blot, after);\n }\n };\n BlockBlot.prototype.update = function (mutations, context) {\n if (navigator.userAgent.match(/Trident/)) {\n this.build();\n }\n else {\n _super.prototype.update.call(this, mutations, context);\n }\n };\n BlockBlot.blotName = 'block';\n BlockBlot.scope = Registry.Scope.BLOCK_BLOT;\n BlockBlot.tagName = 'P';\n return BlockBlot;\n}(format_1.default));\nexports.default = BlockBlot;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/parchment/src/blot/block.ts\n// module id = 50\n// module chunks = 0","\"use strict\";\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar leaf_1 = require(\"./abstract/leaf\");\nvar EmbedBlot = /** @class */ (function (_super) {\n __extends(EmbedBlot, _super);\n function EmbedBlot() {\n return _super !== null && _super.apply(this, arguments) || this;\n }\n EmbedBlot.formats = function (domNode) {\n return undefined;\n };\n EmbedBlot.prototype.format = function (name, value) {\n // super.formatAt wraps, which is what we want in general,\n // but this allows subclasses to overwrite for formats\n // that just apply to particular embeds\n _super.prototype.formatAt.call(this, 0, this.length(), name, value);\n };\n EmbedBlot.prototype.formatAt = function (index, length, name, value) {\n if (index === 0 && length === this.length()) {\n this.format(name, value);\n }\n else {\n _super.prototype.formatAt.call(this, index, length, name, value);\n }\n };\n EmbedBlot.prototype.formats = function () {\n return this.statics.formats(this.domNode);\n };\n return EmbedBlot;\n}(leaf_1.default));\nexports.default = EmbedBlot;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/parchment/src/blot/embed.ts\n// module id = 51\n// module chunks = 0","\"use strict\";\nvar __extends = (this && this.__extends) || (function () {\n var extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\n return function (d, b) {\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n };\n})();\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar leaf_1 = require(\"./abstract/leaf\");\nvar Registry = require(\"../registry\");\nvar TextBlot = /** @class */ (function (_super) {\n __extends(TextBlot, _super);\n function TextBlot(node) {\n var _this = _super.call(this, node) || this;\n _this.text = _this.statics.value(_this.domNode);\n return _this;\n }\n TextBlot.create = function (value) {\n return document.createTextNode(value);\n };\n TextBlot.value = function (domNode) {\n var text = domNode.data;\n // @ts-ignore\n if (text['normalize'])\n text = text['normalize']();\n return text;\n };\n TextBlot.prototype.deleteAt = function (index, length) {\n this.domNode.data = this.text = this.text.slice(0, index) + this.text.slice(index + length);\n };\n TextBlot.prototype.index = function (node, offset) {\n if (this.domNode === node) {\n return offset;\n }\n return -1;\n };\n TextBlot.prototype.insertAt = function (index, value, def) {\n if (def == null) {\n this.text = this.text.slice(0, index) + value + this.text.slice(index);\n this.domNode.data = this.text;\n }\n else {\n _super.prototype.insertAt.call(this, index, value, def);\n }\n };\n TextBlot.prototype.length = function () {\n return this.text.length;\n };\n TextBlot.prototype.optimize = function (context) {\n _super.prototype.optimize.call(this, context);\n this.text = this.statics.value(this.domNode);\n if (this.text.length === 0) {\n this.remove();\n }\n else if (this.next instanceof TextBlot && this.next.prev === this) {\n this.insertAt(this.length(), this.next.value());\n this.next.remove();\n }\n };\n TextBlot.prototype.position = function (index, inclusive) {\n if (inclusive === void 0) { inclusive = false; }\n return [this.domNode, index];\n };\n TextBlot.prototype.split = function (index, force) {\n if (force === void 0) { force = false; }\n if (!force) {\n if (index === 0)\n return this;\n if (index === this.length())\n return this.next;\n }\n var after = Registry.create(this.domNode.splitText(index));\n this.parent.insertBefore(after, this.next);\n this.text = this.statics.value(this.domNode);\n return after;\n };\n TextBlot.prototype.update = function (mutations, context) {\n var _this = this;\n if (mutations.some(function (mutation) {\n return mutation.type === 'characterData' && mutation.target === _this.domNode;\n })) {\n this.text = this.statics.value(this.domNode);\n }\n };\n TextBlot.prototype.value = function () {\n return this.text;\n };\n TextBlot.blotName = 'text';\n TextBlot.scope = Registry.Scope.INLINE_BLOT;\n return TextBlot;\n}(leaf_1.default));\nexports.default = TextBlot;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/parchment/src/blot/text.ts\n// module id = 52\n// module chunks = 0","let elem = document.createElement('div');\nelem.classList.toggle('test-class', false);\nif (elem.classList.contains('test-class')) {\n let _toggle = DOMTokenList.prototype.toggle;\n DOMTokenList.prototype.toggle = function(token, force) {\n if (arguments.length > 1 && !this.contains(token) === !force) {\n return force;\n } else {\n return _toggle.call(this, token);\n }\n };\n}\n\nif (!String.prototype.startsWith) {\n String.prototype.startsWith = function(searchString, position){\n position = position || 0;\n return this.substr(position, searchString.length) === searchString;\n };\n}\n\nif (!String.prototype.endsWith) {\n String.prototype.endsWith = function(searchString, position) {\n var subjectString = this.toString();\n if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {\n position = subjectString.length;\n }\n position -= searchString.length;\n var lastIndex = subjectString.indexOf(searchString, position);\n return lastIndex !== -1 && lastIndex === position;\n };\n}\n\nif (!Array.prototype.find) {\n Object.defineProperty(Array.prototype, \"find\", {\n value: function(predicate) {\n if (this === null) {\n throw new TypeError('Array.prototype.find called on null or undefined');\n }\n if (typeof predicate !== 'function') {\n throw new TypeError('predicate must be a function');\n }\n var list = Object(this);\n var length = list.length >>> 0;\n var thisArg = arguments[1];\n var value;\n\n for (var i = 0; i < length; i++) {\n value = list[i];\n if (predicate.call(thisArg, value, i, list)) {\n return value;\n }\n }\n return undefined;\n }\n });\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", function() {\n // Disable resizing in Firefox\n document.execCommand(\"enableObjectResizing\", false, false);\n // Disable automatic linkifying in IE11\n document.execCommand(\"autoUrlDetect\", false, false);\n});\n\n\n\n// WEBPACK FOOTER //\n// ./core/polyfill.js","/**\n * This library modifies the diff-patch-match library by Neil Fraser\n * by removing the patch and match functionality and certain advanced\n * options in the diff function. The original license is as follows:\n *\n * ===\n *\n * Diff Match and Patch\n *\n * Copyright 2006 Google Inc.\n * http://code.google.com/p/google-diff-match-patch/\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\n/**\n * The data structure representing a diff is an array of tuples:\n * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]\n * which means: delete 'Hello', add 'Goodbye' and keep ' world.'\n */\nvar DIFF_DELETE = -1;\nvar DIFF_INSERT = 1;\nvar DIFF_EQUAL = 0;\n\n\n/**\n * Find the differences between two texts. Simplifies the problem by stripping\n * any common prefix or suffix off the texts before diffing.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @param {Int} cursor_pos Expected edit position in text1 (optional)\n * @return {Array} Array of diff tuples.\n */\nfunction diff_main(text1, text2, cursor_pos) {\n // Check for equality (speedup).\n if (text1 == text2) {\n if (text1) {\n return [[DIFF_EQUAL, text1]];\n }\n return [];\n }\n\n // Check cursor_pos within bounds\n if (cursor_pos < 0 || text1.length < cursor_pos) {\n cursor_pos = null;\n }\n\n // Trim off common prefix (speedup).\n var commonlength = diff_commonPrefix(text1, text2);\n var commonprefix = text1.substring(0, commonlength);\n text1 = text1.substring(commonlength);\n text2 = text2.substring(commonlength);\n\n // Trim off common suffix (speedup).\n commonlength = diff_commonSuffix(text1, text2);\n var commonsuffix = text1.substring(text1.length - commonlength);\n text1 = text1.substring(0, text1.length - commonlength);\n text2 = text2.substring(0, text2.length - commonlength);\n\n // Compute the diff on the middle block.\n var diffs = diff_compute_(text1, text2);\n\n // Restore the prefix and suffix.\n if (commonprefix) {\n diffs.unshift([DIFF_EQUAL, commonprefix]);\n }\n if (commonsuffix) {\n diffs.push([DIFF_EQUAL, commonsuffix]);\n }\n diff_cleanupMerge(diffs);\n if (cursor_pos != null) {\n diffs = fix_cursor(diffs, cursor_pos);\n }\n diffs = fix_emoji(diffs);\n return diffs;\n};\n\n\n/**\n * Find the differences between two texts. Assumes that the texts do not\n * have any common prefix or suffix.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @return {Array} Array of diff tuples.\n */\nfunction diff_compute_(text1, text2) {\n var diffs;\n\n if (!text1) {\n // Just add some text (speedup).\n return [[DIFF_INSERT, text2]];\n }\n\n if (!text2) {\n // Just delete some text (speedup).\n return [[DIFF_DELETE, text1]];\n }\n\n var longtext = text1.length > text2.length ? text1 : text2;\n var shorttext = text1.length > text2.length ? text2 : text1;\n var i = longtext.indexOf(shorttext);\n if (i != -1) {\n // Shorter text is inside the longer text (speedup).\n diffs = [[DIFF_INSERT, longtext.substring(0, i)],\n [DIFF_EQUAL, shorttext],\n [DIFF_INSERT, longtext.substring(i + shorttext.length)]];\n // Swap insertions for deletions if diff is reversed.\n if (text1.length > text2.length) {\n diffs[0][0] = diffs[2][0] = DIFF_DELETE;\n }\n return diffs;\n }\n\n if (shorttext.length == 1) {\n // Single character string.\n // After the previous speedup, the character can't be an equality.\n return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];\n }\n\n // Check to see if the problem can be split in two.\n var hm = diff_halfMatch_(text1, text2);\n if (hm) {\n // A half-match was found, sort out the return data.\n var text1_a = hm[0];\n var text1_b = hm[1];\n var text2_a = hm[2];\n var text2_b = hm[3];\n var mid_common = hm[4];\n // Send both pairs off for separate processing.\n var diffs_a = diff_main(text1_a, text2_a);\n var diffs_b = diff_main(text1_b, text2_b);\n // Merge the results.\n return diffs_a.concat([[DIFF_EQUAL, mid_common]], diffs_b);\n }\n\n return diff_bisect_(text1, text2);\n};\n\n\n/**\n * Find the 'middle snake' of a diff, split the problem in two\n * and return the recursively constructed diff.\n * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @return {Array} Array of diff tuples.\n * @private\n */\nfunction diff_bisect_(text1, text2) {\n // Cache the text lengths to prevent multiple calls.\n var text1_length = text1.length;\n var text2_length = text2.length;\n var max_d = Math.ceil((text1_length + text2_length) / 2);\n var v_offset = max_d;\n var v_length = 2 * max_d;\n var v1 = new Array(v_length);\n var v2 = new Array(v_length);\n // Setting all elements to -1 is faster in Chrome & Firefox than mixing\n // integers and undefined.\n for (var x = 0; x < v_length; x++) {\n v1[x] = -1;\n v2[x] = -1;\n }\n v1[v_offset + 1] = 0;\n v2[v_offset + 1] = 0;\n var delta = text1_length - text2_length;\n // If the total number of characters is odd, then the front path will collide\n // with the reverse path.\n var front = (delta % 2 != 0);\n // Offsets for start and end of k loop.\n // Prevents mapping of space beyond the grid.\n var k1start = 0;\n var k1end = 0;\n var k2start = 0;\n var k2end = 0;\n for (var d = 0; d < max_d; d++) {\n // Walk the front path one step.\n for (var k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {\n var k1_offset = v_offset + k1;\n var x1;\n if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) {\n x1 = v1[k1_offset + 1];\n } else {\n x1 = v1[k1_offset - 1] + 1;\n }\n var y1 = x1 - k1;\n while (x1 < text1_length && y1 < text2_length &&\n text1.charAt(x1) == text2.charAt(y1)) {\n x1++;\n y1++;\n }\n v1[k1_offset] = x1;\n if (x1 > text1_length) {\n // Ran off the right of the graph.\n k1end += 2;\n } else if (y1 > text2_length) {\n // Ran off the bottom of the graph.\n k1start += 2;\n } else if (front) {\n var k2_offset = v_offset + delta - k1;\n if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) {\n // Mirror x2 onto top-left coordinate system.\n var x2 = text1_length - v2[k2_offset];\n if (x1 >= x2) {\n // Overlap detected.\n return diff_bisectSplit_(text1, text2, x1, y1);\n }\n }\n }\n }\n\n // Walk the reverse path one step.\n for (var k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {\n var k2_offset = v_offset + k2;\n var x2;\n if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) {\n x2 = v2[k2_offset + 1];\n } else {\n x2 = v2[k2_offset - 1] + 1;\n }\n var y2 = x2 - k2;\n while (x2 < text1_length && y2 < text2_length &&\n text1.charAt(text1_length - x2 - 1) ==\n text2.charAt(text2_length - y2 - 1)) {\n x2++;\n y2++;\n }\n v2[k2_offset] = x2;\n if (x2 > text1_length) {\n // Ran off the left of the graph.\n k2end += 2;\n } else if (y2 > text2_length) {\n // Ran off the top of the graph.\n k2start += 2;\n } else if (!front) {\n var k1_offset = v_offset + delta - k2;\n if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) {\n var x1 = v1[k1_offset];\n var y1 = v_offset + x1 - k1_offset;\n // Mirror x2 onto top-left coordinate system.\n x2 = text1_length - x2;\n if (x1 >= x2) {\n // Overlap detected.\n return diff_bisectSplit_(text1, text2, x1, y1);\n }\n }\n }\n }\n }\n // Diff took too long and hit the deadline or\n // number of diffs equals number of characters, no commonality at all.\n return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];\n};\n\n\n/**\n * Given the location of the 'middle snake', split the diff in two parts\n * and recurse.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @param {number} x Index of split point in text1.\n * @param {number} y Index of split point in text2.\n * @return {Array} Array of diff tuples.\n */\nfunction diff_bisectSplit_(text1, text2, x, y) {\n var text1a = text1.substring(0, x);\n var text2a = text2.substring(0, y);\n var text1b = text1.substring(x);\n var text2b = text2.substring(y);\n\n // Compute both diffs serially.\n var diffs = diff_main(text1a, text2a);\n var diffsb = diff_main(text1b, text2b);\n\n return diffs.concat(diffsb);\n};\n\n\n/**\n * Determine the common prefix of two strings.\n * @param {string} text1 First string.\n * @param {string} text2 Second string.\n * @return {number} The number of characters common to the start of each\n * string.\n */\nfunction diff_commonPrefix(text1, text2) {\n // Quick check for common null cases.\n if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) {\n return 0;\n }\n // Binary search.\n // Performance analysis: http://neil.fraser.name/news/2007/10/09/\n var pointermin = 0;\n var pointermax = Math.min(text1.length, text2.length);\n var pointermid = pointermax;\n var pointerstart = 0;\n while (pointermin < pointermid) {\n if (text1.substring(pointerstart, pointermid) ==\n text2.substring(pointerstart, pointermid)) {\n pointermin = pointermid;\n pointerstart = pointermin;\n } else {\n pointermax = pointermid;\n }\n pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);\n }\n return pointermid;\n};\n\n\n/**\n * Determine the common suffix of two strings.\n * @param {string} text1 First string.\n * @param {string} text2 Second string.\n * @return {number} The number of characters common to the end of each string.\n */\nfunction diff_commonSuffix(text1, text2) {\n // Quick check for common null cases.\n if (!text1 || !text2 ||\n text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) {\n return 0;\n }\n // Binary search.\n // Performance analysis: http://neil.fraser.name/news/2007/10/09/\n var pointermin = 0;\n var pointermax = Math.min(text1.length, text2.length);\n var pointermid = pointermax;\n var pointerend = 0;\n while (pointermin < pointermid) {\n if (text1.substring(text1.length - pointermid, text1.length - pointerend) ==\n text2.substring(text2.length - pointermid, text2.length - pointerend)) {\n pointermin = pointermid;\n pointerend = pointermin;\n } else {\n pointermax = pointermid;\n }\n pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);\n }\n return pointermid;\n};\n\n\n/**\n * Do the two texts share a substring which is at least half the length of the\n * longer text?\n * This speedup can produce non-minimal diffs.\n * @param {string} text1 First string.\n * @param {string} text2 Second string.\n * @return {Array.} Five element Array, containing the prefix of\n * text1, the suffix of text1, the prefix of text2, the suffix of\n * text2 and the common middle. Or null if there was no match.\n */\nfunction diff_halfMatch_(text1, text2) {\n var longtext = text1.length > text2.length ? text1 : text2;\n var shorttext = text1.length > text2.length ? text2 : text1;\n if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {\n return null; // Pointless.\n }\n\n /**\n * Does a substring of shorttext exist within longtext such that the substring\n * is at least half the length of longtext?\n * Closure, but does not reference any external variables.\n * @param {string} longtext Longer string.\n * @param {string} shorttext Shorter string.\n * @param {number} i Start index of quarter length substring within longtext.\n * @return {Array.} Five element Array, containing the prefix of\n * longtext, the suffix of longtext, the prefix of shorttext, the suffix\n * of shorttext and the common middle. Or null if there was no match.\n * @private\n */\n function diff_halfMatchI_(longtext, shorttext, i) {\n // Start with a 1/4 length substring at position i as a seed.\n var seed = longtext.substring(i, i + Math.floor(longtext.length / 4));\n var j = -1;\n var best_common = '';\n var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b;\n while ((j = shorttext.indexOf(seed, j + 1)) != -1) {\n var prefixLength = diff_commonPrefix(longtext.substring(i),\n shorttext.substring(j));\n var suffixLength = diff_commonSuffix(longtext.substring(0, i),\n shorttext.substring(0, j));\n if (best_common.length < suffixLength + prefixLength) {\n best_common = shorttext.substring(j - suffixLength, j) +\n shorttext.substring(j, j + prefixLength);\n best_longtext_a = longtext.substring(0, i - suffixLength);\n best_longtext_b = longtext.substring(i + prefixLength);\n best_shorttext_a = shorttext.substring(0, j - suffixLength);\n best_shorttext_b = shorttext.substring(j + prefixLength);\n }\n }\n if (best_common.length * 2 >= longtext.length) {\n return [best_longtext_a, best_longtext_b,\n best_shorttext_a, best_shorttext_b, best_common];\n } else {\n return null;\n }\n }\n\n // First check if the second quarter is the seed for a half-match.\n var hm1 = diff_halfMatchI_(longtext, shorttext,\n Math.ceil(longtext.length / 4));\n // Check again based on the third quarter.\n var hm2 = diff_halfMatchI_(longtext, shorttext,\n Math.ceil(longtext.length / 2));\n var hm;\n if (!hm1 && !hm2) {\n return null;\n } else if (!hm2) {\n hm = hm1;\n } else if (!hm1) {\n hm = hm2;\n } else {\n // Both matched. Select the longest.\n hm = hm1[4].length > hm2[4].length ? hm1 : hm2;\n }\n\n // A half-match was found, sort out the return data.\n var text1_a, text1_b, text2_a, text2_b;\n if (text1.length > text2.length) {\n text1_a = hm[0];\n text1_b = hm[1];\n text2_a = hm[2];\n text2_b = hm[3];\n } else {\n text2_a = hm[0];\n text2_b = hm[1];\n text1_a = hm[2];\n text1_b = hm[3];\n }\n var mid_common = hm[4];\n return [text1_a, text1_b, text2_a, text2_b, mid_common];\n};\n\n\n/**\n * Reorder and merge like edit sections. Merge equalities.\n * Any edit section can move as long as it doesn't cross an equality.\n * @param {Array} diffs Array of diff tuples.\n */\nfunction diff_cleanupMerge(diffs) {\n diffs.push([DIFF_EQUAL, '']); // Add a dummy entry at the end.\n var pointer = 0;\n var count_delete = 0;\n var count_insert = 0;\n var text_delete = '';\n var text_insert = '';\n var commonlength;\n while (pointer < diffs.length) {\n switch (diffs[pointer][0]) {\n case DIFF_INSERT:\n count_insert++;\n text_insert += diffs[pointer][1];\n pointer++;\n break;\n case DIFF_DELETE:\n count_delete++;\n text_delete += diffs[pointer][1];\n pointer++;\n break;\n case DIFF_EQUAL:\n // Upon reaching an equality, check for prior redundancies.\n if (count_delete + count_insert > 1) {\n if (count_delete !== 0 && count_insert !== 0) {\n // Factor out any common prefixies.\n commonlength = diff_commonPrefix(text_insert, text_delete);\n if (commonlength !== 0) {\n if ((pointer - count_delete - count_insert) > 0 &&\n diffs[pointer - count_delete - count_insert - 1][0] ==\n DIFF_EQUAL) {\n diffs[pointer - count_delete - count_insert - 1][1] +=\n text_insert.substring(0, commonlength);\n } else {\n diffs.splice(0, 0, [DIFF_EQUAL,\n text_insert.substring(0, commonlength)]);\n pointer++;\n }\n text_insert = text_insert.substring(commonlength);\n text_delete = text_delete.substring(commonlength);\n }\n // Factor out any common suffixies.\n commonlength = diff_commonSuffix(text_insert, text_delete);\n if (commonlength !== 0) {\n diffs[pointer][1] = text_insert.substring(text_insert.length -\n commonlength) + diffs[pointer][1];\n text_insert = text_insert.substring(0, text_insert.length -\n commonlength);\n text_delete = text_delete.substring(0, text_delete.length -\n commonlength);\n }\n }\n // Delete the offending records and add the merged ones.\n if (count_delete === 0) {\n diffs.splice(pointer - count_insert,\n count_delete + count_insert, [DIFF_INSERT, text_insert]);\n } else if (count_insert === 0) {\n diffs.splice(pointer - count_delete,\n count_delete + count_insert, [DIFF_DELETE, text_delete]);\n } else {\n diffs.splice(pointer - count_delete - count_insert,\n count_delete + count_insert, [DIFF_DELETE, text_delete],\n [DIFF_INSERT, text_insert]);\n }\n pointer = pointer - count_delete - count_insert +\n (count_delete ? 1 : 0) + (count_insert ? 1 : 0) + 1;\n } else if (pointer !== 0 && diffs[pointer - 1][0] == DIFF_EQUAL) {\n // Merge this equality with the previous one.\n diffs[pointer - 1][1] += diffs[pointer][1];\n diffs.splice(pointer, 1);\n } else {\n pointer++;\n }\n count_insert = 0;\n count_delete = 0;\n text_delete = '';\n text_insert = '';\n break;\n }\n }\n if (diffs[diffs.length - 1][1] === '') {\n diffs.pop(); // Remove the dummy entry at the end.\n }\n\n // Second pass: look for single edits surrounded on both sides by equalities\n // which can be shifted sideways to eliminate an equality.\n // e.g: ABAC -> ABAC\n var changes = false;\n pointer = 1;\n // Intentionally ignore the first and last element (don't need checking).\n while (pointer < diffs.length - 1) {\n if (diffs[pointer - 1][0] == DIFF_EQUAL &&\n diffs[pointer + 1][0] == DIFF_EQUAL) {\n // This is a single edit surrounded by equalities.\n if (diffs[pointer][1].substring(diffs[pointer][1].length -\n diffs[pointer - 1][1].length) == diffs[pointer - 1][1]) {\n // Shift the edit over the previous equality.\n diffs[pointer][1] = diffs[pointer - 1][1] +\n diffs[pointer][1].substring(0, diffs[pointer][1].length -\n diffs[pointer - 1][1].length);\n diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];\n diffs.splice(pointer - 1, 1);\n changes = true;\n } else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) ==\n diffs[pointer + 1][1]) {\n // Shift the edit over the next equality.\n diffs[pointer - 1][1] += diffs[pointer + 1][1];\n diffs[pointer][1] =\n diffs[pointer][1].substring(diffs[pointer + 1][1].length) +\n diffs[pointer + 1][1];\n diffs.splice(pointer + 1, 1);\n changes = true;\n }\n }\n pointer++;\n }\n // If shifts were made, the diff needs reordering and another shift sweep.\n if (changes) {\n diff_cleanupMerge(diffs);\n }\n};\n\n\nvar diff = diff_main;\ndiff.INSERT = DIFF_INSERT;\ndiff.DELETE = DIFF_DELETE;\ndiff.EQUAL = DIFF_EQUAL;\n\nmodule.exports = diff;\n\n/*\n * Modify a diff such that the cursor position points to the start of a change:\n * E.g.\n * cursor_normalize_diff([[DIFF_EQUAL, 'abc']], 1)\n * => [1, [[DIFF_EQUAL, 'a'], [DIFF_EQUAL, 'bc']]]\n * cursor_normalize_diff([[DIFF_INSERT, 'new'], [DIFF_DELETE, 'xyz']], 2)\n * => [2, [[DIFF_INSERT, 'new'], [DIFF_DELETE, 'xy'], [DIFF_DELETE, 'z']]]\n *\n * @param {Array} diffs Array of diff tuples\n * @param {Int} cursor_pos Suggested edit position. Must not be out of bounds!\n * @return {Array} A tuple [cursor location in the modified diff, modified diff]\n */\nfunction cursor_normalize_diff (diffs, cursor_pos) {\n if (cursor_pos === 0) {\n return [DIFF_EQUAL, diffs];\n }\n for (var current_pos = 0, i = 0; i < diffs.length; i++) {\n var d = diffs[i];\n if (d[0] === DIFF_DELETE || d[0] === DIFF_EQUAL) {\n var next_pos = current_pos + d[1].length;\n if (cursor_pos === next_pos) {\n return [i + 1, diffs];\n } else if (cursor_pos < next_pos) {\n // copy to prevent side effects\n diffs = diffs.slice();\n // split d into two diff changes\n var split_pos = cursor_pos - current_pos;\n var d_left = [d[0], d[1].slice(0, split_pos)];\n var d_right = [d[0], d[1].slice(split_pos)];\n diffs.splice(i, 1, d_left, d_right);\n return [i + 1, diffs];\n } else {\n current_pos = next_pos;\n }\n }\n }\n throw new Error('cursor_pos is out of bounds!')\n}\n\n/*\n * Modify a diff such that the edit position is \"shifted\" to the proposed edit location (cursor_position).\n *\n * Case 1)\n * Check if a naive shift is possible:\n * [0, X], [ 1, Y] -> [ 1, Y], [0, X] (if X + Y === Y + X)\n * [0, X], [-1, Y] -> [-1, Y], [0, X] (if X + Y === Y + X) - holds same result\n * Case 2)\n * Check if the following shifts are possible:\n * [0, 'pre'], [ 1, 'prefix'] -> [ 1, 'pre'], [0, 'pre'], [ 1, 'fix']\n * [0, 'pre'], [-1, 'prefix'] -> [-1, 'pre'], [0, 'pre'], [-1, 'fix']\n * ^ ^\n * d d_next\n *\n * @param {Array} diffs Array of diff tuples\n * @param {Int} cursor_pos Suggested edit position. Must not be out of bounds!\n * @return {Array} Array of diff tuples\n */\nfunction fix_cursor (diffs, cursor_pos) {\n var norm = cursor_normalize_diff(diffs, cursor_pos);\n var ndiffs = norm[1];\n var cursor_pointer = norm[0];\n var d = ndiffs[cursor_pointer];\n var d_next = ndiffs[cursor_pointer + 1];\n\n if (d == null) {\n // Text was deleted from end of original string,\n // cursor is now out of bounds in new string\n return diffs;\n } else if (d[0] !== DIFF_EQUAL) {\n // A modification happened at the cursor location.\n // This is the expected outcome, so we can return the original diff.\n return diffs;\n } else {\n if (d_next != null && d[1] + d_next[1] === d_next[1] + d[1]) {\n // Case 1)\n // It is possible to perform a naive shift\n ndiffs.splice(cursor_pointer, 2, d_next, d)\n return merge_tuples(ndiffs, cursor_pointer, 2)\n } else if (d_next != null && d_next[1].indexOf(d[1]) === 0) {\n // Case 2)\n // d[1] is a prefix of d_next[1]\n // We can assume that d_next[0] !== 0, since d[0] === 0\n // Shift edit locations..\n ndiffs.splice(cursor_pointer, 2, [d_next[0], d[1]], [0, d[1]]);\n var suffix = d_next[1].slice(d[1].length);\n if (suffix.length > 0) {\n ndiffs.splice(cursor_pointer + 2, 0, [d_next[0], suffix]);\n }\n return merge_tuples(ndiffs, cursor_pointer, 3)\n } else {\n // Not possible to perform any modification\n return diffs;\n }\n }\n}\n\n/*\n * Check diff did not split surrogate pairs.\n * Ex. [0, '\\uD83D'], [-1, '\\uDC36'], [1, '\\uDC2F'] -> [-1, '\\uD83D\\uDC36'], [1, '\\uD83D\\uDC2F']\n * '\\uD83D\\uDC36' === '🐶', '\\uD83D\\uDC2F' === '🐯'\n *\n * @param {Array} diffs Array of diff tuples\n * @return {Array} Array of diff tuples\n */\nfunction fix_emoji (diffs) {\n var compact = false;\n var starts_with_pair_end = function(str) {\n return str.charCodeAt(0) >= 0xDC00 && str.charCodeAt(0) <= 0xDFFF;\n }\n var ends_with_pair_start = function(str) {\n return str.charCodeAt(str.length-1) >= 0xD800 && str.charCodeAt(str.length-1) <= 0xDBFF;\n }\n for (var i = 2; i < diffs.length; i += 1) {\n if (diffs[i-2][0] === DIFF_EQUAL && ends_with_pair_start(diffs[i-2][1]) &&\n diffs[i-1][0] === DIFF_DELETE && starts_with_pair_end(diffs[i-1][1]) &&\n diffs[i][0] === DIFF_INSERT && starts_with_pair_end(diffs[i][1])) {\n compact = true;\n\n diffs[i-1][1] = diffs[i-2][1].slice(-1) + diffs[i-1][1];\n diffs[i][1] = diffs[i-2][1].slice(-1) + diffs[i][1];\n\n diffs[i-2][1] = diffs[i-2][1].slice(0, -1);\n }\n }\n if (!compact) {\n return diffs;\n }\n var fixed_diffs = [];\n for (var i = 0; i < diffs.length; i += 1) {\n if (diffs[i][1].length > 0) {\n fixed_diffs.push(diffs[i]);\n }\n }\n return fixed_diffs;\n}\n\n/*\n * Try to merge tuples with their neigbors in a given range.\n * E.g. [0, 'a'], [0, 'b'] -> [0, 'ab']\n *\n * @param {Array} diffs Array of diff tuples.\n * @param {Int} start Position of the first element to merge (diffs[start] is also merged with diffs[start - 1]).\n * @param {Int} length Number of consecutive elements to check.\n * @return {Array} Array of merged diff tuples.\n */\nfunction merge_tuples (diffs, start, length) {\n // Check from (start-1) to (start+length).\n for (var i = start + length - 1; i >= 0 && i >= start - 1; i--) {\n if (i + 1 < diffs.length) {\n var left_d = diffs[i];\n var right_d = diffs[i+1];\n if (left_d[0] === right_d[1]) {\n diffs.splice(i, 2, [left_d[0], left_d[1] + right_d[1]]);\n }\n }\n }\n return diffs;\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/fast-diff/diff.js\n// module id = 54\n// module chunks = 0","exports = module.exports = typeof Object.keys === 'function'\n ? Object.keys : shim;\n\nexports.shim = shim;\nfunction shim (obj) {\n var keys = [];\n for (var key in obj) keys.push(key);\n return keys;\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/deep-equal/lib/keys.js\n// module id = 55\n// module chunks = 0","var supportsArgumentsClass = (function(){\n return Object.prototype.toString.call(arguments)\n})() == '[object Arguments]';\n\nexports = module.exports = supportsArgumentsClass ? supported : unsupported;\n\nexports.supported = supported;\nfunction supported(object) {\n return Object.prototype.toString.call(object) == '[object Arguments]';\n};\n\nexports.unsupported = unsupported;\nfunction unsupported(object){\n return object &&\n typeof object == 'object' &&\n typeof object.length == 'number' &&\n Object.prototype.hasOwnProperty.call(object, 'callee') &&\n !Object.prototype.propertyIsEnumerable.call(object, 'callee') ||\n false;\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/deep-equal/lib/is_arguments.js\n// module id = 56\n// module chunks = 0","import Delta from 'quill-delta';\nimport DeltaOp from 'quill-delta/lib/op';\nimport Parchment from 'parchment';\nimport CodeBlock from '../formats/code';\nimport CursorBlot from '../blots/cursor';\nimport Block, { bubbleFormats } from '../blots/block';\nimport Break from '../blots/break';\nimport clone from 'clone';\nimport equal from 'deep-equal';\nimport extend from 'extend';\n\n\nconst ASCII = /^[ -~]*$/;\n\n\nclass Editor {\n constructor(scroll) {\n this.scroll = scroll;\n this.delta = this.getDelta();\n }\n\n applyDelta(delta) {\n let consumeNextNewline = false;\n this.scroll.update();\n let scrollLength = this.scroll.length();\n this.scroll.batchStart();\n delta = normalizeDelta(delta);\n delta.reduce((index, op) => {\n let length = op.retain || op.delete || op.insert.length || 1;\n let attributes = op.attributes || {};\n if (op.insert != null) {\n if (typeof op.insert === 'string') {\n let text = op.insert;\n if (text.endsWith('\\n') && consumeNextNewline) {\n consumeNextNewline = false;\n text = text.slice(0, -1);\n }\n if (index >= scrollLength && !text.endsWith('\\n')) {\n consumeNextNewline = true;\n }\n this.scroll.insertAt(index, text);\n let [line, offset] = this.scroll.line(index);\n let formats = extend({}, bubbleFormats(line));\n if (line instanceof Block) {\n let [leaf, ] = line.descendant(Parchment.Leaf, offset);\n formats = extend(formats, bubbleFormats(leaf));\n }\n attributes = DeltaOp.attributes.diff(formats, attributes) || {};\n } else if (typeof op.insert === 'object') {\n let key = Object.keys(op.insert)[0]; // There should only be one key\n if (key == null) return index;\n this.scroll.insertAt(index, key, op.insert[key]);\n }\n scrollLength += length;\n }\n Object.keys(attributes).forEach((name) => {\n this.scroll.formatAt(index, length, name, attributes[name]);\n });\n return index + length;\n }, 0);\n delta.reduce((index, op) => {\n if (typeof op.delete === 'number') {\n this.scroll.deleteAt(index, op.delete);\n return index;\n }\n return index + (op.retain || op.insert.length || 1);\n }, 0);\n this.scroll.batchEnd();\n return this.update(delta);\n }\n\n deleteText(index, length) {\n this.scroll.deleteAt(index, length);\n return this.update(new Delta().retain(index).delete(length));\n }\n\n formatLine(index, length, formats = {}) {\n this.scroll.update();\n Object.keys(formats).forEach((format) => {\n if (this.scroll.whitelist != null && !this.scroll.whitelist[format]) return;\n let lines = this.scroll.lines(index, Math.max(length, 1));\n let lengthRemaining = length;\n lines.forEach((line) => {\n let lineLength = line.length();\n if (!(line instanceof CodeBlock)) {\n line.format(format, formats[format]);\n } else {\n let codeIndex = index - line.offset(this.scroll);\n let codeLength = line.newlineIndex(codeIndex + lengthRemaining) - codeIndex + 1;\n line.formatAt(codeIndex, codeLength, format, formats[format]);\n }\n lengthRemaining -= lineLength;\n });\n });\n this.scroll.optimize();\n return this.update(new Delta().retain(index).retain(length, clone(formats)));\n }\n\n formatText(index, length, formats = {}) {\n Object.keys(formats).forEach((format) => {\n this.scroll.formatAt(index, length, format, formats[format]);\n });\n return this.update(new Delta().retain(index).retain(length, clone(formats)));\n }\n\n getContents(index, length) {\n return this.delta.slice(index, index + length);\n }\n\n getDelta() {\n return this.scroll.lines().reduce((delta, line) => {\n return delta.concat(line.delta());\n }, new Delta());\n }\n\n getFormat(index, length = 0) {\n let lines = [], leaves = [];\n if (length === 0) {\n this.scroll.path(index).forEach(function(path) {\n let [blot, ] = path;\n if (blot instanceof Block) {\n lines.push(blot);\n } else if (blot instanceof Parchment.Leaf) {\n leaves.push(blot);\n }\n });\n } else {\n lines = this.scroll.lines(index, length);\n leaves = this.scroll.descendants(Parchment.Leaf, index, length);\n }\n let formatsArr = [lines, leaves].map(function(blots) {\n if (blots.length === 0) return {};\n let formats = bubbleFormats(blots.shift());\n while (Object.keys(formats).length > 0) {\n let blot = blots.shift();\n if (blot == null) return formats;\n formats = combineFormats(bubbleFormats(blot), formats);\n }\n return formats;\n });\n return extend.apply(extend, formatsArr);\n }\n\n getText(index, length) {\n return this.getContents(index, length).filter(function(op) {\n return typeof op.insert === 'string';\n }).map(function(op) {\n return op.insert;\n }).join('');\n }\n\n insertEmbed(index, embed, value) {\n this.scroll.insertAt(index, embed, value);\n return this.update(new Delta().retain(index).insert({ [embed]: value }));\n }\n\n insertText(index, text, formats = {}) {\n text = text.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n this.scroll.insertAt(index, text);\n Object.keys(formats).forEach((format) => {\n this.scroll.formatAt(index, text.length, format, formats[format]);\n });\n return this.update(new Delta().retain(index).insert(text, clone(formats)));\n }\n\n isBlank() {\n if (this.scroll.children.length == 0) return true;\n if (this.scroll.children.length > 1) return false;\n let block = this.scroll.children.head;\n if (block.statics.blotName !== Block.blotName) return false;\n if (block.children.length > 1) return false;\n return block.children.head instanceof Break;\n }\n\n removeFormat(index, length) {\n let text = this.getText(index, length);\n let [line, offset] = this.scroll.line(index + length);\n let suffixLength = 0, suffix = new Delta();\n if (line != null) {\n if (!(line instanceof CodeBlock)) {\n suffixLength = line.length() - offset;\n } else {\n suffixLength = line.newlineIndex(offset) - offset + 1;\n }\n suffix = line.delta().slice(offset, offset + suffixLength - 1).insert('\\n');\n }\n let contents = this.getContents(index, length + suffixLength);\n let diff = contents.diff(new Delta().insert(text).concat(suffix));\n let delta = new Delta().retain(index).concat(diff);\n return this.applyDelta(delta);\n }\n\n update(change, mutations = [], cursorIndex = undefined) {\n let oldDelta = this.delta;\n if (mutations.length === 1 &&\n mutations[0].type === 'characterData' &&\n mutations[0].target.data.match(ASCII) &&\n Parchment.find(mutations[0].target)) {\n // Optimization for character changes\n let textBlot = Parchment.find(mutations[0].target);\n let formats = bubbleFormats(textBlot);\n let index = textBlot.offset(this.scroll);\n let oldValue = mutations[0].oldValue.replace(CursorBlot.CONTENTS, '');\n let oldText = new Delta().insert(oldValue);\n let newText = new Delta().insert(textBlot.value());\n let diffDelta = new Delta().retain(index).concat(oldText.diff(newText, cursorIndex));\n change = diffDelta.reduce(function(delta, op) {\n if (op.insert) {\n return delta.insert(op.insert, formats);\n } else {\n return delta.push(op);\n }\n }, new Delta());\n this.delta = oldDelta.compose(change);\n } else {\n this.delta = this.getDelta();\n if (!change || !equal(oldDelta.compose(change), this.delta)) {\n change = oldDelta.diff(this.delta, cursorIndex);\n }\n }\n return change;\n }\n}\n\n\nfunction combineFormats(formats, combined) {\n return Object.keys(combined).reduce(function(merged, name) {\n if (formats[name] == null) return merged;\n if (combined[name] === formats[name]) {\n merged[name] = combined[name];\n } else if (Array.isArray(combined[name])) {\n if (combined[name].indexOf(formats[name]) < 0) {\n merged[name] = combined[name].concat([formats[name]]);\n }\n } else {\n merged[name] = [combined[name], formats[name]];\n }\n return merged;\n }, {});\n}\n\nfunction normalizeDelta(delta) {\n return delta.reduce(function(delta, op) {\n if (op.insert === 1) {\n let attributes = clone(op.attributes);\n delete attributes['image'];\n return delta.insert({ image: op.attributes.image }, attributes);\n }\n if (op.attributes != null && (op.attributes.list === true || op.attributes.bullet === true)) {\n op = clone(op);\n if (op.attributes.list) {\n op.attributes.list = 'ordered';\n } else {\n op.attributes.list = 'bullet';\n delete op.attributes.bullet;\n }\n }\n if (typeof op.insert === 'string') {\n let text = op.insert.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n return delta.insert(text, op.attributes);\n }\n return delta.push(op);\n }, new Delta());\n}\n\n\nexport default Editor;\n\n\n\n// WEBPACK FOOTER //\n// ./core/editor.js","'use strict';\n\nvar has = Object.prototype.hasOwnProperty\n , prefix = '~';\n\n/**\n * Constructor to create a storage for our `EE` objects.\n * An `Events` instance is a plain object whose properties are event names.\n *\n * @constructor\n * @api private\n */\nfunction Events() {}\n\n//\n// We try to not inherit from `Object.prototype`. In some engines creating an\n// instance in this way is faster than calling `Object.create(null)` directly.\n// If `Object.create(null)` is not supported we prefix the event names with a\n// character to make sure that the built-in object properties are not\n// overridden or used as an attack vector.\n//\nif (Object.create) {\n Events.prototype = Object.create(null);\n\n //\n // This hack is needed because the `__proto__` property is still inherited in\n // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.\n //\n if (!new Events().__proto__) prefix = false;\n}\n\n/**\n * Representation of a single event listener.\n *\n * @param {Function} fn The listener function.\n * @param {Mixed} context The context to invoke the listener with.\n * @param {Boolean} [once=false] Specify if the listener is a one-time listener.\n * @constructor\n * @api private\n */\nfunction EE(fn, context, once) {\n this.fn = fn;\n this.context = context;\n this.once = once || false;\n}\n\n/**\n * Minimal `EventEmitter` interface that is molded against the Node.js\n * `EventEmitter` interface.\n *\n * @constructor\n * @api public\n */\nfunction EventEmitter() {\n this._events = new Events();\n this._eventsCount = 0;\n}\n\n/**\n * Return an array listing the events for which the emitter has registered\n * listeners.\n *\n * @returns {Array}\n * @api public\n */\nEventEmitter.prototype.eventNames = function eventNames() {\n var names = []\n , events\n , name;\n\n if (this._eventsCount === 0) return names;\n\n for (name in (events = this._events)) {\n if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);\n }\n\n if (Object.getOwnPropertySymbols) {\n return names.concat(Object.getOwnPropertySymbols(events));\n }\n\n return names;\n};\n\n/**\n * Return the listeners registered for a given event.\n *\n * @param {String|Symbol} event The event name.\n * @param {Boolean} exists Only check if there are listeners.\n * @returns {Array|Boolean}\n * @api public\n */\nEventEmitter.prototype.listeners = function listeners(event, exists) {\n var evt = prefix ? prefix + event : event\n , available = this._events[evt];\n\n if (exists) return !!available;\n if (!available) return [];\n if (available.fn) return [available.fn];\n\n for (var i = 0, l = available.length, ee = new Array(l); i < l; i++) {\n ee[i] = available[i].fn;\n }\n\n return ee;\n};\n\n/**\n * Calls each of the listeners registered for a given event.\n *\n * @param {String|Symbol} event The event name.\n * @returns {Boolean} `true` if the event had listeners, else `false`.\n * @api public\n */\nEventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {\n var evt = prefix ? prefix + event : event;\n\n if (!this._events[evt]) return false;\n\n var listeners = this._events[evt]\n , len = arguments.length\n , args\n , i;\n\n if (listeners.fn) {\n if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);\n\n switch (len) {\n case 1: return listeners.fn.call(listeners.context), true;\n case 2: return listeners.fn.call(listeners.context, a1), true;\n case 3: return listeners.fn.call(listeners.context, a1, a2), true;\n case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;\n case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;\n case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;\n }\n\n for (i = 1, args = new Array(len -1); i < len; i++) {\n args[i - 1] = arguments[i];\n }\n\n listeners.fn.apply(listeners.context, args);\n } else {\n var length = listeners.length\n , j;\n\n for (i = 0; i < length; i++) {\n if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);\n\n switch (len) {\n case 1: listeners[i].fn.call(listeners[i].context); break;\n case 2: listeners[i].fn.call(listeners[i].context, a1); break;\n case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;\n case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;\n default:\n if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {\n args[j - 1] = arguments[j];\n }\n\n listeners[i].fn.apply(listeners[i].context, args);\n }\n }\n }\n\n return true;\n};\n\n/**\n * Add a listener for a given event.\n *\n * @param {String|Symbol} event The event name.\n * @param {Function} fn The listener function.\n * @param {Mixed} [context=this] The context to invoke the listener with.\n * @returns {EventEmitter} `this`.\n * @api public\n */\nEventEmitter.prototype.on = function on(event, fn, context) {\n var listener = new EE(fn, context || this)\n , evt = prefix ? prefix + event : event;\n\n if (!this._events[evt]) this._events[evt] = listener, this._eventsCount++;\n else if (!this._events[evt].fn) this._events[evt].push(listener);\n else this._events[evt] = [this._events[evt], listener];\n\n return this;\n};\n\n/**\n * Add a one-time listener for a given event.\n *\n * @param {String|Symbol} event The event name.\n * @param {Function} fn The listener function.\n * @param {Mixed} [context=this] The context to invoke the listener with.\n * @returns {EventEmitter} `this`.\n * @api public\n */\nEventEmitter.prototype.once = function once(event, fn, context) {\n var listener = new EE(fn, context || this, true)\n , evt = prefix ? prefix + event : event;\n\n if (!this._events[evt]) this._events[evt] = listener, this._eventsCount++;\n else if (!this._events[evt].fn) this._events[evt].push(listener);\n else this._events[evt] = [this._events[evt], listener];\n\n return this;\n};\n\n/**\n * Remove the listeners of a given event.\n *\n * @param {String|Symbol} event The event name.\n * @param {Function} fn Only remove the listeners that match this function.\n * @param {Mixed} context Only remove the listeners that have this context.\n * @param {Boolean} once Only remove one-time listeners.\n * @returns {EventEmitter} `this`.\n * @api public\n */\nEventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {\n var evt = prefix ? prefix + event : event;\n\n if (!this._events[evt]) return this;\n if (!fn) {\n if (--this._eventsCount === 0) this._events = new Events();\n else delete this._events[evt];\n return this;\n }\n\n var listeners = this._events[evt];\n\n if (listeners.fn) {\n if (\n listeners.fn === fn\n && (!once || listeners.once)\n && (!context || listeners.context === context)\n ) {\n if (--this._eventsCount === 0) this._events = new Events();\n else delete this._events[evt];\n }\n } else {\n for (var i = 0, events = [], length = listeners.length; i < length; i++) {\n if (\n listeners[i].fn !== fn\n || (once && !listeners[i].once)\n || (context && listeners[i].context !== context)\n ) {\n events.push(listeners[i]);\n }\n }\n\n //\n // Reset the array, or remove it completely if we have no more listeners.\n //\n if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;\n else if (--this._eventsCount === 0) this._events = new Events();\n else delete this._events[evt];\n }\n\n return this;\n};\n\n/**\n * Remove all listeners, or those of the specified event.\n *\n * @param {String|Symbol} [event] The event name.\n * @returns {EventEmitter} `this`.\n * @api public\n */\nEventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {\n var evt;\n\n if (event) {\n evt = prefix ? prefix + event : event;\n if (this._events[evt]) {\n if (--this._eventsCount === 0) this._events = new Events();\n else delete this._events[evt];\n }\n } else {\n this._events = new Events();\n this._eventsCount = 0;\n }\n\n return this;\n};\n\n//\n// Alias methods names because people roll like that.\n//\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\nEventEmitter.prototype.addListener = EventEmitter.prototype.on;\n\n//\n// This function doesn't apply anymore.\n//\nEventEmitter.prototype.setMaxListeners = function setMaxListeners() {\n return this;\n};\n\n//\n// Expose the prefix.\n//\nEventEmitter.prefixed = prefix;\n\n//\n// Allow `EventEmitter` to be imported as module namespace.\n//\nEventEmitter.EventEmitter = EventEmitter;\n\n//\n// Expose the module.\n//\nif ('undefined' !== typeof module) {\n module.exports = EventEmitter;\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/eventemitter3/index.js\n// module id = 58\n// module chunks = 0","import Parchment from 'parchment';\nimport Emitter from '../core/emitter';\nimport Block, { BlockEmbed } from './block';\nimport Break from './break';\nimport CodeBlock from '../formats/code';\nimport Container from './container';\n\n\nfunction isLine(blot) {\n return (blot instanceof Block || blot instanceof BlockEmbed);\n}\n\n\nclass Scroll extends Parchment.Scroll {\n constructor(domNode, config) {\n super(domNode);\n this.emitter = config.emitter;\n if (Array.isArray(config.whitelist)) {\n this.whitelist = config.whitelist.reduce(function(whitelist, format) {\n whitelist[format] = true;\n return whitelist;\n }, {});\n }\n // Some reason fixes composition issues with character languages in Windows/Chrome, Safari\n this.domNode.addEventListener('DOMNodeInserted', function() {});\n this.optimize();\n this.enable();\n }\n\n batchStart() {\n this.batch = true;\n }\n\n batchEnd() {\n this.batch = false;\n this.optimize();\n }\n\n deleteAt(index, length) {\n let [first, offset] = this.line(index);\n let [last, ] = this.line(index + length);\n super.deleteAt(index, length);\n if (last != null && first !== last && offset > 0) {\n if (first instanceof BlockEmbed || last instanceof BlockEmbed) {\n this.optimize();\n return;\n }\n if (first instanceof CodeBlock) {\n let newlineIndex = first.newlineIndex(first.length(), true);\n if (newlineIndex > -1) {\n first = first.split(newlineIndex + 1);\n if (first === last) {\n this.optimize();\n return;\n }\n }\n } else if (last instanceof CodeBlock) {\n let newlineIndex = last.newlineIndex(0);\n if (newlineIndex > -1) {\n last.split(newlineIndex + 1);\n }\n }\n let ref = last.children.head instanceof Break ? null : last.children.head;\n first.moveChildren(last, ref);\n first.remove();\n }\n this.optimize();\n }\n\n enable(enabled = true) {\n this.domNode.setAttribute('contenteditable', enabled);\n }\n\n formatAt(index, length, format, value) {\n if (this.whitelist != null && !this.whitelist[format]) return;\n super.formatAt(index, length, format, value);\n this.optimize();\n }\n\n insertAt(index, value, def) {\n if (def != null && this.whitelist != null && !this.whitelist[value]) return;\n if (index >= this.length()) {\n if (def == null || Parchment.query(value, Parchment.Scope.BLOCK) == null) {\n let blot = Parchment.create(this.statics.defaultChild);\n this.appendChild(blot);\n if (def == null && value.endsWith('\\n')) {\n value = value.slice(0, -1);\n }\n blot.insertAt(0, value, def);\n } else {\n let embed = Parchment.create(value, def);\n this.appendChild(embed);\n }\n } else {\n super.insertAt(index, value, def);\n }\n this.optimize();\n }\n\n insertBefore(blot, ref) {\n if (blot.statics.scope === Parchment.Scope.INLINE_BLOT) {\n let wrapper = Parchment.create(this.statics.defaultChild);\n wrapper.appendChild(blot);\n blot = wrapper;\n }\n super.insertBefore(blot, ref);\n }\n\n leaf(index) {\n return this.path(index).pop() || [null, -1];\n }\n\n line(index) {\n if (index === this.length()) {\n return this.line(index - 1);\n }\n return this.descendant(isLine, index);\n }\n\n lines(index = 0, length = Number.MAX_VALUE) {\n let getLines = (blot, index, length) => {\n let lines = [], lengthLeft = length;\n blot.children.forEachAt(index, length, function(child, index, length) {\n if (isLine(child)) {\n lines.push(child);\n } else if (child instanceof Parchment.Container) {\n lines = lines.concat(getLines(child, index, lengthLeft));\n }\n lengthLeft -= length;\n });\n return lines;\n };\n return getLines(this, index, length);\n }\n\n optimize(mutations = [], context = {}) {\n if (this.batch === true) return;\n super.optimize(mutations, context);\n if (mutations.length > 0) {\n this.emitter.emit(Emitter.events.SCROLL_OPTIMIZE, mutations, context);\n }\n }\n\n path(index) {\n return super.path(index).slice(1); // Exclude self\n }\n\n update(mutations) {\n if (this.batch === true) return;\n let source = Emitter.sources.USER;\n if (typeof mutations === 'string') {\n source = mutations;\n }\n if (!Array.isArray(mutations)) {\n mutations = this.observer.takeRecords();\n }\n if (mutations.length > 0) {\n this.emitter.emit(Emitter.events.SCROLL_BEFORE_UPDATE, source, mutations);\n }\n super.update(mutations.concat([])); // pass copy\n if (mutations.length > 0) {\n this.emitter.emit(Emitter.events.SCROLL_UPDATE, source, mutations);\n }\n }\n}\nScroll.blotName = 'scroll';\nScroll.className = 'ql-editor';\nScroll.tagName = 'DIV';\nScroll.defaultChild = 'block';\nScroll.allowedChildren = [Block, BlockEmbed, Container];\n\n\nexport default Scroll;\n\n\n\n// WEBPACK FOOTER //\n// ./blots/scroll.js","import extend from 'extend';\nimport Delta from 'quill-delta';\nimport Parchment from 'parchment';\nimport Quill from '../core/quill';\nimport logger from '../core/logger';\nimport Module from '../core/module';\n\nimport { AlignAttribute, AlignStyle } from '../formats/align';\nimport { BackgroundStyle } from '../formats/background';\nimport CodeBlock from '../formats/code';\nimport { ColorStyle } from '../formats/color';\nimport { DirectionAttribute, DirectionStyle } from '../formats/direction';\nimport { FontStyle } from '../formats/font';\nimport { SizeStyle } from '../formats/size';\n\nlet debug = logger('quill:clipboard');\n\n\nconst DOM_KEY = '__ql-matcher';\n\nconst CLIPBOARD_CONFIG = [\n [Node.TEXT_NODE, matchText],\n [Node.TEXT_NODE, matchNewline],\n ['br', matchBreak],\n [Node.ELEMENT_NODE, matchNewline],\n [Node.ELEMENT_NODE, matchBlot],\n [Node.ELEMENT_NODE, matchSpacing],\n [Node.ELEMENT_NODE, matchAttributor],\n [Node.ELEMENT_NODE, matchStyles],\n ['li', matchIndent],\n ['b', matchAlias.bind(matchAlias, 'bold')],\n ['i', matchAlias.bind(matchAlias, 'italic')],\n ['style', matchIgnore]\n];\n\nconst ATTRIBUTE_ATTRIBUTORS = [\n AlignAttribute,\n DirectionAttribute\n].reduce(function(memo, attr) {\n memo[attr.keyName] = attr;\n return memo;\n}, {});\n\nconst STYLE_ATTRIBUTORS = [\n AlignStyle,\n BackgroundStyle,\n ColorStyle,\n DirectionStyle,\n FontStyle,\n SizeStyle\n].reduce(function(memo, attr) {\n memo[attr.keyName] = attr;\n return memo;\n}, {});\n\n\nclass Clipboard extends Module {\n constructor(quill, options) {\n super(quill, options);\n this.quill.root.addEventListener('paste', this.onPaste.bind(this));\n this.container = this.quill.addContainer('ql-clipboard');\n this.container.setAttribute('contenteditable', true);\n this.container.setAttribute('tabindex', -1);\n this.matchers = [];\n CLIPBOARD_CONFIG.concat(this.options.matchers).forEach(([selector, matcher]) => {\n if (!options.matchVisual && matcher === matchSpacing) return;\n this.addMatcher(selector, matcher);\n });\n }\n\n addMatcher(selector, matcher) {\n this.matchers.push([selector, matcher]);\n }\n\n convert(html) {\n if (typeof html === 'string') {\n this.container.innerHTML = html.replace(/\\>\\r?\\n +\\<'); // Remove spaces between tags\n return this.convert();\n }\n const formats = this.quill.getFormat(this.quill.selection.savedRange.index);\n if (formats[CodeBlock.blotName]) {\n const text = this.container.innerText;\n this.container.innerHTML = '';\n return new Delta().insert(text, { [CodeBlock.blotName]: formats[CodeBlock.blotName] });\n }\n let [elementMatchers, textMatchers] = this.prepareMatching();\n let delta = traverse(this.container, elementMatchers, textMatchers);\n // Remove trailing newline\n if (deltaEndsWith(delta, '\\n') && delta.ops[delta.ops.length - 1].attributes == null) {\n delta = delta.compose(new Delta().retain(delta.length() - 1).delete(1));\n }\n debug.log('convert', this.container.innerHTML, delta);\n this.container.innerHTML = '';\n return delta;\n }\n\n dangerouslyPasteHTML(index, html, source = Quill.sources.API) {\n if (typeof index === 'string') {\n this.quill.setContents(this.convert(index), html);\n this.quill.setSelection(0, Quill.sources.SILENT);\n } else {\n let paste = this.convert(html);\n this.quill.updateContents(new Delta().retain(index).concat(paste), source);\n this.quill.setSelection(index + paste.length(), Quill.sources.SILENT);\n }\n }\n\n onPaste(e) {\n if (e.defaultPrevented || !this.quill.isEnabled()) return;\n let range = this.quill.getSelection();\n let delta = new Delta().retain(range.index);\n let scrollTop = this.quill.scrollingContainer.scrollTop;\n this.container.focus();\n this.quill.selection.update(Quill.sources.SILENT);\n setTimeout(() => {\n delta = delta.concat(this.convert()).delete(range.length);\n this.quill.updateContents(delta, Quill.sources.USER);\n // range.length contributes to delta.length()\n this.quill.setSelection(delta.length() - range.length, Quill.sources.SILENT);\n this.quill.scrollingContainer.scrollTop = scrollTop;\n this.quill.focus();\n }, 1);\n }\n\n prepareMatching() {\n let elementMatchers = [], textMatchers = [];\n this.matchers.forEach((pair) => {\n let [selector, matcher] = pair;\n switch (selector) {\n case Node.TEXT_NODE:\n textMatchers.push(matcher);\n break;\n case Node.ELEMENT_NODE:\n elementMatchers.push(matcher);\n break;\n default:\n [].forEach.call(this.container.querySelectorAll(selector), (node) => {\n // TODO use weakmap\n node[DOM_KEY] = node[DOM_KEY] || [];\n node[DOM_KEY].push(matcher);\n });\n break;\n }\n });\n return [elementMatchers, textMatchers];\n }\n}\nClipboard.DEFAULTS = {\n matchers: [],\n matchVisual: true\n};\n\n\nfunction applyFormat(delta, format, value) {\n if (typeof format === 'object') {\n return Object.keys(format).reduce(function(delta, key) {\n return applyFormat(delta, key, format[key]);\n }, delta);\n } else {\n return delta.reduce(function(delta, op) {\n if (op.attributes && op.attributes[format]) {\n return delta.push(op);\n } else {\n return delta.insert(op.insert, extend({}, {[format]: value}, op.attributes));\n }\n }, new Delta());\n }\n}\n\nfunction computeStyle(node) {\n if (node.nodeType !== Node.ELEMENT_NODE) return {};\n const DOM_KEY = '__ql-computed-style';\n return node[DOM_KEY] || (node[DOM_KEY] = window.getComputedStyle(node));\n}\n\nfunction deltaEndsWith(delta, text) {\n let endText = \"\";\n for (let i = delta.ops.length - 1; i >= 0 && endText.length < text.length; --i) {\n let op = delta.ops[i];\n if (typeof op.insert !== 'string') break;\n endText = op.insert + endText;\n }\n return endText.slice(-1*text.length) === text;\n}\n\nfunction isLine(node) {\n if (node.childNodes.length === 0) return false; // Exclude embed blocks\n let style = computeStyle(node);\n return ['block', 'list-item'].indexOf(style.display) > -1;\n}\n\nfunction traverse(node, elementMatchers, textMatchers) { // Post-order\n if (node.nodeType === node.TEXT_NODE) {\n return textMatchers.reduce(function(delta, matcher) {\n return matcher(node, delta);\n }, new Delta());\n } else if (node.nodeType === node.ELEMENT_NODE) {\n return [].reduce.call(node.childNodes || [], (delta, childNode) => {\n let childrenDelta = traverse(childNode, elementMatchers, textMatchers);\n if (childNode.nodeType === node.ELEMENT_NODE) {\n childrenDelta = elementMatchers.reduce(function(childrenDelta, matcher) {\n return matcher(childNode, childrenDelta);\n }, childrenDelta);\n childrenDelta = (childNode[DOM_KEY] || []).reduce(function(childrenDelta, matcher) {\n return matcher(childNode, childrenDelta);\n }, childrenDelta);\n }\n return delta.concat(childrenDelta);\n }, new Delta());\n } else {\n return new Delta();\n }\n}\n\n\nfunction matchAlias(format, node, delta) {\n return applyFormat(delta, format, true);\n}\n\nfunction matchAttributor(node, delta) {\n let attributes = Parchment.Attributor.Attribute.keys(node);\n let classes = Parchment.Attributor.Class.keys(node);\n let styles = Parchment.Attributor.Style.keys(node);\n let formats = {};\n attributes.concat(classes).concat(styles).forEach((name) => {\n let attr = Parchment.query(name, Parchment.Scope.ATTRIBUTE);\n if (attr != null) {\n formats[attr.attrName] = attr.value(node);\n if (formats[attr.attrName]) return;\n }\n attr = ATTRIBUTE_ATTRIBUTORS[name];\n if (attr != null && (attr.attrName === name || attr.keyName === name)) {\n formats[attr.attrName] = attr.value(node) || undefined;\n }\n attr = STYLE_ATTRIBUTORS[name]\n if (attr != null && (attr.attrName === name || attr.keyName === name)) {\n attr = STYLE_ATTRIBUTORS[name];\n formats[attr.attrName] = attr.value(node) || undefined;\n }\n });\n if (Object.keys(formats).length > 0) {\n delta = applyFormat(delta, formats);\n }\n return delta;\n}\n\nfunction matchBlot(node, delta) {\n let match = Parchment.query(node);\n if (match == null) return delta;\n if (match.prototype instanceof Parchment.Embed) {\n let embed = {};\n let value = match.value(node);\n if (value != null) {\n embed[match.blotName] = value;\n delta = new Delta().insert(embed, match.formats(node));\n }\n } else if (typeof match.formats === 'function') {\n delta = applyFormat(delta, match.blotName, match.formats(node));\n }\n return delta;\n}\n\nfunction matchBreak(node, delta) {\n if (!deltaEndsWith(delta, '\\n')) {\n delta.insert('\\n');\n }\n return delta;\n}\n\nfunction matchIgnore() {\n return new Delta();\n}\n\nfunction matchIndent(node, delta) {\n let match = Parchment.query(node);\n if (match == null || match.blotName !== 'list-item' || !deltaEndsWith(delta, '\\n')) {\n return delta;\n }\n let indent = -1, parent = node.parentNode;\n while (!parent.classList.contains('ql-clipboard')) {\n if ((Parchment.query(parent) || {}).blotName === 'list') {\n indent += 1;\n }\n parent = parent.parentNode;\n }\n if (indent <= 0) return delta;\n return delta.compose(new Delta().retain(delta.length() - 1).retain(1, { indent: indent}));\n}\n\nfunction matchNewline(node, delta) {\n if (!deltaEndsWith(delta, '\\n')) {\n if (isLine(node) || (delta.length() > 0 && node.nextSibling && isLine(node.nextSibling))) {\n delta.insert('\\n');\n }\n }\n return delta;\n}\n\nfunction matchSpacing(node, delta) {\n if (isLine(node) && node.nextElementSibling != null && !deltaEndsWith(delta, '\\n\\n')) {\n let nodeHeight = node.offsetHeight + parseFloat(computeStyle(node).marginTop) + parseFloat(computeStyle(node).marginBottom);\n if (node.nextElementSibling.offsetTop > node.offsetTop + nodeHeight*1.5) {\n delta.insert('\\n');\n }\n }\n return delta;\n}\n\nfunction matchStyles(node, delta) {\n let formats = {};\n let style = node.style || {};\n if (style.fontStyle && computeStyle(node).fontStyle === 'italic') {\n formats.italic = true;\n }\n if (style.fontWeight && (computeStyle(node).fontWeight.startsWith('bold') ||\n parseInt(computeStyle(node).fontWeight) >= 700)) {\n formats.bold = true;\n }\n if (Object.keys(formats).length > 0) {\n delta = applyFormat(delta, formats);\n }\n if (parseFloat(style.textIndent || 0) > 0) { // Could be 0.5in\n delta = new Delta().insert('\\t').concat(delta);\n }\n return delta;\n}\n\nfunction matchText(node, delta) {\n let text = node.data;\n // Word represents empty line with  \n if (node.parentNode.tagName === 'O:P') {\n return delta.insert(text.trim());\n }\n if (text.trim().length === 0 && node.parentNode.classList.contains('ql-clipboard')) {\n return delta;\n }\n if (!computeStyle(node.parentNode).whiteSpace.startsWith('pre')) {\n // eslint-disable-next-line func-style\n let replacer = function(collapse, match) {\n match = match.replace(/[^\\u00a0]/g, ''); // \\u00a0 is nbsp;\n return match.length < 1 && collapse ? ' ' : match;\n };\n text = text.replace(/\\r\\n/g, ' ').replace(/\\n/g, ' ');\n text = text.replace(/\\s\\s+/g, replacer.bind(replacer, true)); // collapse whitespace\n if ((node.previousSibling == null && isLine(node.parentNode)) ||\n (node.previousSibling != null && isLine(node.previousSibling))) {\n text = text.replace(/^\\s+/, replacer.bind(replacer, false));\n }\n if ((node.nextSibling == null && isLine(node.parentNode)) ||\n (node.nextSibling != null && isLine(node.nextSibling))) {\n text = text.replace(/\\s+$/, replacer.bind(replacer, false));\n }\n }\n return delta.insert(text);\n}\n\n\nexport { Clipboard as default, matchAttributor, matchBlot, matchNewline, matchSpacing, matchText };\n\n\n\n// WEBPACK FOOTER //\n// ./modules/clipboard.js","import Parchment from 'parchment';\nimport Quill from '../core/quill';\nimport Module from '../core/module';\n\n\nclass History extends Module {\n constructor(quill, options) {\n super(quill, options);\n this.lastRecorded = 0;\n this.ignoreChange = false;\n this.clear();\n this.quill.on(Quill.events.EDITOR_CHANGE, (eventName, delta, oldDelta, source) => {\n if (eventName !== Quill.events.TEXT_CHANGE || this.ignoreChange) return;\n if (!this.options.userOnly || source === Quill.sources.USER) {\n this.record(delta, oldDelta);\n } else {\n this.transform(delta);\n }\n });\n this.quill.keyboard.addBinding({ key: 'Z', shortKey: true }, this.undo.bind(this));\n this.quill.keyboard.addBinding({ key: 'Z', shortKey: true, shiftKey: true }, this.redo.bind(this));\n if (/Win/i.test(navigator.platform)) {\n this.quill.keyboard.addBinding({ key: 'Y', shortKey: true }, this.redo.bind(this));\n }\n }\n\n change(source, dest) {\n if (this.stack[source].length === 0) return;\n let delta = this.stack[source].pop();\n this.stack[dest].push(delta);\n this.lastRecorded = 0;\n this.ignoreChange = true;\n this.quill.updateContents(delta[source], Quill.sources.USER);\n this.ignoreChange = false;\n let index = getLastChangeIndex(delta[source]);\n this.quill.setSelection(index);\n }\n\n clear() {\n this.stack = { undo: [], redo: [] };\n }\n\n cutoff() {\n this.lastRecorded = 0;\n }\n\n record(changeDelta, oldDelta) {\n if (changeDelta.ops.length === 0) return;\n this.stack.redo = [];\n let undoDelta = this.quill.getContents().diff(oldDelta);\n let timestamp = Date.now();\n if (this.lastRecorded + this.options.delay > timestamp && this.stack.undo.length > 0) {\n let delta = this.stack.undo.pop();\n undoDelta = undoDelta.compose(delta.undo);\n changeDelta = delta.redo.compose(changeDelta);\n } else {\n this.lastRecorded = timestamp;\n }\n this.stack.undo.push({\n redo: changeDelta,\n undo: undoDelta\n });\n if (this.stack.undo.length > this.options.maxStack) {\n this.stack.undo.shift();\n }\n }\n\n redo() {\n this.change('redo', 'undo');\n }\n\n transform(delta) {\n this.stack.undo.forEach(function(change) {\n change.undo = delta.transform(change.undo, true);\n change.redo = delta.transform(change.redo, true);\n });\n this.stack.redo.forEach(function(change) {\n change.undo = delta.transform(change.undo, true);\n change.redo = delta.transform(change.redo, true);\n });\n }\n\n undo() {\n this.change('undo', 'redo');\n }\n}\nHistory.DEFAULTS = {\n delay: 1000,\n maxStack: 100,\n userOnly: false\n};\n\nfunction endsWithNewlineChange(delta) {\n let lastOp = delta.ops[delta.ops.length - 1];\n if (lastOp == null) return false;\n if (lastOp.insert != null) {\n return typeof lastOp.insert === 'string' && lastOp.insert.endsWith('\\n');\n }\n if (lastOp.attributes != null) {\n return Object.keys(lastOp.attributes).some(function(attr) {\n return Parchment.query(attr, Parchment.Scope.BLOCK) != null;\n });\n }\n return false;\n}\n\nfunction getLastChangeIndex(delta) {\n let deleteLength = delta.reduce(function(length, op) {\n length += (op.delete || 0);\n return length;\n }, 0);\n let changeIndex = delta.length() - deleteLength;\n if (endsWithNewlineChange(delta)) {\n changeIndex -= 1;\n }\n return changeIndex;\n}\n\n\nexport { History as default, getLastChangeIndex };\n\n\n\n// WEBPACK FOOTER //\n// ./modules/history.js","import Parchment from 'parchment';\n\nclass IdentAttributor extends Parchment.Attributor.Class {\n add(node, value) {\n if (value === '+1' || value === '-1') {\n let indent = this.value(node) || 0;\n value = (value === '+1' ? (indent + 1) : (indent - 1));\n }\n if (value === 0) {\n this.remove(node);\n return true;\n } else {\n return super.add(node, value);\n }\n }\n\n canAdd(node, value) {\n return super.canAdd(node, value) || super.canAdd(node, parseInt(value));\n }\n\n value(node) {\n return parseInt(super.value(node)) || undefined; // Don't return NaN\n }\n}\n\nlet IndentClass = new IdentAttributor('indent', 'ql-indent', {\n scope: Parchment.Scope.BLOCK,\n whitelist: [1, 2, 3, 4, 5, 6, 7, 8]\n});\n\nexport { IndentClass };\n\n\n\n// WEBPACK FOOTER //\n// ./formats/indent.js","import Block from '../blots/block';\n\n\nclass Blockquote extends Block {}\nBlockquote.blotName = 'blockquote';\nBlockquote.tagName = 'blockquote';\n\n\nexport default Blockquote;\n\n\n\n// WEBPACK FOOTER //\n// ./formats/blockquote.js","import Block from '../blots/block';\n\n\nclass Header extends Block {\n static formats(domNode) {\n return this.tagName.indexOf(domNode.tagName) + 1;\n }\n}\nHeader.blotName = 'header';\nHeader.tagName = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'];\n\n\nexport default Header;\n\n\n\n// WEBPACK FOOTER //\n// ./formats/header.js","import Parchment from 'parchment';\nimport Block from '../blots/block';\nimport Container from '../blots/container';\n\n\nclass ListItem extends Block {\n static formats(domNode) {\n return domNode.tagName === this.tagName ? undefined : super.formats(domNode);\n }\n\n format(name, value) {\n if (name === List.blotName && !value) {\n this.replaceWith(Parchment.create(this.statics.scope));\n } else {\n super.format(name, value);\n }\n }\n\n remove() {\n if (this.prev == null && this.next == null) {\n this.parent.remove();\n } else {\n super.remove();\n }\n }\n\n replaceWith(name, value) {\n this.parent.isolate(this.offset(this.parent), this.length());\n if (name === this.parent.statics.blotName) {\n this.parent.replaceWith(name, value);\n return this;\n } else {\n this.parent.unwrap();\n return super.replaceWith(name, value);\n }\n }\n}\nListItem.blotName = 'list-item';\nListItem.tagName = 'LI';\n\n\nclass List extends Container {\n static create(value) {\n let tagName = value === 'ordered' ? 'OL' : 'UL';\n let node = super.create(tagName);\n if (value === 'checked' || value === 'unchecked') {\n node.setAttribute('data-checked', value === 'checked');\n }\n return node;\n }\n\n static formats(domNode) {\n if (domNode.tagName === 'OL') return 'ordered';\n if (domNode.tagName === 'UL') {\n if (domNode.hasAttribute('data-checked')) {\n return domNode.getAttribute('data-checked') === 'true' ? 'checked' : 'unchecked';\n } else {\n return 'bullet';\n }\n }\n return undefined;\n }\n\n constructor(domNode) {\n super(domNode);\n const listEventHandler = (e) => {\n if (e.target.parentNode !== domNode) return;\n let format = this.statics.formats(domNode);\n let blot = Parchment.find(e.target);\n if (format === 'checked') {\n blot.format('list', 'unchecked');\n } else if(format === 'unchecked') {\n blot.format('list', 'checked');\n }\n }\n\n domNode.addEventListener('touchstart', listEventHandler);\n domNode.addEventListener('mousedown', listEventHandler);\n }\n\n format(name, value) {\n if (this.children.length > 0) {\n this.children.tail.format(name, value);\n }\n }\n\n formats() {\n // We don't inherit from FormatBlot\n return { [this.statics.blotName]: this.statics.formats(this.domNode) };\n }\n\n insertBefore(blot, ref) {\n if (blot instanceof ListItem) {\n super.insertBefore(blot, ref);\n } else {\n let index = ref == null ? this.length() : ref.offset(this);\n let after = this.split(index);\n after.parent.insertBefore(blot, after);\n }\n }\n\n optimize(context) {\n super.optimize(context);\n let next = this.next;\n if (next != null && next.prev === this &&\n next.statics.blotName === this.statics.blotName &&\n next.domNode.tagName === this.domNode.tagName &&\n next.domNode.getAttribute('data-checked') === this.domNode.getAttribute('data-checked')) {\n next.moveChildren(this);\n next.remove();\n }\n }\n\n replace(target) {\n if (target.statics.blotName !== this.statics.blotName) {\n let item = Parchment.create(this.statics.defaultChild);\n target.moveChildren(item);\n this.appendChild(item);\n }\n super.replace(target);\n }\n}\nList.blotName = 'list';\nList.scope = Parchment.Scope.BLOCK_BLOT;\nList.tagName = ['OL', 'UL'];\nList.defaultChild = 'list-item';\nList.allowedChildren = [ListItem];\n\n\nexport { ListItem, List as default };\n\n\n\n// WEBPACK FOOTER //\n// ./formats/list.js","import Bold from './bold';\n\nclass Italic extends Bold { }\nItalic.blotName = 'italic';\nItalic.tagName = ['EM', 'I'];\n\nexport default Italic;\n\n\n\n// WEBPACK FOOTER //\n// ./formats/italic.js","import Inline from '../blots/inline';\n\nclass Script extends Inline {\n static create(value) {\n if (value === 'super') {\n return document.createElement('sup');\n } else if (value === 'sub') {\n return document.createElement('sub');\n } else {\n return super.create(value);\n }\n }\n\n static formats(domNode) {\n if (domNode.tagName === 'SUB') return 'sub';\n if (domNode.tagName === 'SUP') return 'super';\n return undefined;\n }\n}\nScript.blotName = 'script';\nScript.tagName = ['SUB', 'SUP'];\n\nexport default Script;\n\n\n\n// WEBPACK FOOTER //\n// ./formats/script.js","import Inline from '../blots/inline';\n\nclass Strike extends Inline { }\nStrike.blotName = 'strike';\nStrike.tagName = 'S';\n\nexport default Strike;\n\n\n\n// WEBPACK FOOTER //\n// ./formats/strike.js","import Inline from '../blots/inline';\n\nclass Underline extends Inline { }\nUnderline.blotName = 'underline';\nUnderline.tagName = 'U';\n\nexport default Underline;\n\n\n\n// WEBPACK FOOTER //\n// ./formats/underline.js","import Parchment from 'parchment';\nimport { sanitize } from '../formats/link';\n\nconst ATTRIBUTES = [\n 'alt',\n 'height',\n 'width'\n];\n\n\nclass Image extends Parchment.Embed {\n static create(value) {\n let node = super.create(value);\n if (typeof value === 'string') {\n node.setAttribute('src', this.sanitize(value));\n }\n return node;\n }\n\n static formats(domNode) {\n return ATTRIBUTES.reduce(function(formats, attribute) {\n if (domNode.hasAttribute(attribute)) {\n formats[attribute] = domNode.getAttribute(attribute);\n }\n return formats;\n }, {});\n }\n\n static match(url) {\n return /\\.(jpe?g|gif|png)$/.test(url) || /^data:image\\/.+;base64/.test(url);\n }\n\n static sanitize(url) {\n return sanitize(url, ['http', 'https', 'data']) ? url : '//:0';\n }\n\n static value(domNode) {\n return domNode.getAttribute('src');\n }\n\n format(name, value) {\n if (ATTRIBUTES.indexOf(name) > -1) {\n if (value) {\n this.domNode.setAttribute(name, value);\n } else {\n this.domNode.removeAttribute(name);\n }\n } else {\n super.format(name, value);\n }\n }\n}\nImage.blotName = 'image';\nImage.tagName = 'IMG';\n\n\nexport default Image;\n\n\n\n// WEBPACK FOOTER //\n// ./formats/image.js","import { BlockEmbed } from '../blots/block';\nimport Link from '../formats/link';\n\nconst ATTRIBUTES = [\n 'height',\n 'width'\n];\n\n\nclass Video extends BlockEmbed {\n static create(value) {\n let node = super.create(value);\n node.setAttribute('frameborder', '0');\n node.setAttribute('allowfullscreen', true);\n node.setAttribute('src', this.sanitize(value));\n return node;\n }\n\n static formats(domNode) {\n return ATTRIBUTES.reduce(function(formats, attribute) {\n if (domNode.hasAttribute(attribute)) {\n formats[attribute] = domNode.getAttribute(attribute);\n }\n return formats;\n }, {});\n }\n\n static sanitize(url) {\n return Link.sanitize(url);\n }\n\n static value(domNode) {\n return domNode.getAttribute('src');\n }\n\n format(name, value) {\n if (ATTRIBUTES.indexOf(name) > -1) {\n if (value) {\n this.domNode.setAttribute(name, value);\n } else {\n this.domNode.removeAttribute(name);\n }\n } else {\n super.format(name, value);\n }\n }\n}\nVideo.blotName = 'video';\nVideo.className = 'ql-video';\nVideo.tagName = 'IFRAME';\n\n\nexport default Video;\n\n\n\n// WEBPACK FOOTER //\n// ./formats/video.js","import Embed from '../blots/embed';\nimport Quill from '../core/quill';\nimport Module from '../core/module';\n\n\nclass FormulaBlot extends Embed {\n static create(value) {\n let node = super.create(value);\n if (typeof value === 'string') {\n window.katex.render(value, node, {\n throwOnError: false,\n errorColor: '#f00'\n });\n node.setAttribute('data-value', value);\n }\n return node;\n }\n\n static value(domNode) {\n return domNode.getAttribute('data-value');\n }\n}\nFormulaBlot.blotName = 'formula';\nFormulaBlot.className = 'ql-formula';\nFormulaBlot.tagName = 'SPAN';\n\n\nclass Formula extends Module {\n static register() {\n Quill.register(FormulaBlot, true);\n }\n\n constructor() {\n super();\n if (window.katex == null) {\n throw new Error('Formula module requires KaTeX.');\n }\n }\n}\n\n\nexport { FormulaBlot, Formula as default };\n\n\n\n// WEBPACK FOOTER //\n// ./modules/formula.js","import Parchment from 'parchment';\nimport Quill from '../core/quill';\nimport Module from '../core/module';\nimport CodeBlock from '../formats/code';\n\n\nclass SyntaxCodeBlock extends CodeBlock {\n replaceWith(block) {\n this.domNode.textContent = this.domNode.textContent;\n this.attach();\n super.replaceWith(block);\n }\n\n highlight(highlight) {\n let text = this.domNode.textContent;\n if (this.cachedText !== text) {\n if (text.trim().length > 0 || this.cachedText == null) {\n this.domNode.innerHTML = highlight(text);\n this.domNode.normalize();\n this.attach();\n }\n this.cachedText = text;\n }\n }\n}\nSyntaxCodeBlock.className = 'ql-syntax';\n\n\nlet CodeToken = new Parchment.Attributor.Class('token', 'hljs', {\n scope: Parchment.Scope.INLINE\n});\n\n\nclass Syntax extends Module {\n static register() {\n Quill.register(CodeToken, true);\n Quill.register(SyntaxCodeBlock, true);\n }\n\n constructor(quill, options) {\n super(quill, options);\n if (typeof this.options.highlight !== 'function') {\n throw new Error('Syntax module requires highlight.js. Please include the library on the page before Quill.');\n }\n let timer = null;\n this.quill.on(Quill.events.SCROLL_OPTIMIZE, () => {\n clearTimeout(timer);\n timer = setTimeout(() => {\n this.highlight();\n timer = null;\n }, this.options.interval);\n });\n this.highlight();\n }\n\n highlight() {\n if (this.quill.selection.composing) return;\n this.quill.update(Quill.sources.USER);\n let range = this.quill.getSelection();\n this.quill.scroll.descendants(SyntaxCodeBlock).forEach((code) => {\n code.highlight(this.options.highlight);\n });\n this.quill.update(Quill.sources.SILENT);\n if (range != null) {\n this.quill.setSelection(range, Quill.sources.SILENT);\n }\n }\n}\nSyntax.DEFAULTS = {\n highlight: (function() {\n if (window.hljs == null) return null;\n return function(text) {\n let result = window.hljs.highlightAuto(text);\n return result.value;\n };\n })(),\n interval: 1000\n};\n\n\nexport { SyntaxCodeBlock as CodeBlock, CodeToken, Syntax as default};\n\n\n\n// WEBPACK FOOTER //\n// ./modules/syntax.js","import Delta from 'quill-delta';\nimport Parchment from 'parchment';\nimport Quill from '../core/quill';\nimport logger from '../core/logger';\nimport Module from '../core/module';\n\nlet debug = logger('quill:toolbar');\n\n\nclass Toolbar extends Module {\n constructor(quill, options) {\n super(quill, options);\n if (Array.isArray(this.options.container)) {\n let container = document.createElement('div');\n addControls(container, this.options.container);\n quill.container.parentNode.insertBefore(container, quill.container);\n this.container = container;\n } else if (typeof this.options.container === 'string') {\n this.container = document.querySelector(this.options.container);\n } else {\n this.container = this.options.container;\n }\n if (!(this.container instanceof HTMLElement)) {\n return debug.error('Container required for toolbar', this.options);\n }\n this.container.classList.add('ql-toolbar');\n this.controls = [];\n this.handlers = {};\n Object.keys(this.options.handlers).forEach((format) => {\n this.addHandler(format, this.options.handlers[format]);\n });\n [].forEach.call(this.container.querySelectorAll('button, select'), (input) => {\n this.attach(input);\n });\n this.quill.on(Quill.events.EDITOR_CHANGE, (type, range) => {\n if (type === Quill.events.SELECTION_CHANGE) {\n this.update(range);\n }\n });\n this.quill.on(Quill.events.SCROLL_OPTIMIZE, () => {\n let [range, ] = this.quill.selection.getRange(); // quill.getSelection triggers update\n this.update(range);\n });\n }\n\n addHandler(format, handler) {\n this.handlers[format] = handler;\n }\n\n attach(input) {\n let format = [].find.call(input.classList, (className) => {\n return className.indexOf('ql-') === 0;\n });\n if (!format) return;\n format = format.slice('ql-'.length);\n if (input.tagName === 'BUTTON') {\n input.setAttribute('type', 'button');\n }\n if (this.handlers[format] == null) {\n if (this.quill.scroll.whitelist != null && this.quill.scroll.whitelist[format] == null) {\n debug.warn('ignoring attaching to disabled format', format, input);\n return;\n }\n if (Parchment.query(format) == null) {\n debug.warn('ignoring attaching to nonexistent format', format, input);\n return;\n }\n }\n let eventName = input.tagName === 'SELECT' ? 'change' : 'click';\n input.addEventListener(eventName, (e) => {\n let value;\n if (input.tagName === 'SELECT') {\n if (input.selectedIndex < 0) return;\n let selected = input.options[input.selectedIndex];\n if (selected.hasAttribute('selected')) {\n value = false;\n } else {\n value = selected.value || false;\n }\n } else {\n if (input.classList.contains('ql-active')) {\n value = false;\n } else {\n value = input.value || !input.hasAttribute('value');\n }\n e.preventDefault();\n }\n this.quill.focus();\n let [range, ] = this.quill.selection.getRange();\n if (this.handlers[format] != null) {\n this.handlers[format].call(this, value);\n } else if (Parchment.query(format).prototype instanceof Parchment.Embed) {\n value = prompt(`Enter ${format}`);\n if (!value) return;\n this.quill.updateContents(new Delta()\n .retain(range.index)\n .delete(range.length)\n .insert({ [format]: value })\n , Quill.sources.USER);\n } else {\n this.quill.format(format, value, Quill.sources.USER);\n }\n this.update(range);\n });\n // TODO use weakmap\n this.controls.push([format, input]);\n }\n\n update(range) {\n let formats = range == null ? {} : this.quill.getFormat(range);\n this.controls.forEach(function(pair) {\n let [format, input] = pair;\n if (input.tagName === 'SELECT') {\n let option;\n if (range == null) {\n option = null;\n } else if (formats[format] == null) {\n option = input.querySelector('option[selected]');\n } else if (!Array.isArray(formats[format])) {\n let value = formats[format];\n if (typeof value === 'string') {\n value = value.replace(/\\\"/g, '\\\\\"');\n }\n option = input.querySelector(`option[value=\"${value}\"]`);\n }\n if (option == null) {\n input.value = ''; // TODO make configurable?\n input.selectedIndex = -1;\n } else {\n option.selected = true;\n }\n } else {\n if (range == null) {\n input.classList.remove('ql-active');\n } else if (input.hasAttribute('value')) {\n // both being null should match (default values)\n // '1' should match with 1 (headers)\n let isActive = formats[format] === input.getAttribute('value') ||\n (formats[format] != null && formats[format].toString() === input.getAttribute('value')) ||\n (formats[format] == null && !input.getAttribute('value'));\n input.classList.toggle('ql-active', isActive);\n } else {\n input.classList.toggle('ql-active', formats[format] != null);\n }\n }\n });\n }\n}\nToolbar.DEFAULTS = {};\n\n\nfunction addButton(container, format, value) {\n let input = document.createElement('button');\n input.setAttribute('type', 'button');\n input.classList.add('ql-' + format);\n if (value != null) {\n input.value = value;\n }\n container.appendChild(input);\n}\n\nfunction addControls(container, groups) {\n if (!Array.isArray(groups[0])) {\n groups = [groups];\n }\n groups.forEach(function(controls) {\n let group = document.createElement('span');\n group.classList.add('ql-formats');\n controls.forEach(function(control) {\n if (typeof control === 'string') {\n addButton(group, control);\n } else {\n let format = Object.keys(control)[0];\n let value = control[format];\n if (Array.isArray(value)) {\n addSelect(group, format, value);\n } else {\n addButton(group, format, value);\n }\n }\n });\n container.appendChild(group);\n });\n}\n\nfunction addSelect(container, format, values) {\n let input = document.createElement('select');\n input.classList.add('ql-' + format);\n values.forEach(function(value) {\n let option = document.createElement('option');\n if (value !== false) {\n option.setAttribute('value', value);\n } else {\n option.setAttribute('selected', 'selected');\n }\n input.appendChild(option);\n });\n container.appendChild(input);\n}\n\nToolbar.DEFAULTS = {\n container: null,\n handlers: {\n clean: function() {\n let range = this.quill.getSelection();\n if (range == null) return;\n if (range.length == 0) {\n let formats = this.quill.getFormat();\n Object.keys(formats).forEach((name) => {\n // Clean functionality in existing apps only clean inline formats\n if (Parchment.query(name, Parchment.Scope.INLINE) != null) {\n this.quill.format(name, false);\n }\n });\n } else {\n this.quill.removeFormat(range, Quill.sources.USER);\n }\n },\n direction: function(value) {\n let align = this.quill.getFormat()['align'];\n if (value === 'rtl' && align == null) {\n this.quill.format('align', 'right', Quill.sources.USER);\n } else if (!value && align === 'right') {\n this.quill.format('align', false, Quill.sources.USER);\n }\n this.quill.format('direction', value, Quill.sources.USER);\n },\n indent: function(value) {\n let range = this.quill.getSelection();\n let formats = this.quill.getFormat(range);\n let indent = parseInt(formats.indent || 0);\n if (value === '+1' || value === '-1') {\n let modifier = (value === '+1') ? 1 : -1;\n if (formats.direction === 'rtl') modifier *= -1;\n this.quill.format('indent', indent + modifier, Quill.sources.USER);\n }\n },\n link: function(value) {\n if (value === true) {\n value = prompt('Enter link URL:');\n }\n this.quill.format('link', value, Quill.sources.USER);\n },\n list: function(value) {\n let range = this.quill.getSelection();\n let formats = this.quill.getFormat(range);\n if (value === 'check') {\n if (formats['list'] === 'checked' || formats['list'] === 'unchecked') {\n this.quill.format('list', false, Quill.sources.USER);\n } else {\n this.quill.format('list', 'unchecked', Quill.sources.USER);\n }\n } else {\n this.quill.format('list', value, Quill.sources.USER);\n }\n }\n }\n}\n\n\nexport { Toolbar as default, addControls };\n\n\n\n// WEBPACK FOOTER //\n// ./modules/toolbar.js","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/align-left.svg\n// module id = 75\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/align-center.svg\n// module id = 76\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/align-right.svg\n// module id = 77\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/align-justify.svg\n// module id = 78\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/background.svg\n// module id = 79\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/blockquote.svg\n// module id = 80\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/bold.svg\n// module id = 81\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/clean.svg\n// module id = 82\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/color.svg\n// module id = 83\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/direction-ltr.svg\n// module id = 84\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/direction-rtl.svg\n// module id = 85\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/float-center.svg\n// module id = 86\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/float-full.svg\n// module id = 87\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/float-left.svg\n// module id = 88\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/float-right.svg\n// module id = 89\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/formula.svg\n// module id = 90\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/header.svg\n// module id = 91\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/header-2.svg\n// module id = 92\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/italic.svg\n// module id = 93\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/image.svg\n// module id = 94\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/indent.svg\n// module id = 95\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/outdent.svg\n// module id = 96\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/link.svg\n// module id = 97\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/list-ordered.svg\n// module id = 98\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/list-bullet.svg\n// module id = 99\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/list-check.svg\n// module id = 100\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/subscript.svg\n// module id = 101\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/superscript.svg\n// module id = 102\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/strike.svg\n// module id = 103\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/underline.svg\n// module id = 104\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/video.svg\n// module id = 105\n// module chunks = 0","module.exports = \" \";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./assets/icons/dropdown.svg\n// module id = 106\n// module chunks = 0","import extend from 'extend';\nimport Emitter from '../core/emitter';\nimport BaseTheme, { BaseTooltip } from './base';\nimport { Range } from '../core/selection';\nimport icons from '../ui/icons';\n\n\nconst TOOLBAR_CONFIG = [\n ['bold', 'italic', 'link'],\n [{ header: 1 }, { header: 2 }, 'blockquote']\n];\n\nclass BubbleTheme extends BaseTheme {\n constructor(quill, options) {\n if (options.modules.toolbar != null && options.modules.toolbar.container == null) {\n options.modules.toolbar.container = TOOLBAR_CONFIG;\n }\n super(quill, options);\n this.quill.container.classList.add('ql-bubble');\n }\n\n extendToolbar(toolbar) {\n this.tooltip = new BubbleTooltip(this.quill, this.options.bounds);\n this.tooltip.root.appendChild(toolbar.container);\n this.buildButtons([].slice.call(toolbar.container.querySelectorAll('button')), icons);\n this.buildPickers([].slice.call(toolbar.container.querySelectorAll('select')), icons);\n }\n}\nBubbleTheme.DEFAULTS = extend(true, {}, BaseTheme.DEFAULTS, {\n modules: {\n toolbar: {\n handlers: {\n link: function(value) {\n if (!value) {\n this.quill.format('link', false);\n } else {\n this.quill.theme.tooltip.edit();\n }\n }\n }\n }\n }\n});\n\n\nclass BubbleTooltip extends BaseTooltip {\n constructor(quill, bounds) {\n super(quill, bounds);\n this.quill.on(Emitter.events.EDITOR_CHANGE, (type, range, oldRange, source) => {\n if (type !== Emitter.events.SELECTION_CHANGE) return;\n if (range != null && range.length > 0 && source === Emitter.sources.USER) {\n this.show();\n // Lock our width so we will expand beyond our offsetParent boundaries\n this.root.style.left = '0px';\n this.root.style.width = '';\n this.root.style.width = this.root.offsetWidth + 'px';\n let lines = this.quill.getLines(range.index, range.length);\n if (lines.length === 1) {\n this.position(this.quill.getBounds(range));\n } else {\n let lastLine = lines[lines.length - 1];\n let index = this.quill.getIndex(lastLine);\n let length = Math.min(lastLine.length() - 1, range.index + range.length - index);\n let bounds = this.quill.getBounds(new Range(index, length));\n this.position(bounds);\n }\n } else if (document.activeElement !== this.textbox && this.quill.hasFocus()) {\n this.hide();\n }\n });\n }\n\n listen() {\n super.listen();\n this.root.querySelector('.ql-close').addEventListener('click', () => {\n this.root.classList.remove('ql-editing');\n });\n this.quill.on(Emitter.events.SCROLL_OPTIMIZE, () => {\n // Let selection be restored by toolbar handlers before repositioning\n setTimeout(() => {\n if (this.root.classList.contains('ql-hidden')) return;\n let range = this.quill.getSelection();\n if (range != null) {\n this.position(this.quill.getBounds(range));\n }\n }, 1);\n });\n }\n\n cancel() {\n this.show();\n }\n\n position(reference) {\n let shift = super.position(reference);\n let arrow = this.root.querySelector('.ql-tooltip-arrow');\n arrow.style.marginLeft = '';\n if (shift === 0) return shift;\n arrow.style.marginLeft = (-1*shift - arrow.offsetWidth/2) + 'px';\n }\n}\nBubbleTooltip.TEMPLATE = [\n '',\n '
',\n '',\n '',\n '
'\n].join('');\n\n\nexport { BubbleTooltip, BubbleTheme as default };\n\n\n\n// WEBPACK FOOTER //\n// ./themes/bubble.js","import extend from 'extend';\nimport Emitter from '../core/emitter';\nimport BaseTheme, { BaseTooltip } from './base';\nimport LinkBlot from '../formats/link';\nimport { Range } from '../core/selection';\nimport icons from '../ui/icons';\n\n\nconst TOOLBAR_CONFIG = [\n [{ header: ['1', '2', '3', false] }],\n ['bold', 'italic', 'underline', 'link'],\n [{ list: 'ordered' }, { list: 'bullet' }],\n ['clean']\n];\n\nclass SnowTheme extends BaseTheme {\n constructor(quill, options) {\n if (options.modules.toolbar != null && options.modules.toolbar.container == null) {\n options.modules.toolbar.container = TOOLBAR_CONFIG;\n }\n super(quill, options);\n this.quill.container.classList.add('ql-snow');\n }\n\n extendToolbar(toolbar) {\n toolbar.container.classList.add('ql-snow');\n this.buildButtons([].slice.call(toolbar.container.querySelectorAll('button')), icons);\n this.buildPickers([].slice.call(toolbar.container.querySelectorAll('select')), icons);\n this.tooltip = new SnowTooltip(this.quill, this.options.bounds);\n if (toolbar.container.querySelector('.ql-link')) {\n this.quill.keyboard.addBinding({ key: 'K', shortKey: true }, function(range, context) {\n toolbar.handlers['link'].call(toolbar, !context.format.link);\n });\n }\n }\n}\nSnowTheme.DEFAULTS = extend(true, {}, BaseTheme.DEFAULTS, {\n modules: {\n toolbar: {\n handlers: {\n link: function(value) {\n if (value) {\n let range = this.quill.getSelection();\n if (range == null || range.length == 0) return;\n let preview = this.quill.getText(range);\n if (/^\\S+@\\S+\\.\\S+$/.test(preview) && preview.indexOf('mailto:') !== 0) {\n preview = 'mailto:' + preview;\n }\n let tooltip = this.quill.theme.tooltip;\n tooltip.edit('link', preview);\n } else {\n this.quill.format('link', false);\n }\n }\n }\n }\n }\n});\n\n\nclass SnowTooltip extends BaseTooltip {\n constructor(quill, bounds) {\n super(quill, bounds);\n this.preview = this.root.querySelector('a.ql-preview');\n }\n\n listen() {\n super.listen();\n this.root.querySelector('a.ql-action').addEventListener('click', (event) => {\n if (this.root.classList.contains('ql-editing')) {\n this.save();\n } else {\n this.edit('link', this.preview.textContent);\n }\n event.preventDefault();\n });\n this.root.querySelector('a.ql-remove').addEventListener('click', (event) => {\n if (this.linkRange != null) {\n let range = this.linkRange;\n this.restoreFocus();\n this.quill.formatText(range, 'link', false, Emitter.sources.USER);\n delete this.linkRange;\n }\n event.preventDefault();\n this.hide();\n });\n this.quill.on(Emitter.events.SELECTION_CHANGE, (range, oldRange, source) => {\n if (range == null) return;\n if (range.length === 0 && source === Emitter.sources.USER) {\n let [link, offset] = this.quill.scroll.descendant(LinkBlot, range.index);\n if (link != null) {\n this.linkRange = new Range(range.index - offset, link.length());\n let preview = LinkBlot.formats(link.domNode);\n this.preview.textContent = preview;\n this.preview.setAttribute('href', preview);\n this.show();\n this.position(this.quill.getBounds(this.linkRange));\n return;\n }\n } else {\n delete this.linkRange;\n }\n this.hide();\n });\n }\n\n show() {\n super.show();\n this.root.removeAttribute('data-mode');\n }\n}\nSnowTooltip.TEMPLATE = [\n '',\n '',\n '',\n ''\n].join('');\n\n\nexport default SnowTheme;\n\n\n\n// WEBPACK FOOTER //\n// ./themes/snow.js"],"sourceRoot":""} \ No newline at end of file diff --git a/Wino.Mail/JS/Quill/quill.snow.css b/Wino.Mail/JS/Quill/quill.snow.css new file mode 100644 index 00000000..c4792697 --- /dev/null +++ b/Wino.Mail/JS/Quill/quill.snow.css @@ -0,0 +1,947 @@ +/*! + * Quill Editor v1.3.6 + * https://quilljs.com/ + * Copyright (c) 2014, Jason Chen + * Copyright (c) 2013, salesforce.com + */ +.ql-container { + + font-family: Helvetica, Arial, sans-serif; + font-size: 13px; + height: 1000px !important; + resize: vertical; + margin: 0px; +} +.ql-container.ql-disabled .ql-tooltip { + visibility: hidden; +} +.ql-container.ql-disabled .ql-editor ul[data-checked] > li::before { + pointer-events: none; +} +.ql-clipboard { + left: -100000px; + height: 1px; + overflow-y: hidden; + position: absolute; + top: 50%; +} +.ql-clipboard p { + margin: 0; + padding: 0; +} +.ql-editor { + line-height: 1.40; + height: 100%; + outline: none; + overflow-y: auto; + padding: 4px 4px; + tab-size: 4; + -moz-tab-size: 4; + text-align: left; + white-space: pre-wrap; + word-wrap: break-word; +} + +/* Reply Border */ +.ql-container.ql-snow { + /* border: solid #636e72; */ +} + +.ql-editor > * { + cursor: text; +} +.ql-editor p, +.ql-editor ol, +.ql-editor ul, +.ql-editor pre, +.ql-editor blockquote, +.ql-editor h1, +.ql-editor h2, +.ql-editor h3, +.ql-editor h4, +.ql-editor h5, +.ql-editor h6 { + margin: 0; + padding: 0; + counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9; +} +.ql-editor ol, +.ql-editor ul { + padding-left: 1.5em; +} +.ql-editor ol > li, +.ql-editor ul > li { + list-style-type: none; +} +.ql-editor ul > li::before { + content: '\2022'; +} +.ql-editor ul[data-checked=true], +.ql-editor ul[data-checked=false] { + pointer-events: none; +} +.ql-editor ul[data-checked=true] > li *, +.ql-editor ul[data-checked=false] > li * { + pointer-events: all; +} +.ql-editor ul[data-checked=true] > li::before, +.ql-editor ul[data-checked=false] > li::before { + color: #777; + cursor: pointer; + pointer-events: all; +} +.ql-editor ul[data-checked=true] > li::before { + content: '\2611'; +} +.ql-editor ul[data-checked=false] > li::before { + content: '\2610'; +} +.ql-editor li::before { + display: inline-block; + white-space: nowrap; + width: 1.2em; +} +.ql-editor li:not(.ql-direction-rtl)::before { + margin-left: -1.5em; + margin-right: 0.3em; + text-align: right; +} +.ql-editor li.ql-direction-rtl::before { + margin-left: 0.3em; + margin-right: -1.5em; +} +.ql-editor ol li:not(.ql-direction-rtl), +.ql-editor ul li:not(.ql-direction-rtl) { + padding-left: 1.5em; +} +.ql-editor ol li.ql-direction-rtl, +.ql-editor ul li.ql-direction-rtl { + padding-right: 1.5em; +} +.ql-editor ol li { + counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9; + counter-increment: list-0; +} +.ql-editor ol li:before { + content: counter(list-0, decimal) '. '; +} +.ql-editor ol li.ql-indent-1 { + counter-increment: list-1; +} +.ql-editor ol li.ql-indent-1:before { + content: counter(list-1, lower-alpha) '. '; +} +.ql-editor ol li.ql-indent-1 { + counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9; +} +.ql-editor ol li.ql-indent-2 { + counter-increment: list-2; +} +.ql-editor ol li.ql-indent-2:before { + content: counter(list-2, lower-roman) '. '; +} +.ql-editor ol li.ql-indent-2 { + counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9; +} +.ql-editor ol li.ql-indent-3 { + counter-increment: list-3; +} +.ql-editor ol li.ql-indent-3:before { + content: counter(list-3, decimal) '. '; +} +.ql-editor ol li.ql-indent-3 { + counter-reset: list-4 list-5 list-6 list-7 list-8 list-9; +} +.ql-editor ol li.ql-indent-4 { + counter-increment: list-4; +} +.ql-editor ol li.ql-indent-4:before { + content: counter(list-4, lower-alpha) '. '; +} +.ql-editor ol li.ql-indent-4 { + counter-reset: list-5 list-6 list-7 list-8 list-9; +} +.ql-editor ol li.ql-indent-5 { + counter-increment: list-5; +} +.ql-editor ol li.ql-indent-5:before { + content: counter(list-5, lower-roman) '. '; +} +.ql-editor ol li.ql-indent-5 { + counter-reset: list-6 list-7 list-8 list-9; +} +.ql-editor ol li.ql-indent-6 { + counter-increment: list-6; +} +.ql-editor ol li.ql-indent-6:before { + content: counter(list-6, decimal) '. '; +} +.ql-editor ol li.ql-indent-6 { + counter-reset: list-7 list-8 list-9; +} +.ql-editor ol li.ql-indent-7 { + counter-increment: list-7; +} +.ql-editor ol li.ql-indent-7:before { + content: counter(list-7, lower-alpha) '. '; +} +.ql-editor ol li.ql-indent-7 { + counter-reset: list-8 list-9; +} +.ql-editor ol li.ql-indent-8 { + counter-increment: list-8; +} +.ql-editor ol li.ql-indent-8:before { + content: counter(list-8, lower-roman) '. '; +} +.ql-editor ol li.ql-indent-8 { + counter-reset: list-9; +} +.ql-editor ol li.ql-indent-9 { + counter-increment: list-9; +} +.ql-editor ol li.ql-indent-9:before { + content: counter(list-9, decimal) '. '; +} +.ql-editor .ql-indent-1:not(.ql-direction-rtl) { + padding-left: 3em; +} +.ql-editor li.ql-indent-1:not(.ql-direction-rtl) { + padding-left: 4.5em; +} +.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right { + padding-right: 3em; +} +.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right { + padding-right: 4.5em; +} +.ql-editor .ql-indent-2:not(.ql-direction-rtl) { + padding-left: 6em; +} +.ql-editor li.ql-indent-2:not(.ql-direction-rtl) { + padding-left: 7.5em; +} +.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right { + padding-right: 6em; +} +.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right { + padding-right: 7.5em; +} +.ql-editor .ql-indent-3:not(.ql-direction-rtl) { + padding-left: 9em; +} +.ql-editor li.ql-indent-3:not(.ql-direction-rtl) { + padding-left: 10.5em; +} +.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right { + padding-right: 9em; +} +.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right { + padding-right: 10.5em; +} +.ql-editor .ql-indent-4:not(.ql-direction-rtl) { + padding-left: 12em; +} +.ql-editor li.ql-indent-4:not(.ql-direction-rtl) { + padding-left: 13.5em; +} +.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right { + padding-right: 12em; +} +.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right { + padding-right: 13.5em; +} +.ql-editor .ql-indent-5:not(.ql-direction-rtl) { + padding-left: 15em; +} +.ql-editor li.ql-indent-5:not(.ql-direction-rtl) { + padding-left: 16.5em; +} +.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right { + padding-right: 15em; +} +.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right { + padding-right: 16.5em; +} +.ql-editor .ql-indent-6:not(.ql-direction-rtl) { + padding-left: 18em; +} +.ql-editor li.ql-indent-6:not(.ql-direction-rtl) { + padding-left: 19.5em; +} +.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right { + padding-right: 18em; +} +.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right { + padding-right: 19.5em; +} +.ql-editor .ql-indent-7:not(.ql-direction-rtl) { + padding-left: 21em; +} +.ql-editor li.ql-indent-7:not(.ql-direction-rtl) { + padding-left: 22.5em; +} +.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right { + padding-right: 21em; +} +.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right { + padding-right: 22.5em; +} +.ql-editor .ql-indent-8:not(.ql-direction-rtl) { + padding-left: 24em; +} +.ql-editor li.ql-indent-8:not(.ql-direction-rtl) { + padding-left: 25.5em; +} +.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right { + padding-right: 24em; +} +.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right { + padding-right: 25.5em; +} +.ql-editor .ql-indent-9:not(.ql-direction-rtl) { + padding-left: 27em; +} +.ql-editor li.ql-indent-9:not(.ql-direction-rtl) { + padding-left: 28.5em; +} +.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right { + padding-right: 27em; +} +.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right { + padding-right: 28.5em; +} +.ql-editor .ql-video { + display: block; + max-width: 100%; +} +.ql-editor .ql-video.ql-align-center { + margin: 0 auto; +} +.ql-editor .ql-video.ql-align-right { + margin: 0 0 0 auto; +} +.ql-editor .ql-bg-black { + background-color: #000; +} +.ql-editor .ql-bg-red { + background-color: #e60000; +} +.ql-editor .ql-bg-orange { + background-color: #f90; +} +.ql-editor .ql-bg-yellow { + background-color: #ff0; +} +.ql-editor .ql-bg-green { + background-color: #008a00; +} +.ql-editor .ql-bg-blue { + background-color: #06c; +} +.ql-editor .ql-bg-purple { + background-color: #93f; +} +.ql-editor .ql-color-white { + color: #fff; +} +.ql-editor .ql-color-red { + color: #e60000; +} +.ql-editor .ql-color-orange { + color: #f90; +} +.ql-editor .ql-color-yellow { + color: #ff0; +} +.ql-editor .ql-color-green { + color: #008a00; +} +.ql-editor .ql-color-blue { + color: #06c; +} +.ql-editor .ql-color-purple { + color: #93f; +} +.ql-editor .ql-font-serif { + font-family: Georgia, Times New Roman, serif; +} +.ql-editor .ql-font-monospace { + font-family: Monaco, Courier New, monospace; +} +.ql-editor .ql-size-small { + font-size: 0.75em; +} +.ql-editor .ql-size-large { + font-size: 1.5em; +} +.ql-editor .ql-size-huge { + font-size: 2.5em; +} +.ql-editor .ql-direction-rtl { + direction: rtl; + text-align: inherit; +} +.ql-editor .ql-align-center { + text-align: center; +} +.ql-editor .ql-align-justify { + text-align: justify; +} +.ql-editor .ql-align-right { + text-align: right; +} +.ql-editor.ql-blank::before { + color: rgba(0,0,0,0.6); + content: attr(data-placeholder); + left: 4px; + pointer-events: none; + position: absolute; + right: 4px; +} +.ql-snow.ql-toolbar:after, +.ql-snow .ql-toolbar:after { + clear: both; + content: ''; + display: table; +} +.ql-snow.ql-toolbar button, +.ql-snow .ql-toolbar button { + background: none; + border: none; + cursor: pointer; + display: inline-block; + float: left; + height: 24px; + padding: 3px 5px; + width: 28px; +} +.ql-snow.ql-toolbar button svg, +.ql-snow .ql-toolbar button svg { + float: left; + height: 100%; +} +.ql-snow.ql-toolbar button:active:hover, +.ql-snow .ql-toolbar button:active:hover { + outline: none; +} +.ql-snow.ql-toolbar input.ql-image[type=file], +.ql-snow .ql-toolbar input.ql-image[type=file] { + display: none; +} +.ql-snow.ql-toolbar button:hover, +.ql-snow .ql-toolbar button:hover, +.ql-snow.ql-toolbar button:focus, +.ql-snow .ql-toolbar button:focus, +.ql-snow.ql-toolbar button.ql-active, +.ql-snow .ql-toolbar button.ql-active, +.ql-snow.ql-toolbar .ql-picker-label:hover, +.ql-snow .ql-toolbar .ql-picker-label:hover, +.ql-snow.ql-toolbar .ql-picker-label.ql-active, +.ql-snow .ql-toolbar .ql-picker-label.ql-active, +.ql-snow.ql-toolbar .ql-picker-item:hover, +.ql-snow .ql-toolbar .ql-picker-item:hover, +.ql-snow.ql-toolbar .ql-picker-item.ql-selected, +.ql-snow .ql-toolbar .ql-picker-item.ql-selected { + color: #06c; +} +.ql-snow.ql-toolbar button:hover .ql-fill, +.ql-snow .ql-toolbar button:hover .ql-fill, +.ql-snow.ql-toolbar button:focus .ql-fill, +.ql-snow .ql-toolbar button:focus .ql-fill, +.ql-snow.ql-toolbar button.ql-active .ql-fill, +.ql-snow .ql-toolbar button.ql-active .ql-fill, +.ql-snow.ql-toolbar .ql-picker-label:hover .ql-fill, +.ql-snow .ql-toolbar .ql-picker-label:hover .ql-fill, +.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-fill, +.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-fill, +.ql-snow.ql-toolbar .ql-picker-item:hover .ql-fill, +.ql-snow .ql-toolbar .ql-picker-item:hover .ql-fill, +.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-fill, +.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-fill, +.ql-snow.ql-toolbar button:hover .ql-stroke.ql-fill, +.ql-snow .ql-toolbar button:hover .ql-stroke.ql-fill, +.ql-snow.ql-toolbar button:focus .ql-stroke.ql-fill, +.ql-snow .ql-toolbar button:focus .ql-stroke.ql-fill, +.ql-snow.ql-toolbar button.ql-active .ql-stroke.ql-fill, +.ql-snow .ql-toolbar button.ql-active .ql-stroke.ql-fill, +.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill, +.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill, +.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill, +.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill, +.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill, +.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill, +.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill, +.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill { + fill: #06c; +} +.ql-snow.ql-toolbar button:hover .ql-stroke, +.ql-snow .ql-toolbar button:hover .ql-stroke, +.ql-snow.ql-toolbar button:focus .ql-stroke, +.ql-snow .ql-toolbar button:focus .ql-stroke, +.ql-snow.ql-toolbar button.ql-active .ql-stroke, +.ql-snow .ql-toolbar button.ql-active .ql-stroke, +.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke, +.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke, +.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke, +.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke, +.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke, +.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke, +.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke, +.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke, +.ql-snow.ql-toolbar button:hover .ql-stroke-miter, +.ql-snow .ql-toolbar button:hover .ql-stroke-miter, +.ql-snow.ql-toolbar button:focus .ql-stroke-miter, +.ql-snow .ql-toolbar button:focus .ql-stroke-miter, +.ql-snow.ql-toolbar button.ql-active .ql-stroke-miter, +.ql-snow .ql-toolbar button.ql-active .ql-stroke-miter, +.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke-miter, +.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke-miter, +.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter, +.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter, +.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke-miter, +.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke-miter, +.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter, +.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter { + stroke: #06c; +} +@media (pointer: coarse) { + .ql-snow.ql-toolbar button:hover:not(.ql-active), + .ql-snow .ql-toolbar button:hover:not(.ql-active) { + color: #444; + } + .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-fill, + .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-fill, + .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill, + .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill { + fill: #444; + } + .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke, + .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke, + .ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter, + .ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter { + stroke: #444; + } +} +.ql-snow { + box-sizing: border-box; +} +.ql-snow * { + box-sizing: border-box; +} +.ql-snow .ql-hidden { + display: none; +} +.ql-snow .ql-out-bottom, +.ql-snow .ql-out-top { + visibility: hidden; +} +.ql-snow .ql-tooltip { + position: absolute; + transform: translateY(10px); +} +.ql-snow .ql-tooltip a { + cursor: pointer; + text-decoration: none; +} +.ql-snow .ql-tooltip.ql-flip { + transform: translateY(-10px); +} +.ql-snow .ql-formats { + display: inline-block; + vertical-align: middle; +} +.ql-snow .ql-formats:after { + clear: both; + content: ''; + display: table; +} +.ql-snow .ql-stroke { + fill: none; + stroke: #444; + stroke-linecap: round; + stroke-linejoin: round; + stroke-width: 2; +} +.ql-snow .ql-stroke-miter { + fill: none; + stroke: #444; + stroke-miterlimit: 10; + stroke-width: 2; +} +.ql-snow .ql-fill, +.ql-snow .ql-stroke.ql-fill { + fill: #444; +} +.ql-snow .ql-empty { + fill: none; +} +.ql-snow .ql-even { + fill-rule: evenodd; +} +.ql-snow .ql-thin, +.ql-snow .ql-stroke.ql-thin { + stroke-width: 1; +} +.ql-snow .ql-transparent { + opacity: 0.4; +} +.ql-snow .ql-direction svg:last-child { + display: none; +} +.ql-snow .ql-direction.ql-active svg:last-child { + display: inline; +} +.ql-snow .ql-direction.ql-active svg:first-child { + display: none; +} +.ql-snow .ql-editor h1 { + font-size: 2em; +} +.ql-snow .ql-editor h2 { + font-size: 1.5em; +} +.ql-snow .ql-editor h3 { + font-size: 1.17em; +} +.ql-snow .ql-editor h4 { + font-size: 1em; +} +.ql-snow .ql-editor h5 { + font-size: 0.83em; +} +.ql-snow .ql-editor h6 { + font-size: 0.67em; +} +.ql-snow .ql-editor a { + text-decoration: underline; +} +.ql-snow .ql-editor blockquote { + border-left: 4px solid #ccc; + margin-bottom: 5px; + margin-top: 5px; + padding-left: 16px; +} +.ql-snow .ql-editor code, +.ql-snow .ql-editor pre { + background-color: #f0f0f0; + border-radius: 3px; +} +.ql-snow .ql-editor pre { + white-space: pre-wrap; + margin-bottom: 5px; + margin-top: 5px; + padding: 5px 10px; +} +.ql-snow .ql-editor code { + font-size: 85%; + padding: 2px 4px; +} +.ql-snow .ql-editor pre.ql-syntax { + background-color: #23241f; + color: #f8f8f2; + overflow: visible; +} +.ql-snow .ql-editor img { + max-width: 100%; +} +.ql-snow .ql-picker { + color: #444; + display: inline-block; + float: left; + font-size: 14px; + font-weight: 500; + height: 24px; + position: relative; + vertical-align: middle; +} +.ql-snow .ql-picker-label { + cursor: pointer; + display: inline-block; + height: 100%; + padding-left: 8px; + padding-right: 2px; + position: relative; + width: 100%; +} +.ql-snow .ql-picker-label::before { + display: inline-block; + line-height: 22px; +} +.ql-snow .ql-picker-options { + background-color: #fff; + display: none; + min-width: 100%; + padding: 4px 8px; + position: absolute; + white-space: nowrap; +} +.ql-snow .ql-picker-options .ql-picker-item { + cursor: pointer; + display: block; + padding-bottom: 5px; + padding-top: 5px; +} +.ql-snow .ql-picker.ql-expanded .ql-picker-label { + color: #ccc; + z-index: 2; +} +.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-fill { + fill: #ccc; +} +.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-stroke { + stroke: #ccc; +} +.ql-snow .ql-picker.ql-expanded .ql-picker-options { + display: block; + margin-top: -1px; + top: 100%; + z-index: 1; +} +.ql-snow .ql-color-picker, +.ql-snow .ql-icon-picker { + width: 28px; +} +.ql-snow .ql-color-picker .ql-picker-label, +.ql-snow .ql-icon-picker .ql-picker-label { + padding: 2px 4px; +} +.ql-snow .ql-color-picker .ql-picker-label svg, +.ql-snow .ql-icon-picker .ql-picker-label svg { + right: 4px; +} +.ql-snow .ql-icon-picker .ql-picker-options { + padding: 4px 0px; +} +.ql-snow .ql-icon-picker .ql-picker-item { + height: 24px; + width: 24px; + padding: 2px 4px; +} +.ql-snow .ql-color-picker .ql-picker-options { + padding: 3px 5px; + width: 152px; +} +.ql-snow .ql-color-picker .ql-picker-item { + border: 1px solid transparent; + float: left; + height: 16px; + margin: 2px; + padding: 0px; + width: 16px; +} +.ql-snow .ql-picker:not(.ql-color-picker):not(.ql-icon-picker) svg { + position: absolute; + margin-top: -9px; + right: 0; + top: 50%; + width: 18px; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-label]:not([data-label=''])::before, +.ql-snow .ql-picker.ql-font .ql-picker-label[data-label]:not([data-label=''])::before, +.ql-snow .ql-picker.ql-size .ql-picker-label[data-label]:not([data-label=''])::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-label]:not([data-label=''])::before, +.ql-snow .ql-picker.ql-font .ql-picker-item[data-label]:not([data-label=''])::before, +.ql-snow .ql-picker.ql-size .ql-picker-item[data-label]:not([data-label=''])::before { + content: attr(data-label); +} +.ql-snow .ql-picker.ql-header { + width: 98px; +} +.ql-snow .ql-picker.ql-header .ql-picker-label::before, +.ql-snow .ql-picker.ql-header .ql-picker-item::before { + content: 'Normal'; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before { + content: 'Heading 1'; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { + content: 'Heading 2'; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { + content: 'Heading 3'; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before { + content: 'Heading 4'; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before { + content: 'Heading 5'; +} +.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before, +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before { + content: 'Heading 6'; +} +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before { + font-size: 2em; +} +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { + font-size: 1.5em; +} +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { + font-size: 1.17em; +} +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before { + font-size: 1em; +} +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before { + font-size: 0.83em; +} +.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before { + font-size: 0.67em; +} +.ql-snow .ql-picker.ql-font { + width: 108px; +} +.ql-snow .ql-picker.ql-font .ql-picker-label::before, +.ql-snow .ql-picker.ql-font .ql-picker-item::before { + content: 'Sans Serif'; +} +.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=serif]::before, +.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before { + content: 'Serif'; +} +.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before, +.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before { + content: 'Monospace'; +} +.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before { + font-family: Georgia, Times New Roman, serif; +} +.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before { + font-family: Monaco, Courier New, monospace; +} +.ql-snow .ql-picker.ql-size { + width: 98px; +} +.ql-snow .ql-picker.ql-size .ql-picker-label::before, +.ql-snow .ql-picker.ql-size .ql-picker-item::before { + content: 'Normal'; +} +.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=small]::before, +.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before { + content: 'Small'; +} +.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=large]::before, +.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before { + content: 'Large'; +} +.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=huge]::before, +.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before { + content: 'Huge'; +} +.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before { + font-size: 10px; +} +.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before { + font-size: 18px; +} +.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before { + font-size: 32px; +} +.ql-snow .ql-color-picker.ql-background .ql-picker-item { + background-color: #fff; +} +.ql-snow .ql-color-picker.ql-color .ql-picker-item { + background-color: #000; +} +.ql-toolbar.ql-snow { + border: 1px solid #ccc; + box-sizing: border-box; + font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif; + padding: 8px; +} +.ql-toolbar.ql-snow .ql-formats { + margin-right: 15px; +} +.ql-toolbar.ql-snow .ql-picker-label { + border: 1px solid transparent; +} +.ql-toolbar.ql-snow .ql-picker-options { + border: 1px solid transparent; + box-shadow: rgba(0,0,0,0.2) 0 2px 8px; +} +.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-label { + border-color: #ccc; +} +.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-options { + border-color: #ccc; +} +.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item.ql-selected, +.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item:hover { + border-color: #000; +} +.ql-toolbar.ql-snow + .ql-container.ql-snow { + border-top: 0px; +} +.ql-snow .ql-tooltip { + background-color: #fff; + border: 1px solid #ccc; + box-shadow: 0px 0px 5px #ddd; + color: #444; + padding: 5px 12px; + white-space: nowrap; +} +.ql-snow .ql-tooltip::before { + content: "Visit URL:"; + line-height: 26px; + margin-right: 8px; +} +.ql-snow .ql-tooltip input[type=text] { + display: none; + border: 1px solid #ccc; + font-size: 13px; + height: 26px; + margin: 0px; + padding: 3px 5px; + width: 170px; +} +.ql-snow .ql-tooltip a.ql-preview { + display: inline-block; + max-width: 200px; + overflow-x: hidden; + text-overflow: ellipsis; + vertical-align: top; +} +.ql-snow .ql-tooltip a.ql-action::after { + border-right: 1px solid #ccc; + content: 'Edit'; + margin-left: 16px; + padding-right: 8px; +} +.ql-snow .ql-tooltip a.ql-remove::before { + content: 'Remove'; + margin-left: 8px; +} +.ql-snow .ql-tooltip a { + line-height: 26px; +} +.ql-snow .ql-tooltip.ql-editing a.ql-preview, +.ql-snow .ql-tooltip.ql-editing a.ql-remove { + display: none; +} +.ql-snow .ql-tooltip.ql-editing input[type=text] { + display: inline-block; +} +.ql-snow .ql-tooltip.ql-editing a.ql-action::after { + border-right: 0px; + content: 'Save'; + padding-right: 0px; +} +.ql-snow .ql-tooltip[data-mode=link]::before { + content: "Enter link:"; +} +.ql-snow .ql-tooltip[data-mode=formula]::before { + content: "Enter formula:"; +} +.ql-snow .ql-tooltip[data-mode=video]::before { + content: "Enter video:"; +} +.ql-snow a { + color: #06c; +} + diff --git a/Wino.Mail/JS/Quill/reader.css b/Wino.Mail/JS/Quill/reader.css new file mode 100644 index 00000000..1fbed5bc --- /dev/null +++ b/Wino.Mail/JS/Quill/reader.css @@ -0,0 +1,10 @@ +#readerDiv { + font-family: Arial, Helvetica, sans-serif; + background-color: transparent !important; +} + +/*@font-face { + font-family: Starborn; + src: url(Fonts/Starborn.ttf); +} +*/ diff --git a/Wino.Mail/JS/Quill/reader.html b/Wino.Mail/JS/Quill/reader.html new file mode 100644 index 00000000..30adca81 --- /dev/null +++ b/Wino.Mail/JS/Quill/reader.html @@ -0,0 +1,32 @@ + + + + + + + + + +
+
+ + diff --git a/Wino.Mail/MenuFlyouts/AccountSelectorFlyout.cs b/Wino.Mail/MenuFlyouts/AccountSelectorFlyout.cs new file mode 100644 index 00000000..134623f1 --- /dev/null +++ b/Wino.Mail/MenuFlyouts/AccountSelectorFlyout.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Windows.UI.Xaml.Controls; +using Wino.Controls; +using Wino.Core.Domain.Entities; +using Wino.Helpers; + +namespace Wino.MenuFlyouts +{ + public class AccountSelectorFlyout : MenuFlyout, IDisposable + { + private readonly IEnumerable _accounts; + private readonly Func _onItemSelection; + + public AccountSelectorFlyout(IEnumerable accounts, Func onItemSelection) + { + _accounts = accounts; + _onItemSelection = onItemSelection; + + foreach (var account in _accounts) + { + var pathData = new WinoFontIcon() { Icon = XamlHelpers.GetProviderIcon(account.ProviderType) }; + var menuItem = new MenuFlyoutItem() { Tag = account.Address, Icon = pathData, Text = $"{account.Name} ({account.Address})", MinHeight = 55 }; + + menuItem.Click += AccountClicked; + Items.Add(menuItem); + } + } + + public void Dispose() + { + foreach (var menuItem in Items) + { + if (menuItem is MenuFlyoutItem flyoutItem) + { + flyoutItem.Click -= AccountClicked; + } + } + } + + private async void AccountClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e) + { + if (sender is MenuFlyoutItem menuItem && menuItem.Tag is string accountAddress) + { + var selectedMenuItem = _accounts.FirstOrDefault(a => a.Address == accountAddress); + + if (selectedMenuItem != null) + { + await _onItemSelection(selectedMenuItem); + } + } + + Dispose(); + Hide(); + } + } +} diff --git a/Wino.Mail/MenuFlyouts/FilterMenuFlyout.cs b/Wino.Mail/MenuFlyouts/FilterMenuFlyout.cs new file mode 100644 index 00000000..67530eb6 --- /dev/null +++ b/Wino.Mail/MenuFlyouts/FilterMenuFlyout.cs @@ -0,0 +1,220 @@ +using System.Collections.Generic; +using System.Linq; +using CommunityToolkit.Mvvm.Input; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Controls; +using Wino.Core.Domain.Models.Reader; +using Wino.Helpers; + +namespace Wino.MenuFlyouts +{ + public class FilterMenuFlyout : MenuFlyout + { + public static readonly DependencyProperty SelectedFilterChangedCommandProperty = DependencyProperty.Register(nameof(SelectedFilterChangedCommand), typeof(IRelayCommand), typeof(FilterMenuFlyout), new PropertyMetadata(null)); + public static readonly DependencyProperty FilterOptionsProperty = DependencyProperty.Register(nameof(FilterOptions), typeof(List), typeof(FilterMenuFlyout), new PropertyMetadata(null, new PropertyChangedCallback(OnOptionsChanged))); + public static readonly DependencyProperty SelectedFilterOptionProperty = DependencyProperty.Register(nameof(SelectedFilterOption), typeof(FilterOption), typeof(FilterMenuFlyout), new PropertyMetadata(null, OnSelectedFilterOptionChanged)); + public static readonly DependencyProperty SelectedSortingOptionProperty = DependencyProperty.Register(nameof(SelectedSortingOption), typeof(SortingOption), typeof(FilterMenuFlyout), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedSortingOptionChanged))); + public static readonly DependencyProperty SortingOptionsProperty = DependencyProperty.Register(nameof(SortingOptions), typeof(List), typeof(FilterMenuFlyout), new PropertyMetadata(null, new PropertyChangedCallback(OnOptionsChanged))); + public static readonly DependencyProperty SelectedSortingOptionChangedCommandProperty = DependencyProperty.Register(nameof(SelectedSortingOptionChangedCommand), typeof(IRelayCommand), typeof(FilterMenuFlyout), new PropertyMetadata(null)); + + public IRelayCommand SelectedFilterChangedCommand + { + get { return (IRelayCommand)GetValue(SelectedFilterChangedCommandProperty); } + set { SetValue(SelectedFilterChangedCommandProperty, value); } + } + + public IRelayCommand SelectedSortingOptionChangedCommand + { + get { return (IRelayCommand)GetValue(SelectedSortingOptionChangedCommandProperty); } + set { SetValue(SelectedSortingOptionChangedCommandProperty, value); } + } + + public List FilterOptions + { + get { return (List)GetValue(FilterOptionsProperty); } + set { SetValue(FilterOptionsProperty, value); } + } + + public List SortingOptions + { + get { return (List)GetValue(SortingOptionsProperty); } + set { SetValue(SortingOptionsProperty, value); } + } + + public FilterOption SelectedFilterOption + { + get { return (FilterOption)GetValue(SelectedFilterOptionProperty); } + set { SetValue(SelectedFilterOptionProperty, value); } + } + + public SortingOption SelectedSortingOption + { + get { return (SortingOption)GetValue(SelectedSortingOptionProperty); } + set { SetValue(SelectedSortingOptionProperty, value); } + } + + private static void OnSelectedFilterOptionChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is FilterMenuFlyout bar) + { + bar.SelectFilterOption(bar.SelectedFilterOption); + bar.SelectedFilterChangedCommand?.Execute(bar.SelectedFilterOption); + } + } + + private static void OnSelectedSortingOptionChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is FilterMenuFlyout bar) + { + bar.SelectSortingOption(bar.SelectedSortingOption); + bar.SelectedSortingOptionChangedCommand?.Execute(bar.SelectedSortingOption); + } + } + + private ToggleMenuFlyoutItem CreateFilterToggleButton(FilterOption option) + { + var button = new ToggleMenuFlyoutItem() + { + Text = option.Title, + Tag = option, + Icon = new WinoFontIcon() { Icon = XamlHelpers.GetWinoIconGlyph(option.Type) }, + IsChecked = option == SelectedFilterOption + }; + + button.Click += FilterToggleChecked; + + return button; + } + + private ToggleMenuFlyoutItem CreateSortingToggleButton(SortingOption option) + { + var button = new ToggleMenuFlyoutItem() + { + Text = option.Title, + Tag = option, + IsChecked = option == SelectedSortingOption + }; + + button.Click += SortingOptionChecked; + + return button; + } + + private void SortingOptionChecked(object sender, RoutedEventArgs e) + { + if (sender is ToggleMenuFlyoutItem button) + { + button.IsHitTestVisible = false; + + var optionModel = button.Tag as SortingOption; + + SelectSortingOption(optionModel); + } + } + + + + private void FilterToggleChecked(object sender, RoutedEventArgs e) + { + if (sender is ToggleMenuFlyoutItem button) + { + button.IsHitTestVisible = false; + + var optionModel = button.Tag as FilterOption; + + SelectFilterOption(optionModel); + } + } + + private void SelectFilterOption(FilterOption option) + { + SelectedFilterOption = option; + + UncheckOtherFilterOptions(); + } + + private void SelectSortingOption(SortingOption option) + { + SelectedSortingOption = option; + + UncheckOtherSortingOptions(); + } + + private void UnregisterCheckedHandler(ToggleMenuFlyoutItem button) + { + button.Click -= FilterToggleChecked; + } + + private void UncheckOtherFilterOptions() + { + if (Items.Any()) + { + foreach (var item in Items) + { + if (item is ToggleMenuFlyoutItem toggleButton && toggleButton.Tag is FilterOption option && option != SelectedFilterOption) + { + toggleButton.IsChecked = false; + toggleButton.IsHitTestVisible = true; + } + } + } + } + + private void UncheckOtherSortingOptions() + { + if (Items.Any()) + { + foreach (var item in Items) + { + if (item is ToggleMenuFlyoutItem toggleButton && toggleButton.Tag is SortingOption option && option != SelectedSortingOption) + { + toggleButton.IsChecked = false; + toggleButton.IsHitTestVisible = true; + } + } + } + } + + public void Dispose() + { + foreach (var item in Items) + { + if (item is ToggleMenuFlyoutItem toggleButton) + { + UnregisterCheckedHandler(toggleButton); + } + } + } + + private static void OnOptionsChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is FilterMenuFlyout bar && bar.SortingOptions != null && bar.FilterOptions != null) + { + bar.Dispose(); + + bar.Items.Clear(); + + if (bar.FilterOptions != null) + { + foreach (var item in bar.FilterOptions) + { + bar.Items.Add(bar.CreateFilterToggleButton(item)); + } + } + + bar.Items.Add(new MenuFlyoutSeparator()); + + // Sorting options. + + if (bar.SortingOptions != null) + { + foreach (var item in bar.SortingOptions) + { + bar.Items.Add(bar.CreateSortingToggleButton(item)); + } + } + } + } + } +} diff --git a/Wino.Mail/MenuFlyouts/FolderOperationFlyout.cs b/Wino.Mail/MenuFlyouts/FolderOperationFlyout.cs new file mode 100644 index 00000000..c3ef1f51 --- /dev/null +++ b/Wino.Mail/MenuFlyouts/FolderOperationFlyout.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Folders; + +namespace Wino.MenuFlyouts.Context +{ + public class FolderOperationFlyout : WinoOperationFlyout + { + public FolderOperationFlyout(IEnumerable availableActions, TaskCompletionSource completionSource) : base(availableActions, completionSource) + { + if (AvailableActions == null) return; + + foreach (var action in AvailableActions) + { + if (action.Operation == FolderOperation.Seperator) + Items.Add(new MenuFlyoutSeparator()); + else + { + var menuFlyoutItem = new FolderOperationMenuFlyoutItem(action, (c) => MenuItemClicked(c)); + + Items.Add(menuFlyoutItem); + } + } + } + } +} diff --git a/Wino.Mail/MenuFlyouts/FolderOperationMenuFlyoutItem.cs b/Wino.Mail/MenuFlyouts/FolderOperationMenuFlyoutItem.cs new file mode 100644 index 00000000..c6b788c6 --- /dev/null +++ b/Wino.Mail/MenuFlyouts/FolderOperationMenuFlyoutItem.cs @@ -0,0 +1,12 @@ +using System; +using Wino.Core.Domain.Models.Folders; + +namespace Wino.MenuFlyouts +{ + public class FolderOperationMenuFlyoutItem : WinoOperationFlyoutItem + { + public FolderOperationMenuFlyoutItem(FolderOperationMenuItem operationMenuItem, Action clicked) : base(operationMenuItem, clicked) + { + } + } +} diff --git a/Wino.Mail/MenuFlyouts/MailOperationFlyout.cs b/Wino.Mail/MenuFlyouts/MailOperationFlyout.cs new file mode 100644 index 00000000..a5982881 --- /dev/null +++ b/Wino.Mail/MenuFlyouts/MailOperationFlyout.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Menus; + +namespace Wino.MenuFlyouts.Context +{ + public class MailOperationFlyout : WinoOperationFlyout + { + public MailOperationFlyout(IEnumerable availableActions, TaskCompletionSource completionSource) : base(availableActions, completionSource) + { + if (AvailableActions == null) return; + + foreach (var action in AvailableActions) + { + if (action.Operation == MailOperation.Seperator) + Items.Add(new MenuFlyoutSeparator()); + else + { + var menuFlyoutItem = new MailOperationMenuFlyoutItem(action, (c) => MenuItemClicked(c)); + + Items.Add(menuFlyoutItem); + } + } + } + } +} diff --git a/Wino.Mail/MenuFlyouts/MailOperationMenuFlyoutItem.cs b/Wino.Mail/MenuFlyouts/MailOperationMenuFlyoutItem.cs new file mode 100644 index 00000000..51c1953b --- /dev/null +++ b/Wino.Mail/MenuFlyouts/MailOperationMenuFlyoutItem.cs @@ -0,0 +1,12 @@ +using System; +using Wino.Core.Domain.Models.Menus; + +namespace Wino.MenuFlyouts.Context +{ + public class MailOperationMenuFlyoutItem : WinoOperationFlyoutItem + { + public MailOperationMenuFlyoutItem(MailOperationMenuItem operationMenuItem, Action clicked) : base(operationMenuItem, clicked) + { + } + } +} diff --git a/Wino.Mail/MenuFlyouts/MoveButtonFlyout.cs b/Wino.Mail/MenuFlyouts/MoveButtonFlyout.cs new file mode 100644 index 00000000..cee02589 --- /dev/null +++ b/Wino.Mail/MenuFlyouts/MoveButtonFlyout.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Windows.Foundation; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain.Entities; + +namespace Wino.MenuFlyouts +{ + public class MoveButtonMenuItemClickedEventArgs + { + public Guid ClickedFolderId { get; set; } + } + + public class MoveButtonFlyout : MenuFlyout + { + public event TypedEventHandler MenuItemClick; + public static readonly DependencyProperty FoldersProperty = DependencyProperty.Register(nameof(Folders), typeof(List), typeof(MoveButtonFlyout), new PropertyMetadata(null, new PropertyChangedCallback(OnFoldersChanged))); + + public List Folders + { + get { return (List)GetValue(FoldersProperty); } + set { SetValue(FoldersProperty, value); } + } + + private static void OnFoldersChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is MoveButtonFlyout menu) + { + menu.InitializeMenu(); + } + + + } + + private void InitializeMenu() + { + Dispose(); + + Items.Clear(); + + if (Folders == null || !Folders.Any()) + return; + + // TODO: Child folders. + + foreach (var item in Folders) + { + // We don't expect this, but it crashes startup. + // Just to be on the safe side. + if (item.FolderName != null) + { + var folderMenuItem = new MenuFlyoutItem() + { + Tag = item, + Text = item.FolderName + }; + + folderMenuItem.Click += MenuItemClicked; + + Items.Add(folderMenuItem); + } + } + } + + private void MenuItemClicked(object sender, RoutedEventArgs e) + { + var clickedFolder = (sender as MenuFlyoutItem).Tag as MailItemFolder; + + MenuItemClick?.Invoke(this, new MoveButtonMenuItemClickedEventArgs() + { + ClickedFolderId = clickedFolder.Id + }); + } + + public void Dispose() + { + foreach (var item in Items) + { + if (item is MenuFlyoutItem menuItem) + { + menuItem.Click -= MenuItemClicked; + } + } + } + } +} diff --git a/Wino.Mail/MenuFlyouts/RendererCommandBarItem.cs b/Wino.Mail/MenuFlyouts/RendererCommandBarItem.cs new file mode 100644 index 00000000..7e4b20d4 --- /dev/null +++ b/Wino.Mail/MenuFlyouts/RendererCommandBarItem.cs @@ -0,0 +1,39 @@ +using System; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Controls; +using Wino.Core.Domain.Enums; +using Wino.Helpers; + +namespace Wino.MenuFlyouts +{ + public class RendererCommandBarItem : AppBarButton, IDisposable + { + public MailOperation Operation { get; set; } + Action Clicked { get; set; } + + public RendererCommandBarItem(MailOperation operation, Action clicked) + { + Margin = new Thickness(6, 0, 6, 0); + CornerRadius = new CornerRadius(6); + + Operation = operation; + Clicked = clicked; + + Label = XamlHelpers.GetOperationString(operation); + Icon = new WinoFontIcon() { Icon = WinoIconGlyph.Archive }; + + Click += MenuClicked; + } + + private void MenuClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e) + { + Clicked(Operation); + } + + public void Dispose() + { + Click -= MenuClicked; + } + } +} diff --git a/Wino.Mail/MenuFlyouts/WinoOperationFlyout.cs b/Wino.Mail/MenuFlyouts/WinoOperationFlyout.cs new file mode 100644 index 00000000..6ecd9996 --- /dev/null +++ b/Wino.Mail/MenuFlyouts/WinoOperationFlyout.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Windows.UI.Xaml.Controls; + +namespace Wino.MenuFlyouts +{ + public class WinoOperationFlyout : MenuFlyout, IDisposable where TActionType : class + { + public TActionType ClickedOperation { get; set; } + + protected readonly IEnumerable AvailableActions; + + private readonly TaskCompletionSource _completionSource; + + public WinoOperationFlyout(IEnumerable availableActions, TaskCompletionSource completionSource) + { + _completionSource = completionSource; + + AvailableActions = availableActions; + + Closing += FlyoutClosing; + } + + private void FlyoutClosing(Windows.UI.Xaml.Controls.Primitives.FlyoutBase sender, Windows.UI.Xaml.Controls.Primitives.FlyoutBaseClosingEventArgs args) + { + Closing -= FlyoutClosing; + + _completionSource.TrySetResult(ClickedOperation); + } + + protected void MenuItemClicked(TActionType operation) + { + ClickedOperation = operation; + + Hide(); + } + + public void Dispose() + { + foreach (var item in Items) + { + if (item is IDisposable disposableItem) + { + disposableItem.Dispose(); + } + } + } + } +} diff --git a/Wino.Mail/MenuFlyouts/WinoOperationFlyoutItem.cs b/Wino.Mail/MenuFlyouts/WinoOperationFlyoutItem.cs new file mode 100644 index 00000000..ab0fdc04 --- /dev/null +++ b/Wino.Mail/MenuFlyouts/WinoOperationFlyoutItem.cs @@ -0,0 +1,58 @@ +using System; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Controls; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Folders; +using Wino.Core.Domain.Models.Menus; +using Wino.Helpers; + +namespace Wino.MenuFlyouts +{ + public class WinoOperationFlyoutItem : MenuFlyoutItem, IDisposable where TOperationMenuItem : IMenuOperation + { + private const double CustomHeight = 35; + + public TOperationMenuItem Operation { get; set; } + Action Clicked { get; set; } + + public WinoOperationFlyoutItem(TOperationMenuItem operationMenuItem, Action clicked) + { + Margin = new Thickness(4, 2, 4, 2); + CornerRadius = new CornerRadius(6, 6, 6, 6); + + MinHeight = CustomHeight; + + Operation = operationMenuItem; + IsEnabled = operationMenuItem.IsEnabled; + + if (Operation is FolderOperationMenuItem folderOperationMenuItem) + { + var internalOperation = folderOperationMenuItem.Operation; + + Icon = new WinoFontIcon() { Icon = XamlHelpers.GetPathGeometry(internalOperation) }; + Text = XamlHelpers.GetOperationString(internalOperation); + } + else if (Operation is MailOperationMenuItem mailOperationMenuItem) + { + var internalOperation = mailOperationMenuItem.Operation; + + Icon = new WinoFontIcon() { Icon = XamlHelpers.GetWinoIconGlyph(internalOperation) }; + Text = XamlHelpers.GetOperationString(internalOperation); + } + + Clicked = clicked; + Click += MenuClicked; + } + + private void MenuClicked(object sender, Windows.UI.Xaml.RoutedEventArgs e) + { + Clicked(Operation); + } + + public void Dispose() + { + Click -= MenuClicked; + } + } +} diff --git a/Wino.Mail/Package.appxmanifest b/Wino.Mail/Package.appxmanifest new file mode 100644 index 00000000..0ce91ba0 --- /dev/null +++ b/Wino.Mail/Package.appxmanifest @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + Wino Mail + Burak KÖSE + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Google Auth Protocol + + + + + + + EML\eml.png + + .eml + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Properties/AssemblyInfo.cs b/Wino.Mail/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..c05af972 --- /dev/null +++ b/Wino.Mail/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Wino")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Wino")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: ComVisible(false)] diff --git a/Wino.Mail/Properties/Default.rd.xml b/Wino.Mail/Properties/Default.rd.xml new file mode 100644 index 00000000..3f89a13a --- /dev/null +++ b/Wino.Mail/Properties/Default.rd.xml @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Selectors/AccountProviderViewModelTemplateSelector.cs b/Wino.Mail/Selectors/AccountProviderViewModelTemplateSelector.cs new file mode 100644 index 00000000..73b71c4e --- /dev/null +++ b/Wino.Mail/Selectors/AccountProviderViewModelTemplateSelector.cs @@ -0,0 +1,20 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Mail.ViewModels.Data; + +namespace Wino.Selectors +{ + public class AccountProviderViewModelTemplateSelector : DataTemplateSelector + { + public DataTemplate RootAccountTemplate { get; set; } + public DataTemplate MergedAccountTemplate { get; set; } + + protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) + { + if (item is MergedAccountProviderDetailViewModel) + return MergedAccountTemplate; + else + return RootAccountTemplate; + } + } +} diff --git a/Wino.Mail/Selectors/AppThemePreviewTemplateSelector.cs b/Wino.Mail/Selectors/AppThemePreviewTemplateSelector.cs new file mode 100644 index 00000000..f3afe31e --- /dev/null +++ b/Wino.Mail/Selectors/AppThemePreviewTemplateSelector.cs @@ -0,0 +1,25 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Core.UWP.Models.Personalization; + +namespace Wino.Selectors +{ + public class AppThemePreviewTemplateSelector : DataTemplateSelector + { + public DataTemplate SystemThemeTemplate { get; set; } + public DataTemplate PreDefinedThemeTemplate { get; set; } + public DataTemplate CustomAppTemplate { get; set; } + + protected override DataTemplate SelectTemplateCore(object item) + { + if (item is SystemAppTheme) + return SystemThemeTemplate; + else if (item is PreDefinedAppTheme) + return PreDefinedThemeTemplate; + else if (item is CustomAppTheme) + return CustomAppTemplate; + + return base.SelectTemplateCore(item); + } + } +} diff --git a/Wino.Mail/Selectors/FileAttachmentTypeSelector.cs b/Wino.Mail/Selectors/FileAttachmentTypeSelector.cs new file mode 100644 index 00000000..e117fcd2 --- /dev/null +++ b/Wino.Mail/Selectors/FileAttachmentTypeSelector.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain.Enums; + +namespace Wino.Selectors +{ + public class FileAttachmentTypeSelector : DataTemplateSelector + { + public DataTemplate None { get; set; } + public DataTemplate Executable { get; set; } + public DataTemplate Image { get; set; } + public DataTemplate Audio { get; set; } + public DataTemplate Video { get; set; } + public DataTemplate PDF { get; set; } + public DataTemplate HTML { get; set; } + public DataTemplate RarArchive { get; set; } + public DataTemplate Archive { get; set; } + public DataTemplate Other { get; set; } + + protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) + { + if (item == null) + return None; + + var type = (MailAttachmentType)item; + + switch (type) + { + case MailAttachmentType.None: + return None; + case MailAttachmentType.Executable: + return Executable; + case MailAttachmentType.Image: + return Image; + case MailAttachmentType.Audio: + return Audio; + case MailAttachmentType.Video: + return Video; + case MailAttachmentType.PDF: + return PDF; + case MailAttachmentType.HTML: + return HTML; + case MailAttachmentType.RarArchive: + return RarArchive; + case MailAttachmentType.Archive: + return Archive; + default: + return Other; + } + } + } +} diff --git a/Wino.Mail/Selectors/MailItemContainerStyleSelector.cs b/Wino.Mail/Selectors/MailItemContainerStyleSelector.cs new file mode 100644 index 00000000..17959c3f --- /dev/null +++ b/Wino.Mail/Selectors/MailItemContainerStyleSelector.cs @@ -0,0 +1,19 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Mail.ViewModels.Data; + +namespace Wino.Selectors +{ + public class MailItemContainerStyleSelector : StyleSelector + { + public Style Thread { get; set; } + + protected override Style SelectStyleCore(object item, DependencyObject container) + { + if (item is ThreadMailItemViewModel) + return Thread; + else + return base.SelectStyleCore(item, container); + } + } +} diff --git a/Wino.Mail/Selectors/MailItemDisplayModePreviewTemplateSelector.cs b/Wino.Mail/Selectors/MailItemDisplayModePreviewTemplateSelector.cs new file mode 100644 index 00000000..55949830 --- /dev/null +++ b/Wino.Mail/Selectors/MailItemDisplayModePreviewTemplateSelector.cs @@ -0,0 +1,34 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain.Enums; + +namespace Wino.Selectors +{ + /// + /// Template selector for previewing mail item display modes in Settings->Personalization page. + /// + public class MailItemDisplayModePreviewTemplateSelector : DataTemplateSelector + { + public DataTemplate CompactTemplate { get; set; } + public DataTemplate MediumTemplate { get; set; } + public DataTemplate SpaciousTemplate { get; set; } + + protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) + { + if (item is MailListDisplayMode mode) + { + switch (mode) + { + case MailListDisplayMode.Spacious: + return SpaciousTemplate; + case MailListDisplayMode.Medium: + return MediumTemplate; + case MailListDisplayMode.Compact: + return CompactTemplate; + } + } + + return base.SelectTemplateCore(item, container); + } + } +} diff --git a/Wino.Mail/Selectors/MailItemDisplaySelector.cs b/Wino.Mail/Selectors/MailItemDisplaySelector.cs new file mode 100644 index 00000000..fe7a3cf1 --- /dev/null +++ b/Wino.Mail/Selectors/MailItemDisplaySelector.cs @@ -0,0 +1,22 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Mail.ViewModels.Data; + +namespace Wino.Selectors +{ + public class MailItemDisplaySelector : DataTemplateSelector + { + public DataTemplate SingleMailItemTemplate { get; set; } + public DataTemplate ThreadMailItemTemplate { get; set; } + + protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) + { + if (item is MailItemViewModel) + return SingleMailItemTemplate; + else if (item is ThreadMailItemViewModel) + return ThreadMailItemTemplate; + + return base.SelectTemplateCore(item, container); + } + } +} diff --git a/Wino.Mail/Selectors/NavigationMenuTemplateSelector.cs b/Wino.Mail/Selectors/NavigationMenuTemplateSelector.cs new file mode 100644 index 00000000..f04e0bd5 --- /dev/null +++ b/Wino.Mail/Selectors/NavigationMenuTemplateSelector.cs @@ -0,0 +1,60 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Core.MenuItems; + +namespace Wino.Selectors +{ + public class NavigationMenuTemplateSelector : DataTemplateSelector + { + public DataTemplate MenuItemTemplate { get; set; } + public DataTemplate AccountManagementTemplate { get; set; } + public DataTemplate NestedAccountMenuTemplate { get; set; } + public DataTemplate ClickableAccountMenuTemplate { get; set; } + public DataTemplate MergedAccountTemplate { get; set; } + public DataTemplate MergedAccountFolderTemplate { get; set; } + public DataTemplate MergedAccountMoreExpansionItemTemplate { get; set; } + public DataTemplate FolderMenuTemplate { get; set; } + public DataTemplate SettingsItemTemplate { get; set; } + public DataTemplate MoreItemsFolderTemplate { get; set; } + public DataTemplate RatingItemTemplate { get; set; } + public DataTemplate CreateNewFolderTemplate { get; set; } + public DataTemplate SeperatorTemplate { get; set; } + public DataTemplate NewMailTemplate { get; set; } + public DataTemplate CategoryItemsTemplate { get; set; } + public DataTemplate FixAuthenticationIssueTemplate { get; set; } + public DataTemplate FixMissingFolderConfigTemplate { get; set; } + + protected override DataTemplate SelectTemplateCore(object item) + { + if (item is NewMailMenuItem) + return NewMailTemplate; + else if (item is SettingsItem) + return SettingsItemTemplate; + else if (item is SeperatorItem) + return SeperatorTemplate; + else if (item is AccountMenuItem accountMenuItem) + // Merged inbox account menu items must be nested. + return accountMenuItem.Parameter.MergedInboxId != null ? NestedAccountMenuTemplate : ClickableAccountMenuTemplate; + else if (item is ManageAccountsMenuItem) + return AccountManagementTemplate; + else if (item is RateMenuItem) + return RatingItemTemplate; + else if (item is MergedAccountMenuItem) + return MergedAccountTemplate; + else if (item is MergedAccountMoreFolderMenuItem) + return MergedAccountMoreExpansionItemTemplate; + else if (item is MergedAccountFolderMenuItem) + return MergedAccountFolderTemplate; + else if (item is FolderMenuItem) + return FolderMenuTemplate; + else if (item is FixAccountIssuesMenuItem fixAccountIssuesMenuItem) + return fixAccountIssuesMenuItem.Account.AttentionReason == Core.Domain.Enums.AccountAttentionReason.MissingSystemFolderConfiguration + ? FixMissingFolderConfigTemplate : FixAuthenticationIssueTemplate; + else + { + var type = item.GetType(); + return null; + } + } + } +} diff --git a/Wino.Mail/Selectors/RendererCommandBarItemTemplateSelector.cs b/Wino.Mail/Selectors/RendererCommandBarItemTemplateSelector.cs new file mode 100644 index 00000000..2ad2dd67 --- /dev/null +++ b/Wino.Mail/Selectors/RendererCommandBarItemTemplateSelector.cs @@ -0,0 +1,84 @@ +using Windows.Graphics.Printing.OptionDetails; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Menus; + +namespace Wino.Selectors +{ + public class RendererCommandBarItemTemplateSelector : DataTemplateSelector + { + public DataTemplate Reply { get; set; } + public DataTemplate ReplyAll { get; set; } + public DataTemplate Archive { get; set; } + public DataTemplate Unarchive { get; set; } + public DataTemplate SetFlag { get; set; } + public DataTemplate ClearFlag { get; set; } + public DataTemplate MarkAsRead { get; set; } + public DataTemplate MarkAsUnread { get; set; } + public DataTemplate Delete { get; set; } + public DataTemplate Move { get; set; } + public DataTemplate MoveToJunk { get; set; } + public DataTemplate SaveAs { get; set; } + public DataTemplate Zoom { get; set; } + public DataTemplate Forward { get; set; } + public DataTemplate DarkEditor { get; set; } + public DataTemplate LightEditor { get; set; } + public DataTemplate SeperatorTemplate { get; set; } + public DataTemplate Find { get; set; } + public DataTemplate Print { get; set; } + protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) + { + if (item is MailOperationMenuItem mailOperationItem) + { + switch (mailOperationItem.Operation) + { + case MailOperation.None: + break; + case MailOperation.Archive: + return Archive; + case MailOperation.UnArchive: + return Unarchive; + case MailOperation.SoftDelete: + return Delete; + case MailOperation.Move: + return Move; + case MailOperation.MoveToJunk: + return MoveToJunk; + case MailOperation.SetFlag: + return SetFlag; + case MailOperation.ClearFlag: + return ClearFlag; + case MailOperation.MarkAsRead: + return MarkAsRead; + case MailOperation.MarkAsUnread: + return MarkAsUnread; + case MailOperation.Reply: + return Reply; + case MailOperation.ReplyAll: + return ReplyAll; + case MailOperation.Zoom: + return Zoom; + case MailOperation.SaveAs: + return SaveAs; + case MailOperation.Find: + return Find; + case MailOperation.Forward: + return Forward; + case MailOperation.DarkEditor: + return DarkEditor; + case MailOperation.LightEditor: + return LightEditor; + case MailOperation.Seperator: + return SeperatorTemplate; + case MailOperation.Print: + return Print; + default: + break; + } + } + + return base.SelectTemplateCore(item, container); + } + } +} diff --git a/Wino.Mail/Services/ApplicationResourceManager.cs b/Wino.Mail/Services/ApplicationResourceManager.cs new file mode 100644 index 00000000..24ef610b --- /dev/null +++ b/Wino.Mail/Services/ApplicationResourceManager.cs @@ -0,0 +1,23 @@ +using System.Linq; +using Windows.UI.Xaml; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Services +{ + public class ApplicationResourceManager : IApplicationResourceManager + { + public void AddResource(ResourceDictionary resource) + => App.Current.Resources.MergedDictionaries.Add(resource); + public void RemoveResource(ResourceDictionary resource) + => App.Current.Resources.MergedDictionaries.Remove(resource); + + public bool ContainsResourceKey(string resourceKey) + => App.Current.Resources.ContainsKey(resourceKey); + + public ResourceDictionary GetLastResource() + => App.Current.Resources.MergedDictionaries.LastOrDefault(); + + public void ReplaceResource(string resourceKey, object resource) + => App.Current.Resources[resourceKey] = resource; + } +} diff --git a/Wino.Mail/Services/DialogService.cs b/Wino.Mail/Services/DialogService.cs new file mode 100644 index 00000000..8f134a09 --- /dev/null +++ b/Wino.Mail/Services/DialogService.cs @@ -0,0 +1,335 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.Messaging; +using Microsoft.Toolkit.Uwp.Helpers; +using Serilog; +using Windows.Storage; +using Windows.Storage.Pickers; +using Windows.UI.Xaml.Controls; +using Wino.Core.Domain; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Folders; +using Wino.Core.Domain.Models.Synchronization; +using Wino.Core.Messages.Shell; +using Wino.Core.Messages.Synchronization; +using Wino.Core.Requests; +using Wino.Core.UWP.Extensions; +using Wino.Dialogs; + +namespace Wino.Services +{ + public class DialogService : IDialogService + { + private SemaphoreSlim _presentationSemaphore = new SemaphoreSlim(1); + + private readonly IThemeService _themeService; + + public DialogService(IThemeService themeService) + { + _themeService = themeService; + } + + public void ShowNotSupportedMessage() + { + InfoBarMessage(Translator.Info_UnsupportedFunctionalityTitle, Translator.Info_UnsupportedFunctionalityDescription, InfoBarMessageType.Error); + } + + public async Task ShowMessageAsync(string message, string title) + { + var dialog = new WinoMessageDialog() + { + RequestedTheme = _themeService.RootTheme.ToWindowsElementTheme() + }; + + await HandleDialogPresentation(() => dialog.ShowDialogAsync(title, message)); + } + + /// + /// Waits for PopupRoot to be available before presenting the dialog and returns the result after presentation. + /// + /// Dialog to present and wait for closing. + /// Dialog result from WinRT. + private async Task HandleDialogPresentationAsync(ContentDialog dialog) + { + await _presentationSemaphore.WaitAsync(); + + try + { + return await dialog.ShowAsync(); + } + catch (Exception ex) + { + Log.Error(ex, $"Handling dialog service failed. Dialog was {dialog.GetType().Name}"); + } + finally + { + _presentationSemaphore.Release(); + } + + return ContentDialogResult.None; + } + + /// + /// Waits for PopupRoot to be available before executing the given Task that returns customized result. + /// + /// Task that presents the dialog and returns result. + /// Dialog result from the custom dialog. + private async Task HandleDialogPresentation(Func> executionTask) + { + await _presentationSemaphore.WaitAsync(); + + try + { + return await executionTask(); + } + catch (Exception ex) + { + Log.Error(ex, "Handling dialog service failed."); + } + finally + { + _presentationSemaphore.Release(); + } + + return false; + } + + public async Task ShowConfirmationDialogAsync(string question, string title, string confirmationButtonTitle) + { + var dialog = new ConfirmationDialog() + { + RequestedTheme = _themeService.RootTheme.ToWindowsElementTheme() + }; + + return await HandleDialogPresentation(() => dialog.ShowDialogAsync(title, question, confirmationButtonTitle)); + } + + public async Task> ShowNewAccountMailProviderDialogAsync(List availableProviders) + { + var dialog = new NewAccountDialog + { + Providers = availableProviders, + RequestedTheme = _themeService.RootTheme.ToWindowsElementTheme() + }; + + await HandleDialogPresentationAsync(dialog); + + return dialog.AccountInformationTuple; + } + + public IAccountCreationDialog GetAccountCreationDialog(MailProviderType type) + { + IAccountCreationDialog dialog = null; + + if (type == MailProviderType.IMAP4) + { + dialog = new NewImapSetupDialog + { + RequestedTheme = _themeService.RootTheme.ToWindowsElementTheme() + }; + } + else + { + dialog = new AccountCreationDialog + { + RequestedTheme = _themeService.RootTheme.ToWindowsElementTheme() + }; + } + + return dialog; + } + + public void InfoBarMessage(string title, string message, InfoBarMessageType messageType) + => WeakReferenceMessenger.Default.Send(new InfoBarMessageRequested(messageType, title, message)); + + public void InfoBarMessage(string title, string message, InfoBarMessageType messageType, string actionButtonText, Action action) + => WeakReferenceMessenger.Default.Send(new InfoBarMessageRequested(messageType, title, message, actionButtonText, action)); + + public async Task ShowTextInputDialogAsync(string currentInput, string dialogTitle, string dialogDescription) + { + var inputDialog = new TextInputDialog() + { + CurrentInput = currentInput, + RequestedTheme = _themeService.RootTheme.ToWindowsElementTheme(), + Title = dialogTitle + }; + + inputDialog.SetDescription(dialogDescription); + + await HandleDialogPresentationAsync(inputDialog); + + if (inputDialog.HasInput.GetValueOrDefault() && !currentInput.Equals(inputDialog.CurrentInput)) + return inputDialog.CurrentInput; + + return string.Empty; + } + + public async Task PickWindowsFolderAsync() + { + var picker = new FolderPicker + { + SuggestedStartLocation = PickerLocationId.DocumentsLibrary + }; + + picker.FileTypeFilter.Add("*"); + + var pickedFolder = await picker.PickSingleFolderAsync(); + + if (pickedFolder != null) + { + Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.AddOrReplace("FolderPickerToken", pickedFolder); + + return pickedFolder.Path; + } + + return string.Empty; + } + + public async Task ShowEditAccountDialogAsync(MailAccount account) + { + var editAccountDialog = new AccountEditDialog(account) + { + RequestedTheme = _themeService.RootTheme.ToWindowsElementTheme() + }; + + await HandleDialogPresentationAsync(editAccountDialog); + + return editAccountDialog.IsSaved ? editAccountDialog.Account : null; + } + + public async Task ShowRatingDialogAsync() + { + var storeDialog = new StoreRatingDialog() + { + RequestedTheme = _themeService.RootTheme.ToWindowsElementTheme() + }; + + await HandleDialogPresentationAsync(storeDialog); + + return storeDialog; + } + + public async Task HandleSystemFolderConfigurationDialogAsync(Guid accountId, IFolderService folderService) + { + try + { + var configurableFolder = await folderService.GetFoldersAsync(accountId); + + var systemFolderConfigurationDialog = new SystemFolderConfigurationDialog(configurableFolder) + { + RequestedTheme = _themeService.RootTheme.ToWindowsElementTheme() + }; + + await HandleDialogPresentationAsync(systemFolderConfigurationDialog); + + var configuration = systemFolderConfigurationDialog.Configuration; + + if (configuration != null) + { + var updatedAccount = await folderService.UpdateSystemFolderConfigurationAsync(accountId, configuration); + + // Update account menu item and force re-synchronization. + WeakReferenceMessenger.Default.Send(new AccountUpdatedMessage(updatedAccount)); + + var options = new SynchronizationOptions() + { + AccountId = updatedAccount.Id, + Type = SynchronizationType.Full, + }; + + WeakReferenceMessenger.Default.Send(new NewSynchronizationRequested(options)); + } + + if (configuration != null) + { + InfoBarMessage(Translator.SystemFolderConfigSetupSuccess_Title, Translator.SystemFolderConfigSetupSuccess_Message, InfoBarMessageType.Success); + } + } + catch (Exception ex) + { + InfoBarMessage(Translator.Error_FailedToSetupSystemFolders_Title, ex.Message, InfoBarMessageType.Error); + } + } + + public async Task ShowMoveMailFolderDialogAsync(List availableFolders) + { + var moveDialog = new MoveMailDialog(availableFolders) + { + RequestedTheme = _themeService.RootTheme.ToWindowsElementTheme() + }; + + await HandleDialogPresentationAsync(moveDialog); + + return moveDialog.SelectedFolder; + } + + public async Task PickFolderAsync(Guid accountId, PickFolderReason reason, IFolderService folderService) + { + var allFolders = await folderService.GetFolderStructureForAccountAsync(accountId, true); + + return await ShowMoveMailFolderDialogAsync(allFolders.Folders); + } + + public async Task ShowCustomThemeBuilderDialogAsync() + { + var themeBuilderDialog = new CustomThemeBuilderDialog() + { + RequestedTheme = _themeService.RootTheme.ToWindowsElementTheme() + }; + + var dialogResult = await HandleDialogPresentationAsync(themeBuilderDialog); + + return dialogResult == ContentDialogResult.Primary; + } + + private async Task PickFileAsync(params object[] typeFilters) + { + var picker = new FileOpenPicker + { + ViewMode = PickerViewMode.Thumbnail + }; + + foreach (var filter in typeFilters) + { + picker.FileTypeFilter.Add(filter.ToString()); + } + + var file = await picker.PickSingleFileAsync(); + + if (file == null) return null; + + Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.AddOrReplace("FilePickerPath", file); + + return file; + } + + public async Task PickWindowsFileContentAsync(params object[] typeFilters) + { + var file = await PickFileAsync(typeFilters); + + if (file == null) return Array.Empty(); + + return await file.ReadBytesAsync(); + } + + public Task ShowHardDeleteConfirmationAsync() => ShowConfirmationDialogAsync(Translator.DialogMessage_HardDeleteConfirmationMessage, Translator.DialogMessage_HardDeleteConfirmationTitle, Translator.Buttons_Yes); + + public async Task ShowAccountPickerDialogAsync(List availableAccounts) + { + var accountPicker = new AccountPickerDialog(availableAccounts) + { + RequestedTheme = _themeService.RootTheme.ToWindowsElementTheme() + }; + + await HandleDialogPresentationAsync(accountPicker); + + return accountPicker.PickedAccount; + } + + + } +} diff --git a/Wino.Mail/Services/LaunchProtocolService.cs b/Wino.Mail/Services/LaunchProtocolService.cs new file mode 100644 index 00000000..40a699ed --- /dev/null +++ b/Wino.Mail/Services/LaunchProtocolService.cs @@ -0,0 +1,11 @@ +using System.Collections.Specialized; +using Wino.Core.Domain.Interfaces; + +namespace Wino.Core.UWP.Services +{ + public class LaunchProtocolService : ILaunchProtocolService + { + public object LaunchParameter { get; set; } + public NameValueCollection MailtoParameters { get; set; } + } +} diff --git a/Wino.Mail/Services/PreferencesService.cs b/Wino.Mail/Services/PreferencesService.cs new file mode 100644 index 00000000..7b2fe0fa --- /dev/null +++ b/Wino.Mail/Services/PreferencesService.cs @@ -0,0 +1,193 @@ +using System; +using System.ComponentModel; +using System.Diagnostics; +using CommunityToolkit.Mvvm.ComponentModel; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Reader; +using Wino.Core.Services; + +namespace Wino.Services +{ + public class PreferencesService : ObservableObject, IPreferencesService + { + private readonly IConfigurationService _configurationService; + + public event EventHandler PreferenceChanged; + + public PreferencesService(IConfigurationService configurationService) + { + _configurationService = configurationService; + } + + protected override void OnPropertyChanged(PropertyChangedEventArgs e) + { + base.OnPropertyChanged(e); + + PreferenceChanged?.Invoke(this, e.PropertyName); + } + + private void SaveProperty(string propertyName, object value) => _configurationService.Set(propertyName, value); + + private void SetPropertyAndSave(string propertyName, object value) + { + _configurationService.Set(propertyName, value); + + OnPropertyChanged(propertyName); + Debug.WriteLine($"PreferencesService -> {propertyName}:{value?.ToString()}"); + } + + public MailRenderingOptions GetRenderingOptions() + => new MailRenderingOptions() { LoadImages = RenderImages, LoadStyles = RenderStyles }; + + public MailListDisplayMode MailItemDisplayMode + { + get => _configurationService.Get(nameof(MailItemDisplayMode), MailListDisplayMode.Spacious); + set => SetPropertyAndSave(nameof(MailItemDisplayMode), value); + } + + public bool IsSemanticZoomEnabled + { + get => _configurationService.Get(nameof(IsSemanticZoomEnabled), true); + set => SetPropertyAndSave(nameof(IsSemanticZoomEnabled), value); + } + + public bool IsHardDeleteProtectionEnabled + { + get => _configurationService.Get(nameof(IsHardDeleteProtectionEnabled), true); + set => SetPropertyAndSave(nameof(IsHardDeleteProtectionEnabled), value); + } + + public bool IsThreadingEnabled + { + get => _configurationService.Get(nameof(IsThreadingEnabled), true); + set => SetPropertyAndSave(nameof(IsThreadingEnabled), value); + } + + public bool IsShowSenderPicturesEnabled + { + get => _configurationService.Get(nameof(IsShowSenderPicturesEnabled), true); + set => SetPropertyAndSave(nameof(IsShowSenderPicturesEnabled), value); + } + + public bool IsShowPreviewEnabled + { + get => _configurationService.Get(nameof(IsShowPreviewEnabled), true); + set => SetPropertyAndSave(nameof(IsShowPreviewEnabled), value); + } + + public bool RenderStyles + { + get => _configurationService.Get(nameof(RenderStyles), true); + set => SetPropertyAndSave(nameof(RenderStyles), value); + } + + public bool RenderImages + { + get => _configurationService.Get(nameof(RenderImages), true); + set => SetPropertyAndSave(nameof(RenderImages), value); + } + + public bool Prefer24HourTimeFormat + { + get => _configurationService.Get(nameof(Prefer24HourTimeFormat), false); + set => SetPropertyAndSave(nameof(Prefer24HourTimeFormat), value); + } + + public MailMarkAsOption MarkAsPreference + { + get => _configurationService.Get(nameof(MarkAsPreference), MailMarkAsOption.WhenSelected); + set => SetPropertyAndSave(nameof(MarkAsPreference), value); + } + + public int MarkAsDelay + { + get => _configurationService.Get(nameof(MarkAsDelay), 5); + set => SetPropertyAndSave(nameof(MarkAsDelay), value); + } + + public MailOperation RightSwipeOperation + { + get => _configurationService.Get(nameof(RightSwipeOperation), MailOperation.MarkAsRead); + set => SetPropertyAndSave(nameof(RightSwipeOperation), value); + } + + public MailOperation LeftSwipeOperation + { + get => _configurationService.Get(nameof(LeftSwipeOperation), MailOperation.SoftDelete); + set => SetPropertyAndSave(nameof(LeftSwipeOperation), value); + } + + public bool IsHoverActionsEnabled + { + get => _configurationService.Get(nameof(IsHoverActionsEnabled), true); + set => SetPropertyAndSave(nameof(IsHoverActionsEnabled), value); + } + + public MailOperation LeftHoverAction + { + get => _configurationService.Get(nameof(LeftHoverAction), MailOperation.Archive); + set => SetPropertyAndSave(nameof(LeftHoverAction), value); + } + + public MailOperation CenterHoverAction + { + get => _configurationService.Get(nameof(CenterHoverAction), MailOperation.SoftDelete); + set => SetPropertyAndSave(nameof(CenterHoverAction), value); + } + + public MailOperation RightHoverAction + { + get => _configurationService.Get(nameof(RightHoverAction), MailOperation.SetFlag); + set => SetPropertyAndSave(nameof(RightHoverAction), value); + } + + public bool IsLoggingEnabled + { + get => _configurationService.Get(nameof(IsLoggingEnabled), true); + set => SetPropertyAndSave(nameof(IsLoggingEnabled), value); + } + + public bool IsMailkitProtocolLoggerEnabled + { + get => _configurationService.Get(nameof(IsMailkitProtocolLoggerEnabled), false); + set => SetPropertyAndSave(nameof(IsMailkitProtocolLoggerEnabled), value); + } + + public Guid? StartupEntityId + { + get => _configurationService.Get(nameof(StartupEntityId), null); + set => SaveProperty(propertyName: nameof(StartupEntityId), value); + } + + public AppLanguage CurrentLanguage + { + get => _configurationService.Get(nameof(CurrentLanguage), TranslationService.DefaultAppLanguage); + set => SaveProperty(propertyName: nameof(CurrentLanguage), value); + } + + public ReaderFont ReaderFont + { + get => _configurationService.Get(nameof(ReaderFont), ReaderFont.Calibri); + set => SaveProperty(propertyName: nameof(ReaderFont), value); + } + + public int ReaderFontSize + { + get => _configurationService.Get(nameof(ReaderFontSize), 14); + set => SaveProperty(propertyName: nameof(ReaderFontSize), value); + } + + public bool IsNavigationPaneOpened + { + get => _configurationService.Get(nameof(IsNavigationPaneOpened), true); + set => SaveProperty(propertyName: nameof(IsNavigationPaneOpened), value); + } + + public bool AutoSelectNextItem + { + get => _configurationService.Get(nameof(AutoSelectNextItem), true); + set => SaveProperty(propertyName: nameof(AutoSelectNextItem), value); + } + } +} diff --git a/Wino.Mail/Services/StatePersistenceService.cs b/Wino.Mail/Services/StatePersistenceService.cs new file mode 100644 index 00000000..4c995ef9 --- /dev/null +++ b/Wino.Mail/Services/StatePersistenceService.cs @@ -0,0 +1,132 @@ +using System; +using System.ComponentModel; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Messaging; +using Microsoft.AppCenter.Crashes; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Messages.Shell; + +namespace Wino.Services +{ + public class StatePersistenceService : ObservableObject, IStatePersistanceService + { + public event EventHandler StatePropertyChanged; + + private const string OpenPaneLengthKey = nameof(OpenPaneLengthKey); + private const string MailListPaneLengthKey = nameof(MailListPaneLengthKey); + + private readonly IConfigurationService _configurationService; + + public StatePersistenceService(IConfigurationService configurationService) + { + _configurationService = configurationService; + + openPaneLength = _configurationService.Get(OpenPaneLengthKey, 320d); + _mailListPaneLength = _configurationService.Get(MailListPaneLengthKey, 420d); + + PropertyChanged += ServicePropertyChanged; + } + + private void ServicePropertyChanged(object sender, PropertyChangedEventArgs e) => StatePropertyChanged?.Invoke(this, e.PropertyName); + + public bool IsBackButtonVisible => IsReadingMail && IsReaderNarrowed; + + private bool isReadingMail; + + public bool IsReadingMail + { + get => isReadingMail; + set + { + if (SetProperty(ref isReadingMail, value)) + { + OnPropertyChanged(nameof(IsBackButtonVisible)); + WeakReferenceMessenger.Default.Send(new ShellStateUpdated()); + } + } + } + + private bool shouldShiftMailRenderingDesign; + + public bool ShouldShiftMailRenderingDesign + { + get { return shouldShiftMailRenderingDesign; } + set { shouldShiftMailRenderingDesign = value; } + } + + private bool isReaderNarrowed; + + public bool IsReaderNarrowed + { + get => isReaderNarrowed; + set + { + if (SetProperty(ref isReaderNarrowed, value)) + { + OnPropertyChanged(nameof(IsBackButtonVisible)); + WeakReferenceMessenger.Default.Send(new ShellStateUpdated()); + } + } + } + + private string coreWindowTitle; + + public string CoreWindowTitle + { + get => coreWindowTitle; + set + { + if (SetProperty(ref coreWindowTitle, value)) + { + UpdateAppCoreWindowTitle(); + } + } + } + + #region Settings + + private double openPaneLength; + public double OpenPaneLength + { + get => openPaneLength; + set + { + if (SetProperty(ref openPaneLength, value)) + { + _configurationService.Set(OpenPaneLengthKey, value); + } + } + } + + private double _mailListPaneLength; + public double MailListPaneLength + { + get => _mailListPaneLength; + set + { + if (SetProperty(ref _mailListPaneLength, value)) + { + _configurationService.Set(MailListPaneLengthKey, value); + } + } + + } + + #endregion + + private void UpdateAppCoreWindowTitle() + { + try + { + var appView = Windows.UI.ViewManagement.ApplicationView.GetForCurrentView(); + + if (appView != null) + appView.Title = CoreWindowTitle; + } + catch (System.Exception ex) + { + Crashes.TrackError(ex); + } + } + } +} diff --git a/Wino.Mail/Services/ToastActivationService.cs b/Wino.Mail/Services/ToastActivationService.cs new file mode 100644 index 00000000..57eaaf05 --- /dev/null +++ b/Wino.Mail/Services/ToastActivationService.cs @@ -0,0 +1,20 @@ +using Wino.Core.Domain.Interfaces; + +namespace Wino.Services +{ + public class ToastActivationService + { + private readonly IMailService _mailService; + private readonly IWinoRequestDelegator _winoRequestDelegator; + private readonly INativeAppService _nativeAppService; + + public ToastActivationService(IMailService mailService, + IWinoRequestDelegator winoRequestDelegator, + INativeAppService nativeAppService) + { + _mailService = mailService; + _winoRequestDelegator = winoRequestDelegator; + _nativeAppService = nativeAppService; + } + } +} diff --git a/Wino.Mail/Services/WinoNavigationService.cs b/Wino.Mail/Services/WinoNavigationService.cs new file mode 100644 index 00000000..cadd6b02 --- /dev/null +++ b/Wino.Mail/Services/WinoNavigationService.cs @@ -0,0 +1,163 @@ +using System; +using CommunityToolkit.Mvvm.Messaging; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media.Animation; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.MailItem; +using Wino.Core.Domain.Models.Navigation; +using Wino.Helpers; +using Wino.Mail.ViewModels.Data; +using Wino.Mail.ViewModels.Messages; +using Wino.Views; +using Wino.Views.Account; +using Wino.Views.Settings; + +namespace Wino.Services +{ + public class WinoNavigationService : IWinoNavigationService + { + private Frame GetCoreFrame(NavigationReferenceFrame frameType) + { + if (Window.Current.Content is Frame appFrame && appFrame.Content is AppShell shellPage) + return WinoVisualTreeHelper.GetChildObject(shellPage, frameType.ToString()); + + return null; + } + + private Type GetCurrentFrameType(ref Frame _frame) + { + if (_frame != null && _frame.Content != null) + return _frame.Content.GetType(); + else + { + return null; + } + } + + private Type GetPageType(WinoPage winoPage) + { + switch (winoPage) + { + case WinoPage.None: + return null; + case WinoPage.IdlePage: + return typeof(IdlePage); + case WinoPage.AccountDetailsPage: + return typeof(AccountDetailsPage); + case WinoPage.MergedAccountDetailsPage: + return typeof(MergedAccountDetailsPage); + case WinoPage.AccountManagementPage: + return typeof(NewAccountManagementPage); + case WinoPage.SignatureManagementPage: + return typeof(SignatureManagementPage); + case WinoPage.AboutPage: + return typeof(AboutPage); + case WinoPage.PersonalizationPage: + return typeof(PersonalizationPage); + case WinoPage.MessageListPage: + return typeof(MessageListPage); + case WinoPage.ReadingPanePage: + return typeof(ReadingPanePage); + case WinoPage.MailRenderingPage: + return typeof(MailRenderingPage); + case WinoPage.ComposePage: + return typeof(ComposePage); + case WinoPage.MailListPage: + return typeof(MailListPage); + case WinoPage.SettingsPage: + return typeof(SettingsPage); + case WinoPage.WelcomePage: + return typeof(WelcomePage); + case WinoPage.SettingOptionsPage: + return typeof(SettingOptionsPage); + default: + return null; + } + } + + public bool Navigate(WinoPage page, + object parameter = null, + NavigationReferenceFrame frame = NavigationReferenceFrame.ShellFrame, + NavigationTransitionType transition = NavigationTransitionType.None) + { + var pageType = GetPageType(page); + Frame shellFrame = GetCoreFrame(NavigationReferenceFrame.ShellFrame); + + if (shellFrame != null) + { + var currentFrameType = GetCurrentFrameType(ref shellFrame); + + bool isMailListingPageActive = currentFrameType != null && currentFrameType == typeof(MailListPage); + + // Active page is mail list page and we are refreshing the folder. + if (isMailListingPageActive && currentFrameType == pageType && parameter is NavigateMailFolderEventArgs folderNavigationArgs) + { + // No need for new navigation, just refresh the folder. + WeakReferenceMessenger.Default.Send(new ActiveMailFolderChangedEvent(folderNavigationArgs.BaseFolderMenuItem, folderNavigationArgs.FolderInitLoadAwaitTask)); + + return true; + } + + var transitionInfo = GetNavigationTransitionInfo(transition); + + // This page must be opened in the Frame placed in MailListingPage. + if (isMailListingPageActive && frame == NavigationReferenceFrame.RenderingFrame) + { + var listingFrame = GetCoreFrame(NavigationReferenceFrame.RenderingFrame); + + if (listingFrame == null) return false; + + listingFrame.Navigate(pageType, parameter, transitionInfo); + + return true; + } + + if ((currentFrameType != null && currentFrameType != pageType) || currentFrameType == null) + { + return shellFrame.Navigate(pageType, parameter, transitionInfo); + } + } + + return false; + } + + private NavigationTransitionInfo GetNavigationTransitionInfo(NavigationTransitionType transition) + { + return transition switch + { + NavigationTransitionType.DrillIn => new DrillInNavigationTransitionInfo(), + _ => new SuppressNavigationTransitionInfo(), + }; + } + + public void NavigateCompose(IMailItem mailItem, NavigationTransitionType transition = NavigationTransitionType.None) + => Navigate(WinoPage.ComposePage, mailItem, NavigationReferenceFrame.RenderingFrame, transition); + + // Standalone EML viewer. + public void NavigateRendering(MimeMessageInformation mimeMessageInformation, NavigationTransitionType transition = NavigationTransitionType.None) + { + if (mimeMessageInformation == null) + throw new ArgumentException("MimeMessage cannot be null."); + + Navigate(WinoPage.MailRenderingPage, mimeMessageInformation, NavigationReferenceFrame.RenderingFrame, transition); + } + + // Mail item view model clicked handler. + public void NavigateRendering(IMailItem mailItem, NavigationTransitionType transition = NavigationTransitionType.None) + { + if (mailItem is MailItemViewModel mailItemViewModel) + Navigate(WinoPage.MailRenderingPage, mailItemViewModel, NavigationReferenceFrame.RenderingFrame, transition); + else + throw new ArgumentException("MailItem must be of type MailItemViewModel."); + } + + public void NavigateWelcomePage() => Navigate(WinoPage.WelcomePage); + + public void NavigateManageAccounts() => Navigate(WinoPage.AccountManagementPage); + + public void NavigateFolder(NavigateMailFolderEventArgs args) + => Navigate(WinoPage.MailListPage, args, NavigationReferenceFrame.ShellFrame); + } +} diff --git a/Wino.Mail/Styles/Colors.xaml b/Wino.Mail/Styles/Colors.xaml new file mode 100644 index 00000000..f33c0dba --- /dev/null +++ b/Wino.Mail/Styles/Colors.xaml @@ -0,0 +1,19 @@ + + + #e74c3c + #e74c3c + #ff7675 + + #1abc9c + + + + #fdcb6e + #636e72 + + + #fdcb6e + #2d3436 + + + diff --git a/Wino.Mail/Styles/CommandBarItems.xaml b/Wino.Mail/Styles/CommandBarItems.xaml new file mode 100644 index 00000000..dd596d20 --- /dev/null +++ b/Wino.Mail/Styles/CommandBarItems.xaml @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Styles/CommandBarItems.xaml.cs b/Wino.Mail/Styles/CommandBarItems.xaml.cs new file mode 100644 index 00000000..a2695111 --- /dev/null +++ b/Wino.Mail/Styles/CommandBarItems.xaml.cs @@ -0,0 +1,12 @@ +using Windows.UI.Xaml; + +namespace Wino.Styles +{ + public partial class CommandBarItems : ResourceDictionary + { + public CommandBarItems() + { + InitializeComponent(); + } + } +} diff --git a/Wino.Mail/Styles/ContentPresenters.xaml b/Wino.Mail/Styles/ContentPresenters.xaml new file mode 100644 index 00000000..7e1293aa --- /dev/null +++ b/Wino.Mail/Styles/ContentPresenters.xaml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Styles/Converters.xaml b/Wino.Mail/Styles/Converters.xaml new file mode 100644 index 00000000..0ef5e1cb --- /dev/null +++ b/Wino.Mail/Styles/Converters.xaml @@ -0,0 +1,7 @@ + + + + diff --git a/Wino.Mail/Styles/FontIcons.xaml b/Wino.Mail/Styles/FontIcons.xaml new file mode 100644 index 00000000..b30f9def --- /dev/null +++ b/Wino.Mail/Styles/FontIcons.xaml @@ -0,0 +1,29 @@ + + + + M128.5,799.5C128.5,778.5 132.5,758.25 140.5,738.75C148.5,719.25 160,702 175,687L525,337C531.333,330.667 538.833,327.5 547.5,327.5C556.167,327.5 563.667,330.667 570,337C576.333,343.333 579.5,350.833 579.5,359.5C579.5,368.167 576.333,375.667 570,382L220,732C211.333,740.667 204.5,750.917 199.5,762.75C194.5,774.583 192,786.833 192,799.5C192,812.833 194.5,825.333 199.5,837C204.5,848.667 211.333,858.833 220,867.5C228.667,876.167 238.833,883 250.5,888C262.167,893 274.667,895.5 288,895.5C300.667,895.5 312.833,893.167 324.5,888.5C336.167,883.833 346.5,877 355.5,868L775,456.5C793,438.833 807,418.083 817,394.25C827,370.417 832,346 832,321C832,295.667 827.083,271.333 817.25,248C807.417,224.667 794,204.083 777,186.25C760,168.417 740.083,154.25 717.25,143.75C694.417,133.25 670,128 644,128C617.333,128 592.083,132.583 568.25,141.75C544.417,150.917 523,165 504,184L120,568C113.667,574.333 106.167,577.5 97.5,577.5C88.8333,577.5 81.3333,574.333 75,568C68.6667,561.667 65.5,554.167 65.5,545.5C65.5,536.833 68.6667,529.333 75,523L459,139C472.333,125.667 485.75,114.25 499.25,104.75C512.75,95.25 526.917,87.5 541.75,81.5C556.583,75.5 572.083,71.0834 588.25,68.25C604.417,65.4167 621.833,64.0001 640.5,64C675.833,64.0001 709,70.8334 740,84.5C771,98.1667 798.083,116.667 821.25,140C844.417,163.333 862.667,190.583 876,221.75C889.333,252.917 896,286.167 896,321.5C896,338.167 894.25,354.75 890.75,371.25C887.25,387.75 882.25,403.833 875.75,419.5C869.25,435.167 861.25,450.083 851.75,464.25C842.25,478.417 831.667,491.167 820,502.5L400.5,914C385.167,929 367.75,940.333 348.25,948C328.75,955.667 308.333,959.5 287,959.5C265,959.5 244.333,955.25 225,946.75C205.667,938.25 188.917,926.75 174.75,912.25C160.583,897.75 149.333,880.833 141,861.5C132.667,842.167 128.5,821.5 128.5,799.5Z + M896,392L896,898.5C896,915.167 892.583,931.083 885.75,946.25C878.917,961.417 869.75,974.75 858.25,986.25C846.75,997.75 833.417,1006.92 818.25,1013.75C803.083,1020.58 787.167,1024 770.5,1024L517,1024C528.667,1014.67 539.833,1004.67 550.5,994C561.167,983.333 571,972 580,960L768,960C777,960 785.333,958.333 793,955C800.667,951.667 807.417,947.083 813.25,941.25C819.083,935.417 823.667,928.667 827,921C830.333,913.333 832,905 832,896L832,392C832,390.667 832,389.333 832,388C832,386.667 831.833,385.333 831.5,384L637.5,384C620.167,384 603.917,380.5 588.75,373.5C573.583,366.5 560.333,357.167 549,345.5C537.667,333.833 528.667,320.25 522,304.75C515.333,289.25 512,273 512,256L512,64.5C510.667,64.1667 509.333,64.0001 508,64C506.667,64.0001 505.333,64.0001 504,64L256,64C247,64.0001 238.667,65.6667 231,69C223.333,72.3334 216.583,76.9167 210.75,82.75C204.917,88.5834 200.333,95.3334 197,103C193.667,110.667 192,119 192,128L192,380.5C169.667,386.5 148.333,394.5 128,404.5L128,125.5C128,108.833 131.417,92.9167 138.25,77.75C145.083,62.5834 154.25,49.2501 165.75,37.75C177.25,26.2501 190.583,17.0834 205.75,10.25C220.917,3.41669 236.833,0 253.5,0L504,0C521,0 537.333,3.25 553,9.75C568.667,16.25 582.5,25.5 594.5,37.5L858.5,301.5C870.5,313.5 879.75,327.333 886.25,343C892.75,358.667 896,375 896,392ZM576,256C576,265 577.667,273.417 581,281.25C584.333,289.083 588.833,295.833 594.5,301.5C600.167,307.167 606.917,311.667 614.75,315C622.583,318.333 631,320 640,320L787,320L576,109ZM576,736C576,775.667 568.417,813 553.25,848C538.083,883 517.5,913.5 491.5,939.5C465.5,965.5 435,986.083 400,1001.25C365,1016.42 327.667,1024 288,1024C248,1024 210.5,1016.5 175.5,1001.5C140.5,986.5 110,966 84,940C58,914 37.5,883.5 22.5,848.5C7.5,813.5 0,776 0,736C0,696.333 7.58333,659 22.75,624C37.9167,589 58.5,558.5 84.5,532.5C110.5,506.5 141,485.917 176,470.75C211,455.583 248.333,448 288,448C314.333,448 339.75,451.417 364.25,458.25C388.75,465.083 411.667,474.75 433,487.25C454.333,499.75 473.833,514.833 491.5,532.5C509.167,550.167 524.25,569.667 536.75,591C549.25,612.333 558.917,635.25 565.75,659.75C572.583,684.25 576,709.667 576,736ZM452,736C452,726 448.5,717.5 441.5,710.5L313.5,582.5C306.5,575.5 298,572 288,572C278,572 269.5,575.5 262.5,582.5L134.5,710.5C127.5,717.5 124,726 124,736C124,746 127.5,754.5 134.5,761.5C141.5,768.5 150,772 160,772C170,772 178.5,768.5 185.5,761.5L256,691L256,864C256,872.667 259.167,880.167 265.5,886.5C271.833,892.833 279.333,896 288,896C296.667,896 304.167,892.833 310.5,886.5C316.833,880.167 320,872.667 320,864L320,691L390.5,761.5C397.5,768.5 406,772 416,772C426,772 434.5,768.5 441.5,761.5C448.5,754.5 452,746 452,736Z + M192,960C174.333,960 157.75,956.667 142.25,950C126.75,943.333 113.167,934.167 101.5,922.5C89.8333,910.833 80.6667,897.25 74,881.75C67.3333,866.25 64,849.667 64,832L64,192C64,174.333 67.3333,157.75 74,142.25C80.6667,126.75 89.8333,113.167 101.5,101.5C113.167,89.8334 126.75,80.6667 142.25,74C157.75,67.3334 174.333,64.0001 192,64L696,64C713,64.0001 729.333,67.2501 745,73.75C760.667,80.2501 774.5,89.5001 786.5,101.5L922.5,237.5C934.5,249.5 943.75,263.333 950.25,279C956.75,294.667 960,311 960,328L960,444C950,436.333 939.667,429.25 929,422.75C918.333,416.25 907.333,410.167 896,404.5L896,328C896,319.333 894.417,311.083 891.25,303.25C888.083,295.417 883.5,288.5 877.5,282.5L741.5,146.5C731.167,136.167 718.667,130.167 704,128.5L704,288C704,301.333 701.5,313.833 696.5,325.5C691.5,337.167 684.667,347.333 676,356C667.333,364.667 657.167,371.5 645.5,376.5C633.833,381.5 621.333,384 608,384L352,384C338.667,384 326.167,381.5 314.5,376.5C302.833,371.5 292.667,364.667 284,356C275.333,347.333 268.5,337.167 263.5,325.5C258.5,313.833 256,301.333 256,288L256,128L192,128C183,128 174.583,129.667 166.75,133C158.917,136.333 152.167,140.833 146.5,146.5C140.833,152.167 136.333,158.917 133,166.75C129.667,174.583 128,183 128,192L128,832C128,841 129.667,849.417 133,857.25C136.333,865.083 140.833,871.833 146.5,877.5C152.167,883.167 158.917,887.667 166.75,891C174.583,894.333 183,896 192,896L192,608C192,594.667 194.5,582.167 199.5,570.5C204.5,558.833 211.333,548.667 220,540C228.667,531.333 238.833,524.5 250.5,519.5C262.167,514.5 274.667,512 288,512L444,512C436.333,522 429.25,532.333 422.75,543C416.25,553.667 410.167,564.667 404.5,576L288,576C279.333,576 271.833,579.167 265.5,585.5C259.167,591.833 256,599.333 256,608L256,896L404.5,896C410.167,907.333 416.25,918.333 422.75,929C429.25,939.667 436.333,950 444,960ZM608,320C616.667,320 624.167,316.833 630.5,310.5C636.833,304.167 640,296.667 640,288L640,128L320,128L320,288C320,296.667 323.167,304.167 329.5,310.5C335.833,316.833 343.333,320 352,320ZM448,736C448,696.333 455.583,659 470.75,624C485.917,589 506.5,558.5 532.5,532.5C558.5,506.5 589,485.917 624,470.75C659,455.583 696.333,448 736,448C762.333,448 787.75,451.417 812.25,458.25C836.75,465.083 859.667,474.75 881,487.25C902.333,499.75 921.833,514.833 939.5,532.5C957.167,550.167 972.25,569.667 984.75,591C997.25,612.333 1006.92,635.25 1013.75,659.75C1020.58,684.25 1024,709.667 1024,736C1024,775.667 1016.42,813 1001.25,848C986.083,883 965.5,913.5 939.5,939.5C913.5,965.5 883,986.083 848,1001.25C813,1016.42 775.667,1024 736,1024C696,1024 658.5,1016.5 623.5,1001.5C588.5,986.5 558,966 532,940C506,914 485.5,883.5 470.5,848.5C455.5,813.5 448,776 448,736ZM736,900C746,900 754.5,896.5 761.5,889.5L889.5,761.5C896.5,754.5 900,746 900,736C900,726 896.5,717.5 889.5,710.5C882.5,703.5 874,700 864,700C854,700 845.5,703.5 838.5,710.5L768,781L768,608C768,599.333 764.833,591.833 758.5,585.5C752.167,579.167 744.667,576 736,576C727.333,576 719.833,579.167 713.5,585.5C707.167,591.833 704,599.333 704,608L704,781L633.5,710.5C626.5,703.5 618,700 608,700C598,700 589.5,703.5 582.5,710.5C575.5,717.5 572,726 572,736C572,746 575.5,754.5 582.5,761.5L710.5,889.5C717.5,896.5 726,900 736,900Z + + + M270.5,954C259.833,954 249.75,952 240.25,948C230.75,944 222.417,938.5 215.25,931.5C208.083,924.5 202.417,916.333 198.25,907C194.083,897.667 192,887.667 192,877L192,147C192,136 194.25,125.5 198.75,115.5C203.25,105.5 209.333,96.6667 217,89C224.667,81.3334 233.5,75.2501 243.5,70.75C253.5,66.2501 264,64.0001 275,64L499,64C534.333,64.0001 567.5,70.7501 598.5,84.25C629.5,97.7501 656.583,116.083 679.75,139.25C702.917,162.417 721.25,189.5 734.75,220.5C748.25,251.5 755,284.667 755,320C755,348.333 750.583,375.25 741.75,400.75C732.917,426.25 720.333,450.5 704,473.5C737.333,499.5 762.75,530.667 780.25,567C797.75,603.333 806.5,642.667 806.5,685C806.5,711.333 803.333,737.083 797,762.25C790.667,787.417 780,811 765,833C752.333,851.667 737.583,868.417 720.75,883.25C703.917,898.083 685.667,910.75 666,921.25C646.333,931.75 625.583,939.75 603.75,945.25C581.917,950.75 559.833,953.5 537.5,953.5C492.833,953.5 448.333,953.583 404,953.75C359.667,953.917 315.167,954 270.5,954ZM493,416C506.333,416 518.833,413.5 530.5,408.5C542.167,403.5 552.333,396.667 561,388C569.667,379.333 576.5,369.167 581.5,357.5C586.5,345.833 589,333.333 589,320C589,306.667 586.5,294.167 581.5,282.5C576.5,270.833 569.667,260.667 561,252C552.333,243.333 542.167,236.5 530.5,231.5C518.833,226.5 506.333,224 493,224L352,224L352,416ZM531,800C546.667,800 561.167,797.083 574.5,791.25C587.833,785.417 599.333,777.5 609,767.5C618.667,757.5 626.25,745.75 631.75,732.25C637.25,718.75 640,704.167 640,688.5C640,673.833 637.083,659.75 631.25,646.25C625.417,632.75 617.583,620.75 607.75,610.25C597.917,599.75 586.417,591.417 573.25,585.25C560.083,579.083 546,576 531,576L352,576L352,800Z + M896,64C904.667,64.0001 912.167,67.1667 918.5,73.5C924.833,79.8334 928,87.3334 928,96C928,104.667 924.833,112.167 918.5,118.5C912.167,124.833 904.667,128 896,128L694,128L398.5,896L608,896C616.667,896 624.167,899.167 630.5,905.5C636.833,911.833 640,919.333 640,928C640,936.667 636.833,944.167 630.5,950.5C624.167,956.833 616.667,960 608,960L128,960C119.333,960 111.833,956.833 105.5,950.5C99.1667,944.167 96,936.667 96,928C96,919.333 99.1667,911.833 105.5,905.5C111.833,899.167 119.333,896 128,896L330,896L625.5,128L416,128C407.333,128 399.833,124.833 393.5,118.5C387.167,112.167 384,104.667 384,96C384,87.3334 387.167,79.8334 393.5,73.5C399.833,67.1667 407.333,64.0001 416,64Z + M192,509L192,96C192,87.3334 195.167,79.8334 201.5,73.5C207.833,67.1667 215.333,64.0001 224,64C232.667,64.0001 240.167,67.1667 246.5,73.5C252.833,79.8334 256,87.3334 256,96L256,514C256,548.667 262.833,581.417 276.5,612.25C290.167,643.083 308.667,670 332,693C355.333,716 382.5,734.25 413.5,747.75C444.5,761.25 477.333,768 512,768C547.667,768 581.083,761.167 612.25,747.5C643.417,733.833 670.5,715.25 693.5,691.75C716.5,668.25 734.667,640.833 748,609.5C761.333,578.167 768,544.667 768,509L768,96C768,87.3334 771.167,79.8334 777.5,73.5C783.833,67.1667 791.333,64.0001 800,64C808.667,64.0001 816.167,67.1667 822.5,73.5C828.833,79.8334 832,87.3334 832,96L832,509C832,538.667 828.25,567.167 820.75,594.5C813.25,621.833 802.583,647.5 788.75,671.5C774.917,695.5 758.25,717.333 738.75,737C719.25,756.667 697.667,773.5 674,787.5C650.333,801.5 624.833,812.417 597.5,820.25C570.167,828.083 541.667,832 512,832C482.333,832 453.833,828.083 426.5,820.25C399.167,812.417 373.667,801.5 350,787.5C326.333,773.5 304.75,756.667 285.25,737C265.75,717.333 249.083,695.5 235.25,671.5C221.417,647.5 210.75,621.75 203.25,594.25C195.75,566.75 192,538.333 192,509ZM224,960C215.333,960 207.833,956.833 201.5,950.5C195.167,944.167 192,936.667 192,928C192,919.333 195.167,911.833 201.5,905.5C207.833,899.167 215.333,896 224,896L800,896C808.667,896 816.167,899.167 822.5,905.5C828.833,911.833 832,919.333 832,928C832,936.667 828.833,944.167 822.5,950.5C816.167,956.833 808.667,960 800,960Z + M270.5,448C218.167,407.333 192,354 192,288C192,271.333 194.667,255.417 200,240.25C205.333,225.083 212.5,210.75 221.5,197.25C230.5,183.75 240.917,171.333 252.75,160C264.583,148.667 277,138.5 290,129.5C322,107.5 357.333,91.0834 396,80.25C434.667,69.4167 473.333,64.0001 512,64C536.333,64.0001 560.5,65.8334 584.5,69.5C608.5,73.1667 632,79.1667 655,87.5C670.333,93.1667 686,99.8334 702,107.5C718,115.167 733.583,124 748.75,134C763.917,144 778.083,154.917 791.25,166.75C804.417,178.583 815.833,191.333 825.5,205C827.833,208.333 829.5,211.25 830.5,213.75C831.5,216.25 832,219.667 832,224C832,232.667 828.833,240.167 822.5,246.5C816.167,252.833 808.667,256 800,256C794,256 789.167,254.917 785.5,252.75C781.833,250.583 778.167,247.333 774.5,243C766.167,233.667 757.833,225 749.5,217C741.167,209 732,201.167 722,193.5C707.333,182.5 691.583,172.833 674.75,164.5C657.917,156.167 640.5,149.333 622.5,144C604.5,138.667 586.167,134.667 567.5,132C548.833,129.333 530.333,128 512,128C479.333,128 446.917,132.333 414.75,141C382.583,149.667 353,163.5 326,182.5C317.667,188.5 309.333,195.25 301,202.75C292.667,210.25 285.167,218.5 278.5,227.5C271.833,236.5 266.417,246.083 262.25,256.25C258.083,266.417 256,277 256,288C256,311.333 260.417,331.5 269.25,348.5C278.083,365.5 289.75,380.25 304.25,392.75C318.75,405.25 335.25,415.917 353.75,424.75C372.25,433.583 391.167,441.333 410.5,448ZM192,800C192,791 195.25,783.417 201.75,777.25C208.25,771.083 215.833,768 224.5,768C230.167,768 235.083,769.25 239.25,771.75C243.417,774.25 247.167,777.667 250.5,782C258.833,792.667 266.333,802.25 273,810.75C279.667,819.25 286.667,827 294,834C301.333,841 309.583,847.417 318.75,853.25C327.917,859.083 339,864.833 352,870.5C376.667,881.167 402.917,888.083 430.75,891.25C458.583,894.417 485.667,896 512,896C528,896 545.417,894.833 564.25,892.5C583.083,890.167 602,886.417 621,881.25C640,876.083 658.333,869.417 676,861.25C693.667,853.083 709.333,843.083 723,831.25C736.667,819.417 747.583,805.583 755.75,789.75C763.917,773.917 768,756 768,736C768,707 761.583,682.833 748.75,663.5C735.917,644.167 717.5,627.167 693.5,612.5C690.5,610.833 685.083,608 677.25,604C669.417,600 661.25,595.917 652.75,591.75C644.25,587.583 636.167,583.917 628.5,580.75C620.833,577.583 615.833,576 613.5,576L96,576C87.3333,576 79.8333,572.833 73.5,566.5C67.1667,560.167 64,552.667 64,544C64,535.333 67.1667,527.833 73.5,521.5C79.8333,515.167 87.3333,512 96,512L928,512C936.667,512 944.167,515.167 950.5,521.5C956.833,527.833 960,535.333 960,544C960,552.667 956.833,560.167 950.5,566.5C944.167,572.833 936.667,576 928,576L753.5,576C805.833,616.667 832,670 832,736C832,763 827.167,787.25 817.5,808.75C807.833,830.25 794.917,849.333 778.75,866C762.583,882.667 743.75,896.917 722.25,908.75C700.75,920.583 678.167,930.333 654.5,938C630.833,945.667 606.75,951.25 582.25,954.75C557.75,958.25 534.333,960 512,960C480.667,960 449.333,958.083 418,954.25C386.667,950.417 356.333,942.167 327,929.5C301,918.167 276.667,902.833 254,883.5C231.333,864.167 212.5,842.333 197.5,818C193.833,812.333 192,806.333 192,800Z + M0,224C0,215 1.66667,206.667 5,199C8.33333,191.333 12.9167,184.583 18.75,178.75C24.5833,172.917 31.3333,168.333 39,165C46.6667,161.667 55,160 64,160C72.6667,160 80.9167,161.667 88.75,165C96.5833,168.333 103.417,172.917 109.25,178.75C115.083,184.583 119.667,191.417 123,199.25C126.333,207.083 128,215.333 128,224C128,233 126.333,241.333 123,249C119.667,256.667 115.083,263.417 109.25,269.25C103.417,275.083 96.6667,279.667 89,283C81.3333,286.333 73,288 64,288C55,288 46.5833,286.333 38.75,283C30.9167,279.667 24.1667,275.167 18.5,269.5C12.8333,263.833 8.33333,257.083 5,249.25C1.66667,241.417 0,233 0,224ZM288,256C279.333,256 271.833,252.833 265.5,246.5C259.167,240.167 256,232.667 256,224C256,215.333 259.167,207.833 265.5,201.5C271.833,195.167 279.333,192 288,192L992,192C1000.67,192 1008.17,195.167 1014.5,201.5C1020.83,207.833 1024,215.333 1024,224C1024,232.667 1020.83,240.167 1014.5,246.5C1008.17,252.833 1000.67,256 992,256ZM0,480C0,471 1.66667,462.667 5,455C8.33333,447.333 12.9167,440.583 18.75,434.75C24.5833,428.917 31.3333,424.333 39,421C46.6667,417.667 55,416 64,416C72.6667,416 80.9167,417.667 88.75,421C96.5833,424.333 103.417,428.917 109.25,434.75C115.083,440.583 119.667,447.417 123,455.25C126.333,463.083 128,471.333 128,480C128,489 126.333,497.333 123,505C119.667,512.667 115.083,519.417 109.25,525.25C103.417,531.083 96.6667,535.667 89,539C81.3333,542.333 73,544 64,544C55,544 46.5833,542.333 38.75,539C30.9167,535.667 24.1667,531.167 18.5,525.5C12.8333,519.833 8.33333,513.083 5,505.25C1.66667,497.417 0,489 0,480ZM288,512C279.333,512 271.833,508.833 265.5,502.5C259.167,496.167 256,488.667 256,480C256,471.333 259.167,463.833 265.5,457.5C271.833,451.167 279.333,448 288,448L992,448C1000.67,448 1008.17,451.167 1014.5,457.5C1020.83,463.833 1024,471.333 1024,480C1024,488.667 1020.83,496.167 1014.5,502.5C1008.17,508.833 1000.67,512 992,512ZM0,736C0,727 1.66667,718.667 5,711C8.33333,703.333 12.9167,696.583 18.75,690.75C24.5833,684.917 31.3333,680.333 39,677C46.6667,673.667 55,672 64,672C72.6667,672 80.9167,673.667 88.75,677C96.5833,680.333 103.417,684.917 109.25,690.75C115.083,696.583 119.667,703.417 123,711.25C126.333,719.083 128,727.333 128,736C128,745 126.333,753.333 123,761C119.667,768.667 115.083,775.417 109.25,781.25C103.417,787.083 96.6667,791.667 89,795C81.3333,798.333 73,800 64,800C55,800 46.5833,798.333 38.75,795C30.9167,791.667 24.1667,787.167 18.5,781.5C12.8333,775.833 8.33333,769.083 5,761.25C1.66667,753.417 0,745 0,736ZM288,768C279.333,768 271.833,764.833 265.5,758.5C259.167,752.167 256,744.667 256,736C256,727.333 259.167,719.833 265.5,713.5C271.833,707.167 279.333,704 288,704L992,704C1000.67,704 1008.17,707.167 1014.5,713.5C1020.83,719.833 1024,727.333 1024,736C1024,744.667 1020.83,752.167 1014.5,758.5C1008.17,764.833 1000.67,768 992,768Z + F1 M 15 6.25 C 14.824218 6.25 14.65983 6.217448 14.506836 6.152344 C 14.353841 6.08724 14.222005 5.99935 14.111328 5.888672 C 14.00065 5.777995 13.91276 5.646159 13.847656 5.493164 C 13.782552 5.34017 13.75 5.175782 13.75 5 L 13.75 2.5 C 13.75 2.324219 13.782552 2.161459 13.847656 2.011719 C 13.91276 1.86198 14.002277 1.730145 14.116211 1.616211 C 14.230143 1.502279 14.361979 1.412762 14.511719 1.347656 C 14.661457 1.282553 14.824218 1.25 15 1.25 L 17.5 1.25 C 17.66927 1.25 17.830402 1.282553 17.983398 1.347656 C 18.136393 1.412762 18.269855 1.502279 18.383789 1.616211 C 18.497721 1.730145 18.587238 1.863607 18.652344 2.016602 C 18.717447 2.169598 18.75 2.33073 18.75 2.5 L 18.75 5 C 18.75 5.175782 18.717447 5.338543 18.652344 5.488281 C 18.587238 5.638021 18.497721 5.769857 18.383789 5.883789 C 18.269855 5.997723 18.13802 6.08724 17.988281 6.152344 C 17.838541 6.217448 17.675781 6.25 17.5 6.25 Z M 1.875 3.75 C 1.705729 3.75 1.559245 3.688152 1.435547 3.564453 C 1.311849 3.440756 1.25 3.294271 1.25 3.125 C 1.25 2.95573 1.311849 2.809246 1.435547 2.685547 C 1.559245 2.56185 1.705729 2.5 1.875 2.5 L 11.875 2.5 C 12.04427 2.5 12.190754 2.56185 12.314453 2.685547 C 12.43815 2.809246 12.5 2.95573 12.5 3.125 C 12.5 3.294271 12.43815 3.440756 12.314453 3.564453 C 12.190754 3.688152 12.04427 3.75 11.875 3.75 Z M 15 2.5 L 15 5 L 17.5 5 L 17.5 2.5 Z M 15 12.5 C 14.824218 12.5 14.65983 12.467448 14.506836 12.402344 C 14.353841 12.33724 14.222005 12.24935 14.111328 12.138672 C 14.00065 12.027995 13.91276 11.896159 13.847656 11.743164 C 13.782552 11.59017 13.75 11.425781 13.75 11.25 L 13.75 8.75 C 13.75 8.574219 13.782552 8.411459 13.847656 8.261719 C 13.91276 8.111979 14.002277 7.980144 14.116211 7.866211 C 14.230143 7.752279 14.361979 7.662762 14.511719 7.597656 C 14.661457 7.532553 14.824218 7.5 15 7.5 L 17.5 7.5 C 17.66927 7.5 17.830402 7.532553 17.983398 7.597656 C 18.136393 7.662762 18.269855 7.752279 18.383789 7.866211 C 18.497721 7.980144 18.587238 8.113607 18.652344 8.266602 C 18.717447 8.419597 18.75 8.580729 18.75 8.75 L 18.75 11.25 C 18.75 11.425781 18.717447 11.588542 18.652344 11.738281 C 18.587238 11.888021 18.497721 12.019857 18.383789 12.133789 C 18.269855 12.247722 18.13802 12.33724 17.988281 12.402344 C 17.838541 12.467448 17.675781 12.5 17.5 12.5 Z M 1.875 10 C 1.705729 10 1.559245 9.938151 1.435547 9.814453 C 1.311849 9.690756 1.25 9.544271 1.25 9.375 C 1.25 9.205729 1.311849 9.059245 1.435547 8.935547 C 1.559245 8.81185 1.705729 8.75 1.875 8.75 L 11.875 8.75 C 12.04427 8.75 12.190754 8.81185 12.314453 8.935547 C 12.43815 9.059245 12.5 9.205729 12.5 9.375 C 12.5 9.544271 12.43815 9.690756 12.314453 9.814453 C 12.190754 9.938151 12.04427 10 11.875 10 Z M 15 8.75 L 15 11.25 L 17.5 11.25 L 17.5 8.75 Z M 15 18.75 C 14.824218 18.75 14.65983 18.717447 14.506836 18.652344 C 14.353841 18.58724 14.222005 18.49935 14.111328 18.388672 C 14.00065 18.277994 13.91276 18.146158 13.847656 17.993164 C 13.782552 17.84017 13.75 17.675781 13.75 17.5 L 13.75 15 C 13.75 14.824219 13.782552 14.661459 13.847656 14.511719 C 13.91276 14.361979 14.002277 14.230144 14.116211 14.116211 C 14.230143 14.002279 14.361979 13.912761 14.511719 13.847656 C 14.661457 13.782553 14.824218 13.75 15 13.75 L 17.5 13.75 C 17.66927 13.75 17.830402 13.782553 17.983398 13.847656 C 18.136393 13.912761 18.269855 14.002279 18.383789 14.116211 C 18.497721 14.230144 18.587238 14.363607 18.652344 14.516602 C 18.717447 14.669597 18.75 14.830729 18.75 15 L 18.75 17.5 C 18.75 17.675781 18.717447 17.838541 18.652344 17.988281 C 18.587238 18.138021 18.497721 18.269857 18.383789 18.383789 C 18.269855 18.497721 18.13802 18.58724 17.988281 18.652344 C 17.838541 18.717447 17.675781 18.75 17.5 18.75 Z M 1.875 16.25 C 1.705729 16.25 1.559245 16.188152 1.435547 16.064453 C 1.311849 15.940756 1.25 15.794271 1.25 15.625 C 1.25 15.455729 1.311849 15.309245 1.435547 15.185547 C 1.559245 15.06185 1.705729 15 1.875 15 L 11.875 15 C 12.04427 15 12.190754 15.06185 12.314453 15.185547 C 12.43815 15.309245 12.5 15.455729 12.5 15.625 C 12.5 15.794271 12.43815 15.940756 12.314453 16.064453 C 12.190754 16.188152 12.04427 16.25 11.875 16.25 Z M 15 15 L 15 17.5 L 17.5 17.5 L 17.5 15 Z + M288,192C279.333,192 271.833,188.833 265.5,182.5C259.167,176.167 256,168.667 256,160C256,151.333 259.167,143.833 265.5,137.5C271.833,131.167 279.333,128 288,128L800,128C808.667,128 816.167,131.167 822.5,137.5C828.833,143.833 832,151.333 832,160C832,168.667 828.833,176.167 822.5,182.5C816.167,188.833 808.667,192 800,192ZM0,480C0,471.333 3.16667,463.833 9.5,457.5L100,367C106.333,360.667 113.833,357.5 122.5,357.5C131.167,357.5 138.667,360.667 145,367C151.333,373.333 154.5,380.833 154.5,389.5C154.5,398.167 151.333,405.667 145,412L77.5,480L145,548C151.333,554.333 154.5,561.833 154.5,570.5C154.5,579.167 151.333,586.667 145,593C138.667,599.333 131.167,602.5 122.5,602.5C113.833,602.5 106.333,599.333 100,593L9.5,502.5C3.16667,496.167 0,488.667 0,480ZM288,512C279.333,512 271.833,508.833 265.5,502.5C259.167,496.167 256,488.667 256,480C256,471.333 259.167,463.833 265.5,457.5C271.833,451.167 279.333,448 288,448L992,448C1000.67,448 1008.17,451.167 1014.5,457.5C1020.83,463.833 1024,471.333 1024,480C1024,488.667 1020.83,496.167 1014.5,502.5C1008.17,508.833 1000.67,512 992,512ZM288,832C279.333,832 271.833,828.833 265.5,822.5C259.167,816.167 256,808.667 256,800C256,791.333 259.167,783.833 265.5,777.5C271.833,771.167 279.333,768 288,768L672,768C680.667,768 688.167,771.167 694.5,777.5C700.833,783.833 704,791.333 704,800C704,808.667 700.833,816.167 694.5,822.5C688.167,828.833 680.667,832 672,832Z + M288,192C279.333,192 271.833,188.833 265.5,182.5C259.167,176.167 256,168.667 256,160C256,151.333 259.167,143.833 265.5,137.5C271.833,131.167 279.333,128 288,128L800,128C808.667,128 816.167,131.167 822.5,137.5C828.833,143.833 832,151.333 832,160C832,168.667 828.833,176.167 822.5,182.5C816.167,188.833 808.667,192 800,192ZM0,570.5C0,561.833 3.16667,554.333 9.5,548L77.5,480L9.5,412C3.16667,405.667 0,398.167 0,389.5C0,380.833 3.16667,373.333 9.5,367C15.8333,360.667 23.3333,357.5 32,357.5C40.6667,357.5 48.1667,360.667 54.5,367L145,457.5C151.333,463.833 154.5,471.333 154.5,480C154.5,488.667 151.333,496.167 145,502.5L54.5,593C48.1667,599.333 40.6667,602.5 32,602.5C23.3333,602.5 15.8333,599.333 9.5,593C3.16667,586.667 0,579.167 0,570.5ZM288,512C279.333,512 271.833,508.833 265.5,502.5C259.167,496.167 256,488.667 256,480C256,471.333 259.167,463.833 265.5,457.5C271.833,451.167 279.333,448 288,448L992,448C1000.67,448 1008.17,451.167 1014.5,457.5C1020.83,463.833 1024,471.333 1024,480C1024,488.667 1020.83,496.167 1014.5,502.5C1008.17,508.833 1000.67,512 992,512ZM288,832C279.333,832 271.833,828.833 265.5,822.5C259.167,816.167 256,808.667 256,800C256,791.333 259.167,783.833 265.5,777.5C271.833,771.167 279.333,768 288,768L672,768C680.667,768 688.167,771.167 694.5,777.5C700.833,783.833 704,791.333 704,800C704,808.667 700.833,816.167 694.5,822.5C688.167,828.833 680.667,832 672,832Z + M768,576L667,576C632.333,576 599.75,569.083 569.25,555.25C538.75,541.417 512.167,522.75 489.5,499.25C466.833,475.75 448.917,448.583 435.75,417.75C422.583,386.917 416,354.333 416,320C416,285.667 422.583,253.083 435.75,222.25C448.917,191.417 466.833,164.25 489.5,140.75C512.167,117.25 538.75,98.5834 569.25,84.75C599.75,70.9167 632.333,64.0001 667,64L992,64C1000.67,64.0001 1008.17,67.1667 1014.5,73.5C1020.83,79.8334 1024,87.3334 1024,96C1024,103.333 1022.58,109.167 1019.75,113.5C1016.92,117.833 1013.25,121.083 1008.75,123.25C1004.25,125.417 999.25,126.833 993.75,127.5C988.25,128.167 982.667,128.5 977,128.5C974,128.5 971.083,128.417 968.25,128.25C965.417,128.083 962.667,128 960,128L960,992C960,1000.67 956.833,1008.17 950.5,1014.5C944.167,1020.83 936.667,1024 928,1024C919.333,1024 911.833,1020.83 905.5,1014.5C899.167,1008.17 896,1000.67 896,992L896,128L832,128L832,992C832,1000.67 828.833,1008.17 822.5,1014.5C816.167,1020.83 808.667,1024 800,1024C791.333,1024 783.833,1020.83 777.5,1014.5C771.167,1008.17 768,1000.67 768,992ZM768,512L768,128L668,128C642,128 617.583,133.167 594.75,143.5C571.917,153.833 552,167.833 535,185.5C518,203.167 504.583,223.583 494.75,246.75C484.917,269.917 480,294.333 480,320C480,345.667 484.917,370.083 494.75,393.25C504.583,416.417 518,436.833 535,454.5C552,472.167 571.917,486.167 594.75,496.5C617.583,506.833 642,512 668,512ZM64,672C64,663.333 67.1667,655.833 73.5,649.5L243,480L73.5,310.5C67.1667,304.167 64,296.667 64,288C64,279.333 67.1667,271.833 73.5,265.5C79.8333,259.167 87.3333,256 96,256C104.667,256 112.167,259.167 118.5,265.5L310.5,457.5C316.833,463.833 320,471.333 320,480C320,488.667 316.833,496.167 310.5,502.5L118.5,694.5C112.167,700.833 104.667,704 96,704C87.3333,704 79.8333,700.833 73.5,694.5C67.1667,688.167 64,680.667 64,672Z + M832,706.5C832,723.167 828.583,739.083 821.75,754.25C814.917,769.417 805.75,782.75 794.25,794.25C782.75,805.75 769.417,814.917 754.25,821.75C739.083,828.583 723.167,832 706.5,832L189.5,832C172.833,832 156.917,828.583 141.75,821.75C126.583,814.917 113.25,805.75 101.75,794.25C90.25,782.75 81.0833,769.417 74.25,754.25C67.4167,739.083 64,723.167 64,706.5L64,189.5C64,172.833 67.4167,156.917 74.25,141.75C81.0833,126.583 90.25,113.25 101.75,101.75C113.25,90.25 126.583,81.0834 141.75,74.25C156.917,67.4167 172.833,64.0001 189.5,64L706.5,64C723.167,64.0001 739.083,67.4167 754.25,74.25C769.417,81.0834 782.75,90.25 794.25,101.75C805.75,113.25 814.917,126.583 821.75,141.75C828.583,156.917 832,172.833 832,189.5ZM128,704C128,706.667 128.167,709.5 128.5,712.5C128.833,715.5 129.333,718.333 130,721L357.5,493C369.167,481.333 383,472.417 399,466.25C415,460.083 431.333,457 448,457C465,457 481.417,460 497.25,466C513.083,472 526.833,481 538.5,493L766,721C766.667,718.333 767.167,715.5 767.5,712.5C767.833,709.5 768,706.667 768,704L768,192C768,183.333 766.333,175.083 763,167.25C759.667,159.417 755.083,152.583 749.25,146.75C743.417,140.917 736.583,136.333 728.75,133C720.917,129.667 712.667,128 704,128L192,128C183,128 174.667,129.667 167,133C159.333,136.333 152.583,140.917 146.75,146.75C140.917,152.583 136.333,159.333 133,167C129.667,174.667 128,183 128,192ZM960,320L960,772C960,797 954.917,820.917 944.75,843.75C934.583,866.583 920.917,886.583 903.75,903.75C886.583,920.917 866.583,934.583 843.75,944.75C820.917,954.917 797,960 772,960L320,960C297,960 275.583,954.333 255.75,943C235.917,931.667 220.333,916 209,896L770.5,896C787.833,896 804.083,892.5 819.25,885.5C834.417,878.5 847.667,869.167 859,857.5C870.333,845.833 879.333,832.25 886,816.75C892.667,801.25 896,785 896,768L896,209C916,220.333 931.667,235.917 943,255.75C954.333,275.583 960,297 960,320ZM544,288C544,279 545.667,270.667 549,263C552.333,255.333 556.917,248.583 562.75,242.75C568.583,236.917 575.333,232.333 583,229C590.667,225.667 599,224 608,224C616.667,224 624.917,225.667 632.75,229C640.583,232.333 647.417,236.917 653.25,242.75C659.083,248.583 663.667,255.417 667,263.25C670.333,271.083 672,279.333 672,288C672,297 670.333,305.333 667,313C663.667,320.667 659.083,327.417 653.25,333.25C647.417,339.083 640.667,343.667 633,347C625.333,350.333 617,352 608,352C599,352 590.583,350.333 582.75,347C574.917,343.667 568.167,339.167 562.5,333.5C556.833,327.833 552.333,321.083 549,313.25C545.667,305.417 544,297 544,288ZM175,766C177.667,766.667 180.5,767.167 183.5,767.5C186.5,767.833 189.333,768 192,768L704,768C706.667,768 709.5,767.833 712.5,767.5C715.5,767.167 718.333,766.667 721,766L493.5,538.5C487.5,532.5 480.5,528 472.5,525C464.5,522 456.333,520.5 448,520.5C439.667,520.5 431.583,522 423.75,525C415.917,528 409,532.5 403,538.5Z + M32,192C23.3333,192 15.8333,188.833 9.5,182.5C3.16667,176.167 0,168.667 0,160C0,151.333 3.16667,143.833 9.5,137.5C15.8333,131.167 23.3333,128 32,128L736,128C744.667,128 752.167,131.167 758.5,137.5C764.833,143.833 768,151.333 768,160C768,168.667 764.833,176.167 758.5,182.5C752.167,188.833 744.667,192 736,192ZM32,512C23.3333,512 15.8333,508.833 9.5,502.5C3.16667,496.167 0,488.667 0,480C0,471.333 3.16667,463.833 9.5,457.5C15.8333,451.167 23.3333,448 32,448L992,448C1000.67,448 1008.17,451.167 1014.5,457.5C1020.83,463.833 1024,471.333 1024,480C1024,488.667 1020.83,496.167 1014.5,502.5C1008.17,508.833 1000.67,512 992,512ZM32,832C23.3333,832 15.8333,828.833 9.5,822.5C3.16667,816.167 0,808.667 0,800C0,791.333 3.16667,783.833 9.5,777.5C15.8333,771.167 23.3333,768 32,768L608,768C616.667,768 624.167,771.167 630.5,777.5C636.833,783.833 640,791.333 640,800C640,808.667 636.833,816.167 630.5,822.5C624.167,828.833 616.667,832 608,832Z + M160,192C151.333,192 143.833,188.833 137.5,182.5C131.167,176.167 128,168.667 128,160C128,151.333 131.167,143.833 137.5,137.5C143.833,131.167 151.333,128 160,128L864,128C872.667,128 880.167,131.167 886.5,137.5C892.833,143.833 896,151.333 896,160C896,168.667 892.833,176.167 886.5,182.5C880.167,188.833 872.667,192 864,192ZM32,512C23.3333,512 15.8333,508.833 9.5,502.5C3.16667,496.167 0,488.667 0,480C0,471.333 3.16667,463.833 9.5,457.5C15.8333,451.167 23.3333,448 32,448L992,448C1000.67,448 1008.17,451.167 1014.5,457.5C1020.83,463.833 1024,471.333 1024,480C1024,488.667 1020.83,496.167 1014.5,502.5C1008.17,508.833 1000.67,512 992,512ZM288,832C279.333,832 271.833,828.833 265.5,822.5C259.167,816.167 256,808.667 256,800C256,791.333 259.167,783.833 265.5,777.5C271.833,771.167 279.333,768 288,768L736,768C744.667,768 752.167,771.167 758.5,777.5C764.833,783.833 768,791.333 768,800C768,808.667 764.833,816.167 758.5,822.5C752.167,828.833 744.667,832 736,832Z + M288,192C279.333,192 271.833,188.833 265.5,182.5C259.167,176.167 256,168.667 256,160C256,151.333 259.167,143.833 265.5,137.5C271.833,131.167 279.333,128 288,128L992,128C1000.67,128 1008.17,131.167 1014.5,137.5C1020.83,143.833 1024,151.333 1024,160C1024,168.667 1020.83,176.167 1014.5,182.5C1008.17,188.833 1000.67,192 992,192ZM32,512C23.3333,512 15.8333,508.833 9.5,502.5C3.16667,496.167 0,488.667 0,480C0,471.333 3.16667,463.833 9.5,457.5C15.8333,451.167 23.3333,448 32,448L992,448C1000.67,448 1008.17,451.167 1014.5,457.5C1020.83,463.833 1024,471.333 1024,480C1024,488.667 1020.83,496.167 1014.5,502.5C1008.17,508.833 1000.67,512 992,512ZM480,832C471.333,832 463.833,828.833 457.5,822.5C451.167,816.167 448,808.667 448,800C448,791.333 451.167,783.833 457.5,777.5C463.833,771.167 471.333,768 480,768L992,768C1000.67,768 1008.17,771.167 1014.5,777.5C1020.83,783.833 1024,791.333 1024,800C1024,808.667 1020.83,816.167 1014.5,822.5C1008.17,828.833 1000.67,832 992,832Z + M32,192C23.3333,192 15.8333,188.833 9.5,182.5C3.16667,176.167 0,168.667 0,160C0,151.333 3.16667,143.833 9.5,137.5C15.8333,131.167 23.3333,128 32,128L992,128C1000.67,128 1008.17,131.167 1014.5,137.5C1020.83,143.833 1024,151.333 1024,160C1024,168.667 1020.83,176.167 1014.5,182.5C1008.17,188.833 1000.67,192 992,192ZM32,512C23.3333,512 15.8333,508.833 9.5,502.5C3.16667,496.167 0,488.667 0,480C0,471.333 3.16667,463.833 9.5,457.5C15.8333,451.167 23.3333,448 32,448L992,448C1000.67,448 1008.17,451.167 1014.5,457.5C1020.83,463.833 1024,471.333 1024,480C1024,488.667 1020.83,496.167 1014.5,502.5C1008.17,508.833 1000.67,512 992,512ZM32,832C23.3333,832 15.8333,828.833 9.5,822.5C3.16667,816.167 0,808.667 0,800C0,791.333 3.16667,783.833 9.5,777.5C15.8333,771.167 23.3333,768 32,768L992,768C1000.67,768 1008.17,771.167 1014.5,777.5C1020.83,783.833 1024,791.333 1024,800C1024,808.667 1020.83,816.167 1014.5,822.5C1008.17,828.833 1000.67,832 992,832Z + F1 M 0 10 C 0 9.082031 0.118815 8.196615 0.356445 7.34375 C 0.594076 6.490886 0.93099 5.694987 1.367188 4.956055 C 1.803385 4.217123 2.325846 3.543295 2.93457 2.93457 C 3.543294 2.325848 4.217122 1.803387 4.956055 1.367188 C 5.694986 0.93099 6.490885 0.594076 7.34375 0.356445 C 8.196614 0.118816 9.082031 0 10 0 C 10.917969 0 11.803385 0.118816 12.65625 0.356445 C 13.509114 0.594076 14.305013 0.93099 15.043945 1.367188 C 15.782877 1.803387 16.456705 2.325848 17.06543 2.93457 C 17.674152 3.543295 18.196613 4.217123 18.632812 4.956055 C 19.06901 5.694987 19.405924 6.490886 19.643555 7.34375 C 19.881184 8.196615 20 9.082031 20 10 C 20 10.917969 19.881184 11.803386 19.643555 12.65625 C 19.405924 13.509115 19.06901 14.305014 18.632812 15.043945 C 18.196613 15.782878 17.674152 16.456705 17.06543 17.06543 C 16.456705 17.674154 15.782877 18.196615 15.043945 18.632812 C 14.305013 19.06901 13.509114 19.405924 12.65625 19.643555 C 11.803385 19.881186 10.917969 20 10 20 C 9.082031 20 8.196614 19.881186 7.34375 19.643555 C 6.490885 19.405924 5.694986 19.06901 4.956055 18.632812 C 4.217122 18.196615 3.543294 17.674154 2.93457 17.06543 C 2.325846 16.456705 1.803385 15.782878 1.367188 15.043945 C 0.93099 14.305014 0.594076 13.509115 0.356445 12.65625 C 0.118815 11.803386 0 10.917969 0 10 Z M 18.75 10 C 18.75 9.199219 18.645832 8.426107 18.4375 7.680664 C 18.229166 6.935222 17.93457 6.238607 17.553711 5.59082 C 17.172852 4.943035 16.715494 4.352215 16.181641 3.818359 C 15.647785 3.284506 15.056965 2.827148 14.40918 2.446289 C 13.761393 2.06543 13.064778 1.770834 12.319336 1.5625 C 11.573893 1.354168 10.800781 1.25 10 1.25 C 9.192708 1.25 8.416341 1.354168 7.670898 1.5625 C 6.925456 1.770834 6.228841 2.06543 5.581055 2.446289 C 4.933268 2.827148 4.344075 3.282879 3.813477 3.813477 C 3.282877 4.344076 2.827148 4.933269 2.446289 5.581055 C 2.06543 6.228842 1.770833 6.925457 1.5625 7.670898 C 1.354167 8.416342 1.25 9.192709 1.25 10 C 1.25 10.807292 1.354167 11.583659 1.5625 12.329102 C 1.770833 13.074545 2.06543 13.771159 2.446289 14.418945 C 2.827148 15.066732 3.282877 15.655925 3.813477 16.186523 C 4.344075 16.717123 4.933268 17.172852 5.581055 17.553711 C 6.228841 17.93457 6.925456 18.229166 7.670898 18.4375 C 8.416341 18.645834 9.192708 18.75 10 18.75 C 10.807291 18.75 11.583658 18.645834 12.329102 18.4375 C 13.074543 18.229166 13.771158 17.93457 14.418945 17.553711 C 15.066731 17.172852 15.655924 16.717123 16.186523 16.186523 C 16.717121 15.655925 17.172852 15.066732 17.553711 14.418945 C 17.93457 13.771159 18.229166 13.074545 18.4375 12.329102 C 18.645832 11.583659 18.75 10.807292 18.75 10 Z M 5.625 8.125 C 5.625 7.949219 5.657552 7.786459 5.722656 7.636719 C 5.78776 7.486979 5.877278 7.355144 5.991211 7.241211 C 6.105143 7.127279 6.236979 7.037761 6.386719 6.972656 C 6.536458 6.907553 6.699219 6.875001 6.875 6.875 C 7.044271 6.875001 7.205403 6.907553 7.358398 6.972656 C 7.511393 7.037761 7.644856 7.127279 7.758789 7.241211 C 7.872721 7.355144 7.962239 7.488607 8.027344 7.641602 C 8.092447 7.794598 8.125 7.95573 8.125 8.125 C 8.125 8.300781 8.092447 8.463542 8.027344 8.613281 C 7.962239 8.763021 7.872721 8.894857 7.758789 9.008789 C 7.644856 9.122722 7.513021 9.21224 7.363281 9.277344 C 7.213542 9.342448 7.050781 9.375 6.875 9.375 C 6.699219 9.375 6.534831 9.342448 6.381836 9.277344 C 6.228841 9.21224 6.097005 9.12435 5.986328 9.013672 C 5.875651 8.902995 5.78776 8.771159 5.722656 8.618164 C 5.657552 8.46517 5.625 8.300781 5.625 8.125 Z M 11.875 8.125 C 11.875 7.949219 11.907552 7.786459 11.972656 7.636719 C 12.03776 7.486979 12.127277 7.355144 12.241211 7.241211 C 12.355143 7.127279 12.486979 7.037761 12.636719 6.972656 C 12.786457 6.907553 12.949218 6.875001 13.125 6.875 C 13.294271 6.875001 13.455403 6.907553 13.608398 6.972656 C 13.761393 7.037761 13.894856 7.127279 14.008789 7.241211 C 14.122721 7.355144 14.212238 7.488607 14.277344 7.641602 C 14.342447 7.794598 14.375 7.95573 14.375 8.125 C 14.375 8.300781 14.342447 8.463542 14.277344 8.613281 C 14.212238 8.763021 14.122721 8.894857 14.008789 9.008789 C 13.894856 9.122722 13.763021 9.21224 13.613281 9.277344 C 13.463541 9.342448 13.30078 9.375 13.125 9.375 C 12.949218 9.375 12.78483 9.342448 12.631836 9.277344 C 12.478841 9.21224 12.347005 9.12435 12.236328 9.013672 C 12.12565 8.902995 12.03776 8.771159 11.972656 8.618164 C 11.907552 8.46517 11.875 8.300781 11.875 8.125 Z M 5.3125 13.4375 C 5.3125 13.268229 5.374349 13.121745 5.498047 12.998047 C 5.621745 12.87435 5.768229 12.8125 5.9375 12.8125 C 6.041666 12.8125 6.123046 12.827148 6.181641 12.856445 C 6.240234 12.885742 6.305338 12.932943 6.376953 12.998047 C 6.669922 13.245443 6.948242 13.463542 7.211914 13.652344 C 7.475586 13.841146 7.747396 14.000651 8.027344 14.130859 C 8.307291 14.261068 8.606771 14.360352 8.925781 14.428711 C 9.244791 14.49707 9.602864 14.53125 10 14.53125 C 10.572916 14.53125 11.101888 14.448242 11.586914 14.282227 C 12.071939 14.116211 12.542317 13.860678 12.998047 13.515625 C 13.108724 13.43099 13.212891 13.346354 13.310547 13.261719 C 13.408203 13.177084 13.512369 13.089193 13.623047 12.998047 C 13.694661 12.939453 13.759766 12.893881 13.818359 12.861328 C 13.876953 12.828776 13.958333 12.8125 14.0625 12.8125 C 14.231771 12.8125 14.378255 12.87435 14.501953 12.998047 C 14.62565 13.121745 14.6875 13.268229 14.6875 13.4375 C 14.6875 13.528646 14.671224 13.606771 14.638672 13.671875 C 14.606119 13.736979 14.560546 13.805339 14.501953 13.876953 C 14.397786 14.000651 14.269205 14.125977 14.116211 14.25293 C 13.963215 14.379883 13.800455 14.501953 13.62793 14.619141 C 13.455403 14.736328 13.282877 14.845378 13.110352 14.946289 C 12.937825 15.047201 12.779947 15.133464 12.636719 15.205078 C 12.226562 15.413412 11.798502 15.561523 11.352539 15.649414 C 10.906575 15.737305 10.455729 15.78125 10 15.78125 C 9.53125 15.78125 9.070638 15.73405 8.618164 15.639648 C 8.165689 15.545248 7.731119 15.390625 7.314453 15.175781 C 7.229817 15.136719 7.122396 15.079753 6.992188 15.004883 C 6.861979 14.930014 6.723632 14.84375 6.577148 14.746094 C 6.430664 14.648438 6.282552 14.544271 6.132812 14.433594 C 5.983073 14.322917 5.846354 14.210612 5.722656 14.09668 C 5.598958 13.982748 5.499674 13.868815 5.424805 13.754883 C 5.349935 13.640951 5.3125 13.535156 5.3125 13.4375 Z + F1 M 5.625 15 C 4.85026 15 4.121094 14.851889 3.4375 14.555664 C 2.753906 14.25944 2.158203 13.857422 1.650391 13.349609 C 1.142578 12.841797 0.74056 12.246094 0.444336 11.5625 C 0.148112 10.878906 0 10.14974 0 9.375 C 0 8.600261 0.148112 7.871094 0.444336 7.1875 C 0.74056 6.503906 1.142578 5.908203 1.650391 5.400391 C 2.158203 4.892578 2.753906 4.490561 3.4375 4.194336 C 4.121094 3.898113 4.85026 3.75 5.625 3.75 L 8.125 3.75 C 8.294271 3.75 8.440755 3.81185 8.564453 3.935547 C 8.68815 4.059246 8.75 4.20573 8.75 4.375 C 8.75 4.544271 8.68815 4.690756 8.564453 4.814453 C 8.440755 4.938152 8.294271 5.000001 8.125 5 L 5.625 5 C 5.019531 5.000001 4.451497 5.113934 3.920898 5.341797 C 3.390299 5.569662 2.926432 5.882162 2.529297 6.279297 C 2.132161 6.676434 1.819661 7.140301 1.591797 7.670898 C 1.363932 8.201498 1.25 8.769531 1.25 9.375 C 1.25 9.980469 1.363932 10.548503 1.591797 11.079102 C 1.819661 11.609701 2.132161 12.073568 2.529297 12.470703 C 2.926432 12.867839 3.390299 13.180339 3.920898 13.408203 C 4.451497 13.636068 5.019531 13.75 5.625 13.75 L 8.125 13.75 C 8.294271 13.75 8.440755 13.81185 8.564453 13.935547 C 8.68815 14.059245 8.75 14.205729 8.75 14.375 C 8.75 14.544271 8.68815 14.690756 8.564453 14.814453 C 8.440755 14.938151 8.294271 15 8.125 15 Z M 11.875 15 C 11.705729 15 11.559244 14.938151 11.435547 14.814453 C 11.311849 14.690756 11.25 14.544271 11.25 14.375 C 11.25 14.205729 11.311849 14.059245 11.435547 13.935547 C 11.559244 13.81185 11.705729 13.75 11.875 13.75 L 14.375 13.75 C 14.980469 13.75 15.548502 13.636068 16.079102 13.408203 C 16.609699 13.180339 17.073566 12.867839 17.470703 12.470703 C 17.867838 12.073568 18.180338 11.609701 18.408203 11.079102 C 18.636066 10.548503 18.75 9.980469 18.75 9.375 C 18.75 8.769531 18.636066 8.201498 18.408203 7.670898 C 18.180338 7.140301 17.867838 6.676434 17.470703 6.279297 C 17.073566 5.882162 16.609699 5.569662 16.079102 5.341797 C 15.548502 5.113934 14.980469 5.000001 14.375 5 L 11.875 5 C 11.705729 5.000001 11.559244 4.938152 11.435547 4.814453 C 11.311849 4.690756 11.25 4.544271 11.25 4.375 C 11.25 4.20573 11.311849 4.059246 11.435547 3.935547 C 11.559244 3.81185 11.705729 3.75 11.875 3.75 L 14.375 3.75 C 15.149739 3.75 15.878906 3.898113 16.5625 4.194336 C 17.246094 4.490561 17.841797 4.892578 18.349609 5.400391 C 18.857422 5.908203 19.259439 6.503906 19.555664 7.1875 C 19.851887 7.871094 20 8.600261 20 9.375 C 20 10.14974 19.851887 10.878906 19.555664 11.5625 C 19.259439 12.246094 18.857422 12.841797 18.349609 13.349609 C 17.841797 13.857422 17.246094 14.25944 16.5625 14.555664 C 15.878906 14.851889 15.149739 15 14.375 15 Z M 5.556641 10 C 5.38737 10 5.252278 9.934896 5.151367 9.804688 C 5.050456 9.674479 5 9.53125 5 9.375 C 5 9.21875 5.050456 9.075521 5.151367 8.945312 C 5.252278 8.815104 5.38737 8.75 5.556641 8.75 L 14.443359 8.75 C 14.61263 8.75 14.747721 8.815104 14.848633 8.945312 C 14.949543 9.075521 14.999999 9.21875 15 9.375 C 14.999999 9.53125 14.949543 9.674479 14.848633 9.804688 C 14.747721 9.934896 14.61263 10 14.443359 10 Z + + + M1024,317.5L1024,507C1014.67,495.333 1004.67,484.167 994,473.5C983.333,462.833 972,453 960,444L960,320C960,311.333 958.333,303.083 955,295.25C951.667,287.417 947.083,280.583 941.25,274.75C935.417,268.917 928.583,264.333 920.75,261C912.917,257.667 904.667,256 896,256L522,256L458,298.5C436,312.833 412.333,320 387,320L64,320L64,832C64,841 65.6667,849.417 69,857.25C72.3333,865.083 76.8333,871.833 82.5,877.5C88.1667,883.167 94.9167,887.667 102.75,891C110.583,894.333 119,896 128,896L404.5,896C410.167,907.333 416.25,918.333 422.75,929C429.25,939.667 436.333,950 444,960L125.5,960C108.833,960 92.9167,956.583 77.75,949.75C62.5833,942.917 49.25,933.75 37.75,922.25C26.25,910.75 17.0833,897.417 10.25,882.25C3.41667,867.083 0,851.167 0,834.5L0,189.5C0,172.833 3.41667,156.917 10.25,141.75C17.0833,126.583 26.25,113.25 37.75,101.75C49.25,90.25 62.5833,81.0834 77.75,74.25C92.9167,67.4167 108.833,64.0001 125.5,64L368,64C388,64.0001 407.167,68.5001 425.5,77.5C443.833,86.5001 458.833,99.0001 470.5,115L528,192L898.5,192C915.167,192 931.083,195.417 946.25,202.25C961.417,209.083 974.75,218.25 986.25,229.75C997.75,241.25 1006.92,254.583 1013.75,269.75C1020.58,284.917 1024,300.833 1024,317.5ZM466,216L419,153.5C413,145.5 405.5,139.25 396.5,134.75C387.5,130.25 378,128 368,128L128,128C119,128 110.667,129.667 103,133C95.3333,136.333 88.5833,140.917 82.75,146.75C76.9167,152.583 72.3333,159.333 69,167C65.6667,174.667 64,183 64,192L64,256L387,256C394.333,256 401.5,254.667 408.5,252C415.5,249.333 422.25,246 428.75,242C435.25,238 441.583,233.667 447.75,229C453.917,224.333 460,220 466,216ZM1024,736C1024,775.667 1016.42,813 1001.25,848C986.083,883 965.5,913.5 939.5,939.5C913.5,965.5 883,986.083 848,1001.25C813,1016.42 775.667,1024 736,1024C696,1024 658.5,1016.5 623.5,1001.5C588.5,986.5 558,966 532,940C506,914 485.5,883.5 470.5,848.5C455.5,813.5 448,776 448,736C448,696.333 455.583,659 470.75,624C485.917,589 506.5,558.5 532.5,532.5C558.5,506.5 589,485.917 624,470.75C659,455.583 696.333,448 736,448C762.333,448 787.75,451.417 812.25,458.25C836.75,465.083 859.667,474.75 881,487.25C902.333,499.75 921.833,514.833 939.5,532.5C957.167,550.167 972.25,569.667 984.75,591C997.25,612.333 1006.92,635.25 1013.75,659.75C1020.58,684.25 1024,709.667 1024,736ZM896,576C896,567.333 892.833,559.833 886.5,553.5C880.167,547.167 872.667,544 864,544C857.667,544 852.5,545.167 848.5,547.5C844.5,549.833 841.25,552.917 838.75,556.75C836.25,560.583 834.5,565 833.5,570C832.5,575 832,580.167 832,585.5C816.333,577.167 800.917,570.833 785.75,566.5C770.583,562.167 754,560 736,560C724,560 711.75,561.25 699.25,563.75C686.75,566.25 674.5,569.917 662.5,574.75C650.5,579.583 639.083,585.417 628.25,592.25C617.417,599.083 608,607 600,616C597,619.333 594.667,622.75 593,626.25C591.333,629.75 590.5,633.833 590.5,638.5C590.5,647.5 593.667,655.167 600,661.5C606.333,667.833 614,671 623,671C628.667,671 634.75,668.583 641.25,663.75C647.75,658.917 655.333,653.5 664,647.5C672.667,641.5 682.75,636.083 694.25,631.25C705.75,626.417 719.333,624 735,624C746.667,624 757.5,625.25 767.5,627.75C777.5,630.25 787.667,634.333 798,640L785,640C779,640 773.083,640.25 767.25,640.75C761.417,641.25 756.167,642.583 751.5,644.75C746.833,646.917 743.083,650.167 740.25,654.5C737.417,658.833 736,664.667 736,672C736,680.667 739.167,688.167 745.5,694.5C751.833,700.833 759.333,704 768,704L864,704C872.667,704 880.167,700.833 886.5,694.5C892.833,688.167 896,680.667 896,672ZM881.5,833C881.5,824.333 878.333,816.833 872,810.5C865.667,804.167 858.167,801 849.5,801C842.833,801 836.333,803.417 830,808.25C823.667,813.083 816.333,818.5 808,824.5C799.667,830.5 789.833,835.917 778.5,840.75C767.167,845.583 753.333,848 737,848C725.333,848 714.5,846.75 704.5,844.25C694.5,841.75 684.333,837.667 674,832L687,832C692.667,832 698.417,831.75 704.25,831.25C710.083,830.75 715.333,829.417 720,827.25C724.667,825.083 728.5,821.833 731.5,817.5C734.5,813.167 736,807.333 736,800C736,791.333 732.833,783.833 726.5,777.5C720.167,771.167 712.667,768 704,768L608,768C599.333,768 591.833,771.167 585.5,777.5C579.167,783.833 576,791.333 576,800L576,896C576,904.667 579.167,912.167 585.5,918.5C591.833,924.833 599.333,928 608,928C614.333,928 619.5,926.833 623.5,924.5C627.5,922.167 630.75,919.083 633.25,915.25C635.75,911.417 637.5,907 638.5,902C639.5,897 640,891.833 640,886.5C655.667,894.833 671.083,901.167 686.25,905.5C701.417,909.833 718,912 736,912C748,912 760.333,910.75 773,908.25C785.667,905.75 797.917,902.083 809.75,897.25C821.583,892.417 832.833,886.583 843.5,879.75C854.167,872.917 863.667,865 872,856C878.333,849.333 881.5,841.667 881.5,833Z + M128,220C128,190 134.083,161.667 146.25,135C158.417,108.333 174.75,85 195.25,65C215.75,45 239.5,29.1667 266.5,17.5C293.5,5.83337 322,0 352,0C382.667,0 411.583,5.91669 438.75,17.75C465.917,29.5834 489.667,45.6667 510,66C530.333,86.3334 546.417,110.083 558.25,137.25C570.083,164.417 576,193.333 576,224C576,254.667 570.083,283.583 558.25,310.75C546.417,337.917 530.333,361.667 510,382C489.667,402.333 465.917,418.417 438.75,430.25C411.583,442.083 382.667,448 352,448C320.333,448 290.833,442 263.5,430C236.167,418 212.5,401.667 192.5,381C172.5,360.333 156.75,336.167 145.25,308.5C133.75,280.833 128,251.333 128,220ZM512,220.5C512,198.833 507.667,178.5 499,159.5C490.333,140.5 478.667,123.917 464,109.75C449.333,95.5834 432.333,84.4167 413,76.25C393.667,68.0834 373.333,64.0001 352,64C329.667,64.0001 308.833,68.1667 289.5,76.5C270.167,84.8334 253.25,96.25 238.75,110.75C224.25,125.25 212.833,142.167 204.5,161.5C196.167,180.833 192,201.667 192,224C192,246.333 196.167,267.167 204.5,286.5C212.833,305.833 224.25,322.75 238.75,337.25C253.25,351.75 270.167,363.167 289.5,371.5C308.833,379.833 329.667,384 352,384C375,384 396.25,379.75 415.75,371.25C435.25,362.75 452.167,351.083 466.5,336.25C480.833,321.417 492,304.083 500,284.25C508,264.417 512,243.167 512,220.5ZM640,284.5C640,262.833 644.333,242.5 653,223.5C661.667,204.5 673.333,187.917 688,173.75C702.667,159.583 719.667,148.417 739,140.25C758.333,132.083 778.667,128 800,128C822.333,128 843.167,132.167 862.5,140.5C881.833,148.833 898.75,160.25 913.25,174.75C927.75,189.25 939.167,206.167 947.5,225.5C955.833,244.833 960,265.667 960,288C960,310.333 955.833,331.167 947.5,350.5C939.167,369.833 927.75,386.75 913.25,401.25C898.75,415.75 881.833,427.167 862.5,435.5C843.167,443.833 822.333,448 800,448C777,448 755.75,443.75 736.25,435.25C716.75,426.75 699.833,415.083 685.5,400.25C671.167,385.417 660,368.083 652,348.25C644,328.417 640,307.167 640,284.5ZM896,288C896,275 893.5,262.667 888.5,251C883.5,239.333 876.583,229.083 867.75,220.25C858.917,211.417 848.667,204.5 837,199.5C825.333,194.5 813,192 800,192C787,192 774.667,194.5 763,199.5C751.333,204.5 741.083,211.417 732.25,220.25C723.417,229.083 716.5,239.333 711.5,251C706.5,262.667 704,275 704,288C704,301 706.5,313.333 711.5,325C716.5,336.667 723.417,346.917 732.25,355.75C741.083,364.583 751.333,371.5 763,376.5C774.667,381.5 787,384 800,384C813,384 825.333,381.5 837,376.5C848.667,371.5 858.917,364.583 867.75,355.75C876.583,346.917 883.5,336.667 888.5,325C893.5,313.333 896,301 896,288ZM0,638C0,623.667 2.41667,609 7.25,594C12.0833,579 19,565.417 28,553.25C37,541.083 47.9167,531.167 60.75,523.5C73.5833,515.833 88,512 104,512L600,512C616,512 630.417,515.833 643.25,523.5C656.083,531.167 667,541.083 676,553.25C685,565.417 691.917,579 696.75,594C701.583,609 704,623.667 704,638C704,667 701,694.917 695,721.75C689,748.583 679.75,773.833 667.25,797.5C654.75,821.167 639.083,842.917 620.25,862.75C601.417,882.583 579.167,899.833 553.5,914.5C538.5,923.167 522.75,930.417 506.25,936.25C489.75,942.083 473,946.75 456,950.25C439,953.75 421.75,956.25 404.25,957.75C386.75,959.25 369.333,960 352,960C317,960 282.333,956.833 248,950.5C213.667,944.167 181.167,932.167 150.5,914.5C124.5,899.5 102.083,882.167 83.25,862.5C64.4167,842.833 48.8333,821.25 36.5,797.75C24.1667,774.25 15,749.083 9,722.25C3,695.417 0,667.333 0,638ZM698.5,896C714.833,876.333 728.833,854.5 740.5,830.5C771.5,828.167 800.417,822.417 827.25,813.25C854.083,804.083 877.333,791.083 897,774.25C916.667,757.417 932.083,736.583 943.25,711.75C954.417,686.917 960,657.667 960,624C960,619 959.25,613.75 957.75,608.25C956.25,602.75 954.083,597.583 951.25,592.75C948.417,587.917 944.833,583.917 940.5,580.75C936.167,577.583 931.167,576 925.5,576L775,576C771.333,564.667 767,553.583 762,542.75C757,531.917 751.167,521.667 744.5,512L925.5,512C940.167,512 953.583,515.25 965.75,521.75C977.917,528.25 988.333,536.75 997,547.25C1005.67,557.75 1012.33,569.75 1017,583.25C1021.67,596.75 1024,610.333 1024,624C1024,675.333 1012.5,720.833 989.5,760.5C966.5,800.167 932.333,832.333 887,857C858.333,872.667 827.833,883.083 795.5,888.25C763.167,893.417 730.833,896 698.5,896ZM640,640C640,634.333 639.167,627.75 637.5,620.25C635.833,612.75 633.25,605.75 629.75,599.25C626.25,592.75 621.833,587.25 616.5,582.75C611.167,578.25 605,576 598,576L106,576C99,576 92.8333,578.25 87.5,582.75C82.1667,587.25 77.75,592.75 74.25,599.25C70.75,605.75 68.1667,612.75 66.5,620.25C64.8333,627.75 64,634.333 64,640C64,684.333 71.25,722.583 85.75,754.75C100.25,786.917 120.333,813.5 146,834.5C171.667,855.5 202.083,871 237.25,881C272.417,891 310.667,896 352,896C393.333,896 431.583,891 466.75,881C501.917,871 532.333,855.5 558,834.5C583.667,813.5 603.75,786.917 618.25,754.75C632.75,722.583 640,684.333 640,640Z + diff --git a/Wino.Mail/Styles/ImagePreviewControl.xaml b/Wino.Mail/Styles/ImagePreviewControl.xaml new file mode 100644 index 00000000..67ad57f5 --- /dev/null +++ b/Wino.Mail/Styles/ImagePreviewControl.xaml @@ -0,0 +1,44 @@ + + + + diff --git a/Wino.Mail/Styles/ItemContainerStyles.xaml b/Wino.Mail/Styles/ItemContainerStyles.xaml new file mode 100644 index 00000000..adf0dac2 --- /dev/null +++ b/Wino.Mail/Styles/ItemContainerStyles.xaml @@ -0,0 +1,291 @@ + + + + + #34495e + + + + #ecf0f1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Styles/WinoInfoBar.xaml b/Wino.Mail/Styles/WinoInfoBar.xaml new file mode 100644 index 00000000..07eb899f --- /dev/null +++ b/Wino.Mail/Styles/WinoInfoBar.xaml @@ -0,0 +1,422 @@ + + + + #74b9ff + + + + + + + + + + + + + + + + + + + + + + 1 + + + + #3867d6 + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + 2 + + + + 14 + SemiBold + + 14 + Normal + + 48 + + 38 + 16 + + + -12,0,0,0 + + + + + + + + 16,0,0,0 + + 0,16,14,16 + 16 + + 0,0,16,0 + 0,0,0,0 + 0,14,0,18 + + 0,14,0,0 + 0,14,0,0 + + 12,14,0,0 + 0,4,0,0 + + 16,8,0,0 + 0,12,0,0 + + Cancel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Views/Abstract/AboutPageAbstract.cs b/Wino.Mail/Views/Abstract/AboutPageAbstract.cs new file mode 100644 index 00000000..16d785c5 --- /dev/null +++ b/Wino.Mail/Views/Abstract/AboutPageAbstract.cs @@ -0,0 +1,8 @@ +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public abstract class AboutPageAbstract : BasePage + { + } +} diff --git a/Wino.Mail/Views/Abstract/AccountDetailsPageAbstract.cs b/Wino.Mail/Views/Abstract/AccountDetailsPageAbstract.cs new file mode 100644 index 00000000..036ff394 --- /dev/null +++ b/Wino.Mail/Views/Abstract/AccountDetailsPageAbstract.cs @@ -0,0 +1,8 @@ +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public abstract class AccountDetailsPageAbstract : BasePage + { + } +} diff --git a/Wino.Mail/Views/Abstract/AccountManagementPageAbstract.cs b/Wino.Mail/Views/Abstract/AccountManagementPageAbstract.cs new file mode 100644 index 00000000..c92876a7 --- /dev/null +++ b/Wino.Mail/Views/Abstract/AccountManagementPageAbstract.cs @@ -0,0 +1,10 @@ +using Windows.UI.Xaml.Navigation; +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public abstract class AccountManagementPageAbstract : BasePage + { + + } +} diff --git a/Wino.Mail/Views/Abstract/AppShellAbstract.cs b/Wino.Mail/Views/Abstract/AppShellAbstract.cs new file mode 100644 index 00000000..5817c572 --- /dev/null +++ b/Wino.Mail/Views/Abstract/AppShellAbstract.cs @@ -0,0 +1,8 @@ +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public abstract class AppShellAbstract : BasePage + { + } +} diff --git a/Wino.Mail/Views/Abstract/ComposePageAbstract.cs b/Wino.Mail/Views/Abstract/ComposePageAbstract.cs new file mode 100644 index 00000000..fbe85077 --- /dev/null +++ b/Wino.Mail/Views/Abstract/ComposePageAbstract.cs @@ -0,0 +1,8 @@ +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public abstract class ComposePageAbstract : BasePage + { + } +} diff --git a/Wino.Mail/Views/Abstract/IdlePageAbstract.cs b/Wino.Mail/Views/Abstract/IdlePageAbstract.cs new file mode 100644 index 00000000..9e28d8ff --- /dev/null +++ b/Wino.Mail/Views/Abstract/IdlePageAbstract.cs @@ -0,0 +1,8 @@ +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public abstract class IdlePageAbstract : BasePage + { + } +} diff --git a/Wino.Mail/Views/Abstract/MailListPageAbstract.cs b/Wino.Mail/Views/Abstract/MailListPageAbstract.cs new file mode 100644 index 00000000..70a4cb4c --- /dev/null +++ b/Wino.Mail/Views/Abstract/MailListPageAbstract.cs @@ -0,0 +1,9 @@ +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public class MailListPageAbstract : BasePage + { + + } +} diff --git a/Wino.Mail/Views/Abstract/MailRenderingPageAbstract.cs b/Wino.Mail/Views/Abstract/MailRenderingPageAbstract.cs new file mode 100644 index 00000000..30fbb8d9 --- /dev/null +++ b/Wino.Mail/Views/Abstract/MailRenderingPageAbstract.cs @@ -0,0 +1,26 @@ +using Windows.UI.Xaml; +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public abstract class MailRenderingPageAbstract : BasePage + { + public bool IsDarkEditor + { + get { return (bool)GetValue(IsDarkEditorProperty); } + set { SetValue(IsDarkEditorProperty, value); } + } + + public static readonly DependencyProperty IsDarkEditorProperty = DependencyProperty.Register(nameof(IsDarkEditor), typeof(bool), typeof(MailRenderingPageAbstract), new PropertyMetadata(false, OnIsComposerDarkModeChanged)); + + private static void OnIsComposerDarkModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is MailRenderingPageAbstract page) + { + page.OnEditorThemeChanged(); + } + } + + public virtual void OnEditorThemeChanged() { } + } +} diff --git a/Wino.Mail/Views/Abstract/MergedAccountDetailsPageAbstract.cs b/Wino.Mail/Views/Abstract/MergedAccountDetailsPageAbstract.cs new file mode 100644 index 00000000..29f1a364 --- /dev/null +++ b/Wino.Mail/Views/Abstract/MergedAccountDetailsPageAbstract.cs @@ -0,0 +1,8 @@ +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public abstract class MergedAccountDetailsPageAbstract : BasePage + { + } +} diff --git a/Wino.Mail/Views/Abstract/MessageListPageAbstract.cs b/Wino.Mail/Views/Abstract/MessageListPageAbstract.cs new file mode 100644 index 00000000..d474c4be --- /dev/null +++ b/Wino.Mail/Views/Abstract/MessageListPageAbstract.cs @@ -0,0 +1,6 @@ +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public abstract class MessageListPageAbstract : BasePage { } +} diff --git a/Wino.Mail/Views/Abstract/NewAccountManagementPageAbstract.cs b/Wino.Mail/Views/Abstract/NewAccountManagementPageAbstract.cs new file mode 100644 index 00000000..cdd8b272 --- /dev/null +++ b/Wino.Mail/Views/Abstract/NewAccountManagementPageAbstract.cs @@ -0,0 +1,8 @@ +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public abstract class NewAccountManagementPageAbstract : BasePage + { + } +} diff --git a/Wino.Mail/Views/Abstract/PersonalizationPageAbstract.cs b/Wino.Mail/Views/Abstract/PersonalizationPageAbstract.cs new file mode 100644 index 00000000..ca4d0da0 --- /dev/null +++ b/Wino.Mail/Views/Abstract/PersonalizationPageAbstract.cs @@ -0,0 +1,10 @@ +using Windows.UI.Xaml; +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public abstract class PersonalizationPageAbstract : SettingsPageBase + { + + } +} diff --git a/Wino.Mail/Views/Abstract/ReadingPanePageAbstract.cs b/Wino.Mail/Views/Abstract/ReadingPanePageAbstract.cs new file mode 100644 index 00000000..01ab1f48 --- /dev/null +++ b/Wino.Mail/Views/Abstract/ReadingPanePageAbstract.cs @@ -0,0 +1,6 @@ +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public abstract class ReadingPanePageAbstract : BasePage { } +} diff --git a/Wino.Mail/Views/Abstract/SettingOptionsPageAbstract.cs b/Wino.Mail/Views/Abstract/SettingOptionsPageAbstract.cs new file mode 100644 index 00000000..c9939223 --- /dev/null +++ b/Wino.Mail/Views/Abstract/SettingOptionsPageAbstract.cs @@ -0,0 +1,8 @@ +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public abstract class SettingOptionsPageAbstract : SettingsPageBase + { + } +} diff --git a/Wino.Mail/Views/Abstract/SettingsPageAbstract.cs b/Wino.Mail/Views/Abstract/SettingsPageAbstract.cs new file mode 100644 index 00000000..5abe8aed --- /dev/null +++ b/Wino.Mail/Views/Abstract/SettingsPageAbstract.cs @@ -0,0 +1,6 @@ +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public abstract class SettingsPageAbstract : BasePage { } +} diff --git a/Wino.Mail/Views/Abstract/SettingsPageBase.cs b/Wino.Mail/Views/Abstract/SettingsPageBase.cs new file mode 100644 index 00000000..8b0b7109 --- /dev/null +++ b/Wino.Mail/Views/Abstract/SettingsPageBase.cs @@ -0,0 +1,16 @@ +using Windows.UI.Xaml; +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public class SettingsPageBase : BasePage where T : BaseViewModel + { + public string Title + { + get { return (string)GetValue(TitleProperty); } + set { SetValue(TitleProperty, value); } + } + + public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(SettingsPageBase), new PropertyMetadata(string.Empty)); + } +} diff --git a/Wino.Mail/Views/Abstract/SignatureManagementPageAbstract.cs b/Wino.Mail/Views/Abstract/SignatureManagementPageAbstract.cs new file mode 100644 index 00000000..22936f9c --- /dev/null +++ b/Wino.Mail/Views/Abstract/SignatureManagementPageAbstract.cs @@ -0,0 +1,6 @@ +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public abstract class SignatureManagementPageAbstract : BasePage { } +} diff --git a/Wino.Mail/Views/Abstract/WelcomePageAbstract.cs b/Wino.Mail/Views/Abstract/WelcomePageAbstract.cs new file mode 100644 index 00000000..17959876 --- /dev/null +++ b/Wino.Mail/Views/Abstract/WelcomePageAbstract.cs @@ -0,0 +1,9 @@ +using Wino.Mail.ViewModels; + +namespace Wino.Views.Abstract +{ + public abstract class WelcomePageAbstract : BasePage + { + + } +} diff --git a/Wino.Mail/Views/Account/AccountDetailsPage.xaml b/Wino.Mail/Views/Account/AccountDetailsPage.xaml new file mode 100644 index 00000000..3deac2d1 --- /dev/null +++ b/Wino.Mail/Views/Account/AccountDetailsPage.xaml @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Views/Account/AccountManagementPage.xaml.cs b/Wino.Mail/Views/Account/AccountManagementPage.xaml.cs new file mode 100644 index 00000000..273814fc --- /dev/null +++ b/Wino.Mail/Views/Account/AccountManagementPage.xaml.cs @@ -0,0 +1,16 @@ +using System; +using Windows.UI.Xaml.Navigation; +using Wino.Views.Abstract; + +namespace Wino.Views +{ + public sealed partial class AccountManagementPage : AccountManagementPageAbstract + { + public AccountManagementPage() + { + InitializeComponent(); + + NavigationCacheMode = NavigationCacheMode.Enabled; + } + } +} diff --git a/Wino.Mail/Views/Account/MergedAccountDetailsPage.xaml b/Wino.Mail/Views/Account/MergedAccountDetailsPage.xaml new file mode 100644 index 00000000..405205e7 --- /dev/null +++ b/Wino.Mail/Views/Account/MergedAccountDetailsPage.xaml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Views/Account/MergedAccountDetailsPage.xaml.cs b/Wino.Mail/Views/Account/MergedAccountDetailsPage.xaml.cs new file mode 100644 index 00000000..1dabc46f --- /dev/null +++ b/Wino.Mail/Views/Account/MergedAccountDetailsPage.xaml.cs @@ -0,0 +1,13 @@ +using Wino.Views.Abstract; + + +namespace Wino.Views.Account +{ + public sealed partial class MergedAccountDetailsPage : MergedAccountDetailsPageAbstract + { + public MergedAccountDetailsPage() + { + InitializeComponent(); + } + } +} diff --git a/Wino.Mail/Views/ComposePage.xaml b/Wino.Mail/Views/ComposePage.xaml new file mode 100644 index 00000000..9573fb21 --- /dev/null +++ b/Wino.Mail/Views/ComposePage.xaml @@ -0,0 +1,656 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Transparent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Views/ComposePage.xaml.cs b/Wino.Mail/Views/ComposePage.xaml.cs new file mode 100644 index 00000000..84cffd23 --- /dev/null +++ b/Wino.Mail/Views/ComposePage.xaml.cs @@ -0,0 +1,570 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; +using System.Reactive.Linq; +using System.Threading; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.Messaging; +using CommunityToolkit.WinUI.Controls; +using EmailValidation; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.UI.Xaml.Controls; +using Microsoft.Web.WebView2.Core; +using MimeKit; +using Newtonsoft.Json; +using Windows.Foundation; +using Windows.Storage.Pickers; +using Windows.UI.ViewManagement.Core; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media.Animation; +using Windows.UI.Xaml.Navigation; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Entities; +using Wino.Core.Domain.Enums; +using Wino.Core.Messages.Mails; +using Wino.Core.Messages.Shell; +using Wino.Extensions; +using Wino.Mail.ViewModels.Data; +using Wino.Views.Abstract; + +namespace Wino.Views +{ + public sealed partial class ComposePage : ComposePageAbstract, + IRecipient, + IRecipient, + IRecipient + { + public bool IsComposerDarkMode + { + get { return (bool)GetValue(IsComposerDarkModeProperty); } + set { SetValue(IsComposerDarkModeProperty, value); } + } + + public static readonly DependencyProperty IsComposerDarkModeProperty = DependencyProperty.Register(nameof(IsComposerDarkMode), typeof(bool), typeof(ComposePage), new PropertyMetadata(false, OnIsComposerDarkModeChanged)); + + + public WebView2 GetWebView() => Chromium; + + private TaskCompletionSource DOMLoadedTask = new TaskCompletionSource(); + + private List Disposables = new List(); + + public ComposePage() + { + InitializeComponent(); + + Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF"); + } + + private static async void OnIsComposerDarkModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is ComposePage page) + { + await page.UpdateEditorThemeAsync(); + } + } + + private IDisposable GetSuggestionBoxDisposable(TokenizingTextBox box) + { + return Observable.FromEventPattern, AutoSuggestBoxTextChangedEventArgs>( + x => box.TextChanged += x, + x => box.TextChanged -= x) + .Throttle(TimeSpan.FromMilliseconds(120)) + .ObserveOn(SynchronizationContext.Current) + .Subscribe(t => + { + if (t.EventArgs.Reason == AutoSuggestionBoxTextChangeReason.UserInput) + { + if (t.Sender is AutoSuggestBox senderBox && senderBox.Text.Length >= 3) + { + _ = ViewModel.ContactService.GetAddressInformationAsync(senderBox.Text).ContinueWith(x => + { + _ = ViewModel.ExecuteUIThread(() => + { + var addresses = x.Result; + + senderBox.ItemsSource = addresses; + }); + }); + } + } + }); + } + + private async void AddFilesClicked(object sender, RoutedEventArgs e) + { + // TODO: Pick files + var picker = new FileOpenPicker() + { + SuggestedStartLocation = PickerLocationId.Desktop + }; + + picker.FileTypeFilter.Add("*"); + var files = await picker.PickMultipleFilesAsync(); + + if (files == null) return; + + // Convert files to MailAttachmentViewModel. + + if (files.Any()) + { + foreach (var file in files) + { + if (!ViewModel.IncludedAttachments.Any(a => a.FileName == file.Path)) + { + var attachmentViewModel = await file.ToAttachmentViewModelAsync(); + + ViewModel.IncludedAttachments.Add(attachmentViewModel); + } + } + } + } + + private async void BoldButtonClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('boldButton').click();"); + } + + private async void ItalicButtonClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('italicButton').click();"); + } + + private async void UnderlineButtonClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('underlineButton').click();"); + } + + private async void StrokeButtonClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('strikeButton').click();"); + } + + private async void BulletListButtonClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('bulletListButton').click();"); + } + + private async void OrderedListButtonClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('orderedListButton').click();"); + } + + private async void IncreaseIndentClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('increaseIndentButton').click();"); + } + + private async void DecreaseIndentClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('decreaseIndentButton').click();"); + } + + private async void DirectionButtonClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('directionButton').click();"); + } + + private async void AlignmentChanged(object sender, SelectionChangedEventArgs e) + { + var selectedItem = AlignmentListView.SelectedItem as ComboBoxItem; + var alignment = selectedItem.Tag.ToString(); + + switch (alignment) + { + case "left": + await InvokeScriptSafeAsync("document.getElementById('ql-align-left').click();"); + break; + case "center": + await InvokeScriptSafeAsync("document.getElementById('ql-align-center').click();"); + break; + case "right": + await InvokeScriptSafeAsync("document.getElementById('ql-align-right').click();"); + break; + case "justify": + await InvokeScriptSafeAsync("document.getElementById('ql-align-justify').click();"); + break; + } + } + + public async Task ExecuteScriptFunctionAsync(string functionName, params object[] parameters) + { + string script = functionName + "("; + for (int i = 0; i < parameters.Length; i++) + { + script += JsonConvert.SerializeObject(parameters[i]); + if (i < parameters.Length - 1) + { + script += ", "; + } + } + script += ");"; + + return await Chromium.ExecuteScriptAsync(script); + } + + private async Task InvokeScriptSafeAsync(string function) + { + try + { + return await Chromium.ExecuteScriptAsync(function); + } + catch (Exception) { } + + return string.Empty; + } + + private async void AddImageClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('addImageButton').click();"); + } + + private async Task FocusEditorAsync() + { + await InvokeScriptSafeAsync("quill.focus();"); + + Chromium.Focus(FocusState.Keyboard); + Chromium.Focus(FocusState.Programmatic); + } + + private async void EmojiButtonClicked(object sender, RoutedEventArgs e) + { + CoreInputView.GetForCurrentView().TryShow(CoreInputViewKind.Emoji); + + await FocusEditorAsync(); + } + + private async Task TryGetSelectedTextAsync() + { + try + { + return await Chromium.ExecuteScriptAsync("getSelectedText();"); + } + catch (Exception) { } + + return string.Empty; + } + + private async void LinkButtonClicked(object sender, RoutedEventArgs e) + { + // Get selected text from Quill. + + HyperlinkTextBox.Text = await TryGetSelectedTextAsync(); + } + + public async Task UpdateEditorThemeAsync() + { + await DOMLoadedTask.Task; + + if (IsComposerDarkMode) + { + await InvokeScriptSafeAsync("DarkReader.enable();"); + } + else + { + await InvokeScriptSafeAsync("DarkReader.disable();"); + } + } + + private async Task RenderInternalAsync(string htmlBody) + { + await DOMLoadedTask.Task; + + await UpdateEditorThemeAsync(); + + if (string.IsNullOrEmpty(htmlBody)) + { + await ExecuteScriptFunctionAsync("RenderHTML", " "); + } + else + { + await ExecuteScriptFunctionAsync("RenderHTML", htmlBody); + } + } + + protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) + { + base.OnNavigatingFrom(e); + + DisposeDisposables(); + + Chromium.CoreWebView2Initialized -= ChromiumInitialized; + + if (Chromium.CoreWebView2 != null) + { + Chromium.CoreWebView2.DOMContentLoaded -= DOMLoaded; + Chromium.CoreWebView2.WebMessageReceived -= ScriptMessageRecieved; + } + } + + private void DisposeDisposables() + { + if (Disposables.Any()) + Disposables.ForEach(a => a.Dispose()); + } + + protected override async void OnNavigatedTo(NavigationEventArgs e) + { + base.OnNavigatedTo(e); + + var anim = ConnectedAnimationService.GetForCurrentView().GetAnimation("WebViewConnectedAnimation"); + anim?.TryStart(Chromium); + + DisposeDisposables(); + + Disposables.Add(GetSuggestionBoxDisposable(ToBox)); + Disposables.Add(GetSuggestionBoxDisposable(CCBox)); + Disposables.Add(GetSuggestionBoxDisposable(BccBox)); + + Chromium.CoreWebView2Initialized -= ChromiumInitialized; + Chromium.CoreWebView2Initialized += ChromiumInitialized; + + await Chromium.EnsureCoreWebView2Async(); + + ViewModel.GetHTMLBodyFunction = new Func>(async () => + { + var quillContent = await InvokeScriptSafeAsync("GetHTMLContent();"); + + return JsonConvert.DeserializeObject(quillContent); + }); + + var underlyingThemeService = App.Current.Services.GetService(); + + IsComposerDarkMode = underlyingThemeService.IsUnderlyingThemeDark(); + } + + private async void ChromiumInitialized(Microsoft.UI.Xaml.Controls.WebView2 sender, Microsoft.UI.Xaml.Controls.CoreWebView2InitializedEventArgs args) + { + var editorBundlePath = (await ViewModel.NativeAppService.GetQuillEditorBundlePathAsync()).Replace("full.html", string.Empty); + + Chromium.CoreWebView2.SetVirtualHostNameToFolderMapping("app.example", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow); + Chromium.Source = new Uri("https://app.example/full.html"); + + Chromium.CoreWebView2.DOMContentLoaded -= DOMLoaded; + Chromium.CoreWebView2.DOMContentLoaded += DOMLoaded; + + Chromium.CoreWebView2.WebMessageReceived -= ScriptMessageRecieved; + Chromium.CoreWebView2.WebMessageReceived += ScriptMessageRecieved; + } + + private void ScriptMessageRecieved(CoreWebView2 sender, CoreWebView2WebMessageReceivedEventArgs args) + { + var change = JsonConvert.DeserializeObject(args.WebMessageAsJson); + + bool isEnabled = change.EndsWith("ql-active"); + + if (change.StartsWith("ql-bold")) + BoldButton.IsChecked = isEnabled; + else if (change.StartsWith("ql-italic")) + ItalicButton.IsChecked = isEnabled; + else if (change.StartsWith("ql-underline")) + UnderlineButton.IsChecked = isEnabled; + else if (change.StartsWith("ql-strike")) + StrokeButton.IsChecked = isEnabled; + else if (change.StartsWith("orderedListButton")) + OrderedListButton.IsChecked = isEnabled; + else if (change.StartsWith("bulletListButton")) + BulletListButton.IsChecked = isEnabled; + else if (change.StartsWith("ql-direction")) + DirectionButton.IsChecked = isEnabled; + else if (change.StartsWith("ql-align-left")) + { + AlignmentListView.SelectionChanged -= AlignmentChanged; + AlignmentListView.SelectedIndex = 0; + AlignmentListView.SelectionChanged += AlignmentChanged; + } + else if (change.StartsWith("ql-align-center")) + { + AlignmentListView.SelectionChanged -= AlignmentChanged; + AlignmentListView.SelectedIndex = 1; + AlignmentListView.SelectionChanged += AlignmentChanged; + } + else if (change.StartsWith("ql-align-right")) + { + AlignmentListView.SelectionChanged -= AlignmentChanged; + AlignmentListView.SelectedIndex = 2; + AlignmentListView.SelectionChanged += AlignmentChanged; + } + else if (change.StartsWith("ql-align-justify")) + { + AlignmentListView.SelectionChanged -= AlignmentChanged; + AlignmentListView.SelectedIndex = 3; + AlignmentListView.SelectionChanged += AlignmentChanged; + } + } + + private void DOMLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args) => DOMLoadedTask.TrySetResult(true); + + void IRecipient.Receive(NavigationPaneModeChanged message) + { + if (message.NewMode == MenuPaneMode.Hidden) + TopPanelGrid.Padding = new Thickness(48, 6, 6, 6); + else + TopPanelGrid.Padding = new Thickness(16, 6, 6, 6); + } + + async void IRecipient.Receive(CreateNewComposeMailRequested message) + { + await RenderInternalAsync(message.RenderModel.RenderHtml); + } + + private async void HyperlinkAddClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync($"addHyperlink('{LinkUrlTextBox.Text}')"); + + LinkUrlTextBox.Text = string.Empty; + HyperlinkFlyout.Hide(); + } + + private void BarDynamicOverflowChanging(CommandBar sender, DynamicOverflowItemsChangingEventArgs args) + { + if (args.Action == CommandBarDynamicOverflowAction.AddingToOverflow) + sender.OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Visible; + else + sender.OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Collapsed; + } + + private void ShowCCBCCClicked(object sender, RoutedEventArgs e) + { + CCBCCShowButton.Visibility = Visibility.Collapsed; + + CCTextBlock.Visibility = Visibility.Visible; + CCBox.Visibility = Visibility.Visible; + BccTextBlock.Visibility = Visibility.Visible; + BccBox.Visibility = Visibility.Visible; + } + + private async void TokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args) + { + // Check is valid email. + + if (!EmailValidator.Validate(args.TokenText)) + { + args.Cancel = true; + ViewModel.NotifyInvalidEmail(args.TokenText); + + return; + } + + var deferal = args.GetDeferral(); + + AddressInformation addedItem = null; + + var boxTag = sender.Tag?.ToString(); + + if (boxTag == "ToBox") + addedItem = await ViewModel.GetAddressInformationAsync(args.TokenText, ViewModel.ToItems); + else if (boxTag == "CCBox") + addedItem = await ViewModel.GetAddressInformationAsync(args.TokenText, ViewModel.CCItemsItems); + else if (boxTag == "BCCBox") + addedItem = await ViewModel.GetAddressInformationAsync(args.TokenText, ViewModel.BCCItems); + + if (addedItem == null) + { + args.Cancel = true; + ViewModel.NotifyAddressExists(); + } + else + { + args.Item = addedItem; + } + + deferal.Complete(); + } + + void IRecipient.Receive(ApplicationThemeChanged message) + { + IsComposerDarkMode = message.IsUnderlyingThemeDark; + } + + private void InvertComposerThemeClicked(object sender, RoutedEventArgs e) + { + IsComposerDarkMode = !IsComposerDarkMode; + } + + private void ImportanceClicked(object sender, RoutedEventArgs e) + { + ImportanceFlyout.Hide(); + ImportanceSplitButton.IsChecked = true; + + if (sender is Button senderButton) + { + var selectedImportance = (MessageImportance)senderButton.Tag; + + ViewModel.SelectedMessageImportance = selectedImportance; + (ImportanceSplitButton.Content as SymbolIcon).Symbol = (senderButton.Content as SymbolIcon).Symbol; + } + } + + private void AttachmentClicked(object sender, ItemClickEventArgs e) + { + if (e.ClickedItem is MailAttachmentViewModel attachmentViewModel) + { + ViewModel.RemoveAttachmentCommand.Execute(attachmentViewModel); + } + } + + private async void AddressBoxLostFocus(object sender, RoutedEventArgs e) + { + // Automatically add current text as item if it is valid mail address. + + if (sender is TokenizingTextBox tokenizingTextBox) + { + if (!(tokenizingTextBox.Items.LastOrDefault() is ITokenStringContainer info)) return; + + var currentText = info.Text; + + if (!string.IsNullOrEmpty(currentText) && EmailValidator.Validate(currentText)) + { + var boxTag = tokenizingTextBox.Tag?.ToString(); + + AddressInformation addedItem = null; + ObservableCollection addressCollection = null; + + if (boxTag == "ToBox") + addressCollection = ViewModel.ToItems; + else if (boxTag == "CCBox") + addressCollection = ViewModel.CCItemsItems; + else if (boxTag == "BCCBox") + addressCollection = ViewModel.BCCItems; + + if (addressCollection != null) + addedItem = await ViewModel.GetAddressInformationAsync(currentText, addressCollection); + + // Item has already been added. + if (addedItem == null) + { + tokenizingTextBox.Text = string.Empty; + } + else if (addressCollection != null) + { + addressCollection.Add(addedItem); + tokenizingTextBox.Text = string.Empty; + } + } + } + } + + // Hack: Tokenizing text box losing focus somehow on page Loaded and shifting focus to this element. + // For once we'll switch back to it once CCBBCGotFocus element got focus. + + private bool isInitialFocusHandled = false; + + private void ComposerLoaded(object sender, RoutedEventArgs e) + { + ToBox.Focus(FocusState.Programmatic); + } + + private void CCBBCGotFocus(object sender, RoutedEventArgs e) + { + if (!isInitialFocusHandled) + { + isInitialFocusHandled = true; + ToBox.Focus(FocusState.Programmatic); + } + } + } +} diff --git a/Wino.Mail/Views/IdlePage.xaml b/Wino.Mail/Views/IdlePage.xaml new file mode 100644 index 00000000..7ca48a84 --- /dev/null +++ b/Wino.Mail/Views/IdlePage.xaml @@ -0,0 +1,30 @@ + + + + + + + + + + + + diff --git a/Wino.Mail/Views/IdlePage.xaml.cs b/Wino.Mail/Views/IdlePage.xaml.cs new file mode 100644 index 00000000..dfc0c4f1 --- /dev/null +++ b/Wino.Mail/Views/IdlePage.xaml.cs @@ -0,0 +1,12 @@ +using Wino.Views.Abstract; + +namespace Wino.Views +{ + public sealed partial class IdlePage : IdlePageAbstract + { + public IdlePage() + { + InitializeComponent(); + } + } +} diff --git a/Wino.Mail/Views/ImapSetup/AdvancedImapSetupPage.xaml b/Wino.Mail/Views/ImapSetup/AdvancedImapSetupPage.xaml new file mode 100644 index 00000000..5fb44e21 --- /dev/null +++ b/Wino.Mail/Views/ImapSetup/AdvancedImapSetupPage.xaml @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Views/Settings/SignatureManagementPage.xaml.cs b/Wino.Mail/Views/Settings/SignatureManagementPage.xaml.cs new file mode 100644 index 00000000..a310b8d5 --- /dev/null +++ b/Wino.Mail/Views/Settings/SignatureManagementPage.xaml.cs @@ -0,0 +1,319 @@ +using System; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.Messaging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Web.WebView2.Core; +using Newtonsoft.Json; +using Windows.UI.ViewManagement.Core; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Navigation; +using Wino.Core.Domain.Interfaces; +using Wino.Core.Messages.Mails; +using Wino.Views.Abstract; + +namespace Wino.Views.Settings +{ + public sealed partial class SignatureManagementPage : SignatureManagementPageAbstract, IRecipient + { + private TaskCompletionSource DOMLoadedTask = new TaskCompletionSource(); + + public bool IsComposerDarkMode + { + get { return (bool)GetValue(IsComposerDarkModeProperty); } + set { SetValue(IsComposerDarkModeProperty, value); } + } + + public static readonly DependencyProperty IsComposerDarkModeProperty = DependencyProperty.Register(nameof(IsComposerDarkMode), typeof(bool), typeof(SignatureManagementPage), new PropertyMetadata(false, OnIsComposerDarkModeChanged)); + + public SignatureManagementPage() + { + this.InitializeComponent(); + + // Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF"); + } + + protected override void OnNavigatedFrom(NavigationEventArgs e) + { + base.OnNavigatedFrom(e); + + Chromium.CoreWebView2Initialized -= ChromiumInitialized; + + if (Chromium.CoreWebView2 != null) + { + Chromium.CoreWebView2.DOMContentLoaded -= DOMLoaded; + Chromium.CoreWebView2.WebMessageReceived -= ScriptMessageRecieved; + } + } + + protected override async void OnNavigatedTo(NavigationEventArgs e) + { + base.OnNavigatedTo(e); + + Chromium.CoreWebView2Initialized -= ChromiumInitialized; + Chromium.CoreWebView2Initialized += ChromiumInitialized; + + await Chromium.EnsureCoreWebView2Async(); + + ViewModel.GetHTMLBodyFunction = new Func>(async () => + { + var quillContent = await InvokeScriptSafeAsync("GetHTMLContent();"); + + return JsonConvert.DeserializeObject(quillContent); + }); + + ViewModel.GetTextBodyFunction = new Func>(() => InvokeScriptSafeAsync("GetTextContent();")); + + var underlyingThemeService = App.Current.Services.GetService(); + + IsComposerDarkMode = underlyingThemeService.IsUnderlyingThemeDark(); + } + + private async void HyperlinkAddClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync($"addHyperlink('{LinkUrlTextBox.Text}')"); + + LinkUrlTextBox.Text = string.Empty; + HyperlinkFlyout.Hide(); + } + + private async void BoldButtonClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('boldButton').click();"); + } + + private async void ItalicButtonClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('italicButton').click();"); + } + + private async void UnderlineButtonClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('underlineButton').click();"); + } + + private async void StrokeButtonClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('strikeButton').click();"); + } + + private async void BulletListButtonClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('bulletListButton').click();"); + } + + private async void OrderedListButtonClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('orderedListButton').click();"); + } + + private async void IncreaseIndentClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('increaseIndentButton').click();"); + } + + private async void DecreaseIndentClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('decreaseIndentButton').click();"); + } + + private async void DirectionButtonClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('directionButton').click();"); + } + + private async void AlignmentChanged(object sender, SelectionChangedEventArgs e) + { + var selectedItem = AlignmentListView.SelectedItem as ComboBoxItem; + var alignment = selectedItem.Tag.ToString(); + + switch (alignment) + { + case "left": + await InvokeScriptSafeAsync("document.getElementById('ql-align-left').click();"); + break; + case "center": + await InvokeScriptSafeAsync("document.getElementById('ql-align-center').click();"); + break; + case "right": + await InvokeScriptSafeAsync("document.getElementById('ql-align-right').click();"); + break; + case "justify": + await InvokeScriptSafeAsync("document.getElementById('ql-align-justify').click();"); + break; + } + } + + public async Task ExecuteScriptFunctionAsync(string functionName, params object[] parameters) + { + string script = functionName + "("; + for (int i = 0; i < parameters.Length; i++) + { + script += JsonConvert.SerializeObject(parameters[i]); + if (i < parameters.Length - 1) + { + script += ", "; + } + } + script += ");"; + + return await Chromium.ExecuteScriptAsync(script); + } + + private async Task InvokeScriptSafeAsync(string function) + { + try + { + return await Chromium.ExecuteScriptAsync(function); + } + catch (Exception) { } + + return string.Empty; + } + + private async void AddImageClicked(object sender, RoutedEventArgs e) + { + await InvokeScriptSafeAsync("document.getElementById('addImageButton').click();"); + } + + private async Task FocusEditorAsync() + { + await InvokeScriptSafeAsync("quill.focus();"); + + Chromium.Focus(FocusState.Keyboard); + Chromium.Focus(FocusState.Programmatic); + } + + private async void EmojiButtonClicked(object sender, RoutedEventArgs e) + { + CoreInputView.GetForCurrentView().TryShow(CoreInputViewKind.Emoji); + + await FocusEditorAsync(); + } + + private async Task TryGetSelectedTextAsync() + { + try + { + return await Chromium.ExecuteScriptAsync("getSelectedText();"); + } + catch (Exception) { } + + return string.Empty; + } + + private async void LinkButtonClicked(object sender, RoutedEventArgs e) + { + // Get selected text from Quill. + + HyperlinkTextBox.Text = await TryGetSelectedTextAsync(); + } + + private async Task UpdateEditorThemeAsync() + { + await DOMLoadedTask.Task; + + if (IsComposerDarkMode) + { + await InvokeScriptSafeAsync("DarkReader.enable();"); + } + else + { + await InvokeScriptSafeAsync("DarkReader.disable();"); + } + } + + private async Task RenderInternalAsync(string htmlBody) + { + await DOMLoadedTask.Task; + + await UpdateEditorThemeAsync(); + + if (string.IsNullOrEmpty(htmlBody)) + { + await ExecuteScriptFunctionAsync("RenderHTML", " "); + } + else + { + await ExecuteScriptFunctionAsync("RenderHTML", htmlBody); + + await FocusEditorAsync(); + } + } + + private async void ChromiumInitialized(Microsoft.UI.Xaml.Controls.WebView2 sender, Microsoft.UI.Xaml.Controls.CoreWebView2InitializedEventArgs args) + { + var editorBundlePath = (await ViewModel.NativeAppService.GetQuillEditorBundlePathAsync()).Replace("full.html", string.Empty); + + Chromium.CoreWebView2.SetVirtualHostNameToFolderMapping("app.reader", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow); + Chromium.Source = new Uri("https://app.reader/full.html"); + + Chromium.CoreWebView2.DOMContentLoaded -= DOMLoaded; + Chromium.CoreWebView2.DOMContentLoaded += DOMLoaded; + + Chromium.CoreWebView2.WebMessageReceived -= ScriptMessageRecieved; + Chromium.CoreWebView2.WebMessageReceived += ScriptMessageRecieved; + } + + private static async void OnIsComposerDarkModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) + { + if (obj is ComposePage page) + { + await page.UpdateEditorThemeAsync(); + } + } + + private void ScriptMessageRecieved(CoreWebView2 sender, CoreWebView2WebMessageReceivedEventArgs args) + { + var change = JsonConvert.DeserializeObject(args.WebMessageAsJson); + + bool isEnabled = change.EndsWith("ql-active"); + + if (change.StartsWith("ql-bold")) + BoldButton.IsChecked = isEnabled; + else if (change.StartsWith("ql-italic")) + ItalicButton.IsChecked = isEnabled; + else if (change.StartsWith("ql-underline")) + UnderlineButton.IsChecked = isEnabled; + else if (change.StartsWith("ql-strike")) + StrokeButton.IsChecked = isEnabled; + else if (change.StartsWith("orderedListButton")) + OrderedListButton.IsChecked = isEnabled; + else if (change.StartsWith("bulletListButton")) + BulletListButton.IsChecked = isEnabled; + else if (change.StartsWith("ql-direction")) + DirectionButton.IsChecked = isEnabled; + else if (change.StartsWith("ql-align-left")) + { + AlignmentListView.SelectionChanged -= AlignmentChanged; + AlignmentListView.SelectedIndex = 0; + AlignmentListView.SelectionChanged += AlignmentChanged; + } + else if (change.StartsWith("ql-align-center")) + { + AlignmentListView.SelectionChanged -= AlignmentChanged; + AlignmentListView.SelectedIndex = 1; + AlignmentListView.SelectionChanged += AlignmentChanged; + } + else if (change.StartsWith("ql-align-right")) + { + AlignmentListView.SelectionChanged -= AlignmentChanged; + AlignmentListView.SelectedIndex = 2; + AlignmentListView.SelectionChanged += AlignmentChanged; + } + else if (change.StartsWith("ql-align-justify")) + { + AlignmentListView.SelectionChanged -= AlignmentChanged; + AlignmentListView.SelectedIndex = 3; + AlignmentListView.SelectionChanged += AlignmentChanged; + } + } + + private void DOMLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args) => DOMLoadedTask.TrySetResult(true); + + public async void Receive(HtmlRenderingRequested message) + { + await RenderInternalAsync(message.HtmlBody); + } + } +} diff --git a/Wino.Mail/Views/SettingsPage.xaml b/Wino.Mail/Views/SettingsPage.xaml new file mode 100644 index 00000000..7ab40b63 --- /dev/null +++ b/Wino.Mail/Views/SettingsPage.xaml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wino.Mail/Views/SettingsPage.xaml.cs b/Wino.Mail/Views/SettingsPage.xaml.cs new file mode 100644 index 00000000..bc759574 --- /dev/null +++ b/Wino.Mail/Views/SettingsPage.xaml.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.ObjectModel; +using System.Linq; +using CommunityToolkit.Mvvm.Messaging; +using MoreLinq; +using Windows.UI.Xaml.Media.Animation; +using Windows.UI.Xaml.Navigation; +using Wino.Core.Domain; +using Wino.Core.Domain.Enums; +using Wino.Core.Messages.Navigation; +using Wino.Mail.ViewModels.Data; +using Wino.Views.Abstract; +using Wino.Views.Settings; + +namespace Wino.Views +{ + public sealed partial class SettingsPage : SettingsPageAbstract, IRecipient + { + public ObservableCollection PageHistory { get; set; } = new ObservableCollection(); + + public SettingsPage() + { + InitializeComponent(); + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + base.OnNavigatedTo(e); + + SettingsFrame.Navigate(typeof(SettingOptionsPage), null, new SuppressNavigationTransitionInfo()); + + var initialRequest = new BreadcrumbNavigationRequested(Translator.MenuSettings, WinoPage.SettingOptionsPage); + PageHistory.Add(new BreadcrumbNavigationItemViewModel(initialRequest, true)); + } + + public override void OnLanguageChanged() + { + base.OnLanguageChanged(); + + // Update Settings header in breadcrumb. + + var settingsHeader = PageHistory.FirstOrDefault(); + + if (settingsHeader == null) return; + + settingsHeader.Title = Translator.MenuSettings; + } + + private Type GetNavigationPageType(WinoPage page) + { + switch (page) + { + case WinoPage.AboutPage: + return typeof(AboutPage); + case WinoPage.PersonalizationPage: + return typeof(PersonalizationPage); + case WinoPage.MessageListPage: + return typeof(MessageListPage); + case WinoPage.ReadingPanePage: + return typeof(ReadingPanePage); + default: + return null; + } + } + + void IRecipient.Receive(BreadcrumbNavigationRequested message) + { + var pageType = GetNavigationPageType(message.PageType); + + if (pageType == null) return; + + SettingsFrame.Navigate(pageType, message.Parameter, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight }); + + PageHistory.ForEach(a => a.IsActive = false); + + PageHistory.Add(new BreadcrumbNavigationItemViewModel(message, true)); + } + + private void BreadItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, Microsoft.UI.Xaml.Controls.BreadcrumbBarItemClickedEventArgs args) + { + var clickedPageHistory = PageHistory[args.Index]; + var activeIndex = PageHistory.IndexOf(PageHistory.FirstOrDefault(a => a.IsActive)); + + while (PageHistory.FirstOrDefault(a => a.IsActive) != clickedPageHistory) + { + SettingsFrame.GoBack(new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight }); + PageHistory.RemoveAt(PageHistory.Count - 1); + PageHistory[PageHistory.Count - 1].IsActive = true; + } + } + } +} diff --git a/Wino.Mail/Views/WelcomePage.xaml b/Wino.Mail/Views/WelcomePage.xaml new file mode 100644 index 00000000..25f28769 --- /dev/null +++ b/Wino.Mail/Views/WelcomePage.xaml @@ -0,0 +1,28 @@ + + + + + + + + + diff --git a/Wino.Mail/Views/WelcomePage.xaml.cs b/Wino.Mail/Views/WelcomePage.xaml.cs new file mode 100644 index 00000000..b5206aeb --- /dev/null +++ b/Wino.Mail/Views/WelcomePage.xaml.cs @@ -0,0 +1,19 @@ +using System; +using Windows.System; +using Wino.Views.Abstract; + +namespace Wino.Views +{ + public sealed partial class WelcomePage : WelcomePageAbstract + { + public WelcomePage() + { + InitializeComponent(); + } + + private async void HyperlinkClicked(object sender, Microsoft.Toolkit.Uwp.UI.Controls.LinkClickedEventArgs e) + { + await Launcher.LaunchUriAsync(new System.Uri(e.Link)); + } + } +} diff --git a/Wino.Mail/Wino.Mail.csproj b/Wino.Mail/Wino.Mail.csproj new file mode 100644 index 00000000..0b59c6a6 --- /dev/null +++ b/Wino.Mail/Wino.Mail.csproj @@ -0,0 +1,888 @@ + + + + + 8.0 + + + + + Debug + x86 + {68A432B8-C1B7-494C-8D6D-230788EA683E} + AppContainerExe + Properties + Wino + Wino + en-US + UAP + 10.0.22621.0 + 10.0.17763.0 + 14 + 512 + {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + true + Wino.Mail_TemporaryKey.pfx + False + SHA256 + False + True + Always + x86|x64 + 0 + True + True + C:\Users\bkaan\Desktop\Packages\ + True + + + true + bin\x86\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + x86 + false + prompt + true + + + bin\x86\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + x86 + false + prompt + true + + true + x64 + true + true + true + true + + true + + + + true + bin\ARM\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + ARM + false + prompt + true + + + bin\ARM\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + ARM + false + prompt + true + + true + x64 + true + true + true + true + + true + + + + true + bin\ARM64\Debug\ + DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP + ;2008 + full + ARM64 + false + prompt + true + true + + + bin\ARM64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + ARM64 + false + prompt + true + + true + x64 + true + true + true + true + + true + + + + true + bin\x64\Debug\ + TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS; DEBUG + + + full + x64 + false + prompt + true + + + bin\x64\Release\ + TRACE;NETFX_CORE;WINDOWS_UWP + true + ;2008 + pdbonly + x64 + false + prompt + + true + x64 + true + true + true + true + + true + + + + PackageReference + + false + + + + 1.2.2 + + + 1.0.0 + + + 8.2.2 + + + 8.2.2 + + + 8.2.2 + + + 8.0.240109 + + + 8.0.240109 + + + 8.0.240109 + + + 8.0.240109 + + + 1.0.10 + + + 5.0.3 + + + 5.0.3 + + + 8.0.0 + + + 6.2.14 + + + 7.1.3 + + + 2.8.6 + + + 2.0.1 + + + 5.1.2 + + + 19.6.1 + + + 3.1.1 + + + 8.4.0 + + + 1.8.116 + + + 1.27.1 + + + + + + + + + + + + + + + WinoAppTitleBar.xaml + + + + + + + + + AccountEditDialog.xaml + + + AccountPickerDialog.xaml + + + + CustomThemeBuilderDialog.xaml + + + MoveMailDialog.xaml + + + NewImapSetupDialog.xaml + + + StoreRatingDialog.xaml + + + SystemFolderConfigurationDialog.xaml + + + WinoMessageDialog.xaml + + + TextInputDialog.xaml + + + + + + + + + + + + + + WinoPivotControl.xaml + + + + AccountCreationDialog.xaml + + + + + + + + + + + + + MailItemDisplayInformationControl.xaml + + + + + + + ConfirmationDialog.xaml + + + NewAccountDialog.xaml + + + + + + + + + + + + + + + + + + + + + CommandBarItems.xaml + + + + + + + + + + + + + + + + + + + + + AccountDetailsPage.xaml + + + AccountManagementPage.xaml + + + MergedAccountDetailsPage.xaml + + + ComposePage.xaml + + + IdlePage.xaml + + + AdvancedImapSetupPage.xaml + + + AutoDiscoveryPage.xaml + + + PreparingImapFoldersPage.xaml + + + TestingImapConnectionPage.xaml + + + WelcomeImapSetupPage.xaml + + + MailListPage.xaml + + + MailRenderingPage.xaml + + + NewAccountManagementPage.xaml + + + SettingsPage.xaml + + + AboutPage.xaml + + + MessageListPage.xaml + + + PersonalizationPage.xaml + + + AppShell.xaml + + + ReadingPanePage.xaml + + + SettingOptionsPage.xaml + + + SignatureManagementPage.xaml + + + WelcomePage.xaml + + + + + App.xaml + + + + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + + + Designer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MSBuild:Compile + Designer + + + + + {d9ef0f59-f5f2-4d6c-a5ba-84043d8f3e08} + Wino.BackgroundTasks + + + {CF3312E5-5DA0-4867-9945-49EA7598AF1F} + Wino.Core.Domain + + + {395f19ba-1e42-495c-9db5-1a6f537fccb8} + Wino.Core.UWP + + + {E6B1632A-8901-41E8-9DDF-6793C7698B0B} + Wino.Core + + + {d62f1c03-da57-4709-a640-0283296a8e66} + Wino.Mail.ViewModels + + + + + Windows Desktop Extensions for the UWP + + + + + + + 14.0 + + + bin\x86\Debug .NET Native\ + TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS + false + ;2008 + true + pdbonly + x86 + false + 7.3 + prompt + true + true + true + + true + x64 + true + true + true + true + + true + + + + bin\ARM\Debug .NET Native\ + TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS + true + ;2008 + true + pdbonly + ARM + false + 7.3 + prompt + true + + + bin\ARM64\Debug .NET Native\ + TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS + true + ;2008 + true + pdbonly + ARM64 + false + 7.3 + prompt + true + + + bin\x64\Debug .NET Native\ + TRACE;NETFX_CORE;WINDOWS_UWP;CODE_ANALYSIS + true + ;2008 + true + pdbonly + x64 + false + 7.3 + prompt + true + + + + \ No newline at end of file diff --git a/Wino.Mail/Wino.sln b/Wino.Mail/Wino.sln new file mode 100644 index 00000000..eee1b122 --- /dev/null +++ b/Wino.Mail/Wino.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wino.Mail", "Wino.Mail.csproj", "{0B5C02DC-6B11-437C-9C46-EAB6430C3155}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0B5C02DC-6B11-437C-9C46-EAB6430C3155}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B5C02DC-6B11-437C-9C46-EAB6430C3155}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B5C02DC-6B11-437C-9C46-EAB6430C3155}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B5C02DC-6B11-437C-9C46-EAB6430C3155}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0254B19E-9B52-4594-9F8A-8616BE750243} + EndGlobalSection +EndGlobal diff --git a/Wino.sln b/Wino.sln new file mode 100644 index 00000000..bcd61664 --- /dev/null +++ b/Wino.sln @@ -0,0 +1,231 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32112.339 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wino.Mail", "Wino.Mail\Wino.Mail.csproj", "{68A432B8-C1B7-494C-8D6D-230788EA683E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wino.Core", "Wino.Core\Wino.Core.csproj", "{E6B1632A-8901-41E8-9DDF-6793C7698B0B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wino.Core.UWP", "Wino.Core.UWP\Wino.Core.UWP.csproj", "{395F19BA-1E42-495C-9DB5-1A6F537FCCB8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wino.Core.Domain", "Wino.Core.Domain\Wino.Core.Domain.csproj", "{CF3312E5-5DA0-4867-9945-49EA7598AF1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wino.BackgroundTasks", "Wino.BackgroundTasks\Wino.BackgroundTasks.csproj", "{D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wino.Calendar", "Wino.Calendar\Wino.Calendar.csproj", "{600F4979-DB7E-409D-B7DA-B60BE4C55C35}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wino.Mail.ViewModels", "Wino.Mail.ViewModels\Wino.Mail.ViewModels.csproj", "{D62F1C03-DA57-4709-A640-0283296A8E66}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug .NET Native|Any CPU = Debug .NET Native|Any CPU + Debug .NET Native|ARM64 = Debug .NET Native|ARM64 + Debug .NET Native|x64 = Debug .NET Native|x64 + Debug .NET Native|x86 = Debug .NET Native|x86 + Debug|Any CPU = Debug|Any CPU + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug .NET Native|Any CPU.ActiveCfg = Debug .NET Native|x86 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug .NET Native|ARM64.ActiveCfg = Debug .NET Native|ARM64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug .NET Native|ARM64.Build.0 = Debug .NET Native|ARM64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug .NET Native|ARM64.Deploy.0 = Debug .NET Native|ARM64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug .NET Native|x64.ActiveCfg = Debug .NET Native|x64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug .NET Native|x64.Build.0 = Debug .NET Native|x64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug .NET Native|x64.Deploy.0 = Debug .NET Native|x64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug .NET Native|x86.ActiveCfg = Debug .NET Native|x86 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug .NET Native|x86.Build.0 = Debug .NET Native|x86 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug .NET Native|x86.Deploy.0 = Debug .NET Native|x86 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug|Any CPU.ActiveCfg = Debug|x86 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug|Any CPU.Build.0 = Debug|x86 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug|Any CPU.Deploy.0 = Debug|x86 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug|ARM64.Build.0 = Debug|ARM64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug|x64.ActiveCfg = Debug|x64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug|x64.Build.0 = Debug|x64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug|x64.Deploy.0 = Debug|x64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug|x86.ActiveCfg = Debug|x86 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug|x86.Build.0 = Debug|x86 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Debug|x86.Deploy.0 = Debug|x86 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Release|Any CPU.ActiveCfg = Release|x86 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Release|ARM64.ActiveCfg = Release|ARM64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Release|ARM64.Build.0 = Release|ARM64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Release|ARM64.Deploy.0 = Release|ARM64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Release|x64.ActiveCfg = Release|x64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Release|x64.Build.0 = Release|x64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Release|x64.Deploy.0 = Release|x64 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Release|x86.ActiveCfg = Release|x86 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Release|x86.Build.0 = Release|x86 + {68A432B8-C1B7-494C-8D6D-230788EA683E}.Release|x86.Deploy.0 = Release|x86 + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Debug .NET Native|Any CPU.ActiveCfg = Debug .NET Native|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Debug .NET Native|Any CPU.Build.0 = Debug .NET Native|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Debug .NET Native|ARM64.ActiveCfg = Debug .NET Native|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Debug .NET Native|ARM64.Build.0 = Debug .NET Native|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Debug .NET Native|x64.ActiveCfg = Debug .NET Native|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Debug .NET Native|x64.Build.0 = Debug .NET Native|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Debug .NET Native|x86.ActiveCfg = Debug .NET Native|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Debug .NET Native|x86.Build.0 = Debug .NET Native|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Debug|ARM64.Build.0 = Debug|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Debug|x64.ActiveCfg = Debug|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Debug|x64.Build.0 = Debug|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Debug|x86.ActiveCfg = Debug|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Debug|x86.Build.0 = Debug|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Release|Any CPU.Build.0 = Release|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Release|ARM64.ActiveCfg = Release|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Release|ARM64.Build.0 = Release|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Release|x64.ActiveCfg = Release|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Release|x64.Build.0 = Release|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Release|x86.ActiveCfg = Release|Any CPU + {E6B1632A-8901-41E8-9DDF-6793C7698B0B}.Release|x86.Build.0 = Release|Any CPU + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug .NET Native|Any CPU.ActiveCfg = Debug|Any CPU + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug .NET Native|Any CPU.Build.0 = Debug|Any CPU + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug .NET Native|ARM64.ActiveCfg = Debug|ARM64 + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug .NET Native|ARM64.Build.0 = Debug|ARM64 + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug .NET Native|x64.ActiveCfg = Debug|x64 + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug .NET Native|x64.Build.0 = Debug|x64 + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug .NET Native|x86.ActiveCfg = Debug|x86 + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug .NET Native|x86.Build.0 = Debug|x86 + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|ARM64.Build.0 = Debug|ARM64 + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|x64.ActiveCfg = Debug|x64 + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|x64.Build.0 = Debug|x64 + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|x86.ActiveCfg = Debug|x86 + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Debug|x86.Build.0 = Debug|x86 + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|Any CPU.Build.0 = Release|Any CPU + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|ARM64.ActiveCfg = Release|ARM64 + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|ARM64.Build.0 = Release|ARM64 + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|x64.ActiveCfg = Release|x64 + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|x64.Build.0 = Release|x64 + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|x86.ActiveCfg = Release|x86 + {395F19BA-1E42-495C-9DB5-1A6F537FCCB8}.Release|x86.Build.0 = Release|x86 + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Debug .NET Native|Any CPU.ActiveCfg = Debug|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Debug .NET Native|Any CPU.Build.0 = Debug|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Debug .NET Native|ARM64.ActiveCfg = Debug|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Debug .NET Native|ARM64.Build.0 = Debug|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Debug .NET Native|x64.ActiveCfg = Debug|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Debug .NET Native|x64.Build.0 = Debug|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Debug .NET Native|x86.ActiveCfg = Debug|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Debug .NET Native|x86.Build.0 = Debug|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Debug|ARM64.Build.0 = Debug|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Debug|x64.ActiveCfg = Debug|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Debug|x64.Build.0 = Debug|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Debug|x86.ActiveCfg = Debug|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Debug|x86.Build.0 = Debug|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Release|Any CPU.Build.0 = Release|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Release|ARM64.ActiveCfg = Release|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Release|ARM64.Build.0 = Release|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Release|x64.ActiveCfg = Release|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Release|x64.Build.0 = Release|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Release|x86.ActiveCfg = Release|Any CPU + {CF3312E5-5DA0-4867-9945-49EA7598AF1F}.Release|x86.Build.0 = Release|Any CPU + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Debug .NET Native|Any CPU.ActiveCfg = Debug|Any CPU + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Debug .NET Native|Any CPU.Build.0 = Debug|Any CPU + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Debug .NET Native|ARM64.ActiveCfg = Debug|ARM64 + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Debug .NET Native|ARM64.Build.0 = Debug|ARM64 + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Debug .NET Native|x64.ActiveCfg = Debug|x64 + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Debug .NET Native|x64.Build.0 = Debug|x64 + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Debug .NET Native|x86.ActiveCfg = Debug|x86 + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Debug .NET Native|x86.Build.0 = Debug|x86 + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Debug|ARM64.Build.0 = Debug|ARM64 + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Debug|x64.ActiveCfg = Debug|x64 + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Debug|x64.Build.0 = Debug|x64 + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Debug|x86.ActiveCfg = Debug|x86 + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Debug|x86.Build.0 = Debug|x86 + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Release|Any CPU.Build.0 = Release|Any CPU + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Release|ARM64.ActiveCfg = Release|ARM64 + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Release|ARM64.Build.0 = Release|ARM64 + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Release|x64.ActiveCfg = Release|x64 + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Release|x64.Build.0 = Release|x64 + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Release|x86.ActiveCfg = Release|x86 + {D9EF0F59-F5F2-4D6C-A5BA-84043D8F3E08}.Release|x86.Build.0 = Release|x86 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug .NET Native|Any CPU.ActiveCfg = Debug|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug .NET Native|Any CPU.Build.0 = Debug|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug .NET Native|Any CPU.Deploy.0 = Debug|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug .NET Native|ARM64.ActiveCfg = Debug|ARM64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug .NET Native|ARM64.Build.0 = Debug|ARM64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug .NET Native|ARM64.Deploy.0 = Debug|ARM64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug .NET Native|x64.ActiveCfg = Debug|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug .NET Native|x64.Build.0 = Debug|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug .NET Native|x64.Deploy.0 = Debug|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug .NET Native|x86.ActiveCfg = Debug|x86 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug .NET Native|x86.Build.0 = Debug|x86 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug .NET Native|x86.Deploy.0 = Debug|x86 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|Any CPU.ActiveCfg = Debug|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|Any CPU.Build.0 = Debug|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|Any CPU.Deploy.0 = Debug|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|ARM64.Build.0 = Debug|ARM64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x64.ActiveCfg = Debug|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x64.Build.0 = Debug|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x64.Deploy.0 = Debug|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x86.ActiveCfg = Debug|x86 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x86.Build.0 = Debug|x86 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Debug|x86.Deploy.0 = Debug|x86 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|Any CPU.ActiveCfg = Release|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|Any CPU.Build.0 = Release|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|Any CPU.Deploy.0 = Release|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|ARM64.ActiveCfg = Release|ARM64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|ARM64.Build.0 = Release|ARM64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|ARM64.Deploy.0 = Release|ARM64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x64.ActiveCfg = Release|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x64.Build.0 = Release|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x64.Deploy.0 = Release|x64 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x86.ActiveCfg = Release|x86 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x86.Build.0 = Release|x86 + {600F4979-DB7E-409D-B7DA-B60BE4C55C35}.Release|x86.Deploy.0 = Release|x86 + {D62F1C03-DA57-4709-A640-0283296A8E66}.Debug .NET Native|Any CPU.ActiveCfg = Debug|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Debug .NET Native|Any CPU.Build.0 = Debug|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Debug .NET Native|ARM64.ActiveCfg = Debug|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Debug .NET Native|ARM64.Build.0 = Debug|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Debug .NET Native|x64.ActiveCfg = Debug|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Debug .NET Native|x64.Build.0 = Debug|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Debug .NET Native|x86.ActiveCfg = Debug|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Debug .NET Native|x86.Build.0 = Debug|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Debug|ARM64.Build.0 = Debug|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Debug|x64.ActiveCfg = Debug|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Debug|x64.Build.0 = Debug|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Debug|x86.ActiveCfg = Debug|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Debug|x86.Build.0 = Debug|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Release|Any CPU.Build.0 = Release|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Release|ARM64.ActiveCfg = Release|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Release|ARM64.Build.0 = Release|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Release|x64.ActiveCfg = Release|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Release|x64.Build.0 = Release|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Release|x86.ActiveCfg = Release|Any CPU + {D62F1C03-DA57-4709-A640-0283296A8E66}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {721F946E-F69F-4987-823A-D084B436FC1E} + EndGlobalSection +EndGlobal diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 00000000..2b709c3d --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,3 @@ +files: + - source: /Wino.Core.Domain/Translations/en_US/resources.json + translation: /Wino.Core.Domain/Translations/%locale_with_underscore%/resources.json