diff --git a/.venv/bin/Activate.ps1 b/.venv/bin/Activate.ps1 new file mode 100644 index 00000000..2fb3852c --- /dev/null +++ b/.venv/bin/Activate.ps1 @@ -0,0 +1,241 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virutal environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/.venv/bin/activate b/.venv/bin/activate new file mode 100644 index 00000000..b0ce86a0 --- /dev/null +++ b/.venv/bin/activate @@ -0,0 +1,66 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null + fi + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1="(.venv) ${PS1:-}" + export PS1 +fi + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null +fi diff --git a/.venv/bin/activate.csh b/.venv/bin/activate.csh new file mode 100644 index 00000000..93f2cc10 --- /dev/null +++ b/.venv/bin/activate.csh @@ -0,0 +1,25 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = "(.venv) $prompt" +endif + +alias pydoc python -m pydoc + +rehash diff --git a/.venv/bin/activate.fish b/.venv/bin/activate.fish new file mode 100644 index 00000000..6904ea1e --- /dev/null +++ b/.venv/bin/activate.fish @@ -0,0 +1,64 @@ +# This file must be used with "source /bin/activate.fish" *from fish* +# (https://fishshell.com/); you cannot run it directly. + +function deactivate -d "Exit virtual environment and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + functions -e fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + + set -e VIRTUAL_ENV + if test "$argv[1]" != "nondestructive" + # Self-destruct! + functions -e deactivate + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV "/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# Unset PYTHONHOME if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # Save the current fish_prompt function as the function _old_fish_prompt. + functions -c fish_prompt _old_fish_prompt + + # With the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command. + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. + printf "%s%s%s" (set_color 4B8BBE) "(.venv) " (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/.venv/bin/ghp-import b/.venv/bin/ghp-import new file mode 100755 index 00000000..6f682850 --- /dev/null +++ b/.venv/bin/ghp-import @@ -0,0 +1,7 @@ +#!/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/bin/python3 +import sys +from ghp_import import main +if __name__ == '__main__': + if sys.argv[0].endswith('.exe'): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/htmlmin b/.venv/bin/htmlmin new file mode 100755 index 00000000..a25c3d7c --- /dev/null +++ b/.venv/bin/htmlmin @@ -0,0 +1,7 @@ +#!/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/bin/python3 +import sys +from htmlmin.command import main +if __name__ == '__main__': + if sys.argv[0].endswith('.exe'): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/markdown_py b/.venv/bin/markdown_py new file mode 100755 index 00000000..6e63bc8e --- /dev/null +++ b/.venv/bin/markdown_py @@ -0,0 +1,7 @@ +#!/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/bin/python3 +import sys +from markdown.__main__ import run +if __name__ == '__main__': + if sys.argv[0].endswith('.exe'): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(run()) diff --git a/.venv/bin/mkdocs b/.venv/bin/mkdocs new file mode 100755 index 00000000..204b3c02 --- /dev/null +++ b/.venv/bin/mkdocs @@ -0,0 +1,7 @@ +#!/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/bin/python3 +import sys +from mkdocs.__main__ import cli +if __name__ == '__main__': + if sys.argv[0].endswith('.exe'): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(cli()) diff --git a/.venv/bin/mkdocs-get-deps b/.venv/bin/mkdocs-get-deps new file mode 100755 index 00000000..5193dcfa --- /dev/null +++ b/.venv/bin/mkdocs-get-deps @@ -0,0 +1,7 @@ +#!/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/bin/python3 +import sys +from mkdocs_get_deps.__main__ import cli +if __name__ == '__main__': + if sys.argv[0].endswith('.exe'): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(cli()) diff --git a/.venv/bin/natsort b/.venv/bin/natsort new file mode 100755 index 00000000..bc25ab85 --- /dev/null +++ b/.venv/bin/natsort @@ -0,0 +1,7 @@ +#!/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/bin/python3 +import sys +from natsort.__main__ import main +if __name__ == '__main__': + if sys.argv[0].endswith('.exe'): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/normalizer b/.venv/bin/normalizer new file mode 100755 index 00000000..d05795f6 --- /dev/null +++ b/.venv/bin/normalizer @@ -0,0 +1,7 @@ +#!/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/bin/python3 +import sys +from charset_normalizer.cli import cli_detect +if __name__ == '__main__': + if sys.argv[0].endswith('.exe'): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(cli_detect()) diff --git a/.venv/bin/pip b/.venv/bin/pip new file mode 100755 index 00000000..b69fd522 --- /dev/null +++ b/.venv/bin/pip @@ -0,0 +1,8 @@ +#!/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pip3 b/.venv/bin/pip3 new file mode 100755 index 00000000..b69fd522 --- /dev/null +++ b/.venv/bin/pip3 @@ -0,0 +1,8 @@ +#!/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pip3.9 b/.venv/bin/pip3.9 new file mode 100755 index 00000000..b69fd522 --- /dev/null +++ b/.venv/bin/pip3.9 @@ -0,0 +1,8 @@ +#!/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pybabel b/.venv/bin/pybabel new file mode 100755 index 00000000..2366c3c4 --- /dev/null +++ b/.venv/bin/pybabel @@ -0,0 +1,7 @@ +#!/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/bin/python3 +import sys +from babel.messages.frontend import main +if __name__ == '__main__': + if sys.argv[0].endswith('.exe'): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/pygmentize b/.venv/bin/pygmentize new file mode 100755 index 00000000..ac542612 --- /dev/null +++ b/.venv/bin/pygmentize @@ -0,0 +1,7 @@ +#!/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/bin/python3 +import sys +from pygments.cmdline import main +if __name__ == '__main__': + if sys.argv[0].endswith('.exe'): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/python b/.venv/bin/python new file mode 120000 index 00000000..b8a0adbb --- /dev/null +++ b/.venv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/.venv/bin/python3 b/.venv/bin/python3 new file mode 120000 index 00000000..f25545fe --- /dev/null +++ b/.venv/bin/python3 @@ -0,0 +1 @@ +/Library/Developer/CommandLineTools/usr/bin/python3 \ No newline at end of file diff --git a/.venv/bin/python3.9 b/.venv/bin/python3.9 new file mode 120000 index 00000000..b8a0adbb --- /dev/null +++ b/.venv/bin/python3.9 @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/.venv/bin/watchmedo b/.venv/bin/watchmedo new file mode 100755 index 00000000..85914296 --- /dev/null +++ b/.venv/bin/watchmedo @@ -0,0 +1,7 @@ +#!/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/bin/python3 +import sys +from watchdog.watchmedo import main +if __name__ == '__main__': + if sys.argv[0].endswith('.exe'): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/lib/python3.9/site-packages/_distutils_hack/__init__.py b/.venv/lib/python3.9/site-packages/_distutils_hack/__init__.py new file mode 100644 index 00000000..5f40996a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/_distutils_hack/__init__.py @@ -0,0 +1,128 @@ +import sys +import os +import re +import importlib +import warnings + + +is_pypy = '__pypy__' in sys.builtin_module_names + + +warnings.filterwarnings('ignore', + r'.+ distutils\b.+ deprecated', + DeprecationWarning) + + +def warn_distutils_present(): + if 'distutils' not in sys.modules: + return + if is_pypy and sys.version_info < (3, 7): + # PyPy for 3.6 unconditionally imports distutils, so bypass the warning + # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 + return + warnings.warn( + "Distutils was imported before Setuptools, but importing Setuptools " + "also replaces the `distutils` module in `sys.modules`. This may lead " + "to undesirable behaviors or errors. To avoid these issues, avoid " + "using distutils directly, ensure that setuptools is installed in the " + "traditional way (e.g. not an editable install), and/or make sure " + "that setuptools is always imported before distutils.") + + +def clear_distutils(): + if 'distutils' not in sys.modules: + return + warnings.warn("Setuptools is replacing distutils.") + mods = [name for name in sys.modules if re.match(r'distutils\b', name)] + for name in mods: + del sys.modules[name] + + +def enabled(): + """ + Allow selection of distutils by environment variable. + """ + which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib') + return which == 'local' + + +def ensure_local_distutils(): + clear_distutils() + distutils = importlib.import_module('setuptools._distutils') + distutils.__name__ = 'distutils' + sys.modules['distutils'] = distutils + + # sanity check that submodules load as expected + core = importlib.import_module('distutils.core') + assert '_distutils' in core.__file__, core.__file__ + + +def do_override(): + """ + Ensure that the local copy of distutils is preferred over stdlib. + + See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 + for more motivation. + """ + if enabled(): + warn_distutils_present() + ensure_local_distutils() + + +class DistutilsMetaFinder: + def find_spec(self, fullname, path, target=None): + if path is not None: + return + + method_name = 'spec_for_{fullname}'.format(**locals()) + method = getattr(self, method_name, lambda: None) + return method() + + def spec_for_distutils(self): + import importlib.abc + import importlib.util + + class DistutilsLoader(importlib.abc.Loader): + + def create_module(self, spec): + return importlib.import_module('setuptools._distutils') + + def exec_module(self, module): + pass + + return importlib.util.spec_from_loader('distutils', DistutilsLoader()) + + def spec_for_pip(self): + """ + Ensure stdlib distutils when running under pip. + See pypa/pip#8761 for rationale. + """ + if self.pip_imported_during_build(): + return + clear_distutils() + self.spec_for_distutils = lambda: None + + @staticmethod + def pip_imported_during_build(): + """ + Detect if pip is being imported in a build script. Ref #2355. + """ + import traceback + return any( + frame.f_globals['__file__'].endswith('setup.py') + for frame, line in traceback.walk_stack(None) + ) + + +DISTUTILS_FINDER = DistutilsMetaFinder() + + +def add_shim(): + sys.meta_path.insert(0, DISTUTILS_FINDER) + + +def remove_shim(): + try: + sys.meta_path.remove(DISTUTILS_FINDER) + except ValueError: + pass diff --git a/.venv/lib/python3.9/site-packages/_distutils_hack/override.py b/.venv/lib/python3.9/site-packages/_distutils_hack/override.py new file mode 100644 index 00000000..2cc433a4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/_distutils_hack/override.py @@ -0,0 +1 @@ +__import__('_distutils_hack').do_override() diff --git a/.venv/lib/python3.9/site-packages/_watchdog_fsevents.cpython-39-darwin.so b/.venv/lib/python3.9/site-packages/_watchdog_fsevents.cpython-39-darwin.so new file mode 100755 index 00000000..9f9a610a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/_watchdog_fsevents.cpython-39-darwin.so differ diff --git a/.venv/lib/python3.9/site-packages/_yaml/__init__.py b/.venv/lib/python3.9/site-packages/_yaml/__init__.py new file mode 100644 index 00000000..7baa8c4b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/_yaml/__init__.py @@ -0,0 +1,33 @@ +# This is a stub package designed to roughly emulate the _yaml +# extension module, which previously existed as a standalone module +# and has been moved into the `yaml` package namespace. +# It does not perfectly mimic its old counterpart, but should get +# close enough for anyone who's relying on it even when they shouldn't. +import yaml + +# in some circumstances, the yaml module we imoprted may be from a different version, so we need +# to tread carefully when poking at it here (it may not have the attributes we expect) +if not getattr(yaml, '__with_libyaml__', False): + from sys import version_info + + exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError + raise exc("No module named '_yaml'") +else: + from yaml._yaml import * + import warnings + warnings.warn( + 'The _yaml extension module is now located at yaml._yaml' + ' and its location is subject to change. To use the' + ' LibYAML-based parser and emitter, import from `yaml`:' + ' `from yaml import CLoader as Loader, CDumper as Dumper`.', + DeprecationWarning + ) + del warnings + # Don't `del yaml` here because yaml is actually an existing + # namespace member of _yaml. + +__name__ = '_yaml' +# If the module is top-level (i.e. not a part of any specific package) +# then the attribute should be set to ''. +# https://docs.python.org/3.8/library/types.html +__package__ = '' diff --git a/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/LICENSE b/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/LICENSE new file mode 100644 index 00000000..6ddae98e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2013-2025 by the Babel Team, see AUTHORS for more information. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/METADATA b/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/METADATA new file mode 100644 index 00000000..8cdc8462 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/METADATA @@ -0,0 +1,54 @@ +Metadata-Version: 2.2 +Name: babel +Version: 2.17.0 +Summary: Internationalization utilities +Home-page: https://babel.pocoo.org/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Aarni Koskela +Maintainer-email: akx@iki.fi +License: BSD-3-Clause +Project-URL: Source, https://github.com/python-babel/babel +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.8 +License-File: LICENSE +Requires-Dist: pytz>=2015.7; python_version < "3.9" +Provides-Extra: dev +Requires-Dist: tzdata; sys_platform == "win32" and extra == "dev" +Requires-Dist: backports.zoneinfo; python_version < "3.9" and extra == "dev" +Requires-Dist: freezegun~=1.0; extra == "dev" +Requires-Dist: jinja2>=3.0; extra == "dev" +Requires-Dist: pytest-cov; extra == "dev" +Requires-Dist: pytest>=6.0; extra == "dev" +Requires-Dist: pytz; extra == "dev" +Requires-Dist: setuptools; extra == "dev" +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: license +Dynamic: maintainer +Dynamic: maintainer-email +Dynamic: project-url +Dynamic: provides-extra +Dynamic: requires-dist +Dynamic: requires-python +Dynamic: summary + +A collection of tools for internationalizing Python applications. diff --git a/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/RECORD b/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/RECORD new file mode 100644 index 00000000..7b34fc38 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/RECORD @@ -0,0 +1,1132 @@ +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/core.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/dates.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/languages.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/lists.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/localedata.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/localtime/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/localtime/_fallback.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/localtime/_helpers.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/localtime/_unix.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/localtime/_win32.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/messages/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/messages/_compat.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/messages/catalog.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/messages/checkers.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/messages/extract.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/messages/frontend.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/messages/jslexer.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/messages/mofile.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/messages/plurals.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/messages/pofile.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/messages/setuptools_frontend.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/numbers.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/plural.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/support.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/units.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/babel/util.cpython-39.pyc,, +../../../bin/pybabel,sha256=bq5FUhp09uxzCa3pJobFEJKed0jZ7eYuIqodKOBQcAs,237 +babel-2.17.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +babel-2.17.0.dist-info/LICENSE,sha256=eAGoT-gRJ5yzUL20-xj5TDaxjvgT-mqsBDtsbTlITQ0,1531 +babel-2.17.0.dist-info/METADATA,sha256=xbADd4FX7x9g3Jol93V9dBRbALmMLlTVcQCDcl12nnw,2032 +babel-2.17.0.dist-info/RECORD,, +babel-2.17.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91 +babel-2.17.0.dist-info/entry_points.txt,sha256=Y2Cr1P3E8Yt7kqzvVz4wnTvD1H3-BVD4FOkVqHIGBfc,750 +babel-2.17.0.dist-info/top_level.txt,sha256=mQO3vNkqlcYs_xRaL5EpRIy1IRjMp4N9_vdwmiemPXo,6 +babel/__init__.py,sha256=Ow3KHphrtw4WSFK-FzacGR8JVX5JQZ1YCdfJKD8UYS0,882 +babel/core.py,sha256=QbuWyWBg4RJlYQ5Avg-ci71j4ZQ07VUGhhVxdI9y-Qo,45371 +babel/dates.py,sha256=fJCaifpved33Rf5T1thxxPG6d3MmPU99wnIO2RAA-mc,76378 +babel/global.dat,sha256=wRYHp_nSwZk6S1S0kn3g20zHAYm2vr7ThmvV7uqDqgM,459316 +babel/languages.py,sha256=2LDTV9WizAuZVam0vfw-Q-QKYCQpF5DxLbmVoat7TKI,2844 +babel/lists.py,sha256=Y4m1gScEDZlHAweDb-H5J6bEhlzTFmhKCWywS2h6V8M,4361 +babel/locale-data/LICENSE.unicode,sha256=tMCujvBPcFn5bOW74EZ_n-b22Bu-E1F3Ad_rlh-00LY,2033 +babel/locale-data/aa.dat,sha256=CI5kOVaXWX2CQoHT6AXhrOMwFPCiQyd7Wke2QBJ1UtE,2795 +babel/locale-data/aa_DJ.dat,sha256=VKjSgKVsftG91nj0-AQMYFuF_p4YAGfRkgAvycB2EYU,1112 +babel/locale-data/aa_ER.dat,sha256=6b6qROwoeOq0-batM1Z7Ji2gvGDogl3ODcftt1Jkm9g,637 +babel/locale-data/aa_ET.dat,sha256=piwFu94H7e-jPwd4ZaWiDc-j2nAH_GiC5UZ2IxMx7bs,635 +babel/locale-data/ab.dat,sha256=xeio1NLBNIAXcafnonWjO3w0zofwM-aKr0-RiaL7pRQ,95311 +babel/locale-data/ab_GE.dat,sha256=b5be2Uv1YtP-bkMasJMzoQPxJPNLFqifXnCiK8rCyLE,635 +babel/locale-data/af.dat,sha256=pO9_3BpJSQAJ3gZ_j6ZdUpGq3zqEv6CIZZ-CpJTXwr4,144732 +babel/locale-data/af_NA.dat,sha256=zif__TyNY44mSCYOCrrglsyr_H31JhBNPwSJftg8200,1450 +babel/locale-data/af_ZA.dat,sha256=6lT-mLg7LRtyAEhWhiF2xVtiPyzEa28U6hsFw_BMrZE,635 +babel/locale-data/agq.dat,sha256=JJbF6NFR9qwwQTrIyrLh04DFJniIqDuFsbmHDGu5ThM,16628 +babel/locale-data/agq_CM.dat,sha256=hYtg7pRw7J6TsYHvhlEEv7DqpyXoK3KS9DAqtYtgBX0,636 +babel/locale-data/ak.dat,sha256=L73y_ylhZy39SlEaAwA4jCqP_92qLvIlsagl-o2RlKE,116224 +babel/locale-data/ak_GH.dat,sha256=tM-OSpXWyBwZFfKPVStWBbFv3dwNj_7AqcBz4ONrOQs,616 +babel/locale-data/am.dat,sha256=TddfeRISFvzjTFpB-vH3gjSKs2r8iT3CYckrriibXZY,173260 +babel/locale-data/am_ET.dat,sha256=2VZDSh5bY9vLLuTqcKYG66p5OYlvPRPX58rpNTFWPok,635 +babel/locale-data/an.dat,sha256=a1gEztcqWoMLW3f5_jlDxcPsYY-ItHKVud7gjisrS4E,28050 +babel/locale-data/an_ES.dat,sha256=rQuq-zKj7-fCjY-5fGGoGJT1x60pAyCEAVEswXPnHrI,653 +babel/locale-data/ann.dat,sha256=fUenvOdPjqayLEiVDRNncK8SOBi9gAGAdxMuOATlsI0,737 +babel/locale-data/ann_NG.dat,sha256=0i2K6cv6eCurgFgwtiCr09hYwZCA1kHkQaKnDyCD86M,617 +babel/locale-data/apc.dat,sha256=G96W-ebLqQlTuPN3-VqSfAY-GF0K99c6uXHza5f4e2g,1564 +babel/locale-data/apc_SY.dat,sha256=tagFeQz8BA9Pc7RMqkce1ZalfKIBjqg0SR4NlpGQaaM,679 +babel/locale-data/ar.dat,sha256=yAijlkkaH-RgRQidvCyNBkpi9Z7OTC9cNfIbKo4n4gk,288582 +babel/locale-data/ar_001.dat,sha256=hRKDI3wzxO5T191MfSdCn_MZ6ckM2Rz_r__0JZqS00s,1707 +babel/locale-data/ar_AE.dat,sha256=hEbIYUoNE5mRLOVXhmkGsvT0NAcq-rxs51VIMMzG9LY,982 +babel/locale-data/ar_BH.dat,sha256=FoQ2c3E3TDqKcioarWBvLi_MllJmSd_56r0U45_gHWw,720 +babel/locale-data/ar_DJ.dat,sha256=qbNam_QAmhobsIUfJLCL9pe146SvcmNIE_L3cWpmkBs,698 +babel/locale-data/ar_DZ.dat,sha256=_Y-JLC5hincnxze7sGmEpb7N4JnBh7LdFdaVPcSMDvM,1263 +babel/locale-data/ar_EG.dat,sha256=qE3gLQ6BzLaBOPNJVgaBhPQiIL2kQ4AiD_jLl7o52IM,720 +babel/locale-data/ar_EH.dat,sha256=a0xJdGJYzGFzfvwxZVDP7yURlTm93FQSTQ5qp3XDMYo,658 +babel/locale-data/ar_ER.dat,sha256=l-WjJfxm7kP05-Hnr5PingHaKWJ46ScfW4KEBcwD5HM,679 +babel/locale-data/ar_IL.dat,sha256=O3UYAjf4BB3NdcSQTn-RzNjyj4kzTBhX3q7E8-9Aihw,1264 +babel/locale-data/ar_IQ.dat,sha256=7-DnoHqsoBDe-ZXe0rIcEotb-F_CFyChc73dwnXuUoE,1975 +babel/locale-data/ar_JO.dat,sha256=hT1z-Me5SOjul3BHFubcqlyEk9H57fwD4EFENddiVt8,1398 +babel/locale-data/ar_KM.dat,sha256=Zx5XtfTuRPiW6iViOjeal_Fq-18EA7GuERViMP1_mc4,1230 +babel/locale-data/ar_KW.dat,sha256=1wSXHTcgFKwhNciVS8IvGKvkzzqOIMBFykEJEQXdhEE,720 +babel/locale-data/ar_LB.dat,sha256=-SuXO6LdhXNi4f_PHNrlxl8iXX47perQ_oS19bwRGpI,1414 +babel/locale-data/ar_LY.dat,sha256=2OxSGD1gkiWPcaRHd1qQv9ifG82WEjGahKqhsoFXfnY,1249 +babel/locale-data/ar_MA.dat,sha256=hTOwsXC8tsJxBVlksxmAMlIkn4kK50juJa6TuAPFkp0,1559 +babel/locale-data/ar_MR.dat,sha256=Kq7uT5LxvYXnGWz-cVcI6yDNximu9hZIpoKydmVse78,1359 +babel/locale-data/ar_OM.dat,sha256=HVOqTfoH3JVTHJL-yDBiIafNfckU7CIcO_sIzXBffKw,720 +babel/locale-data/ar_PS.dat,sha256=F-HZRPcMe_tJ8r9V01h-7mQ0UKQxQmuWuXNSi94MVMM,1336 +babel/locale-data/ar_QA.dat,sha256=UKfLpyX856fNgeifdr22wXpDncirWJi9NxqfSnS-7fw,720 +babel/locale-data/ar_SA.dat,sha256=8WE8HwCwO-OT_G2JBSgmPWSzyHOdIA3zajXPbBZr2Tg,24787 +babel/locale-data/ar_SD.dat,sha256=f7aUKlrKmmC7ijTjVwn5r9m3mfZfEob1a7GHVCj4Qxk,720 +babel/locale-data/ar_SO.dat,sha256=4LD_gds2VsvxdFHpFo56CxTFenzNdvEkMofc4oTGBTU,677 +babel/locale-data/ar_SS.dat,sha256=T6eeWPeFejmBC7InKqg-6qmWoer1vjl6srHe_5ybkDU,700 +babel/locale-data/ar_SY.dat,sha256=u42uhje_zt806WlUMpp5sIdi1yjw3Yv7vYbl6aELsyc,1398 +babel/locale-data/ar_TD.dat,sha256=OG7NYtRrJiDeRt7HP-B4ta5ri-6fdSnYomppfy2A4ec,658 +babel/locale-data/ar_TN.dat,sha256=xO974NyO5T-pRs-5b55sfQFGIwU2Y4q3VQUeqtqWf80,1201 +babel/locale-data/ar_YE.dat,sha256=vu8wYJgzcJ1douKfKLx3-hAg1sjndD5_PTHWG_bzRL0,720 +babel/locale-data/arn.dat,sha256=JB-QfwCOVQbVrLRlq2LMxU6SHO-V4ZaU2_0W15-0Rfs,721 +babel/locale-data/arn_CL.dat,sha256=cf_JrdIAxFwbltVwtloNC9527Du7zaoBDM-_KmctSvE,636 +babel/locale-data/as.dat,sha256=xoTmhieoAVjw59h2-_pLp0wjfZPVvYMV6qDSJjLrjq8,209876 +babel/locale-data/as_IN.dat,sha256=bE-OZ0Y3d2-QtD23ztjluSsFk1_RFNYMFzKgBC3waw4,658 +babel/locale-data/asa.dat,sha256=zzPu7L8RV8SVEKqeRiGIoWLBCJkeS0acDh0pYq87qMI,15492 +babel/locale-data/asa_TZ.dat,sha256=U0GU10aP58LxCeqyjl69RQCZi_6tTQxm2vni6XVkoqY,617 +babel/locale-data/ast.dat,sha256=_Qeoc0gz1ag9MkmNIuv4ufVjk0fH5xtW4M7vp8ST4hY,168961 +babel/locale-data/ast_ES.dat,sha256=51kFxigV5COuiPiNisp-OBFYMith5rUgBmU48rGk7Hc,654 +babel/locale-data/az.dat,sha256=2EcwMc8qIqCW_F0Ic2XjFQthkCGyfLNzJzJZU99ZcaA,173802 +babel/locale-data/az_Arab.dat,sha256=IGBla73oKavlLHhtIW37idHxTkmQWZ-S8NE54b65d4k,7499 +babel/locale-data/az_Arab_IQ.dat,sha256=VsM0-KQPCP4MUw-2wMsBv-F467cr2Bup3jfxx8Fc3ZE,678 +babel/locale-data/az_Arab_IR.dat,sha256=ROb0gFTPwK5CcjpQMB7CJ662eld9sZj7IA6QMrvxgto,678 +babel/locale-data/az_Arab_TR.dat,sha256=EV6cNf3keBNREHkfF98GGZGOZz4L5dxf8pFbujInaQY,635 +babel/locale-data/az_Cyrl.dat,sha256=ru4QYee174toLQsGPJn-d6xe65FsLQ87vBV9AW0o4D8,35540 +babel/locale-data/az_Cyrl_AZ.dat,sha256=GRZjR-gp6JYeLNxoiAKNdv9bkTmS1bpYnRJ0c9YIGws,635 +babel/locale-data/az_Latn.dat,sha256=aK2ziytg5Ig6GYk9tVCq4BEyV6UYy9Ip7F1_i4Pq1jw,2258 +babel/locale-data/az_Latn_AZ.dat,sha256=GRZjR-gp6JYeLNxoiAKNdv9bkTmS1bpYnRJ0c9YIGws,635 +babel/locale-data/ba.dat,sha256=Ly1V4wBNM4KCDGtuy9oCug4fZzVNEFqXOI8XKhkWFOQ,732 +babel/locale-data/ba_RU.dat,sha256=8YcwUOx3h4BcSfUIumKJ4wcFyiSgBM74SqenVIQUKi8,653 +babel/locale-data/bal.dat,sha256=mYMTNzej_lac3GimNq9dYgLY3Q4RtXpEvUkQ-zfzVqw,12849 +babel/locale-data/bal_Arab.dat,sha256=25CVl5rF7FqKEeHzAmt0BccVI_ODqmMP86KqQ4i6PL8,934 +babel/locale-data/bal_Arab_PK.dat,sha256=sy4tpUtN1St5yFoTp8C6-vi2bFGPjSaG_zDbY16Q988,636 +babel/locale-data/bal_Latn.dat,sha256=X9sEvC7SU0VUFnapzFKUq0nvc1az1c9lHmrCPHPCg4U,116909 +babel/locale-data/bal_Latn_PK.dat,sha256=sy4tpUtN1St5yFoTp8C6-vi2bFGPjSaG_zDbY16Q988,636 +babel/locale-data/bas.dat,sha256=hts2-gooZheGxndoyiVM9NwFrAitthfb4IPr7TtYb2U,16673 +babel/locale-data/bas_CM.dat,sha256=WBq-_rOvkBnT5pBaCyq844LLSRReQzg2HM0u7GmhKKM,636 +babel/locale-data/be.dat,sha256=8L6Q0C4wjbLMC9I7ltFoY1prATuRd-8nn0udprYBI6A,272322 +babel/locale-data/be_BY.dat,sha256=yN4Rf2-HqinMscTfe4jKEYpsCEaddHce6K25ol595Ag,635 +babel/locale-data/be_TARASK.dat,sha256=93xDxO06nuAugpWjrnx6C5k8Fs26HIAolUcUPuoHhbg,104118 +babel/locale-data/bem.dat,sha256=zBh5HxY-hRgaQd95anmqMAPP0UVzSV7JdcuqtJ78t1c,5782 +babel/locale-data/bem_ZM.dat,sha256=nQvsLK0FOSlgf6wspQdbj00IveO62c2Mvxjzn4eHGSs,617 +babel/locale-data/bew.dat,sha256=mfKR23PSPyS0AHxbaSuMvufZZQlMICxDGuPv78MtDbU,122095 +babel/locale-data/bew_ID.dat,sha256=JWKbnUQc4ZsJYTcBFypK3gSyFRK6Kp8UFEqdytnPMnM,636 +babel/locale-data/bez.dat,sha256=_saESrmbxsbQnGLACykFIs-aLUC-N7IigpyPWGLBebI,16300 +babel/locale-data/bez_TZ.dat,sha256=OwcKelhUxQWu-8Jm9nf2vCay6-8LQvg-ueZSN_mrGz0,617 +babel/locale-data/bg.dat,sha256=ofd_Tv4onIP1cE1pVO-171ySR9JJXuHKzpWqc-vtLP8,227940 +babel/locale-data/bg_BG.dat,sha256=CERFyuC4Dw9BfAZapqaq69iKAp7rBXWNCkLWAL5tAhQ,653 +babel/locale-data/bgc.dat,sha256=gW0lkQNX_h1SATbqzIqe0twmGqqfigkbqo7gvBCG1AI,2493 +babel/locale-data/bgc_IN.dat,sha256=hO39nPiyUx1kfCdagpCQ8lzPLycjdfk59jFQgsd3Qec,659 +babel/locale-data/bgn.dat,sha256=8RKzmGYGYkn5IF7NHRgPtaI9ozwCeEKoUE4aXQpItKY,28964 +babel/locale-data/bgn_AE.dat,sha256=5jo7AWXMNdiiEf-RqzB5xRVj0R89NFdwq5RAIEAeeRE,636 +babel/locale-data/bgn_AF.dat,sha256=UXCb78n57adZBdKHEGnhbmeVYppzlSscVOWnQNxfJOQ,679 +babel/locale-data/bgn_IR.dat,sha256=iXxyPD5Z-ouDWFpOE2atoi6-HHGoD6PNaT9c-TH6x7o,679 +babel/locale-data/bgn_OM.dat,sha256=3O0CzFrjWxOQFdN6-lDhCxRngoXfadd_dwYoVyjK9jA,679 +babel/locale-data/bgn_PK.dat,sha256=gdVUcc-isHd9lf0S-AmFoPIMwDZ8ay6NY4o1udWI6Yo,636 +babel/locale-data/bho.dat,sha256=JQAEhmajFwUOKB7ACN1-ZPcoKcXJaq0-FhsFCVcvn_8,2875 +babel/locale-data/bho_IN.dat,sha256=UcL_aZq35dOHME0Fux9zyt9B6BOTxyR5UvaGWKLs1jE,659 +babel/locale-data/blo.dat,sha256=I0gfsGv80xf7do-oxzjkh2or934YxYgDMkM9RHf7UXk,168499 +babel/locale-data/blo_BJ.dat,sha256=LM79oUzVI4JtLYEUklfbQxoweFAslN9jS-Ex-dya0mE,617 +babel/locale-data/blt.dat,sha256=6G2E9osBy9OrG1iUKLD9cwcRk3IV7fT11iI8zFkLaeo,723 +babel/locale-data/blt_VN.dat,sha256=vpTQEJYssmuBUn_pYqx7ZtYONNNz02_oBYlDMdWqKAc,636 +babel/locale-data/bm.dat,sha256=K0dLlxoDfJb4QLwa_D0SXfrbD53Xed_EJ21843fY9hY,15798 +babel/locale-data/bm_ML.dat,sha256=PAGrUwc1S6q32DDHTu5LkePFXe_M_i_WxIY4oLuUnzw,616 +babel/locale-data/bm_Nkoo.dat,sha256=UiQ-oKSXYmbnUj9nNXi-9Afx2taBc5EiN-JM1huGOno,2805 +babel/locale-data/bm_Nkoo_ML.dat,sha256=PAGrUwc1S6q32DDHTu5LkePFXe_M_i_WxIY4oLuUnzw,616 +babel/locale-data/bn.dat,sha256=00cRKYrwyk5snhrekLj0TQBVzZlV_PjQ0aLlsApgwxw,219185 +babel/locale-data/bn_BD.dat,sha256=mVW90kQmhDcwzKmkmgNAt5iMq1pLWmNndNID9fspVjY,635 +babel/locale-data/bn_IN.dat,sha256=Y4d5tMv6rG2A1fTcfN7-cMeZ1iH8XuqdZgpEBUy_FTg,4035 +babel/locale-data/bo.dat,sha256=2eVqnOFPv04Ix-1FVPHlDQ9UVTQC1JtC7-5lQ89aX7A,20102 +babel/locale-data/bo_CN.dat,sha256=xOnyre81Z1IHnWV5mJH0x313h-ZWu84hIXcMuB50IT0,635 +babel/locale-data/bo_IN.dat,sha256=GvYCgtrOD5zYYsfMtVmkCv8-y6His8Lg7iuytEGjnMI,1307 +babel/locale-data/br.dat,sha256=rBs1Bp9H6Q4ECO2mouoJmnCW0ux5CLPx1zy32GvpkCo,272612 +babel/locale-data/br_FR.dat,sha256=3oVDdZd7Xf4Q-hwmYraF42SM_3NtvSoiZlmlh6gSNOs,653 +babel/locale-data/brx.dat,sha256=0L8rfQwjwtj3y5M5zzdEB10way4I9P2m8A2E0mTIcac,160164 +babel/locale-data/brx_IN.dat,sha256=NKuJh0cEhRQasy6JAGUi5HbqdnhhEFTQ_7osgtdG5Iw,659 +babel/locale-data/bs.dat,sha256=YVLbXGLHf6QZ9vYnCjxjO3uBImuYnoxR-cEuBPVbP_w,213968 +babel/locale-data/bs_Cyrl.dat,sha256=oqFwEFFfYqyzkVvswj8CR2B_RCb51Myv8jQaDDjz0u8,219235 +babel/locale-data/bs_Cyrl_BA.dat,sha256=oA7rsQckE_IxCpXXB3F3KWQwM3WZTR7WAdYxJN98oeM,635 +babel/locale-data/bs_Latn.dat,sha256=6L9SY85hONRHO4Jz389C2Ziy7llKcDoyVNiE96ozCJ8,1990 +babel/locale-data/bs_Latn_BA.dat,sha256=oA7rsQckE_IxCpXXB3F3KWQwM3WZTR7WAdYxJN98oeM,635 +babel/locale-data/bss.dat,sha256=n9LaBwBIJ7adjJ63G7Q9o00llhPJtkgmeEp08nAa4D4,978 +babel/locale-data/bss_CM.dat,sha256=TD7ixCHREfOLDsDJn8FY5YqGYH9czdEzdULCIl_0GhM,636 +babel/locale-data/byn.dat,sha256=qT32E4jh6H_qm7HYSsAbKmaKluaLrGNXNl3F3FivncE,13403 +babel/locale-data/byn_ER.dat,sha256=oAaZEqMJuTbMITO-ZO6gg8qjwROukzh5VgjtLp6rqD4,617 +babel/locale-data/ca.dat,sha256=CIT0-7X7_e8RKWRrhLSmG-aBC2D51-hvRrTV-7iBav8,184038 +babel/locale-data/ca_AD.dat,sha256=tsLumbhbbeXw627WKi-sQdSTFwNpcSRvJIEYcrOMqOM,653 +babel/locale-data/ca_ES.dat,sha256=T-zv3FfnCJA2WUQTeBADKV82ghY-r60wk8mXtD9wbnI,653 +babel/locale-data/ca_ES_VALENCIA.dat,sha256=7Q59_PX0VWyLsDkC-qscZ-dKJ9AFzz7kCzJz4fTdBWY,2976 +babel/locale-data/ca_FR.dat,sha256=5HUFYUl5QRaUMwJxGLIqUhDO4_wF_crrr346wJmEOZo,672 +babel/locale-data/ca_IT.dat,sha256=Zh2rtOq8MIDC2CQXWnhp5HOvPfXNxhRGnnZViOgc1Mg,653 +babel/locale-data/cad.dat,sha256=XxlMk1b9CWE3wERhhQQN0wxW7iq8Mrngb05RXenmaSI,2476 +babel/locale-data/cad_US.dat,sha256=FWV1sofgr9no5P9yBxNDLeKWoQd80XEYEilmLwamkSc,654 +babel/locale-data/cch.dat,sha256=J4S73psT7C_cXs1VNNYow47plBodQ4PKINC59kzvfAw,2430 +babel/locale-data/cch_NG.dat,sha256=0adhw9xNMlVEqDVKvHe4HCPtbjcw5NSo4ny_pOjl0-4,617 +babel/locale-data/ccp.dat,sha256=Jn_O5MwN6DMGxNrXUpdrhT_z5ZOYh3-Sm5yGUcV8bII,207292 +babel/locale-data/ccp_BD.dat,sha256=f00776fnI484B-mzW0rY22KNkzV6wtmk7uxrSK_x9bU,636 +babel/locale-data/ccp_IN.dat,sha256=jMJimrQrx-b96hBSLi8sOyHtYRwNZryt-he0rhwEZ2s,659 +babel/locale-data/ce.dat,sha256=yuopW5QCq1u8E1Rs0hEOv_y1uvbem_P3lK-_AvU5zLE,135403 +babel/locale-data/ce_RU.dat,sha256=NgNmp1uFnm2MT-s_yx_V8KuW1dSR6_SEWf4NcqrAL9o,653 +babel/locale-data/ceb.dat,sha256=fwgJPAV6TfvyavZUW-9pKeLUacMuH03hCkSkHdIGm8I,100779 +babel/locale-data/ceb_PH.dat,sha256=Z2zXPyNI3Yu3QcGL6lT1nIsl04vDDLFvyd9GeucDtqA,636 +babel/locale-data/cgg.dat,sha256=vgLxp4dDUjKyHy-36IvfaPEy6IiXbDiMKShuEKJuXHU,15621 +babel/locale-data/cgg_UG.dat,sha256=HZBma4MFuX2Q7sk-SOYk5OUkbMXWWr_tEInOqPboNYs,640 +babel/locale-data/cho.dat,sha256=hWEL5XrIWAnqjYSb78iPaj2xh34KDcPYAMc46S7ZLuU,795 +babel/locale-data/cho_US.dat,sha256=T3CacgkE-uKPi0CV7fbcCLTNfdjEco_A50vYciuXGGI,654 +babel/locale-data/chr.dat,sha256=4a6eJjX1JZ-inCk-J8gHJq1QtoK3FpOXhOlQMDbHjwM,187187 +babel/locale-data/chr_US.dat,sha256=wZE2RrJ-5jjUQa1RCJgL7hkXdsU3t91orD-ULDYMXSM,654 +babel/locale-data/cic.dat,sha256=KgrmFl1fudc1tdR_g39YkGR6bcIedQMFnUjh831VIhM,2732 +babel/locale-data/cic_US.dat,sha256=DvW1XJS1hEp_W_5ol0pIECeUb3RqXsyieKVS8J6caJ4,654 +babel/locale-data/ckb.dat,sha256=s6wh2TRjabPo4G64RMoQD_J7FK77RBwsK8R0TH1xytI,35553 +babel/locale-data/ckb_IQ.dat,sha256=Osgu1RAUf2yQpZ-nNBKMxTFJnH32ItLg0kVVzjNfmeo,679 +babel/locale-data/ckb_IR.dat,sha256=6wWzlZWn7oDmGIdnF7UtCfIcUgQ1zvMxtAcEw4bbM-g,1231 +babel/locale-data/co.dat,sha256=NbKZ99wTpG7Y64LnyxTQJIo5SYQaUAOybc68DN6IbUY,12637 +babel/locale-data/co_FR.dat,sha256=DAmwXn4n1mRSsT17-DcU0vdVSI_JfVR8oGOnj7hTFHk,653 +babel/locale-data/cs.dat,sha256=o7qnhuIqxpPIfD-oyVLsvpmhtRiIOzgJBoMaZvyvPL4,247800 +babel/locale-data/cs_CZ.dat,sha256=keaiqSsGtB_axj8FvdYDOCDmlg2PbQMI6qnW-2mFUTY,653 +babel/locale-data/csw.dat,sha256=a31wJpIkcegvNW9_O8LfIY4-HK0zpLn4MBUEBb9C3PQ,20303 +babel/locale-data/csw_CA.dat,sha256=aWccZHGNU7G4JJdlGRnqyqm0ROubX0rwI9SnH_EhAxU,636 +babel/locale-data/cu.dat,sha256=mlGQkibxBcukKPAJawHruBMuw21x1j42ebTnraxW55w,16865 +babel/locale-data/cu_RU.dat,sha256=GcbqgE8Mlglk5EGGSVcCR-Zb8AmCqHGE8cbmAUSbpdY,653 +babel/locale-data/cv.dat,sha256=BUqS-4RvAuycOlAm_k33eV98VCp5bIUmrWUqjhmsqls,198348 +babel/locale-data/cv_RU.dat,sha256=oVPRtcHzmRmkAy26Bv1jLiWmm0P18ruvHlCQ5F3IFY4,653 +babel/locale-data/cy.dat,sha256=-bN00WqRhdnCL9VdnGgtUtwbmZ7n2r96oZdLuxSP8KM,276313 +babel/locale-data/cy_GB.dat,sha256=dyPULIteKk9gP7lGQiDtsiH5QDqNdlxMdK__2dq90dg,653 +babel/locale-data/da.dat,sha256=VUVxNljiubvj3e_PhimeMLBVZlAt1GT2bAfnyYu671E,171756 +babel/locale-data/da_DK.dat,sha256=qNTw1H8WXsV8qGBHxu6noCUOV3BcoR63ERJF8Xc86Ls,653 +babel/locale-data/da_GL.dat,sha256=9BNeKX1-U4NDr3pBU1ZnCGAasdYZjBMsvpdS5uTf9qs,616 +babel/locale-data/dav.dat,sha256=LBf1uH_Q7632sBVhZgQSrdn5LU2pyIU389h4LircJM4,15690 +babel/locale-data/dav_KE.dat,sha256=MfFFEDT6fib5be-EmziiEF0NAnEV6lXD7svYtHUtmn0,636 +babel/locale-data/de.dat,sha256=l6ABVg2zXPX9Ws9g2-Weve05S46ii0hnbuCViS6mtmA,178630 +babel/locale-data/de_AT.dat,sha256=OhoRvklFcyYUJXplBoK2rAITKRi6r81lwOaixIuffcA,2010 +babel/locale-data/de_BE.dat,sha256=wvdWykhix9Slofcjm2tx2aEhMjffmMLsNrd8709NIR0,653 +babel/locale-data/de_CH.dat,sha256=PH2rDxHA3lpTBkPMCwWjapEDJtgrkHhq2_pAdSKSiXE,4091 +babel/locale-data/de_DE.dat,sha256=SM44YjGlqgh7bKlXvUdt53t9hnCX5o46ujI4IrGPpeI,653 +babel/locale-data/de_IT.dat,sha256=ALPY2QGlR9-kphEk4KggHVS51fQwEDVRd7aHLIiP9fw,1448 +babel/locale-data/de_LI.dat,sha256=FnGRiippDTTFRFswU_4PblQruAuWuPEpa8cGbXPJpbE,1413 +babel/locale-data/de_LU.dat,sha256=QAkvLfyKmZdQVfkk2BbE2AMisK1i7yhRetcApqn4dxA,1067 +babel/locale-data/dje.dat,sha256=6NxQgBas9S5kmjbRcbcuuIIxKaI5_MlfmSUltxJuraY,15530 +babel/locale-data/dje_NE.dat,sha256=lU1vag_HJ4OrbYNYWFXuSdtqLwSlwBbvD6Tg-hl36e4,617 +babel/locale-data/doi.dat,sha256=JrHDjsVlGntLL6qYf9NH52BWAGCSGeGVaHg_rgUiGkM,47442 +babel/locale-data/doi_IN.dat,sha256=-q_oAqvdTizFG1DB_1p8sZVyjbaFFQ_FFPtNCyVn9ZA,659 +babel/locale-data/dsb.dat,sha256=KvZxymKZ5ZbxQ-4gS_uvnNkrYMYn3D-BKz0MGAn5488,203926 +babel/locale-data/dsb_DE.dat,sha256=Z2Dzjx7eXscEBEAb-ymvb1GKP4GVNCt5L4i-BYC_vrE,654 +babel/locale-data/dua.dat,sha256=lmO8ssPF9bIgTU2SM_SzrGWodmsGsmbS485uZNDasq4,4827 +babel/locale-data/dua_CM.dat,sha256=Z3vD8WEr2Wlqqt39anvKXVhz4_yBSm4t5BoAaEz9PgE,636 +babel/locale-data/dv.dat,sha256=-1zCv4GXiRHdWjQk0qXWNPSf4FX4Zp-Ij2jwgz5BsGk,2225 +babel/locale-data/dv_MV.dat,sha256=lvuU5gj3vDykU2ftU1_4Lza-130DB6_ZwylDrdb_GlY,635 +babel/locale-data/dyo.dat,sha256=w71HDmFfCuKN9nUJnuqpHR9DDAmPLkwIu9E0abhYjWM,9854 +babel/locale-data/dyo_SN.dat,sha256=Z6N-NIVcKkx6sFUm8LfN74usyZfgeI6swJL_A_XbBcc,617 +babel/locale-data/dz.dat,sha256=dbPhCcTpoQh6szaBA4TN5eCi3sbG1UBvlQQxtMGHLEU,85026 +babel/locale-data/dz_BT.dat,sha256=KWd6cypWse4XfXbY4BmTlxGjlnRksYNcQYi_iyXluKw,635 +babel/locale-data/ebu.dat,sha256=S8bggVknCbVHw5x9aJQGB9kUBFNcwj9EONHRNtbaWCA,15583 +babel/locale-data/ebu_KE.dat,sha256=GEcGqVmH0WwgP1BCsd9vnSGxGXl8ON4UPTRbyeaNDfw,636 +babel/locale-data/ee.dat,sha256=xJ5vpKJa546rTvKKQI75-HvYmu6VjEedXENlPSeDgCg,88730 +babel/locale-data/ee_GH.dat,sha256=SkQi-aVmtqi3S99DUTLQzZqUE4LrAIWuy25QuZqYAfI,616 +babel/locale-data/ee_TG.dat,sha256=7y8-rDvag7_EQtEObq3fpHaRedVmuEREnQwjktmLwgk,1168 +babel/locale-data/el.dat,sha256=pMPkYRvK3y4IF93Z-127ZF_-_P_QV-_460LgZAHCw9o,247970 +babel/locale-data/el_CY.dat,sha256=POPjzTYMuP7Ijf1S88cAFR0ge9Id_sSRNembKdhk2aE,635 +babel/locale-data/el_GR.dat,sha256=S32Cm-1ENgLL_lDecCDI4qDfyd3wlQ1JbUwOnW1whm8,653 +babel/locale-data/el_POLYTON.dat,sha256=lkcs2uXW6mWXztmsAXnapnSuQ6gkns1WwsVgrg-24Uo,14964 +babel/locale-data/en.dat,sha256=C0EMuCmMqd7xbacUUkDzM6xvzhEWWQgbd0sIPvX5QbA,217633 +babel/locale-data/en_001.dat,sha256=X7NFTy08b5AyR9I6BDy79hP3x_nq018WoQmMSzR5S1U,31614 +babel/locale-data/en_150.dat,sha256=cjKCV1ZLhNY4TvqJF8BXvQR1jVUBFrdjL1RKSnBa0vc,1851 +babel/locale-data/en_AE.dat,sha256=T2jBGVJpPTUgTICdaQnAVjSsYCVkQLfjabyStaPl2CU,4164 +babel/locale-data/en_AG.dat,sha256=POWSM98pRq9k7lqCsxr-MS9PA-zLoOVUKUcI9iXtKq4,654 +babel/locale-data/en_AI.dat,sha256=7iwWKftqPdOdWJ7v94jsre2ZIq2cDpbVGAs_ScBztbs,1206 +babel/locale-data/en_AS.dat,sha256=M-N7v6BuP47wkTFmv5by1w42IQCUiSqKbaVTXtnwF8Q,635 +babel/locale-data/en_AT.dat,sha256=05-bVy1aMFWZlHMBC86A7OVy1zlhJw7hsjyvIOjxE70,1309 +babel/locale-data/en_AU.dat,sha256=kEhWopkI0vkAChxsUmnaAX4htgJ36t9hiVtaS6t5Gvk,20023 +babel/locale-data/en_BB.dat,sha256=J8vppKlBp03jJvVuVp6Z4GMc_N0BukeQ8f8q14y8fwU,635 +babel/locale-data/en_BE.dat,sha256=0JXHgqUIWZYe88FYC97spm2TqyIaNOwDJBt3LBmUF7A,1547 +babel/locale-data/en_BI.dat,sha256=AbdI5FjEqQLOMbBtD3wLW_7AccuOwY9MZUFpMF1uEco,1189 +babel/locale-data/en_BM.dat,sha256=cZE8sxf44ETY6nCxOgbvJN2RhABZrRaP8SjwsbSepuE,654 +babel/locale-data/en_BS.dat,sha256=sGvlF2LPwG5eVA3IuH8-4S5X7BrvQ5LcgZ8gfAkkwwc,752 +babel/locale-data/en_BW.dat,sha256=k5bc9gYBdl_8ZyCelrlS9ZenMzs8cdfl2MxeCnOAr88,2850 +babel/locale-data/en_BZ.dat,sha256=LJsn2M0RWMbxDmGRxs2lk2ZvkgoguwPf7oLBYQydU4A,2979 +babel/locale-data/en_CA.dat,sha256=za7pG-TGpows2k_XJL8dpzLVs1oOSMKN3zwyG79OAqI,37924 +babel/locale-data/en_CC.dat,sha256=34a3Vb57hT2reRvwn0Ud1nXkoYQJoPmaqh1n5qflFdI,1187 +babel/locale-data/en_CH.dat,sha256=Nfkwk2HNOccSH8Co5SWYnFdHWEDEpKNPQ2o9a67vt_k,1866 +babel/locale-data/en_CK.dat,sha256=gvU21BSAugpjE7CT3xnpHML6oRgHPaBoq7W11YbG-1U,1187 +babel/locale-data/en_CM.dat,sha256=r_7Yzep6S0ZOFvk_TORm5Qhi55yjehVQAotaLnn7igQ,1437 +babel/locale-data/en_CX.dat,sha256=lKYbIRS6r-WMUd1ewX7Q4TpX0PBVcsYN7Cs263_xosM,1187 +babel/locale-data/en_CY.dat,sha256=kQZfhQdPfdBeePOngn-4CpA3ZJ7Th-lmZ-jOrBe115o,635 +babel/locale-data/en_DE.dat,sha256=oI3TmMDoQZsGddvDb0fbbwd_LdTeFbFPdFkZhst2q7Y,1027 +babel/locale-data/en_DG.dat,sha256=xtjjxkuFB7I4p3n0f4Evz631RtMQhIzpoYwUzOzICqA,1168 +babel/locale-data/en_DK.dat,sha256=pE7x4MDJvZGFnqQqXMCDWoMV3Ovu2qH-QO2oYNvgL4Y,2425 +babel/locale-data/en_DM.dat,sha256=gbriVhXcdXi5GWmpgWdGB1LUwI2yquuLk1khkzpo1UU,654 +babel/locale-data/en_Dsrt.dat,sha256=Ttd13uhdXKMHpNCWaH5q74JJ3fok1gV4MFIGd1fGxRk,38656 +babel/locale-data/en_Dsrt_US.dat,sha256=p8c_L5tEvzYmacz0tmq58Wsbfp9wMV_PBgz5R_v7I-k,653 +babel/locale-data/en_ER.dat,sha256=U1vYaQVwnVgfmO9l2_GjBnAhHrShbiHU_E-hAclDvck,887 +babel/locale-data/en_FI.dat,sha256=i4gKdljmc9cXO-E1IT6JGmq2JsRRH71sKe3yUmkhIa8,2357 +babel/locale-data/en_FJ.dat,sha256=k-hXekogcQzt00qXdcWODsHbg7Vh9U8Vx84bERd8CHM,672 +babel/locale-data/en_FK.dat,sha256=r6OGl7mX4ep5aeFMlhog6Xr--kDKFEVhDcepSJBYZWw,1210 +babel/locale-data/en_FM.dat,sha256=88mCYM5_VDSYpJ_mV4AStjiDpKAo_1W7GbmKcI9f_sk,616 +babel/locale-data/en_GB.dat,sha256=2qd2bKhxJeiwIneKwRmH89UCmnYfBdpOFvL02en-YKU,3440 +babel/locale-data/en_GD.dat,sha256=c2ap41e5x-OY5dEYZ5wD4ByRbEzlx0845R1lPBLnJus,635 +babel/locale-data/en_GG.dat,sha256=qhfvbRdldEA4467X0_8N09HwT1ZRPJJtb7Evpvbbarg,1273 +babel/locale-data/en_GH.dat,sha256=bBpzsHl9QhNAQ7hrUtKb6TwGvZYibXQwXfuJVgLMlZ0,889 +babel/locale-data/en_GI.dat,sha256=veSOVDagnkTwyqxP4u5mOkRG8_W4MCZ5ptMBoGOg9SE,1228 +babel/locale-data/en_GM.dat,sha256=jfHQeiMTDNgnIpx3WZ9hOpCR_LXCYLLrVN4SUakudi8,885 +babel/locale-data/en_GU.dat,sha256=WDR_uybAkUii6RHT2x62pzCLvX4bMstibEztBvLSuxI,715 +babel/locale-data/en_GY.dat,sha256=TDiVa4YSapsK9rjQN3ATnrj3m267l35pqRLI7r5bLFc,694 +babel/locale-data/en_HK.dat,sha256=meLPc5_3zahna_xycXEf75TrW5_4BHuqMQcMyoo1eYQ,2315 +babel/locale-data/en_ID.dat,sha256=b8R7owdV-fl30WrUhFz-1YGHiFweaoYs_bBFr9pGhD0,3172 +babel/locale-data/en_IE.dat,sha256=ZNe5XFbdM4UnYu6KaGYHggQqzIVCWwWof5C5ktTsHn4,2094 +babel/locale-data/en_IL.dat,sha256=O4-JPTBiiLezF-iUSDu73id-0VWlSiE0ie8z9qRaxq8,1424 +babel/locale-data/en_IM.dat,sha256=u3JX9jrrsW_G5RQXHsQIGl6ikVduckeldeS_FjKG5Y0,1273 +babel/locale-data/en_IN.dat,sha256=2d4aWIy6Q72ExhZHnku5HKbKmSSavvB7zkflqlBsRQA,14809 +babel/locale-data/en_IO.dat,sha256=TOM0TxdcWcu1wyBXJxsrE-uCIZsbYTCmUkTdfCaOO-E,1168 +babel/locale-data/en_JE.dat,sha256=IxL3Ry70pMOXDbCwU6LZllHXZmg8XHbjocavEWvb9jY,1273 +babel/locale-data/en_JM.dat,sha256=Z9GFZ6aFGEvIEzlbOlA5bBBvxgcxXx6s_7AxPMp2Yg4,1666 +babel/locale-data/en_KE.dat,sha256=oJa3lUdjavkdhkMK_bcnJ08waK9lfIRs8mFkG0JYf-4,1458 +babel/locale-data/en_KI.dat,sha256=3yfkrwv7Yd9yxDNnXz9Sv8ghbDvqczZTAELr3uGNYn0,635 +babel/locale-data/en_KN.dat,sha256=cFebccXOrQo2X1zmHoHYB2vzXynoroYImJTMnK9j3h0,635 +babel/locale-data/en_KY.dat,sha256=slmSWk0IEz_UK7PfHX4KyHlf_iBRfuoOWVuEB0aKaYI,733 +babel/locale-data/en_LC.dat,sha256=jUYdKd248Jkj5XVxXXbvXnfxu4BH_3s4Ld-O67qbPkE,635 +babel/locale-data/en_LR.dat,sha256=ribT8_azFjDC8uzIsOwkGTjfgEiFlSG_wAb_wG1Fmnw,885 +babel/locale-data/en_LS.dat,sha256=2HtV3gUrynDl2mVvqywMfff8VnVjz2ryoNS7D2n5T9c,885 +babel/locale-data/en_MG.dat,sha256=dtMrsBqOpwQExs7dVukbUzWojz_6swx5Ng6OXdKxsSs,1438 +babel/locale-data/en_MH.dat,sha256=NZjQknLyokLgJRNPicAZ55_VoRCATz_WvAxMo_I-HOg,1368 +babel/locale-data/en_MO.dat,sha256=UJVR7td-vlRY_a36ZWbxr_PcK6egIUQSKoky3DeAmEA,830 +babel/locale-data/en_MP.dat,sha256=Sd2vA3CdR_GBw1vQQQBfKIY0asJGsVUwZ6gD-h7Or8o,1349 +babel/locale-data/en_MS.dat,sha256=BXrA3odGnWSeR6404L85ErqMKxTnRCZ16UWL2RpD7TY,1187 +babel/locale-data/en_MT.dat,sha256=sODJaESHndxdT9SYZcnESDA6G6oln6n9NCojeHhKvJg,1990 +babel/locale-data/en_MU.dat,sha256=SefoZho0XzSzzU_Y_-udWPCuRgz8ujdUVkL7hhzZO8Q,1438 +babel/locale-data/en_MV.dat,sha256=Spp5zsOCHzCeZOyUdYqXoqmSoIJ0pxRR3b3nI27jvTs,2034 +babel/locale-data/en_MW.dat,sha256=5d20Ih8j6iwBY545I4nObBWdv9FL37nE-ggfpGeXSVs,886 +babel/locale-data/en_MY.dat,sha256=nUwwM17W9W78nnbXGmrx2zComZQ9Fg_pu3va9wPZzvY,716 +babel/locale-data/en_NA.dat,sha256=-yNAAmx38RoVJt3YRH7Tp1xO9o3GsSVDU6uF_GKhGb0,885 +babel/locale-data/en_NF.dat,sha256=6LqhwHyOnkQA4TNvseuiE1a5Ov7DV3y46CZ0ENcibXE,1187 +babel/locale-data/en_NG.dat,sha256=dvbDZYdlkmhF-IU8M6s2p_kHswy8HhdmR3LLc4EhePI,1439 +babel/locale-data/en_NL.dat,sha256=dDCtXReh9uMfUOVqGWaWjfV1-dLe5_yj-o_HrKZGMeM,1192 +babel/locale-data/en_NR.dat,sha256=ZiUNBY0c1T3LQP-qGmtuxLW2Jfc-lcVTuWpB2jxfAGI,1187 +babel/locale-data/en_NU.dat,sha256=jU4HXz_3l5k-luPPsjZmZBsdOVB5ZfkN1Xuk5i2pduc,1187 +babel/locale-data/en_NZ.dat,sha256=z_qul-H3zQn0Q_lrrHceySj1m2TQaaJZd3QMXE31CcA,2340 +babel/locale-data/en_PG.dat,sha256=WV6D2yT-4GX6ftbnUkK-ihfNg8Y-ImX1Af56okx8LTA,635 +babel/locale-data/en_PH.dat,sha256=oPz_wDvxfCHBpkBUaZ0RiwtPrLtx-9IigR0TIK1b-IM,635 +babel/locale-data/en_PK.dat,sha256=KS_KXItkhkUULbKd9mHdWD9ok4WjZbVSXhU8e5u8blo,2074 +babel/locale-data/en_PN.dat,sha256=qpEWWX0fvhzV6gcX_SHNbJW7E-5FikKwXrQAP9BWmXc,1187 +babel/locale-data/en_PR.dat,sha256=bpAu9yoeKxzQTF5Be7AfXtyE3wax0eP0m-Eu-Cr0jKE,635 +babel/locale-data/en_PW.dat,sha256=--3_iiNqupR9R3ccq5foj9HzkADBEwBYdKE_AxLZD3w,714 +babel/locale-data/en_RW.dat,sha256=Et3hOZwaSDECrCzypgYq1QxKxxnB3XYziIeEe_WiY7c,1438 +babel/locale-data/en_SB.dat,sha256=h9TJ69YThzzLGTh49BBEmRBxSiZhZpQrVJif0QglLVs,635 +babel/locale-data/en_SC.dat,sha256=omYtCZBrLl8fHuT6XInezdetV7WqWZVYriJRnlWKbTg,1188 +babel/locale-data/en_SD.dat,sha256=ozd3rV_icr8p2H9V6StOSMkoHBPNBsXsay1GMRWZ1zs,928 +babel/locale-data/en_SE.dat,sha256=96guhEOzGr2Lcbd3xZ4j7HTj-0H0oaGMQIbI9a_5g48,1502 +babel/locale-data/en_SG.dat,sha256=u9U5CLnqs0iJmSnMsPBHF71HQLpFM3siADzZeC7iaJ0,2096 +babel/locale-data/en_SH.dat,sha256=rBm_0zxjHnEVzs_CPCyuz5LpI9isA2S6copUuNbxCcM,1210 +babel/locale-data/en_SI.dat,sha256=kkeEkPnW48iNoiwNZb0KV29DPU0oVNuENTi_Wmp2ALc,1046 +babel/locale-data/en_SL.dat,sha256=4hJLAAnyuEVVQZ13NC7wZ0JYJQ3rvX0SsOAp0OoW7_U,886 +babel/locale-data/en_SS.dat,sha256=C4kpyEFjyKT9-6wt2pGl4125aN0Bp0wpcYBrr62nPOc,908 +babel/locale-data/en_SX.dat,sha256=7vESruRIiaBqWxuSRpIE2U6PgWwJ9mxgbjjH_XAraa0,1190 +babel/locale-data/en_SZ.dat,sha256=A9wkzZ5cFffcEf1FobYRPxR1EAFwqQvqELsqRpNleOg,885 +babel/locale-data/en_Shaw.dat,sha256=5NskTfYsGHZ63w2s3eYrzrogPpVZeqY3wd60hViYIbo,4774 +babel/locale-data/en_Shaw_GB.dat,sha256=uAIWlJ5gr_NR6lncmMaBrjXUPhZ33rGsXoHz8hrd_E0,653 +babel/locale-data/en_TC.dat,sha256=UWqEc9zpN3NdS1dymbUv2iGKhLkH65Kk9uNFxdXGnOw,616 +babel/locale-data/en_TK.dat,sha256=qz4RHtHQ6cNjB8xW8_BIHdzW1cEcQuyJfdqFIybXAs0,1187 +babel/locale-data/en_TO.dat,sha256=OehJQf8kvWi7A-Rwo2hPjs1Z9pTdg7_Hyx_ewWxbLQ8,636 +babel/locale-data/en_TT.dat,sha256=L23iDn_zvh16iPE5uBlLCRvECSozqyzU9W2OcSjYogE,654 +babel/locale-data/en_TV.dat,sha256=3sj3NCjQg-9UOdTz7wDJRpjx57bI0bntpmx0T50OK9E,1187 +babel/locale-data/en_TZ.dat,sha256=5Q87mjSEaCMmWeWwtesb9lJw2tIWOsvyScG1CqSpWvs,1439 +babel/locale-data/en_UG.dat,sha256=aQLfdbfozsYhK3EG9JXVxsmT4B7UvfSzNBo7kO2WSsc,1462 +babel/locale-data/en_UM.dat,sha256=qd26Sl5bJTEgHU25hvskp4LMxvRJByLOSvUikqz0J4I,653 +babel/locale-data/en_US.dat,sha256=p8c_L5tEvzYmacz0tmq58Wsbfp9wMV_PBgz5R_v7I-k,653 +babel/locale-data/en_US_POSIX.dat,sha256=U8r77o6qQID3Sd5FiZqukSpQnq4LV1BDwHUeuNZjW10,1321 +babel/locale-data/en_VC.dat,sha256=ES4c4Xt6OvJDhxoAZpWR2HFbtyENOl9CrV7XFCBs-MY,635 +babel/locale-data/en_VG.dat,sha256=sqiVUOEtDV2pAc5DzGl76qV1mL5K-UWhMRzG-Ff6r30,616 +babel/locale-data/en_VI.dat,sha256=AGffygfBAafQMxKAAoxt6kwmbES4R_ybbXRV2tHpy04,653 +babel/locale-data/en_VU.dat,sha256=ji2cRB8B-c8uJR7L56HzbkiTR8eJZziVTMcEzuiK2g0,636 +babel/locale-data/en_WS.dat,sha256=gowKsveED3UIWyLgnSWxyE6qq87lH2dMgOVLDGCEcBk,656 +babel/locale-data/en_ZA.dat,sha256=uoSLsVWiMu6x-DYwISGhEwveg0kVsDwgnIhq2Jn0hXo,3411 +babel/locale-data/en_ZM.dat,sha256=8y814a5GtYcC5gvh3YBlcYf5Imr0yNEIV_v8jH3SXqk,885 +babel/locale-data/en_ZW.dat,sha256=GUDDEesQRkUuLyyDlnJ3MGlGzG-kDPxt8OTIqVRKDyw,3320 +babel/locale-data/eo.dat,sha256=kSIbuCrTJF0ZuNV8FzhyheJT33iKwteIHLaYgdfPO4I,101326 +babel/locale-data/eo_001.dat,sha256=3-tP9zaTDfbBBjpd2qKVIhS7WWbmtVKGGW_BmGIhrHQ,850 +babel/locale-data/es.dat,sha256=c62rMW3rHT7taFSTKPt8je50jtFex4sL8nvuVey9224,191377 +babel/locale-data/es_419.dat,sha256=s-JLmJhGKoIbnsjZm-6UXnDsAsgFhIKk9OJk7SPR_oI,27973 +babel/locale-data/es_AR.dat,sha256=rFeyqcUDLb3yYYWMuCP8R4O1n4rT1XCeayaIaoPaOPY,7087 +babel/locale-data/es_BO.dat,sha256=9zgEUshMb69M3LZS_3TOk4kGOt73fnB2UWWR7HPFAgk,1477 +babel/locale-data/es_BR.dat,sha256=oCD_7IoQ5w-C-YXpQ_wBcS0wUkcY268QDcg5y7Vo7KU,1207 +babel/locale-data/es_BZ.dat,sha256=Z3ZskA42UBcpOPDI66F6YBTvzt-g61PmxhiEpdGXpwA,1206 +babel/locale-data/es_CL.dat,sha256=tpiiCMc68Ab3YYDqi_zh0dw_IfsZmLciylujbf72XqQ,4848 +babel/locale-data/es_CO.dat,sha256=_ZNmqQnEj8EoNse4lJuK0Qrg1ZnPSgUVCPCNo7K6FE4,7436 +babel/locale-data/es_CR.dat,sha256=GknsR2O3LbFhHagGvUvgNgBVCVbXZxk-yzivTPR-Q_0,1308 +babel/locale-data/es_CU.dat,sha256=N-TqpCs34sjlEMeJgvrfOSpmnnlGxuuGtSBSaUKDbvg,656 +babel/locale-data/es_DO.dat,sha256=HMY1U9RiyiBlQKc_kUERQ-GHqWCgjaqrBY0mKUE9Gp4,2700 +babel/locale-data/es_EA.dat,sha256=V4UU6BM3pjbuEleLA_MaJtWl8IejNcHzUM660pfKa80,616 +babel/locale-data/es_EC.dat,sha256=i6oUe_5GL6b8ZnbnORW1csbq1-qgo6VN0Z6x9G2xIWk,2941 +babel/locale-data/es_ES.dat,sha256=fd1TSjdlf2lSMxwi0NhYGR2eyiABBaSJvhLlwLO46j0,653 +babel/locale-data/es_GQ.dat,sha256=a7A-hqM9wNt51_CdpjzU8aT9KEeMz165-MyhkWyqJOM,638 +babel/locale-data/es_GT.dat,sha256=zk_WqHmzYsoIAiErSYC9G62MlbgAuySSTAPZLtOX__c,4132 +babel/locale-data/es_HN.dat,sha256=71FLaDfVW5pgllY-BoQoPaDjJ4lvhs_Fi9EBmJQjZj0,3051 +babel/locale-data/es_IC.dat,sha256=hwey8h4GM8i25i-YZR_K-jvCij95wZqr0zdTIKauP58,616 +babel/locale-data/es_MX.dat,sha256=6jxhdWleuQwI_O1FZOl2I5DEwUs3oXjAhJhyBmBCMGM,25204 +babel/locale-data/es_NI.dat,sha256=cF62A52GuQD-QU9-jBmK_8AJ5pZ1W_nJ7NwraSAhMvA,1227 +babel/locale-data/es_PA.dat,sha256=fBNNQGv937Iu4OPHxSuYtPu8M-u6xhUmG0LtMbxDC0E,2775 +babel/locale-data/es_PE.dat,sha256=G6ZcerCUvlSGQmeysFNtMNJEZwPAerVqwOG2hHUpZ_U,7244 +babel/locale-data/es_PH.dat,sha256=fEMZmVPQb97bBjEtCVMLn9JUtosTePmRuQqhpfNYkZQ,1248 +babel/locale-data/es_PR.dat,sha256=wPOPeV-O9rnLzNHObqs9JEUo2ouyqmaiXqtsxHtKpWM,3307 +babel/locale-data/es_PY.dat,sha256=KXiw-rkQCdcoeTv0t0SnA8ioZrHQYfPxen8491aSq80,4442 +babel/locale-data/es_SV.dat,sha256=JM9uw_EVtB6J8bH_Nz0GL_yr6ehay4uAPm4OJIiUmfw,1260 +babel/locale-data/es_US.dat,sha256=ifZ5PL9mZhnqv3E6uRoRS2Sqlkgyz53m2RJs9wDcpMY,22533 +babel/locale-data/es_UY.dat,sha256=31KdHVEWnE6fWuHa7KOdTLcmlWXn8M5liZZMDWlaVrk,2703 +babel/locale-data/es_VE.dat,sha256=kyxdQ51DhLNf6dQauAPw4vOlpjftw9CQgc3yH_uAoKc,2509 +babel/locale-data/et.dat,sha256=eTq6nG039wKW81vnFtx-qzaWVo0wiUey2Y2ZUQLl49k,175652 +babel/locale-data/et_EE.dat,sha256=JpcyzIDpGwHlLSiInuYO3HfxkMEFsDPBeMc-B8Rj3MM,653 +babel/locale-data/eu.dat,sha256=lEQD4nzng6_Fmu8zQtkrvoFWV5AvUaCpOaAlv7vknt4,178023 +babel/locale-data/eu_ES.dat,sha256=ABS9lrwjRkWenVB4fNrwKpr0mOuMF_eOlOrSrobcDl4,653 +babel/locale-data/ewo.dat,sha256=tHyC9Uj5Gkz_nGEiTCFjXn3veej5z1GUhr-j-Dl2vgE,16881 +babel/locale-data/ewo_CM.dat,sha256=likxlZJqzYOVxcG4oSUQLTiH5DjGjdAFsJPIjnnj2Iw,636 +babel/locale-data/fa.dat,sha256=ci_F95dki3wTXhl10yqZP7IomDg1FHIOArxqTEMUEyk,192269 +babel/locale-data/fa_AF.dat,sha256=3lXNqz0r7fjXSYzqZBKGD5gtS0cPs-lx1ExY_s1ucfk,8639 +babel/locale-data/fa_IR.dat,sha256=nPuexwzRHDb9TFMjILK_V43sVIzbcn_fgp0msY3eFQ8,678 +babel/locale-data/ff.dat,sha256=gT8MoHYFJcGDoVjBCYPzsrkMTKylOjXkE64Sc0L-rvM,15911 +babel/locale-data/ff_Adlm.dat,sha256=GbwYyVBbA8SAJP-1BYMW4b3HXivgi8HvRDzIaKuJtvg,326252 +babel/locale-data/ff_Adlm_BF.dat,sha256=eYQLbHvxtEqcJCMbXSkqN2dRSuYHVc6Tg9kOUKayJQs,637 +babel/locale-data/ff_Adlm_CM.dat,sha256=b3VqkcWqXc7s1hCxN60dd-yBRXkcXfMMQ0uAJKmSsg8,656 +babel/locale-data/ff_Adlm_GH.dat,sha256=r1CRe9Z8kZ3B3PDxmZQbvgpsEcY3OjJek9Z_S8FBb5Y,1236 +babel/locale-data/ff_Adlm_GM.dat,sha256=ZM17aBc8xznEjoP4f7HRCHaKn-DIxCSDGkL6GvDYvDw,1232 +babel/locale-data/ff_Adlm_GN.dat,sha256=HWkLsTj8Sd0fWYuYysHhShl08hg0iAvSXhPjy-rUjB0,616 +babel/locale-data/ff_Adlm_GW.dat,sha256=vIUl_C4PZSWmypeEmnsAUbGN2dSquM3nEOdBs22_qNE,637 +babel/locale-data/ff_Adlm_LR.dat,sha256=277rY6zAWlxetFe0D0JpVEY9MNcQiJU9kt1jRoiXE7g,1232 +babel/locale-data/ff_Adlm_MR.dat,sha256=qz618w1XY_zwZ27nB6LBgbzsjsh4UinfnuOVx2bqLgk,1233 +babel/locale-data/ff_Adlm_NE.dat,sha256=waY1EIQKuOkVzrtJ-t-5VY7cK2uKL0SIBO0PQbx4x9Y,637 +babel/locale-data/ff_Adlm_NG.dat,sha256=9UdHELj5G90YopBKktxDyWVQyLxl5f7P3ToBElwlvCo,658 +babel/locale-data/ff_Adlm_SL.dat,sha256=7jLKofwQBRggP61keprrbfFGXvrcJPIOvwEVmUNjcTI,1233 +babel/locale-data/ff_Adlm_SN.dat,sha256=nGazz6Tun3D-XsO6SljxQeeBuzvAb1sBQw21IbkDQKo,637 +babel/locale-data/ff_Latn.dat,sha256=APyNmlFYRwCQOlboY58qsqiF57rrI3F4fHu-uQTdt4g,866 +babel/locale-data/ff_Latn_BF.dat,sha256=Nym5keR3dDPMJBFr7ZyW2cuuY-qTMkdHzXnIHSmfD4Y,616 +babel/locale-data/ff_Latn_CM.dat,sha256=rWaVAF6D5gv_hDhNmSGbcWGpPV7X4gp53FBMXyMjT-8,635 +babel/locale-data/ff_Latn_GH.dat,sha256=-kMvFNIQyikTD2dFakV81ghSR2H584AigLgtmrzlhN8,1231 +babel/locale-data/ff_Latn_GM.dat,sha256=1Air8sk0wb0McqLZXDWIYQeiKgPusBaVjr4zk54IaoU,1227 +babel/locale-data/ff_Latn_GN.dat,sha256=7R-6dXYMBdveo2-9Vc7CjIA2H6y_hGJVbfVGIaJUgbI,636 +babel/locale-data/ff_Latn_GW.dat,sha256=2kbqhzcDeSPQxefHxynmrv2tmFVnWdRNjO7I3AZ6lxk,616 +babel/locale-data/ff_Latn_LR.dat,sha256=MQxcDQnMZC5hN52abzqCjVIL9Z5mA9wOyTqGPP4Oj0g,1227 +babel/locale-data/ff_Latn_MR.dat,sha256=CDUb5owq0Ntsd2lIZJejvYxOjl-NOUPP-v3tgVj1_qU,1228 +babel/locale-data/ff_Latn_NE.dat,sha256=R8C0fI8ql5XpnT29CrwBG8rgdHB1AASDBSdvqIqIQ1o,616 +babel/locale-data/ff_Latn_NG.dat,sha256=00rtiYbJD_ci8-0M4ye95rzGpbyX8qQ-dmFFIajAtRQ,637 +babel/locale-data/ff_Latn_SL.dat,sha256=UaWOeokdbSoxG0JUyyGWkDyz70sXy-cZbc3lXZxmQK0,1228 +babel/locale-data/ff_Latn_SN.dat,sha256=mPVac9iCnwNay8c7Ko3uUvg48lfyfEZrvhpyEFFGmFk,616 +babel/locale-data/fi.dat,sha256=Vd-z6RH112cP0k5AGOlAoYU1U0f5mpQQkiHypB844Jw,204853 +babel/locale-data/fi_FI.dat,sha256=cpbJU4KKG8OWbmLnc9TKTdzmpoi2AJh_Pu1cHhDwVSM,653 +babel/locale-data/fil.dat,sha256=g0INTCXppDgjTGgBHy9hqQ8Mcw7G9K3XLTLyVshWuuA,144724 +babel/locale-data/fil_PH.dat,sha256=JjoZt3zNxy1X6RnHoxUKOMzT-xMh3tGZBRq8cvrBwqA,636 +babel/locale-data/fo.dat,sha256=4P7HQxbB7uhYUyAt1rFID1mHXUe9zlQBvWCt8LDRhNM,148411 +babel/locale-data/fo_DK.dat,sha256=HUuarLNFSsV_gN2fg-OqrPr-6Ry6KFgitCp173jxAOM,674 +babel/locale-data/fo_FO.dat,sha256=SLeNQ33QTZ92kXCzQcFfo-OgwZVFPJKIeRivuvz0zo8,653 +babel/locale-data/fr.dat,sha256=HvPCR3ft-_VxUpJsVp1wKU8aiJ7eCKZEz9XZVlM5jek,213357 +babel/locale-data/fr_BE.dat,sha256=ODfkJC5kWWEEkjr1UV8AaLtKKc4sPCUxlIGkEDkTr6s,1085 +babel/locale-data/fr_BF.dat,sha256=7piWVeMMrZ-YbO9MNqJAu7ZeIdueVnhg07eeMwJHy94,616 +babel/locale-data/fr_BI.dat,sha256=R4kaU1OtMcNIcibkw8OFGP6FaYMxt2uKMRyidkEqbiA,637 +babel/locale-data/fr_BJ.dat,sha256=sel3fdwegY5W4NRg32t4MuXoqpGqTAGO_ffCF2NPggE,616 +babel/locale-data/fr_BL.dat,sha256=n-MRZjeHNkRMwCtGgPZq52Lj0ijntBsxfskC324i2k4,616 +babel/locale-data/fr_CA.dat,sha256=Nu60QdYyJaiQnz1EbBLSWIO86Rf57q3FWoU7ywZMKQ4,72420 +babel/locale-data/fr_CD.dat,sha256=HWaY-wrLseHcf0fGlRkAWt118iQjRZaTGlqs7G23ZrU,1138 +babel/locale-data/fr_CF.dat,sha256=wxzk0Nf6_pKO2VDivQprUY9WvaxRSkrfQBoUE1UVrAA,616 +babel/locale-data/fr_CG.dat,sha256=zP3HTP-nqZjG-OD_DLrDEPPbB3t9g4T2kHVzI-HxhaU,616 +babel/locale-data/fr_CH.dat,sha256=DiO5jczUyCwTWpWSoxVHE6kLMXQPru-XotJQLDInjKw,2876 +babel/locale-data/fr_CI.dat,sha256=TrHL8hQRuJI3Auxe3aTHYa_rljyUvT_s0q-jdSJflJM,616 +babel/locale-data/fr_CM.dat,sha256=31oDAmeRb3F8a4fvVn1QJyIbrla04w8kfC4oQO0vwkk,1970 +babel/locale-data/fr_DJ.dat,sha256=Jqu2FOJQD41B6fLSBEc_Wf-dIQ-B6MWlT0m1buQnmxo,1248 +babel/locale-data/fr_DZ.dat,sha256=uOe9lbjO51q30G0UMs5nKrMLta5CtLW2Fg4dvsfRX3M,1290 +babel/locale-data/fr_FR.dat,sha256=bwtjtv0NG-lxlXO7fJd6r--l38WKaQmqlmn7PpXJdJs,653 +babel/locale-data/fr_GA.dat,sha256=rMwS-msXjMO5T5k1BP2B2S9B_dYvDHlP8HMS-Lq8XU0,616 +babel/locale-data/fr_GF.dat,sha256=x97-H7H0h4_MUTpjMdrkYB790vBvPKKxmnUTu-FOwLg,719 +babel/locale-data/fr_GN.dat,sha256=6KVlBWrF76ZhgyYg47F1TcR8_fbE5Ii2pJzPhmbr3Pk,636 +babel/locale-data/fr_GP.dat,sha256=W99xHjqPQj_NJ1X34ismj8A4xeZcHu_vyqH8tIwXgeI,653 +babel/locale-data/fr_GQ.dat,sha256=Ogf5NI4zMvbVROeIb8F6h1NxJNKOqY4fKU6G2SPJXDk,616 +babel/locale-data/fr_HT.dat,sha256=S2Vvt2b9Wu4dY5Tsd9xxFAx_RLcWXCGf_XeSSQFYbKQ,1836 +babel/locale-data/fr_KM.dat,sha256=ChLpzCyulxYg8Qmm5HK8YkK203GRSUEBNPSl8war7lA,636 +babel/locale-data/fr_LU.dat,sha256=0sYBhn8-9lvfOrU-ZEIbnO5v03qQOQq4wXCKp70b2sw,729 +babel/locale-data/fr_MA.dat,sha256=Um7dK7MvHkPB_h_r0pk47rMFCng1LWFgIekS7cDX9ZE,1098 +babel/locale-data/fr_MC.dat,sha256=IfTCZ89rqHzUxdKyIz86Zs_VUH2zc5JAvntdhyKsNR0,653 +babel/locale-data/fr_MF.dat,sha256=xU0grNJKWgpfArwpLt7vuOpSTSkyPx3o6gFkmZLCxd4,616 +babel/locale-data/fr_MG.dat,sha256=A9zYBbE6gIWuGDPxv92ReIcR49gtIEYaEkCj9ZONHHI,636 +babel/locale-data/fr_ML.dat,sha256=-PoWsHBtnaZisIOqZyaEFmwooRXb2R2xEMe6DYad_sk,1153 +babel/locale-data/fr_MQ.dat,sha256=45qWAev4p2ZaFVky1Qlrw5q907PDfbRJq9q2UACahm8,653 +babel/locale-data/fr_MR.dat,sha256=mVSOABz3cRa1-HEeUKW13sLw1ZR6PmB9h_DPfZsK4Uc,1228 +babel/locale-data/fr_MU.dat,sha256=c4ERuS1GI8h7waxcW6BRtloW7BThLlgqdYTf31Ioh_U,636 +babel/locale-data/fr_NC.dat,sha256=Su2DaGZPnLCaONmgosMYke9kH6dP8xJ8wJF6P5xrJq4,616 +babel/locale-data/fr_NE.dat,sha256=D6ghYGdDXlhaVQ5gAr8wWHxEHYR7mBs0QaiNL5nfxWM,616 +babel/locale-data/fr_PF.dat,sha256=oPN2EBo1hpLjlpDYPB51qEAx_4HNMWG5-Bh9JN6Kal0,616 +babel/locale-data/fr_PM.dat,sha256=gEP6Ay7r-p7uIjOGXP8eCa62yujlTBntENdeNd9KKes,616 +babel/locale-data/fr_RE.dat,sha256=pxNaLi-BUbe3E-Xf5ZXPoeut9NdT0D9OGEgmoSWZEr0,1055 +babel/locale-data/fr_RW.dat,sha256=gP8mpM7gx_X_9NKDm8B9PuTg83jwT5Nn3NOKPpVhi3w,636 +babel/locale-data/fr_SC.dat,sha256=qvz0N08LKCsPCPDZK1NmAhhzmaszBQ8mSlDbAsEwAoI,636 +babel/locale-data/fr_SN.dat,sha256=n6ijBmMl-ztuCLSePI-Q3yIyhzejn7KvRw5ZqjDoXsA,1018 +babel/locale-data/fr_SY.dat,sha256=tpDovgZ3aK7OumRwbntHTknV-tRL_HjcPK1aQINC-x8,1290 +babel/locale-data/fr_TD.dat,sha256=1H47veWKRj_iV9CxHMz7x7tfrL8IYdQaRWhTV455efw,1208 +babel/locale-data/fr_TG.dat,sha256=r_E5Vnj0Kb5CG16ApHFls4UreDeO7-o3uzpQdJjpbRM,616 +babel/locale-data/fr_TN.dat,sha256=hcGRJ2UjuFEFJqb1BDglGsXY8jCEIdM8--OrU3OqRhc,1228 +babel/locale-data/fr_VU.dat,sha256=FYYsiaoM8QZeClMnQzLiLZNXW9hULz-rCgRTYWvrIvs,1228 +babel/locale-data/fr_WF.dat,sha256=jn6VFRw_qpJFxpO-I6C9nhz_wRZ8cV2b2XRBgSzi710,616 +babel/locale-data/fr_YT.dat,sha256=x0WpQCLoqrLY3_nCF7JlcuekkoUtcpE4llC8kRmeRmY,616 +babel/locale-data/frr.dat,sha256=Itmd93DOwAMGcTkWE83BWDS1oXEVckX0EE6mSiOKyoA,102994 +babel/locale-data/frr_DE.dat,sha256=2ohnodzECbJBUTEta36Ts4amZKl2_LDhuD0-Hu5JBo0,654 +babel/locale-data/fur.dat,sha256=pvg46WO2uwbg27leWyWYXkoqYFiaE1u9Txsd_iZJeC8,32441 +babel/locale-data/fur_IT.dat,sha256=PpDH1opUDjndOtvpPadiOtITGyGxKZY0ziEhb-4HKbE,654 +babel/locale-data/fy.dat,sha256=zJce96OaCoG-oKfhQFisyjgVEK3pNVtF9RT-f9h7y1g,108200 +babel/locale-data/fy_NL.dat,sha256=I_M2hFVCAaVAL90yVMloW2eYmzO5C3NZztfEDAes3_I,653 +babel/locale-data/ga.dat,sha256=rx5MLgtNUisB9V7gu0e2dNyCXF2H9KhLVULuYvLNUh0,261523 +babel/locale-data/ga_GB.dat,sha256=NKphVJhmH8egga1dgfO-hSRVFhbxms2JvGMU3ybgj_Q,653 +babel/locale-data/ga_IE.dat,sha256=P948T1Tl4B7kL1T-uxii27q4IWO3yQQ5emTLv7iHvW8,653 +babel/locale-data/gaa.dat,sha256=W9tUKmjglnSUxVlpiGA0clKmagjmzgF1Fwj_UPmg8Ik,33806 +babel/locale-data/gaa_GH.dat,sha256=FF7vo24vzA2ru3t8py246uanyOqyoSkpD65F7KFTXlA,617 +babel/locale-data/gd.dat,sha256=AtSvR97H05LvFNLHpLsb7tOgKYaOjYvWFOLaDDH6xMk,280763 +babel/locale-data/gd_GB.dat,sha256=weaRul21diYZoqLk7SXacBy8GD1bH30cQO6AHejilPM,653 +babel/locale-data/gez.dat,sha256=6gIHA3BFc3-A6-dOq0pc6bqP2ej6kmG9Xvwa0EEhoMo,12743 +babel/locale-data/gez_ER.dat,sha256=MKpSW7Upkpe42Q2mbJStbjtnrW6Kyo8Tg0zXAxnHWqM,638 +babel/locale-data/gez_ET.dat,sha256=93QTSMihpcfFyRhttFplgAcsAhzrPjlbrz5HI4feRSc,636 +babel/locale-data/gl.dat,sha256=N2zvS1gg8f3l6SOLqIIiKCBsbxAcArFclISemEyGx7Y,153642 +babel/locale-data/gl_ES.dat,sha256=us8xWRQnNTO_KC42-FOU2cnLCNl96591qnxZ4exq5Fc,653 +babel/locale-data/gn.dat,sha256=2CbBgzd3kHlm8uIpKVV3yurmEXB_yNUfM2NKTFV95dU,2477 +babel/locale-data/gn_PY.dat,sha256=tArgCo85T_BUNmPc4sXVRz8X5wcHJmBS6XP0r8dPgA0,635 +babel/locale-data/gsw.dat,sha256=TGHS38pKauNWMdFFsCyCJ_9p17aLt14jMA56gxTDBM4,95265 +babel/locale-data/gsw_CH.dat,sha256=fQlRCd1MJ_gI6rcnkrU-lUE7ZM4syRLh-suMRT0DCOk,654 +babel/locale-data/gsw_FR.dat,sha256=vHNQ1q9Zyf6xLfsJpmqpaX1iHerMRt1dsJSPQdQDoTE,654 +babel/locale-data/gsw_LI.dat,sha256=dsg7TA7MECMrbTXC7i7zXIjrEoqrF2jZ_ckGiTMholo,654 +babel/locale-data/gu.dat,sha256=5A_T6q__RzuWdGg9Sfte2wOqRw_Adz-LJMJJzFJ87cg,208242 +babel/locale-data/gu_IN.dat,sha256=shXUMyk4ogB8uJPazALoReGyLqRXSYOeVBVMn313_Y4,658 +babel/locale-data/guz.dat,sha256=491I3x6EkMG0roSEKI3sHxtaweXWNXhXyk4vsG-LzDg,15427 +babel/locale-data/guz_KE.dat,sha256=Uz_x0_YFKJHDkSd46SvuIfO7W0c-uqYnasmmLNib3WU,636 +babel/locale-data/gv.dat,sha256=Ye3v_q7SrnCeiOVw1r7NRcTtUZ3RwOr1ZKs6skxYTos,3960 +babel/locale-data/gv_IM.dat,sha256=bXOsSoV303kSUECHz2xd5HhASRI-XCjqjxnxpk0Bt10,634 +babel/locale-data/ha.dat,sha256=fVfNqCuIXaCTV5E9XNMNVROCwgalJ8f4MKfEvKun3Ao,150526 +babel/locale-data/ha_Arab.dat,sha256=AUoCyyrZDTrf5SEZ2SRizimC11NMSCyonIkMRer1PxY,2197 +babel/locale-data/ha_Arab_NG.dat,sha256=oYJYqTkaDX2p4IJ9j3sEJT49dcVn_f9JyjO0rUJTmAc,616 +babel/locale-data/ha_Arab_SD.dat,sha256=7DD6pzCmpEdjwbTMAbAb_vJey6Qh0tIeSs8LjEpJxBo,678 +babel/locale-data/ha_GH.dat,sha256=Cwfuh0nEMJu4LGZmdOXJHp_5fZRdpTIODJ_Tpd83uPM,1231 +babel/locale-data/ha_NE.dat,sha256=glKzRtSaovyoiyBc-vjFbJ0wNuxfga49w_zeK3ANtvU,616 +babel/locale-data/ha_NG.dat,sha256=oYJYqTkaDX2p4IJ9j3sEJT49dcVn_f9JyjO0rUJTmAc,616 +babel/locale-data/haw.dat,sha256=aNXNxqponTYIxzV21MX0jIzG73qbhbNO6px2WGael6o,10375 +babel/locale-data/haw_US.dat,sha256=W2qJBTNdETpoN-Ue0UYbqyblj-gIbQNOUMr01RwJ2fw,654 +babel/locale-data/he.dat,sha256=o6AACO6FcY_o6bcUuLkcWgbWIT77K8N8BTBDLY0hk7A,217062 +babel/locale-data/he_IL.dat,sha256=KpQ8sod_QOZ94Tr0tfhlATzVVsrqXck6eq7txoQgCbc,678 +babel/locale-data/hi.dat,sha256=TZ6kgfAZH0cuoX3Y7FpTEFJUEmlrSGccY9nWPDk27C8,221948 +babel/locale-data/hi_IN.dat,sha256=UMkxSHgYSdX-hPgYa7rOOzft7xtJwcM0m5nTphUe9xM,658 +babel/locale-data/hi_Latn.dat,sha256=Dn0MJdILlsVYU9R5BJFSPGGr1a35jF4k9ebJMKLX69U,30945 +babel/locale-data/hi_Latn_IN.dat,sha256=UMkxSHgYSdX-hPgYa7rOOzft7xtJwcM0m5nTphUe9xM,658 +babel/locale-data/hnj.dat,sha256=ryXdpBMEUzFyZ_u_YiqAK689QOsEuUO_Auyuhty_4jY,2117 +babel/locale-data/hnj_Hmnp.dat,sha256=6GekZXtZVXtGcI_zk6NqXq3WxMnOx83IpRlJOyUws6s,746 +babel/locale-data/hnj_Hmnp_US.dat,sha256=OydEowg2bNVks5nd5ij7ZJqqQT0ztYv4HL1ZZ4gNajw,654 +babel/locale-data/hr.dat,sha256=kdzKpWmyLAIFw7D5DPKG7aHUQjYlM5fctcBLRoHeujg,207636 +babel/locale-data/hr_BA.dat,sha256=E_S8FRdn0yszTWFg9RlqTV5zEwCc7-qtod45Ud8LHyI,1188 +babel/locale-data/hr_HR.dat,sha256=PqaNYKNdTFgfgmeiINdYp2d3q8qpfkCUs1y4ZHmsphA,635 +babel/locale-data/hsb.dat,sha256=w5bDS336hxU-RChl4QgaayXgMgN_VBUh5GJXznPI_Bs,206887 +babel/locale-data/hsb_DE.dat,sha256=_ELT9KBswSqeyiwbcsP9nFY793FE8PQzEu_2w78Amro,654 +babel/locale-data/hu.dat,sha256=dmS-7GInw1eGbifjgMyXc1m1nBg6dK6IOJIDoLb_Epo,147032 +babel/locale-data/hu_HU.dat,sha256=r3RZskHfUdk1Ba226wqBHAoRMXorlh-IkWLlnY_mBTs,653 +babel/locale-data/hy.dat,sha256=R6n2G8QdkkxlAxBgpTK7l-cPhwHg03vIG1pwmWVrCV4,202316 +babel/locale-data/hy_AM.dat,sha256=r1Bb6WWX2QxVlLP5vRVgox0HgoDOosA-h3oYKLeZMIY,635 +babel/locale-data/ia.dat,sha256=Wa4yDTDv9TA2Imo69v_49GqgV8LvQP1iuWGq6LFAMOk,133538 +babel/locale-data/ia_001.dat,sha256=fzAkHPu7PNj2TOl-kQcJLIdlEK6z-KfVFUVcy-YF5Zg,941 +babel/locale-data/id.dat,sha256=CfCCzK3VV98obeH7x1Ueax-xYczfCwextwJ7aetTQ5A,126977 +babel/locale-data/id_ID.dat,sha256=GvwO35RP_Y8vswE60ItYMQa7XUOBXGLk8tvL8kSJkY4,635 +babel/locale-data/ie.dat,sha256=9nzS8srJ9Mze83D7nKqXSlGvjOtu7utOyAJCLaffEm4,77957 +babel/locale-data/ie_EE.dat,sha256=93dmapYNE5HEAfIRwL_HjNV4fpCJhy1CseggbriCXL0,653 +babel/locale-data/ig.dat,sha256=ts_BawFD_ves9B7kX6GAIxwBY8FP4jNmkZidIdAVaDA,78550 +babel/locale-data/ig_NG.dat,sha256=2MR603ed1L8Y_aZhXPk7K7J8bgQPE4xvvpn8R9Jodiw,616 +babel/locale-data/ii.dat,sha256=WBkpukOuFcoryzq0QnHhdafGfad_u9vdJy0JO9vo-iI,7477 +babel/locale-data/ii_CN.dat,sha256=Vr_Oe23kFnz-EDfl7XeDv6GeTY0LSK-VV6UJpzU8yJE,635 +babel/locale-data/io.dat,sha256=laqBHV7QB933VjLibEOmRezXV9StwhKVzKvWp8e62YA,932 +babel/locale-data/io_001.dat,sha256=T6zZvvSVOcdpy3fhXbdPAWmHCvDTluick2U8_hUDu2E,912 +babel/locale-data/is.dat,sha256=r9gnQpf81NqPVskVKUiSGWa6VU3_NXzJcna1Gho28d8,163097 +babel/locale-data/is_IS.dat,sha256=f3tUj_oYhQ6_tGnn8yda4PZfqChiHmcZXo_nMDCtzSQ,653 +babel/locale-data/it.dat,sha256=n8pxKaMuS2ekD7EOSV-3PmVfdzm9NqxF5zA073x5itU,173579 +babel/locale-data/it_CH.dat,sha256=Tvtge1s4BcbCeKJmPBiSbtbf_qv8wrw3Eb4Dc1sF3dE,2942 +babel/locale-data/it_IT.dat,sha256=-e6EK0A6gEd_G4_BHlmd8_caQ-k_JrEemNgdA0czhsQ,653 +babel/locale-data/it_SM.dat,sha256=xjE63TohE8bjkNiOh-BhOkuB9YWpQ0n2rrBMJoAql5E,653 +babel/locale-data/it_VA.dat,sha256=wwsl_eAmNu9m2JPZnpBwFVh4gsk5AM9lSx8UAXhrMlE,653 +babel/locale-data/iu.dat,sha256=vZIG1AsqwcrTA6zfwBeQydVAn5qDQXT8yTK8wvbaVAA,3209 +babel/locale-data/iu_CA.dat,sha256=C449KxuskTW8w7z56Vv602QRYsQ90O0WbazR7VqlDa8,635 +babel/locale-data/iu_Latn.dat,sha256=oYsdWLomYvVJg6kIQIKYOdSGWyrEHftc2U9bZ0sHFD8,904 +babel/locale-data/iu_Latn_CA.dat,sha256=C449KxuskTW8w7z56Vv602QRYsQ90O0WbazR7VqlDa8,635 +babel/locale-data/ja.dat,sha256=86xPBOY1H1e0fLC6g_61R_ipS4QGbB9sXst_JwwdAWo,179695 +babel/locale-data/ja_JP.dat,sha256=o2pbFmlkEZjV7CHE9cYENYVv2kQA8texhNDhEecgQ9w,635 +babel/locale-data/jbo.dat,sha256=2eB9ymOoFb8abB-v7ISAGcAg4HEEMmpuseHIXFgtqkU,1008 +babel/locale-data/jbo_001.dat,sha256=wwLKEXJOwULYD-q92kQdvpiwW2fgis5YcsP2mT4tnT0,746 +babel/locale-data/jgo.dat,sha256=9X63SqWqQ7TK7Z2RKaQwFBP9sMog_4cQN5Sr_KBlyL0,9031 +babel/locale-data/jgo_CM.dat,sha256=1lDyjZLpK6fS6oSpfnVLdYd2IUgGlUQfF53BDdK7TLY,636 +babel/locale-data/jmc.dat,sha256=lRstulkfCj6kDI8VLH_3GS4awAqVsylMU1hvvWH7kJg,15374 +babel/locale-data/jmc_TZ.dat,sha256=fODp_OsVygbH0UoRsx4HHcXrde02bRKc-hy80sDkcK4,617 +babel/locale-data/jv.dat,sha256=SucM3ZDoHXO7PsbOYNLdx0UmQqWUll2i1hDqn-zJY9U,103138 +babel/locale-data/jv_ID.dat,sha256=G3zTSeqWMngemp5lAcghudTcv-i8egZS12P0-T1DNXw,635 +babel/locale-data/ka.dat,sha256=dIskDjc7ZdRZ1rGJIHxOP_47WO_cdjLDx4eltYETbUE,230234 +babel/locale-data/ka_GE.dat,sha256=zzw4NZBk8dsHIBeEzB3Vtf2_1nC4DhqblLoBUXfNHN8,635 +babel/locale-data/kaa.dat,sha256=KjLQ7_UT8pqVmgqy5BXJHe9hbPceKi6AIyefM4_0pYU,17585 +babel/locale-data/kaa_Cyrl.dat,sha256=zW7tFPX2CPo0NwR2o1DCKu8Tsvm-Aef9RAuy6AcaJXs,693 +babel/locale-data/kaa_Cyrl_UZ.dat,sha256=DugGAZvImbUSHvvGmGw0ekXXBj34vEjSkHhSxVXbc-g,636 +babel/locale-data/kaa_Latn.dat,sha256=zW7tFPX2CPo0NwR2o1DCKu8Tsvm-Aef9RAuy6AcaJXs,693 +babel/locale-data/kaa_Latn_UZ.dat,sha256=DugGAZvImbUSHvvGmGw0ekXXBj34vEjSkHhSxVXbc-g,636 +babel/locale-data/kab.dat,sha256=ykLRvdRtHpY8y7JDHOgC03KxAyvAfxzYQUl44CKKr6s,121528 +babel/locale-data/kab_DZ.dat,sha256=v6fd0kyletm1kfN2ibSUh6oddckpLHjPd_qrq6FQpd8,679 +babel/locale-data/kaj.dat,sha256=tKKi4eYHT0zEJLSHi2DefEGyTTu4RCKpnANu7NQhAPg,2696 +babel/locale-data/kaj_NG.dat,sha256=1RODrBAPxCrrlTgfhpdz-VxQkTdjJEJYHaJEsOgQsNk,617 +babel/locale-data/kam.dat,sha256=br3FOMP0rS2tAq1tSXwYq197v2XXHqWtX_TM-39QP1k,15506 +babel/locale-data/kam_KE.dat,sha256=8AQQhHQdaI4rBKXfTivg884XIDfZlbDEQM28KwvB2Og,636 +babel/locale-data/kcg.dat,sha256=dOZR1bP8sPbhD2Mk496Twz8MOivgGQoliuDMAWSO9fY,2549 +babel/locale-data/kcg_NG.dat,sha256=jCObRZa-t9Khgx_M8ps0PLbeit7rPCFtHeCOhtfCnOY,617 +babel/locale-data/kde.dat,sha256=cFVUYeayn6-kMX6PwQQ3J8rpHMql07_V7lSluInC2x8,15810 +babel/locale-data/kde_TZ.dat,sha256=2sfb3gejYM15YtZeRrc0AJgdaJhqgbVJ9c9yF9SlJmg,617 +babel/locale-data/kea.dat,sha256=o2awnBismFEWQemtUPASmZMVLlOkJNDtuD6egmvMatw,76097 +babel/locale-data/kea_CV.dat,sha256=LyzxPE6yP0f9hdD4uvu8CvEp8nmcmRDVuRI8YxBQ5wM,617 +babel/locale-data/ken.dat,sha256=PMrEuqYRQIhbpP2dPowHOL_NKKFpEBd8qNUV_NpZnDw,719 +babel/locale-data/ken_CM.dat,sha256=jNEojAAtQqJMkEq84kxHU5hGOVQCOW_hnGQScxycYMo,636 +babel/locale-data/kgp.dat,sha256=OKx1ZbBdHw75AdzrT-petm32Dj8wJdtUQH_oai7v0qI,184092 +babel/locale-data/kgp_BR.dat,sha256=FNSYAqPhkzoZzholxTydgasgkuw7jgxCfyKDbeRiLt4,636 +babel/locale-data/khq.dat,sha256=OcW4Zprt-WlTePo7DzquB-yS8vwfPqjaijpkrX5l75w,15753 +babel/locale-data/khq_ML.dat,sha256=ViqThOcZTHZBtebV8LJY-DXdWVX5jAR5pdlBrTKws2Q,617 +babel/locale-data/ki.dat,sha256=vffpb6Wy7TN__hZQH1SJYi81AxamhBnEK_XBCiF3OFc,15451 +babel/locale-data/ki_KE.dat,sha256=PNoxhP-WLxW2vH3fS8N9Nuy0tVimMP-ZAaCfLfnfDN0,635 +babel/locale-data/kk.dat,sha256=93jtpKcfiQBRbyeOyJgqETZXGOntosB2nGedgHShM1w,200871 +babel/locale-data/kk_Arab.dat,sha256=TBsZm6iLJ_dw2mNxDdQk95jELZ87mArNl12iktCwKwo,222820 +babel/locale-data/kk_Arab_CN.dat,sha256=HCoaBi32ENdpJx5usXdBR9aR__MHFeKV-wTo_53tQNk,635 +babel/locale-data/kk_Cyrl.dat,sha256=z3n_4vbUD_OcZhi9Tm8vkSu9h4RUgavZ_CFrujFbuWE,1584 +babel/locale-data/kk_Cyrl_KZ.dat,sha256=eaQW7Va1z2L34hs-D9QgrmyvOo4Lpy2uxxvxms6kL4s,635 +babel/locale-data/kk_KZ.dat,sha256=eaQW7Va1z2L34hs-D9QgrmyvOo4Lpy2uxxvxms6kL4s,635 +babel/locale-data/kkj.dat,sha256=IYeF4Uoeb0kfxooFLFcqmdIwGWbWh0MUfBpztTxWFEw,3373 +babel/locale-data/kkj_CM.dat,sha256=gqHwzVPG_Lo3svaIVe0Kfhj4DaKtstOxJJhUGvvpSbI,636 +babel/locale-data/kl.dat,sha256=D8SSjxLZliXOxyTxpC1x7jYbFQn4WbSV3lfotFFt3Jw,47570 +babel/locale-data/kl_GL.dat,sha256=c49k11GtBG8-vExsQNo_d1pFaQm0LYmXl7QcMSTEApA,616 +babel/locale-data/kln.dat,sha256=pldtRERWUhEOA85_zeFJfshpMl6gtgdELjGyNGtJzOo,17427 +babel/locale-data/kln_KE.dat,sha256=ZhkKgZ2lTdWAvWVOJEFlp4ASvjk2hcyTc2_P9s3mykQ,636 +babel/locale-data/km.dat,sha256=TueDML7PuHjlME0Bew9xgkFSFIxBOsJTzpMwyjyvuXs,176653 +babel/locale-data/km_KH.dat,sha256=bnykdq-RYJ0ZwajV5dFBVtM4anVkLjXutFdUxKByQ2o,635 +babel/locale-data/kn.dat,sha256=n0AHHLkpl4FIYeiSCEMDJF9mFr5nCgHcV8HSCOKz8yw,267281 +babel/locale-data/kn_IN.dat,sha256=rx1LJYeAP8xU6MIU3yhHLuije5OQWL_wzz5glztIdi0,658 +babel/locale-data/ko.dat,sha256=tYIinjTM5ZLw-AvWerUR8h4FRQ09LGfoRNyutl2iMLI,154621 +babel/locale-data/ko_CN.dat,sha256=U_q1api28ucaC-ABOBb822CgSq4nywhMN02uS7TCREI,1187 +babel/locale-data/ko_KP.dat,sha256=BCMOKQSzXNnjAUWfZZ8pbFoSDBed9tnnatyqIePNWio,816 +babel/locale-data/ko_KR.dat,sha256=CJqKij0oULIeCIG8McohdnhEsfJxUwmJHOpJY1bLrNY,635 +babel/locale-data/kok.dat,sha256=1pS0SXgetR-9DHtL71PAw2VIRp51a9KfxoSnw3BoxFs,179992 +babel/locale-data/kok_Deva.dat,sha256=EP15THeV74hd5G3DDXlwIHzxQvpECa67TioBGaur3l8,693 +babel/locale-data/kok_Deva_IN.dat,sha256=GiRy8Q6VCULzgIxNPFEZzABKOg13Un-DblBwXnBSeEk,659 +babel/locale-data/kok_Latn.dat,sha256=jVrQX1SeylbWO6B3ana7r9r1sRvMyOvBVwPfyXxB4uM,29867 +babel/locale-data/kok_Latn_IN.dat,sha256=GiRy8Q6VCULzgIxNPFEZzABKOg13Un-DblBwXnBSeEk,659 +babel/locale-data/kpe.dat,sha256=AHR_ZG1p518D4yulM_cs5E58iMsVXKk4Hi6k0V0IDhQ,1331 +babel/locale-data/kpe_GN.dat,sha256=7eabSFmszupn0IlzP6TRg1-7FZS9AB3JdBkDgJtIs64,1210 +babel/locale-data/kpe_LR.dat,sha256=4O2gzHn_orogC0qOiIyIox-JjyA73hEAe_fZ3Fj5tYc,617 +babel/locale-data/ks.dat,sha256=KySSApsfBfxM26TaXsK2gChd3noV1e5l41gUsaC3DA4,111691 +babel/locale-data/ks_Arab.dat,sha256=fzDL_rCiC-BqymPhF6GBX-d5uVkvZCE4e8JS2L8cjRU,850 +babel/locale-data/ks_Arab_IN.dat,sha256=-OE8PBigMDH8zQrxiaffmabm91Mi8-OVdDR2JEK8xPA,658 +babel/locale-data/ks_Deva.dat,sha256=weXN4SV8247g4MW8R7MHXM6sKk1qhf2Wmowls_zowAM,12510 +babel/locale-data/ks_Deva_IN.dat,sha256=-OE8PBigMDH8zQrxiaffmabm91Mi8-OVdDR2JEK8xPA,658 +babel/locale-data/ksb.dat,sha256=vbB4JVStmpZIu9_vi6f9-Dt40sN2wPvF9Y2JzJJ9oEY,15356 +babel/locale-data/ksb_TZ.dat,sha256=PvWBMUhpMhV27zE9BCEJW8oMqRXWK-6VwmSsXZDMTPY,617 +babel/locale-data/ksf.dat,sha256=IIgvqtnlTxMmLSk38JyZe0PA5gLEX86ZDcaVIULp7Zc,15952 +babel/locale-data/ksf_CM.dat,sha256=0OOsuUS0S_AUxUPySFQOZVbPcB3F2I4yzHZm63YypDU,636 +babel/locale-data/ksh.dat,sha256=M6W02xiGUibDW13RIB7psbwMA2wdNYQHz3Bp9XljhZ0,76781 +babel/locale-data/ksh_DE.dat,sha256=EhgYIJQaC3LGiWrcE2sdfUC8LOWJnciWORi_yoRkOvw,654 +babel/locale-data/ku.dat,sha256=beByrhXjB8iRhaNGxkQaxnUckutus3MJ61Pm-Ro8SqU,117087 +babel/locale-data/ku_TR.dat,sha256=LoNyoL-poYXgqejDzB9-zI_0TIJF3amwyzcs6UnHUII,635 +babel/locale-data/kw.dat,sha256=x5JFxfv4DvLd5Xnq5J7JlbcffhYNXwIn5-Jp3Q5QV5A,7242 +babel/locale-data/kw_GB.dat,sha256=ol7LWhJJFNoNkFEjtk4x1PpLkML9UbuVmZAuG47LokY,653 +babel/locale-data/kxv.dat,sha256=AMdZKJIlfkcpSetPdGRHX8LLN0O1s14NAtDzekSi01E,69503 +babel/locale-data/kxv_Deva.dat,sha256=MlejiNswS_LJAemj-vyEyvXc2SyminqNCWbaq3eceKk,86834 +babel/locale-data/kxv_Deva_IN.dat,sha256=Z43Yc1G4Bsmu7KukFfgXfIl1Cq2Rqa7RQQNYygE5Aak,659 +babel/locale-data/kxv_Latn.dat,sha256=XqtuF9yh0tBrDPoRpZj_1Mw-QUqt98klOlgZXh7qPus,693 +babel/locale-data/kxv_Latn_IN.dat,sha256=Z43Yc1G4Bsmu7KukFfgXfIl1Cq2Rqa7RQQNYygE5Aak,659 +babel/locale-data/kxv_Orya.dat,sha256=lsjWswZU3LWmYNE5tLn9cla-WSbdfqAoltC_OvxtR28,85964 +babel/locale-data/kxv_Orya_IN.dat,sha256=Z43Yc1G4Bsmu7KukFfgXfIl1Cq2Rqa7RQQNYygE5Aak,659 +babel/locale-data/kxv_Telu.dat,sha256=rXJrWxHMFrPeheoNt9NvKLzNoGLpNXiHpc4PsuS4tX0,88218 +babel/locale-data/kxv_Telu_IN.dat,sha256=Z43Yc1G4Bsmu7KukFfgXfIl1Cq2Rqa7RQQNYygE5Aak,659 +babel/locale-data/ky.dat,sha256=PAH_ov1xcnJAie5ltB3I_XAUJsrE_8tFwFlOjorRBts,179889 +babel/locale-data/ky_KG.dat,sha256=CdtjRR2iZgfG5pdz13MX-EHTDtN6BdLeZwGCmrZnrkU,635 +babel/locale-data/la.dat,sha256=0TAdiOxADg0C2AeIj5RRxiLgMbVO-PqRMf4GAm1M4dE,33982 +babel/locale-data/la_VA.dat,sha256=dhULocaXyJGitrljd1w688DTmezcmmY8YcoKsT30n34,653 +babel/locale-data/lag.dat,sha256=mtjtyLHLCfoUr4STtBqLXOxLnZ0igugTC9fZiyJZk8E,16281 +babel/locale-data/lag_TZ.dat,sha256=LlfEls8DDvoVp07MYnFdLBPUhkJs2kzwUDk45BZIO-g,617 +babel/locale-data/lb.dat,sha256=lUGm84DfwLgQ3vC0mL_foFf5dSZZKDTIuwCRf5BjgVs,135641 +babel/locale-data/lb_LU.dat,sha256=s0AY2-F7BqA4tMcRPjCfbzbXRbHsKnD6uIQGSkulCT8,653 +babel/locale-data/lg.dat,sha256=LnMo-qAcWHbbAuRmCTz4_nBGHgUx0aojWG7C8TFn__Q,15834 +babel/locale-data/lg_UG.dat,sha256=nyn_HKdBHGkHlbjlDX28L5Z9k59MKFqvmVL3vg222s0,639 +babel/locale-data/lij.dat,sha256=Ic982St82mPMfuQqqLswPtOy8LlglfcLYk_ftIDM65Q,129648 +babel/locale-data/lij_IT.dat,sha256=rtPerU7ltwkHHc7HErfwJfX8TDSCpTHvBzwKBdSt4x4,654 +babel/locale-data/lkt.dat,sha256=U_yXNWWJkqlepaZ9kS-FVOAHQKVmc0vhwgWgFRzXnqc,11180 +babel/locale-data/lkt_US.dat,sha256=ywWg9m80tNjumAxGNrBwYhXhc28rC4OSdyvkdXAKs5Y,654 +babel/locale-data/lld.dat,sha256=2JBQPu8T-cMVqzVOFs0Vz-0hU2RtHMyTvj0F8wPl2EU,95451 +babel/locale-data/lld_IT.dat,sha256=rHSiZg_-Ba6si2LONUCmQ841RxcX2p7V1jDSuwNNBg4,654 +babel/locale-data/lmo.dat,sha256=GYoR98Ap5hosG9bfENEA3sCkR-msF_KvxV2nLydjs9s,1659 +babel/locale-data/lmo_IT.dat,sha256=Xg4Qvye_hPNJLCTwoBuRMAvhtjQb8VlD9SaH_5hVG9s,654 +babel/locale-data/ln.dat,sha256=OHqUbo7M-600CtpOUNqq3B-Tt0_L3c2GgyR2xZhuMmE,23909 +babel/locale-data/ln_AO.dat,sha256=VNcFViSaGw3f92D60Uk7vxQ8i6Q9H1FD6paDpJh-r4M,636 +babel/locale-data/ln_CD.dat,sha256=Ao_lUWuswJgGHhVJYs2_Z8LyN4iqgYsAda8f9UarT6s,616 +babel/locale-data/ln_CF.dat,sha256=g5Jy7ovUkwcn0W5Ov5bu5SBc2iM4SWwUFwtzWzAAb34,616 +babel/locale-data/ln_CG.dat,sha256=jQld0yw63ufWbhlOt81wjf731zZluc2zWc1A65kBlAM,616 +babel/locale-data/lo.dat,sha256=hXtgT4nkqb8h669TsazfIFBsK23QNpcCZbqvb6SXWBo,189363 +babel/locale-data/lo_LA.dat,sha256=gNa7KRGYWI0h0PcHJ35bwnPskILOH-1jLjHCXP85jaU,635 +babel/locale-data/lrc.dat,sha256=GbK-bWw4TlGvGRyM4WxDujkUr7Gdz9k2A__fSelwEdk,13594 +babel/locale-data/lrc_IQ.dat,sha256=cy9DO9oAKI51NMitAb2MTbZoxtaAPzaYVGV0-xd0mGA,1255 +babel/locale-data/lrc_IR.dat,sha256=-ca4EmTX5VAi_5tDfimpevJFb-g8vAjgpe_jkDTDZWQ,679 +babel/locale-data/lt.dat,sha256=1h5TjFvNSuqrE3JntTmnLN9wW69jfkVakAIICcmoVbc,262613 +babel/locale-data/lt_LT.dat,sha256=j8dIQrFc0s77xJ4P-UGiDcaNqZW6CXOEiOh30FemNiY,653 +babel/locale-data/ltg.dat,sha256=qRaChoBWcmkL8As9MgCysSh58BP_F3US65EaA97ZgZQ,2632 +babel/locale-data/ltg_LV.dat,sha256=Gm8suGbSLaNGzzWPjuEKVdEqJH18ABue4htegryH3bw,636 +babel/locale-data/lu.dat,sha256=4TGAGkYbDHz6QAKQHHXoRkl0Wx-0LOiMkTjDgwJq0TU,15331 +babel/locale-data/lu_CD.dat,sha256=K3k0wMpIbjusgPC2W1q2sxyxynx2M78zOLjt4ikcoZA,616 +babel/locale-data/luo.dat,sha256=xJNSb25OVQogO42d-Jy2fR1FzeSfW9Swwt9Wat4EmsA,15201 +babel/locale-data/luo_KE.dat,sha256=jVUd3oO_U7IP6KmvSkVHYNmlCc8_-m2mpJMBaqArSFI,636 +babel/locale-data/luy.dat,sha256=pcTbaSctQyVgVVh4nXoXxMQGZa3Ip7hsfRN096zwEsY,15030 +babel/locale-data/luy_KE.dat,sha256=NVkQAtmDBXUws3KybnM706J3lwUc5fJo-YEjKd1zhiw,636 +babel/locale-data/lv.dat,sha256=s10obHsTYkBF99rADGvkxupv--JkRPQS2aM6sWvbM4A,201861 +babel/locale-data/lv_LV.dat,sha256=oS9W0pdiTNtMTeGOsef2K7Va2O6k-dQEtN02qCaAoFw,635 +babel/locale-data/mai.dat,sha256=Foj4spiHhuOjBZqaXQxZNCWwdCoslwB7KSX6nXQReu8,95488 +babel/locale-data/mai_IN.dat,sha256=DoclpHa5nlHcAflg286ycOjQ6trZP8uHThzcp37Y91E,659 +babel/locale-data/mas.dat,sha256=xxugbgGc3JxvM2GzV1vmKy1buszviy7NtSH-y6GdMZ4,16595 +babel/locale-data/mas_KE.dat,sha256=zH3Le4j03YNsPjt_-GL5RvPG0znnJKQ3ifbLhMZsoYQ,636 +babel/locale-data/mas_TZ.dat,sha256=VyRJlBqV_fukTqSR9Jxr-PujSA7u-MP9aXrJBVJCKiA,638 +babel/locale-data/mdf.dat,sha256=NQH9Yo77oyvxZj_93XIf0e00JDAoI2ydlCSS3klNgwk,2240 +babel/locale-data/mdf_RU.dat,sha256=sVnTxgzU2rEwl7P4d5UY6XOKv1XUCbtkEQO0cu2OlOk,654 +babel/locale-data/mer.dat,sha256=WFl2qQThWrilUbBaN4UJQb97ZrV3uOB4ZTO9kXsjtnQ,15426 +babel/locale-data/mer_KE.dat,sha256=WqDQTsO6BtbROdcj3p3omUZk8XB6HiJrIlCCj_wLPxo,636 +babel/locale-data/mfe.dat,sha256=n5lJPfd4pjC3xmVm123ivVpjg-WAkLQq9YONw1JlpTs,14835 +babel/locale-data/mfe_MU.dat,sha256=MHUOKiQPHuUbhT6hQXJEkpGDvTUyMV4btJTuiLhJQ4k,617 +babel/locale-data/mg.dat,sha256=9PbZhJ2v9RP2XSzwv7bBPTGlSNYJrUUISNkTwdF1Z7g,18776 +babel/locale-data/mg_MG.dat,sha256=uJbGeEzIn4Rh5Bh4hyV-Hs0_qez9KBeOi4hvfC3zkIw,616 +babel/locale-data/mgh.dat,sha256=ezIhzOd-qzUBRud5ZPjbtBBc_mON2HuKGu_okOahCbk,9753 +babel/locale-data/mgh_MZ.dat,sha256=aFZZ41qtARE6CeecCEx99tzW_fp-XfkAc10wpQ5YeGM,636 +babel/locale-data/mgo.dat,sha256=xyRaEmGV6Fw9nVfReQJY7KfgGwdnvQzScLiH0appsLU,3549 +babel/locale-data/mgo_CM.dat,sha256=Nsp33t4abZsKdDmIgB8tb08OsEh-7JXMY_JKCsd61zY,636 +babel/locale-data/mhn.dat,sha256=RpSxxevhvYxyX0dq1RAhIqoREyyPwKjsByXvOhQkAMc,693 +babel/locale-data/mhn_IT.dat,sha256=3UoH_l7yItN5qXKPaq6bWK_CvK_wD68zkxzLrCwxr9A,654 +babel/locale-data/mi.dat,sha256=ZlyyKSUJCtl7z94uIwEFuC6uBGWXOTM8F1TJLiJeSmw,85086 +babel/locale-data/mi_NZ.dat,sha256=RElP_P4F1HDHjSHsdt54vriFcz0TcIeT26pm-_dy-Mk,635 +babel/locale-data/mic.dat,sha256=Sp71dA3ZhTiiPTfk3b0Mm6y6CM0J-mGR0av-psrUGtI,1638 +babel/locale-data/mic_CA.dat,sha256=uZoBvRFux6M40HcGau7E6V0Pyxh45Bxi0SFngOxLWRQ,636 +babel/locale-data/mk.dat,sha256=xVJU-aubw2FQ_THzBUGd5LHR5IU1VjGkPOeRHpU-T98,216606 +babel/locale-data/mk_MK.dat,sha256=qlI1ZtLgyX2fcYS9pnTa8illdSnPmfNXORjws-H93lc,635 +babel/locale-data/ml.dat,sha256=tR0lt2uQuzmsBgXWIuRpz-AzD00S_OrvLc-QkiYAX-U,246736 +babel/locale-data/ml_IN.dat,sha256=VwCnSFs3oh7xo4-nS1HzWj_xICDzG6E-AW0Oxi6Lu08,658 +babel/locale-data/mn.dat,sha256=O3oSpnDd3Pt-dRrwdg1v245S6Iym3SBnZZ3gqOhQW5A,183647 +babel/locale-data/mn_MN.dat,sha256=qhJGIDXSglsa3BYCalsusoyiKgOj7PJ90Yi4ET2-nxc,635 +babel/locale-data/mn_Mong.dat,sha256=zqCuL28Sx1DS8LGqtC2nyTCoEpSFpF0vE3wvBxpHK1o,1726 +babel/locale-data/mn_Mong_CN.dat,sha256=WTF89EShdtBd0Vz62erFwl9KTG-GgEyzjjmKWuJCFH0,635 +babel/locale-data/mn_Mong_MN.dat,sha256=BTah7qfU9QayUb7evUKdF9c4Ptf4NqqO6SUeUql2Bak,12935 +babel/locale-data/mni.dat,sha256=aS_XIOmHRptFT_dTFuxgTvB2cEPKHJVVM_vtURTFo7A,12972 +babel/locale-data/mni_Beng.dat,sha256=7PTKZT_UE3WNiipyGHnn2SDNFNhAcqHhEJ8T0Y3O7gI,693 +babel/locale-data/mni_Beng_IN.dat,sha256=nAW_LfRtJqC6-ehdC8yyszjhibXqMAXPkuaz8POQav8,659 +babel/locale-data/mni_Mtei.dat,sha256=zdzwNyaRIil7lkjEf6ProQX0nwYlc5VF4r4gHNTUVTY,1720 +babel/locale-data/mni_Mtei_IN.dat,sha256=nAW_LfRtJqC6-ehdC8yyszjhibXqMAXPkuaz8POQav8,659 +babel/locale-data/moh.dat,sha256=FuywEiFRXQe1GUv9zPR15KOsZLr1TykmuglinLINTqE,1316 +babel/locale-data/moh_CA.dat,sha256=AXdrS_As8DrrTGs5IHmQwXoEXndFuoWoTGXPXcL4Pk4,636 +babel/locale-data/mr.dat,sha256=UG0FkufUtQ7qvzcK_WENRq6SgPy4zAcviYDQL-NrGY8,234157 +babel/locale-data/mr_IN.dat,sha256=HOu9QR1BLbciqF6BeTfWvD0L5Igwv5HrCWu9y_vNns0,658 +babel/locale-data/ms.dat,sha256=l_YQob4w4jdu0U-Z-eMYK2bPAyU0Xeekar0olT5G21s,114970 +babel/locale-data/ms_Arab.dat,sha256=9zretwvdo_9N3SIxLKsUtDVNZlmh0OhrPDxmtWqw22U,14359 +babel/locale-data/ms_Arab_BN.dat,sha256=vTEKSh0hjyiWJhf503-fwjADvTCN0AqRSzF6muL52JE,1335 +babel/locale-data/ms_Arab_MY.dat,sha256=w6ZZrz3qEmnsLsWtJA8rTohqF1cxctkzSj4WN0XHuHE,635 +babel/locale-data/ms_BN.dat,sha256=vTEKSh0hjyiWJhf503-fwjADvTCN0AqRSzF6muL52JE,1335 +babel/locale-data/ms_ID.dat,sha256=ZPgnDJgiycDHGZnN3Eg7OgZp1jIYtiZzMqm9f3LOtR8,3440 +babel/locale-data/ms_MY.dat,sha256=w6ZZrz3qEmnsLsWtJA8rTohqF1cxctkzSj4WN0XHuHE,635 +babel/locale-data/ms_SG.dat,sha256=jsfNag5WQWdejzh5hayA7_-IbY03MUVPAPDHUJyCZRg,654 +babel/locale-data/mt.dat,sha256=Xx_-as8jJEXOBAgfFX7vE6-2Qex4FWJ5rYW3DlCbu0A,49384 +babel/locale-data/mt_MT.dat,sha256=L5q5LizZQL0-FLj2U-wn2YhkpL3p7ZvjdyAvH1wwUG0,635 +babel/locale-data/mua.dat,sha256=N4zi8XyQdW_TmszNHQyn4zmia-MpCNWx_Vudoaj075Q,15861 +babel/locale-data/mua_CM.dat,sha256=Ib4K67KfndwGsoZDH0ehHA-F7V_SUj7q9VsK2juzaIQ,636 +babel/locale-data/mus.dat,sha256=zPqFwR9oCIJ0mA6evYwT75cWt_QBF-0eDZx1n6fmsDY,2722 +babel/locale-data/mus_US.dat,sha256=xcd4KKUIqBwSefSC0GGSDkvXL46eDWgWolJsGuSf654,654 +babel/locale-data/my.dat,sha256=9crk_2tyVPKBH_YWC_D0h8o1nbHH9k34e414JywM_6E,183534 +babel/locale-data/my_MM.dat,sha256=l53-D5YL-BbmLZYveFV2ISa4NcrSwTfKsRRc41Jd42U,635 +babel/locale-data/myv.dat,sha256=78-9U5eDf3V2q3Mj0MazIxh1n8sjyVjQ1O0Oyt5875M,16828 +babel/locale-data/myv_RU.dat,sha256=sncxwOpjfFkRASKHMP1ytKcjpF4w4GjU_JEVqXv84Tg,654 +babel/locale-data/mzn.dat,sha256=8Y5VGlzCxKXzBP5iFsyflLZSS1x0_3T8sqbwivfU45s,46144 +babel/locale-data/mzn_IR.dat,sha256=fjN1ek2fAzZyzFJpjX_MIc5oFOEqj7LS_SNdBOQS9ZY,679 +babel/locale-data/naq.dat,sha256=N07nsBSSEuNZ4feWvo7b4YWwbgjBwlxG9653O-OlhmI,15797 +babel/locale-data/naq_NA.dat,sha256=e5gAIOo7le-dVOAX_k2OW6gEyVfEUxcYDUJu_MBDY5M,617 +babel/locale-data/nb.dat,sha256=-bGHDSonSEdqfb2PxNC1_3jOHELm3CwV7xzEtpmWZUE,1330 +babel/locale-data/nb_NO.dat,sha256=J1UiN-AG900acMz4p0fBHtXDpS0HD6uGAi7gg9gOrsY,653 +babel/locale-data/nb_SJ.dat,sha256=XcK9_nKriUY_4k09kmgJalGTls-e5-YS-MCPEKLP8vA,634 +babel/locale-data/nd.dat,sha256=ITmP8mLDb63INBKlxe3yCZqRbvL83DrVBkIIAA_YjmA,15730 +babel/locale-data/nd_ZW.dat,sha256=KpZHrPr_0Ru0t9ojSK9ZiqGZ6-rSpvA7cX9C483ocRk,635 +babel/locale-data/nds.dat,sha256=q3dxEsEcdLTOigPQxhEXuaA4U03umMzIBNC4NkFvino,47839 +babel/locale-data/nds_DE.dat,sha256=Qmh5DjX65W58jXXXX3pxfxS_HVTJVavMspBPdph9sfc,654 +babel/locale-data/nds_NL.dat,sha256=E_EO3ylaBjJNThr211Yzefh5DRbkEFFVpbBMXIQ4U1U,654 +babel/locale-data/ne.dat,sha256=5Ob8IUrudfJp6yCzhHkmYC2mkVY1sd10I79pSXzups0,210152 +babel/locale-data/ne_IN.dat,sha256=SFloADZtyb42wygQmrMy7yx-aADGQECZJUey796oqnw,1292 +babel/locale-data/ne_NP.dat,sha256=LGc7Z5TRi0HRNQdY4bqPitV73gzTPWLd_g-6aPyvryc,635 +babel/locale-data/nl.dat,sha256=zO55PQW5wWMzjRmUGEi5l4H1D1rC90NQt262jbRIyzY,150031 +babel/locale-data/nl_AW.dat,sha256=HYycPPe5HDhuP_zN89ysXR2AqjIbx_-XB2n0-TUPhxw,638 +babel/locale-data/nl_BE.dat,sha256=xHmyTOK9-rwPAH6fYdlPFZv-xinmTFNRN-LIGxb59HE,1876 +babel/locale-data/nl_BQ.dat,sha256=lZQzrmWsFTpiWwT9l6FakrNoll3QNP3drIMcxFkAy6Y,635 +babel/locale-data/nl_CW.dat,sha256=doEeLSsQVaSocP4jr5LxNrT5luSiG4hkgsMvoYjJBtY,638 +babel/locale-data/nl_NL.dat,sha256=bGo4nyjhqe1VtDHlP5EUmNkBKsViUSiH3h_ikXcPuRo,653 +babel/locale-data/nl_SR.dat,sha256=d-YXA5QA5b2Bh-WhxNTkyKQ4ROhuEg1CeGN5XmzWcZM,696 +babel/locale-data/nl_SX.dat,sha256=ciTQIntv0njAJGIUENzo6stN8yarfh-eBpmEzxmpJbs,638 +babel/locale-data/nmg.dat,sha256=NEWUb0RI43oeWByH9l1FvrLDvVQY6501jviEYmM_CCs,15468 +babel/locale-data/nmg_CM.dat,sha256=0A-cVUgt1l1ITnPRdcgWiQ68ZGqHDYd-k2wSc_nXnMQ,636 +babel/locale-data/nn.dat,sha256=E-Y0lC-8QKCXdc8CC_ZPN3bstsXsM3MD4Lu62XMMq3U,64042 +babel/locale-data/nn_NO.dat,sha256=98eWK3CBq5T_dI4a6dQH2TktRygMH0EnpO1EO7w8lVY,653 +babel/locale-data/nnh.dat,sha256=cWTTqJEXln6pUYON7H4PRrYr_uRWktSzcLGCC8jnTno,3530 +babel/locale-data/nnh_CM.dat,sha256=dt9ZNHX5ZQo-kcyT4-mVRCUiuzL-H2A9e0s0JrHwZts,636 +babel/locale-data/no.dat,sha256=xxkSV9bwDKWKK6dmGIaZGo4snfkON9eJUp3-uvlEC1w,191212 +babel/locale-data/nqo.dat,sha256=VQzcAnkUnAETAL8xosXBsQltlC4yqpSZNMkwtsWPRgI,109412 +babel/locale-data/nqo_GN.dat,sha256=3eOSTBaZM0RNLUDZFr__KTR_9iZoGUFq9M6bhtmHjcU,617 +babel/locale-data/nr.dat,sha256=ZHIIC8BaR5XBmnULOTfmAYzXmqImq5QmELqbFPKKQJo,2167 +babel/locale-data/nr_ZA.dat,sha256=e4_czqcyMye0qthJd2hzn_9inTS8WjjbFOzRJ3wAS24,635 +babel/locale-data/nso.dat,sha256=dsq0mvwg5QK35al0bVwZGUL961EuGS05vlFR5qb4HwM,6470 +babel/locale-data/nso_ZA.dat,sha256=TdOqVTzKNigLD6zOPR7W7QHT-Aw40ylsUE7bmMwgaKY,636 +babel/locale-data/nus.dat,sha256=4G1Wwsq1yWIew4nA-QBo2j8LmbqBTRZ0gCU0LYLUSUk,8215 +babel/locale-data/nus_SS.dat,sha256=Is_mWvnSS7j05Vk9RHcCfyLOdhOeOWRqgXDf8FGNEJQ,617 +babel/locale-data/nv.dat,sha256=7CBom-BDX6Wk2o878hmZ8DBY8QAS8Z99ExdmdVpq-vQ,721 +babel/locale-data/nv_US.dat,sha256=ak7j2GDaOMbAAmXQhfC9PRM8E9h_hJZL3UDqxqjCtoI,653 +babel/locale-data/ny.dat,sha256=XXvyFcRnNcH9KcAH1K7jzS5xLFsXMlXVW3yowhXPIO0,2152 +babel/locale-data/ny_MW.dat,sha256=kyhkcpqgIwSrUcfxve2OS2P9S1AfXItRHmy94K7cQ48,616 +babel/locale-data/nyn.dat,sha256=nVlbaYbL0QCkpVvX2rYYUFRsuI8BOjcReljkcamobSY,15668 +babel/locale-data/nyn_UG.dat,sha256=fEm-RMI6-PvIjbMUoZK68dTaiAmOqH1iDQBn23a6r2w,640 +babel/locale-data/oc.dat,sha256=0GZZe142JNEQ78GP7q1Sq7ZolzkVuvuK-QDCpLu4Z6Y,66841 +babel/locale-data/oc_ES.dat,sha256=H-3UJZkJCacW_BELiE_wXsXSVHp-dfxv59o3Hed8uho,33273 +babel/locale-data/oc_FR.dat,sha256=L80Ivl6dUpQJF4fAY1xaAeCRO9BkN0l6oCr8nRwogsM,653 +babel/locale-data/om.dat,sha256=wbIQO2bCc4PnuCB14zg9Gw9XW3mlv8D_D0C39SMnAnw,67446 +babel/locale-data/om_ET.dat,sha256=v0wHN7iwcuJN-iFgWFPrGdplH6P-0c8B2OEqYOEeb40,635 +babel/locale-data/om_KE.dat,sha256=DZ-2HywpH9NGEronFox-2AprzyoSg0xJAS62LNqZxcQ,1365 +babel/locale-data/or.dat,sha256=bdzcLCC-BRprtZBlm8pvQhvGKe2zTqjQM1JPQZrGapM,226771 +babel/locale-data/or_IN.dat,sha256=ur6a2cCdapMoKd-6KLXR2xAF2YU8xaueeW7UTPL2yLU,658 +babel/locale-data/os.dat,sha256=2ARxbjnZ_WYF6VQTsvXN4epcXlWpkXaVEhUNOJpAv8Q,14821 +babel/locale-data/os_GE.dat,sha256=8NgIdMuGlNow1dXmjW6y4xOis1_3raiXcj_jLBPirpI,635 +babel/locale-data/os_RU.dat,sha256=76MsfCZxiy_RXfhW7Do3Z8q4-jhp1hOXTBIsA6SlA-Q,695 +babel/locale-data/osa.dat,sha256=Ob-cu1978hig_-zOnB-PJW1_496aZ_yT6S7VTFkHoEY,4538 +babel/locale-data/osa_US.dat,sha256=qFfjNw8tVOdprzqK2wBlGH4eNMxDxDQqDDReucJZR-E,654 +babel/locale-data/pa.dat,sha256=pMTu_1agUQqVCaM7VB5fe9wM45MEsfVkDyzkKp6UlUE,202684 +babel/locale-data/pa_Arab.dat,sha256=VBfuWzoA5UdGbWTTG3cPAYL0ApWSup5U9LWBBh4bGUE,3789 +babel/locale-data/pa_Arab_PK.dat,sha256=1Si67Mz8DZfo2D8Ld0b5IlZpvnSMgg4W8_zZLrHrWQ0,635 +babel/locale-data/pa_Guru.dat,sha256=fs7wqdmuGaKQCCwGkT3lRQSKmf1dxG3jyLboZeVtPmE,1276 +babel/locale-data/pa_Guru_IN.dat,sha256=kpz873B1I65nfS3Z5vDLmmGliFVPlPM58sB6jW9nkBY,658 +babel/locale-data/pap.dat,sha256=EzSKg0e-PaX4UNpqi0JTYSjLYwqljGKI-uJLjR1uWhQ,28121 +babel/locale-data/pap_AW.dat,sha256=XZSGzQPijHNf5GjxUjDJQt88iD0ETRdZSTF8gn-1U4M,617 +babel/locale-data/pap_CW.dat,sha256=Mu-gCfh3bv9pzz9RrylMffuphhqgiRaODEZm6TtCpaI,617 +babel/locale-data/pcm.dat,sha256=BPyAoOzA7PpWZ4G9w2NMBzu1JqtXwnZbeCSrLk7j-Qs,161833 +babel/locale-data/pcm_NG.dat,sha256=3gBp38NIuVEbHlKAtQPQXJjT3DKvy7zvUqMAZS6pGzk,617 +babel/locale-data/pis.dat,sha256=aCXK39YyoYqy97wCU2idxYXLL9cC7myCIiJZeKgq-po,1510 +babel/locale-data/pis_SB.dat,sha256=-CDkXv_877c__o56bBeYHAAq2X9CcnKdI65DiE61_M0,617 +babel/locale-data/pl.dat,sha256=zWkc9nD_Ipzoj-nuymRuCLdWD3dG_wQicGQE5NpjSLU,227506 +babel/locale-data/pl_PL.dat,sha256=3s2XpVyaKha_K9MesXKD6sZNGSn1PVRtuz9XYp1xrsg,653 +babel/locale-data/prg.dat,sha256=vsad78h5dkCtaL_Gc-AGmXy3cc6Od1j4WCPGBdX6Tt0,17267 +babel/locale-data/prg_PL.dat,sha256=5jT2Yeek_e_MpGShmy2v-0WWhzQkQ0WsJQlUYfIOcfU,654 +babel/locale-data/ps.dat,sha256=2Yvun7eRlWEkh6Om_QxdW3Hq3AwyYqoM8n0418VjcbU,158607 +babel/locale-data/ps_AF.dat,sha256=sNE6bgx_nV0VWKa8InelUaFYSa3Fpx6byJw0KT4B1o4,678 +babel/locale-data/ps_PK.dat,sha256=ZtzTnH2thc6-J5PbuMOq3lPu57kbqDFxO53s7BSdVSQ,7119 +babel/locale-data/pt.dat,sha256=6Db_e_tQGLJop802pMj8aeNGcuKxa9iKTCZYBNM4crs,183448 +babel/locale-data/pt_AO.dat,sha256=49hMN9AJBDNjcsQw-nl-jY_VKHanO0PGfvd--OxqOxI,1022 +babel/locale-data/pt_BR.dat,sha256=dVKYp9IwRMbjZ_ee-eXFPudSuTRrROvbFzY3dYTYmn0,635 +babel/locale-data/pt_CH.dat,sha256=zAvD0sYytEquwzY8ZRbVCcy7DS2ZzUXofl5qXOixZH4,653 +babel/locale-data/pt_CV.dat,sha256=zOQn8d7--PKrhRUhQ5mPKW1Km6Tyh5JF8hstfxpITzM,1039 +babel/locale-data/pt_GQ.dat,sha256=qI1hvtuSHOev12ips9o3Vy78NJpOMjhkW625M60lqZU,616 +babel/locale-data/pt_GW.dat,sha256=SRxbFDIg9FwEjGjutRI9P1SeLiZBqStwSqPuHBFZwLQ,1002 +babel/locale-data/pt_LU.dat,sha256=z9aq0BTWEnk4lXveNpmi89TKcXN_t6Ud4A_C97uyOuw,672 +babel/locale-data/pt_MO.dat,sha256=shItJIIDrxwlcQhWLhlZG0uhyTlSBDcTag5ivxmb9TA,1635 +babel/locale-data/pt_MZ.dat,sha256=UZ1nkXS9CpiCM1IpOG5XpGK2QqM5mE5lAgRLTGAWl-4,1042 +babel/locale-data/pt_PT.dat,sha256=_H5DG5pOSZR_5gjjycrMBKaOJd_euwJln1pa3MPd1Mg,96277 +babel/locale-data/pt_ST.dat,sha256=Cm2HUxlGtZaz8IXX5Ohms-HYuKgazuBcOZBz5rWOzEU,1022 +babel/locale-data/pt_TL.dat,sha256=CIIDM_Zo0xiu4gXngxYtkYK6ldTNZMRaikSD_sAokMw,1002 +babel/locale-data/qu.dat,sha256=UueUbNLnx5ZwsqJcvzKUTdK5_wKBOmR2haCj0zhd4RQ,93300 +babel/locale-data/qu_BO.dat,sha256=7RUvNrmuLtwqP-qab667GocH_c4gIvpas3dbN1uaGxU,878 +babel/locale-data/qu_EC.dat,sha256=_5V4RYoPb3eCAA7jOCNfB7WZqd9sWP1E0tlF8mGeqFc,837 +babel/locale-data/qu_PE.dat,sha256=Vdd0fcK2dtFgyui3Q9NElHuJ841RI-T5l2UHeBq8MQ8,635 +babel/locale-data/quc.dat,sha256=dIAxk2IuDZhWTj-NKC9PiHBZOnG9ARyANdJZUfoVlJw,775 +babel/locale-data/quc_GT.dat,sha256=8M8KtmnsJiTCaf-Nl5X174Omtyc6a0ri07kysDC3-5Y,636 +babel/locale-data/raj.dat,sha256=qkuyRruXTTQLDgijSA0Equfz19Aqg0p5Mg0heaGKT00,2428 +babel/locale-data/raj_IN.dat,sha256=SS8GPzJu5j6nfXlDoX4NFPN2QScl4M0YNsrFmWklymM,659 +babel/locale-data/rhg.dat,sha256=dHahMWjQ_tUshtlLauqf9b4Y36LNJFGrMBsVmjBzlqQ,4611 +babel/locale-data/rhg_Rohg.dat,sha256=DAOuwC-7uaIuZtrIXvUkYt6YkxqaKpHOma1m0-XTtrI,693 +babel/locale-data/rhg_Rohg_BD.dat,sha256=DcLSBtf_k_f6B102-vbhBOjfzNZjiTDvEUdIOQfVc8M,1212 +babel/locale-data/rhg_Rohg_MM.dat,sha256=HspZzbytcZQ853Jstp-eFEdqSMAD3-shgjrdxY_KQrE,636 +babel/locale-data/rif.dat,sha256=4KtUTy4D-OsLftEaIHU2fw1NUhx0pwJXXie51jI3X10,49902 +babel/locale-data/rif_MA.dat,sha256=Qq7ZdymuPdjHeYDUDGCjMuQBRU7w5qGdgzzZ6wPfN2o,617 +babel/locale-data/rm.dat,sha256=aQ4MmwhjJbBakV6-C-6yjDvN8jvVa8Mk0ovdYgVkR9k,95006 +babel/locale-data/rm_CH.dat,sha256=JHAlYXMD9Vks43cU_gcSEQmqo6pogWPl_R4VMUK2iHI,653 +babel/locale-data/rn.dat,sha256=LwQTzbFINNeIhU5qJsEnh-raxcCDXVMGA6ZL95aU9UE,16241 +babel/locale-data/rn_BI.dat,sha256=r5Cweh7L4EAZK-qTmJuloWc33iXeR4IJUbknrl0XC3g,616 +babel/locale-data/ro.dat,sha256=13UpR66j-nQg4kbgGH4ja6ygDq1k3CCErykEWeeG8yY,200714 +babel/locale-data/ro_MD.dat,sha256=pUZY970zkBSBpVOIA3Ss-6vDJUF3ZCEWYNs4_LXcpAY,2881 +babel/locale-data/ro_RO.dat,sha256=lLYIYWhDUmq1EZ9oV5SETauVa_5uNN5p52dW7XeemRk,635 +babel/locale-data/rof.dat,sha256=Yafgax503CHjHvrc_njDTMVOOD2B36sRvWvDE99geMI,15473 +babel/locale-data/rof_TZ.dat,sha256=I_qwGjqqrcwUR_3UNydHOm_EiiLZYyRV0q-s1qfSSi0,617 +babel/locale-data/root.dat,sha256=vcYJrDi6WVier8432VEhdQ3ZZH8ptAXszNP747aIiq0,51983 +babel/locale-data/ru.dat,sha256=8WD_7_Tz0ieaEjXNlFQy8xFSfCwsVf_CUcoHyFfqkgs,305832 +babel/locale-data/ru_BY.dat,sha256=99UlyhoMWnfyhI5ltXGaJkycWLVfJ3FwdxyRm-yr6kM,676 +babel/locale-data/ru_KG.dat,sha256=X-iAT1QuQGhk1mIetpGyWqDlwiBskLfwqrK6ant1XS8,659 +babel/locale-data/ru_KZ.dat,sha256=P1_Gn3esOwDLL8sd8vVYbFA8J7QEsaDAWfZvDsn-WwA,656 +babel/locale-data/ru_MD.dat,sha256=LU12oBwr7MfPTzfs8Dx7uUZVgHin8ybX_oPHrPIJTkw,654 +babel/locale-data/ru_RU.dat,sha256=IVG440mXjphW6iCsKysZCW1Gem_z5_O0lZI6-R3oG_E,653 +babel/locale-data/ru_UA.dat,sha256=WYPDEOUjHl7mtejs302KAURq_ckDn6CFjH-IwmiFF6g,1210 +babel/locale-data/rw.dat,sha256=1KiBkw5N7hioc-ESs0oqgdyOaVdOlsNp1iIz7g3k83E,8418 +babel/locale-data/rw_RW.dat,sha256=tw-gtzH64PeIAmNGeobgUypw7RAyKITeFAcQPGY0MG8,616 +babel/locale-data/rwk.dat,sha256=hCqo5glP1WqFJrEGyW6r3t43NZ7bVHAOGFGDkUzeM3Y,15369 +babel/locale-data/rwk_TZ.dat,sha256=s9duai8NRoHwn8YpQrlMd3VDf6jtQxxcUmu9CIIMzC0,617 +babel/locale-data/sa.dat,sha256=8r4iZXIQ3vnGxlSmOtDhwM-T_iaYN4SXuTSdGh-2JMg,15978 +babel/locale-data/sa_IN.dat,sha256=ABNamoPpYRbO7bK6BGm0tK92kd7su7_OP7Di0wW0zZo,658 +babel/locale-data/sah.dat,sha256=Qlb3HEv5JslfLAkUjLJz_XdRdyLhloQ9uf9q2b_O6jM,39310 +babel/locale-data/sah_RU.dat,sha256=jHlseiwMbtN-IsETeG2dsaIn6OB2kqtAogazMnn0f60,654 +babel/locale-data/saq.dat,sha256=grj85np3tZzXQmLdtYQcu76ynQv5Jq__KIyaDECFIj0,15787 +babel/locale-data/saq_KE.dat,sha256=kN94cJJt8AT0yBRDqUPGoJtMZdOb2e-83NNsq8hmQ64,636 +babel/locale-data/sat.dat,sha256=K_IKB3mSsOkYuYDtbGW3lbGjVk4ctB56WWkJJvE0-mI,66131 +babel/locale-data/sat_Deva.dat,sha256=0EHZ6UcxsVOfi399xQbOkzaH1OU4jkZ10uqmmWnK8VI,1943 +babel/locale-data/sat_Deva_IN.dat,sha256=Bzh9Vmzia_8KLAryt9VqCl9ter9tKxng4b8oFYbYK6I,659 +babel/locale-data/sat_Olck.dat,sha256=mszOmf6B29mFbE6qN6Moy6SfBjwsjaCsbk75dJR245Q,905 +babel/locale-data/sat_Olck_IN.dat,sha256=Bzh9Vmzia_8KLAryt9VqCl9ter9tKxng4b8oFYbYK6I,659 +babel/locale-data/sbp.dat,sha256=1qHxon8_TpGGnLHUBzy5cR1mCEnZmw1EuYz3QLgnT-c,15609 +babel/locale-data/sbp_TZ.dat,sha256=chWs0tZniO53SbtBquTHGS6E4v36tgYL2b3enxHcdCU,617 +babel/locale-data/sc.dat,sha256=ItaljMMLxOEM4DEG_yDNVk8H4AmQrq1xCnDE8AfRDDs,189970 +babel/locale-data/sc_IT.dat,sha256=0G0vL9YXZQLkTmnzJuuygM5ty2Tw5liDg_aWHLvg32k,653 +babel/locale-data/scn.dat,sha256=UKMqekdt_t_Qe_ebhg3eaRsTs37ECpWLEiWSCFjMTLI,70373 +babel/locale-data/scn_IT.dat,sha256=dTXZIHDRhbFDCw02T0wL1iaJ2kfY1ZUzfbvUs4_WKLs,654 +babel/locale-data/sd.dat,sha256=VDMIluqNpBChcALvjd2lGll6sRK6srAF7p2CogEgXlE,154300 +babel/locale-data/sd_Arab.dat,sha256=jgkaYGUhPXll1T7CT68ridccZaERHeZdwUzGoAZBUps,879 +babel/locale-data/sd_Arab_PK.dat,sha256=c3AXdatnrTNDHW_SOh8fNNye4CVey81FnkSXwj7MmcI,635 +babel/locale-data/sd_Deva.dat,sha256=ZZathBrGJyoqspPsHF769J84zQq1y62vN8RAjGj_Cms,13957 +babel/locale-data/sd_Deva_IN.dat,sha256=0KQFEKHV6hHidc5hxrxQEvhO-YyvFf5P5z6t9hPenH4,658 +babel/locale-data/sdh.dat,sha256=5cD8u88qanpCtA8kPvHH9AdAi2-HbhznORqis1PYx6M,1032 +babel/locale-data/sdh_IQ.dat,sha256=B_vUMOiGWVcD1HaR4Zk8XxVzcmMFSISe2vRscyA1lYY,679 +babel/locale-data/sdh_IR.dat,sha256=hwMmQnne41X_lyjbyNbQq34Lpkl91bAgoavPZRuozwU,679 +babel/locale-data/se.dat,sha256=JnN5s3b1OrcP7qmurb8Rb9NKc-ICOr_RmdFs2EBKoRA,54830 +babel/locale-data/se_FI.dat,sha256=9_VJ0wzYFPto0YF2Ge0qieFV9_DOVqqYnPjlC5wnAr8,44207 +babel/locale-data/se_NO.dat,sha256=nm5u2VQ3ne0SUFZtFqwsRa8Gg97Xn1OSxjoP2I-i_PU,653 +babel/locale-data/se_SE.dat,sha256=6M9OxtmQAY_p9fZ_6L8zqo6i--bo_X00cvhTDEbX_UU,694 +babel/locale-data/seh.dat,sha256=viFuK-Mg4wPnvTcrvL-iEzNqFc7Ri_VP_r9pC673SNE,15433 +babel/locale-data/seh_MZ.dat,sha256=Sv94DueqrM3R5RjhCdwrKZsnNdL1WpjLBg6QhBnoaX0,636 +babel/locale-data/ses.dat,sha256=aRW4cH7nGNS5S3B3ff7o02GqcIqn1mVbBRGg3NdxXUc,15812 +babel/locale-data/ses_ML.dat,sha256=y14qGwP_r6VxzJhklYIWfYkolu0OHB-6nJ4tvF7J-iU,617 +babel/locale-data/sg.dat,sha256=ERl6d_xd8JRnkG_D857gWi2xONyI-cEmzt-Un_hXbu4,16476 +babel/locale-data/sg_CF.dat,sha256=QCNYqo_5l2Sv9JUFEWF6YLDR3MknhgkyMz1mO5j2Gv8,616 +babel/locale-data/shi.dat,sha256=fuYtv_xjgmJ0-aAQiuWcJXZY3mpeq__hz4lnouEUyWE,21830 +babel/locale-data/shi_Latn.dat,sha256=aQrfwwcvkgPaWFSev9-3WgwQXhhXj1_5myuR-QifnPo,15435 +babel/locale-data/shi_Latn_MA.dat,sha256=X8cwkkvm2LjcClQWzHRxxmiA8P0c37D7P8lf6RO4iNQ,617 +babel/locale-data/shi_Tfng.dat,sha256=VjsR4oSiK9oFfdIdpaVDwNSIBvFbQ3Jvm63K24bRqk0,974 +babel/locale-data/shi_Tfng_MA.dat,sha256=X8cwkkvm2LjcClQWzHRxxmiA8P0c37D7P8lf6RO4iNQ,617 +babel/locale-data/shn.dat,sha256=awBFmgdlHbpKsnUqWZaGAQ-YbTPm9UWkJKMn9IdJ7pM,890 +babel/locale-data/shn_MM.dat,sha256=7VauV3cmVfAJz7kA76hsF3FdwM75CVjvHdjx6lCoH4M,636 +babel/locale-data/shn_TH.dat,sha256=QHPmr3V4GzTbb6QA5SDl2EsVzQBjpbaDP6OHfJZ0S0Y,636 +babel/locale-data/si.dat,sha256=iuq-0rbecyzhX0pRX5UYdgKKzrn09pkLucKursW_Z94,216775 +babel/locale-data/si_LK.dat,sha256=cWCgV9WmnEwIIsBmhsIVzSzbrV8hkkXaV1xH1ultlUw,635 +babel/locale-data/sid.dat,sha256=zyKwuzw51B0E8sWg60qJ3kO4rzqTtcNBRnH5D3Zm1mU,2176 +babel/locale-data/sid_ET.dat,sha256=vtwy9BuaZwb5A3XdIl-3mws1cEAVk4s8oKy7i-GnM9o,636 +babel/locale-data/sk.dat,sha256=u_TnuG4-zvLxkxQupdNvPS-TYi28xeiPxLit28hxwZM,218242 +babel/locale-data/sk_SK.dat,sha256=eMjPq18mQOAXvkJN5WV6lKEAPyt7-eleGme2pHHevgA,653 +babel/locale-data/skr.dat,sha256=Mrx5bET3kh5Gzd8-2J9Rm4_b_wPZTDoHuhcxqdYj5hA,1702 +babel/locale-data/skr_PK.dat,sha256=UJpLMmJJIM5jeI-tXWPBNX4Qd3WS2nTwp3bvJg5D3WY,636 +babel/locale-data/sl.dat,sha256=eEJU8du-WwCkCviFmusUqH8H6sMl2PHG_khSumUB3co,210137 +babel/locale-data/sl_SI.dat,sha256=3Hk2t-MUfAUtqu_Gy0_fTjOhwBUtxdnndQ7Kowg0qek,635 +babel/locale-data/sma.dat,sha256=BaBVbhsEPN9V4JGtMt2TpMdf0oIrL8Maxt2FXDIEEss,944 +babel/locale-data/sma_NO.dat,sha256=OeIJ-EJtvWH6z2dZ1Hv8QdmQC7M7r51t-_C6LD6ZyxE,654 +babel/locale-data/sma_SE.dat,sha256=M_zYllAUd2UaWvccZ6ShB9-Gd8JYzIEln-88Q23syaU,654 +babel/locale-data/smj.dat,sha256=eyHR5AZqbxxlTYhyxJfvTjpz_v6zfh3TrDtcDkCgTig,939 +babel/locale-data/smj_NO.dat,sha256=5fz-YVJEoXswvC-RdFcJWmt-EMGcNXA3c9j_pfmxqCA,654 +babel/locale-data/smj_SE.dat,sha256=7l73FEZcAemOwXSrfPB3X7a0cto3J4oM7AO9i9PMIBU,654 +babel/locale-data/smn.dat,sha256=LGkyaifqt24L0NeExCtzhg8rKEKnkfJNEKtt67B-ViE,40399 +babel/locale-data/smn_FI.dat,sha256=VRY3dbCyxEmDUTep9bQXDRldd5wSg4skqQpztnMXplk,654 +babel/locale-data/sms.dat,sha256=ylATYbrfu8SVEVdQRbC7dTSOKCNSX7O1gjS2wJXA1tU,6521 +babel/locale-data/sms_FI.dat,sha256=b-Mk20lC32xysqU5v6jGjDUYDZaMDvoMwWOZNdT5yuA,654 +babel/locale-data/sn.dat,sha256=FNTHDspBYVPY7Nse1haEc19O0Sf3btsqNWQFs2Zzcgc,16732 +babel/locale-data/sn_ZW.dat,sha256=8HnH2raHHl8PI25abODlThEhjIOGXJb_u7cyQd5YzZw,635 +babel/locale-data/so.dat,sha256=sAFkgMg-qaZYhHT_lyoaggZ8UM_KX1xbS4miB3l5KN8,166227 +babel/locale-data/so_DJ.dat,sha256=VWnaj_MuW4dJnlO84t60ySCvfJL7FXI4nIP9PTL5Amo,656 +babel/locale-data/so_ET.dat,sha256=EvRRqU4yQPMYhkEK8a4MR808fwKHmW7EvPvEJdjltnE,655 +babel/locale-data/so_KE.dat,sha256=OSiRggRZ7CTNmqiGOvW1W2Cw2RqjVESTVOdUfgVgWyU,1208 +babel/locale-data/so_SO.dat,sha256=2IPbOqSxQGB5FukQ-B-MEme9FHYn22ClXmAV4x08pNE,616 +babel/locale-data/sq.dat,sha256=8lTejyAm-94ixpS6Dly7NQSHNsamftqLIdO4OxPWqok,160229 +babel/locale-data/sq_AL.dat,sha256=MN2RvkKGG2PXjCRsMHsvcnwnIOOp9tNSpcInh2uCWaI,635 +babel/locale-data/sq_MK.dat,sha256=L_GaZJ9XuQMs0zw-xZlrefPF36SH0sJqMrdDDbwicZM,1208 +babel/locale-data/sq_XK.dat,sha256=qILZGRwAhuIBJ9xi_T5WTrrZAVouFjQSvybrwgXTsog,1187 +babel/locale-data/sr.dat,sha256=AAE3LN_CLRWzljEERIYedZepxLIDWzENkL4wmu8Ib6c,265725 +babel/locale-data/sr_Cyrl.dat,sha256=Tbo90CyBAWciKeDesPhmXf7sdBQfjWsmyyrKBco_NzU,1990 +babel/locale-data/sr_Cyrl_BA.dat,sha256=sKEcBX9G9OwCmIZEpTphQyW_LM4xoDGe90s_470_AJs,41992 +babel/locale-data/sr_Cyrl_ME.dat,sha256=rDZOf-zykN151tfgGiO1SUJiClzQ6t40gF927bMLn1I,2320 +babel/locale-data/sr_Cyrl_RS.dat,sha256=Tn9u1EvJAZZEIdgXloi326qjpq8b1N087nRQ9R1JbWE,635 +babel/locale-data/sr_Cyrl_XK.dat,sha256=qVevTILYnuaIjRyPy4rDTavVg-VXxKcZ5fVdh5c-aHQ,1574 +babel/locale-data/sr_Latn.dat,sha256=1joKXYRv5UiiT21s_Za6qE-VA8XpFi1HzYnVOjKpxb8,217950 +babel/locale-data/sr_Latn_BA.dat,sha256=of4wpeCglVK_Td6HnHUjHRwuFtCVtWj3kHb3nl4B0D8,32834 +babel/locale-data/sr_Latn_ME.dat,sha256=Mr0GNVLFIMZpBakd4dEUzJUlAN4nyX2BMAqydgtMzWo,2089 +babel/locale-data/sr_Latn_RS.dat,sha256=Tn9u1EvJAZZEIdgXloi326qjpq8b1N087nRQ9R1JbWE,635 +babel/locale-data/sr_Latn_XK.dat,sha256=wIz7XXQrRFYpzMpR_cgSWikPWI0ehSx5a0gRBgt3rFo,1486 +babel/locale-data/ss.dat,sha256=UAB0p3UMDn8KfBY32a7HVWXWpHlPa26Nr_Woaah2c3U,2178 +babel/locale-data/ss_SZ.dat,sha256=X-Va1uVrsPa1ZX5hwhQ-ME6Q_bovUb24XXFS8UQ87lQ,1208 +babel/locale-data/ss_ZA.dat,sha256=xwT3vZ4tmKEvJpVwFMV9U2j2FLF1tC9PEDN6MC6Qbf4,635 +babel/locale-data/ssy.dat,sha256=QyBWLhH_sreFEBHMQ7aiqBsrGvDeQHyYAKioqksa2iU,2999 +babel/locale-data/ssy_ER.dat,sha256=9CRIaVxgOMeMTc0tcV0Yz9yZzq_uD3g3ylZzqVpYzsM,617 +babel/locale-data/st.dat,sha256=b-jiK03L_9F4QqWW8qmRJ3UZntZDI9eMJglRho3MN7c,8016 +babel/locale-data/st_LS.dat,sha256=pSzdNm6-O5V6-14VcgF2iGNYQPFYIXQlY3SqUW3GcjA,1227 +babel/locale-data/st_ZA.dat,sha256=5tdUX7dxXEL2xiS_0IvHvqhKF-gWXFsi4supkP0RDAc,635 +babel/locale-data/su.dat,sha256=G66EuouIQrCxgKGW9JzPfMZ94NAN87P6REyULyyrFGw,11817 +babel/locale-data/su_Latn.dat,sha256=ievzq_m7eD5pZWLnVlH7oMIdeadf8GIMqEuVPJTBjyM,745 +babel/locale-data/su_Latn_ID.dat,sha256=axNaukDpD_-G-_4R7P1u5w4dh-ToRPN_Yo-QpC1cFRU,635 +babel/locale-data/sv.dat,sha256=RTvQ2li9cKHIEbVl7fI8ENVrmcCa1gERk3S9O2bHKmA,198967 +babel/locale-data/sv_AX.dat,sha256=QFGzIe9UCr5gCpgHunvdPiZ-126vP7GYORL9jjip9-E,653 +babel/locale-data/sv_FI.dat,sha256=Pn3EDh-VD0QJFWupEZ6w4TqAa-WC5GZ8oTA4oqLazvc,2526 +babel/locale-data/sv_SE.dat,sha256=c9PG5p2y_4rnxepGD4uOzemBU3EOQhQNO4BtUS1m3rE,653 +babel/locale-data/sw.dat,sha256=w0uX0MB-_bZQWOnbdyDVg1uRVe1j-K-egjRExoZpE8U,145419 +babel/locale-data/sw_CD.dat,sha256=W7Fw8Wqwo3_x4hWlssmWbVTj4hhSQmRv8c-VSnQdlg8,2577 +babel/locale-data/sw_KE.dat,sha256=YMNWSTJ8qxo0d1KvQwAN5ECRaZ9LGc0BDfX4enggVlc,47036 +babel/locale-data/sw_TZ.dat,sha256=I1bDbpEDiR1jkCgJ_W9fWCPuPxMhjQmwG3sIueKb-Rk,616 +babel/locale-data/sw_UG.dat,sha256=kuNdg2N_dLwUKFm7wSheL1wLOdyrukPxLfDw0PN7tT8,660 +babel/locale-data/syr.dat,sha256=ehEqE2rAN5_5X-4zoWys-oUwiajRRhwkGx-yd-kL4Q4,111439 +babel/locale-data/syr_IQ.dat,sha256=Mrw63eJwh0vRe3Lpj0vRf6Q4cUZXJcx9tlWkuAmMrss,679 +babel/locale-data/syr_SY.dat,sha256=l0Yl2UMjm_7kuzvoG2-KqBp5XR88gdjf1ZlpRRNFMuc,679 +babel/locale-data/szl.dat,sha256=8PpGSXa6ES6yxXD2YOBjG16b6BW9jDZo86RKB0MCNn8,88759 +babel/locale-data/szl_PL.dat,sha256=1D5dKaG2l2Haxde8uraWI2J5lzw1dIWGeJtBesua-IM,654 +babel/locale-data/ta.dat,sha256=RRIrxhZ9pdpGShIrQx7ZQBOPKWn15OGkeL8yQOy-tz8,277264 +babel/locale-data/ta_IN.dat,sha256=BOFaDBw7fc7C6xSSFYNfkhCgCUd7ydHprJ6u0DaneSE,658 +babel/locale-data/ta_LK.dat,sha256=Od6ln5oUnWfaAHwt_SDafWv844c7A7_7fTT2OPVTuyw,1208 +babel/locale-data/ta_MY.dat,sha256=PEUmjhs_RUr1R5kH9uHExdnG59Hy8LmbU18vXZYYPtA,1325 +babel/locale-data/ta_SG.dat,sha256=tbuefTSiXPMwpeV085Qp53l5uDHLsMMXmt8TPEG2fWY,1344 +babel/locale-data/te.dat,sha256=oRTQlv5dIZYav2aQWijvc2wm0VwoR1TwWUPL_qhzkuU,261668 +babel/locale-data/te_IN.dat,sha256=LICnXU-D0uhvxVpQ986Ul1JeAQktXaX53sXZSUY3L2g,658 +babel/locale-data/teo.dat,sha256=jJ-266Aw_zTeJjwGj_VASqzm6Y3CcQYkVPXKuV_kzT8,16012 +babel/locale-data/teo_KE.dat,sha256=S_lAg36VkA1w384kzPfXRLmJmGWfmz3ll7MzOQAtcr8,657 +babel/locale-data/teo_UG.dat,sha256=0WlDglms5bCe1AMwCqroaXpOXu9jQroxllMwXWLb8q0,640 +babel/locale-data/tg.dat,sha256=ErMv0Y4SGno9gTSJbhMVKZXiSe_KNSUFaoBeIDaP8tk,117163 +babel/locale-data/tg_TJ.dat,sha256=TCrsEipGp0reAS2mAvwT58tyzBnYJfL5oNHWpEe1ttc,635 +babel/locale-data/th.dat,sha256=lyG_l3_QzfpnjCv8LVaRTvpvfImkVrqcDoUKrnXlIR4,215576 +babel/locale-data/th_TH.dat,sha256=_QhA0WcHmW6Cr_-gU9e6cdpzTMDT19WyHas58fWDUnc,635 +babel/locale-data/ti.dat,sha256=K4kS476YQonyQifjLpS4UkE7hNa2k5aIDBJRIdh2eDg,214273 +babel/locale-data/ti_ER.dat,sha256=m9w0P5TPQ8IqpWKldwdfnw5_1Upv09AjLdO5WUbcRic,962 +babel/locale-data/ti_ET.dat,sha256=q0b9svPg8aj9xmbXr7F6fPHqeTJmclBl5t_hqQk7IuU,635 +babel/locale-data/tig.dat,sha256=cgqxy3wiehP8yceqa5-mrJVT23IP_bjlkeUOEvxspUM,13481 +babel/locale-data/tig_ER.dat,sha256=OoQ0U6yyzmjKwDtzLUpqR09h_ZaGvzXlp7t-HqNxmm4,617 +babel/locale-data/tk.dat,sha256=z5i4kshTasDEolkNpqXnzlZh75EgbAhL0ylT0_0oioo,164932 +babel/locale-data/tk_TM.dat,sha256=lADxEQoz-_ImDBmpCFUqDPuZ0ozVUhZKOQF2ZwyXVlw,635 +babel/locale-data/tn.dat,sha256=LgoZgcUCS4t_RsyQQKiVsJ7Kbs7HoInZAMndRk6DUCM,8596 +babel/locale-data/tn_BW.dat,sha256=jWMFP1tw-o7rslHAlAIgjEXflko8UCDNaSuQ8ccvNHI,654 +babel/locale-data/tn_ZA.dat,sha256=S3dMOsYSpBwkUv0tW9q0kKawQQl6yx77LexAL5wQqDU,635 +babel/locale-data/to.dat,sha256=CBr6gRgu0qo_-kIoOluPwu_ZdH4-SPPJHto_ERcDzHA,145255 +babel/locale-data/to_TO.dat,sha256=jcfJ2Pjof8jGE4q1y5LHSP9rb4IanN58kR7OgG8d5Q0,616 +babel/locale-data/tok.dat,sha256=kuSPgVhnv8Rr55G-epQDVyh8FfeKxJbd2J0hierJdUQ,8870 +babel/locale-data/tok_001.dat,sha256=PPUMevT2TBOGFSq-U-AccYkLHSbkWnBRDvxtpokTCD4,693 +babel/locale-data/tpi.dat,sha256=mqTAJweAOy1SAH2l4mJAKktX-ZeAdvTC8JnBPxXjB0A,4030 +babel/locale-data/tpi_PG.dat,sha256=uZD_c8yCtiYwt2EO2VOYPzhSTqWMQBOJMCuMCXe3Css,617 +babel/locale-data/tr.dat,sha256=ULbjOvmQqcT1Z2_8bBOe7LzwgBUg3LWM2MiLkmb8M6w,150824 +babel/locale-data/tr_CY.dat,sha256=3EmCqxcPvFuZcHCzpTmHb6pjtPwqQvwCfn27B8rtXNw,1227 +babel/locale-data/tr_TR.dat,sha256=7lS5CMZw76SVlMJxsmIJW5IfL7k9t8MrfKcnElqISSI,635 +babel/locale-data/trv.dat,sha256=iEbFtCCE_U5rmjAv0Pe5J_UigixMKq1cPIThdZui1Lg,9404 +babel/locale-data/trv_TW.dat,sha256=CTQtGdDW4Z0n2Ckb6B63HgEwNE44YTFnJCetwd8q9GU,636 +babel/locale-data/trw.dat,sha256=1e1LGCtVq79RclEMJbgRE4ATdqflV_KDHxBwInSfAKc,110107 +babel/locale-data/trw_PK.dat,sha256=pBHdOulrwRLiQuC96g8ilUzpKBeG6kglBqUXB8MlURA,636 +babel/locale-data/ts.dat,sha256=p7EFPucK9HkTVXeNKTU00s-Oas5U_HCPGX9hqT15p9I,5522 +babel/locale-data/ts_ZA.dat,sha256=AByEO55W3DVVOBIBxIp1F2QwY01-5v6v8sDFOGSxCF0,635 +babel/locale-data/tt.dat,sha256=X-J0BKEHy01aoARwwb0SWDuNm1lZFrT0akKrzBZjHPM,111014 +babel/locale-data/tt_RU.dat,sha256=cmv2MzolKOrsRoqQcFZGcXQj_33dLWGh9oUjnebCnz0,653 +babel/locale-data/twq.dat,sha256=nOSN5UZAxcCgdTxQFh14Cyk5K38olCyAuT6MrVW5PeE,15509 +babel/locale-data/twq_NE.dat,sha256=y8xQDxMtyStWC83tm_JiekFxAY0Fj-CWWdmic9GBwd4,617 +babel/locale-data/tyv.dat,sha256=y90_CH7Yksd1Kuc6KhykhpkdGVLUgFDV-B4oSxGqn5E,693 +babel/locale-data/tyv_RU.dat,sha256=vP_6cjMLB8I8YL7Nqrq4Gu_AWGFuANbZSdBc7ORn_IA,654 +babel/locale-data/tzm.dat,sha256=wYFO2vX38bht1nAdcVA7dxKVMh-3yC5sItggFaXkKBo,15436 +babel/locale-data/tzm_MA.dat,sha256=8eku_7voAqAjN651VYKCuIWvdYGOScCyqbt1rtSVO_g,617 +babel/locale-data/ug.dat,sha256=jRJJxRRKuYljj6kYicIhxWk8ySzpMT9AOePaBk3EO94,117801 +babel/locale-data/ug_CN.dat,sha256=2UselU1_d6K7NgQj0EDnDfb4Llk51eQZ7z78oYE-Ywg,635 +babel/locale-data/uk.dat,sha256=b8iEhvLdQxq7rtlqpx1c9vp6-n3jNGPGSuBWNhZAnFA,339125 +babel/locale-data/uk_UA.dat,sha256=usJw_J4Uu9OD9nrUxNQAx6vMJBWDjbGuc9l7ssJAyuw,635 +babel/locale-data/ur.dat,sha256=RTL85URWDMA3albZQODUCbVTA9oxeYXo5kWtuL1XW6g,167002 +babel/locale-data/ur_IN.dat,sha256=9GV9YIa7Mftq14Po2C6fjMG5sWkU8J5LS5bq1iIcsHg,10550 +babel/locale-data/ur_PK.dat,sha256=EEAGl6fLd1-a7X0n2PwRxlqwYZ0a0ELm2DuLII9spX0,635 +babel/locale-data/uz.dat,sha256=iwj3ukxb4FK0rZYtC66ftQmTtr1SgTzy12Wu3KkQYQo,138052 +babel/locale-data/uz_Arab.dat,sha256=wt8BwimSNmoFzrnduGDXMHhqj3B8-XqqN6ghg2Xm74A,3814 +babel/locale-data/uz_Arab_AF.dat,sha256=68eUCR8KeB0QDQRCvNMiOIkZVRozGKFTDednrQL1X_Y,678 +babel/locale-data/uz_Cyrl.dat,sha256=RpmyW2YhTwN_o6sKyfJxcktFI6VMqkO6YDzDE8XoOTE,76581 +babel/locale-data/uz_Cyrl_UZ.dat,sha256=MWJtHPdj_pF2h6abRP4wUwDWAUF4-zr7jBn2RQcZ0z8,635 +babel/locale-data/uz_Latn.dat,sha256=L5H7Xfz2Ho-vMqviRPC4ai0MnqoF9CIebiq7muISvSA,1292 +babel/locale-data/uz_Latn_UZ.dat,sha256=MWJtHPdj_pF2h6abRP4wUwDWAUF4-zr7jBn2RQcZ0z8,635 +babel/locale-data/vai.dat,sha256=T_MWlDAfbp9qgeArFd8yPDPbyThU15kFu19QTQqAlCE,17416 +babel/locale-data/vai_Latn.dat,sha256=MecJYJumVm7wCyCo-YeAY4eAon5WZ4ZqR0shQ3Pdpu8,14255 +babel/locale-data/vai_Latn_LR.dat,sha256=LdVmBAL7nzDp-Ab_uHidBAi2crIf9nXhf8tBNfa7xdI,617 +babel/locale-data/vai_Vaii.dat,sha256=l_IXC76zzJ2lPLfWfjWzSw6xVtFwr-x8LofcHQsSG_0,693 +babel/locale-data/vai_Vaii_LR.dat,sha256=LdVmBAL7nzDp-Ab_uHidBAi2crIf9nXhf8tBNfa7xdI,617 +babel/locale-data/ve.dat,sha256=5bInQioXkePSDbBerQqf4sYPTiB9-zpKuDokCXX5eNA,2372 +babel/locale-data/ve_ZA.dat,sha256=uJndHi9RMKtDWd58f_Q3g5WgXjL3W7KLQj91rVueabQ,635 +babel/locale-data/vec.dat,sha256=zokLfsgTK7cSP4KaSV8EMpPlHiOkI6tG3rWM7o7vcAM,160413 +babel/locale-data/vec_IT.dat,sha256=mC9jRfk9ndmkpRIpWZNCwdMwvxvB6QX6_9HanawdaUw,654 +babel/locale-data/vi.dat,sha256=F95a-fvLPIeJqOlwx4gr9TnWZ0VpSPyzNwJ0RPc1qE8,134138 +babel/locale-data/vi_VN.dat,sha256=cGwN0vw-qIaeA2g89U97N1xHSMmmaIVNPCLL2P_QJMk,635 +babel/locale-data/vmw.dat,sha256=mhERiGM-732X9KO0CThGEyjFtSbquT1_rmzYPnru2SM,1796 +babel/locale-data/vmw_MZ.dat,sha256=sz0j5RufN9oAzTalcjWNhKtCoEXzeRr5Cv6qCouK45E,636 +babel/locale-data/vo.dat,sha256=werwY25uwomUcf9q1dyQ8Rwgg2aCoLp78XUzBeSw0LY,4609 +babel/locale-data/vo_001.dat,sha256=iF2xY9XIdxC0mOYEp9hjYZp2EG-si_YaNYXri_60-4w,850 +babel/locale-data/vun.dat,sha256=VGNPlnoRipnocGYPgeb39d9MmE6fe5nTMWtCk4m1AEY,15373 +babel/locale-data/vun_TZ.dat,sha256=YKrOeOS1VnU3BWQ7HWtxjS-gMKJcKAPQLf4HTgkzf70,617 +babel/locale-data/wa.dat,sha256=1r6krApI8oecIKQt_9RzbpwSN0LcWSjlG34Y9Rfh07Y,880 +babel/locale-data/wa_BE.dat,sha256=j4YADQloo07Oek1j3aOrLYkzcFkrViS1UdRrA0Z56-o,653 +babel/locale-data/wae.dat,sha256=qBKStFqpkAruVDgeZJvX6U0X-hq_NkETTqgR2e7A4UQ,29641 +babel/locale-data/wae_CH.dat,sha256=Z0Wzu5Q9IVTnLGwWu54ozVx-fgnFz0IE3yrky8Y1IkQ,654 +babel/locale-data/wal.dat,sha256=AD9ZfdNNjEhX2n9T0ykd_XwEbXK-wxmsLqq39O4BjWU,8406 +babel/locale-data/wal_ET.dat,sha256=tNjeuU4GPTWtDmsroGmi4VUql0dA57v1tEtDF4rslVg,636 +babel/locale-data/wbp.dat,sha256=kzXK04f538n-PrJl6nRKIaYHMid6rJ4ACmzPcCz7NgU,746 +babel/locale-data/wbp_AU.dat,sha256=BT2E1-WiomuRrteCzN1_AfLyDUdEOm0ehT5miAiepBQ,636 +babel/locale-data/wo.dat,sha256=iB86SoTMrnphiHy_om1Yxs-J8L5YFWYlbmiQjK6jc2k,61849 +babel/locale-data/wo_SN.dat,sha256=fvlwe-ImWs04fsxSjTsXf8IPxEIiEi45BX8xhoBC_yg,616 +babel/locale-data/xh.dat,sha256=xRy095MO_brgAUoDB3UKwhgDvOkBlhJ3cWj9dM2fvHI,64334 +babel/locale-data/xh_ZA.dat,sha256=9p6wHW1sxOghJMAgsP6BC3RdUhuvLpHoCkqvjsCmPps,635 +babel/locale-data/xnr.dat,sha256=Ot10zi4F4H5Yqx6kIJGPay65Rm-Gam2eJ7kAOgoJoJ0,142585 +babel/locale-data/xnr_IN.dat,sha256=UbNwudCNihEFVZu3ymanDhJ52YoupG0SDGwmDByPhzQ,659 +babel/locale-data/xog.dat,sha256=V4U36ibPCWDoUMf31-o9mDCwylsYU0nZEx5pt-MWecM,15866 +babel/locale-data/xog_UG.dat,sha256=bMX0Tk5zS4P2LqlOLLpVP4nZxBFhS_qc-ndXjilDCNI,640 +babel/locale-data/yav.dat,sha256=UupgFFmPVcV-IgY3Fd8Y9GoOcOWgY1P22ZHuA-4QSMU,14543 +babel/locale-data/yav_CM.dat,sha256=CsaKwep5CARetyBX-JXHFvUVsFe1KTN4Pyz1Wh7NTHw,636 +babel/locale-data/yi.dat,sha256=OWr2zb3k_toEmkSXcPweqE13Tr5RhvlMm4z358uCQiY,24264 +babel/locale-data/yi_UA.dat,sha256=DesjWHTuxEBrjOeq8c-B82j4Hnwu0ElR2IH3fZPmq-A,635 +babel/locale-data/yo.dat,sha256=iWLg-08Fk9QJLZPuokgbCeXkyfyQNaov51TT3i1pKgY,110427 +babel/locale-data/yo_BJ.dat,sha256=CP4B5N8cEouMKth-iCTTBEN74dEpdyZQTJbrbYayZFs,50036 +babel/locale-data/yo_NG.dat,sha256=BEIndIiMT1Mo18S5DAWpjefthptCVQkIPeVs0umSop0,616 +babel/locale-data/yrl.dat,sha256=2nwDuw5Q_dL4H2Oxl5ClzZU2Uw10j-koP5RgfnJ67y0,186854 +babel/locale-data/yrl_BR.dat,sha256=aUgLCNwgEORk2wtuez8AqkYahB8_mX41IWSqogt39YE,636 +babel/locale-data/yrl_CO.dat,sha256=pT0rllDKHZAUIK_itrkWP7irmih4im131Ume_wKzyks,9211 +babel/locale-data/yrl_VE.dat,sha256=_Tqu_pGUkzrd4M9SKXpn4g93z7Q5xlUMAtGvROS5x6M,9211 +babel/locale-data/yue.dat,sha256=lvTaoHXSVZHqcb5UCR8IeDiVRCE9IRsrDkhv0bGwzCg,143872 +babel/locale-data/yue_Hans.dat,sha256=ybTM8LU-H-QunhqnLmCQ9bE6zdsYMJPHdlh_hBv3AWk,145194 +babel/locale-data/yue_Hans_CN.dat,sha256=MOZnu5r0xokf672rwAXfgzG3VEeTfNlDbu36fR1bB6I,636 +babel/locale-data/yue_Hant.dat,sha256=76wefuhsGIjk0jT05KSTEU_pbb7AfNE2u56jdRoJaDM,1306 +babel/locale-data/yue_Hant_CN.dat,sha256=MOZnu5r0xokf672rwAXfgzG3VEeTfNlDbu36fR1bB6I,636 +babel/locale-data/yue_Hant_HK.dat,sha256=eMZ7mOTEyKzQz0MDIjvP_Z57CS9nFIP4-Uf0Jpx1CY4,636 +babel/locale-data/za.dat,sha256=5EbAmE-qqvt_e4geaFxBGJi81CVoZFK4dCcJ346P4yU,12705 +babel/locale-data/za_CN.dat,sha256=WzhnaughSM1C1twuKYBVrelLEer-guzT43a9yBNXCsg,635 +babel/locale-data/zgh.dat,sha256=Q2D9KQFgDfWk4fXOdEbPcLq3yX_zxjhlK_PVI61Ok4U,22018 +babel/locale-data/zgh_MA.dat,sha256=sV8aPBmHY23lKIzsRna-tPz3UiXoKCMQ1Ur2tCyx2YA,617 +babel/locale-data/zh.dat,sha256=SpSkmdVN5PX7CCdso1B-SyQZKMDTyZfZeT2l_kj2FoI,152100 +babel/locale-data/zh_Hans.dat,sha256=-wR64b65stjA2KWj4GWcBWiF3yI0A24p4K7wApj8qtc,1305 +babel/locale-data/zh_Hans_CN.dat,sha256=zz2qPpWEP9ba12T9cGsPXGI7Bg5LcmZ3NkAMiVTf61A,635 +babel/locale-data/zh_Hans_HK.dat,sha256=yPyZyU-xXSNyU1FYJIYpWwnh0d3uXOra9V9aj2j_YQU,3621 +babel/locale-data/zh_Hans_MO.dat,sha256=08kUXwHOXA0pRA8qIX7e4p0Nzl1XHSBjwhMh7f-nZBE,3752 +babel/locale-data/zh_Hans_MY.dat,sha256=iEFxl2XEm1zBzd1K0wXpiZFvU7l0UrNURh2p9DZdRLU,1300 +babel/locale-data/zh_Hans_SG.dat,sha256=OHZNiUfnteSXuzel-lZAMVgv58iiVTNhjq81T9Qu94s,3948 +babel/locale-data/zh_Hant.dat,sha256=31aRGt18bl6MdiI9iAozwltdkSQQ370TvzI8gid8CBI,154802 +babel/locale-data/zh_Hant_HK.dat,sha256=NXPCE_4pYgLatoeKLdQ0d8PQ-Mk_GeMF1s1JYX9m27M,49152 +babel/locale-data/zh_Hant_MO.dat,sha256=qCWPPEr87U1aemaIuf6nCYQ-Q3w_1HcXrYoncYVBd3g,657 +babel/locale-data/zh_Hant_MY.dat,sha256=3n4syQpQ9sXVe82sOEH7yQIdrU3dAtMhp7fEhzGN8_E,1203 +babel/locale-data/zh_Hant_TW.dat,sha256=5N4K8I3XG89knCaSoy97HGzTVOTiUcd_VmfvNJlA4Bg,635 +babel/locale-data/zh_Latn.dat,sha256=-wR64b65stjA2KWj4GWcBWiF3yI0A24p4K7wApj8qtc,1305 +babel/locale-data/zh_Latn_CN.dat,sha256=zz2qPpWEP9ba12T9cGsPXGI7Bg5LcmZ3NkAMiVTf61A,635 +babel/locale-data/zu.dat,sha256=e48g3oI1FjkROV3yHZ2GAwn_8wr2y3dMkj98NHW4X8Q,138626 +babel/locale-data/zu_ZA.dat,sha256=jHloBfkNbQETXgE-3xVxvESitWb994_-SoY0G4_JL5E,635 +babel/localedata.py,sha256=gzh3fMZIkA0k70eqXSRA46nbGWl-4BfQnB59czUmp7o,9116 +babel/localtime/__init__.py,sha256=_6ed9ZDImaEkVQdCbNitxCvRS7Cljpa5wsah0fl_628,1043 +babel/localtime/_fallback.py,sha256=kBPMTXycRen_6lRxSj-VCGcXmzD1gklJpJ7ouxGLuMU,1207 +babel/localtime/_helpers.py,sha256=ZmLc8m46W-3GtsstLBdrw6BU3ZhSob8vKHkxgcZG9uw,1704 +babel/localtime/_unix.py,sha256=63rA_pJWM5FEKpJ3XAChL6Eosu5k-tla7C6n4mBkpyQ,3902 +babel/localtime/_win32.py,sha256=nSNnxSMOVAlvqvX50h9HSCzBfP7mj8njJ80JZ3pOIWk,3211 +babel/messages/__init__.py,sha256=1maeVkLsWdXYovEAxGOcGlNHM80PfphZmmivZJo5yTw,349 +babel/messages/_compat.py,sha256=DNyCNMwH5vXgl_pq7cUYz3VcRC2gIPqeQLO5nN5neWE,1163 +babel/messages/catalog.py,sha256=2231tYkaywOeeb96nxm13aT9rLU9zPCqyA-hedf_QTc,37802 +babel/messages/checkers.py,sha256=VutlPZQQ_a4CqsGBNrywgC5QyW5yOhGpwZ2T3lNemqY,6219 +babel/messages/extract.py,sha256=NRMXMPjsdzirUdmgT3SFWkeZnRQUpj3VGcUPX7AxsPs,34274 +babel/messages/frontend.py,sha256=WsxpORPikJ8uhu7TM290y6d7Vxw35edYeGBlECLNkss,45408 +babel/messages/jslexer.py,sha256=6nGIZfX0dQlY0wlJ1zy5yWwoOaELY_ZWV26a5uKdsIQ,7207 +babel/messages/mofile.py,sha256=Qf0X-vIRYTQQpSJqWFY3nXPgCzxyUWAzrtle2fIpIPU,7265 +babel/messages/plurals.py,sha256=jsEnofU5b4PZKIloX0MjchYSxD3r1RRZ_GfEVRuETWQ,7495 +babel/messages/pofile.py,sha256=lMllxTXYRBa3m8wVsxXsJsayjCOePKYYZtFSZU5DVF4,25955 +babel/messages/setuptools_frontend.py,sha256=m1l9NHuawj1pSncZeC82cUJfdwxib_C7JSUb_2EhbUM,3485 +babel/numbers.py,sha256=GhjFEdksEkfmvPN7VjGjlptbtmVaboBZdRaB5zPMZvY,62758 +babel/plural.py,sha256=9NWPLtYleF8gsi23S9OIRth4WhND56E5fdFatwVb-uI,23112 +babel/py.typed,sha256=DtCsIDq6KOv2NOEdQjTbeMWJKRh6ZEL2E-6Mf1RLeMA,59 +babel/support.py,sha256=PLxaklxjez4pysEQ1n3rGbGMRKOPkOeszy73RZ1qctU,27516 +babel/units.py,sha256=K8CDi8z6u9envHjP1s2_TqDvVtbeg6zBN6QuksrtjIU,13753 +babel/util.py,sha256=gC2c7exKpi9yDALyoL4yGGW3S-BkADAZw9dIWXw95TM,9071 diff --git a/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/WHEEL new file mode 100644 index 00000000..505164bc --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.8.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/entry_points.txt b/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/entry_points.txt new file mode 100644 index 00000000..95235a55 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/entry_points.txt @@ -0,0 +1,20 @@ +[babel.checkers] +num_plurals = babel.messages.checkers:num_plurals +python_format = babel.messages.checkers:python_format + +[babel.extractors] +ignore = babel.messages.extract:extract_nothing +javascript = babel.messages.extract:extract_javascript +python = babel.messages.extract:extract_python + +[console_scripts] +pybabel = babel.messages.frontend:main + +[distutils.commands] +compile_catalog = babel.messages.setuptools_frontend:compile_catalog +extract_messages = babel.messages.setuptools_frontend:extract_messages +init_catalog = babel.messages.setuptools_frontend:init_catalog +update_catalog = babel.messages.setuptools_frontend:update_catalog + +[distutils.setup_keywords] +message_extractors = babel.messages.setuptools_frontend:check_message_extractors diff --git a/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/top_level.txt new file mode 100644 index 00000000..98f65931 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel-2.17.0.dist-info/top_level.txt @@ -0,0 +1 @@ +babel diff --git a/.venv/lib/python3.9/site-packages/babel/__init__.py b/.venv/lib/python3.9/site-packages/babel/__init__.py new file mode 100644 index 00000000..7b277455 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/__init__.py @@ -0,0 +1,38 @@ +""" + babel + ~~~~~ + + Integrated collection of utilities that assist in internationalizing and + localizing applications. + + This package is basically composed of two major parts: + + * tools to build and work with ``gettext`` message catalogs + * a Python interface to the CLDR (Common Locale Data Repository), providing + access to various locale display names, localized number and date + formatting, etc. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +from babel.core import ( + Locale, + UnknownLocaleError, + default_locale, + get_locale_identifier, + negotiate_locale, + parse_locale, +) + +__version__ = '2.17.0' + +__all__ = [ + 'Locale', + 'UnknownLocaleError', + '__version__', + 'default_locale', + 'get_locale_identifier', + 'negotiate_locale', + 'parse_locale', +] diff --git a/.venv/lib/python3.9/site-packages/babel/core.py b/.venv/lib/python3.9/site-packages/babel/core.py new file mode 100644 index 00000000..5762bbe3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/core.py @@ -0,0 +1,1337 @@ +""" + babel.core + ~~~~~~~~~~ + + Core locale representation and locale data access. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +from __future__ import annotations + +import os +import pickle +from collections.abc import Iterable, Mapping +from typing import TYPE_CHECKING, Any, Literal + +from babel import localedata +from babel.plural import PluralRule + +__all__ = [ + 'Locale', + 'UnknownLocaleError', + 'default_locale', + 'get_global', + 'get_locale_identifier', + 'negotiate_locale', + 'parse_locale', +] + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + + _GLOBAL_KEY: TypeAlias = Literal[ + "all_currencies", + "currency_fractions", + "language_aliases", + "likely_subtags", + "meta_zones", + "parent_exceptions", + "script_aliases", + "territory_aliases", + "territory_currencies", + "territory_languages", + "territory_zones", + "variant_aliases", + "windows_zone_mapping", + "zone_aliases", + "zone_territories", + ] + + _global_data: Mapping[_GLOBAL_KEY, Mapping[str, Any]] | None + +_global_data = None +_default_plural_rule = PluralRule({}) + + +def _raise_no_data_error(): + raise RuntimeError('The babel data files are not available. ' + 'This usually happens because you are using ' + 'a source checkout from Babel and you did ' + 'not build the data files. Just make sure ' + 'to run "python setup.py import_cldr" before ' + 'installing the library.') + + +def get_global(key: _GLOBAL_KEY) -> Mapping[str, Any]: + """Return the dictionary for the given key in the global data. + + The global data is stored in the ``babel/global.dat`` file and contains + information independent of individual locales. + + >>> get_global('zone_aliases')['UTC'] + u'Etc/UTC' + >>> get_global('zone_territories')['Europe/Berlin'] + u'DE' + + The keys available are: + + - ``all_currencies`` + - ``currency_fractions`` + - ``language_aliases`` + - ``likely_subtags`` + - ``parent_exceptions`` + - ``script_aliases`` + - ``territory_aliases`` + - ``territory_currencies`` + - ``territory_languages`` + - ``territory_zones`` + - ``variant_aliases`` + - ``windows_zone_mapping`` + - ``zone_aliases`` + - ``zone_territories`` + + .. note:: The internal structure of the data may change between versions. + + .. versionadded:: 0.9 + + :param key: the data key + """ + global _global_data + if _global_data is None: + dirname = os.path.join(os.path.dirname(__file__)) + filename = os.path.join(dirname, 'global.dat') + if not os.path.isfile(filename): + _raise_no_data_error() + with open(filename, 'rb') as fileobj: + _global_data = pickle.load(fileobj) + assert _global_data is not None + return _global_data.get(key, {}) + + +LOCALE_ALIASES = { + 'ar': 'ar_SY', 'bg': 'bg_BG', 'bs': 'bs_BA', 'ca': 'ca_ES', 'cs': 'cs_CZ', + 'da': 'da_DK', 'de': 'de_DE', 'el': 'el_GR', 'en': 'en_US', 'es': 'es_ES', + 'et': 'et_EE', 'fa': 'fa_IR', 'fi': 'fi_FI', 'fr': 'fr_FR', 'gl': 'gl_ES', + 'he': 'he_IL', 'hu': 'hu_HU', 'id': 'id_ID', 'is': 'is_IS', 'it': 'it_IT', + 'ja': 'ja_JP', 'km': 'km_KH', 'ko': 'ko_KR', 'lt': 'lt_LT', 'lv': 'lv_LV', + 'mk': 'mk_MK', 'nl': 'nl_NL', 'nn': 'nn_NO', 'no': 'nb_NO', 'pl': 'pl_PL', + 'pt': 'pt_PT', 'ro': 'ro_RO', 'ru': 'ru_RU', 'sk': 'sk_SK', 'sl': 'sl_SI', + 'sv': 'sv_SE', 'th': 'th_TH', 'tr': 'tr_TR', 'uk': 'uk_UA', +} + + +class UnknownLocaleError(Exception): + """Exception thrown when a locale is requested for which no locale data + is available. + """ + + def __init__(self, identifier: str) -> None: + """Create the exception. + + :param identifier: the identifier string of the unsupported locale + """ + Exception.__init__(self, f"unknown locale {identifier!r}") + + #: The identifier of the locale that could not be found. + self.identifier = identifier + + +class Locale: + """Representation of a specific locale. + + >>> locale = Locale('en', 'US') + >>> repr(locale) + "Locale('en', territory='US')" + >>> locale.display_name + u'English (United States)' + + A `Locale` object can also be instantiated from a raw locale string: + + >>> locale = Locale.parse('en-US', sep='-') + >>> repr(locale) + "Locale('en', territory='US')" + + `Locale` objects provide access to a collection of locale data, such as + territory and language names, number and date format patterns, and more: + + >>> locale.number_symbols['latn']['decimal'] + u'.' + + If a locale is requested for which no locale data is available, an + `UnknownLocaleError` is raised: + + >>> Locale.parse('en_XX') + Traceback (most recent call last): + ... + UnknownLocaleError: unknown locale 'en_XX' + + For more information see :rfc:`3066`. + """ + + def __init__( + self, + language: str, + territory: str | None = None, + script: str | None = None, + variant: str | None = None, + modifier: str | None = None, + ) -> None: + """Initialize the locale object from the given identifier components. + + >>> locale = Locale('en', 'US') + >>> locale.language + 'en' + >>> locale.territory + 'US' + + :param language: the language code + :param territory: the territory (country or region) code + :param script: the script code + :param variant: the variant code + :param modifier: a modifier (following the '@' symbol, sometimes called '@variant') + :raise `UnknownLocaleError`: if no locale data is available for the + requested locale + """ + #: the language code + self.language = language + #: the territory (country or region) code + self.territory = territory + #: the script code + self.script = script + #: the variant code + self.variant = variant + #: the modifier + self.modifier = modifier + self.__data: localedata.LocaleDataDict | None = None + + identifier = str(self) + identifier_without_modifier = identifier.partition('@')[0] + if localedata.exists(identifier): + self.__data_identifier = identifier + elif localedata.exists(identifier_without_modifier): + self.__data_identifier = identifier_without_modifier + else: + raise UnknownLocaleError(identifier) + + @classmethod + def default(cls, category: str | None = None, aliases: Mapping[str, str] = LOCALE_ALIASES) -> Locale: + """Return the system default locale for the specified category. + + >>> for name in ['LANGUAGE', 'LC_ALL', 'LC_CTYPE', 'LC_MESSAGES']: + ... os.environ[name] = '' + >>> os.environ['LANG'] = 'fr_FR.UTF-8' + >>> Locale.default('LC_MESSAGES') + Locale('fr', territory='FR') + + The following fallbacks to the variable are always considered: + + - ``LANGUAGE`` + - ``LC_ALL`` + - ``LC_CTYPE`` + - ``LANG`` + + :param category: one of the ``LC_XXX`` environment variable names + :param aliases: a dictionary of aliases for locale identifiers + """ + # XXX: use likely subtag expansion here instead of the + # aliases dictionary. + locale_string = default_locale(category, aliases=aliases) + return cls.parse(locale_string) + + @classmethod + def negotiate( + cls, + preferred: Iterable[str], + available: Iterable[str], + sep: str = '_', + aliases: Mapping[str, str] = LOCALE_ALIASES, + ) -> Locale | None: + """Find the best match between available and requested locale strings. + + >>> Locale.negotiate(['de_DE', 'en_US'], ['de_DE', 'de_AT']) + Locale('de', territory='DE') + >>> Locale.negotiate(['de_DE', 'en_US'], ['en', 'de']) + Locale('de') + >>> Locale.negotiate(['de_DE', 'de'], ['en_US']) + + You can specify the character used in the locale identifiers to separate + the different components. This separator is applied to both lists. Also, + case is ignored in the comparison: + + >>> Locale.negotiate(['de-DE', 'de'], ['en-us', 'de-de'], sep='-') + Locale('de', territory='DE') + + :param preferred: the list of locale identifiers preferred by the user + :param available: the list of locale identifiers available + :param aliases: a dictionary of aliases for locale identifiers + :param sep: separator for parsing; e.g. Windows tends to use '-' instead of '_'. + """ + identifier = negotiate_locale(preferred, available, sep=sep, + aliases=aliases) + if identifier: + return Locale.parse(identifier, sep=sep) + return None + + @classmethod + def parse( + cls, + identifier: Locale | str | None, + sep: str = '_', + resolve_likely_subtags: bool = True, + ) -> Locale: + """Create a `Locale` instance for the given locale identifier. + + >>> l = Locale.parse('de-DE', sep='-') + >>> l.display_name + u'Deutsch (Deutschland)' + + If the `identifier` parameter is not a string, but actually a `Locale` + object, that object is returned: + + >>> Locale.parse(l) + Locale('de', territory='DE') + + If the `identifier` parameter is neither of these, such as `None` + or an empty string, e.g. because a default locale identifier + could not be determined, a `TypeError` is raised: + + >>> Locale.parse(None) + Traceback (most recent call last): + ... + TypeError: ... + + This also can perform resolving of likely subtags which it does + by default. This is for instance useful to figure out the most + likely locale for a territory you can use ``'und'`` as the + language tag: + + >>> Locale.parse('und_AT') + Locale('de', territory='AT') + + Modifiers are optional, and always at the end, separated by "@": + + >>> Locale.parse('de_AT@euro') + Locale('de', territory='AT', modifier='euro') + + :param identifier: the locale identifier string + :param sep: optional component separator + :param resolve_likely_subtags: if this is specified then a locale will + have its likely subtag resolved if the + locale otherwise does not exist. For + instance ``zh_TW`` by itself is not a + locale that exists but Babel can + automatically expand it to the full + form of ``zh_hant_TW``. Note that this + expansion is only taking place if no + locale exists otherwise. For instance + there is a locale ``en`` that can exist + by itself. + :raise `ValueError`: if the string does not appear to be a valid locale + identifier + :raise `UnknownLocaleError`: if no locale data is available for the + requested locale + :raise `TypeError`: if the identifier is not a string or a `Locale` + :raise `ValueError`: if the identifier is not a valid string + """ + if isinstance(identifier, Locale): + return identifier + + if not identifier: + msg = ( + f"Empty locale identifier value: {identifier!r}\n\n" + f"If you didn't explicitly pass an empty value to a Babel function, " + f"this could be caused by there being no suitable locale environment " + f"variables for the API you tried to use.", + ) + if isinstance(identifier, str): + raise ValueError(msg) # `parse_locale` would raise a ValueError, so let's do that here + raise TypeError(msg) + + if not isinstance(identifier, str): + raise TypeError(f"Unexpected value for identifier: {identifier!r}") + + parts = parse_locale(identifier, sep=sep) + input_id = get_locale_identifier(parts) + + def _try_load(parts): + try: + return cls(*parts) + except UnknownLocaleError: + return None + + def _try_load_reducing(parts): + # Success on first hit, return it. + locale = _try_load(parts) + if locale is not None: + return locale + + # Now try without script and variant + locale = _try_load(parts[:2]) + if locale is not None: + return locale + + locale = _try_load(parts) + if locale is not None: + return locale + if not resolve_likely_subtags: + raise UnknownLocaleError(input_id) + + # From here onwards is some very bad likely subtag resolving. This + # whole logic is not entirely correct but good enough (tm) for the + # time being. This has been added so that zh_TW does not cause + # errors for people when they upgrade. Later we should properly + # implement ICU like fuzzy locale objects and provide a way to + # maximize and minimize locale tags. + + if len(parts) == 5: + language, territory, script, variant, modifier = parts + else: + language, territory, script, variant = parts + modifier = None + language = get_global('language_aliases').get(language, language) + territory = get_global('territory_aliases').get(territory or '', (territory,))[0] + script = get_global('script_aliases').get(script or '', script) + variant = get_global('variant_aliases').get(variant or '', variant) + + if territory == 'ZZ': + territory = None + if script == 'Zzzz': + script = None + + parts = language, territory, script, variant, modifier + + # First match: try the whole identifier + new_id = get_locale_identifier(parts) + likely_subtag = get_global('likely_subtags').get(new_id) + if likely_subtag is not None: + locale = _try_load_reducing(parse_locale(likely_subtag)) + if locale is not None: + return locale + + # If we did not find anything so far, try again with a + # simplified identifier that is just the language + likely_subtag = get_global('likely_subtags').get(language) + if likely_subtag is not None: + parts2 = parse_locale(likely_subtag) + if len(parts2) == 5: + language2, _, script2, variant2, modifier2 = parts2 + else: + language2, _, script2, variant2 = parts2 + modifier2 = None + locale = _try_load_reducing((language2, territory, script2, variant2, modifier2)) + if locale is not None: + return locale + + raise UnknownLocaleError(input_id) + + def __eq__(self, other: object) -> bool: + for key in ('language', 'territory', 'script', 'variant', 'modifier'): + if not hasattr(other, key): + return False + return ( + self.language == getattr(other, 'language') and # noqa: B009 + self.territory == getattr(other, 'territory') and # noqa: B009 + self.script == getattr(other, 'script') and # noqa: B009 + self.variant == getattr(other, 'variant') and # noqa: B009 + self.modifier == getattr(other, 'modifier') # noqa: B009 + ) + + def __ne__(self, other: object) -> bool: + return not self.__eq__(other) + + def __hash__(self) -> int: + return hash((self.language, self.territory, self.script, + self.variant, self.modifier)) + + def __repr__(self) -> str: + parameters = [''] + for key in ('territory', 'script', 'variant', 'modifier'): + value = getattr(self, key) + if value is not None: + parameters.append(f"{key}={value!r}") + return f"Locale({self.language!r}{', '.join(parameters)})" + + def __str__(self) -> str: + return get_locale_identifier((self.language, self.territory, + self.script, self.variant, + self.modifier)) + + @property + def _data(self) -> localedata.LocaleDataDict: + if self.__data is None: + self.__data = localedata.LocaleDataDict(localedata.load(self.__data_identifier)) + return self.__data + + def get_display_name(self, locale: Locale | str | None = None) -> str | None: + """Return the display name of the locale using the given locale. + + The display name will include the language, territory, script, and + variant, if those are specified. + + >>> Locale('zh', 'CN', script='Hans').get_display_name('en') + u'Chinese (Simplified, China)' + + Modifiers are currently passed through verbatim: + + >>> Locale('it', 'IT', modifier='euro').get_display_name('en') + u'Italian (Italy, euro)' + + :param locale: the locale to use + """ + if locale is None: + locale = self + locale = Locale.parse(locale) + retval = locale.languages.get(self.language) + if retval and (self.territory or self.script or self.variant): + details = [] + if self.script: + details.append(locale.scripts.get(self.script)) + if self.territory: + details.append(locale.territories.get(self.territory)) + if self.variant: + details.append(locale.variants.get(self.variant)) + if self.modifier: + details.append(self.modifier) + detail_string = ', '.join(atom for atom in details if atom) + if detail_string: + retval += f" ({detail_string})" + return retval + + display_name = property(get_display_name, doc="""\ + The localized display name of the locale. + + >>> Locale('en').display_name + u'English' + >>> Locale('en', 'US').display_name + u'English (United States)' + >>> Locale('sv').display_name + u'svenska' + + :type: `unicode` + """) + + def get_language_name(self, locale: Locale | str | None = None) -> str | None: + """Return the language of this locale in the given locale. + + >>> Locale('zh', 'CN', script='Hans').get_language_name('de') + u'Chinesisch' + + .. versionadded:: 1.0 + + :param locale: the locale to use + """ + if locale is None: + locale = self + locale = Locale.parse(locale) + return locale.languages.get(self.language) + + language_name = property(get_language_name, doc="""\ + The localized language name of the locale. + + >>> Locale('en', 'US').language_name + u'English' + """) + + def get_territory_name(self, locale: Locale | str | None = None) -> str | None: + """Return the territory name in the given locale.""" + if locale is None: + locale = self + locale = Locale.parse(locale) + return locale.territories.get(self.territory or '') + + territory_name = property(get_territory_name, doc="""\ + The localized territory name of the locale if available. + + >>> Locale('de', 'DE').territory_name + u'Deutschland' + """) + + def get_script_name(self, locale: Locale | str | None = None) -> str | None: + """Return the script name in the given locale.""" + if locale is None: + locale = self + locale = Locale.parse(locale) + return locale.scripts.get(self.script or '') + + script_name = property(get_script_name, doc="""\ + The localized script name of the locale if available. + + >>> Locale('sr', 'ME', script='Latn').script_name + u'latinica' + """) + + @property + def english_name(self) -> str | None: + """The english display name of the locale. + + >>> Locale('de').english_name + u'German' + >>> Locale('de', 'DE').english_name + u'German (Germany)' + + :type: `unicode`""" + return self.get_display_name(Locale('en')) + + # { General Locale Display Names + + @property + def languages(self) -> localedata.LocaleDataDict: + """Mapping of language codes to translated language names. + + >>> Locale('de', 'DE').languages['ja'] + u'Japanisch' + + See `ISO 639 `_ for + more information. + """ + return self._data['languages'] + + @property + def scripts(self) -> localedata.LocaleDataDict: + """Mapping of script codes to translated script names. + + >>> Locale('en', 'US').scripts['Hira'] + u'Hiragana' + + See `ISO 15924 `_ + for more information. + """ + return self._data['scripts'] + + @property + def territories(self) -> localedata.LocaleDataDict: + """Mapping of script codes to translated script names. + + >>> Locale('es', 'CO').territories['DE'] + u'Alemania' + + See `ISO 3166 `_ + for more information. + """ + return self._data['territories'] + + @property + def variants(self) -> localedata.LocaleDataDict: + """Mapping of script codes to translated script names. + + >>> Locale('de', 'DE').variants['1901'] + u'Alte deutsche Rechtschreibung' + """ + return self._data['variants'] + + # { Number Formatting + + @property + def currencies(self) -> localedata.LocaleDataDict: + """Mapping of currency codes to translated currency names. This + only returns the generic form of the currency name, not the count + specific one. If an actual number is requested use the + :func:`babel.numbers.get_currency_name` function. + + >>> Locale('en').currencies['COP'] + u'Colombian Peso' + >>> Locale('de', 'DE').currencies['COP'] + u'Kolumbianischer Peso' + """ + return self._data['currency_names'] + + @property + def currency_symbols(self) -> localedata.LocaleDataDict: + """Mapping of currency codes to symbols. + + >>> Locale('en', 'US').currency_symbols['USD'] + u'$' + >>> Locale('es', 'CO').currency_symbols['USD'] + u'US$' + """ + return self._data['currency_symbols'] + + @property + def number_symbols(self) -> localedata.LocaleDataDict: + """Symbols used in number formatting by number system. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('fr', 'FR').number_symbols["latn"]['decimal'] + u',' + >>> Locale('fa', 'IR').number_symbols["arabext"]['decimal'] + u'٫' + >>> Locale('fa', 'IR').number_symbols["latn"]['decimal'] + u'.' + """ + return self._data['number_symbols'] + + @property + def other_numbering_systems(self) -> localedata.LocaleDataDict: + """ + Mapping of other numbering systems available for the locale. + See: https://www.unicode.org/reports/tr35/tr35-numbers.html#otherNumberingSystems + + >>> Locale('el', 'GR').other_numbering_systems['traditional'] + u'grek' + + .. note:: The format of the value returned may change between + Babel versions. + """ + return self._data['numbering_systems'] + + @property + def default_numbering_system(self) -> str: + """The default numbering system used by the locale. + >>> Locale('el', 'GR').default_numbering_system + u'latn' + """ + return self._data['default_numbering_system'] + + @property + def decimal_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for decimal number formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').decimal_formats[None] + + """ + return self._data['decimal_formats'] + + @property + def compact_decimal_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for compact decimal number formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').compact_decimal_formats["short"]["one"]["1000"] + + """ + return self._data['compact_decimal_formats'] + + @property + def currency_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for currency number formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').currency_formats['standard'] + + >>> Locale('en', 'US').currency_formats['accounting'] + + """ + return self._data['currency_formats'] + + @property + def compact_currency_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for compact currency number formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').compact_currency_formats["short"]["one"]["1000"] + + """ + return self._data['compact_currency_formats'] + + @property + def percent_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for percent number formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').percent_formats[None] + + """ + return self._data['percent_formats'] + + @property + def scientific_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for scientific number formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').scientific_formats[None] + + """ + return self._data['scientific_formats'] + + # { Calendar Information and Date Formatting + + @property + def periods(self) -> localedata.LocaleDataDict: + """Locale display names for day periods (AM/PM). + + >>> Locale('en', 'US').periods['am'] + u'AM' + """ + try: + return self._data['day_periods']['stand-alone']['wide'] + except KeyError: + return localedata.LocaleDataDict({}) # pragma: no cover + + @property + def day_periods(self) -> localedata.LocaleDataDict: + """Locale display names for various day periods (not necessarily only AM/PM). + + These are not meant to be used without the relevant `day_period_rules`. + """ + return self._data['day_periods'] + + @property + def day_period_rules(self) -> localedata.LocaleDataDict: + """Day period rules for the locale. Used by `get_period_id`. + """ + return self._data.get('day_period_rules', localedata.LocaleDataDict({})) + + @property + def days(self) -> localedata.LocaleDataDict: + """Locale display names for weekdays. + + >>> Locale('de', 'DE').days['format']['wide'][3] + u'Donnerstag' + """ + return self._data['days'] + + @property + def months(self) -> localedata.LocaleDataDict: + """Locale display names for months. + + >>> Locale('de', 'DE').months['format']['wide'][10] + u'Oktober' + """ + return self._data['months'] + + @property + def quarters(self) -> localedata.LocaleDataDict: + """Locale display names for quarters. + + >>> Locale('de', 'DE').quarters['format']['wide'][1] + u'1. Quartal' + """ + return self._data['quarters'] + + @property + def eras(self) -> localedata.LocaleDataDict: + """Locale display names for eras. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').eras['wide'][1] + u'Anno Domini' + >>> Locale('en', 'US').eras['abbreviated'][0] + u'BC' + """ + return self._data['eras'] + + @property + def time_zones(self) -> localedata.LocaleDataDict: + """Locale display names for time zones. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').time_zones['Europe/London']['long']['daylight'] + u'British Summer Time' + >>> Locale('en', 'US').time_zones['America/St_Johns']['city'] + u'St. John\u2019s' + """ + return self._data['time_zones'] + + @property + def meta_zones(self) -> localedata.LocaleDataDict: + """Locale display names for meta time zones. + + Meta time zones are basically groups of different Olson time zones that + have the same GMT offset and daylight savings time. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').meta_zones['Europe_Central']['long']['daylight'] + u'Central European Summer Time' + + .. versionadded:: 0.9 + """ + return self._data['meta_zones'] + + @property + def zone_formats(self) -> localedata.LocaleDataDict: + """Patterns related to the formatting of time zones. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').zone_formats['fallback'] + u'%(1)s (%(0)s)' + >>> Locale('pt', 'BR').zone_formats['region'] + u'Hor\\xe1rio %s' + + .. versionadded:: 0.9 + """ + return self._data['zone_formats'] + + @property + def first_week_day(self) -> int: + """The first day of a week, with 0 being Monday. + + >>> Locale('de', 'DE').first_week_day + 0 + >>> Locale('en', 'US').first_week_day + 6 + """ + return self._data['week_data']['first_day'] + + @property + def weekend_start(self) -> int: + """The day the weekend starts, with 0 being Monday. + + >>> Locale('de', 'DE').weekend_start + 5 + """ + return self._data['week_data']['weekend_start'] + + @property + def weekend_end(self) -> int: + """The day the weekend ends, with 0 being Monday. + + >>> Locale('de', 'DE').weekend_end + 6 + """ + return self._data['week_data']['weekend_end'] + + @property + def min_week_days(self) -> int: + """The minimum number of days in a week so that the week is counted as + the first week of a year or month. + + >>> Locale('de', 'DE').min_week_days + 4 + """ + return self._data['week_data']['min_days'] + + @property + def date_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for date formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').date_formats['short'] + + >>> Locale('fr', 'FR').date_formats['long'] + + """ + return self._data['date_formats'] + + @property + def time_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for time formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en', 'US').time_formats['short'] + + >>> Locale('fr', 'FR').time_formats['long'] + + """ + return self._data['time_formats'] + + @property + def datetime_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for datetime formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en').datetime_formats['full'] + u'{1}, {0}' + >>> Locale('th').datetime_formats['medium'] + u'{1} {0}' + """ + return self._data['datetime_formats'] + + @property + def datetime_skeletons(self) -> localedata.LocaleDataDict: + """Locale patterns for formatting parts of a datetime. + + >>> Locale('en').datetime_skeletons['MEd'] + + >>> Locale('fr').datetime_skeletons['MEd'] + + >>> Locale('fr').datetime_skeletons['H'] + + """ + return self._data['datetime_skeletons'] + + @property + def interval_formats(self) -> localedata.LocaleDataDict: + """Locale patterns for interval formatting. + + .. note:: The format of the value returned may change between + Babel versions. + + How to format date intervals in Finnish when the day is the + smallest changing component: + + >>> Locale('fi_FI').interval_formats['MEd']['d'] + [u'E d.\u2009\u2013\u2009', u'E d.M.'] + + .. seealso:: + + The primary API to use this data is :py:func:`babel.dates.format_interval`. + + + :rtype: dict[str, dict[str, list[str]]] + """ + return self._data['interval_formats'] + + @property + def plural_form(self) -> PluralRule: + """Plural rules for the locale. + + >>> Locale('en').plural_form(1) + 'one' + >>> Locale('en').plural_form(0) + 'other' + >>> Locale('fr').plural_form(0) + 'one' + >>> Locale('ru').plural_form(100) + 'many' + """ + return self._data.get('plural_form', _default_plural_rule) + + @property + def list_patterns(self) -> localedata.LocaleDataDict: + """Patterns for generating lists + + .. note:: The format of the value returned may change between + Babel versions. + + >>> Locale('en').list_patterns['standard']['start'] + u'{0}, {1}' + >>> Locale('en').list_patterns['standard']['end'] + u'{0}, and {1}' + >>> Locale('en_GB').list_patterns['standard']['end'] + u'{0} and {1}' + """ + return self._data['list_patterns'] + + @property + def ordinal_form(self) -> PluralRule: + """Plural rules for the locale. + + >>> Locale('en').ordinal_form(1) + 'one' + >>> Locale('en').ordinal_form(2) + 'two' + >>> Locale('en').ordinal_form(3) + 'few' + >>> Locale('fr').ordinal_form(2) + 'other' + >>> Locale('ru').ordinal_form(100) + 'other' + """ + return self._data.get('ordinal_form', _default_plural_rule) + + @property + def measurement_systems(self) -> localedata.LocaleDataDict: + """Localized names for various measurement systems. + + >>> Locale('fr', 'FR').measurement_systems['US'] + u'am\\xe9ricain' + >>> Locale('en', 'US').measurement_systems['US'] + u'US' + + """ + return self._data['measurement_systems'] + + @property + def character_order(self) -> str: + """The text direction for the language. + + >>> Locale('de', 'DE').character_order + 'left-to-right' + >>> Locale('ar', 'SA').character_order + 'right-to-left' + """ + return self._data['character_order'] + + @property + def text_direction(self) -> str: + """The text direction for the language in CSS short-hand form. + + >>> Locale('de', 'DE').text_direction + 'ltr' + >>> Locale('ar', 'SA').text_direction + 'rtl' + """ + return ''.join(word[0] for word in self.character_order.split('-')) + + @property + def unit_display_names(self) -> localedata.LocaleDataDict: + """Display names for units of measurement. + + .. seealso:: + + You may want to use :py:func:`babel.units.get_unit_name` instead. + + .. note:: The format of the value returned may change between + Babel versions. + + """ + return self._data['unit_display_names'] + + +def default_locale( + category: str | tuple[str, ...] | list[str] | None = None, + aliases: Mapping[str, str] = LOCALE_ALIASES, +) -> str | None: + """Returns the system default locale for a given category, based on + environment variables. + + >>> for name in ['LANGUAGE', 'LC_ALL', 'LC_CTYPE']: + ... os.environ[name] = '' + >>> os.environ['LANG'] = 'fr_FR.UTF-8' + >>> default_locale('LC_MESSAGES') + 'fr_FR' + + The "C" or "POSIX" pseudo-locales are treated as aliases for the + "en_US_POSIX" locale: + + >>> os.environ['LC_MESSAGES'] = 'POSIX' + >>> default_locale('LC_MESSAGES') + 'en_US_POSIX' + + The following fallbacks to the variable are always considered: + + - ``LANGUAGE`` + - ``LC_ALL`` + - ``LC_CTYPE`` + - ``LANG`` + + :param category: one or more of the ``LC_XXX`` environment variable names + :param aliases: a dictionary of aliases for locale identifiers + """ + + varnames = ('LANGUAGE', 'LC_ALL', 'LC_CTYPE', 'LANG') + if category: + if isinstance(category, str): + varnames = (category, *varnames) + elif isinstance(category, (list, tuple)): + varnames = (*category, *varnames) + else: + raise TypeError(f"Invalid type for category: {category!r}") + + for name in varnames: + if not name: + continue + locale = os.getenv(name) + if locale: + if name == 'LANGUAGE' and ':' in locale: + # the LANGUAGE variable may contain a colon-separated list of + # language codes; we just pick the language on the list + locale = locale.split(':')[0] + if locale.split('.')[0] in ('C', 'POSIX'): + locale = 'en_US_POSIX' + elif aliases and locale in aliases: + locale = aliases[locale] + try: + return get_locale_identifier(parse_locale(locale)) + except ValueError: + pass + return None + + +def negotiate_locale(preferred: Iterable[str], available: Iterable[str], sep: str = '_', aliases: Mapping[str, str] = LOCALE_ALIASES) -> str | None: + """Find the best match between available and requested locale strings. + + >>> negotiate_locale(['de_DE', 'en_US'], ['de_DE', 'de_AT']) + 'de_DE' + >>> negotiate_locale(['de_DE', 'en_US'], ['en', 'de']) + 'de' + + Case is ignored by the algorithm, the result uses the case of the preferred + locale identifier: + + >>> negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at']) + 'de_DE' + + >>> negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at']) + 'de_DE' + + By default, some web browsers unfortunately do not include the territory + in the locale identifier for many locales, and some don't even allow the + user to easily add the territory. So while you may prefer using qualified + locale identifiers in your web-application, they would not normally match + the language-only locale sent by such browsers. To workaround that, this + function uses a default mapping of commonly used language-only locale + identifiers to identifiers including the territory: + + >>> negotiate_locale(['ja', 'en_US'], ['ja_JP', 'en_US']) + 'ja_JP' + + Some browsers even use an incorrect or outdated language code, such as "no" + for Norwegian, where the correct locale identifier would actually be "nb_NO" + (Bokmål) or "nn_NO" (Nynorsk). The aliases are intended to take care of + such cases, too: + + >>> negotiate_locale(['no', 'sv'], ['nb_NO', 'sv_SE']) + 'nb_NO' + + You can override this default mapping by passing a different `aliases` + dictionary to this function, or you can bypass the behavior althogher by + setting the `aliases` parameter to `None`. + + :param preferred: the list of locale strings preferred by the user + :param available: the list of locale strings available + :param sep: character that separates the different parts of the locale + strings + :param aliases: a dictionary of aliases for locale identifiers + """ + available = [a.lower() for a in available if a] + for locale in preferred: + ll = locale.lower() + if ll in available: + return locale + if aliases: + alias = aliases.get(ll) + if alias: + alias = alias.replace('_', sep) + if alias.lower() in available: + return alias + parts = locale.split(sep) + if len(parts) > 1 and parts[0].lower() in available: + return parts[0] + return None + + +def parse_locale( + identifier: str, + sep: str = '_', +) -> tuple[str, str | None, str | None, str | None] | tuple[str, str | None, str | None, str | None, str | None]: + """Parse a locale identifier into a tuple of the form ``(language, + territory, script, variant, modifier)``. + + >>> parse_locale('zh_CN') + ('zh', 'CN', None, None) + >>> parse_locale('zh_Hans_CN') + ('zh', 'CN', 'Hans', None) + >>> parse_locale('ca_es_valencia') + ('ca', 'ES', None, 'VALENCIA') + >>> parse_locale('en_150') + ('en', '150', None, None) + >>> parse_locale('en_us_posix') + ('en', 'US', None, 'POSIX') + >>> parse_locale('it_IT@euro') + ('it', 'IT', None, None, 'euro') + >>> parse_locale('it_IT@custom') + ('it', 'IT', None, None, 'custom') + >>> parse_locale('it_IT@') + ('it', 'IT', None, None) + + The default component separator is "_", but a different separator can be + specified using the `sep` parameter. + + The optional modifier is always separated with "@" and at the end: + + >>> parse_locale('zh-CN', sep='-') + ('zh', 'CN', None, None) + >>> parse_locale('zh-CN@custom', sep='-') + ('zh', 'CN', None, None, 'custom') + + If the identifier cannot be parsed into a locale, a `ValueError` exception + is raised: + + >>> parse_locale('not_a_LOCALE_String') + Traceback (most recent call last): + ... + ValueError: 'not_a_LOCALE_String' is not a valid locale identifier + + Encoding information is removed from the identifier, while modifiers are + kept: + + >>> parse_locale('en_US.UTF-8') + ('en', 'US', None, None) + >>> parse_locale('de_DE.iso885915@euro') + ('de', 'DE', None, None, 'euro') + + See :rfc:`4646` for more information. + + :param identifier: the locale identifier string + :param sep: character that separates the different components of the locale + identifier + :raise `ValueError`: if the string does not appear to be a valid locale + identifier + """ + if not identifier: + raise ValueError("empty locale identifier") + identifier, _, modifier = identifier.partition('@') + if '.' in identifier: + # this is probably the charset/encoding, which we don't care about + identifier = identifier.split('.', 1)[0] + + parts = identifier.split(sep) + lang = parts.pop(0).lower() + if not lang.isalpha(): + raise ValueError(f"expected only letters, got {lang!r}") + + script = territory = variant = None + if parts and len(parts[0]) == 4 and parts[0].isalpha(): + script = parts.pop(0).title() + + if parts: + if len(parts[0]) == 2 and parts[0].isalpha(): + territory = parts.pop(0).upper() + elif len(parts[0]) == 3 and parts[0].isdigit(): + territory = parts.pop(0) + + if parts and ( + len(parts[0]) == 4 and parts[0][0].isdigit() or + len(parts[0]) >= 5 and parts[0][0].isalpha() + ): + variant = parts.pop().upper() + + if parts: + raise ValueError(f"{identifier!r} is not a valid locale identifier") + + # TODO(3.0): always return a 5-tuple + if modifier: + return lang, territory, script, variant, modifier + else: + return lang, territory, script, variant + + +def get_locale_identifier( + tup: tuple[str] + | tuple[str, str | None] + | tuple[str, str | None, str | None] + | tuple[str, str | None, str | None, str | None] + | tuple[str, str | None, str | None, str | None, str | None], + sep: str = "_", +) -> str: + """The reverse of :func:`parse_locale`. It creates a locale identifier out + of a ``(language, territory, script, variant, modifier)`` tuple. Items can be set to + ``None`` and trailing ``None``\\s can also be left out of the tuple. + + >>> get_locale_identifier(('de', 'DE', None, '1999', 'custom')) + 'de_DE_1999@custom' + >>> get_locale_identifier(('fi', None, None, None, 'custom')) + 'fi@custom' + + + .. versionadded:: 1.0 + + :param tup: the tuple as returned by :func:`parse_locale`. + :param sep: the separator for the identifier. + """ + tup = tuple(tup[:5]) # type: ignore # length should be no more than 5 + lang, territory, script, variant, modifier = tup + (None,) * (5 - len(tup)) + ret = sep.join(filter(None, (lang, script, territory, variant))) + return f'{ret}@{modifier}' if modifier else ret diff --git a/.venv/lib/python3.9/site-packages/babel/dates.py b/.venv/lib/python3.9/site-packages/babel/dates.py new file mode 100644 index 00000000..355a9236 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/dates.py @@ -0,0 +1,1999 @@ +""" + babel.dates + ~~~~~~~~~~~ + + Locale dependent formatting and parsing of dates and times. + + The default locale for the functions in this module is determined by the + following environment variables, in that order: + + * ``LC_TIME``, + * ``LC_ALL``, and + * ``LANG`` + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +from __future__ import annotations + +import math +import re +import warnings +from functools import lru_cache +from typing import TYPE_CHECKING, Literal, SupportsInt + +try: + import pytz +except ModuleNotFoundError: + pytz = None + import zoneinfo + +import datetime +from collections.abc import Iterable + +from babel import localtime +from babel.core import Locale, default_locale, get_global +from babel.localedata import LocaleDataDict + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + _Instant: TypeAlias = datetime.date | datetime.time | float | None + _PredefinedTimeFormat: TypeAlias = Literal['full', 'long', 'medium', 'short'] + _Context: TypeAlias = Literal['format', 'stand-alone'] + _DtOrTzinfo: TypeAlias = datetime.datetime | datetime.tzinfo | str | int | datetime.time | None + +# "If a given short metazone form is known NOT to be understood in a given +# locale and the parent locale has this value such that it would normally +# be inherited, the inheritance of this value can be explicitly disabled by +# use of the 'no inheritance marker' as the value, which is 3 simultaneous [sic] +# empty set characters ( U+2205 )." +# - https://www.unicode.org/reports/tr35/tr35-dates.html#Metazone_Names + +NO_INHERITANCE_MARKER = '\u2205\u2205\u2205' + +UTC = datetime.timezone.utc +LOCALTZ = localtime.LOCALTZ + +LC_TIME = default_locale('LC_TIME') + + +def _localize(tz: datetime.tzinfo, dt: datetime.datetime) -> datetime.datetime: + # Support localizing with both pytz and zoneinfo tzinfos + # nothing to do + if dt.tzinfo is tz: + return dt + + if hasattr(tz, 'localize'): # pytz + return tz.localize(dt) + + if dt.tzinfo is None: + # convert naive to localized + return dt.replace(tzinfo=tz) + + # convert timezones + return dt.astimezone(tz) + + +def _get_dt_and_tzinfo(dt_or_tzinfo: _DtOrTzinfo) -> tuple[datetime.datetime | None, datetime.tzinfo]: + """ + Parse a `dt_or_tzinfo` value into a datetime and a tzinfo. + + See the docs for this function's callers for semantics. + + :rtype: tuple[datetime, tzinfo] + """ + if dt_or_tzinfo is None: + dt = datetime.datetime.now() + tzinfo = LOCALTZ + elif isinstance(dt_or_tzinfo, str): + dt = None + tzinfo = get_timezone(dt_or_tzinfo) + elif isinstance(dt_or_tzinfo, int): + dt = None + tzinfo = UTC + elif isinstance(dt_or_tzinfo, (datetime.datetime, datetime.time)): + dt = _get_datetime(dt_or_tzinfo) + tzinfo = dt.tzinfo if dt.tzinfo is not None else UTC + else: + dt = None + tzinfo = dt_or_tzinfo + return dt, tzinfo + + +def _get_tz_name(dt_or_tzinfo: _DtOrTzinfo) -> str: + """ + Get the timezone name out of a time, datetime, or tzinfo object. + + :rtype: str + """ + dt, tzinfo = _get_dt_and_tzinfo(dt_or_tzinfo) + if hasattr(tzinfo, 'zone'): # pytz object + return tzinfo.zone + elif hasattr(tzinfo, 'key') and tzinfo.key is not None: # ZoneInfo object + return tzinfo.key + else: + return tzinfo.tzname(dt or datetime.datetime.now(UTC)) + + +def _get_datetime(instant: _Instant) -> datetime.datetime: + """ + Get a datetime out of an "instant" (date, time, datetime, number). + + .. warning:: The return values of this function may depend on the system clock. + + If the instant is None, the current moment is used. + If the instant is a time, it's augmented with today's date. + + Dates are converted to naive datetimes with midnight as the time component. + + >>> from datetime import date, datetime + >>> _get_datetime(date(2015, 1, 1)) + datetime.datetime(2015, 1, 1, 0, 0) + + UNIX timestamps are converted to datetimes. + + >>> _get_datetime(1400000000) + datetime.datetime(2014, 5, 13, 16, 53, 20) + + Other values are passed through as-is. + + >>> x = datetime(2015, 1, 1) + >>> _get_datetime(x) is x + True + + :param instant: date, time, datetime, integer, float or None + :type instant: date|time|datetime|int|float|None + :return: a datetime + :rtype: datetime + """ + if instant is None: + return datetime.datetime.now(UTC).replace(tzinfo=None) + elif isinstance(instant, (int, float)): + return datetime.datetime.fromtimestamp(instant, UTC).replace(tzinfo=None) + elif isinstance(instant, datetime.time): + return datetime.datetime.combine(datetime.date.today(), instant) + elif isinstance(instant, datetime.date) and not isinstance(instant, datetime.datetime): + return datetime.datetime.combine(instant, datetime.time()) + # TODO (3.x): Add an assertion/type check for this fallthrough branch: + return instant + + +def _ensure_datetime_tzinfo(dt: datetime.datetime, tzinfo: datetime.tzinfo | None = None) -> datetime.datetime: + """ + Ensure the datetime passed has an attached tzinfo. + + If the datetime is tz-naive to begin with, UTC is attached. + + If a tzinfo is passed in, the datetime is normalized to that timezone. + + >>> from datetime import datetime + >>> _get_tz_name(_ensure_datetime_tzinfo(datetime(2015, 1, 1))) + 'UTC' + + >>> tz = get_timezone("Europe/Stockholm") + >>> _ensure_datetime_tzinfo(datetime(2015, 1, 1, 13, 15, tzinfo=UTC), tzinfo=tz).hour + 14 + + :param datetime: Datetime to augment. + :param tzinfo: optional tzinfo + :return: datetime with tzinfo + :rtype: datetime + """ + if dt.tzinfo is None: + dt = dt.replace(tzinfo=UTC) + if tzinfo is not None: + dt = dt.astimezone(get_timezone(tzinfo)) + if hasattr(tzinfo, 'normalize'): # pytz + dt = tzinfo.normalize(dt) + return dt + + +def _get_time( + time: datetime.time | datetime.datetime | None, + tzinfo: datetime.tzinfo | None = None, +) -> datetime.time: + """ + Get a timezoned time from a given instant. + + .. warning:: The return values of this function may depend on the system clock. + + :param time: time, datetime or None + :rtype: time + """ + if time is None: + time = datetime.datetime.now(UTC) + elif isinstance(time, (int, float)): + time = datetime.datetime.fromtimestamp(time, UTC) + + if time.tzinfo is None: + time = time.replace(tzinfo=UTC) + + if isinstance(time, datetime.datetime): + if tzinfo is not None: + time = time.astimezone(tzinfo) + if hasattr(tzinfo, 'normalize'): # pytz + time = tzinfo.normalize(time) + time = time.timetz() + elif tzinfo is not None: + time = time.replace(tzinfo=tzinfo) + return time + + +def get_timezone(zone: str | datetime.tzinfo | None = None) -> datetime.tzinfo: + """Looks up a timezone by name and returns it. The timezone object + returned comes from ``pytz`` or ``zoneinfo``, whichever is available. + It corresponds to the `tzinfo` interface and can be used with all of + the functions of Babel that operate with dates. + + If a timezone is not known a :exc:`LookupError` is raised. If `zone` + is ``None`` a local zone object is returned. + + :param zone: the name of the timezone to look up. If a timezone object + itself is passed in, it's returned unchanged. + """ + if zone is None: + return LOCALTZ + if not isinstance(zone, str): + return zone + + if pytz: + try: + return pytz.timezone(zone) + except pytz.UnknownTimeZoneError as e: + exc = e + else: + assert zoneinfo + try: + return zoneinfo.ZoneInfo(zone) + except zoneinfo.ZoneInfoNotFoundError as e: + exc = e + + raise LookupError(f"Unknown timezone {zone}") from exc + + +def get_period_names( + width: Literal['abbreviated', 'narrow', 'wide'] = 'wide', + context: _Context = 'stand-alone', + locale: Locale | str | None = None, +) -> LocaleDataDict: + """Return the names for day periods (AM/PM) used by the locale. + + >>> get_period_names(locale='en_US')['am'] + u'AM' + + :param width: the width to use, one of "abbreviated", "narrow", or "wide" + :param context: the context, either "format" or "stand-alone" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + """ + return Locale.parse(locale or LC_TIME).day_periods[context][width] + + +def get_day_names( + width: Literal['abbreviated', 'narrow', 'short', 'wide'] = 'wide', + context: _Context = 'format', + locale: Locale | str | None = None, +) -> LocaleDataDict: + """Return the day names used by the locale for the specified format. + + >>> get_day_names('wide', locale='en_US')[1] + u'Tuesday' + >>> get_day_names('short', locale='en_US')[1] + u'Tu' + >>> get_day_names('abbreviated', locale='es')[1] + u'mar' + >>> get_day_names('narrow', context='stand-alone', locale='de_DE')[1] + u'D' + + :param width: the width to use, one of "wide", "abbreviated", "short" or "narrow" + :param context: the context, either "format" or "stand-alone" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + """ + return Locale.parse(locale or LC_TIME).days[context][width] + + +def get_month_names( + width: Literal['abbreviated', 'narrow', 'wide'] = 'wide', + context: _Context = 'format', + locale: Locale | str | None = None, +) -> LocaleDataDict: + """Return the month names used by the locale for the specified format. + + >>> get_month_names('wide', locale='en_US')[1] + u'January' + >>> get_month_names('abbreviated', locale='es')[1] + u'ene' + >>> get_month_names('narrow', context='stand-alone', locale='de_DE')[1] + u'J' + + :param width: the width to use, one of "wide", "abbreviated", or "narrow" + :param context: the context, either "format" or "stand-alone" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + """ + return Locale.parse(locale or LC_TIME).months[context][width] + + +def get_quarter_names( + width: Literal['abbreviated', 'narrow', 'wide'] = 'wide', + context: _Context = 'format', + locale: Locale | str | None = None, +) -> LocaleDataDict: + """Return the quarter names used by the locale for the specified format. + + >>> get_quarter_names('wide', locale='en_US')[1] + u'1st quarter' + >>> get_quarter_names('abbreviated', locale='de_DE')[1] + u'Q1' + >>> get_quarter_names('narrow', locale='de_DE')[1] + u'1' + + :param width: the width to use, one of "wide", "abbreviated", or "narrow" + :param context: the context, either "format" or "stand-alone" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + """ + return Locale.parse(locale or LC_TIME).quarters[context][width] + + +def get_era_names( + width: Literal['abbreviated', 'narrow', 'wide'] = 'wide', + locale: Locale | str | None = None, +) -> LocaleDataDict: + """Return the era names used by the locale for the specified format. + + >>> get_era_names('wide', locale='en_US')[1] + u'Anno Domini' + >>> get_era_names('abbreviated', locale='de_DE')[1] + u'n. Chr.' + + :param width: the width to use, either "wide", "abbreviated", or "narrow" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + """ + return Locale.parse(locale or LC_TIME).eras[width] + + +def get_date_format( + format: _PredefinedTimeFormat = 'medium', + locale: Locale | str | None = None, +) -> DateTimePattern: + """Return the date formatting patterns used by the locale for the specified + format. + + >>> get_date_format(locale='en_US') + + >>> get_date_format('full', locale='de_DE') + + + :param format: the format to use, one of "full", "long", "medium", or + "short" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + """ + return Locale.parse(locale or LC_TIME).date_formats[format] + + +def get_datetime_format( + format: _PredefinedTimeFormat = 'medium', + locale: Locale | str | None = None, +) -> DateTimePattern: + """Return the datetime formatting patterns used by the locale for the + specified format. + + >>> get_datetime_format(locale='en_US') + u'{1}, {0}' + + :param format: the format to use, one of "full", "long", "medium", or + "short" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + """ + patterns = Locale.parse(locale or LC_TIME).datetime_formats + if format not in patterns: + format = None + return patterns[format] + + +def get_time_format( + format: _PredefinedTimeFormat = 'medium', + locale: Locale | str | None = None, +) -> DateTimePattern: + """Return the time formatting patterns used by the locale for the specified + format. + + >>> get_time_format(locale='en_US') + + >>> get_time_format('full', locale='de_DE') + + + :param format: the format to use, one of "full", "long", "medium", or + "short" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + """ + return Locale.parse(locale or LC_TIME).time_formats[format] + + +def get_timezone_gmt( + datetime: _Instant = None, + width: Literal['long', 'short', 'iso8601', 'iso8601_short'] = 'long', + locale: Locale | str | None = None, + return_z: bool = False, +) -> str: + """Return the timezone associated with the given `datetime` object formatted + as string indicating the offset from GMT. + + >>> from datetime import datetime + >>> dt = datetime(2007, 4, 1, 15, 30) + >>> get_timezone_gmt(dt, locale='en') + u'GMT+00:00' + >>> get_timezone_gmt(dt, locale='en', return_z=True) + 'Z' + >>> get_timezone_gmt(dt, locale='en', width='iso8601_short') + u'+00' + >>> tz = get_timezone('America/Los_Angeles') + >>> dt = _localize(tz, datetime(2007, 4, 1, 15, 30)) + >>> get_timezone_gmt(dt, locale='en') + u'GMT-07:00' + >>> get_timezone_gmt(dt, 'short', locale='en') + u'-0700' + >>> get_timezone_gmt(dt, locale='en', width='iso8601_short') + u'-07' + + The long format depends on the locale, for example in France the acronym + UTC string is used instead of GMT: + + >>> get_timezone_gmt(dt, 'long', locale='fr_FR') + u'UTC-07:00' + + .. versionadded:: 0.9 + + :param datetime: the ``datetime`` object; if `None`, the current date and + time in UTC is used + :param width: either "long" or "short" or "iso8601" or "iso8601_short" + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + :param return_z: True or False; Function returns indicator "Z" + when local time offset is 0 + """ + datetime = _ensure_datetime_tzinfo(_get_datetime(datetime)) + locale = Locale.parse(locale or LC_TIME) + + offset = datetime.tzinfo.utcoffset(datetime) + seconds = offset.days * 24 * 60 * 60 + offset.seconds + hours, seconds = divmod(seconds, 3600) + if return_z and hours == 0 and seconds == 0: + return 'Z' + elif seconds == 0 and width == 'iso8601_short': + return '%+03d' % hours + elif width == 'short' or width == 'iso8601_short': + pattern = '%+03d%02d' + elif width == 'iso8601': + pattern = '%+03d:%02d' + else: + pattern = locale.zone_formats['gmt'] % '%+03d:%02d' + return pattern % (hours, seconds // 60) + + +def get_timezone_location( + dt_or_tzinfo: _DtOrTzinfo = None, + locale: Locale | str | None = None, + return_city: bool = False, +) -> str: + """Return a representation of the given timezone using "location format". + + The result depends on both the local display name of the country and the + city associated with the time zone: + + >>> tz = get_timezone('America/St_Johns') + >>> print(get_timezone_location(tz, locale='de_DE')) + Kanada (St. John’s) (Ortszeit) + >>> print(get_timezone_location(tz, locale='en')) + Canada (St. John’s) Time + >>> print(get_timezone_location(tz, locale='en', return_city=True)) + St. John’s + >>> tz = get_timezone('America/Mexico_City') + >>> get_timezone_location(tz, locale='de_DE') + u'Mexiko (Mexiko-Stadt) (Ortszeit)' + + If the timezone is associated with a country that uses only a single + timezone, just the localized country name is returned: + + >>> tz = get_timezone('Europe/Berlin') + >>> get_timezone_name(tz, locale='de_DE') + u'Mitteleurop\\xe4ische Zeit' + + .. versionadded:: 0.9 + + :param dt_or_tzinfo: the ``datetime`` or ``tzinfo`` object that determines + the timezone; if `None`, the current date and time in + UTC is assumed + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + :param return_city: True or False, if True then return exemplar city (location) + for the time zone + :return: the localized timezone name using location format + + """ + locale = Locale.parse(locale or LC_TIME) + + zone = _get_tz_name(dt_or_tzinfo) + + # Get the canonical time-zone code + zone = get_global('zone_aliases').get(zone, zone) + + info = locale.time_zones.get(zone, {}) + + # Otherwise, if there is only one timezone for the country, return the + # localized country name + region_format = locale.zone_formats['region'] + territory = get_global('zone_territories').get(zone) + if territory not in locale.territories: + territory = 'ZZ' # invalid/unknown + territory_name = locale.territories[territory] + if not return_city and territory and len(get_global('territory_zones').get(territory, [])) == 1: + return region_format % territory_name + + # Otherwise, include the city in the output + fallback_format = locale.zone_formats['fallback'] + if 'city' in info: + city_name = info['city'] + else: + metazone = get_global('meta_zones').get(zone) + metazone_info = locale.meta_zones.get(metazone, {}) + if 'city' in metazone_info: + city_name = metazone_info['city'] + elif '/' in zone: + city_name = zone.split('/', 1)[1].replace('_', ' ') + else: + city_name = zone.replace('_', ' ') + + if return_city: + return city_name + return region_format % (fallback_format % { + '0': city_name, + '1': territory_name, + }) + + +def get_timezone_name( + dt_or_tzinfo: _DtOrTzinfo = None, + width: Literal['long', 'short'] = 'long', + uncommon: bool = False, + locale: Locale | str | None = None, + zone_variant: Literal['generic', 'daylight', 'standard'] | None = None, + return_zone: bool = False, +) -> str: + r"""Return the localized display name for the given timezone. The timezone + may be specified using a ``datetime`` or `tzinfo` object. + + >>> from datetime import time + >>> dt = time(15, 30, tzinfo=get_timezone('America/Los_Angeles')) + >>> get_timezone_name(dt, locale='en_US') # doctest: +SKIP + u'Pacific Standard Time' + >>> get_timezone_name(dt, locale='en_US', return_zone=True) + 'America/Los_Angeles' + >>> get_timezone_name(dt, width='short', locale='en_US') # doctest: +SKIP + u'PST' + + If this function gets passed only a `tzinfo` object and no concrete + `datetime`, the returned display name is independent of daylight savings + time. This can be used for example for selecting timezones, or to set the + time of events that recur across DST changes: + + >>> tz = get_timezone('America/Los_Angeles') + >>> get_timezone_name(tz, locale='en_US') + u'Pacific Time' + >>> get_timezone_name(tz, 'short', locale='en_US') + u'PT' + + If no localized display name for the timezone is available, and the timezone + is associated with a country that uses only a single timezone, the name of + that country is returned, formatted according to the locale: + + >>> tz = get_timezone('Europe/Berlin') + >>> get_timezone_name(tz, locale='de_DE') + u'Mitteleurop\xe4ische Zeit' + >>> get_timezone_name(tz, locale='pt_BR') + u'Hor\xe1rio da Europa Central' + + On the other hand, if the country uses multiple timezones, the city is also + included in the representation: + + >>> tz = get_timezone('America/St_Johns') + >>> get_timezone_name(tz, locale='de_DE') + u'Neufundland-Zeit' + + Note that short format is currently not supported for all timezones and + all locales. This is partially because not every timezone has a short + code in every locale. In that case it currently falls back to the long + format. + + For more information see `LDML Appendix J: Time Zone Display Names + `_ + + .. versionadded:: 0.9 + + .. versionchanged:: 1.0 + Added `zone_variant` support. + + :param dt_or_tzinfo: the ``datetime`` or ``tzinfo`` object that determines + the timezone; if a ``tzinfo`` object is used, the + resulting display name will be generic, i.e. + independent of daylight savings time; if `None`, the + current date in UTC is assumed + :param width: either "long" or "short" + :param uncommon: deprecated and ignored + :param zone_variant: defines the zone variation to return. By default the + variation is defined from the datetime object + passed in. If no datetime object is passed in, the + ``'generic'`` variation is assumed. The following + values are valid: ``'generic'``, ``'daylight'`` and + ``'standard'``. + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + :param return_zone: True or False. If true then function + returns long time zone ID + """ + dt, tzinfo = _get_dt_and_tzinfo(dt_or_tzinfo) + locale = Locale.parse(locale or LC_TIME) + + zone = _get_tz_name(dt_or_tzinfo) + + if zone_variant is None: + if dt is None: + zone_variant = 'generic' + else: + dst = tzinfo.dst(dt) + zone_variant = "daylight" if dst else "standard" + else: + if zone_variant not in ('generic', 'standard', 'daylight'): + raise ValueError('Invalid zone variation') + + # Get the canonical time-zone code + zone = get_global('zone_aliases').get(zone, zone) + if return_zone: + return zone + info = locale.time_zones.get(zone, {}) + # Try explicitly translated zone names first + if width in info and zone_variant in info[width]: + return info[width][zone_variant] + + metazone = get_global('meta_zones').get(zone) + if metazone: + metazone_info = locale.meta_zones.get(metazone, {}) + if width in metazone_info: + name = metazone_info[width].get(zone_variant) + if width == 'short' and name == NO_INHERITANCE_MARKER: + # If the short form is marked no-inheritance, + # try to fall back to the long name instead. + name = metazone_info.get('long', {}).get(zone_variant) + if name: + return name + + # If we have a concrete datetime, we assume that the result can't be + # independent of daylight savings time, so we return the GMT offset + if dt is not None: + return get_timezone_gmt(dt, width=width, locale=locale) + + return get_timezone_location(dt_or_tzinfo, locale=locale) + + +def format_date( + date: datetime.date | None = None, + format: _PredefinedTimeFormat | str = 'medium', + locale: Locale | str | None = None, +) -> str: + """Return a date formatted according to the given pattern. + + >>> from datetime import date + >>> d = date(2007, 4, 1) + >>> format_date(d, locale='en_US') + u'Apr 1, 2007' + >>> format_date(d, format='full', locale='de_DE') + u'Sonntag, 1. April 2007' + + If you don't want to use the locale default formats, you can specify a + custom date pattern: + + >>> format_date(d, "EEE, MMM d, ''yy", locale='en') + u"Sun, Apr 1, '07" + + :param date: the ``date`` or ``datetime`` object; if `None`, the current + date is used + :param format: one of "full", "long", "medium", or "short", or a custom + date/time pattern + :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. + """ + if date is None: + date = datetime.date.today() + elif isinstance(date, datetime.datetime): + date = date.date() + + locale = Locale.parse(locale or LC_TIME) + if format in ('full', 'long', 'medium', 'short'): + format = get_date_format(format, locale=locale) + pattern = parse_pattern(format) + return pattern.apply(date, locale) + + +def format_datetime( + datetime: _Instant = None, + format: _PredefinedTimeFormat | str = 'medium', + tzinfo: datetime.tzinfo | None = None, + locale: Locale | str | None = None, +) -> str: + r"""Return a date formatted according to the given pattern. + + >>> from datetime import datetime + >>> dt = datetime(2007, 4, 1, 15, 30) + >>> format_datetime(dt, locale='en_US') + u'Apr 1, 2007, 3:30:00\u202fPM' + + For any pattern requiring the display of the timezone: + + >>> format_datetime(dt, 'full', tzinfo=get_timezone('Europe/Paris'), + ... locale='fr_FR') + 'dimanche 1 avril 2007, 17:30:00 heure d’été d’Europe centrale' + >>> format_datetime(dt, "yyyy.MM.dd G 'at' HH:mm:ss zzz", + ... tzinfo=get_timezone('US/Eastern'), locale='en') + u'2007.04.01 AD at 11:30:00 EDT' + + :param datetime: the `datetime` object; if `None`, the current date and + time is used + :param format: one of "full", "long", "medium", or "short", or a custom + date/time pattern + :param tzinfo: the timezone to apply to the time for display + :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. + """ + datetime = _ensure_datetime_tzinfo(_get_datetime(datetime), tzinfo) + + locale = Locale.parse(locale or LC_TIME) + if format in ('full', 'long', 'medium', 'short'): + return get_datetime_format(format, locale=locale) \ + .replace("'", "") \ + .replace('{0}', format_time(datetime, format, tzinfo=None, + locale=locale)) \ + .replace('{1}', format_date(datetime, format, locale=locale)) + else: + return parse_pattern(format).apply(datetime, locale) + + +def format_time( + time: datetime.time | datetime.datetime | float | None = None, + format: _PredefinedTimeFormat | str = 'medium', + tzinfo: datetime.tzinfo | None = None, + locale: Locale | str | None = None, +) -> str: + r"""Return a time formatted according to the given pattern. + + >>> from datetime import datetime, time + >>> t = time(15, 30) + >>> format_time(t, locale='en_US') + u'3:30:00\u202fPM' + >>> format_time(t, format='short', locale='de_DE') + u'15:30' + + If you don't want to use the locale default formats, you can specify a + custom time pattern: + + >>> format_time(t, "hh 'o''clock' a", locale='en') + u"03 o'clock PM" + + For any pattern requiring the display of the time-zone a + timezone has to be specified explicitly: + + >>> t = datetime(2007, 4, 1, 15, 30) + >>> tzinfo = get_timezone('Europe/Paris') + >>> t = _localize(tzinfo, t) + >>> format_time(t, format='full', tzinfo=tzinfo, locale='fr_FR') + '15:30:00 heure d’été d’Europe centrale' + >>> format_time(t, "hh 'o''clock' a, zzzz", tzinfo=get_timezone('US/Eastern'), + ... locale='en') + u"09 o'clock AM, Eastern Daylight Time" + + As that example shows, when this function gets passed a + ``datetime.datetime`` value, the actual time in the formatted string is + adjusted to the timezone specified by the `tzinfo` parameter. If the + ``datetime`` is "naive" (i.e. it has no associated timezone information), + it is assumed to be in UTC. + + These timezone calculations are **not** performed if the value is of type + ``datetime.time``, as without date information there's no way to determine + what a given time would translate to in a different timezone without + information about whether daylight savings time is in effect or not. This + means that time values are left as-is, and the value of the `tzinfo` + parameter is only used to display the timezone name if needed: + + >>> t = time(15, 30) + >>> format_time(t, format='full', tzinfo=get_timezone('Europe/Paris'), + ... locale='fr_FR') # doctest: +SKIP + u'15:30:00 heure normale d\u2019Europe centrale' + >>> format_time(t, format='full', tzinfo=get_timezone('US/Eastern'), + ... locale='en_US') # doctest: +SKIP + u'3:30:00\u202fPM Eastern Standard Time' + + :param time: the ``time`` or ``datetime`` object; if `None`, the current + time in UTC is used + :param format: one of "full", "long", "medium", or "short", or a custom + date/time pattern + :param tzinfo: the time-zone to apply to the time for display + :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. + """ + + # get reference date for if we need to find the right timezone variant + # in the pattern + ref_date = time.date() if isinstance(time, datetime.datetime) else None + + time = _get_time(time, tzinfo) + + locale = Locale.parse(locale or LC_TIME) + if format in ('full', 'long', 'medium', 'short'): + format = get_time_format(format, locale=locale) + return parse_pattern(format).apply(time, locale, reference_date=ref_date) + + +def format_skeleton( + skeleton: str, + datetime: _Instant = None, + tzinfo: datetime.tzinfo | None = None, + fuzzy: bool = True, + locale: Locale | str | None = None, +) -> str: + r"""Return a time and/or date formatted according to the given pattern. + + The skeletons are defined in the CLDR data and provide more flexibility + than the simple short/long/medium formats, but are a bit harder to use. + The are defined using the date/time symbols without order or punctuation + and map to a suitable format for the given locale. + + >>> from datetime import datetime + >>> t = datetime(2007, 4, 1, 15, 30) + >>> format_skeleton('MMMEd', t, locale='fr') + u'dim. 1 avr.' + >>> format_skeleton('MMMEd', t, locale='en') + u'Sun, Apr 1' + >>> format_skeleton('yMMd', t, locale='fi') # yMMd is not in the Finnish locale; yMd gets used + u'1.4.2007' + >>> format_skeleton('yMMd', t, fuzzy=False, locale='fi') # yMMd is not in the Finnish locale, an error is thrown + Traceback (most recent call last): + ... + KeyError: yMMd + >>> format_skeleton('GH', t, fuzzy=True, locale='fi_FI') # GH is not in the Finnish locale and there is no close match, an error is thrown + Traceback (most recent call last): + ... + KeyError: None + + After the skeleton is resolved to a pattern `format_datetime` is called so + all timezone processing etc is the same as for that. + + :param skeleton: A date time skeleton as defined in the cldr data. + :param datetime: the ``time`` or ``datetime`` object; if `None`, the current + time in UTC is used + :param tzinfo: the time-zone to apply to the time for display + :param fuzzy: If the skeleton is not found, allow choosing a skeleton that's + close enough to it. If there is no close match, a `KeyError` + is thrown. + :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. + """ + locale = Locale.parse(locale or LC_TIME) + if fuzzy and skeleton not in locale.datetime_skeletons: + skeleton = match_skeleton(skeleton, locale.datetime_skeletons) + format = locale.datetime_skeletons[skeleton] + return format_datetime(datetime, format, tzinfo, locale) + + +TIMEDELTA_UNITS: tuple[tuple[str, int], ...] = ( + ('year', 3600 * 24 * 365), + ('month', 3600 * 24 * 30), + ('week', 3600 * 24 * 7), + ('day', 3600 * 24), + ('hour', 3600), + ('minute', 60), + ('second', 1), +) + + +def format_timedelta( + delta: datetime.timedelta | int, + granularity: Literal['year', 'month', 'week', 'day', 'hour', 'minute', 'second'] = 'second', + threshold: float = .85, + add_direction: bool = False, + format: Literal['narrow', 'short', 'medium', 'long'] = 'long', + locale: Locale | str | None = None, +) -> str: + """Return a time delta according to the rules of the given locale. + + >>> from datetime import timedelta + >>> format_timedelta(timedelta(weeks=12), locale='en_US') + u'3 months' + >>> format_timedelta(timedelta(seconds=1), locale='es') + u'1 segundo' + + The granularity parameter can be provided to alter the lowest unit + presented, which defaults to a second. + + >>> format_timedelta(timedelta(hours=3), granularity='day', locale='en_US') + u'1 day' + + The threshold parameter can be used to determine at which value the + presentation switches to the next higher unit. A higher threshold factor + means the presentation will switch later. For example: + + >>> format_timedelta(timedelta(hours=23), threshold=0.9, locale='en_US') + u'1 day' + >>> format_timedelta(timedelta(hours=23), threshold=1.1, locale='en_US') + u'23 hours' + + In addition directional information can be provided that informs + the user if the date is in the past or in the future: + + >>> format_timedelta(timedelta(hours=1), add_direction=True, locale='en') + u'in 1 hour' + >>> format_timedelta(timedelta(hours=-1), add_direction=True, locale='en') + u'1 hour ago' + + The format parameter controls how compact or wide the presentation is: + + >>> format_timedelta(timedelta(hours=3), format='short', locale='en') + u'3 hr' + >>> format_timedelta(timedelta(hours=3), format='narrow', locale='en') + u'3h' + + :param delta: a ``timedelta`` object representing the time difference to + format, or the delta in seconds as an `int` value + :param granularity: determines the smallest unit that should be displayed, + the value can be one of "year", "month", "week", "day", + "hour", "minute" or "second" + :param threshold: factor that determines at which point the presentation + switches to the next higher unit + :param add_direction: if this flag is set to `True` the return value will + include directional information. For instance a + positive timedelta will include the information about + it being in the future, a negative will be information + about the value being in the past. + :param format: the format, can be "narrow", "short" or "long". ( + "medium" is deprecated, currently converted to "long" to + maintain compatibility) + :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. + """ + if format not in ('narrow', 'short', 'medium', 'long'): + raise TypeError('Format must be one of "narrow", "short" or "long"') + if format == 'medium': + warnings.warn( + '"medium" value for format param of format_timedelta' + ' is deprecated. Use "long" instead', + category=DeprecationWarning, + stacklevel=2, + ) + format = 'long' + if isinstance(delta, datetime.timedelta): + seconds = int((delta.days * 86400) + delta.seconds) + else: + seconds = delta + locale = Locale.parse(locale or LC_TIME) + date_fields = locale._data["date_fields"] + unit_patterns = locale._data["unit_patterns"] + + def _iter_patterns(a_unit): + if add_direction: + # Try to find the length variant version first ("year-narrow") + # before falling back to the default. + unit_rel_patterns = (date_fields.get(f"{a_unit}-{format}") or date_fields[a_unit]) + if seconds >= 0: + yield unit_rel_patterns['future'] + else: + yield unit_rel_patterns['past'] + a_unit = f"duration-{a_unit}" + unit_pats = unit_patterns.get(a_unit, {}) + yield unit_pats.get(format) + # We do not support `` tags at all while ingesting CLDR data, + # so these aliases specified in `root.xml` are hard-coded here: + # + # + if format in ("long", "narrow"): + yield unit_pats.get("short") + + for unit, secs_per_unit in TIMEDELTA_UNITS: + value = abs(seconds) / secs_per_unit + if value >= threshold or unit == granularity: + if unit == granularity and value > 0: + value = max(1, value) + value = int(round(value)) + plural_form = locale.plural_form(value) + pattern = None + for patterns in _iter_patterns(unit): + if patterns is not None: + pattern = patterns.get(plural_form) or patterns.get('other') + if pattern: + break + # This really should not happen + if pattern is None: + return '' + return pattern.replace('{0}', str(value)) + + return '' + + +def _format_fallback_interval( + start: _Instant, + end: _Instant, + skeleton: str | None, + tzinfo: datetime.tzinfo | None, + locale: Locale, +) -> str: + if skeleton in locale.datetime_skeletons: # Use the given skeleton + format = lambda dt: format_skeleton(skeleton, dt, tzinfo, locale=locale) + elif all((isinstance(d, datetime.date) and not isinstance(d, datetime.datetime)) for d in (start, end)): # Both are just dates + format = lambda dt: format_date(dt, locale=locale) + elif all((isinstance(d, datetime.time) and not isinstance(d, datetime.date)) for d in (start, end)): # Both are times + format = lambda dt: format_time(dt, tzinfo=tzinfo, locale=locale) + else: + format = lambda dt: format_datetime(dt, tzinfo=tzinfo, locale=locale) + + formatted_start = format(start) + formatted_end = format(end) + + if formatted_start == formatted_end: + return format(start) + + return ( + locale.interval_formats.get(None, "{0}-{1}"). + replace("{0}", formatted_start). + replace("{1}", formatted_end) + ) + + +def format_interval( + start: _Instant, + end: _Instant, + skeleton: str | None = None, + tzinfo: datetime.tzinfo | None = None, + fuzzy: bool = True, + locale: Locale | str | None = None, +) -> str: + """ + Format an interval between two instants according to the locale's rules. + + >>> from datetime import date, time + >>> format_interval(date(2016, 1, 15), date(2016, 1, 17), "yMd", locale="fi") + u'15.\u201317.1.2016' + + >>> format_interval(time(12, 12), time(16, 16), "Hm", locale="en_GB") + '12:12\u201316:16' + + >>> format_interval(time(5, 12), time(16, 16), "hm", locale="en_US") + '5:12\u202fAM\u2009–\u20094:16\u202fPM' + + >>> format_interval(time(16, 18), time(16, 24), "Hm", locale="it") + '16:18\u201316:24' + + If the start instant equals the end instant, the interval is formatted like the instant. + + >>> format_interval(time(16, 18), time(16, 18), "Hm", locale="it") + '16:18' + + Unknown skeletons fall back to "default" formatting. + + >>> format_interval(date(2015, 1, 1), date(2017, 1, 1), "wzq", locale="ja") + '2015/01/01\uff5e2017/01/01' + + >>> format_interval(time(16, 18), time(16, 24), "xxx", locale="ja") + '16:18:00\uff5e16:24:00' + + >>> format_interval(date(2016, 1, 15), date(2016, 1, 17), "xxx", locale="de") + '15.01.2016\u2009–\u200917.01.2016' + + :param start: First instant (datetime/date/time) + :param end: Second instant (datetime/date/time) + :param skeleton: The "skeleton format" to use for formatting. + :param tzinfo: tzinfo to use (if none is already attached) + :param fuzzy: If the skeleton is not found, allow choosing a skeleton that's + close enough to it. + :param locale: A locale object or identifier. Defaults to the system time locale. + :return: Formatted interval + """ + locale = Locale.parse(locale or LC_TIME) + + # NB: The quote comments below are from the algorithm description in + # https://www.unicode.org/reports/tr35/tr35-dates.html#intervalFormats + + # > Look for the intervalFormatItem element that matches the "skeleton", + # > starting in the current locale and then following the locale fallback + # > chain up to, but not including root. + + interval_formats = locale.interval_formats + + if skeleton not in interval_formats or not skeleton: + # > If no match was found from the previous step, check what the closest + # > match is in the fallback locale chain, as in availableFormats. That + # > is, this allows for adjusting the string value field's width, + # > including adjusting between "MMM" and "MMMM", and using different + # > variants of the same field, such as 'v' and 'z'. + if skeleton and fuzzy: + skeleton = match_skeleton(skeleton, interval_formats) + else: + skeleton = None + if not skeleton: # Still no match whatsoever? + # > Otherwise, format the start and end datetime using the fallback pattern. + return _format_fallback_interval(start, end, skeleton, tzinfo, locale) + + skel_formats = interval_formats[skeleton] + + if start == end: + return format_skeleton(skeleton, start, tzinfo, fuzzy=fuzzy, locale=locale) + + start = _ensure_datetime_tzinfo(_get_datetime(start), tzinfo=tzinfo) + end = _ensure_datetime_tzinfo(_get_datetime(end), tzinfo=tzinfo) + + start_fmt = DateTimeFormat(start, locale=locale) + end_fmt = DateTimeFormat(end, locale=locale) + + # > If a match is found from previous steps, compute the calendar field + # > with the greatest difference between start and end datetime. If there + # > is no difference among any of the fields in the pattern, format as a + # > single date using availableFormats, and return. + + for field in PATTERN_CHAR_ORDER: # These are in largest-to-smallest order + if field in skel_formats and start_fmt.extract(field) != end_fmt.extract(field): + # > If there is a match, use the pieces of the corresponding pattern to + # > format the start and end datetime, as above. + return "".join( + parse_pattern(pattern).apply(instant, locale) + for pattern, instant + in zip(skel_formats[field], (start, end)) + ) + + # > Otherwise, format the start and end datetime using the fallback pattern. + + return _format_fallback_interval(start, end, skeleton, tzinfo, locale) + + +def get_period_id( + time: _Instant, + tzinfo: datetime.tzinfo | None = None, + type: Literal['selection'] | None = None, + locale: Locale | str | None = None, +) -> str: + """ + Get the day period ID for a given time. + + This ID can be used as a key for the period name dictionary. + + >>> from datetime import time + >>> get_period_names(locale="de")[get_period_id(time(7, 42), locale="de")] + u'Morgen' + + >>> get_period_id(time(0), locale="en_US") + u'midnight' + + >>> get_period_id(time(0), type="selection", locale="en_US") + u'night1' + + :param time: The time to inspect. + :param tzinfo: The timezone for the time. See ``format_time``. + :param type: The period type to use. Either "selection" or None. + The selection type is used for selecting among phrases such as + “Your email arrived yesterday evening” or “Your email arrived last night”. + :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. + :return: period ID. Something is always returned -- even if it's just "am" or "pm". + """ + time = _get_time(time, tzinfo) + seconds_past_midnight = int(time.hour * 60 * 60 + time.minute * 60 + time.second) + locale = Locale.parse(locale or LC_TIME) + + # The LDML rules state that the rules may not overlap, so iterating in arbitrary + # order should be alright, though `at` periods should be preferred. + rulesets = locale.day_period_rules.get(type, {}).items() + + for rule_id, rules in rulesets: + for rule in rules: + if "at" in rule and rule["at"] == seconds_past_midnight: + return rule_id + + for rule_id, rules in rulesets: + for rule in rules: + if "from" in rule and "before" in rule: + if rule["from"] < rule["before"]: + if rule["from"] <= seconds_past_midnight < rule["before"]: + return rule_id + else: + # e.g. from="21:00" before="06:00" + if rule["from"] <= seconds_past_midnight < 86400 or \ + 0 <= seconds_past_midnight < rule["before"]: + return rule_id + + start_ok = end_ok = False + + if "from" in rule and seconds_past_midnight >= rule["from"]: + start_ok = True + if "to" in rule and seconds_past_midnight <= rule["to"]: + # This rule type does not exist in the present CLDR data; + # excuse the lack of test coverage. + end_ok = True + if "before" in rule and seconds_past_midnight < rule["before"]: + end_ok = True + if "after" in rule: + raise NotImplementedError("'after' is deprecated as of CLDR 29.") + + if start_ok and end_ok: + return rule_id + + if seconds_past_midnight < 43200: + return "am" + else: + return "pm" + + +class ParseError(ValueError): + pass + + +def parse_date( + string: str, + locale: Locale | str | None = None, + format: _PredefinedTimeFormat | str = 'medium', +) -> datetime.date: + """Parse a date from a string. + + If an explicit format is provided, it is used to parse the date. + + >>> parse_date('01.04.2004', format='dd.MM.yyyy') + datetime.date(2004, 4, 1) + + If no format is given, or if it is one of "full", "long", "medium", + or "short", the function first tries to interpret the string as + ISO-8601 date format and then uses the date format for the locale + as a hint to determine the order in which the date fields appear in + the string. + + >>> parse_date('4/1/04', locale='en_US') + datetime.date(2004, 4, 1) + >>> parse_date('01.04.2004', locale='de_DE') + datetime.date(2004, 4, 1) + >>> parse_date('2004-04-01', locale='en_US') + datetime.date(2004, 4, 1) + >>> parse_date('2004-04-01', locale='de_DE') + datetime.date(2004, 4, 1) + >>> parse_date('01.04.04', locale='de_DE', format='short') + datetime.date(2004, 4, 1) + + :param string: the string containing the date + :param locale: a `Locale` object or a locale identifier + :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. + :param format: the format to use, either an explicit date format, + or one of "full", "long", "medium", or "short" + (see ``get_time_format``) + """ + numbers = re.findall(r'(\d+)', string) + if not numbers: + raise ParseError("No numbers were found in input") + + use_predefined_format = format in ('full', 'long', 'medium', 'short') + # we try ISO-8601 format first, meaning similar to formats + # extended YYYY-MM-DD or basic YYYYMMDD + iso_alike = re.match(r'^(\d{4})-?([01]\d)-?([0-3]\d)$', + string, flags=re.ASCII) # allow only ASCII digits + if iso_alike and use_predefined_format: + try: + return datetime.date(*map(int, iso_alike.groups())) + except ValueError: + pass # a locale format might fit better, so let's continue + + if use_predefined_format: + fmt = get_date_format(format=format, locale=locale) + else: + fmt = parse_pattern(format) + format_str = fmt.pattern.lower() + year_idx = format_str.index('y') + month_idx = format_str.find('m') + if month_idx < 0: + month_idx = format_str.index('l') + day_idx = format_str.index('d') + + indexes = sorted([(year_idx, 'Y'), (month_idx, 'M'), (day_idx, 'D')]) + indexes = {item[1]: idx for idx, item in enumerate(indexes)} + + # FIXME: this currently only supports numbers, but should also support month + # names, both in the requested locale, and english + + year = numbers[indexes['Y']] + year = 2000 + int(year) if len(year) == 2 else int(year) + month = int(numbers[indexes['M']]) + day = int(numbers[indexes['D']]) + if month > 12: + month, day = day, month + return datetime.date(year, month, day) + + +def parse_time( + string: str, + locale: Locale | str | None = None, + format: _PredefinedTimeFormat | str = 'medium', +) -> datetime.time: + """Parse a time from a string. + + This function uses the time format for the locale as a hint to determine + the order in which the time fields appear in the string. + + If an explicit format is provided, the function will use it to parse + the time instead. + + >>> parse_time('15:30:00', locale='en_US') + datetime.time(15, 30) + >>> parse_time('15:30:00', format='H:mm:ss') + datetime.time(15, 30) + + :param string: the string containing the time + :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. + :param format: the format to use, either an explicit time format, + or one of "full", "long", "medium", or "short" + (see ``get_time_format``) + :return: the parsed time + :rtype: `time` + """ + numbers = re.findall(r'(\d+)', string) + if not numbers: + raise ParseError("No numbers were found in input") + + # TODO: try ISO format first? + if format in ('full', 'long', 'medium', 'short'): + fmt = get_time_format(format=format, locale=locale) + else: + fmt = parse_pattern(format) + format_str = fmt.pattern.lower() + hour_idx = format_str.find('h') + if hour_idx < 0: + hour_idx = format_str.index('k') + min_idx = format_str.index('m') + # format might not contain seconds + if (sec_idx := format_str.find('s')) < 0: + sec_idx = math.inf + + indexes = sorted([(hour_idx, 'H'), (min_idx, 'M'), (sec_idx, 'S')]) + indexes = {item[1]: idx for idx, item in enumerate(indexes)} + + # TODO: support time zones + + # Check if the format specifies a period to be used; + # if it does, look for 'pm' to figure out an offset. + hour_offset = 0 + if 'a' in format_str and 'pm' in string.lower(): + hour_offset = 12 + + # Parse up to three numbers from the string. + minute = second = 0 + hour = int(numbers[indexes['H']]) + hour_offset + if len(numbers) > 1: + minute = int(numbers[indexes['M']]) + if len(numbers) > 2: + second = int(numbers[indexes['S']]) + return datetime.time(hour, minute, second) + + +class DateTimePattern: + + def __init__(self, pattern: str, format: DateTimeFormat): + self.pattern = pattern + self.format = format + + def __repr__(self) -> str: + return f"<{type(self).__name__} {self.pattern!r}>" + + def __str__(self) -> str: + pat = self.pattern + return pat + + def __mod__(self, other: DateTimeFormat) -> str: + if not isinstance(other, DateTimeFormat): + return NotImplemented + return self.format % other + + def apply( + self, + datetime: datetime.date | datetime.time, + locale: Locale | str | None, + reference_date: datetime.date | None = None, + ) -> str: + return self % DateTimeFormat(datetime, locale, reference_date) + + +class DateTimeFormat: + + def __init__( + self, + value: datetime.date | datetime.time, + locale: Locale | str, + reference_date: datetime.date | None = None, + ) -> None: + assert isinstance(value, (datetime.date, datetime.datetime, datetime.time)) + if isinstance(value, (datetime.datetime, datetime.time)) and value.tzinfo is None: + value = value.replace(tzinfo=UTC) + self.value = value + self.locale = Locale.parse(locale) + self.reference_date = reference_date + + def __getitem__(self, name: str) -> str: + char = name[0] + num = len(name) + if char == 'G': + return self.format_era(char, num) + elif char in ('y', 'Y', 'u'): + return self.format_year(char, num) + elif char in ('Q', 'q'): + return self.format_quarter(char, num) + elif char in ('M', 'L'): + return self.format_month(char, num) + elif char in ('w', 'W'): + return self.format_week(char, num) + elif char == 'd': + return self.format(self.value.day, num) + elif char == 'D': + return self.format_day_of_year(num) + elif char == 'F': + return self.format_day_of_week_in_month() + elif char in ('E', 'e', 'c'): + return self.format_weekday(char, num) + elif char in ('a', 'b', 'B'): + return self.format_period(char, num) + elif char == 'h': + if self.value.hour % 12 == 0: + return self.format(12, num) + else: + return self.format(self.value.hour % 12, num) + elif char == 'H': + return self.format(self.value.hour, num) + elif char == 'K': + return self.format(self.value.hour % 12, num) + elif char == 'k': + if self.value.hour == 0: + return self.format(24, num) + else: + return self.format(self.value.hour, num) + elif char == 'm': + return self.format(self.value.minute, num) + elif char == 's': + return self.format(self.value.second, num) + elif char == 'S': + return self.format_frac_seconds(num) + elif char == 'A': + return self.format_milliseconds_in_day(num) + elif char in ('z', 'Z', 'v', 'V', 'x', 'X', 'O'): + return self.format_timezone(char, num) + else: + raise KeyError(f"Unsupported date/time field {char!r}") + + def extract(self, char: str) -> int: + char = str(char)[0] + if char == 'y': + return self.value.year + elif char == 'M': + return self.value.month + elif char == 'd': + return self.value.day + elif char == 'H': + return self.value.hour + elif char == 'h': + return self.value.hour % 12 or 12 + elif char == 'm': + return self.value.minute + elif char == 'a': + return int(self.value.hour >= 12) # 0 for am, 1 for pm + else: + raise NotImplementedError(f"Not implemented: extracting {char!r} from {self.value!r}") + + def format_era(self, char: str, num: int) -> str: + width = {3: 'abbreviated', 4: 'wide', 5: 'narrow'}[max(3, num)] + era = int(self.value.year >= 0) + return get_era_names(width, self.locale)[era] + + def format_year(self, char: str, num: int) -> str: + value = self.value.year + if char.isupper(): + month = self.value.month + if month == 1 and self.value.day < 7 and self.get_week_of_year() >= 52: + value -= 1 + elif month == 12 and self.value.day > 25 and self.get_week_of_year() <= 2: + value += 1 + year = self.format(value, num) + if num == 2: + year = year[-2:] + return year + + def format_quarter(self, char: str, num: int) -> str: + quarter = (self.value.month - 1) // 3 + 1 + if num <= 2: + return '%0*d' % (num, quarter) + width = {3: 'abbreviated', 4: 'wide', 5: 'narrow'}[num] + context = {'Q': 'format', 'q': 'stand-alone'}[char] + return get_quarter_names(width, context, self.locale)[quarter] + + def format_month(self, char: str, num: int) -> str: + if num <= 2: + return '%0*d' % (num, self.value.month) + width = {3: 'abbreviated', 4: 'wide', 5: 'narrow'}[num] + context = {'M': 'format', 'L': 'stand-alone'}[char] + return get_month_names(width, context, self.locale)[self.value.month] + + def format_week(self, char: str, num: int) -> str: + if char.islower(): # week of year + week = self.get_week_of_year() + return self.format(week, num) + else: # week of month + week = self.get_week_of_month() + return str(week) + + def format_weekday(self, char: str = 'E', num: int = 4) -> str: + """ + Return weekday from parsed datetime according to format pattern. + + >>> from datetime import date + >>> format = DateTimeFormat(date(2016, 2, 28), Locale.parse('en_US')) + >>> format.format_weekday() + u'Sunday' + + 'E': Day of week - Use one through three letters for the abbreviated day name, four for the full (wide) name, + five for the narrow name, or six for the short name. + >>> format.format_weekday('E',2) + u'Sun' + + 'e': Local day of week. Same as E except adds a numeric value that will depend on the local starting day of the + week, using one or two letters. For this example, Monday is the first day of the week. + >>> format.format_weekday('e',2) + '01' + + 'c': Stand-Alone local day of week - Use one letter for the local numeric value (same as 'e'), three for the + abbreviated day name, four for the full (wide) name, five for the narrow name, or six for the short name. + >>> format.format_weekday('c',1) + '1' + + :param char: pattern format character ('e','E','c') + :param num: count of format character + + """ + if num < 3: + if char.islower(): + value = 7 - self.locale.first_week_day + self.value.weekday() + return self.format(value % 7 + 1, num) + num = 3 + weekday = self.value.weekday() + width = {3: 'abbreviated', 4: 'wide', 5: 'narrow', 6: 'short'}[num] + context = "stand-alone" if char == "c" else "format" + return get_day_names(width, context, self.locale)[weekday] + + def format_day_of_year(self, num: int) -> str: + return self.format(self.get_day_of_year(), num) + + def format_day_of_week_in_month(self) -> str: + return str((self.value.day - 1) // 7 + 1) + + def format_period(self, char: str, num: int) -> str: + """ + Return period from parsed datetime according to format pattern. + + >>> from datetime import datetime, time + >>> format = DateTimeFormat(time(13, 42), 'fi_FI') + >>> format.format_period('a', 1) + u'ip.' + >>> format.format_period('b', 1) + u'iltap.' + >>> format.format_period('b', 4) + u'iltapäivä' + >>> format.format_period('B', 4) + u'iltapäivällä' + >>> format.format_period('B', 5) + u'ip.' + + >>> format = DateTimeFormat(datetime(2022, 4, 28, 6, 27), 'zh_Hant') + >>> format.format_period('a', 1) + u'上午' + >>> format.format_period('B', 1) + u'清晨' + + :param char: pattern format character ('a', 'b', 'B') + :param num: count of format character + + """ + widths = [{3: 'abbreviated', 4: 'wide', 5: 'narrow'}[max(3, num)], + 'wide', 'narrow', 'abbreviated'] + if char == 'a': + period = 'pm' if self.value.hour >= 12 else 'am' + context = 'format' + else: + period = get_period_id(self.value, locale=self.locale) + context = 'format' if char == 'B' else 'stand-alone' + for width in widths: + period_names = get_period_names(context=context, width=width, locale=self.locale) + if period in period_names: + return period_names[period] + raise ValueError(f"Could not format period {period} in {self.locale}") + + def format_frac_seconds(self, num: int) -> str: + """ Return fractional seconds. + + Rounds the time's microseconds to the precision given by the number \ + of digits passed in. + """ + value = self.value.microsecond / 1000000 + return self.format(round(value, num) * 10**num, num) + + def format_milliseconds_in_day(self, num): + msecs = self.value.microsecond // 1000 + self.value.second * 1000 + \ + self.value.minute * 60000 + self.value.hour * 3600000 + return self.format(msecs, num) + + def format_timezone(self, char: str, num: int) -> str: + width = {3: 'short', 4: 'long', 5: 'iso8601'}[max(3, num)] + + # It could be that we only receive a time to format, but also have a + # reference date which is important to distinguish between timezone + # variants (summer/standard time) + value = self.value + if self.reference_date: + value = datetime.datetime.combine(self.reference_date, self.value) + + if char == 'z': + return get_timezone_name(value, width, locale=self.locale) + elif char == 'Z': + if num == 5: + return get_timezone_gmt(value, width, locale=self.locale, return_z=True) + return get_timezone_gmt(value, width, locale=self.locale) + elif char == 'O': + if num == 4: + return get_timezone_gmt(value, width, locale=self.locale) + # TODO: To add support for O:1 + elif char == 'v': + return get_timezone_name(value.tzinfo, width, + locale=self.locale) + elif char == 'V': + if num == 1: + return get_timezone_name(value.tzinfo, width, + uncommon=True, locale=self.locale) + elif num == 2: + return get_timezone_name(value.tzinfo, locale=self.locale, return_zone=True) + elif num == 3: + return get_timezone_location(value.tzinfo, locale=self.locale, return_city=True) + return get_timezone_location(value.tzinfo, locale=self.locale) + # Included additional elif condition to add support for 'Xx' in timezone format + elif char == 'X': + if num == 1: + return get_timezone_gmt(value, width='iso8601_short', locale=self.locale, + return_z=True) + elif num in (2, 4): + return get_timezone_gmt(value, width='short', locale=self.locale, + return_z=True) + elif num in (3, 5): + return get_timezone_gmt(value, width='iso8601', locale=self.locale, + return_z=True) + elif char == 'x': + if num == 1: + return get_timezone_gmt(value, width='iso8601_short', locale=self.locale) + elif num in (2, 4): + return get_timezone_gmt(value, width='short', locale=self.locale) + elif num in (3, 5): + return get_timezone_gmt(value, width='iso8601', locale=self.locale) + + def format(self, value: SupportsInt, length: int) -> str: + return '%0*d' % (length, value) + + def get_day_of_year(self, date: datetime.date | None = None) -> int: + if date is None: + date = self.value + return (date - date.replace(month=1, day=1)).days + 1 + + def get_week_of_year(self) -> int: + """Return the week of the year.""" + day_of_year = self.get_day_of_year(self.value) + week = self.get_week_number(day_of_year) + if week == 0: + date = datetime.date(self.value.year - 1, 12, 31) + week = self.get_week_number(self.get_day_of_year(date), + date.weekday()) + elif week > 52: + weekday = datetime.date(self.value.year + 1, 1, 1).weekday() + if self.get_week_number(1, weekday) == 1 and \ + 32 - (weekday - self.locale.first_week_day) % 7 <= self.value.day: + week = 1 + return week + + def get_week_of_month(self) -> int: + """Return the week of the month.""" + return self.get_week_number(self.value.day) + + def get_week_number(self, day_of_period: int, day_of_week: int | None = None) -> int: + """Return the number of the week of a day within a period. This may be + the week number in a year or the week number in a month. + + Usually this will return a value equal to or greater than 1, but if the + first week of the period is so short that it actually counts as the last + week of the previous period, this function will return 0. + + >>> date = datetime.date(2006, 1, 8) + >>> DateTimeFormat(date, 'de_DE').get_week_number(6) + 1 + >>> DateTimeFormat(date, 'en_US').get_week_number(6) + 2 + + :param day_of_period: the number of the day in the period (usually + either the day of month or the day of year) + :param day_of_week: the week day; if omitted, the week day of the + current date is assumed + """ + if day_of_week is None: + day_of_week = self.value.weekday() + first_day = (day_of_week - self.locale.first_week_day - + day_of_period + 1) % 7 + if first_day < 0: + first_day += 7 + week_number = (day_of_period + first_day - 1) // 7 + if 7 - first_day >= self.locale.min_week_days: + week_number += 1 + return week_number + + +PATTERN_CHARS: dict[str, list[int] | None] = { + 'G': [1, 2, 3, 4, 5], # era + 'y': None, 'Y': None, 'u': None, # year + 'Q': [1, 2, 3, 4, 5], 'q': [1, 2, 3, 4, 5], # quarter + 'M': [1, 2, 3, 4, 5], 'L': [1, 2, 3, 4, 5], # month + 'w': [1, 2], 'W': [1], # week + 'd': [1, 2], 'D': [1, 2, 3], 'F': [1], 'g': None, # day + 'E': [1, 2, 3, 4, 5, 6], 'e': [1, 2, 3, 4, 5, 6], 'c': [1, 3, 4, 5, 6], # week day + 'a': [1, 2, 3, 4, 5], 'b': [1, 2, 3, 4, 5], 'B': [1, 2, 3, 4, 5], # period + 'h': [1, 2], 'H': [1, 2], 'K': [1, 2], 'k': [1, 2], # hour + 'm': [1, 2], # minute + 's': [1, 2], 'S': None, 'A': None, # second + 'z': [1, 2, 3, 4], 'Z': [1, 2, 3, 4, 5], 'O': [1, 4], 'v': [1, 4], # zone + 'V': [1, 2, 3, 4], 'x': [1, 2, 3, 4, 5], 'X': [1, 2, 3, 4, 5], # zone +} + +#: The pattern characters declared in the Date Field Symbol Table +#: (https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table) +#: in order of decreasing magnitude. +PATTERN_CHAR_ORDER = "GyYuUQqMLlwWdDFgEecabBChHKkjJmsSAzZOvVXx" + + +def parse_pattern(pattern: str | DateTimePattern) -> DateTimePattern: + """Parse date, time, and datetime format patterns. + + >>> parse_pattern("MMMMd").format + u'%(MMMM)s%(d)s' + >>> parse_pattern("MMM d, yyyy").format + u'%(MMM)s %(d)s, %(yyyy)s' + + Pattern can contain literal strings in single quotes: + + >>> parse_pattern("H:mm' Uhr 'z").format + u'%(H)s:%(mm)s Uhr %(z)s' + + An actual single quote can be used by using two adjacent single quote + characters: + + >>> parse_pattern("hh' o''clock'").format + u"%(hh)s o'clock" + + :param pattern: the formatting pattern to parse + """ + if isinstance(pattern, DateTimePattern): + return pattern + return _cached_parse_pattern(pattern) + + +@lru_cache(maxsize=1024) +def _cached_parse_pattern(pattern: str) -> DateTimePattern: + result = [] + + for tok_type, tok_value in tokenize_pattern(pattern): + if tok_type == "chars": + result.append(tok_value.replace('%', '%%')) + elif tok_type == "field": + fieldchar, fieldnum = tok_value + limit = PATTERN_CHARS[fieldchar] + if limit and fieldnum not in limit: + raise ValueError(f"Invalid length for field: {fieldchar * fieldnum!r}") + result.append('%%(%s)s' % (fieldchar * fieldnum)) + else: + raise NotImplementedError(f"Unknown token type: {tok_type}") + return DateTimePattern(pattern, ''.join(result)) + + +def tokenize_pattern(pattern: str) -> list[tuple[str, str | tuple[str, int]]]: + """ + Tokenize date format patterns. + + Returns a list of (token_type, token_value) tuples. + + ``token_type`` may be either "chars" or "field". + + For "chars" tokens, the value is the literal value. + + For "field" tokens, the value is a tuple of (field character, repetition count). + + :param pattern: Pattern string + :type pattern: str + :rtype: list[tuple] + """ + result = [] + quotebuf = None + charbuf = [] + fieldchar = [''] + fieldnum = [0] + + def append_chars(): + result.append(('chars', ''.join(charbuf).replace('\0', "'"))) + del charbuf[:] + + def append_field(): + result.append(('field', (fieldchar[0], fieldnum[0]))) + fieldchar[0] = '' + fieldnum[0] = 0 + + for char in pattern.replace("''", '\0'): + if quotebuf is None: + if char == "'": # quote started + if fieldchar[0]: + append_field() + elif charbuf: + append_chars() + quotebuf = [] + elif char in PATTERN_CHARS: + if charbuf: + append_chars() + if char == fieldchar[0]: + fieldnum[0] += 1 + else: + if fieldchar[0]: + append_field() + fieldchar[0] = char + fieldnum[0] = 1 + else: + if fieldchar[0]: + append_field() + charbuf.append(char) + + elif quotebuf is not None: + if char == "'": # end of quote + charbuf.extend(quotebuf) + quotebuf = None + else: # inside quote + quotebuf.append(char) + + if fieldchar[0]: + append_field() + elif charbuf: + append_chars() + + return result + + +def untokenize_pattern(tokens: Iterable[tuple[str, str | tuple[str, int]]]) -> str: + """ + Turn a date format pattern token stream back into a string. + + This is the reverse operation of ``tokenize_pattern``. + + :type tokens: Iterable[tuple] + :rtype: str + """ + output = [] + for tok_type, tok_value in tokens: + if tok_type == "field": + output.append(tok_value[0] * tok_value[1]) + elif tok_type == "chars": + if not any(ch in PATTERN_CHARS for ch in tok_value): # No need to quote + output.append(tok_value) + else: + output.append("'%s'" % tok_value.replace("'", "''")) + return "".join(output) + + +def split_interval_pattern(pattern: str) -> list[str]: + """ + Split an interval-describing datetime pattern into multiple pieces. + + > The pattern is then designed to be broken up into two pieces by determining the first repeating field. + - https://www.unicode.org/reports/tr35/tr35-dates.html#intervalFormats + + >>> split_interval_pattern(u'E d.M. \u2013 E d.M.') + [u'E d.M. \u2013 ', 'E d.M.'] + >>> split_interval_pattern("Y 'text' Y 'more text'") + ["Y 'text '", "Y 'more text'"] + >>> split_interval_pattern(u"E, MMM d \u2013 E") + [u'E, MMM d \u2013 ', u'E'] + >>> split_interval_pattern("MMM d") + ['MMM d'] + >>> split_interval_pattern("y G") + ['y G'] + >>> split_interval_pattern(u"MMM d \u2013 d") + [u'MMM d \u2013 ', u'd'] + + :param pattern: Interval pattern string + :return: list of "subpatterns" + """ + + seen_fields = set() + parts = [[]] + + for tok_type, tok_value in tokenize_pattern(pattern): + if tok_type == "field": + if tok_value[0] in seen_fields: # Repeated field + parts.append([]) + seen_fields.clear() + seen_fields.add(tok_value[0]) + parts[-1].append((tok_type, tok_value)) + + return [untokenize_pattern(tokens) for tokens in parts] + + +def match_skeleton(skeleton: str, options: Iterable[str], allow_different_fields: bool = False) -> str | None: + """ + Find the closest match for the given datetime skeleton among the options given. + + This uses the rules outlined in the TR35 document. + + >>> match_skeleton('yMMd', ('yMd', 'yMMMd')) + 'yMd' + + >>> match_skeleton('yMMd', ('jyMMd',), allow_different_fields=True) + 'jyMMd' + + >>> match_skeleton('yMMd', ('qyMMd',), allow_different_fields=False) + + >>> match_skeleton('hmz', ('hmv',)) + 'hmv' + + :param skeleton: The skeleton to match + :type skeleton: str + :param options: An iterable of other skeletons to match against + :type options: Iterable[str] + :param allow_different_fields: Whether to allow a match that uses different fields + than the skeleton requested. + :type allow_different_fields: bool + + :return: The closest skeleton match, or if no match was found, None. + :rtype: str|None + """ + + # TODO: maybe implement pattern expansion? + + # Based on the implementation in + # https://github.com/unicode-org/icu/blob/main/icu4j/main/core/src/main/java/com/ibm/icu/text/DateIntervalInfo.java + + # Filter out falsy values and sort for stability; when `interval_formats` is passed in, there may be a None key. + options = sorted(option for option in options if option) + + if 'z' in skeleton and not any('z' in option for option in options): + skeleton = skeleton.replace('z', 'v') + if 'k' in skeleton and not any('k' in option for option in options): + skeleton = skeleton.replace('k', 'H') + if 'K' in skeleton and not any('K' in option for option in options): + skeleton = skeleton.replace('K', 'h') + if 'a' in skeleton and not any('a' in option for option in options): + skeleton = skeleton.replace('a', '') + if 'b' in skeleton and not any('b' in option for option in options): + skeleton = skeleton.replace('b', '') + + get_input_field_width = dict(t[1] for t in tokenize_pattern(skeleton) if t[0] == "field").get + best_skeleton = None + best_distance = None + for option in options: + get_opt_field_width = dict(t[1] for t in tokenize_pattern(option) if t[0] == "field").get + distance = 0 + for field in PATTERN_CHARS: + input_width = get_input_field_width(field, 0) + opt_width = get_opt_field_width(field, 0) + if input_width == opt_width: + continue + if opt_width == 0 or input_width == 0: + if not allow_different_fields: # This one is not okay + option = None + break + distance += 0x1000 # Magic weight constant for "entirely different fields" + elif field == 'M' and ((input_width > 2 and opt_width <= 2) or (input_width <= 2 and opt_width > 2)): + distance += 0x100 # Magic weight for "text turns into a number" + else: + distance += abs(input_width - opt_width) + + if not option: # We lost the option along the way (probably due to "allow_different_fields") + continue + + if not best_skeleton or distance < best_distance: + best_skeleton = option + best_distance = distance + + if distance == 0: # Found a perfect match! + break + + return best_skeleton diff --git a/.venv/lib/python3.9/site-packages/babel/global.dat b/.venv/lib/python3.9/site-packages/babel/global.dat new file mode 100644 index 00000000..4c7f1506 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/global.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/languages.py b/.venv/lib/python3.9/site-packages/babel/languages.py new file mode 100644 index 00000000..564f555d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/languages.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from babel.core import get_global + + +def get_official_languages(territory: str, regional: bool = False, de_facto: bool = False) -> tuple[str, ...]: + """ + Get the official language(s) for the given territory. + + The language codes, if any are known, are returned in order of descending popularity. + + If the `regional` flag is set, then languages which are regionally official are also returned. + + If the `de_facto` flag is set, then languages which are "de facto" official are also returned. + + .. warning:: Note that the data is as up to date as the current version of the CLDR used + by Babel. If you need scientifically accurate information, use another source! + + :param territory: Territory code + :type territory: str + :param regional: Whether to return regionally official languages too + :type regional: bool + :param de_facto: Whether to return de-facto official languages too + :type de_facto: bool + :return: Tuple of language codes + :rtype: tuple[str] + """ + + territory = str(territory).upper() + allowed_stati = {"official"} + if regional: + allowed_stati.add("official_regional") + if de_facto: + allowed_stati.add("de_facto_official") + + languages = get_global("territory_languages").get(territory, {}) + pairs = [ + (info['population_percent'], language) + for language, info in languages.items() + if info.get('official_status') in allowed_stati + ] + pairs.sort(reverse=True) + return tuple(lang for _, lang in pairs) + + +def get_territory_language_info(territory: str) -> dict[str, dict[str, float | str | None]]: + """ + Get a dictionary of language information for a territory. + + The dictionary is keyed by language code; the values are dicts with more information. + + The following keys are currently known for the values: + + * `population_percent`: The percentage of the territory's population speaking the + language. + * `official_status`: An optional string describing the officiality status of the language. + Known values are "official", "official_regional" and "de_facto_official". + + .. warning:: Note that the data is as up to date as the current version of the CLDR used + by Babel. If you need scientifically accurate information, use another source! + + .. note:: Note that the format of the dict returned may change between Babel versions. + + See https://www.unicode.org/cldr/charts/latest/supplemental/territory_language_information.html + + :param territory: Territory code + :type territory: str + :return: Language information dictionary + :rtype: dict[str, dict] + """ + territory = str(territory).upper() + return get_global("territory_languages").get(territory, {}).copy() diff --git a/.venv/lib/python3.9/site-packages/babel/lists.py b/.venv/lib/python3.9/site-packages/babel/lists.py new file mode 100644 index 00000000..353171c7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/lists.py @@ -0,0 +1,132 @@ +""" + babel.lists + ~~~~~~~~~~~ + + Locale dependent formatting of lists. + + The default locale for the functions in this module is determined by the + following environment variables, in that order: + + * ``LC_ALL``, and + * ``LANG`` + + :copyright: (c) 2015-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" +from __future__ import annotations + +import warnings +from collections.abc import Sequence +from typing import Literal + +from babel.core import Locale, default_locale + +_DEFAULT_LOCALE = default_locale() # TODO(3.0): Remove this. + + +def __getattr__(name): + if name == "DEFAULT_LOCALE": + warnings.warn( + "The babel.lists.DEFAULT_LOCALE constant is deprecated and will be removed.", + DeprecationWarning, + stacklevel=2, + ) + return _DEFAULT_LOCALE + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +def format_list( + lst: Sequence[str], + style: Literal['standard', 'standard-short', 'or', 'or-short', 'unit', 'unit-short', 'unit-narrow'] = 'standard', + locale: Locale | str | None = None, +) -> str: + """ + Format the items in `lst` as a list. + + >>> format_list(['apples', 'oranges', 'pears'], locale='en') + u'apples, oranges, and pears' + >>> format_list(['apples', 'oranges', 'pears'], locale='zh') + u'apples\u3001oranges\u548cpears' + >>> format_list(['omena', 'peruna', 'aplari'], style='or', locale='fi') + u'omena, peruna tai aplari' + + Not all styles are necessarily available in all locales. + The function will attempt to fall back to replacement styles according to the rules + set forth in the CLDR root XML file, and raise a ValueError if no suitable replacement + can be found. + + The following text is verbatim from the Unicode TR35-49 spec [1]. + + * standard: + A typical 'and' list for arbitrary placeholders. + eg. "January, February, and March" + * standard-short: + A short version of an 'and' list, suitable for use with short or abbreviated placeholder values. + eg. "Jan., Feb., and Mar." + * or: + A typical 'or' list for arbitrary placeholders. + eg. "January, February, or March" + * or-short: + A short version of an 'or' list. + eg. "Jan., Feb., or Mar." + * unit: + A list suitable for wide units. + eg. "3 feet, 7 inches" + * unit-short: + A list suitable for short units + eg. "3 ft, 7 in" + * unit-narrow: + A list suitable for narrow units, where space on the screen is very limited. + eg. "3′ 7″" + + [1]: https://www.unicode.org/reports/tr35/tr35-49/tr35-general.html#ListPatterns + + :param lst: a sequence of items to format in to a list + :param style: the style to format the list with. See above for description. + :param locale: the locale. Defaults to the system locale. + """ + locale = Locale.parse(locale or _DEFAULT_LOCALE) + if not lst: + return '' + if len(lst) == 1: + return lst[0] + + patterns = _resolve_list_style(locale, style) + + if len(lst) == 2 and '2' in patterns: + return patterns['2'].format(*lst) + + result = patterns['start'].format(lst[0], lst[1]) + for elem in lst[2:-1]: + result = patterns['middle'].format(result, elem) + result = patterns['end'].format(result, lst[-1]) + + return result + + +# Based on CLDR 45's root.xml file's ``es. +# The root file defines both `standard` and `or`, +# so they're always available. +# TODO: It would likely be better to use the +# babel.localedata.Alias mechanism for this, +# but I'm not quite sure how it's supposed to +# work with inheritance and data in the root. +_style_fallbacks = { + "or-narrow": ["or-short", "or"], + "or-short": ["or"], + "standard-narrow": ["standard-short", "standard"], + "standard-short": ["standard"], + "unit": ["unit-short", "standard"], + "unit-narrow": ["unit-short", "unit", "standard"], + "unit-short": ["standard"], +} + + +def _resolve_list_style(locale: Locale, style: str): + for style in (style, *(_style_fallbacks.get(style, []))): # noqa: B020 + if style in locale.list_patterns: + return locale.list_patterns[style] + raise ValueError( + f"Locale {locale} does not support list formatting style {style!r} " + f"(supported are {sorted(locale.list_patterns)})", + ) diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/LICENSE.unicode b/.venv/lib/python3.9/site-packages/babel/locale-data/LICENSE.unicode new file mode 100644 index 00000000..861b74f3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/locale-data/LICENSE.unicode @@ -0,0 +1,41 @@ +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2004-2025 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/aa.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/aa.dat new file mode 100644 index 00000000..96b76a00 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/aa.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/aa_DJ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/aa_DJ.dat new file mode 100644 index 00000000..a3cb2563 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/aa_DJ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/aa_ER.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/aa_ER.dat new file mode 100644 index 00000000..a01edfca Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/aa_ER.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/aa_ET.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/aa_ET.dat new file mode 100644 index 00000000..119b891b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/aa_ET.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ab.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ab.dat new file mode 100644 index 00000000..e1fbc6e6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ab.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ab_GE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ab_GE.dat new file mode 100644 index 00000000..15a980a7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ab_GE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/af.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/af.dat new file mode 100644 index 00000000..a1e347f6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/af.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/af_NA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/af_NA.dat new file mode 100644 index 00000000..d5ff2a31 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/af_NA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/af_ZA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/af_ZA.dat new file mode 100644 index 00000000..34779331 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/af_ZA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/agq.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/agq.dat new file mode 100644 index 00000000..a4eb919c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/agq.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/agq_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/agq_CM.dat new file mode 100644 index 00000000..57701c90 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/agq_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ak.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ak.dat new file mode 100644 index 00000000..6c937699 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ak.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ak_GH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ak_GH.dat new file mode 100644 index 00000000..a7c63dae Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ak_GH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/am.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/am.dat new file mode 100644 index 00000000..3647c436 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/am.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/am_ET.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/am_ET.dat new file mode 100644 index 00000000..050cc76c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/am_ET.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/an.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/an.dat new file mode 100644 index 00000000..1678ebce Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/an.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/an_ES.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/an_ES.dat new file mode 100644 index 00000000..a11156aa Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/an_ES.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ann.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ann.dat new file mode 100644 index 00000000..cee6e2bb Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ann.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ann_NG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ann_NG.dat new file mode 100644 index 00000000..612c3f89 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ann_NG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/apc.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/apc.dat new file mode 100644 index 00000000..644ec2a1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/apc.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/apc_SY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/apc_SY.dat new file mode 100644 index 00000000..6c5e15a7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/apc_SY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar.dat new file mode 100644 index 00000000..30da0f40 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_001.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_001.dat new file mode 100644 index 00000000..bdc0475f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_001.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_AE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_AE.dat new file mode 100644 index 00000000..f8386597 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_AE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_BH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_BH.dat new file mode 100644 index 00000000..1792ea6b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_BH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_DJ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_DJ.dat new file mode 100644 index 00000000..579056a5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_DJ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_DZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_DZ.dat new file mode 100644 index 00000000..2d61edcd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_DZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_EG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_EG.dat new file mode 100644 index 00000000..d9be03e1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_EG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_EH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_EH.dat new file mode 100644 index 00000000..df44a58b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_EH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_ER.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_ER.dat new file mode 100644 index 00000000..cd036430 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_ER.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_IL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_IL.dat new file mode 100644 index 00000000..557c47f4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_IL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_IQ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_IQ.dat new file mode 100644 index 00000000..5b3d21c4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_IQ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_JO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_JO.dat new file mode 100644 index 00000000..8bf13f24 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_JO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_KM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_KM.dat new file mode 100644 index 00000000..88f1ac48 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_KM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_KW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_KW.dat new file mode 100644 index 00000000..80762a47 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_KW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_LB.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_LB.dat new file mode 100644 index 00000000..060dfa49 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_LB.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_LY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_LY.dat new file mode 100644 index 00000000..a870e157 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_LY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_MA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_MA.dat new file mode 100644 index 00000000..ecdc22a5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_MA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_MR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_MR.dat new file mode 100644 index 00000000..7d66b5cf Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_MR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_OM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_OM.dat new file mode 100644 index 00000000..d318d99f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_OM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_PS.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_PS.dat new file mode 100644 index 00000000..2c3ef839 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_PS.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_QA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_QA.dat new file mode 100644 index 00000000..d187cc66 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_QA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_SA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_SA.dat new file mode 100644 index 00000000..ae1ecaed Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_SA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_SD.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_SD.dat new file mode 100644 index 00000000..0ce23d8a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_SD.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_SO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_SO.dat new file mode 100644 index 00000000..ed6648bc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_SO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_SS.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_SS.dat new file mode 100644 index 00000000..a2cadef7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_SS.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_SY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_SY.dat new file mode 100644 index 00000000..701de7ad Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_SY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_TD.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_TD.dat new file mode 100644 index 00000000..deb0d9c4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_TD.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_TN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_TN.dat new file mode 100644 index 00000000..46853cec Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_TN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ar_YE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_YE.dat new file mode 100644 index 00000000..8c2212b5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ar_YE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/arn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/arn.dat new file mode 100644 index 00000000..5abcc770 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/arn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/arn_CL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/arn_CL.dat new file mode 100644 index 00000000..4aadebb1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/arn_CL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/as.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/as.dat new file mode 100644 index 00000000..4f1f6cc4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/as.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/as_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/as_IN.dat new file mode 100644 index 00000000..9f24d230 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/as_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/asa.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/asa.dat new file mode 100644 index 00000000..6c4f84f4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/asa.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/asa_TZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/asa_TZ.dat new file mode 100644 index 00000000..0b2adf25 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/asa_TZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ast.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ast.dat new file mode 100644 index 00000000..11a786a0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ast.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ast_ES.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ast_ES.dat new file mode 100644 index 00000000..af96de06 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ast_ES.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/az.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/az.dat new file mode 100644 index 00000000..5ee74802 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/az.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/az_Arab.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/az_Arab.dat new file mode 100644 index 00000000..d18ccb51 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/az_Arab.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/az_Arab_IQ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/az_Arab_IQ.dat new file mode 100644 index 00000000..393175fc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/az_Arab_IQ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/az_Arab_IR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/az_Arab_IR.dat new file mode 100644 index 00000000..8df9df25 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/az_Arab_IR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/az_Arab_TR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/az_Arab_TR.dat new file mode 100644 index 00000000..99fa84c4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/az_Arab_TR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/az_Cyrl.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/az_Cyrl.dat new file mode 100644 index 00000000..2f79fbd0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/az_Cyrl.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/az_Cyrl_AZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/az_Cyrl_AZ.dat new file mode 100644 index 00000000..75ee5220 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/az_Cyrl_AZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/az_Latn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/az_Latn.dat new file mode 100644 index 00000000..88c306fe Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/az_Latn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/az_Latn_AZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/az_Latn_AZ.dat new file mode 100644 index 00000000..75ee5220 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/az_Latn_AZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ba.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ba.dat new file mode 100644 index 00000000..99bc134f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ba.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ba_RU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ba_RU.dat new file mode 100644 index 00000000..93dadc0f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ba_RU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bal.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bal.dat new file mode 100644 index 00000000..6135e5a3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bal.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bal_Arab.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bal_Arab.dat new file mode 100644 index 00000000..9fc6e82e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bal_Arab.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bal_Arab_PK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bal_Arab_PK.dat new file mode 100644 index 00000000..37f77e20 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bal_Arab_PK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bal_Latn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bal_Latn.dat new file mode 100644 index 00000000..8a7c8c09 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bal_Latn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bal_Latn_PK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bal_Latn_PK.dat new file mode 100644 index 00000000..37f77e20 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bal_Latn_PK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bas.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bas.dat new file mode 100644 index 00000000..6afbc89d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bas.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bas_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bas_CM.dat new file mode 100644 index 00000000..992e8632 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bas_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/be.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/be.dat new file mode 100644 index 00000000..160a752f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/be.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/be_BY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/be_BY.dat new file mode 100644 index 00000000..759f67a1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/be_BY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/be_TARASK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/be_TARASK.dat new file mode 100644 index 00000000..7e895362 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/be_TARASK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bem.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bem.dat new file mode 100644 index 00000000..35c138fc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bem.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bem_ZM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bem_ZM.dat new file mode 100644 index 00000000..7f393a8b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bem_ZM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bew.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bew.dat new file mode 100644 index 00000000..798011d5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bew.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bew_ID.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bew_ID.dat new file mode 100644 index 00000000..12cfbff6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bew_ID.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bez.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bez.dat new file mode 100644 index 00000000..04e40d18 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bez.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bez_TZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bez_TZ.dat new file mode 100644 index 00000000..76974eb1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bez_TZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bg.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bg.dat new file mode 100644 index 00000000..dfd5660a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bg.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bg_BG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bg_BG.dat new file mode 100644 index 00000000..b69fe979 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bg_BG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bgc.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bgc.dat new file mode 100644 index 00000000..1a3cdd55 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bgc.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bgc_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bgc_IN.dat new file mode 100644 index 00000000..bc2e0217 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bgc_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bgn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bgn.dat new file mode 100644 index 00000000..f8297eb3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bgn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bgn_AE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bgn_AE.dat new file mode 100644 index 00000000..0b17e78f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bgn_AE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bgn_AF.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bgn_AF.dat new file mode 100644 index 00000000..4062c74d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bgn_AF.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bgn_IR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bgn_IR.dat new file mode 100644 index 00000000..99a1b500 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bgn_IR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bgn_OM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bgn_OM.dat new file mode 100644 index 00000000..f3962683 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bgn_OM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bgn_PK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bgn_PK.dat new file mode 100644 index 00000000..e15afea2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bgn_PK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bho.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bho.dat new file mode 100644 index 00000000..15f164ee Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bho.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bho_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bho_IN.dat new file mode 100644 index 00000000..4f7eb58e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bho_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/blo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/blo.dat new file mode 100644 index 00000000..6952c5e3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/blo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/blo_BJ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/blo_BJ.dat new file mode 100644 index 00000000..fbc0ae51 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/blo_BJ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/blt.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/blt.dat new file mode 100644 index 00000000..fee95211 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/blt.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/blt_VN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/blt_VN.dat new file mode 100644 index 00000000..1cf8aa30 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/blt_VN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bm.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bm.dat new file mode 100644 index 00000000..35a8748f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bm.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bm_ML.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bm_ML.dat new file mode 100644 index 00000000..db406084 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bm_ML.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bm_Nkoo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bm_Nkoo.dat new file mode 100644 index 00000000..318955fe Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bm_Nkoo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bm_Nkoo_ML.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bm_Nkoo_ML.dat new file mode 100644 index 00000000..db406084 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bm_Nkoo_ML.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bn.dat new file mode 100644 index 00000000..d6d5dadf Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bn_BD.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bn_BD.dat new file mode 100644 index 00000000..0814ec91 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bn_BD.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bn_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bn_IN.dat new file mode 100644 index 00000000..ea4c51c3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bn_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bo.dat new file mode 100644 index 00000000..3e914649 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bo_CN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bo_CN.dat new file mode 100644 index 00000000..d9e62431 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bo_CN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bo_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bo_IN.dat new file mode 100644 index 00000000..900a19e7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bo_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/br.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/br.dat new file mode 100644 index 00000000..6ee99829 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/br.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/br_FR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/br_FR.dat new file mode 100644 index 00000000..5964d661 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/br_FR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/brx.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/brx.dat new file mode 100644 index 00000000..db3f702a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/brx.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/brx_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/brx_IN.dat new file mode 100644 index 00000000..6dafcc92 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/brx_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bs.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bs.dat new file mode 100644 index 00000000..9e9a5e5e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bs.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bs_Cyrl.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bs_Cyrl.dat new file mode 100644 index 00000000..2047a88b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bs_Cyrl.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bs_Cyrl_BA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bs_Cyrl_BA.dat new file mode 100644 index 00000000..4a56da8f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bs_Cyrl_BA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bs_Latn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bs_Latn.dat new file mode 100644 index 00000000..767af2e9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bs_Latn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bs_Latn_BA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bs_Latn_BA.dat new file mode 100644 index 00000000..4a56da8f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bs_Latn_BA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bss.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bss.dat new file mode 100644 index 00000000..9b59977c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bss.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/bss_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/bss_CM.dat new file mode 100644 index 00000000..c02e93f2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/bss_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/byn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/byn.dat new file mode 100644 index 00000000..c8a14266 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/byn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/byn_ER.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/byn_ER.dat new file mode 100644 index 00000000..ef7364c0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/byn_ER.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ca.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ca.dat new file mode 100644 index 00000000..89fa5d88 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ca.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ca_AD.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ca_AD.dat new file mode 100644 index 00000000..417de470 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ca_AD.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ca_ES.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ca_ES.dat new file mode 100644 index 00000000..1262e7c7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ca_ES.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ca_ES_VALENCIA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ca_ES_VALENCIA.dat new file mode 100644 index 00000000..f9a67d74 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ca_ES_VALENCIA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ca_FR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ca_FR.dat new file mode 100644 index 00000000..1e13497d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ca_FR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ca_IT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ca_IT.dat new file mode 100644 index 00000000..18daef70 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ca_IT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cad.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cad.dat new file mode 100644 index 00000000..e0751fea Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cad.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cad_US.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cad_US.dat new file mode 100644 index 00000000..649a1277 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cad_US.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cch.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cch.dat new file mode 100644 index 00000000..6bfb265e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cch.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cch_NG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cch_NG.dat new file mode 100644 index 00000000..7ca50d06 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cch_NG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ccp.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ccp.dat new file mode 100644 index 00000000..3c5578b2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ccp.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ccp_BD.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ccp_BD.dat new file mode 100644 index 00000000..23248d87 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ccp_BD.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ccp_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ccp_IN.dat new file mode 100644 index 00000000..e4fe9a01 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ccp_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ce.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ce.dat new file mode 100644 index 00000000..a37c7047 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ce.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ce_RU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ce_RU.dat new file mode 100644 index 00000000..613de660 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ce_RU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ceb.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ceb.dat new file mode 100644 index 00000000..d6b8f188 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ceb.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ceb_PH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ceb_PH.dat new file mode 100644 index 00000000..f5a88a13 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ceb_PH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cgg.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cgg.dat new file mode 100644 index 00000000..5687c38b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cgg.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cgg_UG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cgg_UG.dat new file mode 100644 index 00000000..3ed7de5e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cgg_UG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cho.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cho.dat new file mode 100644 index 00000000..8d978b7c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cho.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cho_US.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cho_US.dat new file mode 100644 index 00000000..927cd451 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cho_US.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/chr.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/chr.dat new file mode 100644 index 00000000..557280d4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/chr.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/chr_US.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/chr_US.dat new file mode 100644 index 00000000..30579852 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/chr_US.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cic.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cic.dat new file mode 100644 index 00000000..87877563 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cic.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cic_US.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cic_US.dat new file mode 100644 index 00000000..a2f944fb Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cic_US.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ckb.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ckb.dat new file mode 100644 index 00000000..e8f1be3e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ckb.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ckb_IQ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ckb_IQ.dat new file mode 100644 index 00000000..68ae78b5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ckb_IQ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ckb_IR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ckb_IR.dat new file mode 100644 index 00000000..407a6058 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ckb_IR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/co.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/co.dat new file mode 100644 index 00000000..cf9e7bc6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/co.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/co_FR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/co_FR.dat new file mode 100644 index 00000000..ac968523 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/co_FR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cs.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cs.dat new file mode 100644 index 00000000..a00782bc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cs.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cs_CZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cs_CZ.dat new file mode 100644 index 00000000..0c492c42 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cs_CZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/csw.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/csw.dat new file mode 100644 index 00000000..1224a99e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/csw.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/csw_CA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/csw_CA.dat new file mode 100644 index 00000000..01b6c607 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/csw_CA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cu.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cu.dat new file mode 100644 index 00000000..19443c52 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cu.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cu_RU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cu_RU.dat new file mode 100644 index 00000000..a9d3101e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cu_RU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cv.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cv.dat new file mode 100644 index 00000000..6e21fed3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cv.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cv_RU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cv_RU.dat new file mode 100644 index 00000000..59759914 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cv_RU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cy.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cy.dat new file mode 100644 index 00000000..d0d1e3c4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cy.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/cy_GB.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/cy_GB.dat new file mode 100644 index 00000000..f3febac6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/cy_GB.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/da.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/da.dat new file mode 100644 index 00000000..bb74382c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/da.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/da_DK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/da_DK.dat new file mode 100644 index 00000000..dbd06720 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/da_DK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/da_GL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/da_GL.dat new file mode 100644 index 00000000..ce46da12 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/da_GL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/dav.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/dav.dat new file mode 100644 index 00000000..8b63c2b6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/dav.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/dav_KE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/dav_KE.dat new file mode 100644 index 00000000..5e3fbf48 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/dav_KE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/de.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/de.dat new file mode 100644 index 00000000..7cf17cc2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/de.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/de_AT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/de_AT.dat new file mode 100644 index 00000000..c8b9b62e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/de_AT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/de_BE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/de_BE.dat new file mode 100644 index 00000000..806b2914 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/de_BE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/de_CH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/de_CH.dat new file mode 100644 index 00000000..9f50b2e1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/de_CH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/de_DE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/de_DE.dat new file mode 100644 index 00000000..1d769ac8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/de_DE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/de_IT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/de_IT.dat new file mode 100644 index 00000000..7d7b9d2d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/de_IT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/de_LI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/de_LI.dat new file mode 100644 index 00000000..f4f1a353 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/de_LI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/de_LU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/de_LU.dat new file mode 100644 index 00000000..ed9d8fb7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/de_LU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/dje.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/dje.dat new file mode 100644 index 00000000..aafbdbda Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/dje.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/dje_NE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/dje_NE.dat new file mode 100644 index 00000000..605bb83a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/dje_NE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/doi.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/doi.dat new file mode 100644 index 00000000..44b79c89 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/doi.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/doi_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/doi_IN.dat new file mode 100644 index 00000000..550ec0ec Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/doi_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/dsb.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/dsb.dat new file mode 100644 index 00000000..4a954bde Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/dsb.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/dsb_DE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/dsb_DE.dat new file mode 100644 index 00000000..f1a8d4bc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/dsb_DE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/dua.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/dua.dat new file mode 100644 index 00000000..24ee4953 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/dua.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/dua_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/dua_CM.dat new file mode 100644 index 00000000..5eab8c9b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/dua_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/dv.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/dv.dat new file mode 100644 index 00000000..f8c88ff8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/dv.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/dv_MV.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/dv_MV.dat new file mode 100644 index 00000000..6a6048b5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/dv_MV.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/dyo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/dyo.dat new file mode 100644 index 00000000..836d954d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/dyo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/dyo_SN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/dyo_SN.dat new file mode 100644 index 00000000..734192ce Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/dyo_SN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/dz.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/dz.dat new file mode 100644 index 00000000..a75ae5bd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/dz.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/dz_BT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/dz_BT.dat new file mode 100644 index 00000000..34b2803b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/dz_BT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ebu.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ebu.dat new file mode 100644 index 00000000..d02c22a0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ebu.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ebu_KE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ebu_KE.dat new file mode 100644 index 00000000..0abfe0e4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ebu_KE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ee.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ee.dat new file mode 100644 index 00000000..9348aa2e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ee.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ee_GH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ee_GH.dat new file mode 100644 index 00000000..3702d427 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ee_GH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ee_TG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ee_TG.dat new file mode 100644 index 00000000..5f19ac9f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ee_TG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/el.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/el.dat new file mode 100644 index 00000000..dee09ba5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/el.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/el_CY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/el_CY.dat new file mode 100644 index 00000000..22fb8748 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/el_CY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/el_GR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/el_GR.dat new file mode 100644 index 00000000..ee095c95 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/el_GR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/el_POLYTON.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/el_POLYTON.dat new file mode 100644 index 00000000..24a37da5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/el_POLYTON.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en.dat new file mode 100644 index 00000000..7a576fce Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_001.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_001.dat new file mode 100644 index 00000000..47db2cc6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_001.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_150.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_150.dat new file mode 100644 index 00000000..82dd2718 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_150.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_AE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_AE.dat new file mode 100644 index 00000000..bd96c499 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_AE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_AG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_AG.dat new file mode 100644 index 00000000..16d7fa5a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_AG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_AI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_AI.dat new file mode 100644 index 00000000..6f0437d6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_AI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_AS.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_AS.dat new file mode 100644 index 00000000..d309d365 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_AS.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_AT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_AT.dat new file mode 100644 index 00000000..ff190b61 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_AT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_AU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_AU.dat new file mode 100644 index 00000000..7c92a1a3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_AU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_BB.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_BB.dat new file mode 100644 index 00000000..fbcfdd8a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_BB.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_BE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_BE.dat new file mode 100644 index 00000000..481689fe Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_BE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_BI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_BI.dat new file mode 100644 index 00000000..e2ef90f8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_BI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_BM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_BM.dat new file mode 100644 index 00000000..f4e57b6a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_BM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_BS.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_BS.dat new file mode 100644 index 00000000..aafa96f0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_BS.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_BW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_BW.dat new file mode 100644 index 00000000..2d083923 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_BW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_BZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_BZ.dat new file mode 100644 index 00000000..26326dcb Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_BZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_CA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_CA.dat new file mode 100644 index 00000000..89cab7ce Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_CA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_CC.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_CC.dat new file mode 100644 index 00000000..1102d2b5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_CC.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_CH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_CH.dat new file mode 100644 index 00000000..bce7db95 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_CH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_CK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_CK.dat new file mode 100644 index 00000000..e4870c84 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_CK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_CM.dat new file mode 100644 index 00000000..1816a83f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_CX.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_CX.dat new file mode 100644 index 00000000..b0a5620b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_CX.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_CY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_CY.dat new file mode 100644 index 00000000..54fb2d1a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_CY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_DE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_DE.dat new file mode 100644 index 00000000..3d7ded02 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_DE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_DG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_DG.dat new file mode 100644 index 00000000..1bdea430 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_DG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_DK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_DK.dat new file mode 100644 index 00000000..cae041ed Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_DK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_DM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_DM.dat new file mode 100644 index 00000000..00ce5e14 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_DM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_Dsrt.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_Dsrt.dat new file mode 100644 index 00000000..114accc1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_Dsrt.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_Dsrt_US.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_Dsrt_US.dat new file mode 100644 index 00000000..663e8261 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_Dsrt_US.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_ER.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_ER.dat new file mode 100644 index 00000000..349173e3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_ER.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_FI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_FI.dat new file mode 100644 index 00000000..90a35e72 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_FI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_FJ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_FJ.dat new file mode 100644 index 00000000..e2013194 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_FJ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_FK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_FK.dat new file mode 100644 index 00000000..3b586dd8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_FK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_FM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_FM.dat new file mode 100644 index 00000000..4a4d6ab9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_FM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_GB.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_GB.dat new file mode 100644 index 00000000..ce4687f5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_GB.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_GD.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_GD.dat new file mode 100644 index 00000000..fbd024b4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_GD.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_GG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_GG.dat new file mode 100644 index 00000000..6195e7d8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_GG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_GH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_GH.dat new file mode 100644 index 00000000..bfcd6f57 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_GH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_GI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_GI.dat new file mode 100644 index 00000000..98b852ea Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_GI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_GM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_GM.dat new file mode 100644 index 00000000..4e7daad8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_GM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_GU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_GU.dat new file mode 100644 index 00000000..6d184675 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_GU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_GY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_GY.dat new file mode 100644 index 00000000..4192d41b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_GY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_HK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_HK.dat new file mode 100644 index 00000000..39a6d7f6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_HK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_ID.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_ID.dat new file mode 100644 index 00000000..763ffdf5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_ID.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_IE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_IE.dat new file mode 100644 index 00000000..36b4ffb1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_IE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_IL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_IL.dat new file mode 100644 index 00000000..9782e31a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_IL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_IM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_IM.dat new file mode 100644 index 00000000..a3789694 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_IM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_IN.dat new file mode 100644 index 00000000..03987cb1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_IO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_IO.dat new file mode 100644 index 00000000..ce4dba27 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_IO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_JE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_JE.dat new file mode 100644 index 00000000..59fad126 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_JE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_JM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_JM.dat new file mode 100644 index 00000000..8bb03e32 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_JM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_KE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_KE.dat new file mode 100644 index 00000000..5c181679 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_KE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_KI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_KI.dat new file mode 100644 index 00000000..2c57a160 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_KI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_KN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_KN.dat new file mode 100644 index 00000000..79c9a818 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_KN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_KY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_KY.dat new file mode 100644 index 00000000..013312f6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_KY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_LC.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_LC.dat new file mode 100644 index 00000000..0368fe36 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_LC.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_LR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_LR.dat new file mode 100644 index 00000000..80d77d8c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_LR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_LS.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_LS.dat new file mode 100644 index 00000000..b3cbbfb6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_LS.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_MG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MG.dat new file mode 100644 index 00000000..cf3db95a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_MH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MH.dat new file mode 100644 index 00000000..f6bc501c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_MO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MO.dat new file mode 100644 index 00000000..6635dc53 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_MP.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MP.dat new file mode 100644 index 00000000..ede9c2bb Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MP.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_MS.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MS.dat new file mode 100644 index 00000000..53115eb5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MS.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_MT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MT.dat new file mode 100644 index 00000000..87c2b953 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_MU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MU.dat new file mode 100644 index 00000000..524b3ac9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_MV.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MV.dat new file mode 100644 index 00000000..852a33d9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MV.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_MW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MW.dat new file mode 100644 index 00000000..d0dcd6a9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_MY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MY.dat new file mode 100644 index 00000000..9479385f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_MY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_NA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_NA.dat new file mode 100644 index 00000000..a3d88293 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_NA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_NF.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_NF.dat new file mode 100644 index 00000000..8003f609 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_NF.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_NG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_NG.dat new file mode 100644 index 00000000..27bae075 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_NG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_NL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_NL.dat new file mode 100644 index 00000000..8cd57735 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_NL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_NR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_NR.dat new file mode 100644 index 00000000..0c788a8d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_NR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_NU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_NU.dat new file mode 100644 index 00000000..f8607f64 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_NU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_NZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_NZ.dat new file mode 100644 index 00000000..7220c4cc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_NZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_PG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_PG.dat new file mode 100644 index 00000000..4269e331 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_PG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_PH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_PH.dat new file mode 100644 index 00000000..9ce4fc0b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_PH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_PK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_PK.dat new file mode 100644 index 00000000..f198d9ec Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_PK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_PN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_PN.dat new file mode 100644 index 00000000..9977c743 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_PN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_PR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_PR.dat new file mode 100644 index 00000000..cdb83dbe Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_PR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_PW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_PW.dat new file mode 100644 index 00000000..e878083f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_PW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_RW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_RW.dat new file mode 100644 index 00000000..c46733b0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_RW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_SB.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SB.dat new file mode 100644 index 00000000..b81625e3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SB.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_SC.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SC.dat new file mode 100644 index 00000000..4816bb86 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SC.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_SD.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SD.dat new file mode 100644 index 00000000..985e3e6b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SD.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_SE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SE.dat new file mode 100644 index 00000000..5f89bad5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_SG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SG.dat new file mode 100644 index 00000000..a21a72a7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_SH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SH.dat new file mode 100644 index 00000000..006487c3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_SI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SI.dat new file mode 100644 index 00000000..97b42d04 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_SL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SL.dat new file mode 100644 index 00000000..127d1285 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_SS.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SS.dat new file mode 100644 index 00000000..7192fd13 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SS.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_SX.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SX.dat new file mode 100644 index 00000000..5dfff7d8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SX.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_SZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SZ.dat new file mode 100644 index 00000000..90a9f7a1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_SZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_Shaw.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_Shaw.dat new file mode 100644 index 00000000..6c394365 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_Shaw.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_Shaw_GB.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_Shaw_GB.dat new file mode 100644 index 00000000..214e5c61 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_Shaw_GB.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_TC.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_TC.dat new file mode 100644 index 00000000..d1edd041 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_TC.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_TK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_TK.dat new file mode 100644 index 00000000..d81803f7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_TK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_TO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_TO.dat new file mode 100644 index 00000000..4512ee44 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_TO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_TT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_TT.dat new file mode 100644 index 00000000..e704f065 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_TT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_TV.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_TV.dat new file mode 100644 index 00000000..c936c695 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_TV.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_TZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_TZ.dat new file mode 100644 index 00000000..62091cee Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_TZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_UG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_UG.dat new file mode 100644 index 00000000..dd077cca Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_UG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_UM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_UM.dat new file mode 100644 index 00000000..3a027b2a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_UM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_US.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_US.dat new file mode 100644 index 00000000..663e8261 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_US.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_US_POSIX.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_US_POSIX.dat new file mode 100644 index 00000000..a9ecf5b4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_US_POSIX.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_VC.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_VC.dat new file mode 100644 index 00000000..86fb17d9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_VC.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_VG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_VG.dat new file mode 100644 index 00000000..3fdd46cb Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_VG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_VI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_VI.dat new file mode 100644 index 00000000..c4520cc6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_VI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_VU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_VU.dat new file mode 100644 index 00000000..12647a8c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_VU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_WS.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_WS.dat new file mode 100644 index 00000000..8531d311 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_WS.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_ZA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_ZA.dat new file mode 100644 index 00000000..8cc6b0e0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_ZA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_ZM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_ZM.dat new file mode 100644 index 00000000..50039250 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_ZM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/en_ZW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/en_ZW.dat new file mode 100644 index 00000000..6a8835f2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/en_ZW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/eo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/eo.dat new file mode 100644 index 00000000..84ba5ece Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/eo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/eo_001.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/eo_001.dat new file mode 100644 index 00000000..ec48fb1f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/eo_001.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es.dat new file mode 100644 index 00000000..0a7e6ed7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_419.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_419.dat new file mode 100644 index 00000000..38c8aa5b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_419.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_AR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_AR.dat new file mode 100644 index 00000000..42787bdf Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_AR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_BO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_BO.dat new file mode 100644 index 00000000..df6a1299 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_BO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_BR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_BR.dat new file mode 100644 index 00000000..7d1f1023 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_BR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_BZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_BZ.dat new file mode 100644 index 00000000..ff0ff6ab Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_BZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_CL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_CL.dat new file mode 100644 index 00000000..87cd6243 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_CL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_CO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_CO.dat new file mode 100644 index 00000000..09e03e3a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_CO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_CR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_CR.dat new file mode 100644 index 00000000..041296d1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_CR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_CU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_CU.dat new file mode 100644 index 00000000..ced5b381 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_CU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_DO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_DO.dat new file mode 100644 index 00000000..cbec7812 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_DO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_EA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_EA.dat new file mode 100644 index 00000000..dfd0d910 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_EA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_EC.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_EC.dat new file mode 100644 index 00000000..0c6f37b6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_EC.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_ES.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_ES.dat new file mode 100644 index 00000000..3414dff2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_ES.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_GQ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_GQ.dat new file mode 100644 index 00000000..775e43b0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_GQ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_GT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_GT.dat new file mode 100644 index 00000000..bedab31c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_GT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_HN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_HN.dat new file mode 100644 index 00000000..c5223aae Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_HN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_IC.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_IC.dat new file mode 100644 index 00000000..3a9f9fd9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_IC.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_MX.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_MX.dat new file mode 100644 index 00000000..91919c15 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_MX.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_NI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_NI.dat new file mode 100644 index 00000000..9e1ab78f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_NI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_PA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_PA.dat new file mode 100644 index 00000000..edfcd545 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_PA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_PE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_PE.dat new file mode 100644 index 00000000..82c50ad2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_PE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_PH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_PH.dat new file mode 100644 index 00000000..91e24f13 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_PH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_PR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_PR.dat new file mode 100644 index 00000000..c45d5911 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_PR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_PY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_PY.dat new file mode 100644 index 00000000..3f365705 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_PY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_SV.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_SV.dat new file mode 100644 index 00000000..2c98032c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_SV.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_US.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_US.dat new file mode 100644 index 00000000..03388117 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_US.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_UY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_UY.dat new file mode 100644 index 00000000..292fa0cc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_UY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/es_VE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/es_VE.dat new file mode 100644 index 00000000..42cba455 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/es_VE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/et.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/et.dat new file mode 100644 index 00000000..c242fb8e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/et.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/et_EE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/et_EE.dat new file mode 100644 index 00000000..a08c6824 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/et_EE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/eu.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/eu.dat new file mode 100644 index 00000000..e01ff5be Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/eu.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/eu_ES.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/eu_ES.dat new file mode 100644 index 00000000..62f7cc61 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/eu_ES.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ewo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ewo.dat new file mode 100644 index 00000000..5f7d570b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ewo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ewo_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ewo_CM.dat new file mode 100644 index 00000000..ae6b0f7a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ewo_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fa.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fa.dat new file mode 100644 index 00000000..5a0663af Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fa.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fa_AF.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fa_AF.dat new file mode 100644 index 00000000..689b4c78 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fa_AF.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fa_IR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fa_IR.dat new file mode 100644 index 00000000..b3dd8eb5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fa_IR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff.dat new file mode 100644 index 00000000..531fffa9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm.dat new file mode 100644 index 00000000..5c7dc646 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_BF.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_BF.dat new file mode 100644 index 00000000..84f0ffbd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_BF.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_CM.dat new file mode 100644 index 00000000..78bf6208 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_GH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_GH.dat new file mode 100644 index 00000000..6667263b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_GH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_GM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_GM.dat new file mode 100644 index 00000000..e641b279 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_GM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_GN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_GN.dat new file mode 100644 index 00000000..216df0ba Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_GN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_GW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_GW.dat new file mode 100644 index 00000000..63799caa Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_GW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_LR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_LR.dat new file mode 100644 index 00000000..f5e3cdeb Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_LR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_MR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_MR.dat new file mode 100644 index 00000000..716f23fd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_MR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_NE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_NE.dat new file mode 100644 index 00000000..12098834 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_NE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_NG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_NG.dat new file mode 100644 index 00000000..a17d85c5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_NG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_SL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_SL.dat new file mode 100644 index 00000000..d8825521 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_SL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_SN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_SN.dat new file mode 100644 index 00000000..125d9994 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Adlm_SN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn.dat new file mode 100644 index 00000000..df806dd4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_BF.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_BF.dat new file mode 100644 index 00000000..bb3a2e0e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_BF.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_CM.dat new file mode 100644 index 00000000..a3229cdc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_GH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_GH.dat new file mode 100644 index 00000000..58222300 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_GH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_GM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_GM.dat new file mode 100644 index 00000000..82022dfa Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_GM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_GN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_GN.dat new file mode 100644 index 00000000..1a99d48f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_GN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_GW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_GW.dat new file mode 100644 index 00000000..a2b55bf7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_GW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_LR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_LR.dat new file mode 100644 index 00000000..5dce447e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_LR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_MR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_MR.dat new file mode 100644 index 00000000..e6178b86 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_MR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_NE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_NE.dat new file mode 100644 index 00000000..82151e8f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_NE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_NG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_NG.dat new file mode 100644 index 00000000..086889e6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_NG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_SL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_SL.dat new file mode 100644 index 00000000..e50753de Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_SL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_SN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_SN.dat new file mode 100644 index 00000000..faeff87c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ff_Latn_SN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fi.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fi.dat new file mode 100644 index 00000000..310cd1c4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fi.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fi_FI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fi_FI.dat new file mode 100644 index 00000000..fa20daec Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fi_FI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fil.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fil.dat new file mode 100644 index 00000000..03bcac01 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fil.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fil_PH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fil_PH.dat new file mode 100644 index 00000000..dc08338c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fil_PH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fo.dat new file mode 100644 index 00000000..7a0b53f6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fo_DK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fo_DK.dat new file mode 100644 index 00000000..669088d7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fo_DK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fo_FO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fo_FO.dat new file mode 100644 index 00000000..7d8a8145 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fo_FO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr.dat new file mode 100644 index 00000000..fb8c3a1d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_BE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_BE.dat new file mode 100644 index 00000000..cc842fe0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_BE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_BF.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_BF.dat new file mode 100644 index 00000000..ba1ecc57 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_BF.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_BI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_BI.dat new file mode 100644 index 00000000..d4036bed Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_BI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_BJ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_BJ.dat new file mode 100644 index 00000000..562758e8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_BJ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_BL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_BL.dat new file mode 100644 index 00000000..e311ee66 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_BL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CA.dat new file mode 100644 index 00000000..da870066 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CD.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CD.dat new file mode 100644 index 00000000..24827887 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CD.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CF.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CF.dat new file mode 100644 index 00000000..2468e1ed Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CF.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CG.dat new file mode 100644 index 00000000..b7df1a92 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CH.dat new file mode 100644 index 00000000..fa5b7b47 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CI.dat new file mode 100644 index 00000000..3d92afcf Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CM.dat new file mode 100644 index 00000000..a779b4a7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_DJ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_DJ.dat new file mode 100644 index 00000000..79aa6e67 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_DJ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_DZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_DZ.dat new file mode 100644 index 00000000..3f56d231 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_DZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_FR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_FR.dat new file mode 100644 index 00000000..553d5e4a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_FR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_GA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_GA.dat new file mode 100644 index 00000000..beb0dd56 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_GA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_GF.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_GF.dat new file mode 100644 index 00000000..eb9ffc90 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_GF.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_GN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_GN.dat new file mode 100644 index 00000000..c4f9f808 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_GN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_GP.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_GP.dat new file mode 100644 index 00000000..e2834c7d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_GP.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_GQ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_GQ.dat new file mode 100644 index 00000000..a04de173 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_GQ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_HT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_HT.dat new file mode 100644 index 00000000..33b39563 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_HT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_KM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_KM.dat new file mode 100644 index 00000000..9ece5936 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_KM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_LU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_LU.dat new file mode 100644 index 00000000..062b9ab0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_LU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MA.dat new file mode 100644 index 00000000..6ee8eb9f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MC.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MC.dat new file mode 100644 index 00000000..52315a3d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MC.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MF.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MF.dat new file mode 100644 index 00000000..e5a1df0e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MF.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MG.dat new file mode 100644 index 00000000..c01d050a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_ML.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_ML.dat new file mode 100644 index 00000000..721771ea Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_ML.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MQ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MQ.dat new file mode 100644 index 00000000..90becb76 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MQ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MR.dat new file mode 100644 index 00000000..28ef3f14 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MU.dat new file mode 100644 index 00000000..76fc2ab4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_MU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_NC.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_NC.dat new file mode 100644 index 00000000..6aaf0342 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_NC.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_NE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_NE.dat new file mode 100644 index 00000000..143ac4fd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_NE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_PF.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_PF.dat new file mode 100644 index 00000000..e2f11dcd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_PF.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_PM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_PM.dat new file mode 100644 index 00000000..4f9cdf7b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_PM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_RE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_RE.dat new file mode 100644 index 00000000..214ad4fa Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_RE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_RW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_RW.dat new file mode 100644 index 00000000..6739c522 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_RW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_SC.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_SC.dat new file mode 100644 index 00000000..1fedefe5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_SC.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_SN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_SN.dat new file mode 100644 index 00000000..48207856 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_SN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_SY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_SY.dat new file mode 100644 index 00000000..8dd53967 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_SY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_TD.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_TD.dat new file mode 100644 index 00000000..6abebabd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_TD.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_TG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_TG.dat new file mode 100644 index 00000000..9395eaaf Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_TG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_TN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_TN.dat new file mode 100644 index 00000000..bf1e5515 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_TN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_VU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_VU.dat new file mode 100644 index 00000000..f29eeed9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_VU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_WF.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_WF.dat new file mode 100644 index 00000000..2d38ac6f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_WF.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fr_YT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_YT.dat new file mode 100644 index 00000000..9e9ba28c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fr_YT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/frr.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/frr.dat new file mode 100644 index 00000000..a6b2c748 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/frr.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/frr_DE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/frr_DE.dat new file mode 100644 index 00000000..ecab3304 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/frr_DE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fur.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fur.dat new file mode 100644 index 00000000..81a28ed2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fur.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fur_IT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fur_IT.dat new file mode 100644 index 00000000..2c40e5c0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fur_IT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fy.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fy.dat new file mode 100644 index 00000000..1036ee09 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fy.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/fy_NL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/fy_NL.dat new file mode 100644 index 00000000..1a8f5811 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/fy_NL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ga.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ga.dat new file mode 100644 index 00000000..087b6029 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ga.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ga_GB.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ga_GB.dat new file mode 100644 index 00000000..7432317e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ga_GB.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ga_IE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ga_IE.dat new file mode 100644 index 00000000..422b05a5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ga_IE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gaa.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gaa.dat new file mode 100644 index 00000000..5028b29c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gaa.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gaa_GH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gaa_GH.dat new file mode 100644 index 00000000..7312993b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gaa_GH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gd.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gd.dat new file mode 100644 index 00000000..785f8fbf Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gd.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gd_GB.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gd_GB.dat new file mode 100644 index 00000000..e3705785 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gd_GB.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gez.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gez.dat new file mode 100644 index 00000000..c3fe514f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gez.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gez_ER.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gez_ER.dat new file mode 100644 index 00000000..20c18a52 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gez_ER.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gez_ET.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gez_ET.dat new file mode 100644 index 00000000..a70d43dd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gez_ET.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gl.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gl.dat new file mode 100644 index 00000000..48f3577d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gl.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gl_ES.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gl_ES.dat new file mode 100644 index 00000000..d432bb25 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gl_ES.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gn.dat new file mode 100644 index 00000000..2d6eb9f5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gn_PY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gn_PY.dat new file mode 100644 index 00000000..a217cc7a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gn_PY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gsw.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gsw.dat new file mode 100644 index 00000000..0d241c69 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gsw.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gsw_CH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gsw_CH.dat new file mode 100644 index 00000000..39ed5a8c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gsw_CH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gsw_FR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gsw_FR.dat new file mode 100644 index 00000000..8687c5c1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gsw_FR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gsw_LI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gsw_LI.dat new file mode 100644 index 00000000..2e72e060 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gsw_LI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gu.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gu.dat new file mode 100644 index 00000000..ceefbb29 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gu.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gu_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gu_IN.dat new file mode 100644 index 00000000..a7071f70 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gu_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/guz.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/guz.dat new file mode 100644 index 00000000..386e37bc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/guz.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/guz_KE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/guz_KE.dat new file mode 100644 index 00000000..3b2c102d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/guz_KE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gv.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gv.dat new file mode 100644 index 00000000..b493494c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gv.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/gv_IM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/gv_IM.dat new file mode 100644 index 00000000..0793d838 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/gv_IM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ha.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ha.dat new file mode 100644 index 00000000..74e86b98 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ha.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ha_Arab.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ha_Arab.dat new file mode 100644 index 00000000..1395d9e0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ha_Arab.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ha_Arab_NG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ha_Arab_NG.dat new file mode 100644 index 00000000..0a98ac88 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ha_Arab_NG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ha_Arab_SD.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ha_Arab_SD.dat new file mode 100644 index 00000000..e33c2b3c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ha_Arab_SD.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ha_GH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ha_GH.dat new file mode 100644 index 00000000..74d57805 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ha_GH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ha_NE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ha_NE.dat new file mode 100644 index 00000000..d06afa95 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ha_NE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ha_NG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ha_NG.dat new file mode 100644 index 00000000..0a98ac88 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ha_NG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/haw.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/haw.dat new file mode 100644 index 00000000..8b2b47cd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/haw.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/haw_US.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/haw_US.dat new file mode 100644 index 00000000..18706f16 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/haw_US.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/he.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/he.dat new file mode 100644 index 00000000..defc3ea7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/he.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/he_IL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/he_IL.dat new file mode 100644 index 00000000..3a0da6c0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/he_IL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/hi.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/hi.dat new file mode 100644 index 00000000..b2096453 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/hi.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/hi_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/hi_IN.dat new file mode 100644 index 00000000..9fbfd57d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/hi_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/hi_Latn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/hi_Latn.dat new file mode 100644 index 00000000..17b23a7d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/hi_Latn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/hi_Latn_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/hi_Latn_IN.dat new file mode 100644 index 00000000..9fbfd57d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/hi_Latn_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/hnj.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/hnj.dat new file mode 100644 index 00000000..07eeb516 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/hnj.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/hnj_Hmnp.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/hnj_Hmnp.dat new file mode 100644 index 00000000..c8935a2e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/hnj_Hmnp.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/hnj_Hmnp_US.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/hnj_Hmnp_US.dat new file mode 100644 index 00000000..46c37147 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/hnj_Hmnp_US.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/hr.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/hr.dat new file mode 100644 index 00000000..e2a005e8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/hr.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/hr_BA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/hr_BA.dat new file mode 100644 index 00000000..44129abf Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/hr_BA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/hr_HR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/hr_HR.dat new file mode 100644 index 00000000..a0ae43dd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/hr_HR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/hsb.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/hsb.dat new file mode 100644 index 00000000..40b68464 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/hsb.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/hsb_DE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/hsb_DE.dat new file mode 100644 index 00000000..5de4a2c3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/hsb_DE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/hu.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/hu.dat new file mode 100644 index 00000000..143d1f0a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/hu.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/hu_HU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/hu_HU.dat new file mode 100644 index 00000000..113faede Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/hu_HU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/hy.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/hy.dat new file mode 100644 index 00000000..a331f211 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/hy.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/hy_AM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/hy_AM.dat new file mode 100644 index 00000000..fa2eaf7d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/hy_AM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ia.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ia.dat new file mode 100644 index 00000000..c9dab730 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ia.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ia_001.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ia_001.dat new file mode 100644 index 00000000..b0a1770b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ia_001.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/id.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/id.dat new file mode 100644 index 00000000..d9f0472f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/id.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/id_ID.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/id_ID.dat new file mode 100644 index 00000000..cf7c8e5e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/id_ID.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ie.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ie.dat new file mode 100644 index 00000000..a6ae1fe3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ie.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ie_EE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ie_EE.dat new file mode 100644 index 00000000..91566139 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ie_EE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ig.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ig.dat new file mode 100644 index 00000000..4f4627c0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ig.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ig_NG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ig_NG.dat new file mode 100644 index 00000000..4b5635b8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ig_NG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ii.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ii.dat new file mode 100644 index 00000000..93158e63 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ii.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ii_CN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ii_CN.dat new file mode 100644 index 00000000..ef6effaa Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ii_CN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/io.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/io.dat new file mode 100644 index 00000000..33aa1fae Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/io.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/io_001.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/io_001.dat new file mode 100644 index 00000000..964a6ab6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/io_001.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/is.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/is.dat new file mode 100644 index 00000000..b0b107d7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/is.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/is_IS.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/is_IS.dat new file mode 100644 index 00000000..b3649a76 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/is_IS.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/it.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/it.dat new file mode 100644 index 00000000..f1e55e7f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/it.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/it_CH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/it_CH.dat new file mode 100644 index 00000000..cddfb6b5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/it_CH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/it_IT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/it_IT.dat new file mode 100644 index 00000000..b9a944aa Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/it_IT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/it_SM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/it_SM.dat new file mode 100644 index 00000000..db8432aa Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/it_SM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/it_VA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/it_VA.dat new file mode 100644 index 00000000..30ecca00 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/it_VA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/iu.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/iu.dat new file mode 100644 index 00000000..ca5b1f7a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/iu.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/iu_CA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/iu_CA.dat new file mode 100644 index 00000000..61bb5448 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/iu_CA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/iu_Latn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/iu_Latn.dat new file mode 100644 index 00000000..20b7cedc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/iu_Latn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/iu_Latn_CA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/iu_Latn_CA.dat new file mode 100644 index 00000000..61bb5448 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/iu_Latn_CA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ja.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ja.dat new file mode 100644 index 00000000..769686f4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ja.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ja_JP.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ja_JP.dat new file mode 100644 index 00000000..f0d4479b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ja_JP.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/jbo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/jbo.dat new file mode 100644 index 00000000..1bf2dbb9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/jbo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/jbo_001.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/jbo_001.dat new file mode 100644 index 00000000..dcc98aef Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/jbo_001.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/jgo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/jgo.dat new file mode 100644 index 00000000..1b6230ad Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/jgo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/jgo_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/jgo_CM.dat new file mode 100644 index 00000000..7a523f3b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/jgo_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/jmc.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/jmc.dat new file mode 100644 index 00000000..a4abf969 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/jmc.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/jmc_TZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/jmc_TZ.dat new file mode 100644 index 00000000..6abba327 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/jmc_TZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/jv.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/jv.dat new file mode 100644 index 00000000..db18e761 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/jv.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/jv_ID.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/jv_ID.dat new file mode 100644 index 00000000..b60dc4db Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/jv_ID.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ka.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ka.dat new file mode 100644 index 00000000..c6dba51a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ka.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ka_GE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ka_GE.dat new file mode 100644 index 00000000..73278fab Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ka_GE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kaa.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kaa.dat new file mode 100644 index 00000000..e6b6abdb Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kaa.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kaa_Cyrl.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kaa_Cyrl.dat new file mode 100644 index 00000000..085710f6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kaa_Cyrl.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kaa_Cyrl_UZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kaa_Cyrl_UZ.dat new file mode 100644 index 00000000..b2e4590f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kaa_Cyrl_UZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kaa_Latn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kaa_Latn.dat new file mode 100644 index 00000000..085710f6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kaa_Latn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kaa_Latn_UZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kaa_Latn_UZ.dat new file mode 100644 index 00000000..b2e4590f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kaa_Latn_UZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kab.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kab.dat new file mode 100644 index 00000000..b95fcc72 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kab.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kab_DZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kab_DZ.dat new file mode 100644 index 00000000..c0cbad8b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kab_DZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kaj.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kaj.dat new file mode 100644 index 00000000..8d304e93 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kaj.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kaj_NG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kaj_NG.dat new file mode 100644 index 00000000..a94d6f7c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kaj_NG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kam.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kam.dat new file mode 100644 index 00000000..2ef195ab Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kam.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kam_KE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kam_KE.dat new file mode 100644 index 00000000..d16ca2a1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kam_KE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kcg.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kcg.dat new file mode 100644 index 00000000..3ff14c35 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kcg.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kcg_NG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kcg_NG.dat new file mode 100644 index 00000000..1e6dc36b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kcg_NG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kde.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kde.dat new file mode 100644 index 00000000..6d51d911 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kde.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kde_TZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kde_TZ.dat new file mode 100644 index 00000000..2fcf1d47 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kde_TZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kea.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kea.dat new file mode 100644 index 00000000..42d05295 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kea.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kea_CV.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kea_CV.dat new file mode 100644 index 00000000..da97962e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kea_CV.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ken.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ken.dat new file mode 100644 index 00000000..57e91a15 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ken.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ken_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ken_CM.dat new file mode 100644 index 00000000..9ef61800 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ken_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kgp.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kgp.dat new file mode 100644 index 00000000..cfe272f7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kgp.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kgp_BR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kgp_BR.dat new file mode 100644 index 00000000..44b499db Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kgp_BR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/khq.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/khq.dat new file mode 100644 index 00000000..61cc45f5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/khq.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/khq_ML.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/khq_ML.dat new file mode 100644 index 00000000..91f7da18 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/khq_ML.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ki.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ki.dat new file mode 100644 index 00000000..0a7eb8d1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ki.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ki_KE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ki_KE.dat new file mode 100644 index 00000000..901b9214 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ki_KE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kk.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kk.dat new file mode 100644 index 00000000..19133c62 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kk.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kk_Arab.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kk_Arab.dat new file mode 100644 index 00000000..8dce5fc9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kk_Arab.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kk_Arab_CN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kk_Arab_CN.dat new file mode 100644 index 00000000..d8b9432a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kk_Arab_CN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kk_Cyrl.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kk_Cyrl.dat new file mode 100644 index 00000000..082c7545 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kk_Cyrl.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kk_Cyrl_KZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kk_Cyrl_KZ.dat new file mode 100644 index 00000000..ca827ac1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kk_Cyrl_KZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kk_KZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kk_KZ.dat new file mode 100644 index 00000000..ca827ac1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kk_KZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kkj.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kkj.dat new file mode 100644 index 00000000..529c4175 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kkj.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kkj_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kkj_CM.dat new file mode 100644 index 00000000..231f08bd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kkj_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kl.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kl.dat new file mode 100644 index 00000000..d82a25bb Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kl.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kl_GL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kl_GL.dat new file mode 100644 index 00000000..a8fb28dc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kl_GL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kln.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kln.dat new file mode 100644 index 00000000..65e435cb Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kln.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kln_KE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kln_KE.dat new file mode 100644 index 00000000..9a12beb4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kln_KE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/km.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/km.dat new file mode 100644 index 00000000..52d3fc4f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/km.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/km_KH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/km_KH.dat new file mode 100644 index 00000000..2ccaba66 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/km_KH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kn.dat new file mode 100644 index 00000000..85aaaec0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kn_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kn_IN.dat new file mode 100644 index 00000000..1f0d6462 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kn_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ko.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ko.dat new file mode 100644 index 00000000..765f2fcb Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ko.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ko_CN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ko_CN.dat new file mode 100644 index 00000000..170698df Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ko_CN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ko_KP.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ko_KP.dat new file mode 100644 index 00000000..7c76309a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ko_KP.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ko_KR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ko_KR.dat new file mode 100644 index 00000000..4af3b628 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ko_KR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kok.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kok.dat new file mode 100644 index 00000000..f01857f2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kok.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kok_Deva.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kok_Deva.dat new file mode 100644 index 00000000..e92bf9d5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kok_Deva.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kok_Deva_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kok_Deva_IN.dat new file mode 100644 index 00000000..8fff8b19 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kok_Deva_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kok_Latn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kok_Latn.dat new file mode 100644 index 00000000..d3fbcce6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kok_Latn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kok_Latn_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kok_Latn_IN.dat new file mode 100644 index 00000000..8fff8b19 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kok_Latn_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kpe.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kpe.dat new file mode 100644 index 00000000..48cf0bf5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kpe.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kpe_GN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kpe_GN.dat new file mode 100644 index 00000000..d333167e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kpe_GN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kpe_LR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kpe_LR.dat new file mode 100644 index 00000000..8afe47b9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kpe_LR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ks.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ks.dat new file mode 100644 index 00000000..96146c2c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ks.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ks_Arab.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ks_Arab.dat new file mode 100644 index 00000000..6441efed Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ks_Arab.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ks_Arab_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ks_Arab_IN.dat new file mode 100644 index 00000000..02a5d116 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ks_Arab_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ks_Deva.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ks_Deva.dat new file mode 100644 index 00000000..e6380926 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ks_Deva.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ks_Deva_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ks_Deva_IN.dat new file mode 100644 index 00000000..02a5d116 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ks_Deva_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ksb.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ksb.dat new file mode 100644 index 00000000..7ced57e8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ksb.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ksb_TZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ksb_TZ.dat new file mode 100644 index 00000000..6837e375 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ksb_TZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ksf.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ksf.dat new file mode 100644 index 00000000..7fba2526 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ksf.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ksf_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ksf_CM.dat new file mode 100644 index 00000000..fc29c498 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ksf_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ksh.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ksh.dat new file mode 100644 index 00000000..b7bb1f17 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ksh.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ksh_DE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ksh_DE.dat new file mode 100644 index 00000000..9318d48d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ksh_DE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ku.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ku.dat new file mode 100644 index 00000000..1bc6fa68 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ku.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ku_TR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ku_TR.dat new file mode 100644 index 00000000..206426aa Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ku_TR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kw.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kw.dat new file mode 100644 index 00000000..3d103e37 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kw.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kw_GB.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kw_GB.dat new file mode 100644 index 00000000..da81c10f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kw_GB.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kxv.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv.dat new file mode 100644 index 00000000..2849d6cc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Deva.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Deva.dat new file mode 100644 index 00000000..184addcd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Deva.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Deva_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Deva_IN.dat new file mode 100644 index 00000000..8a1e2898 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Deva_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Latn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Latn.dat new file mode 100644 index 00000000..35b2695d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Latn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Latn_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Latn_IN.dat new file mode 100644 index 00000000..8a1e2898 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Latn_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Orya.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Orya.dat new file mode 100644 index 00000000..98172abd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Orya.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Orya_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Orya_IN.dat new file mode 100644 index 00000000..8a1e2898 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Orya_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Telu.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Telu.dat new file mode 100644 index 00000000..1b3ac8b8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Telu.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Telu_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Telu_IN.dat new file mode 100644 index 00000000..8a1e2898 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/kxv_Telu_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ky.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ky.dat new file mode 100644 index 00000000..5bd19e57 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ky.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ky_KG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ky_KG.dat new file mode 100644 index 00000000..c61073fe Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ky_KG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/la.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/la.dat new file mode 100644 index 00000000..36656dc1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/la.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/la_VA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/la_VA.dat new file mode 100644 index 00000000..31a68ac9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/la_VA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lag.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lag.dat new file mode 100644 index 00000000..f8da045f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lag.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lag_TZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lag_TZ.dat new file mode 100644 index 00000000..7893a0ed Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lag_TZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lb.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lb.dat new file mode 100644 index 00000000..0d3dc7d0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lb.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lb_LU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lb_LU.dat new file mode 100644 index 00000000..bcb56532 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lb_LU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lg.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lg.dat new file mode 100644 index 00000000..cd4e5a9d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lg.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lg_UG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lg_UG.dat new file mode 100644 index 00000000..6ee321dc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lg_UG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lij.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lij.dat new file mode 100644 index 00000000..8a7414e9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lij.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lij_IT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lij_IT.dat new file mode 100644 index 00000000..6969b528 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lij_IT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lkt.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lkt.dat new file mode 100644 index 00000000..515ac6c4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lkt.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lkt_US.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lkt_US.dat new file mode 100644 index 00000000..44a5969a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lkt_US.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lld.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lld.dat new file mode 100644 index 00000000..04c08068 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lld.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lld_IT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lld_IT.dat new file mode 100644 index 00000000..f8d12f0f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lld_IT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lmo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lmo.dat new file mode 100644 index 00000000..c7388c7d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lmo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lmo_IT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lmo_IT.dat new file mode 100644 index 00000000..50dd5927 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lmo_IT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ln.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ln.dat new file mode 100644 index 00000000..cc8edb15 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ln.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ln_AO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ln_AO.dat new file mode 100644 index 00000000..60dd0ba8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ln_AO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ln_CD.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ln_CD.dat new file mode 100644 index 00000000..b87aad7d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ln_CD.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ln_CF.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ln_CF.dat new file mode 100644 index 00000000..b5a9a827 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ln_CF.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ln_CG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ln_CG.dat new file mode 100644 index 00000000..4059b369 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ln_CG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lo.dat new file mode 100644 index 00000000..754556b8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lo_LA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lo_LA.dat new file mode 100644 index 00000000..6f12ab55 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lo_LA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lrc.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lrc.dat new file mode 100644 index 00000000..4452aa52 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lrc.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lrc_IQ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lrc_IQ.dat new file mode 100644 index 00000000..b7fdaf27 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lrc_IQ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lrc_IR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lrc_IR.dat new file mode 100644 index 00000000..75d89752 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lrc_IR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lt.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lt.dat new file mode 100644 index 00000000..3c64a6d8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lt.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lt_LT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lt_LT.dat new file mode 100644 index 00000000..abd28f77 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lt_LT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ltg.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ltg.dat new file mode 100644 index 00000000..8937b943 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ltg.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ltg_LV.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ltg_LV.dat new file mode 100644 index 00000000..6afb1517 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ltg_LV.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lu.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lu.dat new file mode 100644 index 00000000..438bb41b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lu.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lu_CD.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lu_CD.dat new file mode 100644 index 00000000..b2f405cc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lu_CD.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/luo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/luo.dat new file mode 100644 index 00000000..1fbbe68e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/luo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/luo_KE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/luo_KE.dat new file mode 100644 index 00000000..9ad4a6a4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/luo_KE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/luy.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/luy.dat new file mode 100644 index 00000000..c82c8929 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/luy.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/luy_KE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/luy_KE.dat new file mode 100644 index 00000000..e1b346e4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/luy_KE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lv.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lv.dat new file mode 100644 index 00000000..9d4a804a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lv.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/lv_LV.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/lv_LV.dat new file mode 100644 index 00000000..37bb4e26 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/lv_LV.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mai.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mai.dat new file mode 100644 index 00000000..bde3b353 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mai.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mai_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mai_IN.dat new file mode 100644 index 00000000..288b92f8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mai_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mas.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mas.dat new file mode 100644 index 00000000..7e254ed9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mas.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mas_KE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mas_KE.dat new file mode 100644 index 00000000..b264c837 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mas_KE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mas_TZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mas_TZ.dat new file mode 100644 index 00000000..d5c71c37 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mas_TZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mdf.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mdf.dat new file mode 100644 index 00000000..5d4d3f83 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mdf.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mdf_RU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mdf_RU.dat new file mode 100644 index 00000000..94571359 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mdf_RU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mer.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mer.dat new file mode 100644 index 00000000..f6657fe1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mer.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mer_KE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mer_KE.dat new file mode 100644 index 00000000..a7c286f7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mer_KE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mfe.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mfe.dat new file mode 100644 index 00000000..10eef776 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mfe.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mfe_MU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mfe_MU.dat new file mode 100644 index 00000000..2f4715ba Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mfe_MU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mg.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mg.dat new file mode 100644 index 00000000..f91a1dd1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mg.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mg_MG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mg_MG.dat new file mode 100644 index 00000000..9e18e6cb Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mg_MG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mgh.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mgh.dat new file mode 100644 index 00000000..e8a28060 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mgh.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mgh_MZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mgh_MZ.dat new file mode 100644 index 00000000..999e8efb Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mgh_MZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mgo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mgo.dat new file mode 100644 index 00000000..edbda979 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mgo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mgo_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mgo_CM.dat new file mode 100644 index 00000000..667c0172 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mgo_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mhn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mhn.dat new file mode 100644 index 00000000..1a3303d9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mhn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mhn_IT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mhn_IT.dat new file mode 100644 index 00000000..c6472055 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mhn_IT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mi.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mi.dat new file mode 100644 index 00000000..79eb095a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mi.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mi_NZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mi_NZ.dat new file mode 100644 index 00000000..7c8b4d19 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mi_NZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mic.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mic.dat new file mode 100644 index 00000000..3ca1892e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mic.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mic_CA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mic_CA.dat new file mode 100644 index 00000000..f43465a9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mic_CA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mk.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mk.dat new file mode 100644 index 00000000..81cc0072 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mk.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mk_MK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mk_MK.dat new file mode 100644 index 00000000..33207d9a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mk_MK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ml.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ml.dat new file mode 100644 index 00000000..f5e230e3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ml.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ml_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ml_IN.dat new file mode 100644 index 00000000..5f89f111 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ml_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mn.dat new file mode 100644 index 00000000..e314523d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mn_MN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mn_MN.dat new file mode 100644 index 00000000..096cb104 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mn_MN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mn_Mong.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mn_Mong.dat new file mode 100644 index 00000000..287f8526 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mn_Mong.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mn_Mong_CN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mn_Mong_CN.dat new file mode 100644 index 00000000..284cbee8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mn_Mong_CN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mn_Mong_MN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mn_Mong_MN.dat new file mode 100644 index 00000000..ad54681c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mn_Mong_MN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mni.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mni.dat new file mode 100644 index 00000000..4ebcfc59 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mni.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mni_Beng.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mni_Beng.dat new file mode 100644 index 00000000..de357a87 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mni_Beng.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mni_Beng_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mni_Beng_IN.dat new file mode 100644 index 00000000..28787ccd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mni_Beng_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mni_Mtei.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mni_Mtei.dat new file mode 100644 index 00000000..654042f8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mni_Mtei.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mni_Mtei_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mni_Mtei_IN.dat new file mode 100644 index 00000000..28787ccd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mni_Mtei_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/moh.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/moh.dat new file mode 100644 index 00000000..c301f3f7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/moh.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/moh_CA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/moh_CA.dat new file mode 100644 index 00000000..ba2eddea Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/moh_CA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mr.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mr.dat new file mode 100644 index 00000000..2c7d0d47 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mr.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mr_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mr_IN.dat new file mode 100644 index 00000000..a57af72d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mr_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ms.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ms.dat new file mode 100644 index 00000000..b7fed4f6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ms.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ms_Arab.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ms_Arab.dat new file mode 100644 index 00000000..7ee5bee0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ms_Arab.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ms_Arab_BN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ms_Arab_BN.dat new file mode 100644 index 00000000..1785baa7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ms_Arab_BN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ms_Arab_MY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ms_Arab_MY.dat new file mode 100644 index 00000000..5df0cb75 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ms_Arab_MY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ms_BN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ms_BN.dat new file mode 100644 index 00000000..1785baa7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ms_BN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ms_ID.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ms_ID.dat new file mode 100644 index 00000000..07b0e769 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ms_ID.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ms_MY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ms_MY.dat new file mode 100644 index 00000000..5df0cb75 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ms_MY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ms_SG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ms_SG.dat new file mode 100644 index 00000000..33205113 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ms_SG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mt.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mt.dat new file mode 100644 index 00000000..502594b2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mt.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mt_MT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mt_MT.dat new file mode 100644 index 00000000..9f4731e4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mt_MT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mua.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mua.dat new file mode 100644 index 00000000..7d28adf1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mua.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mua_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mua_CM.dat new file mode 100644 index 00000000..5b71a89b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mua_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mus.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mus.dat new file mode 100644 index 00000000..b323269d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mus.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mus_US.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mus_US.dat new file mode 100644 index 00000000..a506b8bd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mus_US.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/my.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/my.dat new file mode 100644 index 00000000..8bb745b2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/my.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/my_MM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/my_MM.dat new file mode 100644 index 00000000..33bc9111 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/my_MM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/myv.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/myv.dat new file mode 100644 index 00000000..896bdd71 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/myv.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/myv_RU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/myv_RU.dat new file mode 100644 index 00000000..de64fee6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/myv_RU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mzn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mzn.dat new file mode 100644 index 00000000..51d9f132 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mzn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/mzn_IR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/mzn_IR.dat new file mode 100644 index 00000000..73990744 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/mzn_IR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/naq.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/naq.dat new file mode 100644 index 00000000..786b2ffe Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/naq.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/naq_NA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/naq_NA.dat new file mode 100644 index 00000000..82c109a0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/naq_NA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nb.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nb.dat new file mode 100644 index 00000000..5abc2536 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nb.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nb_NO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nb_NO.dat new file mode 100644 index 00000000..fc57a39a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nb_NO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nb_SJ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nb_SJ.dat new file mode 100644 index 00000000..9af3a275 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nb_SJ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nd.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nd.dat new file mode 100644 index 00000000..c13781ab Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nd.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nd_ZW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nd_ZW.dat new file mode 100644 index 00000000..bb86d3f6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nd_ZW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nds.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nds.dat new file mode 100644 index 00000000..e1c76cae Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nds.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nds_DE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nds_DE.dat new file mode 100644 index 00000000..771fba3e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nds_DE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nds_NL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nds_NL.dat new file mode 100644 index 00000000..14b9b123 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nds_NL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ne.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ne.dat new file mode 100644 index 00000000..228d625f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ne.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ne_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ne_IN.dat new file mode 100644 index 00000000..647efe6d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ne_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ne_NP.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ne_NP.dat new file mode 100644 index 00000000..5c513db9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ne_NP.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nl.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nl.dat new file mode 100644 index 00000000..9284bd74 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nl.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nl_AW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nl_AW.dat new file mode 100644 index 00000000..0bd826c4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nl_AW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nl_BE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nl_BE.dat new file mode 100644 index 00000000..4023b7a2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nl_BE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nl_BQ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nl_BQ.dat new file mode 100644 index 00000000..8f686f79 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nl_BQ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nl_CW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nl_CW.dat new file mode 100644 index 00000000..ea3eb94f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nl_CW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nl_NL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nl_NL.dat new file mode 100644 index 00000000..c686473e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nl_NL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nl_SR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nl_SR.dat new file mode 100644 index 00000000..6ffb0f92 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nl_SR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nl_SX.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nl_SX.dat new file mode 100644 index 00000000..6932aa19 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nl_SX.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nmg.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nmg.dat new file mode 100644 index 00000000..1e508106 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nmg.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nmg_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nmg_CM.dat new file mode 100644 index 00000000..f675485d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nmg_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nn.dat new file mode 100644 index 00000000..fde5bfc9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nn_NO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nn_NO.dat new file mode 100644 index 00000000..0e233b7d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nn_NO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nnh.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nnh.dat new file mode 100644 index 00000000..23896790 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nnh.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nnh_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nnh_CM.dat new file mode 100644 index 00000000..7722c099 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nnh_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/no.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/no.dat new file mode 100644 index 00000000..6fad909d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/no.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nqo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nqo.dat new file mode 100644 index 00000000..1f24a4ec Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nqo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nqo_GN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nqo_GN.dat new file mode 100644 index 00000000..3cdbcbcd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nqo_GN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nr.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nr.dat new file mode 100644 index 00000000..1306c925 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nr.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nr_ZA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nr_ZA.dat new file mode 100644 index 00000000..ff0ba0b0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nr_ZA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nso.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nso.dat new file mode 100644 index 00000000..25529c1a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nso.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nso_ZA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nso_ZA.dat new file mode 100644 index 00000000..42f9efca Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nso_ZA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nus.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nus.dat new file mode 100644 index 00000000..6bc93828 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nus.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nus_SS.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nus_SS.dat new file mode 100644 index 00000000..b8b86575 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nus_SS.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nv.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nv.dat new file mode 100644 index 00000000..146e037b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nv.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nv_US.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nv_US.dat new file mode 100644 index 00000000..2c78aa76 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nv_US.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ny.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ny.dat new file mode 100644 index 00000000..e1fc0613 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ny.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ny_MW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ny_MW.dat new file mode 100644 index 00000000..00392558 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ny_MW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nyn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nyn.dat new file mode 100644 index 00000000..ccd34096 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nyn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/nyn_UG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/nyn_UG.dat new file mode 100644 index 00000000..2e51c77b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/nyn_UG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/oc.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/oc.dat new file mode 100644 index 00000000..b678c283 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/oc.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/oc_ES.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/oc_ES.dat new file mode 100644 index 00000000..936a4bac Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/oc_ES.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/oc_FR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/oc_FR.dat new file mode 100644 index 00000000..613abbe7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/oc_FR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/om.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/om.dat new file mode 100644 index 00000000..c83455ff Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/om.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/om_ET.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/om_ET.dat new file mode 100644 index 00000000..82c438b4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/om_ET.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/om_KE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/om_KE.dat new file mode 100644 index 00000000..daf3734d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/om_KE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/or.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/or.dat new file mode 100644 index 00000000..7368025a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/or.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/or_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/or_IN.dat new file mode 100644 index 00000000..12b4f1bc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/or_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/os.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/os.dat new file mode 100644 index 00000000..a90e8198 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/os.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/os_GE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/os_GE.dat new file mode 100644 index 00000000..205ddbf2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/os_GE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/os_RU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/os_RU.dat new file mode 100644 index 00000000..f51d3d91 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/os_RU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/osa.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/osa.dat new file mode 100644 index 00000000..eb1d4ff4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/osa.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/osa_US.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/osa_US.dat new file mode 100644 index 00000000..03c11002 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/osa_US.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pa.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pa.dat new file mode 100644 index 00000000..1186ec2f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pa.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pa_Arab.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pa_Arab.dat new file mode 100644 index 00000000..0d02acbd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pa_Arab.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pa_Arab_PK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pa_Arab_PK.dat new file mode 100644 index 00000000..eefdeba1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pa_Arab_PK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pa_Guru.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pa_Guru.dat new file mode 100644 index 00000000..ed8c6b6f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pa_Guru.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pa_Guru_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pa_Guru_IN.dat new file mode 100644 index 00000000..6af3517d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pa_Guru_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pap.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pap.dat new file mode 100644 index 00000000..68b8dc46 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pap.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pap_AW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pap_AW.dat new file mode 100644 index 00000000..973cc763 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pap_AW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pap_CW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pap_CW.dat new file mode 100644 index 00000000..3c481d47 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pap_CW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pcm.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pcm.dat new file mode 100644 index 00000000..97447ca5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pcm.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pcm_NG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pcm_NG.dat new file mode 100644 index 00000000..7ee82746 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pcm_NG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pis.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pis.dat new file mode 100644 index 00000000..55726a02 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pis.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pis_SB.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pis_SB.dat new file mode 100644 index 00000000..3b1f17cd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pis_SB.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pl.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pl.dat new file mode 100644 index 00000000..65ac0a48 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pl.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pl_PL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pl_PL.dat new file mode 100644 index 00000000..7a4339ab Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pl_PL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/prg.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/prg.dat new file mode 100644 index 00000000..06219914 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/prg.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/prg_PL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/prg_PL.dat new file mode 100644 index 00000000..7b197051 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/prg_PL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ps.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ps.dat new file mode 100644 index 00000000..ea7e2e52 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ps.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ps_AF.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ps_AF.dat new file mode 100644 index 00000000..94a50bff Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ps_AF.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ps_PK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ps_PK.dat new file mode 100644 index 00000000..691557ea Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ps_PK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pt.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pt.dat new file mode 100644 index 00000000..aefae529 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pt.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pt_AO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_AO.dat new file mode 100644 index 00000000..4cd1fbda Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_AO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pt_BR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_BR.dat new file mode 100644 index 00000000..22952c77 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_BR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pt_CH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_CH.dat new file mode 100644 index 00000000..26842865 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_CH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pt_CV.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_CV.dat new file mode 100644 index 00000000..78e2bbcf Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_CV.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pt_GQ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_GQ.dat new file mode 100644 index 00000000..88968337 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_GQ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pt_GW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_GW.dat new file mode 100644 index 00000000..d44108a1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_GW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pt_LU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_LU.dat new file mode 100644 index 00000000..af0b45b3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_LU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pt_MO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_MO.dat new file mode 100644 index 00000000..4fcee099 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_MO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pt_MZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_MZ.dat new file mode 100644 index 00000000..198075e8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_MZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pt_PT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_PT.dat new file mode 100644 index 00000000..aa6a6ccb Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_PT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pt_ST.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_ST.dat new file mode 100644 index 00000000..cdccab77 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_ST.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/pt_TL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_TL.dat new file mode 100644 index 00000000..7bb756cd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/pt_TL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/qu.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/qu.dat new file mode 100644 index 00000000..85cc9eca Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/qu.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/qu_BO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/qu_BO.dat new file mode 100644 index 00000000..bc994ff7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/qu_BO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/qu_EC.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/qu_EC.dat new file mode 100644 index 00000000..8f7f46ad Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/qu_EC.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/qu_PE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/qu_PE.dat new file mode 100644 index 00000000..07aacf94 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/qu_PE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/quc.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/quc.dat new file mode 100644 index 00000000..f7e02d8a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/quc.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/quc_GT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/quc_GT.dat new file mode 100644 index 00000000..c14f849c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/quc_GT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/raj.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/raj.dat new file mode 100644 index 00000000..8f4477fa Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/raj.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/raj_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/raj_IN.dat new file mode 100644 index 00000000..e56e7502 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/raj_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/rhg.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/rhg.dat new file mode 100644 index 00000000..192bbff0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/rhg.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/rhg_Rohg.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/rhg_Rohg.dat new file mode 100644 index 00000000..01b82ecf Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/rhg_Rohg.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/rhg_Rohg_BD.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/rhg_Rohg_BD.dat new file mode 100644 index 00000000..cc14c7f8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/rhg_Rohg_BD.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/rhg_Rohg_MM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/rhg_Rohg_MM.dat new file mode 100644 index 00000000..6cfde861 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/rhg_Rohg_MM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/rif.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/rif.dat new file mode 100644 index 00000000..b5efc905 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/rif.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/rif_MA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/rif_MA.dat new file mode 100644 index 00000000..9b7f4342 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/rif_MA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/rm.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/rm.dat new file mode 100644 index 00000000..2398d907 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/rm.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/rm_CH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/rm_CH.dat new file mode 100644 index 00000000..26eccfc3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/rm_CH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/rn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/rn.dat new file mode 100644 index 00000000..f21edd4d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/rn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/rn_BI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/rn_BI.dat new file mode 100644 index 00000000..d1ef9755 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/rn_BI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ro.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ro.dat new file mode 100644 index 00000000..efa69b04 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ro.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ro_MD.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ro_MD.dat new file mode 100644 index 00000000..82b8bb6d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ro_MD.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ro_RO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ro_RO.dat new file mode 100644 index 00000000..3517a610 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ro_RO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/rof.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/rof.dat new file mode 100644 index 00000000..6f0e5514 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/rof.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/rof_TZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/rof_TZ.dat new file mode 100644 index 00000000..d7ffaeb0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/rof_TZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/root.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/root.dat new file mode 100644 index 00000000..0557e0d2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/root.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ru.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ru.dat new file mode 100644 index 00000000..50c78459 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ru.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ru_BY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ru_BY.dat new file mode 100644 index 00000000..e8ef98d4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ru_BY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ru_KG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ru_KG.dat new file mode 100644 index 00000000..f3e59502 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ru_KG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ru_KZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ru_KZ.dat new file mode 100644 index 00000000..b66765a5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ru_KZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ru_MD.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ru_MD.dat new file mode 100644 index 00000000..d7041994 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ru_MD.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ru_RU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ru_RU.dat new file mode 100644 index 00000000..a1183570 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ru_RU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ru_UA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ru_UA.dat new file mode 100644 index 00000000..cebd8c23 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ru_UA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/rw.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/rw.dat new file mode 100644 index 00000000..d767b309 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/rw.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/rw_RW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/rw_RW.dat new file mode 100644 index 00000000..4ade0dd6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/rw_RW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/rwk.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/rwk.dat new file mode 100644 index 00000000..fd69cc07 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/rwk.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/rwk_TZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/rwk_TZ.dat new file mode 100644 index 00000000..fedafc3e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/rwk_TZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sa.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sa.dat new file mode 100644 index 00000000..76f67c4d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sa.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sa_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sa_IN.dat new file mode 100644 index 00000000..31781751 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sa_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sah.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sah.dat new file mode 100644 index 00000000..bad972f9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sah.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sah_RU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sah_RU.dat new file mode 100644 index 00000000..44075f9f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sah_RU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/saq.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/saq.dat new file mode 100644 index 00000000..95320833 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/saq.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/saq_KE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/saq_KE.dat new file mode 100644 index 00000000..ffd91c35 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/saq_KE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sat.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sat.dat new file mode 100644 index 00000000..3a63c743 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sat.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sat_Deva.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sat_Deva.dat new file mode 100644 index 00000000..9a1af369 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sat_Deva.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sat_Deva_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sat_Deva_IN.dat new file mode 100644 index 00000000..1781be05 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sat_Deva_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sat_Olck.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sat_Olck.dat new file mode 100644 index 00000000..444b3584 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sat_Olck.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sat_Olck_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sat_Olck_IN.dat new file mode 100644 index 00000000..1781be05 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sat_Olck_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sbp.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sbp.dat new file mode 100644 index 00000000..e1ea58b9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sbp.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sbp_TZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sbp_TZ.dat new file mode 100644 index 00000000..2292ffb6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sbp_TZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sc.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sc.dat new file mode 100644 index 00000000..f3609d5c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sc.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sc_IT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sc_IT.dat new file mode 100644 index 00000000..12c8c563 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sc_IT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/scn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/scn.dat new file mode 100644 index 00000000..c8ebaec1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/scn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/scn_IT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/scn_IT.dat new file mode 100644 index 00000000..0e8b90ec Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/scn_IT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sd.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sd.dat new file mode 100644 index 00000000..410ea581 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sd.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sd_Arab.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sd_Arab.dat new file mode 100644 index 00000000..8003f26f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sd_Arab.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sd_Arab_PK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sd_Arab_PK.dat new file mode 100644 index 00000000..42f6290f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sd_Arab_PK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sd_Deva.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sd_Deva.dat new file mode 100644 index 00000000..5193db74 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sd_Deva.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sd_Deva_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sd_Deva_IN.dat new file mode 100644 index 00000000..21aee01c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sd_Deva_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sdh.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sdh.dat new file mode 100644 index 00000000..f51767d1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sdh.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sdh_IQ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sdh_IQ.dat new file mode 100644 index 00000000..774d04d5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sdh_IQ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sdh_IR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sdh_IR.dat new file mode 100644 index 00000000..6e83d1f1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sdh_IR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/se.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/se.dat new file mode 100644 index 00000000..8da45922 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/se.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/se_FI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/se_FI.dat new file mode 100644 index 00000000..2f927646 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/se_FI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/se_NO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/se_NO.dat new file mode 100644 index 00000000..09eca0d2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/se_NO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/se_SE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/se_SE.dat new file mode 100644 index 00000000..632b54e7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/se_SE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/seh.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/seh.dat new file mode 100644 index 00000000..e61f09ba Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/seh.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/seh_MZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/seh_MZ.dat new file mode 100644 index 00000000..ba30e958 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/seh_MZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ses.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ses.dat new file mode 100644 index 00000000..f9858d96 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ses.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ses_ML.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ses_ML.dat new file mode 100644 index 00000000..a0010889 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ses_ML.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sg.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sg.dat new file mode 100644 index 00000000..8ce0d409 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sg.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sg_CF.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sg_CF.dat new file mode 100644 index 00000000..0ca555a6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sg_CF.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/shi.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/shi.dat new file mode 100644 index 00000000..f3d10667 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/shi.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/shi_Latn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/shi_Latn.dat new file mode 100644 index 00000000..fecc8217 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/shi_Latn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/shi_Latn_MA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/shi_Latn_MA.dat new file mode 100644 index 00000000..14955853 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/shi_Latn_MA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/shi_Tfng.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/shi_Tfng.dat new file mode 100644 index 00000000..63ebefe7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/shi_Tfng.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/shi_Tfng_MA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/shi_Tfng_MA.dat new file mode 100644 index 00000000..14955853 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/shi_Tfng_MA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/shn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/shn.dat new file mode 100644 index 00000000..7eb56999 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/shn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/shn_MM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/shn_MM.dat new file mode 100644 index 00000000..3cace292 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/shn_MM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/shn_TH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/shn_TH.dat new file mode 100644 index 00000000..cc5364a2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/shn_TH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/si.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/si.dat new file mode 100644 index 00000000..fba9aa9f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/si.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/si_LK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/si_LK.dat new file mode 100644 index 00000000..eb323029 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/si_LK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sid.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sid.dat new file mode 100644 index 00000000..3445845d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sid.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sid_ET.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sid_ET.dat new file mode 100644 index 00000000..1be7a4a9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sid_ET.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sk.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sk.dat new file mode 100644 index 00000000..413a7038 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sk.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sk_SK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sk_SK.dat new file mode 100644 index 00000000..4ba6c841 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sk_SK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/skr.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/skr.dat new file mode 100644 index 00000000..d9fc4290 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/skr.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/skr_PK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/skr_PK.dat new file mode 100644 index 00000000..8db57bc5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/skr_PK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sl.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sl.dat new file mode 100644 index 00000000..9e805fb2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sl.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sl_SI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sl_SI.dat new file mode 100644 index 00000000..b4955823 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sl_SI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sma.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sma.dat new file mode 100644 index 00000000..925c9565 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sma.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sma_NO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sma_NO.dat new file mode 100644 index 00000000..325e16cc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sma_NO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sma_SE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sma_SE.dat new file mode 100644 index 00000000..b9cb4e75 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sma_SE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/smj.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/smj.dat new file mode 100644 index 00000000..377b17c7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/smj.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/smj_NO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/smj_NO.dat new file mode 100644 index 00000000..7cc36547 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/smj_NO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/smj_SE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/smj_SE.dat new file mode 100644 index 00000000..78aa9de9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/smj_SE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/smn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/smn.dat new file mode 100644 index 00000000..f63177af Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/smn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/smn_FI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/smn_FI.dat new file mode 100644 index 00000000..a88e29d9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/smn_FI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sms.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sms.dat new file mode 100644 index 00000000..590f25ad Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sms.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sms_FI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sms_FI.dat new file mode 100644 index 00000000..91bdf45e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sms_FI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sn.dat new file mode 100644 index 00000000..cb8e1bd8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sn_ZW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sn_ZW.dat new file mode 100644 index 00000000..4dd1932d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sn_ZW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/so.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/so.dat new file mode 100644 index 00000000..cfae4764 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/so.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/so_DJ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/so_DJ.dat new file mode 100644 index 00000000..756644b6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/so_DJ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/so_ET.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/so_ET.dat new file mode 100644 index 00000000..abd6c125 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/so_ET.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/so_KE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/so_KE.dat new file mode 100644 index 00000000..2c422f93 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/so_KE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/so_SO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/so_SO.dat new file mode 100644 index 00000000..aa7955c5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/so_SO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sq.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sq.dat new file mode 100644 index 00000000..aa0a941b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sq.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sq_AL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sq_AL.dat new file mode 100644 index 00000000..5a5320fc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sq_AL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sq_MK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sq_MK.dat new file mode 100644 index 00000000..32565ce7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sq_MK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sq_XK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sq_XK.dat new file mode 100644 index 00000000..bbe0062e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sq_XK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sr.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sr.dat new file mode 100644 index 00000000..0075f8e1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sr.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Cyrl.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Cyrl.dat new file mode 100644 index 00000000..89154355 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Cyrl.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Cyrl_BA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Cyrl_BA.dat new file mode 100644 index 00000000..6a0aaf69 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Cyrl_BA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Cyrl_ME.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Cyrl_ME.dat new file mode 100644 index 00000000..357ab293 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Cyrl_ME.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Cyrl_RS.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Cyrl_RS.dat new file mode 100644 index 00000000..b2633098 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Cyrl_RS.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Cyrl_XK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Cyrl_XK.dat new file mode 100644 index 00000000..b574d058 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Cyrl_XK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Latn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Latn.dat new file mode 100644 index 00000000..efd38936 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Latn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Latn_BA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Latn_BA.dat new file mode 100644 index 00000000..d1be6b89 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Latn_BA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Latn_ME.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Latn_ME.dat new file mode 100644 index 00000000..b8b9a5b4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Latn_ME.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Latn_RS.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Latn_RS.dat new file mode 100644 index 00000000..b2633098 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Latn_RS.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Latn_XK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Latn_XK.dat new file mode 100644 index 00000000..778db676 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sr_Latn_XK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ss.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ss.dat new file mode 100644 index 00000000..91c10e2c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ss.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ss_SZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ss_SZ.dat new file mode 100644 index 00000000..b4fcf779 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ss_SZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ss_ZA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ss_ZA.dat new file mode 100644 index 00000000..67315698 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ss_ZA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ssy.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ssy.dat new file mode 100644 index 00000000..a42b5e83 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ssy.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ssy_ER.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ssy_ER.dat new file mode 100644 index 00000000..1d28855a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ssy_ER.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/st.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/st.dat new file mode 100644 index 00000000..26d9b21a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/st.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/st_LS.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/st_LS.dat new file mode 100644 index 00000000..50eef784 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/st_LS.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/st_ZA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/st_ZA.dat new file mode 100644 index 00000000..9539d624 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/st_ZA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/su.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/su.dat new file mode 100644 index 00000000..011036d5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/su.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/su_Latn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/su_Latn.dat new file mode 100644 index 00000000..79e460ed Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/su_Latn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/su_Latn_ID.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/su_Latn_ID.dat new file mode 100644 index 00000000..5c45bdce Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/su_Latn_ID.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sv.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sv.dat new file mode 100644 index 00000000..1bb77229 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sv.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sv_AX.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sv_AX.dat new file mode 100644 index 00000000..bfb414d6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sv_AX.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sv_FI.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sv_FI.dat new file mode 100644 index 00000000..49deaf31 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sv_FI.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sv_SE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sv_SE.dat new file mode 100644 index 00000000..57a602a0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sv_SE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sw.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sw.dat new file mode 100644 index 00000000..df6f886f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sw.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sw_CD.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sw_CD.dat new file mode 100644 index 00000000..b1a7f0e5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sw_CD.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sw_KE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sw_KE.dat new file mode 100644 index 00000000..f19305af Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sw_KE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sw_TZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sw_TZ.dat new file mode 100644 index 00000000..a267886d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sw_TZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/sw_UG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/sw_UG.dat new file mode 100644 index 00000000..0d160d55 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/sw_UG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/syr.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/syr.dat new file mode 100644 index 00000000..6cfeef61 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/syr.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/syr_IQ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/syr_IQ.dat new file mode 100644 index 00000000..7821e924 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/syr_IQ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/syr_SY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/syr_SY.dat new file mode 100644 index 00000000..1edf041c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/syr_SY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/szl.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/szl.dat new file mode 100644 index 00000000..4a767b48 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/szl.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/szl_PL.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/szl_PL.dat new file mode 100644 index 00000000..f0b99548 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/szl_PL.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ta.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ta.dat new file mode 100644 index 00000000..7559ff89 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ta.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ta_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ta_IN.dat new file mode 100644 index 00000000..cd08ee8f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ta_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ta_LK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ta_LK.dat new file mode 100644 index 00000000..f473b057 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ta_LK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ta_MY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ta_MY.dat new file mode 100644 index 00000000..8e791e92 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ta_MY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ta_SG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ta_SG.dat new file mode 100644 index 00000000..9f1131cc Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ta_SG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/te.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/te.dat new file mode 100644 index 00000000..436255df Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/te.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/te_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/te_IN.dat new file mode 100644 index 00000000..d76a4160 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/te_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/teo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/teo.dat new file mode 100644 index 00000000..d4cfa1af Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/teo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/teo_KE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/teo_KE.dat new file mode 100644 index 00000000..66b33cad Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/teo_KE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/teo_UG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/teo_UG.dat new file mode 100644 index 00000000..e81befad Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/teo_UG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tg.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tg.dat new file mode 100644 index 00000000..03a72512 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tg.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tg_TJ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tg_TJ.dat new file mode 100644 index 00000000..1982383d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tg_TJ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/th.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/th.dat new file mode 100644 index 00000000..e6884133 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/th.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/th_TH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/th_TH.dat new file mode 100644 index 00000000..c2a4c994 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/th_TH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ti.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ti.dat new file mode 100644 index 00000000..ef311739 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ti.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ti_ER.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ti_ER.dat new file mode 100644 index 00000000..41ca554c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ti_ER.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ti_ET.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ti_ET.dat new file mode 100644 index 00000000..50a447b5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ti_ET.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tig.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tig.dat new file mode 100644 index 00000000..ed1b2a79 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tig.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tig_ER.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tig_ER.dat new file mode 100644 index 00000000..5632fa6f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tig_ER.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tk.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tk.dat new file mode 100644 index 00000000..46c4af00 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tk.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tk_TM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tk_TM.dat new file mode 100644 index 00000000..01e7cf61 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tk_TM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tn.dat new file mode 100644 index 00000000..65c0267f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tn_BW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tn_BW.dat new file mode 100644 index 00000000..5da0cc5d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tn_BW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tn_ZA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tn_ZA.dat new file mode 100644 index 00000000..a559a128 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tn_ZA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/to.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/to.dat new file mode 100644 index 00000000..7268ff20 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/to.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/to_TO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/to_TO.dat new file mode 100644 index 00000000..8cdedf55 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/to_TO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tok.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tok.dat new file mode 100644 index 00000000..a8b7a886 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tok.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tok_001.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tok_001.dat new file mode 100644 index 00000000..ea39a455 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tok_001.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tpi.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tpi.dat new file mode 100644 index 00000000..0434b6fa Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tpi.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tpi_PG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tpi_PG.dat new file mode 100644 index 00000000..9057c6e4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tpi_PG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tr.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tr.dat new file mode 100644 index 00000000..1bd0819f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tr.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tr_CY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tr_CY.dat new file mode 100644 index 00000000..a0bbd6f0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tr_CY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tr_TR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tr_TR.dat new file mode 100644 index 00000000..8c20ebb6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tr_TR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/trv.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/trv.dat new file mode 100644 index 00000000..1b784bc8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/trv.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/trv_TW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/trv_TW.dat new file mode 100644 index 00000000..da4e716d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/trv_TW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/trw.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/trw.dat new file mode 100644 index 00000000..e4a66bd8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/trw.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/trw_PK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/trw_PK.dat new file mode 100644 index 00000000..593e957e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/trw_PK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ts.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ts.dat new file mode 100644 index 00000000..900383d3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ts.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ts_ZA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ts_ZA.dat new file mode 100644 index 00000000..53640320 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ts_ZA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tt.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tt.dat new file mode 100644 index 00000000..28fe370c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tt.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tt_RU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tt_RU.dat new file mode 100644 index 00000000..88ba72f3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tt_RU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/twq.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/twq.dat new file mode 100644 index 00000000..2f99122f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/twq.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/twq_NE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/twq_NE.dat new file mode 100644 index 00000000..be95079d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/twq_NE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tyv.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tyv.dat new file mode 100644 index 00000000..fe1564b9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tyv.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tyv_RU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tyv_RU.dat new file mode 100644 index 00000000..92ab47b5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tyv_RU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tzm.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tzm.dat new file mode 100644 index 00000000..14f6276b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tzm.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/tzm_MA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/tzm_MA.dat new file mode 100644 index 00000000..b74c9377 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/tzm_MA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ug.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ug.dat new file mode 100644 index 00000000..28cd5d09 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ug.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ug_CN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ug_CN.dat new file mode 100644 index 00000000..997752b7 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ug_CN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/uk.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/uk.dat new file mode 100644 index 00000000..908f4870 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/uk.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/uk_UA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/uk_UA.dat new file mode 100644 index 00000000..e1ff80ca Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/uk_UA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ur.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ur.dat new file mode 100644 index 00000000..dd035ccd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ur.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ur_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ur_IN.dat new file mode 100644 index 00000000..582a1fc3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ur_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ur_PK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ur_PK.dat new file mode 100644 index 00000000..5a3d6cec Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ur_PK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/uz.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/uz.dat new file mode 100644 index 00000000..267dd64e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/uz.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Arab.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Arab.dat new file mode 100644 index 00000000..0ed5c3ae Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Arab.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Arab_AF.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Arab_AF.dat new file mode 100644 index 00000000..d7355f66 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Arab_AF.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Cyrl.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Cyrl.dat new file mode 100644 index 00000000..382b3cc0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Cyrl.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Cyrl_UZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Cyrl_UZ.dat new file mode 100644 index 00000000..0e9fb275 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Cyrl_UZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Latn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Latn.dat new file mode 100644 index 00000000..217235e4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Latn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Latn_UZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Latn_UZ.dat new file mode 100644 index 00000000..0e9fb275 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/uz_Latn_UZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/vai.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/vai.dat new file mode 100644 index 00000000..c69f2cba Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/vai.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/vai_Latn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/vai_Latn.dat new file mode 100644 index 00000000..849c491d Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/vai_Latn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/vai_Latn_LR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/vai_Latn_LR.dat new file mode 100644 index 00000000..f3bc7f03 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/vai_Latn_LR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/vai_Vaii.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/vai_Vaii.dat new file mode 100644 index 00000000..6519e24e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/vai_Vaii.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/vai_Vaii_LR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/vai_Vaii_LR.dat new file mode 100644 index 00000000..f3bc7f03 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/vai_Vaii_LR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ve.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ve.dat new file mode 100644 index 00000000..517c7a44 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ve.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/ve_ZA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/ve_ZA.dat new file mode 100644 index 00000000..81c087b3 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/ve_ZA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/vec.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/vec.dat new file mode 100644 index 00000000..59dcdec1 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/vec.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/vec_IT.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/vec_IT.dat new file mode 100644 index 00000000..a1f0238c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/vec_IT.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/vi.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/vi.dat new file mode 100644 index 00000000..e887dca5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/vi.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/vi_VN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/vi_VN.dat new file mode 100644 index 00000000..d1942c8e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/vi_VN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/vmw.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/vmw.dat new file mode 100644 index 00000000..7116de27 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/vmw.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/vmw_MZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/vmw_MZ.dat new file mode 100644 index 00000000..21999745 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/vmw_MZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/vo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/vo.dat new file mode 100644 index 00000000..00137183 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/vo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/vo_001.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/vo_001.dat new file mode 100644 index 00000000..e06ed1c8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/vo_001.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/vun.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/vun.dat new file mode 100644 index 00000000..8fd175bb Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/vun.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/vun_TZ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/vun_TZ.dat new file mode 100644 index 00000000..a29794a6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/vun_TZ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/wa.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/wa.dat new file mode 100644 index 00000000..6699fd21 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/wa.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/wa_BE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/wa_BE.dat new file mode 100644 index 00000000..63f30c15 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/wa_BE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/wae.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/wae.dat new file mode 100644 index 00000000..7deede2e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/wae.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/wae_CH.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/wae_CH.dat new file mode 100644 index 00000000..7e10feea Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/wae_CH.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/wal.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/wal.dat new file mode 100644 index 00000000..99770a06 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/wal.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/wal_ET.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/wal_ET.dat new file mode 100644 index 00000000..fa5fc6d0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/wal_ET.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/wbp.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/wbp.dat new file mode 100644 index 00000000..22bcdd77 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/wbp.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/wbp_AU.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/wbp_AU.dat new file mode 100644 index 00000000..3b6b5b1e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/wbp_AU.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/wo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/wo.dat new file mode 100644 index 00000000..ed881e93 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/wo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/wo_SN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/wo_SN.dat new file mode 100644 index 00000000..ce673ad4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/wo_SN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/xh.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/xh.dat new file mode 100644 index 00000000..487e18a0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/xh.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/xh_ZA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/xh_ZA.dat new file mode 100644 index 00000000..68d71668 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/xh_ZA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/xnr.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/xnr.dat new file mode 100644 index 00000000..3b0118bd Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/xnr.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/xnr_IN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/xnr_IN.dat new file mode 100644 index 00000000..360f58c4 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/xnr_IN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/xog.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/xog.dat new file mode 100644 index 00000000..365dd8aa Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/xog.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/xog_UG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/xog_UG.dat new file mode 100644 index 00000000..b5fb4da0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/xog_UG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/yav.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/yav.dat new file mode 100644 index 00000000..547b6b91 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/yav.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/yav_CM.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/yav_CM.dat new file mode 100644 index 00000000..2a0cbb26 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/yav_CM.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/yi.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/yi.dat new file mode 100644 index 00000000..c39639f0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/yi.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/yi_UA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/yi_UA.dat new file mode 100644 index 00000000..9f967db0 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/yi_UA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/yo.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/yo.dat new file mode 100644 index 00000000..a59c6ef9 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/yo.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/yo_BJ.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/yo_BJ.dat new file mode 100644 index 00000000..b4239959 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/yo_BJ.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/yo_NG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/yo_NG.dat new file mode 100644 index 00000000..e473a96c Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/yo_NG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/yrl.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/yrl.dat new file mode 100644 index 00000000..946ffccf Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/yrl.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/yrl_BR.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/yrl_BR.dat new file mode 100644 index 00000000..bf1012ee Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/yrl_BR.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/yrl_CO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/yrl_CO.dat new file mode 100644 index 00000000..c8a8c22e Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/yrl_CO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/yrl_VE.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/yrl_VE.dat new file mode 100644 index 00000000..d8ebd8ed Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/yrl_VE.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/yue.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/yue.dat new file mode 100644 index 00000000..e2fbe58b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/yue.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/yue_Hans.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/yue_Hans.dat new file mode 100644 index 00000000..73446b87 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/yue_Hans.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/yue_Hans_CN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/yue_Hans_CN.dat new file mode 100644 index 00000000..1ba61971 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/yue_Hans_CN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/yue_Hant.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/yue_Hant.dat new file mode 100644 index 00000000..d6740346 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/yue_Hant.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/yue_Hant_CN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/yue_Hant_CN.dat new file mode 100644 index 00000000..1ba61971 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/yue_Hant_CN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/yue_Hant_HK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/yue_Hant_HK.dat new file mode 100644 index 00000000..853efcbb Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/yue_Hant_HK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/za.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/za.dat new file mode 100644 index 00000000..174e6839 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/za.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/za_CN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/za_CN.dat new file mode 100644 index 00000000..f0cbb5db Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/za_CN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zgh.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zgh.dat new file mode 100644 index 00000000..a0c342c2 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zgh.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zgh_MA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zgh_MA.dat new file mode 100644 index 00000000..8475afed Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zgh_MA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zh.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zh.dat new file mode 100644 index 00000000..ddf76651 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zh.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans.dat new file mode 100644 index 00000000..c5c94bf8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans_CN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans_CN.dat new file mode 100644 index 00000000..09d08c10 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans_CN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans_HK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans_HK.dat new file mode 100644 index 00000000..2396da0a Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans_HK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans_MO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans_MO.dat new file mode 100644 index 00000000..4f0232e5 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans_MO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans_MY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans_MY.dat new file mode 100644 index 00000000..bddaab92 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans_MY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans_SG.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans_SG.dat new file mode 100644 index 00000000..646b57d6 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hans_SG.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hant.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hant.dat new file mode 100644 index 00000000..fbfcef27 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hant.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hant_HK.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hant_HK.dat new file mode 100644 index 00000000..d8ef238f Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hant_HK.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hant_MO.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hant_MO.dat new file mode 100644 index 00000000..2e468357 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hant_MO.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hant_MY.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hant_MY.dat new file mode 100644 index 00000000..1198fb41 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hant_MY.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hant_TW.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hant_TW.dat new file mode 100644 index 00000000..657e8885 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Hant_TW.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Latn.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Latn.dat new file mode 100644 index 00000000..c5c94bf8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Latn.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Latn_CN.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Latn_CN.dat new file mode 100644 index 00000000..09d08c10 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zh_Latn_CN.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zu.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zu.dat new file mode 100644 index 00000000..8c98018b Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zu.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/locale-data/zu_ZA.dat b/.venv/lib/python3.9/site-packages/babel/locale-data/zu_ZA.dat new file mode 100644 index 00000000..026694ca Binary files /dev/null and b/.venv/lib/python3.9/site-packages/babel/locale-data/zu_ZA.dat differ diff --git a/.venv/lib/python3.9/site-packages/babel/localedata.py b/.venv/lib/python3.9/site-packages/babel/localedata.py new file mode 100644 index 00000000..59f1db09 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/localedata.py @@ -0,0 +1,278 @@ +""" + babel.localedata + ~~~~~~~~~~~~~~~~ + + Low-level locale data access. + + :note: The `Locale` class, which uses this module under the hood, provides a + more convenient interface for accessing the locale data. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +from __future__ import annotations + +import os +import pickle +import re +import sys +import threading +from collections import abc +from collections.abc import Iterator, Mapping, MutableMapping +from functools import lru_cache +from itertools import chain +from typing import Any + +_cache: dict[str, Any] = {} +_cache_lock = threading.RLock() +_dirname = os.path.join(os.path.dirname(__file__), 'locale-data') +_windows_reserved_name_re = re.compile("^(con|prn|aux|nul|com[0-9]|lpt[0-9])$", re.I) + + +def normalize_locale(name: str) -> str | None: + """Normalize a locale ID by stripping spaces and apply proper casing. + + Returns the normalized locale ID string or `None` if the ID is not + recognized. + """ + if not name or not isinstance(name, str): + return None + name = name.strip().lower() + for locale_id in chain.from_iterable([_cache, locale_identifiers()]): + if name == locale_id.lower(): + return locale_id + + +def resolve_locale_filename(name: os.PathLike[str] | str) -> str: + """ + Resolve a locale identifier to a `.dat` path on disk. + """ + + # Clean up any possible relative paths. + name = os.path.basename(name) + + # Ensure we're not left with one of the Windows reserved names. + if sys.platform == "win32" and _windows_reserved_name_re.match(os.path.splitext(name)[0]): + raise ValueError(f"Name {name} is invalid on Windows") + + # Build the path. + return os.path.join(_dirname, f"{name}.dat") + + +def exists(name: str) -> bool: + """Check whether locale data is available for the given locale. + + Returns `True` if it exists, `False` otherwise. + + :param name: the locale identifier string + """ + if not name or not isinstance(name, str): + return False + if name in _cache: + return True + file_found = os.path.exists(resolve_locale_filename(name)) + return True if file_found else bool(normalize_locale(name)) + + +@lru_cache(maxsize=None) +def locale_identifiers() -> list[str]: + """Return a list of all locale identifiers for which locale data is + available. + + This data is cached after the first invocation. + You can clear the cache by calling `locale_identifiers.cache_clear()`. + + .. versionadded:: 0.8.1 + + :return: a list of locale identifiers (strings) + """ + return [ + stem + for stem, extension in + (os.path.splitext(filename) for filename in os.listdir(_dirname)) + if extension == '.dat' and stem != 'root' + ] + + +def _is_non_likely_script(name: str) -> bool: + """Return whether the locale is of the form ``lang_Script``, + and the script is not the likely script for the language. + + This implements the behavior of the ``nonlikelyScript`` value of the + ``localRules`` attribute for parent locales added in CLDR 45. + """ + from babel.core import get_global, parse_locale + + try: + lang, territory, script, variant, *rest = parse_locale(name) + except ValueError: + return False + + if lang and script and not territory and not variant and not rest: + likely_subtag = get_global('likely_subtags').get(lang) + _, _, likely_script, *_ = parse_locale(likely_subtag) + return script != likely_script + return False + + +def load(name: os.PathLike[str] | str, merge_inherited: bool = True) -> dict[str, Any]: + """Load the locale data for the given locale. + + The locale data is a dictionary that contains much of the data defined by + the Common Locale Data Repository (CLDR). This data is stored as a + collection of pickle files inside the ``babel`` package. + + >>> d = load('en_US') + >>> d['languages']['sv'] + u'Swedish' + + Note that the results are cached, and subsequent requests for the same + locale return the same dictionary: + + >>> d1 = load('en_US') + >>> d2 = load('en_US') + >>> d1 is d2 + True + + :param name: the locale identifier string (or "root") + :param merge_inherited: whether the inherited data should be merged into + the data of the requested locale + :raise `IOError`: if no locale data file is found for the given locale + identifier, or one of the locales it inherits from + """ + name = os.path.basename(name) + _cache_lock.acquire() + try: + data = _cache.get(name) + if not data: + # Load inherited data + if name == 'root' or not merge_inherited: + data = {} + else: + from babel.core import get_global + parent = get_global('parent_exceptions').get(name) + if not parent: + if _is_non_likely_script(name): + parent = 'root' + else: + parts = name.split('_') + parent = "root" if len(parts) == 1 else "_".join(parts[:-1]) + data = load(parent).copy() + filename = resolve_locale_filename(name) + with open(filename, 'rb') as fileobj: + if name != 'root' and merge_inherited: + merge(data, pickle.load(fileobj)) + else: + data = pickle.load(fileobj) + _cache[name] = data + return data + finally: + _cache_lock.release() + + +def merge(dict1: MutableMapping[Any, Any], dict2: Mapping[Any, Any]) -> None: + """Merge the data from `dict2` into the `dict1` dictionary, making copies + of nested dictionaries. + + >>> d = {1: 'foo', 3: 'baz'} + >>> merge(d, {1: 'Foo', 2: 'Bar'}) + >>> sorted(d.items()) + [(1, 'Foo'), (2, 'Bar'), (3, 'baz')] + + :param dict1: the dictionary to merge into + :param dict2: the dictionary containing the data that should be merged + """ + for key, val2 in dict2.items(): + if val2 is not None: + val1 = dict1.get(key) + if isinstance(val2, dict): + if val1 is None: + val1 = {} + if isinstance(val1, Alias): + val1 = (val1, val2) + elif isinstance(val1, tuple): + alias, others = val1 + others = others.copy() + merge(others, val2) + val1 = (alias, others) + else: + val1 = val1.copy() + merge(val1, val2) + else: + val1 = val2 + dict1[key] = val1 + + +class Alias: + """Representation of an alias in the locale data. + + An alias is a value that refers to some other part of the locale data, + as specified by the `keys`. + """ + + def __init__(self, keys: tuple[str, ...]) -> None: + self.keys = tuple(keys) + + def __repr__(self) -> str: + return f"<{type(self).__name__} {self.keys!r}>" + + def resolve(self, data: Mapping[str | int | None, Any]) -> Mapping[str | int | None, Any]: + """Resolve the alias based on the given data. + + This is done recursively, so if one alias resolves to a second alias, + that second alias will also be resolved. + + :param data: the locale data + :type data: `dict` + """ + base = data + for key in self.keys: + data = data[key] + if isinstance(data, Alias): + data = data.resolve(base) + elif isinstance(data, tuple): + alias, others = data + data = alias.resolve(base) + return data + + +class LocaleDataDict(abc.MutableMapping): + """Dictionary wrapper that automatically resolves aliases to the actual + values. + """ + + def __init__(self, data: MutableMapping[str | int | None, Any], base: Mapping[str | int | None, Any] | None = None): + self._data = data + if base is None: + base = data + self.base = base + + def __len__(self) -> int: + return len(self._data) + + def __iter__(self) -> Iterator[str | int | None]: + return iter(self._data) + + def __getitem__(self, key: str | int | None) -> Any: + orig = val = self._data[key] + if isinstance(val, Alias): # resolve an alias + val = val.resolve(self.base) + if isinstance(val, tuple): # Merge a partial dict with an alias + alias, others = val + val = alias.resolve(self.base).copy() + merge(val, others) + if isinstance(val, dict): # Return a nested alias-resolving dict + val = LocaleDataDict(val, base=self.base) + if val is not orig: + self._data[key] = val + return val + + def __setitem__(self, key: str | int | None, value: Any) -> None: + self._data[key] = value + + def __delitem__(self, key: str | int | None) -> None: + del self._data[key] + + def copy(self) -> LocaleDataDict: + return LocaleDataDict(self._data.copy(), base=self.base) diff --git a/.venv/lib/python3.9/site-packages/babel/localtime/__init__.py b/.venv/lib/python3.9/site-packages/babel/localtime/__init__.py new file mode 100644 index 00000000..854c0749 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/localtime/__init__.py @@ -0,0 +1,43 @@ +""" + babel.localtime + ~~~~~~~~~~~~~~~ + + Babel specific fork of tzlocal to determine the local timezone + of the system. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +import datetime +import sys + +if sys.platform == 'win32': + from babel.localtime._win32 import _get_localzone +else: + from babel.localtime._unix import _get_localzone + + +# TODO(3.0): the offset constants are not part of the public API +# and should be removed +from babel.localtime._fallback import ( + DSTDIFF, # noqa: F401 + DSTOFFSET, # noqa: F401 + STDOFFSET, # noqa: F401 + ZERO, # noqa: F401 + _FallbackLocalTimezone, +) + + +def get_localzone() -> datetime.tzinfo: + """Returns the current underlying local timezone object. + Generally this function does not need to be used, it's a + better idea to use the :data:`LOCALTZ` singleton instead. + """ + return _get_localzone() + + +try: + LOCALTZ = get_localzone() +except LookupError: + LOCALTZ = _FallbackLocalTimezone() diff --git a/.venv/lib/python3.9/site-packages/babel/localtime/_fallback.py b/.venv/lib/python3.9/site-packages/babel/localtime/_fallback.py new file mode 100644 index 00000000..fab6867c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/localtime/_fallback.py @@ -0,0 +1,44 @@ +""" + babel.localtime._fallback + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + Emulated fallback local timezone when all else fails. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +import datetime +import time + +STDOFFSET = datetime.timedelta(seconds=-time.timezone) +DSTOFFSET = datetime.timedelta(seconds=-time.altzone) if time.daylight else STDOFFSET + +DSTDIFF = DSTOFFSET - STDOFFSET +ZERO = datetime.timedelta(0) + + +class _FallbackLocalTimezone(datetime.tzinfo): + + def utcoffset(self, dt: datetime.datetime) -> datetime.timedelta: + if self._isdst(dt): + return DSTOFFSET + else: + return STDOFFSET + + def dst(self, dt: datetime.datetime) -> datetime.timedelta: + if self._isdst(dt): + return DSTDIFF + else: + return ZERO + + def tzname(self, dt: datetime.datetime) -> str: + return time.tzname[self._isdst(dt)] + + def _isdst(self, dt: datetime.datetime) -> bool: + tt = (dt.year, dt.month, dt.day, + dt.hour, dt.minute, dt.second, + dt.weekday(), 0, -1) + stamp = time.mktime(tt) + tt = time.localtime(stamp) + return tt.tm_isdst > 0 diff --git a/.venv/lib/python3.9/site-packages/babel/localtime/_helpers.py b/.venv/lib/python3.9/site-packages/babel/localtime/_helpers.py new file mode 100644 index 00000000..e7e67052 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/localtime/_helpers.py @@ -0,0 +1,57 @@ +try: + import pytz +except ModuleNotFoundError: + pytz = None + +try: + import zoneinfo +except ModuleNotFoundError: + zoneinfo = None + + +def _get_tzinfo(tzenv: str): + """Get the tzinfo from `zoneinfo` or `pytz` + + :param tzenv: timezone in the form of Continent/City + :return: tzinfo object or None if not found + """ + if pytz: + try: + return pytz.timezone(tzenv) + except pytz.UnknownTimeZoneError: + pass + else: + try: + return zoneinfo.ZoneInfo(tzenv) + except ValueError as ve: + # This is somewhat hacky, but since _validate_tzfile_path() doesn't + # raise a specific error type, we'll need to check the message to be + # one we know to be from that function. + # If so, we pretend it meant that the TZ didn't exist, for the benefit + # of `babel.localtime` catching the `LookupError` raised by + # `_get_tzinfo_or_raise()`. + # See https://github.com/python-babel/babel/issues/1092 + if str(ve).startswith("ZoneInfo keys "): + return None + except zoneinfo.ZoneInfoNotFoundError: + pass + + return None + + +def _get_tzinfo_or_raise(tzenv: str): + tzinfo = _get_tzinfo(tzenv) + if tzinfo is None: + raise LookupError( + f"Can not find timezone {tzenv}. \n" + "Timezone names are generally in the form `Continent/City`.", + ) + return tzinfo + + +def _get_tzinfo_from_file(tzfilename: str): + with open(tzfilename, 'rb') as tzfile: + if pytz: + return pytz.tzfile.build_tzinfo('local', tzfile) + else: + return zoneinfo.ZoneInfo.from_file(tzfile) diff --git a/.venv/lib/python3.9/site-packages/babel/localtime/_unix.py b/.venv/lib/python3.9/site-packages/babel/localtime/_unix.py new file mode 100644 index 00000000..782a7d24 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/localtime/_unix.py @@ -0,0 +1,104 @@ +import datetime +import os +import re + +from babel.localtime._helpers import ( + _get_tzinfo, + _get_tzinfo_from_file, + _get_tzinfo_or_raise, +) + + +def _tz_from_env(tzenv: str) -> datetime.tzinfo: + if tzenv[0] == ':': + tzenv = tzenv[1:] + + # TZ specifies a file + if os.path.exists(tzenv): + return _get_tzinfo_from_file(tzenv) + + # TZ specifies a zoneinfo zone. + return _get_tzinfo_or_raise(tzenv) + + +def _get_localzone(_root: str = '/') -> datetime.tzinfo: + """Tries to find the local timezone configuration. + This method prefers finding the timezone name and passing that to + zoneinfo or pytz, over passing in the localtime file, as in the later + case the zoneinfo name is unknown. + The parameter _root makes the function look for files like /etc/localtime + beneath the _root directory. This is primarily used by the tests. + In normal usage you call the function without parameters. + """ + + tzenv = os.environ.get('TZ') + if tzenv: + return _tz_from_env(tzenv) + + # This is actually a pretty reliable way to test for the local time + # zone on operating systems like OS X. On OS X especially this is the + # only one that actually works. + try: + link_dst = os.readlink('/etc/localtime') + except OSError: + pass + else: + pos = link_dst.find('/zoneinfo/') + if pos >= 0: + # On occasion, the `/etc/localtime` symlink has a double slash, e.g. + # "/usr/share/zoneinfo//UTC", which would make `zoneinfo.ZoneInfo` + # complain (no absolute paths allowed), and we'd end up returning + # `None` (as a fix for #1092). + # Instead, let's just "fix" the double slash symlink by stripping + # leading slashes before passing the assumed zone name forward. + zone_name = link_dst[pos + 10:].lstrip("/") + tzinfo = _get_tzinfo(zone_name) + if tzinfo is not None: + return tzinfo + + # Now look for distribution specific configuration files + # that contain the timezone name. + tzpath = os.path.join(_root, 'etc/timezone') + if os.path.exists(tzpath): + with open(tzpath, 'rb') as tzfile: + data = tzfile.read() + + # Issue #3 in tzlocal was that /etc/timezone was a zoneinfo file. + # That's a misconfiguration, but we need to handle it gracefully: + if data[:5] != b'TZif2': + etctz = data.strip().decode() + # Get rid of host definitions and comments: + if ' ' in etctz: + etctz, dummy = etctz.split(' ', 1) + if '#' in etctz: + etctz, dummy = etctz.split('#', 1) + + return _get_tzinfo_or_raise(etctz.replace(' ', '_')) + + # CentOS has a ZONE setting in /etc/sysconfig/clock, + # OpenSUSE has a TIMEZONE setting in /etc/sysconfig/clock and + # Gentoo has a TIMEZONE setting in /etc/conf.d/clock + # We look through these files for a timezone: + timezone_re = re.compile(r'\s*(TIME)?ZONE\s*=\s*"(?P.+)"') + + for filename in ('etc/sysconfig/clock', 'etc/conf.d/clock'): + tzpath = os.path.join(_root, filename) + if not os.path.exists(tzpath): + continue + with open(tzpath) as tzfile: + for line in tzfile: + match = timezone_re.match(line) + if match is not None: + # We found a timezone + etctz = match.group("etctz") + return _get_tzinfo_or_raise(etctz.replace(' ', '_')) + + # No explicit setting existed. Use localtime + for filename in ('etc/localtime', 'usr/local/etc/localtime'): + tzpath = os.path.join(_root, filename) + + if not os.path.exists(tzpath): + continue + return _get_tzinfo_from_file(tzpath) + + raise LookupError('Can not find any timezone configuration') diff --git a/.venv/lib/python3.9/site-packages/babel/localtime/_win32.py b/.venv/lib/python3.9/site-packages/babel/localtime/_win32.py new file mode 100644 index 00000000..1a52567b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/localtime/_win32.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +try: + import winreg +except ImportError: + winreg = None + +import datetime +from typing import Any, Dict, cast + +from babel.core import get_global +from babel.localtime._helpers import _get_tzinfo_or_raise + +# When building the cldr data on windows this module gets imported. +# Because at that point there is no global.dat yet this call will +# fail. We want to catch it down in that case then and just assume +# the mapping was empty. +try: + tz_names: dict[str, str] = cast(Dict[str, str], get_global('windows_zone_mapping')) +except RuntimeError: + tz_names = {} + + +def valuestodict(key) -> dict[str, Any]: + """Convert a registry key's values to a dictionary.""" + dict = {} + size = winreg.QueryInfoKey(key)[1] + for i in range(size): + data = winreg.EnumValue(key, i) + dict[data[0]] = data[1] + return dict + + +def get_localzone_name() -> str: + # Windows is special. It has unique time zone names (in several + # meanings of the word) available, but unfortunately, they can be + # translated to the language of the operating system, so we need to + # do a backwards lookup, by going through all time zones and see which + # one matches. + handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) + + TZLOCALKEYNAME = r'SYSTEM\CurrentControlSet\Control\TimeZoneInformation' + localtz = winreg.OpenKey(handle, TZLOCALKEYNAME) + keyvalues = valuestodict(localtz) + localtz.Close() + if 'TimeZoneKeyName' in keyvalues: + # Windows 7 (and Vista?) + + # For some reason this returns a string with loads of NUL bytes at + # least on some systems. I don't know if this is a bug somewhere, I + # just work around it. + tzkeyname = keyvalues['TimeZoneKeyName'].split('\x00', 1)[0] + else: + # Windows 2000 or XP + + # This is the localized name: + tzwin = keyvalues['StandardName'] + + # Open the list of timezones to look up the real name: + TZKEYNAME = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones' + tzkey = winreg.OpenKey(handle, TZKEYNAME) + + # Now, match this value to Time Zone information + tzkeyname = None + for i in range(winreg.QueryInfoKey(tzkey)[0]): + subkey = winreg.EnumKey(tzkey, i) + sub = winreg.OpenKey(tzkey, subkey) + data = valuestodict(sub) + sub.Close() + if data.get('Std', None) == tzwin: + tzkeyname = subkey + break + + tzkey.Close() + handle.Close() + + if tzkeyname is None: + raise LookupError('Can not find Windows timezone configuration') + + timezone = tz_names.get(tzkeyname) + if timezone is None: + # Nope, that didn't work. Try adding 'Standard Time', + # it seems to work a lot of times: + timezone = tz_names.get(f"{tzkeyname} Standard Time") + + # Return what we have. + if timezone is None: + raise LookupError(f"Can not find timezone {tzkeyname}") + + return timezone + + +def _get_localzone() -> datetime.tzinfo: + if winreg is None: + raise LookupError( + 'Runtime support not available') + + return _get_tzinfo_or_raise(get_localzone_name()) diff --git a/.venv/lib/python3.9/site-packages/babel/messages/__init__.py b/.venv/lib/python3.9/site-packages/babel/messages/__init__.py new file mode 100644 index 00000000..ca83faa9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/messages/__init__.py @@ -0,0 +1,21 @@ +""" + babel.messages + ~~~~~~~~~~~~~~ + + Support for ``gettext`` message catalogs. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +from babel.messages.catalog import ( + Catalog, + Message, + TranslationError, +) + +__all__ = [ + "Catalog", + "Message", + "TranslationError", +] diff --git a/.venv/lib/python3.9/site-packages/babel/messages/_compat.py b/.venv/lib/python3.9/site-packages/babel/messages/_compat.py new file mode 100644 index 00000000..319b545f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/messages/_compat.py @@ -0,0 +1,34 @@ +import sys +from functools import partial + + +def find_entrypoints(group_name: str): + """ + Find entrypoints of a given group using either `importlib.metadata` or the + older `pkg_resources` mechanism. + + Yields tuples of the entrypoint name and a callable function that will + load the actual entrypoint. + """ + if sys.version_info >= (3, 10): + # "Changed in version 3.10: importlib.metadata is no longer provisional." + try: + from importlib.metadata import entry_points + except ImportError: + pass + else: + eps = entry_points(group=group_name) + # Only do this if this implementation of `importlib.metadata` is + # modern enough to not return a dict. + if not isinstance(eps, dict): + for entry_point in eps: + yield (entry_point.name, entry_point.load) + return + + try: + from pkg_resources import working_set + except ImportError: + pass + else: + for entry_point in working_set.iter_entry_points(group_name): + yield (entry_point.name, partial(entry_point.load, require=True)) diff --git a/.venv/lib/python3.9/site-packages/babel/messages/catalog.py b/.venv/lib/python3.9/site-packages/babel/messages/catalog.py new file mode 100644 index 00000000..f84a5bd1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/messages/catalog.py @@ -0,0 +1,1000 @@ +""" + babel.messages.catalog + ~~~~~~~~~~~~~~~~~~~~~~ + + Data structures for message catalogs. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" +from __future__ import annotations + +import datetime +import re +from collections.abc import Iterable, Iterator +from copy import copy +from difflib import SequenceMatcher +from email import message_from_string +from heapq import nlargest +from string import Formatter +from typing import TYPE_CHECKING + +from babel import __version__ as VERSION +from babel.core import Locale, UnknownLocaleError +from babel.dates import format_datetime +from babel.messages.plurals import get_plural +from babel.util import LOCALTZ, FixedOffsetTimezone, _cmp, distinct + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + + _MessageID: TypeAlias = str | tuple[str, ...] | list[str] + +__all__ = [ + 'DEFAULT_HEADER', + 'PYTHON_FORMAT', + 'Catalog', + 'Message', + 'TranslationError', +] + + +def get_close_matches(word, possibilities, n=3, cutoff=0.6): + """A modified version of ``difflib.get_close_matches``. + + It just passes ``autojunk=False`` to the ``SequenceMatcher``, to work + around https://github.com/python/cpython/issues/90825. + """ + if not n > 0: # pragma: no cover + raise ValueError(f"n must be > 0: {n!r}") + if not 0.0 <= cutoff <= 1.0: # pragma: no cover + raise ValueError(f"cutoff must be in [0.0, 1.0]: {cutoff!r}") + result = [] + s = SequenceMatcher(autojunk=False) # only line changed from difflib.py + s.set_seq2(word) + for x in possibilities: + s.set_seq1(x) + if s.real_quick_ratio() >= cutoff and \ + s.quick_ratio() >= cutoff and \ + s.ratio() >= cutoff: + result.append((s.ratio(), x)) + + # Move the best scorers to head of list + result = nlargest(n, result) + # Strip scores for the best n matches + return [x for score, x in result] + + +PYTHON_FORMAT = re.compile(r''' + \% + (?:\(([\w]*)\))? + ( + [-#0\ +]?(?:\*|[\d]+)? + (?:\.(?:\*|[\d]+))? + [hlL]? + ) + ([diouxXeEfFgGcrs%]) +''', re.VERBOSE) + + +def _has_python_brace_format(string: str) -> bool: + if "{" not in string: + return False + fmt = Formatter() + try: + # `fmt.parse` returns 3-or-4-tuples of the form + # `(literal_text, field_name, format_spec, conversion)`; + # if `field_name` is set, this smells like brace format + field_name_seen = False + for t in fmt.parse(string): + if t[1] is not None: + field_name_seen = True + # We cannot break here, as we need to consume the whole string + # to ensure that it is a valid format string. + except ValueError: + return False + return field_name_seen + + +def _parse_datetime_header(value: str) -> datetime.datetime: + match = re.match(r'^(?P.*?)(?P[+-]\d{4})?$', value) + + dt = datetime.datetime.strptime(match.group('datetime'), '%Y-%m-%d %H:%M') + + # Separate the offset into a sign component, hours, and # minutes + tzoffset = match.group('tzoffset') + if tzoffset is not None: + plus_minus_s, rest = tzoffset[0], tzoffset[1:] + hours_offset_s, mins_offset_s = rest[:2], rest[2:] + + # Make them all integers + plus_minus = int(f"{plus_minus_s}1") + hours_offset = int(hours_offset_s) + mins_offset = int(mins_offset_s) + + # Calculate net offset + net_mins_offset = hours_offset * 60 + net_mins_offset += mins_offset + net_mins_offset *= plus_minus + + # Create an offset object + tzoffset = FixedOffsetTimezone(net_mins_offset) + + # Store the offset in a datetime object + dt = dt.replace(tzinfo=tzoffset) + + return dt + + +class Message: + """Representation of a single message in a catalog.""" + + def __init__( + self, + id: _MessageID, + string: _MessageID | None = '', + locations: Iterable[tuple[str, int]] = (), + flags: Iterable[str] = (), + auto_comments: Iterable[str] = (), + user_comments: Iterable[str] = (), + previous_id: _MessageID = (), + lineno: int | None = None, + context: str | None = None, + ) -> None: + """Create the message object. + + :param id: the message ID, or a ``(singular, plural)`` tuple for + pluralizable messages + :param string: the translated message string, or a + ``(singular, plural)`` tuple for pluralizable messages + :param locations: a sequence of ``(filename, lineno)`` tuples + :param flags: a set or sequence of flags + :param auto_comments: a sequence of automatic comments for the message + :param user_comments: a sequence of user comments for the message + :param previous_id: the previous message ID, or a ``(singular, plural)`` + tuple for pluralizable messages + :param lineno: the line number on which the msgid line was found in the + PO file, if any + :param context: the message context + """ + self.id = id + if not string and self.pluralizable: + string = ('', '') + self.string = string + self.locations = list(distinct(locations)) + self.flags = set(flags) + if id and self.python_format: + self.flags.add('python-format') + else: + self.flags.discard('python-format') + if id and self.python_brace_format: + self.flags.add('python-brace-format') + else: + self.flags.discard('python-brace-format') + self.auto_comments = list(distinct(auto_comments)) + self.user_comments = list(distinct(user_comments)) + if isinstance(previous_id, str): + self.previous_id = [previous_id] + else: + self.previous_id = list(previous_id) + self.lineno = lineno + self.context = context + + def __repr__(self) -> str: + return f"<{type(self).__name__} {self.id!r} (flags: {list(self.flags)!r})>" + + def __cmp__(self, other: object) -> int: + """Compare Messages, taking into account plural ids""" + def values_to_compare(obj): + if isinstance(obj, Message) and obj.pluralizable: + return obj.id[0], obj.context or '' + return obj.id, obj.context or '' + return _cmp(values_to_compare(self), values_to_compare(other)) + + def __gt__(self, other: object) -> bool: + return self.__cmp__(other) > 0 + + def __lt__(self, other: object) -> bool: + return self.__cmp__(other) < 0 + + def __ge__(self, other: object) -> bool: + return self.__cmp__(other) >= 0 + + def __le__(self, other: object) -> bool: + return self.__cmp__(other) <= 0 + + def __eq__(self, other: object) -> bool: + return self.__cmp__(other) == 0 + + def __ne__(self, other: object) -> bool: + return self.__cmp__(other) != 0 + + def is_identical(self, other: Message) -> bool: + """Checks whether messages are identical, taking into account all + properties. + """ + assert isinstance(other, Message) + return self.__dict__ == other.__dict__ + + def clone(self) -> Message: + return Message(*map(copy, (self.id, self.string, self.locations, + self.flags, self.auto_comments, + self.user_comments, self.previous_id, + self.lineno, self.context))) + + def check(self, catalog: Catalog | None = None) -> list[TranslationError]: + """Run various validation checks on the message. Some validations + are only performed if the catalog is provided. This method returns + a sequence of `TranslationError` objects. + + :rtype: ``iterator`` + :param catalog: A catalog instance that is passed to the checkers + :see: `Catalog.check` for a way to perform checks for all messages + in a catalog. + """ + from babel.messages.checkers import checkers + errors: list[TranslationError] = [] + for checker in checkers: + try: + checker(catalog, self) + except TranslationError as e: + errors.append(e) + return errors + + @property + def fuzzy(self) -> bool: + """Whether the translation is fuzzy. + + >>> Message('foo').fuzzy + False + >>> msg = Message('foo', 'foo', flags=['fuzzy']) + >>> msg.fuzzy + True + >>> msg + + + :type: `bool`""" + return 'fuzzy' in self.flags + + @property + def pluralizable(self) -> bool: + """Whether the message is plurizable. + + >>> Message('foo').pluralizable + False + >>> Message(('foo', 'bar')).pluralizable + True + + :type: `bool`""" + return isinstance(self.id, (list, tuple)) + + @property + def python_format(self) -> bool: + """Whether the message contains Python-style parameters. + + >>> Message('foo %(name)s bar').python_format + True + >>> Message(('foo %(name)s', 'foo %(name)s')).python_format + True + + :type: `bool`""" + ids = self.id + if not isinstance(ids, (list, tuple)): + ids = [ids] + return any(PYTHON_FORMAT.search(id) for id in ids) + + @property + def python_brace_format(self) -> bool: + """Whether the message contains Python f-string parameters. + + >>> Message('Hello, {name}!').python_brace_format + True + >>> Message(('One apple', '{count} apples')).python_brace_format + True + + :type: `bool`""" + ids = self.id + if not isinstance(ids, (list, tuple)): + ids = [ids] + return any(_has_python_brace_format(id) for id in ids) + + +class TranslationError(Exception): + """Exception thrown by translation checkers when invalid message + translations are encountered.""" + + +DEFAULT_HEADER = """\ +# Translations template for PROJECT. +# Copyright (C) YEAR ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , YEAR. +#""" + + +def parse_separated_header(value: str) -> dict[str, str]: + # Adapted from https://peps.python.org/pep-0594/#cgi + from email.message import Message + m = Message() + m['content-type'] = value + return dict(m.get_params()) + + +def _force_text(s: str | bytes, encoding: str = 'utf-8', errors: str = 'strict') -> str: + if isinstance(s, str): + return s + if isinstance(s, bytes): + return s.decode(encoding, errors) + return str(s) + + +class Catalog: + """Representation of a message catalog.""" + + def __init__( + self, + locale: Locale | str | None = None, + domain: str | None = None, + header_comment: str | None = DEFAULT_HEADER, + project: str | None = None, + version: str | None = None, + copyright_holder: str | None = None, + msgid_bugs_address: str | None = None, + creation_date: datetime.datetime | str | None = None, + revision_date: datetime.datetime | datetime.time | float | str | None = None, + last_translator: str | None = None, + language_team: str | None = None, + charset: str | None = None, + fuzzy: bool = True, + ) -> None: + """Initialize the catalog object. + + :param locale: the locale identifier or `Locale` object, or `None` + if the catalog is not bound to a locale (which basically + means it's a template) + :param domain: the message domain + :param header_comment: the header comment as string, or `None` for the + default header + :param project: the project's name + :param version: the project's version + :param copyright_holder: the copyright holder of the catalog + :param msgid_bugs_address: the email address or URL to submit bug + reports to + :param creation_date: the date the catalog was created + :param revision_date: the date the catalog was revised + :param last_translator: the name and email of the last translator + :param language_team: the name and email of the language team + :param charset: the encoding to use in the output (defaults to utf-8) + :param fuzzy: the fuzzy bit on the catalog header + """ + self.domain = domain + self.locale = locale + self._header_comment = header_comment + self._messages: dict[str | tuple[str, str], Message] = {} + + self.project = project or 'PROJECT' + self.version = version or 'VERSION' + self.copyright_holder = copyright_holder or 'ORGANIZATION' + self.msgid_bugs_address = msgid_bugs_address or 'EMAIL@ADDRESS' + + self.last_translator = last_translator or 'FULL NAME ' + """Name and email address of the last translator.""" + self.language_team = language_team or 'LANGUAGE ' + """Name and email address of the language team.""" + + self.charset = charset or 'utf-8' + + if creation_date is None: + creation_date = datetime.datetime.now(LOCALTZ) + elif isinstance(creation_date, datetime.datetime) and not creation_date.tzinfo: + creation_date = creation_date.replace(tzinfo=LOCALTZ) + self.creation_date = creation_date + if revision_date is None: + revision_date = 'YEAR-MO-DA HO:MI+ZONE' + elif isinstance(revision_date, datetime.datetime) and not revision_date.tzinfo: + revision_date = revision_date.replace(tzinfo=LOCALTZ) + self.revision_date = revision_date + self.fuzzy = fuzzy + + # Dictionary of obsolete messages + self.obsolete: dict[str | tuple[str, str], Message] = {} + self._num_plurals = None + self._plural_expr = None + + def _set_locale(self, locale: Locale | str | None) -> None: + if locale is None: + self._locale_identifier = None + self._locale = None + return + + if isinstance(locale, Locale): + self._locale_identifier = str(locale) + self._locale = locale + return + + if isinstance(locale, str): + self._locale_identifier = str(locale) + try: + self._locale = Locale.parse(locale) + except UnknownLocaleError: + self._locale = None + return + + raise TypeError(f"`locale` must be a Locale, a locale identifier string, or None; got {locale!r}") + + def _get_locale(self) -> Locale | None: + return self._locale + + def _get_locale_identifier(self) -> str | None: + return self._locale_identifier + + locale = property(_get_locale, _set_locale) + locale_identifier = property(_get_locale_identifier) + + def _get_header_comment(self) -> str: + comment = self._header_comment + year = datetime.datetime.now(LOCALTZ).strftime('%Y') + if hasattr(self.revision_date, 'strftime'): + year = self.revision_date.strftime('%Y') + comment = comment.replace('PROJECT', self.project) \ + .replace('VERSION', self.version) \ + .replace('YEAR', year) \ + .replace('ORGANIZATION', self.copyright_holder) + locale_name = (self.locale.english_name if self.locale else self.locale_identifier) + if locale_name: + comment = comment.replace("Translations template", f"{locale_name} translations") + return comment + + def _set_header_comment(self, string: str | None) -> None: + self._header_comment = string + + header_comment = property(_get_header_comment, _set_header_comment, doc="""\ + The header comment for the catalog. + + >>> catalog = Catalog(project='Foobar', version='1.0', + ... copyright_holder='Foo Company') + >>> print(catalog.header_comment) #doctest: +ELLIPSIS + # Translations template for Foobar. + # Copyright (C) ... Foo Company + # This file is distributed under the same license as the Foobar project. + # FIRST AUTHOR , .... + # + + The header can also be set from a string. Any known upper-case variables + will be replaced when the header is retrieved again: + + >>> catalog = Catalog(project='Foobar', version='1.0', + ... copyright_holder='Foo Company') + >>> catalog.header_comment = '''\\ + ... # The POT for my really cool PROJECT project. + ... # Copyright (C) 1990-2003 ORGANIZATION + ... # This file is distributed under the same license as the PROJECT + ... # project. + ... #''' + >>> print(catalog.header_comment) + # The POT for my really cool Foobar project. + # Copyright (C) 1990-2003 Foo Company + # This file is distributed under the same license as the Foobar + # project. + # + + :type: `unicode` + """) + + def _get_mime_headers(self) -> list[tuple[str, str]]: + if isinstance(self.revision_date, (datetime.datetime, datetime.time, int, float)): + revision_date = format_datetime(self.revision_date, 'yyyy-MM-dd HH:mmZ', locale='en') + else: + revision_date = self.revision_date + + language_team = self.language_team + if self.locale_identifier and 'LANGUAGE' in language_team: + language_team = language_team.replace('LANGUAGE', str(self.locale_identifier)) + + headers: list[tuple[str, str]] = [ + ("Project-Id-Version", f"{self.project} {self.version}"), + ('Report-Msgid-Bugs-To', self.msgid_bugs_address), + ('POT-Creation-Date', format_datetime(self.creation_date, 'yyyy-MM-dd HH:mmZ', locale='en')), + ('PO-Revision-Date', revision_date), + ('Last-Translator', self.last_translator), + ] + if self.locale_identifier: + headers.append(('Language', str(self.locale_identifier))) + headers.append(('Language-Team', language_team)) + if self.locale is not None: + headers.append(('Plural-Forms', self.plural_forms)) + headers += [ + ('MIME-Version', '1.0'), + ("Content-Type", f"text/plain; charset={self.charset}"), + ('Content-Transfer-Encoding', '8bit'), + ("Generated-By", f"Babel {VERSION}\n"), + ] + return headers + + def _set_mime_headers(self, headers: Iterable[tuple[str, str]]) -> None: + for name, value in headers: + name = _force_text(name.lower(), encoding=self.charset) + value = _force_text(value, encoding=self.charset) + if name == 'project-id-version': + parts = value.split(' ') + self.project = ' '.join(parts[:-1]) + self.version = parts[-1] + elif name == 'report-msgid-bugs-to': + self.msgid_bugs_address = value + elif name == 'last-translator': + self.last_translator = value + elif name == 'language': + value = value.replace('-', '_') + # The `or None` makes sure that the locale is set to None + # if the header's value is an empty string, which is what + # some tools generate (instead of eliding the empty Language + # header altogether). + self._set_locale(value or None) + elif name == 'language-team': + self.language_team = value + elif name == 'content-type': + params = parse_separated_header(value) + if 'charset' in params: + self.charset = params['charset'].lower() + elif name == 'plural-forms': + params = parse_separated_header(f" ;{value}") + self._num_plurals = int(params.get('nplurals', 2)) + self._plural_expr = params.get('plural', '(n != 1)') + elif name == 'pot-creation-date': + self.creation_date = _parse_datetime_header(value) + elif name == 'po-revision-date': + # Keep the value if it's not the default one + if 'YEAR' not in value: + self.revision_date = _parse_datetime_header(value) + + mime_headers = property(_get_mime_headers, _set_mime_headers, doc="""\ + The MIME headers of the catalog, used for the special ``msgid ""`` entry. + + The behavior of this property changes slightly depending on whether a locale + is set or not, the latter indicating that the catalog is actually a template + for actual translations. + + Here's an example of the output for such a catalog template: + + >>> from babel.dates import UTC + >>> from datetime import datetime + >>> created = datetime(1990, 4, 1, 15, 30, tzinfo=UTC) + >>> catalog = Catalog(project='Foobar', version='1.0', + ... creation_date=created) + >>> for name, value in catalog.mime_headers: + ... print('%s: %s' % (name, value)) + Project-Id-Version: Foobar 1.0 + Report-Msgid-Bugs-To: EMAIL@ADDRESS + POT-Creation-Date: 1990-04-01 15:30+0000 + PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE + Last-Translator: FULL NAME + Language-Team: LANGUAGE + MIME-Version: 1.0 + Content-Type: text/plain; charset=utf-8 + Content-Transfer-Encoding: 8bit + Generated-By: Babel ... + + And here's an example of the output when the locale is set: + + >>> revised = datetime(1990, 8, 3, 12, 0, tzinfo=UTC) + >>> catalog = Catalog(locale='de_DE', project='Foobar', version='1.0', + ... creation_date=created, revision_date=revised, + ... last_translator='John Doe ', + ... language_team='de_DE ') + >>> for name, value in catalog.mime_headers: + ... print('%s: %s' % (name, value)) + Project-Id-Version: Foobar 1.0 + Report-Msgid-Bugs-To: EMAIL@ADDRESS + POT-Creation-Date: 1990-04-01 15:30+0000 + PO-Revision-Date: 1990-08-03 12:00+0000 + Last-Translator: John Doe + Language: de_DE + Language-Team: de_DE + Plural-Forms: nplurals=2; plural=(n != 1); + MIME-Version: 1.0 + Content-Type: text/plain; charset=utf-8 + Content-Transfer-Encoding: 8bit + Generated-By: Babel ... + + :type: `list` + """) + + @property + def num_plurals(self) -> int: + """The number of plurals used by the catalog or locale. + + >>> Catalog(locale='en').num_plurals + 2 + >>> Catalog(locale='ga').num_plurals + 5 + + :type: `int`""" + if self._num_plurals is None: + num = 2 + if self.locale: + num = get_plural(self.locale)[0] + self._num_plurals = num + return self._num_plurals + + @property + def plural_expr(self) -> str: + """The plural expression used by the catalog or locale. + + >>> Catalog(locale='en').plural_expr + '(n != 1)' + >>> Catalog(locale='ga').plural_expr + '(n==1 ? 0 : n==2 ? 1 : n>=3 && n<=6 ? 2 : n>=7 && n<=10 ? 3 : 4)' + >>> Catalog(locale='ding').plural_expr # unknown locale + '(n != 1)' + + :type: `str`""" + if self._plural_expr is None: + expr = '(n != 1)' + if self.locale: + expr = get_plural(self.locale)[1] + self._plural_expr = expr + return self._plural_expr + + @property + def plural_forms(self) -> str: + """Return the plural forms declaration for the locale. + + >>> Catalog(locale='en').plural_forms + 'nplurals=2; plural=(n != 1);' + >>> Catalog(locale='pt_BR').plural_forms + 'nplurals=2; plural=(n > 1);' + + :type: `str`""" + return f"nplurals={self.num_plurals}; plural={self.plural_expr};" + + def __contains__(self, id: _MessageID) -> bool: + """Return whether the catalog has a message with the specified ID.""" + return self._key_for(id) in self._messages + + def __len__(self) -> int: + """The number of messages in the catalog. + + This does not include the special ``msgid ""`` entry.""" + return len(self._messages) + + def __iter__(self) -> Iterator[Message]: + """Iterates through all the entries in the catalog, in the order they + were added, yielding a `Message` object for every entry. + + :rtype: ``iterator``""" + buf = [] + for name, value in self.mime_headers: + buf.append(f"{name}: {value}") + flags = set() + if self.fuzzy: + flags |= {'fuzzy'} + yield Message('', '\n'.join(buf), flags=flags) + for key in self._messages: + yield self._messages[key] + + def __repr__(self) -> str: + locale = '' + if self.locale: + locale = f" {self.locale}" + return f"<{type(self).__name__} {self.domain!r}{locale}>" + + def __delitem__(self, id: _MessageID) -> None: + """Delete the message with the specified ID.""" + self.delete(id) + + def __getitem__(self, id: _MessageID) -> Message: + """Return the message with the specified ID. + + :param id: the message ID + """ + return self.get(id) + + def __setitem__(self, id: _MessageID, message: Message) -> None: + """Add or update the message with the specified ID. + + >>> catalog = Catalog() + >>> catalog[u'foo'] = Message(u'foo') + >>> catalog[u'foo'] + + + If a message with that ID is already in the catalog, it is updated + to include the locations and flags of the new message. + + >>> catalog = Catalog() + >>> catalog[u'foo'] = Message(u'foo', locations=[('main.py', 1)]) + >>> catalog[u'foo'].locations + [('main.py', 1)] + >>> catalog[u'foo'] = Message(u'foo', locations=[('utils.py', 5)]) + >>> catalog[u'foo'].locations + [('main.py', 1), ('utils.py', 5)] + + :param id: the message ID + :param message: the `Message` object + """ + assert isinstance(message, Message), 'expected a Message object' + key = self._key_for(id, message.context) + current = self._messages.get(key) + if current: + if message.pluralizable and not current.pluralizable: + # The new message adds pluralization + current.id = message.id + current.string = message.string + current.locations = list(distinct(current.locations + + message.locations)) + current.auto_comments = list(distinct(current.auto_comments + + message.auto_comments)) + current.user_comments = list(distinct(current.user_comments + + message.user_comments)) + current.flags |= message.flags + elif id == '': + # special treatment for the header message + self.mime_headers = message_from_string(message.string).items() + self.header_comment = "\n".join([f"# {c}".rstrip() for c in message.user_comments]) + self.fuzzy = message.fuzzy + else: + if isinstance(id, (list, tuple)): + assert isinstance(message.string, (list, tuple)), \ + f"Expected sequence but got {type(message.string)}" + self._messages[key] = message + + def add( + self, + id: _MessageID, + string: _MessageID | None = None, + locations: Iterable[tuple[str, int]] = (), + flags: Iterable[str] = (), + auto_comments: Iterable[str] = (), + user_comments: Iterable[str] = (), + previous_id: _MessageID = (), + lineno: int | None = None, + context: str | None = None, + ) -> Message: + """Add or update the message with the specified ID. + + >>> catalog = Catalog() + >>> catalog.add(u'foo') + + >>> catalog[u'foo'] + + + This method simply constructs a `Message` object with the given + arguments and invokes `__setitem__` with that object. + + :param id: the message ID, or a ``(singular, plural)`` tuple for + pluralizable messages + :param string: the translated message string, or a + ``(singular, plural)`` tuple for pluralizable messages + :param locations: a sequence of ``(filename, lineno)`` tuples + :param flags: a set or sequence of flags + :param auto_comments: a sequence of automatic comments + :param user_comments: a sequence of user comments + :param previous_id: the previous message ID, or a ``(singular, plural)`` + tuple for pluralizable messages + :param lineno: the line number on which the msgid line was found in the + PO file, if any + :param context: the message context + """ + message = Message(id, string, list(locations), flags, auto_comments, + user_comments, previous_id, lineno=lineno, + context=context) + self[id] = message + return message + + def check(self) -> Iterable[tuple[Message, list[TranslationError]]]: + """Run various validation checks on the translations in the catalog. + + For every message which fails validation, this method yield a + ``(message, errors)`` tuple, where ``message`` is the `Message` object + and ``errors`` is a sequence of `TranslationError` objects. + + :rtype: ``generator`` of ``(message, errors)`` + """ + for message in self._messages.values(): + errors = message.check(catalog=self) + if errors: + yield message, errors + + def get(self, id: _MessageID, context: str | None = None) -> Message | None: + """Return the message with the specified ID and context. + + :param id: the message ID + :param context: the message context, or ``None`` for no context + """ + return self._messages.get(self._key_for(id, context)) + + def delete(self, id: _MessageID, context: str | None = None) -> None: + """Delete the message with the specified ID and context. + + :param id: the message ID + :param context: the message context, or ``None`` for no context + """ + key = self._key_for(id, context) + if key in self._messages: + del self._messages[key] + + def update( + self, + template: Catalog, + no_fuzzy_matching: bool = False, + update_header_comment: bool = False, + keep_user_comments: bool = True, + update_creation_date: bool = True, + ) -> None: + """Update the catalog based on the given template catalog. + + >>> from babel.messages import Catalog + >>> template = Catalog() + >>> template.add('green', locations=[('main.py', 99)]) + + >>> template.add('blue', locations=[('main.py', 100)]) + + >>> template.add(('salad', 'salads'), locations=[('util.py', 42)]) + + >>> catalog = Catalog(locale='de_DE') + >>> catalog.add('blue', u'blau', locations=[('main.py', 98)]) + + >>> catalog.add('head', u'Kopf', locations=[('util.py', 33)]) + + >>> catalog.add(('salad', 'salads'), (u'Salat', u'Salate'), + ... locations=[('util.py', 38)]) + + + >>> catalog.update(template) + >>> len(catalog) + 3 + + >>> msg1 = catalog['green'] + >>> msg1.string + >>> msg1.locations + [('main.py', 99)] + + >>> msg2 = catalog['blue'] + >>> msg2.string + u'blau' + >>> msg2.locations + [('main.py', 100)] + + >>> msg3 = catalog['salad'] + >>> msg3.string + (u'Salat', u'Salate') + >>> msg3.locations + [('util.py', 42)] + + Messages that are in the catalog but not in the template are removed + from the main collection, but can still be accessed via the `obsolete` + member: + + >>> 'head' in catalog + False + >>> list(catalog.obsolete.values()) + [] + + :param template: the reference catalog, usually read from a POT file + :param no_fuzzy_matching: whether to use fuzzy matching of message IDs + :param update_header_comment: whether to copy the header comment from the template + :param keep_user_comments: whether to keep user comments from the old catalog + :param update_creation_date: whether to copy the creation date from the template + """ + messages = self._messages + remaining = messages.copy() + self._messages = {} + + # Prepare for fuzzy matching + fuzzy_candidates = {} + if not no_fuzzy_matching: + for msgid in messages: + if msgid and messages[msgid].string: + key = self._key_for(msgid) + ctxt = messages[msgid].context + fuzzy_candidates[self._to_fuzzy_match_key(key)] = (key, ctxt) + fuzzy_matches = set() + + def _merge(message: Message, oldkey: tuple[str, str] | str, newkey: tuple[str, str] | str) -> None: + message = message.clone() + fuzzy = False + if oldkey != newkey: + fuzzy = True + fuzzy_matches.add(oldkey) + oldmsg = messages.get(oldkey) + assert oldmsg is not None + if isinstance(oldmsg.id, str): + message.previous_id = [oldmsg.id] + else: + message.previous_id = list(oldmsg.id) + else: + oldmsg = remaining.pop(oldkey, None) + assert oldmsg is not None + message.string = oldmsg.string + + if keep_user_comments: + message.user_comments = list(distinct(oldmsg.user_comments)) + + if isinstance(message.id, (list, tuple)): + if not isinstance(message.string, (list, tuple)): + fuzzy = True + message.string = tuple( + [message.string] + ([''] * (len(message.id) - 1)), + ) + elif len(message.string) != self.num_plurals: + fuzzy = True + message.string = tuple(message.string[:len(oldmsg.string)]) + elif isinstance(message.string, (list, tuple)): + fuzzy = True + message.string = message.string[0] + message.flags |= oldmsg.flags + if fuzzy: + message.flags |= {'fuzzy'} + self[message.id] = message + + for message in template: + if message.id: + key = self._key_for(message.id, message.context) + if key in messages: + _merge(message, key, key) + else: + if not no_fuzzy_matching: + # do some fuzzy matching with difflib + matches = get_close_matches( + self._to_fuzzy_match_key(key), + fuzzy_candidates.keys(), + 1, + ) + if matches: + modified_key = matches[0] + newkey, newctxt = fuzzy_candidates[modified_key] + if newctxt is not None: + newkey = newkey, newctxt + _merge(message, newkey, key) + continue + + self[message.id] = message + + for msgid in remaining: + if no_fuzzy_matching or msgid not in fuzzy_matches: + self.obsolete[msgid] = remaining[msgid] + + if update_header_comment: + # Allow the updated catalog's header to be rewritten based on the + # template's header + self.header_comment = template.header_comment + + # Make updated catalog's POT-Creation-Date equal to the template + # used to update the catalog + if update_creation_date: + self.creation_date = template.creation_date + + def _to_fuzzy_match_key(self, key: tuple[str, str] | str) -> str: + """Converts a message key to a string suitable for fuzzy matching.""" + if isinstance(key, tuple): + matchkey = key[0] # just the msgid, no context + else: + matchkey = key + return matchkey.lower().strip() + + def _key_for(self, id: _MessageID, context: str | None = None) -> tuple[str, str] | str: + """The key for a message is just the singular ID even for pluralizable + messages, but is a ``(msgid, msgctxt)`` tuple for context-specific + messages. + """ + key = id + if isinstance(key, (list, tuple)): + key = id[0] + if context is not None: + key = (key, context) + return key + + def is_identical(self, other: Catalog) -> bool: + """Checks if catalogs are identical, taking into account messages and + headers. + """ + assert isinstance(other, Catalog) + for key in self._messages.keys() | other._messages.keys(): + message_1 = self.get(key) + message_2 = other.get(key) + if ( + message_1 is None + or message_2 is None + or not message_1.is_identical(message_2) + ): + return False + return dict(self.mime_headers) == dict(other.mime_headers) diff --git a/.venv/lib/python3.9/site-packages/babel/messages/checkers.py b/.venv/lib/python3.9/site-packages/babel/messages/checkers.py new file mode 100644 index 00000000..df7c3ca7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/messages/checkers.py @@ -0,0 +1,168 @@ +""" + babel.messages.checkers + ~~~~~~~~~~~~~~~~~~~~~~~ + + Various routines that help with validation of translations. + + :since: version 0.9 + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" +from __future__ import annotations + +from collections.abc import Callable + +from babel.messages.catalog import PYTHON_FORMAT, Catalog, Message, TranslationError + +#: list of format chars that are compatible to each other +_string_format_compatibilities = [ + {'i', 'd', 'u'}, + {'x', 'X'}, + {'f', 'F', 'g', 'G'}, +] + + +def num_plurals(catalog: Catalog | None, message: Message) -> None: + """Verify the number of plurals in the translation.""" + if not message.pluralizable: + if not isinstance(message.string, str): + raise TranslationError("Found plural forms for non-pluralizable " + "message") + return + + # skip further tests if no catalog is provided. + elif catalog is None: + return + + msgstrs = message.string + if not isinstance(msgstrs, (list, tuple)): + msgstrs = (msgstrs,) + if len(msgstrs) != catalog.num_plurals: + raise TranslationError("Wrong number of plural forms (expected %d)" % + catalog.num_plurals) + + +def python_format(catalog: Catalog | None, message: Message) -> None: + """Verify the format string placeholders in the translation.""" + if 'python-format' not in message.flags: + return + msgids = message.id + if not isinstance(msgids, (list, tuple)): + msgids = (msgids,) + msgstrs = message.string + if not isinstance(msgstrs, (list, tuple)): + msgstrs = (msgstrs,) + + for msgid, msgstr in zip(msgids, msgstrs): + if msgstr: + _validate_format(msgid, msgstr) + + +def _validate_format(format: str, alternative: str) -> None: + """Test format string `alternative` against `format`. `format` can be the + msgid of a message and `alternative` one of the `msgstr`\\s. The two + arguments are not interchangeable as `alternative` may contain less + placeholders if `format` uses named placeholders. + + If the string formatting of `alternative` is compatible to `format` the + function returns `None`, otherwise a `TranslationError` is raised. + + Examples for compatible format strings: + + >>> _validate_format('Hello %s!', 'Hallo %s!') + >>> _validate_format('Hello %i!', 'Hallo %d!') + + Example for an incompatible format strings: + + >>> _validate_format('Hello %(name)s!', 'Hallo %s!') + Traceback (most recent call last): + ... + TranslationError: the format strings are of different kinds + + This function is used by the `python_format` checker. + + :param format: The original format string + :param alternative: The alternative format string that should be checked + against format + :raises TranslationError: on formatting errors + """ + + def _parse(string: str) -> list[tuple[str, str]]: + result: list[tuple[str, str]] = [] + for match in PYTHON_FORMAT.finditer(string): + name, format, typechar = match.groups() + if typechar == '%' and name is None: + continue + result.append((name, str(typechar))) + return result + + def _compatible(a: str, b: str) -> bool: + if a == b: + return True + for set in _string_format_compatibilities: + if a in set and b in set: + return True + return False + + def _check_positional(results: list[tuple[str, str]]) -> bool: + positional = None + for name, _char in results: + if positional is None: + positional = name is None + else: + if (name is None) != positional: + raise TranslationError('format string mixes positional ' + 'and named placeholders') + return bool(positional) + + a, b = map(_parse, (format, alternative)) + + if not a: + return + + # now check if both strings are positional or named + a_positional, b_positional = map(_check_positional, (a, b)) + if a_positional and not b_positional and not b: + raise TranslationError('placeholders are incompatible') + elif a_positional != b_positional: + raise TranslationError('the format strings are of different kinds') + + # if we are operating on positional strings both must have the + # same number of format chars and those must be compatible + if a_positional: + if len(a) != len(b): + raise TranslationError('positional format placeholders are ' + 'unbalanced') + for idx, ((_, first), (_, second)) in enumerate(zip(a, b)): + if not _compatible(first, second): + raise TranslationError('incompatible format for placeholder ' + '%d: %r and %r are not compatible' % + (idx + 1, first, second)) + + # otherwise the second string must not have names the first one + # doesn't have and the types of those included must be compatible + else: + type_map = dict(a) + for name, typechar in b: + if name not in type_map: + raise TranslationError(f'unknown named placeholder {name!r}') + elif not _compatible(typechar, type_map[name]): + raise TranslationError( + f'incompatible format for placeholder {name!r}: ' + f'{typechar!r} and {type_map[name]!r} are not compatible', + ) + + +def _find_checkers() -> list[Callable[[Catalog | None, Message], object]]: + from babel.messages._compat import find_entrypoints + checkers: list[Callable[[Catalog | None, Message], object]] = [] + checkers.extend(load() for (name, load) in find_entrypoints('babel.checkers')) + if len(checkers) == 0: + # if entrypoints are not available or no usable egg-info was found + # (see #230), just resort to hard-coded checkers + return [num_plurals, python_format] + return checkers + + +checkers: list[Callable[[Catalog | None, Message], object]] = _find_checkers() diff --git a/.venv/lib/python3.9/site-packages/babel/messages/extract.py b/.venv/lib/python3.9/site-packages/babel/messages/extract.py new file mode 100644 index 00000000..7f4230f6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/messages/extract.py @@ -0,0 +1,852 @@ +""" + babel.messages.extract + ~~~~~~~~~~~~~~~~~~~~~~ + + Basic infrastructure for extracting localizable messages from source files. + + This module defines an extensible system for collecting localizable message + strings from a variety of sources. A native extractor for Python source + files is builtin, extractors for other sources can be added using very + simple plugins. + + The main entry points into the extraction functionality are the functions + `extract_from_dir` and `extract_from_file`. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" +from __future__ import annotations + +import ast +import io +import os +import sys +import tokenize +from collections.abc import ( + Callable, + Collection, + Generator, + Iterable, + Mapping, + MutableSequence, +) +from functools import lru_cache +from os.path import relpath +from textwrap import dedent +from tokenize import COMMENT, NAME, NL, OP, STRING, generate_tokens +from typing import TYPE_CHECKING, Any, TypedDict + +from babel.messages._compat import find_entrypoints +from babel.util import parse_encoding, parse_future_flags, pathmatch + +if TYPE_CHECKING: + from typing import IO, Final, Protocol + + from _typeshed import SupportsItems, SupportsRead, SupportsReadline + from typing_extensions import TypeAlias + + class _PyOptions(TypedDict, total=False): + encoding: str + + class _JSOptions(TypedDict, total=False): + encoding: str + jsx: bool + template_string: bool + parse_template_string: bool + + class _FileObj(SupportsRead[bytes], SupportsReadline[bytes], Protocol): + def seek(self, __offset: int, __whence: int = ...) -> int: ... + def tell(self) -> int: ... + + _SimpleKeyword: TypeAlias = tuple[int | tuple[int, int] | tuple[int, str], ...] | None + _Keyword: TypeAlias = dict[int | None, _SimpleKeyword] | _SimpleKeyword + + # 5-tuple of (filename, lineno, messages, comments, context) + _FileExtractionResult: TypeAlias = tuple[str, int, str | tuple[str, ...], list[str], str | None] + + # 4-tuple of (lineno, message, comments, context) + _ExtractionResult: TypeAlias = tuple[int, str | tuple[str, ...], list[str], str | None] + + # Required arguments: fileobj, keywords, comment_tags, options + # Return value: Iterable of (lineno, message, comments, context) + _CallableExtractionMethod: TypeAlias = Callable[ + [_FileObj | IO[bytes], Mapping[str, _Keyword], Collection[str], Mapping[str, Any]], + Iterable[_ExtractionResult], + ] + + _ExtractionMethod: TypeAlias = _CallableExtractionMethod | str + +GROUP_NAME: Final[str] = 'babel.extractors' + +DEFAULT_KEYWORDS: dict[str, _Keyword] = { + '_': None, + 'gettext': None, + 'ngettext': (1, 2), + 'ugettext': None, + 'ungettext': (1, 2), + 'dgettext': (2,), + 'dngettext': (2, 3), + 'N_': None, + 'pgettext': ((1, 'c'), 2), + 'npgettext': ((1, 'c'), 2, 3), +} + +DEFAULT_MAPPING: list[tuple[str, str]] = [('**.py', 'python')] + +# New tokens in Python 3.12, or None on older versions +FSTRING_START = getattr(tokenize, "FSTRING_START", None) +FSTRING_MIDDLE = getattr(tokenize, "FSTRING_MIDDLE", None) +FSTRING_END = getattr(tokenize, "FSTRING_END", None) + + +def _strip_comment_tags(comments: MutableSequence[str], tags: Iterable[str]): + """Helper function for `extract` that strips comment tags from strings + in a list of comment lines. This functions operates in-place. + """ + def _strip(line: str): + for tag in tags: + if line.startswith(tag): + return line[len(tag):].strip() + return line + comments[:] = map(_strip, comments) + + +def default_directory_filter(dirpath: str | os.PathLike[str]) -> bool: + subdir = os.path.basename(dirpath) + # Legacy default behavior: ignore dot and underscore directories + return not (subdir.startswith('.') or subdir.startswith('_')) + + +def extract_from_dir( + dirname: str | os.PathLike[str] | None = None, + method_map: Iterable[tuple[str, str]] = DEFAULT_MAPPING, + options_map: SupportsItems[str, dict[str, Any]] | None = None, + keywords: Mapping[str, _Keyword] = DEFAULT_KEYWORDS, + comment_tags: Collection[str] = (), + callback: Callable[[str, str, dict[str, Any]], object] | None = None, + strip_comment_tags: bool = False, + directory_filter: Callable[[str], bool] | None = None, +) -> Generator[_FileExtractionResult, None, None]: + """Extract messages from any source files found in the given directory. + + This function generates tuples of the form ``(filename, lineno, message, + comments, context)``. + + Which extraction method is used per file is determined by the `method_map` + parameter, which maps extended glob patterns to extraction method names. + For example, the following is the default mapping: + + >>> method_map = [ + ... ('**.py', 'python') + ... ] + + This basically says that files with the filename extension ".py" at any + level inside the directory should be processed by the "python" extraction + method. Files that don't match any of the mapping patterns are ignored. See + the documentation of the `pathmatch` function for details on the pattern + syntax. + + The following extended mapping would also use the "genshi" extraction + method on any file in "templates" subdirectory: + + >>> method_map = [ + ... ('**/templates/**.*', 'genshi'), + ... ('**.py', 'python') + ... ] + + The dictionary provided by the optional `options_map` parameter augments + these mappings. It uses extended glob patterns as keys, and the values are + dictionaries mapping options names to option values (both strings). + + The glob patterns of the `options_map` do not necessarily need to be the + same as those used in the method mapping. For example, while all files in + the ``templates`` folders in an application may be Genshi applications, the + options for those files may differ based on extension: + + >>> options_map = { + ... '**/templates/**.txt': { + ... 'template_class': 'genshi.template:TextTemplate', + ... 'encoding': 'latin-1' + ... }, + ... '**/templates/**.html': { + ... 'include_attrs': '' + ... } + ... } + + :param dirname: the path to the directory to extract messages from. If + not given the current working directory is used. + :param method_map: a list of ``(pattern, method)`` tuples that maps of + extraction method names to extended glob patterns + :param options_map: a dictionary of additional options (optional) + :param keywords: a dictionary mapping keywords (i.e. names of functions + that should be recognized as translation functions) to + tuples that specify which of their arguments contain + localizable strings + :param comment_tags: a list of tags of translator comments to search for + and include in the results + :param callback: a function that is called for every file that message are + extracted from, just before the extraction itself is + performed; the function is passed the filename, the name + of the extraction method and and the options dictionary as + positional arguments, in that order + :param strip_comment_tags: a flag that if set to `True` causes all comment + tags to be removed from the collected comments. + :param directory_filter: a callback to determine whether a directory should + be recursed into. Receives the full directory path; + should return True if the directory is valid. + :see: `pathmatch` + """ + if dirname is None: + dirname = os.getcwd() + if options_map is None: + options_map = {} + if directory_filter is None: + directory_filter = default_directory_filter + + absname = os.path.abspath(dirname) + for root, dirnames, filenames in os.walk(absname): + dirnames[:] = [ + subdir for subdir in dirnames + if directory_filter(os.path.join(root, subdir)) + ] + dirnames.sort() + filenames.sort() + for filename in filenames: + filepath = os.path.join(root, filename).replace(os.sep, '/') + + yield from check_and_call_extract_file( + filepath, + method_map, + options_map, + callback, + keywords, + comment_tags, + strip_comment_tags, + dirpath=absname, + ) + + +def check_and_call_extract_file( + filepath: str | os.PathLike[str], + method_map: Iterable[tuple[str, str]], + options_map: SupportsItems[str, dict[str, Any]], + callback: Callable[[str, str, dict[str, Any]], object] | None, + keywords: Mapping[str, _Keyword], + comment_tags: Collection[str], + strip_comment_tags: bool, + dirpath: str | os.PathLike[str] | None = None, +) -> Generator[_FileExtractionResult, None, None]: + """Checks if the given file matches an extraction method mapping, and if so, calls extract_from_file. + + Note that the extraction method mappings are based relative to dirpath. + So, given an absolute path to a file `filepath`, we want to check using + just the relative path from `dirpath` to `filepath`. + + Yields 5-tuples (filename, lineno, messages, comments, context). + + :param filepath: An absolute path to a file that exists. + :param method_map: a list of ``(pattern, method)`` tuples that maps of + extraction method names to extended glob patterns + :param options_map: a dictionary of additional options (optional) + :param callback: a function that is called for every file that message are + extracted from, just before the extraction itself is + performed; the function is passed the filename, the name + of the extraction method and and the options dictionary as + positional arguments, in that order + :param keywords: a dictionary mapping keywords (i.e. names of functions + that should be recognized as translation functions) to + tuples that specify which of their arguments contain + localizable strings + :param comment_tags: a list of tags of translator comments to search for + and include in the results + :param strip_comment_tags: a flag that if set to `True` causes all comment + tags to be removed from the collected comments. + :param dirpath: the path to the directory to extract messages from. + :return: iterable of 5-tuples (filename, lineno, messages, comments, context) + :rtype: Iterable[tuple[str, int, str|tuple[str], list[str], str|None] + """ + # filename is the relative path from dirpath to the actual file + filename = relpath(filepath, dirpath) + + for pattern, method in method_map: + if not pathmatch(pattern, filename): + continue + + options = {} + for opattern, odict in options_map.items(): + if pathmatch(opattern, filename): + options = odict + break + if callback: + callback(filename, method, options) + for message_tuple in extract_from_file( + method, filepath, + keywords=keywords, + comment_tags=comment_tags, + options=options, + strip_comment_tags=strip_comment_tags, + ): + yield (filename, *message_tuple) + + break + + +def extract_from_file( + method: _ExtractionMethod, + filename: str | os.PathLike[str], + keywords: Mapping[str, _Keyword] = DEFAULT_KEYWORDS, + comment_tags: Collection[str] = (), + options: Mapping[str, Any] | None = None, + strip_comment_tags: bool = False, +) -> list[_ExtractionResult]: + """Extract messages from a specific file. + + This function returns a list of tuples of the form ``(lineno, message, comments, context)``. + + :param filename: the path to the file to extract messages from + :param method: a string specifying the extraction method (.e.g. "python") + :param keywords: a dictionary mapping keywords (i.e. names of functions + that should be recognized as translation functions) to + tuples that specify which of their arguments contain + localizable strings + :param comment_tags: a list of translator tags to search for and include + in the results + :param strip_comment_tags: a flag that if set to `True` causes all comment + tags to be removed from the collected comments. + :param options: a dictionary of additional options (optional) + :returns: list of tuples of the form ``(lineno, message, comments, context)`` + :rtype: list[tuple[int, str|tuple[str], list[str], str|None] + """ + if method == 'ignore': + return [] + + with open(filename, 'rb') as fileobj: + return list(extract(method, fileobj, keywords, comment_tags, + options, strip_comment_tags)) + + +def _match_messages_against_spec( + lineno: int, + messages: list[str | None], + comments: list[str], + fileobj: _FileObj, + spec: tuple[int | tuple[int, str], ...], +): + translatable = [] + context = None + + # last_index is 1 based like the keyword spec + last_index = len(messages) + for index in spec: + if isinstance(index, tuple): # (n, 'c') + context = messages[index[0] - 1] + continue + if last_index < index: + # Not enough arguments + return + message = messages[index - 1] + if message is None: + return + translatable.append(message) + + # keyword spec indexes are 1 based, therefore '-1' + if isinstance(spec[0], tuple): + # context-aware *gettext method + first_msg_index = spec[1] - 1 + else: + first_msg_index = spec[0] - 1 + # An empty string msgid isn't valid, emit a warning + if not messages[first_msg_index]: + filename = (getattr(fileobj, "name", None) or "(unknown)") + sys.stderr.write( + f"{filename}:{lineno}: warning: Empty msgid. It is reserved by GNU gettext: gettext(\"\") " + f"returns the header entry with meta information, not the empty string.\n", + ) + return + + translatable = tuple(translatable) + if len(translatable) == 1: + translatable = translatable[0] + + return lineno, translatable, comments, context + + +@lru_cache(maxsize=None) +def _find_extractor(name: str): + for ep_name, load in find_entrypoints(GROUP_NAME): + if ep_name == name: + return load() + return None + + +def extract( + method: _ExtractionMethod, + fileobj: _FileObj, + keywords: Mapping[str, _Keyword] = DEFAULT_KEYWORDS, + comment_tags: Collection[str] = (), + options: Mapping[str, Any] | None = None, + strip_comment_tags: bool = False, +) -> Generator[_ExtractionResult, None, None]: + """Extract messages from the given file-like object using the specified + extraction method. + + This function returns tuples of the form ``(lineno, message, comments, context)``. + + The implementation dispatches the actual extraction to plugins, based on the + value of the ``method`` parameter. + + >>> source = b'''# foo module + ... def run(argv): + ... print(_('Hello, world!')) + ... ''' + + >>> from io import BytesIO + >>> for message in extract('python', BytesIO(source)): + ... print(message) + (3, u'Hello, world!', [], None) + + :param method: an extraction method (a callable), or + a string specifying the extraction method (.e.g. "python"); + if this is a simple name, the extraction function will be + looked up by entry point; if it is an explicit reference + to a function (of the form ``package.module:funcname`` or + ``package.module.funcname``), the corresponding function + will be imported and used + :param fileobj: the file-like object the messages should be extracted from + :param keywords: a dictionary mapping keywords (i.e. names of functions + that should be recognized as translation functions) to + tuples that specify which of their arguments contain + localizable strings + :param comment_tags: a list of translator tags to search for and include + in the results + :param options: a dictionary of additional options (optional) + :param strip_comment_tags: a flag that if set to `True` causes all comment + tags to be removed from the collected comments. + :raise ValueError: if the extraction method is not registered + :returns: iterable of tuples of the form ``(lineno, message, comments, context)`` + :rtype: Iterable[tuple[int, str|tuple[str], list[str], str|None] + """ + if callable(method): + func = method + elif ':' in method or '.' in method: + if ':' not in method: + lastdot = method.rfind('.') + module, attrname = method[:lastdot], method[lastdot + 1:] + else: + module, attrname = method.split(':', 1) + func = getattr(__import__(module, {}, {}, [attrname]), attrname) + else: + func = _find_extractor(method) + if func is None: + # if no named entry point was found, + # we resort to looking up a builtin extractor + func = _BUILTIN_EXTRACTORS.get(method) + + if func is None: + raise ValueError(f"Unknown extraction method {method!r}") + + results = func(fileobj, keywords.keys(), comment_tags, + options=options or {}) + + for lineno, funcname, messages, comments in results: + if not isinstance(messages, (list, tuple)): + messages = [messages] + if not messages: + continue + + specs = keywords[funcname] or None if funcname else None + # {None: x} may be collapsed into x for backwards compatibility. + if not isinstance(specs, dict): + specs = {None: specs} + + if strip_comment_tags: + _strip_comment_tags(comments, comment_tags) + + # None matches all arities. + for arity in (None, len(messages)): + try: + spec = specs[arity] + except KeyError: + continue + if spec is None: + spec = (1,) + result = _match_messages_against_spec(lineno, messages, comments, fileobj, spec) + if result is not None: + yield result + + +def extract_nothing( + fileobj: _FileObj, + keywords: Mapping[str, _Keyword], + comment_tags: Collection[str], + options: Mapping[str, Any], +) -> list[_ExtractionResult]: + """Pseudo extractor that does not actually extract anything, but simply + returns an empty list. + """ + return [] + + +def extract_python( + fileobj: IO[bytes], + keywords: Mapping[str, _Keyword], + comment_tags: Collection[str], + options: _PyOptions, +) -> Generator[_ExtractionResult, None, None]: + """Extract messages from Python source code. + + It returns an iterator yielding tuples in the following form ``(lineno, + funcname, message, comments)``. + + :param fileobj: the seekable, file-like object the messages should be + extracted from + :param keywords: a list of keywords (i.e. function names) that should be + recognized as translation functions + :param comment_tags: a list of translator tags to search for and include + in the results + :param options: a dictionary of additional options (optional) + :rtype: ``iterator`` + """ + funcname = lineno = message_lineno = None + call_stack = -1 + buf = [] + messages = [] + translator_comments = [] + in_def = in_translator_comments = False + comment_tag = None + + encoding = parse_encoding(fileobj) or options.get('encoding', 'UTF-8') + future_flags = parse_future_flags(fileobj, encoding) + next_line = lambda: fileobj.readline().decode(encoding) + + tokens = generate_tokens(next_line) + + # Current prefix of a Python 3.12 (PEP 701) f-string, or None if we're not + # currently parsing one. + current_fstring_start = None + + for tok, value, (lineno, _), _, _ in tokens: + if call_stack == -1 and tok == NAME and value in ('def', 'class'): + in_def = True + elif tok == OP and value == '(': + if in_def: + # Avoid false positives for declarations such as: + # def gettext(arg='message'): + in_def = False + continue + if funcname: + call_stack += 1 + elif in_def and tok == OP and value == ':': + # End of a class definition without parens + in_def = False + continue + elif call_stack == -1 and tok == COMMENT: + # Strip the comment token from the line + value = value[1:].strip() + if in_translator_comments and \ + translator_comments[-1][0] == lineno - 1: + # We're already inside a translator comment, continue appending + translator_comments.append((lineno, value)) + continue + # If execution reaches this point, let's see if comment line + # starts with one of the comment tags + for comment_tag in comment_tags: + if value.startswith(comment_tag): + in_translator_comments = True + translator_comments.append((lineno, value)) + break + elif funcname and call_stack == 0: + nested = (tok == NAME and value in keywords) + if (tok == OP and value == ')') or nested: + if buf: + messages.append(''.join(buf)) + del buf[:] + else: + messages.append(None) + + messages = tuple(messages) if len(messages) > 1 else messages[0] + # Comments don't apply unless they immediately + # precede the message + if translator_comments and \ + translator_comments[-1][0] < message_lineno - 1: + translator_comments = [] + + yield (message_lineno, funcname, messages, + [comment[1] for comment in translator_comments]) + + funcname = lineno = message_lineno = None + call_stack = -1 + messages = [] + translator_comments = [] + in_translator_comments = False + if nested: + funcname = value + elif tok == STRING: + val = _parse_python_string(value, encoding, future_flags) + if val is not None: + if not message_lineno: + message_lineno = lineno + buf.append(val) + + # Python 3.12+, see https://peps.python.org/pep-0701/#new-tokens + elif tok == FSTRING_START: + current_fstring_start = value + if not message_lineno: + message_lineno = lineno + elif tok == FSTRING_MIDDLE: + if current_fstring_start is not None: + current_fstring_start += value + elif tok == FSTRING_END: + if current_fstring_start is not None: + fstring = current_fstring_start + value + val = _parse_python_string(fstring, encoding, future_flags) + if val is not None: + buf.append(val) + + elif tok == OP and value == ',': + if buf: + messages.append(''.join(buf)) + del buf[:] + else: + messages.append(None) + if translator_comments: + # We have translator comments, and since we're on a + # comma(,) user is allowed to break into a new line + # Let's increase the last comment's lineno in order + # for the comment to still be a valid one + old_lineno, old_comment = translator_comments.pop() + translator_comments.append((old_lineno + 1, old_comment)) + + elif tok != NL and not message_lineno: + message_lineno = lineno + elif call_stack > 0 and tok == OP and value == ')': + call_stack -= 1 + elif funcname and call_stack == -1: + funcname = None + elif tok == NAME and value in keywords: + funcname = value + + if current_fstring_start is not None and tok not in {FSTRING_START, FSTRING_MIDDLE}: + # In Python 3.12, tokens other than FSTRING_* mean the + # f-string is dynamic, so we don't wan't to extract it. + # And if it's FSTRING_END, we've already handled it above. + # Let's forget that we're in an f-string. + current_fstring_start = None + + +def _parse_python_string(value: str, encoding: str, future_flags: int) -> str | None: + # Unwrap quotes in a safe manner, maintaining the string's encoding + # https://sourceforge.net/tracker/?func=detail&atid=355470&aid=617979&group_id=5470 + code = compile( + f'# coding={str(encoding)}\n{value}', + '', + 'eval', + ast.PyCF_ONLY_AST | future_flags, + ) + if isinstance(code, ast.Expression): + body = code.body + if isinstance(body, ast.Constant): + return body.value + if isinstance(body, ast.JoinedStr): # f-string + if all(isinstance(node, ast.Constant) for node in body.values): + return ''.join(node.value for node in body.values) + # TODO: we could raise an error or warning when not all nodes are constants + return None + + +def extract_javascript( + fileobj: _FileObj, + keywords: Mapping[str, _Keyword], + comment_tags: Collection[str], + options: _JSOptions, + lineno: int = 1, +) -> Generator[_ExtractionResult, None, None]: + """Extract messages from JavaScript source code. + + :param fileobj: the seekable, file-like object the messages should be + extracted from + :param keywords: a list of keywords (i.e. function names) that should be + recognized as translation functions + :param comment_tags: a list of translator tags to search for and include + in the results + :param options: a dictionary of additional options (optional) + Supported options are: + * `jsx` -- set to false to disable JSX/E4X support. + * `template_string` -- if `True`, supports gettext(`key`) + * `parse_template_string` -- if `True` will parse the + contents of javascript + template strings. + :param lineno: line number offset (for parsing embedded fragments) + """ + from babel.messages.jslexer import Token, tokenize, unquote_string + funcname = message_lineno = None + messages = [] + last_argument = None + translator_comments = [] + concatenate_next = False + encoding = options.get('encoding', 'utf-8') + last_token = None + call_stack = -1 + dotted = any('.' in kw for kw in keywords) + for token in tokenize( + fileobj.read().decode(encoding), + jsx=options.get("jsx", True), + template_string=options.get("template_string", True), + dotted=dotted, + lineno=lineno, + ): + if ( # Turn keyword`foo` expressions into keyword("foo") calls: + funcname and # have a keyword... + (last_token and last_token.type == 'name') and # we've seen nothing after the keyword... + token.type == 'template_string' # this is a template string + ): + message_lineno = token.lineno + messages = [unquote_string(token.value)] + call_stack = 0 + token = Token('operator', ')', token.lineno) + + if options.get('parse_template_string') and not funcname and token.type == 'template_string': + yield from parse_template_string(token.value, keywords, comment_tags, options, token.lineno) + + elif token.type == 'operator' and token.value == '(': + if funcname: + message_lineno = token.lineno + call_stack += 1 + + elif call_stack == -1 and token.type == 'linecomment': + value = token.value[2:].strip() + if translator_comments and \ + translator_comments[-1][0] == token.lineno - 1: + translator_comments.append((token.lineno, value)) + continue + + for comment_tag in comment_tags: + if value.startswith(comment_tag): + translator_comments.append((token.lineno, value.strip())) + break + + elif token.type == 'multilinecomment': + # only one multi-line comment may precede a translation + translator_comments = [] + value = token.value[2:-2].strip() + for comment_tag in comment_tags: + if value.startswith(comment_tag): + lines = value.splitlines() + if lines: + lines[0] = lines[0].strip() + lines[1:] = dedent('\n'.join(lines[1:])).splitlines() + for offset, line in enumerate(lines): + translator_comments.append((token.lineno + offset, + line)) + break + + elif funcname and call_stack == 0: + if token.type == 'operator' and token.value == ')': + if last_argument is not None: + messages.append(last_argument) + if len(messages) > 1: + messages = tuple(messages) + elif messages: + messages = messages[0] + else: + messages = None + + # Comments don't apply unless they immediately precede the + # message + if translator_comments and \ + translator_comments[-1][0] < message_lineno - 1: + translator_comments = [] + + if messages is not None: + yield (message_lineno, funcname, messages, + [comment[1] for comment in translator_comments]) + + funcname = message_lineno = last_argument = None + concatenate_next = False + translator_comments = [] + messages = [] + call_stack = -1 + + elif token.type in ('string', 'template_string'): + new_value = unquote_string(token.value) + if concatenate_next: + last_argument = (last_argument or '') + new_value + concatenate_next = False + else: + last_argument = new_value + + elif token.type == 'operator': + if token.value == ',': + if last_argument is not None: + messages.append(last_argument) + last_argument = None + else: + messages.append(None) + concatenate_next = False + elif token.value == '+': + concatenate_next = True + + elif call_stack > 0 and token.type == 'operator' \ + and token.value == ')': + call_stack -= 1 + + elif funcname and call_stack == -1: + funcname = None + + elif call_stack == -1 and token.type == 'name' and \ + token.value in keywords and \ + (last_token is None or last_token.type != 'name' or + last_token.value != 'function'): + funcname = token.value + + last_token = token + + +def parse_template_string( + template_string: str, + keywords: Mapping[str, _Keyword], + comment_tags: Collection[str], + options: _JSOptions, + lineno: int = 1, +) -> Generator[_ExtractionResult, None, None]: + """Parse JavaScript template string. + + :param template_string: the template string to be parsed + :param keywords: a list of keywords (i.e. function names) that should be + recognized as translation functions + :param comment_tags: a list of translator tags to search for and include + in the results + :param options: a dictionary of additional options (optional) + :param lineno: starting line number (optional) + """ + from babel.messages.jslexer import line_re + prev_character = None + level = 0 + inside_str = False + expression_contents = '' + for character in template_string[1:-1]: + if not inside_str and character in ('"', "'", '`'): + inside_str = character + elif inside_str == character and prev_character != r'\\': + inside_str = False + if level: + expression_contents += character + if not inside_str: + if character == '{' and prev_character == '$': + level += 1 + elif level and character == '}': + level -= 1 + if level == 0 and expression_contents: + expression_contents = expression_contents[0:-1] + fake_file_obj = io.BytesIO(expression_contents.encode()) + yield from extract_javascript(fake_file_obj, keywords, comment_tags, options, lineno) + lineno += len(line_re.findall(expression_contents)) + expression_contents = '' + prev_character = character + + +_BUILTIN_EXTRACTORS = { + 'ignore': extract_nothing, + 'python': extract_python, + 'javascript': extract_javascript, +} diff --git a/.venv/lib/python3.9/site-packages/babel/messages/frontend.py b/.venv/lib/python3.9/site-packages/babel/messages/frontend.py new file mode 100644 index 00000000..29e5a2aa --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/messages/frontend.py @@ -0,0 +1,1202 @@ +""" + babel.messages.frontend + ~~~~~~~~~~~~~~~~~~~~~~~ + + Frontends for the message extraction functionality. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" + +from __future__ import annotations + +import datetime +import fnmatch +import logging +import optparse +import os +import re +import shutil +import sys +import tempfile +import warnings +from configparser import RawConfigParser +from io import StringIO +from typing import BinaryIO, Iterable, Literal + +from babel import Locale, localedata +from babel import __version__ as VERSION +from babel.core import UnknownLocaleError +from babel.messages.catalog import DEFAULT_HEADER, Catalog +from babel.messages.extract import ( + DEFAULT_KEYWORDS, + DEFAULT_MAPPING, + check_and_call_extract_file, + extract_from_dir, +) +from babel.messages.mofile import write_mo +from babel.messages.pofile import read_po, write_po +from babel.util import LOCALTZ + +log = logging.getLogger('babel') + + +class BaseError(Exception): + pass + + +class OptionError(BaseError): + pass + + +class SetupError(BaseError): + pass + + +class ConfigurationError(BaseError): + """ + Raised for errors in configuration files. + """ + + +def listify_value(arg, split=None): + """ + Make a list out of an argument. + + Values from `distutils` argument parsing are always single strings; + values from `optparse` parsing may be lists of strings that may need + to be further split. + + No matter the input, this function returns a flat list of whitespace-trimmed + strings, with `None` values filtered out. + + >>> listify_value("foo bar") + ['foo', 'bar'] + >>> listify_value(["foo bar"]) + ['foo', 'bar'] + >>> listify_value([["foo"], "bar"]) + ['foo', 'bar'] + >>> listify_value([["foo"], ["bar", None, "foo"]]) + ['foo', 'bar', 'foo'] + >>> listify_value("foo, bar, quux", ",") + ['foo', 'bar', 'quux'] + + :param arg: A string or a list of strings + :param split: The argument to pass to `str.split()`. + :return: + """ + out = [] + + if not isinstance(arg, (list, tuple)): + arg = [arg] + + for val in arg: + if val is None: + continue + if isinstance(val, (list, tuple)): + out.extend(listify_value(val, split=split)) + continue + out.extend(s.strip() for s in str(val).split(split)) + assert all(isinstance(val, str) for val in out) + return out + + +class CommandMixin: + # This class is a small shim between Distutils commands and + # optparse option parsing in the frontend command line. + + #: Option name to be input as `args` on the script command line. + as_args = None + + #: Options which allow multiple values. + #: This is used by the `optparse` transmogrification code. + multiple_value_options = () + + #: Options which are booleans. + #: This is used by the `optparse` transmogrification code. + # (This is actually used by distutils code too, but is never + # declared in the base class.) + boolean_options = () + + #: Option aliases, to retain standalone command compatibility. + #: Distutils does not support option aliases, but optparse does. + #: This maps the distutils argument name to an iterable of aliases + #: that are usable with optparse. + option_aliases = {} + + #: Choices for options that needed to be restricted to specific + #: list of choices. + option_choices = {} + + #: Log object. To allow replacement in the script command line runner. + log = log + + def __init__(self, dist=None): + # A less strict version of distutils' `__init__`. + self.distribution = dist + self.initialize_options() + self._dry_run = None + self.verbose = False + self.force = None + self.help = 0 + self.finalized = 0 + + def initialize_options(self): + pass + + def ensure_finalized(self): + if not self.finalized: + self.finalize_options() + self.finalized = 1 + + def finalize_options(self): + raise RuntimeError( + f"abstract method -- subclass {self.__class__} must override", + ) + + +class CompileCatalog(CommandMixin): + description = 'compile message catalogs to binary MO files' + user_options = [ + ('domain=', 'D', + "domains of PO files (space separated list, default 'messages')"), + ('directory=', 'd', + 'path to base directory containing the catalogs'), + ('input-file=', 'i', + 'name of the input file'), + ('output-file=', 'o', + "name of the output file (default " + "'//LC_MESSAGES/.mo')"), + ('locale=', 'l', + 'locale of the catalog to compile'), + ('use-fuzzy', 'f', + 'also include fuzzy translations'), + ('statistics', None, + 'print statistics about translations'), + ] + boolean_options = ['use-fuzzy', 'statistics'] + + def initialize_options(self): + self.domain = 'messages' + self.directory = None + self.input_file = None + self.output_file = None + self.locale = None + self.use_fuzzy = False + self.statistics = False + + def finalize_options(self): + self.domain = listify_value(self.domain) + if not self.input_file and not self.directory: + raise OptionError('you must specify either the input file or the base directory') + if not self.output_file and not self.directory: + raise OptionError('you must specify either the output file or the base directory') + + def run(self): + n_errors = 0 + for domain in self.domain: + for errors in self._run_domain(domain).values(): + n_errors += len(errors) + if n_errors: + self.log.error('%d errors encountered.', n_errors) + return (1 if n_errors else 0) + + def _run_domain(self, domain): + po_files = [] + mo_files = [] + + if not self.input_file: + if self.locale: + po_files.append((self.locale, + os.path.join(self.directory, self.locale, + 'LC_MESSAGES', + f"{domain}.po"))) + mo_files.append(os.path.join(self.directory, self.locale, + 'LC_MESSAGES', + f"{domain}.mo")) + else: + for locale in os.listdir(self.directory): + po_file = os.path.join(self.directory, locale, + 'LC_MESSAGES', f"{domain}.po") + if os.path.exists(po_file): + po_files.append((locale, po_file)) + mo_files.append(os.path.join(self.directory, locale, + 'LC_MESSAGES', + f"{domain}.mo")) + else: + po_files.append((self.locale, self.input_file)) + if self.output_file: + mo_files.append(self.output_file) + else: + mo_files.append(os.path.join(self.directory, self.locale, + 'LC_MESSAGES', + f"{domain}.mo")) + + if not po_files: + raise OptionError('no message catalogs found') + + catalogs_and_errors = {} + + for idx, (locale, po_file) in enumerate(po_files): + mo_file = mo_files[idx] + with open(po_file, 'rb') as infile: + catalog = read_po(infile, locale) + + if self.statistics: + translated = 0 + for message in list(catalog)[1:]: + if message.string: + translated += 1 + percentage = 0 + if len(catalog): + percentage = translated * 100 // len(catalog) + self.log.info( + '%d of %d messages (%d%%) translated in %s', + translated, len(catalog), percentage, po_file, + ) + + if catalog.fuzzy and not self.use_fuzzy: + self.log.info('catalog %s is marked as fuzzy, skipping', po_file) + continue + + catalogs_and_errors[catalog] = catalog_errors = list(catalog.check()) + for message, errors in catalog_errors: + for error in errors: + self.log.error( + 'error: %s:%d: %s', po_file, message.lineno, error, + ) + + self.log.info('compiling catalog %s to %s', po_file, mo_file) + + with open(mo_file, 'wb') as outfile: + write_mo(outfile, catalog, use_fuzzy=self.use_fuzzy) + + return catalogs_and_errors + + +def _make_directory_filter(ignore_patterns): + """ + Build a directory_filter function based on a list of ignore patterns. + """ + + def cli_directory_filter(dirname): + basename = os.path.basename(dirname) + return not any( + fnmatch.fnmatch(basename, ignore_pattern) + for ignore_pattern + in ignore_patterns + ) + + return cli_directory_filter + + +class ExtractMessages(CommandMixin): + description = 'extract localizable strings from the project code' + user_options = [ + ('charset=', None, + 'charset to use in the output file (default "utf-8")'), + ('keywords=', 'k', + 'space-separated list of keywords to look for in addition to the ' + 'defaults (may be repeated multiple times)'), + ('no-default-keywords', None, + 'do not include the default keywords'), + ('mapping-file=', 'F', + 'path to the mapping configuration file'), + ('no-location', None, + 'do not include location comments with filename and line number'), + ('add-location=', None, + 'location lines format. If it is not given or "full", it generates ' + 'the lines with both file name and line number. If it is "file", ' + 'the line number part is omitted. If it is "never", it completely ' + 'suppresses the lines (same as --no-location).'), + ('omit-header', None, + 'do not include msgid "" entry in header'), + ('output-file=', 'o', + 'name of the output file'), + ('width=', 'w', + 'set output line width (default 76)'), + ('no-wrap', None, + 'do not break long message lines, longer than the output line width, ' + 'into several lines'), + ('sort-output', None, + 'generate sorted output (default False)'), + ('sort-by-file', None, + 'sort output by file location (default False)'), + ('msgid-bugs-address=', None, + 'set report address for msgid'), + ('copyright-holder=', None, + 'set copyright holder in output'), + ('project=', None, + 'set project name in output'), + ('version=', None, + 'set project version in output'), + ('add-comments=', 'c', + 'place comment block with TAG (or those preceding keyword lines) in ' + 'output file. Separate multiple TAGs with commas(,)'), # TODO: Support repetition of this argument + ('strip-comments', 's', + 'strip the comment TAGs from the comments.'), + ('input-paths=', None, + 'files or directories that should be scanned for messages. Separate multiple ' + 'files or directories with commas(,)'), # TODO: Support repetition of this argument + ('input-dirs=', None, # TODO (3.x): Remove me. + 'alias for input-paths (does allow files as well as directories).'), + ('ignore-dirs=', None, + 'Patterns for directories to ignore when scanning for messages. ' + 'Separate multiple patterns with spaces (default ".* ._")'), + ('header-comment=', None, + 'header comment for the catalog'), + ('last-translator=', None, + 'set the name and email of the last translator in output'), + ] + boolean_options = [ + 'no-default-keywords', 'no-location', 'omit-header', 'no-wrap', + 'sort-output', 'sort-by-file', 'strip-comments', + ] + as_args = 'input-paths' + multiple_value_options = ( + 'add-comments', + 'keywords', + 'ignore-dirs', + ) + option_aliases = { + 'keywords': ('--keyword',), + 'mapping-file': ('--mapping',), + 'output-file': ('--output',), + 'strip-comments': ('--strip-comment-tags',), + 'last-translator': ('--last-translator',), + } + option_choices = { + 'add-location': ('full', 'file', 'never'), + } + + def initialize_options(self): + self.charset = 'utf-8' + self.keywords = None + self.no_default_keywords = False + self.mapping_file = None + self.no_location = False + self.add_location = None + self.omit_header = False + self.output_file = None + self.input_dirs = None + self.input_paths = None + self.width = None + self.no_wrap = False + self.sort_output = False + self.sort_by_file = False + self.msgid_bugs_address = None + self.copyright_holder = None + self.project = None + self.version = None + self.add_comments = None + self.strip_comments = False + self.include_lineno = True + self.ignore_dirs = None + self.header_comment = None + self.last_translator = None + + def finalize_options(self): + if self.input_dirs: + if not self.input_paths: + self.input_paths = self.input_dirs + else: + raise OptionError( + 'input-dirs and input-paths are mutually exclusive', + ) + + keywords = {} if self.no_default_keywords else DEFAULT_KEYWORDS.copy() + + keywords.update(parse_keywords(listify_value(self.keywords))) + + self.keywords = keywords + + if not self.keywords: + raise OptionError( + 'you must specify new keywords if you disable the default ones', + ) + + if not self.output_file: + raise OptionError('no output file specified') + if self.no_wrap and self.width: + raise OptionError( + "'--no-wrap' and '--width' are mutually exclusive", + ) + if not self.no_wrap and not self.width: + self.width = 76 + elif self.width is not None: + self.width = int(self.width) + + if self.sort_output and self.sort_by_file: + raise OptionError( + "'--sort-output' and '--sort-by-file' are mutually exclusive", + ) + + if self.input_paths: + if isinstance(self.input_paths, str): + self.input_paths = re.split(r',\s*', self.input_paths) + elif self.distribution is not None: + self.input_paths = dict.fromkeys([ + k.split('.', 1)[0] + for k in (self.distribution.packages or ()) + ]).keys() + else: + self.input_paths = [] + + if not self.input_paths: + raise OptionError("no input files or directories specified") + + for path in self.input_paths: + if not os.path.exists(path): + raise OptionError(f"Input path: {path} does not exist") + + self.add_comments = listify_value(self.add_comments or (), ",") + + if self.distribution: + if not self.project: + self.project = self.distribution.get_name() + if not self.version: + self.version = self.distribution.get_version() + + if self.add_location == 'never': + self.no_location = True + elif self.add_location == 'file': + self.include_lineno = False + + ignore_dirs = listify_value(self.ignore_dirs) + if ignore_dirs: + self.directory_filter = _make_directory_filter(ignore_dirs) + else: + self.directory_filter = None + + def _build_callback(self, path: str): + def callback(filename: str, method: str, options: dict): + if method == 'ignore': + return + + # If we explicitly provide a full filepath, just use that. + # Otherwise, path will be the directory path and filename + # is the relative path from that dir to the file. + # So we can join those to get the full filepath. + if os.path.isfile(path): + filepath = path + else: + filepath = os.path.normpath(os.path.join(path, filename)) + + optstr = '' + if options: + opt_values = ", ".join(f'{k}="{v}"' for k, v in options.items()) + optstr = f" ({opt_values})" + self.log.info('extracting messages from %s%s', filepath, optstr) + + return callback + + def run(self): + mappings = self._get_mappings() + with open(self.output_file, 'wb') as outfile: + catalog = Catalog(project=self.project, + version=self.version, + msgid_bugs_address=self.msgid_bugs_address, + copyright_holder=self.copyright_holder, + charset=self.charset, + header_comment=(self.header_comment or DEFAULT_HEADER), + last_translator=self.last_translator) + + for path, method_map, options_map in mappings: + callback = self._build_callback(path) + if os.path.isfile(path): + current_dir = os.getcwd() + extracted = check_and_call_extract_file( + path, method_map, options_map, + callback, self.keywords, self.add_comments, + self.strip_comments, current_dir, + ) + else: + extracted = extract_from_dir( + path, method_map, options_map, + keywords=self.keywords, + comment_tags=self.add_comments, + callback=callback, + strip_comment_tags=self.strip_comments, + directory_filter=self.directory_filter, + ) + for filename, lineno, message, comments, context in extracted: + if os.path.isfile(path): + filepath = filename # already normalized + else: + filepath = os.path.normpath(os.path.join(path, filename)) + + catalog.add(message, None, [(filepath, lineno)], + auto_comments=comments, context=context) + + self.log.info('writing PO template file to %s', self.output_file) + write_po(outfile, catalog, width=self.width, + no_location=self.no_location, + omit_header=self.omit_header, + sort_output=self.sort_output, + sort_by_file=self.sort_by_file, + include_lineno=self.include_lineno) + + def _get_mappings(self): + mappings = [] + + if self.mapping_file: + if self.mapping_file.endswith(".toml"): + with open(self.mapping_file, "rb") as fileobj: + file_style = ( + "pyproject.toml" + if os.path.basename(self.mapping_file) == "pyproject.toml" + else "standalone" + ) + method_map, options_map = _parse_mapping_toml( + fileobj, + filename=self.mapping_file, + style=file_style, + ) + else: + with open(self.mapping_file) as fileobj: + method_map, options_map = parse_mapping_cfg(fileobj, filename=self.mapping_file) + for path in self.input_paths: + mappings.append((path, method_map, options_map)) + + elif getattr(self.distribution, 'message_extractors', None): + message_extractors = self.distribution.message_extractors + for path, mapping in message_extractors.items(): + if isinstance(mapping, str): + method_map, options_map = parse_mapping_cfg(StringIO(mapping)) + else: + method_map, options_map = [], {} + for pattern, method, options in mapping: + method_map.append((pattern, method)) + options_map[pattern] = options or {} + mappings.append((path, method_map, options_map)) + + else: + for path in self.input_paths: + mappings.append((path, DEFAULT_MAPPING, {})) + + return mappings + + +class InitCatalog(CommandMixin): + description = 'create a new catalog based on a POT file' + user_options = [ + ('domain=', 'D', + "domain of PO file (default 'messages')"), + ('input-file=', 'i', + 'name of the input file'), + ('output-dir=', 'd', + 'path to output directory'), + ('output-file=', 'o', + "name of the output file (default " + "'//LC_MESSAGES/.po')"), + ('locale=', 'l', + 'locale for the new localized catalog'), + ('width=', 'w', + 'set output line width (default 76)'), + ('no-wrap', None, + 'do not break long message lines, longer than the output line width, ' + 'into several lines'), + ] + boolean_options = ['no-wrap'] + + def initialize_options(self): + self.output_dir = None + self.output_file = None + self.input_file = None + self.locale = None + self.domain = 'messages' + self.no_wrap = False + self.width = None + + def finalize_options(self): + if not self.input_file: + raise OptionError('you must specify the input file') + + if not self.locale: + raise OptionError('you must provide a locale for the new catalog') + try: + self._locale = Locale.parse(self.locale) + except UnknownLocaleError as e: + raise OptionError(e) from e + + if not self.output_file and not self.output_dir: + raise OptionError('you must specify the output directory') + if not self.output_file: + self.output_file = os.path.join(self.output_dir, self.locale, + 'LC_MESSAGES', f"{self.domain}.po") + + if not os.path.exists(os.path.dirname(self.output_file)): + os.makedirs(os.path.dirname(self.output_file)) + if self.no_wrap and self.width: + raise OptionError("'--no-wrap' and '--width' are mutually exclusive") + if not self.no_wrap and not self.width: + self.width = 76 + elif self.width is not None: + self.width = int(self.width) + + def run(self): + self.log.info( + 'creating catalog %s based on %s', self.output_file, self.input_file, + ) + + with open(self.input_file, 'rb') as infile: + # Although reading from the catalog template, read_po must be fed + # the locale in order to correctly calculate plurals + catalog = read_po(infile, locale=self.locale) + + catalog.locale = self._locale + catalog.revision_date = datetime.datetime.now(LOCALTZ) + catalog.fuzzy = False + + with open(self.output_file, 'wb') as outfile: + write_po(outfile, catalog, width=self.width) + + +class UpdateCatalog(CommandMixin): + description = 'update message catalogs from a POT file' + user_options = [ + ('domain=', 'D', + "domain of PO file (default 'messages')"), + ('input-file=', 'i', + 'name of the input file'), + ('output-dir=', 'd', + 'path to base directory containing the catalogs'), + ('output-file=', 'o', + "name of the output file (default " + "'//LC_MESSAGES/.po')"), + ('omit-header', None, + "do not include msgid "" entry in header"), + ('locale=', 'l', + 'locale of the catalog to compile'), + ('width=', 'w', + 'set output line width (default 76)'), + ('no-wrap', None, + 'do not break long message lines, longer than the output line width, ' + 'into several lines'), + ('ignore-obsolete=', None, + 'whether to omit obsolete messages from the output'), + ('init-missing=', None, + 'if any output files are missing, initialize them first'), + ('no-fuzzy-matching', 'N', + 'do not use fuzzy matching'), + ('update-header-comment', None, + 'update target header comment'), + ('previous', None, + 'keep previous msgids of translated messages'), + ('check=', None, + 'don\'t update the catalog, just return the status. Return code 0 ' + 'means nothing would change. Return code 1 means that the catalog ' + 'would be updated'), + ('ignore-pot-creation-date=', None, + 'ignore changes to POT-Creation-Date when updating or checking'), + ] + boolean_options = [ + 'omit-header', 'no-wrap', 'ignore-obsolete', 'init-missing', + 'no-fuzzy-matching', 'previous', 'update-header-comment', + 'check', 'ignore-pot-creation-date', + ] + + def initialize_options(self): + self.domain = 'messages' + self.input_file = None + self.output_dir = None + self.output_file = None + self.omit_header = False + self.locale = None + self.width = None + self.no_wrap = False + self.ignore_obsolete = False + self.init_missing = False + self.no_fuzzy_matching = False + self.update_header_comment = False + self.previous = False + self.check = False + self.ignore_pot_creation_date = False + + def finalize_options(self): + if not self.input_file: + raise OptionError('you must specify the input file') + if not self.output_file and not self.output_dir: + raise OptionError('you must specify the output file or directory') + if self.output_file and not self.locale: + raise OptionError('you must specify the locale') + + if self.init_missing: + if not self.locale: + raise OptionError( + 'you must specify the locale for ' + 'the init-missing option to work', + ) + + try: + self._locale = Locale.parse(self.locale) + except UnknownLocaleError as e: + raise OptionError(e) from e + else: + self._locale = None + + if self.no_wrap and self.width: + raise OptionError("'--no-wrap' and '--width' are mutually exclusive") + if not self.no_wrap and not self.width: + self.width = 76 + elif self.width is not None: + self.width = int(self.width) + if self.no_fuzzy_matching and self.previous: + self.previous = False + + def run(self): + check_status = {} + po_files = [] + if not self.output_file: + if self.locale: + po_files.append((self.locale, + os.path.join(self.output_dir, self.locale, + 'LC_MESSAGES', + f"{self.domain}.po"))) + else: + for locale in os.listdir(self.output_dir): + po_file = os.path.join(self.output_dir, locale, + 'LC_MESSAGES', + f"{self.domain}.po") + if os.path.exists(po_file): + po_files.append((locale, po_file)) + else: + po_files.append((self.locale, self.output_file)) + + if not po_files: + raise OptionError('no message catalogs found') + + domain = self.domain + if not domain: + domain = os.path.splitext(os.path.basename(self.input_file))[0] + + with open(self.input_file, 'rb') as infile: + template = read_po(infile) + + for locale, filename in po_files: + if self.init_missing and not os.path.exists(filename): + if self.check: + check_status[filename] = False + continue + self.log.info( + 'creating catalog %s based on %s', filename, self.input_file, + ) + + with open(self.input_file, 'rb') as infile: + # Although reading from the catalog template, read_po must + # be fed the locale in order to correctly calculate plurals + catalog = read_po(infile, locale=self.locale) + + catalog.locale = self._locale + catalog.revision_date = datetime.datetime.now(LOCALTZ) + catalog.fuzzy = False + + with open(filename, 'wb') as outfile: + write_po(outfile, catalog) + + self.log.info('updating catalog %s based on %s', filename, self.input_file) + with open(filename, 'rb') as infile: + catalog = read_po(infile, locale=locale, domain=domain) + + catalog.update( + template, self.no_fuzzy_matching, + update_header_comment=self.update_header_comment, + update_creation_date=not self.ignore_pot_creation_date, + ) + + tmpname = os.path.join(os.path.dirname(filename), + tempfile.gettempprefix() + + os.path.basename(filename)) + try: + with open(tmpname, 'wb') as tmpfile: + write_po(tmpfile, catalog, + omit_header=self.omit_header, + ignore_obsolete=self.ignore_obsolete, + include_previous=self.previous, width=self.width) + except Exception: + os.remove(tmpname) + raise + + if self.check: + with open(filename, "rb") as origfile: + original_catalog = read_po(origfile) + with open(tmpname, "rb") as newfile: + updated_catalog = read_po(newfile) + updated_catalog.revision_date = original_catalog.revision_date + check_status[filename] = updated_catalog.is_identical(original_catalog) + os.remove(tmpname) + continue + + try: + os.rename(tmpname, filename) + except OSError: + # We're probably on Windows, which doesn't support atomic + # renames, at least not through Python + # If the error is in fact due to a permissions problem, that + # same error is going to be raised from one of the following + # operations + os.remove(filename) + shutil.copy(tmpname, filename) + os.remove(tmpname) + + if self.check: + for filename, up_to_date in check_status.items(): + if up_to_date: + self.log.info('Catalog %s is up to date.', filename) + else: + self.log.warning('Catalog %s is out of date.', filename) + if not all(check_status.values()): + raise BaseError("Some catalogs are out of date.") + else: + self.log.info("All the catalogs are up-to-date.") + return + + +class CommandLineInterface: + """Command-line interface. + + This class provides a simple command-line interface to the message + extraction and PO file generation functionality. + """ + + usage = '%%prog %s [options] %s' + version = f'%prog {VERSION}' + commands = { + 'compile': 'compile message catalogs to MO files', + 'extract': 'extract messages from source files and generate a POT file', + 'init': 'create new message catalogs from a POT file', + 'update': 'update existing message catalogs from a POT file', + } + + command_classes = { + 'compile': CompileCatalog, + 'extract': ExtractMessages, + 'init': InitCatalog, + 'update': UpdateCatalog, + } + + log = None # Replaced on instance level + + def run(self, argv=None): + """Main entry point of the command-line interface. + + :param argv: list of arguments passed on the command-line + """ + + if argv is None: + argv = sys.argv + + self.parser = optparse.OptionParser(usage=self.usage % ('command', '[args]'), + version=self.version) + self.parser.disable_interspersed_args() + self.parser.print_help = self._help + self.parser.add_option('--list-locales', dest='list_locales', + action='store_true', + help="print all known locales and exit") + self.parser.add_option('-v', '--verbose', action='store_const', + dest='loglevel', const=logging.DEBUG, + help='print as much as possible') + self.parser.add_option('-q', '--quiet', action='store_const', + dest='loglevel', const=logging.ERROR, + help='print as little as possible') + self.parser.set_defaults(list_locales=False, loglevel=logging.INFO) + + options, args = self.parser.parse_args(argv[1:]) + + self._configure_logging(options.loglevel) + if options.list_locales: + identifiers = localedata.locale_identifiers() + id_width = max(len(identifier) for identifier in identifiers) + 1 + for identifier in sorted(identifiers): + locale = Locale.parse(identifier) + print(f"{identifier:<{id_width}} {locale.english_name}") + return 0 + + if not args: + self.parser.error('no valid command or option passed. ' + 'Try the -h/--help option for more information.') + + cmdname = args[0] + if cmdname not in self.commands: + self.parser.error(f'unknown command "{cmdname}"') + + cmdinst = self._configure_command(cmdname, args[1:]) + return cmdinst.run() + + def _configure_logging(self, loglevel): + self.log = log + self.log.setLevel(loglevel) + # Don't add a new handler for every instance initialization (#227), this + # would cause duplicated output when the CommandLineInterface as an + # normal Python class. + if self.log.handlers: + handler = self.log.handlers[0] + else: + handler = logging.StreamHandler() + self.log.addHandler(handler) + handler.setLevel(loglevel) + formatter = logging.Formatter('%(message)s') + handler.setFormatter(formatter) + + def _help(self): + print(self.parser.format_help()) + print("commands:") + cmd_width = max(8, max(len(command) for command in self.commands) + 1) + for name, description in sorted(self.commands.items()): + print(f" {name:<{cmd_width}} {description}") + + def _configure_command(self, cmdname, argv): + """ + :type cmdname: str + :type argv: list[str] + """ + cmdclass = self.command_classes[cmdname] + cmdinst = cmdclass() + if self.log: + cmdinst.log = self.log # Use our logger, not distutils'. + assert isinstance(cmdinst, CommandMixin) + cmdinst.initialize_options() + + parser = optparse.OptionParser( + usage=self.usage % (cmdname, ''), + description=self.commands[cmdname], + ) + as_args: str | None = getattr(cmdclass, "as_args", None) + for long, short, help in cmdclass.user_options: + name = long.strip("=") + default = getattr(cmdinst, name.replace("-", "_")) + strs = [f"--{name}"] + if short: + strs.append(f"-{short}") + strs.extend(cmdclass.option_aliases.get(name, ())) + choices = cmdclass.option_choices.get(name, None) + if name == as_args: + parser.usage += f"<{name}>" + elif name in cmdclass.boolean_options: + parser.add_option(*strs, action="store_true", help=help) + elif name in cmdclass.multiple_value_options: + parser.add_option(*strs, action="append", help=help, choices=choices) + else: + parser.add_option(*strs, help=help, default=default, choices=choices) + options, args = parser.parse_args(argv) + + if as_args: + setattr(options, as_args.replace('-', '_'), args) + + for key, value in vars(options).items(): + setattr(cmdinst, key, value) + + try: + cmdinst.ensure_finalized() + except OptionError as err: + parser.error(str(err)) + + return cmdinst + + +def main(): + return CommandLineInterface().run(sys.argv) + + +def parse_mapping(fileobj, filename=None): + warnings.warn( + "parse_mapping is deprecated, use parse_mapping_cfg instead", + DeprecationWarning, + stacklevel=2, + ) + return parse_mapping_cfg(fileobj, filename) + + +def parse_mapping_cfg(fileobj, filename=None): + """Parse an extraction method mapping from a file-like object. + + :param fileobj: a readable file-like object containing the configuration + text to parse + :param filename: the name of the file being parsed, for error messages + """ + extractors = {} + method_map = [] + options_map = {} + + parser = RawConfigParser() + parser.read_file(fileobj, filename) + + for section in parser.sections(): + if section == 'extractors': + extractors = dict(parser.items(section)) + else: + method, pattern = (part.strip() for part in section.split(':', 1)) + method_map.append((pattern, method)) + options_map[pattern] = dict(parser.items(section)) + + if extractors: + for idx, (pattern, method) in enumerate(method_map): + if method in extractors: + method = extractors[method] + method_map[idx] = (pattern, method) + + return method_map, options_map + + +def _parse_config_object(config: dict, *, filename="(unknown)"): + extractors = {} + method_map = [] + options_map = {} + + extractors_read = config.get("extractors", {}) + if not isinstance(extractors_read, dict): + raise ConfigurationError(f"{filename}: extractors: Expected a dictionary, got {type(extractors_read)!r}") + for method, callable_spec in extractors_read.items(): + if not isinstance(method, str): + # Impossible via TOML, but could happen with a custom object. + raise ConfigurationError(f"{filename}: extractors: Extraction method must be a string, got {method!r}") + if not isinstance(callable_spec, str): + raise ConfigurationError(f"{filename}: extractors: Callable specification must be a string, got {callable_spec!r}") + extractors[method] = callable_spec + + if "mapping" in config: + raise ConfigurationError(f"{filename}: 'mapping' is not a valid key, did you mean 'mappings'?") + + mappings_read = config.get("mappings", []) + if not isinstance(mappings_read, list): + raise ConfigurationError(f"{filename}: mappings: Expected a list, got {type(mappings_read)!r}") + for idx, entry in enumerate(mappings_read): + if not isinstance(entry, dict): + raise ConfigurationError(f"{filename}: mappings[{idx}]: Expected a dictionary, got {type(entry)!r}") + entry = entry.copy() + + method = entry.pop("method", None) + if not isinstance(method, str): + raise ConfigurationError(f"{filename}: mappings[{idx}]: 'method' must be a string, got {method!r}") + method = extractors.get(method, method) # Map the extractor name to the callable now + + pattern = entry.pop("pattern", None) + if not isinstance(pattern, (list, str)): + raise ConfigurationError(f"{filename}: mappings[{idx}]: 'pattern' must be a list or a string, got {pattern!r}") + if not isinstance(pattern, list): + pattern = [pattern] + + for pat in pattern: + if not isinstance(pat, str): + raise ConfigurationError(f"{filename}: mappings[{idx}]: 'pattern' elements must be strings, got {pat!r}") + method_map.append((pat, method)) + options_map[pat] = entry + + return method_map, options_map + + +def _parse_mapping_toml( + fileobj: BinaryIO, + filename: str = "(unknown)", + style: Literal["standalone", "pyproject.toml"] = "standalone", +): + """Parse an extraction method mapping from a binary file-like object. + + .. warning: As of this version of Babel, this is a private API subject to changes. + + :param fileobj: a readable binary file-like object containing the configuration TOML to parse + :param filename: the name of the file being parsed, for error messages + :param style: whether the file is in the style of a `pyproject.toml` file, i.e. whether to look for `tool.babel`. + """ + try: + import tomllib + except ImportError: + try: + import tomli as tomllib + except ImportError as ie: # pragma: no cover + raise ImportError("tomli or tomllib is required to parse TOML files") from ie + + try: + parsed_data = tomllib.load(fileobj) + except tomllib.TOMLDecodeError as e: + raise ConfigurationError(f"{filename}: Error parsing TOML file: {e}") from e + + if style == "pyproject.toml": + try: + babel_data = parsed_data["tool"]["babel"] + except (TypeError, KeyError) as e: + raise ConfigurationError(f"{filename}: No 'tool.babel' section found in file") from e + elif style == "standalone": + babel_data = parsed_data + if "babel" in babel_data: + raise ConfigurationError(f"{filename}: 'babel' should not be present in a stand-alone configuration file") + else: # pragma: no cover + raise ValueError(f"Unknown TOML style {style!r}") + + return _parse_config_object(babel_data, filename=filename) + + +def _parse_spec(s: str) -> tuple[int | None, tuple[int | tuple[int, str], ...]]: + inds = [] + number = None + for x in s.split(','): + if x[-1] == 't': + number = int(x[:-1]) + elif x[-1] == 'c': + inds.append((int(x[:-1]), 'c')) + else: + inds.append(int(x)) + return number, tuple(inds) + + +def parse_keywords(strings: Iterable[str] = ()): + """Parse keywords specifications from the given list of strings. + + >>> import pprint + >>> keywords = ['_', 'dgettext:2', 'dngettext:2,3', 'pgettext:1c,2', + ... 'polymorphic:1', 'polymorphic:2,2t', 'polymorphic:3c,3t'] + >>> pprint.pprint(parse_keywords(keywords)) + {'_': None, + 'dgettext': (2,), + 'dngettext': (2, 3), + 'pgettext': ((1, 'c'), 2), + 'polymorphic': {None: (1,), 2: (2,), 3: ((3, 'c'),)}} + + The input keywords are in GNU Gettext style; see :doc:`cmdline` for details. + + The output is a dictionary mapping keyword names to a dictionary of specifications. + Keys in this dictionary are numbers of arguments, where ``None`` means that all numbers + of arguments are matched, and a number means only calls with that number of arguments + are matched (which happens when using the "t" specifier). However, as a special + case for backwards compatibility, if the dictionary of specifications would + be ``{None: x}``, i.e., there is only one specification and it matches all argument + counts, then it is collapsed into just ``x``. + + A specification is either a tuple or None. If a tuple, each element can be either a number + ``n``, meaning that the nth argument should be extracted as a message, or the tuple + ``(n, 'c')``, meaning that the nth argument should be extracted as context for the + messages. A ``None`` specification is equivalent to ``(1,)``, extracting the first + argument. + """ + keywords = {} + for string in strings: + if ':' in string: + funcname, spec_str = string.split(':') + number, spec = _parse_spec(spec_str) + else: + funcname = string + number = None + spec = None + keywords.setdefault(funcname, {})[number] = spec + + # For best backwards compatibility, collapse {None: x} into x. + for k, v in keywords.items(): + if set(v) == {None}: + keywords[k] = v[None] + + return keywords + + +def __getattr__(name: str): + # Re-exports for backwards compatibility; + # `setuptools_frontend` is the canonical import location. + if name in {'check_message_extractors', 'compile_catalog', 'extract_messages', 'init_catalog', 'update_catalog'}: + from babel.messages import setuptools_frontend + + return getattr(setuptools_frontend, name) + + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +if __name__ == '__main__': + main() diff --git a/.venv/lib/python3.9/site-packages/babel/messages/jslexer.py b/.venv/lib/python3.9/site-packages/babel/messages/jslexer.py new file mode 100644 index 00000000..5fc4956f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/babel/messages/jslexer.py @@ -0,0 +1,204 @@ +""" + babel.messages.jslexer + ~~~~~~~~~~~~~~~~~~~~~~ + + A simple JavaScript 1.5 lexer which is used for the JavaScript + extractor. + + :copyright: (c) 2013-2025 by the Babel Team. + :license: BSD, see LICENSE for more details. +""" +from __future__ import annotations + +import re +from collections.abc import Generator +from typing import NamedTuple + +operators: list[str] = sorted([ + '+', '-', '*', '%', '!=', '==', '<', '>', '<=', '>=', '=', + '+=', '-=', '*=', '%=', '<<', '>>', '>>>', '<<=', '>>=', + '>>>=', '&', '&=', '|', '|=', '&&', '||', '^', '^=', '(', ')', + '[', ']', '{', '}', '!', '--', '++', '~', ',', ';', '.', ':', +], key=len, reverse=True) + +escapes: dict[str, str] = {'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t'} + +name_re = re.compile(r'[\w$_][\w\d$_]*', re.UNICODE) +dotted_name_re = re.compile(r'[\w$_][\w\d$_.]*[\w\d$_.]', re.UNICODE) +division_re = re.compile(r'/=?') +regex_re = re.compile(r'/(?:[^/\\]*(?:\\.[^/\\]*)*)/[a-zA-Z]*', re.DOTALL) +line_re = re.compile(r'(\r\n|\n|\r)') +line_join_re = re.compile(r'\\' + line_re.pattern) +uni_escape_re = re.compile(r'[a-fA-F0-9]{1,4}') +hex_escape_re = re.compile(r'[a-fA-F0-9]{1,2}') + + +class Token(NamedTuple): + type: str + value: str + lineno: int + + +_rules: list[tuple[str | None, re.Pattern[str]]] = [ + (None, re.compile(r'\s+', re.UNICODE)), + (None, re.compile(r' Use Coherence + if chaos_difference < 0.01 and coherence_difference > 0.02: + return self.coherence > other.coherence + elif chaos_difference < 0.01 and coherence_difference <= 0.02: + # When having a difficult decision, use the result that decoded as many multi-byte as possible. + # preserve RAM usage! + if len(self._payload) >= TOO_BIG_SEQUENCE: + return self.chaos < other.chaos + return self.multi_byte_usage > other.multi_byte_usage + + return self.chaos < other.chaos + + @property + def multi_byte_usage(self) -> float: + return 1.0 - (len(str(self)) / len(self.raw)) + + def __str__(self) -> str: + # Lazy Str Loading + if self._string is None: + self._string = str(self._payload, self._encoding, "strict") + return self._string + + def __repr__(self) -> str: + return f"" + + def add_submatch(self, other: CharsetMatch) -> None: + if not isinstance(other, CharsetMatch) or other == self: + raise ValueError( + "Unable to add instance <{}> as a submatch of a CharsetMatch".format( + other.__class__ + ) + ) + + other._string = None # Unload RAM usage; dirty trick. + self._leaves.append(other) + + @property + def encoding(self) -> str: + return self._encoding + + @property + def encoding_aliases(self) -> list[str]: + """ + Encoding name are known by many name, using this could help when searching for IBM855 when it's listed as CP855. + """ + also_known_as: list[str] = [] + for u, p in aliases.items(): + if self.encoding == u: + also_known_as.append(p) + elif self.encoding == p: + also_known_as.append(u) + return also_known_as + + @property + def bom(self) -> bool: + return self._has_sig_or_bom + + @property + def byte_order_mark(self) -> bool: + return self._has_sig_or_bom + + @property + def languages(self) -> list[str]: + """ + Return the complete list of possible languages found in decoded sequence. + Usually not really useful. Returned list may be empty even if 'language' property return something != 'Unknown'. + """ + return [e[0] for e in self._languages] + + @property + def language(self) -> str: + """ + Most probable language found in decoded sequence. If none were detected or inferred, the property will return + "Unknown". + """ + if not self._languages: + # Trying to infer the language based on the given encoding + # Its either English or we should not pronounce ourselves in certain cases. + if "ascii" in self.could_be_from_charset: + return "English" + + # doing it there to avoid circular import + from charset_normalizer.cd import encoding_languages, mb_encoding_languages + + languages = ( + mb_encoding_languages(self.encoding) + if is_multi_byte_encoding(self.encoding) + else encoding_languages(self.encoding) + ) + + if len(languages) == 0 or "Latin Based" in languages: + return "Unknown" + + return languages[0] + + return self._languages[0][0] + + @property + def chaos(self) -> float: + return self._mean_mess_ratio + + @property + def coherence(self) -> float: + if not self._languages: + return 0.0 + return self._languages[0][1] + + @property + def percent_chaos(self) -> float: + return round(self.chaos * 100, ndigits=3) + + @property + def percent_coherence(self) -> float: + return round(self.coherence * 100, ndigits=3) + + @property + def raw(self) -> bytes: + """ + Original untouched bytes. + """ + return self._payload + + @property + def submatch(self) -> list[CharsetMatch]: + return self._leaves + + @property + def has_submatch(self) -> bool: + return len(self._leaves) > 0 + + @property + def alphabets(self) -> list[str]: + if self._unicode_ranges is not None: + return self._unicode_ranges + # list detected ranges + detected_ranges: list[str | None] = [unicode_range(char) for char in str(self)] + # filter and sort + self._unicode_ranges = sorted(list({r for r in detected_ranges if r})) + return self._unicode_ranges + + @property + def could_be_from_charset(self) -> list[str]: + """ + The complete list of encoding that output the exact SAME str result and therefore could be the originating + encoding. + This list does include the encoding available in property 'encoding'. + """ + return [self._encoding] + [m.encoding for m in self._leaves] + + def output(self, encoding: str = "utf_8") -> bytes: + """ + Method to get re-encoded bytes payload using given target encoding. Default to UTF-8. + Any errors will be simply ignored by the encoder NOT replaced. + """ + if self._output_encoding is None or self._output_encoding != encoding: + self._output_encoding = encoding + decoded_string = str(self) + if ( + self._preemptive_declaration is not None + and self._preemptive_declaration.lower() + not in ["utf-8", "utf8", "utf_8"] + ): + patched_header = sub( + RE_POSSIBLE_ENCODING_INDICATION, + lambda m: m.string[m.span()[0] : m.span()[1]].replace( + m.groups()[0], + iana_name(self._output_encoding).replace("_", "-"), # type: ignore[arg-type] + ), + decoded_string[:8192], + count=1, + ) + + decoded_string = patched_header + decoded_string[8192:] + + self._output_payload = decoded_string.encode(encoding, "replace") + + return self._output_payload # type: ignore + + @property + def fingerprint(self) -> str: + """ + Retrieve the unique SHA256 computed using the transformed (re-encoded) payload. Not the original one. + """ + return sha256(self.output()).hexdigest() + + +class CharsetMatches: + """ + Container with every CharsetMatch items ordered by default from most probable to the less one. + Act like a list(iterable) but does not implements all related methods. + """ + + def __init__(self, results: list[CharsetMatch] | None = None): + self._results: list[CharsetMatch] = sorted(results) if results else [] + + def __iter__(self) -> Iterator[CharsetMatch]: + yield from self._results + + def __getitem__(self, item: int | str) -> CharsetMatch: + """ + Retrieve a single item either by its position or encoding name (alias may be used here). + Raise KeyError upon invalid index or encoding not present in results. + """ + if isinstance(item, int): + return self._results[item] + if isinstance(item, str): + item = iana_name(item, False) + for result in self._results: + if item in result.could_be_from_charset: + return result + raise KeyError + + def __len__(self) -> int: + return len(self._results) + + def __bool__(self) -> bool: + return len(self._results) > 0 + + def append(self, item: CharsetMatch) -> None: + """ + Insert a single match. Will be inserted accordingly to preserve sort. + Can be inserted as a submatch. + """ + if not isinstance(item, CharsetMatch): + raise ValueError( + "Cannot append instance '{}' to CharsetMatches".format( + str(item.__class__) + ) + ) + # We should disable the submatch factoring when the input file is too heavy (conserve RAM usage) + if len(item.raw) < TOO_BIG_SEQUENCE: + for match in self._results: + if match.fingerprint == item.fingerprint and match.chaos == item.chaos: + match.add_submatch(item) + return + self._results.append(item) + self._results = sorted(self._results) + + def best(self) -> CharsetMatch | None: + """ + Simply return the first match. Strict equivalent to matches[0]. + """ + if not self._results: + return None + return self._results[0] + + def first(self) -> CharsetMatch | None: + """ + Redundant method, call the method best(). Kept for BC reasons. + """ + return self.best() + + +CoherenceMatch = Tuple[str, float] +CoherenceMatches = List[CoherenceMatch] + + +class CliDetectionResult: + def __init__( + self, + path: str, + encoding: str | None, + encoding_aliases: list[str], + alternative_encodings: list[str], + language: str, + alphabets: list[str], + has_sig_or_bom: bool, + chaos: float, + coherence: float, + unicode_path: str | None, + is_preferred: bool, + ): + self.path: str = path + self.unicode_path: str | None = unicode_path + self.encoding: str | None = encoding + self.encoding_aliases: list[str] = encoding_aliases + self.alternative_encodings: list[str] = alternative_encodings + self.language: str = language + self.alphabets: list[str] = alphabets + self.has_sig_or_bom: bool = has_sig_or_bom + self.chaos: float = chaos + self.coherence: float = coherence + self.is_preferred: bool = is_preferred + + @property + def __dict__(self) -> dict[str, Any]: # type: ignore + return { + "path": self.path, + "encoding": self.encoding, + "encoding_aliases": self.encoding_aliases, + "alternative_encodings": self.alternative_encodings, + "language": self.language, + "alphabets": self.alphabets, + "has_sig_or_bom": self.has_sig_or_bom, + "chaos": self.chaos, + "coherence": self.coherence, + "unicode_path": self.unicode_path, + "is_preferred": self.is_preferred, + } + + def to_json(self) -> str: + return dumps(self.__dict__, ensure_ascii=True, indent=4) diff --git a/.venv/lib/python3.9/site-packages/charset_normalizer/py.typed b/.venv/lib/python3.9/site-packages/charset_normalizer/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.9/site-packages/charset_normalizer/utils.py b/.venv/lib/python3.9/site-packages/charset_normalizer/utils.py new file mode 100644 index 00000000..6bf0384c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer/utils.py @@ -0,0 +1,414 @@ +from __future__ import annotations + +import importlib +import logging +import unicodedata +from codecs import IncrementalDecoder +from encodings.aliases import aliases +from functools import lru_cache +from re import findall +from typing import Generator + +from _multibytecodec import ( # type: ignore[import-not-found,import] + MultibyteIncrementalDecoder, +) + +from .constant import ( + ENCODING_MARKS, + IANA_SUPPORTED_SIMILAR, + RE_POSSIBLE_ENCODING_INDICATION, + UNICODE_RANGES_COMBINED, + UNICODE_SECONDARY_RANGE_KEYWORD, + UTF8_MAXIMAL_ALLOCATION, + COMMON_CJK_CHARACTERS, +) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_accentuated(character: str) -> bool: + try: + description: str = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + return ( + "WITH GRAVE" in description + or "WITH ACUTE" in description + or "WITH CEDILLA" in description + or "WITH DIAERESIS" in description + or "WITH CIRCUMFLEX" in description + or "WITH TILDE" in description + or "WITH MACRON" in description + or "WITH RING ABOVE" in description + ) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def remove_accent(character: str) -> str: + decomposed: str = unicodedata.decomposition(character) + if not decomposed: + return character + + codes: list[str] = decomposed.split(" ") + + return chr(int(codes[0], 16)) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def unicode_range(character: str) -> str | None: + """ + Retrieve the Unicode range official name from a single character. + """ + character_ord: int = ord(character) + + for range_name, ord_range in UNICODE_RANGES_COMBINED.items(): + if character_ord in ord_range: + return range_name + + return None + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_latin(character: str) -> bool: + try: + description: str = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + return "LATIN" in description + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_punctuation(character: str) -> bool: + character_category: str = unicodedata.category(character) + + if "P" in character_category: + return True + + character_range: str | None = unicode_range(character) + + if character_range is None: + return False + + return "Punctuation" in character_range + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_symbol(character: str) -> bool: + character_category: str = unicodedata.category(character) + + if "S" in character_category or "N" in character_category: + return True + + character_range: str | None = unicode_range(character) + + if character_range is None: + return False + + return "Forms" in character_range and character_category != "Lo" + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_emoticon(character: str) -> bool: + character_range: str | None = unicode_range(character) + + if character_range is None: + return False + + return "Emoticons" in character_range or "Pictographs" in character_range + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_separator(character: str) -> bool: + if character.isspace() or character in {"|", "+", "<", ">"}: + return True + + character_category: str = unicodedata.category(character) + + return "Z" in character_category or character_category in {"Po", "Pd", "Pc"} + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_case_variable(character: str) -> bool: + return character.islower() != character.isupper() + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_cjk(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + + return "CJK" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_hiragana(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + + return "HIRAGANA" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_katakana(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + + return "KATAKANA" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_hangul(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + + return "HANGUL" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_thai(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + + return "THAI" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_arabic(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + + return "ARABIC" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_arabic_isolated_form(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: # Defensive: unicode database outdated? + return False + + return "ARABIC" in character_name and "ISOLATED FORM" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_cjk_uncommon(character: str) -> bool: + return character not in COMMON_CJK_CHARACTERS + + +@lru_cache(maxsize=len(UNICODE_RANGES_COMBINED)) +def is_unicode_range_secondary(range_name: str) -> bool: + return any(keyword in range_name for keyword in UNICODE_SECONDARY_RANGE_KEYWORD) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_unprintable(character: str) -> bool: + return ( + character.isspace() is False # includes \n \t \r \v + and character.isprintable() is False + and character != "\x1a" # Why? Its the ASCII substitute character. + and character != "\ufeff" # bug discovered in Python, + # Zero Width No-Break Space located in Arabic Presentation Forms-B, Unicode 1.1 not acknowledged as space. + ) + + +def any_specified_encoding(sequence: bytes, search_zone: int = 8192) -> str | None: + """ + Extract using ASCII-only decoder any specified encoding in the first n-bytes. + """ + if not isinstance(sequence, bytes): + raise TypeError + + seq_len: int = len(sequence) + + results: list[str] = findall( + RE_POSSIBLE_ENCODING_INDICATION, + sequence[: min(seq_len, search_zone)].decode("ascii", errors="ignore"), + ) + + if len(results) == 0: + return None + + for specified_encoding in results: + specified_encoding = specified_encoding.lower().replace("-", "_") + + encoding_alias: str + encoding_iana: str + + for encoding_alias, encoding_iana in aliases.items(): + if encoding_alias == specified_encoding: + return encoding_iana + if encoding_iana == specified_encoding: + return encoding_iana + + return None + + +@lru_cache(maxsize=128) +def is_multi_byte_encoding(name: str) -> bool: + """ + Verify is a specific encoding is a multi byte one based on it IANA name + """ + return name in { + "utf_8", + "utf_8_sig", + "utf_16", + "utf_16_be", + "utf_16_le", + "utf_32", + "utf_32_le", + "utf_32_be", + "utf_7", + } or issubclass( + importlib.import_module(f"encodings.{name}").IncrementalDecoder, + MultibyteIncrementalDecoder, + ) + + +def identify_sig_or_bom(sequence: bytes) -> tuple[str | None, bytes]: + """ + Identify and extract SIG/BOM in given sequence. + """ + + for iana_encoding in ENCODING_MARKS: + marks: bytes | list[bytes] = ENCODING_MARKS[iana_encoding] + + if isinstance(marks, bytes): + marks = [marks] + + for mark in marks: + if sequence.startswith(mark): + return iana_encoding, mark + + return None, b"" + + +def should_strip_sig_or_bom(iana_encoding: str) -> bool: + return iana_encoding not in {"utf_16", "utf_32"} + + +def iana_name(cp_name: str, strict: bool = True) -> str: + """Returns the Python normalized encoding name (Not the IANA official name).""" + cp_name = cp_name.lower().replace("-", "_") + + encoding_alias: str + encoding_iana: str + + for encoding_alias, encoding_iana in aliases.items(): + if cp_name in [encoding_alias, encoding_iana]: + return encoding_iana + + if strict: + raise ValueError(f"Unable to retrieve IANA for '{cp_name}'") + + return cp_name + + +def cp_similarity(iana_name_a: str, iana_name_b: str) -> float: + if is_multi_byte_encoding(iana_name_a) or is_multi_byte_encoding(iana_name_b): + return 0.0 + + decoder_a = importlib.import_module(f"encodings.{iana_name_a}").IncrementalDecoder + decoder_b = importlib.import_module(f"encodings.{iana_name_b}").IncrementalDecoder + + id_a: IncrementalDecoder = decoder_a(errors="ignore") + id_b: IncrementalDecoder = decoder_b(errors="ignore") + + character_match_count: int = 0 + + for i in range(255): + to_be_decoded: bytes = bytes([i]) + if id_a.decode(to_be_decoded) == id_b.decode(to_be_decoded): + character_match_count += 1 + + return character_match_count / 254 + + +def is_cp_similar(iana_name_a: str, iana_name_b: str) -> bool: + """ + Determine if two code page are at least 80% similar. IANA_SUPPORTED_SIMILAR dict was generated using + the function cp_similarity. + """ + return ( + iana_name_a in IANA_SUPPORTED_SIMILAR + and iana_name_b in IANA_SUPPORTED_SIMILAR[iana_name_a] + ) + + +def set_logging_handler( + name: str = "charset_normalizer", + level: int = logging.INFO, + format_string: str = "%(asctime)s | %(levelname)s | %(message)s", +) -> None: + logger = logging.getLogger(name) + logger.setLevel(level) + + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter(format_string)) + logger.addHandler(handler) + + +def cut_sequence_chunks( + sequences: bytes, + encoding_iana: str, + offsets: range, + chunk_size: int, + bom_or_sig_available: bool, + strip_sig_or_bom: bool, + sig_payload: bytes, + is_multi_byte_decoder: bool, + decoded_payload: str | None = None, +) -> Generator[str, None, None]: + if decoded_payload and is_multi_byte_decoder is False: + for i in offsets: + chunk = decoded_payload[i : i + chunk_size] + if not chunk: + break + yield chunk + else: + for i in offsets: + chunk_end = i + chunk_size + if chunk_end > len(sequences) + 8: + continue + + cut_sequence = sequences[i : i + chunk_size] + + if bom_or_sig_available and strip_sig_or_bom is False: + cut_sequence = sig_payload + cut_sequence + + chunk = cut_sequence.decode( + encoding_iana, + errors="ignore" if is_multi_byte_decoder else "strict", + ) + + # multi-byte bad cutting detector and adjustment + # not the cleanest way to perform that fix but clever enough for now. + if is_multi_byte_decoder and i > 0: + chunk_partial_size_chk: int = min(chunk_size, 16) + + if ( + decoded_payload + and chunk[:chunk_partial_size_chk] not in decoded_payload + ): + for j in range(i, i - 4, -1): + cut_sequence = sequences[j:chunk_end] + + if bom_or_sig_available and strip_sig_or_bom is False: + cut_sequence = sig_payload + cut_sequence + + chunk = cut_sequence.decode(encoding_iana, errors="ignore") + + if chunk[:chunk_partial_size_chk] in decoded_payload: + break + + yield chunk diff --git a/.venv/lib/python3.9/site-packages/charset_normalizer/version.py b/.venv/lib/python3.9/site-packages/charset_normalizer/version.py new file mode 100644 index 00000000..c843e533 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/charset_normalizer/version.py @@ -0,0 +1,8 @@ +""" +Expose version +""" + +from __future__ import annotations + +__version__ = "3.4.4" +VERSION = __version__.split(".") diff --git a/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/LICENSE.txt b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/LICENSE.txt new file mode 100644 index 00000000..d12a8491 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2014 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/METADATA b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/METADATA new file mode 100644 index 00000000..366d1a7e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/METADATA @@ -0,0 +1,74 @@ +Metadata-Version: 2.3 +Name: click +Version: 8.1.8 +Summary: Composable command line interface toolkit +Maintainer-email: Pallets +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Typing :: Typed +Requires-Dist: colorama; platform_system == 'Windows' +Requires-Dist: importlib-metadata; python_version < '3.8' +Project-URL: Changes, https://click.palletsprojects.com/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/click/ + +# $ click_ + +Click is a Python package for creating beautiful command line interfaces +in a composable way with as little code as necessary. It's the "Command +Line Interface Creation Kit". It's highly configurable but comes with +sensible defaults out of the box. + +It aims to make the process of writing command line tools quick and fun +while also preventing any frustration caused by the inability to +implement an intended CLI API. + +Click in three points: + +- Arbitrary nesting of commands +- Automatic help page generation +- Supports lazy loading of subcommands at runtime + + +## A Simple Example + +```python +import click + +@click.command() +@click.option("--count", default=1, help="Number of greetings.") +@click.option("--name", prompt="Your name", help="The person to greet.") +def hello(count, name): + """Simple program that greets NAME for a total of COUNT times.""" + for _ in range(count): + click.echo(f"Hello, {name}!") + +if __name__ == '__main__': + hello() +``` + +``` +$ python hello.py --count=3 +Your name: Click +Hello, Click! +Hello, Click! +Hello, Click! +``` + + +## Donate + +The Pallets organization develops and supports Click and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, [please +donate today][]. + +[please donate today]: https://palletsprojects.com/donate + diff --git a/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/RECORD b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/RECORD new file mode 100644 index 00000000..89c055fc --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/RECORD @@ -0,0 +1,38 @@ +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/click/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/click/_compat.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/click/_termui_impl.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/click/_textwrap.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/click/_winconsole.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/click/core.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/click/decorators.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/click/exceptions.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/click/formatting.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/click/globals.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/click/parser.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/click/shell_completion.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/click/termui.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/click/testing.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/click/types.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/click/utils.cpython-39.pyc,, +click-8.1.8.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click-8.1.8.dist-info/LICENSE.txt,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click-8.1.8.dist-info/METADATA,sha256=WJtQ6uGS2ybLfvUE4vC0XIhIBr4yFGwjrMBR2fiCQ-Q,2263 +click-8.1.8.dist-info/RECORD,, +click-8.1.8.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82 +click/__init__.py,sha256=j1DJeCbga4ribkv5uyvIAzI0oFN13fW9mevDKShFelo,3188 +click/_compat.py,sha256=IGKh_J5QdfKELitnRfTGHneejWxoCw_NX9tfMbdcg3w,18730 +click/_termui_impl.py,sha256=a5z7I9gOFeMmu7Gb6_RPyQ8GPuVP1EeblixcWSPSQPk,24783 +click/_textwrap.py,sha256=10fQ64OcBUMuK7mFvh8363_uoOxPlRItZBmKzRJDgoY,1353 +click/_winconsole.py,sha256=5ju3jQkcZD0W27WEMGqmEP4y_crUVzPCqsX_FYb7BO0,7860 +click/core.py,sha256=Q1nEVdctZwvIPOlt4vfHko0TYnHCeE40UEEul8Wpyvs,114748 +click/decorators.py,sha256=7t6F-QWowtLh6F_6l-4YV4Y4yNTcqFQEu9i37zIz68s,18925 +click/exceptions.py,sha256=V7zDT6emqJ8iNl0kF1P5kpFmLMWQ1T1L7aNNKM4YR0w,9600 +click/formatting.py,sha256=Frf0-5W33-loyY_i9qrwXR8-STnW3m5gvyxLVUdyxyk,9706 +click/globals.py,sha256=cuJ6Bbo073lgEEmhjr394PeM-QFmXM-Ci-wmfsd7H5g,1954 +click/parser.py,sha256=h4sndcpF5OHrZQN8vD8IWb5OByvW7ABbhRToxovrqS8,19067 +click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click/shell_completion.py,sha256=TR0dXEGcvWb9Eo3aaQEXGhnvNS3FF4H4QcuLnvAvYo4,18636 +click/termui.py,sha256=dLxiS70UOvIYBda_nEEZaPAFOVDVmRs1sEPMuLDowQo,28310 +click/testing.py,sha256=3RA8anCf7TZ8-5RAF5it2Te-aWXBAL5VLasQnMiC2ZQ,16282 +click/types.py,sha256=BD5Qqq4h-8kawBmOIzJlmq4xzThAf4wCvaOLZSBDNx0,36422 +click/utils.py,sha256=ce-IrO9ilII76LGkU354pOdHbepM8UftfNH7SfMU_28,20330 diff --git a/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/WHEEL new file mode 100644 index 00000000..e3c6feef --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click-8.1.8.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.10.1 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.9/site-packages/click/__init__.py b/.venv/lib/python3.9/site-packages/click/__init__.py new file mode 100644 index 00000000..2610d0e1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/__init__.py @@ -0,0 +1,75 @@ +""" +Click is a simple Python module inspired by the stdlib optparse to make +writing command line scripts fun. Unlike other modules, it's based +around a simple API that does not come with too much magic and is +composable. +""" + +from .core import Argument as Argument +from .core import BaseCommand as BaseCommand +from .core import Command as Command +from .core import CommandCollection as CommandCollection +from .core import Context as Context +from .core import Group as Group +from .core import MultiCommand as MultiCommand +from .core import Option as Option +from .core import Parameter as Parameter +from .decorators import argument as argument +from .decorators import command as command +from .decorators import confirmation_option as confirmation_option +from .decorators import group as group +from .decorators import help_option as help_option +from .decorators import HelpOption as HelpOption +from .decorators import make_pass_decorator as make_pass_decorator +from .decorators import option as option +from .decorators import pass_context as pass_context +from .decorators import pass_obj as pass_obj +from .decorators import password_option as password_option +from .decorators import version_option as version_option +from .exceptions import Abort as Abort +from .exceptions import BadArgumentUsage as BadArgumentUsage +from .exceptions import BadOptionUsage as BadOptionUsage +from .exceptions import BadParameter as BadParameter +from .exceptions import ClickException as ClickException +from .exceptions import FileError as FileError +from .exceptions import MissingParameter as MissingParameter +from .exceptions import NoSuchOption as NoSuchOption +from .exceptions import UsageError as UsageError +from .formatting import HelpFormatter as HelpFormatter +from .formatting import wrap_text as wrap_text +from .globals import get_current_context as get_current_context +from .parser import OptionParser as OptionParser +from .termui import clear as clear +from .termui import confirm as confirm +from .termui import echo_via_pager as echo_via_pager +from .termui import edit as edit +from .termui import getchar as getchar +from .termui import launch as launch +from .termui import pause as pause +from .termui import progressbar as progressbar +from .termui import prompt as prompt +from .termui import secho as secho +from .termui import style as style +from .termui import unstyle as unstyle +from .types import BOOL as BOOL +from .types import Choice as Choice +from .types import DateTime as DateTime +from .types import File as File +from .types import FLOAT as FLOAT +from .types import FloatRange as FloatRange +from .types import INT as INT +from .types import IntRange as IntRange +from .types import ParamType as ParamType +from .types import Path as Path +from .types import STRING as STRING +from .types import Tuple as Tuple +from .types import UNPROCESSED as UNPROCESSED +from .types import UUID as UUID +from .utils import echo as echo +from .utils import format_filename as format_filename +from .utils import get_app_dir as get_app_dir +from .utils import get_binary_stream as get_binary_stream +from .utils import get_text_stream as get_text_stream +from .utils import open_file as open_file + +__version__ = "8.1.8" diff --git a/.venv/lib/python3.9/site-packages/click/_compat.py b/.venv/lib/python3.9/site-packages/click/_compat.py new file mode 100644 index 00000000..9153d150 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/_compat.py @@ -0,0 +1,623 @@ +import codecs +import io +import os +import re +import sys +import typing as t +from weakref import WeakKeyDictionary + +CYGWIN = sys.platform.startswith("cygwin") +WIN = sys.platform.startswith("win") +auto_wrap_for_ansi: t.Optional[t.Callable[[t.TextIO], t.TextIO]] = None +_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") + + +def _make_text_stream( + stream: t.BinaryIO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if encoding is None: + encoding = get_best_encoding(stream) + if errors is None: + errors = "replace" + return _NonClosingTextIOWrapper( + stream, + encoding, + errors, + line_buffering=True, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def is_ascii_encoding(encoding: str) -> bool: + """Checks if a given encoding is ascii.""" + try: + return codecs.lookup(encoding).name == "ascii" + except LookupError: + return False + + +def get_best_encoding(stream: t.IO[t.Any]) -> str: + """Returns the default stream encoding if not found.""" + rv = getattr(stream, "encoding", None) or sys.getdefaultencoding() + if is_ascii_encoding(rv): + return "utf-8" + return rv + + +class _NonClosingTextIOWrapper(io.TextIOWrapper): + def __init__( + self, + stream: t.BinaryIO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, + force_writable: bool = False, + **extra: t.Any, + ) -> None: + self._stream = stream = t.cast( + t.BinaryIO, _FixupStream(stream, force_readable, force_writable) + ) + super().__init__(stream, encoding, errors, **extra) + + def __del__(self) -> None: + try: + self.detach() + except Exception: + pass + + def isatty(self) -> bool: + # https://bitbucket.org/pypy/pypy/issue/1803 + return self._stream.isatty() + + +class _FixupStream: + """The new io interface needs more from streams than streams + traditionally implement. As such, this fix-up code is necessary in + some circumstances. + + The forcing of readable and writable flags are there because some tools + put badly patched objects on sys (one such offender are certain version + of jupyter notebook). + """ + + def __init__( + self, + stream: t.BinaryIO, + force_readable: bool = False, + force_writable: bool = False, + ): + self._stream = stream + self._force_readable = force_readable + self._force_writable = force_writable + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._stream, name) + + def read1(self, size: int) -> bytes: + f = getattr(self._stream, "read1", None) + + if f is not None: + return t.cast(bytes, f(size)) + + return self._stream.read(size) + + def readable(self) -> bool: + if self._force_readable: + return True + x = getattr(self._stream, "readable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.read(0) + except Exception: + return False + return True + + def writable(self) -> bool: + if self._force_writable: + return True + x = getattr(self._stream, "writable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.write("") # type: ignore + except Exception: + try: + self._stream.write(b"") + except Exception: + return False + return True + + def seekable(self) -> bool: + x = getattr(self._stream, "seekable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.seek(self._stream.tell()) + except Exception: + return False + return True + + +def _is_binary_reader(stream: t.IO[t.Any], default: bool = False) -> bool: + try: + return isinstance(stream.read(0), bytes) + except Exception: + return default + # This happens in some cases where the stream was already + # closed. In this case, we assume the default. + + +def _is_binary_writer(stream: t.IO[t.Any], default: bool = False) -> bool: + try: + stream.write(b"") + except Exception: + try: + stream.write("") + return False + except Exception: + pass + return default + return True + + +def _find_binary_reader(stream: t.IO[t.Any]) -> t.Optional[t.BinaryIO]: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_reader(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_reader(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _find_binary_writer(stream: t.IO[t.Any]) -> t.Optional[t.BinaryIO]: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_writer(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_writer(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _stream_is_misconfigured(stream: t.TextIO) -> bool: + """A stream is misconfigured if its encoding is ASCII.""" + # If the stream does not have an encoding set, we assume it's set + # to ASCII. This appears to happen in certain unittest + # environments. It's not quite clear what the correct behavior is + # but this at least will force Click to recover somehow. + return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii") + + +def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: t.Optional[str]) -> bool: + """A stream attribute is compatible if it is equal to the + desired value or the desired value is unset and the attribute + has a value. + """ + stream_value = getattr(stream, attr, None) + return stream_value == value or (value is None and stream_value is not None) + + +def _is_compatible_text_stream( + stream: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] +) -> bool: + """Check if a stream's encoding and errors attributes are + compatible with the desired values. + """ + return _is_compat_stream_attr( + stream, "encoding", encoding + ) and _is_compat_stream_attr(stream, "errors", errors) + + +def _force_correct_text_stream( + text_stream: t.IO[t.Any], + encoding: t.Optional[str], + errors: t.Optional[str], + is_binary: t.Callable[[t.IO[t.Any], bool], bool], + find_binary: t.Callable[[t.IO[t.Any]], t.Optional[t.BinaryIO]], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if is_binary(text_stream, False): + binary_reader = t.cast(t.BinaryIO, text_stream) + else: + text_stream = t.cast(t.TextIO, text_stream) + # If the stream looks compatible, and won't default to a + # misconfigured ascii encoding, return it as-is. + if _is_compatible_text_stream(text_stream, encoding, errors) and not ( + encoding is None and _stream_is_misconfigured(text_stream) + ): + return text_stream + + # Otherwise, get the underlying binary reader. + possible_binary_reader = find_binary(text_stream) + + # If that's not possible, silently use the original reader + # and get mojibake instead of exceptions. + if possible_binary_reader is None: + return text_stream + + binary_reader = possible_binary_reader + + # Default errors to replace instead of strict in order to get + # something that works. + if errors is None: + errors = "replace" + + # Wrap the binary stream in a text stream with the correct + # encoding parameters. + return _make_text_stream( + binary_reader, + encoding, + errors, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def _force_correct_text_reader( + text_reader: t.IO[t.Any], + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_reader, + encoding, + errors, + _is_binary_reader, + _find_binary_reader, + force_readable=force_readable, + ) + + +def _force_correct_text_writer( + text_writer: t.IO[t.Any], + encoding: t.Optional[str], + errors: t.Optional[str], + force_writable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_writer, + encoding, + errors, + _is_binary_writer, + _find_binary_writer, + force_writable=force_writable, + ) + + +def get_binary_stdin() -> t.BinaryIO: + reader = _find_binary_reader(sys.stdin) + if reader is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdin.") + return reader + + +def get_binary_stdout() -> t.BinaryIO: + writer = _find_binary_writer(sys.stdout) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdout.") + return writer + + +def get_binary_stderr() -> t.BinaryIO: + writer = _find_binary_writer(sys.stderr) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stderr.") + return writer + + +def get_text_stdin( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdin, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True) + + +def get_text_stdout( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdout, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True) + + +def get_text_stderr( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stderr, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True) + + +def _wrap_io_open( + file: t.Union[str, "os.PathLike[str]", int], + mode: str, + encoding: t.Optional[str], + errors: t.Optional[str], +) -> t.IO[t.Any]: + """Handles not passing ``encoding`` and ``errors`` in binary mode.""" + if "b" in mode: + return open(file, mode) + + return open(file, mode, encoding=encoding, errors=errors) + + +def open_stream( + filename: "t.Union[str, os.PathLike[str]]", + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, +) -> t.Tuple[t.IO[t.Any], bool]: + binary = "b" in mode + filename = os.fspath(filename) + + # Standard streams first. These are simple because they ignore the + # atomic flag. Use fsdecode to handle Path("-"). + if os.fsdecode(filename) == "-": + if any(m in mode for m in ["w", "a", "x"]): + if binary: + return get_binary_stdout(), False + return get_text_stdout(encoding=encoding, errors=errors), False + if binary: + return get_binary_stdin(), False + return get_text_stdin(encoding=encoding, errors=errors), False + + # Non-atomic writes directly go out through the regular open functions. + if not atomic: + return _wrap_io_open(filename, mode, encoding, errors), True + + # Some usability stuff for atomic writes + if "a" in mode: + raise ValueError( + "Appending to an existing file is not supported, because that" + " would involve an expensive `copy`-operation to a temporary" + " file. Open the file in normal `w`-mode and copy explicitly" + " if that's what you're after." + ) + if "x" in mode: + raise ValueError("Use the `overwrite`-parameter instead.") + if "w" not in mode: + raise ValueError("Atomic writes only make sense with `w`-mode.") + + # Atomic writes are more complicated. They work by opening a file + # as a proxy in the same folder and then using the fdopen + # functionality to wrap it in a Python file. Then we wrap it in an + # atomic file that moves the file over on close. + import errno + import random + + try: + perm: t.Optional[int] = os.stat(filename).st_mode + except OSError: + perm = None + + flags = os.O_RDWR | os.O_CREAT | os.O_EXCL + + if binary: + flags |= getattr(os, "O_BINARY", 0) + + while True: + tmp_filename = os.path.join( + os.path.dirname(filename), + f".__atomic-write{random.randrange(1 << 32):08x}", + ) + try: + fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) + break + except OSError as e: + if e.errno == errno.EEXIST or ( + os.name == "nt" + and e.errno == errno.EACCES + and os.path.isdir(e.filename) + and os.access(e.filename, os.W_OK) + ): + continue + raise + + if perm is not None: + os.chmod(tmp_filename, perm) # in case perm includes bits in umask + + f = _wrap_io_open(fd, mode, encoding, errors) + af = _AtomicFile(f, tmp_filename, os.path.realpath(filename)) + return t.cast(t.IO[t.Any], af), True + + +class _AtomicFile: + def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None: + self._f = f + self._tmp_filename = tmp_filename + self._real_filename = real_filename + self.closed = False + + @property + def name(self) -> str: + return self._real_filename + + def close(self, delete: bool = False) -> None: + if self.closed: + return + self._f.close() + os.replace(self._tmp_filename, self._real_filename) + self.closed = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._f, name) + + def __enter__(self) -> "_AtomicFile": + return self + + def __exit__(self, exc_type: t.Optional[t.Type[BaseException]], *_: t.Any) -> None: + self.close(delete=exc_type is not None) + + def __repr__(self) -> str: + return repr(self._f) + + +def strip_ansi(value: str) -> str: + return _ansi_re.sub("", value) + + +def _is_jupyter_kernel_output(stream: t.IO[t.Any]) -> bool: + while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): + stream = stream._stream + + return stream.__class__.__module__.startswith("ipykernel.") + + +def should_strip_ansi( + stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None +) -> bool: + if color is None: + if stream is None: + stream = sys.stdin + return not isatty(stream) and not _is_jupyter_kernel_output(stream) + return not color + + +# On Windows, wrap the output streams with colorama to support ANSI +# color codes. +# NOTE: double check is needed so mypy does not analyze this on Linux +if sys.platform.startswith("win") and WIN: + from ._winconsole import _get_windows_console_stream + + def _get_argv_encoding() -> str: + import locale + + return locale.getpreferredencoding() + + _ansi_stream_wrappers: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def auto_wrap_for_ansi( + stream: t.TextIO, color: t.Optional[bool] = None + ) -> t.TextIO: + """Support ANSI color and style codes on Windows by wrapping a + stream with colorama. + """ + try: + cached = _ansi_stream_wrappers.get(stream) + except Exception: + cached = None + + if cached is not None: + return cached + + import colorama + + strip = should_strip_ansi(stream, color) + ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) + rv = t.cast(t.TextIO, ansi_wrapper.stream) + _write = rv.write + + def _safe_write(s): + try: + return _write(s) + except BaseException: + ansi_wrapper.reset_all() + raise + + rv.write = _safe_write + + try: + _ansi_stream_wrappers[stream] = rv + except Exception: + pass + + return rv + +else: + + def _get_argv_encoding() -> str: + return getattr(sys.stdin, "encoding", None) or sys.getfilesystemencoding() + + def _get_windows_console_stream( + f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] + ) -> t.Optional[t.TextIO]: + return None + + +def term_len(x: str) -> int: + return len(strip_ansi(x)) + + +def isatty(stream: t.IO[t.Any]) -> bool: + try: + return stream.isatty() + except Exception: + return False + + +def _make_cached_stream_func( + src_func: t.Callable[[], t.Optional[t.TextIO]], + wrapper_func: t.Callable[[], t.TextIO], +) -> t.Callable[[], t.Optional[t.TextIO]]: + cache: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def func() -> t.Optional[t.TextIO]: + stream = src_func() + + if stream is None: + return None + + try: + rv = cache.get(stream) + except Exception: + rv = None + if rv is not None: + return rv + rv = wrapper_func() + try: + cache[stream] = rv + except Exception: + pass + return rv + + return func + + +_default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin) +_default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout) +_default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr) + + +binary_streams: t.Mapping[str, t.Callable[[], t.BinaryIO]] = { + "stdin": get_binary_stdin, + "stdout": get_binary_stdout, + "stderr": get_binary_stderr, +} + +text_streams: t.Mapping[ + str, t.Callable[[t.Optional[str], t.Optional[str]], t.TextIO] +] = { + "stdin": get_text_stdin, + "stdout": get_text_stdout, + "stderr": get_text_stderr, +} diff --git a/.venv/lib/python3.9/site-packages/click/_termui_impl.py b/.venv/lib/python3.9/site-packages/click/_termui_impl.py new file mode 100644 index 00000000..ad9f8f6c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/_termui_impl.py @@ -0,0 +1,788 @@ +""" +This module contains implementations for the termui module. To keep the +import time of Click down, some infrequently used functionality is +placed in this module and only imported as needed. +""" + +import contextlib +import math +import os +import sys +import time +import typing as t +from gettext import gettext as _ +from io import StringIO +from shutil import which +from types import TracebackType + +from ._compat import _default_text_stdout +from ._compat import CYGWIN +from ._compat import get_best_encoding +from ._compat import isatty +from ._compat import open_stream +from ._compat import strip_ansi +from ._compat import term_len +from ._compat import WIN +from .exceptions import ClickException +from .utils import echo + +V = t.TypeVar("V") + +if os.name == "nt": + BEFORE_BAR = "\r" + AFTER_BAR = "\n" +else: + BEFORE_BAR = "\r\033[?25l" + AFTER_BAR = "\033[?25h\n" + + +class ProgressBar(t.Generic[V]): + def __init__( + self, + iterable: t.Optional[t.Iterable[V]], + length: t.Optional[int] = None, + fill_char: str = "#", + empty_char: str = " ", + bar_template: str = "%(bar)s", + info_sep: str = " ", + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + label: t.Optional[str] = None, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = None, + update_min_steps: int = 1, + width: int = 30, + ) -> None: + self.fill_char = fill_char + self.empty_char = empty_char + self.bar_template = bar_template + self.info_sep = info_sep + self.show_eta = show_eta + self.show_percent = show_percent + self.show_pos = show_pos + self.item_show_func = item_show_func + self.label: str = label or "" + + if file is None: + file = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + file = StringIO() + + self.file = file + self.color = color + self.update_min_steps = update_min_steps + self._completed_intervals = 0 + self.width: int = width + self.autowidth: bool = width == 0 + + if length is None: + from operator import length_hint + + length = length_hint(iterable, -1) + + if length == -1: + length = None + if iterable is None: + if length is None: + raise TypeError("iterable or length is required") + iterable = t.cast(t.Iterable[V], range(length)) + self.iter: t.Iterable[V] = iter(iterable) + self.length = length + self.pos = 0 + self.avg: t.List[float] = [] + self.last_eta: float + self.start: float + self.start = self.last_eta = time.time() + self.eta_known: bool = False + self.finished: bool = False + self.max_width: t.Optional[int] = None + self.entered: bool = False + self.current_item: t.Optional[V] = None + self.is_hidden: bool = not isatty(self.file) + self._last_line: t.Optional[str] = None + + def __enter__(self) -> "ProgressBar[V]": + self.entered = True + self.render_progress() + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + self.render_finish() + + def __iter__(self) -> t.Iterator[V]: + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + self.render_progress() + return self.generator() + + def __next__(self) -> V: + # Iteration is defined in terms of a generator function, + # returned by iter(self); use that to define next(). This works + # because `self.iter` is an iterable consumed by that generator, + # so it is re-entry safe. Calling `next(self.generator())` + # twice works and does "what you want". + return next(iter(self)) + + def render_finish(self) -> None: + if self.is_hidden: + return + self.file.write(AFTER_BAR) + self.file.flush() + + @property + def pct(self) -> float: + if self.finished: + return 1.0 + return min(self.pos / (float(self.length or 1) or 1), 1.0) + + @property + def time_per_iteration(self) -> float: + if not self.avg: + return 0.0 + return sum(self.avg) / float(len(self.avg)) + + @property + def eta(self) -> float: + if self.length is not None and not self.finished: + return self.time_per_iteration * (self.length - self.pos) + return 0.0 + + def format_eta(self) -> str: + if self.eta_known: + t = int(self.eta) + seconds = t % 60 + t //= 60 + minutes = t % 60 + t //= 60 + hours = t % 24 + t //= 24 + if t > 0: + return f"{t}d {hours:02}:{minutes:02}:{seconds:02}" + else: + return f"{hours:02}:{minutes:02}:{seconds:02}" + return "" + + def format_pos(self) -> str: + pos = str(self.pos) + if self.length is not None: + pos += f"/{self.length}" + return pos + + def format_pct(self) -> str: + return f"{int(self.pct * 100): 4}%"[1:] + + def format_bar(self) -> str: + if self.length is not None: + bar_length = int(self.pct * self.width) + bar = self.fill_char * bar_length + bar += self.empty_char * (self.width - bar_length) + elif self.finished: + bar = self.fill_char * self.width + else: + chars = list(self.empty_char * (self.width or 1)) + if self.time_per_iteration != 0: + chars[ + int( + (math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5) + * self.width + ) + ] = self.fill_char + bar = "".join(chars) + return bar + + def format_progress_line(self) -> str: + show_percent = self.show_percent + + info_bits = [] + if self.length is not None and show_percent is None: + show_percent = not self.show_pos + + if self.show_pos: + info_bits.append(self.format_pos()) + if show_percent: + info_bits.append(self.format_pct()) + if self.show_eta and self.eta_known and not self.finished: + info_bits.append(self.format_eta()) + if self.item_show_func is not None: + item_info = self.item_show_func(self.current_item) + if item_info is not None: + info_bits.append(item_info) + + return ( + self.bar_template + % { + "label": self.label, + "bar": self.format_bar(), + "info": self.info_sep.join(info_bits), + } + ).rstrip() + + def render_progress(self) -> None: + import shutil + + if self.is_hidden: + # Only output the label as it changes if the output is not a + # TTY. Use file=stderr if you expect to be piping stdout. + if self._last_line != self.label: + self._last_line = self.label + echo(self.label, file=self.file, color=self.color) + + return + + buf = [] + # Update width in case the terminal has been resized + if self.autowidth: + old_width = self.width + self.width = 0 + clutter_length = term_len(self.format_progress_line()) + new_width = max(0, shutil.get_terminal_size().columns - clutter_length) + if new_width < old_width: + buf.append(BEFORE_BAR) + buf.append(" " * self.max_width) # type: ignore + self.max_width = new_width + self.width = new_width + + clear_width = self.width + if self.max_width is not None: + clear_width = self.max_width + + buf.append(BEFORE_BAR) + line = self.format_progress_line() + line_len = term_len(line) + if self.max_width is None or self.max_width < line_len: + self.max_width = line_len + + buf.append(line) + buf.append(" " * (clear_width - line_len)) + line = "".join(buf) + # Render the line only if it changed. + + if line != self._last_line: + self._last_line = line + echo(line, file=self.file, color=self.color, nl=False) + self.file.flush() + + def make_step(self, n_steps: int) -> None: + self.pos += n_steps + if self.length is not None and self.pos >= self.length: + self.finished = True + + if (time.time() - self.last_eta) < 1.0: + return + + self.last_eta = time.time() + + # self.avg is a rolling list of length <= 7 of steps where steps are + # defined as time elapsed divided by the total progress through + # self.length. + if self.pos: + step = (time.time() - self.start) / self.pos + else: + step = time.time() - self.start + + self.avg = self.avg[-6:] + [step] + + self.eta_known = self.length is not None + + def update(self, n_steps: int, current_item: t.Optional[V] = None) -> None: + """Update the progress bar by advancing a specified number of + steps, and optionally set the ``current_item`` for this new + position. + + :param n_steps: Number of steps to advance. + :param current_item: Optional item to set as ``current_item`` + for the updated position. + + .. versionchanged:: 8.0 + Added the ``current_item`` optional parameter. + + .. versionchanged:: 8.0 + Only render when the number of steps meets the + ``update_min_steps`` threshold. + """ + if current_item is not None: + self.current_item = current_item + + self._completed_intervals += n_steps + + if self._completed_intervals >= self.update_min_steps: + self.make_step(self._completed_intervals) + self.render_progress() + self._completed_intervals = 0 + + def finish(self) -> None: + self.eta_known = False + self.current_item = None + self.finished = True + + def generator(self) -> t.Iterator[V]: + """Return a generator which yields the items added to the bar + during construction, and updates the progress bar *after* the + yielded block returns. + """ + # WARNING: the iterator interface for `ProgressBar` relies on + # this and only works because this is a simple generator which + # doesn't create or manage additional state. If this function + # changes, the impact should be evaluated both against + # `iter(bar)` and `next(bar)`. `next()` in particular may call + # `self.generator()` repeatedly, and this must remain safe in + # order for that interface to work. + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + + if self.is_hidden: + yield from self.iter + else: + for rv in self.iter: + self.current_item = rv + + # This allows show_item_func to be updated before the + # item is processed. Only trigger at the beginning of + # the update interval. + if self._completed_intervals == 0: + self.render_progress() + + yield rv + self.update(1) + + self.finish() + self.render_progress() + + +def pager(generator: t.Iterable[str], color: t.Optional[bool] = None) -> None: + """Decide what method to use for paging through text.""" + stdout = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if stdout is None: + stdout = StringIO() + + if not isatty(sys.stdin) or not isatty(stdout): + return _nullpager(stdout, generator, color) + pager_cmd = (os.environ.get("PAGER", None) or "").strip() + if pager_cmd: + if WIN: + if _tempfilepager(generator, pager_cmd, color): + return + elif _pipepager(generator, pager_cmd, color): + return + if os.environ.get("TERM") in ("dumb", "emacs"): + return _nullpager(stdout, generator, color) + if (WIN or sys.platform.startswith("os2")) and _tempfilepager( + generator, "more", color + ): + return + if _pipepager(generator, "less", color): + return + + import tempfile + + fd, filename = tempfile.mkstemp() + os.close(fd) + try: + if _pipepager(generator, "more", color): + return + return _nullpager(stdout, generator, color) + finally: + os.unlink(filename) + + +def _pipepager(generator: t.Iterable[str], cmd: str, color: t.Optional[bool]) -> bool: + """Page through text by feeding it to another program. Invoking a + pager through this might support colors. + + Returns True if the command was found, False otherwise and thus another + pager should be attempted. + """ + cmd_absolute = which(cmd) + if cmd_absolute is None: + return False + + import subprocess + + env = dict(os.environ) + + # If we're piping to less we might support colors under the + # condition that + cmd_detail = cmd.rsplit("/", 1)[-1].split() + if color is None and cmd_detail[0] == "less": + less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_detail[1:])}" + if not less_flags: + env["LESS"] = "-R" + color = True + elif "r" in less_flags or "R" in less_flags: + color = True + + c = subprocess.Popen( + [cmd_absolute], + shell=True, + stdin=subprocess.PIPE, + env=env, + errors="replace", + text=True, + ) + assert c.stdin is not None + try: + for text in generator: + if not color: + text = strip_ansi(text) + + c.stdin.write(text) + except (OSError, KeyboardInterrupt): + pass + else: + c.stdin.close() + + # Less doesn't respect ^C, but catches it for its own UI purposes (aborting + # search or other commands inside less). + # + # That means when the user hits ^C, the parent process (click) terminates, + # but less is still alive, paging the output and messing up the terminal. + # + # If the user wants to make the pager exit on ^C, they should set + # `LESS='-K'`. It's not our decision to make. + while True: + try: + c.wait() + except KeyboardInterrupt: + pass + else: + break + + return True + + +def _tempfilepager( + generator: t.Iterable[str], + cmd: str, + color: t.Optional[bool], +) -> bool: + """Page through text by invoking a program on a temporary file. + + Returns True if the command was found, False otherwise and thus another + pager should be attempted. + """ + # Which is necessary for Windows, it is also recommended in the Popen docs. + cmd_absolute = which(cmd) + if cmd_absolute is None: + return False + + import subprocess + import tempfile + + fd, filename = tempfile.mkstemp() + # TODO: This never terminates if the passed generator never terminates. + text = "".join(generator) + if not color: + text = strip_ansi(text) + encoding = get_best_encoding(sys.stdout) + with open_stream(filename, "wb")[0] as f: + f.write(text.encode(encoding)) + try: + subprocess.call([cmd_absolute, filename]) + except OSError: + # Command not found + pass + finally: + os.close(fd) + os.unlink(filename) + + return True + + +def _nullpager( + stream: t.TextIO, generator: t.Iterable[str], color: t.Optional[bool] +) -> None: + """Simply print unformatted text. This is the ultimate fallback.""" + for text in generator: + if not color: + text = strip_ansi(text) + stream.write(text) + + +class Editor: + def __init__( + self, + editor: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = None, + require_save: bool = True, + extension: str = ".txt", + ) -> None: + self.editor = editor + self.env = env + self.require_save = require_save + self.extension = extension + + def get_editor(self) -> str: + if self.editor is not None: + return self.editor + for key in "VISUAL", "EDITOR": + rv = os.environ.get(key) + if rv: + return rv + if WIN: + return "notepad" + for editor in "sensible-editor", "vim", "nano": + if which(editor) is not None: + return editor + return "vi" + + def edit_file(self, filename: str) -> None: + import subprocess + + editor = self.get_editor() + environ: t.Optional[t.Dict[str, str]] = None + + if self.env: + environ = os.environ.copy() + environ.update(self.env) + + try: + c = subprocess.Popen(f'{editor} "{filename}"', env=environ, shell=True) + exit_code = c.wait() + if exit_code != 0: + raise ClickException( + _("{editor}: Editing failed").format(editor=editor) + ) + except OSError as e: + raise ClickException( + _("{editor}: Editing failed: {e}").format(editor=editor, e=e) + ) from e + + def edit(self, text: t.Optional[t.AnyStr]) -> t.Optional[t.AnyStr]: + import tempfile + + if not text: + data = b"" + elif isinstance(text, (bytes, bytearray)): + data = text + else: + if text and not text.endswith("\n"): + text += "\n" + + if WIN: + data = text.replace("\n", "\r\n").encode("utf-8-sig") + else: + data = text.encode("utf-8") + + fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension) + f: t.BinaryIO + + try: + with os.fdopen(fd, "wb") as f: + f.write(data) + + # If the filesystem resolution is 1 second, like Mac OS + # 10.12 Extended, or 2 seconds, like FAT32, and the editor + # closes very fast, require_save can fail. Set the modified + # time to be 2 seconds in the past to work around this. + os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2)) + # Depending on the resolution, the exact value might not be + # recorded, so get the new recorded value. + timestamp = os.path.getmtime(name) + + self.edit_file(name) + + if self.require_save and os.path.getmtime(name) == timestamp: + return None + + with open(name, "rb") as f: + rv = f.read() + + if isinstance(text, (bytes, bytearray)): + return rv + + return rv.decode("utf-8-sig").replace("\r\n", "\n") # type: ignore + finally: + os.unlink(name) + + +def open_url(url: str, wait: bool = False, locate: bool = False) -> int: + import subprocess + + def _unquote_file(url: str) -> str: + from urllib.parse import unquote + + if url.startswith("file://"): + url = unquote(url[7:]) + + return url + + if sys.platform == "darwin": + args = ["open"] + if wait: + args.append("-W") + if locate: + args.append("-R") + args.append(_unquote_file(url)) + null = open("/dev/null", "w") + try: + return subprocess.Popen(args, stderr=null).wait() + finally: + null.close() + elif WIN: + if locate: + url = _unquote_file(url) + args = ["explorer", f"/select,{url}"] + else: + args = ["start"] + if wait: + args.append("/WAIT") + args.append("") + args.append(url) + try: + return subprocess.call(args) + except OSError: + # Command not found + return 127 + elif CYGWIN: + if locate: + url = _unquote_file(url) + args = ["cygstart", os.path.dirname(url)] + else: + args = ["cygstart"] + if wait: + args.append("-w") + args.append(url) + try: + return subprocess.call(args) + except OSError: + # Command not found + return 127 + + try: + if locate: + url = os.path.dirname(_unquote_file(url)) or "." + else: + url = _unquote_file(url) + c = subprocess.Popen(["xdg-open", url]) + if wait: + return c.wait() + return 0 + except OSError: + if url.startswith(("http://", "https://")) and not locate and not wait: + import webbrowser + + webbrowser.open(url) + return 0 + return 1 + + +def _translate_ch_to_exc(ch: str) -> t.Optional[BaseException]: + if ch == "\x03": + raise KeyboardInterrupt() + + if ch == "\x04" and not WIN: # Unix-like, Ctrl+D + raise EOFError() + + if ch == "\x1a" and WIN: # Windows, Ctrl+Z + raise EOFError() + + return None + + +if WIN: + import msvcrt + + @contextlib.contextmanager + def raw_terminal() -> t.Iterator[int]: + yield -1 + + def getchar(echo: bool) -> str: + # The function `getch` will return a bytes object corresponding to + # the pressed character. Since Windows 10 build 1803, it will also + # return \x00 when called a second time after pressing a regular key. + # + # `getwch` does not share this probably-bugged behavior. Moreover, it + # returns a Unicode object by default, which is what we want. + # + # Either of these functions will return \x00 or \xe0 to indicate + # a special key, and you need to call the same function again to get + # the "rest" of the code. The fun part is that \u00e0 is + # "latin small letter a with grave", so if you type that on a French + # keyboard, you _also_ get a \xe0. + # E.g., consider the Up arrow. This returns \xe0 and then \x48. The + # resulting Unicode string reads as "a with grave" + "capital H". + # This is indistinguishable from when the user actually types + # "a with grave" and then "capital H". + # + # When \xe0 is returned, we assume it's part of a special-key sequence + # and call `getwch` again, but that means that when the user types + # the \u00e0 character, `getchar` doesn't return until a second + # character is typed. + # The alternative is returning immediately, but that would mess up + # cross-platform handling of arrow keys and others that start with + # \xe0. Another option is using `getch`, but then we can't reliably + # read non-ASCII characters, because return values of `getch` are + # limited to the current 8-bit codepage. + # + # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` + # is doing the right thing in more situations than with `getch`. + func: t.Callable[[], str] + + if echo: + func = msvcrt.getwche # type: ignore + else: + func = msvcrt.getwch # type: ignore + + rv = func() + + if rv in ("\x00", "\xe0"): + # \x00 and \xe0 are control characters that indicate special key, + # see above. + rv += func() + + _translate_ch_to_exc(rv) + return rv + +else: + import termios + import tty + + @contextlib.contextmanager + def raw_terminal() -> t.Iterator[int]: + f: t.Optional[t.TextIO] + fd: int + + if not isatty(sys.stdin): + f = open("/dev/tty") + fd = f.fileno() + else: + fd = sys.stdin.fileno() + f = None + + try: + old_settings = termios.tcgetattr(fd) + + try: + tty.setraw(fd) + yield fd + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + sys.stdout.flush() + + if f is not None: + f.close() + except termios.error: + pass + + def getchar(echo: bool) -> str: + with raw_terminal() as fd: + ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace") + + if echo and isatty(sys.stdout): + sys.stdout.write(ch) + + _translate_ch_to_exc(ch) + return ch diff --git a/.venv/lib/python3.9/site-packages/click/_textwrap.py b/.venv/lib/python3.9/site-packages/click/_textwrap.py new file mode 100644 index 00000000..b47dcbd4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/_textwrap.py @@ -0,0 +1,49 @@ +import textwrap +import typing as t +from contextlib import contextmanager + + +class TextWrapper(textwrap.TextWrapper): + def _handle_long_word( + self, + reversed_chunks: t.List[str], + cur_line: t.List[str], + cur_len: int, + width: int, + ) -> None: + space_left = max(width - cur_len, 1) + + if self.break_long_words: + last = reversed_chunks[-1] + cut = last[:space_left] + res = last[space_left:] + cur_line.append(cut) + reversed_chunks[-1] = res + elif not cur_line: + cur_line.append(reversed_chunks.pop()) + + @contextmanager + def extra_indent(self, indent: str) -> t.Iterator[None]: + old_initial_indent = self.initial_indent + old_subsequent_indent = self.subsequent_indent + self.initial_indent += indent + self.subsequent_indent += indent + + try: + yield + finally: + self.initial_indent = old_initial_indent + self.subsequent_indent = old_subsequent_indent + + def indent_only(self, text: str) -> str: + rv = [] + + for idx, line in enumerate(text.splitlines()): + indent = self.initial_indent + + if idx > 0: + indent = self.subsequent_indent + + rv.append(f"{indent}{line}") + + return "\n".join(rv) diff --git a/.venv/lib/python3.9/site-packages/click/_winconsole.py b/.venv/lib/python3.9/site-packages/click/_winconsole.py new file mode 100644 index 00000000..6b20df31 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/_winconsole.py @@ -0,0 +1,279 @@ +# This module is based on the excellent work by Adam Bartoš who +# provided a lot of what went into the implementation here in +# the discussion to issue1602 in the Python bug tracker. +# +# There are some general differences in regards to how this works +# compared to the original patches as we do not need to patch +# the entire interpreter but just work in our little world of +# echo and prompt. +import io +import sys +import time +import typing as t +from ctypes import byref +from ctypes import c_char +from ctypes import c_char_p +from ctypes import c_int +from ctypes import c_ssize_t +from ctypes import c_ulong +from ctypes import c_void_p +from ctypes import POINTER +from ctypes import py_object +from ctypes import Structure +from ctypes.wintypes import DWORD +from ctypes.wintypes import HANDLE +from ctypes.wintypes import LPCWSTR +from ctypes.wintypes import LPWSTR + +from ._compat import _NonClosingTextIOWrapper + +assert sys.platform == "win32" +import msvcrt # noqa: E402 +from ctypes import windll # noqa: E402 +from ctypes import WINFUNCTYPE # noqa: E402 + +c_ssize_p = POINTER(c_ssize_t) + +kernel32 = windll.kernel32 +GetStdHandle = kernel32.GetStdHandle +ReadConsoleW = kernel32.ReadConsoleW +WriteConsoleW = kernel32.WriteConsoleW +GetConsoleMode = kernel32.GetConsoleMode +GetLastError = kernel32.GetLastError +GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) +CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( + ("CommandLineToArgvW", windll.shell32) +) +LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32)) + +STDIN_HANDLE = GetStdHandle(-10) +STDOUT_HANDLE = GetStdHandle(-11) +STDERR_HANDLE = GetStdHandle(-12) + +PyBUF_SIMPLE = 0 +PyBUF_WRITABLE = 1 + +ERROR_SUCCESS = 0 +ERROR_NOT_ENOUGH_MEMORY = 8 +ERROR_OPERATION_ABORTED = 995 + +STDIN_FILENO = 0 +STDOUT_FILENO = 1 +STDERR_FILENO = 2 + +EOF = b"\x1a" +MAX_BYTES_WRITTEN = 32767 + +try: + from ctypes import pythonapi +except ImportError: + # On PyPy we cannot get buffers so our ability to operate here is + # severely limited. + get_buffer = None +else: + + class Py_buffer(Structure): + _fields_ = [ + ("buf", c_void_p), + ("obj", py_object), + ("len", c_ssize_t), + ("itemsize", c_ssize_t), + ("readonly", c_int), + ("ndim", c_int), + ("format", c_char_p), + ("shape", c_ssize_p), + ("strides", c_ssize_p), + ("suboffsets", c_ssize_p), + ("internal", c_void_p), + ] + + PyObject_GetBuffer = pythonapi.PyObject_GetBuffer + PyBuffer_Release = pythonapi.PyBuffer_Release + + def get_buffer(obj, writable=False): + buf = Py_buffer() + flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE + PyObject_GetBuffer(py_object(obj), byref(buf), flags) + + try: + buffer_type = c_char * buf.len + return buffer_type.from_address(buf.buf) + finally: + PyBuffer_Release(byref(buf)) + + +class _WindowsConsoleRawIOBase(io.RawIOBase): + def __init__(self, handle): + self.handle = handle + + def isatty(self): + super().isatty() + return True + + +class _WindowsConsoleReader(_WindowsConsoleRawIOBase): + def readable(self): + return True + + def readinto(self, b): + bytes_to_be_read = len(b) + if not bytes_to_be_read: + return 0 + elif bytes_to_be_read % 2: + raise ValueError( + "cannot read odd number of bytes from UTF-16-LE encoded console" + ) + + buffer = get_buffer(b, writable=True) + code_units_to_be_read = bytes_to_be_read // 2 + code_units_read = c_ulong() + + rv = ReadConsoleW( + HANDLE(self.handle), + buffer, + code_units_to_be_read, + byref(code_units_read), + None, + ) + if GetLastError() == ERROR_OPERATION_ABORTED: + # wait for KeyboardInterrupt + time.sleep(0.1) + if not rv: + raise OSError(f"Windows error: {GetLastError()}") + + if buffer[0] == EOF: + return 0 + return 2 * code_units_read.value + + +class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): + def writable(self): + return True + + @staticmethod + def _get_error_message(errno): + if errno == ERROR_SUCCESS: + return "ERROR_SUCCESS" + elif errno == ERROR_NOT_ENOUGH_MEMORY: + return "ERROR_NOT_ENOUGH_MEMORY" + return f"Windows error {errno}" + + def write(self, b): + bytes_to_be_written = len(b) + buf = get_buffer(b) + code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2 + code_units_written = c_ulong() + + WriteConsoleW( + HANDLE(self.handle), + buf, + code_units_to_be_written, + byref(code_units_written), + None, + ) + bytes_written = 2 * code_units_written.value + + if bytes_written == 0 and bytes_to_be_written > 0: + raise OSError(self._get_error_message(GetLastError())) + return bytes_written + + +class ConsoleStream: + def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None: + self._text_stream = text_stream + self.buffer = byte_stream + + @property + def name(self) -> str: + return self.buffer.name + + def write(self, x: t.AnyStr) -> int: + if isinstance(x, str): + return self._text_stream.write(x) + try: + self.flush() + except Exception: + pass + return self.buffer.write(x) + + def writelines(self, lines: t.Iterable[t.AnyStr]) -> None: + for line in lines: + self.write(line) + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._text_stream, name) + + def isatty(self) -> bool: + return self.buffer.isatty() + + def __repr__(self): + return f"" + + +def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +_stream_factories: t.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = { + 0: _get_text_stdin, + 1: _get_text_stdout, + 2: _get_text_stderr, +} + + +def _is_console(f: t.TextIO) -> bool: + if not hasattr(f, "fileno"): + return False + + try: + fileno = f.fileno() + except (OSError, io.UnsupportedOperation): + return False + + handle = msvcrt.get_osfhandle(fileno) + return bool(GetConsoleMode(handle, byref(DWORD()))) + + +def _get_windows_console_stream( + f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] +) -> t.Optional[t.TextIO]: + if ( + get_buffer is not None + and encoding in {"utf-16-le", None} + and errors in {"strict", None} + and _is_console(f) + ): + func = _stream_factories.get(f.fileno()) + if func is not None: + b = getattr(f, "buffer", None) + + if b is None: + return None + + return func(b) diff --git a/.venv/lib/python3.9/site-packages/click/core.py b/.venv/lib/python3.9/site-packages/click/core.py new file mode 100644 index 00000000..e6305011 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/core.py @@ -0,0 +1,3047 @@ +import enum +import errno +import inspect +import os +import sys +import typing as t +from collections import abc +from contextlib import contextmanager +from contextlib import ExitStack +from functools import update_wrapper +from gettext import gettext as _ +from gettext import ngettext +from itertools import repeat +from types import TracebackType + +from . import types +from .exceptions import Abort +from .exceptions import BadParameter +from .exceptions import ClickException +from .exceptions import Exit +from .exceptions import MissingParameter +from .exceptions import UsageError +from .formatting import HelpFormatter +from .formatting import join_options +from .globals import pop_context +from .globals import push_context +from .parser import _flag_needs_value +from .parser import OptionParser +from .parser import split_opt +from .termui import confirm +from .termui import prompt +from .termui import style +from .utils import _detect_program_name +from .utils import _expand_args +from .utils import echo +from .utils import make_default_short_help +from .utils import make_str +from .utils import PacifyFlushWrapper + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .decorators import HelpOption + from .shell_completion import CompletionItem + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) +V = t.TypeVar("V") + + +def _complete_visible_commands( + ctx: "Context", incomplete: str +) -> t.Iterator[t.Tuple[str, "Command"]]: + """List all the subcommands of a group that start with the + incomplete value and aren't hidden. + + :param ctx: Invocation context for the group. + :param incomplete: Value being completed. May be empty. + """ + multi = t.cast(MultiCommand, ctx.command) + + for name in multi.list_commands(ctx): + if name.startswith(incomplete): + command = multi.get_command(ctx, name) + + if command is not None and not command.hidden: + yield name, command + + +def _check_multicommand( + base_command: "MultiCommand", cmd_name: str, cmd: "Command", register: bool = False +) -> None: + if not base_command.chain or not isinstance(cmd, MultiCommand): + return + if register: + hint = ( + "It is not possible to add multi commands as children to" + " another multi command that is in chain mode." + ) + else: + hint = ( + "Found a multi command as subcommand to a multi command" + " that is in chain mode. This is not supported." + ) + raise RuntimeError( + f"{hint}. Command {base_command.name!r} is set to chain and" + f" {cmd_name!r} was added as a subcommand but it in itself is a" + f" multi command. ({cmd_name!r} is a {type(cmd).__name__}" + f" within a chained {type(base_command).__name__} named" + f" {base_command.name!r})." + ) + + +def batch(iterable: t.Iterable[V], batch_size: int) -> t.List[t.Tuple[V, ...]]: + return list(zip(*repeat(iter(iterable), batch_size))) + + +@contextmanager +def augment_usage_errors( + ctx: "Context", param: t.Optional["Parameter"] = None +) -> t.Iterator[None]: + """Context manager that attaches extra information to exceptions.""" + try: + yield + except BadParameter as e: + if e.ctx is None: + e.ctx = ctx + if param is not None and e.param is None: + e.param = param + raise + except UsageError as e: + if e.ctx is None: + e.ctx = ctx + raise + + +def iter_params_for_processing( + invocation_order: t.Sequence["Parameter"], + declaration_order: t.Sequence["Parameter"], +) -> t.List["Parameter"]: + """Returns all declared parameters in the order they should be processed. + + The declared parameters are re-shuffled depending on the order in which + they were invoked, as well as the eagerness of each parameters. + + The invocation order takes precedence over the declaration order. I.e. the + order in which the user provided them to the CLI is respected. + + This behavior and its effect on callback evaluation is detailed at: + https://click.palletsprojects.com/en/stable/advanced/#callback-evaluation-order + """ + + def sort_key(item: "Parameter") -> t.Tuple[bool, float]: + try: + idx: float = invocation_order.index(item) + except ValueError: + idx = float("inf") + + return not item.is_eager, idx + + return sorted(declaration_order, key=sort_key) + + +class ParameterSource(enum.Enum): + """This is an :class:`~enum.Enum` that indicates the source of a + parameter's value. + + Use :meth:`click.Context.get_parameter_source` to get the + source for a parameter by name. + + .. versionchanged:: 8.0 + Use :class:`~enum.Enum` and drop the ``validate`` method. + + .. versionchanged:: 8.0 + Added the ``PROMPT`` value. + """ + + COMMANDLINE = enum.auto() + """The value was provided by the command line args.""" + ENVIRONMENT = enum.auto() + """The value was provided with an environment variable.""" + DEFAULT = enum.auto() + """Used the default specified by the parameter.""" + DEFAULT_MAP = enum.auto() + """Used a default provided by :attr:`Context.default_map`.""" + PROMPT = enum.auto() + """Used a prompt to confirm a default or provide a value.""" + + +class Context: + """The context is a special internal object that holds state relevant + for the script execution at every single level. It's normally invisible + to commands unless they opt-in to getting access to it. + + The context is useful as it can pass internal objects around and can + control special execution features such as reading data from + environment variables. + + A context can be used as context manager in which case it will call + :meth:`close` on teardown. + + :param command: the command class for this context. + :param parent: the parent context. + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it is usually + the name of the script, for commands below it it's + the name of the script. + :param obj: an arbitrary object of user data. + :param auto_envvar_prefix: the prefix to use for automatic environment + variables. If this is `None` then reading + from environment variables is disabled. This + does not affect manually set environment + variables which are always read. + :param default_map: a dictionary (like object) with default values + for parameters. + :param terminal_width: the width of the terminal. The default is + inherit from parent context. If no context + defines the terminal width then auto + detection will be applied. + :param max_content_width: the maximum width for content rendered by + Click (this currently only affects help + pages). This defaults to 80 characters if + not overridden. In other words: even if the + terminal is larger than that, Click will not + format things wider than 80 characters by + default. In addition to that, formatters might + add some safety mapping on the right. + :param resilient_parsing: if this flag is enabled then Click will + parse without any interactivity or callback + invocation. Default values will also be + ignored. This is useful for implementing + things such as completion support. + :param allow_extra_args: if this is set to `True` then extra arguments + at the end will not raise an error and will be + kept on the context. The default is to inherit + from the command. + :param allow_interspersed_args: if this is set to `False` then options + and arguments cannot be mixed. The + default is to inherit from the command. + :param ignore_unknown_options: instructs click to ignore options it does + not know and keeps them for later + processing. + :param help_option_names: optionally a list of strings that define how + the default help parameter is named. The + default is ``['--help']``. + :param token_normalize_func: an optional function that is used to + normalize tokens (options, choices, + etc.). This for instance can be used to + implement case insensitive behavior. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are used in texts that Click prints which is by + default not the case. This for instance would affect + help output. + :param show_default: Show the default value for commands. If this + value is not set, it defaults to the value from the parent + context. ``Command.show_default`` overrides this default for the + specific command. + + .. versionchanged:: 8.1 + The ``show_default`` parameter is overridden by + ``Command.show_default``, instead of the other way around. + + .. versionchanged:: 8.0 + The ``show_default`` parameter defaults to the value from the + parent context. + + .. versionchanged:: 7.1 + Added the ``show_default`` parameter. + + .. versionchanged:: 4.0 + Added the ``color``, ``ignore_unknown_options``, and + ``max_content_width`` parameters. + + .. versionchanged:: 3.0 + Added the ``allow_extra_args`` and ``allow_interspersed_args`` + parameters. + + .. versionchanged:: 2.0 + Added the ``resilient_parsing``, ``help_option_names``, and + ``token_normalize_func`` parameters. + """ + + #: The formatter class to create with :meth:`make_formatter`. + #: + #: .. versionadded:: 8.0 + formatter_class: t.Type["HelpFormatter"] = HelpFormatter + + def __init__( + self, + command: "Command", + parent: t.Optional["Context"] = None, + info_name: t.Optional[str] = None, + obj: t.Optional[t.Any] = None, + auto_envvar_prefix: t.Optional[str] = None, + default_map: t.Optional[t.MutableMapping[str, t.Any]] = None, + terminal_width: t.Optional[int] = None, + max_content_width: t.Optional[int] = None, + resilient_parsing: bool = False, + allow_extra_args: t.Optional[bool] = None, + allow_interspersed_args: t.Optional[bool] = None, + ignore_unknown_options: t.Optional[bool] = None, + help_option_names: t.Optional[t.List[str]] = None, + token_normalize_func: t.Optional[t.Callable[[str], str]] = None, + color: t.Optional[bool] = None, + show_default: t.Optional[bool] = None, + ) -> None: + #: the parent context or `None` if none exists. + self.parent = parent + #: the :class:`Command` for this context. + self.command = command + #: the descriptive information name + self.info_name = info_name + #: Map of parameter names to their parsed values. Parameters + #: with ``expose_value=False`` are not stored. + self.params: t.Dict[str, t.Any] = {} + #: the leftover arguments. + self.args: t.List[str] = [] + #: protected arguments. These are arguments that are prepended + #: to `args` when certain parsing scenarios are encountered but + #: must be never propagated to another arguments. This is used + #: to implement nested parsing. + self.protected_args: t.List[str] = [] + #: the collected prefixes of the command's options. + self._opt_prefixes: t.Set[str] = set(parent._opt_prefixes) if parent else set() + + if obj is None and parent is not None: + obj = parent.obj + + #: the user object stored. + self.obj: t.Any = obj + self._meta: t.Dict[str, t.Any] = getattr(parent, "meta", {}) + + #: A dictionary (-like object) with defaults for parameters. + if ( + default_map is None + and info_name is not None + and parent is not None + and parent.default_map is not None + ): + default_map = parent.default_map.get(info_name) + + self.default_map: t.Optional[t.MutableMapping[str, t.Any]] = default_map + + #: This flag indicates if a subcommand is going to be executed. A + #: group callback can use this information to figure out if it's + #: being executed directly or because the execution flow passes + #: onwards to a subcommand. By default it's None, but it can be + #: the name of the subcommand to execute. + #: + #: If chaining is enabled this will be set to ``'*'`` in case + #: any commands are executed. It is however not possible to + #: figure out which ones. If you require this knowledge you + #: should use a :func:`result_callback`. + self.invoked_subcommand: t.Optional[str] = None + + if terminal_width is None and parent is not None: + terminal_width = parent.terminal_width + + #: The width of the terminal (None is autodetection). + self.terminal_width: t.Optional[int] = terminal_width + + if max_content_width is None and parent is not None: + max_content_width = parent.max_content_width + + #: The maximum width of formatted content (None implies a sensible + #: default which is 80 for most things). + self.max_content_width: t.Optional[int] = max_content_width + + if allow_extra_args is None: + allow_extra_args = command.allow_extra_args + + #: Indicates if the context allows extra args or if it should + #: fail on parsing. + #: + #: .. versionadded:: 3.0 + self.allow_extra_args = allow_extra_args + + if allow_interspersed_args is None: + allow_interspersed_args = command.allow_interspersed_args + + #: Indicates if the context allows mixing of arguments and + #: options or not. + #: + #: .. versionadded:: 3.0 + self.allow_interspersed_args: bool = allow_interspersed_args + + if ignore_unknown_options is None: + ignore_unknown_options = command.ignore_unknown_options + + #: Instructs click to ignore options that a command does not + #: understand and will store it on the context for later + #: processing. This is primarily useful for situations where you + #: want to call into external programs. Generally this pattern is + #: strongly discouraged because it's not possibly to losslessly + #: forward all arguments. + #: + #: .. versionadded:: 4.0 + self.ignore_unknown_options: bool = ignore_unknown_options + + if help_option_names is None: + if parent is not None: + help_option_names = parent.help_option_names + else: + help_option_names = ["--help"] + + #: The names for the help options. + self.help_option_names: t.List[str] = help_option_names + + if token_normalize_func is None and parent is not None: + token_normalize_func = parent.token_normalize_func + + #: An optional normalization function for tokens. This is + #: options, choices, commands etc. + self.token_normalize_func: t.Optional[t.Callable[[str], str]] = ( + token_normalize_func + ) + + #: Indicates if resilient parsing is enabled. In that case Click + #: will do its best to not cause any failures and default values + #: will be ignored. Useful for completion. + self.resilient_parsing: bool = resilient_parsing + + # If there is no envvar prefix yet, but the parent has one and + # the command on this level has a name, we can expand the envvar + # prefix automatically. + if auto_envvar_prefix is None: + if ( + parent is not None + and parent.auto_envvar_prefix is not None + and self.info_name is not None + ): + auto_envvar_prefix = ( + f"{parent.auto_envvar_prefix}_{self.info_name.upper()}" + ) + else: + auto_envvar_prefix = auto_envvar_prefix.upper() + + if auto_envvar_prefix is not None: + auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") + + self.auto_envvar_prefix: t.Optional[str] = auto_envvar_prefix + + if color is None and parent is not None: + color = parent.color + + #: Controls if styling output is wanted or not. + self.color: t.Optional[bool] = color + + if show_default is None and parent is not None: + show_default = parent.show_default + + #: Show option default values when formatting help text. + self.show_default: t.Optional[bool] = show_default + + self._close_callbacks: t.List[t.Callable[[], t.Any]] = [] + self._depth = 0 + self._parameter_source: t.Dict[str, ParameterSource] = {} + self._exit_stack = ExitStack() + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire CLI + structure. + + .. code-block:: python + + with Context(cli) as ctx: + info = ctx.to_info_dict() + + .. versionadded:: 8.0 + """ + return { + "command": self.command.to_info_dict(self), + "info_name": self.info_name, + "allow_extra_args": self.allow_extra_args, + "allow_interspersed_args": self.allow_interspersed_args, + "ignore_unknown_options": self.ignore_unknown_options, + "auto_envvar_prefix": self.auto_envvar_prefix, + } + + def __enter__(self) -> "Context": + self._depth += 1 + push_context(self) + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + self._depth -= 1 + if self._depth == 0: + self.close() + pop_context() + + @contextmanager + def scope(self, cleanup: bool = True) -> t.Iterator["Context"]: + """This helper method can be used with the context object to promote + it to the current thread local (see :func:`get_current_context`). + The default behavior of this is to invoke the cleanup functions which + can be disabled by setting `cleanup` to `False`. The cleanup + functions are typically used for things such as closing file handles. + + If the cleanup is intended the context object can also be directly + used as a context manager. + + Example usage:: + + with ctx.scope(): + assert get_current_context() is ctx + + This is equivalent:: + + with ctx: + assert get_current_context() is ctx + + .. versionadded:: 5.0 + + :param cleanup: controls if the cleanup functions should be run or + not. The default is to run these functions. In + some situations the context only wants to be + temporarily pushed in which case this can be disabled. + Nested pushes automatically defer the cleanup. + """ + if not cleanup: + self._depth += 1 + try: + with self as rv: + yield rv + finally: + if not cleanup: + self._depth -= 1 + + @property + def meta(self) -> t.Dict[str, t.Any]: + """This is a dictionary which is shared with all the contexts + that are nested. It exists so that click utilities can store some + state here if they need to. It is however the responsibility of + that code to manage this dictionary well. + + The keys are supposed to be unique dotted strings. For instance + module paths are a good choice for it. What is stored in there is + irrelevant for the operation of click. However what is important is + that code that places data here adheres to the general semantics of + the system. + + Example usage:: + + LANG_KEY = f'{__name__}.lang' + + def set_language(value): + ctx = get_current_context() + ctx.meta[LANG_KEY] = value + + def get_language(): + return get_current_context().meta.get(LANG_KEY, 'en_US') + + .. versionadded:: 5.0 + """ + return self._meta + + def make_formatter(self) -> HelpFormatter: + """Creates the :class:`~click.HelpFormatter` for the help and + usage output. + + To quickly customize the formatter class used without overriding + this method, set the :attr:`formatter_class` attribute. + + .. versionchanged:: 8.0 + Added the :attr:`formatter_class` attribute. + """ + return self.formatter_class( + width=self.terminal_width, max_width=self.max_content_width + ) + + def with_resource(self, context_manager: t.ContextManager[V]) -> V: + """Register a resource as if it were used in a ``with`` + statement. The resource will be cleaned up when the context is + popped. + + Uses :meth:`contextlib.ExitStack.enter_context`. It calls the + resource's ``__enter__()`` method and returns the result. When + the context is popped, it closes the stack, which calls the + resource's ``__exit__()`` method. + + To register a cleanup function for something that isn't a + context manager, use :meth:`call_on_close`. Or use something + from :mod:`contextlib` to turn it into a context manager first. + + .. code-block:: python + + @click.group() + @click.option("--name") + @click.pass_context + def cli(ctx): + ctx.obj = ctx.with_resource(connect_db(name)) + + :param context_manager: The context manager to enter. + :return: Whatever ``context_manager.__enter__()`` returns. + + .. versionadded:: 8.0 + """ + return self._exit_stack.enter_context(context_manager) + + def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: + """Register a function to be called when the context tears down. + + This can be used to close resources opened during the script + execution. Resources that support Python's context manager + protocol which would be used in a ``with`` statement should be + registered with :meth:`with_resource` instead. + + :param f: The function to execute on teardown. + """ + return self._exit_stack.callback(f) + + def close(self) -> None: + """Invoke all close callbacks registered with + :meth:`call_on_close`, and exit all context managers entered + with :meth:`with_resource`. + """ + self._exit_stack.close() + # In case the context is reused, create a new exit stack. + self._exit_stack = ExitStack() + + @property + def command_path(self) -> str: + """The computed command path. This is used for the ``usage`` + information on the help page. It's automatically created by + combining the info names of the chain of contexts to the root. + """ + rv = "" + if self.info_name is not None: + rv = self.info_name + if self.parent is not None: + parent_command_path = [self.parent.command_path] + + if isinstance(self.parent.command, Command): + for param in self.parent.command.get_params(self): + parent_command_path.extend(param.get_usage_pieces(self)) + + rv = f"{' '.join(parent_command_path)} {rv}" + return rv.lstrip() + + def find_root(self) -> "Context": + """Finds the outermost context.""" + node = self + while node.parent is not None: + node = node.parent + return node + + def find_object(self, object_type: t.Type[V]) -> t.Optional[V]: + """Finds the closest object of a given type.""" + node: t.Optional[Context] = self + + while node is not None: + if isinstance(node.obj, object_type): + return node.obj + + node = node.parent + + return None + + def ensure_object(self, object_type: t.Type[V]) -> V: + """Like :meth:`find_object` but sets the innermost object to a + new instance of `object_type` if it does not exist. + """ + rv = self.find_object(object_type) + if rv is None: + self.obj = rv = object_type() + return rv + + @t.overload + def lookup_default( + self, name: str, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: ... + + @t.overload + def lookup_default( + self, name: str, call: "te.Literal[False]" = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: ... + + def lookup_default(self, name: str, call: bool = True) -> t.Optional[t.Any]: + """Get the default for a parameter from :attr:`default_map`. + + :param name: Name of the parameter. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + if self.default_map is not None: + value = self.default_map.get(name) + + if call and callable(value): + return value() + + return value + + return None + + def fail(self, message: str) -> "te.NoReturn": + """Aborts the execution of the program with a specific error + message. + + :param message: the error message to fail with. + """ + raise UsageError(message, self) + + def abort(self) -> "te.NoReturn": + """Aborts the script.""" + raise Abort() + + def exit(self, code: int = 0) -> "te.NoReturn": + """Exits the application with a given exit code.""" + raise Exit(code) + + def get_usage(self) -> str: + """Helper method to get formatted usage string for the current + context and command. + """ + return self.command.get_usage(self) + + def get_help(self) -> str: + """Helper method to get formatted help page for the current + context and command. + """ + return self.command.get_help(self) + + def _make_sub_context(self, command: "Command") -> "Context": + """Create a new context of the same type as this context, but + for a new command. + + :meta private: + """ + return type(self)(command, info_name=command.name, parent=self) + + @t.overload + def invoke( + __self, + __callback: "t.Callable[..., V]", + *args: t.Any, + **kwargs: t.Any, + ) -> V: ... + + @t.overload + def invoke( + __self, + __callback: "Command", + *args: t.Any, + **kwargs: t.Any, + ) -> t.Any: ... + + def invoke( + __self, + __callback: t.Union["Command", "t.Callable[..., V]"], + *args: t.Any, + **kwargs: t.Any, + ) -> t.Union[t.Any, V]: + """Invokes a command callback in exactly the way it expects. There + are two ways to invoke this method: + + 1. the first argument can be a callback and all other arguments and + keyword arguments are forwarded directly to the function. + 2. the first argument is a click command object. In that case all + arguments are forwarded as well but proper click parameters + (options and click arguments) must be keyword arguments and Click + will fill in defaults. + + Note that before Click 3.2 keyword arguments were not properly filled + in against the intention of this code and no context was created. For + more information about this change and why it was done in a bugfix + release see :ref:`upgrade-to-3.2`. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if :meth:`forward` is called at multiple levels. + """ + if isinstance(__callback, Command): + other_cmd = __callback + + if other_cmd.callback is None: + raise TypeError( + "The given command does not have a callback that can be invoked." + ) + else: + __callback = t.cast("t.Callable[..., V]", other_cmd.callback) + + ctx = __self._make_sub_context(other_cmd) + + for param in other_cmd.params: + if param.name not in kwargs and param.expose_value: + kwargs[param.name] = param.type_cast_value( # type: ignore + ctx, param.get_default(ctx) + ) + + # Track all kwargs as params, so that forward() will pass + # them on in subsequent calls. + ctx.params.update(kwargs) + else: + ctx = __self + + with augment_usage_errors(__self): + with ctx: + return __callback(*args, **kwargs) + + def forward(__self, __cmd: "Command", *args: t.Any, **kwargs: t.Any) -> t.Any: + """Similar to :meth:`invoke` but fills in default keyword + arguments from the current context if the other command expects + it. This cannot invoke callbacks directly, only other commands. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if ``forward`` is called at multiple levels. + """ + # Can only forward to other commands, not direct callbacks. + if not isinstance(__cmd, Command): + raise TypeError("Callback is not a command.") + + for param in __self.params: + if param not in kwargs: + kwargs[param] = __self.params[param] + + return __self.invoke(__cmd, *args, **kwargs) + + def set_parameter_source(self, name: str, source: ParameterSource) -> None: + """Set the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + :param name: The name of the parameter. + :param source: A member of :class:`~click.core.ParameterSource`. + """ + self._parameter_source[name] = source + + def get_parameter_source(self, name: str) -> t.Optional[ParameterSource]: + """Get the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + This can be useful for determining when a user specified a value + on the command line that is the same as the default value. It + will be :attr:`~click.core.ParameterSource.DEFAULT` only if the + value was actually taken from the default. + + :param name: The name of the parameter. + :rtype: ParameterSource + + .. versionchanged:: 8.0 + Returns ``None`` if the parameter was not provided from any + source. + """ + return self._parameter_source.get(name) + + +class BaseCommand: + """The base command implements the minimal API contract of commands. + Most code will never use this as it does not implement a lot of useful + functionality but it can act as the direct subclass of alternative + parsing methods that do not depend on the Click parser. + + For instance, this can be used to bridge Click and other systems like + argparse or docopt. + + Because base commands do not implement a lot of the API that other + parts of Click take for granted, they are not supported for all + operations. For instance, they cannot be used with the decorators + usually and they have no built-in callback system. + + .. versionchanged:: 2.0 + Added the `context_settings` parameter. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + """ + + #: The context class to create with :meth:`make_context`. + #: + #: .. versionadded:: 8.0 + context_class: t.Type[Context] = Context + #: the default for the :attr:`Context.allow_extra_args` flag. + allow_extra_args = False + #: the default for the :attr:`Context.allow_interspersed_args` flag. + allow_interspersed_args = True + #: the default for the :attr:`Context.ignore_unknown_options` flag. + ignore_unknown_options = False + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None, + ) -> None: + #: the name the command thinks it has. Upon registering a command + #: on a :class:`Group` the group will default the command name + #: with this information. You should instead use the + #: :class:`Context`\'s :attr:`~Context.info_name` attribute. + self.name = name + + if context_settings is None: + context_settings = {} + + #: an optional dictionary with defaults passed to the context. + self.context_settings: t.MutableMapping[str, t.Any] = context_settings + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire structure + below this command. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + :param ctx: A :class:`Context` representing this command. + + .. versionadded:: 8.0 + """ + return {"name": self.name} + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def get_usage(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get usage") + + def get_help(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get help") + + def make_context( + self, + info_name: t.Optional[str], + args: t.List[str], + parent: t.Optional[Context] = None, + **extra: t.Any, + ) -> Context: + """This function when given an info name and arguments will kick + off the parsing and create a new :class:`Context`. It does not + invoke the actual command callback though. + + To quickly customize the context class used without overriding + this method, set the :attr:`context_class` attribute. + + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it's usually + the name of the script, for commands below it's + the name of the command. + :param args: the arguments to parse as list of strings. + :param parent: the parent context if available. + :param extra: extra keyword arguments forwarded to the context + constructor. + + .. versionchanged:: 8.0 + Added the :attr:`context_class` attribute. + """ + for key, value in self.context_settings.items(): + if key not in extra: + extra[key] = value + + ctx = self.context_class( + self, # type: ignore[arg-type] + info_name=info_name, + parent=parent, + **extra, + ) + + with ctx.scope(cleanup=False): + self.parse_args(ctx, args) + return ctx + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + """Given a context and a list of arguments this creates the parser + and parses the arguments, then modifies the context as necessary. + This is automatically invoked by :meth:`make_context`. + """ + raise NotImplementedError("Base commands do not know how to parse arguments.") + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the command. The default + implementation is raising a not implemented error. + """ + raise NotImplementedError("Base commands are not invocable by default") + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of chained multi-commands. + + Any command could be part of a chained multi-command, so sibling + commands are valid at any point during command completion. Other + command classes will return more completions. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: t.List[CompletionItem] = [] + + while ctx.parent is not None: + ctx = ctx.parent + + if isinstance(ctx.command, MultiCommand) and ctx.command.chain: + results.extend( + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + if name not in ctx.protected_args + ) + + return results + + @t.overload + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: "te.Literal[True]" = True, + **extra: t.Any, + ) -> "te.NoReturn": ... + + @t.overload + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: bool = ..., + **extra: t.Any, + ) -> t.Any: ... + + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: bool = True, + windows_expand_args: bool = True, + **extra: t.Any, + ) -> t.Any: + """This is the way to invoke a script with all the bells and + whistles as a command line application. This will always terminate + the application after a call. If this is not wanted, ``SystemExit`` + needs to be caught. + + This method is also available by directly calling the instance of + a :class:`Command`. + + :param args: the arguments that should be used for parsing. If not + provided, ``sys.argv[1:]`` is used. + :param prog_name: the program name that should be used. By default + the program name is constructed by taking the file + name from ``sys.argv[0]``. + :param complete_var: the environment variable that controls the + bash completion support. The default is + ``"__COMPLETE"`` with prog_name in + uppercase. + :param standalone_mode: the default behavior is to invoke the script + in standalone mode. Click will then + handle exceptions and convert them into + error messages and the function will never + return but shut down the interpreter. If + this is set to `False` they will be + propagated to the caller and the return + value of this function is the return value + of :meth:`invoke`. + :param windows_expand_args: Expand glob patterns, user dir, and + env vars in command line args on Windows. + :param extra: extra keyword arguments are forwarded to the context + constructor. See :class:`Context` for more information. + + .. versionchanged:: 8.0.1 + Added the ``windows_expand_args`` parameter to allow + disabling command line arg expansion on Windows. + + .. versionchanged:: 8.0 + When taking arguments from ``sys.argv`` on Windows, glob + patterns, user dir, and env vars are expanded. + + .. versionchanged:: 3.0 + Added the ``standalone_mode`` parameter. + """ + if args is None: + args = sys.argv[1:] + + if os.name == "nt" and windows_expand_args: + args = _expand_args(args) + else: + args = list(args) + + if prog_name is None: + prog_name = _detect_program_name() + + # Process shell completion requests and exit early. + self._main_shell_completion(extra, prog_name, complete_var) + + try: + try: + with self.make_context(prog_name, args, **extra) as ctx: + rv = self.invoke(ctx) + if not standalone_mode: + return rv + # it's not safe to `ctx.exit(rv)` here! + # note that `rv` may actually contain data like "1" which + # has obvious effects + # more subtle case: `rv=[None, None]` can come out of + # chained commands which all returned `None` -- so it's not + # even always obvious that `rv` indicates success/failure + # by its truthiness/falsiness + ctx.exit() + except (EOFError, KeyboardInterrupt) as e: + echo(file=sys.stderr) + raise Abort() from e + except ClickException as e: + if not standalone_mode: + raise + e.show() + sys.exit(e.exit_code) + except OSError as e: + if e.errno == errno.EPIPE: + sys.stdout = t.cast(t.TextIO, PacifyFlushWrapper(sys.stdout)) + sys.stderr = t.cast(t.TextIO, PacifyFlushWrapper(sys.stderr)) + sys.exit(1) + else: + raise + except Exit as e: + if standalone_mode: + sys.exit(e.exit_code) + else: + # in non-standalone mode, return the exit code + # note that this is only reached if `self.invoke` above raises + # an Exit explicitly -- thus bypassing the check there which + # would return its result + # the results of non-standalone execution may therefore be + # somewhat ambiguous: if there are codepaths which lead to + # `ctx.exit(1)` and to `return 1`, the caller won't be able to + # tell the difference between the two + return e.exit_code + except Abort: + if not standalone_mode: + raise + echo(_("Aborted!"), file=sys.stderr) + sys.exit(1) + + def _main_shell_completion( + self, + ctx_args: t.MutableMapping[str, t.Any], + prog_name: str, + complete_var: t.Optional[str] = None, + ) -> None: + """Check if the shell is asking for tab completion, process + that, then exit early. Called from :meth:`main` before the + program is invoked. + + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. Defaults to + ``_{PROG_NAME}_COMPLETE``. + + .. versionchanged:: 8.2.0 + Dots (``.``) in ``prog_name`` are replaced with underscores (``_``). + """ + if complete_var is None: + complete_name = prog_name.replace("-", "_").replace(".", "_") + complete_var = f"_{complete_name}_COMPLETE".upper() + + instruction = os.environ.get(complete_var) + + if not instruction: + return + + from .shell_completion import shell_complete + + rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction) + sys.exit(rv) + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: + """Alias for :meth:`main`.""" + return self.main(*args, **kwargs) + + +class Command(BaseCommand): + """Commands are the basic building block of command line interfaces in + Click. A basic command handles command line parsing and might dispatch + more parsing to commands nested below it. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + :param callback: the callback to invoke. This is optional. + :param params: the parameters to register with this command. This can + be either :class:`Option` or :class:`Argument` objects. + :param help: the help string to use for this command. + :param epilog: like the help string but it's printed at the end of the + help page after everything else. + :param short_help: the short help to use for this command. This is + shown on the command listing of the parent command. + :param add_help_option: by default each command registers a ``--help`` + option. This can be disabled by this parameter. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is disabled by default. + If enabled this will add ``--help`` as argument + if no arguments are passed + :param hidden: hide this command from help outputs. + + :param deprecated: issues a message indicating that + the command is deprecated. + + .. versionchanged:: 8.1 + ``help``, ``epilog``, and ``short_help`` are stored unprocessed, + all formatting is done when outputting help text, not at init, + and is done even if not using the ``@command`` decorator. + + .. versionchanged:: 8.0 + Added a ``repr`` showing the command name. + + .. versionchanged:: 7.1 + Added the ``no_args_is_help`` parameter. + + .. versionchanged:: 2.0 + Added the ``context_settings`` parameter. + """ + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None, + callback: t.Optional[t.Callable[..., t.Any]] = None, + params: t.Optional[t.List["Parameter"]] = None, + help: t.Optional[str] = None, + epilog: t.Optional[str] = None, + short_help: t.Optional[str] = None, + options_metavar: t.Optional[str] = "[OPTIONS]", + add_help_option: bool = True, + no_args_is_help: bool = False, + hidden: bool = False, + deprecated: bool = False, + ) -> None: + super().__init__(name, context_settings) + #: the callback to execute when the command fires. This might be + #: `None` in which case nothing happens. + self.callback = callback + #: the list of parameters for this command in the order they + #: should show up in the help page and execute. Eager parameters + #: will automatically be handled before non eager ones. + self.params: t.List[Parameter] = params or [] + self.help = help + self.epilog = epilog + self.options_metavar = options_metavar + self.short_help = short_help + self.add_help_option = add_help_option + self._help_option: t.Optional[HelpOption] = None + self.no_args_is_help = no_args_is_help + self.hidden = hidden + self.deprecated = deprecated + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + info_dict.update( + params=[param.to_info_dict() for param in self.get_params(ctx)], + help=self.help, + epilog=self.epilog, + short_help=self.short_help, + hidden=self.hidden, + deprecated=self.deprecated, + ) + return info_dict + + def get_usage(self, ctx: Context) -> str: + """Formats the usage line into a string and returns it. + + Calls :meth:`format_usage` internally. + """ + formatter = ctx.make_formatter() + self.format_usage(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_params(self, ctx: Context) -> t.List["Parameter"]: + rv = self.params + help_option = self.get_help_option(ctx) + + if help_option is not None: + rv = [*rv, help_option] + + return rv + + def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the usage line into the formatter. + + This is a low-level method called by :meth:`get_usage`. + """ + pieces = self.collect_usage_pieces(ctx) + formatter.write_usage(ctx.command_path, " ".join(pieces)) + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + """Returns all the pieces that go into the usage line and returns + it as a list of strings. + """ + rv = [self.options_metavar] if self.options_metavar else [] + + for param in self.get_params(ctx): + rv.extend(param.get_usage_pieces(ctx)) + + return rv + + def get_help_option_names(self, ctx: Context) -> t.List[str]: + """Returns the names for the help option.""" + all_names = set(ctx.help_option_names) + for param in self.params: + all_names.difference_update(param.opts) + all_names.difference_update(param.secondary_opts) + return list(all_names) + + def get_help_option(self, ctx: Context) -> t.Optional["Option"]: + """Returns the help option object. + + Unless ``add_help_option`` is ``False``. + + .. versionchanged:: 8.1.8 + The help option is now cached to avoid creating it multiple times. + """ + help_options = self.get_help_option_names(ctx) + + if not help_options or not self.add_help_option: + return None + + # Cache the help option object in private _help_option attribute to + # avoid creating it multiple times. Not doing this will break the + # callback odering by iter_params_for_processing(), which relies on + # object comparison. + if self._help_option is None: + # Avoid circular import. + from .decorators import HelpOption + + self._help_option = HelpOption(help_options) + + return self._help_option + + def make_parser(self, ctx: Context) -> OptionParser: + """Creates the underlying option parser for this command.""" + parser = OptionParser(ctx) + for param in self.get_params(ctx): + param.add_to_parser(parser, ctx) + return parser + + def get_help(self, ctx: Context) -> str: + """Formats the help into a string and returns it. + + Calls :meth:`format_help` internally. + """ + formatter = ctx.make_formatter() + self.format_help(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_short_help_str(self, limit: int = 45) -> str: + """Gets short help for the command or makes it by shortening the + long help string. + """ + if self.short_help: + text = inspect.cleandoc(self.short_help) + elif self.help: + text = make_default_short_help(self.help, limit) + else: + text = "" + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + return text.strip() + + def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help into the formatter if it exists. + + This is a low-level method called by :meth:`get_help`. + + This calls the following methods: + + - :meth:`format_usage` + - :meth:`format_help_text` + - :meth:`format_options` + - :meth:`format_epilog` + """ + self.format_usage(ctx, formatter) + self.format_help_text(ctx, formatter) + self.format_options(ctx, formatter) + self.format_epilog(ctx, formatter) + + def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help text to the formatter if it exists.""" + if self.help is not None: + # truncate the help text to the first form feed + text = inspect.cleandoc(self.help).partition("\f")[0] + else: + text = "" + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + if text: + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(text) + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes all the options into the formatter if they exist.""" + opts = [] + for param in self.get_params(ctx): + rv = param.get_help_record(ctx) + if rv is not None: + opts.append(rv) + + if opts: + with formatter.section(_("Options")): + formatter.write_dl(opts) + + def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the epilog into the formatter if it exists.""" + if self.epilog: + epilog = inspect.cleandoc(self.epilog) + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(epilog) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + parser = self.make_parser(ctx) + opts, args, param_order = parser.parse_args(args=args) + + for param in iter_params_for_processing(param_order, self.get_params(ctx)): + value, args = param.handle_parse_result(ctx, opts, args) + + if args and not ctx.allow_extra_args and not ctx.resilient_parsing: + ctx.fail( + ngettext( + "Got unexpected extra argument ({args})", + "Got unexpected extra arguments ({args})", + len(args), + ).format(args=" ".join(map(str, args))) + ) + + ctx.args = args + ctx._opt_prefixes.update(parser._opt_prefixes) + return args + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the attached callback (if it exists) + in the right way. + """ + if self.deprecated: + message = _( + "DeprecationWarning: The command {name!r} is deprecated." + ).format(name=self.name) + echo(style(message, fg="red"), err=True) + + if self.callback is not None: + return ctx.invoke(self.callback, **ctx.params) + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options and chained multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: t.List[CompletionItem] = [] + + if incomplete and not incomplete[0].isalnum(): + for param in self.get_params(ctx): + if ( + not isinstance(param, Option) + or param.hidden + or ( + not param.multiple + and ctx.get_parameter_source(param.name) # type: ignore + is ParameterSource.COMMANDLINE + ) + ): + continue + + results.extend( + CompletionItem(name, help=param.help) + for name in [*param.opts, *param.secondary_opts] + if name.startswith(incomplete) + ) + + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class MultiCommand(Command): + """A multi command is the basic implementation of a command that + dispatches to subcommands. The most common version is the + :class:`Group`. + + :param invoke_without_command: this controls how the multi command itself + is invoked. By default it's only invoked + if a subcommand is provided. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is enabled by default if + `invoke_without_command` is disabled or disabled + if it's enabled. If enabled this will add + ``--help`` as argument if no arguments are + passed. + :param subcommand_metavar: the string that is used in the documentation + to indicate the subcommand place. + :param chain: if this is set to `True` chaining of multiple subcommands + is enabled. This restricts the form of commands in that + they cannot have optional arguments but it allows + multiple commands to be chained together. + :param result_callback: The result callback to attach to this multi + command. This can be set or changed later with the + :meth:`result_callback` decorator. + :param attrs: Other command arguments described in :class:`Command`. + """ + + allow_extra_args = True + allow_interspersed_args = False + + def __init__( + self, + name: t.Optional[str] = None, + invoke_without_command: bool = False, + no_args_is_help: t.Optional[bool] = None, + subcommand_metavar: t.Optional[str] = None, + chain: bool = False, + result_callback: t.Optional[t.Callable[..., t.Any]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if no_args_is_help is None: + no_args_is_help = not invoke_without_command + + self.no_args_is_help = no_args_is_help + self.invoke_without_command = invoke_without_command + + if subcommand_metavar is None: + if chain: + subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." + else: + subcommand_metavar = "COMMAND [ARGS]..." + + self.subcommand_metavar = subcommand_metavar + self.chain = chain + # The result callback that is stored. This can be set or + # overridden with the :func:`result_callback` decorator. + self._result_callback = result_callback + + if self.chain: + for param in self.params: + if isinstance(param, Argument) and not param.required: + raise RuntimeError( + "Multi commands in chain mode cannot have" + " optional arguments." + ) + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + commands = {} + + for name in self.list_commands(ctx): + command = self.get_command(ctx, name) + + if command is None: + continue + + sub_ctx = ctx._make_sub_context(command) + + with sub_ctx.scope(cleanup=False): + commands[name] = command.to_info_dict(sub_ctx) + + info_dict.update(commands=commands, chain=self.chain) + return info_dict + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + rv = super().collect_usage_pieces(ctx) + rv.append(self.subcommand_metavar) + return rv + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + super().format_options(ctx, formatter) + self.format_commands(ctx, formatter) + + def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: + """Adds a result callback to the command. By default if a + result callback is already registered this will chain them but + this can be disabled with the `replace` parameter. The result + callback is invoked with the return value of the subcommand + (or the list of return values from all subcommands if chaining + is enabled) as well as the parameters as they would be passed + to the main callback. + + Example:: + + @click.group() + @click.option('-i', '--input', default=23) + def cli(input): + return 42 + + @cli.result_callback() + def process_result(result, input): + return result + input + + :param replace: if set to `True` an already existing result + callback will be removed. + + .. versionchanged:: 8.0 + Renamed from ``resultcallback``. + + .. versionadded:: 3.0 + """ + + def decorator(f: F) -> F: + old_callback = self._result_callback + + if old_callback is None or replace: + self._result_callback = f + return f + + def function(__value, *args, **kwargs): # type: ignore + inner = old_callback(__value, *args, **kwargs) + return f(inner, *args, **kwargs) + + self._result_callback = rv = update_wrapper(t.cast(F, function), f) + return rv # type: ignore[return-value] + + return decorator + + def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: + """Extra format methods for multi methods that adds all the commands + after the options. + """ + commands = [] + for subcommand in self.list_commands(ctx): + cmd = self.get_command(ctx, subcommand) + # What is this, the tool lied about a command. Ignore it + if cmd is None: + continue + if cmd.hidden: + continue + + commands.append((subcommand, cmd)) + + # allow for 3 times the default spacing + if len(commands): + limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) + + rows = [] + for subcommand, cmd in commands: + help = cmd.get_short_help_str(limit) + rows.append((subcommand, help)) + + if rows: + with formatter.section(_("Commands")): + formatter.write_dl(rows) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + rest = super().parse_args(ctx, args) + + if self.chain: + ctx.protected_args = rest + ctx.args = [] + elif rest: + ctx.protected_args, ctx.args = rest[:1], rest[1:] + + return ctx.args + + def invoke(self, ctx: Context) -> t.Any: + def _process_result(value: t.Any) -> t.Any: + if self._result_callback is not None: + value = ctx.invoke(self._result_callback, value, **ctx.params) + return value + + if not ctx.protected_args: + if self.invoke_without_command: + # No subcommand was invoked, so the result callback is + # invoked with the group return value for regular + # groups, or an empty list for chained groups. + with ctx: + rv = super().invoke(ctx) + return _process_result([] if self.chain else rv) + ctx.fail(_("Missing command.")) + + # Fetch args back out + args = [*ctx.protected_args, *ctx.args] + ctx.args = [] + ctx.protected_args = [] + + # If we're not in chain mode, we only allow the invocation of a + # single command but we also inform the current context about the + # name of the command to invoke. + if not self.chain: + # Make sure the context is entered so we do not clean up + # resources until the result processor has worked. + with ctx: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + ctx.invoked_subcommand = cmd_name + super().invoke(ctx) + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) + with sub_ctx: + return _process_result(sub_ctx.command.invoke(sub_ctx)) + + # In chain mode we create the contexts step by step, but after the + # base command has been invoked. Because at that point we do not + # know the subcommands yet, the invoked subcommand attribute is + # set to ``*`` to inform the command that subcommands are executed + # but nothing else. + with ctx: + ctx.invoked_subcommand = "*" if args else None + super().invoke(ctx) + + # Otherwise we make every single context and invoke them in a + # chain. In that case the return value to the result processor + # is the list of all invoked subcommand's results. + contexts = [] + while args: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + sub_ctx = cmd.make_context( + cmd_name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + ) + contexts.append(sub_ctx) + args, sub_ctx.args = sub_ctx.args, [] + + rv = [] + for sub_ctx in contexts: + with sub_ctx: + rv.append(sub_ctx.command.invoke(sub_ctx)) + return _process_result(rv) + + def resolve_command( + self, ctx: Context, args: t.List[str] + ) -> t.Tuple[t.Optional[str], t.Optional[Command], t.List[str]]: + cmd_name = make_str(args[0]) + original_cmd_name = cmd_name + + # Get the command + cmd = self.get_command(ctx, cmd_name) + + # If we can't find the command but there is a normalization + # function available, we try with that one. + if cmd is None and ctx.token_normalize_func is not None: + cmd_name = ctx.token_normalize_func(cmd_name) + cmd = self.get_command(ctx, cmd_name) + + # If we don't find the command we want to show an error message + # to the user that it was not provided. However, there is + # something else we should do: if the first argument looks like + # an option we want to kick off parsing again for arguments to + # resolve things like --help which now should go to the main + # place. + if cmd is None and not ctx.resilient_parsing: + if split_opt(cmd_name)[0]: + self.parse_args(ctx, ctx.args) + ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) + return cmd_name if cmd else None, cmd, args[1:] + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + """Given a context and a command name, this returns a + :class:`Command` object if it exists or returns `None`. + """ + raise NotImplementedError + + def list_commands(self, ctx: Context) -> t.List[str]: + """Returns a list of subcommand names in the order they should + appear. + """ + return [] + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options, subcommands, and chained + multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results = [ + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + ] + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class Group(MultiCommand): + """A group allows a command to have subcommands attached. This is + the most common way to implement nesting in Click. + + :param name: The name of the group command. + :param commands: A dict mapping names to :class:`Command` objects. + Can also be a list of :class:`Command`, which will use + :attr:`Command.name` to create the dict. + :param attrs: Other command arguments described in + :class:`MultiCommand`, :class:`Command`, and + :class:`BaseCommand`. + + .. versionchanged:: 8.0 + The ``commands`` argument can be a list of command objects. + """ + + #: If set, this is used by the group's :meth:`command` decorator + #: as the default :class:`Command` class. This is useful to make all + #: subcommands use a custom command class. + #: + #: .. versionadded:: 8.0 + command_class: t.Optional[t.Type[Command]] = None + + #: If set, this is used by the group's :meth:`group` decorator + #: as the default :class:`Group` class. This is useful to make all + #: subgroups use a custom group class. + #: + #: If set to the special value :class:`type` (literally + #: ``group_class = type``), this group's class will be used as the + #: default class. This makes a custom group class continue to make + #: custom groups. + #: + #: .. versionadded:: 8.0 + group_class: t.Optional[t.Union[t.Type["Group"], t.Type[type]]] = None + # Literal[type] isn't valid, so use Type[type] + + def __init__( + self, + name: t.Optional[str] = None, + commands: t.Optional[ + t.Union[t.MutableMapping[str, Command], t.Sequence[Command]] + ] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if commands is None: + commands = {} + elif isinstance(commands, abc.Sequence): + commands = {c.name: c for c in commands if c.name is not None} + + #: The registered subcommands by their exported names. + self.commands: t.MutableMapping[str, Command] = commands + + def add_command(self, cmd: Command, name: t.Optional[str] = None) -> None: + """Registers another :class:`Command` with this group. If the name + is not provided, the name of the command is used. + """ + name = name or cmd.name + if name is None: + raise TypeError("Command has no name.") + _check_multicommand(self, name, cmd, register=True) + self.commands[name] = cmd + + @t.overload + def command(self, __func: t.Callable[..., t.Any]) -> Command: ... + + @t.overload + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command]: ... + + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], Command], Command]: + """A shortcut decorator for declaring and attaching a command to + the group. This takes the same arguments as :func:`command` and + immediately registers the created command with this group by + calling :meth:`add_command`. + + To customize the command class used, set the + :attr:`command_class` attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`command_class` attribute. + """ + from .decorators import command + + func: t.Optional[t.Callable[..., t.Any]] = None + + if args and callable(args[0]): + assert ( + len(args) == 1 and not kwargs + ), "Use 'command(**kwargs)(callable)' to provide arguments." + (func,) = args + args = () + + if self.command_class and kwargs.get("cls") is None: + kwargs["cls"] = self.command_class + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd: Command = command(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + @t.overload + def group(self, __func: t.Callable[..., t.Any]) -> "Group": ... + + @t.overload + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], "Group"]: ... + + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], "Group"], "Group"]: + """A shortcut decorator for declaring and attaching a group to + the group. This takes the same arguments as :func:`group` and + immediately registers the created group with this group by + calling :meth:`add_command`. + + To customize the group class used, set the :attr:`group_class` + attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`group_class` attribute. + """ + from .decorators import group + + func: t.Optional[t.Callable[..., t.Any]] = None + + if args and callable(args[0]): + assert ( + len(args) == 1 and not kwargs + ), "Use 'group(**kwargs)(callable)' to provide arguments." + (func,) = args + args = () + + if self.group_class is not None and kwargs.get("cls") is None: + if self.group_class is type: + kwargs["cls"] = type(self) + else: + kwargs["cls"] = self.group_class + + def decorator(f: t.Callable[..., t.Any]) -> "Group": + cmd: Group = group(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + return self.commands.get(cmd_name) + + def list_commands(self, ctx: Context) -> t.List[str]: + return sorted(self.commands) + + +class CommandCollection(MultiCommand): + """A command collection is a multi command that merges multiple multi + commands together into one. This is a straightforward implementation + that accepts a list of different multi commands as sources and + provides all the commands for each of them. + + See :class:`MultiCommand` and :class:`Command` for the description of + ``name`` and ``attrs``. + """ + + def __init__( + self, + name: t.Optional[str] = None, + sources: t.Optional[t.List[MultiCommand]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + #: The list of registered multi commands. + self.sources: t.List[MultiCommand] = sources or [] + + def add_source(self, multi_cmd: MultiCommand) -> None: + """Adds a new multi command to the chain dispatcher.""" + self.sources.append(multi_cmd) + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + for source in self.sources: + rv = source.get_command(ctx, cmd_name) + + if rv is not None: + if self.chain: + _check_multicommand(self, cmd_name, rv) + + return rv + + return None + + def list_commands(self, ctx: Context) -> t.List[str]: + rv: t.Set[str] = set() + + for source in self.sources: + rv.update(source.list_commands(ctx)) + + return sorted(rv) + + +def _check_iter(value: t.Any) -> t.Iterator[t.Any]: + """Check if the value is iterable but not a string. Raises a type + error, or return an iterator over the value. + """ + if isinstance(value, str): + raise TypeError + + return iter(value) + + +class Parameter: + r"""A parameter to a command comes in two versions: they are either + :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently + not supported by design as some of the internals for parsing are + intentionally not finalized. + + Some settings are supported by both options and arguments. + + :param param_decls: the parameter declarations for this option or + argument. This is a list of flags or argument + names. + :param type: the type that should be used. Either a :class:`ParamType` + or a Python type. The latter is converted into the former + automatically if supported. + :param required: controls if this is optional or not. + :param default: the default value if omitted. This can also be a callable, + in which case it's invoked when the default is needed + without any arguments. + :param callback: A function to further process or validate the value + after type conversion. It is called as ``f(ctx, param, value)`` + and must return the value. It is called for all sources, + including prompts. + :param nargs: the number of arguments to match. If not ``1`` the return + value is a tuple instead of single value. The default for + nargs is ``1`` (except if the type is a tuple, then it's + the arity of the tuple). If ``nargs=-1``, all remaining + parameters are collected. + :param metavar: how the value is represented in the help page. + :param expose_value: if this is `True` then the value is passed onwards + to the command callback and stored on the context, + otherwise it's skipped. + :param is_eager: eager values are processed before non eager ones. This + should not be set for arguments or it will inverse the + order of processing. + :param envvar: a string or list of strings that are environment variables + that should be checked. + :param shell_complete: A function that returns custom shell + completions. Used instead of the param's type completion if + given. Takes ``ctx, param, incomplete`` and must return a list + of :class:`~click.shell_completion.CompletionItem` or a list of + strings. + + .. versionchanged:: 8.0 + ``process_value`` validates required parameters and bounded + ``nargs``, and invokes the parameter callback before returning + the value. This allows the callback to validate prompts. + ``full_process_value`` is removed. + + .. versionchanged:: 8.0 + ``autocompletion`` is renamed to ``shell_complete`` and has new + semantics described above. The old name is deprecated and will + be removed in 8.1, until then it will be wrapped to match the + new requirements. + + .. versionchanged:: 8.0 + For ``multiple=True, nargs>1``, the default must be a list of + tuples. + + .. versionchanged:: 8.0 + Setting a default is no longer required for ``nargs>1``, it will + default to ``None``. ``multiple=True`` or ``nargs=-1`` will + default to ``()``. + + .. versionchanged:: 7.1 + Empty environment variables are ignored rather than taking the + empty string value. This makes it possible for scripts to clear + variables if they can't unset them. + + .. versionchanged:: 2.0 + Changed signature for parameter callback to also be passed the + parameter. The old callback format will still work, but it will + raise a warning to give you a chance to migrate the code easier. + """ + + param_type_name = "parameter" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + required: bool = False, + default: t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]] = None, + callback: t.Optional[t.Callable[[Context, "Parameter", t.Any], t.Any]] = None, + nargs: t.Optional[int] = None, + multiple: bool = False, + metavar: t.Optional[str] = None, + expose_value: bool = True, + is_eager: bool = False, + envvar: t.Optional[t.Union[str, t.Sequence[str]]] = None, + shell_complete: t.Optional[ + t.Callable[ + [Context, "Parameter", str], + t.Union[t.List["CompletionItem"], t.List[str]], + ] + ] = None, + ) -> None: + self.name: t.Optional[str] + self.opts: t.List[str] + self.secondary_opts: t.List[str] + self.name, self.opts, self.secondary_opts = self._parse_decls( + param_decls or (), expose_value + ) + self.type: types.ParamType = types.convert_type(type, default) + + # Default nargs to what the type tells us if we have that + # information available. + if nargs is None: + if self.type.is_composite: + nargs = self.type.arity + else: + nargs = 1 + + self.required = required + self.callback = callback + self.nargs = nargs + self.multiple = multiple + self.expose_value = expose_value + self.default = default + self.is_eager = is_eager + self.metavar = metavar + self.envvar = envvar + self._custom_shell_complete = shell_complete + + if __debug__: + if self.type.is_composite and nargs != self.type.arity: + raise ValueError( + f"'nargs' must be {self.type.arity} (or None) for" + f" type {self.type!r}, but it was {nargs}." + ) + + # Skip no default or callable default. + check_default = default if not callable(default) else None + + if check_default is not None: + if multiple: + try: + # Only check the first value against nargs. + check_default = next(_check_iter(check_default), None) + except TypeError: + raise ValueError( + "'default' must be a list when 'multiple' is true." + ) from None + + # Can be None for multiple with empty default. + if nargs != 1 and check_default is not None: + try: + _check_iter(check_default) + except TypeError: + if multiple: + message = ( + "'default' must be a list of lists when 'multiple' is" + " true and 'nargs' != 1." + ) + else: + message = "'default' must be a list when 'nargs' != 1." + + raise ValueError(message) from None + + if nargs > 1 and len(check_default) != nargs: + subject = "item length" if multiple else "length" + raise ValueError( + f"'default' {subject} must match nargs={nargs}." + ) + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + return { + "name": self.name, + "param_type_name": self.param_type_name, + "opts": self.opts, + "secondary_opts": self.secondary_opts, + "type": self.type.to_info_dict(), + "required": self.required, + "nargs": self.nargs, + "multiple": self.multiple, + "default": self.default, + "envvar": self.envvar, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + raise NotImplementedError() + + @property + def human_readable_name(self) -> str: + """Returns the human readable name of this parameter. This is the + same as the name for options, but the metavar for arguments. + """ + return self.name # type: ignore + + def make_metavar(self) -> str: + if self.metavar is not None: + return self.metavar + + metavar = self.type.get_metavar(self) + + if metavar is None: + metavar = self.type.name.upper() + + if self.nargs != 1: + metavar += "..." + + return metavar + + @t.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + """Get the default for the parameter. Tries + :meth:`Context.lookup_default` first, then the local default. + + :param ctx: Current context. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0.2 + Type casting is no longer performed when getting a default. + + .. versionchanged:: 8.0.1 + Type casting can fail in resilient parsing mode. Invalid + defaults will not prevent showing help text. + + .. versionchanged:: 8.0 + Looks at ``ctx.default_map`` first. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + value = ctx.lookup_default(self.name, call=False) # type: ignore + + if value is None: + value = self.default + + if call and callable(value): + value = value() + + return value + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + raise NotImplementedError() + + def consume_value( + self, ctx: Context, opts: t.Mapping[str, t.Any] + ) -> t.Tuple[t.Any, ParameterSource]: + value = opts.get(self.name) # type: ignore + source = ParameterSource.COMMANDLINE + + if value is None: + value = self.value_from_envvar(ctx) + source = ParameterSource.ENVIRONMENT + + if value is None: + value = ctx.lookup_default(self.name) # type: ignore + source = ParameterSource.DEFAULT_MAP + + if value is None: + value = self.get_default(ctx) + source = ParameterSource.DEFAULT + + return value, source + + def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any: + """Convert and validate a value against the option's + :attr:`type`, :attr:`multiple`, and :attr:`nargs`. + """ + if value is None: + return () if self.multiple or self.nargs == -1 else None + + def check_iter(value: t.Any) -> t.Iterator[t.Any]: + try: + return _check_iter(value) + except TypeError: + # This should only happen when passing in args manually, + # the parser should construct an iterable when parsing + # the command line. + raise BadParameter( + _("Value must be an iterable."), ctx=ctx, param=self + ) from None + + if self.nargs == 1 or self.type.is_composite: + + def convert(value: t.Any) -> t.Any: + return self.type(value, param=self, ctx=ctx) + + elif self.nargs == -1: + + def convert(value: t.Any) -> t.Any: # t.Tuple[t.Any, ...] + return tuple(self.type(x, self, ctx) for x in check_iter(value)) + + else: # nargs > 1 + + def convert(value: t.Any) -> t.Any: # t.Tuple[t.Any, ...] + value = tuple(check_iter(value)) + + if len(value) != self.nargs: + raise BadParameter( + ngettext( + "Takes {nargs} values but 1 was given.", + "Takes {nargs} values but {len} were given.", + len(value), + ).format(nargs=self.nargs, len=len(value)), + ctx=ctx, + param=self, + ) + + return tuple(self.type(x, self, ctx) for x in value) + + if self.multiple: + return tuple(convert(x) for x in check_iter(value)) + + return convert(value) + + def value_is_missing(self, value: t.Any) -> bool: + if value is None: + return True + + if (self.nargs != 1 or self.multiple) and value == (): + return True + + return False + + def process_value(self, ctx: Context, value: t.Any) -> t.Any: + value = self.type_cast_value(ctx, value) + + if self.required and self.value_is_missing(value): + raise MissingParameter(ctx=ctx, param=self) + + if self.callback is not None: + value = self.callback(ctx, self, value) + + return value + + def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: + if self.envvar is None: + return None + + if isinstance(self.envvar, str): + rv = os.environ.get(self.envvar) + + if rv: + return rv + else: + for envvar in self.envvar: + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: + rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) + + if rv is not None and self.nargs != 1: + rv = self.type.split_envvar_value(rv) + + return rv + + def handle_parse_result( + self, ctx: Context, opts: t.Mapping[str, t.Any], args: t.List[str] + ) -> t.Tuple[t.Any, t.List[str]]: + with augment_usage_errors(ctx, param=self): + value, source = self.consume_value(ctx, opts) + ctx.set_parameter_source(self.name, source) # type: ignore + + try: + value = self.process_value(ctx, value) + except Exception: + if not ctx.resilient_parsing: + raise + + value = None + + if self.expose_value: + ctx.params[self.name] = value # type: ignore + + return value, args + + def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: + pass + + def get_usage_pieces(self, ctx: Context) -> t.List[str]: + return [] + + def get_error_hint(self, ctx: Context) -> str: + """Get a stringified version of the param for use in error messages to + indicate which param caused the error. + """ + hint_list = self.opts or [self.human_readable_name] + return " / ".join(f"'{x}'" for x in hint_list) + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. If a + ``shell_complete`` function was given during init, it is used. + Otherwise, the :attr:`type` + :meth:`~click.types.ParamType.shell_complete` function is used. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + if self._custom_shell_complete is not None: + results = self._custom_shell_complete(ctx, self, incomplete) + + if results and isinstance(results[0], str): + from click.shell_completion import CompletionItem + + results = [CompletionItem(c) for c in results] + + return t.cast(t.List["CompletionItem"], results) + + return self.type.shell_complete(ctx, self, incomplete) + + +class Option(Parameter): + """Options are usually optional values on the command line and + have some extra features that arguments don't have. + + All other parameters are passed onwards to the parameter constructor. + + :param show_default: Show the default value for this option in its + help text. Values are not shown by default, unless + :attr:`Context.show_default` is ``True``. If this value is a + string, it shows that string in parentheses instead of the + actual value. This is particularly useful for dynamic options. + For single option boolean flags, the default remains hidden if + its value is ``False``. + :param show_envvar: Controls if an environment variable should be + shown on the help page. Normally, environment variables are not + shown. + :param prompt: If set to ``True`` or a non empty string then the + user will be prompted for input. If set to ``True`` the prompt + will be the option name capitalized. + :param confirmation_prompt: Prompt a second time to confirm the + value if it was prompted for. Can be set to a string instead of + ``True`` to customize the message. + :param prompt_required: If set to ``False``, the user will be + prompted for input only when the option was specified as a flag + without a value. + :param hide_input: If this is ``True`` then the input on the prompt + will be hidden from the user. This is useful for password input. + :param is_flag: forces this option to act as a flag. The default is + auto detection. + :param flag_value: which value should be used for this flag if it's + enabled. This is set to a boolean automatically if + the option string contains a slash to mark two options. + :param multiple: if this is set to `True` then the argument is accepted + multiple times and recorded. This is similar to ``nargs`` + in how it works but supports arbitrary number of + arguments. + :param count: this flag makes an option increment an integer. + :param allow_from_autoenv: if this is enabled then the value of this + parameter will be pulled from an environment + variable in case a prefix is defined on the + context. + :param help: the help string. + :param hidden: hide this option from help outputs. + :param attrs: Other command arguments described in :class:`Parameter`. + + .. versionchanged:: 8.1.0 + Help text indentation is cleaned here instead of only in the + ``@option`` decorator. + + .. versionchanged:: 8.1.0 + The ``show_default`` parameter overrides + ``Context.show_default``. + + .. versionchanged:: 8.1.0 + The default of a single option boolean flag is not shown if the + default value is ``False``. + + .. versionchanged:: 8.0.1 + ``type`` is detected from ``flag_value`` if given. + """ + + param_type_name = "option" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + show_default: t.Union[bool, str, None] = None, + prompt: t.Union[bool, str] = False, + confirmation_prompt: t.Union[bool, str] = False, + prompt_required: bool = True, + hide_input: bool = False, + is_flag: t.Optional[bool] = None, + flag_value: t.Optional[t.Any] = None, + multiple: bool = False, + count: bool = False, + allow_from_autoenv: bool = True, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + help: t.Optional[str] = None, + hidden: bool = False, + show_choices: bool = True, + show_envvar: bool = False, + **attrs: t.Any, + ) -> None: + if help: + help = inspect.cleandoc(help) + + default_is_missing = "default" not in attrs + super().__init__(param_decls, type=type, multiple=multiple, **attrs) + + if prompt is True: + if self.name is None: + raise TypeError("'name' is required with 'prompt=True'.") + + prompt_text: t.Optional[str] = self.name.replace("_", " ").capitalize() + elif prompt is False: + prompt_text = None + else: + prompt_text = prompt + + self.prompt = prompt_text + self.confirmation_prompt = confirmation_prompt + self.prompt_required = prompt_required + self.hide_input = hide_input + self.hidden = hidden + + # If prompt is enabled but not required, then the option can be + # used as a flag to indicate using prompt or flag_value. + self._flag_needs_value = self.prompt is not None and not self.prompt_required + + if is_flag is None: + if flag_value is not None: + # Implicitly a flag because flag_value was set. + is_flag = True + elif self._flag_needs_value: + # Not a flag, but when used as a flag it shows a prompt. + is_flag = False + else: + # Implicitly a flag because flag options were given. + is_flag = bool(self.secondary_opts) + elif is_flag is False and not self._flag_needs_value: + # Not a flag, and prompt is not enabled, can be used as a + # flag if flag_value is set. + self._flag_needs_value = flag_value is not None + + self.default: t.Union[t.Any, t.Callable[[], t.Any]] + + if is_flag and default_is_missing and not self.required: + if multiple: + self.default = () + else: + self.default = False + + if flag_value is None: + flag_value = not self.default + + self.type: types.ParamType + if is_flag and type is None: + # Re-guess the type from the flag value instead of the + # default. + self.type = types.convert_type(None, flag_value) + + self.is_flag: bool = is_flag + self.is_bool_flag: bool = is_flag and isinstance(self.type, types.BoolParamType) + self.flag_value: t.Any = flag_value + + # Counting + self.count = count + if count: + if type is None: + self.type = types.IntRange(min=0) + if default_is_missing: + self.default = 0 + + self.allow_from_autoenv = allow_from_autoenv + self.help = help + self.show_default = show_default + self.show_choices = show_choices + self.show_envvar = show_envvar + + if __debug__: + if self.nargs == -1: + raise TypeError("nargs=-1 is not supported for options.") + + if self.prompt and self.is_flag and not self.is_bool_flag: + raise TypeError("'prompt' is not valid for non-boolean flag.") + + if not self.is_bool_flag and self.secondary_opts: + raise TypeError("Secondary flag is not valid for non-boolean flag.") + + if self.is_bool_flag and self.hide_input and self.prompt is not None: + raise TypeError( + "'prompt' with 'hide_input' is not valid for boolean flag." + ) + + if self.count: + if self.multiple: + raise TypeError("'count' is not valid with 'multiple'.") + + if self.is_flag: + raise TypeError("'count' is not valid with 'is_flag'.") + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + help=self.help, + prompt=self.prompt, + is_flag=self.is_flag, + flag_value=self.flag_value, + count=self.count, + hidden=self.hidden, + ) + return info_dict + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + opts = [] + secondary_opts = [] + name = None + possible_names = [] + + for decl in decls: + if decl.isidentifier(): + if name is not None: + raise TypeError(f"Name '{name}' defined twice") + name = decl + else: + split_char = ";" if decl[:1] == "/" else "/" + if split_char in decl: + first, second = decl.split(split_char, 1) + first = first.rstrip() + if first: + possible_names.append(split_opt(first)) + opts.append(first) + second = second.lstrip() + if second: + secondary_opts.append(second.lstrip()) + if first == second: + raise ValueError( + f"Boolean option {decl!r} cannot use the" + " same flag for true/false." + ) + else: + possible_names.append(split_opt(decl)) + opts.append(decl) + + if name is None and possible_names: + possible_names.sort(key=lambda x: -len(x[0])) # group long options first + name = possible_names[0][1].replace("-", "_").lower() + if not name.isidentifier(): + name = None + + if name is None: + if not expose_value: + return None, opts, secondary_opts + raise TypeError( + f"Could not determine name for option with declarations {decls!r}" + ) + + if not opts and not secondary_opts: + raise TypeError( + f"No options defined but a name was passed ({name})." + " Did you mean to declare an argument instead? Did" + f" you mean to pass '--{name}'?" + ) + + return name, opts, secondary_opts + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + if self.multiple: + action = "append" + elif self.count: + action = "count" + else: + action = "store" + + if self.is_flag: + action = f"{action}_const" + + if self.is_bool_flag and self.secondary_opts: + parser.add_option( + obj=self, opts=self.opts, dest=self.name, action=action, const=True + ) + parser.add_option( + obj=self, + opts=self.secondary_opts, + dest=self.name, + action=action, + const=False, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + const=self.flag_value, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + nargs=self.nargs, + ) + + def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: + if self.hidden: + return None + + any_prefix_is_slash = False + + def _write_opts(opts: t.Sequence[str]) -> str: + nonlocal any_prefix_is_slash + + rv, any_slashes = join_options(opts) + + if any_slashes: + any_prefix_is_slash = True + + if not self.is_flag and not self.count: + rv += f" {self.make_metavar()}" + + return rv + + rv = [_write_opts(self.opts)] + + if self.secondary_opts: + rv.append(_write_opts(self.secondary_opts)) + + help = self.help or "" + extra = [] + + if self.show_envvar: + envvar = self.envvar + + if envvar is None: + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + + if envvar is not None: + var_str = ( + envvar + if isinstance(envvar, str) + else ", ".join(str(d) for d in envvar) + ) + extra.append(_("env var: {var}").format(var=var_str)) + + # Temporarily enable resilient parsing to avoid type casting + # failing for the default. Might be possible to extend this to + # help formatting in general. + resilient = ctx.resilient_parsing + ctx.resilient_parsing = True + + try: + default_value = self.get_default(ctx, call=False) + finally: + ctx.resilient_parsing = resilient + + show_default = False + show_default_is_str = False + + if self.show_default is not None: + if isinstance(self.show_default, str): + show_default_is_str = show_default = True + else: + show_default = self.show_default + elif ctx.show_default is not None: + show_default = ctx.show_default + + if show_default_is_str or (show_default and (default_value is not None)): + if show_default_is_str: + default_string = f"({self.show_default})" + elif isinstance(default_value, (list, tuple)): + default_string = ", ".join(str(d) for d in default_value) + elif inspect.isfunction(default_value): + default_string = _("(dynamic)") + elif self.is_bool_flag and self.secondary_opts: + # For boolean flags that have distinct True/False opts, + # use the opt without prefix instead of the value. + default_string = split_opt( + (self.opts if default_value else self.secondary_opts)[0] + )[1] + elif self.is_bool_flag and not self.secondary_opts and not default_value: + default_string = "" + elif default_value == "": + default_string = '""' + else: + default_string = str(default_value) + + if default_string: + extra.append(_("default: {default}").format(default=default_string)) + + if ( + isinstance(self.type, types._NumberRangeBase) + # skip count with default range type + and not (self.count and self.type.min == 0 and self.type.max is None) + ): + range_str = self.type._describe_range() + + if range_str: + extra.append(range_str) + + if self.required: + extra.append(_("required")) + + if extra: + extra_str = "; ".join(extra) + help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" + + return ("; " if any_prefix_is_slash else " / ").join(rv), help + + @t.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + # If we're a non boolean flag our default is more complex because + # we need to look at all flags in the same group to figure out + # if we're the default one in which case we return the flag + # value as default. + if self.is_flag and not self.is_bool_flag: + for param in ctx.command.params: + if param.name == self.name and param.default: + return t.cast(Option, param).flag_value + + return None + + return super().get_default(ctx, call=call) + + def prompt_for_value(self, ctx: Context) -> t.Any: + """This is an alternative flow that can be activated in the full + value processing if a value does not exist. It will prompt the + user until a valid value exists and then returns the processed + value as result. + """ + assert self.prompt is not None + + # Calculate the default before prompting anything to be stable. + default = self.get_default(ctx) + + # If this is a prompt for a flag we need to handle this + # differently. + if self.is_bool_flag: + return confirm(self.prompt, default) + + return prompt( + self.prompt, + default=default, + type=self.type, + hide_input=self.hide_input, + show_choices=self.show_choices, + confirmation_prompt=self.confirmation_prompt, + value_proc=lambda x: self.process_value(ctx, x), + ) + + def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: + rv = super().resolve_envvar_value(ctx) + + if rv is not None: + return rv + + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: + rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) + + if rv is None: + return None + + value_depth = (self.nargs != 1) + bool(self.multiple) + + if value_depth > 0: + rv = self.type.split_envvar_value(rv) + + if self.multiple and self.nargs != 1: + rv = batch(rv, self.nargs) + + return rv + + def consume_value( + self, ctx: Context, opts: t.Mapping[str, "Parameter"] + ) -> t.Tuple[t.Any, ParameterSource]: + value, source = super().consume_value(ctx, opts) + + # The parser will emit a sentinel value if the option can be + # given as a flag without a value. This is different from None + # to distinguish from the flag not being given at all. + if value is _flag_needs_value: + if self.prompt is not None and not ctx.resilient_parsing: + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + else: + value = self.flag_value + source = ParameterSource.COMMANDLINE + + elif ( + self.multiple + and value is not None + and any(v is _flag_needs_value for v in value) + ): + value = [self.flag_value if v is _flag_needs_value else v for v in value] + source = ParameterSource.COMMANDLINE + + # The value wasn't set, or used the param's default, prompt if + # prompting is enabled. + elif ( + source in {None, ParameterSource.DEFAULT} + and self.prompt is not None + and (self.required or self.prompt_required) + and not ctx.resilient_parsing + ): + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + + return value, source + + +class Argument(Parameter): + """Arguments are positional parameters to a command. They generally + provide fewer features than options but can have infinite ``nargs`` + and are required by default. + + All parameters are passed onwards to the constructor of :class:`Parameter`. + """ + + param_type_name = "argument" + + def __init__( + self, + param_decls: t.Sequence[str], + required: t.Optional[bool] = None, + **attrs: t.Any, + ) -> None: + if required is None: + if attrs.get("default") is not None: + required = False + else: + required = attrs.get("nargs", 1) > 0 + + if "multiple" in attrs: + raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") + + super().__init__(param_decls, required=required, **attrs) + + if __debug__: + if self.default is not None and self.nargs == -1: + raise TypeError("'default' is not supported for nargs=-1.") + + @property + def human_readable_name(self) -> str: + if self.metavar is not None: + return self.metavar + return self.name.upper() # type: ignore + + def make_metavar(self) -> str: + if self.metavar is not None: + return self.metavar + var = self.type.get_metavar(self) + if not var: + var = self.name.upper() # type: ignore + if not self.required: + var = f"[{var}]" + if self.nargs != 1: + var += "..." + return var + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + if not decls: + if not expose_value: + return None, [], [] + raise TypeError("Argument is marked as exposed, but does not have a name.") + if len(decls) == 1: + name = arg = decls[0] + name = name.replace("-", "_").lower() + else: + raise TypeError( + "Arguments take exactly one parameter declaration, got" + f" {len(decls)}." + ) + return name, [arg], [] + + def get_usage_pieces(self, ctx: Context) -> t.List[str]: + return [self.make_metavar()] + + def get_error_hint(self, ctx: Context) -> str: + return f"'{self.make_metavar()}'" + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) diff --git a/.venv/lib/python3.9/site-packages/click/decorators.py b/.venv/lib/python3.9/site-packages/click/decorators.py new file mode 100644 index 00000000..bcf8906e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/decorators.py @@ -0,0 +1,562 @@ +import inspect +import types +import typing as t +from functools import update_wrapper +from gettext import gettext as _ + +from .core import Argument +from .core import Command +from .core import Context +from .core import Group +from .core import Option +from .core import Parameter +from .globals import get_current_context +from .utils import echo + +if t.TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = t.TypeVar("R") +T = t.TypeVar("T") +_AnyCallable = t.Callable[..., t.Any] +FC = t.TypeVar("FC", bound=t.Union[_AnyCallable, Command]) + + +def pass_context(f: "t.Callable[te.Concatenate[Context, P], R]") -> "t.Callable[P, R]": + """Marks a callback as wanting to receive the current context + object as first argument. + """ + + def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": + return f(get_current_context(), *args, **kwargs) + + return update_wrapper(new_func, f) + + +def pass_obj(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]": + """Similar to :func:`pass_context`, but only pass the object on the + context onwards (:attr:`Context.obj`). This is useful if that object + represents the state of a nested system. + """ + + def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": + return f(get_current_context().obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + +def make_pass_decorator( + object_type: t.Type[T], ensure: bool = False +) -> t.Callable[["t.Callable[te.Concatenate[T, P], R]"], "t.Callable[P, R]"]: + """Given an object type this creates a decorator that will work + similar to :func:`pass_obj` but instead of passing the object of the + current context, it will find the innermost context of type + :func:`object_type`. + + This generates a decorator that works roughly like this:: + + from functools import update_wrapper + + def decorator(f): + @pass_context + def new_func(ctx, *args, **kwargs): + obj = ctx.find_object(object_type) + return ctx.invoke(f, obj, *args, **kwargs) + return update_wrapper(new_func, f) + return decorator + + :param object_type: the type of the object to pass. + :param ensure: if set to `True`, a new object will be created and + remembered on the context if it's not there yet. + """ + + def decorator(f: "t.Callable[te.Concatenate[T, P], R]") -> "t.Callable[P, R]": + def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": + ctx = get_current_context() + + obj: t.Optional[T] + if ensure: + obj = ctx.ensure_object(object_type) + else: + obj = ctx.find_object(object_type) + + if obj is None: + raise RuntimeError( + "Managed to invoke callback without a context" + f" object of type {object_type.__name__!r}" + " existing." + ) + + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + return decorator + + +def pass_meta_key( + key: str, *, doc_description: t.Optional[str] = None +) -> "t.Callable[[t.Callable[te.Concatenate[t.Any, P], R]], t.Callable[P, R]]": + """Create a decorator that passes a key from + :attr:`click.Context.meta` as the first argument to the decorated + function. + + :param key: Key in ``Context.meta`` to pass. + :param doc_description: Description of the object being passed, + inserted into the decorator's docstring. Defaults to "the 'key' + key from Context.meta". + + .. versionadded:: 8.0 + """ + + def decorator(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]": + def new_func(*args: "P.args", **kwargs: "P.kwargs") -> R: + ctx = get_current_context() + obj = ctx.meta[key] + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + if doc_description is None: + doc_description = f"the {key!r} key from :attr:`click.Context.meta`" + + decorator.__doc__ = ( + f"Decorator that passes {doc_description} as the first argument" + " to the decorated function." + ) + return decorator + + +CmdType = t.TypeVar("CmdType", bound=Command) + + +# variant: no call, directly as decorator for a function. +@t.overload +def command(name: _AnyCallable) -> Command: ... + + +# variant: with positional name and with positional or keyword cls argument: +# @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...) +@t.overload +def command( + name: t.Optional[str], + cls: t.Type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: ... + + +# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...) +@t.overload +def command( + name: None = None, + *, + cls: t.Type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def command( + name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Command]: ... + + +def command( + name: t.Union[t.Optional[str], _AnyCallable] = None, + cls: t.Optional[t.Type[CmdType]] = None, + **attrs: t.Any, +) -> t.Union[Command, t.Callable[[_AnyCallable], t.Union[Command, CmdType]]]: + r"""Creates a new :class:`Command` and uses the decorated function as + callback. This will also automatically attach all decorated + :func:`option`\s and :func:`argument`\s as parameters to the command. + + The name of the command defaults to the name of the function with + underscores replaced by dashes. If you want to change that, you can + pass the intended name as the first argument. + + All keyword arguments are forwarded to the underlying command class. + For the ``params`` argument, any decorated params are appended to + the end of the list. + + Once decorated the function turns into a :class:`Command` instance + that can be invoked as a command line utility or be attached to a + command :class:`Group`. + + :param name: the name of the command. This defaults to the function + name with underscores replaced by dashes. + :param cls: the command class to instantiate. This defaults to + :class:`Command`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.1 + The ``params`` argument can be used. Decorated params are + appended to the end of the list. + """ + + func: t.Optional[t.Callable[[_AnyCallable], t.Any]] = None + + if callable(name): + func = name + name = None + assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." + assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." + + if cls is None: + cls = t.cast(t.Type[CmdType], Command) + + def decorator(f: _AnyCallable) -> CmdType: + if isinstance(f, Command): + raise TypeError("Attempted to convert a callback into a command twice.") + + attr_params = attrs.pop("params", None) + params = attr_params if attr_params is not None else [] + + try: + decorator_params = f.__click_params__ # type: ignore + except AttributeError: + pass + else: + del f.__click_params__ # type: ignore + params.extend(reversed(decorator_params)) + + if attrs.get("help") is None: + attrs["help"] = f.__doc__ + + if t.TYPE_CHECKING: + assert cls is not None + assert not callable(name) + + cmd = cls( + name=name or f.__name__.lower().replace("_", "-"), + callback=f, + params=params, + **attrs, + ) + cmd.__doc__ = f.__doc__ + return cmd + + if func is not None: + return decorator(func) + + return decorator + + +GrpType = t.TypeVar("GrpType", bound=Group) + + +# variant: no call, directly as decorator for a function. +@t.overload +def group(name: _AnyCallable) -> Group: ... + + +# variant: with positional name and with positional or keyword cls argument: +# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...) +@t.overload +def group( + name: t.Optional[str], + cls: t.Type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: ... + + +# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...) +@t.overload +def group( + name: None = None, + *, + cls: t.Type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def group( + name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Group]: ... + + +def group( + name: t.Union[str, _AnyCallable, None] = None, + cls: t.Optional[t.Type[GrpType]] = None, + **attrs: t.Any, +) -> t.Union[Group, t.Callable[[_AnyCallable], t.Union[Group, GrpType]]]: + """Creates a new :class:`Group` with a function as callback. This + works otherwise the same as :func:`command` just that the `cls` + parameter is set to :class:`Group`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + """ + if cls is None: + cls = t.cast(t.Type[GrpType], Group) + + if callable(name): + return command(cls=cls, **attrs)(name) + + return command(name, cls, **attrs) + + +def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None: + if isinstance(f, Command): + f.params.append(param) + else: + if not hasattr(f, "__click_params__"): + f.__click_params__ = [] # type: ignore + + f.__click_params__.append(param) # type: ignore + + +def argument( + *param_decls: str, cls: t.Optional[t.Type[Argument]] = None, **attrs: t.Any +) -> t.Callable[[FC], FC]: + """Attaches an argument to the command. All positional arguments are + passed as parameter declarations to :class:`Argument`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Argument` instance manually + and attaching it to the :attr:`Command.params` list. + + For the default argument class, refer to :class:`Argument` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the argument class to instantiate. This defaults to + :class:`Argument`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Argument + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **attrs)) + return f + + return decorator + + +def option( + *param_decls: str, cls: t.Optional[t.Type[Option]] = None, **attrs: t.Any +) -> t.Callable[[FC], FC]: + """Attaches an option to the command. All positional arguments are + passed as parameter declarations to :class:`Option`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Option` instance manually + and attaching it to the :attr:`Command.params` list. + + For the default option class, refer to :class:`Option` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the option class to instantiate. This defaults to + :class:`Option`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Option + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **attrs)) + return f + + return decorator + + +def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--yes`` option which shows a prompt before continuing if + not passed. If the prompt is declined, the program will exit. + + :param param_decls: One or more option names. Defaults to the single + value ``"--yes"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value: + ctx.abort() + + if not param_decls: + param_decls = ("--yes",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("callback", callback) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("prompt", "Do you want to continue?") + kwargs.setdefault("help", "Confirm the action without prompting.") + return option(*param_decls, **kwargs) + + +def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--password`` option which prompts for a password, hiding + input and asking to enter the value again for confirmation. + + :param param_decls: One or more option names. Defaults to the single + value ``"--password"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + if not param_decls: + param_decls = ("--password",) + + kwargs.setdefault("prompt", True) + kwargs.setdefault("confirmation_prompt", True) + kwargs.setdefault("hide_input", True) + return option(*param_decls, **kwargs) + + +def version_option( + version: t.Optional[str] = None, + *param_decls: str, + package_name: t.Optional[str] = None, + prog_name: t.Optional[str] = None, + message: t.Optional[str] = None, + **kwargs: t.Any, +) -> t.Callable[[FC], FC]: + """Add a ``--version`` option which immediately prints the version + number and exits the program. + + If ``version`` is not provided, Click will try to detect it using + :func:`importlib.metadata.version` to get the version for the + ``package_name``. On Python < 3.8, the ``importlib_metadata`` + backport must be installed. + + If ``package_name`` is not provided, Click will try to detect it by + inspecting the stack frames. This will be used to detect the + version, so it must match the name of the installed package. + + :param version: The version number to show. If not provided, Click + will try to detect it. + :param param_decls: One or more option names. Defaults to the single + value ``"--version"``. + :param package_name: The package name to detect the version from. If + not provided, Click will try to detect it. + :param prog_name: The name of the CLI to show in the message. If not + provided, it will be detected from the command. + :param message: The message to show. The values ``%(prog)s``, + ``%(package)s``, and ``%(version)s`` are available. Defaults to + ``"%(prog)s, version %(version)s"``. + :param kwargs: Extra arguments are passed to :func:`option`. + :raise RuntimeError: ``version`` could not be detected. + + .. versionchanged:: 8.0 + Add the ``package_name`` parameter, and the ``%(package)s`` + value for messages. + + .. versionchanged:: 8.0 + Use :mod:`importlib.metadata` instead of ``pkg_resources``. The + version is detected based on the package name, not the entry + point name. The Python package name must match the installed + package name, or be passed with ``package_name=``. + """ + if message is None: + message = _("%(prog)s, version %(version)s") + + if version is None and package_name is None: + frame = inspect.currentframe() + f_back = frame.f_back if frame is not None else None + f_globals = f_back.f_globals if f_back is not None else None + # break reference cycle + # https://docs.python.org/3/library/inspect.html#the-interpreter-stack + del frame + + if f_globals is not None: + package_name = f_globals.get("__name__") + + if package_name == "__main__": + package_name = f_globals.get("__package__") + + if package_name: + package_name = package_name.partition(".")[0] + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + nonlocal prog_name + nonlocal version + + if prog_name is None: + prog_name = ctx.find_root().info_name + + if version is None and package_name is not None: + metadata: t.Optional[types.ModuleType] + + try: + from importlib import metadata + except ImportError: + # Python < 3.8 + import importlib_metadata as metadata # type: ignore + + try: + version = metadata.version(package_name) # type: ignore + except metadata.PackageNotFoundError: # type: ignore + raise RuntimeError( + f"{package_name!r} is not installed. Try passing" + " 'package_name' instead." + ) from None + + if version is None: + raise RuntimeError( + f"Could not determine the version for {package_name!r} automatically." + ) + + echo( + message % {"prog": prog_name, "package": package_name, "version": version}, + color=ctx.color, + ) + ctx.exit() + + if not param_decls: + param_decls = ("--version",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show the version and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) + + +class HelpOption(Option): + """Pre-configured ``--help`` option which immediately prints the help page + and exits the program. + """ + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + **kwargs: t.Any, + ) -> None: + if not param_decls: + param_decls = ("--help",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show this message and exit.")) + kwargs.setdefault("callback", self.show_help) + + super().__init__(param_decls, **kwargs) + + @staticmethod + def show_help(ctx: Context, param: Parameter, value: bool) -> None: + """Callback that print the help page on ```` and exits.""" + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + +def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Decorator for the pre-configured ``--help`` option defined above. + + :param param_decls: One or more option names. Defaults to the single + value ``"--help"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + kwargs.setdefault("cls", HelpOption) + return option(*param_decls, **kwargs) diff --git a/.venv/lib/python3.9/site-packages/click/exceptions.py b/.venv/lib/python3.9/site-packages/click/exceptions.py new file mode 100644 index 00000000..0b831516 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/exceptions.py @@ -0,0 +1,296 @@ +import typing as t +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import get_text_stderr +from .globals import resolve_color_default +from .utils import echo +from .utils import format_filename + +if t.TYPE_CHECKING: + from .core import Command + from .core import Context + from .core import Parameter + + +def _join_param_hints( + param_hint: t.Optional[t.Union[t.Sequence[str], str]], +) -> t.Optional[str]: + if param_hint is not None and not isinstance(param_hint, str): + return " / ".join(repr(x) for x in param_hint) + + return param_hint + + +class ClickException(Exception): + """An exception that Click can handle and show to the user.""" + + #: The exit code for this exception. + exit_code = 1 + + def __init__(self, message: str) -> None: + super().__init__(message) + # The context will be removed by the time we print the message, so cache + # the color settings here to be used later on (in `show`) + self.show_color: t.Optional[bool] = resolve_color_default() + self.message = message + + def format_message(self) -> str: + return self.message + + def __str__(self) -> str: + return self.message + + def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: + if file is None: + file = get_text_stderr() + + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=self.show_color, + ) + + +class UsageError(ClickException): + """An internal exception that signals a usage error. This typically + aborts any further handling. + + :param message: the error message to display. + :param ctx: optionally the context that caused this error. Click will + fill in the context automatically in some situations. + """ + + exit_code = 2 + + def __init__(self, message: str, ctx: t.Optional["Context"] = None) -> None: + super().__init__(message) + self.ctx = ctx + self.cmd: t.Optional[Command] = self.ctx.command if self.ctx else None + + def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: + if file is None: + file = get_text_stderr() + color = None + hint = "" + if ( + self.ctx is not None + and self.ctx.command.get_help_option(self.ctx) is not None + ): + hint = _("Try '{command} {option}' for help.").format( + command=self.ctx.command_path, option=self.ctx.help_option_names[0] + ) + hint = f"{hint}\n" + if self.ctx is not None: + color = self.ctx.color + echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color) + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=color, + ) + + +class BadParameter(UsageError): + """An exception that formats out a standardized error message for a + bad parameter. This is useful when thrown from a callback or type as + Click will attach contextual information to it (for instance, which + parameter it is). + + .. versionadded:: 2.0 + + :param param: the parameter object that caused this error. This can + be left out, and Click will attach this info itself + if possible. + :param param_hint: a string that shows up as parameter name. This + can be used as alternative to `param` in cases + where custom validation should happen. If it is + a string it's used as such, if it's a list then + each item is quoted and separated. + """ + + def __init__( + self, + message: str, + ctx: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = None, + ) -> None: + super().__init__(message, ctx) + self.param = param + self.param_hint = param_hint + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + return _("Invalid value: {message}").format(message=self.message) + + return _("Invalid value for {param_hint}: {message}").format( + param_hint=_join_param_hints(param_hint), message=self.message + ) + + +class MissingParameter(BadParameter): + """Raised if click required an option or argument but it was not + provided when invoking the script. + + .. versionadded:: 4.0 + + :param param_type: a string that indicates the type of the parameter. + The default is to inherit the parameter type from + the given `param`. Valid values are ``'parameter'``, + ``'option'`` or ``'argument'``. + """ + + def __init__( + self, + message: t.Optional[str] = None, + ctx: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = None, + param_type: t.Optional[str] = None, + ) -> None: + super().__init__(message or "", ctx, param, param_hint) + self.param_type = param_type + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint: t.Optional[str] = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + param_hint = None + + param_hint = _join_param_hints(param_hint) + param_hint = f" {param_hint}" if param_hint else "" + + param_type = self.param_type + if param_type is None and self.param is not None: + param_type = self.param.param_type_name + + msg = self.message + if self.param is not None: + msg_extra = self.param.type.get_missing_message(self.param) + if msg_extra: + if msg: + msg += f". {msg_extra}" + else: + msg = msg_extra + + msg = f" {msg}" if msg else "" + + # Translate param_type for known types. + if param_type == "argument": + missing = _("Missing argument") + elif param_type == "option": + missing = _("Missing option") + elif param_type == "parameter": + missing = _("Missing parameter") + else: + missing = _("Missing {param_type}").format(param_type=param_type) + + return f"{missing}{param_hint}.{msg}" + + def __str__(self) -> str: + if not self.message: + param_name = self.param.name if self.param else None + return _("Missing parameter: {param_name}").format(param_name=param_name) + else: + return self.message + + +class NoSuchOption(UsageError): + """Raised if click attempted to handle an option that does not + exist. + + .. versionadded:: 4.0 + """ + + def __init__( + self, + option_name: str, + message: t.Optional[str] = None, + possibilities: t.Optional[t.Sequence[str]] = None, + ctx: t.Optional["Context"] = None, + ) -> None: + if message is None: + message = _("No such option: {name}").format(name=option_name) + + super().__init__(message, ctx) + self.option_name = option_name + self.possibilities = possibilities + + def format_message(self) -> str: + if not self.possibilities: + return self.message + + possibility_str = ", ".join(sorted(self.possibilities)) + suggest = ngettext( + "Did you mean {possibility}?", + "(Possible options: {possibilities})", + len(self.possibilities), + ).format(possibility=possibility_str, possibilities=possibility_str) + return f"{self.message} {suggest}" + + +class BadOptionUsage(UsageError): + """Raised if an option is generally supplied but the use of the option + was incorrect. This is for instance raised if the number of arguments + for an option is not correct. + + .. versionadded:: 4.0 + + :param option_name: the name of the option being used incorrectly. + """ + + def __init__( + self, option_name: str, message: str, ctx: t.Optional["Context"] = None + ) -> None: + super().__init__(message, ctx) + self.option_name = option_name + + +class BadArgumentUsage(UsageError): + """Raised if an argument is generally supplied but the use of the argument + was incorrect. This is for instance raised if the number of values + for an argument is not correct. + + .. versionadded:: 6.0 + """ + + +class FileError(ClickException): + """Raised if a file cannot be opened.""" + + def __init__(self, filename: str, hint: t.Optional[str] = None) -> None: + if hint is None: + hint = _("unknown error") + + super().__init__(hint) + self.ui_filename: str = format_filename(filename) + self.filename = filename + + def format_message(self) -> str: + return _("Could not open file {filename!r}: {message}").format( + filename=self.ui_filename, message=self.message + ) + + +class Abort(RuntimeError): + """An internal signalling exception that signals Click to abort.""" + + +class Exit(RuntimeError): + """An exception that indicates that the application should exit with some + status code. + + :param code: the status code to exit with. + """ + + __slots__ = ("exit_code",) + + def __init__(self, code: int = 0) -> None: + self.exit_code: int = code diff --git a/.venv/lib/python3.9/site-packages/click/formatting.py b/.venv/lib/python3.9/site-packages/click/formatting.py new file mode 100644 index 00000000..ddd2a2f8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/formatting.py @@ -0,0 +1,301 @@ +import typing as t +from contextlib import contextmanager +from gettext import gettext as _ + +from ._compat import term_len +from .parser import split_opt + +# Can force a width. This is used by the test system +FORCED_WIDTH: t.Optional[int] = None + + +def measure_table(rows: t.Iterable[t.Tuple[str, str]]) -> t.Tuple[int, ...]: + widths: t.Dict[int, int] = {} + + for row in rows: + for idx, col in enumerate(row): + widths[idx] = max(widths.get(idx, 0), term_len(col)) + + return tuple(y for x, y in sorted(widths.items())) + + +def iter_rows( + rows: t.Iterable[t.Tuple[str, str]], col_count: int +) -> t.Iterator[t.Tuple[str, ...]]: + for row in rows: + yield row + ("",) * (col_count - len(row)) + + +def wrap_text( + text: str, + width: int = 78, + initial_indent: str = "", + subsequent_indent: str = "", + preserve_paragraphs: bool = False, +) -> str: + """A helper function that intelligently wraps text. By default, it + assumes that it operates on a single paragraph of text but if the + `preserve_paragraphs` parameter is provided it will intelligently + handle paragraphs (defined by two empty lines). + + If paragraphs are handled, a paragraph can be prefixed with an empty + line containing the ``\\b`` character (``\\x08``) to indicate that + no rewrapping should happen in that block. + + :param text: the text that should be rewrapped. + :param width: the maximum width for the text. + :param initial_indent: the initial indent that should be placed on the + first line as a string. + :param subsequent_indent: the indent string that should be placed on + each consecutive line. + :param preserve_paragraphs: if this flag is set then the wrapping will + intelligently handle paragraphs. + """ + from ._textwrap import TextWrapper + + text = text.expandtabs() + wrapper = TextWrapper( + width, + initial_indent=initial_indent, + subsequent_indent=subsequent_indent, + replace_whitespace=False, + ) + if not preserve_paragraphs: + return wrapper.fill(text) + + p: t.List[t.Tuple[int, bool, str]] = [] + buf: t.List[str] = [] + indent = None + + def _flush_par() -> None: + if not buf: + return + if buf[0].strip() == "\b": + p.append((indent or 0, True, "\n".join(buf[1:]))) + else: + p.append((indent or 0, False, " ".join(buf))) + del buf[:] + + for line in text.splitlines(): + if not line: + _flush_par() + indent = None + else: + if indent is None: + orig_len = term_len(line) + line = line.lstrip() + indent = orig_len - term_len(line) + buf.append(line) + _flush_par() + + rv = [] + for indent, raw, text in p: + with wrapper.extra_indent(" " * indent): + if raw: + rv.append(wrapper.indent_only(text)) + else: + rv.append(wrapper.fill(text)) + + return "\n\n".join(rv) + + +class HelpFormatter: + """This class helps with formatting text-based help pages. It's + usually just needed for very special internal cases, but it's also + exposed so that developers can write their own fancy outputs. + + At present, it always writes into memory. + + :param indent_increment: the additional increment for each level. + :param width: the width for the text. This defaults to the terminal + width clamped to a maximum of 78. + """ + + def __init__( + self, + indent_increment: int = 2, + width: t.Optional[int] = None, + max_width: t.Optional[int] = None, + ) -> None: + import shutil + + self.indent_increment = indent_increment + if max_width is None: + max_width = 80 + if width is None: + width = FORCED_WIDTH + if width is None: + width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50) + self.width = width + self.current_indent = 0 + self.buffer: t.List[str] = [] + + def write(self, string: str) -> None: + """Writes a unicode string into the internal buffer.""" + self.buffer.append(string) + + def indent(self) -> None: + """Increases the indentation.""" + self.current_indent += self.indent_increment + + def dedent(self) -> None: + """Decreases the indentation.""" + self.current_indent -= self.indent_increment + + def write_usage( + self, prog: str, args: str = "", prefix: t.Optional[str] = None + ) -> None: + """Writes a usage line into the buffer. + + :param prog: the program name. + :param args: whitespace separated list of arguments. + :param prefix: The prefix for the first line. Defaults to + ``"Usage: "``. + """ + if prefix is None: + prefix = f"{_('Usage:')} " + + usage_prefix = f"{prefix:>{self.current_indent}}{prog} " + text_width = self.width - self.current_indent + + if text_width >= (term_len(usage_prefix) + 20): + # The arguments will fit to the right of the prefix. + indent = " " * term_len(usage_prefix) + self.write( + wrap_text( + args, + text_width, + initial_indent=usage_prefix, + subsequent_indent=indent, + ) + ) + else: + # The prefix is too long, put the arguments on the next line. + self.write(usage_prefix) + self.write("\n") + indent = " " * (max(self.current_indent, term_len(prefix)) + 4) + self.write( + wrap_text( + args, text_width, initial_indent=indent, subsequent_indent=indent + ) + ) + + self.write("\n") + + def write_heading(self, heading: str) -> None: + """Writes a heading into the buffer.""" + self.write(f"{'':>{self.current_indent}}{heading}:\n") + + def write_paragraph(self) -> None: + """Writes a paragraph into the buffer.""" + if self.buffer: + self.write("\n") + + def write_text(self, text: str) -> None: + """Writes re-indented text into the buffer. This rewraps and + preserves paragraphs. + """ + indent = " " * self.current_indent + self.write( + wrap_text( + text, + self.width, + initial_indent=indent, + subsequent_indent=indent, + preserve_paragraphs=True, + ) + ) + self.write("\n") + + def write_dl( + self, + rows: t.Sequence[t.Tuple[str, str]], + col_max: int = 30, + col_spacing: int = 2, + ) -> None: + """Writes a definition list into the buffer. This is how options + and commands are usually formatted. + + :param rows: a list of two item tuples for the terms and values. + :param col_max: the maximum width of the first column. + :param col_spacing: the number of spaces between the first and + second column. + """ + rows = list(rows) + widths = measure_table(rows) + if len(widths) != 2: + raise TypeError("Expected two columns for definition list") + + first_col = min(widths[0], col_max) + col_spacing + + for first, second in iter_rows(rows, len(widths)): + self.write(f"{'':>{self.current_indent}}{first}") + if not second: + self.write("\n") + continue + if term_len(first) <= first_col - col_spacing: + self.write(" " * (first_col - term_len(first))) + else: + self.write("\n") + self.write(" " * (first_col + self.current_indent)) + + text_width = max(self.width - first_col - 2, 10) + wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True) + lines = wrapped_text.splitlines() + + if lines: + self.write(f"{lines[0]}\n") + + for line in lines[1:]: + self.write(f"{'':>{first_col + self.current_indent}}{line}\n") + else: + self.write("\n") + + @contextmanager + def section(self, name: str) -> t.Iterator[None]: + """Helpful context manager that writes a paragraph, a heading, + and the indents. + + :param name: the section name that is written as heading. + """ + self.write_paragraph() + self.write_heading(name) + self.indent() + try: + yield + finally: + self.dedent() + + @contextmanager + def indentation(self) -> t.Iterator[None]: + """A context manager that increases the indentation.""" + self.indent() + try: + yield + finally: + self.dedent() + + def getvalue(self) -> str: + """Returns the buffer contents.""" + return "".join(self.buffer) + + +def join_options(options: t.Sequence[str]) -> t.Tuple[str, bool]: + """Given a list of option strings this joins them in the most appropriate + way and returns them in the form ``(formatted_string, + any_prefix_is_slash)`` where the second item in the tuple is a flag that + indicates if any of the option prefixes was a slash. + """ + rv = [] + any_prefix_is_slash = False + + for opt in options: + prefix = split_opt(opt)[0] + + if prefix == "/": + any_prefix_is_slash = True + + rv.append((len(prefix), opt)) + + rv.sort(key=lambda x: x[0]) + return ", ".join(x[1] for x in rv), any_prefix_is_slash diff --git a/.venv/lib/python3.9/site-packages/click/globals.py b/.venv/lib/python3.9/site-packages/click/globals.py new file mode 100644 index 00000000..191e712d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/globals.py @@ -0,0 +1,67 @@ +import typing as t +from threading import local + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .core import Context + +_local = local() + + +@t.overload +def get_current_context(silent: "te.Literal[False]" = False) -> "Context": ... + + +@t.overload +def get_current_context(silent: bool = ...) -> t.Optional["Context"]: ... + + +def get_current_context(silent: bool = False) -> t.Optional["Context"]: + """Returns the current click context. This can be used as a way to + access the current context object from anywhere. This is a more implicit + alternative to the :func:`pass_context` decorator. This function is + primarily useful for helpers such as :func:`echo` which might be + interested in changing its behavior based on the current context. + + To push the current context, :meth:`Context.scope` can be used. + + .. versionadded:: 5.0 + + :param silent: if set to `True` the return value is `None` if no context + is available. The default behavior is to raise a + :exc:`RuntimeError`. + """ + try: + return t.cast("Context", _local.stack[-1]) + except (AttributeError, IndexError) as e: + if not silent: + raise RuntimeError("There is no active click context.") from e + + return None + + +def push_context(ctx: "Context") -> None: + """Pushes a new context to the current stack.""" + _local.__dict__.setdefault("stack", []).append(ctx) + + +def pop_context() -> None: + """Removes the top level from the stack.""" + _local.stack.pop() + + +def resolve_color_default(color: t.Optional[bool] = None) -> t.Optional[bool]: + """Internal helper to get the default value of the color flag. If a + value is passed it's returned unchanged, otherwise it's looked up from + the current context. + """ + if color is not None: + return color + + ctx = get_current_context(silent=True) + + if ctx is not None: + return ctx.color + + return None diff --git a/.venv/lib/python3.9/site-packages/click/parser.py b/.venv/lib/python3.9/site-packages/click/parser.py new file mode 100644 index 00000000..600b8436 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/parser.py @@ -0,0 +1,531 @@ +""" +This module started out as largely a copy paste from the stdlib's +optparse module with the features removed that we do not need from +optparse because we implement them in Click on a higher level (for +instance type handling, help formatting and a lot more). + +The plan is to remove more and more from here over time. + +The reason this is a different module and not optparse from the stdlib +is that there are differences in 2.x and 3.x about the error messages +generated and optparse in the stdlib uses gettext for no good reason +and might cause us issues. + +Click uses parts of optparse written by Gregory P. Ward and maintained +by the Python Software Foundation. This is limited to code in parser.py. + +Copyright 2001-2006 Gregory P. Ward. All rights reserved. +Copyright 2002-2006 Python Software Foundation. All rights reserved. +""" + +# This code uses parts of optparse written by Gregory P. Ward and +# maintained by the Python Software Foundation. +# Copyright 2001-2006 Gregory P. Ward +# Copyright 2002-2006 Python Software Foundation +import typing as t +from collections import deque +from gettext import gettext as _ +from gettext import ngettext + +from .exceptions import BadArgumentUsage +from .exceptions import BadOptionUsage +from .exceptions import NoSuchOption +from .exceptions import UsageError + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .core import Argument as CoreArgument + from .core import Context + from .core import Option as CoreOption + from .core import Parameter as CoreParameter + +V = t.TypeVar("V") + +# Sentinel value that indicates an option was passed as a flag without a +# value but is not a flag option. Option.consume_value uses this to +# prompt or use the flag_value. +_flag_needs_value = object() + + +def _unpack_args( + args: t.Sequence[str], nargs_spec: t.Sequence[int] +) -> t.Tuple[t.Sequence[t.Union[str, t.Sequence[t.Optional[str]], None]], t.List[str]]: + """Given an iterable of arguments and an iterable of nargs specifications, + it returns a tuple with all the unpacked arguments at the first index + and all remaining arguments as the second. + + The nargs specification is the number of arguments that should be consumed + or `-1` to indicate that this position should eat up all the remainders. + + Missing items are filled with `None`. + """ + args = deque(args) + nargs_spec = deque(nargs_spec) + rv: t.List[t.Union[str, t.Tuple[t.Optional[str], ...], None]] = [] + spos: t.Optional[int] = None + + def _fetch(c: "te.Deque[V]") -> t.Optional[V]: + try: + if spos is None: + return c.popleft() + else: + return c.pop() + except IndexError: + return None + + while nargs_spec: + nargs = _fetch(nargs_spec) + + if nargs is None: + continue + + if nargs == 1: + rv.append(_fetch(args)) + elif nargs > 1: + x = [_fetch(args) for _ in range(nargs)] + + # If we're reversed, we're pulling in the arguments in reverse, + # so we need to turn them around. + if spos is not None: + x.reverse() + + rv.append(tuple(x)) + elif nargs < 0: + if spos is not None: + raise TypeError("Cannot have two nargs < 0") + + spos = len(rv) + rv.append(None) + + # spos is the position of the wildcard (star). If it's not `None`, + # we fill it with the remainder. + if spos is not None: + rv[spos] = tuple(args) + args = [] + rv[spos + 1 :] = reversed(rv[spos + 1 :]) + + return tuple(rv), list(args) + + +def split_opt(opt: str) -> t.Tuple[str, str]: + first = opt[:1] + if first.isalnum(): + return "", opt + if opt[1:2] == first: + return opt[:2], opt[2:] + return first, opt[1:] + + +def normalize_opt(opt: str, ctx: t.Optional["Context"]) -> str: + if ctx is None or ctx.token_normalize_func is None: + return opt + prefix, opt = split_opt(opt) + return f"{prefix}{ctx.token_normalize_func(opt)}" + + +def split_arg_string(string: str) -> t.List[str]: + """Split an argument string as with :func:`shlex.split`, but don't + fail if the string is incomplete. Ignores a missing closing quote or + incomplete escape sequence and uses the partial token as-is. + + .. code-block:: python + + split_arg_string("example 'my file") + ["example", "my file"] + + split_arg_string("example my\\") + ["example", "my"] + + :param string: String to split. + """ + import shlex + + lex = shlex.shlex(string, posix=True) + lex.whitespace_split = True + lex.commenters = "" + out = [] + + try: + for token in lex: + out.append(token) + except ValueError: + # Raised when end-of-string is reached in an invalid state. Use + # the partial token as-is. The quote or escape character is in + # lex.state, not lex.token. + out.append(lex.token) + + return out + + +class Option: + def __init__( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = None, + ): + self._short_opts = [] + self._long_opts = [] + self.prefixes: t.Set[str] = set() + + for opt in opts: + prefix, value = split_opt(opt) + if not prefix: + raise ValueError(f"Invalid start character for option ({opt})") + self.prefixes.add(prefix[0]) + if len(prefix) == 1 and len(value) == 1: + self._short_opts.append(opt) + else: + self._long_opts.append(opt) + self.prefixes.add(prefix) + + if action is None: + action = "store" + + self.dest = dest + self.action = action + self.nargs = nargs + self.const = const + self.obj = obj + + @property + def takes_value(self) -> bool: + return self.action in ("store", "append") + + def process(self, value: t.Any, state: "ParsingState") -> None: + if self.action == "store": + state.opts[self.dest] = value # type: ignore + elif self.action == "store_const": + state.opts[self.dest] = self.const # type: ignore + elif self.action == "append": + state.opts.setdefault(self.dest, []).append(value) # type: ignore + elif self.action == "append_const": + state.opts.setdefault(self.dest, []).append(self.const) # type: ignore + elif self.action == "count": + state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore + else: + raise ValueError(f"unknown action '{self.action}'") + state.order.append(self.obj) + + +class Argument: + def __init__(self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1): + self.dest = dest + self.nargs = nargs + self.obj = obj + + def process( + self, + value: t.Union[t.Optional[str], t.Sequence[t.Optional[str]]], + state: "ParsingState", + ) -> None: + if self.nargs > 1: + assert value is not None + holes = sum(1 for x in value if x is None) + if holes == len(value): + value = None + elif holes != 0: + raise BadArgumentUsage( + _("Argument {name!r} takes {nargs} values.").format( + name=self.dest, nargs=self.nargs + ) + ) + + if self.nargs == -1 and self.obj.envvar is not None and value == (): + # Replace empty tuple with None so that a value from the + # environment may be tried. + value = None + + state.opts[self.dest] = value # type: ignore + state.order.append(self.obj) + + +class ParsingState: + def __init__(self, rargs: t.List[str]) -> None: + self.opts: t.Dict[str, t.Any] = {} + self.largs: t.List[str] = [] + self.rargs = rargs + self.order: t.List[CoreParameter] = [] + + +class OptionParser: + """The option parser is an internal class that is ultimately used to + parse options and arguments. It's modelled after optparse and brings + a similar but vastly simplified API. It should generally not be used + directly as the high level Click classes wrap it for you. + + It's not nearly as extensible as optparse or argparse as it does not + implement features that are implemented on a higher level (such as + types or defaults). + + :param ctx: optionally the :class:`~click.Context` where this parser + should go with. + """ + + def __init__(self, ctx: t.Optional["Context"] = None) -> None: + #: The :class:`~click.Context` for this parser. This might be + #: `None` for some advanced use cases. + self.ctx = ctx + #: This controls how the parser deals with interspersed arguments. + #: If this is set to `False`, the parser will stop on the first + #: non-option. Click uses this to implement nested subcommands + #: safely. + self.allow_interspersed_args: bool = True + #: This tells the parser how to deal with unknown options. By + #: default it will error out (which is sensible), but there is a + #: second mode where it will ignore it and continue processing + #: after shifting all the unknown options into the resulting args. + self.ignore_unknown_options: bool = False + + if ctx is not None: + self.allow_interspersed_args = ctx.allow_interspersed_args + self.ignore_unknown_options = ctx.ignore_unknown_options + + self._short_opt: t.Dict[str, Option] = {} + self._long_opt: t.Dict[str, Option] = {} + self._opt_prefixes = {"-", "--"} + self._args: t.List[Argument] = [] + + def add_option( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = None, + ) -> None: + """Adds a new option named `dest` to the parser. The destination + is not inferred (unlike with optparse) and needs to be explicitly + provided. Action can be any of ``store``, ``store_const``, + ``append``, ``append_const`` or ``count``. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + opts = [normalize_opt(opt, self.ctx) for opt in opts] + option = Option(obj, opts, dest, action=action, nargs=nargs, const=const) + self._opt_prefixes.update(option.prefixes) + for opt in option._short_opts: + self._short_opt[opt] = option + for opt in option._long_opts: + self._long_opt[opt] = option + + def add_argument( + self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1 + ) -> None: + """Adds a positional argument named `dest` to the parser. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + self._args.append(Argument(obj, dest=dest, nargs=nargs)) + + def parse_args( + self, args: t.List[str] + ) -> t.Tuple[t.Dict[str, t.Any], t.List[str], t.List["CoreParameter"]]: + """Parses positional arguments and returns ``(values, args, order)`` + for the parsed options and arguments as well as the leftover + arguments if there are any. The order is a list of objects as they + appear on the command line. If arguments appear multiple times they + will be memorized multiple times as well. + """ + state = ParsingState(args) + try: + self._process_args_for_options(state) + self._process_args_for_args(state) + except UsageError: + if self.ctx is None or not self.ctx.resilient_parsing: + raise + return state.opts, state.largs, state.order + + def _process_args_for_args(self, state: ParsingState) -> None: + pargs, args = _unpack_args( + state.largs + state.rargs, [x.nargs for x in self._args] + ) + + for idx, arg in enumerate(self._args): + arg.process(pargs[idx], state) + + state.largs = args + state.rargs = [] + + def _process_args_for_options(self, state: ParsingState) -> None: + while state.rargs: + arg = state.rargs.pop(0) + arglen = len(arg) + # Double dashes always handled explicitly regardless of what + # prefixes are valid. + if arg == "--": + return + elif arg[:1] in self._opt_prefixes and arglen > 1: + self._process_opts(arg, state) + elif self.allow_interspersed_args: + state.largs.append(arg) + else: + state.rargs.insert(0, arg) + return + + # Say this is the original argument list: + # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] + # ^ + # (we are about to process arg(i)). + # + # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of + # [arg0, ..., arg(i-1)] (any options and their arguments will have + # been removed from largs). + # + # The while loop will usually consume 1 or more arguments per pass. + # If it consumes 1 (eg. arg is an option that takes no arguments), + # then after _process_arg() is done the situation is: + # + # largs = subset of [arg0, ..., arg(i)] + # rargs = [arg(i+1), ..., arg(N-1)] + # + # If allow_interspersed_args is false, largs will always be + # *empty* -- still a subset of [arg0, ..., arg(i-1)], but + # not a very interesting subset! + + def _match_long_opt( + self, opt: str, explicit_value: t.Optional[str], state: ParsingState + ) -> None: + if opt not in self._long_opt: + from difflib import get_close_matches + + possibilities = get_close_matches(opt, self._long_opt) + raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) + + option = self._long_opt[opt] + if option.takes_value: + # At this point it's safe to modify rargs by injecting the + # explicit value, because no exception is raised in this + # branch. This means that the inserted value will be fully + # consumed. + if explicit_value is not None: + state.rargs.insert(0, explicit_value) + + value = self._get_value_from_state(opt, option, state) + + elif explicit_value is not None: + raise BadOptionUsage( + opt, _("Option {name!r} does not take a value.").format(name=opt) + ) + + else: + value = None + + option.process(value, state) + + def _match_short_opt(self, arg: str, state: ParsingState) -> None: + stop = False + i = 1 + prefix = arg[0] + unknown_options = [] + + for ch in arg[1:]: + opt = normalize_opt(f"{prefix}{ch}", self.ctx) + option = self._short_opt.get(opt) + i += 1 + + if not option: + if self.ignore_unknown_options: + unknown_options.append(ch) + continue + raise NoSuchOption(opt, ctx=self.ctx) + if option.takes_value: + # Any characters left in arg? Pretend they're the + # next arg, and stop consuming characters of arg. + if i < len(arg): + state.rargs.insert(0, arg[i:]) + stop = True + + value = self._get_value_from_state(opt, option, state) + + else: + value = None + + option.process(value, state) + + if stop: + break + + # If we got any unknown options we recombine the string of the + # remaining options and re-attach the prefix, then report that + # to the state as new larg. This way there is basic combinatorics + # that can be achieved while still ignoring unknown arguments. + if self.ignore_unknown_options and unknown_options: + state.largs.append(f"{prefix}{''.join(unknown_options)}") + + def _get_value_from_state( + self, option_name: str, option: Option, state: ParsingState + ) -> t.Any: + nargs = option.nargs + + if len(state.rargs) < nargs: + if option.obj._flag_needs_value: + # Option allows omitting the value. + value = _flag_needs_value + else: + raise BadOptionUsage( + option_name, + ngettext( + "Option {name!r} requires an argument.", + "Option {name!r} requires {nargs} arguments.", + nargs, + ).format(name=option_name, nargs=nargs), + ) + elif nargs == 1: + next_rarg = state.rargs[0] + + if ( + option.obj._flag_needs_value + and isinstance(next_rarg, str) + and next_rarg[:1] in self._opt_prefixes + and len(next_rarg) > 1 + ): + # The next arg looks like the start of an option, don't + # use it as the value if omitting the value is allowed. + value = _flag_needs_value + else: + value = state.rargs.pop(0) + else: + value = tuple(state.rargs[:nargs]) + del state.rargs[:nargs] + + return value + + def _process_opts(self, arg: str, state: ParsingState) -> None: + explicit_value = None + # Long option handling happens in two parts. The first part is + # supporting explicitly attached values. In any case, we will try + # to long match the option first. + if "=" in arg: + long_opt, explicit_value = arg.split("=", 1) + else: + long_opt = arg + norm_long_opt = normalize_opt(long_opt, self.ctx) + + # At this point we will match the (assumed) long option through + # the long option matching code. Note that this allows options + # like "-foo" to be matched as long options. + try: + self._match_long_opt(norm_long_opt, explicit_value, state) + except NoSuchOption: + # At this point the long option matching failed, and we need + # to try with short options. However there is a special rule + # which says, that if we have a two character options prefix + # (applies to "--foo" for instance), we do not dispatch to the + # short option code and will instead raise the no option + # error. + if arg[:2] not in self._opt_prefixes: + self._match_short_opt(arg, state) + return + + if not self.ignore_unknown_options: + raise + + state.largs.append(arg) diff --git a/.venv/lib/python3.9/site-packages/click/py.typed b/.venv/lib/python3.9/site-packages/click/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.9/site-packages/click/shell_completion.py b/.venv/lib/python3.9/site-packages/click/shell_completion.py new file mode 100644 index 00000000..07d0f09b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/shell_completion.py @@ -0,0 +1,603 @@ +import os +import re +import typing as t +from gettext import gettext as _ + +from .core import Argument +from .core import BaseCommand +from .core import Context +from .core import MultiCommand +from .core import Option +from .core import Parameter +from .core import ParameterSource +from .parser import split_arg_string +from .utils import echo + + +def shell_complete( + cli: BaseCommand, + ctx_args: t.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str, + instruction: str, +) -> int: + """Perform shell completion for the given CLI program. + + :param cli: Command being called. + :param ctx_args: Extra arguments to pass to + ``cli.make_context``. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + :param instruction: Value of ``complete_var`` with the completion + instruction and shell, in the form ``instruction_shell``. + :return: Status code to exit with. + """ + shell, _, instruction = instruction.partition("_") + comp_cls = get_completion_class(shell) + + if comp_cls is None: + return 1 + + comp = comp_cls(cli, ctx_args, prog_name, complete_var) + + if instruction == "source": + echo(comp.source()) + return 0 + + if instruction == "complete": + echo(comp.complete()) + return 0 + + return 1 + + +class CompletionItem: + """Represents a completion value and metadata about the value. The + default metadata is ``type`` to indicate special shell handling, + and ``help`` if a shell supports showing a help string next to the + value. + + Arbitrary parameters can be passed when creating the object, and + accessed using ``item.attr``. If an attribute wasn't passed, + accessing it returns ``None``. + + :param value: The completion suggestion. + :param type: Tells the shell script to provide special completion + support for the type. Click uses ``"dir"`` and ``"file"``. + :param help: String shown next to the value if supported. + :param kwargs: Arbitrary metadata. The built-in implementations + don't use this, but custom type completions paired with custom + shell support could use it. + """ + + __slots__ = ("value", "type", "help", "_info") + + def __init__( + self, + value: t.Any, + type: str = "plain", + help: t.Optional[str] = None, + **kwargs: t.Any, + ) -> None: + self.value: t.Any = value + self.type: str = type + self.help: t.Optional[str] = help + self._info = kwargs + + def __getattr__(self, name: str) -> t.Any: + return self._info.get(name) + + +# Only Bash >= 4.4 has the nosort option. +_SOURCE_BASH = """\ +%(complete_func)s() { + local IFS=$'\\n' + local response + + response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \ +%(complete_var)s=bash_complete $1) + + for completion in $response; do + IFS=',' read type value <<< "$completion" + + if [[ $type == 'dir' ]]; then + COMPREPLY=() + compopt -o dirnames + elif [[ $type == 'file' ]]; then + COMPREPLY=() + compopt -o default + elif [[ $type == 'plain' ]]; then + COMPREPLY+=($value) + fi + done + + return 0 +} + +%(complete_func)s_setup() { + complete -o nosort -F %(complete_func)s %(prog_name)s +} + +%(complete_func)s_setup; +""" + +_SOURCE_ZSH = """\ +#compdef %(prog_name)s + +%(complete_func)s() { + local -a completions + local -a completions_with_descriptions + local -a response + (( ! $+commands[%(prog_name)s] )) && return 1 + + response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \ +%(complete_var)s=zsh_complete %(prog_name)s)}") + + for type key descr in ${response}; do + if [[ "$type" == "plain" ]]; then + if [[ "$descr" == "_" ]]; then + completions+=("$key") + else + completions_with_descriptions+=("$key":"$descr") + fi + elif [[ "$type" == "dir" ]]; then + _path_files -/ + elif [[ "$type" == "file" ]]; then + _path_files -f + fi + done + + if [ -n "$completions_with_descriptions" ]; then + _describe -V unsorted completions_with_descriptions -U + fi + + if [ -n "$completions" ]; then + compadd -U -V unsorted -a completions + fi +} + +if [[ $zsh_eval_context[-1] == loadautofunc ]]; then + # autoload from fpath, call function directly + %(complete_func)s "$@" +else + # eval/source/. command, register function for later + compdef %(complete_func)s %(prog_name)s +fi +""" + +_SOURCE_FISH = """\ +function %(complete_func)s; + set -l response (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \ +COMP_CWORD=(commandline -t) %(prog_name)s); + + for completion in $response; + set -l metadata (string split "," $completion); + + if test $metadata[1] = "dir"; + __fish_complete_directories $metadata[2]; + else if test $metadata[1] = "file"; + __fish_complete_path $metadata[2]; + else if test $metadata[1] = "plain"; + echo $metadata[2]; + end; + end; +end; + +complete --no-files --command %(prog_name)s --arguments \ +"(%(complete_func)s)"; +""" + + +class ShellComplete: + """Base class for providing shell completion support. A subclass for + a given shell will override attributes and methods to implement the + completion instructions (``source`` and ``complete``). + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + + .. versionadded:: 8.0 + """ + + name: t.ClassVar[str] + """Name to register the shell as with :func:`add_completion_class`. + This is used in completion instructions (``{name}_source`` and + ``{name}_complete``). + """ + + source_template: t.ClassVar[str] + """Completion script template formatted by :meth:`source`. This must + be provided by subclasses. + """ + + def __init__( + self, + cli: BaseCommand, + ctx_args: t.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str, + ) -> None: + self.cli = cli + self.ctx_args = ctx_args + self.prog_name = prog_name + self.complete_var = complete_var + + @property + def func_name(self) -> str: + """The name of the shell function defined by the completion + script. + """ + safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), flags=re.ASCII) + return f"_{safe_name}_completion" + + def source_vars(self) -> t.Dict[str, t.Any]: + """Vars for formatting :attr:`source_template`. + + By default this provides ``complete_func``, ``complete_var``, + and ``prog_name``. + """ + return { + "complete_func": self.func_name, + "complete_var": self.complete_var, + "prog_name": self.prog_name, + } + + def source(self) -> str: + """Produce the shell script that defines the completion + function. By default this ``%``-style formats + :attr:`source_template` with the dict returned by + :meth:`source_vars`. + """ + return self.source_template % self.source_vars() + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + """Use the env vars defined by the shell script to return a + tuple of ``args, incomplete``. This must be implemented by + subclasses. + """ + raise NotImplementedError + + def get_completions( + self, args: t.List[str], incomplete: str + ) -> t.List[CompletionItem]: + """Determine the context and last complete command or parameter + from the complete args. Call that object's ``shell_complete`` + method to get the completions for the incomplete value. + + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args) + obj, incomplete = _resolve_incomplete(ctx, args, incomplete) + return obj.shell_complete(ctx, incomplete) + + def format_completion(self, item: CompletionItem) -> str: + """Format a completion item into the form recognized by the + shell script. This must be implemented by subclasses. + + :param item: Completion item to format. + """ + raise NotImplementedError + + def complete(self) -> str: + """Produce the completion data to send back to the shell. + + By default this calls :meth:`get_completion_args`, gets the + completions, then calls :meth:`format_completion` for each + completion. + """ + args, incomplete = self.get_completion_args() + completions = self.get_completions(args, incomplete) + out = [self.format_completion(item) for item in completions] + return "\n".join(out) + + +class BashComplete(ShellComplete): + """Shell completion for Bash.""" + + name = "bash" + source_template = _SOURCE_BASH + + @staticmethod + def _check_version() -> None: + import shutil + import subprocess + + bash_exe = shutil.which("bash") + + if bash_exe is None: + match = None + else: + output = subprocess.run( + [bash_exe, "--norc", "-c", 'echo "${BASH_VERSION}"'], + stdout=subprocess.PIPE, + ) + match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) + + if match is not None: + major, minor = match.groups() + + if major < "4" or major == "4" and minor < "4": + echo( + _( + "Shell completion is not supported for Bash" + " versions older than 4.4." + ), + err=True, + ) + else: + echo( + _("Couldn't detect Bash version, shell completion is not supported."), + err=True, + ) + + def source(self) -> str: + self._check_version() + return super().source() + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type},{item.value}" + + +class ZshComplete(ShellComplete): + """Shell completion for Zsh.""" + + name = "zsh" + source_template = _SOURCE_ZSH + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}" + + +class FishComplete(ShellComplete): + """Shell completion for Fish.""" + + name = "fish" + source_template = _SOURCE_FISH + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + incomplete = os.environ["COMP_CWORD"] + args = cwords[1:] + + # Fish stores the partial word in both COMP_WORDS and + # COMP_CWORD, remove it from complete args. + if incomplete and args and args[-1] == incomplete: + args.pop() + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + if item.help: + return f"{item.type},{item.value}\t{item.help}" + + return f"{item.type},{item.value}" + + +ShellCompleteType = t.TypeVar("ShellCompleteType", bound=t.Type[ShellComplete]) + + +_available_shells: t.Dict[str, t.Type[ShellComplete]] = { + "bash": BashComplete, + "fish": FishComplete, + "zsh": ZshComplete, +} + + +def add_completion_class( + cls: ShellCompleteType, name: t.Optional[str] = None +) -> ShellCompleteType: + """Register a :class:`ShellComplete` subclass under the given name. + The name will be provided by the completion instruction environment + variable during completion. + + :param cls: The completion class that will handle completion for the + shell. + :param name: Name to register the class under. Defaults to the + class's ``name`` attribute. + """ + if name is None: + name = cls.name + + _available_shells[name] = cls + + return cls + + +def get_completion_class(shell: str) -> t.Optional[t.Type[ShellComplete]]: + """Look up a registered :class:`ShellComplete` subclass by the name + provided by the completion instruction environment variable. If the + name isn't registered, returns ``None``. + + :param shell: Name the class is registered under. + """ + return _available_shells.get(shell) + + +def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: + """Determine if the given parameter is an argument that can still + accept values. + + :param ctx: Invocation context for the command represented by the + parsed complete args. + :param param: Argument object being checked. + """ + if not isinstance(param, Argument): + return False + + assert param.name is not None + # Will be None if expose_value is False. + value = ctx.params.get(param.name) + return ( + param.nargs == -1 + or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE + or ( + param.nargs > 1 + and isinstance(value, (tuple, list)) + and len(value) < param.nargs + ) + ) + + +def _start_of_option(ctx: Context, value: str) -> bool: + """Check if the value looks like the start of an option.""" + if not value: + return False + + c = value[0] + return c in ctx._opt_prefixes + + +def _is_incomplete_option(ctx: Context, args: t.List[str], param: Parameter) -> bool: + """Determine if the given parameter is an option that needs a value. + + :param args: List of complete args before the incomplete value. + :param param: Option object being checked. + """ + if not isinstance(param, Option): + return False + + if param.is_flag or param.count: + return False + + last_option = None + + for index, arg in enumerate(reversed(args)): + if index + 1 > param.nargs: + break + + if _start_of_option(ctx, arg): + last_option = arg + + return last_option is not None and last_option in param.opts + + +def _resolve_context( + cli: BaseCommand, + ctx_args: t.MutableMapping[str, t.Any], + prog_name: str, + args: t.List[str], +) -> Context: + """Produce the context hierarchy starting with the command and + traversing the complete arguments. This only follows the commands, + it doesn't trigger input prompts or callbacks. + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param args: List of complete args before the incomplete value. + """ + ctx_args["resilient_parsing"] = True + ctx = cli.make_context(prog_name, args.copy(), **ctx_args) + args = ctx.protected_args + ctx.args + + while args: + command = ctx.command + + if isinstance(command, MultiCommand): + if not command.chain: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + ctx = cmd.make_context(name, args, parent=ctx, resilient_parsing=True) + args = ctx.protected_args + ctx.args + else: + sub_ctx = ctx + + while args: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + sub_ctx = cmd.make_context( + name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True, + ) + args = sub_ctx.args + + ctx = sub_ctx + args = [*sub_ctx.protected_args, *sub_ctx.args] + else: + break + + return ctx + + +def _resolve_incomplete( + ctx: Context, args: t.List[str], incomplete: str +) -> t.Tuple[t.Union[BaseCommand, Parameter], str]: + """Find the Click object that will handle the completion of the + incomplete value. Return the object and the incomplete value. + + :param ctx: Invocation context for the command represented by + the parsed complete args. + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + # Different shells treat an "=" between a long option name and + # value differently. Might keep the value joined, return the "=" + # as a separate item, or return the split name and value. Always + # split and discard the "=" to make completion easier. + if incomplete == "=": + incomplete = "" + elif "=" in incomplete and _start_of_option(ctx, incomplete): + name, _, incomplete = incomplete.partition("=") + args.append(name) + + # The "--" marker tells Click to stop treating values as options + # even if they start with the option character. If it hasn't been + # given and the incomplete arg looks like an option, the current + # command will provide option name completions. + if "--" not in args and _start_of_option(ctx, incomplete): + return ctx.command, incomplete + + params = ctx.command.get_params(ctx) + + # If the last complete arg is an option name with an incomplete + # value, the option will provide value completions. + for param in params: + if _is_incomplete_option(ctx, args, param): + return param, incomplete + + # It's not an option name or value. The first argument without a + # parsed value will provide value completions. + for param in params: + if _is_incomplete_argument(ctx, param): + return param, incomplete + + # There were no unparsed arguments, the command may be a group that + # will provide command name completions. + return ctx.command, incomplete diff --git a/.venv/lib/python3.9/site-packages/click/termui.py b/.venv/lib/python3.9/site-packages/click/termui.py new file mode 100644 index 00000000..c084f196 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/termui.py @@ -0,0 +1,784 @@ +import inspect +import io +import itertools +import sys +import typing as t +from gettext import gettext as _ + +from ._compat import isatty +from ._compat import strip_ansi +from .exceptions import Abort +from .exceptions import UsageError +from .globals import resolve_color_default +from .types import Choice +from .types import convert_type +from .types import ParamType +from .utils import echo +from .utils import LazyFile + +if t.TYPE_CHECKING: + from ._termui_impl import ProgressBar + +V = t.TypeVar("V") + +# The prompt functions to use. The doc tools currently override these +# functions to customize how they work. +visible_prompt_func: t.Callable[[str], str] = input + +_ansi_colors = { + "black": 30, + "red": 31, + "green": 32, + "yellow": 33, + "blue": 34, + "magenta": 35, + "cyan": 36, + "white": 37, + "reset": 39, + "bright_black": 90, + "bright_red": 91, + "bright_green": 92, + "bright_yellow": 93, + "bright_blue": 94, + "bright_magenta": 95, + "bright_cyan": 96, + "bright_white": 97, +} +_ansi_reset_all = "\033[0m" + + +def hidden_prompt_func(prompt: str) -> str: + import getpass + + return getpass.getpass(prompt) + + +def _build_prompt( + text: str, + suffix: str, + show_default: bool = False, + default: t.Optional[t.Any] = None, + show_choices: bool = True, + type: t.Optional[ParamType] = None, +) -> str: + prompt = text + if type is not None and show_choices and isinstance(type, Choice): + prompt += f" ({', '.join(map(str, type.choices))})" + if default is not None and show_default: + prompt = f"{prompt} [{_format_default(default)}]" + return f"{prompt}{suffix}" + + +def _format_default(default: t.Any) -> t.Any: + if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"): + return default.name + + return default + + +def prompt( + text: str, + default: t.Optional[t.Any] = None, + hide_input: bool = False, + confirmation_prompt: t.Union[bool, str] = False, + type: t.Optional[t.Union[ParamType, t.Any]] = None, + value_proc: t.Optional[t.Callable[[str], t.Any]] = None, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, + show_choices: bool = True, +) -> t.Any: + """Prompts a user for input. This is a convenience function that can + be used to prompt a user for input later. + + If the user aborts the input by sending an interrupt signal, this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the text to show for the prompt. + :param default: the default value to use if no input happens. If this + is not given it will prompt until it's aborted. + :param hide_input: if this is set to true then the input value will + be hidden. + :param confirmation_prompt: Prompt a second time to confirm the + value. Can be set to a string instead of ``True`` to customize + the message. + :param type: the type to use to check the value against. + :param value_proc: if this parameter is provided it's a function that + is invoked instead of the type conversion to + convert a value. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + :param show_choices: Show or hide choices if the passed type is a Choice. + For example if type is a Choice of either day or week, + show_choices is true and text is "Group by" then the + prompt will be "Group by (day, week): ". + + .. versionadded:: 8.0 + ``confirmation_prompt`` can be a custom string. + + .. versionadded:: 7.0 + Added the ``show_choices`` parameter. + + .. versionadded:: 6.0 + Added unicode support for cmd.exe on Windows. + + .. versionadded:: 4.0 + Added the `err` parameter. + + """ + + def prompt_func(text: str) -> str: + f = hidden_prompt_func if hide_input else visible_prompt_func + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(text.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + return f(" ") + except (KeyboardInterrupt, EOFError): + # getpass doesn't print a newline if the user aborts input with ^C. + # Allegedly this behavior is inherited from getpass(3). + # A doc bug has been filed at https://bugs.python.org/issue24711 + if hide_input: + echo(None, err=err) + raise Abort() from None + + if value_proc is None: + value_proc = convert_type(type, default) + + prompt = _build_prompt( + text, prompt_suffix, show_default, default, show_choices, type + ) + + if confirmation_prompt: + if confirmation_prompt is True: + confirmation_prompt = _("Repeat for confirmation") + + confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix) + + while True: + while True: + value = prompt_func(prompt) + if value: + break + elif default is not None: + value = default + break + try: + result = value_proc(value) + except UsageError as e: + if hide_input: + echo(_("Error: The value you entered was invalid."), err=err) + else: + echo(_("Error: {e.message}").format(e=e), err=err) + continue + if not confirmation_prompt: + return result + while True: + value2 = prompt_func(confirmation_prompt) + is_empty = not value and not value2 + if value2 or is_empty: + break + if value == value2: + return result + echo(_("Error: The two entered values do not match."), err=err) + + +def confirm( + text: str, + default: t.Optional[bool] = False, + abort: bool = False, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, +) -> bool: + """Prompts for confirmation (yes/no question). + + If the user aborts the input by sending a interrupt signal this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the question to ask. + :param default: The default value to use when no input is given. If + ``None``, repeat until input is given. + :param abort: if this is set to `True` a negative answer aborts the + exception by raising :exc:`Abort`. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + + .. versionchanged:: 8.0 + Repeat until input is given if ``default`` is ``None``. + + .. versionadded:: 4.0 + Added the ``err`` parameter. + """ + prompt = _build_prompt( + text, + prompt_suffix, + show_default, + "y/n" if default is None else ("Y/n" if default else "y/N"), + ) + + while True: + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(prompt.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + value = visible_prompt_func(" ").lower().strip() + except (KeyboardInterrupt, EOFError): + raise Abort() from None + if value in ("y", "yes"): + rv = True + elif value in ("n", "no"): + rv = False + elif default is not None and value == "": + rv = default + else: + echo(_("Error: invalid input"), err=err) + continue + break + if abort and not rv: + raise Abort() + return rv + + +def echo_via_pager( + text_or_generator: t.Union[t.Iterable[str], t.Callable[[], t.Iterable[str]], str], + color: t.Optional[bool] = None, +) -> None: + """This function takes a text and shows it via an environment specific + pager on stdout. + + .. versionchanged:: 3.0 + Added the `color` flag. + + :param text_or_generator: the text to page, or alternatively, a + generator emitting the text to page. + :param color: controls if the pager supports ANSI colors or not. The + default is autodetection. + """ + color = resolve_color_default(color) + + if inspect.isgeneratorfunction(text_or_generator): + i = t.cast(t.Callable[[], t.Iterable[str]], text_or_generator)() + elif isinstance(text_or_generator, str): + i = [text_or_generator] + else: + i = iter(t.cast(t.Iterable[str], text_or_generator)) + + # convert every element of i to a text type if necessary + text_generator = (el if isinstance(el, str) else str(el) for el in i) + + from ._termui_impl import pager + + return pager(itertools.chain(text_generator, "\n"), color) + + +def progressbar( + iterable: t.Optional[t.Iterable[V]] = None, + length: t.Optional[int] = None, + label: t.Optional[str] = None, + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = None, + update_min_steps: int = 1, +) -> "ProgressBar[V]": + """This function creates an iterable context manager that can be used + to iterate over something while showing a progress bar. It will + either iterate over the `iterable` or `length` items (that are counted + up). While iteration happens, this function will print a rendered + progress bar to the given `file` (defaults to stdout) and will attempt + to calculate remaining time and more. By default, this progress bar + will not be rendered if the file is not a terminal. + + The context manager creates the progress bar. When the context + manager is entered the progress bar is already created. With every + iteration over the progress bar, the iterable passed to the bar is + advanced and the bar is updated. When the context manager exits, + a newline is printed and the progress bar is finalized on screen. + + Note: The progress bar is currently designed for use cases where the + total progress can be expected to take at least several seconds. + Because of this, the ProgressBar class object won't display + progress that is considered too fast, and progress where the time + between steps is less than a second. + + No printing must happen or the progress bar will be unintentionally + destroyed. + + Example usage:: + + with progressbar(items) as bar: + for item in bar: + do_something_with(item) + + Alternatively, if no iterable is specified, one can manually update the + progress bar through the `update()` method instead of directly + iterating over the progress bar. The update method accepts the number + of steps to increment the bar with:: + + with progressbar(length=chunks.total_bytes) as bar: + for chunk in chunks: + process_chunk(chunk) + bar.update(chunks.bytes) + + The ``update()`` method also takes an optional value specifying the + ``current_item`` at the new position. This is useful when used + together with ``item_show_func`` to customize the output for each + manual step:: + + with click.progressbar( + length=total_size, + label='Unzipping archive', + item_show_func=lambda a: a.filename + ) as bar: + for archive in zip_file: + archive.extract() + bar.update(archive.size, archive) + + :param iterable: an iterable to iterate over. If not provided the length + is required. + :param length: the number of items to iterate over. By default the + progressbar will attempt to ask the iterator about its + length, which might or might not work. If an iterable is + also provided this parameter can be used to override the + length. If an iterable is not provided the progress bar + will iterate over a range of that length. + :param label: the label to show next to the progress bar. + :param show_eta: enables or disables the estimated time display. This is + automatically disabled if the length cannot be + determined. + :param show_percent: enables or disables the percentage display. The + default is `True` if the iterable has a length or + `False` if not. + :param show_pos: enables or disables the absolute position display. The + default is `False`. + :param item_show_func: A function called with the current item which + can return a string to show next to the progress bar. If the + function returns ``None`` nothing is shown. The current item can + be ``None``, such as when entering and exiting the bar. + :param fill_char: the character to use to show the filled part of the + progress bar. + :param empty_char: the character to use to show the non-filled part of + the progress bar. + :param bar_template: the format string to use as template for the bar. + The parameters in it are ``label`` for the label, + ``bar`` for the progress bar and ``info`` for the + info section. + :param info_sep: the separator between multiple info items (eta etc.) + :param width: the width of the progress bar in characters, 0 means full + terminal width + :param file: The file to write to. If this is not a terminal then + only the label is printed. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are included anywhere in the progress bar output + which is not the case by default. + :param update_min_steps: Render only when this many updates have + completed. This allows tuning for very fast iterators. + + .. versionchanged:: 8.0 + Output is shown even if execution time is less than 0.5 seconds. + + .. versionchanged:: 8.0 + ``item_show_func`` shows the current item, not the previous one. + + .. versionchanged:: 8.0 + Labels are echoed if the output is not a TTY. Reverts a change + in 7.0 that removed all output. + + .. versionadded:: 8.0 + Added the ``update_min_steps`` parameter. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. Added the ``update`` method to + the object. + + .. versionadded:: 2.0 + """ + from ._termui_impl import ProgressBar + + color = resolve_color_default(color) + return ProgressBar( + iterable=iterable, + length=length, + show_eta=show_eta, + show_percent=show_percent, + show_pos=show_pos, + item_show_func=item_show_func, + fill_char=fill_char, + empty_char=empty_char, + bar_template=bar_template, + info_sep=info_sep, + file=file, + label=label, + width=width, + color=color, + update_min_steps=update_min_steps, + ) + + +def clear() -> None: + """Clears the terminal screen. This will have the effect of clearing + the whole visible space of the terminal and moving the cursor to the + top left. This does not do anything if not connected to a terminal. + + .. versionadded:: 2.0 + """ + if not isatty(sys.stdout): + return + + # ANSI escape \033[2J clears the screen, \033[1;1H moves the cursor + echo("\033[2J\033[1;1H", nl=False) + + +def _interpret_color( + color: t.Union[int, t.Tuple[int, int, int], str], offset: int = 0 +) -> str: + if isinstance(color, int): + return f"{38 + offset};5;{color:d}" + + if isinstance(color, (tuple, list)): + r, g, b = color + return f"{38 + offset};2;{r:d};{g:d};{b:d}" + + return str(_ansi_colors[color] + offset) + + +def style( + text: t.Any, + fg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bold: t.Optional[bool] = None, + dim: t.Optional[bool] = None, + underline: t.Optional[bool] = None, + overline: t.Optional[bool] = None, + italic: t.Optional[bool] = None, + blink: t.Optional[bool] = None, + reverse: t.Optional[bool] = None, + strikethrough: t.Optional[bool] = None, + reset: bool = True, +) -> str: + """Styles a text with ANSI styles and returns the new string. By + default the styling is self contained which means that at the end + of the string a reset code is issued. This can be prevented by + passing ``reset=False``. + + Examples:: + + click.echo(click.style('Hello World!', fg='green')) + click.echo(click.style('ATTENTION!', blink=True)) + click.echo(click.style('Some things', reverse=True, fg='cyan')) + click.echo(click.style('More colors', fg=(255, 12, 128), bg=117)) + + Supported color names: + + * ``black`` (might be a gray) + * ``red`` + * ``green`` + * ``yellow`` (might be an orange) + * ``blue`` + * ``magenta`` + * ``cyan`` + * ``white`` (might be light gray) + * ``bright_black`` + * ``bright_red`` + * ``bright_green`` + * ``bright_yellow`` + * ``bright_blue`` + * ``bright_magenta`` + * ``bright_cyan`` + * ``bright_white`` + * ``reset`` (reset the color code only) + + If the terminal supports it, color may also be specified as: + + - An integer in the interval [0, 255]. The terminal must support + 8-bit/256-color mode. + - An RGB tuple of three integers in [0, 255]. The terminal must + support 24-bit/true-color mode. + + See https://en.wikipedia.org/wiki/ANSI_color and + https://gist.github.com/XVilka/8346728 for more information. + + :param text: the string to style with ansi codes. + :param fg: if provided this will become the foreground color. + :param bg: if provided this will become the background color. + :param bold: if provided this will enable or disable bold mode. + :param dim: if provided this will enable or disable dim mode. This is + badly supported. + :param underline: if provided this will enable or disable underline. + :param overline: if provided this will enable or disable overline. + :param italic: if provided this will enable or disable italic. + :param blink: if provided this will enable or disable blinking. + :param reverse: if provided this will enable or disable inverse + rendering (foreground becomes background and the + other way round). + :param strikethrough: if provided this will enable or disable + striking through text. + :param reset: by default a reset-all code is added at the end of the + string which means that styles do not carry over. This + can be disabled to compose styles. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. + + .. versionchanged:: 8.0 + Added support for 256 and RGB color codes. + + .. versionchanged:: 8.0 + Added the ``strikethrough``, ``italic``, and ``overline`` + parameters. + + .. versionchanged:: 7.0 + Added support for bright colors. + + .. versionadded:: 2.0 + """ + if not isinstance(text, str): + text = str(text) + + bits = [] + + if fg: + try: + bits.append(f"\033[{_interpret_color(fg)}m") + except KeyError: + raise TypeError(f"Unknown color {fg!r}") from None + + if bg: + try: + bits.append(f"\033[{_interpret_color(bg, 10)}m") + except KeyError: + raise TypeError(f"Unknown color {bg!r}") from None + + if bold is not None: + bits.append(f"\033[{1 if bold else 22}m") + if dim is not None: + bits.append(f"\033[{2 if dim else 22}m") + if underline is not None: + bits.append(f"\033[{4 if underline else 24}m") + if overline is not None: + bits.append(f"\033[{53 if overline else 55}m") + if italic is not None: + bits.append(f"\033[{3 if italic else 23}m") + if blink is not None: + bits.append(f"\033[{5 if blink else 25}m") + if reverse is not None: + bits.append(f"\033[{7 if reverse else 27}m") + if strikethrough is not None: + bits.append(f"\033[{9 if strikethrough else 29}m") + bits.append(text) + if reset: + bits.append(_ansi_reset_all) + return "".join(bits) + + +def unstyle(text: str) -> str: + """Removes ANSI styling information from a string. Usually it's not + necessary to use this function as Click's echo function will + automatically remove styling if necessary. + + .. versionadded:: 2.0 + + :param text: the text to remove style information from. + """ + return strip_ansi(text) + + +def secho( + message: t.Optional[t.Any] = None, + file: t.Optional[t.IO[t.AnyStr]] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = None, + **styles: t.Any, +) -> None: + """This function combines :func:`echo` and :func:`style` into one + call. As such the following two calls are the same:: + + click.secho('Hello World!', fg='green') + click.echo(click.style('Hello World!', fg='green')) + + All keyword arguments are forwarded to the underlying functions + depending on which one they go with. + + Non-string types will be converted to :class:`str`. However, + :class:`bytes` are passed directly to :meth:`echo` without applying + style. If you want to style bytes that represent text, call + :meth:`bytes.decode` first. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. Bytes are + passed through without style applied. + + .. versionadded:: 2.0 + """ + if message is not None and not isinstance(message, (bytes, bytearray)): + message = style(message, **styles) + + return echo(message, file=file, nl=nl, err=err, color=color) + + +def edit( + text: t.Optional[t.AnyStr] = None, + editor: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = None, + require_save: bool = True, + extension: str = ".txt", + filename: t.Optional[str] = None, +) -> t.Optional[t.AnyStr]: + r"""Edits the given text in the defined editor. If an editor is given + (should be the full path to the executable but the regular operating + system search path is used for finding the executable) it overrides + the detected editor. Optionally, some environment variables can be + used. If the editor is closed without changes, `None` is returned. In + case a file is edited directly the return value is always `None` and + `require_save` and `extension` are ignored. + + If the editor cannot be opened a :exc:`UsageError` is raised. + + Note for Windows: to simplify cross-platform usage, the newlines are + automatically converted from POSIX to Windows and vice versa. As such, + the message here will have ``\n`` as newline markers. + + :param text: the text to edit. + :param editor: optionally the editor to use. Defaults to automatic + detection. + :param env: environment variables to forward to the editor. + :param require_save: if this is true, then not saving in the editor + will make the return value become `None`. + :param extension: the extension to tell the editor about. This defaults + to `.txt` but changing this might change syntax + highlighting. + :param filename: if provided it will edit this file instead of the + provided text contents. It will not use a temporary + file as an indirection in that case. + """ + from ._termui_impl import Editor + + ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension) + + if filename is None: + return ed.edit(text) + + ed.edit_file(filename) + return None + + +def launch(url: str, wait: bool = False, locate: bool = False) -> int: + """This function launches the given URL (or filename) in the default + viewer application for this file type. If this is an executable, it + might launch the executable in a new session. The return value is + the exit code of the launched application. Usually, ``0`` indicates + success. + + Examples:: + + click.launch('https://click.palletsprojects.com/') + click.launch('/my/downloaded/file', locate=True) + + .. versionadded:: 2.0 + + :param url: URL or filename of the thing to launch. + :param wait: Wait for the program to exit before returning. This + only works if the launched program blocks. In particular, + ``xdg-open`` on Linux does not block. + :param locate: if this is set to `True` then instead of launching the + application associated with the URL it will attempt to + launch a file manager with the file located. This + might have weird effects if the URL does not point to + the filesystem. + """ + from ._termui_impl import open_url + + return open_url(url, wait=wait, locate=locate) + + +# If this is provided, getchar() calls into this instead. This is used +# for unittesting purposes. +_getchar: t.Optional[t.Callable[[bool], str]] = None + + +def getchar(echo: bool = False) -> str: + """Fetches a single character from the terminal and returns it. This + will always return a unicode character and under certain rare + circumstances this might return more than one character. The + situations which more than one character is returned is when for + whatever reason multiple characters end up in the terminal buffer or + standard input was not actually a terminal. + + Note that this will always read from the terminal, even if something + is piped into the standard input. + + Note for Windows: in rare cases when typing non-ASCII characters, this + function might wait for a second character and then return both at once. + This is because certain Unicode characters look like special-key markers. + + .. versionadded:: 2.0 + + :param echo: if set to `True`, the character read will also show up on + the terminal. The default is to not show it. + """ + global _getchar + + if _getchar is None: + from ._termui_impl import getchar as f + + _getchar = f + + return _getchar(echo) + + +def raw_terminal() -> t.ContextManager[int]: + from ._termui_impl import raw_terminal as f + + return f() + + +def pause(info: t.Optional[str] = None, err: bool = False) -> None: + """This command stops execution and waits for the user to press any + key to continue. This is similar to the Windows batch "pause" + command. If the program is not run through a terminal, this command + will instead do nothing. + + .. versionadded:: 2.0 + + .. versionadded:: 4.0 + Added the `err` parameter. + + :param info: The message to print before pausing. Defaults to + ``"Press any key to continue..."``. + :param err: if set to message goes to ``stderr`` instead of + ``stdout``, the same as with echo. + """ + if not isatty(sys.stdin) or not isatty(sys.stdout): + return + + if info is None: + info = _("Press any key to continue...") + + try: + if info: + echo(info, nl=False, err=err) + try: + getchar() + except (KeyboardInterrupt, EOFError): + pass + finally: + if info: + echo(err=err) diff --git a/.venv/lib/python3.9/site-packages/click/testing.py b/.venv/lib/python3.9/site-packages/click/testing.py new file mode 100644 index 00000000..772b2159 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/testing.py @@ -0,0 +1,483 @@ +import contextlib +import io +import os +import shlex +import shutil +import sys +import tempfile +import typing as t +from types import TracebackType + +from . import _compat +from . import formatting +from . import termui +from . import utils +from ._compat import _find_binary_reader + +if t.TYPE_CHECKING: + from .core import BaseCommand + + +class EchoingStdin: + def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None: + self._input = input + self._output = output + self._paused = False + + def __getattr__(self, x: str) -> t.Any: + return getattr(self._input, x) + + def _echo(self, rv: bytes) -> bytes: + if not self._paused: + self._output.write(rv) + + return rv + + def read(self, n: int = -1) -> bytes: + return self._echo(self._input.read(n)) + + def read1(self, n: int = -1) -> bytes: + return self._echo(self._input.read1(n)) # type: ignore + + def readline(self, n: int = -1) -> bytes: + return self._echo(self._input.readline(n)) + + def readlines(self) -> t.List[bytes]: + return [self._echo(x) for x in self._input.readlines()] + + def __iter__(self) -> t.Iterator[bytes]: + return iter(self._echo(x) for x in self._input) + + def __repr__(self) -> str: + return repr(self._input) + + +@contextlib.contextmanager +def _pause_echo(stream: t.Optional[EchoingStdin]) -> t.Iterator[None]: + if stream is None: + yield + else: + stream._paused = True + yield + stream._paused = False + + +class _NamedTextIOWrapper(io.TextIOWrapper): + def __init__( + self, buffer: t.BinaryIO, name: str, mode: str, **kwargs: t.Any + ) -> None: + super().__init__(buffer, **kwargs) + self._name = name + self._mode = mode + + @property + def name(self) -> str: + return self._name + + @property + def mode(self) -> str: + return self._mode + + +def make_input_stream( + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]], charset: str +) -> t.BinaryIO: + # Is already an input stream. + if hasattr(input, "read"): + rv = _find_binary_reader(t.cast(t.IO[t.Any], input)) + + if rv is not None: + return rv + + raise TypeError("Could not find binary reader for input stream.") + + if input is None: + input = b"" + elif isinstance(input, str): + input = input.encode(charset) + + return io.BytesIO(input) + + +class Result: + """Holds the captured result of an invoked CLI script.""" + + def __init__( + self, + runner: "CliRunner", + stdout_bytes: bytes, + stderr_bytes: t.Optional[bytes], + return_value: t.Any, + exit_code: int, + exception: t.Optional[BaseException], + exc_info: t.Optional[ + t.Tuple[t.Type[BaseException], BaseException, TracebackType] + ] = None, + ): + #: The runner that created the result + self.runner = runner + #: The standard output as bytes. + self.stdout_bytes = stdout_bytes + #: The standard error as bytes, or None if not available + self.stderr_bytes = stderr_bytes + #: The value returned from the invoked command. + #: + #: .. versionadded:: 8.0 + self.return_value = return_value + #: The exit code as integer. + self.exit_code = exit_code + #: The exception that happened if one did. + self.exception = exception + #: The traceback + self.exc_info = exc_info + + @property + def output(self) -> str: + """The (standard) output as unicode string.""" + return self.stdout + + @property + def stdout(self) -> str: + """The standard output as unicode string.""" + return self.stdout_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + @property + def stderr(self) -> str: + """The standard error as unicode string.""" + if self.stderr_bytes is None: + raise ValueError("stderr not separately captured") + return self.stderr_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + def __repr__(self) -> str: + exc_str = repr(self.exception) if self.exception else "okay" + return f"<{type(self).__name__} {exc_str}>" + + +class CliRunner: + """The CLI runner provides functionality to invoke a Click command line + script for unittesting purposes in a isolated environment. This only + works in single-threaded systems without any concurrency as it changes the + global interpreter state. + + :param charset: the character set for the input and output data. + :param env: a dictionary with environment variables for overriding. + :param echo_stdin: if this is set to `True`, then reading from stdin writes + to stdout. This is useful for showing examples in + some circumstances. Note that regular prompts + will automatically echo the input. + :param mix_stderr: if this is set to `False`, then stdout and stderr are + preserved as independent streams. This is useful for + Unix-philosophy apps that have predictable stdout and + noisy stderr, such that each may be measured + independently + """ + + def __init__( + self, + charset: str = "utf-8", + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + echo_stdin: bool = False, + mix_stderr: bool = True, + ) -> None: + self.charset = charset + self.env: t.Mapping[str, t.Optional[str]] = env or {} + self.echo_stdin = echo_stdin + self.mix_stderr = mix_stderr + + def get_default_prog_name(self, cli: "BaseCommand") -> str: + """Given a command object it will return the default program name + for it. The default is the `name` attribute or ``"root"`` if not + set. + """ + return cli.name or "root" + + def make_env( + self, overrides: t.Optional[t.Mapping[str, t.Optional[str]]] = None + ) -> t.Mapping[str, t.Optional[str]]: + """Returns the environment overrides for invoking a script.""" + rv = dict(self.env) + if overrides: + rv.update(overrides) + return rv + + @contextlib.contextmanager + def isolation( + self, + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + color: bool = False, + ) -> t.Iterator[t.Tuple[io.BytesIO, t.Optional[io.BytesIO]]]: + """A context manager that sets up the isolation for invoking of a + command line tool. This sets up stdin with the given input data + and `os.environ` with the overrides from the given dictionary. + This also rebinds some internals in Click to be mocked (like the + prompt functionality). + + This is automatically done in the :meth:`invoke` method. + + :param input: the input stream to put into sys.stdin. + :param env: the environment overrides as dictionary. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionchanged:: 8.0 + ``stderr`` is opened with ``errors="backslashreplace"`` + instead of the default ``"strict"``. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + """ + bytes_input = make_input_stream(input, self.charset) + echo_input = None + + old_stdin = sys.stdin + old_stdout = sys.stdout + old_stderr = sys.stderr + old_forced_width = formatting.FORCED_WIDTH + formatting.FORCED_WIDTH = 80 + + env = self.make_env(env) + + bytes_output = io.BytesIO() + + if self.echo_stdin: + bytes_input = echo_input = t.cast( + t.BinaryIO, EchoingStdin(bytes_input, bytes_output) + ) + + sys.stdin = text_input = _NamedTextIOWrapper( + bytes_input, encoding=self.charset, name="", mode="r" + ) + + if self.echo_stdin: + # Force unbuffered reads, otherwise TextIOWrapper reads a + # large chunk which is echoed early. + text_input._CHUNK_SIZE = 1 # type: ignore + + sys.stdout = _NamedTextIOWrapper( + bytes_output, encoding=self.charset, name="", mode="w" + ) + + bytes_error = None + if self.mix_stderr: + sys.stderr = sys.stdout + else: + bytes_error = io.BytesIO() + sys.stderr = _NamedTextIOWrapper( + bytes_error, + encoding=self.charset, + name="", + mode="w", + errors="backslashreplace", + ) + + @_pause_echo(echo_input) # type: ignore + def visible_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(prompt or "") + val = text_input.readline().rstrip("\r\n") + sys.stdout.write(f"{val}\n") + sys.stdout.flush() + return val + + @_pause_echo(echo_input) # type: ignore + def hidden_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(f"{prompt or ''}\n") + sys.stdout.flush() + return text_input.readline().rstrip("\r\n") + + @_pause_echo(echo_input) # type: ignore + def _getchar(echo: bool) -> str: + char = sys.stdin.read(1) + + if echo: + sys.stdout.write(char) + + sys.stdout.flush() + return char + + default_color = color + + def should_strip_ansi( + stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None + ) -> bool: + if color is None: + return not default_color + return not color + + old_visible_prompt_func = termui.visible_prompt_func + old_hidden_prompt_func = termui.hidden_prompt_func + old__getchar_func = termui._getchar + old_should_strip_ansi = utils.should_strip_ansi # type: ignore + old__compat_should_strip_ansi = _compat.should_strip_ansi + termui.visible_prompt_func = visible_input + termui.hidden_prompt_func = hidden_input + termui._getchar = _getchar + utils.should_strip_ansi = should_strip_ansi # type: ignore + _compat.should_strip_ansi = should_strip_ansi + + old_env = {} + try: + for key, value in env.items(): + old_env[key] = os.environ.get(key) + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + yield (bytes_output, bytes_error) + finally: + for key, value in old_env.items(): + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + sys.stdout = old_stdout + sys.stderr = old_stderr + sys.stdin = old_stdin + termui.visible_prompt_func = old_visible_prompt_func + termui.hidden_prompt_func = old_hidden_prompt_func + termui._getchar = old__getchar_func + utils.should_strip_ansi = old_should_strip_ansi # type: ignore + _compat.should_strip_ansi = old__compat_should_strip_ansi + formatting.FORCED_WIDTH = old_forced_width + + def invoke( + self, + cli: "BaseCommand", + args: t.Optional[t.Union[str, t.Sequence[str]]] = None, + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + catch_exceptions: bool = True, + color: bool = False, + **extra: t.Any, + ) -> Result: + """Invokes a command in an isolated environment. The arguments are + forwarded directly to the command line script, the `extra` keyword + arguments are passed to the :meth:`~clickpkg.Command.main` function of + the command. + + This returns a :class:`Result` object. + + :param cli: the command to invoke + :param args: the arguments to invoke. It may be given as an iterable + or a string. When given as string it will be interpreted + as a Unix shell command. More details at + :func:`shlex.split`. + :param input: the input data for `sys.stdin`. + :param env: the environment overrides. + :param catch_exceptions: Whether to catch any other exceptions than + ``SystemExit``. + :param extra: the keyword arguments to pass to :meth:`main`. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionchanged:: 8.0 + The result object has the ``return_value`` attribute with + the value returned from the invoked command. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionchanged:: 3.0 + Added the ``catch_exceptions`` parameter. + + .. versionchanged:: 3.0 + The result object has the ``exc_info`` attribute with the + traceback if available. + """ + exc_info = None + with self.isolation(input=input, env=env, color=color) as outstreams: + return_value = None + exception: t.Optional[BaseException] = None + exit_code = 0 + + if isinstance(args, str): + args = shlex.split(args) + + try: + prog_name = extra.pop("prog_name") + except KeyError: + prog_name = self.get_default_prog_name(cli) + + try: + return_value = cli.main(args=args or (), prog_name=prog_name, **extra) + except SystemExit as e: + exc_info = sys.exc_info() + e_code = t.cast(t.Optional[t.Union[int, t.Any]], e.code) + + if e_code is None: + e_code = 0 + + if e_code != 0: + exception = e + + if not isinstance(e_code, int): + sys.stdout.write(str(e_code)) + sys.stdout.write("\n") + e_code = 1 + + exit_code = e_code + + except Exception as e: + if not catch_exceptions: + raise + exception = e + exit_code = 1 + exc_info = sys.exc_info() + finally: + sys.stdout.flush() + stdout = outstreams[0].getvalue() + if self.mix_stderr: + stderr = None + else: + stderr = outstreams[1].getvalue() # type: ignore + + return Result( + runner=self, + stdout_bytes=stdout, + stderr_bytes=stderr, + return_value=return_value, + exit_code=exit_code, + exception=exception, + exc_info=exc_info, # type: ignore + ) + + @contextlib.contextmanager + def isolated_filesystem( + self, temp_dir: t.Optional[t.Union[str, "os.PathLike[str]"]] = None + ) -> t.Iterator[str]: + """A context manager that creates a temporary directory and + changes the current working directory to it. This isolates tests + that affect the contents of the CWD to prevent them from + interfering with each other. + + :param temp_dir: Create the temporary directory under this + directory. If given, the created directory is not removed + when exiting. + + .. versionchanged:: 8.0 + Added the ``temp_dir`` parameter. + """ + cwd = os.getcwd() + dt = tempfile.mkdtemp(dir=temp_dir) + os.chdir(dt) + + try: + yield dt + finally: + os.chdir(cwd) + + if temp_dir is None: + try: + shutil.rmtree(dt) + except OSError: + pass diff --git a/.venv/lib/python3.9/site-packages/click/types.py b/.venv/lib/python3.9/site-packages/click/types.py new file mode 100644 index 00000000..a70fd58c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/types.py @@ -0,0 +1,1093 @@ +import os +import stat +import sys +import typing as t +from datetime import datetime +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import _get_argv_encoding +from ._compat import open_stream +from .exceptions import BadParameter +from .utils import format_filename +from .utils import LazyFile +from .utils import safecall + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .core import Context + from .core import Parameter + from .shell_completion import CompletionItem + + +class ParamType: + """Represents the type of a parameter. Validates and converts values + from the command line or Python into the correct type. + + To implement a custom type, subclass and implement at least the + following: + + - The :attr:`name` class attribute must be set. + - Calling an instance of the type with ``None`` must return + ``None``. This is already implemented by default. + - :meth:`convert` must convert string values to the correct type. + - :meth:`convert` must accept values that are already the correct + type. + - It must be able to convert a value if the ``ctx`` and ``param`` + arguments are ``None``. This can occur when converting prompt + input. + """ + + is_composite: t.ClassVar[bool] = False + arity: t.ClassVar[int] = 1 + + #: the descriptive name of this type + name: str + + #: if a list of this type is expected and the value is pulled from a + #: string environment variable, this is what splits it up. `None` + #: means any whitespace. For all parameters the general rule is that + #: whitespace splits them up. The exception are paths and files which + #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on + #: Windows). + envvar_list_splitter: t.ClassVar[t.Optional[str]] = None + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + # The class name without the "ParamType" suffix. + param_type = type(self).__name__.partition("ParamType")[0] + param_type = param_type.partition("ParameterType")[0] + + # Custom subclasses might not remember to set a name. + if hasattr(self, "name"): + name = self.name + else: + name = param_type + + return {"param_type": param_type, "name": name} + + def __call__( + self, + value: t.Any, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> t.Any: + if value is not None: + return self.convert(value, param, ctx) + + def get_metavar(self, param: "Parameter") -> t.Optional[str]: + """Returns the metavar default for this param if it provides one.""" + + def get_missing_message(self, param: "Parameter") -> t.Optional[str]: + """Optionally might return extra information about a missing + parameter. + + .. versionadded:: 2.0 + """ + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + """Convert the value to the correct type. This is not called if + the value is ``None`` (the missing value). + + This must accept string values from the command line, as well as + values that are already the correct type. It may also convert + other compatible types. + + The ``param`` and ``ctx`` arguments may be ``None`` in certain + situations, such as when converting prompt input. + + If the value cannot be converted, call :meth:`fail` with a + descriptive message. + + :param value: The value to convert. + :param param: The parameter that is using this type to convert + its value. May be ``None``. + :param ctx: The current context that arrived at this value. May + be ``None``. + """ + return value + + def split_envvar_value(self, rv: str) -> t.Sequence[str]: + """Given a value from an environment variable this splits it up + into small chunks depending on the defined envvar list splitter. + + If the splitter is set to `None`, which means that whitespace splits, + then leading and trailing whitespace is ignored. Otherwise, leading + and trailing splitters usually lead to empty items being included. + """ + return (rv or "").split(self.envvar_list_splitter) + + def fail( + self, + message: str, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> "t.NoReturn": + """Helper method to fail with an invalid value message.""" + raise BadParameter(message, ctx=ctx, param=param) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a list of + :class:`~click.shell_completion.CompletionItem` objects for the + incomplete value. Most types do not provide completions, but + some do, and this allows custom types to provide custom + completions as well. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + return [] + + +class CompositeParamType(ParamType): + is_composite = True + + @property + def arity(self) -> int: # type: ignore + raise NotImplementedError() + + +class FuncParamType(ParamType): + def __init__(self, func: t.Callable[[t.Any], t.Any]) -> None: + self.name: str = func.__name__ + self.func = func + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["func"] = self.func + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self.func(value) + except ValueError: + try: + value = str(value) + except UnicodeError: + value = value.decode("utf-8", "replace") + + self.fail(value, param, ctx) + + +class UnprocessedParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + return value + + def __repr__(self) -> str: + return "UNPROCESSED" + + +class StringParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, bytes): + enc = _get_argv_encoding() + try: + value = value.decode(enc) + except UnicodeError: + fs_enc = sys.getfilesystemencoding() + if fs_enc != enc: + try: + value = value.decode(fs_enc) + except UnicodeError: + value = value.decode("utf-8", "replace") + else: + value = value.decode("utf-8", "replace") + return value + return str(value) + + def __repr__(self) -> str: + return "STRING" + + +class Choice(ParamType): + """The choice type allows a value to be checked against a fixed set + of supported values. All of these values have to be strings. + + You should only pass a list or tuple of choices. Other iterables + (like generators) may lead to surprising results. + + The resulting value will always be one of the originally passed choices + regardless of ``case_sensitive`` or any ``ctx.token_normalize_func`` + being specified. + + See :ref:`choice-opts` for an example. + + :param case_sensitive: Set to false to make choices case + insensitive. Defaults to true. + """ + + name = "choice" + + def __init__(self, choices: t.Sequence[str], case_sensitive: bool = True) -> None: + self.choices = choices + self.case_sensitive = case_sensitive + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["choices"] = self.choices + info_dict["case_sensitive"] = self.case_sensitive + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + choices_str = "|".join(self.choices) + + # Use curly braces to indicate a required argument. + if param.required and param.param_type_name == "argument": + return f"{{{choices_str}}}" + + # Use square braces to indicate an option or optional argument. + return f"[{choices_str}]" + + def get_missing_message(self, param: "Parameter") -> str: + return _("Choose from:\n\t{choices}").format(choices=",\n\t".join(self.choices)) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + # Match through normalization and case sensitivity + # first do token_normalize_func, then lowercase + # preserve original `value` to produce an accurate message in + # `self.fail` + normed_value = value + normed_choices = {choice: choice for choice in self.choices} + + if ctx is not None and ctx.token_normalize_func is not None: + normed_value = ctx.token_normalize_func(value) + normed_choices = { + ctx.token_normalize_func(normed_choice): original + for normed_choice, original in normed_choices.items() + } + + if not self.case_sensitive: + normed_value = normed_value.casefold() + normed_choices = { + normed_choice.casefold(): original + for normed_choice, original in normed_choices.items() + } + + if normed_value in normed_choices: + return normed_choices[normed_value] + + choices_str = ", ".join(map(repr, self.choices)) + self.fail( + ngettext( + "{value!r} is not {choice}.", + "{value!r} is not one of {choices}.", + len(self.choices), + ).format(value=value, choice=choices_str, choices=choices_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return f"Choice({list(self.choices)})" + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Complete choices that start with the incomplete value. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + str_choices = map(str, self.choices) + + if self.case_sensitive: + matched = (c for c in str_choices if c.startswith(incomplete)) + else: + incomplete = incomplete.lower() + matched = (c for c in str_choices if c.lower().startswith(incomplete)) + + return [CompletionItem(c) for c in matched] + + +class DateTime(ParamType): + """The DateTime type converts date strings into `datetime` objects. + + The format strings which are checked are configurable, but default to some + common (non-timezone aware) ISO 8601 formats. + + When specifying *DateTime* formats, you should only pass a list or a tuple. + Other iterables, like generators, may lead to surprising results. + + The format strings are processed using ``datetime.strptime``, and this + consequently defines the format strings which are allowed. + + Parsing is tried using each format, in order, and the first format which + parses successfully is used. + + :param formats: A list or tuple of date format strings, in the order in + which they should be tried. Defaults to + ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, + ``'%Y-%m-%d %H:%M:%S'``. + """ + + name = "datetime" + + def __init__(self, formats: t.Optional[t.Sequence[str]] = None): + self.formats: t.Sequence[str] = formats or [ + "%Y-%m-%d", + "%Y-%m-%dT%H:%M:%S", + "%Y-%m-%d %H:%M:%S", + ] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["formats"] = self.formats + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + return f"[{'|'.join(self.formats)}]" + + def _try_to_convert_date(self, value: t.Any, format: str) -> t.Optional[datetime]: + try: + return datetime.strptime(value, format) + except ValueError: + return None + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, datetime): + return value + + for format in self.formats: + converted = self._try_to_convert_date(value, format) + + if converted is not None: + return converted + + formats_str = ", ".join(map(repr, self.formats)) + self.fail( + ngettext( + "{value!r} does not match the format {format}.", + "{value!r} does not match the formats {formats}.", + len(self.formats), + ).format(value=value, format=formats_str, formats=formats_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return "DateTime" + + +class _NumberParamTypeBase(ParamType): + _number_class: t.ClassVar[t.Type[t.Any]] + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self._number_class(value) + except ValueError: + self.fail( + _("{value!r} is not a valid {number_type}.").format( + value=value, number_type=self.name + ), + param, + ctx, + ) + + +class _NumberRangeBase(_NumberParamTypeBase): + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + self.min = min + self.max = max + self.min_open = min_open + self.max_open = max_open + self.clamp = clamp + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + min=self.min, + max=self.max, + min_open=self.min_open, + max_open=self.max_open, + clamp=self.clamp, + ) + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import operator + + rv = super().convert(value, param, ctx) + lt_min: bool = self.min is not None and ( + operator.le if self.min_open else operator.lt + )(rv, self.min) + gt_max: bool = self.max is not None and ( + operator.ge if self.max_open else operator.gt + )(rv, self.max) + + if self.clamp: + if lt_min: + return self._clamp(self.min, 1, self.min_open) # type: ignore + + if gt_max: + return self._clamp(self.max, -1, self.max_open) # type: ignore + + if lt_min or gt_max: + self.fail( + _("{value} is not in the range {range}.").format( + value=rv, range=self._describe_range() + ), + param, + ctx, + ) + + return rv + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + """Find the valid value to clamp to bound in the given + direction. + + :param bound: The boundary value. + :param dir: 1 or -1 indicating the direction to move. + :param open: If true, the range does not include the bound. + """ + raise NotImplementedError + + def _describe_range(self) -> str: + """Describe the range for use in help text.""" + if self.min is None: + op = "<" if self.max_open else "<=" + return f"x{op}{self.max}" + + if self.max is None: + op = ">" if self.min_open else ">=" + return f"x{op}{self.min}" + + lop = "<" if self.min_open else "<=" + rop = "<" if self.max_open else "<=" + return f"{self.min}{lop}x{rop}{self.max}" + + def __repr__(self) -> str: + clamp = " clamped" if self.clamp else "" + return f"<{type(self).__name__} {self._describe_range()}{clamp}>" + + +class IntParamType(_NumberParamTypeBase): + name = "integer" + _number_class = int + + def __repr__(self) -> str: + return "INT" + + +class IntRange(_NumberRangeBase, IntParamType): + """Restrict an :data:`click.INT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "integer range" + + def _clamp( # type: ignore + self, bound: int, dir: "te.Literal[1, -1]", open: bool + ) -> int: + if not open: + return bound + + return bound + dir + + +class FloatParamType(_NumberParamTypeBase): + name = "float" + _number_class = float + + def __repr__(self) -> str: + return "FLOAT" + + +class FloatRange(_NumberRangeBase, FloatParamType): + """Restrict a :data:`click.FLOAT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. This is not supported if either + boundary is marked ``open``. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "float range" + + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + super().__init__( + min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp + ) + + if (min_open or max_open) and clamp: + raise TypeError("Clamping is not supported for open bounds.") + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + if not open: + return bound + + # Could use Python 3.9's math.nextafter here, but clamping an + # open float range doesn't seem to be particularly useful. It's + # left up to the user to write a callback to do it if needed. + raise RuntimeError("Clamping is not supported for open bounds.") + + +class BoolParamType(ParamType): + name = "boolean" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if value in {False, True}: + return bool(value) + + norm = value.strip().lower() + + if norm in {"1", "true", "t", "yes", "y", "on"}: + return True + + if norm in {"0", "false", "f", "no", "n", "off"}: + return False + + self.fail( + _("{value!r} is not a valid boolean.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "BOOL" + + +class UUIDParameterType(ParamType): + name = "uuid" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import uuid + + if isinstance(value, uuid.UUID): + return value + + value = value.strip() + + try: + return uuid.UUID(value) + except ValueError: + self.fail( + _("{value!r} is not a valid UUID.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "UUID" + + +class File(ParamType): + """Declares a parameter to be a file for reading or writing. The file + is automatically closed once the context tears down (after the command + finished working). + + Files can be opened for reading or writing. The special value ``-`` + indicates stdin or stdout depending on the mode. + + By default, the file is opened for reading text data, but it can also be + opened in binary mode or for writing. The encoding parameter can be used + to force a specific encoding. + + The `lazy` flag controls if the file should be opened immediately or upon + first IO. The default is to be non-lazy for standard input and output + streams as well as files opened for reading, `lazy` otherwise. When opening a + file lazily for reading, it is still opened temporarily for validation, but + will not be held open until first IO. lazy is mainly useful when opening + for writing to avoid creating the file until it is needed. + + Files can also be opened atomically in which case all writes go into a + separate file in the same folder and upon completion the file will + be moved over to the original location. This is useful if a file + regularly read by other users is modified. + + See :ref:`file-args` for more information. + + .. versionchanged:: 2.0 + Added the ``atomic`` parameter. + """ + + name = "filename" + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + def __init__( + self, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: t.Optional[bool] = None, + atomic: bool = False, + ) -> None: + self.mode = mode + self.encoding = encoding + self.errors = errors + self.lazy = lazy + self.atomic = atomic + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update(mode=self.mode, encoding=self.encoding) + return info_dict + + def resolve_lazy_flag(self, value: "t.Union[str, os.PathLike[str]]") -> bool: + if self.lazy is not None: + return self.lazy + if os.fspath(value) == "-": + return False + elif "w" in self.mode: + return True + return False + + def convert( + self, + value: t.Union[str, "os.PathLike[str]", t.IO[t.Any]], + param: t.Optional["Parameter"], + ctx: t.Optional["Context"], + ) -> t.IO[t.Any]: + if _is_file_like(value): + return value + + value = t.cast("t.Union[str, os.PathLike[str]]", value) + + try: + lazy = self.resolve_lazy_flag(value) + + if lazy: + lf = LazyFile( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + if ctx is not None: + ctx.call_on_close(lf.close_intelligently) + + return t.cast(t.IO[t.Any], lf) + + f, should_close = open_stream( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + # If a context is provided, we automatically close the file + # at the end of the context execution (or flush out). If a + # context does not exist, it's the caller's responsibility to + # properly close the file. This for instance happens when the + # type is used with prompts. + if ctx is not None: + if should_close: + ctx.call_on_close(safecall(f.close)) + else: + ctx.call_on_close(safecall(f.flush)) + + return f + except OSError as e: + self.fail(f"'{format_filename(value)}': {e.strerror}", param, ctx) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a special completion marker that tells the completion + system to use the shell to provide file path completions. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + return [CompletionItem(incomplete, type="file")] + + +def _is_file_like(value: t.Any) -> "te.TypeGuard[t.IO[t.Any]]": + return hasattr(value, "read") or hasattr(value, "write") + + +class Path(ParamType): + """The ``Path`` type is similar to the :class:`File` type, but + returns the filename instead of an open file. Various checks can be + enabled to validate the type of file and permissions. + + :param exists: The file or directory needs to exist for the value to + be valid. If this is not set to ``True``, and the file does not + exist, then all further checks are silently skipped. + :param file_okay: Allow a file as a value. + :param dir_okay: Allow a directory as a value. + :param readable: if true, a readable check is performed. + :param writable: if true, a writable check is performed. + :param executable: if true, an executable check is performed. + :param resolve_path: Make the value absolute and resolve any + symlinks. A ``~`` is not expanded, as this is supposed to be + done by the shell only. + :param allow_dash: Allow a single dash as a value, which indicates + a standard stream (but does not open it). Use + :func:`~click.open_file` to handle opening this value. + :param path_type: Convert the incoming path value to this type. If + ``None``, keep Python's default, which is ``str``. Useful to + convert to :class:`pathlib.Path`. + + .. versionchanged:: 8.1 + Added the ``executable`` parameter. + + .. versionchanged:: 8.0 + Allow passing ``path_type=pathlib.Path``. + + .. versionchanged:: 6.0 + Added the ``allow_dash`` parameter. + """ + + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + def __init__( + self, + exists: bool = False, + file_okay: bool = True, + dir_okay: bool = True, + writable: bool = False, + readable: bool = True, + resolve_path: bool = False, + allow_dash: bool = False, + path_type: t.Optional[t.Type[t.Any]] = None, + executable: bool = False, + ): + self.exists = exists + self.file_okay = file_okay + self.dir_okay = dir_okay + self.readable = readable + self.writable = writable + self.executable = executable + self.resolve_path = resolve_path + self.allow_dash = allow_dash + self.type = path_type + + if self.file_okay and not self.dir_okay: + self.name: str = _("file") + elif self.dir_okay and not self.file_okay: + self.name = _("directory") + else: + self.name = _("path") + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + exists=self.exists, + file_okay=self.file_okay, + dir_okay=self.dir_okay, + writable=self.writable, + readable=self.readable, + allow_dash=self.allow_dash, + ) + return info_dict + + def coerce_path_result( + self, value: "t.Union[str, os.PathLike[str]]" + ) -> "t.Union[str, bytes, os.PathLike[str]]": + if self.type is not None and not isinstance(value, self.type): + if self.type is str: + return os.fsdecode(value) + elif self.type is bytes: + return os.fsencode(value) + else: + return t.cast("os.PathLike[str]", self.type(value)) + + return value + + def convert( + self, + value: "t.Union[str, os.PathLike[str]]", + param: t.Optional["Parameter"], + ctx: t.Optional["Context"], + ) -> "t.Union[str, bytes, os.PathLike[str]]": + rv = value + + is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") + + if not is_dash: + if self.resolve_path: + # os.path.realpath doesn't resolve symlinks on Windows + # until Python 3.8. Use pathlib for now. + import pathlib + + rv = os.fsdecode(pathlib.Path(rv).resolve()) + + try: + st = os.stat(rv) + except OSError: + if not self.exists: + return self.coerce_path_result(rv) + self.fail( + _("{name} {filename!r} does not exist.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if not self.file_okay and stat.S_ISREG(st.st_mode): + self.fail( + _("{name} {filename!r} is a file.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + if not self.dir_okay and stat.S_ISDIR(st.st_mode): + self.fail( + _("{name} {filename!r} is a directory.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.readable and not os.access(rv, os.R_OK): + self.fail( + _("{name} {filename!r} is not readable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.writable and not os.access(rv, os.W_OK): + self.fail( + _("{name} {filename!r} is not writable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.executable and not os.access(value, os.X_OK): + self.fail( + _("{name} {filename!r} is not executable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + return self.coerce_path_result(rv) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a special completion marker that tells the completion + system to use the shell to provide path completions for only + directories or any paths. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + type = "dir" if self.dir_okay and not self.file_okay else "file" + return [CompletionItem(incomplete, type=type)] + + +class Tuple(CompositeParamType): + """The default behavior of Click is to apply a type on a value directly. + This works well in most cases, except for when `nargs` is set to a fixed + count and different types should be used for different items. In this + case the :class:`Tuple` type can be used. This type can only be used + if `nargs` is set to a fixed number. + + For more information see :ref:`tuple-type`. + + This can be selected by using a Python tuple literal as a type. + + :param types: a list of types that should be used for the tuple items. + """ + + def __init__(self, types: t.Sequence[t.Union[t.Type[t.Any], ParamType]]) -> None: + self.types: t.Sequence[ParamType] = [convert_type(ty) for ty in types] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["types"] = [t.to_info_dict() for t in self.types] + return info_dict + + @property + def name(self) -> str: # type: ignore + return f"<{' '.join(ty.name for ty in self.types)}>" + + @property + def arity(self) -> int: # type: ignore + return len(self.types) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + len_type = len(self.types) + len_value = len(value) + + if len_value != len_type: + self.fail( + ngettext( + "{len_type} values are required, but {len_value} was given.", + "{len_type} values are required, but {len_value} were given.", + len_value, + ).format(len_type=len_type, len_value=len_value), + param=param, + ctx=ctx, + ) + + return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value)) + + +def convert_type(ty: t.Optional[t.Any], default: t.Optional[t.Any] = None) -> ParamType: + """Find the most appropriate :class:`ParamType` for the given Python + type. If the type isn't provided, it can be inferred from a default + value. + """ + guessed_type = False + + if ty is None and default is not None: + if isinstance(default, (tuple, list)): + # If the default is empty, ty will remain None and will + # return STRING. + if default: + item = default[0] + + # A tuple of tuples needs to detect the inner types. + # Can't call convert recursively because that would + # incorrectly unwind the tuple to a single type. + if isinstance(item, (tuple, list)): + ty = tuple(map(type, item)) + else: + ty = type(item) + else: + ty = type(default) + + guessed_type = True + + if isinstance(ty, tuple): + return Tuple(ty) + + if isinstance(ty, ParamType): + return ty + + if ty is str or ty is None: + return STRING + + if ty is int: + return INT + + if ty is float: + return FLOAT + + if ty is bool: + return BOOL + + if guessed_type: + return STRING + + if __debug__: + try: + if issubclass(ty, ParamType): + raise AssertionError( + f"Attempted to use an uninstantiated parameter type ({ty})." + ) + except TypeError: + # ty is an instance (correct), so issubclass fails. + pass + + return FuncParamType(ty) + + +#: A dummy parameter type that just does nothing. From a user's +#: perspective this appears to just be the same as `STRING` but +#: internally no string conversion takes place if the input was bytes. +#: This is usually useful when working with file paths as they can +#: appear in bytes and unicode. +#: +#: For path related uses the :class:`Path` type is a better choice but +#: there are situations where an unprocessed type is useful which is why +#: it is is provided. +#: +#: .. versionadded:: 4.0 +UNPROCESSED = UnprocessedParamType() + +#: A unicode string parameter type which is the implicit default. This +#: can also be selected by using ``str`` as type. +STRING = StringParamType() + +#: An integer parameter. This can also be selected by using ``int`` as +#: type. +INT = IntParamType() + +#: A floating point value parameter. This can also be selected by using +#: ``float`` as type. +FLOAT = FloatParamType() + +#: A boolean parameter. This is the default for boolean flags. This can +#: also be selected by using ``bool`` as a type. +BOOL = BoolParamType() + +#: A UUID parameter. +UUID = UUIDParameterType() diff --git a/.venv/lib/python3.9/site-packages/click/utils.py b/.venv/lib/python3.9/site-packages/click/utils.py new file mode 100644 index 00000000..836c6f21 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/click/utils.py @@ -0,0 +1,624 @@ +import os +import re +import sys +import typing as t +from functools import update_wrapper +from types import ModuleType +from types import TracebackType + +from ._compat import _default_text_stderr +from ._compat import _default_text_stdout +from ._compat import _find_binary_writer +from ._compat import auto_wrap_for_ansi +from ._compat import binary_streams +from ._compat import open_stream +from ._compat import should_strip_ansi +from ._compat import strip_ansi +from ._compat import text_streams +from ._compat import WIN +from .globals import resolve_color_default + +if t.TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = t.TypeVar("R") + + +def _posixify(name: str) -> str: + return "-".join(name.split()).lower() + + +def safecall(func: "t.Callable[P, R]") -> "t.Callable[P, t.Optional[R]]": + """Wraps a function so that it swallows exceptions.""" + + def wrapper(*args: "P.args", **kwargs: "P.kwargs") -> t.Optional[R]: + try: + return func(*args, **kwargs) + except Exception: + pass + return None + + return update_wrapper(wrapper, func) + + +def make_str(value: t.Any) -> str: + """Converts a value into a valid string.""" + if isinstance(value, bytes): + try: + return value.decode(sys.getfilesystemencoding()) + except UnicodeError: + return value.decode("utf-8", "replace") + return str(value) + + +def make_default_short_help(help: str, max_length: int = 45) -> str: + """Returns a condensed version of help string.""" + # Consider only the first paragraph. + paragraph_end = help.find("\n\n") + + if paragraph_end != -1: + help = help[:paragraph_end] + + # Collapse newlines, tabs, and spaces. + words = help.split() + + if not words: + return "" + + # The first paragraph started with a "no rewrap" marker, ignore it. + if words[0] == "\b": + words = words[1:] + + total_length = 0 + last_index = len(words) - 1 + + for i, word in enumerate(words): + total_length += len(word) + (i > 0) + + if total_length > max_length: # too long, truncate + break + + if word[-1] == ".": # sentence end, truncate without "..." + return " ".join(words[: i + 1]) + + if total_length == max_length and i != last_index: + break # not at sentence end, truncate with "..." + else: + return " ".join(words) # no truncation needed + + # Account for the length of the suffix. + total_length += len("...") + + # remove words until the length is short enough + while i > 0: + total_length -= len(words[i]) + (i > 0) + + if total_length <= max_length: + break + + i -= 1 + + return " ".join(words[:i]) + "..." + + +class LazyFile: + """A lazy file works like a regular file but it does not fully open + the file but it does perform some basic checks early to see if the + filename parameter does make sense. This is useful for safely opening + files for writing. + """ + + def __init__( + self, + filename: t.Union[str, "os.PathLike[str]"], + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, + ): + self.name: str = os.fspath(filename) + self.mode = mode + self.encoding = encoding + self.errors = errors + self.atomic = atomic + self._f: t.Optional[t.IO[t.Any]] + self.should_close: bool + + if self.name == "-": + self._f, self.should_close = open_stream(filename, mode, encoding, errors) + else: + if "r" in mode: + # Open and close the file in case we're opening it for + # reading so that we can catch at least some errors in + # some cases early. + open(filename, mode).close() + self._f = None + self.should_close = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self.open(), name) + + def __repr__(self) -> str: + if self._f is not None: + return repr(self._f) + return f"" + + def open(self) -> t.IO[t.Any]: + """Opens the file if it's not yet open. This call might fail with + a :exc:`FileError`. Not handling this error will produce an error + that Click shows. + """ + if self._f is not None: + return self._f + try: + rv, self.should_close = open_stream( + self.name, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + except OSError as e: + from .exceptions import FileError + + raise FileError(self.name, hint=e.strerror) from e + self._f = rv + return rv + + def close(self) -> None: + """Closes the underlying file, no matter what.""" + if self._f is not None: + self._f.close() + + def close_intelligently(self) -> None: + """This function only closes the file if it was opened by the lazy + file wrapper. For instance this will never close stdin. + """ + if self.should_close: + self.close() + + def __enter__(self) -> "LazyFile": + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + self.close_intelligently() + + def __iter__(self) -> t.Iterator[t.AnyStr]: + self.open() + return iter(self._f) # type: ignore + + +class KeepOpenFile: + def __init__(self, file: t.IO[t.Any]) -> None: + self._file: t.IO[t.Any] = file + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._file, name) + + def __enter__(self) -> "KeepOpenFile": + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + pass + + def __repr__(self) -> str: + return repr(self._file) + + def __iter__(self) -> t.Iterator[t.AnyStr]: + return iter(self._file) + + +def echo( + message: t.Optional[t.Any] = None, + file: t.Optional[t.IO[t.Any]] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = None, +) -> None: + """Print a message and newline to stdout or a file. This should be + used instead of :func:`print` because it provides better support + for different data, files, and environments. + + Compared to :func:`print`, this does the following: + + - Ensures that the output encoding is not misconfigured on Linux. + - Supports Unicode in the Windows console. + - Supports writing to binary outputs, and supports writing bytes + to text outputs. + - Supports colors and styles on Windows. + - Removes ANSI color and style codes if the output does not look + like an interactive terminal. + - Always flushes the output. + + :param message: The string or bytes to output. Other objects are + converted to strings. + :param file: The file to write to. Defaults to ``stdout``. + :param err: Write to ``stderr`` instead of ``stdout``. + :param nl: Print a newline after the message. Enabled by default. + :param color: Force showing or hiding colors and other styles. By + default Click will remove color if the output does not look like + an interactive terminal. + + .. versionchanged:: 6.0 + Support Unicode output on the Windows console. Click does not + modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()`` + will still not support Unicode. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionadded:: 3.0 + Added the ``err`` parameter. + + .. versionchanged:: 2.0 + Support colors on Windows if colorama is installed. + """ + if file is None: + if err: + file = _default_text_stderr() + else: + file = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + return + + # Convert non bytes/text into the native string type. + if message is not None and not isinstance(message, (str, bytes, bytearray)): + out: t.Optional[t.Union[str, bytes]] = str(message) + else: + out = message + + if nl: + out = out or "" + if isinstance(out, str): + out += "\n" + else: + out += b"\n" + + if not out: + file.flush() + return + + # If there is a message and the value looks like bytes, we manually + # need to find the binary stream and write the message in there. + # This is done separately so that most stream types will work as you + # would expect. Eg: you can write to StringIO for other cases. + if isinstance(out, (bytes, bytearray)): + binary_file = _find_binary_writer(file) + + if binary_file is not None: + file.flush() + binary_file.write(out) + binary_file.flush() + return + + # ANSI style code support. For no message or bytes, nothing happens. + # When outputting to a file instead of a terminal, strip codes. + else: + color = resolve_color_default(color) + + if should_strip_ansi(file, color): + out = strip_ansi(out) + elif WIN: + if auto_wrap_for_ansi is not None: + file = auto_wrap_for_ansi(file, color) # type: ignore + elif not color: + out = strip_ansi(out) + + file.write(out) # type: ignore + file.flush() + + +def get_binary_stream(name: "te.Literal['stdin', 'stdout', 'stderr']") -> t.BinaryIO: + """Returns a system stream for byte processing. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + """ + opener = binary_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener() + + +def get_text_stream( + name: "te.Literal['stdin', 'stdout', 'stderr']", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", +) -> t.TextIO: + """Returns a system stream for text processing. This usually returns + a wrapped stream around a binary stream returned from + :func:`get_binary_stream` but it also can take shortcuts for already + correctly configured streams. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + :param encoding: overrides the detected default encoding. + :param errors: overrides the default error mode. + """ + opener = text_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener(encoding, errors) + + +def open_file( + filename: t.Union[str, "os.PathLike[str]"], + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: bool = False, + atomic: bool = False, +) -> t.IO[t.Any]: + """Open a file, with extra behavior to handle ``'-'`` to indicate + a standard stream, lazy open on write, and atomic write. Similar to + the behavior of the :class:`~click.File` param type. + + If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is + wrapped so that using it in a context manager will not close it. + This makes it possible to use the function without accidentally + closing a standard stream: + + .. code-block:: python + + with open_file(filename) as f: + ... + + :param filename: The name or Path of the file to open, or ``'-'`` for + ``stdin``/``stdout``. + :param mode: The mode in which to open the file. + :param encoding: The encoding to decode or encode a file opened in + text mode. + :param errors: The error handling mode. + :param lazy: Wait to open the file until it is accessed. For read + mode, the file is temporarily opened to raise access errors + early, then closed until it is read again. + :param atomic: Write to a temporary file and replace the given file + on close. + + .. versionadded:: 3.0 + """ + if lazy: + return t.cast( + t.IO[t.Any], LazyFile(filename, mode, encoding, errors, atomic=atomic) + ) + + f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic) + + if not should_close: + f = t.cast(t.IO[t.Any], KeepOpenFile(f)) + + return f + + +def format_filename( + filename: "t.Union[str, bytes, os.PathLike[str], os.PathLike[bytes]]", + shorten: bool = False, +) -> str: + """Format a filename as a string for display. Ensures the filename can be + displayed by replacing any invalid bytes or surrogate escapes in the name + with the replacement character ``�``. + + Invalid bytes or surrogate escapes will raise an error when written to a + stream with ``errors="strict"``. This will typically happen with ``stdout`` + when the locale is something like ``en_GB.UTF-8``. + + Many scenarios *are* safe to write surrogates though, due to PEP 538 and + PEP 540, including: + + - Writing to ``stderr``, which uses ``errors="backslashreplace"``. + - The system has ``LANG=C.UTF-8``, ``C``, or ``POSIX``. Python opens + stdout and stderr with ``errors="surrogateescape"``. + - None of ``LANG/LC_*`` are set. Python assumes ``LANG=C.UTF-8``. + - Python is started in UTF-8 mode with ``PYTHONUTF8=1`` or ``-X utf8``. + Python opens stdout and stderr with ``errors="surrogateescape"``. + + :param filename: formats a filename for UI display. This will also convert + the filename into unicode without failing. + :param shorten: this optionally shortens the filename to strip of the + path that leads up to it. + """ + if shorten: + filename = os.path.basename(filename) + else: + filename = os.fspath(filename) + + if isinstance(filename, bytes): + filename = filename.decode(sys.getfilesystemencoding(), "replace") + else: + filename = filename.encode("utf-8", "surrogateescape").decode( + "utf-8", "replace" + ) + + return filename + + +def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str: + r"""Returns the config folder for the application. The default behavior + is to return whatever is most appropriate for the operating system. + + To give you an idea, for an app called ``"Foo Bar"``, something like + the following folders could be returned: + + Mac OS X: + ``~/Library/Application Support/Foo Bar`` + Mac OS X (POSIX): + ``~/.foo-bar`` + Unix: + ``~/.config/foo-bar`` + Unix (POSIX): + ``~/.foo-bar`` + Windows (roaming): + ``C:\Users\\AppData\Roaming\Foo Bar`` + Windows (not roaming): + ``C:\Users\\AppData\Local\Foo Bar`` + + .. versionadded:: 2.0 + + :param app_name: the application name. This should be properly capitalized + and can contain whitespace. + :param roaming: controls if the folder should be roaming or not on Windows. + Has no effect otherwise. + :param force_posix: if this is set to `True` then on any POSIX system the + folder will be stored in the home folder with a leading + dot instead of the XDG config home or darwin's + application support folder. + """ + if WIN: + key = "APPDATA" if roaming else "LOCALAPPDATA" + folder = os.environ.get(key) + if folder is None: + folder = os.path.expanduser("~") + return os.path.join(folder, app_name) + if force_posix: + return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}")) + if sys.platform == "darwin": + return os.path.join( + os.path.expanduser("~/Library/Application Support"), app_name + ) + return os.path.join( + os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), + _posixify(app_name), + ) + + +class PacifyFlushWrapper: + """This wrapper is used to catch and suppress BrokenPipeErrors resulting + from ``.flush()`` being called on broken pipe during the shutdown/final-GC + of the Python interpreter. Notably ``.flush()`` is always called on + ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any + other cleanup code, and the case where the underlying file is not a broken + pipe, all calls and attributes are proxied. + """ + + def __init__(self, wrapped: t.IO[t.Any]) -> None: + self.wrapped = wrapped + + def flush(self) -> None: + try: + self.wrapped.flush() + except OSError as e: + import errno + + if e.errno != errno.EPIPE: + raise + + def __getattr__(self, attr: str) -> t.Any: + return getattr(self.wrapped, attr) + + +def _detect_program_name( + path: t.Optional[str] = None, _main: t.Optional[ModuleType] = None +) -> str: + """Determine the command used to run the program, for use in help + text. If a file or entry point was executed, the file name is + returned. If ``python -m`` was used to execute a module or package, + ``python -m name`` is returned. + + This doesn't try to be too precise, the goal is to give a concise + name for help text. Files are only shown as their name without the + path. ``python`` is only shown for modules, and the full path to + ``sys.executable`` is not shown. + + :param path: The Python file being executed. Python puts this in + ``sys.argv[0]``, which is used by default. + :param _main: The ``__main__`` module. This should only be passed + during internal testing. + + .. versionadded:: 8.0 + Based on command args detection in the Werkzeug reloader. + + :meta private: + """ + if _main is None: + _main = sys.modules["__main__"] + + if not path: + path = sys.argv[0] + + # The value of __package__ indicates how Python was called. It may + # not exist if a setuptools script is installed as an egg. It may be + # set incorrectly for entry points created with pip on Windows. + # It is set to "" inside a Shiv or PEX zipapp. + if getattr(_main, "__package__", None) in {None, ""} or ( + os.name == "nt" + and _main.__package__ == "" + and not os.path.exists(path) + and os.path.exists(f"{path}.exe") + ): + # Executed a file, like "python app.py". + return os.path.basename(path) + + # Executed a module, like "python -m example". + # Rewritten by Python from "-m script" to "/path/to/script.py". + # Need to look at main module to determine how it was executed. + py_module = t.cast(str, _main.__package__) + name = os.path.splitext(os.path.basename(path))[0] + + # A submodule like "example.cli". + if name != "__main__": + py_module = f"{py_module}.{name}" + + return f"python -m {py_module.lstrip('.')}" + + +def _expand_args( + args: t.Iterable[str], + *, + user: bool = True, + env: bool = True, + glob_recursive: bool = True, +) -> t.List[str]: + """Simulate Unix shell expansion with Python functions. + + See :func:`glob.glob`, :func:`os.path.expanduser`, and + :func:`os.path.expandvars`. + + This is intended for use on Windows, where the shell does not do any + expansion. It may not exactly match what a Unix shell would do. + + :param args: List of command line arguments to expand. + :param user: Expand user home directory. + :param env: Expand environment variables. + :param glob_recursive: ``**`` matches directories recursively. + + .. versionchanged:: 8.1 + Invalid glob patterns are treated as empty expansions rather + than raising an error. + + .. versionadded:: 8.0 + + :meta private: + """ + from glob import glob + + out = [] + + for arg in args: + if user: + arg = os.path.expanduser(arg) + + if env: + arg = os.path.expandvars(arg) + + try: + matches = glob(arg, recursive=glob_recursive) + except re.error: + matches = [] + + if not matches: + out.append(arg) + else: + out.extend(matches) + + return out diff --git a/.venv/lib/python3.9/site-packages/colorama-0.4.6.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/colorama-0.4.6.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama-0.4.6.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/colorama-0.4.6.dist-info/METADATA b/.venv/lib/python3.9/site-packages/colorama-0.4.6.dist-info/METADATA new file mode 100644 index 00000000..a1b5c575 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama-0.4.6.dist-info/METADATA @@ -0,0 +1,441 @@ +Metadata-Version: 2.1 +Name: colorama +Version: 0.4.6 +Summary: Cross-platform colored terminal text. +Project-URL: Homepage, https://github.com/tartley/colorama +Author-email: Jonathan Hartley +License-File: LICENSE.txt +Keywords: ansi,color,colour,crossplatform,terminal,text,windows,xplatform +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Terminals +Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7 +Description-Content-Type: text/x-rst + +.. image:: https://img.shields.io/pypi/v/colorama.svg + :target: https://pypi.org/project/colorama/ + :alt: Latest Version + +.. image:: https://img.shields.io/pypi/pyversions/colorama.svg + :target: https://pypi.org/project/colorama/ + :alt: Supported Python versions + +.. image:: https://github.com/tartley/colorama/actions/workflows/test.yml/badge.svg + :target: https://github.com/tartley/colorama/actions/workflows/test.yml + :alt: Build Status + +Colorama +======== + +Makes ANSI escape character sequences (for producing colored terminal text and +cursor positioning) work under MS Windows. + +.. |donate| image:: https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif + :target: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2MZ9D2GMLYCUJ&item_name=Colorama¤cy_code=USD + :alt: Donate with Paypal + +`PyPI for releases `_ | +`Github for source `_ | +`Colorama for enterprise on Tidelift `_ + +If you find Colorama useful, please |donate| to the authors. Thank you! + +Installation +------------ + +Tested on CPython 2.7, 3.7, 3.8, 3.9 and 3.10 and Pypy 2.7 and 3.8. + +No requirements other than the standard library. + +.. code-block:: bash + + pip install colorama + # or + conda install -c anaconda colorama + +Description +----------- + +ANSI escape character sequences have long been used to produce colored terminal +text and cursor positioning on Unix and Macs. Colorama makes this work on +Windows, too, by wrapping ``stdout``, stripping ANSI sequences it finds (which +would appear as gobbledygook in the output), and converting them into the +appropriate win32 calls to modify the state of the terminal. On other platforms, +Colorama does nothing. + +This has the upshot of providing a simple cross-platform API for printing +colored terminal text from Python, and has the happy side-effect that existing +applications or libraries which use ANSI sequences to produce colored output on +Linux or Macs can now also work on Windows, simply by calling +``colorama.just_fix_windows_console()`` (since v0.4.6) or ``colorama.init()`` +(all versions, but may have other side-effects – see below). + +An alternative approach is to install ``ansi.sys`` on Windows machines, which +provides the same behaviour for all applications running in terminals. Colorama +is intended for situations where that isn't easy (e.g., maybe your app doesn't +have an installer.) + +Demo scripts in the source code repository print some colored text using +ANSI sequences. Compare their output under Gnome-terminal's built in ANSI +handling, versus on Windows Command-Prompt using Colorama: + +.. image:: https://github.com/tartley/colorama/raw/master/screenshots/ubuntu-demo.png + :width: 661 + :height: 357 + :alt: ANSI sequences on Ubuntu under gnome-terminal. + +.. image:: https://github.com/tartley/colorama/raw/master/screenshots/windows-demo.png + :width: 668 + :height: 325 + :alt: Same ANSI sequences on Windows, using Colorama. + +These screenshots show that, on Windows, Colorama does not support ANSI 'dim +text'; it looks the same as 'normal text'. + +Usage +----- + +Initialisation +.............. + +If the only thing you want from Colorama is to get ANSI escapes to work on +Windows, then run: + +.. code-block:: python + + from colorama import just_fix_windows_console + just_fix_windows_console() + +If you're on a recent version of Windows 10 or better, and your stdout/stderr +are pointing to a Windows console, then this will flip the magic configuration +switch to enable Windows' built-in ANSI support. + +If you're on an older version of Windows, and your stdout/stderr are pointing to +a Windows console, then this will wrap ``sys.stdout`` and/or ``sys.stderr`` in a +magic file object that intercepts ANSI escape sequences and issues the +appropriate Win32 calls to emulate them. + +In all other circumstances, it does nothing whatsoever. Basically the idea is +that this makes Windows act like Unix with respect to ANSI escape handling. + +It's safe to call this function multiple times. It's safe to call this function +on non-Windows platforms, but it won't do anything. It's safe to call this +function when one or both of your stdout/stderr are redirected to a file – it +won't do anything to those streams. + +Alternatively, you can use the older interface with more features (but also more +potential footguns): + +.. code-block:: python + + from colorama import init + init() + +This does the same thing as ``just_fix_windows_console``, except for the +following differences: + +- It's not safe to call ``init`` multiple times; you can end up with multiple + layers of wrapping and broken ANSI support. + +- Colorama will apply a heuristic to guess whether stdout/stderr support ANSI, + and if it thinks they don't, then it will wrap ``sys.stdout`` and + ``sys.stderr`` in a magic file object that strips out ANSI escape sequences + before printing them. This happens on all platforms, and can be convenient if + you want to write your code to emit ANSI escape sequences unconditionally, and + let Colorama decide whether they should actually be output. But note that + Colorama's heuristic is not particularly clever. + +- ``init`` also accepts explicit keyword args to enable/disable various + functionality – see below. + +To stop using Colorama before your program exits, simply call ``deinit()``. +This will restore ``stdout`` and ``stderr`` to their original values, so that +Colorama is disabled. To resume using Colorama again, call ``reinit()``; it is +cheaper than calling ``init()`` again (but does the same thing). + +Most users should depend on ``colorama >= 0.4.6``, and use +``just_fix_windows_console``. The old ``init`` interface will be supported +indefinitely for backwards compatibility, but we don't plan to fix any issues +with it, also for backwards compatibility. + +Colored Output +.............. + +Cross-platform printing of colored text can then be done using Colorama's +constant shorthand for ANSI escape sequences. These are deliberately +rudimentary, see below. + +.. code-block:: python + + from colorama import Fore, Back, Style + print(Fore.RED + 'some red text') + print(Back.GREEN + 'and with a green background') + print(Style.DIM + 'and in dim text') + print(Style.RESET_ALL) + print('back to normal now') + +...or simply by manually printing ANSI sequences from your own code: + +.. code-block:: python + + print('\033[31m' + 'some red text') + print('\033[39m') # and reset to default color + +...or, Colorama can be used in conjunction with existing ANSI libraries +such as the venerable `Termcolor `_ +the fabulous `Blessings `_, +or the incredible `_Rich `_. + +If you wish Colorama's Fore, Back and Style constants were more capable, +then consider using one of the above highly capable libraries to generate +colors, etc, and use Colorama just for its primary purpose: to convert +those ANSI sequences to also work on Windows: + +SIMILARLY, do not send PRs adding the generation of new ANSI types to Colorama. +We are only interested in converting ANSI codes to win32 API calls, not +shortcuts like the above to generate ANSI characters. + +.. code-block:: python + + from colorama import just_fix_windows_console + from termcolor import colored + + # use Colorama to make Termcolor work on Windows too + just_fix_windows_console() + + # then use Termcolor for all colored text output + print(colored('Hello, World!', 'green', 'on_red')) + +Available formatting constants are:: + + Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. + Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. + Style: DIM, NORMAL, BRIGHT, RESET_ALL + +``Style.RESET_ALL`` resets foreground, background, and brightness. Colorama will +perform this reset automatically on program exit. + +These are fairly well supported, but not part of the standard:: + + Fore: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX + Back: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX + +Cursor Positioning +.................. + +ANSI codes to reposition the cursor are supported. See ``demos/demo06.py`` for +an example of how to generate them. + +Init Keyword Args +................. + +``init()`` accepts some ``**kwargs`` to override default behaviour. + +init(autoreset=False): + If you find yourself repeatedly sending reset sequences to turn off color + changes at the end of every print, then ``init(autoreset=True)`` will + automate that: + + .. code-block:: python + + from colorama import init + init(autoreset=True) + print(Fore.RED + 'some red text') + print('automatically back to default color again') + +init(strip=None): + Pass ``True`` or ``False`` to override whether ANSI codes should be + stripped from the output. The default behaviour is to strip if on Windows + or if output is redirected (not a tty). + +init(convert=None): + Pass ``True`` or ``False`` to override whether to convert ANSI codes in the + output into win32 calls. The default behaviour is to convert if on Windows + and output is to a tty (terminal). + +init(wrap=True): + On Windows, Colorama works by replacing ``sys.stdout`` and ``sys.stderr`` + with proxy objects, which override the ``.write()`` method to do their work. + If this wrapping causes you problems, then this can be disabled by passing + ``init(wrap=False)``. The default behaviour is to wrap if ``autoreset`` or + ``strip`` or ``convert`` are True. + + When wrapping is disabled, colored printing on non-Windows platforms will + continue to work as normal. To do cross-platform colored output, you can + use Colorama's ``AnsiToWin32`` proxy directly: + + .. code-block:: python + + import sys + from colorama import init, AnsiToWin32 + init(wrap=False) + stream = AnsiToWin32(sys.stderr).stream + + # Python 2 + print >>stream, Fore.BLUE + 'blue text on stderr' + + # Python 3 + print(Fore.BLUE + 'blue text on stderr', file=stream) + +Recognised ANSI Sequences +......................... + +ANSI sequences generally take the form:: + + ESC [ ; ... + +Where ```` is an integer, and ```` is a single letter. Zero or +more params are passed to a ````. If no params are passed, it is +generally synonymous with passing a single zero. No spaces exist in the +sequence; they have been inserted here simply to read more easily. + +The only ANSI sequences that Colorama converts into win32 calls are:: + + ESC [ 0 m # reset all (colors and brightness) + ESC [ 1 m # bright + ESC [ 2 m # dim (looks same as normal brightness) + ESC [ 22 m # normal brightness + + # FOREGROUND: + ESC [ 30 m # black + ESC [ 31 m # red + ESC [ 32 m # green + ESC [ 33 m # yellow + ESC [ 34 m # blue + ESC [ 35 m # magenta + ESC [ 36 m # cyan + ESC [ 37 m # white + ESC [ 39 m # reset + + # BACKGROUND + ESC [ 40 m # black + ESC [ 41 m # red + ESC [ 42 m # green + ESC [ 43 m # yellow + ESC [ 44 m # blue + ESC [ 45 m # magenta + ESC [ 46 m # cyan + ESC [ 47 m # white + ESC [ 49 m # reset + + # cursor positioning + ESC [ y;x H # position cursor at x across, y down + ESC [ y;x f # position cursor at x across, y down + ESC [ n A # move cursor n lines up + ESC [ n B # move cursor n lines down + ESC [ n C # move cursor n characters forward + ESC [ n D # move cursor n characters backward + + # clear the screen + ESC [ mode J # clear the screen + + # clear the line + ESC [ mode K # clear the line + +Multiple numeric params to the ``'m'`` command can be combined into a single +sequence:: + + ESC [ 36 ; 45 ; 1 m # bright cyan text on magenta background + +All other ANSI sequences of the form ``ESC [ ; ... `` +are silently stripped from the output on Windows. + +Any other form of ANSI sequence, such as single-character codes or alternative +initial characters, are not recognised or stripped. It would be cool to add +them though. Let me know if it would be useful for you, via the Issues on +GitHub. + +Status & Known Problems +----------------------- + +I've personally only tested it on Windows XP (CMD, Console2), Ubuntu +(gnome-terminal, xterm), and OS X. + +Some valid ANSI sequences aren't recognised. + +If you're hacking on the code, see `README-hacking.md`_. ESPECIALLY, see the +explanation there of why we do not want PRs that allow Colorama to generate new +types of ANSI codes. + +See outstanding issues and wish-list: +https://github.com/tartley/colorama/issues + +If anything doesn't work for you, or doesn't do what you expected or hoped for, +I'd love to hear about it on that issues list, would be delighted by patches, +and would be happy to grant commit access to anyone who submits a working patch +or two. + +.. _README-hacking.md: README-hacking.md + +License +------- + +Copyright Jonathan Hartley & Arnon Yaari, 2013-2020. BSD 3-Clause license; see +LICENSE file. + +Professional support +-------------------- + +.. |tideliftlogo| image:: https://cdn2.hubspot.net/hubfs/4008838/website/logos/logos_for_download/Tidelift_primary-shorthand-logo.png + :alt: Tidelift + :target: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme + +.. list-table:: + :widths: 10 100 + + * - |tideliftlogo| + - Professional support for colorama is available as part of the + `Tidelift Subscription`_. + Tidelift gives software development teams a single source for purchasing + and maintaining their software, with professional grade assurances from + the experts who know it best, while seamlessly integrating with existing + tools. + +.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme + +Thanks +------ + +See the CHANGELOG for more thanks! + +* Marc Schlaich (schlamar) for a ``setup.py`` fix for Python2.5. +* Marc Abramowitz, reported & fixed a crash on exit with closed ``stdout``, + providing a solution to issue #7's setuptools/distutils debate, + and other fixes. +* User 'eryksun', for guidance on correctly instantiating ``ctypes.windll``. +* Matthew McCormick for politely pointing out a longstanding crash on non-Win. +* Ben Hoyt, for a magnificent fix under 64-bit Windows. +* Jesse at Empty Square for submitting a fix for examples in the README. +* User 'jamessp', an observant documentation fix for cursor positioning. +* User 'vaal1239', Dave Mckee & Lackner Kristof for a tiny but much-needed Win7 + fix. +* Julien Stuyck, for wisely suggesting Python3 compatible updates to README. +* Daniel Griffith for multiple fabulous patches. +* Oscar Lesta for a valuable fix to stop ANSI chars being sent to non-tty + output. +* Roger Binns, for many suggestions, valuable feedback, & bug reports. +* Tim Golden for thought and much appreciated feedback on the initial idea. +* User 'Zearin' for updates to the README file. +* John Szakmeister for adding support for light colors +* Charles Merriam for adding documentation to demos +* Jurko for a fix on 64-bit Windows CPython2.5 w/o ctypes +* Florian Bruhin for a fix when stdout or stderr are None +* Thomas Weininger for fixing ValueError on Windows +* Remi Rampin for better Github integration and fixes to the README file +* Simeon Visser for closing a file handle using 'with' and updating classifiers + to include Python 3.3 and 3.4 +* Andy Neff for fixing RESET of LIGHT_EX colors. +* Jonathan Hartley for the initial idea and implementation. diff --git a/.venv/lib/python3.9/site-packages/colorama-0.4.6.dist-info/RECORD b/.venv/lib/python3.9/site-packages/colorama-0.4.6.dist-info/RECORD new file mode 100644 index 00000000..11f39e1d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama-0.4.6.dist-info/RECORD @@ -0,0 +1,31 @@ +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/colorama/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/colorama/ansi.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/colorama/ansitowin32.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/colorama/initialise.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/colorama/tests/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/colorama/tests/ansi_test.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/colorama/tests/ansitowin32_test.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/colorama/tests/initialise_test.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/colorama/tests/isatty_test.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/colorama/tests/utils.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/colorama/tests/winterm_test.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/colorama/win32.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/colorama/winterm.cpython-39.pyc,, +colorama-0.4.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +colorama-0.4.6.dist-info/METADATA,sha256=e67SnrUMOym9sz_4TjF3vxvAV4T3aF7NyqRHHH3YEMw,17158 +colorama-0.4.6.dist-info/RECORD,, +colorama-0.4.6.dist-info/WHEEL,sha256=cdcF4Fbd0FPtw2EMIOwH-3rSOTUdTCeOSXRMD1iLUb8,105 +colorama-0.4.6.dist-info/licenses/LICENSE.txt,sha256=ysNcAmhuXQSlpxQL-zs25zrtSWZW6JEQLkKIhteTAxg,1491 +colorama/__init__.py,sha256=wePQA4U20tKgYARySLEC047ucNX-g8pRLpYBuiHlLb8,266 +colorama/ansi.py,sha256=Top4EeEuaQdBWdteKMEcGOTeKeF19Q-Wo_6_Cj5kOzQ,2522 +colorama/ansitowin32.py,sha256=vPNYa3OZbxjbuFyaVo0Tmhmy1FZ1lKMWCnT7odXpItk,11128 +colorama/initialise.py,sha256=-hIny86ClXo39ixh5iSCfUIa2f_h_bgKRDW7gqs-KLU,3325 +colorama/tests/__init__.py,sha256=MkgPAEzGQd-Rq0w0PZXSX2LadRWhUECcisJY8lSrm4Q,75 +colorama/tests/ansi_test.py,sha256=FeViDrUINIZcr505PAxvU4AjXz1asEiALs9GXMhwRaE,2839 +colorama/tests/ansitowin32_test.py,sha256=RN7AIhMJ5EqDsYaCjVo-o4u8JzDD4ukJbmevWKS70rY,10678 +colorama/tests/initialise_test.py,sha256=BbPy-XfyHwJ6zKozuQOvNvQZzsx9vdb_0bYXn7hsBTc,6741 +colorama/tests/isatty_test.py,sha256=Pg26LRpv0yQDB5Ac-sxgVXG7hsA1NYvapFgApZfYzZg,1866 +colorama/tests/utils.py,sha256=1IIRylG39z5-dzq09R_ngufxyPZxgldNbrxKxUGwGKE,1079 +colorama/tests/winterm_test.py,sha256=qoWFPEjym5gm2RuMwpf3pOis3a5r_PJZFCzK254JL8A,3709 +colorama/win32.py,sha256=YQOKwMTwtGBbsY4dL5HYTvwTeP9wIQra5MvPNddpxZs,6181 +colorama/winterm.py,sha256=XCQFDHjPi6AHYNdZwy0tA02H-Jh48Jp-HvCjeLeLp3U,7134 diff --git a/.venv/lib/python3.9/site-packages/colorama-0.4.6.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/colorama-0.4.6.dist-info/WHEEL new file mode 100644 index 00000000..d79189fd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama-0.4.6.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.11.1 +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any diff --git a/.venv/lib/python3.9/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt b/.venv/lib/python3.9/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt new file mode 100644 index 00000000..3105888e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt @@ -0,0 +1,27 @@ +Copyright (c) 2010 Jonathan Hartley +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holders, nor those of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/lib/python3.9/site-packages/colorama/__init__.py b/.venv/lib/python3.9/site-packages/colorama/__init__.py new file mode 100644 index 00000000..383101cd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama/__init__.py @@ -0,0 +1,7 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from .initialise import init, deinit, reinit, colorama_text, just_fix_windows_console +from .ansi import Fore, Back, Style, Cursor +from .ansitowin32 import AnsiToWin32 + +__version__ = '0.4.6' + diff --git a/.venv/lib/python3.9/site-packages/colorama/ansi.py b/.venv/lib/python3.9/site-packages/colorama/ansi.py new file mode 100644 index 00000000..11ec695f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama/ansi.py @@ -0,0 +1,102 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +''' +This module generates ANSI character codes to printing colors to terminals. +See: http://en.wikipedia.org/wiki/ANSI_escape_code +''' + +CSI = '\033[' +OSC = '\033]' +BEL = '\a' + + +def code_to_chars(code): + return CSI + str(code) + 'm' + +def set_title(title): + return OSC + '2;' + title + BEL + +def clear_screen(mode=2): + return CSI + str(mode) + 'J' + +def clear_line(mode=2): + return CSI + str(mode) + 'K' + + +class AnsiCodes(object): + def __init__(self): + # the subclasses declare class attributes which are numbers. + # Upon instantiation we define instance attributes, which are the same + # as the class attributes but wrapped with the ANSI escape sequence + for name in dir(self): + if not name.startswith('_'): + value = getattr(self, name) + setattr(self, name, code_to_chars(value)) + + +class AnsiCursor(object): + def UP(self, n=1): + return CSI + str(n) + 'A' + def DOWN(self, n=1): + return CSI + str(n) + 'B' + def FORWARD(self, n=1): + return CSI + str(n) + 'C' + def BACK(self, n=1): + return CSI + str(n) + 'D' + def POS(self, x=1, y=1): + return CSI + str(y) + ';' + str(x) + 'H' + + +class AnsiFore(AnsiCodes): + BLACK = 30 + RED = 31 + GREEN = 32 + YELLOW = 33 + BLUE = 34 + MAGENTA = 35 + CYAN = 36 + WHITE = 37 + RESET = 39 + + # These are fairly well supported, but not part of the standard. + LIGHTBLACK_EX = 90 + LIGHTRED_EX = 91 + LIGHTGREEN_EX = 92 + LIGHTYELLOW_EX = 93 + LIGHTBLUE_EX = 94 + LIGHTMAGENTA_EX = 95 + LIGHTCYAN_EX = 96 + LIGHTWHITE_EX = 97 + + +class AnsiBack(AnsiCodes): + BLACK = 40 + RED = 41 + GREEN = 42 + YELLOW = 43 + BLUE = 44 + MAGENTA = 45 + CYAN = 46 + WHITE = 47 + RESET = 49 + + # These are fairly well supported, but not part of the standard. + LIGHTBLACK_EX = 100 + LIGHTRED_EX = 101 + LIGHTGREEN_EX = 102 + LIGHTYELLOW_EX = 103 + LIGHTBLUE_EX = 104 + LIGHTMAGENTA_EX = 105 + LIGHTCYAN_EX = 106 + LIGHTWHITE_EX = 107 + + +class AnsiStyle(AnsiCodes): + BRIGHT = 1 + DIM = 2 + NORMAL = 22 + RESET_ALL = 0 + +Fore = AnsiFore() +Back = AnsiBack() +Style = AnsiStyle() +Cursor = AnsiCursor() diff --git a/.venv/lib/python3.9/site-packages/colorama/ansitowin32.py b/.venv/lib/python3.9/site-packages/colorama/ansitowin32.py new file mode 100644 index 00000000..abf209e6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama/ansitowin32.py @@ -0,0 +1,277 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import re +import sys +import os + +from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style, BEL +from .winterm import enable_vt_processing, WinTerm, WinColor, WinStyle +from .win32 import windll, winapi_test + + +winterm = None +if windll is not None: + winterm = WinTerm() + + +class StreamWrapper(object): + ''' + Wraps a stream (such as stdout), acting as a transparent proxy for all + attribute access apart from method 'write()', which is delegated to our + Converter instance. + ''' + def __init__(self, wrapped, converter): + # double-underscore everything to prevent clashes with names of + # attributes on the wrapped stream object. + self.__wrapped = wrapped + self.__convertor = converter + + def __getattr__(self, name): + return getattr(self.__wrapped, name) + + def __enter__(self, *args, **kwargs): + # special method lookup bypasses __getattr__/__getattribute__, see + # https://stackoverflow.com/questions/12632894/why-doesnt-getattr-work-with-exit + # thus, contextlib magic methods are not proxied via __getattr__ + return self.__wrapped.__enter__(*args, **kwargs) + + def __exit__(self, *args, **kwargs): + return self.__wrapped.__exit__(*args, **kwargs) + + def __setstate__(self, state): + self.__dict__ = state + + def __getstate__(self): + return self.__dict__ + + def write(self, text): + self.__convertor.write(text) + + def isatty(self): + stream = self.__wrapped + if 'PYCHARM_HOSTED' in os.environ: + if stream is not None and (stream is sys.__stdout__ or stream is sys.__stderr__): + return True + try: + stream_isatty = stream.isatty + except AttributeError: + return False + else: + return stream_isatty() + + @property + def closed(self): + stream = self.__wrapped + try: + return stream.closed + # AttributeError in the case that the stream doesn't support being closed + # ValueError for the case that the stream has already been detached when atexit runs + except (AttributeError, ValueError): + return True + + +class AnsiToWin32(object): + ''' + Implements a 'write()' method which, on Windows, will strip ANSI character + sequences from the text, and if outputting to a tty, will convert them into + win32 function calls. + ''' + ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer + ANSI_OSC_RE = re.compile('\001?\033\\]([^\a]*)(\a)\002?') # Operating System Command + + def __init__(self, wrapped, convert=None, strip=None, autoreset=False): + # The wrapped stream (normally sys.stdout or sys.stderr) + self.wrapped = wrapped + + # should we reset colors to defaults after every .write() + self.autoreset = autoreset + + # create the proxy wrapping our output stream + self.stream = StreamWrapper(wrapped, self) + + on_windows = os.name == 'nt' + # We test if the WinAPI works, because even if we are on Windows + # we may be using a terminal that doesn't support the WinAPI + # (e.g. Cygwin Terminal). In this case it's up to the terminal + # to support the ANSI codes. + conversion_supported = on_windows and winapi_test() + try: + fd = wrapped.fileno() + except Exception: + fd = -1 + system_has_native_ansi = not on_windows or enable_vt_processing(fd) + have_tty = not self.stream.closed and self.stream.isatty() + need_conversion = conversion_supported and not system_has_native_ansi + + # should we strip ANSI sequences from our output? + if strip is None: + strip = need_conversion or not have_tty + self.strip = strip + + # should we should convert ANSI sequences into win32 calls? + if convert is None: + convert = need_conversion and have_tty + self.convert = convert + + # dict of ansi codes to win32 functions and parameters + self.win32_calls = self.get_win32_calls() + + # are we wrapping stderr? + self.on_stderr = self.wrapped is sys.stderr + + def should_wrap(self): + ''' + True if this class is actually needed. If false, then the output + stream will not be affected, nor will win32 calls be issued, so + wrapping stdout is not actually required. This will generally be + False on non-Windows platforms, unless optional functionality like + autoreset has been requested using kwargs to init() + ''' + return self.convert or self.strip or self.autoreset + + def get_win32_calls(self): + if self.convert and winterm: + return { + AnsiStyle.RESET_ALL: (winterm.reset_all, ), + AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), + AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), + AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), + AnsiFore.BLACK: (winterm.fore, WinColor.BLACK), + AnsiFore.RED: (winterm.fore, WinColor.RED), + AnsiFore.GREEN: (winterm.fore, WinColor.GREEN), + AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW), + AnsiFore.BLUE: (winterm.fore, WinColor.BLUE), + AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), + AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), + AnsiFore.WHITE: (winterm.fore, WinColor.GREY), + AnsiFore.RESET: (winterm.fore, ), + AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True), + AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True), + AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True), + AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True), + AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True), + AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True), + AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True), + AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True), + AnsiBack.BLACK: (winterm.back, WinColor.BLACK), + AnsiBack.RED: (winterm.back, WinColor.RED), + AnsiBack.GREEN: (winterm.back, WinColor.GREEN), + AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW), + AnsiBack.BLUE: (winterm.back, WinColor.BLUE), + AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), + AnsiBack.CYAN: (winterm.back, WinColor.CYAN), + AnsiBack.WHITE: (winterm.back, WinColor.GREY), + AnsiBack.RESET: (winterm.back, ), + AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True), + AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True), + AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True), + AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True), + AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True), + AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True), + AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True), + AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True), + } + return dict() + + def write(self, text): + if self.strip or self.convert: + self.write_and_convert(text) + else: + self.wrapped.write(text) + self.wrapped.flush() + if self.autoreset: + self.reset_all() + + + def reset_all(self): + if self.convert: + self.call_win32('m', (0,)) + elif not self.strip and not self.stream.closed: + self.wrapped.write(Style.RESET_ALL) + + + def write_and_convert(self, text): + ''' + Write the given text to our wrapped stream, stripping any ANSI + sequences from the text, and optionally converting them into win32 + calls. + ''' + cursor = 0 + text = self.convert_osc(text) + for match in self.ANSI_CSI_RE.finditer(text): + start, end = match.span() + self.write_plain_text(text, cursor, start) + self.convert_ansi(*match.groups()) + cursor = end + self.write_plain_text(text, cursor, len(text)) + + + def write_plain_text(self, text, start, end): + if start < end: + self.wrapped.write(text[start:end]) + self.wrapped.flush() + + + def convert_ansi(self, paramstring, command): + if self.convert: + params = self.extract_params(command, paramstring) + self.call_win32(command, params) + + + def extract_params(self, command, paramstring): + if command in 'Hf': + params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';')) + while len(params) < 2: + # defaults: + params = params + (1,) + else: + params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0) + if len(params) == 0: + # defaults: + if command in 'JKm': + params = (0,) + elif command in 'ABCD': + params = (1,) + + return params + + + def call_win32(self, command, params): + if command == 'm': + for param in params: + if param in self.win32_calls: + func_args = self.win32_calls[param] + func = func_args[0] + args = func_args[1:] + kwargs = dict(on_stderr=self.on_stderr) + func(*args, **kwargs) + elif command in 'J': + winterm.erase_screen(params[0], on_stderr=self.on_stderr) + elif command in 'K': + winterm.erase_line(params[0], on_stderr=self.on_stderr) + elif command in 'Hf': # cursor position - absolute + winterm.set_cursor_position(params, on_stderr=self.on_stderr) + elif command in 'ABCD': # cursor position - relative + n = params[0] + # A - up, B - down, C - forward, D - back + x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command] + winterm.cursor_adjust(x, y, on_stderr=self.on_stderr) + + + def convert_osc(self, text): + for match in self.ANSI_OSC_RE.finditer(text): + start, end = match.span() + text = text[:start] + text[end:] + paramstring, command = match.groups() + if command == BEL: + if paramstring.count(";") == 1: + params = paramstring.split(";") + # 0 - change title and icon (we will only change title) + # 1 - change icon (we don't support this) + # 2 - change title + if params[0] in '02': + winterm.set_title(params[1]) + return text + + + def flush(self): + self.wrapped.flush() diff --git a/.venv/lib/python3.9/site-packages/colorama/initialise.py b/.venv/lib/python3.9/site-packages/colorama/initialise.py new file mode 100644 index 00000000..d5fd4b71 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama/initialise.py @@ -0,0 +1,121 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import atexit +import contextlib +import sys + +from .ansitowin32 import AnsiToWin32 + + +def _wipe_internal_state_for_tests(): + global orig_stdout, orig_stderr + orig_stdout = None + orig_stderr = None + + global wrapped_stdout, wrapped_stderr + wrapped_stdout = None + wrapped_stderr = None + + global atexit_done + atexit_done = False + + global fixed_windows_console + fixed_windows_console = False + + try: + # no-op if it wasn't registered + atexit.unregister(reset_all) + except AttributeError: + # python 2: no atexit.unregister. Oh well, we did our best. + pass + + +def reset_all(): + if AnsiToWin32 is not None: # Issue #74: objects might become None at exit + AnsiToWin32(orig_stdout).reset_all() + + +def init(autoreset=False, convert=None, strip=None, wrap=True): + + if not wrap and any([autoreset, convert, strip]): + raise ValueError('wrap=False conflicts with any other arg=True') + + global wrapped_stdout, wrapped_stderr + global orig_stdout, orig_stderr + + orig_stdout = sys.stdout + orig_stderr = sys.stderr + + if sys.stdout is None: + wrapped_stdout = None + else: + sys.stdout = wrapped_stdout = \ + wrap_stream(orig_stdout, convert, strip, autoreset, wrap) + if sys.stderr is None: + wrapped_stderr = None + else: + sys.stderr = wrapped_stderr = \ + wrap_stream(orig_stderr, convert, strip, autoreset, wrap) + + global atexit_done + if not atexit_done: + atexit.register(reset_all) + atexit_done = True + + +def deinit(): + if orig_stdout is not None: + sys.stdout = orig_stdout + if orig_stderr is not None: + sys.stderr = orig_stderr + + +def just_fix_windows_console(): + global fixed_windows_console + + if sys.platform != "win32": + return + if fixed_windows_console: + return + if wrapped_stdout is not None or wrapped_stderr is not None: + # Someone already ran init() and it did stuff, so we won't second-guess them + return + + # On newer versions of Windows, AnsiToWin32.__init__ will implicitly enable the + # native ANSI support in the console as a side-effect. We only need to actually + # replace sys.stdout/stderr if we're in the old-style conversion mode. + new_stdout = AnsiToWin32(sys.stdout, convert=None, strip=None, autoreset=False) + if new_stdout.convert: + sys.stdout = new_stdout + new_stderr = AnsiToWin32(sys.stderr, convert=None, strip=None, autoreset=False) + if new_stderr.convert: + sys.stderr = new_stderr + + fixed_windows_console = True + +@contextlib.contextmanager +def colorama_text(*args, **kwargs): + init(*args, **kwargs) + try: + yield + finally: + deinit() + + +def reinit(): + if wrapped_stdout is not None: + sys.stdout = wrapped_stdout + if wrapped_stderr is not None: + sys.stderr = wrapped_stderr + + +def wrap_stream(stream, convert, strip, autoreset, wrap): + if wrap: + wrapper = AnsiToWin32(stream, + convert=convert, strip=strip, autoreset=autoreset) + if wrapper.should_wrap(): + stream = wrapper.stream + return stream + + +# Use this for initial setup as well, to reduce code duplication +_wipe_internal_state_for_tests() diff --git a/.venv/lib/python3.9/site-packages/colorama/tests/__init__.py b/.venv/lib/python3.9/site-packages/colorama/tests/__init__.py new file mode 100644 index 00000000..8c5661e9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama/tests/__init__.py @@ -0,0 +1 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. diff --git a/.venv/lib/python3.9/site-packages/colorama/tests/ansi_test.py b/.venv/lib/python3.9/site-packages/colorama/tests/ansi_test.py new file mode 100644 index 00000000..0a20c80f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama/tests/ansi_test.py @@ -0,0 +1,76 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main + +from ..ansi import Back, Fore, Style +from ..ansitowin32 import AnsiToWin32 + +stdout_orig = sys.stdout +stderr_orig = sys.stderr + + +class AnsiTest(TestCase): + + def setUp(self): + # sanity check: stdout should be a file or StringIO object. + # It will only be AnsiToWin32 if init() has previously wrapped it + self.assertNotEqual(type(sys.stdout), AnsiToWin32) + self.assertNotEqual(type(sys.stderr), AnsiToWin32) + + def tearDown(self): + sys.stdout = stdout_orig + sys.stderr = stderr_orig + + + def testForeAttributes(self): + self.assertEqual(Fore.BLACK, '\033[30m') + self.assertEqual(Fore.RED, '\033[31m') + self.assertEqual(Fore.GREEN, '\033[32m') + self.assertEqual(Fore.YELLOW, '\033[33m') + self.assertEqual(Fore.BLUE, '\033[34m') + self.assertEqual(Fore.MAGENTA, '\033[35m') + self.assertEqual(Fore.CYAN, '\033[36m') + self.assertEqual(Fore.WHITE, '\033[37m') + self.assertEqual(Fore.RESET, '\033[39m') + + # Check the light, extended versions. + self.assertEqual(Fore.LIGHTBLACK_EX, '\033[90m') + self.assertEqual(Fore.LIGHTRED_EX, '\033[91m') + self.assertEqual(Fore.LIGHTGREEN_EX, '\033[92m') + self.assertEqual(Fore.LIGHTYELLOW_EX, '\033[93m') + self.assertEqual(Fore.LIGHTBLUE_EX, '\033[94m') + self.assertEqual(Fore.LIGHTMAGENTA_EX, '\033[95m') + self.assertEqual(Fore.LIGHTCYAN_EX, '\033[96m') + self.assertEqual(Fore.LIGHTWHITE_EX, '\033[97m') + + + def testBackAttributes(self): + self.assertEqual(Back.BLACK, '\033[40m') + self.assertEqual(Back.RED, '\033[41m') + self.assertEqual(Back.GREEN, '\033[42m') + self.assertEqual(Back.YELLOW, '\033[43m') + self.assertEqual(Back.BLUE, '\033[44m') + self.assertEqual(Back.MAGENTA, '\033[45m') + self.assertEqual(Back.CYAN, '\033[46m') + self.assertEqual(Back.WHITE, '\033[47m') + self.assertEqual(Back.RESET, '\033[49m') + + # Check the light, extended versions. + self.assertEqual(Back.LIGHTBLACK_EX, '\033[100m') + self.assertEqual(Back.LIGHTRED_EX, '\033[101m') + self.assertEqual(Back.LIGHTGREEN_EX, '\033[102m') + self.assertEqual(Back.LIGHTYELLOW_EX, '\033[103m') + self.assertEqual(Back.LIGHTBLUE_EX, '\033[104m') + self.assertEqual(Back.LIGHTMAGENTA_EX, '\033[105m') + self.assertEqual(Back.LIGHTCYAN_EX, '\033[106m') + self.assertEqual(Back.LIGHTWHITE_EX, '\033[107m') + + + def testStyleAttributes(self): + self.assertEqual(Style.DIM, '\033[2m') + self.assertEqual(Style.NORMAL, '\033[22m') + self.assertEqual(Style.BRIGHT, '\033[1m') + + +if __name__ == '__main__': + main() diff --git a/.venv/lib/python3.9/site-packages/colorama/tests/ansitowin32_test.py b/.venv/lib/python3.9/site-packages/colorama/tests/ansitowin32_test.py new file mode 100644 index 00000000..91ca551f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama/tests/ansitowin32_test.py @@ -0,0 +1,294 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from io import StringIO, TextIOWrapper +from unittest import TestCase, main +try: + from contextlib import ExitStack +except ImportError: + # python 2 + from contextlib2 import ExitStack + +try: + from unittest.mock import MagicMock, Mock, patch +except ImportError: + from mock import MagicMock, Mock, patch + +from ..ansitowin32 import AnsiToWin32, StreamWrapper +from ..win32 import ENABLE_VIRTUAL_TERMINAL_PROCESSING +from .utils import osname + + +class StreamWrapperTest(TestCase): + + def testIsAProxy(self): + mockStream = Mock() + wrapper = StreamWrapper(mockStream, None) + self.assertTrue( wrapper.random_attr is mockStream.random_attr ) + + def testDelegatesWrite(self): + mockStream = Mock() + mockConverter = Mock() + wrapper = StreamWrapper(mockStream, mockConverter) + wrapper.write('hello') + self.assertTrue(mockConverter.write.call_args, (('hello',), {})) + + def testDelegatesContext(self): + mockConverter = Mock() + s = StringIO() + with StreamWrapper(s, mockConverter) as fp: + fp.write(u'hello') + self.assertTrue(s.closed) + + def testProxyNoContextManager(self): + mockStream = MagicMock() + mockStream.__enter__.side_effect = AttributeError() + mockConverter = Mock() + with self.assertRaises(AttributeError) as excinfo: + with StreamWrapper(mockStream, mockConverter) as wrapper: + wrapper.write('hello') + + def test_closed_shouldnt_raise_on_closed_stream(self): + stream = StringIO() + stream.close() + wrapper = StreamWrapper(stream, None) + self.assertEqual(wrapper.closed, True) + + def test_closed_shouldnt_raise_on_detached_stream(self): + stream = TextIOWrapper(StringIO()) + stream.detach() + wrapper = StreamWrapper(stream, None) + self.assertEqual(wrapper.closed, True) + +class AnsiToWin32Test(TestCase): + + def testInit(self): + mockStdout = Mock() + auto = Mock() + stream = AnsiToWin32(mockStdout, autoreset=auto) + self.assertEqual(stream.wrapped, mockStdout) + self.assertEqual(stream.autoreset, auto) + + @patch('colorama.ansitowin32.winterm', None) + @patch('colorama.ansitowin32.winapi_test', lambda *_: True) + def testStripIsTrueOnWindows(self): + with osname('nt'): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + self.assertTrue(stream.strip) + + def testStripIsFalseOffWindows(self): + with osname('posix'): + mockStdout = Mock(closed=False) + stream = AnsiToWin32(mockStdout) + self.assertFalse(stream.strip) + + def testWriteStripsAnsi(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + stream.wrapped = Mock() + stream.write_and_convert = Mock() + stream.strip = True + + stream.write('abc') + + self.assertFalse(stream.wrapped.write.called) + self.assertEqual(stream.write_and_convert.call_args, (('abc',), {})) + + def testWriteDoesNotStripAnsi(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + stream.wrapped = Mock() + stream.write_and_convert = Mock() + stream.strip = False + stream.convert = False + + stream.write('abc') + + self.assertFalse(stream.write_and_convert.called) + self.assertEqual(stream.wrapped.write.call_args, (('abc',), {})) + + def assert_autoresets(self, convert, autoreset=True): + stream = AnsiToWin32(Mock()) + stream.convert = convert + stream.reset_all = Mock() + stream.autoreset = autoreset + stream.winterm = Mock() + + stream.write('abc') + + self.assertEqual(stream.reset_all.called, autoreset) + + def testWriteAutoresets(self): + self.assert_autoresets(convert=True) + self.assert_autoresets(convert=False) + self.assert_autoresets(convert=True, autoreset=False) + self.assert_autoresets(convert=False, autoreset=False) + + def testWriteAndConvertWritesPlainText(self): + stream = AnsiToWin32(Mock()) + stream.write_and_convert( 'abc' ) + self.assertEqual( stream.wrapped.write.call_args, (('abc',), {}) ) + + def testWriteAndConvertStripsAllValidAnsi(self): + stream = AnsiToWin32(Mock()) + stream.call_win32 = Mock() + data = [ + 'abc\033[mdef', + 'abc\033[0mdef', + 'abc\033[2mdef', + 'abc\033[02mdef', + 'abc\033[002mdef', + 'abc\033[40mdef', + 'abc\033[040mdef', + 'abc\033[0;1mdef', + 'abc\033[40;50mdef', + 'abc\033[50;30;40mdef', + 'abc\033[Adef', + 'abc\033[0Gdef', + 'abc\033[1;20;128Hdef', + ] + for datum in data: + stream.wrapped.write.reset_mock() + stream.write_and_convert( datum ) + self.assertEqual( + [args[0] for args in stream.wrapped.write.call_args_list], + [ ('abc',), ('def',) ] + ) + + def testWriteAndConvertSkipsEmptySnippets(self): + stream = AnsiToWin32(Mock()) + stream.call_win32 = Mock() + stream.write_and_convert( '\033[40m\033[41m' ) + self.assertFalse( stream.wrapped.write.called ) + + def testWriteAndConvertCallsWin32WithParamsAndCommand(self): + stream = AnsiToWin32(Mock()) + stream.convert = True + stream.call_win32 = Mock() + stream.extract_params = Mock(return_value='params') + data = { + 'abc\033[adef': ('a', 'params'), + 'abc\033[;;bdef': ('b', 'params'), + 'abc\033[0cdef': ('c', 'params'), + 'abc\033[;;0;;Gdef': ('G', 'params'), + 'abc\033[1;20;128Hdef': ('H', 'params'), + } + for datum, expected in data.items(): + stream.call_win32.reset_mock() + stream.write_and_convert( datum ) + self.assertEqual( stream.call_win32.call_args[0], expected ) + + def test_reset_all_shouldnt_raise_on_closed_orig_stdout(self): + stream = StringIO() + converter = AnsiToWin32(stream) + stream.close() + + converter.reset_all() + + def test_wrap_shouldnt_raise_on_closed_orig_stdout(self): + stream = StringIO() + stream.close() + with \ + patch("colorama.ansitowin32.os.name", "nt"), \ + patch("colorama.ansitowin32.winapi_test", lambda: True): + converter = AnsiToWin32(stream) + self.assertTrue(converter.strip) + self.assertFalse(converter.convert) + + def test_wrap_shouldnt_raise_on_missing_closed_attr(self): + with \ + patch("colorama.ansitowin32.os.name", "nt"), \ + patch("colorama.ansitowin32.winapi_test", lambda: True): + converter = AnsiToWin32(object()) + self.assertTrue(converter.strip) + self.assertFalse(converter.convert) + + def testExtractParams(self): + stream = AnsiToWin32(Mock()) + data = { + '': (0,), + ';;': (0,), + '2': (2,), + ';;002;;': (2,), + '0;1': (0, 1), + ';;003;;456;;': (3, 456), + '11;22;33;44;55': (11, 22, 33, 44, 55), + } + for datum, expected in data.items(): + self.assertEqual(stream.extract_params('m', datum), expected) + + def testCallWin32UsesLookup(self): + listener = Mock() + stream = AnsiToWin32(listener) + stream.win32_calls = { + 1: (lambda *_, **__: listener(11),), + 2: (lambda *_, **__: listener(22),), + 3: (lambda *_, **__: listener(33),), + } + stream.call_win32('m', (3, 1, 99, 2)) + self.assertEqual( + [a[0][0] for a in listener.call_args_list], + [33, 11, 22] ) + + def test_osc_codes(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout, convert=True) + with patch('colorama.ansitowin32.winterm') as winterm: + data = [ + '\033]0\x07', # missing arguments + '\033]0;foo\x08', # wrong OSC command + '\033]0;colorama_test_title\x07', # should work + '\033]1;colorama_test_title\x07', # wrong set command + '\033]2;colorama_test_title\x07', # should work + '\033]' + ';' * 64 + '\x08', # see issue #247 + ] + for code in data: + stream.write(code) + self.assertEqual(winterm.set_title.call_count, 2) + + def test_native_windows_ansi(self): + with ExitStack() as stack: + def p(a, b): + stack.enter_context(patch(a, b, create=True)) + # Pretend to be on Windows + p("colorama.ansitowin32.os.name", "nt") + p("colorama.ansitowin32.winapi_test", lambda: True) + p("colorama.win32.winapi_test", lambda: True) + p("colorama.winterm.win32.windll", "non-None") + p("colorama.winterm.get_osfhandle", lambda _: 1234) + + # Pretend that our mock stream has native ANSI support + p( + "colorama.winterm.win32.GetConsoleMode", + lambda _: ENABLE_VIRTUAL_TERMINAL_PROCESSING, + ) + SetConsoleMode = Mock() + p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) + + stdout = Mock() + stdout.closed = False + stdout.isatty.return_value = True + stdout.fileno.return_value = 1 + + # Our fake console says it has native vt support, so AnsiToWin32 should + # enable that support and do nothing else. + stream = AnsiToWin32(stdout) + SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) + self.assertFalse(stream.strip) + self.assertFalse(stream.convert) + self.assertFalse(stream.should_wrap()) + + # Now let's pretend we're on an old Windows console, that doesn't have + # native ANSI support. + p("colorama.winterm.win32.GetConsoleMode", lambda _: 0) + SetConsoleMode = Mock() + p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) + + stream = AnsiToWin32(stdout) + SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) + self.assertTrue(stream.strip) + self.assertTrue(stream.convert) + self.assertTrue(stream.should_wrap()) + + +if __name__ == '__main__': + main() diff --git a/.venv/lib/python3.9/site-packages/colorama/tests/initialise_test.py b/.venv/lib/python3.9/site-packages/colorama/tests/initialise_test.py new file mode 100644 index 00000000..89f9b075 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama/tests/initialise_test.py @@ -0,0 +1,189 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main, skipUnless + +try: + from unittest.mock import patch, Mock +except ImportError: + from mock import patch, Mock + +from ..ansitowin32 import StreamWrapper +from ..initialise import init, just_fix_windows_console, _wipe_internal_state_for_tests +from .utils import osname, replace_by + +orig_stdout = sys.stdout +orig_stderr = sys.stderr + + +class InitTest(TestCase): + + @skipUnless(sys.stdout.isatty(), "sys.stdout is not a tty") + def setUp(self): + # sanity check + self.assertNotWrapped() + + def tearDown(self): + _wipe_internal_state_for_tests() + sys.stdout = orig_stdout + sys.stderr = orig_stderr + + def assertWrapped(self): + self.assertIsNot(sys.stdout, orig_stdout, 'stdout should be wrapped') + self.assertIsNot(sys.stderr, orig_stderr, 'stderr should be wrapped') + self.assertTrue(isinstance(sys.stdout, StreamWrapper), + 'bad stdout wrapper') + self.assertTrue(isinstance(sys.stderr, StreamWrapper), + 'bad stderr wrapper') + + def assertNotWrapped(self): + self.assertIs(sys.stdout, orig_stdout, 'stdout should not be wrapped') + self.assertIs(sys.stderr, orig_stderr, 'stderr should not be wrapped') + + @patch('colorama.initialise.reset_all') + @patch('colorama.ansitowin32.winapi_test', lambda *_: True) + @patch('colorama.ansitowin32.enable_vt_processing', lambda *_: False) + def testInitWrapsOnWindows(self, _): + with osname("nt"): + init() + self.assertWrapped() + + @patch('colorama.initialise.reset_all') + @patch('colorama.ansitowin32.winapi_test', lambda *_: False) + def testInitDoesntWrapOnEmulatedWindows(self, _): + with osname("nt"): + init() + self.assertNotWrapped() + + def testInitDoesntWrapOnNonWindows(self): + with osname("posix"): + init() + self.assertNotWrapped() + + def testInitDoesntWrapIfNone(self): + with replace_by(None): + init() + # We can't use assertNotWrapped here because replace_by(None) + # changes stdout/stderr already. + self.assertIsNone(sys.stdout) + self.assertIsNone(sys.stderr) + + def testInitAutoresetOnWrapsOnAllPlatforms(self): + with osname("posix"): + init(autoreset=True) + self.assertWrapped() + + def testInitWrapOffDoesntWrapOnWindows(self): + with osname("nt"): + init(wrap=False) + self.assertNotWrapped() + + def testInitWrapOffIncompatibleWithAutoresetOn(self): + self.assertRaises(ValueError, lambda: init(autoreset=True, wrap=False)) + + @patch('colorama.win32.SetConsoleTextAttribute') + @patch('colorama.initialise.AnsiToWin32') + def testAutoResetPassedOn(self, mockATW32, _): + with osname("nt"): + init(autoreset=True) + self.assertEqual(len(mockATW32.call_args_list), 2) + self.assertEqual(mockATW32.call_args_list[1][1]['autoreset'], True) + self.assertEqual(mockATW32.call_args_list[0][1]['autoreset'], True) + + @patch('colorama.initialise.AnsiToWin32') + def testAutoResetChangeable(self, mockATW32): + with osname("nt"): + init() + + init(autoreset=True) + self.assertEqual(len(mockATW32.call_args_list), 4) + self.assertEqual(mockATW32.call_args_list[2][1]['autoreset'], True) + self.assertEqual(mockATW32.call_args_list[3][1]['autoreset'], True) + + init() + self.assertEqual(len(mockATW32.call_args_list), 6) + self.assertEqual( + mockATW32.call_args_list[4][1]['autoreset'], False) + self.assertEqual( + mockATW32.call_args_list[5][1]['autoreset'], False) + + + @patch('colorama.initialise.atexit.register') + def testAtexitRegisteredOnlyOnce(self, mockRegister): + init() + self.assertTrue(mockRegister.called) + mockRegister.reset_mock() + init() + self.assertFalse(mockRegister.called) + + +class JustFixWindowsConsoleTest(TestCase): + def _reset(self): + _wipe_internal_state_for_tests() + sys.stdout = orig_stdout + sys.stderr = orig_stderr + + def tearDown(self): + self._reset() + + @patch("colorama.ansitowin32.winapi_test", lambda: True) + def testJustFixWindowsConsole(self): + if sys.platform != "win32": + # just_fix_windows_console should be a no-op + just_fix_windows_console() + self.assertIs(sys.stdout, orig_stdout) + self.assertIs(sys.stderr, orig_stderr) + else: + def fake_std(): + # Emulate stdout=not a tty, stderr=tty + # to check that we handle both cases correctly + stdout = Mock() + stdout.closed = False + stdout.isatty.return_value = False + stdout.fileno.return_value = 1 + sys.stdout = stdout + + stderr = Mock() + stderr.closed = False + stderr.isatty.return_value = True + stderr.fileno.return_value = 2 + sys.stderr = stderr + + for native_ansi in [False, True]: + with patch( + 'colorama.ansitowin32.enable_vt_processing', + lambda *_: native_ansi + ): + self._reset() + fake_std() + + # Regular single-call test + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(sys.stdout, prev_stdout) + if native_ansi: + self.assertIs(sys.stderr, prev_stderr) + else: + self.assertIsNot(sys.stderr, prev_stderr) + + # second call without resetting is always a no-op + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(sys.stdout, prev_stdout) + self.assertIs(sys.stderr, prev_stderr) + + self._reset() + fake_std() + + # If init() runs first, just_fix_windows_console should be a no-op + init() + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(prev_stdout, sys.stdout) + self.assertIs(prev_stderr, sys.stderr) + + +if __name__ == '__main__': + main() diff --git a/.venv/lib/python3.9/site-packages/colorama/tests/isatty_test.py b/.venv/lib/python3.9/site-packages/colorama/tests/isatty_test.py new file mode 100644 index 00000000..0f84e4be --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama/tests/isatty_test.py @@ -0,0 +1,57 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main + +from ..ansitowin32 import StreamWrapper, AnsiToWin32 +from .utils import pycharm, replace_by, replace_original_by, StreamTTY, StreamNonTTY + + +def is_a_tty(stream): + return StreamWrapper(stream, None).isatty() + +class IsattyTest(TestCase): + + def test_TTY(self): + tty = StreamTTY() + self.assertTrue(is_a_tty(tty)) + with pycharm(): + self.assertTrue(is_a_tty(tty)) + + def test_nonTTY(self): + non_tty = StreamNonTTY() + self.assertFalse(is_a_tty(non_tty)) + with pycharm(): + self.assertFalse(is_a_tty(non_tty)) + + def test_withPycharm(self): + with pycharm(): + self.assertTrue(is_a_tty(sys.stderr)) + self.assertTrue(is_a_tty(sys.stdout)) + + def test_withPycharmTTYOverride(self): + tty = StreamTTY() + with pycharm(), replace_by(tty): + self.assertTrue(is_a_tty(tty)) + + def test_withPycharmNonTTYOverride(self): + non_tty = StreamNonTTY() + with pycharm(), replace_by(non_tty): + self.assertFalse(is_a_tty(non_tty)) + + def test_withPycharmNoneOverride(self): + with pycharm(): + with replace_by(None), replace_original_by(None): + self.assertFalse(is_a_tty(None)) + self.assertFalse(is_a_tty(StreamNonTTY())) + self.assertTrue(is_a_tty(StreamTTY())) + + def test_withPycharmStreamWrapped(self): + with pycharm(): + self.assertTrue(AnsiToWin32(StreamTTY()).stream.isatty()) + self.assertFalse(AnsiToWin32(StreamNonTTY()).stream.isatty()) + self.assertTrue(AnsiToWin32(sys.stdout).stream.isatty()) + self.assertTrue(AnsiToWin32(sys.stderr).stream.isatty()) + + +if __name__ == '__main__': + main() diff --git a/.venv/lib/python3.9/site-packages/colorama/tests/utils.py b/.venv/lib/python3.9/site-packages/colorama/tests/utils.py new file mode 100644 index 00000000..472fafb4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama/tests/utils.py @@ -0,0 +1,49 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from contextlib import contextmanager +from io import StringIO +import sys +import os + + +class StreamTTY(StringIO): + def isatty(self): + return True + +class StreamNonTTY(StringIO): + def isatty(self): + return False + +@contextmanager +def osname(name): + orig = os.name + os.name = name + yield + os.name = orig + +@contextmanager +def replace_by(stream): + orig_stdout = sys.stdout + orig_stderr = sys.stderr + sys.stdout = stream + sys.stderr = stream + yield + sys.stdout = orig_stdout + sys.stderr = orig_stderr + +@contextmanager +def replace_original_by(stream): + orig_stdout = sys.__stdout__ + orig_stderr = sys.__stderr__ + sys.__stdout__ = stream + sys.__stderr__ = stream + yield + sys.__stdout__ = orig_stdout + sys.__stderr__ = orig_stderr + +@contextmanager +def pycharm(): + os.environ["PYCHARM_HOSTED"] = "1" + non_tty = StreamNonTTY() + with replace_by(non_tty), replace_original_by(non_tty): + yield + del os.environ["PYCHARM_HOSTED"] diff --git a/.venv/lib/python3.9/site-packages/colorama/tests/winterm_test.py b/.venv/lib/python3.9/site-packages/colorama/tests/winterm_test.py new file mode 100644 index 00000000..d0955f9e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama/tests/winterm_test.py @@ -0,0 +1,131 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main, skipUnless + +try: + from unittest.mock import Mock, patch +except ImportError: + from mock import Mock, patch + +from ..winterm import WinColor, WinStyle, WinTerm + + +class WinTermTest(TestCase): + + @patch('colorama.winterm.win32') + def testInit(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 7 + 6 * 16 + 8 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + self.assertEqual(term._fore, 7) + self.assertEqual(term._back, 6) + self.assertEqual(term._style, 8) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testGetAttrs(self): + term = WinTerm() + + term._fore = 0 + term._back = 0 + term._style = 0 + self.assertEqual(term.get_attrs(), 0) + + term._fore = WinColor.YELLOW + self.assertEqual(term.get_attrs(), WinColor.YELLOW) + + term._back = WinColor.MAGENTA + self.assertEqual( + term.get_attrs(), + WinColor.YELLOW + WinColor.MAGENTA * 16) + + term._style = WinStyle.BRIGHT + self.assertEqual( + term.get_attrs(), + WinColor.YELLOW + WinColor.MAGENTA * 16 + WinStyle.BRIGHT) + + @patch('colorama.winterm.win32') + def testResetAll(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 1 + 2 * 16 + 8 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + + term.set_console = Mock() + term._fore = -1 + term._back = -1 + term._style = -1 + + term.reset_all() + + self.assertEqual(term._fore, 1) + self.assertEqual(term._back, 2) + self.assertEqual(term._style, 8) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testFore(self): + term = WinTerm() + term.set_console = Mock() + term._fore = 0 + + term.fore(5) + + self.assertEqual(term._fore, 5) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testBack(self): + term = WinTerm() + term.set_console = Mock() + term._back = 0 + + term.back(5) + + self.assertEqual(term._back, 5) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testStyle(self): + term = WinTerm() + term.set_console = Mock() + term._style = 0 + + term.style(22) + + self.assertEqual(term._style, 22) + self.assertEqual(term.set_console.called, True) + + @patch('colorama.winterm.win32') + def testSetConsole(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 0 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + term.windll = Mock() + + term.set_console() + + self.assertEqual( + mockWin32.SetConsoleTextAttribute.call_args, + ((mockWin32.STDOUT, term.get_attrs()), {}) + ) + + @patch('colorama.winterm.win32') + def testSetConsoleOnStderr(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 0 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + term.windll = Mock() + + term.set_console(on_stderr=True) + + self.assertEqual( + mockWin32.SetConsoleTextAttribute.call_args, + ((mockWin32.STDERR, term.get_attrs()), {}) + ) + + +if __name__ == '__main__': + main() diff --git a/.venv/lib/python3.9/site-packages/colorama/win32.py b/.venv/lib/python3.9/site-packages/colorama/win32.py new file mode 100644 index 00000000..841b0e27 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama/win32.py @@ -0,0 +1,180 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. + +# from winbase.h +STDOUT = -11 +STDERR = -12 + +ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + +try: + import ctypes + from ctypes import LibraryLoader + windll = LibraryLoader(ctypes.WinDLL) + from ctypes import wintypes +except (AttributeError, ImportError): + windll = None + SetConsoleTextAttribute = lambda *_: None + winapi_test = lambda *_: None +else: + from ctypes import byref, Structure, c_char, POINTER + + COORD = wintypes._COORD + + class CONSOLE_SCREEN_BUFFER_INFO(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("dwSize", COORD), + ("dwCursorPosition", COORD), + ("wAttributes", wintypes.WORD), + ("srWindow", wintypes.SMALL_RECT), + ("dwMaximumWindowSize", COORD), + ] + def __str__(self): + return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( + self.dwSize.Y, self.dwSize.X + , self.dwCursorPosition.Y, self.dwCursorPosition.X + , self.wAttributes + , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right + , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X + ) + + _GetStdHandle = windll.kernel32.GetStdHandle + _GetStdHandle.argtypes = [ + wintypes.DWORD, + ] + _GetStdHandle.restype = wintypes.HANDLE + + _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo + _GetConsoleScreenBufferInfo.argtypes = [ + wintypes.HANDLE, + POINTER(CONSOLE_SCREEN_BUFFER_INFO), + ] + _GetConsoleScreenBufferInfo.restype = wintypes.BOOL + + _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute + _SetConsoleTextAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + ] + _SetConsoleTextAttribute.restype = wintypes.BOOL + + _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition + _SetConsoleCursorPosition.argtypes = [ + wintypes.HANDLE, + COORD, + ] + _SetConsoleCursorPosition.restype = wintypes.BOOL + + _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA + _FillConsoleOutputCharacterA.argtypes = [ + wintypes.HANDLE, + c_char, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputCharacterA.restype = wintypes.BOOL + + _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute + _FillConsoleOutputAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputAttribute.restype = wintypes.BOOL + + _SetConsoleTitleW = windll.kernel32.SetConsoleTitleW + _SetConsoleTitleW.argtypes = [ + wintypes.LPCWSTR + ] + _SetConsoleTitleW.restype = wintypes.BOOL + + _GetConsoleMode = windll.kernel32.GetConsoleMode + _GetConsoleMode.argtypes = [ + wintypes.HANDLE, + POINTER(wintypes.DWORD) + ] + _GetConsoleMode.restype = wintypes.BOOL + + _SetConsoleMode = windll.kernel32.SetConsoleMode + _SetConsoleMode.argtypes = [ + wintypes.HANDLE, + wintypes.DWORD + ] + _SetConsoleMode.restype = wintypes.BOOL + + def _winapi_test(handle): + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return bool(success) + + def winapi_test(): + return any(_winapi_test(h) for h in + (_GetStdHandle(STDOUT), _GetStdHandle(STDERR))) + + def GetConsoleScreenBufferInfo(stream_id=STDOUT): + handle = _GetStdHandle(stream_id) + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return csbi + + def SetConsoleTextAttribute(stream_id, attrs): + handle = _GetStdHandle(stream_id) + return _SetConsoleTextAttribute(handle, attrs) + + def SetConsoleCursorPosition(stream_id, position, adjust=True): + position = COORD(*position) + # If the position is out of range, do nothing. + if position.Y <= 0 or position.X <= 0: + return + # Adjust for Windows' SetConsoleCursorPosition: + # 1. being 0-based, while ANSI is 1-based. + # 2. expecting (x,y), while ANSI uses (y,x). + adjusted_position = COORD(position.Y - 1, position.X - 1) + if adjust: + # Adjust for viewport's scroll position + sr = GetConsoleScreenBufferInfo(STDOUT).srWindow + adjusted_position.Y += sr.Top + adjusted_position.X += sr.Left + # Resume normal processing + handle = _GetStdHandle(stream_id) + return _SetConsoleCursorPosition(handle, adjusted_position) + + def FillConsoleOutputCharacter(stream_id, char, length, start): + handle = _GetStdHandle(stream_id) + char = c_char(char.encode()) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + success = _FillConsoleOutputCharacterA( + handle, char, length, start, byref(num_written)) + return num_written.value + + def FillConsoleOutputAttribute(stream_id, attr, length, start): + ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )''' + handle = _GetStdHandle(stream_id) + attribute = wintypes.WORD(attr) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + return _FillConsoleOutputAttribute( + handle, attribute, length, start, byref(num_written)) + + def SetConsoleTitle(title): + return _SetConsoleTitleW(title) + + def GetConsoleMode(handle): + mode = wintypes.DWORD() + success = _GetConsoleMode(handle, byref(mode)) + if not success: + raise ctypes.WinError() + return mode.value + + def SetConsoleMode(handle, mode): + success = _SetConsoleMode(handle, mode) + if not success: + raise ctypes.WinError() diff --git a/.venv/lib/python3.9/site-packages/colorama/winterm.py b/.venv/lib/python3.9/site-packages/colorama/winterm.py new file mode 100644 index 00000000..aad867e8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/colorama/winterm.py @@ -0,0 +1,195 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +try: + from msvcrt import get_osfhandle +except ImportError: + def get_osfhandle(_): + raise OSError("This isn't windows!") + + +from . import win32 + +# from wincon.h +class WinColor(object): + BLACK = 0 + BLUE = 1 + GREEN = 2 + CYAN = 3 + RED = 4 + MAGENTA = 5 + YELLOW = 6 + GREY = 7 + +# from wincon.h +class WinStyle(object): + NORMAL = 0x00 # dim text, dim background + BRIGHT = 0x08 # bright text, dim background + BRIGHT_BACKGROUND = 0x80 # dim text, bright background + +class WinTerm(object): + + def __init__(self): + self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes + self.set_attrs(self._default) + self._default_fore = self._fore + self._default_back = self._back + self._default_style = self._style + # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style. + # So that LIGHT_EX colors and BRIGHT style do not clobber each other, + # we track them separately, since LIGHT_EX is overwritten by Fore/Back + # and BRIGHT is overwritten by Style codes. + self._light = 0 + + def get_attrs(self): + return self._fore + self._back * 16 + (self._style | self._light) + + def set_attrs(self, value): + self._fore = value & 7 + self._back = (value >> 4) & 7 + self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND) + + def reset_all(self, on_stderr=None): + self.set_attrs(self._default) + self.set_console(attrs=self._default) + self._light = 0 + + def fore(self, fore=None, light=False, on_stderr=False): + if fore is None: + fore = self._default_fore + self._fore = fore + # Emulate LIGHT_EX with BRIGHT Style + if light: + self._light |= WinStyle.BRIGHT + else: + self._light &= ~WinStyle.BRIGHT + self.set_console(on_stderr=on_stderr) + + def back(self, back=None, light=False, on_stderr=False): + if back is None: + back = self._default_back + self._back = back + # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style + if light: + self._light |= WinStyle.BRIGHT_BACKGROUND + else: + self._light &= ~WinStyle.BRIGHT_BACKGROUND + self.set_console(on_stderr=on_stderr) + + def style(self, style=None, on_stderr=False): + if style is None: + style = self._default_style + self._style = style + self.set_console(on_stderr=on_stderr) + + def set_console(self, attrs=None, on_stderr=False): + if attrs is None: + attrs = self.get_attrs() + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleTextAttribute(handle, attrs) + + def get_position(self, handle): + position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition + # Because Windows coordinates are 0-based, + # and win32.SetConsoleCursorPosition expects 1-based. + position.X += 1 + position.Y += 1 + return position + + def set_cursor_position(self, position=None, on_stderr=False): + if position is None: + # I'm not currently tracking the position, so there is no default. + # position = self.get_position() + return + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleCursorPosition(handle, position) + + def cursor_adjust(self, x, y, on_stderr=False): + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + position = self.get_position(handle) + adjusted_position = (position.Y + y, position.X + x) + win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False) + + def erase_screen(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the screen. + # 1 should clear from the cursor to the beginning of the screen. + # 2 should clear the entire screen, and move cursor to (1,1) + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + # get the number of character cells in the current buffer + cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y + # get number of character cells before current cursor position + cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = cells_in_screen - cells_before_cursor + elif mode == 1: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_before_cursor + elif mode == 2: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_in_screen + else: + # invalid mode + return + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + if mode == 2: + # put the cursor where needed + win32.SetConsoleCursorPosition(handle, (1, 1)) + + def erase_line(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the line. + # 1 should clear from the cursor to the beginning of the line. + # 2 should clear the entire line. + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X + elif mode == 1: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwCursorPosition.X + elif mode == 2: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwSize.X + else: + # invalid mode + return + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + + def set_title(self, title): + win32.SetConsoleTitle(title) + + +def enable_vt_processing(fd): + if win32.windll is None or not win32.winapi_test(): + return False + + try: + handle = get_osfhandle(fd) + mode = win32.GetConsoleMode(handle) + win32.SetConsoleMode( + handle, + mode | win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING, + ) + + mode = win32.GetConsoleMode(handle) + if mode & win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING: + return True + # Can get TypeError in testsuite where 'fd' is a Mock() + except (OSError, TypeError): + return False diff --git a/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/METADATA b/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/METADATA new file mode 100644 index 00000000..07f1fcca --- /dev/null +++ b/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/METADATA @@ -0,0 +1,83 @@ +Metadata-Version: 2.4 +Name: csscompressor +Version: 0.9.5 +Summary: A python port of YUI CSS Compressor +Home-page: http://github.com/sprymix/csscompressor +Author: Yury Selivanov +Author-email: info@sprymix.com +License: BSD +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Topic :: Software Development :: Build Tools +Classifier: Topic :: Text Processing :: General +Classifier: Topic :: Utilities +License-File: LICENSE +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: license +Dynamic: license-file +Dynamic: summary + +.. image:: https://travis-ci.org/sprymix/csscompressor.svg?branch=master + :target: https://travis-ci.org/sprymix/csscompressor + + +Almost exact port of YUI CSS Compressor. +Passes all original unittests. + + +Usage +===== + +.. code:: pycon + + >>> from csscompressor import compress + >>> compress(''' + ... your css { + ... content: "!"; + ... } + ... ''') + 'your css{content:"!"}' + +Or, if you want to use it from command line: + +.. code:: + + $ python3 -m csscompressor --help + + +Compatibility +============= + +Tested under Python 2.7 and 3.3+ + + +Installation +============ + +Use ``pip`` or ``easy_install``: + +.. code:: + + $ pip install csscompressor + + +Development +=========== + +Use py.test to run unittests + + +License +======= + +Published under the original Yahoo License for YUI Compressor -- BSD. diff --git a/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/RECORD b/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/RECORD new file mode 100644 index 00000000..cdbbaef3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/RECORD @@ -0,0 +1,22 @@ +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/csscompressor/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/csscompressor/__main__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/csscompressor/tests/__init__.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/csscompressor/tests/base.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/csscompressor/tests/test_compress.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/csscompressor/tests/test_other.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/csscompressor/tests/test_partition.cpython-39.pyc,, +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/csscompressor/tests/test_yui.cpython-39.pyc,, +csscompressor-0.9.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +csscompressor-0.9.5.dist-info/METADATA,sha256=SCEDQ2JwR-1mn-ralOHn8zTJLnSbltUNntxkWVEq_hw,1738 +csscompressor-0.9.5.dist-info/RECORD,, +csscompressor-0.9.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +csscompressor-0.9.5.dist-info/licenses/LICENSE,sha256=r0LlEOk2Uxh4lkWHP5MdiggJURbfkWZsVCZLJXqYreU,1637 +csscompressor-0.9.5.dist-info/top_level.txt,sha256=aNSq_PH2kSkvx1fa1bT6uRA91O_uhoxdbx-Klm3ROBo,14 +csscompressor/__init__.py,sha256=yiwhMVwnWMYkC8-eXpF9aJtmD0GKfE4m-TlZHyoKkbc,20963 +csscompressor/__main__.py,sha256=S41OFk9gMTSIVxpoNaz8bk4mxtnn7tBGC8xJqInJdP4,1241 +csscompressor/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +csscompressor/tests/base.py,sha256=c9_H2po8qc-ctVkSGcj_1ucIgVSDUQalGg1vcAm17UA,646 +csscompressor/tests/test_compress.py,sha256=TBSnj_4aIjBF7Dasl303qIw9IucPNNboseFGRao58UY,1759 +csscompressor/tests/test_other.py,sha256=M0G4xWx_9WxVx3Vixqzi_E07sRm4fE41yy7m3lKcYl4,1686 +csscompressor/tests/test_partition.py,sha256=x4v7pC8j_Yitd3VzompnyrQnscNsTQGCwnLR_J13ZB0,3576 +csscompressor/tests/test_yui.py,sha256=Uy_vRkreqlM2CpOxd26Kxo9hZ1uOornakitEejfO16U,591334 diff --git a/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/WHEEL new file mode 100644 index 00000000..e7fa31b6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/licenses/LICENSE b/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/licenses/LICENSE new file mode 100644 index 00000000..92b0833b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/licenses/LICENSE @@ -0,0 +1,36 @@ +Portions Copyright (c) 2013 Sprymix Inc. +Portions Copyright (c) 2011-2013, Yahoo! Inc. + +Original YUI-Compressor LICENSE +=============================== + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of Yahoo! Inc. nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of Yahoo! Inc. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/top_level.txt new file mode 100644 index 00000000..82210c59 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/csscompressor-0.9.5.dist-info/top_level.txt @@ -0,0 +1 @@ +csscompressor diff --git a/.venv/lib/python3.9/site-packages/csscompressor/__init__.py b/.venv/lib/python3.9/site-packages/csscompressor/__init__.py new file mode 100644 index 00000000..e34af04a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/csscompressor/__init__.py @@ -0,0 +1,576 @@ +## +# Portions Copyright (c) 2013 Sprymix Inc. +# Author of python port: Yury Selivanov - http://sprymix.com +# +# Author: Julien Lecomte - http://www.julienlecomte.net/ +# Author: Isaac Schlueter - http://foohack.com/ +# Author: Stoyan Stefanov - http://phpied.com/ +# Contributor: Dan Beam - http://danbeam.org/ +# Contributor: w.Tayyeb - http://tayyeb.info/ +# Portions Copyright (c) 2011-2013 Yahoo! Inc. All rights reserved. +# LICENSE: BSD (revised) +## + + +__all__ = ('compress', 'compress_partitioned') +__version__ = '0.9.4' + + +import re + + +_url_re = re.compile(r'''(url)\s*\(\s*(['"]?)data\:''', re.I) +_calc_re = re.compile(r'(calc)\s*\(', re.I) +_hsl_re = re.compile(r'(hsl|hsla)\s*\(', re.I) +_ws_re = re.compile(r'\s+') +_str_re = re.compile(r'''("([^\\"]|\\.|\\)*")|('([^\\']|\\.|\\)*')''') +_yui_comment_re = re.compile(r'___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_(?P\d+)___') +_ms_filter_re = re.compile(r'progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity=', re.I) +_spaces1_re = re.compile(r'(^|\})(([^\{:])+:)+([^\{]*\{)') +_spaces2_re = re.compile(r'\s+([!{};:>+\(\)\],])') +_ie6special_re = re.compile(r':first-(line|letter)(\{|,)', re.I) +_charset1_re = re.compile(r'^(.*)(@charset)\s+("[^"]*";)', re.I) +_charset2_re = re.compile(r'^((\s*)(@charset)\s+([^;]+;\s*))+', re.I) +_dirs_re = re.compile(r'''@(font-face|import| + (?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)? + keyframe|media|page|namespace)''', + re.I | re.X) + +_pseudo_re = re.compile(r''':(active|after|before|checked|disabled|empty|enabled| + first-(?:child|of-type)|focus|hover|last-(?:child|of-type)| + link|only-(?:child|of-type)|root|:selection|target|visited)''', + re.I | re.X) + +_common_funcs_re = re.compile(r''':(lang|not|nth-child|nth-last-child|nth-last-of-type| + nth-of-type|(?:-(?:moz|webkit)-)?any)\(''', re.I | re.X) + +_common_val_funcs_re = re.compile(r'''([:,\(\s]\s*)(attr|color-stop|from|rgba|to|url| + (?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)? + (?:calc|max|min|(?:repeating-)? + (?:linear|radial)-gradient)|-webkit-gradient)''', + re.I | re.X) + +_space_and_re = re.compile(r'\band\(', re.I) + +_space_after_re = re.compile(r'([!{}:;>+\(\[,])\s+') + +_semi_re = re.compile(r';+}') + +_zero_fmt_spec_re = re.compile(r'''(\s|:|\(|,)(?:0?\.)?0 + (?:px|em|%|in|cm|mm|pc|pt|ex|deg|g?rad|k?hz)''', + re.I | re.X) + +_zero_req_unit_re = re.compile(r'''(\s|:|\(|,)(?:0?\.)?0 + (m?s)''', re.I | re.X) + +_bg_pos_re = re.compile(r'''(background-position|webkit-mask-position|transform-origin| + webkit-transform-origin|moz-transform-origin|o-transform-origin| + ms-transform-origin):0(;|})''', re.I | re.X) + +_quad_0_re = re.compile(r':0 0 0 0(;|})') +_trip_0_re = re.compile(r':0 0 0(;|})') +_coup_0_re = re.compile(r':0 0(;|})') + +_point_float_re = re.compile(r'(:|\s)0+\.(\d+)') +_point_float_neg_re = re.compile(r'(:|\s)-0+\.(\d+)') +_point_float_pos_re = re.compile(r'(:|\s)\+(\d+)+\.(\d+)') + +_border_re = re.compile(r'''(border|border-top|border-right|border-bottom| + border-left|outline|background):none(;|})''', re.I | re.X) + +_o_px_ratio_re = re.compile(r'\(([\-A-Za-z]+):([0-9]+)\/([0-9]+)\)') + +_empty_rules_re = re.compile(r'[^\}\{/;]+\{\}') + +_many_semi_re = re.compile(';;+') + +_rgb_re = re.compile(r'rgb\s*\(\s*([0-9,\s]+)\s*\)') + +_hex_color_re = re.compile(r'''(\=\s*?["']?)? + \#([0-9a-fA-F])([0-9a-fA-F]) + ([0-9a-fA-F])([0-9a-fA-F]) + ([0-9a-fA-F])([0-9a-fA-F]) + (\}|[^0-9a-fA-F{][^{]*?\})''', re.X) + +_ie_matrix_re = re.compile(r'\s*filter:\s*progid:DXImageTransform\.Microsoft\.Matrix\(([^\)]+)\);') + +_colors_map = { + 'f00': 'red', + '000080': 'navy', + '808080': 'gray', + '808000': 'olive', + '800080': 'purple', + 'c0c0c0': 'silver', + '008080': 'teal', + 'ffa500': 'orange', + '800000': 'maroon' +} + +_colors_re = re.compile(r'(:|\s)' + '(\\#(' + '|'.join(_colors_map.keys()) + '))' + r'(;|})', re.I) + + +def _preserve_call_tokens(css, regexp, preserved_tokens, remove_ws=False): + max_idx = len(css) - 1 + append_idx = 0 + sb = [] + nest_term = None + + for match in regexp.finditer(css): + name = match.group(1) + start_idx = match.start(0) + len(name) + 1 # "len" of "url(" + + term = match.group(2) if match.lastindex > 1 else None + if not term: + term = ')' + nest_term = '(' + + found_term = False + end_idx = match.end(0) - 1 + nest_idx = end_idx if nest_term else 0 + nested = False + while not found_term and (end_idx + 1) <= max_idx: + if nest_term: + nest_idx = css.find(nest_term, nest_idx + 1) + end_idx = css.find(term, end_idx + 1) + + if end_idx > 0: + if nest_idx > 0 and nest_idx < end_idx and \ + css[nest_idx - 1] != '\\': + nested = True + + if css[end_idx - 1] != '\\': + if nested: + nested = False + else: + found_term = True + if term != ')': + end_idx = css.find(')', end_idx) + else: + raise ValueError('malformed css') + + sb.append(css[append_idx:match.start(0)]) + + assert found_term + + token = css[start_idx:end_idx].strip() + + if remove_ws: + token = _ws_re.sub('', token) + + preserver = ('{0}(___YUICSSMIN_PRESERVED_TOKEN_{1}___)' + .format(name, len(preserved_tokens))) + + preserved_tokens.append(token) + sb.append(preserver) + + append_idx = end_idx + 1 + + sb.append(css[append_idx:]) + + return ''.join(sb) + + +def _compress_rgb_calls(css): + # Shorten colors from rgb(51,102,153) to #336699 + # This makes it more likely that it'll get further compressed in the next step. + def _replace(match): + rgb_colors = match.group(1).split(',') + result = '#' + for comp in rgb_colors: + comp = int(comp) + if comp < 16: + result += '0' + if comp > 255: + comp = 255 + result += hex(comp)[2:].lower() + return result + return _rgb_re.sub(_replace, css) + + +def _compress_hex_colors(css): + # Shorten colors from #AABBCC to #ABC. Note that we want to make sure + # the color is not preceded by either ", " or =. Indeed, the property + # filter: chroma(color="#FFFFFF"); + # would become + # filter: chroma(color="#FFF"); + # which makes the filter break in IE. + # We also want to make sure we're only compressing #AABBCC patterns inside { }, + # not id selectors ( #FAABAC {} ) + # We also want to avoid compressing invalid values (e.g. #AABBCCD to #ABCD) + + buf = [] + + index = 0 + while True: + match = _hex_color_re.search(css, index) + if not match: + break + + buf.append(css[index:match.start(0)]) + + + if match.group(1): + # Restore, as is. Compression will break filters + buf.append(match.group(1) + ('#' + match.group(2) + match.group(3) + match.group(4) + + match.group(5) + match.group(6) + match.group(7))) + + else: + if (match.group(2).lower() == match.group(3).lower() and + match.group(4).lower() == match.group(5).lower() and + match.group(6).lower() == match.group(7).lower()): + + buf.append('#' + (match.group(2) + match.group(4) + match.group(6)).lower()) + + else: + buf.append('#' + (match.group(2) + match.group(3) + match.group(4) + + match.group(5) + match.group(6) + match.group(7)).lower()) + + index = match.end(7) + + buf.append(css[index:]) + + return ''.join(buf) + + +def _compress(css, max_linelen=0, preserve_exclamation_comments=True): + start_idx = end_idx = 0 + total_len = len(css) + + preserved_tokens = [] + css = _preserve_call_tokens(css, _url_re, preserved_tokens, remove_ws=True) + css = _preserve_call_tokens(css, _calc_re, preserved_tokens, remove_ws=False) + css = _preserve_call_tokens(css, _hsl_re, preserved_tokens, remove_ws=True) + + # Collect all comments blocks... + comments = [] + while True: + start_idx = css.find('/*', start_idx) + if start_idx < 0: + break + + suffix = '' + end_idx = css.find('*/', start_idx + 2) + if end_idx < 0: + end_idx = total_len + suffix = '*/' + + token = css[start_idx + 2:end_idx] + comments.append(token) + + css = (css[:start_idx + 2] + + '___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_{0}___'.format(len(comments)-1) + + css[end_idx:] + suffix) + + start_idx += 2 + + # preserve strings so their content doesn't get accidentally minified + def _replace(match): + token = match.group(0) + quote = token[0] + token = token[1:-1] + + + # maybe the string contains a comment-like substring? + # one, maybe more? put'em back then + if '___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_' in token: + token = _yui_comment_re.sub(lambda match: comments[int(match.group('num'))], token) + + token = _ms_filter_re.sub('alpha(opacity=', token) + + preserved_tokens.append(token) + return (quote + + '___YUICSSMIN_PRESERVED_TOKEN_{0}___'.format(len(preserved_tokens)-1) + + quote) + + css = _str_re.sub(_replace, css) + + # strings are safe, now wrestle the comments + comments_iter = iter(comments) + for i, token in enumerate(comments_iter): + placeholder = "___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_{0}___".format(i) + + # ! in the first position of the comment means preserve + # so push to the preserved tokens while stripping the ! + if preserve_exclamation_comments and token.startswith('!'): + preserved_tokens.append(token) + css = css.replace(placeholder, '___YUICSSMIN_PRESERVED_TOKEN_{0}___'. + format(len(preserved_tokens)-1)) + continue + + # \ in the last position looks like hack for Mac/IE5 + # shorten that to /*\*/ and the next one to /**/ + if token.endswith('\\'): + preserved_tokens.append('\\') + css = css.replace(placeholder, + '___YUICSSMIN_PRESERVED_TOKEN_{0}___'.format(len(preserved_tokens)-1)) + + # attn: advancing the loop + next(comments_iter) + + preserved_tokens.append('') + css = css.replace('___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_{0}___'.format(i+1), + '___YUICSSMIN_PRESERVED_TOKEN_{0}___'.format(len(preserved_tokens)-1)) + + continue + + # keep empty comments after child selectors (IE7 hack) + # e.g. html >/**/ body + if not token: + start_idx = css.find(placeholder) + if start_idx > 2: + if css[start_idx-3] == '>': + preserved_tokens.append('') + css = css.replace(placeholder, + '___YUICSSMIN_PRESERVED_TOKEN_{0}___'. + format(len(preserved_tokens)-1)) + + # in all other cases kill the comment + css = css.replace('/*{0}*/'.format(placeholder), '') + + # Normalize all whitespace strings to single spaces. Easier to work with that way. + css = _ws_re.sub(' ', css) + + def _replace(match): + token = match.group(1) + preserved_tokens.append(token); + return ('filter:progid:DXImageTransform.Microsoft.Matrix(' + + '___YUICSSMIN_PRESERVED_TOKEN_{0}___);'.format(len(preserved_tokens)-1)) + css = _ie_matrix_re.sub(_replace, css) + + # remove + sign where it is before a float +0.1 +2.34 + css = _point_float_pos_re.sub(r'\1\2.\3', css) + + # Remove the spaces before the things that should not have spaces before them. + # But, be careful not to turn "p :link {...}" into "p:link{...}" + # Swap out any pseudo-class colons with the token, and then swap back. + css = _spaces1_re.sub(lambda match: match.group(0) \ + .replace(':', '___YUICSSMIN_PSEUDOCLASSCOLON___'), css) + + css = _spaces2_re.sub(lambda match: match.group(1), css) + + # Restore spaces for !important + css = css.replace('!important', ' !important'); + + # bring back the colon + css = css.replace('___YUICSSMIN_PSEUDOCLASSCOLON___', ':') + + # retain space for special IE6 cases + css = _ie6special_re.sub( + lambda match: ':first-{0} {1}'.format(match.group(1).lower(), match.group(2)), + css) + + # no space after the end of a preserved comment + css = css.replace('*/ ', '*/') + + # If there are multiple @charset directives, push them to the top of the file. + css = _charset1_re.sub(lambda match: match.group(2).lower() + \ + ' ' + match.group(3) + match.group(1), + css) + + # When all @charset are at the top, remove the second and after (as they are completely ignored) + css = _charset2_re.sub(lambda match: match.group(2) + \ + match.group(3).lower() + ' ' + match.group(4), + css) + + # lowercase some popular @directives (@charset is done right above) + css = _dirs_re.sub(lambda match: '@' + match.group(1).lower(), css) + + # lowercase some more common pseudo-elements + css = _pseudo_re.sub(lambda match: ':' + match.group(1).lower(), css) + + # lowercase some more common functions + css = _common_funcs_re.sub(lambda match: ':' + match.group(1).lower() + '(', css) + + # lower case some common function that can be values + # NOTE: rgb() isn't useful as we replace with #hex later, as well as and() + # is already done for us right after this + css = _common_val_funcs_re.sub(lambda match: match.group(1) + match.group(2).lower(), css) + + # Put the space back in some cases, to support stuff like + # @media screen and (-webkit-min-device-pixel-ratio:0){ + css = _space_and_re.sub('and (', css) + + # Remove the spaces after the things that should not have spaces after them. + css = _space_after_re.sub(r'\1', css) + + # remove unnecessary semicolons + css = _semi_re.sub('}', css) + + # Replace 0(px,em,%) with 0. + css = _zero_fmt_spec_re.sub(lambda match: match.group(1) + '0', css) + + # Replace 0.0(m,ms) or .0(m,ms) with 0(m,ms) + css = _zero_req_unit_re.sub(lambda match: match.group(1) + '0' + match.group(2), css) + + # Replace 0 0 0 0; with 0. + css = _quad_0_re.sub(r':0\1', css) + css = _trip_0_re.sub(r':0\1', css) + css = _coup_0_re.sub(r':0\1', css) + + # Replace background-position:0; with background-position:0 0; + # same for transform-origin + css = _bg_pos_re.sub(lambda match: match.group(1).lower() + ':0 0' + match.group(2), css) + + # Replace 0.6 to .6, but only when preceded by : or a white-space + css = _point_float_re.sub(r'\1.\2', css) + # Replace -0.6 to -.6, but only when preceded by : or a white-space + css = _point_float_neg_re.sub(r'\1-.\2', css) + + css = _compress_rgb_calls(css) + css = _compress_hex_colors(css) + + # Replace #f00 -> red; other short color keywords + css = _colors_re.sub(lambda match: match.group(1) + _colors_map[match.group(3).lower()] + + match.group(4), + css) + + # border: none -> border:0 + css = _border_re.sub(lambda match: match.group(1).lower() + ':0' + match.group(2), css) + + # shorter opacity IE filter + css = _ms_filter_re.sub('alpha(opacity=', css) + + # Find a fraction that is used for Opera's -o-device-pixel-ratio query + # Add token to add the "\" back in later + css = _o_px_ratio_re.sub(r'\1:\2___YUI_QUERY_FRACTION___\3', css) + + # Remove empty rules. + css = _empty_rules_re.sub('', css) + + # Add "\" back to fix Opera -o-device-pixel-ratio query + css = css.replace('___YUI_QUERY_FRACTION___', '/') + + if max_linelen and len(css) > max_linelen: + buf = [] + start_pos = 0 + while True: + buf.append(css[start_pos:start_pos + max_linelen]) + start_pos += max_linelen + while start_pos < len(css): + if css[start_pos] == '}': + buf.append('}\n') + start_pos += 1 + break + else: + buf.append(css[start_pos]) + start_pos += 1 + if start_pos >= len(css): + break + css = ''.join(buf) + + # Replace multiple semi-colons in a row by a single one + # See SF bug #1980989 + css = _many_semi_re.sub(';', css) + + return css, preserved_tokens + + +def _apply_preserved(css, preserved_tokens): + # restore preserved comments and strings + for i, token in reversed(tuple(enumerate(preserved_tokens))): + css = css.replace('___YUICSSMIN_PRESERVED_TOKEN_{0}___'.format(i), token) + + css = css.strip() + return css + + +def compress(css, max_linelen=0, preserve_exclamation_comments=True): + """Compress given CSS stylesheet. + + Parameters: + + - css : str + An str with CSS rules. + + - max_linelen : int = 0 + Some source control tools don't like it when files containing lines longer + than, say 8000 characters, are checked in. This option is used in + that case to split long lines after a specific column. + + - preserve_exclamation_comments : boolean = True + Some stylesheets contain /*! ... */ comment block which used for copyright + notices or else. By default compress dont remove them like other comment + blocks. It will lead to bigger file size. but once you decide to remove + them just set this parameter to False. + + Returns a ``str`` object with compressed CSS. + """ + + css, preserved_tokens = _compress(css, max_linelen=max_linelen, preserve_exclamation_comments=preserve_exclamation_comments) + css = _apply_preserved(css, preserved_tokens) + return css + + +def compress_partitioned(css, + max_linelen=0, + max_rules_per_file=4000, + preserve_exclamation_comments=True): + """Compress given CSS stylesheet into a set of files. + + Parameters: + + - max_linelen : int = 0 + Has the same meaning as for "compress()" function. + + - max_rules_per_file : int = 0 + Internet Explorers <= 9 have an artificial max number of rules per CSS + file (4096; http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/10164546.aspx) + When ``max_rules_per_file`` is a positive number, the function *always* returns + a list of ``str`` objects, each limited to contain less than the passed number + of rules. + + - preserve_exclamation_comments : boolean = True + Has the same meaning as for "compress()" function. + + + Always returns a ``list`` of ``str`` objects with compressed CSS. + """ + + assert max_rules_per_file > 0 + + css, preserved_tokens = _compress(css, max_linelen=max_linelen, preserve_exclamation_comments=preserve_exclamation_comments) + css = css.strip() + + bufs = [] + buf = [] + rules = 0 + while css: + if rules >= max_rules_per_file: + bufs.append(''.join(buf)) + rules = 0 + buf = [] + + nested = 0 + while True: + op_idx = css.find('{') + cl_idx = css.find('}') + + if cl_idx < 0: + raise ValueError('malformed CSS: non-balanced curly-braces') + + if op_idx < 0 or cl_idx < op_idx: # ... } ... { ... + nested -= 1 + + if nested < 0: + raise ValueError('malformed CSS: non-balanced curly-braces') + + buf.append(css[:cl_idx+1]) + css = css[cl_idx+1:] + + if not nested: # closing rules + break + + else: # ... { ... } ... + nested += 1 + + rule_line = css[:op_idx+1] + buf.append(rule_line) + css = css[op_idx+1:] + + rules += rule_line.count(',') + 1 + + bufs.append(''.join(buf)) + + bufs = [_apply_preserved(buf, preserved_tokens) for buf in bufs] + + return bufs diff --git a/.venv/lib/python3.9/site-packages/csscompressor/__main__.py b/.venv/lib/python3.9/site-packages/csscompressor/__main__.py new file mode 100644 index 00000000..28fea597 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/csscompressor/__main__.py @@ -0,0 +1,51 @@ +## +# Copyright (c) 2013 Sprymix Inc. +# All rights reserved. +# +# See LICENSE for details. +## + + +import argparse +import csscompressor + + +def _get_args(): + parser = argparse.ArgumentParser( + description='CSS Compressor {}'.format(csscompressor.__version__)) + + parser.add_argument('input', nargs='+', type=str, + help='File(s) to compress') + parser.add_argument('--line-break', type=int, metavar='', + help='Insert a line break after the specified column number') + parser.add_argument('-o', '--output', type=str, metavar='', + help='Place the output into . Defaults to stdout') + + args = parser.parse_args() + return args + + +def main(): + args = _get_args() + + buffer = [] + for name in args.input: + with open(name, 'rt') as f: + buffer.append(f.read()) + buffer = '\n\n'.join(buffer) + + line_break = 0 + if args.line_break is not None: + line_break = args.line_break + + output = csscompressor.compress(buffer, max_linelen=line_break) + + if args.output: + with open(args.output, 'wt') as f: + f.write(output) + f.write('\n') + else: + print(output) + + +main() diff --git a/.venv/lib/python3.9/site-packages/csscompressor/tests/__init__.py b/.venv/lib/python3.9/site-packages/csscompressor/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.9/site-packages/csscompressor/tests/base.py b/.venv/lib/python3.9/site-packages/csscompressor/tests/base.py new file mode 100644 index 00000000..75d99f1c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/csscompressor/tests/base.py @@ -0,0 +1,30 @@ +## +# Copyright (c) 2013 Sprymix Inc. +# All rights reserved. +# +# See LICENSE for details. +## + + +from csscompressor import compress + +import unittest + + +class BaseTest(unittest.TestCase): + def _test(self, input, output): + result = compress(input) + if result != output.strip(): + print() + print('CSM', repr(result)) + print() + print('YUI', repr(output)) + print() + + # import difflib + # d = difflib.Differ() + # diff = list(d.compare(result, output.strip())) + # from pprint import pprint + # pprint(diff) + + assert False diff --git a/.venv/lib/python3.9/site-packages/csscompressor/tests/test_compress.py b/.venv/lib/python3.9/site-packages/csscompressor/tests/test_compress.py new file mode 100644 index 00000000..e65768a1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/csscompressor/tests/test_compress.py @@ -0,0 +1,67 @@ +## +# Copyright (c) 2013 Sprymix Inc. +# All rights reserved. +# +# See LICENSE for details. +## + + +from csscompressor.tests.base import BaseTest +from csscompressor import compress + +import unittest + + +class Tests(unittest.TestCase): + def test_linelen_1(self): + input = ''' + a {content: '}}'} + b {content: '}'} + c {content: '{'} + ''' + output = compress(input, max_linelen=2) + assert output == "a{content:'}}'}\nb{content:'}'}\nc{content:'{'}" + + def test_linelen_2(self): + input = '' + output = compress(input, max_linelen=2) + assert output == "" + + def test_linelen_3(self): + input = ''' + a {content: '}}'} + b {content: '}'} + c {content: '{'} + d {content: '{'} + ''' + output = compress(input, max_linelen=100) + assert output == "a{content:'}}'}b{content:'}'}c{content:'{'}\nd{content:'{'}" + + def test_compress_1(self): + input = ''' + a {content: '}}'} /* + b {content: '}'} + c {content: '{'} + d {content: '{'} + ''' + output = compress(input) + assert output == "a{content:'}}'}" + + def test_compress_2(self): + input = ''' + a {content: calc(10px-10%} + ''' + self.assertRaises(ValueError, compress, input) + + def test_nested_1(self): + input = ''' + a { width: calc( (10vh - 100px) / 4 + 30px ) } + ''' + output = compress(input) + assert output == "a{width:calc((10vh - 100px) / 4 + 30px)}" + + def test_nested_2(self): + input = ''' + a { width: calc( ((10vh - 100px) / 4 + 30px ) } + ''' + self.assertRaises(ValueError, compress, input) diff --git a/.venv/lib/python3.9/site-packages/csscompressor/tests/test_other.py b/.venv/lib/python3.9/site-packages/csscompressor/tests/test_other.py new file mode 100644 index 00000000..a8fb6083 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/csscompressor/tests/test_other.py @@ -0,0 +1,58 @@ +## +# Copyright (c) 2013 Sprymix Inc. +# All rights reserved. +# +# See LICENSE for details. +## + + +from csscompressor.tests.base import BaseTest + + +class Tests(BaseTest): + def test_issue_108(self): + # https://github.com/yui/yuicompressor/issues/108 + + input = ''' + table thead tr td { + color: #CEDB00; + padding: 0.5em 0 1.0em 0; + text-transform: uppercase; + vertical-align: bottom; + } + ''' + + output = '''table thead tr td{color:#cedb00;padding:.5em 0 1.0em 0;text-transform:uppercase;vertical-align:bottom}''' + + self._test(input, output) + + def test_issue_59(self): + # https://github.com/yui/yuicompressor/issues/59 + + input = ''' + .issue-59 { + width:100%; + width: -webkit-calc(100% + 30px); + width: -moz-calc(100% + 30px); + width: calc(100% + 30px); + } + ''' + + output = '''.issue-59{width:100%;width:-webkit-calc(100% + 30px);width:-moz-calc(100% + 30px);width:calc(100% + 30px)}''' + + self._test(input, output) + + def test_issue_81(self): + # https://github.com/yui/yuicompressor/issues/81 + + input = ''' + .SB-messages .SB-message a { + color: rgb(185, 99, 117); + border-bottom: 1px dotted text-shadow: 0 1px 0 hsl(0, 0%, 0%); + text-shadow: 0 1px 0 hsla(0, 0%, 0%, 1); + } + ''' + + output = '.SB-messages .SB-message a{color:#b96375;border-bottom:1px dotted text-shadow:0 1px 0 hsl(0,0%,0%);text-shadow:0 1px 0 hsla(0,0%,0%,1)}' + + self._test(input, output) diff --git a/.venv/lib/python3.9/site-packages/csscompressor/tests/test_partition.py b/.venv/lib/python3.9/site-packages/csscompressor/tests/test_partition.py new file mode 100644 index 00000000..1c30b2a2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/csscompressor/tests/test_partition.py @@ -0,0 +1,138 @@ +## +# Copyright (c) 2013 Sprymix Inc. +# All rights reserved. +# +# See LICENSE for details. +## + + +from csscompressor.tests.base import BaseTest +from csscompressor import compress_partitioned + +import unittest + + +class Tests(unittest.TestCase): + def test_partition_1(self): + input = '' + output = compress_partitioned(input, max_rules_per_file=2) + assert output == [''] + + def test_partition_2(self): + input = ''' + a {content: '}}'} + b {content: '}'} + c {content: '{'} + ''' + + output = compress_partitioned(input, max_rules_per_file=2) + assert output == ["a{content:'}}'}b{content:'}'}", "c{content:'{'}"] + + def test_partition_3(self): + input = ''' + @media{ + a {p: 1} + b {p: 2} + x {p: 2} + } + @media{ + c {p: 1} + d {p: 2} + y {p: 2} + } + @media{ + e {p: 1} + f {p: 2} + z {p: 2} + } + ''' + + output = compress_partitioned(input, max_rules_per_file=2) + assert output == ['@media{a{p:1}b{p:2}x{p:2}}', + '@media{c{p:1}d{p:2}y{p:2}}', + '@media{e{p:1}f{p:2}z{p:2}}'] + + def test_partition_4(self): + input = ''' + @media{ + a {p: 1} + b {p: 2} + x {p: 2} + ''' + + self.assertRaises(ValueError, compress_partitioned, + input, max_rules_per_file=2) + + def test_partition_5(self): + input = ''' + @media{ + a {p: 1} + b {p: 2} + x {p: 2} + + @media{ + c {p: 1} + d {p: 2} + y {p: 2} + } + @media{ + e {p: 1} + f {p: 2} + z {p: 2} + } + ''' + + self.assertRaises(ValueError, compress_partitioned, + input, max_rules_per_file=2) + + def test_partition_6(self): + input = ''' + @media{}} + + a {p: 1} + b {p: 2} + x {p: 2} + ''' + + self.assertRaises(ValueError, compress_partitioned, + input, max_rules_per_file=2) + + def test_partition_7(self): + input = ''' + a, a1, a2 {color: red} + b, b2, b3 {color: red} + c, c3, c4, c5 {color: red} + d {color: red} + ''' + + output = compress_partitioned(input, max_rules_per_file=2) + assert output == ['a,a1,a2{color:red}', 'b,b2,b3{color:red}', + 'c,c3,c4,c5{color:red}', 'd{color:red}'] + + def test_partition_8(self): + input = ''' + @media{ + a {p: 1} + b {p: 2} + x {p: 2} + } + @media{ + c {p: 1} + d {p: 2} + y {p: 2} + } + @media{ + e {p: 1} + f {p: 2} + z {p: 2} + } + z {p: 2} + ''' + + # carefully pick 'max_linelen' to have a trailing '\n' after + # '_compress' call + output = compress_partitioned(input, max_rules_per_file=2, max_linelen=6) + assert output == ['@media{a{p:1}\nb{p:2}x{p:2}\n}', + '@media{c{p:1}\nd{p:2}y{p:2}\n}', + '@media{e{p:1}\nf{p:2}z{p:2}\n}', + 'z{p:2}'] diff --git a/.venv/lib/python3.9/site-packages/csscompressor/tests/test_yui.py b/.venv/lib/python3.9/site-packages/csscompressor/tests/test_yui.py new file mode 100644 index 00000000..0e4cb3a1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/csscompressor/tests/test_yui.py @@ -0,0 +1,1534 @@ +## +# Portions Copyright (c) 2013 Sprymix Inc. +# Portions Copyright (c) 2011-2013 Yahoo! Inc. +# All rights reserved. +# LICENSE: BSD (revised) +## + + +from csscompressor.tests.base import BaseTest + + +class TestYUI(BaseTest): + """YUI-Compressor test-suite""" + + def test_yui_background_position(self): + input = """ + a {background-position: 0 0 0 0;} + b {BACKGROUND-POSITION: 0 0;} + """ + + output = """a{background-position:0 0}b{background-position:0 0}""" + + self._test(input, output) + + def test_yui_border_none(self): + input = """ + a { + border: none; + } + b {BACKGROUND:none} + s { + border-top: none; + border-right: none; + border-bottom:none; + border-left: none + } + """ + + output = """a{border:0}b{background:0}s{border-top:0;border-right:0;border-bottom:0;border-left:0}""" + + self._test(input, output) + + def test_yui_box_model_hack(self): + input = """ + #elem { + width: 100px; + voice-family: "\"}\""; + voice-family:inherit; + width: 200px; + } + html>body #elem { + width: 200px; + } + + """ + + output = """#elem{width:100px;voice-family:"\"}\"";voice-family:inherit;width:200px}html>body #elem{width:200px}""" + + self._test(input, output) + + def test_yui_bug2527974(self): + input = """ + /* this file contains no css, it exists purely to put the revision number into the + combined css before uploading it to SiteManager. The exclaimation at the start + of the comment informs yuicompressor not to strip the comment out */ + + /*! $LastChangedRevision: 81 $ $LastChangedDate: 2009-05-27 17:41:02 +0100 (Wed, 27 May 2009) $ */ + + body { + yo: cats; + } + ul[id$=foo] label:hover {yo: yo;} + """ + + output = """/*! $LastChangedRevision: 81 $ $LastChangedDate: 2009-05-27 17:41:02 +0100 (Wed, 27 May 2009) $ */body{yo:cats}ul[id$=foo] label:hover{yo:yo}""" + + self._test(input, output) + + def test_yui_bug2527991(self): + input = """ + @media screen and/*!YUI-Compresser */(-webkit-min-device-pixel-ratio:0) { + a{ + b: 1; + } + } + + + @media screen and/*! */ /*! */(-webkit-min-device-pixel-ratio:0) { + a{ + b: 1; + } + } + + + @media -webkit-min-device-pixel-ratio:0 { + a{ + b: 1; + } + } + """ + + output = """@media screen and/*!YUI-Compresser */(-webkit-min-device-pixel-ratio:0){a{b:1}}@media screen and/*! *//*! */(-webkit-min-device-pixel-ratio:0){a{b:1}}@media -webkit-min-device-pixel-ratio:0{a{b:1}}""" + + self._test(input, output) + + def test_yui_bug2527998(self): + input = """ + /*! special */ + body { + + } + + """ + + output = """/*! special */""" + + self._test(input, output) + + def test_yui_bug2528034(self): + input = """ + a[href$="/test/"] span:first-child { b:1; } + a[href$="/test/"] span:first-child { } + + + + + """ + + output = """a[href$="/test/"] span:first-child{b:1}""" + + self._test(input, output) + + def test_yui_charset_media(self): + input = """ + /* re: 2495387 */ + @charset 'utf-8'; + @media all { + body { + } + body { + background-color: gold; + } + } + """ + + output = """@charset 'utf-8';@media all{body{background-color:gold}}""" + + self._test(input, output) + + def test_yui_color_keyword(self): + input = """ + .c1{color:#FF0000}.c2{color:#000080}.c3{color:#808080}.c4{color:#808000}.c5{color:#800080}.c6{color:#C0C0C0}.c7{color:#008080}.c8{color:#FFA500}.c9{color:#800000} + + """ + + output = """.c1{color:red}.c2{color:navy}.c3{color:gray}.c4{color:olive}.c5{color:purple}.c6{color:silver}.c7{color:teal}.c8{color:orange}.c9{color:maroon}""" + + self._test(input, output) + + def test_yui_color_simple(self): + input = """ + .foo, #AABBCC { + background-color:#aabbcc; + border-color:#Ee66aA #ABCDEF #FeAb2C; + filter:chroma(color = #FFFFFF ); + filter:chroma(color="#AABBCC"); + filter:chroma(color='#BBDDEE'); + color:#112233 + } + """ + + output = """.foo,#AABBCC{background-color:#abc;border-color:#e6a #abcdef #feab2c;filter:chroma(color = #FFFFFF);filter:chroma(color="#AABBCC");filter:chroma(color='#BBDDEE');color:#123}""" + + self._test(input, output) + + def test_yui_color(self): + input = """ + .color { + me: rgb(123, 123, 123); + test-overflow: rgb(1000, 500, 300); + impressed: #FfEedD; + again: #ABCDEF; + andagain:#aa66cc; + background-color:#aa66ccc; + filter: chroma(color="#FFFFFF"); + background: none repeat scroll 0 0 rgb(255, 0,0); + alpha: rgba(1, 2, 3, 4); + border-color: RGBA(1,2,3,4); /* tests uppercase RGBA() */ + color:#1122aa + } + + #AABBCC { + background-color:#ffee11; + filter: chroma(color = #FFFFFF ); + color:#441122; + foo:#00fF11 #ABC #AABbCc #123344; + border-color:#aa66ccC + } + + .foo #AABBCC { + background-color:#fFEe11; + color:#441122; + border-color:#AbC; + filter: chroma(color= #FFFFFF) + } + + .bar, #AABBCC { + background-color:#FFee11; + border-color:#00fF11 #ABCDEF; + filter: chroma(color=#11FFFFFF); + color:#441122; + } + + .foo, #AABBCC.foobar { + background-color:#ffee11; + border-color:#00fF11 #ABCDEF #AABbCc; + color:#441122; + } + + @media screen { + .bar, #AABBCC { + background-color:#ffEE11; + color:#441122 + } + } + + """ + + output = """.color{me:#7b7b7b;test-overflow:#fff;impressed:#fed;again:#abcdef;andagain:#a6c;background-color:#aa66ccc;filter:chroma(color="#FFFFFF");background:none repeat scroll 0 0 red;alpha:rgba(1,2,3,4);border-color:rgba(1,2,3,4);color:#12a}#AABBCC{background-color:#fe1;filter:chroma(color = #FFFFFF);color:#412;foo:#0f1 #ABC #abc #123344;border-color:#aa66ccC}.foo #AABBCC{background-color:#fe1;color:#412;border-color:#AbC;filter:chroma(color= #FFFFFF)}.bar,#AABBCC{background-color:#fe1;border-color:#0f1 #abcdef;filter:chroma(color=#11FFFFFF);color:#412}.foo,#AABBCC.foobar{background-color:#fe1;border-color:#0f1 #abcdef #abc;color:#412}@media screen{.bar,#AABBCC{background-color:#fe1;color:#412}}""" + + self._test(input, output) + + def test_yui_comment(self): + input = """ + html >/**/ body p { + color: blue; + } + + """ + + output = """html>/**/body p{color:blue}""" + + self._test(input, output) + + def test_yui_concat_charset(self): + input = """ + /* This is invalid CSS, but frequently happens as a result of concatenation. */ + @CHARSET "utf-8"; + #foo { + border-width:1px; + } + /* + Note that this is erroneous! + The actual CSS file can only have a single charset. + However, this is the job of the author/application. + The compressor should not get involved. + */ + @charset "another one"; + #bar { + border-width:10px; + } + + """ + + output = """@charset "utf-8";#foo{border-width:1px}#bar{border-width:10px}""" + + self._test(input, output) + + def test_yui_dataurl_base64_doublequotes(self): + input = """ + .yui3-skin-night .yui3-dial-ring-vml, + .yui3-skin-night .yui3-dial-center-button-vml, + .yui3-skin-night .yui3-dial-marker v\:oval.yui3-dial-marker-max-min, + .yui3-skin-night v\:oval.yui3-dial-marker-max-min, + .yui3-skin-night .yui3-dial-marker-vml, + .yui3-skin-night .yui3-dial-handle-vml { + background: none; + opacity:1; + } + + div.base64-doublequotes { + width:100px; + height:100px; + background-image:url( "data:image/jpeg;base64,%2F9j%2F4AAQSkZJRgABAgEAZABkAAD%2F4RfJRXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAABAAAAagEoAAMAAAABAAIAAAExAAIAAAAeAAAAcgEyAAIAAAAUAAAAkIdpAAQAAAABAAAApAAAANAAD0JAAAAnEAAPQkAAACcQQWRvYmUgUGhvdG9zaG9wIENTMiBNYWNpbnRvc2gAMjAwODowNzoxOSAxNDo1ODowNQAAA6ABAAMAAAAB%2F%2F8AAKACAAQAAAABAAABwqADAAQAAAABAAABRQAAAAAAAAAGAQMAAwAAAAEABgAAARoABQAAAAEAAAEeARsABQAAAAEAAAEmASgAAwAAAAEAAgAAAgEABAAAAAEAAAEuAgIABAAAAAEAABaTAAAAAAAAAEgAAAABAAAASAAAAAH%2F2P%2FgABBKRklGAAECAABIAEgAAP%2FtAAxBZG9iZV9DTQAC%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQADAgICAkIDAkJDBELCgsRFQ8MDA8VGBMTFRMTGBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAENCwsNDg0QDg4QFA4ODhQUDg4ODhQRDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgAdACgAwEiAAIRAQMRAf%2FdAAQACv%2FEAT8AAAEFAQEBAQEBAAAAAAAAAAMAAQIEBQYHCAkKCwEAAQUBAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAABBAEDAgQCBQcGCAUDDDMBAAIRAwQhEjEFQVFhEyJxgTIGFJGhsUIjJBVSwWIzNHKC0UMHJZJT8OHxY3M1FqKygyZEk1RkRcKjdDYX0lXiZfKzhMPTdePzRieUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm9jdHV2d3h5ent8fX5%2FcRAAICAQIEBAMEBQYHBwYFNQEAAhEDITESBEFRYXEiEwUygZEUobFCI8FS0fAzJGLhcoKSQ1MVY3M08SUGFqKygwcmNcLSRJNUoxdkRVU2dGXi8rOEw9N14%2FNGlKSFtJXE1OT0pbXF1eX1VmZ2hpamtsbW5vYnN0dXZ3eHl6e3x%2F%2FaAAwDAQACEQMRAD8A9VSSSSUpJJJJSkkkklKSSSSUs4uDSWiXRoOJKFi3Ovx2Wvbse4e5gMwRo5soypdOyN9mTSRHpWvj4Oc4%2FwDVf9WkpupJJJKUkkkkpSSSSSlJJJJKUkkkkp%2F%2F0PVUklCqxttbbG%2FReA4fApKZpJJJKUo2WNrrdY8w1gLnHyAlVOsWvowXZDZ%2FQOZa8D9xrmm3%2FNr3PUswm%2FpmQGamyl4bHiWuhJTYqsFtTLBw9od94U1Q6FkfaOk4tszuqYfwV9JSll1%2FoOt2sOguDXgeTm7f%2FPmN%2FwCCLUWZ1VhrysbKA%2Bjua7zgtuZ%2F1FqIUXTSQcvKZi47sh7XOY0tBDYJhzms3akfR3blVd13prMtuI%2Bwse%2F6L3Ahm7n0%2FU%2Bi2zafzkFOgq9%2Bdj0ZFOM4k3ZDtrGNEnhzt7v3WbWPU8bJpyavVpduZuc2eNWONb2%2F2XtXOY2VU7rA6hnO9RuJS%2Br1hO1lguGJbuYz2extvv3fzLPU%2FwCESU9QkkkkpSSSSSlJJJJKf%2F%2FR9VWf0iwiu3Ff9LGsewT%2B6D7P%2FAyxy0FmunH6xPDMpgd%2FbZFT%2FwDoOx%2F%2B20Qp0lC02CtxqALwDtB4J8FNJBTVovx%2Bo4jvbLLGlltTuRMssrf%2FANJiz%2BhZDqMO%2FEyCX2dOc6t%2Bkuc1gmt%2B3%2FhaPTeoZ5s6R1IZrBGDlkNvI%2BjXcYYyx%2F7tWV9B%2FwDw%2FwDwlyD1LqWH0jq2N1O55rxOogY1rwC4C9uuP6mzdt9Wl1v6T%2Bb%2FAFdFS%2F1Myq%2F2VThusa%2B2tst2k6sPuY73Brm%2Fo3Vv2fy%2F3F0S4TAzq%2Bm%2FWDquKGnbhWOya62DUUlrcmyulktZ6f2ey19Wz8%2F2Ls2Z%2BFYx1jL6yxgBe%2FcIAI3CXfNIqZX3ursqqrZ6j7TqJgNY2PUsPP0dypfWJ%2Fo9NOT2xrarHTxs3tZkf%2By77VR6v9bejdNsbkb3Zbqm2sfVjgOOhrdZFljqsffXs%2FmvV9V%2F%2BDXHdZ%2BuvU8zA6i%2Bj02Y1zawaZLt2LY21tltb7XOb6j6%2FwDR1V%2FzV36v6rPVYFU%2BiCs5PS7cd43O2WUOB77d1X%2FT27lwvVnNyKK8d15q6hij207o9dg3DHdW9m9zLv8AQ%2F4Wu71K%2FTtr9aqzJ%2BuHV8tmR0zNoyLvsXU8Nr7cdtjmtN1J9K%2B2podsbk1%2Fo%2F8A0YsnG6te%2BnLZbdXXY6pttADNwucwmhv2cWbv0tnr%2FpKnfpKbKf8AtsqD1DvrjnfV70cKlldgzgbqjf7QHB3p2sG0sb6lv0%2FfZ6TEcZg630xz2vezK6jVbfhVtf6biQ%2BxltmNY7a2z9Js3Vu%2FwayvrHh1%2BhhdKv3XubjBhY9wDbHtPpOdXkOH6t1Dd%2FRsl%2F6C%2FwDomV7Fg9Wc%2FF%2Bq3RL6biben5WVjh4lljdxbexltf06La9vvp%2FM%2FwAFZ%2FhEuqn0r6lfXM9WYzpvVoo6wxvcbBcGj9Ltr09LLoj9bxf%2BvU%2Fof5mz9avrJd0%2B2jBwHAZTrcd17iA7bVZa2nY0H%2FCXw9v%2FAAdf%2FWl5zidQZ9ZcY3l3oddwg2z1mHZ6zWfQv3t2%2Bnk0%2FwCn%2FwDRf8xf6Df1DqvXGZ%2Be5tzsi7Gd6lm2kPsqNbqqOG1%2F4Cuxnpt%2FS7%2F9I9ALjR1Gh7f96%2BuJJpA1KZj2WNDmODmnhwMhJaySSSSU%2FwD%2F0vVVn9airFGYSGjEcLXuOkV%2Fzd5J%2FdZU%2FwBX%2Fra0EHM9D7Jf9pE4%2Fpv9YHX2bT6mn9RIKcE9U6lgXnJDv2l0jN%2FS497S39C530sOyxn%2BBc%2F%2Bh5P83%2F2kyfS%2FR5NlPrv16uwczExsPHFozLBRWHz6rriWM9Ouv6Hs9T3vseue6B9buoZF5xBU84BDC%2BtoZYW1PczG%2B02Gxm71Gb2XP2fod7P5mtct167q%2FT87Gdfk%2FbscWHI6V1NrQNGu3O2NZ6fp5FN9dTcrH3epR6f9RKwRYXzhKEuGYo%2FxfSMvM6lZlF%2FVfSobU11Q9MGyqLfa%2BrP9PJsr9Kz09n6bGq%2F7dWN1z9VxC3KfZT0%2B%2BPUyGg5NIaPdU7ezZe7KxPR%2FR5Gyr1sX9Db6%2FwDguZx%2Bv352be4WNwft%2B9tb2n21mza%2F07Xn2%2Fp7qmVep6dbP0vqfolqZD7mY91eDmXh9pBAZDGjT9awvb%2Bkp3W%2Fp2Mdkfpav1b%2BXUb0WulTeP23h5Di3JL3UYWTYzlzbKn1sc5rN7baM3FZ9pw8xrvSvr%2FQf0im6uvH6vY7G6hfS2mx1WFYaq7bnk%2Bo2r0rKPTa7bustxX20%2Bz%2Blf4RW%2FqPXeM403s%2FQ9LpdbQ4v97Wl4DcW1nud6PqfrH2e9nszMavNx%2Fs9%2F2hA6i5r77HZLH24Tn1W4%2BUwj2Pva3Jdj5bfzaHb8n7Fk%2F4P9YxP0tX8wulqaWfb1CzZ6GaM30622WDD%2FSV1mp3sfY6tjWOsY1tf8776a%2FTQ7s27qFNVIrqwAfUsbZRXt9S0N9Ky1tX83%2Bgre%2F1sej%2FAE1lzKltdDtbi9ZJsqqbTaBXdS0TbW68PycUxNj8qrIbR%2FpPU%2FSb6%2FTyv6VWf0qvOrzmsYasnGzHmk7gx1Za6K2XR7sd%2B5v6K79JXi3bPU%2FV8i71RRU5%2FWaLMv6kYj7SGX9L6g%2BqK%2Fosrymeux1cf4B17fYsrpuPmNxftz63Pcwl9DWDQ2sIqZkNJ%2FNZe6luR6f%2FAAX%2FAAS1f2pXX0vq%2BJln7LfkVVta3ZAfl4929myn%2FA76rLq86r%2Bbo%2F4qyhVruoZfSek4ldmM12Pe227D9XQBtvptu3%2B7d6Vr66Xf2PU%2FwiSnsuqnDyLMfFtMZORj%2BrXSPeHFseo2l7g31La%2FV%2Bh%2F2oq%2FwfqfpFznVcd9uDZivaxzDZXa1zztZaXB1LW%2Bv%2Fgb%2FZVXi5tv83%2FRsj9AsLqfXv2j0%2FpXqPFedgNurfa10F3uqsx8lv8Ao3Obvrf%2FAMT6n%2BFQquq59%2FURec1zb7CHXMc8htmw7n47v8BtucPUb6v6P9Ld69iNoS9H6Vk0Zzcpj3tqqeW1AAtuc%2F8Am7Meyhv6WvZ%2FNZX%2FAIEugzuk14eLiN69fbi49u6wgBjyWNb6dP6IOa52V6vt247P0dD%2FAFP0ajjdc6kBeekYQd1DIs2tsZVZbdVXt9tYqsG9%2B3%2FTPZ%2Bl%2FwBCsajpXV%2BtX2Zwo%2Fal7Wuvv9S0Mea2%2FT2%2BvZ7m1e1j6GV76PZ%2FpK0NE7avpH1Z69ndU6Ti4mP6mVZktBdbkP3uqbWTRkjKuYGb9t1e6v2776rP9Iuyw8VuJjtoaS8tkueeXOcdz3%2F2nLyb6vdZv6DjDKwmVt3lzbsZz3VsZcHluTgX499hfRYxuzJwn%2FznsyKrLLv8H2vT%2Fr9hODK%2BsM%2FZuSWkvrtFjC1wLmvr%2FWKqW%2Fm%2BzbZ%2BkRR4vVpKn03q%2FTeq47Mnp%2BQy%2Bt7WvG0%2B4Bw3N9Ss%2FpK%2F%2BuNVxBT%2FAP%2FT9VTOa17SxwDmuBDmnUEHsU6SSnxf6x9CzPqt1ECix7GMeben5Q0caneyylzvouto3MruZ%2Fhav0np%2FpFUryaMjFvrzps6dkPbZmUsHvx7zFber4f7rbLP6Wz%2FAEln%2BivYvY%2Bu9L6f1Xpl2H1GG0OG71SQ01uH0b2Pd9B7P%2FUb%2FwBGvH8vpF3T7%2FQpcH5NIea7az6leXSd2%2BzHjdW70qHbMnE%2FwlSZrE2NuzfjKHMY%2BGfpzR2n%2FL9H95wM%2FByOk5hxsgiyuwb6rm613VH6N1X%2FAKMrW10jqbrmHDyHl8MhjY3G6sHd6PO318dv6eiz07fW9P0v%2BOHiPxc6n9j552Ydh3YmVy7FtP5%2Bvvfiu%2Fw%2F%2FB%2Fpf5zehYv1a6pVk5WLnh%2BJZhML6rmiWvsBY6t1Fn0n1NY77Tvp%2FSf8WnijqNWnOEscjGQ4ZB7Oig0fV7Lf01rr8rqR2tez3XPbBx67XsYzayxjbMl9m38%2F9J%2Bl%2FS%2BpzrvUxMWqvqNVuJksx3402zW5oosbdScig725OHd9qY6v%2Bc2ZOLT6Nn88y3e6X1uyk9NexznYd%2BPdi2Mc1lTxl4ztznWu3bKfWZZXbs9T06f0ypdY6J1Hr%2FVH9Wrtpsx6mMqFDS%2F1WisF30XNa5%2B577bf0eyz%2FRV%2BqnHwW1RotDF6xay49Tviks9OHNBAc9g2sfc1m7Zu2%2FoG%2FQxv3PST9Lw2dTblM%2B1W1sdDixoM2vu3f0936Ot3pOZsurp%2FQ5H%2Bj%2FwaJj4GXf052ZjepRjbmOrywNzNjHulh9Meg5zN%2B1lrn%2F4R9fp%2FpFYxcHD6ey6nItdSGAOtdlFwhlh%2FRuHpN3Ppbv8A5xnqemgFOZ1%2FDyMW77T1DIOQy4vra91bqC00en%2BiYx%2F06Nrqnev%2FAIf%2FAEj1WxaG2sxKOo9Mu6iK32UYeOLHML3vLrNrrmu3%2Blv%2FAPRi1cvpl1PUqW3Nfh5VJ34%2BNmO9XEurHu%2FV7t1jGMsYf0noP9n%2FAANqv9Q6W84w630hn2cuFd1jcYGysjS6qzLxmiu3Fyadv896NX%2Bl%2B2eqhSSBQ1u%2Fwed6z9X%2BrtFJH1dZj1agENsqZuIB9EPsvbkW7Wj6eS%2F6fq%2Bj%2BjWOcbNw8hnqdNxq3tizZYXOa4Ty7fkWbmNd%2FObfof4RdpT1fqHUs1rc%2FLfktyRU2vGa4bSHNePtFL3el6VmLlVejZ7f0m%2F1cj%2BZ%2B0WbvSfqn0%2Fq1eTjdRIdlVspsqcxwBqc8PbfW%2BrX376%2FTtf%2FAKH0bcS6v2WJXqulj4YCXEDZqurxn1dxXdRosuw78hnXcR5dSWu22OrY2bqsdn03ZFTt932Oz2ZWL6lVLPWoXS19Hzup9Pq%2BsfTMYty8kn7ZRXDCy5hdVb1DEre%2F9NU79LvxHbLclj7Kf5rIspXE5mPm9J6q%2FLqa82YrmvfVJHqUsd%2BgyqLatln0a%2F03pP8AWxLmerV%2B5j%2Bz%2FU7qvR%2Bp9Cot6QNlLJbZQ5xdZXb9O1l73S99jnu3%2Bq7%2Be%2FnEmN57qv1X6b9c%2Fq99oxWNx%2BuYxcyx7g9rjc2PXxM5t36x6djmt9H1v02L%2Bi9L%2FCVWcz9VPr51npPWX9H%2Bsxuy8bItFNoyDvtot9tIcN%2F06He316P%2FAEKo%2FwAJ9p9hbXW17ntaA98b3AQXQIbuP5y43%2FGB9Q2dfp%2FaXTWtZ1iloEGGjIY3%2FAPe7%2BayG%2F8AafJ%2F6zb%2Bi%2FSUJT2TK66xtraGAAABoAEAbW8fyVJecfUbqP8AjHPV31dXxMjJ6c%2Fay27LaMd1JaNgsoDxX9o9rf0zaW2%2Br%2FPep%2FpPR0lP%2F9T1VJJJJSDNL%2Fsr9geXGABW1rnakD6F36P%2BsuAwumV%2FWXNrxHvcymqh2S7IqLnRa%2B7Jfj2UPcGtqdvyfVspZ7K%2FQ%2By1%2FoaV6Ffj05FZqvYLKyQS13Eg7m%2F9JKimvHprorEV1NDGDwDRtakkEg2NCNnxDqdGb0Lru3qFDHX41rLTWGxVeyR7626tdVmNa%2F1P3LPWr9i2cmzrpY5tdB6gzpL7Kq7WGXPqqf6D8HIc1gZ9upo%2F7T%2FzmVX%2FADXqr0brOF0%2B4135bWVFgc05kD1WMP06ari17q25P81Z%2B%2FX%2Bi%2BnauTysfqnQeoM6h06l91eW2H4jwf1uhgc70Lq3DdV1XGxm78d7%2FwCfo%2FQXfpK7GIRFX4s2fN7vAeGpRjwk93IyOj5VDnN6U5%2FUMa%2FflYlLGuLm%2BqxnpfadrG1tdk302Ufav%2Bs2en%2FNrQ%2Brd9%2BNlNdfS%2FDuDXMy8e7R1bmjfJ%2FkNhl9Nv59KtOeLbMDO6FktJte%2B%2FCstLh%2Bhcaxm4mY5oc5tLMr08XK3st9H7TRd%2FSqPWW11fA%2Fa%2BOOpdOc6jqeMNllYjcdvudjWt9zfVr3b8d%2F%2Fom%2F1E%2B2F5jpf1u6Dj9RyLKcqu3peaf1rF1aGh%2Bhym0XMr91TfZksr%2Fncb%2FSWY9bFTwftfUOpdQwRZvxsDLOLh2uBexrLH21YzLLAHv9K3ZX%2FwAH%2FOLUw%2BnfU%2F6xVDB6lg4%2FTutEFrLaG%2BgLXD%2FC42zY2x%2F%2Blw7f01f8ur9On%2BroyPqllZfThhMsqBZblWhxFjahFLLWE%2B27HZv3sbsr9H9N63%2FBjW1L9J9PDa%2F6udcx%2FU6Ta7bSH6uxLT7W0Oc0%2Fo6HT6mFl1fzX%2BnV9n1R6v07bbhZv2za4w149K0Vk6enkMeWPu2fzn8xVb%2Fwf0F1GVh4mdSW3MD2vbAfA3AH9xyoYmRZ0o%2FY894%2BztBNGS7QbR%2Ba8n6Oz%2FwH%2FttLyRbyPUcXMqzznY2Nfh9TrbZbkkVyxzQBvzm%2Bm04vr7f0d%2Fo2frX%2BD%2FTV2rc%2BrmH0bqHT8fcW%2Fb2Ned1FrmWsaXOZLfRLH01W%2Bpu9L%2BaXUiCJHBWGegM6d1IdU6WzbJi%2FFbAaWuPvdV%2FV%2FnPTStN6U4%2FWfqPk5jhRj2BorDrMXLOjqnge2m1rNrnV3e1lnpf8b%2FO1Urm%2FqLlZH1a%2BseT03IwLa7s97K8nFrY%2Bx9fp%2Bp6OfjupZ6VvT3%2BrY3I%2F0P6Oyn9F%2BjXrKSCFJJJJKUkkkkp%2F%2F9X1VJJJJSkkkklKQM3Dpzcd2PbuAdBa9hLXscNWWVPb9CxjkdJJTxHVfq4Ol5bc%2Bq55dlvLLq2Dax1lprY7IFbP5i63b77Kf9H%2Bk%2FQrpPq%2FcLMAVlgrupcWXM4duBj9I0y7f%2F1f00vrB0hnVunPo%2BjkMmzFtGjq7QC1tlb9Njvd%2FqxS6dWMj0upH9FfbXsyqwIabGwx30%2Ff%2BifW9n9RHopwvrBh4eF1V%2BXkU%2FqmZQQLSB6deYxwux7HAfpGvs9L%2FB%2F8J6ivWVZ2dj4vV8FgfbkY%2Fo5WO8hosrdr3%2Bi6t7nrefWx8b2h20hzZEwR9Fw%2FlIOLg4%2BI652OCxt7vUfWD7Q4%2FScxv5m%2F89K1IujYj8PpWLjWDa%2BusB7SZIcfc5u6XfnFW31seIe0OEzBE6%2FNSSQUpJJJJSkkkklKSSSSUpJJJJT%2FAP%2FW9VSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2F%2FZ%2F%2B01IlBob3Rvc2hvcCAzLjAAOEJJTQQlAAAAAAAQAAAAAAAAAAAAAAAAAAAAADhCSU0D6gAAAAAYEDw%2FeG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04Ij8%2BCjwhRE9DVFlQRSBwbGlzdCBQVUJMSUMgIi0vL0FwcGxlLy9EVEQgUExJU1QgMS4wLy9FTiIgImh0dHA6Ly93d3cuYXBwbGUuY29tL0RURHMvUHJvcGVydHlMaXN0LTEuMC5kdGQiPgo8cGxpc3QgdmVyc2lvbj0iMS4wIj4KPGRpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNSG9yaXpvbnRhbFJlczwva2V5PgoJPGRpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJPGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1Ib3Jpem9udGFsUmVzPC9rZXk%2BCgkJCQk8cmVhbD43MjwvcmVhbD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTU9yaWVudGF0aW9uPC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk%2BCgkJCTxkaWN0PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTU9yaWVudGF0aW9uPC9rZXk%2BCgkJCQk8aW50ZWdlcj4xPC9pbnRlZ2VyPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNU2NhbGluZzwva2V5PgoJPGRpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJPGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1TY2FsaW5nPC9rZXk%2BCgkJCQk8cmVhbD4xPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxSZXM8L2tleT4KCTxkaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk%2BCgkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCTxhcnJheT4KCQkJPGRpY3Q%2BCgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxSZXM8L2tleT4KCQkJCTxyZWFsPjcyPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxTY2FsaW5nPC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk%2BCgkJCTxkaWN0PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTVZlcnRpY2FsU2NhbGluZzwva2V5PgoJCQkJPHJlYWw%2BMTwvcmVhbD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuc3ViVGlja2V0LnBhcGVyX2luZm9fdGlja2V0PC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PlBNUFBEUGFwZXJDb2RlTmFtZTwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PlBNUFBEUGFwZXJDb2RlTmFtZTwva2V5PgoJCQkJCTxzdHJpbmc%2BTGV0dGVyPC9zdHJpbmc%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BUE1UaW9nYVBhcGVyTmFtZTwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PlBNVGlvZ2FQYXBlck5hbWU8L2tleT4KCQkJCQk8c3RyaW5nPm5hLWxldHRlcjwvc3RyaW5nPgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BMC4wPC9yZWFsPgoJCQkJCQk8cmVhbD4wLjA8L3JlYWw%2BCgkJCQkJCTxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTc2PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1BZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQkJCQk8YXJyYXk%2BCgkJCQkJCTxyZWFsPi0xODwvcmVhbD4KCQkJCQkJPHJlYWw%2BLTE4PC9yZWFsPgoJCQkJCQk8cmVhbD43NzQ8L3JlYWw%2BCgkJCQkJCTxyZWFsPjU5NDwvcmVhbD4KCQkJCQk8L2FycmF5PgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYXBlckluZm8uUE1QYXBlck5hbWU8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNUGFwZXJOYW1lPC9rZXk%2BCgkJCQkJPHN0cmluZz5uYS1sZXR0ZXI8L3N0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVGbGFnPC9rZXk%2BCgkJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJCTwvZGljdD4KCQkJPC9hcnJheT4KCQk8L2RpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNVW5hZGp1c3RlZFBhZ2VSZWN0PC9rZXk%2BCgkJPGRpY3Q%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk%2BCgkJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BMC4wPC9yZWFsPgoJCQkJCQk8cmVhbD4wLjA8L3JlYWw%2BCgkJCQkJCTxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTc2PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNVW5hZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BLTE4PC9yZWFsPgoJCQkJCQk8cmVhbD4tMTg8L3JlYWw%2BCgkJCQkJCTxyZWFsPjc3NDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTk0PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5wcGQuUE1QYXBlck5hbWU8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLnBwZC5QTVBhcGVyTmFtZTwva2V5PgoJCQkJCTxzdHJpbmc%2BVVMgTGV0dGVyPC9zdHJpbmc%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5BUElWZXJzaW9uPC9rZXk%2BCgkJPHN0cmluZz4wMC4yMDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC50eXBlPC9rZXk%2BCgkJPHN0cmluZz5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvVGlja2V0PC9zdHJpbmc%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuQVBJVmVyc2lvbjwva2V5PgoJPHN0cmluZz4wMC4yMDwvc3RyaW5nPgoJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnR5cGU8L2tleT4KCTxzdHJpbmc%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXRUaWNrZXQ8L3N0cmluZz4KPC9kaWN0Pgo8L3BsaXN0Pgo4QklNA%2BkAAAAAAHgAAwAAAEgASAAAAAAC3gJA%2F%2B7%2F7gMGAlIDZwUoA%2FwAAgAAAEgASAAAAAAC2AIoAAEAAABkAAAAAQADAwMAAAABf%2F8AAQABAAAAAAAAAAAAAAAAaAgAGQGQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4QklNA%2B0AAAAAABAAZAAAAAEAAQBkAAAAAQABOEJJTQQmAAAAAAAOAAAAAAAAAAAAAD%2BAAAA4QklNBA0AAAAAAAQAAAAeOEJJTQQZAAAAAAAEAAAAHjhCSU0D8wAAAAAACQAAAAAAAAAAAQA4QklNBAoAAAAAAAEAADhCSU0nEAAAAAAACgABAAAAAAAAAAE4QklNA%2FUAAAAAAEgAL2ZmAAEAbGZmAAYAAAAAAAEAL2ZmAAEAoZmaAAYAAAAAAAEAMgAAAAEAWgAAAAYAAAAAAAEANQAAAAEALQAAAAYAAAAAAAE4QklNA%2FgAAAAAAHAAAP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8D6AAAAAD%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FA%2BgAAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwPoAAAAAP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8D6AAAOEJJTQQIAAAAAAAQAAAAAQAAAkAAAAJAAAAAADhCSU0EHgAAAAAABAAAAAA4QklNBBoAAAAAA1UAAAAGAAAAAAAAAAAAAAFFAAABwgAAABAAcwB3AGkAcwBzAF8AYQByAG0AeQBfAGsAbgBpAGYAZQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABwgAAAUUAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAQAAAAAAAG51bGwAAAACAAAABmJvdW5kc09iamMAAAABAAAAAAAAUmN0MQAAAAQAAAAAVG9wIGxvbmcAAAAAAAAAAExlZnRsb25nAAAAAAAAAABCdG9tbG9uZwAAAUUAAAAAUmdodGxvbmcAAAHCAAAABnNsaWNlc1ZsTHMAAAABT2JqYwAAAAEAAAAAAAVzbGljZQAAABIAAAAHc2xpY2VJRGxvbmcAAAAAAAAAB2dyb3VwSURsb25nAAAAAAAAAAZvcmlnaW5lbnVtAAAADEVTbGljZU9yaWdpbgAAAA1hdXRvR2VuZXJhdGVkAAAAAFR5cGVlbnVtAAAACkVTbGljZVR5cGUAAAAASW1nIAAAAAZib3VuZHNPYmpjAAAAAQAAAAAAAFJjdDEAAAAEAAAAAFRvcCBsb25nAAAAAAAAAABMZWZ0bG9uZwAAAAAAAAAAQnRvbWxvbmcAAAFFAAAAAFJnaHRsb25nAAABwgAAAAN1cmxURVhUAAAAAQAAAAAAAG51bGxURVhUAAAAAQAAAAAAAE1zZ2VURVhUAAAAAQAAAAAABmFsdFRhZ1RFWFQAAAABAAAAAAAOY2VsbFRleHRJc0hUTUxib29sAQAAAAhjZWxsVGV4dFRFWFQAAAABAAAAAAAJaG9yekFsaWduZW51bQAAAA9FU2xpY2VIb3J6QWxpZ24AAAAHZGVmYXVsdAAAAAl2ZXJ0QWxpZ25lbnVtAAAAD0VTbGljZVZlcnRBbGlnbgAAAAdkZWZhdWx0AAAAC2JnQ29sb3JUeXBlZW51bQAAABFFU2xpY2VCR0NvbG9yVHlwZQAAAABOb25lAAAACXRvcE91dHNldGxvbmcAAAAAAAAACmxlZnRPdXRzZXRsb25nAAAAAAAAAAxib3R0b21PdXRzZXRsb25nAAAAAAAAAAtyaWdodE91dHNldGxvbmcAAAAAADhCSU0EKAAAAAAADAAAAAE%2F8AAAAAAAADhCSU0EEQAAAAAAAQEAOEJJTQQUAAAAAAAEAAAAAThCSU0EDAAAAAAWrwAAAAEAAACgAAAAdAAAAeAAANmAAAAWkwAYAAH%2F2P%2FgABBKRklGAAECAABIAEgAAP%2FtAAxBZG9iZV9DTQAC%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQADAgICAkIDAkJDBELCgsRFQ8MDA8VGBMTFRMTGBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAENCwsNDg0QDg4QFA4ODhQUDg4ODhQRDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgAdACgAwEiAAIRAQMRAf%2FdAAQACv%2FEAT8AAAEFAQEBAQEBAAAAAAAAAAMAAQIEBQYHCAkKCwEAAQUBAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAABBAEDAgQCBQcGCAUDDDMBAAIRAwQhEjEFQVFhEyJxgTIGFJGhsUIjJBVSwWIzNHKC0UMHJZJT8OHxY3M1FqKygyZEk1RkRcKjdDYX0lXiZfKzhMPTdePzRieUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm9jdHV2d3h5ent8fX5%2FcRAAICAQIEBAMEBQYHBwYFNQEAAhEDITESBEFRYXEiEwUygZEUobFCI8FS0fAzJGLhcoKSQ1MVY3M08SUGFqKygwcmNcLSRJNUoxdkRVU2dGXi8rOEw9N14%2FNGlKSFtJXE1OT0pbXF1eX1VmZ2hpamtsbW5vYnN0dXZ3eHl6e3x%2F%2FaAAwDAQACEQMRAD8A9VSSSSUpJJJJSkkkklKSSSSUs4uDSWiXRoOJKFi3Ovx2Wvbse4e5gMwRo5soypdOyN9mTSRHpWvj4Oc4%2FwDVf9WkpupJJJKUkkkkpSSSSSlJJJJKUkkkkp%2F%2F0PVUklCqxttbbG%2FReA4fApKZpJJJKUo2WNrrdY8w1gLnHyAlVOsWvowXZDZ%2FQOZa8D9xrmm3%2FNr3PUswm%2FpmQGamyl4bHiWuhJTYqsFtTLBw9od94U1Q6FkfaOk4tszuqYfwV9JSll1%2FoOt2sOguDXgeTm7f%2FPmN%2FwCCLUWZ1VhrysbKA%2Bjua7zgtuZ%2F1FqIUXTSQcvKZi47sh7XOY0tBDYJhzms3akfR3blVd13prMtuI%2Bwse%2F6L3Ahm7n0%2FU%2Bi2zafzkFOgq9%2Bdj0ZFOM4k3ZDtrGNEnhzt7v3WbWPU8bJpyavVpduZuc2eNWONb2%2F2XtXOY2VU7rA6hnO9RuJS%2Br1hO1lguGJbuYz2extvv3fzLPU%2FwCESU9QkkkkpSSSSSlJJJJKf%2F%2FR9VWf0iwiu3Ff9LGsewT%2B6D7P%2FAyxy0FmunH6xPDMpgd%2FbZFT%2FwDoOx%2F%2B20Qp0lC02CtxqALwDtB4J8FNJBTVovx%2Bo4jvbLLGlltTuRMssrf%2FANJiz%2BhZDqMO%2FEyCX2dOc6t%2Bkuc1gmt%2B3%2FhaPTeoZ5s6R1IZrBGDlkNvI%2BjXcYYyx%2F7tWV9B%2FwDw%2FwDwlyD1LqWH0jq2N1O55rxOogY1rwC4C9uuP6mzdt9Wl1v6T%2Bb%2FAFdFS%2F1Myq%2F2VThusa%2B2tst2k6sPuY73Brm%2Fo3Vv2fy%2F3F0S4TAzq%2Bm%2FWDquKGnbhWOya62DUUlrcmyulktZ6f2ey19Wz8%2F2Ls2Z%2BFYx1jL6yxgBe%2FcIAI3CXfNIqZX3ursqqrZ6j7TqJgNY2PUsPP0dypfWJ%2Fo9NOT2xrarHTxs3tZkf%2By77VR6v9bejdNsbkb3Zbqm2sfVjgOOhrdZFljqsffXs%2FmvV9V%2F%2BDXHdZ%2BuvU8zA6i%2Bj02Y1zawaZLt2LY21tltb7XOb6j6%2FwDR1V%2FzV36v6rPVYFU%2BiCs5PS7cd43O2WUOB77d1X%2FT27lwvVnNyKK8d15q6hij207o9dg3DHdW9m9zLv8AQ%2F4Wu71K%2FTtr9aqzJ%2BuHV8tmR0zNoyLvsXU8Nr7cdtjmtN1J9K%2B2podsbk1%2Fo%2F8A0YsnG6te%2BnLZbdXXY6pttADNwucwmhv2cWbv0tnr%2FpKnfpKbKf8AtsqD1DvrjnfV70cKlldgzgbqjf7QHB3p2sG0sb6lv0%2FfZ6TEcZg630xz2vezK6jVbfhVtf6biQ%2BxltmNY7a2z9Js3Vu%2FwayvrHh1%2BhhdKv3XubjBhY9wDbHtPpOdXkOH6t1Dd%2FRsl%2F6C%2FwDomV7Fg9Wc%2FF%2Bq3RL6biben5WVjh4lljdxbexltf06La9vvp%2FM%2FwAFZ%2FhEuqn0r6lfXM9WYzpvVoo6wxvcbBcGj9Ltr09LLoj9bxf%2BvU%2Fof5mz9avrJd0%2B2jBwHAZTrcd17iA7bVZa2nY0H%2FCXw9v%2FAAdf%2FWl5zidQZ9ZcY3l3oddwg2z1mHZ6zWfQv3t2%2Bnk0%2FwCn%2FwDRf8xf6Df1DqvXGZ%2Be5tzsi7Gd6lm2kPsqNbqqOG1%2F4Cuxnpt%2FS7%2F9I9ALjR1Gh7f96%2BuJJpA1KZj2WNDmODmnhwMhJaySSSSU%2FwD%2F0vVVn9airFGYSGjEcLXuOkV%2Fzd5J%2FdZU%2FwBX%2Fra0EHM9D7Jf9pE4%2Fpv9YHX2bT6mn9RIKcE9U6lgXnJDv2l0jN%2FS497S39C530sOyxn%2BBc%2F%2Bh5P83%2F2kyfS%2FR5NlPrv16uwczExsPHFozLBRWHz6rriWM9Ouv6Hs9T3vseue6B9buoZF5xBU84BDC%2BtoZYW1PczG%2B02Gxm71Gb2XP2fod7P5mtct167q%2FT87Gdfk%2FbscWHI6V1NrQNGu3O2NZ6fp5FN9dTcrH3epR6f9RKwRYXzhKEuGYo%2FxfSMvM6lZlF%2FVfSobU11Q9MGyqLfa%2BrP9PJsr9Kz09n6bGq%2F7dWN1z9VxC3KfZT0%2B%2BPUyGg5NIaPdU7ezZe7KxPR%2FR5Gyr1sX9Db6%2FwDguZx%2Bv352be4WNwft%2B9tb2n21mza%2F07Xn2%2Fp7qmVep6dbP0vqfolqZD7mY91eDmXh9pBAZDGjT9awvb%2Bkp3W%2Fp2Mdkfpav1b%2BXUb0WulTeP23h5Di3JL3UYWTYzlzbKn1sc5rN7baM3FZ9pw8xrvSvr%2FQf0im6uvH6vY7G6hfS2mx1WFYaq7bnk%2Bo2r0rKPTa7bustxX20%2Bz%2Blf4RW%2FqPXeM403s%2FQ9LpdbQ4v97Wl4DcW1nud6PqfrH2e9nszMavNx%2Fs9%2F2hA6i5r77HZLH24Tn1W4%2BUwj2Pva3Jdj5bfzaHb8n7Fk%2F4P9YxP0tX8wulqaWfb1CzZ6GaM30622WDD%2FSV1mp3sfY6tjWOsY1tf8776a%2FTQ7s27qFNVIrqwAfUsbZRXt9S0N9Ky1tX83%2Bgre%2F1sej%2FAE1lzKltdDtbi9ZJsqqbTaBXdS0TbW68PycUxNj8qrIbR%2FpPU%2FSb6%2FTyv6VWf0qvOrzmsYasnGzHmk7gx1Za6K2XR7sd%2B5v6K79JXi3bPU%2FV8i71RRU5%2FWaLMv6kYj7SGX9L6g%2BqK%2Fosrymeux1cf4B17fYsrpuPmNxftz63Pcwl9DWDQ2sIqZkNJ%2FNZe6luR6f%2FAAX%2FAAS1f2pXX0vq%2BJln7LfkVVta3ZAfl4929myn%2FA76rLq86r%2Bbo%2F4qyhVruoZfSek4ldmM12Pe227D9XQBtvptu3%2B7d6Vr66Xf2PU%2FwiSnsuqnDyLMfFtMZORj%2BrXSPeHFseo2l7g31La%2FV%2Bh%2F2oq%2FwfqfpFznVcd9uDZivaxzDZXa1zztZaXB1LW%2Bv%2Fgb%2FZVXi5tv83%2FRsj9AsLqfXv2j0%2FpXqPFedgNurfa10F3uqsx8lv8Ao3Obvrf%2FAMT6n%2BFQquq59%2FURec1zb7CHXMc8htmw7n47v8BtucPUb6v6P9Ld69iNoS9H6Vk0Zzcpj3tqqeW1AAtuc%2F8Am7Meyhv6WvZ%2FNZX%2FAIEugzuk14eLiN69fbi49u6wgBjyWNb6dP6IOa52V6vt247P0dD%2FAFP0ajjdc6kBeekYQd1DIs2tsZVZbdVXt9tYqsG9%2B3%2FTPZ%2Bl%2FwBCsajpXV%2BtX2Zwo%2Fal7Wuvv9S0Mea2%2FT2%2BvZ7m1e1j6GV76PZ%2FpK0NE7avpH1Z69ndU6Ti4mP6mVZktBdbkP3uqbWTRkjKuYGb9t1e6v2776rP9Iuyw8VuJjtoaS8tkueeXOcdz3%2F2nLyb6vdZv6DjDKwmVt3lzbsZz3VsZcHluTgX499hfRYxuzJwn%2FznsyKrLLv8H2vT%2Fr9hODK%2BsM%2FZuSWkvrtFjC1wLmvr%2FWKqW%2Fm%2BzbZ%2BkRR4vVpKn03q%2FTeq47Mnp%2BQy%2Bt7WvG0%2B4Bw3N9Ss%2FpK%2F%2BuNVxBT%2FAP%2FT9VTOa17SxwDmuBDmnUEHsU6SSnxf6x9CzPqt1ECix7GMeben5Q0caneyylzvouto3MruZ%2Fhav0np%2FpFUryaMjFvrzps6dkPbZmUsHvx7zFber4f7rbLP6Wz%2FAEln%2BivYvY%2Bu9L6f1Xpl2H1GG0OG71SQ01uH0b2Pd9B7P%2FUb%2FwBGvH8vpF3T7%2FQpcH5NIea7az6leXSd2%2BzHjdW70qHbMnE%2FwlSZrE2NuzfjKHMY%2BGfpzR2n%2FL9H95wM%2FByOk5hxsgiyuwb6rm613VH6N1X%2FAKMrW10jqbrmHDyHl8MhjY3G6sHd6PO318dv6eiz07fW9P0v%2BOHiPxc6n9j552Ydh3YmVy7FtP5%2Bvvfiu%2Fw%2F%2FB%2Fpf5zehYv1a6pVk5WLnh%2BJZhML6rmiWvsBY6t1Fn0n1NY77Tvp%2FSf8WnijqNWnOEscjGQ4ZB7Oig0fV7Lf01rr8rqR2tez3XPbBx67XsYzayxjbMl9m38%2F9J%2Bl%2FS%2BpzrvUxMWqvqNVuJksx3402zW5oosbdScig725OHd9qY6v%2Bc2ZOLT6Nn88y3e6X1uyk9NexznYd%2BPdi2Mc1lTxl4ztznWu3bKfWZZXbs9T06f0ypdY6J1Hr%2FVH9Wrtpsx6mMqFDS%2F1WisF30XNa5%2B577bf0eyz%2FRV%2BqnHwW1RotDF6xay49Tviks9OHNBAc9g2sfc1m7Zu2%2FoG%2FQxv3PST9Lw2dTblM%2B1W1sdDixoM2vu3f0936Ot3pOZsurp%2FQ5H%2Bj%2FwaJj4GXf052ZjepRjbmOrywNzNjHulh9Meg5zN%2B1lrn%2F4R9fp%2FpFYxcHD6ey6nItdSGAOtdlFwhlh%2FRuHpN3Ppbv8A5xnqemgFOZ1%2FDyMW77T1DIOQy4vra91bqC00en%2BiYx%2F06Nrqnev%2FAIf%2FAEj1WxaG2sxKOo9Mu6iK32UYeOLHML3vLrNrrmu3%2Blv%2FAPRi1cvpl1PUqW3Nfh5VJ34%2BNmO9XEurHu%2FV7t1jGMsYf0noP9n%2FAANqv9Q6W84w630hn2cuFd1jcYGysjS6qzLxmiu3Fyadv896NX%2Bl%2B2eqhSSBQ1u%2Fwed6z9X%2BrtFJH1dZj1agENsqZuIB9EPsvbkW7Wj6eS%2F6fq%2Bj%2BjWOcbNw8hnqdNxq3tizZYXOa4Ty7fkWbmNd%2FObfof4RdpT1fqHUs1rc%2FLfktyRU2vGa4bSHNePtFL3el6VmLlVejZ7f0m%2F1cj%2BZ%2B0WbvSfqn0%2Fq1eTjdRIdlVspsqcxwBqc8PbfW%2BrX376%2FTtf%2FAKH0bcS6v2WJXqulj4YCXEDZqurxn1dxXdRosuw78hnXcR5dSWu22OrY2bqsdn03ZFTt932Oz2ZWL6lVLPWoXS19Hzup9Pq%2BsfTMYty8kn7ZRXDCy5hdVb1DEre%2F9NU79LvxHbLclj7Kf5rIspXE5mPm9J6q%2FLqa82YrmvfVJHqUsd%2BgyqLatln0a%2F03pP8AWxLmerV%2B5j%2Bz%2FU7qvR%2Bp9Cot6QNlLJbZQ5xdZXb9O1l73S99jnu3%2Bq7%2Be%2FnEmN57qv1X6b9c%2Fq99oxWNx%2BuYxcyx7g9rjc2PXxM5t36x6djmt9H1v02L%2Bi9L%2FCVWcz9VPr51npPWX9H%2Bsxuy8bItFNoyDvtot9tIcN%2F06He316P%2FAEKo%2FwAJ9p9hbXW17ntaA98b3AQXQIbuP5y43%2FGB9Q2dfp%2FaXTWtZ1iloEGGjIY3%2FAPe7%2BayG%2F8AafJ%2F6zb%2Bi%2FSUJT2TK66xtraGAAABoAEAbW8fyVJecfUbqP8AjHPV31dXxMjJ6c%2Fay27LaMd1JaNgsoDxX9o9rf0zaW2%2Br%2FPep%2FpPR0lP%2F9T1VJJJJSDNL%2Fsr9geXGABW1rnakD6F36P%2BsuAwumV%2FWXNrxHvcymqh2S7IqLnRa%2B7Jfj2UPcGtqdvyfVspZ7K%2FQ%2By1%2FoaV6Ffj05FZqvYLKyQS13Eg7m%2F9JKimvHprorEV1NDGDwDRtakkEg2NCNnxDqdGb0Lru3qFDHX41rLTWGxVeyR7626tdVmNa%2F1P3LPWr9i2cmzrpY5tdB6gzpL7Kq7WGXPqqf6D8HIc1gZ9upo%2F7T%2FzmVX%2FADXqr0brOF0%2B4135bWVFgc05kD1WMP06ari17q25P81Z%2B%2FX%2Bi%2BnauTysfqnQeoM6h06l91eW2H4jwf1uhgc70Lq3DdV1XGxm78d7%2FwCfo%2FQXfpK7GIRFX4s2fN7vAeGpRjwk93IyOj5VDnN6U5%2FUMa%2FflYlLGuLm%2BqxnpfadrG1tdk302Ufav%2Bs2en%2FNrQ%2Brd9%2BNlNdfS%2FDuDXMy8e7R1bmjfJ%2FkNhl9Nv59KtOeLbMDO6FktJte%2B%2FCstLh%2Bhcaxm4mY5oc5tLMr08XK3st9H7TRd%2FSqPWW11fA%2Fa%2BOOpdOc6jqeMNllYjcdvudjWt9zfVr3b8d%2F%2Fom%2F1E%2B2F5jpf1u6Dj9RyLKcqu3peaf1rF1aGh%2Bhym0XMr91TfZksr%2Fncb%2FSWY9bFTwftfUOpdQwRZvxsDLOLh2uBexrLH21YzLLAHv9K3ZX%2FwAH%2FOLUw%2BnfU%2F6xVDB6lg4%2FTutEFrLaG%2BgLXD%2FC42zY2x%2F%2Blw7f01f8ur9On%2BroyPqllZfThhMsqBZblWhxFjahFLLWE%2B27HZv3sbsr9H9N63%2FBjW1L9J9PDa%2F6udcx%2FU6Ta7bSH6uxLT7W0Oc0%2Fo6HT6mFl1fzX%2BnV9n1R6v07bbhZv2za4w149K0Vk6enkMeWPu2fzn8xVb%2Fwf0F1GVh4mdSW3MD2vbAfA3AH9xyoYmRZ0o%2FY894%2BztBNGS7QbR%2Ba8n6Oz%2FwH%2FttLyRbyPUcXMqzznY2Nfh9TrbZbkkVyxzQBvzm%2Bm04vr7f0d%2Fo2frX%2BD%2FTV2rc%2BrmH0bqHT8fcW%2Fb2Ned1FrmWsaXOZLfRLH01W%2Bpu9L%2BaXUiCJHBWGegM6d1IdU6WzbJi%2FFbAaWuPvdV%2FV%2FnPTStN6U4%2FWfqPk5jhRj2BorDrMXLOjqnge2m1rNrnV3e1lnpf8b%2FO1Urm%2FqLlZH1a%2BseT03IwLa7s97K8nFrY%2Bx9fp%2Bp6OfjupZ6VvT3%2BrY3I%2F0P6Oyn9F%2BjXrKSCFJJJJKUkkkkp%2F%2F9X1VJJJJSkkkklKQM3Dpzcd2PbuAdBa9hLXscNWWVPb9CxjkdJJTxHVfq4Ol5bc%2Bq55dlvLLq2Dax1lprY7IFbP5i63b77Kf9H%2Bk%2FQrpPq%2FcLMAVlgrupcWXM4duBj9I0y7f%2F1f00vrB0hnVunPo%2BjkMmzFtGjq7QC1tlb9Njvd%2FqxS6dWMj0upH9FfbXsyqwIabGwx30%2Ff%2BifW9n9RHopwvrBh4eF1V%2BXkU%2FqmZQQLSB6deYxwux7HAfpGvs9L%2FB%2F8J6ivWVZ2dj4vV8FgfbkY%2Fo5WO8hosrdr3%2Bi6t7nrefWx8b2h20hzZEwR9Fw%2FlIOLg4%2BI652OCxt7vUfWD7Q4%2FScxv5m%2F89K1IujYj8PpWLjWDa%2BusB7SZIcfc5u6XfnFW31seIe0OEzBE6%2FNSSQUpJJJJSkkkklKSSSSUpJJJJT%2FAP%2FW9VSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2F%2FZADhCSU0EIQAAAAAAVQAAAAEBAAAADwBBAGQAbwBiAGUAIABQAGgAbwB0AG8AcwBoAG8AcAAAABMAQQBkAG8AYgBlACAAUABoAG8AdABvAHMAaABvAHAAIABDAFMAMgAAAAEAOEJJTQQGAAAAAAAHAAIAAAABAQD%2F4TkjaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu%2B7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI%2FPgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSIzLjEuMS0xMTIiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4YXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyI%2BCiAgICAgICAgIDx4YXBNTTpEb2N1bWVudElEPnV1aWQ6RTcxOTVFNTY1NzMzMTFERDlFNzJGQ0E2QjkwQUZBRjU8L3hhcE1NOkRvY3VtZW50SUQ%2BCiAgICAgICAgIDx4YXBNTTpJbnN0YW5jZUlEPnV1aWQ6RTcxOTVFNTc1NzMzMTFERDlFNzJGQ0E2QjkwQUZBRjU8L3hhcE1NOkluc3RhbmNlSUQ%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4YXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPgogICAgICAgICA8eGFwOkNyZWF0ZURhdGU%2BMjAwOC0wNy0xOVQxNDo1Nzo0MS0wNTowMDwveGFwOkNyZWF0ZURhdGU%2BCiAgICAgICAgIDx4YXA6TW9kaWZ5RGF0ZT4yMDA4LTA3LTE5VDE0OjU4OjA1LTA1OjAwPC94YXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhhcDpNZXRhZGF0YURhdGU%2BMjAwOC0wNy0xOVQxNDo1ODowNS0wNTowMDwveGFwOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhhcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ1MyIE1hY2ludG9zaDwveGFwOkNyZWF0b3JUb29sPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIj4KICAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9qcGVnPC9kYzpmb3JtYXQ%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iPgogICAgICAgICA8cGhvdG9zaG9wOkNvbG9yTW9kZT4zPC9waG90b3Nob3A6Q29sb3JNb2RlPgogICAgICAgICA8cGhvdG9zaG9wOkhpc3RvcnkvPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICAgICA8dGlmZjpYUmVzb2x1dGlvbj4xMDAwMDAwLzEwMDAwPC90aWZmOlhSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj4xMDAwMDAwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8dGlmZjpOYXRpdmVEaWdlc3Q%2BMjU2LDI1NywyNTgsMjU5LDI2MiwyNzQsMjc3LDI4NCw1MzAsNTMxLDI4MiwyODMsMjk2LDMwMSwzMTgsMzE5LDUyOSw1MzIsMzA2LDI3MCwyNzEsMjcyLDMwNSwzMTUsMzM0MzI7QzA1QTE5MDRGRjAwQUJEQzA1MUJERkFGMDIwNEVBNTE8L3RpZmY6TmF0aXZlRGlnZXN0PgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24%2BNDUwPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjMyNTwvZXhpZjpQaXhlbFlEaW1lbnNpb24%2BCiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U%2BLTE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6TmF0aXZlRGlnZXN0PjM2ODY0LDQwOTYwLDQwOTYxLDM3MTIxLDM3MTIyLDQwOTYyLDQwOTYzLDM3NTEwLDQwOTY0LDM2ODY3LDM2ODY4LDMzNDM0LDMzNDM3LDM0ODUwLDM0ODUyLDM0ODU1LDM0ODU2LDM3Mzc3LDM3Mzc4LDM3Mzc5LDM3MzgwLDM3MzgxLDM3MzgyLDM3MzgzLDM3Mzg0LDM3Mzg1LDM3Mzg2LDM3Mzk2LDQxNDgzLDQxNDg0LDQxNDg2LDQxNDg3LDQxNDg4LDQxNDkyLDQxNDkzLDQxNDk1LDQxNzI4LDQxNzI5LDQxNzMwLDQxOTg1LDQxOTg2LDQxOTg3LDQxOTg4LDQxOTg5LDQxOTkwLDQxOTkxLDQxOTkyLDQxOTkzLDQxOTk0LDQxOTk1LDQxOTk2LDQyMDE2LDAsMiw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNSwxNiwxNywxOCwyMCwyMiwyMywyNCwyNSwyNiwyNywyOCwzMDtENDYzN0NCOUQ0MUExMEJBN0VGNUVCQ0RCNjMxODMyOTwvZXhpZjpOYXRpdmVEaWdlc3Q%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY%2BCjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8%2B%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQACAYGBgYGCAYGCAwIBwgMDgoICAoOEA0NDg0NEBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAEJCAgJCgkLCQkLDgsNCw4RDg4ODhERDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgBRQHCAwEiAAIRAQMRAf%2FdAAQAHf%2FEAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPBUtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4%2FPE1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1%2Bf3OEhYaHiImKi4yNjo%2BCk5SVlpeYmZqbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEyobHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp0%2BPzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1%2Bf3OEhYaHiImKi4yNjo%2BDlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq%2Bv%2FaAAwDAQACEQMRAD8A7%2FmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmrlHI2utXn6WltnFESb0RFSp41WjD5q3LCBaslzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Dv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2E2rj0LuzvF7PxftUf8AXPLDnC3XI0ewdpOkTK4P08T%2FAMK2Ec1TLNgaxmE9pFIDy%2BEAn3GxwTgV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV%2F%2F0e%2F5s2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXYUeZpfR0a4kpUAx19h6i74b4X65CbjR76FV5FoJKKe5C1GIVLdDuGhuXs2IKOX4gfsvGdx%2FskKtkiyD6bctHHbXgPImOC4qOpXj6E1ff4Mm4Ndxkpc770BvNmzZFLs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F9Lv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVokKCT0G%2F3ZgaioNQemANZuhaWLyE0LlY1%2BbGn6sWspA8QA%2FZ%2FUcVRWbNmxV2bNmxV2bNmxV2NZQwKnodj9OOzYqwDT4iiPZsa%2FVLm5sj7I%2F76L%2BOTXTpjPZQSt9pkAb5r8JyKXqfUvMOpIQOFxFDfoP8qFvTl%2F4Q5INDkrFPAf8AdUhI%2FwBV%2FiH8cnLeAPcxHMprmzZsgydmzZsVdmzZsVdmzZsVdmzYyWRIo2kkYKiirMdgAMVX5sjTeaGl1CzsbSAOLuX0w7k14KCzyAD%2BUZJBirebNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV%2F9Pv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVo4CXUFa9e0CbJRWkr%2B0RWlMHZEJ5WtPMtzE5%2BGYJKnyK0%2F4kuEC1ZeM2IW0okjFT8S7H%2BuL4FYz52mMWn2tDTlcpX6FbDHR5ecCH%2BZevywl%2FMM8dJtn7LdJ%2BKvgry9ccrOJ69KVw9E9GS5s2bAh2bNmxV2bNmxV2bNmxVi3miFU1LSbxvsStJYzH%2FJnSi%2F8ADYpoEzLcxq1T68FGr%2FvyE8GrgjzdC0mhXEsY%2Fe2pS5i%2F1omDf8RwttJRHdrcDaNbhZVI6endIG%2F4mcnHeJCDzDL82bNkEuzZs2KuzZs2KuzZs2KurkZ843Zhs4bVTQzvVqfyrvg3zHNPb2UMsLtGBPGJSpIqjEqRUb9ch%2BvSSvp0JlcvJCLyMuxJb4JNvi%2F1SuEBVvkaX9L%2BZbm8Sv1fTbf0Y6%2F78lPxH%2FgFzpeRvyPbWcXlyyubWBIXuoxJOUG7uKjk5%2FaOSSoxPNXZs2bArs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F%2FU7%2FmzZsVQ2osyWM7o3FlQkMOxGPtZhPBHKCDyUE08e%2BM1Bedjcr4xPT%2FgThV5cvBJbpGergEfMDcYa2PkqfZs2bArs2bNirs2bNirs2bNirshfnRGtb7T9TX7LcoJD2qDzSv3vk0wl81WDajodzDGKzRj1oR35R%2FFT6RthHNXaVcrKsUtftDi304dZz7yrqPrQCIncdMnsEnqRK%2Fcjf54yVjP5gwtJ5ckcD%2B5mik%2BivH%2FAI3wD5RnEtpwrWgw983RGby1qiqKlYGkA%2F4x%2FvP%2BNchnke5BIUGoPQ%2FPEJHJ6XG3JFbxAOPxOA1iX7sUwIdmzZsVdmzZsVdmzZsVUbqBbq2mtm%2BzMjRn5MKZDLP4rC2UVq9qYj7SWshWn%2BtxOTnIVG4ttWvdNaiiK7FxGDtWG6Ti%2FwDwMuShzRLky%2B1lE9vFMP8AdiK33gYscKtBmDWhtq%2FHbOyMp68SeSH5UOGpyJ5pYde3moWPmW5KzH0pIo2WNt0A3Wv0ts2D73zJ6FgzxqqXpKpHG9eDMx7EYXed45VeC4gPCdYyqua0I5q5VgOv2cAW8sWo2QMqhwwoyHcVyQFhVOLzvrNrdf6fFHLFX4kVeBH%2Br1%2F4bJlp%2Bt2GqWrXFnICyKWeJtnUgV3XOP67qFjaFLHUrprecy%2BlZajxPDmN1jn8Dv8Aa%2By%2BIaXrclrdtZ34NrfR1USofgeo2K06c%2F8AgcBCXr%2FlvXRrdnzk4rcoSJFXYfMbnDzOV%2BTr82eqAOQIpaBq7AV2r%2BOdTriRSEp8z8RoN9I5AWJPV5HYD0yHr%2BGQg3Ntq1lcLazx3CiZviiYOKzQo9Kjb7YzoWqRevpt5AOskEij5lCOmc60%2BWCK0j9ONYo5IYp%2BEahQWBeNjRafEcMVTvTb%2B80fyHZ3FugE9vSORJBUqPVZW%2BEHw3yVaXeC%2FsobulGkX41HQMNmH%2FBZzC%2B1iTTbadZUkjtJjzkQqSKqNm5fsdMPfyr1%2FRdT8vra2E5N5C8kl3bSMTKpkctz%2BL7SP9peOA7fFkIkgkCwOfkz7NlA1y8DF2bNmxV2bEJLy1injtpJVWeavpREjk3Hc8V64virs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F9Xv%2BbNmxVplDqUbcMKH5HIPokjWsstqx%2BK2lZPf4Wpk5yF6nF9R8xswHGO8QSj%2FAFh8D%2Fqrkoc6VmSMHUOOjAEfTjsAaZMXhMRPxRnYf5J6YPyJV2bNmxV2bNmxVQuzOtu729PVUclBFa07U98DafqsF6i9EkI6ePywwyF6vG2i6oJRUWl6xZCP2JOrr%2FsvtLhG6s0yiK4X6Zfi4QI7Vb9lv5h%2FXDHrgV5c8B0LzLc2Q%2BGF29WDw4SfF%2Fwp%2BHOg6ZMHi4j2YfTkZ%2FMGwpFaazGDytm9GYj%2FAH25%2BEn%2FAFX%2FAOJ4N8uXwliicnps305LmFZDewrc2dxbt0lieNvkylc45%2BX10UCQzHjLATE4P%2BQeNc6L55e5i0J57WVonikRmZDSq7jf2yB2Op%2FVYor2%2FwBNjktpAGjv7ZvU%2BInoy8Vao%2Fa44xCQ9as25RVBqKnfBGRby9rBEsljcIY0aQ%2Bg5FCCQD6bj9lslGAikN5s2bArs2bGyOI0aRuiAsfoFcVS%2B%2F1q2sJltyrSytuyp%2ByDsC3zwxBqAfHfI%2Fodmt2z6pcjnJK5dAexPT%2FgRkhxV2c782mSy82WUgNIdUhNs4PTmlXQ%2FwDBDOiZAvzLb6nHpOrlFkjs7lWdG9yvxVG%2FwiuGJ3Up3YTJFr7op%2BC8t1ljHjxodv8AYtkiyHbxx6XfKx%2FcXBilJ3%2BAsVUV8FRlyYA4Z80DkkPmq3EtlHJSpR%2BJPgHBH66ZBtIufqt9Np8pALHnEpO5p1oM6Xq0P1jTriPoePIfNdxnLtZtrGZluBcG3vF%2BFJYuJfx40IIxHJKzzb5et9ZtZYp1LwygcgPtKy%2FZkT%2FKWuRKKCOC6tNH1CblcWiILDUWHH6wg6wyDejx9Fw4sfOMjEBY5biH1HgLy8VYulOfJV%2By38q4ndH9LlL219K4iilHrWzLxlhI6SgE1%2F2SnFKo0Czxy28zMqcSzspIYBPj2I%2F1cd%2FytnVZdNQ2D1I%2BESMoPw%2FzMftdMG20TSQyyblmjenzoc4TY6ld6ddyKwMUsbFZoHqNwd1KnGXRXs9n5%2Fv7lg98GlWhMjwk1A%2Fm4k%2FF7jDSwSPULTTJI3PGBphGNwG4OHVW%2BgnONX%2BsWFpDFPaSSJcTESG3TcRlf2uRK%2Fazq%2BkR3aeT7e%2BjnX6yoW8ibht%2B9i9Tg%2B%2FQ98QVZHqstutowuSPSdSpV91IPVWzif1i98t%2BZ7m40FjbSWUrNCqEmkex4EH7acT9lv2c6lonmbTvMUTWdwgiumWktrLQhge6H9of6uRLzd5ZurDVP8Q2KetYFUW7jXeSKi%2Bm0hH7UZH7X7OCY4g2YM0sMrjvexB5SHcXsnkPz3Y%2BcdPqKQ6lAB9bta%2F8lI69Y2%2F4XJeDXPJlvPe6JqMeq6NMYLuE842To6ntToyuv2k%2Faz0J5E8%2B2HnGyI2t9VgA%2Bt2ZP0epH%2FNEf%2BEyESeUuff3tmbFAx8bB9H8Uf4sZ8%2F6P82TMMLNb1iDRrQ3EpBkkPC3j7s5%2FwCNV%2B0%2BGVc4n5m8yfpnzJcsr%2F6HYsLe232PBv3j0%2Fy3%2BH%2FY5JxkLourX175%2Ftb28maSZrloqsdlQ1Tgo%2FZUDO755%2B8uxlvPcMI6i6LAe2zZ6BwlS7NmzYFdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVf%2FW7%2FmzZsVdkb83W5%2BrW1%2Bg%2BK1kAc%2F5D%2FCf%2BG45JMC6jare2FxaEf3qMo%2BdPh%2F4bCNiqV6VOOcTV%2BGReB37%2FaX9WHoyFaJMz23BqiWE7juCh%2FsyaRsHRXHRgCPpwy52gLs2bNkUuzZs2Kuwu1rS49W06Wzb4XI5Qv8Ayuu6nDHMemKvONE1KWGVrG7rHPCxRgeqsppk7sb0XC8W2lUbjxHiMhnnnTHtLmPXrZfgakd1TsR9iQ%2FP7LYtomq%2FWI0Iakybof4H55LmFZdqNlFqNjcWMwqk6FD8z9k%2FQc515XuZbW4l0%2B5%2BGWFzE6n%2BZTTOlW063EQkXY9GXwPhnO%2FNsS6T5qtbofBHqaniexmi%2B2v%2BsU4v%2FlfFgHcrPTFbanZNb3caywyApLE3Qj3zjutwW%2FljzBc6Pp6fV9JHpsLNSSgR1HIorV3Vvizq2jXQk%2BAndlBA9xnNvzRiWPzJBJWnr2gJP%2FGNyv8AHAdikJ3YGf0o5pZDIboSD1GJJ9aAgh6%2F5aUzoFnN9YtYZ6gmRFY06VI3zlXk%2FVI5bi2hnlBgdBuxFFahhfr0rSNsnHlXU4JI5NJaSt1avIQm%2B8RaquD0I%2BLjkpbgIZJmzZsirsDagC1jcqvUxOB%2FwJwTjXUMrK32WBB%2BWKpfofE6Xb8f5d%2FnhjkMPmvR%2FKlrJBqkxBSVkjC0PIDpTfI15s%2FMNbu3jbR5plsZVp69swRi9N0eSjMnH%2BRcVpm669cWWtXGnatwjgKma2uPsj0x%2FNXw%2FayOfmLr%2BhX3l%2BPTYrkXM%2BpGtm1uPUUceX7yRx8KLtx%2F1s5xoN%2FefpVr6C8muVVfUuLO8kMrEIeTem7faRk5Iy5FdTuZ9B169trGZvqkc5eFD8SmJzzUcTtsrceWHzTT3fRJv0p5UZvtOYYrgCu%2FJV4P%2FwANHnPrj83vM2lX9zpWoBJYInaJ540pOi1%2BGRQDxf4cln5Z3qT2stjWrK0iexEgE60%2F2XLOUfmHYNZeZbhWP94Ayn%2FV%2BD%2FjVclLlbEdQn955q1wTiU3huLadeQCsfSljO9VNev%2FAA0bYHub1Z7V5yWkhHxpcVHJNwGiuP8AKStVk%2FbyGafqLQxyafO5S2lPJWUAmGQ%2F7uiB8f8Ad0X%2B7F%2Fy8Ctd6jpl3Lb3v%2BkQTpxniqTDcQn7LoR%2FwUb%2FALDZCyyekWl9ZyaFLPelZGN2tqDEtZJHenEs67ckpVXwq1bU7G1iKWvqtcCR4bkkhFYx%2FCHXhvvX48C%2BU7H1SbK3k9Wya5hv4HchSBF8FxBL2WeOJ1k%2F4s9P4cu%2F0j%2Fc1FphmFxPLeOhZBxAiL8jt40%2Fa5YegV6PprvLpcE8sfpTNbh3QVoGK12zluoQ2PnZih42fmWIVt5z8Md4q9I5P5J17Z2A8YrWVl%2BERoaDwoNvupnLtb02LzREdR0hBba7bVe4tEPFZwpr6sH%2FABb%2FAJOSkgPNLqGaG7e3v42iuoTwlicUIK9iM755KnGoeT7ZX6m1CH%2FYSNCf%2BFOctnuIfNkSWeqEWuu249OC8ccfW47elcd%2Bf%2BVnQ%2FyyS5t9Dk067jMVzaSXMLK3hRJlp4r9rjkQl5PbalcaRd%2BlI7PbwysI5lNJIirU%2BH%2FmnOueV%2FOUeo%2BnY6hIpuWX9xPtwnU%2Fhz%2FnTOPeY4zY%2BYNTgpVBcSVQ9CrHkP14Gsb9rFgRWSzY1K1%2BKNh%2B0p%2FZcY2Qdlev%2BZ%2FLKxK19pcdYBUzWqdYx1Lwj%2BT%2BaP8AZ%2FZyHW9xe6ZfQ6ppk7QXsBDRTIevsR%2B0p%2FlyY%2BUPNR1KJLK5lD3KrW3m6esg67f79T9pMD%2BZtCjTnqVilIW%2BKeFRshPWVAP2P9%2BJ%2BzkZRB3DPFlljlxRPz5Edx8mcw%2Fm3aXvkzULyUC3122jET2oNA8kh9NZYT%2FJvzb%2BTOZabIhnWkgaOQcwxO577%2F5fjhKIYndoZvgaUD05v5SN1J%2FyG6YI0RZ4tS%2BoyKQS1HTwYHsffALrdOQwM%2BLGOEHfhu6PWvJ6f5FsfrXnqW8K%2FDBEZD4cmAVf1Z2fOSeSLmfT9RrHEWlvDSZaVagNR0%2BztnW8mWsuzZs2BDs2bNirs2bNirs2bNirs2bNirs2bNir%2F9fv%2BbNmxV2Y5so4qw6WL9HeYLiLpFcUnjHQfH9r%2FhuWSbTnBgMXeJiv0H4l%2FA4T%2Ba4fTW01FRvDJ6chA%2FYfpX5MMGaXcBmHxbSrsCdi6%2F2f8RyZ3j7kdU4zZzrTfO2qQa3eaHrkax6haux%2Br9FkgJ%2FdT2790ZP%2BGydWWoWt%2BnK3erAfHGdmX5rkSEovNm2yiQASTQDqcCt5sL5dZ06Ko9YSMNqRAv8Aiu2Iv5i0qJecsrKo%2FwAkk%2FctTho9yo69htbi2e2vApgmHB1cgA1%2BffOYX%2BnXflXUghJezkNbabxH8jH%2BdcPPN82l65b2j213DN9WdmmtefCRkdeJaNSVb1I%2FtYXWM8cVk2javM95pUv9zcPvLB%2FI3LvwwhIT2x12KCBrxjyjRS06jrxG5YD%2BZcrzzpkXmjyhLPpzCSaFVv8ATZozuXjHP4GH%2B%2FE5JkUmju9BujaTsJYHHKC4G6Sxn9r%2FAJqwJH5g1fyjDaw2KrcaE0zSNE27RiQmtuXPSJ6%2FuW%2FYkxI6hUg8ia9qulatHe3s0k8DgxGKZywBk%2By3%2BTkn%2FN63a8ttG1a3rHL%2B9hIB%2BIdGK%2F8ABA5DNbS0tL%2B4ksJOenzETWrjYiOT4gjD9l4WqjL%2FAJOTTU9XtNc8r6dDP8E8rc4iSP71RwkTfcP%2FALsX%2Bflgq09zFvKeoTMwV2NeCsB0BaNqf8a5N9Zu59K83W2pWb%2Bn9cjBo24ZXUNxI%2F1lyCWsL6fNb3LLxid24tSgINA9Puyb67pd%2Fr2kaPc6bxN5b8owWYKAYW%2BGpP8Ak4RyQU8ufOF7JLD9XRLaAj96ZPiYv4A9An%2FDYOfz9oEbtA0kjXSqG%2Brqh5NtU%2BkW4iQL%2Fk5yK%2FiuNagjEcpTU7Rj6YqaSAbtG3%2FFin4om%2Fa%2BxhXcGeWxlDgpPbfvUFaPDKpFXiI%2FYk%2Fk%2FmwFXr0n5hfXBJFpVtxmC84jOa81%2FaKqv7Sfy5GL7zJq2rwXFhqF40AlWiSQfAEPVW%2BH7Sfz%2FwCRkTl1dLW3t7xi8108C3g9EBArCnI8jtVvtNRcR1zzTdJcQvYwRW7y28dwzFfUblIvI05fAo%2F2OGxSt3zSW9LPXITMlCjiuzofsyRt2YfsPkd0iabTtQksp1Zra4U84X6SJ%2Bw4%2FwAoD7Ei5dzqV7LFCt7KZnVAY%2BRqyg9aj%2BX%2BTJdouuaHrltb2PmCNfrkKmO2vKcT%2FkgsP2v5cjtapfo0Jt%2FMllFGHltZvVeK4ptwWNi6yU%2BzIvRlwm1mUarpNtJHEWutOkuIbh0FSbfkGiZ2%2Fb4MxH%2Bpk6uZNM8saddywStNK605tsKnYcF%2FmbOZy3s2nWaLE%2FG4e4%2BsK43HELxowOzK%2FL4lx5fFWb%2FlprP1TWLQMaLMFRh%2FlRtxr%2FwLYn%2BfNjLp%2Bp2WpwghWLxuf2SGoRX%2FAIDI9peo2kMtjfWQMUi3JM9u24j5rT4D%2B1G5qyfyfYzp%2FwCcdimr%2BUra%2FG%2FwpISOoIpXJDcIPN4HDcR3Scl2I6qeoOGNvcQyw%2Fo%2B%2FJWGpNvP1aBz%2B0PGFv8Adkf%2BzXIxSaym2NCOh7MMNYLhLlOS7H9pfA5FU30vUNQ8uaqjIqsaqHiO8UsZ6EeKsrfA%2BS3yUr6p5iudTl2S1RvRTchTKeCgV%2FlTlkOsvUveOn8VkdatauxoyN%2FID%2B0r%2FwAmTfyYHsrnTdPasc1%2FNczXMZArwhT04g3%2FAD05Yjml6Fditlcmu%2FpvX%2FgTnK5bS4sp1mDtFID6kLioPXZgc61NGTBNH4o1PpUjIpNbyX0Ulvcw%2FV9Tst7i2IqHWn99EP2kZf7xF%2F18nMX1pANMR1TTYfMkRuYYlt%2FMEY5EfYW7UftL%2FwAXjDf8u%2FMFzM8mnX6MJLWRFkMh4yiqtGUYU%2FZP82IzW%2FH4DVQG%2FduPtRP1FG%2Flb9lsG6ZJDJfPcvGI9WCoJZwPhmRGHF2X%2Ffi4AP2peafmBE8Hmq8LgATCOVaeBUL%2BtcjauUPip6qe%2BdJ82x293Pb2GtyL9ZmWRoL5QAUZZGQK47p9nOd39hdaZctaXScXXdSN1YHoynupwFCL0zUX0y4SVGf6uWDqybPG46On%2BWv%2FAA652nRtY%2FTdqJbfjLdqnK4gjpSVen1iBf5G%2FwB3Rf7qfOCxycaqwqjfaH8ck%2Fk%2B61K11AC1kZYIT6gmB%2Bxy2oP%2BMi%2FC6f7LEGlZCbBr%2B%2Bu%2FqqpBCjP6cbtxHwn4ljrX%2FgckOj2EMcC6lK6yTRKYmkIoYl6gE9xT7L5HhcTJqM1jJCEjjQSQsK7qT3rgi%2FuJ7PTDKQ31S5f03ZPi3SrfGP5RiKG6bej%2BQPzB0CG%2BuNHvB9VeZx9WvZKBX7cCf2P8jOvghgGU1B3BHfPHaQrfuEt3DHkAJakKhP8AO37A%2BedX8i%2BYPOen6dNp2qK5t4JFjs5HClyoNJfTdieUaL8S%2FwDCZEcRNEc%2BrfOOKWMTgeGUaEoHfi%2FpRP8Aunt2Ab3UorWqAc5QK8egHzOPhuoTYrdesJYgnJptgDTqdu%2FtkT1C9XnLPIwofjLduJ6fhkgLcYsi0vVRfSSRSUWRd1UeGGgyN%2BVrCSkmrXKlXnHC3RtuMXXlT%2BaTJKMTV7JdmzZsCuzZs2KuzZs2KuzZs2Kv%2F9Dv%2BbNmxV2bNmxVB6rafXtOuLXvIh4HwYbr%2BORbRL%2BsMTMaSQsOSnrseLZNDnIdfXXtP8zXtnZGC2tJWEsUz8pH4yAE8Y14rs3JfibJRPMKyf8AMXye3mKyh1LTH%2Bra1px9S0ulFSV6mNwPtRnIb5e8wXlw%2FwCj9VR9N1u12PGoD9vUgfpJG%2F7UedN8uapcXsH1e7C%2BrAi%2FvV6OPs1K%2FsttgLzP5Ysr%2B3NzHFxmjPMlNj%2FrLTdWHtiNtlU9I82sxa11lAssf2bmMfA9PFf2G%2F4XIt501y81SykEEjRQRMGEaMV5L0%2BKn2sq4ke3j9O4asuyLNSnMduX%2BV%2FxLCfVZ%2FS0u8cDkVhYhfGgxodGQDDZPMculusTzpwryMT7mneh%2B0uTVNL1e%2Bsob1tLmmtJlEkbFW5FGFR8NfUzi2iQ%2FXNXtLjUAXWe6hE3L%2BQyKHG%2FbjnqzzP5ok0H0baxgWVyodi5IRU6KBx74ASrzOzsfL1tcH69ppMtdxM0h4%2F883OS2GLS7239K1pGpGwj%2BEr%2FALHphVcecBcyrc6vBFdabL%2B7lV0UPbt12KDnx%2FlYYGvrWKwP1nTp3kirsT0BpWgcdf8AZDJg%2FFjSanSb8xC2ursXtpG5kt7dkVRGaU4g%2FETy%2FwBbIzc60xSeylsgphDJeafNtIY%2BnqREfCygYcWPmV14x3qkqQDzAo4X%2BYr%2B0ME6h%2BidZZImjM90FLQzQj40BFNn2%2B1X7DYkd2yi%2BrHNIh8uXbLp7yLJdToZbZJDX1EU0NOXxCeL9tf%2BJ4PhbTLya58sXqqt3EoKlV9MSoRUMn8ssf7XH%2FXyD%2BcfKFzoWnRatFqUUU%2BnSGSGAsBcFXZaFeHJVZDu2HOn3See9Kt1%2BsCx8x2y%2BpaXabB2TxH8pP21%2FZwAnu3VQuV1CC2iW9cx%2FwCkS28kMtA5dKLHL1%2B00TJyZPhk%2B1nS%2FKN59Z0GSNno6yI6e1V4v%2Fwy5z9pv8YWj6Hrcf6P836EWmiiHSfiv2ox%2B2kwH7P2ftLk28uxrbeXYLhYXV7gMzxkE0IqPiqNq%2Fy4gb%2BSk7PNdX1m%2BHmqaytJ%2FTtTdx%2FBGoSu6swZqcj8YbCnRmM97drKxKXcslu7tXb1%2BSBq%2FwCSzZK9c06xuNUutStkBls5IFkAIAMoHqUUfzNRkbCPW5Xs70rZcVtL0%2BtCoAFVc1oK%2FwArni2RIShdD0rU9Z0g2UNW1DR5JLG%2BirRlQuXhmp1ZP72M%2FwCph75g0K2sra21G8ch5baGJUpt6yARuvL6OWEQk1eJrq9s55YLxBzuGhJEjr3LKvxSqv7WISanq2oQRXGryvd2MtVhuSQ8aNQch8OyNjsqElhK1hbcr%2Fcv3K%2FyV%2FycRSQBmUiifsfQM6FpfljSbrR0mCy3EjDlDIzN9sGnphU%2FZ%2FlbIrqnl%2F6jM3Cb1VRqOoU8078XoOHPISIiQCd5ckgE3tyS4StK6xSy%2FDJ%2B7HqseO%2FYVw0W2thatp10he2b7YH21cfZljP86f8AD%2FYxO4tbO4so4I7ZUvG5Lpl3PssprUws392JD%2Fut%2FwCbAOh6sLuU6VqYMF4hKRu2x5DrFID0YY7rsh4tMn06%2FkspjzjniaS1nQfDIE%2BJWXwdafEv7Od0ugnmL8tmYipWEOB1oGWp%2FwCH5Zy4hoT9WuU5BTzj7FHp%2FeIe3%2BX%2FADrnR%2FysvRfaHeaJIwZ4RJGo8QDzQ%2F8AAy5OPVBfPNxbpMpifYqaBu4IwFYWlx9fEIJU7lmH7S%2F83ZJ9e0x7K9unRg8azMkoHWJySVR%2F9YH4W%2Bzj%2FKtvFdaxBDLQKzAVO243G%2F8ArYPJUXb2NvDZtdyWxe3B4meCQ%2BtG37Jkjb9lafaXHRXl9q2r6etnWJ7Yxx28h%2B1UNyMjt%2FlH4myX6lp1poPm57CIFLG8jikCsKisqqXWncc2%2BzhLZW0Om35hCuLiCeSgIHFY67KT9rl4YaV63IwMVSakg1pt27eGRuzvrTWILdYbnlcIWXSdQc8W5pXlp91%2FLIP91M%2F94mG9tcfW3AQ14xgsPH4c4Zp%2Btz6HqN2rJ61nNIy3loTTkA54yIf91zR%2FailyRNUinpV0iXqyyxw%2BjdW5Md%2FaN1iPdqf74f8A5Jtka1ZZ4LWYxsySKpaCRT8QI%2BIxkj2Hw5KLe6%2FTsUF%2Fp1wsmrRRk2l0QAL6Fft290g%2Fu7uP7Lo395hFquqaL9XZmmWCRwySWTgs8MgG6Oo%2FY5fZbAUhC%2BZ9JsfMl1SKYW%2BqyQRT2DMf3UvJeTwt4M5%2BJchKyrOr6D5gQwTQEpFK%2FwDeQv8Ayn%2BZDk%2B1Gyj1ew0SaC4gS5ntmCtX0ELQtQAM%2FEcv5Fwv1bQxr%2Bnhr4oms2ymP6xuCStSIbof6v8AdzYOaGAQ%2BXdTm1NdLjj5SN8QkG6FOvqBu%2BTjTrS2sbZLCMcVUmkpFGLn7Rf%2FAFv%2BFxTTme102DTuILIwZpKksW%2BzxWtW41%2FZyU2XlW41HhcahytOQJZVHKaXw%2Fd%2F7rY%2FzviAqQ21ldXtytrDG0k524qKmnj8sO736roNmNIltfVv7hFeaMnkUqTwpxNA2S2MaT5etS9w6WdtEvx1b42A7Symh%2F2C5zTzb%2BZun3swOjacGVKwrqMgoajeiJSr%2FOTDVdVZPYa3pOj%2BW7u31uKKzuZGP1dePP1Iz1UovxtJ%2FlYU%2Fl%2F5hOtyS%2BWrlis1Gm0pyfiR4%2Fi9Kv7SumcrvNYnu5DcXH7yToamrfSf6YL8ratLZ%2BZtJvoT6bwXMbEjuCwVh8uJauPFuFe%2Bx%2BYJ4NMawQiOFzyZOlDX4h%2FwQyQeVtAn1BY9R1QN9WU1toG%2Fa3qGYeGE3lnRLPVfM19LdAyWkTtcW8a7xOGaq1kGz7k%2FAM6moAACigA2AxJ7lojn72wANgKAbDLzZsirs2bNirs2bNirs2bNirs2bNir%2F9Hv%2BbNmxV2bNmxV2Qzz3p3P6rqKbFKwyt%2Fkn4lJPseWTPOVfnX5gmstNtdBtnMbajykuXHX0YyPh%2F2b%2FwDEcbrfm24MRzZI447GR59w6pMfNyWl7EdD1KM3Cp6dwoKldj%2B1zHE%2FRko0rz1rUtz9SvLe2lm2Hp8jC7BhWqV5xvUb%2FDnn01jPEin8pHgckemXmsTaHcxANw05o57O7IIKxliJYll%2FaRT%2B84%2FsNkIZhIkSjRDl6zs%2FwIjJCXFHkb5gp3%2Ba2r%2BbdPuEvYLVbLRpiFL27c2EvWkj0%2Fd8v2VpkGsfN2p3Kra3V2XicgEsQeQJ3qSNs7d%2BkdO1rSbKw1LhN%2BlbRXVJKFZyq8ZV3%2F3YGHLOEecvKFx5TvTLDym0idv3EvdG%2FwB9yf5a%2Fst%2B3lpHUOvsua4iNwbWCruHK%2FB0qN9j0zoy%2BarW48r2Oq3l1Le2sSizuGkqk6zgUVXMda8F%2FwCDTOO2M01rIs0XCRQ4kHqCqGnZl74NgkTlMqSOIZpPVNrXjGG8Qvfj2wXzSyqz1eO%2FtNSjQM8sVubhFbbkIpPiI%2BUR5Yca3q17IdPmtZ2js7yztriONNhyAKSciPtMJUfIZaXDWL%2Bra0jcq0ZYAV4yAq6mv8ynJf5Yex1PRjpUsfO%2F0pnuLJWNPUgkNZ4l%2Fm9Nh6vH%2BVpMRa2uvBcXej2Gp2rSC5tJJbOZkqW4v%2B%2FgY%2Bx%2FeJhtBB5sOi313boLa9ihMlo3EB5ClGkULy%2B16XJk4rhlZarBFAbZljtrS5Tg%2FoIF4fyOT1JjbC1L%2BbSbowyMwu4WrzJryX9h1PQpxw%2FFWFXNut9PB6srS6frlqI4JZHJ%2Br30bCsbOf5phx3%2FAN1T%2FwCRgPQbtreVrEFrW8gfnDU0dJUPF09unHD%2FAFeygjjvbi2haTQbpxcXlpHvLp9x9n6xClfjtjXi1P2P3b%2FGseE99ol9qtyjIKamYlktb6Mn0L5QKJ8Zp6d3x%2BH4vt8OD%2FvciTXPZaZ%2Bkdp5%2Bgt19Yad5p0%2Fe0vU2Y8T0NPiaM%2FtJ%2FuvOj6lcGysFaRlYxJWUjfkUXk3z5MM5p%2BV2l3U2pPdalFHK1sOLyqQxSZaFOdDWOT7W%2F7eTTzK8UdlcxREhXBTf%2BdzWg%2B7JxNji70HnTz%2B51GXyrrFxaapD69nfcRqSftqxHL1oj%2FOjPjtQ0GNYCfrCXWi3QMtlelhyjkYbFfdvsyx4M87Wy6zqemTq4X9KwQnmegkK%2Bmf%2BSi5G9M1PVfLUt3od3CJIGDEQS7iKUD4Zov8%2FjyErqrruPNkK58%2FJN7Sx0%2B0sDe3k0k9xA6JBJACGqwPHiKp8S8fts2F0mrDS3nubPThH6ZLa5YcQsnBvs3kSsCnJeXJuK%2FB%2FwAYmwQ%2FrXdjEsQRizhvjKCrUp8If4uW%2FwCyuK3Fs893p1nezfVdeDCOxmHFi8ZH%2B89wSeBjP2YuX%2BVFiYggA70Pmt9QzfyjdxXeiPeaY3rNYt9ZjdKUkgP2gyj7L8ftL%2FPgTz2tuLdJIT6cN1JFcxzJ2U7Sb%2Fs%2Fa5BsKfLEMvlPXby2sJI40uF%2F0izVuUcb05n0z3gkTlw%2F3237vHeddXN5pEMdnbmOys4BFcy0qDcF6GJa%2FwDFacmyX8Pu2R1QOi6Yupabd2F78dj6xjLVFUlpySaHwkUf7F0%2BDIrrvl6dr42N0eOtRIHs7ldkv4B9ijf8tKgfB%2FP%2FAHX28mPluyb0Vs7OdTL6SSReoK8%2FTZqN%2FrqOPL%2FJwTqdhFr9m1lqUhjnhcm2uv8AdltN%2FMKf7pb9tP8AZrjVhWHaHrEWqxjTb9gt9GKQytsXptxNf2sl35d3J0jznJbCojuQlR26cT%2Btc55qthdyXcyyJ9X8wWfx3ESbC6UbrcwfzS8fjfj%2FAHyfvPt88N%2FKuq3F3q1hqBIEyc4pWZuCkoAwck9MA50VPJV%2FMq2k0TznfCJQUmPJom%2BzJG%2FxcW9t%2FwDY4Q6cILa6ZY2eKRwslv6goaDfjy%2FaK4f%2FAJleadM80a8t1pcZ9GCJYmuG2MrD7TAfsqv2V%2Fmwr0WyuNXUw%2FVxci1%2BKLt%2Fw23wrg6qGVanr1vrK2t3csIry1aESBjxJEYYOQff4cjvm3zLbpZXLWTGC5vr%2FwCswRjfjBGoC8yfi%2BIjCPX2vNMu%2FqZDRNGKSAgUqxqgqKruF%2BH4sjsyPPIZZSXJ7knfG1e9%2Flxrdt5ksbqdSFu7eMLcW5O4%2BE0cV6xvnF75uVzcb14yyBW7%2FaO2KeS9RutC8yWd7DMYLfn6d3L%2Bx6D7SLJ7YhdkNd3UkRDRSXErxMDUFGclT9IwndCtpWrz6ZN8MskcDsGdojxkjcfZmip%2B2v8AL%2B3iWoySfXbiHUmFy14frKXqH4nL14zK%2FVuX7cbYEeJWDPQVpU0Pj0x6yqLL0pJUW4tXE1o5O9SaSRbeP28Cob1Z1T6vPKzxjeNSSQK9aV%2BznXLfUrdtDt9T1U%2BjeRxpa3iMvF5V41jcg%2Ftrx%2BLOd6VbaTqJlS7lMl5Iri3hHwRiQj920snhzODppWsii6zcGeSZg3po3NSy%2FD%2Fen4K9V542r0LyheaXcm5uaCCWNhxkl%2FvClOqcvs4rrn5g2enI9tpKi4uqlWck0rSvxP8Atf7HITazpccriNaLGD%2FozGnBVIJkZP261%2BFuWE4juLqSZo2pbJMjyitAQeVPwrjxbUE01reuXGvCKPUZJ2Cs0iqgUgFtunw%2FCKfDhWLXSzayW73jxuGElWgJUbUoSDhkLcXEjJDSJqkoSd%2BH7OGlh5a1HzDONIit%2FWvZEMkJRlQtGlKsCxVX4%2Fy%2FawWtILTLTTmeMmbTrm1jXe0nmMLOwH2nZlrWv%2BVhlBb6Lql09zpemrbx2cJe5WNqiRi3EKrfyj%2BZR8WJ6x%2BVPnXQrK41i9sljsrUB5pBLGzAVCgiNWLNufs4I8h6hp738sNxRBexei5BoBJWscg%2F2Xw4VZjoMllFbx635Y52k9mvPUdKaRjHIF%2B08PInhJQV6%2Fa%2BHOq6d5%2F0G7toZzK%2FBwKzBCVrTo3GpQ%2F62cYW21DQ%2FMUVrFH%2B5vyInPSIqzfE1f2afawkmtp42klt0f6uzuInWoDqrFQRQjkMJV9Q2ms6VfitpeRS%2BwYV%2FwCBNDg2ozzr5Y1vTI7V9L1PnbTDk8E4Qs7M1KKHBDp9GTfQfOdosSxw6txkp8UN0DxBG37WNea09UzZBNY%2FM3SfLcVrLrDIyXZKwm2bmW40DsF3%2BFa%2FF8eTW1uoby2iu7ducMyCSNh3VhUYEK2bNXNirs2bNirs2bNir%2F%2FS7%2FmzZsVdmzZsVdnE%2FwA9rKYXmj6jT9w0ctuT4OpEgB%2F1lOdsyO%2Bd%2FLUfmvy%2FcaXstxtNZyHosybofk32G%2F1sB5ORpMoxZ4TPIGj7pbPl%2BNPrERi%2FaG6H38MkPlXzh%2BjhHo%2BrmunCqwXFKmDmfiSRf27d%2FwBv9pMJBDcWN7JaXcRhuYHKTRPsVdT0P%2FNWB9RiWO6ag%2FdygSL7cuv3HKpkwImPcXoM2KGaHhy3B3BHf3h6B5qslXQooNOVof0a7Xdi6fDxBPKQQ78vT35xNl6Fr1l5s0yXSNZRHujHxlRhtMv%2B%2FE8JB9r%2FAIfILo2pT6XfLd8mmiKehPAzE8oT%2ByvKtOPWP%2BXKvrafSpl1XT2L6ez84LqLrE1a8WH7PH7NDlkMoluPiHRarRz05F%2BqJ5SHf1CT%2BZ%2FLlz5WvvTUmXT5jW2nP%2FEH%2FwCLB%2Fw2FauD8Sn6c67a3Vh5x0p7G%2BRfrAWskY6EHpLFXp%2FxpnLdc0S88t3ptrir27msE3ZlH6nX9pcmR1HJxFS3uA4oftDBlvcz2lxHc2sjQzxMGjlTYgjCVG6Mh%2BWD4ZRIvv3GC1Z7pdxHraNJCViukq13aswVAO9xCW%2F3Uf8Adkf%2B6%2F8AVw1t5NPkKabe3SyqKG2uIquYDX7Jc7ei37a%2FZ%2Fazm9tdT2dxHdWzcJYzyQ9R4UI7qw%2BFhkwhvDqECTaZbExyHhPbqKmCYivBf%2BKZPtR%2F8DgIkSDxVHqK%2FSyBFbjfvR8k1xY6gVtLb6vJbVWZbhvVLo38yAemYZF%2F4XCjW0McX986%2BX72Uem6V56bekVAYLu0D%2F8AJSL%2FAItjw7W0utVgj0%2B7k9DULc%2BnaSMaFkrvaz%2F8yXP%2Bpgq00iztbkaVcP8AWBer6NzEworLWjRsoJ4yRv8AEjr9jGOMDbn5y3UyJ8vcyfyHpl1puhy31zEE1e9cJfKxXjK8JMaTqfGZCrt%2FN9rCX8wda%2BqyabAGAM85aYA9FUcV%2FwCGyZXElvb20VgHb1LeNDEKmpKDhGS%2FfOK%2BcrhtU1G8vLZ%2BYsz6M0Q%2B0nBh6cwH%2B%2B5af7F8slsAAxG5tkesTm68saddo3GbTZ5bb1B1G%2FrRfrxGTW7LzPpcrahALfzDpir9biI4tJESAZowftJ8Svx%2FY%2F1cD6VMb3RNTsShJmhju4gVP2ovtH%2FgG%2BLBVnb6b5g0ux1JGWDXNDj4SEiongQcGjk%2F34hX7LfsYBukt%2BWba4gWHWmj%2BsWdpK0Dwqy%2BoWkonJE%2FvHZOfNcKdasnk80288TN6EEqxOXNWSOJiS7n2p8eB4bySw1ilv8Au4OQk47kKo%2BLv%2FLX7WK6jr4u9Uee1jItndiIiasyv9sV%2Fl3wWAFZBbvYajqsV%2FDSP1yITPUgSKH9SSinYcIRw%2BH7TSJhr%2BYOr2sOkWmnhHSK6YiRolFF5A%2Bn6ngJif8AWyP2ML6dbiRI%2FrqsAI3SRAsMY%2BxHKrlGhfl%2FefB%2B8%2F3XgXXYppJG1m2unkbj6Op2pUSKVAqksSNsif5L%2FFH9vHoUlrytdtDCgdmV7d5I2boU%2BINGT%2FLhpr3nLTKJ9UT19TB4zNHtCe3Jj3b2XIssp1Y8bUfFOQsix7SCSlAXpTmj4av5OnsdM%2BuzB3Zm9OWaNGD2%2FMgRTolP3sXP4Zv92fyYi6oITDVrG28x6NBeBvqWoWS1ivSeIikU8jFI%2BzLA6%2FGn%2B%2B3yFaq1pcXix28ZUGi3U2wadx%2FuziAEWv8Aq%2FH9vDLV7TzXeWrqbaWO4VhHeNHtFdCP7EhX5D4v9%2BfbwpC3liGgvIoVdSqt6pLPGr0pTj%2Bz8Xw4CUkEGil7RwRu9ZzwjPx8UZiKePShyb%2BRfMdhHM2kLEV%2BsDlDLQFncUCxcRX7WEF1DCs63zCs6r6c1Nkencr7rgXSpFsb5zCForepC9PiUEfZr4DADRVD%2BddUkPmPU4lUgVNtNG4qDw2Bof2kP2GyK1LECpY%2BAqTnUFhsta1Ce%2B1G3inuZCrySsgqwqFNRkwu9R8m%2BWIwscEHrharDBGjSH5tT4f9kcIoop4da6Vqt2GW0tbiQHbikbkH57UwHNFLbSvb3CNFLExSSJxxKsuzKwPQjOu3%2Fmq%2B1mFo1C2NmFMjhTQLGu%2FKRtumQfU%2FMWh3uoTXx0r1pJePKSWSnJlVU5lQNufHnhQxmOYxklSPiFCDvUZQKUIpUn7Jr0%2B7D8eYrBP7nRbUf61W%2FhmPmtkBFvptnEezCMkj78CpRZOY5w%2FBmWhBCjseuSyyn0pIpIr2CSr2gtbYTQt6aylufPp8PBWbCZPNurROskYgVkYOv7tSAVNRtg2780%2BYPNd5CdWuRJHEWkWONFjRaih%2BFAPljsqLeRrVh%2BjQt3GBRoXBBKAfFwag%2BLJ15B17yfealE2taTHcXMG63Eq%2FvbYL%2FvyH%2B6eFK%2F3iJyT9vAXkPygPNEuoxx3KQ3VrAHtYWP8AeOxpv%2FxWqijMv2WZMLdS8vBLx4pg9jqVqTFJIvwup6fvP5lP%2FDLillv5i%2BStPtL867pEEcem6hxkivLWjIk%2FevHb05ftZF9Okd5Et5mayvoWEtrdRmnCUfYmhYfzftp%2B3k3%2FAC68zLfx%2FwCFdWt1eElrbUoW%2BwGIrDPGv7CyU%2BLj%2B18eIeavKM3lq7WqfWNOkYizuXFeNesE9P8AiX7WNbqCncfm2fzRoUmg6zEqa7ZPDPPCv93eQRMC00A%2Fa%2FZaWLCHzf8Al7oOra5b6h5KuBHe3rj61Ywr%2B5Xlu0%2FL%2FdDfzx%2F8Qwpup4VWBrORhfW7iS0YkrJbkdRLKv2om%2FY4fbzsvkXU7fWNHju3jhj1ZAI9UEKhSJvEmg%2BF1%2BPEhXgPm%2B98y%2BWriTyxrM0iCMBo5VVW9aDoJIZm%2BLif5ftRthN5X8zxWXm%2FTZ9dHq6HH%2Foz2z1eNLd1Kq4TvwY%2Bo2el%2FPXkjTPPGkNY3n7q7hq9jeqPjikp%2FwANE%2F8AuxM8mazo%2Bq%2BV9UuNI1KL07y2PFlbdWU%2FZljP7UTfstih9Jy6d%2BVV3f2dja3ltHe6ipezS2mrzAFfh%2B2iMf2K%2FE2Xcfk5ojoTZ311bz1JEjFXG%2FitE%2F4lnlh3USLKCY7lSGLQ7UI3DA9mz0L%2BVP5wLqxi8s%2BaZRHqIpHZX0hAE9NljlPaf%2FK%2F3Z%2FrYqqal%2BS%2BoXb2EYvraWG3kcyvLG3IRuwY%2BkgPFW2PfJjZ%2BRruwuFa21iRYFACKFKsKf6r%2Bmf%2BReTUDxy6Yqo20LQQpE8rzso3lkpyb58QBi2bNirs2bNirs2bNir%2FAP%2FT7%2FmzZsVdmzZsVdlEA9cvNirzb8zvIf6bhOu6REP0tbL%2B%2BjUb3EQ%2FZ%2F4yp%2Bx%2FP9jOHagvqWsMw%2B1ETGwPWh33z1znI%2FzL8hqEutf0qP4JRzvrdR9lwR%2FpCDw%2F37%2FweRnGxTtNBreExw5DsD6Sen9F4gpphto2prYSSQ3Ketp11RbyAiu3QSoP50%2F4ZcK5YzG5Q7eGPEUyRpMykRSEhH7MV2IzGAkDY5h3OSEMkDjyCxL8beaZaglxoOpxTW7qLSWkmn3cX2eJ34nx%2FwApclMsen%2BcdIeC4QCZQPXjH2kanwyxe2RjT7%2B2e0bRtVHLT5SWhl%2Fat5Ozr%2FxWx%2B2uJWtxe6HqSw8wtxDvBL%2BxLGegP8yOMyceQEbcuo7nnNVpZ4JmMtwd4y%2FnD9bENU0y80C%2BazuRyTrHIPsunZh%2FTKil4kSIaj%2BGdZ1HTtM846SXUenKv2gN3glp1%2FykP%2FD5yO9s7vRb2SxvU4sh3I6MOzp7NkyOo5OKmSuGUMvQ9MMdF1m40S%2FjvoAHUfDPA32ZIz9pGH%2FDIf2Gwjt5Qh3%2Bw3%2BdcFnrir0GW5Kyx6hav6ltfqDHddW5Dbi1do5V%2Bw6%2FzZLPLbQ6nP8AWrpAby3oJGI2kXqkrfyyqR9rOU6HrKWHq2N8Gl0y6p66Lu0bjZZ4v%2BLF%2FaX%2FAHYmda8p2i2tk8sjrI7miTITxkj%2B1HJQ7huJ%2BLJR3K3snuq3fo2ruoqQC3idvsgD%2FWpnMJ%2FJ%2Fme4Msq2jC9tuUaXcXER3drJ1japH72NW%2BFmX%2Fit%2FwB5nTdIksyzW0c%2FryxVZgzBmCsa4eCZAKHDIWgbPDvK9jquh6xaW%2BtW81rDL6lozTKaFJQVAB%2BKtfhwvs47myvL%2BNQfTs2kD9UK8gQKlv2Xp8StnR%2FzQ1GW08u%2BlZO0d3dTxxQshKsKHm7hh9jiq%2Fazi6MxEiI5YOa3NyxJMjVrux%2BIrlctmQ3TS91E3jCG2jHI09R1qOdOgFf91r%2Fw2CLKFYxyJBIFZJOwHgMC2NryG3wRnq56t%2FZhusZiihu7cMWt5ir2zpyikUAU9T%2BYcuWR5pdPqiacQrWqXCzR1USVBAb%2FAHYp%2FnwutdVhtdTa%2FgWVBOAk8BcNG9BTqyk4c6rc2dxe2F3c2kcKJGI7qx4kUjpyR0B6Vr%2FNhBqc9haSxpHERaXRaWIt%2FuvcoTRa75PlyQn%2FAJZvYI%2FMUS2SNELiVTEygUjnVW9OlftI8hVWVsnSXVxfTGS6laSViCS2wodjsNs5%2Fo8rrL69qwd6pIrqAw%2Fd%2FYPw9OmSua51GVJ762swQXLCD1AiryNQrMfi2%2FyVwxKkIjW7%2BezjR7PTpr%2BUbv6WyUH8zfa6duOEPnjT7SEWmrxxkSXatazowA5KY%2BScgD9qIrxwQ3mPX7EetqFhAtqPtegzySAewA45F%2FMHm1dcRIGjMdvC%2FqRIKcuVCvJ2%2BR%2BzglLY2yhjnLaESfcEquburW8M5HpzKvIr1FaDl79cRbRr6S8%2Br245GCoac%2FCjKp67%2FwAww%2F8AL3li01qBLgPJO8f7t4wP7s%2FsE9fhOHer%2BVtfgg%2BvabcJe1FJrPjwZVUU%2Fdy%2FR9llyIFixuEzjKEjCYojmGM6jaajZ28RjBiEisJzULTi3iSKR0wboHkTXdWt21OLT5ru2A5RlN1k8SrsVWT%2FAGD5ena1aXA%2Bo6pD%2B9J2WcenL%2FlBJR8D%2FwCrnSfy880W%2Flyzl0m7uJZtJR%2BVmXSsltyNXhYD7cP7ScfiT%2BXDXcxosR1D8rvP2qWUdlZadHZW7kPcPcXEYeSn2EKR8%2BKJ9rh%2FNkRu%2FwArtc026ey1FooJo9yKs4IP7StQclz0brf5jeXtKhR7eYX0sieoiQHYL29Rv2Cf5T8WQ7W%2FMj%2BZrOKd0t1Ct%2B6eDk7xMaj05CaNxb%2FVwEgCy249PlyECMefJ4235fXQ63Uf%2FAt%2FXC7VvKsmkfV7iab1dPlISW7jTeFz%2BxJHWoPg37edQ5kyNDIvpyx%2FbQ%2B%2FQg91OB7hFCurIssUq8JoHFUdD%2Byw%2FU2IIIsMMmKeOZhkiYyHMF5g%2BhCNipcyFf3nwD7cH%2B%2FoT%2B1x%2FwB2J%2BziNyW01%2BNseVtMoMc4G7VH82TCbyxfWMxtUMlnDIPrOli55JIh%2FwCK2ZeMqthYYOMVxBNb%2Fu2HK%2Fsl6JU7Xlp%2B1xDfbT%2FdeFgmPknW7q0eC%2FsZ%2BOoWTck5ftRnrG9PtIfstnatY0iw%2FMfQ4%2FMWh8bfXbdSjxnbmyD47S4%2F5ky55gWcaZqDPp1yZo42Ppz8SvIePHOn%2BTfO2qaXML%2FSHiPqcRf2M9fTYf78Xj8XIfs4qoWsGsQ64Tpp%2Bo31zGbK9WYfFE8Z9TceI4FP9lnpNbWDVdGit9RRbiO4gT1g3QkqCW9jX4lzksE1z5382DU7WzEKoqq5UVAoOIeV9uR%2BL%2Fgc7RDGsMMcS%2FZjUIPkopj0UvHtQ%2FKnV9H1F9X8vXCajyYt9TuQoYLSijkx9OTj%2FlcMS8q%2BaLny95guoNYs2s%2FrfEXsLLxKyJXjLH4pxOdpwl1zy1p%2BvCOSctDcw19K5ioHAOxU1HxJ%2Fk4otNoZYriJJoWDxSKGRh0II2IyF%2FmN%2BXll550z4OMGs2oJsLynfr6Mv80L%2FwDCfayU6RYNplhDYNMZxACqyEBSVqSoIH8uDj0xV8NappV7pd7Np%2BoQNbX9qxSaJuoYfrr9pWwuSNg4NSHBqKbEHxrnrL80Py2g86WX1%2BwCw69aJS3kOyzoN%2Fq8x%2F5NP%2Bw2eXm0q8guZoblGglicpNA4pIroaMjfy0OKvpf8lvMusa5ol1ZazOLqXS3jiiuTvIyOvJRIf2ylKc86eM8y%2Fk35usfK2r3FtfyMtjqoRZZ3qRFNGT6bP4IQ7I%2F%2BxztF1%2BaHke01SDR31aN7mZ%2FTLRVeKNu3qTD4F5dMVZlmylIZQwNQdwRuMvFXZs2bFXZs2bFX%2F%2FU7%2FmzZsVdmzZsVdmzZsVdjXVXVlYclIIIPQg47GSOsaNI5oqgsx9h1xV4R%2BZvkA6VK2r6VH%2FuPlarIv8Aulz%2Bz%2Fxjb%2Fdf%2FA5EoJLK%2FwBFh0yOJU1AyrH6oABboqJItebtyJkjZP8AKVs73qnm%2FwAsyW89les7xzwtSMxMRKG%2BHghpx55w%2FwAy%2BWpbIHWNNjeGKFkDjlVw5%2BIPGwp8S%2FtcfsZGUauQHvdvpNWJxjhyyMZA%2BiXQ%2BUmMXNtcWVzJaXSGOeJisiHsRtll1uLf6rcMeKb20vVoW%2F6pP%2B0v7GHsLr5ptvq7mOPU4ElmEhHH1eCII4kp%2Bz6aO7%2FtephBNBPbytDcRtFNGaPG4oR7EZVKJHrhz%2B92JjDPA4cw3HMdR%2FSiiNG1m7028EiEC4j%2BGWMn4JU7qf8AjVslmvaBp3nLSFvbCiTKCY2P2opP24pKfs%2F9d5BTFFIys5ZGX9pKch9B65JvKmrSaVdevxea1m%2BCcEFQyj9tR%2FvxMtxZBIV8x3Oi1eknp5Ud4n6Zd%2F7XmskVxp11JY3sZikjbiyHsf8Amk%2Fs4caPay6peQadE6rJMwVXboF6k%2B%2FEDOn%2BdPIkfmSyGp6QA12qc4JFpSVPtcG9%2FwDjfI35a8qzaFNa3urKrXLmscfI1gP7NSv2n%2FmywxouIN1DWPKDW95DY6K0t5IfgnaUKgD05cl49I6H7TZ0bynBAmmjSVvhcz2qEM60%2BHnVRw9om%2BH%2FAFsItfhndg1q%2FpNdxmIuDQVU%2FECf8oYReSLk6R5nWBw1bmT6sSD8FGBb4h%2FNyXDyPvbsWITE9%2FVGJkB30jPKV1PpOsrBduRLFcPb3JY9VditW%2F2XxZ0%2B91O2spIoJWL3E54wW0Y5SOf9UdF%2Fmdvhzm%2Fm2w%2BoeapJlBVdRjWdCDsZE2cfeuHdvfQW0BngZptVvEH1u%2FlG6gj%2B5hH7Kr0xHc0pD%2BYg8wa1NDFZWjSWtsrCRoiGJcn46b1YdF2yFWemzxsW1CN4Ej%2FZlUofubOs2WpxpNGl2oMP2eSbEe5yTXGkadqFpJaT26T2860YEVBB6EHx8MeCzdpunlEtpHYQ38GqRrFNaPA8EgJcFa%2FGqhadao2XBGlmbe2uCzevbGeRo2PcNJx%2F2S%2FDht51s3sLpYLoH6ncIq21wRU1UceLt9Hxf8Hhj5f8pXerWc%2Bq2VrHqE1oqQWdvNLwUoijmJB%2FO9W4%2Fs8cjVH3LezEvM2mXWqyabNozB7SS1WJ5SwBrHWnLv8A3ZUbfy4onlazmtrKC%2BJlNoHFFNFbmQaHvtTJ9rPlzU7GOO6vrRbeNioQQMrRxmn938IXj4YE0fQLnXBdQ2062l1CP3SSry9Xav7og0%2F4LDsikps7C3s1WK2iWJB%2Bygpg5lNOX0YAVr7TL0xXsxcxsVlhktwpFPtLVWwbf%2Ban0e8tp7LTYrmyILlpyT6hoRw2%2Fu%2FTbdlw3taoK4v7a1dY2dW5GjQ1qVHj%2FwA24Vah5a03VlNxZEQTtvyX7LH%2FAClyfjyfo3nrTrbV9PRNI1i6gM8qRENC8gb05OUI%2BNR6nR1%2F4HIRqOj655Vu%2FQ1SBogT%2B7mU1ikA7pJ0%2FwBi3xZGwdizhOeOXFCRBSPSZNX8maml1MhNsfgldPijdD1H%2BS37S8s6ZLrEEZt2uEe1W6VWtLsg%2BlKGHIGOReS%2F6wOR9dWtRp11dSQ%2FWHt4Wk%2Br93IGyU%2Fysk%2Fk%2FW5INATT9Z0eAWczGT6gr%2BoIlfcgBxsf2vT%2FAGMYxMSeE2D07m7PqBmjEzjWSO3EOUo%2BY72M%2BYfLcd3FJeQRxyo7EzQU792GQ60bU9Hkf9HTAwts1jdVeNl%2FyX%2B2n%2FEc7ZL5XstVSS58p6kYZqVbTrysif6tWPrRr%2FspVzmuvaDq2jzsup2JtA5NCfjtmNf91Tjb%2FYyccJ8tmgJPqV4JJLJpI3sYLqJ2uY%2FtBgP91q6jpIy8Vw0sNNn8tG11m8KWNnqTrAums5YhTvyct%2B0R8fw%2FYb%2FWwLdRpfaVEkkPO50r1HhjJALRSg9HpX92%2FwCzgSW4TV9Hga5uppdVspFt7a0FOMNshHJm3VuZY8fV%2B0zfBlZHq3BNigOnm52LIfDHCYwMCJmVXI8IqMYnzI4f85l%2BsRQc7eSKVRPHKts4ruUkPAcv9R%2FiwbZaY1p5gtrHUoipjnjWZOo4lhRv8pGwcPL9pcROZo2S6ZkaXgyqoaFVk4hn%2BJ9uPqNxwVrmsWupvYzalF9S1K1lTi6GscsYYMpV%2FFWUNx%2FlwYoSjd8jybO0suLMcUsdmUQROxRrnH75PQtd8vaT5itBZapAJI0YPE6njJGw%2FajcfZzg%2FwCZWh3GhW%2Fr2wH1vSZ0DXlPia1m%2FuZuK7MV%2FuLj%2BbPQ1pdR3ltHcxGqyCtOtD3H0HIF%2BZFnb3k8EMqhxPBJHOh6MnIUDfSdssDq3zfqWmR6lFJqenRCO6iHK%2Fsk3FD0ng%2FmjfC3SNVudJuUvrKQJdQOrxc1DoafaSRGqrK%2FRlyU32i3XlnU4RHOYrF2Isr1hy9Jm%2F495%2F8AimT%2FACsKte0Zp%2FW1Kxg9KaL%2FAI6Nipr6bH%2Fd0X88En2lfFX0%2FwDlz5x0fzbpAlsIYrO%2BgAF%2FYxBVCOR%2FeIBTlE%2F7Lf7HJpniLyz5m1TyrqVtq2ly8ZYzuvVWX9qOQftI37WeufJnnHTfOekJqViwSZaLd2pNXhk7qf8AJP7D%2FtYFZJmzZsVdmzZsVaOct%2FNj8sx5ntJNb0NfT1uBayxrt9ajUfY%2F4zqP7tv2vsZ1PKOKvhz0CsbwMzqSSHPRgw2%2BL3%2FmwrlheB%2BD9exHQjxGeqfPH5Oad5lupdW0icabqU3xTqV5QTN%2FOyr8SOf2nX%2FgM59H%2BQfmu5nWC9ns4rbl8U6OzMB%2FkpwGKr%2Fyk%2FOBtOMPlnzTMWsyRHY6g5qYq9I5j3i%2Flf8A3XnoxWV1VkIZWAKsNwQehGc68sfkv5Q8vBZriE6rdrQ%2BpdAemCO6wj4f%2BD550VVCqFUUVRQAbAAYq3mzZsVdmzZsVf%2FV7%2FmzZsVdmzZsVdmzZsVdiNzNDbwSTXLBIUUmRj0A98WwNfPaJbSm%2BKi2pSUv9mh2pirAPON5pMWiStoiPJfM1bVI4yy1G8jDkCnwrhDrD6jFb%2BWbC7Wt36Ecl%2BCP%2BKiZHbtyJajf5WDvP9xZRaNCfKtxKb4zj04IOR403JKkfAOfH%2FWws84ajNd3EEklFuSOUtK%2FDRVXiKduQOFIeeatpk0F5PeaUj%2BlATLIYwSYhXd%2Fh%2ByleuLP9U12ykmiWO2vrWOW4n3PxgcAFXmaty3cf77%2Bx9jO1%2Flvo0EejT388SvJqDMjFxXlEvw8TX9ls5h%2BY%2FkSTyne%2FpHTVY6LdsVUj%2FdDt1hf%2Fitv91t%2FsMgRW4dxo9X4vDiySqcfon%2FO%2FoyYJsQGHfJL5FsrXUdZk0m6uTbR3UTvHQAgzRjkONSODla5GgcX0%2FUJNL1S0v41DPFINm8G%2BE%2Frym%2BHMD0k52txeLppjqPUPfF6l5X8xWlm995evZlguLSdkjR9gTWjrXovP4XTEPM6wwzKSKCT4tuoIO%2B3vke80xXU2tW13ZQ%2Bo15bq8npJ8UlELVp%2B3sMBWOsXOtWy2iEyXNuCyIw5Myf8VsaseFP7tvizL4tq%2BTzNUfvT%2BOU6npky9ZrciVAO5Xw%2Ba8sis01rp%2FmC01O5YrSWJ4kXcyFiB9nsK%2Ft5fljzLXzMlg0bRQujQESbVlDcl5IenL4kyeebPIp8y6j5e1DSEjX0XWO4QsqKYk%2FfRgV%2Fb5gx8cHMe5lGUom4mjuPnzSW%2F1OfVL6R7sJW2keKFUGygGhIJ3PKmNVy%2FTAk1pdWc9zBdrwuElcTIezA%2FEMj%2FmjXbvQtaW3tEBgCJM6PuCHFQExtizSKI1DHJv5VuxKrWMp%2BwC8JPh%2B0v0ZCNLvrfUrGG9tjWOVQfkf2lP%2BqcPdHme3vIZV24sKn2Ox%2FDJBSyzzBplrqWnSQSxLI0P76EsoIDqO3%2BsMgmlaxceUdRXU7ZS9jJRb62XoUP7QH80f2k%2F4HOgXdyVYgHp1%2BWQW8g4TzQyoQpJIVh1VuhGJCh7FG9hrempKhW4sryMMp6hlYV%2B%2FOWa%2FbXfk%2FU0ZJOEL1ayu2%2By1P91udv3if8OuCPJ%2FmhPL1xaaJetx0y4rFFMx2imJJSvhHLXj%2Fr4N%2FO9G%2FwAJ28o%2FYvY69%2BqOMgdlYzqPmWy8xr615aouoxAA3dk4dJB0%2FfRGjofBl54U%2BnCySRNSa1l%2B2vQq3861%2By65GPKcztqxjY7NEw4jYbU32zoNppF1qfrw2JX6yierHExoJivWIf5fH7DYYnZaY3pN9N5N1n9JJEbuIwyRoisY1kDbpVu3F%2FidM7xpl3Y%2BadFt7m5tle3vIUkaCYBx8Qowof5WB%2BPONG0jvLY28qlRUgqwoyOOu37LpiXl3XbjyZqk7XglmtZIfTEKNQGh5REFvsxr8XLjgIrdWQedvy7GiQPrfl4SNaxVe5s1q7Rp3ki%2FaaNf20%2B1kT07WGCqwcMjdJFNQc9C2N9BqNvHc29WiljSVGI2KyCopkFv%2FwAotHu9cOq2d1JYW0x53dhAo4O53JQt8MYf9ocMFyB23ZwGOQIkeCQGxqwfIsatNQSQrIrmOUbrIhoQfEEb5LLHzPP6X1TV4RqFo4ozFVL0%2FwApD8EmFms%2FlxeWBNxoEpuIhubSQj1B%2FqPsHwo0%2B4ltUmW7Ro5ojRo3BVhT2OWCi1t%2BbPJ1pFCfMXlJgtl%2Fx92VCyRHuwjb4kX%2Bdf2ciOlG2huhcX2n8JI2BE8FHHzMTb%2F8TVc6FpWutaySPblJEmHGeCQVVwP1ZGtcsIbe5N3p0Ui2MlWaM7mB%2FwBpQ4%2B1F%2FLkTGulhnDIYnYprf6tYaxaxxCDlxIaG7iko8Z7kGtWr%2B3ywxsdGl1WwitzGbkgkRzugDGnRm34R0%2FZbIJBZwxv9YhAVx8Qp0JPiBnafJl1eXekLNdyrKOXGNgADQD4g3Gg%2BE7Y8WyTLfiHNJbbQvNHle%2Fe50eQX%2BnXAD3Vg53EoWnKImhQmn7Pwv8AyYRX9xql1dmXWOS3XHiqMnp0QEkKq%2BA5fazrQwHqOmWmqQGC7j5D9lxsynxVsFsLeO39hb39tJbXMYlhlHGSNuhB%2FUffOZXy3%2Fl7UV05n5MgrpN1LTjLCT8VncEj%2FYf5Odt1nQbzR3JcerasfgnA2%2BT%2FAMrYB%2F5V3Y%2BdtAvhefubrn%2FuNux1jkQUYkD7Ubnir%2F6uEq8D1rTFk9XUdNgaHix%2Bu2XVoHPcD%2FfbfstnQfyAeb%2FFktC%2FptaSF1VgFNGTiXFfiA%2BLj%2FlZC9WuNd8p6kNM1e04anYsY2mY7TW%2FZG2pKjfsS4K003mkCHzl5WmDLazercWoB9WCvX1FB%2FeQt9lv8nIofYNRl5FPI3njTfO2li7taRXkQUXlp3Rj0Zf5on%2FYbJXirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F%2FW7%2FmzZsVdmzZsVdmzZsVdgHVY9PmtHi1JlW3cgEuQBUbr1wdgPUdNttTtzb3IJWoZSOoYdxirzHztP%2Bi20aTyvdm6uDcJSCMJIPTVhRWKj7Bbbi2B9b8keZrnV3FvberbsQsVwHUKFJ6tU8hSvhk%2B%2FwAPGy1GwvLOkqK5ju0cIP3YQ%2BnItAPiif8A4nkkphKQaQOk6eml6ba6fGQVt4whPif2j%2FwWO1LTbPVrGfTr%2BITWtyhSWMjqD%2FEfs4MzEVwIsg2Ni%2BVPOHlm58p65NpU1XhP72znP%2B7IWPwn%2FXX7En%2BVgW28s63f2kWr29m76Ys6xyXQpxUggNUfa4iv2uOen9c8t6J5it0t9as0uo4zyjLVDKf8l1oy5EL7TriGW30Lytp7Q6Dbs0d7xI4Oz%2FFWPlycmNvib%2BfKziufETs7WXahOn8Ph9dVIn6a7%2FewDzPd3ukNplzYtxtEhW2adVBKzRktTkalOdf%2BBw60bydZ%2BaNR%2FwARaPq0VjOIw509bcLIkw%2B16yh%2FjRm%2F3an28de6dLYTXGk6xDztpx8aHoyn7Lqfb9nI3YX%2BreQ9ThS3pcWhcvazMBWSE%2FbgaQ%2FF8P8AJlrrDvunvmryB9buFvzAtnrkQD84z%2B6mKHkGRtu%2F82HWnO%2BoadLpU5aC4ZeUL9Gil%2B0jKR0aOYZOLO80jzho0d3Zv6kMn2WFPUhlGxVh%2By6H7WQWaO80rV1hvwBMjcfUAoskTbLIP%2BNsIr5oSu10tfOt7Ikt4th5ijQC6hkSsdz6fwfWIiCCr7UmTj%2Fl5JG%2FKTSdTjg%2FxDILh4UCUgHD7P8Axafj45GPMdkYtRkvbWf6jewEXNhcr9ppT9pEVatJWnxJx%2BznRPIfmz%2FFujfW5Y%2FSvLZ%2Fq94gBCmQCvNQfiVX%2B1xb7OA2NleaX%2Fkr%2FA19Jb2rO%2BlXbc7UvuFbuvL%2Bb%2FiWC7M7HOuarpdrq9jLY3i8o5Bse6t2df8AKXOP6jbaj5dvpLK5USBd0fcB07Ov8cMT3qkP5l6trkWp2kNi8sVr6CShoyVDyH7RJHXj8OQpfNfmCN1%2BsXcsnBeCrOfUAUdFHP4uP052ny%2Fq2g6veR6T5hgX94Almsp%2Fdsw%2F3W%2F%2BU37GSHUfyp8n6hUpavZse8DkD%2FgH5p%2BGCXNeTwM%2BbzeWxtr%2B3SVHFGMZKMP8oBqr%2FwANh9qv5gpr3kVfLV9zk1O3uIjFOwrzt4w1GZhUeqn2P8rJD5i%2FJJrK2lvNKufrSx%2FEYCnCXiOtCpKPx%2F1M503lq7tpDxDNUUUGlPwyJtWQflfpMWseaFtLgssZtZnDJ1VhxCn3651e38qanp%2Bs2ZX95brKG%2BtR7UVfio6%2Fs16YS%2Fk3pGmWxvNR%2BspJqfEW7WnR4kryLFW3PqN%2FL%2FLnW8I2UsW8x%2BVo71n1LT0CXp3mjGwlp3%2F4yD%2Fhsgl9oyXUaxX1u8LoCY2dStV%2FaG%2Fgc7JTEbiztrr0zcRrIYmDxlhWjDJCXQoeVeW9Rv8Ay1PKL25kuNO9ELEo%2BIjgaoKk%2FAqJyzqtrdQXkCXNs3qQyqGjcdCCK7ZFPMPl1Yg95aLWA7zQjfjXqw%2FycB6JrNzY3BW7uAbARKgUqSQVIC8KfCq8K8vhxq9wlntK4DvtJ07UgBfW6TU6FhvTwqN8FRyRyoHiYOjCoZTUEHH5FDC9Y%2FL6wuSZ9Ic2M3X0tzEfo6p%2FscL9B%2FLy8XVJNS8y3Yuo41aKz0%2BEkQrGwoWlO3N2zomVTDZV4l5n8s3Plq94oS%2BnTk%2FVZj27%2Bk5%2FnX%2FhsMNI893Wj2ht2thNVSEp8PGSlBK%2F%2BSf286nqOnWmqWcllexiSGQbg9QezKf2WGco13yhdaVMUT95A39zL0DDwPg%2BIZc3pPlzU5dStGa4IaeNgGZaAFWHJWoMOc5v5E1B7Wf6tcAoD%2B5PLb3jP0fYzo4xIYrZIo5UaOVQ6MKMrCoIPtidrZ29lAttaoI4UrxQdBU1OL5sCsO8%2FwD5e6V5708RXNINQgB%2Bp3oFStf2JAKc4m%2F4XOIWX5R%2FmVoOpummwIySAxNMssZhZG2q3Mqen80eeoKZqYq8l8h%2Fk%2FceW9Qt9Z1DU3W4gPJLS0JCEGvwTytT1U3%2Bx6arnWs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F1%2B%2F5s2bFXZs2bFXZs2bFXZs2bFXUzZs2KuzZs2KoXUjILG49E0k4EKetK43TYkhsoQp5EqGZvEnvgp1DKVbdW2I9jhDdaDqT%2FuLLWJbOzP8AulEVnAPVVkb4hiqB8wWUXmS%2BXTYWQi3ikZphuUlqOKmn7P8ANnOtRsiPW0TWUKqD9ofajYfZkQ%2B3%2FDLnYtH0Wy0W3aC0BJc8pJHPJ2J8ThV5u8tJrdoZrdQL6EH0z0Lj%2BQ%2F8a4bTbxXQPMGq%2Fl9r7mSs9jcUN3Cv2Zo%2B1xD4Sp%2FzZnaNYhtfNmgRaho7JcMwE1pKCASP246%2Fsn%2BZf58gsugWWtaPFp1xRL21qFkFCyP3U96YR%2BUfMt7%2BX%2BqvpmqEtpUsnGeOtQjE0FxD%2FwAzUxOy%2B5kHmGCOa1s7%2BVeEsFYpHI3SvwyBvDi647RtH1by3qEXmPT5VlgnWmo2C1Amj6rIrH4RMnVcCz3FvJ5x1pIqX2h30cVy3ouv2nQEeka9XZW5ZJNPult7dbZpTLasP9HlYUalP7uVf2JlH2v%2BEw891ZzY3tvqNtHdWz84pBUHoQe6sP2WX9rC7zJoEOu2Xp7LdRVa3lPY90b%2FACHwi0IahBqtdOo9hKf9LjY0Vf8ALU%2F78ybZFD591jS3R3gnRo5Ymp4MjA9snHkr8weRi0LzJII7pQEtdQc0SYdFWQ%2Fsy%2F5X7eH%2FAJw8uLqMDX9qg%2BtRL%2B8Uf7sQf8bLnJbuyjcNFKnND1U%2Fw8Dh5pfQo3Gc487eVFhMmq2KfuHNbmNR%2Fdsf92KP5G%2Fa%2FlyPeXPM%2Fmfy2Y4pmk1PRqfBBKpNwiDr6E3%2B7OH8j51%2B1ubXU7JLiKklvcJWjDqrDdWH%2FEsV5PntoFiuUuQOFxFvHKpKsPpWnLJZ5b8w65p1xEguXuIp3q8d07P1%2FZVm%2Bx%2Fkccbr%2Bhm2125sbPggX95AZq8FRhy3p%2FL8Srk181aDHqempqtvRLu2hDngPheMDmV%2F2P2ozjsrKLW5ju4Eni%2By4rQ9Qe4OL5D%2FACp5hgu7iOwkkUXFxD6yx9DWM8HNMmGJFFDRFRQioPUHIhrmgrAXubdK2z19WOleFepH%2BRkwymCkUIqD1BxBpWIaNq09o31WURrZoiem32ePEheCIB8VV%2BLJcGB3BBHtka1LQXVne0XnC9eUQ%2B0tevHxXA2lxazHfW620rx2atW4gkSqFOPCgdvjDClVwkA7hWYZswzZFXYnNBFPG0UyCSNvtKwqDimbFWE6voE1hIbu0Be3BrQVLIP8rxXJRo92b3T4Z2%2B1Ti3zG1cGkAih3B7ZSRpGvCNQqjsooN8NquzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Dv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxViPmbyo91cfpvQiINVT%2B%2Bh6RXSD9iXwl%2F33N%2FwWQzW9AXXo%2FXaFg7L%2B%2BhK0mjdBuafzACjrnYcDTWNrPKk8kYMsZDI42NR40w3tRW3z1p2l6ho%2BotLAfXVE5OgDVljA%2BLj%2FxZGv7OS2BbeaRbscpI5YuLQq3ETIfiQcv2ZVP90%2F8AsMk3mi1FjqBnt0CpcIHIUU%2BNT8RFP5tuWRDTFEulwPaP9YjVSfVjBIFGPM%2F7BvHCOSXqXlu6trnTI1t7Y2Zj%2BGW1YhmQ%2BJcfb5fzYcZBPKl1I7GRXHKGi3AY%2FajPRvdsnQNd8BFFDjnL%2FPWhJaXguIKRw33KngklPi%2F2Pxcs6jkY892Ul3onqwoZJLWRZeAHIlT8D7fJsAVBaLpUeueS9PtriYPcWykQXkfZ4yVDD%2FJP2WwX5X1NJ2NrQIGXkoXpzQ8JP65GPJfmOTR5G0zXLmGCzlYmzhZPSeE05ceP7UTjfliOhagkWs3CW0gkUXbPZjoZVkY8kQHr8OSHUJZD540vmYtVRBIAvoTo260JrGzU7cvhzaJ5jgm0t9O1u7giu%2FRdY%2BJ48oyCg%2Fd9QyfZ%2FwArJhPBFcwvbzqHikBV0PQg9sgt%2FwCSLpJy9pxniPRiQsgH8rfzYB5ql%2Fkq1hXU4ZigNwjsizftemU3Uf5Jzp%2BRXyloF%2Fpj3FzqYiR3YraxREsyx%2FzTOaL6j%2Fyp8CZKsSUOzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Hv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVg35iPrMEFld6XbR3CRM%2Fro1fUIIHwx%2Fs4U%2FlrqOkz3c1lYQmENFJI8DCgRzIDIg5b1JavTOiajZLf2cls1KsKofBh9k5ydpJPL%2FmzT9ZcBIWY2t5sFoD8Ls5C1%2FwCDkyQ5KyjWdLGi6ml3brSyvKxuo6I58P8AJPVclOiXZurPi5rLAfTf5fsn7sE3drBqFq9vLR4pV2I7dwy%2B%2BF2j6Xf2F5dS3EqNbyhViVa8iV%2Fbfag6%2FZXBdj3KnWURXLzYFYV5n8n3us6ut7btEITGsbc6hl418B8WFd75Ym8svHqVqPrQiCsJSPiikH2jQfsNnScayhwVYBlOxB3BGG1Sby7rf6YgZ5KCZftIBSmHeFllodlp95LeWqlDMKGIfYB7kDDPE%2BSuzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F%2F0u%2F5s2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZFfM%2BipLW%2BSJZAf71CtaMPsygf8SyVZRAIIO4PbCDW6pP5YvDd6Wiu1Zbc%2Bi%2B9Tt9mv%2Bxw5xG3tbe1Vlt41jDHkwUUqfE4tgPNXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Pv%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV%2F%2FZ" ); + background-position:center center; + border:1px solid #00aa00; + } + + .yui-skin-sam .yui-h-slider { + background: url(bg-h.gif) no-repeat 5px 0; + height: 28px; + width: 228px; + } + + """ + + output = """.yui3-skin-night .yui3-dial-ring-vml,.yui3-skin-night .yui3-dial-center-button-vml,.yui3-skin-night .yui3-dial-marker v\:oval.yui3-dial-marker-max-min,.yui3-skin-night v\:oval.yui3-dial-marker-max-min,.yui3-skin-night .yui3-dial-marker-vml,.yui3-skin-night .yui3-dial-handle-vml{background:0;opacity:1}div.base64-doublequotes{width:100px;height:100px;background-image:url("data:image/jpeg;base64,%2F9j%2F4AAQSkZJRgABAgEAZABkAAD%2F4RfJRXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAABAAAAagEoAAMAAAABAAIAAAExAAIAAAAeAAAAcgEyAAIAAAAUAAAAkIdpAAQAAAABAAAApAAAANAAD0JAAAAnEAAPQkAAACcQQWRvYmUgUGhvdG9zaG9wIENTMiBNYWNpbnRvc2gAMjAwODowNzoxOSAxNDo1ODowNQAAA6ABAAMAAAAB%2F%2F8AAKACAAQAAAABAAABwqADAAQAAAABAAABRQAAAAAAAAAGAQMAAwAAAAEABgAAARoABQAAAAEAAAEeARsABQAAAAEAAAEmASgAAwAAAAEAAgAAAgEABAAAAAEAAAEuAgIABAAAAAEAABaTAAAAAAAAAEgAAAABAAAASAAAAAH%2F2P%2FgABBKRklGAAECAABIAEgAAP%2FtAAxBZG9iZV9DTQAC%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQADAgICAkIDAkJDBELCgsRFQ8MDA8VGBMTFRMTGBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAENCwsNDg0QDg4QFA4ODhQUDg4ODhQRDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgAdACgAwEiAAIRAQMRAf%2FdAAQACv%2FEAT8AAAEFAQEBAQEBAAAAAAAAAAMAAQIEBQYHCAkKCwEAAQUBAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAABBAEDAgQCBQcGCAUDDDMBAAIRAwQhEjEFQVFhEyJxgTIGFJGhsUIjJBVSwWIzNHKC0UMHJZJT8OHxY3M1FqKygyZEk1RkRcKjdDYX0lXiZfKzhMPTdePzRieUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm9jdHV2d3h5ent8fX5%2FcRAAICAQIEBAMEBQYHBwYFNQEAAhEDITESBEFRYXEiEwUygZEUobFCI8FS0fAzJGLhcoKSQ1MVY3M08SUGFqKygwcmNcLSRJNUoxdkRVU2dGXi8rOEw9N14%2FNGlKSFtJXE1OT0pbXF1eX1VmZ2hpamtsbW5vYnN0dXZ3eHl6e3x%2F%2FaAAwDAQACEQMRAD8A9VSSSSUpJJJJSkkkklKSSSSUs4uDSWiXRoOJKFi3Ovx2Wvbse4e5gMwRo5soypdOyN9mTSRHpWvj4Oc4%2FwDVf9WkpupJJJKUkkkkpSSSSSlJJJJKUkkkkp%2F%2F0PVUklCqxttbbG%2FReA4fApKZpJJJKUo2WNrrdY8w1gLnHyAlVOsWvowXZDZ%2FQOZa8D9xrmm3%2FNr3PUswm%2FpmQGamyl4bHiWuhJTYqsFtTLBw9od94U1Q6FkfaOk4tszuqYfwV9JSll1%2FoOt2sOguDXgeTm7f%2FPmN%2FwCCLUWZ1VhrysbKA%2Bjua7zgtuZ%2F1FqIUXTSQcvKZi47sh7XOY0tBDYJhzms3akfR3blVd13prMtuI%2Bwse%2F6L3Ahm7n0%2FU%2Bi2zafzkFOgq9%2Bdj0ZFOM4k3ZDtrGNEnhzt7v3WbWPU8bJpyavVpduZuc2eNWONb2%2F2XtXOY2VU7rA6hnO9RuJS%2Br1hO1lguGJbuYz2extvv3fzLPU%2FwCESU9QkkkkpSSSSSlJJJJKf%2F%2FR9VWf0iwiu3Ff9LGsewT%2B6D7P%2FAyxy0FmunH6xPDMpgd%2FbZFT%2FwDoOx%2F%2B20Qp0lC02CtxqALwDtB4J8FNJBTVovx%2Bo4jvbLLGlltTuRMssrf%2FANJiz%2BhZDqMO%2FEyCX2dOc6t%2Bkuc1gmt%2B3%2FhaPTeoZ5s6R1IZrBGDlkNvI%2BjXcYYyx%2F7tWV9B%2FwDw%2FwDwlyD1LqWH0jq2N1O55rxOogY1rwC4C9uuP6mzdt9Wl1v6T%2Bb%2FAFdFS%2F1Myq%2F2VThusa%2B2tst2k6sPuY73Brm%2Fo3Vv2fy%2F3F0S4TAzq%2Bm%2FWDquKGnbhWOya62DUUlrcmyulktZ6f2ey19Wz8%2F2Ls2Z%2BFYx1jL6yxgBe%2FcIAI3CXfNIqZX3ursqqrZ6j7TqJgNY2PUsPP0dypfWJ%2Fo9NOT2xrarHTxs3tZkf%2By77VR6v9bejdNsbkb3Zbqm2sfVjgOOhrdZFljqsffXs%2FmvV9V%2F%2BDXHdZ%2BuvU8zA6i%2Bj02Y1zawaZLt2LY21tltb7XOb6j6%2FwDR1V%2FzV36v6rPVYFU%2BiCs5PS7cd43O2WUOB77d1X%2FT27lwvVnNyKK8d15q6hij207o9dg3DHdW9m9zLv8AQ%2F4Wu71K%2FTtr9aqzJ%2BuHV8tmR0zNoyLvsXU8Nr7cdtjmtN1J9K%2B2podsbk1%2Fo%2F8A0YsnG6te%2BnLZbdXXY6pttADNwucwmhv2cWbv0tnr%2FpKnfpKbKf8AtsqD1DvrjnfV70cKlldgzgbqjf7QHB3p2sG0sb6lv0%2FfZ6TEcZg630xz2vezK6jVbfhVtf6biQ%2BxltmNY7a2z9Js3Vu%2FwayvrHh1%2BhhdKv3XubjBhY9wDbHtPpOdXkOH6t1Dd%2FRsl%2F6C%2FwDomV7Fg9Wc%2FF%2Bq3RL6biben5WVjh4lljdxbexltf06La9vvp%2FM%2FwAFZ%2FhEuqn0r6lfXM9WYzpvVoo6wxvcbBcGj9Ltr09LLoj9bxf%2BvU%2Fof5mz9avrJd0%2B2jBwHAZTrcd17iA7bVZa2nY0H%2FCXw9v%2FAAdf%2FWl5zidQZ9ZcY3l3oddwg2z1mHZ6zWfQv3t2%2Bnk0%2FwCn%2FwDRf8xf6Df1DqvXGZ%2Be5tzsi7Gd6lm2kPsqNbqqOG1%2F4Cuxnpt%2FS7%2F9I9ALjR1Gh7f96%2BuJJpA1KZj2WNDmODmnhwMhJaySSSSU%2FwD%2F0vVVn9airFGYSGjEcLXuOkV%2Fzd5J%2FdZU%2FwBX%2Fra0EHM9D7Jf9pE4%2Fpv9YHX2bT6mn9RIKcE9U6lgXnJDv2l0jN%2FS497S39C530sOyxn%2BBc%2F%2Bh5P83%2F2kyfS%2FR5NlPrv16uwczExsPHFozLBRWHz6rriWM9Ouv6Hs9T3vseue6B9buoZF5xBU84BDC%2BtoZYW1PczG%2B02Gxm71Gb2XP2fod7P5mtct167q%2FT87Gdfk%2FbscWHI6V1NrQNGu3O2NZ6fp5FN9dTcrH3epR6f9RKwRYXzhKEuGYo%2FxfSMvM6lZlF%2FVfSobU11Q9MGyqLfa%2BrP9PJsr9Kz09n6bGq%2F7dWN1z9VxC3KfZT0%2B%2BPUyGg5NIaPdU7ezZe7KxPR%2FR5Gyr1sX9Db6%2FwDguZx%2Bv352be4WNwft%2B9tb2n21mza%2F07Xn2%2Fp7qmVep6dbP0vqfolqZD7mY91eDmXh9pBAZDGjT9awvb%2Bkp3W%2Fp2Mdkfpav1b%2BXUb0WulTeP23h5Di3JL3UYWTYzlzbKn1sc5rN7baM3FZ9pw8xrvSvr%2FQf0im6uvH6vY7G6hfS2mx1WFYaq7bnk%2Bo2r0rKPTa7bustxX20%2Bz%2Blf4RW%2FqPXeM403s%2FQ9LpdbQ4v97Wl4DcW1nud6PqfrH2e9nszMavNx%2Fs9%2F2hA6i5r77HZLH24Tn1W4%2BUwj2Pva3Jdj5bfzaHb8n7Fk%2F4P9YxP0tX8wulqaWfb1CzZ6GaM30622WDD%2FSV1mp3sfY6tjWOsY1tf8776a%2FTQ7s27qFNVIrqwAfUsbZRXt9S0N9Ky1tX83%2Bgre%2F1sej%2FAE1lzKltdDtbi9ZJsqqbTaBXdS0TbW68PycUxNj8qrIbR%2FpPU%2FSb6%2FTyv6VWf0qvOrzmsYasnGzHmk7gx1Za6K2XR7sd%2B5v6K79JXi3bPU%2FV8i71RRU5%2FWaLMv6kYj7SGX9L6g%2BqK%2Fosrymeux1cf4B17fYsrpuPmNxftz63Pcwl9DWDQ2sIqZkNJ%2FNZe6luR6f%2FAAX%2FAAS1f2pXX0vq%2BJln7LfkVVta3ZAfl4929myn%2FA76rLq86r%2Bbo%2F4qyhVruoZfSek4ldmM12Pe227D9XQBtvptu3%2B7d6Vr66Xf2PU%2FwiSnsuqnDyLMfFtMZORj%2BrXSPeHFseo2l7g31La%2FV%2Bh%2F2oq%2FwfqfpFznVcd9uDZivaxzDZXa1zztZaXB1LW%2Bv%2Fgb%2FZVXi5tv83%2FRsj9AsLqfXv2j0%2FpXqPFedgNurfa10F3uqsx8lv8Ao3Obvrf%2FAMT6n%2BFQquq59%2FURec1zb7CHXMc8htmw7n47v8BtucPUb6v6P9Ld69iNoS9H6Vk0Zzcpj3tqqeW1AAtuc%2F8Am7Meyhv6WvZ%2FNZX%2FAIEugzuk14eLiN69fbi49u6wgBjyWNb6dP6IOa52V6vt247P0dD%2FAFP0ajjdc6kBeekYQd1DIs2tsZVZbdVXt9tYqsG9%2B3%2FTPZ%2Bl%2FwBCsajpXV%2BtX2Zwo%2Fal7Wuvv9S0Mea2%2FT2%2BvZ7m1e1j6GV76PZ%2FpK0NE7avpH1Z69ndU6Ti4mP6mVZktBdbkP3uqbWTRkjKuYGb9t1e6v2776rP9Iuyw8VuJjtoaS8tkueeXOcdz3%2F2nLyb6vdZv6DjDKwmVt3lzbsZz3VsZcHluTgX499hfRYxuzJwn%2FznsyKrLLv8H2vT%2Fr9hODK%2BsM%2FZuSWkvrtFjC1wLmvr%2FWKqW%2Fm%2BzbZ%2BkRR4vVpKn03q%2FTeq47Mnp%2BQy%2Bt7WvG0%2B4Bw3N9Ss%2FpK%2F%2BuNVxBT%2FAP%2FT9VTOa17SxwDmuBDmnUEHsU6SSnxf6x9CzPqt1ECix7GMeben5Q0caneyylzvouto3MruZ%2Fhav0np%2FpFUryaMjFvrzps6dkPbZmUsHvx7zFber4f7rbLP6Wz%2FAEln%2BivYvY%2Bu9L6f1Xpl2H1GG0OG71SQ01uH0b2Pd9B7P%2FUb%2FwBGvH8vpF3T7%2FQpcH5NIea7az6leXSd2%2BzHjdW70qHbMnE%2FwlSZrE2NuzfjKHMY%2BGfpzR2n%2FL9H95wM%2FByOk5hxsgiyuwb6rm613VH6N1X%2FAKMrW10jqbrmHDyHl8MhjY3G6sHd6PO318dv6eiz07fW9P0v%2BOHiPxc6n9j552Ydh3YmVy7FtP5%2Bvvfiu%2Fw%2F%2FB%2Fpf5zehYv1a6pVk5WLnh%2BJZhML6rmiWvsBY6t1Fn0n1NY77Tvp%2FSf8WnijqNWnOEscjGQ4ZB7Oig0fV7Lf01rr8rqR2tez3XPbBx67XsYzayxjbMl9m38%2F9J%2Bl%2FS%2BpzrvUxMWqvqNVuJksx3402zW5oosbdScig725OHd9qY6v%2Bc2ZOLT6Nn88y3e6X1uyk9NexznYd%2BPdi2Mc1lTxl4ztznWu3bKfWZZXbs9T06f0ypdY6J1Hr%2FVH9Wrtpsx6mMqFDS%2F1WisF30XNa5%2B577bf0eyz%2FRV%2BqnHwW1RotDF6xay49Tviks9OHNBAc9g2sfc1m7Zu2%2FoG%2FQxv3PST9Lw2dTblM%2B1W1sdDixoM2vu3f0936Ot3pOZsurp%2FQ5H%2Bj%2FwaJj4GXf052ZjepRjbmOrywNzNjHulh9Meg5zN%2B1lrn%2F4R9fp%2FpFYxcHD6ey6nItdSGAOtdlFwhlh%2FRuHpN3Ppbv8A5xnqemgFOZ1%2FDyMW77T1DIOQy4vra91bqC00en%2BiYx%2F06Nrqnev%2FAIf%2FAEj1WxaG2sxKOo9Mu6iK32UYeOLHML3vLrNrrmu3%2Blv%2FAPRi1cvpl1PUqW3Nfh5VJ34%2BNmO9XEurHu%2FV7t1jGMsYf0noP9n%2FAANqv9Q6W84w630hn2cuFd1jcYGysjS6qzLxmiu3Fyadv896NX%2Bl%2B2eqhSSBQ1u%2Fwed6z9X%2BrtFJH1dZj1agENsqZuIB9EPsvbkW7Wj6eS%2F6fq%2Bj%2BjWOcbNw8hnqdNxq3tizZYXOa4Ty7fkWbmNd%2FObfof4RdpT1fqHUs1rc%2FLfktyRU2vGa4bSHNePtFL3el6VmLlVejZ7f0m%2F1cj%2BZ%2B0WbvSfqn0%2Fq1eTjdRIdlVspsqcxwBqc8PbfW%2BrX376%2FTtf%2FAKH0bcS6v2WJXqulj4YCXEDZqurxn1dxXdRosuw78hnXcR5dSWu22OrY2bqsdn03ZFTt932Oz2ZWL6lVLPWoXS19Hzup9Pq%2BsfTMYty8kn7ZRXDCy5hdVb1DEre%2F9NU79LvxHbLclj7Kf5rIspXE5mPm9J6q%2FLqa82YrmvfVJHqUsd%2BgyqLatln0a%2F03pP8AWxLmerV%2B5j%2Bz%2FU7qvR%2Bp9Cot6QNlLJbZQ5xdZXb9O1l73S99jnu3%2Bq7%2Be%2FnEmN57qv1X6b9c%2Fq99oxWNx%2BuYxcyx7g9rjc2PXxM5t36x6djmt9H1v02L%2Bi9L%2FCVWcz9VPr51npPWX9H%2Bsxuy8bItFNoyDvtot9tIcN%2F06He316P%2FAEKo%2FwAJ9p9hbXW17ntaA98b3AQXQIbuP5y43%2FGB9Q2dfp%2FaXTWtZ1iloEGGjIY3%2FAPe7%2BayG%2F8AafJ%2F6zb%2Bi%2FSUJT2TK66xtraGAAABoAEAbW8fyVJecfUbqP8AjHPV31dXxMjJ6c%2Fay27LaMd1JaNgsoDxX9o9rf0zaW2%2Br%2FPep%2FpPR0lP%2F9T1VJJJJSDNL%2Fsr9geXGABW1rnakD6F36P%2BsuAwumV%2FWXNrxHvcymqh2S7IqLnRa%2B7Jfj2UPcGtqdvyfVspZ7K%2FQ%2By1%2FoaV6Ffj05FZqvYLKyQS13Eg7m%2F9JKimvHprorEV1NDGDwDRtakkEg2NCNnxDqdGb0Lru3qFDHX41rLTWGxVeyR7626tdVmNa%2F1P3LPWr9i2cmzrpY5tdB6gzpL7Kq7WGXPqqf6D8HIc1gZ9upo%2F7T%2FzmVX%2FADXqr0brOF0%2B4135bWVFgc05kD1WMP06ari17q25P81Z%2B%2FX%2Bi%2BnauTysfqnQeoM6h06l91eW2H4jwf1uhgc70Lq3DdV1XGxm78d7%2FwCfo%2FQXfpK7GIRFX4s2fN7vAeGpRjwk93IyOj5VDnN6U5%2FUMa%2FflYlLGuLm%2BqxnpfadrG1tdk302Ufav%2Bs2en%2FNrQ%2Brd9%2BNlNdfS%2FDuDXMy8e7R1bmjfJ%2FkNhl9Nv59KtOeLbMDO6FktJte%2B%2FCstLh%2Bhcaxm4mY5oc5tLMr08XK3st9H7TRd%2FSqPWW11fA%2Fa%2BOOpdOc6jqeMNllYjcdvudjWt9zfVr3b8d%2F%2Fom%2F1E%2B2F5jpf1u6Dj9RyLKcqu3peaf1rF1aGh%2Bhym0XMr91TfZksr%2Fncb%2FSWY9bFTwftfUOpdQwRZvxsDLOLh2uBexrLH21YzLLAHv9K3ZX%2FwAH%2FOLUw%2BnfU%2F6xVDB6lg4%2FTutEFrLaG%2BgLXD%2FC42zY2x%2F%2Blw7f01f8ur9On%2BroyPqllZfThhMsqBZblWhxFjahFLLWE%2B27HZv3sbsr9H9N63%2FBjW1L9J9PDa%2F6udcx%2FU6Ta7bSH6uxLT7W0Oc0%2Fo6HT6mFl1fzX%2BnV9n1R6v07bbhZv2za4w149K0Vk6enkMeWPu2fzn8xVb%2Fwf0F1GVh4mdSW3MD2vbAfA3AH9xyoYmRZ0o%2FY894%2BztBNGS7QbR%2Ba8n6Oz%2FwH%2FttLyRbyPUcXMqzznY2Nfh9TrbZbkkVyxzQBvzm%2Bm04vr7f0d%2Fo2frX%2BD%2FTV2rc%2BrmH0bqHT8fcW%2Fb2Ned1FrmWsaXOZLfRLH01W%2Bpu9L%2BaXUiCJHBWGegM6d1IdU6WzbJi%2FFbAaWuPvdV%2FV%2FnPTStN6U4%2FWfqPk5jhRj2BorDrMXLOjqnge2m1rNrnV3e1lnpf8b%2FO1Urm%2FqLlZH1a%2BseT03IwLa7s97K8nFrY%2Bx9fp%2Bp6OfjupZ6VvT3%2BrY3I%2F0P6Oyn9F%2BjXrKSCFJJJJKUkkkkp%2F%2F9X1VJJJJSkkkklKQM3Dpzcd2PbuAdBa9hLXscNWWVPb9CxjkdJJTxHVfq4Ol5bc%2Bq55dlvLLq2Dax1lprY7IFbP5i63b77Kf9H%2Bk%2FQrpPq%2FcLMAVlgrupcWXM4duBj9I0y7f%2F1f00vrB0hnVunPo%2BjkMmzFtGjq7QC1tlb9Njvd%2FqxS6dWMj0upH9FfbXsyqwIabGwx30%2Ff%2BifW9n9RHopwvrBh4eF1V%2BXkU%2FqmZQQLSB6deYxwux7HAfpGvs9L%2FB%2F8J6ivWVZ2dj4vV8FgfbkY%2Fo5WO8hosrdr3%2Bi6t7nrefWx8b2h20hzZEwR9Fw%2FlIOLg4%2BI652OCxt7vUfWD7Q4%2FScxv5m%2F89K1IujYj8PpWLjWDa%2BusB7SZIcfc5u6XfnFW31seIe0OEzBE6%2FNSSQUpJJJJSkkkklKSSSSUpJJJJT%2FAP%2FW9VSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2F%2FZ%2F%2B01IlBob3Rvc2hvcCAzLjAAOEJJTQQlAAAAAAAQAAAAAAAAAAAAAAAAAAAAADhCSU0D6gAAAAAYEDw%2FeG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04Ij8%2BCjwhRE9DVFlQRSBwbGlzdCBQVUJMSUMgIi0vL0FwcGxlLy9EVEQgUExJU1QgMS4wLy9FTiIgImh0dHA6Ly93d3cuYXBwbGUuY29tL0RURHMvUHJvcGVydHlMaXN0LTEuMC5kdGQiPgo8cGxpc3QgdmVyc2lvbj0iMS4wIj4KPGRpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNSG9yaXpvbnRhbFJlczwva2V5PgoJPGRpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJPGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1Ib3Jpem9udGFsUmVzPC9rZXk%2BCgkJCQk8cmVhbD43MjwvcmVhbD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTU9yaWVudGF0aW9uPC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk%2BCgkJCTxkaWN0PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTU9yaWVudGF0aW9uPC9rZXk%2BCgkJCQk8aW50ZWdlcj4xPC9pbnRlZ2VyPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNU2NhbGluZzwva2V5PgoJPGRpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJPGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1TY2FsaW5nPC9rZXk%2BCgkJCQk8cmVhbD4xPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxSZXM8L2tleT4KCTxkaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk%2BCgkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCTxhcnJheT4KCQkJPGRpY3Q%2BCgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxSZXM8L2tleT4KCQkJCTxyZWFsPjcyPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxTY2FsaW5nPC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk%2BCgkJCTxkaWN0PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTVZlcnRpY2FsU2NhbGluZzwva2V5PgoJCQkJPHJlYWw%2BMTwvcmVhbD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuc3ViVGlja2V0LnBhcGVyX2luZm9fdGlja2V0PC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PlBNUFBEUGFwZXJDb2RlTmFtZTwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PlBNUFBEUGFwZXJDb2RlTmFtZTwva2V5PgoJCQkJCTxzdHJpbmc%2BTGV0dGVyPC9zdHJpbmc%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BUE1UaW9nYVBhcGVyTmFtZTwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PlBNVGlvZ2FQYXBlck5hbWU8L2tleT4KCQkJCQk8c3RyaW5nPm5hLWxldHRlcjwvc3RyaW5nPgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BMC4wPC9yZWFsPgoJCQkJCQk8cmVhbD4wLjA8L3JlYWw%2BCgkJCQkJCTxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTc2PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1BZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQkJCQk8YXJyYXk%2BCgkJCQkJCTxyZWFsPi0xODwvcmVhbD4KCQkJCQkJPHJlYWw%2BLTE4PC9yZWFsPgoJCQkJCQk8cmVhbD43NzQ8L3JlYWw%2BCgkJCQkJCTxyZWFsPjU5NDwvcmVhbD4KCQkJCQk8L2FycmF5PgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYXBlckluZm8uUE1QYXBlck5hbWU8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNUGFwZXJOYW1lPC9rZXk%2BCgkJCQkJPHN0cmluZz5uYS1sZXR0ZXI8L3N0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVGbGFnPC9rZXk%2BCgkJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJCTwvZGljdD4KCQkJPC9hcnJheT4KCQk8L2RpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNVW5hZGp1c3RlZFBhZ2VSZWN0PC9rZXk%2BCgkJPGRpY3Q%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk%2BCgkJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BMC4wPC9yZWFsPgoJCQkJCQk8cmVhbD4wLjA8L3JlYWw%2BCgkJCQkJCTxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTc2PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNVW5hZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BLTE4PC9yZWFsPgoJCQkJCQk8cmVhbD4tMTg8L3JlYWw%2BCgkJCQkJCTxyZWFsPjc3NDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTk0PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5wcGQuUE1QYXBlck5hbWU8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLnBwZC5QTVBhcGVyTmFtZTwva2V5PgoJCQkJCTxzdHJpbmc%2BVVMgTGV0dGVyPC9zdHJpbmc%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5BUElWZXJzaW9uPC9rZXk%2BCgkJPHN0cmluZz4wMC4yMDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC50eXBlPC9rZXk%2BCgkJPHN0cmluZz5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvVGlja2V0PC9zdHJpbmc%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuQVBJVmVyc2lvbjwva2V5PgoJPHN0cmluZz4wMC4yMDwvc3RyaW5nPgoJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnR5cGU8L2tleT4KCTxzdHJpbmc%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXRUaWNrZXQ8L3N0cmluZz4KPC9kaWN0Pgo8L3BsaXN0Pgo4QklNA%2BkAAAAAAHgAAwAAAEgASAAAAAAC3gJA%2F%2B7%2F7gMGAlIDZwUoA%2FwAAgAAAEgASAAAAAAC2AIoAAEAAABkAAAAAQADAwMAAAABf%2F8AAQABAAAAAAAAAAAAAAAAaAgAGQGQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4QklNA%2B0AAAAAABAAZAAAAAEAAQBkAAAAAQABOEJJTQQmAAAAAAAOAAAAAAAAAAAAAD%2BAAAA4QklNBA0AAAAAAAQAAAAeOEJJTQQZAAAAAAAEAAAAHjhCSU0D8wAAAAAACQAAAAAAAAAAAQA4QklNBAoAAAAAAAEAADhCSU0nEAAAAAAACgABAAAAAAAAAAE4QklNA%2FUAAAAAAEgAL2ZmAAEAbGZmAAYAAAAAAAEAL2ZmAAEAoZmaAAYAAAAAAAEAMgAAAAEAWgAAAAYAAAAAAAEANQAAAAEALQAAAAYAAAAAAAE4QklNA%2FgAAAAAAHAAAP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8D6AAAAAD%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FA%2BgAAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwPoAAAAAP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8D6AAAOEJJTQQIAAAAAAAQAAAAAQAAAkAAAAJAAAAAADhCSU0EHgAAAAAABAAAAAA4QklNBBoAAAAAA1UAAAAGAAAAAAAAAAAAAAFFAAABwgAAABAAcwB3AGkAcwBzAF8AYQByAG0AeQBfAGsAbgBpAGYAZQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABwgAAAUUAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAQAAAAAAAG51bGwAAAACAAAABmJvdW5kc09iamMAAAABAAAAAAAAUmN0MQAAAAQAAAAAVG9wIGxvbmcAAAAAAAAAAExlZnRsb25nAAAAAAAAAABCdG9tbG9uZwAAAUUAAAAAUmdodGxvbmcAAAHCAAAABnNsaWNlc1ZsTHMAAAABT2JqYwAAAAEAAAAAAAVzbGljZQAAABIAAAAHc2xpY2VJRGxvbmcAAAAAAAAAB2dyb3VwSURsb25nAAAAAAAAAAZvcmlnaW5lbnVtAAAADEVTbGljZU9yaWdpbgAAAA1hdXRvR2VuZXJhdGVkAAAAAFR5cGVlbnVtAAAACkVTbGljZVR5cGUAAAAASW1nIAAAAAZib3VuZHNPYmpjAAAAAQAAAAAAAFJjdDEAAAAEAAAAAFRvcCBsb25nAAAAAAAAAABMZWZ0bG9uZwAAAAAAAAAAQnRvbWxvbmcAAAFFAAAAAFJnaHRsb25nAAABwgAAAAN1cmxURVhUAAAAAQAAAAAAAG51bGxURVhUAAAAAQAAAAAAAE1zZ2VURVhUAAAAAQAAAAAABmFsdFRhZ1RFWFQAAAABAAAAAAAOY2VsbFRleHRJc0hUTUxib29sAQAAAAhjZWxsVGV4dFRFWFQAAAABAAAAAAAJaG9yekFsaWduZW51bQAAAA9FU2xpY2VIb3J6QWxpZ24AAAAHZGVmYXVsdAAAAAl2ZXJ0QWxpZ25lbnVtAAAAD0VTbGljZVZlcnRBbGlnbgAAAAdkZWZhdWx0AAAAC2JnQ29sb3JUeXBlZW51bQAAABFFU2xpY2VCR0NvbG9yVHlwZQAAAABOb25lAAAACXRvcE91dHNldGxvbmcAAAAAAAAACmxlZnRPdXRzZXRsb25nAAAAAAAAAAxib3R0b21PdXRzZXRsb25nAAAAAAAAAAtyaWdodE91dHNldGxvbmcAAAAAADhCSU0EKAAAAAAADAAAAAE%2F8AAAAAAAADhCSU0EEQAAAAAAAQEAOEJJTQQUAAAAAAAEAAAAAThCSU0EDAAAAAAWrwAAAAEAAACgAAAAdAAAAeAAANmAAAAWkwAYAAH%2F2P%2FgABBKRklGAAECAABIAEgAAP%2FtAAxBZG9iZV9DTQAC%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQADAgICAkIDAkJDBELCgsRFQ8MDA8VGBMTFRMTGBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAENCwsNDg0QDg4QFA4ODhQUDg4ODhQRDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgAdACgAwEiAAIRAQMRAf%2FdAAQACv%2FEAT8AAAEFAQEBAQEBAAAAAAAAAAMAAQIEBQYHCAkKCwEAAQUBAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAABBAEDAgQCBQcGCAUDDDMBAAIRAwQhEjEFQVFhEyJxgTIGFJGhsUIjJBVSwWIzNHKC0UMHJZJT8OHxY3M1FqKygyZEk1RkRcKjdDYX0lXiZfKzhMPTdePzRieUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm9jdHV2d3h5ent8fX5%2FcRAAICAQIEBAMEBQYHBwYFNQEAAhEDITESBEFRYXEiEwUygZEUobFCI8FS0fAzJGLhcoKSQ1MVY3M08SUGFqKygwcmNcLSRJNUoxdkRVU2dGXi8rOEw9N14%2FNGlKSFtJXE1OT0pbXF1eX1VmZ2hpamtsbW5vYnN0dXZ3eHl6e3x%2F%2FaAAwDAQACEQMRAD8A9VSSSSUpJJJJSkkkklKSSSSUs4uDSWiXRoOJKFi3Ovx2Wvbse4e5gMwRo5soypdOyN9mTSRHpWvj4Oc4%2FwDVf9WkpupJJJKUkkkkpSSSSSlJJJJKUkkkkp%2F%2F0PVUklCqxttbbG%2FReA4fApKZpJJJKUo2WNrrdY8w1gLnHyAlVOsWvowXZDZ%2FQOZa8D9xrmm3%2FNr3PUswm%2FpmQGamyl4bHiWuhJTYqsFtTLBw9od94U1Q6FkfaOk4tszuqYfwV9JSll1%2FoOt2sOguDXgeTm7f%2FPmN%2FwCCLUWZ1VhrysbKA%2Bjua7zgtuZ%2F1FqIUXTSQcvKZi47sh7XOY0tBDYJhzms3akfR3blVd13prMtuI%2Bwse%2F6L3Ahm7n0%2FU%2Bi2zafzkFOgq9%2Bdj0ZFOM4k3ZDtrGNEnhzt7v3WbWPU8bJpyavVpduZuc2eNWONb2%2F2XtXOY2VU7rA6hnO9RuJS%2Br1hO1lguGJbuYz2extvv3fzLPU%2FwCESU9QkkkkpSSSSSlJJJJKf%2F%2FR9VWf0iwiu3Ff9LGsewT%2B6D7P%2FAyxy0FmunH6xPDMpgd%2FbZFT%2FwDoOx%2F%2B20Qp0lC02CtxqALwDtB4J8FNJBTVovx%2Bo4jvbLLGlltTuRMssrf%2FANJiz%2BhZDqMO%2FEyCX2dOc6t%2Bkuc1gmt%2B3%2FhaPTeoZ5s6R1IZrBGDlkNvI%2BjXcYYyx%2F7tWV9B%2FwDw%2FwDwlyD1LqWH0jq2N1O55rxOogY1rwC4C9uuP6mzdt9Wl1v6T%2Bb%2FAFdFS%2F1Myq%2F2VThusa%2B2tst2k6sPuY73Brm%2Fo3Vv2fy%2F3F0S4TAzq%2Bm%2FWDquKGnbhWOya62DUUlrcmyulktZ6f2ey19Wz8%2F2Ls2Z%2BFYx1jL6yxgBe%2FcIAI3CXfNIqZX3ursqqrZ6j7TqJgNY2PUsPP0dypfWJ%2Fo9NOT2xrarHTxs3tZkf%2By77VR6v9bejdNsbkb3Zbqm2sfVjgOOhrdZFljqsffXs%2FmvV9V%2F%2BDXHdZ%2BuvU8zA6i%2Bj02Y1zawaZLt2LY21tltb7XOb6j6%2FwDR1V%2FzV36v6rPVYFU%2BiCs5PS7cd43O2WUOB77d1X%2FT27lwvVnNyKK8d15q6hij207o9dg3DHdW9m9zLv8AQ%2F4Wu71K%2FTtr9aqzJ%2BuHV8tmR0zNoyLvsXU8Nr7cdtjmtN1J9K%2B2podsbk1%2Fo%2F8A0YsnG6te%2BnLZbdXXY6pttADNwucwmhv2cWbv0tnr%2FpKnfpKbKf8AtsqD1DvrjnfV70cKlldgzgbqjf7QHB3p2sG0sb6lv0%2FfZ6TEcZg630xz2vezK6jVbfhVtf6biQ%2BxltmNY7a2z9Js3Vu%2FwayvrHh1%2BhhdKv3XubjBhY9wDbHtPpOdXkOH6t1Dd%2FRsl%2F6C%2FwDomV7Fg9Wc%2FF%2Bq3RL6biben5WVjh4lljdxbexltf06La9vvp%2FM%2FwAFZ%2FhEuqn0r6lfXM9WYzpvVoo6wxvcbBcGj9Ltr09LLoj9bxf%2BvU%2Fof5mz9avrJd0%2B2jBwHAZTrcd17iA7bVZa2nY0H%2FCXw9v%2FAAdf%2FWl5zidQZ9ZcY3l3oddwg2z1mHZ6zWfQv3t2%2Bnk0%2FwCn%2FwDRf8xf6Df1DqvXGZ%2Be5tzsi7Gd6lm2kPsqNbqqOG1%2F4Cuxnpt%2FS7%2F9I9ALjR1Gh7f96%2BuJJpA1KZj2WNDmODmnhwMhJaySSSSU%2FwD%2F0vVVn9airFGYSGjEcLXuOkV%2Fzd5J%2FdZU%2FwBX%2Fra0EHM9D7Jf9pE4%2Fpv9YHX2bT6mn9RIKcE9U6lgXnJDv2l0jN%2FS497S39C530sOyxn%2BBc%2F%2Bh5P83%2F2kyfS%2FR5NlPrv16uwczExsPHFozLBRWHz6rriWM9Ouv6Hs9T3vseue6B9buoZF5xBU84BDC%2BtoZYW1PczG%2B02Gxm71Gb2XP2fod7P5mtct167q%2FT87Gdfk%2FbscWHI6V1NrQNGu3O2NZ6fp5FN9dTcrH3epR6f9RKwRYXzhKEuGYo%2FxfSMvM6lZlF%2FVfSobU11Q9MGyqLfa%2BrP9PJsr9Kz09n6bGq%2F7dWN1z9VxC3KfZT0%2B%2BPUyGg5NIaPdU7ezZe7KxPR%2FR5Gyr1sX9Db6%2FwDguZx%2Bv352be4WNwft%2B9tb2n21mza%2F07Xn2%2Fp7qmVep6dbP0vqfolqZD7mY91eDmXh9pBAZDGjT9awvb%2Bkp3W%2Fp2Mdkfpav1b%2BXUb0WulTeP23h5Di3JL3UYWTYzlzbKn1sc5rN7baM3FZ9pw8xrvSvr%2FQf0im6uvH6vY7G6hfS2mx1WFYaq7bnk%2Bo2r0rKPTa7bustxX20%2Bz%2Blf4RW%2FqPXeM403s%2FQ9LpdbQ4v97Wl4DcW1nud6PqfrH2e9nszMavNx%2Fs9%2F2hA6i5r77HZLH24Tn1W4%2BUwj2Pva3Jdj5bfzaHb8n7Fk%2F4P9YxP0tX8wulqaWfb1CzZ6GaM30622WDD%2FSV1mp3sfY6tjWOsY1tf8776a%2FTQ7s27qFNVIrqwAfUsbZRXt9S0N9Ky1tX83%2Bgre%2F1sej%2FAE1lzKltdDtbi9ZJsqqbTaBXdS0TbW68PycUxNj8qrIbR%2FpPU%2FSb6%2FTyv6VWf0qvOrzmsYasnGzHmk7gx1Za6K2XR7sd%2B5v6K79JXi3bPU%2FV8i71RRU5%2FWaLMv6kYj7SGX9L6g%2BqK%2Fosrymeux1cf4B17fYsrpuPmNxftz63Pcwl9DWDQ2sIqZkNJ%2FNZe6luR6f%2FAAX%2FAAS1f2pXX0vq%2BJln7LfkVVta3ZAfl4929myn%2FA76rLq86r%2Bbo%2F4qyhVruoZfSek4ldmM12Pe227D9XQBtvptu3%2B7d6Vr66Xf2PU%2FwiSnsuqnDyLMfFtMZORj%2BrXSPeHFseo2l7g31La%2FV%2Bh%2F2oq%2FwfqfpFznVcd9uDZivaxzDZXa1zztZaXB1LW%2Bv%2Fgb%2FZVXi5tv83%2FRsj9AsLqfXv2j0%2FpXqPFedgNurfa10F3uqsx8lv8Ao3Obvrf%2FAMT6n%2BFQquq59%2FURec1zb7CHXMc8htmw7n47v8BtucPUb6v6P9Ld69iNoS9H6Vk0Zzcpj3tqqeW1AAtuc%2F8Am7Meyhv6WvZ%2FNZX%2FAIEugzuk14eLiN69fbi49u6wgBjyWNb6dP6IOa52V6vt247P0dD%2FAFP0ajjdc6kBeekYQd1DIs2tsZVZbdVXt9tYqsG9%2B3%2FTPZ%2Bl%2FwBCsajpXV%2BtX2Zwo%2Fal7Wuvv9S0Mea2%2FT2%2BvZ7m1e1j6GV76PZ%2FpK0NE7avpH1Z69ndU6Ti4mP6mVZktBdbkP3uqbWTRkjKuYGb9t1e6v2776rP9Iuyw8VuJjtoaS8tkueeXOcdz3%2F2nLyb6vdZv6DjDKwmVt3lzbsZz3VsZcHluTgX499hfRYxuzJwn%2FznsyKrLLv8H2vT%2Fr9hODK%2BsM%2FZuSWkvrtFjC1wLmvr%2FWKqW%2Fm%2BzbZ%2BkRR4vVpKn03q%2FTeq47Mnp%2BQy%2Bt7WvG0%2B4Bw3N9Ss%2FpK%2F%2BuNVxBT%2FAP%2FT9VTOa17SxwDmuBDmnUEHsU6SSnxf6x9CzPqt1ECix7GMeben5Q0caneyylzvouto3MruZ%2Fhav0np%2FpFUryaMjFvrzps6dkPbZmUsHvx7zFber4f7rbLP6Wz%2FAEln%2BivYvY%2Bu9L6f1Xpl2H1GG0OG71SQ01uH0b2Pd9B7P%2FUb%2FwBGvH8vpF3T7%2FQpcH5NIea7az6leXSd2%2BzHjdW70qHbMnE%2FwlSZrE2NuzfjKHMY%2BGfpzR2n%2FL9H95wM%2FByOk5hxsgiyuwb6rm613VH6N1X%2FAKMrW10jqbrmHDyHl8MhjY3G6sHd6PO318dv6eiz07fW9P0v%2BOHiPxc6n9j552Ydh3YmVy7FtP5%2Bvvfiu%2Fw%2F%2FB%2Fpf5zehYv1a6pVk5WLnh%2BJZhML6rmiWvsBY6t1Fn0n1NY77Tvp%2FSf8WnijqNWnOEscjGQ4ZB7Oig0fV7Lf01rr8rqR2tez3XPbBx67XsYzayxjbMl9m38%2F9J%2Bl%2FS%2BpzrvUxMWqvqNVuJksx3402zW5oosbdScig725OHd9qY6v%2Bc2ZOLT6Nn88y3e6X1uyk9NexznYd%2BPdi2Mc1lTxl4ztznWu3bKfWZZXbs9T06f0ypdY6J1Hr%2FVH9Wrtpsx6mMqFDS%2F1WisF30XNa5%2B577bf0eyz%2FRV%2BqnHwW1RotDF6xay49Tviks9OHNBAc9g2sfc1m7Zu2%2FoG%2FQxv3PST9Lw2dTblM%2B1W1sdDixoM2vu3f0936Ot3pOZsurp%2FQ5H%2Bj%2FwaJj4GXf052ZjepRjbmOrywNzNjHulh9Meg5zN%2B1lrn%2F4R9fp%2FpFYxcHD6ey6nItdSGAOtdlFwhlh%2FRuHpN3Ppbv8A5xnqemgFOZ1%2FDyMW77T1DIOQy4vra91bqC00en%2BiYx%2F06Nrqnev%2FAIf%2FAEj1WxaG2sxKOo9Mu6iK32UYeOLHML3vLrNrrmu3%2Blv%2FAPRi1cvpl1PUqW3Nfh5VJ34%2BNmO9XEurHu%2FV7t1jGMsYf0noP9n%2FAANqv9Q6W84w630hn2cuFd1jcYGysjS6qzLxmiu3Fyadv896NX%2Bl%2B2eqhSSBQ1u%2Fwed6z9X%2BrtFJH1dZj1agENsqZuIB9EPsvbkW7Wj6eS%2F6fq%2Bj%2BjWOcbNw8hnqdNxq3tizZYXOa4Ty7fkWbmNd%2FObfof4RdpT1fqHUs1rc%2FLfktyRU2vGa4bSHNePtFL3el6VmLlVejZ7f0m%2F1cj%2BZ%2B0WbvSfqn0%2Fq1eTjdRIdlVspsqcxwBqc8PbfW%2BrX376%2FTtf%2FAKH0bcS6v2WJXqulj4YCXEDZqurxn1dxXdRosuw78hnXcR5dSWu22OrY2bqsdn03ZFTt932Oz2ZWL6lVLPWoXS19Hzup9Pq%2BsfTMYty8kn7ZRXDCy5hdVb1DEre%2F9NU79LvxHbLclj7Kf5rIspXE5mPm9J6q%2FLqa82YrmvfVJHqUsd%2BgyqLatln0a%2F03pP8AWxLmerV%2B5j%2Bz%2FU7qvR%2Bp9Cot6QNlLJbZQ5xdZXb9O1l73S99jnu3%2Bq7%2Be%2FnEmN57qv1X6b9c%2Fq99oxWNx%2BuYxcyx7g9rjc2PXxM5t36x6djmt9H1v02L%2Bi9L%2FCVWcz9VPr51npPWX9H%2Bsxuy8bItFNoyDvtot9tIcN%2F06He316P%2FAEKo%2FwAJ9p9hbXW17ntaA98b3AQXQIbuP5y43%2FGB9Q2dfp%2FaXTWtZ1iloEGGjIY3%2FAPe7%2BayG%2F8AafJ%2F6zb%2Bi%2FSUJT2TK66xtraGAAABoAEAbW8fyVJecfUbqP8AjHPV31dXxMjJ6c%2Fay27LaMd1JaNgsoDxX9o9rf0zaW2%2Br%2FPep%2FpPR0lP%2F9T1VJJJJSDNL%2Fsr9geXGABW1rnakD6F36P%2BsuAwumV%2FWXNrxHvcymqh2S7IqLnRa%2B7Jfj2UPcGtqdvyfVspZ7K%2FQ%2By1%2FoaV6Ffj05FZqvYLKyQS13Eg7m%2F9JKimvHprorEV1NDGDwDRtakkEg2NCNnxDqdGb0Lru3qFDHX41rLTWGxVeyR7626tdVmNa%2F1P3LPWr9i2cmzrpY5tdB6gzpL7Kq7WGXPqqf6D8HIc1gZ9upo%2F7T%2FzmVX%2FADXqr0brOF0%2B4135bWVFgc05kD1WMP06ari17q25P81Z%2B%2FX%2Bi%2BnauTysfqnQeoM6h06l91eW2H4jwf1uhgc70Lq3DdV1XGxm78d7%2FwCfo%2FQXfpK7GIRFX4s2fN7vAeGpRjwk93IyOj5VDnN6U5%2FUMa%2FflYlLGuLm%2BqxnpfadrG1tdk302Ufav%2Bs2en%2FNrQ%2Brd9%2BNlNdfS%2FDuDXMy8e7R1bmjfJ%2FkNhl9Nv59KtOeLbMDO6FktJte%2B%2FCstLh%2Bhcaxm4mY5oc5tLMr08XK3st9H7TRd%2FSqPWW11fA%2Fa%2BOOpdOc6jqeMNllYjcdvudjWt9zfVr3b8d%2F%2Fom%2F1E%2B2F5jpf1u6Dj9RyLKcqu3peaf1rF1aGh%2Bhym0XMr91TfZksr%2Fncb%2FSWY9bFTwftfUOpdQwRZvxsDLOLh2uBexrLH21YzLLAHv9K3ZX%2FwAH%2FOLUw%2BnfU%2F6xVDB6lg4%2FTutEFrLaG%2BgLXD%2FC42zY2x%2F%2Blw7f01f8ur9On%2BroyPqllZfThhMsqBZblWhxFjahFLLWE%2B27HZv3sbsr9H9N63%2FBjW1L9J9PDa%2F6udcx%2FU6Ta7bSH6uxLT7W0Oc0%2Fo6HT6mFl1fzX%2BnV9n1R6v07bbhZv2za4w149K0Vk6enkMeWPu2fzn8xVb%2Fwf0F1GVh4mdSW3MD2vbAfA3AH9xyoYmRZ0o%2FY894%2BztBNGS7QbR%2Ba8n6Oz%2FwH%2FttLyRbyPUcXMqzznY2Nfh9TrbZbkkVyxzQBvzm%2Bm04vr7f0d%2Fo2frX%2BD%2FTV2rc%2BrmH0bqHT8fcW%2Fb2Ned1FrmWsaXOZLfRLH01W%2Bpu9L%2BaXUiCJHBWGegM6d1IdU6WzbJi%2FFbAaWuPvdV%2FV%2FnPTStN6U4%2FWfqPk5jhRj2BorDrMXLOjqnge2m1rNrnV3e1lnpf8b%2FO1Urm%2FqLlZH1a%2BseT03IwLa7s97K8nFrY%2Bx9fp%2Bp6OfjupZ6VvT3%2BrY3I%2F0P6Oyn9F%2BjXrKSCFJJJJKUkkkkp%2F%2F9X1VJJJJSkkkklKQM3Dpzcd2PbuAdBa9hLXscNWWVPb9CxjkdJJTxHVfq4Ol5bc%2Bq55dlvLLq2Dax1lprY7IFbP5i63b77Kf9H%2Bk%2FQrpPq%2FcLMAVlgrupcWXM4duBj9I0y7f%2F1f00vrB0hnVunPo%2BjkMmzFtGjq7QC1tlb9Njvd%2FqxS6dWMj0upH9FfbXsyqwIabGwx30%2Ff%2BifW9n9RHopwvrBh4eF1V%2BXkU%2FqmZQQLSB6deYxwux7HAfpGvs9L%2FB%2F8J6ivWVZ2dj4vV8FgfbkY%2Fo5WO8hosrdr3%2Bi6t7nrefWx8b2h20hzZEwR9Fw%2FlIOLg4%2BI652OCxt7vUfWD7Q4%2FScxv5m%2F89K1IujYj8PpWLjWDa%2BusB7SZIcfc5u6XfnFW31seIe0OEzBE6%2FNSSQUpJJJJSkkkklKSSSSUpJJJJT%2FAP%2FW9VSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2F%2FZADhCSU0EIQAAAAAAVQAAAAEBAAAADwBBAGQAbwBiAGUAIABQAGgAbwB0AG8AcwBoAG8AcAAAABMAQQBkAG8AYgBlACAAUABoAG8AdABvAHMAaABvAHAAIABDAFMAMgAAAAEAOEJJTQQGAAAAAAAHAAIAAAABAQD%2F4TkjaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu%2B7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI%2FPgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSIzLjEuMS0xMTIiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4YXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyI%2BCiAgICAgICAgIDx4YXBNTTpEb2N1bWVudElEPnV1aWQ6RTcxOTVFNTY1NzMzMTFERDlFNzJGQ0E2QjkwQUZBRjU8L3hhcE1NOkRvY3VtZW50SUQ%2BCiAgICAgICAgIDx4YXBNTTpJbnN0YW5jZUlEPnV1aWQ6RTcxOTVFNTc1NzMzMTFERDlFNzJGQ0E2QjkwQUZBRjU8L3hhcE1NOkluc3RhbmNlSUQ%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4YXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPgogICAgICAgICA8eGFwOkNyZWF0ZURhdGU%2BMjAwOC0wNy0xOVQxNDo1Nzo0MS0wNTowMDwveGFwOkNyZWF0ZURhdGU%2BCiAgICAgICAgIDx4YXA6TW9kaWZ5RGF0ZT4yMDA4LTA3LTE5VDE0OjU4OjA1LTA1OjAwPC94YXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhhcDpNZXRhZGF0YURhdGU%2BMjAwOC0wNy0xOVQxNDo1ODowNS0wNTowMDwveGFwOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhhcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ1MyIE1hY2ludG9zaDwveGFwOkNyZWF0b3JUb29sPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIj4KICAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9qcGVnPC9kYzpmb3JtYXQ%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iPgogICAgICAgICA8cGhvdG9zaG9wOkNvbG9yTW9kZT4zPC9waG90b3Nob3A6Q29sb3JNb2RlPgogICAgICAgICA8cGhvdG9zaG9wOkhpc3RvcnkvPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICAgICA8dGlmZjpYUmVzb2x1dGlvbj4xMDAwMDAwLzEwMDAwPC90aWZmOlhSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj4xMDAwMDAwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8dGlmZjpOYXRpdmVEaWdlc3Q%2BMjU2LDI1NywyNTgsMjU5LDI2MiwyNzQsMjc3LDI4NCw1MzAsNTMxLDI4MiwyODMsMjk2LDMwMSwzMTgsMzE5LDUyOSw1MzIsMzA2LDI3MCwyNzEsMjcyLDMwNSwzMTUsMzM0MzI7QzA1QTE5MDRGRjAwQUJEQzA1MUJERkFGMDIwNEVBNTE8L3RpZmY6TmF0aXZlRGlnZXN0PgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24%2BNDUwPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjMyNTwvZXhpZjpQaXhlbFlEaW1lbnNpb24%2BCiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U%2BLTE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6TmF0aXZlRGlnZXN0PjM2ODY0LDQwOTYwLDQwOTYxLDM3MTIxLDM3MTIyLDQwOTYyLDQwOTYzLDM3NTEwLDQwOTY0LDM2ODY3LDM2ODY4LDMzNDM0LDMzNDM3LDM0ODUwLDM0ODUyLDM0ODU1LDM0ODU2LDM3Mzc3LDM3Mzc4LDM3Mzc5LDM3MzgwLDM3MzgxLDM3MzgyLDM3MzgzLDM3Mzg0LDM3Mzg1LDM3Mzg2LDM3Mzk2LDQxNDgzLDQxNDg0LDQxNDg2LDQxNDg3LDQxNDg4LDQxNDkyLDQxNDkzLDQxNDk1LDQxNzI4LDQxNzI5LDQxNzMwLDQxOTg1LDQxOTg2LDQxOTg3LDQxOTg4LDQxOTg5LDQxOTkwLDQxOTkxLDQxOTkyLDQxOTkzLDQxOTk0LDQxOTk1LDQxOTk2LDQyMDE2LDAsMiw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNSwxNiwxNywxOCwyMCwyMiwyMywyNCwyNSwyNiwyNywyOCwzMDtENDYzN0NCOUQ0MUExMEJBN0VGNUVCQ0RCNjMxODMyOTwvZXhpZjpOYXRpdmVEaWdlc3Q%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY%2BCjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8%2B%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQACAYGBgYGCAYGCAwIBwgMDgoICAoOEA0NDg0NEBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAEJCAgJCgkLCQkLDgsNCw4RDg4ODhERDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgBRQHCAwEiAAIRAQMRAf%2FdAAQAHf%2FEAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPBUtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4%2FPE1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1%2Bf3OEhYaHiImKi4yNjo%2BCk5SVlpeYmZqbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEyobHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp0%2BPzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1%2Bf3OEhYaHiImKi4yNjo%2BDlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq%2Bv%2FaAAwDAQACEQMRAD8A7%2FmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmrlHI2utXn6WltnFESb0RFSp41WjD5q3LCBaslzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Dv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2E2rj0LuzvF7PxftUf8AXPLDnC3XI0ewdpOkTK4P08T%2FAMK2Ec1TLNgaxmE9pFIDy%2BEAn3GxwTgV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV%2F%2F0e%2F5s2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXYUeZpfR0a4kpUAx19h6i74b4X65CbjR76FV5FoJKKe5C1GIVLdDuGhuXs2IKOX4gfsvGdx%2FskKtkiyD6bctHHbXgPImOC4qOpXj6E1ff4Mm4Ndxkpc770BvNmzZFLs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F9Lv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVokKCT0G%2F3ZgaioNQemANZuhaWLyE0LlY1%2BbGn6sWspA8QA%2FZ%2FUcVRWbNmxV2bNmxV2bNmxV2NZQwKnodj9OOzYqwDT4iiPZsa%2FVLm5sj7I%2F76L%2BOTXTpjPZQSt9pkAb5r8JyKXqfUvMOpIQOFxFDfoP8qFvTl%2F4Q5INDkrFPAf8AdUhI%2FwBV%2FiH8cnLeAPcxHMprmzZsgydmzZsVdmzZsVdmzZsVdmzYyWRIo2kkYKiirMdgAMVX5sjTeaGl1CzsbSAOLuX0w7k14KCzyAD%2BUZJBirebNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV%2F9Pv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVo4CXUFa9e0CbJRWkr%2B0RWlMHZEJ5WtPMtzE5%2BGYJKnyK0%2F4kuEC1ZeM2IW0okjFT8S7H%2BuL4FYz52mMWn2tDTlcpX6FbDHR5ecCH%2BZevywl%2FMM8dJtn7LdJ%2BKvgry9ccrOJ69KVw9E9GS5s2bAh2bNmxV2bNmxV2bNmxVi3miFU1LSbxvsStJYzH%2FJnSi%2F8ADYpoEzLcxq1T68FGr%2FvyE8GrgjzdC0mhXEsY%2Fe2pS5i%2F1omDf8RwttJRHdrcDaNbhZVI6endIG%2F4mcnHeJCDzDL82bNkEuzZs2KuzZs2KuzZs2KurkZ843Zhs4bVTQzvVqfyrvg3zHNPb2UMsLtGBPGJSpIqjEqRUb9ch%2BvSSvp0JlcvJCLyMuxJb4JNvi%2F1SuEBVvkaX9L%2BZbm8Sv1fTbf0Y6%2F78lPxH%2FgFzpeRvyPbWcXlyyubWBIXuoxJOUG7uKjk5%2FaOSSoxPNXZs2bArs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F%2FU7%2FmzZsVQ2osyWM7o3FlQkMOxGPtZhPBHKCDyUE08e%2BM1Bedjcr4xPT%2FgThV5cvBJbpGergEfMDcYa2PkqfZs2bArs2bNirs2bNirs2bNirshfnRGtb7T9TX7LcoJD2qDzSv3vk0wl81WDajodzDGKzRj1oR35R%2FFT6RthHNXaVcrKsUtftDi304dZz7yrqPrQCIncdMnsEnqRK%2Fcjf54yVjP5gwtJ5ckcD%2B5mik%2BivH%2FAI3wD5RnEtpwrWgw983RGby1qiqKlYGkA%2F4x%2FvP%2BNchnke5BIUGoPQ%2FPEJHJ6XG3JFbxAOPxOA1iX7sUwIdmzZsVdmzZsVdmzZsVUbqBbq2mtm%2BzMjRn5MKZDLP4rC2UVq9qYj7SWshWn%2BtxOTnIVG4ttWvdNaiiK7FxGDtWG6Ti%2FwDwMuShzRLky%2B1lE9vFMP8AdiK33gYscKtBmDWhtq%2FHbOyMp68SeSH5UOGpyJ5pYde3moWPmW5KzH0pIo2WNt0A3Wv0ts2D73zJ6FgzxqqXpKpHG9eDMx7EYXed45VeC4gPCdYyqua0I5q5VgOv2cAW8sWo2QMqhwwoyHcVyQFhVOLzvrNrdf6fFHLFX4kVeBH%2Br1%2F4bJlp%2Bt2GqWrXFnICyKWeJtnUgV3XOP67qFjaFLHUrprecy%2BlZajxPDmN1jn8Dv8Aa%2By%2BIaXrclrdtZ34NrfR1USofgeo2K06c%2F8AgcBCXr%2FlvXRrdnzk4rcoSJFXYfMbnDzOV%2BTr82eqAOQIpaBq7AV2r%2BOdTriRSEp8z8RoN9I5AWJPV5HYD0yHr%2BGQg3Ntq1lcLazx3CiZviiYOKzQo9Kjb7YzoWqRevpt5AOskEij5lCOmc60%2BWCK0j9ONYo5IYp%2BEahQWBeNjRafEcMVTvTb%2B80fyHZ3FugE9vSORJBUqPVZW%2BEHw3yVaXeC%2FsobulGkX41HQMNmH%2FBZzC%2B1iTTbadZUkjtJjzkQqSKqNm5fsdMPfyr1%2FRdT8vra2E5N5C8kl3bSMTKpkctz%2BL7SP9peOA7fFkIkgkCwOfkz7NlA1y8DF2bNmxV2bEJLy1injtpJVWeavpREjk3Hc8V64virs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F9Xv%2BbNmxVplDqUbcMKH5HIPokjWsstqx%2BK2lZPf4Wpk5yF6nF9R8xswHGO8QSj%2FAFh8D%2Fqrkoc6VmSMHUOOjAEfTjsAaZMXhMRPxRnYf5J6YPyJV2bNmxV2bNmxVQuzOtu729PVUclBFa07U98DafqsF6i9EkI6ePywwyF6vG2i6oJRUWl6xZCP2JOrr%2FsvtLhG6s0yiK4X6Zfi4QI7Vb9lv5h%2FXDHrgV5c8B0LzLc2Q%2BGF29WDw4SfF%2Fwp%2BHOg6ZMHi4j2YfTkZ%2FMGwpFaazGDytm9GYj%2FAH25%2BEn%2FAFX%2FAOJ4N8uXwliicnps305LmFZDewrc2dxbt0lieNvkylc45%2BX10UCQzHjLATE4P%2BQeNc6L55e5i0J57WVonikRmZDSq7jf2yB2Op%2FVYor2%2FwBNjktpAGjv7ZvU%2BInoy8Vao%2Fa44xCQ9as25RVBqKnfBGRby9rBEsljcIY0aQ%2Bg5FCCQD6bj9lslGAikN5s2bArs2bGyOI0aRuiAsfoFcVS%2B%2F1q2sJltyrSytuyp%2ByDsC3zwxBqAfHfI%2Fodmt2z6pcjnJK5dAexPT%2FgRkhxV2c782mSy82WUgNIdUhNs4PTmlXQ%2FwDBDOiZAvzLb6nHpOrlFkjs7lWdG9yvxVG%2FwiuGJ3Up3YTJFr7op%2BC8t1ljHjxodv8AYtkiyHbxx6XfKx%2FcXBilJ3%2BAsVUV8FRlyYA4Z80DkkPmq3EtlHJSpR%2BJPgHBH66ZBtIufqt9Np8pALHnEpO5p1oM6Xq0P1jTriPoePIfNdxnLtZtrGZluBcG3vF%2BFJYuJfx40IIxHJKzzb5et9ZtZYp1LwygcgPtKy%2FZkT%2FKWuRKKCOC6tNH1CblcWiILDUWHH6wg6wyDejx9Fw4sfOMjEBY5biH1HgLy8VYulOfJV%2By38q4ndH9LlL219K4iilHrWzLxlhI6SgE1%2F2SnFKo0Czxy28zMqcSzspIYBPj2I%2F1cd%2FytnVZdNQ2D1I%2BESMoPw%2FzMftdMG20TSQyyblmjenzoc4TY6ld6ddyKwMUsbFZoHqNwd1KnGXRXs9n5%2Fv7lg98GlWhMjwk1A%2Fm4k%2FF7jDSwSPULTTJI3PGBphGNwG4OHVW%2BgnONX%2BsWFpDFPaSSJcTESG3TcRlf2uRK%2Fazq%2BkR3aeT7e%2BjnX6yoW8ibht%2B9i9Tg%2B%2FQ98QVZHqstutowuSPSdSpV91IPVWzif1i98t%2BZ7m40FjbSWUrNCqEmkex4EH7acT9lv2c6lonmbTvMUTWdwgiumWktrLQhge6H9of6uRLzd5ZurDVP8Q2KetYFUW7jXeSKi%2Bm0hH7UZH7X7OCY4g2YM0sMrjvexB5SHcXsnkPz3Y%2BcdPqKQ6lAB9bta%2F8lI69Y2%2F4XJeDXPJlvPe6JqMeq6NMYLuE842To6ntToyuv2k%2Faz0J5E8%2B2HnGyI2t9VgA%2Bt2ZP0epH%2FNEf%2BEyESeUuff3tmbFAx8bB9H8Uf4sZ8%2F6P82TMMLNb1iDRrQ3EpBkkPC3j7s5%2FwCNV%2B0%2BGVc4n5m8yfpnzJcsr%2F6HYsLe232PBv3j0%2Fy3%2BH%2FY5JxkLourX175%2Ftb28maSZrloqsdlQ1Tgo%2FZUDO755%2B8uxlvPcMI6i6LAe2zZ6BwlS7NmzYFdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVf%2FW7%2FmzZsVdkb83W5%2BrW1%2Bg%2BK1kAc%2F5D%2FCf%2BG45JMC6jare2FxaEf3qMo%2BdPh%2F4bCNiqV6VOOcTV%2BGReB37%2FaX9WHoyFaJMz23BqiWE7juCh%2FsyaRsHRXHRgCPpwy52gLs2bNkUuzZs2Kuwu1rS49W06Wzb4XI5Qv8Ayuu6nDHMemKvONE1KWGVrG7rHPCxRgeqsppk7sb0XC8W2lUbjxHiMhnnnTHtLmPXrZfgakd1TsR9iQ%2FP7LYtomq%2FWI0Iakybof4H55LmFZdqNlFqNjcWMwqk6FD8z9k%2FQc515XuZbW4l0%2B5%2BGWFzE6n%2BZTTOlW063EQkXY9GXwPhnO%2FNsS6T5qtbofBHqaniexmi%2B2v%2BsU4v%2FlfFgHcrPTFbanZNb3caywyApLE3Qj3zjutwW%2FljzBc6Pp6fV9JHpsLNSSgR1HIorV3Vvizq2jXQk%2BAndlBA9xnNvzRiWPzJBJWnr2gJP%2FGNyv8AHAdikJ3YGf0o5pZDIboSD1GJJ9aAgh6%2F5aUzoFnN9YtYZ6gmRFY06VI3zlXk%2FVI5bi2hnlBgdBuxFFahhfr0rSNsnHlXU4JI5NJaSt1avIQm%2B8RaquD0I%2BLjkpbgIZJmzZsirsDagC1jcqvUxOB%2FwJwTjXUMrK32WBB%2BWKpfofE6Xb8f5d%2FnhjkMPmvR%2FKlrJBqkxBSVkjC0PIDpTfI15s%2FMNbu3jbR5plsZVp69swRi9N0eSjMnH%2BRcVpm669cWWtXGnatwjgKma2uPsj0x%2FNXw%2FayOfmLr%2BhX3l%2BPTYrkXM%2BpGtm1uPUUceX7yRx8KLtx%2F1s5xoN%2FefpVr6C8muVVfUuLO8kMrEIeTem7faRk5Iy5FdTuZ9B169trGZvqkc5eFD8SmJzzUcTtsrceWHzTT3fRJv0p5UZvtOYYrgCu%2FJV4P%2FwANHnPrj83vM2lX9zpWoBJYInaJ540pOi1%2BGRQDxf4cln5Z3qT2stjWrK0iexEgE60%2F2XLOUfmHYNZeZbhWP94Ayn%2FV%2BD%2FjVclLlbEdQn955q1wTiU3huLadeQCsfSljO9VNev%2FAA0bYHub1Z7V5yWkhHxpcVHJNwGiuP8AKStVk%2FbyGafqLQxyafO5S2lPJWUAmGQ%2F7uiB8f8Ad0X%2B7F%2Fy8Ctd6jpl3Lb3v%2BkQTpxniqTDcQn7LoR%2FwUb%2FALDZCyyekWl9ZyaFLPelZGN2tqDEtZJHenEs67ckpVXwq1bU7G1iKWvqtcCR4bkkhFYx%2FCHXhvvX48C%2BU7H1SbK3k9Wya5hv4HchSBF8FxBL2WeOJ1k%2F4s9P4cu%2F0j%2Fc1FphmFxPLeOhZBxAiL8jt40%2Fa5YegV6PprvLpcE8sfpTNbh3QVoGK12zluoQ2PnZih42fmWIVt5z8Md4q9I5P5J17Z2A8YrWVl%2BERoaDwoNvupnLtb02LzREdR0hBba7bVe4tEPFZwpr6sH%2FABb%2FAJOSkgPNLqGaG7e3v42iuoTwlicUIK9iM755KnGoeT7ZX6m1CH%2FYSNCf%2BFOctnuIfNkSWeqEWuu249OC8ccfW47elcd%2Bf%2BVnQ%2FyyS5t9Dk067jMVzaSXMLK3hRJlp4r9rjkQl5PbalcaRd%2BlI7PbwysI5lNJIirU%2BH%2FmnOueV%2FOUeo%2BnY6hIpuWX9xPtwnU%2Fhz%2FnTOPeY4zY%2BYNTgpVBcSVQ9CrHkP14Gsb9rFgRWSzY1K1%2BKNh%2B0p%2FZcY2Qdlev%2BZ%2FLKxK19pcdYBUzWqdYx1Lwj%2BT%2BaP8AZ%2FZyHW9xe6ZfQ6ppk7QXsBDRTIevsR%2B0p%2FlyY%2BUPNR1KJLK5lD3KrW3m6esg67f79T9pMD%2BZtCjTnqVilIW%2BKeFRshPWVAP2P9%2BJ%2BzkZRB3DPFlljlxRPz5Edx8mcw%2Fm3aXvkzULyUC3122jET2oNA8kh9NZYT%2FJvzb%2BTOZabIhnWkgaOQcwxO577%2F5fjhKIYndoZvgaUD05v5SN1J%2FyG6YI0RZ4tS%2BoyKQS1HTwYHsffALrdOQwM%2BLGOEHfhu6PWvJ6f5FsfrXnqW8K%2FDBEZD4cmAVf1Z2fOSeSLmfT9RrHEWlvDSZaVagNR0%2BztnW8mWsuzZs2BDs2bNirs2bNirs2bNirs2bNirs2bNir%2F9fv%2BbNmxV2Y5so4qw6WL9HeYLiLpFcUnjHQfH9r%2FhuWSbTnBgMXeJiv0H4l%2FA4T%2Ba4fTW01FRvDJ6chA%2FYfpX5MMGaXcBmHxbSrsCdi6%2F2f8RyZ3j7kdU4zZzrTfO2qQa3eaHrkax6haux%2Br9FkgJ%2FdT2790ZP%2BGydWWoWt%2BnK3erAfHGdmX5rkSEovNm2yiQASTQDqcCt5sL5dZ06Ko9YSMNqRAv8Aiu2Iv5i0qJecsrKo%2FwAkk%2FctTho9yo69htbi2e2vApgmHB1cgA1%2BffOYX%2BnXflXUghJezkNbabxH8jH%2BdcPPN82l65b2j213DN9WdmmtefCRkdeJaNSVb1I%2FtYXWM8cVk2javM95pUv9zcPvLB%2FI3LvwwhIT2x12KCBrxjyjRS06jrxG5YD%2BZcrzzpkXmjyhLPpzCSaFVv8ATZozuXjHP4GH%2B%2FE5JkUmju9BujaTsJYHHKC4G6Sxn9r%2FAJqwJH5g1fyjDaw2KrcaE0zSNE27RiQmtuXPSJ6%2FuW%2FYkxI6hUg8ia9qulatHe3s0k8DgxGKZywBk%2By3%2BTkn%2FN63a8ttG1a3rHL%2B9hIB%2BIdGK%2F8ABA5DNbS0tL%2B4ksJOenzETWrjYiOT4gjD9l4WqjL%2FAJOTTU9XtNc8r6dDP8E8rc4iSP71RwkTfcP%2FALsX%2Bflgq09zFvKeoTMwV2NeCsB0BaNqf8a5N9Zu59K83W2pWb%2Bn9cjBo24ZXUNxI%2F1lyCWsL6fNb3LLxid24tSgINA9Puyb67pd%2Fr2kaPc6bxN5b8owWYKAYW%2BGpP8Ak4RyQU8ufOF7JLD9XRLaAj96ZPiYv4A9An%2FDYOfz9oEbtA0kjXSqG%2Brqh5NtU%2BkW4iQL%2Fk5yK%2FiuNagjEcpTU7Rj6YqaSAbtG3%2FFin4om%2Fa%2BxhXcGeWxlDgpPbfvUFaPDKpFXiI%2FYk%2Fk%2FmwFXr0n5hfXBJFpVtxmC84jOa81%2FaKqv7Sfy5GL7zJq2rwXFhqF40AlWiSQfAEPVW%2BH7Sfz%2FwCRkTl1dLW3t7xi8108C3g9EBArCnI8jtVvtNRcR1zzTdJcQvYwRW7y28dwzFfUblIvI05fAo%2F2OGxSt3zSW9LPXITMlCjiuzofsyRt2YfsPkd0iabTtQksp1Zra4U84X6SJ%2Bw4%2FwAoD7Ei5dzqV7LFCt7KZnVAY%2BRqyg9aj%2BX%2BTJdouuaHrltb2PmCNfrkKmO2vKcT%2FkgsP2v5cjtapfo0Jt%2FMllFGHltZvVeK4ptwWNi6yU%2BzIvRlwm1mUarpNtJHEWutOkuIbh0FSbfkGiZ2%2Fb4MxH%2Bpk6uZNM8saddywStNK605tsKnYcF%2FmbOZy3s2nWaLE%2FG4e4%2BsK43HELxowOzK%2FL4lx5fFWb%2FlprP1TWLQMaLMFRh%2FlRtxr%2FwLYn%2BfNjLp%2Bp2WpwghWLxuf2SGoRX%2FAIDI9peo2kMtjfWQMUi3JM9u24j5rT4D%2B1G5qyfyfYzp%2FwCcdimr%2BUra%2FG%2FwpISOoIpXJDcIPN4HDcR3Scl2I6qeoOGNvcQyw%2Fo%2B%2FJWGpNvP1aBz%2B0PGFv8Adkf%2BzXIxSaym2NCOh7MMNYLhLlOS7H9pfA5FU30vUNQ8uaqjIqsaqHiO8UsZ6EeKsrfA%2BS3yUr6p5iudTl2S1RvRTchTKeCgV%2FlTlkOsvUveOn8VkdatauxoyN%2FID%2B0r%2FwAmTfyYHsrnTdPasc1%2FNczXMZArwhT04g3%2FAD05Yjml6Fditlcmu%2FpvX%2FgTnK5bS4sp1mDtFID6kLioPXZgc61NGTBNH4o1PpUjIpNbyX0Ulvcw%2FV9Tst7i2IqHWn99EP2kZf7xF%2F18nMX1pANMR1TTYfMkRuYYlt%2FMEY5EfYW7UftL%2FwAXjDf8u%2FMFzM8mnX6MJLWRFkMh4yiqtGUYU%2FZP82IzW%2FH4DVQG%2FduPtRP1FG%2Flb9lsG6ZJDJfPcvGI9WCoJZwPhmRGHF2X%2Ffi4AP2peafmBE8Hmq8LgATCOVaeBUL%2BtcjauUPip6qe%2BdJ82x293Pb2GtyL9ZmWRoL5QAUZZGQK47p9nOd39hdaZctaXScXXdSN1YHoynupwFCL0zUX0y4SVGf6uWDqybPG46On%2BWv%2FAA652nRtY%2FTdqJbfjLdqnK4gjpSVen1iBf5G%2FwB3Rf7qfOCxycaqwqjfaH8ck%2Fk%2B61K11AC1kZYIT6gmB%2Bxy2oP%2BMi%2FC6f7LEGlZCbBr%2B%2Bu%2FqqpBCjP6cbtxHwn4ljrX%2FgckOj2EMcC6lK6yTRKYmkIoYl6gE9xT7L5HhcTJqM1jJCEjjQSQsK7qT3rgi%2FuJ7PTDKQ31S5f03ZPi3SrfGP5RiKG6bej%2BQPzB0CG%2BuNHvB9VeZx9WvZKBX7cCf2P8jOvghgGU1B3BHfPHaQrfuEt3DHkAJakKhP8AO37A%2BedX8i%2BYPOen6dNp2qK5t4JFjs5HClyoNJfTdieUaL8S%2FwDCZEcRNEc%2BrfOOKWMTgeGUaEoHfi%2FpRP8Aunt2Ab3UorWqAc5QK8egHzOPhuoTYrdesJYgnJptgDTqdu%2FtkT1C9XnLPIwofjLduJ6fhkgLcYsi0vVRfSSRSUWRd1UeGGgyN%2BVrCSkmrXKlXnHC3RtuMXXlT%2BaTJKMTV7JdmzZsCuzZs2KuzZs2KuzZs2Kv%2F9Dv%2BbNmxV2bNmxVB6rafXtOuLXvIh4HwYbr%2BORbRL%2BsMTMaSQsOSnrseLZNDnIdfXXtP8zXtnZGC2tJWEsUz8pH4yAE8Y14rs3JfibJRPMKyf8AMXye3mKyh1LTH%2Bra1px9S0ulFSV6mNwPtRnIb5e8wXlw%2FwCj9VR9N1u12PGoD9vUgfpJG%2F7UedN8uapcXsH1e7C%2BrAi%2FvV6OPs1K%2FsttgLzP5Ysr%2B3NzHFxmjPMlNj%2FrLTdWHtiNtlU9I82sxa11lAssf2bmMfA9PFf2G%2F4XIt501y81SykEEjRQRMGEaMV5L0%2BKn2sq4ke3j9O4asuyLNSnMduX%2BV%2FxLCfVZ%2FS0u8cDkVhYhfGgxodGQDDZPMculusTzpwryMT7mneh%2B0uTVNL1e%2Bsob1tLmmtJlEkbFW5FGFR8NfUzi2iQ%2FXNXtLjUAXWe6hE3L%2BQyKHG%2FbjnqzzP5ok0H0baxgWVyodi5IRU6KBx74ASrzOzsfL1tcH69ppMtdxM0h4%2F883OS2GLS7239K1pGpGwj%2BEr%2FALHphVcecBcyrc6vBFdabL%2B7lV0UPbt12KDnx%2FlYYGvrWKwP1nTp3kirsT0BpWgcdf8AZDJg%2FFjSanSb8xC2ursXtpG5kt7dkVRGaU4g%2FETy%2FwBbIzc60xSeylsgphDJeafNtIY%2BnqREfCygYcWPmV14x3qkqQDzAo4X%2BYr%2B0ME6h%2BidZZImjM90FLQzQj40BFNn2%2B1X7DYkd2yi%2BrHNIh8uXbLp7yLJdToZbZJDX1EU0NOXxCeL9tf%2BJ4PhbTLya58sXqqt3EoKlV9MSoRUMn8ssf7XH%2FXyD%2BcfKFzoWnRatFqUUU%2BnSGSGAsBcFXZaFeHJVZDu2HOn3See9Kt1%2BsCx8x2y%2BpaXabB2TxH8pP21%2FZwAnu3VQuV1CC2iW9cx%2FwCkS28kMtA5dKLHL1%2B00TJyZPhk%2B1nS%2FKN59Z0GSNno6yI6e1V4v%2Fwy5z9pv8YWj6Hrcf6P836EWmiiHSfiv2ox%2B2kwH7P2ftLk28uxrbeXYLhYXV7gMzxkE0IqPiqNq%2Fy4gb%2BSk7PNdX1m%2BHmqaytJ%2FTtTdx%2FBGoSu6swZqcj8YbCnRmM97drKxKXcslu7tXb1%2BSBq%2FwCSzZK9c06xuNUutStkBls5IFkAIAMoHqUUfzNRkbCPW5Xs70rZcVtL0%2BtCoAFVc1oK%2FwArni2RIShdD0rU9Z0g2UNW1DR5JLG%2BirRlQuXhmp1ZP72M%2FwCph75g0K2sra21G8ch5baGJUpt6yARuvL6OWEQk1eJrq9s55YLxBzuGhJEjr3LKvxSqv7WISanq2oQRXGryvd2MtVhuSQ8aNQch8OyNjsqElhK1hbcr%2Fcv3K%2FyV%2FycRSQBmUiifsfQM6FpfljSbrR0mCy3EjDlDIzN9sGnphU%2FZ%2FlbIrqnl%2F6jM3Cb1VRqOoU8078XoOHPISIiQCd5ckgE3tyS4StK6xSy%2FDJ%2B7HqseO%2FYVw0W2thatp10he2b7YH21cfZljP86f8AD%2FYxO4tbO4so4I7ZUvG5Lpl3PssprUws392JD%2Fut%2FwCbAOh6sLuU6VqYMF4hKRu2x5DrFID0YY7rsh4tMn06%2FkspjzjniaS1nQfDIE%2BJWXwdafEv7Od0ugnmL8tmYipWEOB1oGWp%2FwCH5Zy4hoT9WuU5BTzj7FHp%2FeIe3%2BX%2FADrnR%2FysvRfaHeaJIwZ4RJGo8QDzQ%2F8AAy5OPVBfPNxbpMpifYqaBu4IwFYWlx9fEIJU7lmH7S%2F83ZJ9e0x7K9unRg8azMkoHWJySVR%2F9YH4W%2Bzj%2FKtvFdaxBDLQKzAVO243G%2F8ArYPJUXb2NvDZtdyWxe3B4meCQ%2BtG37Jkjb9lafaXHRXl9q2r6etnWJ7Yxx28h%2B1UNyMjt%2FlH4myX6lp1poPm57CIFLG8jikCsKisqqXWncc2%2BzhLZW0Om35hCuLiCeSgIHFY67KT9rl4YaV63IwMVSakg1pt27eGRuzvrTWILdYbnlcIWXSdQc8W5pXlp91%2FLIP91M%2F94mG9tcfW3AQ14xgsPH4c4Zp%2Btz6HqN2rJ61nNIy3loTTkA54yIf91zR%2FailyRNUinpV0iXqyyxw%2BjdW5Md%2FaN1iPdqf74f8A5Jtka1ZZ4LWYxsySKpaCRT8QI%2BIxkj2Hw5KLe6%2FTsUF%2Fp1wsmrRRk2l0QAL6Fft290g%2Fu7uP7Lo395hFquqaL9XZmmWCRwySWTgs8MgG6Oo%2FY5fZbAUhC%2BZ9JsfMl1SKYW%2BqyQRT2DMf3UvJeTwt4M5%2BJchKyrOr6D5gQwTQEpFK%2FwDeQv8Ayn%2BZDk%2B1Gyj1ew0SaC4gS5ntmCtX0ELQtQAM%2FEcv5Fwv1bQxr%2Bnhr4oms2ymP6xuCStSIbof6v8AdzYOaGAQ%2BXdTm1NdLjj5SN8QkG6FOvqBu%2BTjTrS2sbZLCMcVUmkpFGLn7Rf%2FAFv%2BFxTTme102DTuILIwZpKksW%2BzxWtW41%2FZyU2XlW41HhcahytOQJZVHKaXw%2Fd%2F7rY%2FzviAqQ21ldXtytrDG0k524qKmnj8sO736roNmNIltfVv7hFeaMnkUqTwpxNA2S2MaT5etS9w6WdtEvx1b42A7Symh%2F2C5zTzb%2BZun3swOjacGVKwrqMgoajeiJSr%2FOTDVdVZPYa3pOj%2BW7u31uKKzuZGP1dePP1Iz1UovxtJ%2FlYU%2Fl%2F5hOtyS%2BWrlis1Gm0pyfiR4%2Fi9Kv7SumcrvNYnu5DcXH7yToamrfSf6YL8ratLZ%2BZtJvoT6bwXMbEjuCwVh8uJauPFuFe%2Bx%2BYJ4NMawQiOFzyZOlDX4h%2FwQyQeVtAn1BY9R1QN9WU1toG%2Fa3qGYeGE3lnRLPVfM19LdAyWkTtcW8a7xOGaq1kGz7k%2FAM6moAACigA2AxJ7lojn72wANgKAbDLzZsirs2bNirs2bNirs2bNirs2bNir%2F9Hv%2BbNmxV2bNmxV2Qzz3p3P6rqKbFKwyt%2Fkn4lJPseWTPOVfnX5gmstNtdBtnMbajykuXHX0YyPh%2F2b%2FwDEcbrfm24MRzZI447GR59w6pMfNyWl7EdD1KM3Cp6dwoKldj%2B1zHE%2FRko0rz1rUtz9SvLe2lm2Hp8jC7BhWqV5xvUb%2FDnn01jPEin8pHgckemXmsTaHcxANw05o57O7IIKxliJYll%2FaRT%2B84%2FsNkIZhIkSjRDl6zs%2FwIjJCXFHkb5gp3%2Ba2r%2BbdPuEvYLVbLRpiFL27c2EvWkj0%2Fd8v2VpkGsfN2p3Kra3V2XicgEsQeQJ3qSNs7d%2BkdO1rSbKw1LhN%2BlbRXVJKFZyq8ZV3%2F3YGHLOEecvKFx5TvTLDym0idv3EvdG%2FwB9yf5a%2Fst%2B3lpHUOvsua4iNwbWCruHK%2FB0qN9j0zoy%2BarW48r2Oq3l1Le2sSizuGkqk6zgUVXMda8F%2FwCDTOO2M01rIs0XCRQ4kHqCqGnZl74NgkTlMqSOIZpPVNrXjGG8Qvfj2wXzSyqz1eO%2FtNSjQM8sVubhFbbkIpPiI%2BUR5Yca3q17IdPmtZ2js7yztriONNhyAKSciPtMJUfIZaXDWL%2Bra0jcq0ZYAV4yAq6mv8ynJf5Yex1PRjpUsfO%2F0pnuLJWNPUgkNZ4l%2Fm9Nh6vH%2BVpMRa2uvBcXej2Gp2rSC5tJJbOZkqW4v%2B%2FgY%2Bx%2FeJhtBB5sOi313boLa9ihMlo3EB5ClGkULy%2B16XJk4rhlZarBFAbZljtrS5Tg%2FoIF4fyOT1JjbC1L%2BbSbowyMwu4WrzJryX9h1PQpxw%2FFWFXNut9PB6srS6frlqI4JZHJ%2Br30bCsbOf5phx3%2FAN1T%2FwCRgPQbtreVrEFrW8gfnDU0dJUPF09unHD%2FAFeygjjvbi2haTQbpxcXlpHvLp9x9n6xClfjtjXi1P2P3b%2FGseE99ol9qtyjIKamYlktb6Mn0L5QKJ8Zp6d3x%2BH4vt8OD%2FvciTXPZaZ%2Bkdp5%2Bgt19Yad5p0%2Fe0vU2Y8T0NPiaM%2FtJ%2FuvOj6lcGysFaRlYxJWUjfkUXk3z5MM5p%2BV2l3U2pPdalFHK1sOLyqQxSZaFOdDWOT7W%2F7eTTzK8UdlcxREhXBTf%2BdzWg%2B7JxNji70HnTz%2B51GXyrrFxaapD69nfcRqSftqxHL1oj%2FOjPjtQ0GNYCfrCXWi3QMtlelhyjkYbFfdvsyx4M87Wy6zqemTq4X9KwQnmegkK%2Bmf%2BSi5G9M1PVfLUt3od3CJIGDEQS7iKUD4Zov8%2FjyErqrruPNkK58%2FJN7Sx0%2B0sDe3k0k9xA6JBJACGqwPHiKp8S8fts2F0mrDS3nubPThH6ZLa5YcQsnBvs3kSsCnJeXJuK%2FB%2FwAYmwQ%2FrXdjEsQRizhvjKCrUp8If4uW%2FwCyuK3Fs893p1nezfVdeDCOxmHFi8ZH%2B89wSeBjP2YuX%2BVFiYggA70Pmt9QzfyjdxXeiPeaY3rNYt9ZjdKUkgP2gyj7L8ftL%2FPgTz2tuLdJIT6cN1JFcxzJ2U7Sb%2Fs%2Fa5BsKfLEMvlPXby2sJI40uF%2F0izVuUcb05n0z3gkTlw%2F3237vHeddXN5pEMdnbmOys4BFcy0qDcF6GJa%2FwDFacmyX8Pu2R1QOi6Yupabd2F78dj6xjLVFUlpySaHwkUf7F0%2BDIrrvl6dr42N0eOtRIHs7ldkv4B9ijf8tKgfB%2FP%2FAHX28mPluyb0Vs7OdTL6SSReoK8%2FTZqN%2FrqOPL%2FJwTqdhFr9m1lqUhjnhcm2uv8AdltN%2FMKf7pb9tP8AZrjVhWHaHrEWqxjTb9gt9GKQytsXptxNf2sl35d3J0jznJbCojuQlR26cT%2Btc55qthdyXcyyJ9X8wWfx3ESbC6UbrcwfzS8fjfj%2FAHyfvPt88N%2FKuq3F3q1hqBIEyc4pWZuCkoAwck9MA50VPJV%2FMq2k0TznfCJQUmPJom%2BzJG%2FxcW9t%2FwDY4Q6cILa6ZY2eKRwslv6goaDfjy%2FaK4f%2FAJleadM80a8t1pcZ9GCJYmuG2MrD7TAfsqv2V%2Fmwr0WyuNXUw%2FVxci1%2BKLt%2Fw23wrg6qGVanr1vrK2t3csIry1aESBjxJEYYOQff4cjvm3zLbpZXLWTGC5vr%2FwCswRjfjBGoC8yfi%2BIjCPX2vNMu%2FqZDRNGKSAgUqxqgqKruF%2BH4sjsyPPIZZSXJ7knfG1e9%2Flxrdt5ksbqdSFu7eMLcW5O4%2BE0cV6xvnF75uVzcb14yyBW7%2FaO2KeS9RutC8yWd7DMYLfn6d3L%2Bx6D7SLJ7YhdkNd3UkRDRSXErxMDUFGclT9IwndCtpWrz6ZN8MskcDsGdojxkjcfZmip%2B2v8AL%2B3iWoySfXbiHUmFy14frKXqH4nL14zK%2FVuX7cbYEeJWDPQVpU0Pj0x6yqLL0pJUW4tXE1o5O9SaSRbeP28Cob1Z1T6vPKzxjeNSSQK9aV%2BznXLfUrdtDt9T1U%2BjeRxpa3iMvF5V41jcg%2Ftrx%2BLOd6VbaTqJlS7lMl5Iri3hHwRiQj920snhzODppWsii6zcGeSZg3po3NSy%2FD%2Fen4K9V542r0LyheaXcm5uaCCWNhxkl%2FvClOqcvs4rrn5g2enI9tpKi4uqlWck0rSvxP8Atf7HITazpccriNaLGD%2FozGnBVIJkZP261%2BFuWE4juLqSZo2pbJMjyitAQeVPwrjxbUE01reuXGvCKPUZJ2Cs0iqgUgFtunw%2FCKfDhWLXSzayW73jxuGElWgJUbUoSDhkLcXEjJDSJqkoSd%2BH7OGlh5a1HzDONIit%2FWvZEMkJRlQtGlKsCxVX4%2Fy%2FawWtILTLTTmeMmbTrm1jXe0nmMLOwH2nZlrWv%2BVhlBb6Lql09zpemrbx2cJe5WNqiRi3EKrfyj%2BZR8WJ6x%2BVPnXQrK41i9sljsrUB5pBLGzAVCgiNWLNufs4I8h6hp738sNxRBexei5BoBJWscg%2F2Xw4VZjoMllFbx635Y52k9mvPUdKaRjHIF%2B08PInhJQV6%2Fa%2BHOq6d5%2F0G7toZzK%2FBwKzBCVrTo3GpQ%2F62cYW21DQ%2FMUVrFH%2B5vyInPSIqzfE1f2afawkmtp42klt0f6uzuInWoDqrFQRQjkMJV9Q2ms6VfitpeRS%2BwYV%2FwCBNDg2ozzr5Y1vTI7V9L1PnbTDk8E4Qs7M1KKHBDp9GTfQfOdosSxw6txkp8UN0DxBG37WNea09UzZBNY%2FM3SfLcVrLrDIyXZKwm2bmW40DsF3%2BFa%2FF8eTW1uoby2iu7ducMyCSNh3VhUYEK2bNXNirs2bNirs2bNir%2F%2FS7%2FmzZsVdmzZsVdnE%2FwA9rKYXmj6jT9w0ctuT4OpEgB%2F1lOdsyO%2Bd%2FLUfmvy%2FcaXstxtNZyHosybofk32G%2F1sB5ORpMoxZ4TPIGj7pbPl%2BNPrERi%2FaG6H38MkPlXzh%2BjhHo%2BrmunCqwXFKmDmfiSRf27d%2FwBv9pMJBDcWN7JaXcRhuYHKTRPsVdT0P%2FNWB9RiWO6ag%2FdygSL7cuv3HKpkwImPcXoM2KGaHhy3B3BHf3h6B5qslXQooNOVof0a7Xdi6fDxBPKQQ78vT35xNl6Fr1l5s0yXSNZRHujHxlRhtMv%2B%2FE8JB9r%2FAIfILo2pT6XfLd8mmiKehPAzE8oT%2ByvKtOPWP%2BXKvrafSpl1XT2L6ez84LqLrE1a8WH7PH7NDlkMoluPiHRarRz05F%2BqJ5SHf1CT%2BZ%2FLlz5WvvTUmXT5jW2nP%2FEH%2FwCLB%2Fw2FauD8Sn6c67a3Vh5x0p7G%2BRfrAWskY6EHpLFXp%2FxpnLdc0S88t3ptrir27msE3ZlH6nX9pcmR1HJxFS3uA4oftDBlvcz2lxHc2sjQzxMGjlTYgjCVG6Mh%2BWD4ZRIvv3GC1Z7pdxHraNJCViukq13aswVAO9xCW%2F3Uf8Adkf%2B6%2F8AVw1t5NPkKabe3SyqKG2uIquYDX7Jc7ei37a%2FZ%2Fazm9tdT2dxHdWzcJYzyQ9R4UI7qw%2BFhkwhvDqECTaZbExyHhPbqKmCYivBf%2BKZPtR%2F8DgIkSDxVHqK%2FSyBFbjfvR8k1xY6gVtLb6vJbVWZbhvVLo38yAemYZF%2F4XCjW0McX986%2BX72Uem6V56bekVAYLu0D%2F8AJSL%2FAItjw7W0utVgj0%2B7k9DULc%2BnaSMaFkrvaz%2F8yXP%2Bpgq00iztbkaVcP8AWBer6NzEworLWjRsoJ4yRv8AEjr9jGOMDbn5y3UyJ8vcyfyHpl1puhy31zEE1e9cJfKxXjK8JMaTqfGZCrt%2FN9rCX8wda%2BqyabAGAM85aYA9FUcV%2FwCGyZXElvb20VgHb1LeNDEKmpKDhGS%2FfOK%2BcrhtU1G8vLZ%2BYsz6M0Q%2B0nBh6cwH%2B%2B5af7F8slsAAxG5tkesTm68saddo3GbTZ5bb1B1G%2FrRfrxGTW7LzPpcrahALfzDpir9biI4tJESAZowftJ8Svx%2FY%2F1cD6VMb3RNTsShJmhju4gVP2ovtH%2FgG%2BLBVnb6b5g0ux1JGWDXNDj4SEiongQcGjk%2F34hX7LfsYBukt%2BWba4gWHWmj%2BsWdpK0Dwqy%2BoWkonJE%2FvHZOfNcKdasnk80288TN6EEqxOXNWSOJiS7n2p8eB4bySw1ilv8Au4OQk47kKo%2BLv%2FLX7WK6jr4u9Uee1jItndiIiasyv9sV%2Fl3wWAFZBbvYajqsV%2FDSP1yITPUgSKH9SSinYcIRw%2BH7TSJhr%2BYOr2sOkWmnhHSK6YiRolFF5A%2Bn6ngJif8AWyP2ML6dbiRI%2FrqsAI3SRAsMY%2BxHKrlGhfl%2FefB%2B8%2F3XgXXYppJG1m2unkbj6Op2pUSKVAqksSNsif5L%2FFH9vHoUlrytdtDCgdmV7d5I2boU%2BINGT%2FLhpr3nLTKJ9UT19TB4zNHtCe3Jj3b2XIssp1Y8bUfFOQsix7SCSlAXpTmj4av5OnsdM%2BuzB3Zm9OWaNGD2%2FMgRTolP3sXP4Zv92fyYi6oITDVrG28x6NBeBvqWoWS1ivSeIikU8jFI%2BzLA6%2FGn%2B%2B3yFaq1pcXix28ZUGi3U2wadx%2FuziAEWv8Aq%2FH9vDLV7TzXeWrqbaWO4VhHeNHtFdCP7EhX5D4v9%2BfbwpC3liGgvIoVdSqt6pLPGr0pTj%2Bz8Xw4CUkEGil7RwRu9ZzwjPx8UZiKePShyb%2BRfMdhHM2kLEV%2BsDlDLQFncUCxcRX7WEF1DCs63zCs6r6c1Nkencr7rgXSpFsb5zCForepC9PiUEfZr4DADRVD%2BddUkPmPU4lUgVNtNG4qDw2Bof2kP2GyK1LECpY%2BAqTnUFhsta1Ce%2B1G3inuZCrySsgqwqFNRkwu9R8m%2BWIwscEHrharDBGjSH5tT4f9kcIoop4da6Vqt2GW0tbiQHbikbkH57UwHNFLbSvb3CNFLExSSJxxKsuzKwPQjOu3%2Fmq%2B1mFo1C2NmFMjhTQLGu%2FKRtumQfU%2FMWh3uoTXx0r1pJePKSWSnJlVU5lQNufHnhQxmOYxklSPiFCDvUZQKUIpUn7Jr0%2B7D8eYrBP7nRbUf61W%2FhmPmtkBFvptnEezCMkj78CpRZOY5w%2FBmWhBCjseuSyyn0pIpIr2CSr2gtbYTQt6aylufPp8PBWbCZPNurROskYgVkYOv7tSAVNRtg2780%2BYPNd5CdWuRJHEWkWONFjRaih%2BFAPljsqLeRrVh%2BjQt3GBRoXBBKAfFwag%2BLJ15B17yfealE2taTHcXMG63Eq%2FvbYL%2FvyH%2B6eFK%2F3iJyT9vAXkPygPNEuoxx3KQ3VrAHtYWP8AeOxpv%2FxWqijMv2WZMLdS8vBLx4pg9jqVqTFJIvwup6fvP5lP%2FDLillv5i%2BStPtL867pEEcem6hxkivLWjIk%2FevHb05ftZF9Okd5Et5mayvoWEtrdRmnCUfYmhYfzftp%2B3k3%2FAC68zLfx%2FwCFdWt1eElrbUoW%2BwGIrDPGv7CyU%2BLj%2B18eIeavKM3lq7WqfWNOkYizuXFeNesE9P8AiX7WNbqCncfm2fzRoUmg6zEqa7ZPDPPCv93eQRMC00A%2Fa%2FZaWLCHzf8Al7oOra5b6h5KuBHe3rj61Ywr%2B5Xlu0%2FL%2FdDfzx%2F8Qwpup4VWBrORhfW7iS0YkrJbkdRLKv2om%2FY4fbzsvkXU7fWNHju3jhj1ZAI9UEKhSJvEmg%2BF1%2BPEhXgPm%2B98y%2BWriTyxrM0iCMBo5VVW9aDoJIZm%2BLif5ftRthN5X8zxWXm%2FTZ9dHq6HH%2Foz2z1eNLd1Kq4TvwY%2Bo2el%2FPXkjTPPGkNY3n7q7hq9jeqPjikp%2FwANE%2F8AuxM8mazo%2Bq%2BV9UuNI1KL07y2PFlbdWU%2FZljP7UTfstih9Jy6d%2BVV3f2dja3ltHe6ipezS2mrzAFfh%2B2iMf2K%2FE2Xcfk5ojoTZ311bz1JEjFXG%2FitE%2F4lnlh3USLKCY7lSGLQ7UI3DA9mz0L%2BVP5wLqxi8s%2BaZRHqIpHZX0hAE9NljlPaf%2FK%2F3Z%2FrYqqal%2BS%2BoXb2EYvraWG3kcyvLG3IRuwY%2BkgPFW2PfJjZ%2BRruwuFa21iRYFACKFKsKf6r%2Bmf%2BReTUDxy6Yqo20LQQpE8rzso3lkpyb58QBi2bNirs2bNirs2bNir%2FAP%2FT7%2FmzZsVdmzZsVdlEA9cvNirzb8zvIf6bhOu6REP0tbL%2B%2BjUb3EQ%2FZ%2F4yp%2Bx%2FP9jOHagvqWsMw%2B1ETGwPWh33z1znI%2FzL8hqEutf0qP4JRzvrdR9lwR%2FpCDw%2F37%2FweRnGxTtNBreExw5DsD6Sen9F4gpphto2prYSSQ3Ketp11RbyAiu3QSoP50%2F4ZcK5YzG5Q7eGPEUyRpMykRSEhH7MV2IzGAkDY5h3OSEMkDjyCxL8beaZaglxoOpxTW7qLSWkmn3cX2eJ34nx%2FwApclMsen%2BcdIeC4QCZQPXjH2kanwyxe2RjT7%2B2e0bRtVHLT5SWhl%2Fat5Ozr%2FxWx%2B2uJWtxe6HqSw8wtxDvBL%2BxLGegP8yOMyceQEbcuo7nnNVpZ4JmMtwd4y%2FnD9bENU0y80C%2BazuRyTrHIPsunZh%2FTKil4kSIaj%2BGdZ1HTtM846SXUenKv2gN3glp1%2FykP%2FD5yO9s7vRb2SxvU4sh3I6MOzp7NkyOo5OKmSuGUMvQ9MMdF1m40S%2FjvoAHUfDPA32ZIz9pGH%2FDIf2Gwjt5Qh3%2Bw3%2BdcFnrir0GW5Kyx6hav6ltfqDHddW5Dbi1do5V%2Bw6%2FzZLPLbQ6nP8AWrpAby3oJGI2kXqkrfyyqR9rOU6HrKWHq2N8Gl0y6p66Lu0bjZZ4v%2BLF%2FaX%2FAHYmda8p2i2tk8sjrI7miTITxkj%2B1HJQ7huJ%2BLJR3K3snuq3fo2ruoqQC3idvsgD%2FWpnMJ%2FJ%2Fme4Msq2jC9tuUaXcXER3drJ1japH72NW%2BFmX%2Fit%2FwB5nTdIksyzW0c%2FryxVZgzBmCsa4eCZAKHDIWgbPDvK9jquh6xaW%2BtW81rDL6lozTKaFJQVAB%2BKtfhwvs47myvL%2BNQfTs2kD9UK8gQKlv2Xp8StnR%2FzQ1GW08u%2BlZO0d3dTxxQshKsKHm7hh9jiq%2Fazi6MxEiI5YOa3NyxJMjVrux%2BIrlctmQ3TS91E3jCG2jHI09R1qOdOgFf91r%2Fw2CLKFYxyJBIFZJOwHgMC2NryG3wRnq56t%2FZhusZiihu7cMWt5ir2zpyikUAU9T%2BYcuWR5pdPqiacQrWqXCzR1USVBAb%2FAHYp%2FnwutdVhtdTa%2FgWVBOAk8BcNG9BTqyk4c6rc2dxe2F3c2kcKJGI7qx4kUjpyR0B6Vr%2FNhBqc9haSxpHERaXRaWIt%2FuvcoTRa75PlyQn%2FAJZvYI%2FMUS2SNELiVTEygUjnVW9OlftI8hVWVsnSXVxfTGS6laSViCS2wodjsNs5%2Fo8rrL69qwd6pIrqAw%2Fd%2FYPw9OmSua51GVJ762swQXLCD1AiryNQrMfi2%2FyVwxKkIjW7%2BezjR7PTpr%2BUbv6WyUH8zfa6duOEPnjT7SEWmrxxkSXatazowA5KY%2BScgD9qIrxwQ3mPX7EetqFhAtqPtegzySAewA45F%2FMHm1dcRIGjMdvC%2FqRIKcuVCvJ2%2BR%2BzglLY2yhjnLaESfcEquburW8M5HpzKvIr1FaDl79cRbRr6S8%2Br245GCoac%2FCjKp67%2FwAww%2F8AL3li01qBLgPJO8f7t4wP7s%2FsE9fhOHer%2BVtfgg%2BvabcJe1FJrPjwZVUU%2Fdy%2FR9llyIFixuEzjKEjCYojmGM6jaajZ28RjBiEisJzULTi3iSKR0wboHkTXdWt21OLT5ru2A5RlN1k8SrsVWT%2FAGD5ena1aXA%2Bo6pD%2B9J2WcenL%2FlBJR8D%2FwCrnSfy880W%2Flyzl0m7uJZtJR%2BVmXSsltyNXhYD7cP7ScfiT%2BXDXcxosR1D8rvP2qWUdlZadHZW7kPcPcXEYeSn2EKR8%2BKJ9rh%2FNkRu%2FwArtc026ey1FooJo9yKs4IP7StQclz0brf5jeXtKhR7eYX0sieoiQHYL29Rv2Cf5T8WQ7W%2FMj%2BZrOKd0t1Ct%2B6eDk7xMaj05CaNxb%2FVwEgCy249PlyECMefJ4235fXQ63Uf%2FAt%2FXC7VvKsmkfV7iab1dPlISW7jTeFz%2BxJHWoPg37edQ5kyNDIvpyx%2FbQ%2B%2FQg91OB7hFCurIssUq8JoHFUdD%2Byw%2FU2IIIsMMmKeOZhkiYyHMF5g%2BhCNipcyFf3nwD7cH%2B%2FoT%2B1x%2FwB2J%2BziNyW01%2BNseVtMoMc4G7VH82TCbyxfWMxtUMlnDIPrOli55JIh%2FwCK2ZeMqthYYOMVxBNb%2Fu2HK%2Fsl6JU7Xlp%2B1xDfbT%2FdeFgmPknW7q0eC%2FsZ%2BOoWTck5ftRnrG9PtIfstnatY0iw%2FMfQ4%2FMWh8bfXbdSjxnbmyD47S4%2F5ky55gWcaZqDPp1yZo42Ppz8SvIePHOn%2BTfO2qaXML%2FSHiPqcRf2M9fTYf78Xj8XIfs4qoWsGsQ64Tpp%2Bo31zGbK9WYfFE8Z9TceI4FP9lnpNbWDVdGit9RRbiO4gT1g3QkqCW9jX4lzksE1z5382DU7WzEKoqq5UVAoOIeV9uR%2BL%2Fgc7RDGsMMcS%2FZjUIPkopj0UvHtQ%2FKnV9H1F9X8vXCajyYt9TuQoYLSijkx9OTj%2FlcMS8q%2BaLny95guoNYs2s%2FrfEXsLLxKyJXjLH4pxOdpwl1zy1p%2BvCOSctDcw19K5ioHAOxU1HxJ%2Fk4otNoZYriJJoWDxSKGRh0II2IyF%2FmN%2BXll550z4OMGs2oJsLynfr6Mv80L%2FwDCfayU6RYNplhDYNMZxACqyEBSVqSoIH8uDj0xV8NappV7pd7Np%2BoQNbX9qxSaJuoYfrr9pWwuSNg4NSHBqKbEHxrnrL80Py2g86WX1%2BwCw69aJS3kOyzoN%2Fq8x%2F5NP%2Bw2eXm0q8guZoblGglicpNA4pIroaMjfy0OKvpf8lvMusa5ol1ZazOLqXS3jiiuTvIyOvJRIf2ylKc86eM8y%2Fk35usfK2r3FtfyMtjqoRZZ3qRFNGT6bP4IQ7I%2F%2BxztF1%2BaHke01SDR31aN7mZ%2FTLRVeKNu3qTD4F5dMVZlmylIZQwNQdwRuMvFXZs2bFXZs2bFX%2F%2FU7%2FmzZsVdmzZsVdmzZsVdjXVXVlYclIIIPQg47GSOsaNI5oqgsx9h1xV4R%2BZvkA6VK2r6VH%2FuPlarIv8Aulz%2Bz%2Fxjb%2Fdf%2FA5EoJLK%2FwBFh0yOJU1AyrH6oABboqJItebtyJkjZP8AKVs73qnm%2FwAsyW89les7xzwtSMxMRKG%2BHghpx55w%2FwAy%2BWpbIHWNNjeGKFkDjlVw5%2BIPGwp8S%2FtcfsZGUauQHvdvpNWJxjhyyMZA%2BiXQ%2BUmMXNtcWVzJaXSGOeJisiHsRtll1uLf6rcMeKb20vVoW%2F6pP%2B0v7GHsLr5ptvq7mOPU4ElmEhHH1eCII4kp%2Bz6aO7%2FtephBNBPbytDcRtFNGaPG4oR7EZVKJHrhz%2B92JjDPA4cw3HMdR%2FSiiNG1m7028EiEC4j%2BGWMn4JU7qf8AjVslmvaBp3nLSFvbCiTKCY2P2opP24pKfs%2F9d5BTFFIys5ZGX9pKch9B65JvKmrSaVdevxea1m%2BCcEFQyj9tR%2FvxMtxZBIV8x3Oi1eknp5Ud4n6Zd%2F7XmskVxp11JY3sZikjbiyHsf8Amk%2Fs4caPay6peQadE6rJMwVXboF6k%2B%2FEDOn%2BdPIkfmSyGp6QA12qc4JFpSVPtcG9%2FwDjfI35a8qzaFNa3urKrXLmscfI1gP7NSv2n%2FmywxouIN1DWPKDW95DY6K0t5IfgnaUKgD05cl49I6H7TZ0bynBAmmjSVvhcz2qEM60%2BHnVRw9om%2BH%2FAFsItfhndg1q%2FpNdxmIuDQVU%2FECf8oYReSLk6R5nWBw1bmT6sSD8FGBb4h%2FNyXDyPvbsWITE9%2FVGJkB30jPKV1PpOsrBduRLFcPb3JY9VditW%2F2XxZ0%2B91O2spIoJWL3E54wW0Y5SOf9UdF%2Fmdvhzm%2Fm2w%2BoeapJlBVdRjWdCDsZE2cfeuHdvfQW0BngZptVvEH1u%2FlG6gj%2B5hH7Kr0xHc0pD%2BYg8wa1NDFZWjSWtsrCRoiGJcn46b1YdF2yFWemzxsW1CN4Ej%2FZlUofubOs2WpxpNGl2oMP2eSbEe5yTXGkadqFpJaT26T2860YEVBB6EHx8MeCzdpunlEtpHYQ38GqRrFNaPA8EgJcFa%2FGqhadao2XBGlmbe2uCzevbGeRo2PcNJx%2F2S%2FDht51s3sLpYLoH6ncIq21wRU1UceLt9Hxf8Hhj5f8pXerWc%2Bq2VrHqE1oqQWdvNLwUoijmJB%2FO9W4%2Fs8cjVH3LezEvM2mXWqyabNozB7SS1WJ5SwBrHWnLv8A3ZUbfy4onlazmtrKC%2BJlNoHFFNFbmQaHvtTJ9rPlzU7GOO6vrRbeNioQQMrRxmn938IXj4YE0fQLnXBdQ2062l1CP3SSry9Xav7og0%2F4LDsikps7C3s1WK2iWJB%2Bygpg5lNOX0YAVr7TL0xXsxcxsVlhktwpFPtLVWwbf%2Ban0e8tp7LTYrmyILlpyT6hoRw2%2Fu%2FTbdlw3taoK4v7a1dY2dW5GjQ1qVHj%2FwA24Vah5a03VlNxZEQTtvyX7LH%2FAClyfjyfo3nrTrbV9PRNI1i6gM8qRENC8gb05OUI%2BNR6nR1%2F4HIRqOj655Vu%2FQ1SBogT%2B7mU1ikA7pJ0%2FwBi3xZGwdizhOeOXFCRBSPSZNX8maml1MhNsfgldPijdD1H%2BS37S8s6ZLrEEZt2uEe1W6VWtLsg%2BlKGHIGOReS%2F6wOR9dWtRp11dSQ%2FWHt4Wk%2Br93IGyU%2Fysk%2Fk%2FW5INATT9Z0eAWczGT6gr%2BoIlfcgBxsf2vT%2FAGMYxMSeE2D07m7PqBmjEzjWSO3EOUo%2BY72M%2BYfLcd3FJeQRxyo7EzQU792GQ60bU9Hkf9HTAwts1jdVeNl%2FyX%2B2n%2FEc7ZL5XstVSS58p6kYZqVbTrysif6tWPrRr%2FspVzmuvaDq2jzsup2JtA5NCfjtmNf91Tjb%2FYyccJ8tmgJPqV4JJLJpI3sYLqJ2uY%2FtBgP91q6jpIy8Vw0sNNn8tG11m8KWNnqTrAums5YhTvyct%2B0R8fw%2FYb%2FWwLdRpfaVEkkPO50r1HhjJALRSg9HpX92%2FwCzgSW4TV9Hga5uppdVspFt7a0FOMNshHJm3VuZY8fV%2B0zfBlZHq3BNigOnm52LIfDHCYwMCJmVXI8IqMYnzI4f85l%2BsRQc7eSKVRPHKts4ruUkPAcv9R%2FiwbZaY1p5gtrHUoipjnjWZOo4lhRv8pGwcPL9pcROZo2S6ZkaXgyqoaFVk4hn%2BJ9uPqNxwVrmsWupvYzalF9S1K1lTi6GscsYYMpV%2FFWUNx%2FlwYoSjd8jybO0suLMcUsdmUQROxRrnH75PQtd8vaT5itBZapAJI0YPE6njJGw%2FajcfZzg%2FwCZWh3GhW%2Fr2wH1vSZ0DXlPia1m%2FuZuK7MV%2FuLj%2BbPQ1pdR3ltHcxGqyCtOtD3H0HIF%2BZFnb3k8EMqhxPBJHOh6MnIUDfSdssDq3zfqWmR6lFJqenRCO6iHK%2Fsk3FD0ng%2FmjfC3SNVudJuUvrKQJdQOrxc1DoafaSRGqrK%2FRlyU32i3XlnU4RHOYrF2Isr1hy9Jm%2F495%2F8AimT%2FACsKte0Zp%2FW1Kxg9KaL%2FAI6Nipr6bH%2Fd0X88En2lfFX0%2FwDlz5x0fzbpAlsIYrO%2BgAF%2FYxBVCOR%2FeIBTlE%2F7Lf7HJpniLyz5m1TyrqVtq2ly8ZYzuvVWX9qOQftI37WeufJnnHTfOekJqViwSZaLd2pNXhk7qf8AJP7D%2FtYFZJmzZsVdmzZsVaOct%2FNj8sx5ntJNb0NfT1uBayxrt9ajUfY%2F4zqP7tv2vsZ1PKOKvhz0CsbwMzqSSHPRgw2%2BL3%2FmwrlheB%2BD9exHQjxGeqfPH5Oad5lupdW0icabqU3xTqV5QTN%2FOyr8SOf2nX%2FgM59H%2BQfmu5nWC9ns4rbl8U6OzMB%2FkpwGKr%2Fyk%2FOBtOMPlnzTMWsyRHY6g5qYq9I5j3i%2Flf8A3XnoxWV1VkIZWAKsNwQehGc68sfkv5Q8vBZriE6rdrQ%2BpdAemCO6wj4f%2BD550VVCqFUUVRQAbAAYq3mzZsVdmzZsVf%2FV7%2FmzZsVdmzZsVdmzZsVdiNzNDbwSTXLBIUUmRj0A98WwNfPaJbSm%2BKi2pSUv9mh2pirAPON5pMWiStoiPJfM1bVI4yy1G8jDkCnwrhDrD6jFb%2BWbC7Wt36Ecl%2BCP%2BKiZHbtyJajf5WDvP9xZRaNCfKtxKb4zj04IOR403JKkfAOfH%2FWws84ajNd3EEklFuSOUtK%2FDRVXiKduQOFIeeatpk0F5PeaUj%2BlATLIYwSYhXd%2Fh%2ByleuLP9U12ykmiWO2vrWOW4n3PxgcAFXmaty3cf77%2Bx9jO1%2Flvo0EejT388SvJqDMjFxXlEvw8TX9ls5h%2BY%2FkSTyne%2FpHTVY6LdsVUj%2FdDt1hf%2Fitv91t%2FsMgRW4dxo9X4vDiySqcfon%2FO%2FoyYJsQGHfJL5FsrXUdZk0m6uTbR3UTvHQAgzRjkONSODla5GgcX0%2FUJNL1S0v41DPFINm8G%2BE%2Frym%2BHMD0k52txeLppjqPUPfF6l5X8xWlm995evZlguLSdkjR9gTWjrXovP4XTEPM6wwzKSKCT4tuoIO%2B3vke80xXU2tW13ZQ%2Bo15bq8npJ8UlELVp%2B3sMBWOsXOtWy2iEyXNuCyIw5Myf8VsaseFP7tvizL4tq%2BTzNUfvT%2BOU6npky9ZrciVAO5Xw%2Ba8sis01rp%2FmC01O5YrSWJ4kXcyFiB9nsK%2Ft5fljzLXzMlg0bRQujQESbVlDcl5IenL4kyeebPIp8y6j5e1DSEjX0XWO4QsqKYk%2FfRgV%2Fb5gx8cHMe5lGUom4mjuPnzSW%2F1OfVL6R7sJW2keKFUGygGhIJ3PKmNVy%2FTAk1pdWc9zBdrwuElcTIezA%2FEMj%2FmjXbvQtaW3tEBgCJM6PuCHFQExtizSKI1DHJv5VuxKrWMp%2BwC8JPh%2B0v0ZCNLvrfUrGG9tjWOVQfkf2lP%2BqcPdHme3vIZV24sKn2Ox%2FDJBSyzzBplrqWnSQSxLI0P76EsoIDqO3%2BsMgmlaxceUdRXU7ZS9jJRb62XoUP7QH80f2k%2F4HOgXdyVYgHp1%2BWQW8g4TzQyoQpJIVh1VuhGJCh7FG9hrempKhW4sryMMp6hlYV%2B%2FOWa%2FbXfk%2FU0ZJOEL1ayu2%2By1P91udv3if8OuCPJ%2FmhPL1xaaJetx0y4rFFMx2imJJSvhHLXj%2Fr4N%2FO9G%2FwAJ28o%2FYvY69%2BqOMgdlYzqPmWy8xr615aouoxAA3dk4dJB0%2FfRGjofBl54U%2BnCySRNSa1l%2B2vQq3861%2By65GPKcztqxjY7NEw4jYbU32zoNppF1qfrw2JX6yierHExoJivWIf5fH7DYYnZaY3pN9N5N1n9JJEbuIwyRoisY1kDbpVu3F%2FidM7xpl3Y%2BadFt7m5tle3vIUkaCYBx8Qowof5WB%2BPONG0jvLY28qlRUgqwoyOOu37LpiXl3XbjyZqk7XglmtZIfTEKNQGh5REFvsxr8XLjgIrdWQedvy7GiQPrfl4SNaxVe5s1q7Rp3ki%2FaaNf20%2B1kT07WGCqwcMjdJFNQc9C2N9BqNvHc29WiljSVGI2KyCopkFv%2FwAotHu9cOq2d1JYW0x53dhAo4O53JQt8MYf9ocMFyB23ZwGOQIkeCQGxqwfIsatNQSQrIrmOUbrIhoQfEEb5LLHzPP6X1TV4RqFo4ozFVL0%2FwApD8EmFms%2FlxeWBNxoEpuIhubSQj1B%2FqPsHwo0%2B4ltUmW7Ro5ojRo3BVhT2OWCi1t%2BbPJ1pFCfMXlJgtl%2Fx92VCyRHuwjb4kX%2Bdf2ciOlG2huhcX2n8JI2BE8FHHzMTb%2F8TVc6FpWutaySPblJEmHGeCQVVwP1ZGtcsIbe5N3p0Ui2MlWaM7mB%2FwBpQ4%2B1F%2FLkTGulhnDIYnYprf6tYaxaxxCDlxIaG7iko8Z7kGtWr%2B3ywxsdGl1WwitzGbkgkRzugDGnRm34R0%2FZbIJBZwxv9YhAVx8Qp0JPiBnafJl1eXekLNdyrKOXGNgADQD4g3Gg%2BE7Y8WyTLfiHNJbbQvNHle%2Fe50eQX%2BnXAD3Vg53EoWnKImhQmn7Pwv8AyYRX9xql1dmXWOS3XHiqMnp0QEkKq%2BA5fazrQwHqOmWmqQGC7j5D9lxsynxVsFsLeO39hb39tJbXMYlhlHGSNuhB%2FUffOZXy3%2Fl7UV05n5MgrpN1LTjLCT8VncEj%2FYf5Odt1nQbzR3JcerasfgnA2%2BT%2FAMrYB%2F5V3Y%2BdtAvhefubrn%2FuNux1jkQUYkD7Ubnir%2F6uEq8D1rTFk9XUdNgaHix%2Bu2XVoHPcD%2FfbfstnQfyAeb%2FFktC%2FptaSF1VgFNGTiXFfiA%2BLj%2FlZC9WuNd8p6kNM1e04anYsY2mY7TW%2FZG2pKjfsS4K003mkCHzl5WmDLazercWoB9WCvX1FB%2FeQt9lv8nIofYNRl5FPI3njTfO2li7taRXkQUXlp3Rj0Zf5on%2FYbJXirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F%2FW7%2FmzZsVdmzZsVdmzZsVdgHVY9PmtHi1JlW3cgEuQBUbr1wdgPUdNttTtzb3IJWoZSOoYdxirzHztP%2Bi20aTyvdm6uDcJSCMJIPTVhRWKj7Bbbi2B9b8keZrnV3FvberbsQsVwHUKFJ6tU8hSvhk%2B%2FwAPGy1GwvLOkqK5ju0cIP3YQ%2BnItAPiif8A4nkkphKQaQOk6eml6ba6fGQVt4whPif2j%2FwWO1LTbPVrGfTr%2BITWtyhSWMjqD%2FEfs4MzEVwIsg2Ni%2BVPOHlm58p65NpU1XhP72znP%2B7IWPwn%2FXX7En%2BVgW28s63f2kWr29m76Ys6xyXQpxUggNUfa4iv2uOen9c8t6J5it0t9as0uo4zyjLVDKf8l1oy5EL7TriGW30Lytp7Q6Dbs0d7xI4Oz%2FFWPlycmNvib%2BfKziufETs7WXahOn8Ph9dVIn6a7%2FewDzPd3ukNplzYtxtEhW2adVBKzRktTkalOdf%2BBw60bydZ%2BaNR%2FwARaPq0VjOIw509bcLIkw%2B16yh%2FjRm%2F3an28de6dLYTXGk6xDztpx8aHoyn7Lqfb9nI3YX%2BreQ9ThS3pcWhcvazMBWSE%2FbgaQ%2FF8P8AJlrrDvunvmryB9buFvzAtnrkQD84z%2B6mKHkGRtu%2F82HWnO%2BoadLpU5aC4ZeUL9Gil%2B0jKR0aOYZOLO80jzho0d3Zv6kMn2WFPUhlGxVh%2By6H7WQWaO80rV1hvwBMjcfUAoskTbLIP%2BNsIr5oSu10tfOt7Ikt4th5ijQC6hkSsdz6fwfWIiCCr7UmTj%2Fl5JG%2FKTSdTjg%2FxDILh4UCUgHD7P8Axafj45GPMdkYtRkvbWf6jewEXNhcr9ppT9pEVatJWnxJx%2BznRPIfmz%2FFujfW5Y%2FSvLZ%2Fq94gBCmQCvNQfiVX%2B1xb7OA2NleaX%2Fkr%2FA19Jb2rO%2BlXbc7UvuFbuvL%2Bb%2FiWC7M7HOuarpdrq9jLY3i8o5Bse6t2df8AKXOP6jbaj5dvpLK5USBd0fcB07Ov8cMT3qkP5l6trkWp2kNi8sVr6CShoyVDyH7RJHXj8OQpfNfmCN1%2BsXcsnBeCrOfUAUdFHP4uP052ny%2Fq2g6veR6T5hgX94Almsp%2Fdsw%2F3W%2F%2BU37GSHUfyp8n6hUpavZse8DkD%2FgH5p%2BGCXNeTwM%2BbzeWxtr%2B3SVHFGMZKMP8oBqr%2FwANh9qv5gpr3kVfLV9zk1O3uIjFOwrzt4w1GZhUeqn2P8rJD5i%2FJJrK2lvNKufrSx%2FEYCnCXiOtCpKPx%2F1M503lq7tpDxDNUUUGlPwyJtWQflfpMWseaFtLgssZtZnDJ1VhxCn3651e38qanp%2Bs2ZX95brKG%2BtR7UVfio6%2Fs16YS%2Fk3pGmWxvNR%2BspJqfEW7WnR4kryLFW3PqN%2FL%2FLnW8I2UsW8x%2BVo71n1LT0CXp3mjGwlp3%2F4yD%2Fhsgl9oyXUaxX1u8LoCY2dStV%2FaG%2Fgc7JTEbiztrr0zcRrIYmDxlhWjDJCXQoeVeW9Rv8Ay1PKL25kuNO9ELEo%2BIjgaoKk%2FAqJyzqtrdQXkCXNs3qQyqGjcdCCK7ZFPMPl1Yg95aLWA7zQjfjXqw%2FycB6JrNzY3BW7uAbARKgUqSQVIC8KfCq8K8vhxq9wlntK4DvtJ07UgBfW6TU6FhvTwqN8FRyRyoHiYOjCoZTUEHH5FDC9Y%2FL6wuSZ9Ic2M3X0tzEfo6p%2FscL9B%2FLy8XVJNS8y3Yuo41aKz0%2BEkQrGwoWlO3N2zomVTDZV4l5n8s3Plq94oS%2BnTk%2FVZj27%2Bk5%2FnX%2FhsMNI893Wj2ht2thNVSEp8PGSlBK%2F%2BSf286nqOnWmqWcllexiSGQbg9QezKf2WGco13yhdaVMUT95A39zL0DDwPg%2BIZc3pPlzU5dStGa4IaeNgGZaAFWHJWoMOc5v5E1B7Wf6tcAoD%2B5PLb3jP0fYzo4xIYrZIo5UaOVQ6MKMrCoIPtidrZ29lAttaoI4UrxQdBU1OL5sCsO8%2FwD5e6V5708RXNINQgB%2Bp3oFStf2JAKc4m%2F4XOIWX5R%2FmVoOpummwIySAxNMssZhZG2q3Mqen80eeoKZqYq8l8h%2Fk%2FceW9Qt9Z1DU3W4gPJLS0JCEGvwTytT1U3%2Bx6arnWs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F1%2B%2F5s2bFXZs2bFXZs2bFXZs2bFXUzZs2KuzZs2KoXUjILG49E0k4EKetK43TYkhsoQp5EqGZvEnvgp1DKVbdW2I9jhDdaDqT%2FuLLWJbOzP8AulEVnAPVVkb4hiqB8wWUXmS%2BXTYWQi3ikZphuUlqOKmn7P8ANnOtRsiPW0TWUKqD9ofajYfZkQ%2B3%2FDLnYtH0Wy0W3aC0BJc8pJHPJ2J8ThV5u8tJrdoZrdQL6EH0z0Lj%2BQ%2F8a4bTbxXQPMGq%2Fl9r7mSs9jcUN3Cv2Zo%2B1xD4Sp%2FzZnaNYhtfNmgRaho7JcMwE1pKCASP246%2Fsn%2BZf58gsugWWtaPFp1xRL21qFkFCyP3U96YR%2BUfMt7%2BX%2BqvpmqEtpUsnGeOtQjE0FxD%2FwAzUxOy%2B5kHmGCOa1s7%2BVeEsFYpHI3SvwyBvDi647RtH1by3qEXmPT5VlgnWmo2C1Amj6rIrH4RMnVcCz3FvJ5x1pIqX2h30cVy3ouv2nQEeka9XZW5ZJNPult7dbZpTLasP9HlYUalP7uVf2JlH2v%2BEw891ZzY3tvqNtHdWz84pBUHoQe6sP2WX9rC7zJoEOu2Xp7LdRVa3lPY90b%2FACHwi0IahBqtdOo9hKf9LjY0Vf8ALU%2F78ybZFD591jS3R3gnRo5Ymp4MjA9snHkr8weRi0LzJII7pQEtdQc0SYdFWQ%2Fsy%2F5X7eH%2FAJw8uLqMDX9qg%2BtRL%2B8Uf7sQf8bLnJbuyjcNFKnND1U%2Fw8Dh5pfQo3Gc487eVFhMmq2KfuHNbmNR%2Fdsf92KP5G%2Fa%2FlyPeXPM%2Fmfy2Y4pmk1PRqfBBKpNwiDr6E3%2B7OH8j51%2B1ubXU7JLiKklvcJWjDqrDdWH%2FEsV5PntoFiuUuQOFxFvHKpKsPpWnLJZ5b8w65p1xEguXuIp3q8d07P1%2FZVm%2Bx%2Fkccbr%2Bhm2125sbPggX95AZq8FRhy3p%2FL8Srk181aDHqempqtvRLu2hDngPheMDmV%2F2P2ozjsrKLW5ju4Eni%2By4rQ9Qe4OL5D%2FACp5hgu7iOwkkUXFxD6yx9DWM8HNMmGJFFDRFRQioPUHIhrmgrAXubdK2z19WOleFepH%2BRkwymCkUIqD1BxBpWIaNq09o31WURrZoiem32ePEheCIB8VV%2BLJcGB3BBHtka1LQXVne0XnC9eUQ%2B0tevHxXA2lxazHfW620rx2atW4gkSqFOPCgdvjDClVwkA7hWYZswzZFXYnNBFPG0UyCSNvtKwqDimbFWE6voE1hIbu0Be3BrQVLIP8rxXJRo92b3T4Z2%2B1Ti3zG1cGkAih3B7ZSRpGvCNQqjsooN8NquzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Dv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxViPmbyo91cfpvQiINVT%2B%2Bh6RXSD9iXwl%2F33N%2FwWQzW9AXXo%2FXaFg7L%2B%2BhK0mjdBuafzACjrnYcDTWNrPKk8kYMsZDI42NR40w3tRW3z1p2l6ho%2BotLAfXVE5OgDVljA%2BLj%2FxZGv7OS2BbeaRbscpI5YuLQq3ETIfiQcv2ZVP90%2F8AsMk3mi1FjqBnt0CpcIHIUU%2BNT8RFP5tuWRDTFEulwPaP9YjVSfVjBIFGPM%2F7BvHCOSXqXlu6trnTI1t7Y2Zj%2BGW1YhmQ%2BJcfb5fzYcZBPKl1I7GRXHKGi3AY%2FajPRvdsnQNd8BFFDjnL%2FPWhJaXguIKRw33KngklPi%2F2Pxcs6jkY892Ul3onqwoZJLWRZeAHIlT8D7fJsAVBaLpUeueS9PtriYPcWykQXkfZ4yVDD%2FJP2WwX5X1NJ2NrQIGXkoXpzQ8JP65GPJfmOTR5G0zXLmGCzlYmzhZPSeE05ceP7UTjfliOhagkWs3CW0gkUXbPZjoZVkY8kQHr8OSHUJZD540vmYtVRBIAvoTo260JrGzU7cvhzaJ5jgm0t9O1u7giu%2FRdY%2BJ48oyCg%2Fd9QyfZ%2FwArJhPBFcwvbzqHikBV0PQg9sgt%2FwCSLpJy9pxniPRiQsgH8rfzYB5ql%2Fkq1hXU4ZigNwjsizftemU3Uf5Jzp%2BRXyloF%2Fpj3FzqYiR3YraxREsyx%2FzTOaL6j%2Fyp8CZKsSUOzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Hv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVg35iPrMEFld6XbR3CRM%2Fro1fUIIHwx%2Fs4U%2FlrqOkz3c1lYQmENFJI8DCgRzIDIg5b1JavTOiajZLf2cls1KsKofBh9k5ydpJPL%2FmzT9ZcBIWY2t5sFoD8Ls5C1%2FwCDkyQ5KyjWdLGi6ml3brSyvKxuo6I58P8AJPVclOiXZurPi5rLAfTf5fsn7sE3drBqFq9vLR4pV2I7dwy%2B%2BF2j6Xf2F5dS3EqNbyhViVa8iV%2Fbfag6%2FZXBdj3KnWURXLzYFYV5n8n3us6ut7btEITGsbc6hl418B8WFd75Ym8svHqVqPrQiCsJSPiikH2jQfsNnScayhwVYBlOxB3BGG1Sby7rf6YgZ5KCZftIBSmHeFllodlp95LeWqlDMKGIfYB7kDDPE%2BSuzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F%2F0u%2F5s2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZFfM%2BipLW%2BSJZAf71CtaMPsygf8SyVZRAIIO4PbCDW6pP5YvDd6Wiu1Zbc%2Bi%2B9Tt9mv%2Bxw5xG3tbe1Vlt41jDHkwUUqfE4tgPNXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Pv%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV%2F%2FZ");background-position:center center;border:1px solid #0a0}.yui-skin-sam .yui-h-slider{background:url(bg-h.gif) no-repeat 5px 0;height:28px;width:228px}""" + + self._test(input, output) + + def test_yui_dataurl_base64_eof(self): + input = """ + div.base64-singlequotes { + width:100px; + height:100px; + background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QAAAAAAAD5Q7t%2FAAAACXBIWXMAAA3WAAAN1gGQb3mcAAAFrUlEQVRYw%2B2Xz28kRxXHP%2B9Vdc8vj2fG9uIkm2yUeFGEhNCKQwBpj1yRUBAnpJU4ceZP4MxfkBxy2GO45bQXuEGQohUKigQ%2BsBBE1sbYXv8Yz%2FRMd9fjUN09PV5nFZA4kZZa79W3quu9er%2FqNfy%2FPwJgZty%2Ffz%2FZ29vrpmmqhlWTgmFg1UpbfWhYhG6Yq2cFi%2FNrj9nJyWnx%2BPHjeafbMTEzPvjVB9%2B6d%2B%2FezweDwV1BfHubivkC3lZya%2F4m7Np8UZYXhweHH7733rvvC0Kyv7%2F%2F7p07d34qIjyvr63RNb4l4CbsRUrNrq6OfvfRRz%2FxDx486A%2F6g7vXhX9ZIf%2Bp4JomaToZj8d7Pk1SFRFXb1aWodq09l%2F9YZu%2FCWMVL9e%2FaVFVQVUREEG8r3VUEZ4cX%2FHL30%2B5shRtAhFq4wggNb6GrXgVa2K2jYtACIHv7Sz54TfHjWWagBOBy0Xgt%2BcjLtwQxVCpNwURQat1DdYaT7pwdwKDFA6ncHgFizJmSb3WLPDa7PMIVlb2daqZxdONUnAetEojEdBGiXXhTmAjhZ0%2B3N6AlzeEROH1TXiWGf%2BcwmkWZYnAfAkdx1pq%2BhUbT9z3IElUJlVhI4WsgCLUyggi4MQYd4VXNmCYRqXOspV7UoHbQ3hpEMeLAk5mkFYRYlWI%2BHZ6CTDwkKSQqrA7gFeHMF3C6VyYLmFZGiKCE6GrMFsaWb7yuVOh66N1FkUUpBJ3TyvFovToBl9XMbNY2bZ70BsKfW94hbyErosmXpQwy4VgkCjMizjvRRik0EtiLNwagBf465lwOqtkETEn60XT1%2BlTazrpKlR%2BKkooy3Xfb3Wj3wGWJRQhnni7B53q5FqZ4%2FUR7PYhGMxyuMwgWVqTxrYWAwZYQX78J5i8ie9txSAkoOqqQAqk3pBgKLDhlY5XVKPCi3w97YRoKSFaceCgF2hqSXRBPQAIOcXRH8hP%2F0y6%2BRpWLrEyg3SbS9tmzggzJQSLQZo4xoOE8UbCsJfQ7zi6iaIizZ5OBVfXDKPireWC%2BvAYWOBqeszl9Jg03afbH6OqGH%2FBSUrqbrFIXiHoiEDC%2FPySi2d9nnZHJE7opY7dcY%2FdcZej84zpPGfY9Uw2ErYGCf2O4mUVEzELWtqUxZKjf3zK2cURzicMRy%2BzvbuH8ylFPqXIPqNczClKY1mUZFcnSDKkv%2FUNuptbWG%2BLw%2FMh%2FzrYpKBHKV3ONOXzYxeV20z49qhs7gaMWIjqQQiBxVXB4irg05yz4u%2FMpieoJuTLjDJfUJYBzBAFUUHkhMvLvzFPuvQ2JvQ3duh0R%2FjuiDQdIn6MJdvkxTafZUPuaN6UaOo6UNtgNl9weboguwqoA5fCjEusNCzE8LJQFRuNrzpQD%2BrmZNM55%2F4A5z1J2iHtDEg6fZK0j0uHlDrh2N4i3Ptuc%2BH5tjp5HliWQmmOUAhlYe0rqUqWSEOhcZ05AvE1UdColSQJaHWlSUYgJy%2FO2fFfo%2F34piphFOZ4OrvNdL5EJZbcmE8OQzFxIJEGEgIeE1%2FhrjJLxZcOrIVpVC7TW00WmLWyAMDEs%2Bi9wVJDtG17Y21vVmNxLKqIOkQqqoq2qHORmgj9UQcza7mgFm4GmiDjO5C72DSoQ6uPVTW%2BzuFUcQ2teKd4pzhd8b6Nu6jg7vZsrb1rClEIxhu7Q37x47fIy3j%2Fi8iqqlUuEWTFN%2FOrMU3fEEuhUM8FIPDqZgcRIVhsAXxtjGDGS5M%2BP%2Fv%2Bm7yopVrRVsPabsW%2BYG1T%2Fy3KahqSg6cHRb5cTmt5ZXhxQ7nS6yZsVWOajDFbw2JSCSGEIsuyC%2F%2Fo0aPp%2Fv7%2Br9NO5%2F5gMOhJLNn%2FpQLrLfw6tkKKorCDg4NPP%2Fnkjx%2FLOz96h2enzyZvf%2BftH9za2fm6qLrnO9tGk2vY86f%2FMliWZRdPnjz5zcOHDz%2B%2B%2Fifimorzv31C9X718G%2FYrCYSNJa5LgAAACJ6VFh0U29mdHdhcmUAAHjaKy8v18vMyy5OTixI1csvSgcANtgGWBBTylwAAAAASUVORK5CYII%3D'); + background-position:center center; + border:1px solid #00aa00; + } + div.otherdataurl { + background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kFBQ0WDWwqwjwAAANMSURBVEjHrdZbaFxVFAbgb2aSTG6GTi6mVIwxNxF9qFI0RQnFUqiYamutVutLa2t9EY0oPggFoYgPRR%2FaghYviA%2BiIAYvmBJKoYWiiBCigVTT1FisbUhrEtNkJpc5PuQkjGEmJqkLFmdz2Hv%2Fa%2F3rX3tvlm95oS%2FLokuZtIpbdvAs7KFtL22wjb3V1C41upy2ke1DXC2k%2FBjv1HHXDrbkEamg7lX2P8QTldQ2UtfOB8uiJsHNiB%2Fik0GmO%2BgZIxgnGA59nGCMoJPeQaYP047iBDXZEohkAYof4%2FNyKlZRdR%2F1ASZCnoOMhWkUheMz9F1laJSRZ3gEqVw1ipZQjcoBRrbT3Ez9OJLhZkHG5CD8l8Q47qXhMZp%2FYxhVFaxBLBtQejdtA%2FTtZPMIJnOknI2WSYzicTYN8OtTvICZbECxdr5Pkm6iPL0C3c%2BgkfIJgi%2F4LnP%2FudRKD3K4jf1VJGLEAiuz6VnA4AGam1h7gpNIzSFe66D3NurLKVhJNkHo07N9V9BE3XHOYmyeuirqG1l9mdHgOkDSGd8%2FGWtg9Roa56lrYdchDtRQPLlCkEywKVRScDfrurnwC2diiPTRe47iVtbnLZDxckGCkKYpPM%2FRr3kbyRhsYOtRDiQonFoBSHrBOI18rOeOPvr76YrCeUbf5fTvjOddJ0gQ1uMPku9z6hwjiEZhgOMn%2BaaUeHQJIOkMD7KMA5QQP01HP18hPbfvDTvZPETqb2YiS1BWrozM6jk9SPJJHkZZ5qFagtoaNnbyZg1FE4sUPRdlAQpwkdSjvDZAJ%2FoxNt%2Bw6NlGbQVFl5iKLKKsXCAwyFQZ8S3ciu65ho1lBJ5%2FkZk3OBGjpJWGmVCmsjTkQvA8JHCUU7s5eImevzg%2Fd7BGFhzCARIf8uVN3J5Heh1VM%2BHlko2y%2FHBxF0NJolfo38eDuJJxbf0ro%2FnMurh8hM%2FupH4tdT8zciOFsTC8SAgQoJfRxKzCuvfw0k%2F8MDx7xqUyez%2BS48ESIN7Ky6OUpfAtrxzhx03UR4m0c%2FZF7tnKW2mma4l9yuthTSIZIvxPi6EcpZW0PM0xtOzjo%2Bf4GPfv4r1qNqAUFYs9diJLBC1CIa7FZx8fUlwI22LuNv%2FfLbKMAOftH9TwRXg6%2FiCDAAAAAElFTkSuQmCC"); + } + """ + + output = """div.base64-singlequotes{width:100px;height:100px;background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QAAAAAAAD5Q7t%2FAAAACXBIWXMAAA3WAAAN1gGQb3mcAAAFrUlEQVRYw%2B2Xz28kRxXHP%2B9Vdc8vj2fG9uIkm2yUeFGEhNCKQwBpj1yRUBAnpJU4ceZP4MxfkBxy2GO45bQXuEGQohUKigQ%2BsBBE1sbYXv8Yz%2FRMd9fjUN09PV5nFZA4kZZa79W3quu9er%2FqNfy%2FPwJgZty%2Ffz%2FZ29vrpmmqhlWTgmFg1UpbfWhYhG6Yq2cFi%2FNrj9nJyWnx%2BPHjeafbMTEzPvjVB9%2B6d%2B%2FezweDwV1BfHubivkC3lZya%2F4m7Np8UZYXhweHH7733rvvC0Kyv7%2F%2F7p07d34qIjyvr63RNb4l4CbsRUrNrq6OfvfRRz%2FxDx486A%2F6g7vXhX9ZIf%2Bp4JomaToZj8d7Pk1SFRFXb1aWodq09l%2F9YZu%2FCWMVL9e%2FaVFVQVUREEG8r3VUEZ4cX%2FHL30%2B5shRtAhFq4wggNb6GrXgVa2K2jYtACIHv7Sz54TfHjWWagBOBy0Xgt%2BcjLtwQxVCpNwURQat1DdYaT7pwdwKDFA6ncHgFizJmSb3WLPDa7PMIVlb2daqZxdONUnAetEojEdBGiXXhTmAjhZ0%2B3N6AlzeEROH1TXiWGf%2BcwmkWZYnAfAkdx1pq%2BhUbT9z3IElUJlVhI4WsgCLUyggi4MQYd4VXNmCYRqXOspV7UoHbQ3hpEMeLAk5mkFYRYlWI%2BHZ6CTDwkKSQqrA7gFeHMF3C6VyYLmFZGiKCE6GrMFsaWb7yuVOh66N1FkUUpBJ3TyvFovToBl9XMbNY2bZ70BsKfW94hbyErosmXpQwy4VgkCjMizjvRRik0EtiLNwagBf465lwOqtkETEn60XT1%2BlTazrpKlR%2BKkooy3Xfb3Wj3wGWJRQhnni7B53q5FqZ4%2FUR7PYhGMxyuMwgWVqTxrYWAwZYQX78J5i8ie9txSAkoOqqQAqk3pBgKLDhlY5XVKPCi3w97YRoKSFaceCgF2hqSXRBPQAIOcXRH8hP%2F0y6%2BRpWLrEyg3SbS9tmzggzJQSLQZo4xoOE8UbCsJfQ7zi6iaIizZ5OBVfXDKPireWC%2BvAYWOBqeszl9Jg03afbH6OqGH%2FBSUrqbrFIXiHoiEDC%2FPySi2d9nnZHJE7opY7dcY%2FdcZej84zpPGfY9Uw2ErYGCf2O4mUVEzELWtqUxZKjf3zK2cURzicMRy%2BzvbuH8ylFPqXIPqNczClKY1mUZFcnSDKkv%2FUNuptbWG%2BLw%2FMh%2FzrYpKBHKV3ONOXzYxeV20z49qhs7gaMWIjqQQiBxVXB4irg05yz4u%2FMpieoJuTLjDJfUJYBzBAFUUHkhMvLvzFPuvQ2JvQ3duh0R%2FjuiDQdIn6MJdvkxTafZUPuaN6UaOo6UNtgNl9weboguwqoA5fCjEusNCzE8LJQFRuNrzpQD%2BrmZNM55%2F4A5z1J2iHtDEg6fZK0j0uHlDrh2N4i3Ptuc%2BH5tjp5HliWQmmOUAhlYe0rqUqWSEOhcZ05AvE1UdColSQJaHWlSUYgJy%2FO2fFfo%2F34piphFOZ4OrvNdL5EJZbcmE8OQzFxIJEGEgIeE1%2FhrjJLxZcOrIVpVC7TW00WmLWyAMDEs%2Bi9wVJDtG17Y21vVmNxLKqIOkQqqoq2qHORmgj9UQcza7mgFm4GmiDjO5C72DSoQ6uPVTW%2BzuFUcQ2teKd4pzhd8b6Nu6jg7vZsrb1rClEIxhu7Q37x47fIy3j%2Fi8iqqlUuEWTFN%2FOrMU3fEEuhUM8FIPDqZgcRIVhsAXxtjGDGS5M%2BP%2Fv%2Bm7yopVrRVsPabsW%2BYG1T%2Fy3KahqSg6cHRb5cTmt5ZXhxQ7nS6yZsVWOajDFbw2JSCSGEIsuyC%2F%2Fo0aPp%2Fv7%2Br9NO5%2F5gMOhJLNn%2FpQLrLfw6tkKKorCDg4NPP%2Fnkjx%2FLOz96h2enzyZvf%2BftH9za2fm6qLrnO9tGk2vY86f%2FMliWZRdPnjz5zcOHDz%2B%2B%2Fifimorzv31C9X718G%2FYrCYSNJa5LgAAACJ6VFh0U29mdHdhcmUAAHjaKy8v18vMyy5OTixI1csvSgcANtgGWBBTylwAAAAASUVORK5CYII%3D');background-position:center center;border:1px solid #0a0}div.otherdataurl{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kFBQ0WDWwqwjwAAANMSURBVEjHrdZbaFxVFAbgb2aSTG6GTi6mVIwxNxF9qFI0RQnFUqiYamutVutLa2t9EY0oPggFoYgPRR%2FaghYviA%2BiIAYvmBJKoYWiiBCigVTT1FisbUhrEtNkJpc5PuQkjGEmJqkLFmdz2Hv%2Fa%2F3rX3tvlm95oS%2FLokuZtIpbdvAs7KFtL22wjb3V1C41upy2ke1DXC2k%2FBjv1HHXDrbkEamg7lX2P8QTldQ2UtfOB8uiJsHNiB%2Fik0GmO%2BgZIxgnGA59nGCMoJPeQaYP047iBDXZEohkAYof4%2FNyKlZRdR%2F1ASZCnoOMhWkUheMz9F1laJSRZ3gEqVw1ipZQjcoBRrbT3Ez9OJLhZkHG5CD8l8Q47qXhMZp%2FYxhVFaxBLBtQejdtA%2FTtZPMIJnOknI2WSYzicTYN8OtTvICZbECxdr5Pkm6iPL0C3c%2BgkfIJgi%2F4LnP%2FudRKD3K4jf1VJGLEAiuz6VnA4AGam1h7gpNIzSFe66D3NurLKVhJNkHo07N9V9BE3XHOYmyeuirqG1l9mdHgOkDSGd8%2FGWtg9Roa56lrYdchDtRQPLlCkEywKVRScDfrurnwC2diiPTRe47iVtbnLZDxckGCkKYpPM%2FRr3kbyRhsYOtRDiQonFoBSHrBOI18rOeOPvr76YrCeUbf5fTvjOddJ0gQ1uMPku9z6hwjiEZhgOMn%2BaaUeHQJIOkMD7KMA5QQP01HP18hPbfvDTvZPETqb2YiS1BWrozM6jk9SPJJHkZZ5qFagtoaNnbyZg1FE4sUPRdlAQpwkdSjvDZAJ%2FoxNt%2Bw6NlGbQVFl5iKLKKsXCAwyFQZ8S3ciu65ho1lBJ5%2FkZk3OBGjpJWGmVCmsjTkQvA8JHCUU7s5eImevzg%2Fd7BGFhzCARIf8uVN3J5Heh1VM%2BHlko2y%2FHBxF0NJolfo38eDuJJxbf0ro%2FnMurh8hM%2FupH4tdT8zciOFsTC8SAgQoJfRxKzCuvfw0k%2F8MDx7xqUyez%2BS48ESIN7Ky6OUpfAtrxzhx03UR4m0c%2FZF7tnKW2mma4l9yuthTSIZIvxPi6EcpZW0PM0xtOzjo%2Bf4GPfv4r1qNqAUFYs9diJLBC1CIa7FZx8fUlwI22LuNv%2FfLbKMAOftH9TwRXg6%2FiCDAAAAAElFTkSuQmCC")}""" + + self._test(input, output) + + def test_yui_dataurl_base64_linebreakindata(self): + input = """ + .yui3-skin-night .yui3-dial-ring-vml, + .yui3-skin-night .yui3-dial-center-button-vml, + .yui3-skin-night .yui3-dial-marker v\:oval.yui3-dial-marker-max-min, + .yui3-skin-night v\:oval.yui3-dial-marker-max-min, + .yui3-skin-night .yui3-dial-marker-vml, + .yui3-skin-night .yui3-dial-handle-vml { + background: none; + opacity:1; + } + + div.base64-doublequotes { + width:100px; + height:100px; + background-image:url( "data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kFBQ0WDWwq + wjwAAANMSURBVEjHrdZbaFxVFAbgb2aSTG6GTi6mVIwxNxF9qFI0RQnFUqiYamutVutLa2t9EY0oPggFoYgPRR%2FaghYviA%2BiIAYvmBJKoYWi + iBCigVTT1FisbUhrEtNkJpc5PuQkjGEmJqkLFmdz2Hv%2Fa%2F3rX3tvlm95oS%2FLokuZtIpbdvAs7KFtL22wjb3V1C41upy2ke1DXC2k%2FBjv + 1HHXDrbkEamg7lX2P8QTldQ2UtfOB8uiJsHNiB%2Fik0GmO%2BgZIxgnGA59nGCMoJPeQaYP047iBDXZEohkAYof4%2FNyKlZRdR%2F1ASZCnoOM + hWkUheMz9F1laJSRZ3gEqVw1ipZQjcoBRrbT3Ez9OJLhZkHG5CD8l8Q47qXhMZp%2FYxhVFaxBLBtQejdtA%2FTtZPMIJnOknI2WSYzicTYN8OtT + vICZbECxdr5Pkm6iPL0C3c%2BgkfIJgi%2F4LnP%2FudRKD3K4jf1VJGLEAiuz6VnA4AGam1h7gpNIzSFe66D3NurLKVhJNkHo07N9V9BE3XHOYm + yeuirqG1l9mdHgOkDSGd8%2FGWtg9Roa56lrYdchDtRQPLlCkEywKVRScDfrurnwC2diiPTRe47iVtbnLZDxckGCkKYpPM%2FRr3kbyRhsYOtRDi + QonFoBSHrBOI18rOeOPvr76YrCeUbf5fTvjOddJ0gQ1uMPku9z6hwjiEZhgOMn%2BaaUeHQJIOkMD7KMA5QQP01HP18hPbfvDTvZPETqb2YiS1BW + rozM6jk9SPJJHkZZ5qFagtoaNnbyZg1FE4sUPRdlAQpwkdSjvDZAJ%2FoxNt%2Bw6NlGbQVFl5iKLKKsXCAwyFQZ8S3ciu65ho1lBJ5%2FkZk3OB + GjpJWGmVCmsjTkQvA8JHCUU7s5eImevzg%2Fd7BGFhzCARIf8uVN3J5Heh1VM%2BHlko2y%2FHBxF0NJolfo38eDuJJxbf0ro%2FnMurh8hM%2Fu + pH4tdT8zciOFsTC8SAgQoJfRxKzCuvfw0k%2F8MDx7xqUyez%2BS48ESIN7Ky6OUpfAtrxzhx03UR4m0c%2FZF7tnKW2mma4l9yuthTSIZIvxPi6 + EcpZW0PM0xtOzjo%2Bf4GPfv4r1qNqAUFYs9diJLBC1CIa7FZx8fUlwI22LuNv%2FfLbKMAOftH9TwRXg6%2FiCDAAAAAElFTkSuQmCC" ); + background-position:center center; + border:1px solid #00aa00; + } + + .yui-skin-sam .yui-h-slider { + background: url(bg-h.gif) no-repeat 5px 0; + height: 28px; + width: 228px; + } + + """ + + output = """.yui3-skin-night .yui3-dial-ring-vml,.yui3-skin-night .yui3-dial-center-button-vml,.yui3-skin-night .yui3-dial-marker v\:oval.yui3-dial-marker-max-min,.yui3-skin-night v\:oval.yui3-dial-marker-max-min,.yui3-skin-night .yui3-dial-marker-vml,.yui3-skin-night .yui3-dial-handle-vml{background:0;opacity:1}div.base64-doublequotes{width:100px;height:100px;background-image:url("data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kFBQ0WDWwqwjwAAANMSURBVEjHrdZbaFxVFAbgb2aSTG6GTi6mVIwxNxF9qFI0RQnFUqiYamutVutLa2t9EY0oPggFoYgPRR%2FaghYviA%2BiIAYvmBJKoYWiiBCigVTT1FisbUhrEtNkJpc5PuQkjGEmJqkLFmdz2Hv%2Fa%2F3rX3tvlm95oS%2FLokuZtIpbdvAs7KFtL22wjb3V1C41upy2ke1DXC2k%2FBjv1HHXDrbkEamg7lX2P8QTldQ2UtfOB8uiJsHNiB%2Fik0GmO%2BgZIxgnGA59nGCMoJPeQaYP047iBDXZEohkAYof4%2FNyKlZRdR%2F1ASZCnoOMhWkUheMz9F1laJSRZ3gEqVw1ipZQjcoBRrbT3Ez9OJLhZkHG5CD8l8Q47qXhMZp%2FYxhVFaxBLBtQejdtA%2FTtZPMIJnOknI2WSYzicTYN8OtTvICZbECxdr5Pkm6iPL0C3c%2BgkfIJgi%2F4LnP%2FudRKD3K4jf1VJGLEAiuz6VnA4AGam1h7gpNIzSFe66D3NurLKVhJNkHo07N9V9BE3XHOYmyeuirqG1l9mdHgOkDSGd8%2FGWtg9Roa56lrYdchDtRQPLlCkEywKVRScDfrurnwC2diiPTRe47iVtbnLZDxckGCkKYpPM%2FRr3kbyRhsYOtRDiQonFoBSHrBOI18rOeOPvr76YrCeUbf5fTvjOddJ0gQ1uMPku9z6hwjiEZhgOMn%2BaaUeHQJIOkMD7KMA5QQP01HP18hPbfvDTvZPETqb2YiS1BWrozM6jk9SPJJHkZZ5qFagtoaNnbyZg1FE4sUPRdlAQpwkdSjvDZAJ%2FoxNt%2Bw6NlGbQVFl5iKLKKsXCAwyFQZ8S3ciu65ho1lBJ5%2FkZk3OBGjpJWGmVCmsjTkQvA8JHCUU7s5eImevzg%2Fd7BGFhzCARIf8uVN3J5Heh1VM%2BHlko2y%2FHBxF0NJolfo38eDuJJxbf0ro%2FnMurh8hM%2FupH4tdT8zciOFsTC8SAgQoJfRxKzCuvfw0k%2F8MDx7xqUyez%2BS48ESIN7Ky6OUpfAtrxzhx03UR4m0c%2FZF7tnKW2mma4l9yuthTSIZIvxPi6EcpZW0PM0xtOzjo%2Bf4GPfv4r1qNqAUFYs9diJLBC1CIa7FZx8fUlwI22LuNv%2FfLbKMAOftH9TwRXg6%2FiCDAAAAAElFTkSuQmCC");background-position:center center;border:1px solid #0a0}.yui-skin-sam .yui-h-slider{background:url(bg-h.gif) no-repeat 5px 0;height:28px;width:228px}""" + + self._test(input, output) + + def test_yui_dataurl_base64_noquotes(self): + input = """ + .yui3-skin-night .yui3-dial-ring-vml, + .yui3-skin-night .yui3-dial-center-button-vml, + .yui3-skin-night .yui3-dial-marker v\:oval.yui3-dial-marker-max-min, + .yui3-skin-night v\:oval.yui3-dial-marker-max-min, + .yui3-skin-night .yui3-dial-marker-vml, + .yui3-skin-night .yui3-dial-handle-vml { + background: none; + opacity:1; + } + + div.base64-noquotes { + width:100px; + height:100px; + background-image:url( + data:image/jpeg;base64, + %2F9j%2F4AAQSkZJRgABAgEAZABkAAD%2F4RfJRXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAABAAAAagEoAAMAAAABAAIAAAExAAIAAAAeAAAAcgEyAAIAAAAUAAAAkIdpAAQAAAABAAAApAAAANAAD0JAAAAnEAAPQkAAACcQQWRvYmUgUGhvdG9zaG9wIENTMiBNYWNpbnRvc2gAMjAwODowNzoxOSAxNDo1ODowNQAAA6ABAAMAAAAB%2F%2F8AAKACAAQAAAABAAABwqADAAQAAAABAAABRQAAAAAAAAAGAQMAAwAAAAEABgAAARoABQAAAAEAAAEeARsABQAAAAEAAAEmASgAAwAAAAEAAgAAAgEABAAAAAEAAAEuAgIABAAAAAEAABaTAAAAAAAAAEgAAAABAAAASAAAAAH%2F2P%2FgABBKRklGAAECAABIAEgAAP%2FtAAxBZG9iZV9DTQAC%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQADAgICAkIDAkJDBELCgsRFQ8MDA8VGBMTFRMTGBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAENCwsNDg0QDg4QFA4ODhQUDg4ODhQRDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgAdACgAwEiAAIRAQMRAf%2FdAAQACv%2FEAT8AAAEFAQEBAQEBAAAAAAAAAAMAAQIEBQYHCAkKCwEAAQUBAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAABBAEDAgQCBQcGCAUDDDMBAAIRAwQhEjEFQVFhEyJxgTIGFJGhsUIjJBVSwWIzNHKC0UMHJZJT8OHxY3M1FqKygyZEk1RkRcKjdDYX0lXiZfKzhMPTdePzRieUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm9jdHV2d3h5ent8fX5%2FcRAAICAQIEBAMEBQYHBwYFNQEAAhEDITESBEFRYXEiEwUygZEUobFCI8FS0fAzJGLhcoKSQ1MVY3M08SUGFqKygwcmNcLSRJNUoxdkRVU2dGXi8rOEw9N14%2FNGlKSFtJXE1OT0pbXF1eX1VmZ2hpamtsbW5vYnN0dXZ3eHl6e3x%2F%2FaAAwDAQACEQMRAD8A9VSSSSUpJJJJSkkkklKSSSSUs4uDSWiXRoOJKFi3Ovx2Wvbse4e5gMwRo5soypdOyN9mTSRHpWvj4Oc4%2FwDVf9WkpupJJJKUkkkkpSSSSSlJJJJKUkkkkp%2F%2F0PVUklCqxttbbG%2FReA4fApKZpJJJKUo2WNrrdY8w1gLnHyAlVOsWvowXZDZ%2FQOZa8D9xrmm3%2FNr3PUswm%2FpmQGamyl4bHiWuhJTYqsFtTLBw9od94U1Q6FkfaOk4tszuqYfwV9JSll1%2FoOt2sOguDXgeTm7f%2FPmN%2FwCCLUWZ1VhrysbKA%2Bjua7zgtuZ%2F1FqIUXTSQcvKZi47sh7XOY0tBDYJhzms3akfR3blVd13prMtuI%2Bwse%2F6L3Ahm7n0%2FU%2Bi2zafzkFOgq9%2Bdj0ZFOM4k3ZDtrGNEnhzt7v3WbWPU8bJpyavVpduZuc2eNWONb2%2F2XtXOY2VU7rA6hnO9RuJS%2Br1hO1lguGJbuYz2extvv3fzLPU%2FwCESU9QkkkkpSSSSSlJJJJKf%2F%2FR9VWf0iwiu3Ff9LGsewT%2B6D7P%2FAyxy0FmunH6xPDMpgd%2FbZFT%2FwDoOx%2F%2B20Qp0lC02CtxqALwDtB4J8FNJBTVovx%2Bo4jvbLLGlltTuRMssrf%2FANJiz%2BhZDqMO%2FEyCX2dOc6t%2Bkuc1gmt%2B3%2FhaPTeoZ5s6R1IZrBGDlkNvI%2BjXcYYyx%2F7tWV9B%2FwDw%2FwDwlyD1LqWH0jq2N1O55rxOogY1rwC4C9uuP6mzdt9Wl1v6T%2Bb%2FAFdFS%2F1Myq%2F2VThusa%2B2tst2k6sPuY73Brm%2Fo3Vv2fy%2F3F0S4TAzq%2Bm%2FWDquKGnbhWOya62DUUlrcmyulktZ6f2ey19Wz8%2F2Ls2Z%2BFYx1jL6yxgBe%2FcIAI3CXfNIqZX3ursqqrZ6j7TqJgNY2PUsPP0dypfWJ%2Fo9NOT2xrarHTxs3tZkf%2By77VR6v9bejdNsbkb3Zbqm2sfVjgOOhrdZFljqsffXs%2FmvV9V%2F%2BDXHdZ%2BuvU8zA6i%2Bj02Y1zawaZLt2LY21tltb7XOb6j6%2FwDR1V%2FzV36v6rPVYFU%2BiCs5PS7cd43O2WUOB77d1X%2FT27lwvVnNyKK8d15q6hij207o9dg3DHdW9m9zLv8AQ%2F4Wu71K%2FTtr9aqzJ%2BuHV8tmR0zNoyLvsXU8Nr7cdtjmtN1J9K%2B2podsbk1%2Fo%2F8A0YsnG6te%2BnLZbdXXY6pttADNwucwmhv2cWbv0tnr%2FpKnfpKbKf8AtsqD1DvrjnfV70cKlldgzgbqjf7QHB3p2sG0sb6lv0%2FfZ6TEcZg630xz2vezK6jVbfhVtf6biQ%2BxltmNY7a2z9Js3Vu%2FwayvrHh1%2BhhdKv3XubjBhY9wDbHtPpOdXkOH6t1Dd%2FRsl%2F6C%2FwDomV7Fg9Wc%2FF%2Bq3RL6biben5WVjh4lljdxbexltf06La9vvp%2FM%2FwAFZ%2FhEuqn0r6lfXM9WYzpvVoo6wxvcbBcGj9Ltr09LLoj9bxf%2BvU%2Fof5mz9avrJd0%2B2jBwHAZTrcd17iA7bVZa2nY0H%2FCXw9v%2FAAdf%2FWl5zidQZ9ZcY3l3oddwg2z1mHZ6zWfQv3t2%2Bnk0%2FwCn%2FwDRf8xf6Df1DqvXGZ%2Be5tzsi7Gd6lm2kPsqNbqqOG1%2F4Cuxnpt%2FS7%2F9I9ALjR1Gh7f96%2BuJJpA1KZj2WNDmODmnhwMhJaySSSSU%2FwD%2F0vVVn9airFGYSGjEcLXuOkV%2Fzd5J%2FdZU%2FwBX%2Fra0EHM9D7Jf9pE4%2Fpv9YHX2bT6mn9RIKcE9U6lgXnJDv2l0jN%2FS497S39C530sOyxn%2BBc%2F%2Bh5P83%2F2kyfS%2FR5NlPrv16uwczExsPHFozLBRWHz6rriWM9Ouv6Hs9T3vseue6B9buoZF5xBU84BDC%2BtoZYW1PczG%2B02Gxm71Gb2XP2fod7P5mtct167q%2FT87Gdfk%2FbscWHI6V1NrQNGu3O2NZ6fp5FN9dTcrH3epR6f9RKwRYXzhKEuGYo%2FxfSMvM6lZlF%2FVfSobU11Q9MGyqLfa%2BrP9PJsr9Kz09n6bGq%2F7dWN1z9VxC3KfZT0%2B%2BPUyGg5NIaPdU7ezZe7KxPR%2FR5Gyr1sX9Db6%2FwDguZx%2Bv352be4WNwft%2B9tb2n21mza%2F07Xn2%2Fp7qmVep6dbP0vqfolqZD7mY91eDmXh9pBAZDGjT9awvb%2Bkp3W%2Fp2Mdkfpav1b%2BXUb0WulTeP23h5Di3JL3UYWTYzlzbKn1sc5rN7baM3FZ9pw8xrvSvr%2FQf0im6uvH6vY7G6hfS2mx1WFYaq7bnk%2Bo2r0rKPTa7bustxX20%2Bz%2Blf4RW%2FqPXeM403s%2FQ9LpdbQ4v97Wl4DcW1nud6PqfrH2e9nszMavNx%2Fs9%2F2hA6i5r77HZLH24Tn1W4%2BUwj2Pva3Jdj5bfzaHb8n7Fk%2F4P9YxP0tX8wulqaWfb1CzZ6GaM30622WDD%2FSV1mp3sfY6tjWOsY1tf8776a%2FTQ7s27qFNVIrqwAfUsbZRXt9S0N9Ky1tX83%2Bgre%2F1sej%2FAE1lzKltdDtbi9ZJsqqbTaBXdS0TbW68PycUxNj8qrIbR%2FpPU%2FSb6%2FTyv6VWf0qvOrzmsYasnGzHmk7gx1Za6K2XR7sd%2B5v6K79JXi3bPU%2FV8i71RRU5%2FWaLMv6kYj7SGX9L6g%2BqK%2Fosrymeux1cf4B17fYsrpuPmNxftz63Pcwl9DWDQ2sIqZkNJ%2FNZe6luR6f%2FAAX%2FAAS1f2pXX0vq%2BJln7LfkVVta3ZAfl4929myn%2FA76rLq86r%2Bbo%2F4qyhVruoZfSek4ldmM12Pe227D9XQBtvptu3%2B7d6Vr66Xf2PU%2FwiSnsuqnDyLMfFtMZORj%2BrXSPeHFseo2l7g31La%2FV%2Bh%2F2oq%2FwfqfpFznVcd9uDZivaxzDZXa1zztZaXB1LW%2Bv%2Fgb%2FZVXi5tv83%2FRsj9AsLqfXv2j0%2FpXqPFedgNurfa10F3uqsx8lv8Ao3Obvrf%2FAMT6n%2BFQquq59%2FURec1zb7CHXMc8htmw7n47v8BtucPUb6v6P9Ld69iNoS9H6Vk0Zzcpj3tqqeW1AAtuc%2F8Am7Meyhv6WvZ%2FNZX%2FAIEugzuk14eLiN69fbi49u6wgBjyWNb6dP6IOa52V6vt247P0dD%2FAFP0ajjdc6kBeekYQd1DIs2tsZVZbdVXt9tYqsG9%2B3%2FTPZ%2Bl%2FwBCsajpXV%2BtX2Zwo%2Fal7Wuvv9S0Mea2%2FT2%2BvZ7m1e1j6GV76PZ%2FpK0NE7avpH1Z69ndU6Ti4mP6mVZktBdbkP3uqbWTRkjKuYGb9t1e6v2776rP9Iuyw8VuJjtoaS8tkueeXOcdz3%2F2nLyb6vdZv6DjDKwmVt3lzbsZz3VsZcHluTgX499hfRYxuzJwn%2FznsyKrLLv8H2vT%2Fr9hODK%2BsM%2FZuSWkvrtFjC1wLmvr%2FWKqW%2Fm%2BzbZ%2BkRR4vVpKn03q%2FTeq47Mnp%2BQy%2Bt7WvG0%2B4Bw3N9Ss%2FpK%2F%2BuNVxBT%2FAP%2FT9VTOa17SxwDmuBDmnUEHsU6SSnxf6x9CzPqt1ECix7GMeben5Q0caneyylzvouto3MruZ%2Fhav0np%2FpFUryaMjFvrzps6dkPbZmUsHvx7zFber4f7rbLP6Wz%2FAEln%2BivYvY%2Bu9L6f1Xpl2H1GG0OG71SQ01uH0b2Pd9B7P%2FUb%2FwBGvH8vpF3T7%2FQpcH5NIea7az6leXSd2%2BzHjdW70qHbMnE%2FwlSZrE2NuzfjKHMY%2BGfpzR2n%2FL9H95wM%2FByOk5hxsgiyuwb6rm613VH6N1X%2FAKMrW10jqbrmHDyHl8MhjY3G6sHd6PO318dv6eiz07fW9P0v%2BOHiPxc6n9j552Ydh3YmVy7FtP5%2Bvvfiu%2Fw%2F%2FB%2Fpf5zehYv1a6pVk5WLnh%2BJZhML6rmiWvsBY6t1Fn0n1NY77Tvp%2FSf8WnijqNWnOEscjGQ4ZB7Oig0fV7Lf01rr8rqR2tez3XPbBx67XsYzayxjbMl9m38%2F9J%2Bl%2FS%2BpzrvUxMWqvqNVuJksx3402zW5oosbdScig725OHd9qY6v%2Bc2ZOLT6Nn88y3e6X1uyk9NexznYd%2BPdi2Mc1lTxl4ztznWu3bKfWZZXbs9T06f0ypdY6J1Hr%2FVH9Wrtpsx6mMqFDS%2F1WisF30XNa5%2B577bf0eyz%2FRV%2BqnHwW1RotDF6xay49Tviks9OHNBAc9g2sfc1m7Zu2%2FoG%2FQxv3PST9Lw2dTblM%2B1W1sdDixoM2vu3f0936Ot3pOZsurp%2FQ5H%2Bj%2FwaJj4GXf052ZjepRjbmOrywNzNjHulh9Meg5zN%2B1lrn%2F4R9fp%2FpFYxcHD6ey6nItdSGAOtdlFwhlh%2FRuHpN3Ppbv8A5xnqemgFOZ1%2FDyMW77T1DIOQy4vra91bqC00en%2BiYx%2F06Nrqnev%2FAIf%2FAEj1WxaG2sxKOo9Mu6iK32UYeOLHML3vLrNrrmu3%2Blv%2FAPRi1cvpl1PUqW3Nfh5VJ34%2BNmO9XEurHu%2FV7t1jGMsYf0noP9n%2FAANqv9Q6W84w630hn2cuFd1jcYGysjS6qzLxmiu3Fyadv896NX%2Bl%2B2eqhSSBQ1u%2Fwed6z9X%2BrtFJH1dZj1agENsqZuIB9EPsvbkW7Wj6eS%2F6fq%2Bj%2BjWOcbNw8hnqdNxq3tizZYXOa4Ty7fkWbmNd%2FObfof4RdpT1fqHUs1rc%2FLfktyRU2vGa4bSHNePtFL3el6VmLlVejZ7f0m%2F1cj%2BZ%2B0WbvSfqn0%2Fq1eTjdRIdlVspsqcxwBqc8PbfW%2BrX376%2FTtf%2FAKH0bcS6v2WJXqulj4YCXEDZqurxn1dxXdRosuw78hnXcR5dSWu22OrY2bqsdn03ZFTt932Oz2ZWL6lVLPWoXS19Hzup9Pq%2BsfTMYty8kn7ZRXDCy5hdVb1DEre%2F9NU79LvxHbLclj7Kf5rIspXE5mPm9J6q%2FLqa82YrmvfVJHqUsd%2BgyqLatln0a%2F03pP8AWxLmerV%2B5j%2Bz%2FU7qvR%2Bp9Cot6QNlLJbZQ5xdZXb9O1l73S99jnu3%2Bq7%2Be%2FnEmN57qv1X6b9c%2Fq99oxWNx%2BuYxcyx7g9rjc2PXxM5t36x6djmt9H1v02L%2Bi9L%2FCVWcz9VPr51npPWX9H%2Bsxuy8bItFNoyDvtot9tIcN%2F06He316P%2FAEKo%2FwAJ9p9hbXW17ntaA98b3AQXQIbuP5y43%2FGB9Q2dfp%2FaXTWtZ1iloEGGjIY3%2FAPe7%2BayG%2F8AafJ%2F6zb%2Bi%2FSUJT2TK66xtraGAAABoAEAbW8fyVJecfUbqP8AjHPV31dXxMjJ6c%2Fay27LaMd1JaNgsoDxX9o9rf0zaW2%2Br%2FPep%2FpPR0lP%2F9T1VJJJJSDNL%2Fsr9geXGABW1rnakD6F36P%2BsuAwumV%2FWXNrxHvcymqh2S7IqLnRa%2B7Jfj2UPcGtqdvyfVspZ7K%2FQ%2By1%2FoaV6Ffj05FZqvYLKyQS13Eg7m%2F9JKimvHprorEV1NDGDwDRtakkEg2NCNnxDqdGb0Lru3qFDHX41rLTWGxVeyR7626tdVmNa%2F1P3LPWr9i2cmzrpY5tdB6gzpL7Kq7WGXPqqf6D8HIc1gZ9upo%2F7T%2FzmVX%2FADXqr0brOF0%2B4135bWVFgc05kD1WMP06ari17q25P81Z%2B%2FX%2Bi%2BnauTysfqnQeoM6h06l91eW2H4jwf1uhgc70Lq3DdV1XGxm78d7%2FwCfo%2FQXfpK7GIRFX4s2fN7vAeGpRjwk93IyOj5VDnN6U5%2FUMa%2FflYlLGuLm%2BqxnpfadrG1tdk302Ufav%2Bs2en%2FNrQ%2Brd9%2BNlNdfS%2FDuDXMy8e7R1bmjfJ%2FkNhl9Nv59KtOeLbMDO6FktJte%2B%2FCstLh%2Bhcaxm4mY5oc5tLMr08XK3st9H7TRd%2FSqPWW11fA%2Fa%2BOOpdOc6jqeMNllYjcdvudjWt9zfVr3b8d%2F%2Fom%2F1E%2B2F5jpf1u6Dj9RyLKcqu3peaf1rF1aGh%2Bhym0XMr91TfZksr%2Fncb%2FSWY9bFTwftfUOpdQwRZvxsDLOLh2uBexrLH21YzLLAHv9K3ZX%2FwAH%2FOLUw%2BnfU%2F6xVDB6lg4%2FTutEFrLaG%2BgLXD%2FC42zY2x%2F%2Blw7f01f8ur9On%2BroyPqllZfThhMsqBZblWhxFjahFLLWE%2B27HZv3sbsr9H9N63%2FBjW1L9J9PDa%2F6udcx%2FU6Ta7bSH6uxLT7W0Oc0%2Fo6HT6mFl1fzX%2BnV9n1R6v07bbhZv2za4w149K0Vk6enkMeWPu2fzn8xVb%2Fwf0F1GVh4mdSW3MD2vbAfA3AH9xyoYmRZ0o%2FY894%2BztBNGS7QbR%2Ba8n6Oz%2FwH%2FttLyRbyPUcXMqzznY2Nfh9TrbZbkkVyxzQBvzm%2Bm04vr7f0d%2Fo2frX%2BD%2FTV2rc%2BrmH0bqHT8fcW%2Fb2Ned1FrmWsaXOZLfRLH01W%2Bpu9L%2BaXUiCJHBWGegM6d1IdU6WzbJi%2FFbAaWuPvdV%2FV%2FnPTStN6U4%2FWfqPk5jhRj2BorDrMXLOjqnge2m1rNrnV3e1lnpf8b%2FO1Urm%2FqLlZH1a%2BseT03IwLa7s97K8nFrY%2Bx9fp%2Bp6OfjupZ6VvT3%2BrY3I%2F0P6Oyn9F%2BjXrKSCFJJJJKUkkkkp%2F%2F9X1VJJJJSkkkklKQM3Dpzcd2PbuAdBa9hLXscNWWVPb9CxjkdJJTxHVfq4Ol5bc%2Bq55dlvLLq2Dax1lprY7IFbP5i63b77Kf9H%2Bk%2FQrpPq%2FcLMAVlgrupcWXM4duBj9I0y7f%2F1f00vrB0hnVunPo%2BjkMmzFtGjq7QC1tlb9Njvd%2FqxS6dWMj0upH9FfbXsyqwIabGwx30%2Ff%2BifW9n9RHopwvrBh4eF1V%2BXkU%2FqmZQQLSB6deYxwux7HAfpGvs9L%2FB%2F8J6ivWVZ2dj4vV8FgfbkY%2Fo5WO8hosrdr3%2Bi6t7nrefWx8b2h20hzZEwR9Fw%2FlIOLg4%2BI652OCxt7vUfWD7Q4%2FScxv5m%2F89K1IujYj8PpWLjWDa%2BusB7SZIcfc5u6XfnFW31seIe0OEzBE6%2FNSSQUpJJJJSkkkklKSSSSUpJJJJT%2FAP%2FW9VSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2F%2FZ%2F%2B01IlBob3Rvc2hvcCAzLjAAOEJJTQQlAAAAAAAQAAAAAAAAAAAAAAAAAAAAADhCSU0D6gAAAAAYEDw%2FeG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04Ij8%2BCjwhRE9DVFlQRSBwbGlzdCBQVUJMSUMgIi0vL0FwcGxlLy9EVEQgUExJU1QgMS4wLy9FTiIgImh0dHA6Ly93d3cuYXBwbGUuY29tL0RURHMvUHJvcGVydHlMaXN0LTEuMC5kdGQiPgo8cGxpc3QgdmVyc2lvbj0iMS4wIj4KPGRpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNSG9yaXpvbnRhbFJlczwva2V5PgoJPGRpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJPGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1Ib3Jpem9udGFsUmVzPC9rZXk%2BCgkJCQk8cmVhbD43MjwvcmVhbD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTU9yaWVudGF0aW9uPC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk%2BCgkJCTxkaWN0PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTU9yaWVudGF0aW9uPC9rZXk%2BCgkJCQk8aW50ZWdlcj4xPC9pbnRlZ2VyPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNU2NhbGluZzwva2V5PgoJPGRpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJPGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1TY2FsaW5nPC9rZXk%2BCgkJCQk8cmVhbD4xPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxSZXM8L2tleT4KCTxkaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk%2BCgkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCTxhcnJheT4KCQkJPGRpY3Q%2BCgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxSZXM8L2tleT4KCQkJCTxyZWFsPjcyPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxTY2FsaW5nPC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk%2BCgkJCTxkaWN0PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTVZlcnRpY2FsU2NhbGluZzwva2V5PgoJCQkJPHJlYWw%2BMTwvcmVhbD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuc3ViVGlja2V0LnBhcGVyX2luZm9fdGlja2V0PC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PlBNUFBEUGFwZXJDb2RlTmFtZTwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PlBNUFBEUGFwZXJDb2RlTmFtZTwva2V5PgoJCQkJCTxzdHJpbmc%2BTGV0dGVyPC9zdHJpbmc%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BUE1UaW9nYVBhcGVyTmFtZTwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PlBNVGlvZ2FQYXBlck5hbWU8L2tleT4KCQkJCQk8c3RyaW5nPm5hLWxldHRlcjwvc3RyaW5nPgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BMC4wPC9yZWFsPgoJCQkJCQk8cmVhbD4wLjA8L3JlYWw%2BCgkJCQkJCTxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTc2PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1BZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQkJCQk8YXJyYXk%2BCgkJCQkJCTxyZWFsPi0xODwvcmVhbD4KCQkJCQkJPHJlYWw%2BLTE4PC9yZWFsPgoJCQkJCQk8cmVhbD43NzQ8L3JlYWw%2BCgkJCQkJCTxyZWFsPjU5NDwvcmVhbD4KCQkJCQk8L2FycmF5PgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYXBlckluZm8uUE1QYXBlck5hbWU8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNUGFwZXJOYW1lPC9rZXk%2BCgkJCQkJPHN0cmluZz5uYS1sZXR0ZXI8L3N0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVGbGFnPC9rZXk%2BCgkJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJCTwvZGljdD4KCQkJPC9hcnJheT4KCQk8L2RpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNVW5hZGp1c3RlZFBhZ2VSZWN0PC9rZXk%2BCgkJPGRpY3Q%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk%2BCgkJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BMC4wPC9yZWFsPgoJCQkJCQk8cmVhbD4wLjA8L3JlYWw%2BCgkJCQkJCTxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTc2PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNVW5hZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BLTE4PC9yZWFsPgoJCQkJCQk8cmVhbD4tMTg8L3JlYWw%2BCgkJCQkJCTxyZWFsPjc3NDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTk0PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5wcGQuUE1QYXBlck5hbWU8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLnBwZC5QTVBhcGVyTmFtZTwva2V5PgoJCQkJCTxzdHJpbmc%2BVVMgTGV0dGVyPC9zdHJpbmc%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5BUElWZXJzaW9uPC9rZXk%2BCgkJPHN0cmluZz4wMC4yMDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC50eXBlPC9rZXk%2BCgkJPHN0cmluZz5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvVGlja2V0PC9zdHJpbmc%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuQVBJVmVyc2lvbjwva2V5PgoJPHN0cmluZz4wMC4yMDwvc3RyaW5nPgoJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnR5cGU8L2tleT4KCTxzdHJpbmc%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXRUaWNrZXQ8L3N0cmluZz4KPC9kaWN0Pgo8L3BsaXN0Pgo4QklNA%2BkAAAAAAHgAAwAAAEgASAAAAAAC3gJA%2F%2B7%2F7gMGAlIDZwUoA%2FwAAgAAAEgASAAAAAAC2AIoAAEAAABkAAAAAQADAwMAAAABf%2F8AAQABAAAAAAAAAAAAAAAAaAgAGQGQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4QklNA%2B0AAAAAABAAZAAAAAEAAQBkAAAAAQABOEJJTQQmAAAAAAAOAAAAAAAAAAAAAD%2BAAAA4QklNBA0AAAAAAAQAAAAeOEJJTQQZAAAAAAAEAAAAHjhCSU0D8wAAAAAACQAAAAAAAAAAAQA4QklNBAoAAAAAAAEAADhCSU0nEAAAAAAACgABAAAAAAAAAAE4QklNA%2FUAAAAAAEgAL2ZmAAEAbGZmAAYAAAAAAAEAL2ZmAAEAoZmaAAYAAAAAAAEAMgAAAAEAWgAAAAYAAAAAAAEANQAAAAEALQAAAAYAAAAAAAE4QklNA%2FgAAAAAAHAAAP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8D6AAAAAD%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FA%2BgAAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwPoAAAAAP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8D6AAAOEJJTQQIAAAAAAAQAAAAAQAAAkAAAAJAAAAAADhCSU0EHgAAAAAABAAAAAA4QklNBBoAAAAAA1UAAAAGAAAAAAAAAAAAAAFFAAABwgAAABAAcwB3AGkAcwBzAF8AYQByAG0AeQBfAGsAbgBpAGYAZQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABwgAAAUUAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAQAAAAAAAG51bGwAAAACAAAABmJvdW5kc09iamMAAAABAAAAAAAAUmN0MQAAAAQAAAAAVG9wIGxvbmcAAAAAAAAAAExlZnRsb25nAAAAAAAAAABCdG9tbG9uZwAAAUUAAAAAUmdodGxvbmcAAAHCAAAABnNsaWNlc1ZsTHMAAAABT2JqYwAAAAEAAAAAAAVzbGljZQAAABIAAAAHc2xpY2VJRGxvbmcAAAAAAAAAB2dyb3VwSURsb25nAAAAAAAAAAZvcmlnaW5lbnVtAAAADEVTbGljZU9yaWdpbgAAAA1hdXRvR2VuZXJhdGVkAAAAAFR5cGVlbnVtAAAACkVTbGljZVR5cGUAAAAASW1nIAAAAAZib3VuZHNPYmpjAAAAAQAAAAAAAFJjdDEAAAAEAAAAAFRvcCBsb25nAAAAAAAAAABMZWZ0bG9uZwAAAAAAAAAAQnRvbWxvbmcAAAFFAAAAAFJnaHRsb25nAAABwgAAAAN1cmxURVhUAAAAAQAAAAAAAG51bGxURVhUAAAAAQAAAAAAAE1zZ2VURVhUAAAAAQAAAAAABmFsdFRhZ1RFWFQAAAABAAAAAAAOY2VsbFRleHRJc0hUTUxib29sAQAAAAhjZWxsVGV4dFRFWFQAAAABAAAAAAAJaG9yekFsaWduZW51bQAAAA9FU2xpY2VIb3J6QWxpZ24AAAAHZGVmYXVsdAAAAAl2ZXJ0QWxpZ25lbnVtAAAAD0VTbGljZVZlcnRBbGlnbgAAAAdkZWZhdWx0AAAAC2JnQ29sb3JUeXBlZW51bQAAABFFU2xpY2VCR0NvbG9yVHlwZQAAAABOb25lAAAACXRvcE91dHNldGxvbmcAAAAAAAAACmxlZnRPdXRzZXRsb25nAAAAAAAAAAxib3R0b21PdXRzZXRsb25nAAAAAAAAAAtyaWdodE91dHNldGxvbmcAAAAAADhCSU0EKAAAAAAADAAAAAE%2F8AAAAAAAADhCSU0EEQAAAAAAAQEAOEJJTQQUAAAAAAAEAAAAAThCSU0EDAAAAAAWrwAAAAEAAACgAAAAdAAAAeAAANmAAAAWkwAYAAH%2F2P%2FgABBKRklGAAECAABIAEgAAP%2FtAAxBZG9iZV9DTQAC%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQADAgICAkIDAkJDBELCgsRFQ8MDA8VGBMTFRMTGBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAENCwsNDg0QDg4QFA4ODhQUDg4ODhQRDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgAdACgAwEiAAIRAQMRAf%2FdAAQACv%2FEAT8AAAEFAQEBAQEBAAAAAAAAAAMAAQIEBQYHCAkKCwEAAQUBAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAABBAEDAgQCBQcGCAUDDDMBAAIRAwQhEjEFQVFhEyJxgTIGFJGhsUIjJBVSwWIzNHKC0UMHJZJT8OHxY3M1FqKygyZEk1RkRcKjdDYX0lXiZfKzhMPTdePzRieUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm9jdHV2d3h5ent8fX5%2FcRAAICAQIEBAMEBQYHBwYFNQEAAhEDITESBEFRYXEiEwUygZEUobFCI8FS0fAzJGLhcoKSQ1MVY3M08SUGFqKygwcmNcLSRJNUoxdkRVU2dGXi8rOEw9N14%2FNGlKSFtJXE1OT0pbXF1eX1VmZ2hpamtsbW5vYnN0dXZ3eHl6e3x%2F%2FaAAwDAQACEQMRAD8A9VSSSSUpJJJJSkkkklKSSSSUs4uDSWiXRoOJKFi3Ovx2Wvbse4e5gMwRo5soypdOyN9mTSRHpWvj4Oc4%2FwDVf9WkpupJJJKUkkkkpSSSSSlJJJJKUkkkkp%2F%2F0PVUklCqxttbbG%2FReA4fApKZpJJJKUo2WNrrdY8w1gLnHyAlVOsWvowXZDZ%2FQOZa8D9xrmm3%2FNr3PUswm%2FpmQGamyl4bHiWuhJTYqsFtTLBw9od94U1Q6FkfaOk4tszuqYfwV9JSll1%2FoOt2sOguDXgeTm7f%2FPmN%2FwCCLUWZ1VhrysbKA%2Bjua7zgtuZ%2F1FqIUXTSQcvKZi47sh7XOY0tBDYJhzms3akfR3blVd13prMtuI%2Bwse%2F6L3Ahm7n0%2FU%2Bi2zafzkFOgq9%2Bdj0ZFOM4k3ZDtrGNEnhzt7v3WbWPU8bJpyavVpduZuc2eNWONb2%2F2XtXOY2VU7rA6hnO9RuJS%2Br1hO1lguGJbuYz2extvv3fzLPU%2FwCESU9QkkkkpSSSSSlJJJJKf%2F%2FR9VWf0iwiu3Ff9LGsewT%2B6D7P%2FAyxy0FmunH6xPDMpgd%2FbZFT%2FwDoOx%2F%2B20Qp0lC02CtxqALwDtB4J8FNJBTVovx%2Bo4jvbLLGlltTuRMssrf%2FANJiz%2BhZDqMO%2FEyCX2dOc6t%2Bkuc1gmt%2B3%2FhaPTeoZ5s6R1IZrBGDlkNvI%2BjXcYYyx%2F7tWV9B%2FwDw%2FwDwlyD1LqWH0jq2N1O55rxOogY1rwC4C9uuP6mzdt9Wl1v6T%2Bb%2FAFdFS%2F1Myq%2F2VThusa%2B2tst2k6sPuY73Brm%2Fo3Vv2fy%2F3F0S4TAzq%2Bm%2FWDquKGnbhWOya62DUUlrcmyulktZ6f2ey19Wz8%2F2Ls2Z%2BFYx1jL6yxgBe%2FcIAI3CXfNIqZX3ursqqrZ6j7TqJgNY2PUsPP0dypfWJ%2Fo9NOT2xrarHTxs3tZkf%2By77VR6v9bejdNsbkb3Zbqm2sfVjgOOhrdZFljqsffXs%2FmvV9V%2F%2BDXHdZ%2BuvU8zA6i%2Bj02Y1zawaZLt2LY21tltb7XOb6j6%2FwDR1V%2FzV36v6rPVYFU%2BiCs5PS7cd43O2WUOB77d1X%2FT27lwvVnNyKK8d15q6hij207o9dg3DHdW9m9zLv8AQ%2F4Wu71K%2FTtr9aqzJ%2BuHV8tmR0zNoyLvsXU8Nr7cdtjmtN1J9K%2B2podsbk1%2Fo%2F8A0YsnG6te%2BnLZbdXXY6pttADNwucwmhv2cWbv0tnr%2FpKnfpKbKf8AtsqD1DvrjnfV70cKlldgzgbqjf7QHB3p2sG0sb6lv0%2FfZ6TEcZg630xz2vezK6jVbfhVtf6biQ%2BxltmNY7a2z9Js3Vu%2FwayvrHh1%2BhhdKv3XubjBhY9wDbHtPpOdXkOH6t1Dd%2FRsl%2F6C%2FwDomV7Fg9Wc%2FF%2Bq3RL6biben5WVjh4lljdxbexltf06La9vvp%2FM%2FwAFZ%2FhEuqn0r6lfXM9WYzpvVoo6wxvcbBcGj9Ltr09LLoj9bxf%2BvU%2Fof5mz9avrJd0%2B2jBwHAZTrcd17iA7bVZa2nY0H%2FCXw9v%2FAAdf%2FWl5zidQZ9ZcY3l3oddwg2z1mHZ6zWfQv3t2%2Bnk0%2FwCn%2FwDRf8xf6Df1DqvXGZ%2Be5tzsi7Gd6lm2kPsqNbqqOG1%2F4Cuxnpt%2FS7%2F9I9ALjR1Gh7f96%2BuJJpA1KZj2WNDmODmnhwMhJaySSSSU%2FwD%2F0vVVn9airFGYSGjEcLXuOkV%2Fzd5J%2FdZU%2FwBX%2Fra0EHM9D7Jf9pE4%2Fpv9YHX2bT6mn9RIKcE9U6lgXnJDv2l0jN%2FS497S39C530sOyxn%2BBc%2F%2Bh5P83%2F2kyfS%2FR5NlPrv16uwczExsPHFozLBRWHz6rriWM9Ouv6Hs9T3vseue6B9buoZF5xBU84BDC%2BtoZYW1PczG%2B02Gxm71Gb2XP2fod7P5mtct167q%2FT87Gdfk%2FbscWHI6V1NrQNGu3O2NZ6fp5FN9dTcrH3epR6f9RKwRYXzhKEuGYo%2FxfSMvM6lZlF%2FVfSobU11Q9MGyqLfa%2BrP9PJsr9Kz09n6bGq%2F7dWN1z9VxC3KfZT0%2B%2BPUyGg5NIaPdU7ezZe7KxPR%2FR5Gyr1sX9Db6%2FwDguZx%2Bv352be4WNwft%2B9tb2n21mza%2F07Xn2%2Fp7qmVep6dbP0vqfolqZD7mY91eDmXh9pBAZDGjT9awvb%2Bkp3W%2Fp2Mdkfpav1b%2BXUb0WulTeP23h5Di3JL3UYWTYzlzbKn1sc5rN7baM3FZ9pw8xrvSvr%2FQf0im6uvH6vY7G6hfS2mx1WFYaq7bnk%2Bo2r0rKPTa7bustxX20%2Bz%2Blf4RW%2FqPXeM403s%2FQ9LpdbQ4v97Wl4DcW1nud6PqfrH2e9nszMavNx%2Fs9%2F2hA6i5r77HZLH24Tn1W4%2BUwj2Pva3Jdj5bfzaHb8n7Fk%2F4P9YxP0tX8wulqaWfb1CzZ6GaM30622WDD%2FSV1mp3sfY6tjWOsY1tf8776a%2FTQ7s27qFNVIrqwAfUsbZRXt9S0N9Ky1tX83%2Bgre%2F1sej%2FAE1lzKltdDtbi9ZJsqqbTaBXdS0TbW68PycUxNj8qrIbR%2FpPU%2FSb6%2FTyv6VWf0qvOrzmsYasnGzHmk7gx1Za6K2XR7sd%2B5v6K79JXi3bPU%2FV8i71RRU5%2FWaLMv6kYj7SGX9L6g%2BqK%2Fosrymeux1cf4B17fYsrpuPmNxftz63Pcwl9DWDQ2sIqZkNJ%2FNZe6luR6f%2FAAX%2FAAS1f2pXX0vq%2BJln7LfkVVta3ZAfl4929myn%2FA76rLq86r%2Bbo%2F4qyhVruoZfSek4ldmM12Pe227D9XQBtvptu3%2B7d6Vr66Xf2PU%2FwiSnsuqnDyLMfFtMZORj%2BrXSPeHFseo2l7g31La%2FV%2Bh%2F2oq%2FwfqfpFznVcd9uDZivaxzDZXa1zztZaXB1LW%2Bv%2Fgb%2FZVXi5tv83%2FRsj9AsLqfXv2j0%2FpXqPFedgNurfa10F3uqsx8lv8Ao3Obvrf%2FAMT6n%2BFQquq59%2FURec1zb7CHXMc8htmw7n47v8BtucPUb6v6P9Ld69iNoS9H6Vk0Zzcpj3tqqeW1AAtuc%2F8Am7Meyhv6WvZ%2FNZX%2FAIEugzuk14eLiN69fbi49u6wgBjyWNb6dP6IOa52V6vt247P0dD%2FAFP0ajjdc6kBeekYQd1DIs2tsZVZbdVXt9tYqsG9%2B3%2FTPZ%2Bl%2FwBCsajpXV%2BtX2Zwo%2Fal7Wuvv9S0Mea2%2FT2%2BvZ7m1e1j6GV76PZ%2FpK0NE7avpH1Z69ndU6Ti4mP6mVZktBdbkP3uqbWTRkjKuYGb9t1e6v2776rP9Iuyw8VuJjtoaS8tkueeXOcdz3%2F2nLyb6vdZv6DjDKwmVt3lzbsZz3VsZcHluTgX499hfRYxuzJwn%2FznsyKrLLv8H2vT%2Fr9hODK%2BsM%2FZuSWkvrtFjC1wLmvr%2FWKqW%2Fm%2BzbZ%2BkRR4vVpKn03q%2FTeq47Mnp%2BQy%2Bt7WvG0%2B4Bw3N9Ss%2FpK%2F%2BuNVxBT%2FAP%2FT9VTOa17SxwDmuBDmnUEHsU6SSnxf6x9CzPqt1ECix7GMeben5Q0caneyylzvouto3MruZ%2Fhav0np%2FpFUryaMjFvrzps6dkPbZmUsHvx7zFber4f7rbLP6Wz%2FAEln%2BivYvY%2Bu9L6f1Xpl2H1GG0OG71SQ01uH0b2Pd9B7P%2FUb%2FwBGvH8vpF3T7%2FQpcH5NIea7az6leXSd2%2BzHjdW70qHbMnE%2FwlSZrE2NuzfjKHMY%2BGfpzR2n%2FL9H95wM%2FByOk5hxsgiyuwb6rm613VH6N1X%2FAKMrW10jqbrmHDyHl8MhjY3G6sHd6PO318dv6eiz07fW9P0v%2BOHiPxc6n9j552Ydh3YmVy7FtP5%2Bvvfiu%2Fw%2F%2FB%2Fpf5zehYv1a6pVk5WLnh%2BJZhML6rmiWvsBY6t1Fn0n1NY77Tvp%2FSf8WnijqNWnOEscjGQ4ZB7Oig0fV7Lf01rr8rqR2tez3XPbBx67XsYzayxjbMl9m38%2F9J%2Bl%2FS%2BpzrvUxMWqvqNVuJksx3402zW5oosbdScig725OHd9qY6v%2Bc2ZOLT6Nn88y3e6X1uyk9NexznYd%2BPdi2Mc1lTxl4ztznWu3bKfWZZXbs9T06f0ypdY6J1Hr%2FVH9Wrtpsx6mMqFDS%2F1WisF30XNa5%2B577bf0eyz%2FRV%2BqnHwW1RotDF6xay49Tviks9OHNBAc9g2sfc1m7Zu2%2FoG%2FQxv3PST9Lw2dTblM%2B1W1sdDixoM2vu3f0936Ot3pOZsurp%2FQ5H%2Bj%2FwaJj4GXf052ZjepRjbmOrywNzNjHulh9Meg5zN%2B1lrn%2F4R9fp%2FpFYxcHD6ey6nItdSGAOtdlFwhlh%2FRuHpN3Ppbv8A5xnqemgFOZ1%2FDyMW77T1DIOQy4vra91bqC00en%2BiYx%2F06Nrqnev%2FAIf%2FAEj1WxaG2sxKOo9Mu6iK32UYeOLHML3vLrNrrmu3%2Blv%2FAPRi1cvpl1PUqW3Nfh5VJ34%2BNmO9XEurHu%2FV7t1jGMsYf0noP9n%2FAANqv9Q6W84w630hn2cuFd1jcYGysjS6qzLxmiu3Fyadv896NX%2Bl%2B2eqhSSBQ1u%2Fwed6z9X%2BrtFJH1dZj1agENsqZuIB9EPsvbkW7Wj6eS%2F6fq%2Bj%2BjWOcbNw8hnqdNxq3tizZYXOa4Ty7fkWbmNd%2FObfof4RdpT1fqHUs1rc%2FLfktyRU2vGa4bSHNePtFL3el6VmLlVejZ7f0m%2F1cj%2BZ%2B0WbvSfqn0%2Fq1eTjdRIdlVspsqcxwBqc8PbfW%2BrX376%2FTtf%2FAKH0bcS6v2WJXqulj4YCXEDZqurxn1dxXdRosuw78hnXcR5dSWu22OrY2bqsdn03ZFTt932Oz2ZWL6lVLPWoXS19Hzup9Pq%2BsfTMYty8kn7ZRXDCy5hdVb1DEre%2F9NU79LvxHbLclj7Kf5rIspXE5mPm9J6q%2FLqa82YrmvfVJHqUsd%2BgyqLatln0a%2F03pP8AWxLmerV%2B5j%2Bz%2FU7qvR%2Bp9Cot6QNlLJbZQ5xdZXb9O1l73S99jnu3%2Bq7%2Be%2FnEmN57qv1X6b9c%2Fq99oxWNx%2BuYxcyx7g9rjc2PXxM5t36x6djmt9H1v02L%2Bi9L%2FCVWcz9VPr51npPWX9H%2Bsxuy8bItFNoyDvtot9tIcN%2F06He316P%2FAEKo%2FwAJ9p9hbXW17ntaA98b3AQXQIbuP5y43%2FGB9Q2dfp%2FaXTWtZ1iloEGGjIY3%2FAPe7%2BayG%2F8AafJ%2F6zb%2Bi%2FSUJT2TK66xtraGAAABoAEAbW8fyVJecfUbqP8AjHPV31dXxMjJ6c%2Fay27LaMd1JaNgsoDxX9o9rf0zaW2%2Br%2FPep%2FpPR0lP%2F9T1VJJJJSDNL%2Fsr9geXGABW1rnakD6F36P%2BsuAwumV%2FWXNrxHvcymqh2S7IqLnRa%2B7Jfj2UPcGtqdvyfVspZ7K%2FQ%2By1%2FoaV6Ffj05FZqvYLKyQS13Eg7m%2F9JKimvHprorEV1NDGDwDRtakkEg2NCNnxDqdGb0Lru3qFDHX41rLTWGxVeyR7626tdVmNa%2F1P3LPWr9i2cmzrpY5tdB6gzpL7Kq7WGXPqqf6D8HIc1gZ9upo%2F7T%2FzmVX%2FADXqr0brOF0%2B4135bWVFgc05kD1WMP06ari17q25P81Z%2B%2FX%2Bi%2BnauTysfqnQeoM6h06l91eW2H4jwf1uhgc70Lq3DdV1XGxm78d7%2FwCfo%2FQXfpK7GIRFX4s2fN7vAeGpRjwk93IyOj5VDnN6U5%2FUMa%2FflYlLGuLm%2BqxnpfadrG1tdk302Ufav%2Bs2en%2FNrQ%2Brd9%2BNlNdfS%2FDuDXMy8e7R1bmjfJ%2FkNhl9Nv59KtOeLbMDO6FktJte%2B%2FCstLh%2Bhcaxm4mY5oc5tLMr08XK3st9H7TRd%2FSqPWW11fA%2Fa%2BOOpdOc6jqeMNllYjcdvudjWt9zfVr3b8d%2F%2Fom%2F1E%2B2F5jpf1u6Dj9RyLKcqu3peaf1rF1aGh%2Bhym0XMr91TfZksr%2Fncb%2FSWY9bFTwftfUOpdQwRZvxsDLOLh2uBexrLH21YzLLAHv9K3ZX%2FwAH%2FOLUw%2BnfU%2F6xVDB6lg4%2FTutEFrLaG%2BgLXD%2FC42zY2x%2F%2Blw7f01f8ur9On%2BroyPqllZfThhMsqBZblWhxFjahFLLWE%2B27HZv3sbsr9H9N63%2FBjW1L9J9PDa%2F6udcx%2FU6Ta7bSH6uxLT7W0Oc0%2Fo6HT6mFl1fzX%2BnV9n1R6v07bbhZv2za4w149K0Vk6enkMeWPu2fzn8xVb%2Fwf0F1GVh4mdSW3MD2vbAfA3AH9xyoYmRZ0o%2FY894%2BztBNGS7QbR%2Ba8n6Oz%2FwH%2FttLyRbyPUcXMqzznY2Nfh9TrbZbkkVyxzQBvzm%2Bm04vr7f0d%2Fo2frX%2BD%2FTV2rc%2BrmH0bqHT8fcW%2Fb2Ned1FrmWsaXOZLfRLH01W%2Bpu9L%2BaXUiCJHBWGegM6d1IdU6WzbJi%2FFbAaWuPvdV%2FV%2FnPTStN6U4%2FWfqPk5jhRj2BorDrMXLOjqnge2m1rNrnV3e1lnpf8b%2FO1Urm%2FqLlZH1a%2BseT03IwLa7s97K8nFrY%2Bx9fp%2Bp6OfjupZ6VvT3%2BrY3I%2F0P6Oyn9F%2BjXrKSCFJJJJKUkkkkp%2F%2F9X1VJJJJSkkkklKQM3Dpzcd2PbuAdBa9hLXscNWWVPb9CxjkdJJTxHVfq4Ol5bc%2Bq55dlvLLq2Dax1lprY7IFbP5i63b77Kf9H%2Bk%2FQrpPq%2FcLMAVlgrupcWXM4duBj9I0y7f%2F1f00vrB0hnVunPo%2BjkMmzFtGjq7QC1tlb9Njvd%2FqxS6dWMj0upH9FfbXsyqwIabGwx30%2Ff%2BifW9n9RHopwvrBh4eF1V%2BXkU%2FqmZQQLSB6deYxwux7HAfpGvs9L%2FB%2F8J6ivWVZ2dj4vV8FgfbkY%2Fo5WO8hosrdr3%2Bi6t7nrefWx8b2h20hzZEwR9Fw%2FlIOLg4%2BI652OCxt7vUfWD7Q4%2FScxv5m%2F89K1IujYj8PpWLjWDa%2BusB7SZIcfc5u6XfnFW31seIe0OEzBE6%2FNSSQUpJJJJSkkkklKSSSSUpJJJJT%2FAP%2FW9VSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2F%2FZADhCSU0EIQAAAAAAVQAAAAEBAAAADwBBAGQAbwBiAGUAIABQAGgAbwB0AG8AcwBoAG8AcAAAABMAQQBkAG8AYgBlACAAUABoAG8AdABvAHMAaABvAHAAIABDAFMAMgAAAAEAOEJJTQQGAAAAAAAHAAIAAAABAQD%2F4TkjaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu%2B7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI%2FPgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSIzLjEuMS0xMTIiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4YXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyI%2BCiAgICAgICAgIDx4YXBNTTpEb2N1bWVudElEPnV1aWQ6RTcxOTVFNTY1NzMzMTFERDlFNzJGQ0E2QjkwQUZBRjU8L3hhcE1NOkRvY3VtZW50SUQ%2BCiAgICAgICAgIDx4YXBNTTpJbnN0YW5jZUlEPnV1aWQ6RTcxOTVFNTc1NzMzMTFERDlFNzJGQ0E2QjkwQUZBRjU8L3hhcE1NOkluc3RhbmNlSUQ%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4YXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPgogICAgICAgICA8eGFwOkNyZWF0ZURhdGU%2BMjAwOC0wNy0xOVQxNDo1Nzo0MS0wNTowMDwveGFwOkNyZWF0ZURhdGU%2BCiAgICAgICAgIDx4YXA6TW9kaWZ5RGF0ZT4yMDA4LTA3LTE5VDE0OjU4OjA1LTA1OjAwPC94YXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhhcDpNZXRhZGF0YURhdGU%2BMjAwOC0wNy0xOVQxNDo1ODowNS0wNTowMDwveGFwOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhhcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ1MyIE1hY2ludG9zaDwveGFwOkNyZWF0b3JUb29sPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIj4KICAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9qcGVnPC9kYzpmb3JtYXQ%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iPgogICAgICAgICA8cGhvdG9zaG9wOkNvbG9yTW9kZT4zPC9waG90b3Nob3A6Q29sb3JNb2RlPgogICAgICAgICA8cGhvdG9zaG9wOkhpc3RvcnkvPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICAgICA8dGlmZjpYUmVzb2x1dGlvbj4xMDAwMDAwLzEwMDAwPC90aWZmOlhSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj4xMDAwMDAwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8dGlmZjpOYXRpdmVEaWdlc3Q%2BMjU2LDI1NywyNTgsMjU5LDI2MiwyNzQsMjc3LDI4NCw1MzAsNTMxLDI4MiwyODMsMjk2LDMwMSwzMTgsMzE5LDUyOSw1MzIsMzA2LDI3MCwyNzEsMjcyLDMwNSwzMTUsMzM0MzI7QzA1QTE5MDRGRjAwQUJEQzA1MUJERkFGMDIwNEVBNTE8L3RpZmY6TmF0aXZlRGlnZXN0PgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24%2BNDUwPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjMyNTwvZXhpZjpQaXhlbFlEaW1lbnNpb24%2BCiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U%2BLTE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6TmF0aXZlRGlnZXN0PjM2ODY0LDQwOTYwLDQwOTYxLDM3MTIxLDM3MTIyLDQwOTYyLDQwOTYzLDM3NTEwLDQwOTY0LDM2ODY3LDM2ODY4LDMzNDM0LDMzNDM3LDM0ODUwLDM0ODUyLDM0ODU1LDM0ODU2LDM3Mzc3LDM3Mzc4LDM3Mzc5LDM3MzgwLDM3MzgxLDM3MzgyLDM3MzgzLDM3Mzg0LDM3Mzg1LDM3Mzg2LDM3Mzk2LDQxNDgzLDQxNDg0LDQxNDg2LDQxNDg3LDQxNDg4LDQxNDkyLDQxNDkzLDQxNDk1LDQxNzI4LDQxNzI5LDQxNzMwLDQxOTg1LDQxOTg2LDQxOTg3LDQxOTg4LDQxOTg5LDQxOTkwLDQxOTkxLDQxOTkyLDQxOTkzLDQxOTk0LDQxOTk1LDQxOTk2LDQyMDE2LDAsMiw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNSwxNiwxNywxOCwyMCwyMiwyMywyNCwyNSwyNiwyNywyOCwzMDtENDYzN0NCOUQ0MUExMEJBN0VGNUVCQ0RCNjMxODMyOTwvZXhpZjpOYXRpdmVEaWdlc3Q%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY%2BCjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8%2B%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQACAYGBgYGCAYGCAwIBwgMDgoICAoOEA0NDg0NEBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAEJCAgJCgkLCQkLDgsNCw4RDg4ODhERDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgBRQHCAwEiAAIRAQMRAf%2FdAAQAHf%2FEAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPBUtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4%2FPE1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1%2Bf3OEhYaHiImKi4yNjo%2BCk5SVlpeYmZqbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEyobHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp0%2BPzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1%2Bf3OEhYaHiImKi4yNjo%2BDlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq%2Bv%2FaAAwDAQACEQMRAD8A7%2FmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmrlHI2utXn6WltnFESb0RFSp41WjD5q3LCBaslzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Dv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2E2rj0LuzvF7PxftUf8AXPLDnC3XI0ewdpOkTK4P08T%2FAMK2Ec1TLNgaxmE9pFIDy%2BEAn3GxwTgV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV%2F%2F0e%2F5s2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXYUeZpfR0a4kpUAx19h6i74b4X65CbjR76FV5FoJKKe5C1GIVLdDuGhuXs2IKOX4gfsvGdx%2FskKtkiyD6bctHHbXgPImOC4qOpXj6E1ff4Mm4Ndxkpc770BvNmzZFLs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F9Lv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVokKCT0G%2F3ZgaioNQemANZuhaWLyE0LlY1%2BbGn6sWspA8QA%2FZ%2FUcVRWbNmxV2bNmxV2bNmxV2NZQwKnodj9OOzYqwDT4iiPZsa%2FVLm5sj7I%2F76L%2BOTXTpjPZQSt9pkAb5r8JyKXqfUvMOpIQOFxFDfoP8qFvTl%2F4Q5INDkrFPAf8AdUhI%2FwBV%2FiH8cnLeAPcxHMprmzZsgydmzZsVdmzZsVdmzZsVdmzYyWRIo2kkYKiirMdgAMVX5sjTeaGl1CzsbSAOLuX0w7k14KCzyAD%2BUZJBirebNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV%2F9Pv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVo4CXUFa9e0CbJRWkr%2B0RWlMHZEJ5WtPMtzE5%2BGYJKnyK0%2F4kuEC1ZeM2IW0okjFT8S7H%2BuL4FYz52mMWn2tDTlcpX6FbDHR5ecCH%2BZevywl%2FMM8dJtn7LdJ%2BKvgry9ccrOJ69KVw9E9GS5s2bAh2bNmxV2bNmxV2bNmxVi3miFU1LSbxvsStJYzH%2FJnSi%2F8ADYpoEzLcxq1T68FGr%2FvyE8GrgjzdC0mhXEsY%2Fe2pS5i%2F1omDf8RwttJRHdrcDaNbhZVI6endIG%2F4mcnHeJCDzDL82bNkEuzZs2KuzZs2KuzZs2KurkZ843Zhs4bVTQzvVqfyrvg3zHNPb2UMsLtGBPGJSpIqjEqRUb9ch%2BvSSvp0JlcvJCLyMuxJb4JNvi%2F1SuEBVvkaX9L%2BZbm8Sv1fTbf0Y6%2F78lPxH%2FgFzpeRvyPbWcXlyyubWBIXuoxJOUG7uKjk5%2FaOSSoxPNXZs2bArs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F%2FU7%2FmzZsVQ2osyWM7o3FlQkMOxGPtZhPBHKCDyUE08e%2BM1Bedjcr4xPT%2FgThV5cvBJbpGergEfMDcYa2PkqfZs2bArs2bNirs2bNirs2bNirshfnRGtb7T9TX7LcoJD2qDzSv3vk0wl81WDajodzDGKzRj1oR35R%2FFT6RthHNXaVcrKsUtftDi304dZz7yrqPrQCIncdMnsEnqRK%2Fcjf54yVjP5gwtJ5ckcD%2B5mik%2BivH%2FAI3wD5RnEtpwrWgw983RGby1qiqKlYGkA%2F4x%2FvP%2BNchnke5BIUGoPQ%2FPEJHJ6XG3JFbxAOPxOA1iX7sUwIdmzZsVdmzZsVdmzZsVUbqBbq2mtm%2BzMjRn5MKZDLP4rC2UVq9qYj7SWshWn%2BtxOTnIVG4ttWvdNaiiK7FxGDtWG6Ti%2FwDwMuShzRLky%2B1lE9vFMP8AdiK33gYscKtBmDWhtq%2FHbOyMp68SeSH5UOGpyJ5pYde3moWPmW5KzH0pIo2WNt0A3Wv0ts2D73zJ6FgzxqqXpKpHG9eDMx7EYXed45VeC4gPCdYyqua0I5q5VgOv2cAW8sWo2QMqhwwoyHcVyQFhVOLzvrNrdf6fFHLFX4kVeBH%2Br1%2F4bJlp%2Bt2GqWrXFnICyKWeJtnUgV3XOP67qFjaFLHUrprecy%2BlZajxPDmN1jn8Dv8Aa%2By%2BIaXrclrdtZ34NrfR1USofgeo2K06c%2F8AgcBCXr%2FlvXRrdnzk4rcoSJFXYfMbnDzOV%2BTr82eqAOQIpaBq7AV2r%2BOdTriRSEp8z8RoN9I5AWJPV5HYD0yHr%2BGQg3Ntq1lcLazx3CiZviiYOKzQo9Kjb7YzoWqRevpt5AOskEij5lCOmc60%2BWCK0j9ONYo5IYp%2BEahQWBeNjRafEcMVTvTb%2B80fyHZ3FugE9vSORJBUqPVZW%2BEHw3yVaXeC%2FsobulGkX41HQMNmH%2FBZzC%2B1iTTbadZUkjtJjzkQqSKqNm5fsdMPfyr1%2FRdT8vra2E5N5C8kl3bSMTKpkctz%2BL7SP9peOA7fFkIkgkCwOfkz7NlA1y8DF2bNmxV2bEJLy1injtpJVWeavpREjk3Hc8V64virs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F9Xv%2BbNmxVplDqUbcMKH5HIPokjWsstqx%2BK2lZPf4Wpk5yF6nF9R8xswHGO8QSj%2FAFh8D%2Fqrkoc6VmSMHUOOjAEfTjsAaZMXhMRPxRnYf5J6YPyJV2bNmxV2bNmxVQuzOtu729PVUclBFa07U98DafqsF6i9EkI6ePywwyF6vG2i6oJRUWl6xZCP2JOrr%2FsvtLhG6s0yiK4X6Zfi4QI7Vb9lv5h%2FXDHrgV5c8B0LzLc2Q%2BGF29WDw4SfF%2Fwp%2BHOg6ZMHi4j2YfTkZ%2FMGwpFaazGDytm9GYj%2FAH25%2BEn%2FAFX%2FAOJ4N8uXwliicnps305LmFZDewrc2dxbt0lieNvkylc45%2BX10UCQzHjLATE4P%2BQeNc6L55e5i0J57WVonikRmZDSq7jf2yB2Op%2FVYor2%2FwBNjktpAGjv7ZvU%2BInoy8Vao%2Fa44xCQ9as25RVBqKnfBGRby9rBEsljcIY0aQ%2Bg5FCCQD6bj9lslGAikN5s2bArs2bGyOI0aRuiAsfoFcVS%2B%2F1q2sJltyrSytuyp%2ByDsC3zwxBqAfHfI%2Fodmt2z6pcjnJK5dAexPT%2FgRkhxV2c782mSy82WUgNIdUhNs4PTmlXQ%2FwDBDOiZAvzLb6nHpOrlFkjs7lWdG9yvxVG%2FwiuGJ3Up3YTJFr7op%2BC8t1ljHjxodv8AYtkiyHbxx6XfKx%2FcXBilJ3%2BAsVUV8FRlyYA4Z80DkkPmq3EtlHJSpR%2BJPgHBH66ZBtIufqt9Np8pALHnEpO5p1oM6Xq0P1jTriPoePIfNdxnLtZtrGZluBcG3vF%2BFJYuJfx40IIxHJKzzb5et9ZtZYp1LwygcgPtKy%2FZkT%2FKWuRKKCOC6tNH1CblcWiILDUWHH6wg6wyDejx9Fw4sfOMjEBY5biH1HgLy8VYulOfJV%2By38q4ndH9LlL219K4iilHrWzLxlhI6SgE1%2F2SnFKo0Czxy28zMqcSzspIYBPj2I%2F1cd%2FytnVZdNQ2D1I%2BESMoPw%2FzMftdMG20TSQyyblmjenzoc4TY6ld6ddyKwMUsbFZoHqNwd1KnGXRXs9n5%2Fv7lg98GlWhMjwk1A%2Fm4k%2FF7jDSwSPULTTJI3PGBphGNwG4OHVW%2BgnONX%2BsWFpDFPaSSJcTESG3TcRlf2uRK%2Fazq%2BkR3aeT7e%2BjnX6yoW8ibht%2B9i9Tg%2B%2FQ98QVZHqstutowuSPSdSpV91IPVWzif1i98t%2BZ7m40FjbSWUrNCqEmkex4EH7acT9lv2c6lonmbTvMUTWdwgiumWktrLQhge6H9of6uRLzd5ZurDVP8Q2KetYFUW7jXeSKi%2Bm0hH7UZH7X7OCY4g2YM0sMrjvexB5SHcXsnkPz3Y%2BcdPqKQ6lAB9bta%2F8lI69Y2%2F4XJeDXPJlvPe6JqMeq6NMYLuE842To6ntToyuv2k%2Faz0J5E8%2B2HnGyI2t9VgA%2Bt2ZP0epH%2FNEf%2BEyESeUuff3tmbFAx8bB9H8Uf4sZ8%2F6P82TMMLNb1iDRrQ3EpBkkPC3j7s5%2FwCNV%2B0%2BGVc4n5m8yfpnzJcsr%2F6HYsLe232PBv3j0%2Fy3%2BH%2FY5JxkLourX175%2Ftb28maSZrloqsdlQ1Tgo%2FZUDO755%2B8uxlvPcMI6i6LAe2zZ6BwlS7NmzYFdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVf%2FW7%2FmzZsVdkb83W5%2BrW1%2Bg%2BK1kAc%2F5D%2FCf%2BG45JMC6jare2FxaEf3qMo%2BdPh%2F4bCNiqV6VOOcTV%2BGReB37%2FaX9WHoyFaJMz23BqiWE7juCh%2FsyaRsHRXHRgCPpwy52gLs2bNkUuzZs2Kuwu1rS49W06Wzb4XI5Qv8Ayuu6nDHMemKvONE1KWGVrG7rHPCxRgeqsppk7sb0XC8W2lUbjxHiMhnnnTHtLmPXrZfgakd1TsR9iQ%2FP7LYtomq%2FWI0Iakybof4H55LmFZdqNlFqNjcWMwqk6FD8z9k%2FQc515XuZbW4l0%2B5%2BGWFzE6n%2BZTTOlW063EQkXY9GXwPhnO%2FNsS6T5qtbofBHqaniexmi%2B2v%2BsU4v%2FlfFgHcrPTFbanZNb3caywyApLE3Qj3zjutwW%2FljzBc6Pp6fV9JHpsLNSSgR1HIorV3Vvizq2jXQk%2BAndlBA9xnNvzRiWPzJBJWnr2gJP%2FGNyv8AHAdikJ3YGf0o5pZDIboSD1GJJ9aAgh6%2F5aUzoFnN9YtYZ6gmRFY06VI3zlXk%2FVI5bi2hnlBgdBuxFFahhfr0rSNsnHlXU4JI5NJaSt1avIQm%2B8RaquD0I%2BLjkpbgIZJmzZsirsDagC1jcqvUxOB%2FwJwTjXUMrK32WBB%2BWKpfofE6Xb8f5d%2FnhjkMPmvR%2FKlrJBqkxBSVkjC0PIDpTfI15s%2FMNbu3jbR5plsZVp69swRi9N0eSjMnH%2BRcVpm669cWWtXGnatwjgKma2uPsj0x%2FNXw%2FayOfmLr%2BhX3l%2BPTYrkXM%2BpGtm1uPUUceX7yRx8KLtx%2F1s5xoN%2FefpVr6C8muVVfUuLO8kMrEIeTem7faRk5Iy5FdTuZ9B169trGZvqkc5eFD8SmJzzUcTtsrceWHzTT3fRJv0p5UZvtOYYrgCu%2FJV4P%2FwANHnPrj83vM2lX9zpWoBJYInaJ540pOi1%2BGRQDxf4cln5Z3qT2stjWrK0iexEgE60%2F2XLOUfmHYNZeZbhWP94Ayn%2FV%2BD%2FjVclLlbEdQn955q1wTiU3huLadeQCsfSljO9VNev%2FAA0bYHub1Z7V5yWkhHxpcVHJNwGiuP8AKStVk%2FbyGafqLQxyafO5S2lPJWUAmGQ%2F7uiB8f8Ad0X%2B7F%2Fy8Ctd6jpl3Lb3v%2BkQTpxniqTDcQn7LoR%2FwUb%2FALDZCyyekWl9ZyaFLPelZGN2tqDEtZJHenEs67ckpVXwq1bU7G1iKWvqtcCR4bkkhFYx%2FCHXhvvX48C%2BU7H1SbK3k9Wya5hv4HchSBF8FxBL2WeOJ1k%2F4s9P4cu%2F0j%2Fc1FphmFxPLeOhZBxAiL8jt40%2Fa5YegV6PprvLpcE8sfpTNbh3QVoGK12zluoQ2PnZih42fmWIVt5z8Md4q9I5P5J17Z2A8YrWVl%2BERoaDwoNvupnLtb02LzREdR0hBba7bVe4tEPFZwpr6sH%2FABb%2FAJOSkgPNLqGaG7e3v42iuoTwlicUIK9iM755KnGoeT7ZX6m1CH%2FYSNCf%2BFOctnuIfNkSWeqEWuu249OC8ccfW47elcd%2Bf%2BVnQ%2FyyS5t9Dk067jMVzaSXMLK3hRJlp4r9rjkQl5PbalcaRd%2BlI7PbwysI5lNJIirU%2BH%2FmnOueV%2FOUeo%2BnY6hIpuWX9xPtwnU%2Fhz%2FnTOPeY4zY%2BYNTgpVBcSVQ9CrHkP14Gsb9rFgRWSzY1K1%2BKNh%2B0p%2FZcY2Qdlev%2BZ%2FLKxK19pcdYBUzWqdYx1Lwj%2BT%2BaP8AZ%2FZyHW9xe6ZfQ6ppk7QXsBDRTIevsR%2B0p%2FlyY%2BUPNR1KJLK5lD3KrW3m6esg67f79T9pMD%2BZtCjTnqVilIW%2BKeFRshPWVAP2P9%2BJ%2BzkZRB3DPFlljlxRPz5Edx8mcw%2Fm3aXvkzULyUC3122jET2oNA8kh9NZYT%2FJvzb%2BTOZabIhnWkgaOQcwxO577%2F5fjhKIYndoZvgaUD05v5SN1J%2FyG6YI0RZ4tS%2BoyKQS1HTwYHsffALrdOQwM%2BLGOEHfhu6PWvJ6f5FsfrXnqW8K%2FDBEZD4cmAVf1Z2fOSeSLmfT9RrHEWlvDSZaVagNR0%2BztnW8mWsuzZs2BDs2bNirs2bNirs2bNirs2bNirs2bNir%2F9fv%2BbNmxV2Y5so4qw6WL9HeYLiLpFcUnjHQfH9r%2FhuWSbTnBgMXeJiv0H4l%2FA4T%2Ba4fTW01FRvDJ6chA%2FYfpX5MMGaXcBmHxbSrsCdi6%2F2f8RyZ3j7kdU4zZzrTfO2qQa3eaHrkax6haux%2Br9FkgJ%2FdT2790ZP%2BGydWWoWt%2BnK3erAfHGdmX5rkSEovNm2yiQASTQDqcCt5sL5dZ06Ko9YSMNqRAv8Aiu2Iv5i0qJecsrKo%2FwAkk%2FctTho9yo69htbi2e2vApgmHB1cgA1%2BffOYX%2BnXflXUghJezkNbabxH8jH%2BdcPPN82l65b2j213DN9WdmmtefCRkdeJaNSVb1I%2FtYXWM8cVk2javM95pUv9zcPvLB%2FI3LvwwhIT2x12KCBrxjyjRS06jrxG5YD%2BZcrzzpkXmjyhLPpzCSaFVv8ATZozuXjHP4GH%2B%2FE5JkUmju9BujaTsJYHHKC4G6Sxn9r%2FAJqwJH5g1fyjDaw2KrcaE0zSNE27RiQmtuXPSJ6%2FuW%2FYkxI6hUg8ia9qulatHe3s0k8DgxGKZywBk%2By3%2BTkn%2FN63a8ttG1a3rHL%2B9hIB%2BIdGK%2F8ABA5DNbS0tL%2B4ksJOenzETWrjYiOT4gjD9l4WqjL%2FAJOTTU9XtNc8r6dDP8E8rc4iSP71RwkTfcP%2FALsX%2Bflgq09zFvKeoTMwV2NeCsB0BaNqf8a5N9Zu59K83W2pWb%2Bn9cjBo24ZXUNxI%2F1lyCWsL6fNb3LLxid24tSgINA9Puyb67pd%2Fr2kaPc6bxN5b8owWYKAYW%2BGpP8Ak4RyQU8ufOF7JLD9XRLaAj96ZPiYv4A9An%2FDYOfz9oEbtA0kjXSqG%2Brqh5NtU%2BkW4iQL%2Fk5yK%2FiuNagjEcpTU7Rj6YqaSAbtG3%2FFin4om%2Fa%2BxhXcGeWxlDgpPbfvUFaPDKpFXiI%2FYk%2Fk%2FmwFXr0n5hfXBJFpVtxmC84jOa81%2FaKqv7Sfy5GL7zJq2rwXFhqF40AlWiSQfAEPVW%2BH7Sfz%2FwCRkTl1dLW3t7xi8108C3g9EBArCnI8jtVvtNRcR1zzTdJcQvYwRW7y28dwzFfUblIvI05fAo%2F2OGxSt3zSW9LPXITMlCjiuzofsyRt2YfsPkd0iabTtQksp1Zra4U84X6SJ%2Bw4%2FwAoD7Ei5dzqV7LFCt7KZnVAY%2BRqyg9aj%2BX%2BTJdouuaHrltb2PmCNfrkKmO2vKcT%2FkgsP2v5cjtapfo0Jt%2FMllFGHltZvVeK4ptwWNi6yU%2BzIvRlwm1mUarpNtJHEWutOkuIbh0FSbfkGiZ2%2Fb4MxH%2Bpk6uZNM8saddywStNK605tsKnYcF%2FmbOZy3s2nWaLE%2FG4e4%2BsK43HELxowOzK%2FL4lx5fFWb%2FlprP1TWLQMaLMFRh%2FlRtxr%2FwLYn%2BfNjLp%2Bp2WpwghWLxuf2SGoRX%2FAIDI9peo2kMtjfWQMUi3JM9u24j5rT4D%2B1G5qyfyfYzp%2FwCcdimr%2BUra%2FG%2FwpISOoIpXJDcIPN4HDcR3Scl2I6qeoOGNvcQyw%2Fo%2B%2FJWGpNvP1aBz%2B0PGFv8Adkf%2BzXIxSaym2NCOh7MMNYLhLlOS7H9pfA5FU30vUNQ8uaqjIqsaqHiO8UsZ6EeKsrfA%2BS3yUr6p5iudTl2S1RvRTchTKeCgV%2FlTlkOsvUveOn8VkdatauxoyN%2FID%2B0r%2FwAmTfyYHsrnTdPasc1%2FNczXMZArwhT04g3%2FAD05Yjml6Fditlcmu%2FpvX%2FgTnK5bS4sp1mDtFID6kLioPXZgc61NGTBNH4o1PpUjIpNbyX0Ulvcw%2FV9Tst7i2IqHWn99EP2kZf7xF%2F18nMX1pANMR1TTYfMkRuYYlt%2FMEY5EfYW7UftL%2FwAXjDf8u%2FMFzM8mnX6MJLWRFkMh4yiqtGUYU%2FZP82IzW%2FH4DVQG%2FduPtRP1FG%2Flb9lsG6ZJDJfPcvGI9WCoJZwPhmRGHF2X%2Ffi4AP2peafmBE8Hmq8LgATCOVaeBUL%2BtcjauUPip6qe%2BdJ82x293Pb2GtyL9ZmWRoL5QAUZZGQK47p9nOd39hdaZctaXScXXdSN1YHoynupwFCL0zUX0y4SVGf6uWDqybPG46On%2BWv%2FAA652nRtY%2FTdqJbfjLdqnK4gjpSVen1iBf5G%2FwB3Rf7qfOCxycaqwqjfaH8ck%2Fk%2B61K11AC1kZYIT6gmB%2Bxy2oP%2BMi%2FC6f7LEGlZCbBr%2B%2Bu%2FqqpBCjP6cbtxHwn4ljrX%2FgckOj2EMcC6lK6yTRKYmkIoYl6gE9xT7L5HhcTJqM1jJCEjjQSQsK7qT3rgi%2FuJ7PTDKQ31S5f03ZPi3SrfGP5RiKG6bej%2BQPzB0CG%2BuNHvB9VeZx9WvZKBX7cCf2P8jOvghgGU1B3BHfPHaQrfuEt3DHkAJakKhP8AO37A%2BedX8i%2BYPOen6dNp2qK5t4JFjs5HClyoNJfTdieUaL8S%2FwDCZEcRNEc%2BrfOOKWMTgeGUaEoHfi%2FpRP8Aunt2Ab3UorWqAc5QK8egHzOPhuoTYrdesJYgnJptgDTqdu%2FtkT1C9XnLPIwofjLduJ6fhkgLcYsi0vVRfSSRSUWRd1UeGGgyN%2BVrCSkmrXKlXnHC3RtuMXXlT%2BaTJKMTV7JdmzZsCuzZs2KuzZs2KuzZs2Kv%2F9Dv%2BbNmxV2bNmxVB6rafXtOuLXvIh4HwYbr%2BORbRL%2BsMTMaSQsOSnrseLZNDnIdfXXtP8zXtnZGC2tJWEsUz8pH4yAE8Y14rs3JfibJRPMKyf8AMXye3mKyh1LTH%2Bra1px9S0ulFSV6mNwPtRnIb5e8wXlw%2FwCj9VR9N1u12PGoD9vUgfpJG%2F7UedN8uapcXsH1e7C%2BrAi%2FvV6OPs1K%2FsttgLzP5Ysr%2B3NzHFxmjPMlNj%2FrLTdWHtiNtlU9I82sxa11lAssf2bmMfA9PFf2G%2F4XIt501y81SykEEjRQRMGEaMV5L0%2BKn2sq4ke3j9O4asuyLNSnMduX%2BV%2FxLCfVZ%2FS0u8cDkVhYhfGgxodGQDDZPMculusTzpwryMT7mneh%2B0uTVNL1e%2Bsob1tLmmtJlEkbFW5FGFR8NfUzi2iQ%2FXNXtLjUAXWe6hE3L%2BQyKHG%2FbjnqzzP5ok0H0baxgWVyodi5IRU6KBx74ASrzOzsfL1tcH69ppMtdxM0h4%2F883OS2GLS7239K1pGpGwj%2BEr%2FALHphVcecBcyrc6vBFdabL%2B7lV0UPbt12KDnx%2FlYYGvrWKwP1nTp3kirsT0BpWgcdf8AZDJg%2FFjSanSb8xC2ursXtpG5kt7dkVRGaU4g%2FETy%2FwBbIzc60xSeylsgphDJeafNtIY%2BnqREfCygYcWPmV14x3qkqQDzAo4X%2BYr%2B0ME6h%2BidZZImjM90FLQzQj40BFNn2%2B1X7DYkd2yi%2BrHNIh8uXbLp7yLJdToZbZJDX1EU0NOXxCeL9tf%2BJ4PhbTLya58sXqqt3EoKlV9MSoRUMn8ssf7XH%2FXyD%2BcfKFzoWnRatFqUUU%2BnSGSGAsBcFXZaFeHJVZDu2HOn3See9Kt1%2BsCx8x2y%2BpaXabB2TxH8pP21%2FZwAnu3VQuV1CC2iW9cx%2FwCkS28kMtA5dKLHL1%2B00TJyZPhk%2B1nS%2FKN59Z0GSNno6yI6e1V4v%2Fwy5z9pv8YWj6Hrcf6P836EWmiiHSfiv2ox%2B2kwH7P2ftLk28uxrbeXYLhYXV7gMzxkE0IqPiqNq%2Fy4gb%2BSk7PNdX1m%2BHmqaytJ%2FTtTdx%2FBGoSu6swZqcj8YbCnRmM97drKxKXcslu7tXb1%2BSBq%2FwCSzZK9c06xuNUutStkBls5IFkAIAMoHqUUfzNRkbCPW5Xs70rZcVtL0%2BtCoAFVc1oK%2FwArni2RIShdD0rU9Z0g2UNW1DR5JLG%2BirRlQuXhmp1ZP72M%2FwCph75g0K2sra21G8ch5baGJUpt6yARuvL6OWEQk1eJrq9s55YLxBzuGhJEjr3LKvxSqv7WISanq2oQRXGryvd2MtVhuSQ8aNQch8OyNjsqElhK1hbcr%2Fcv3K%2FyV%2FycRSQBmUiifsfQM6FpfljSbrR0mCy3EjDlDIzN9sGnphU%2FZ%2FlbIrqnl%2F6jM3Cb1VRqOoU8078XoOHPISIiQCd5ckgE3tyS4StK6xSy%2FDJ%2B7HqseO%2FYVw0W2thatp10he2b7YH21cfZljP86f8AD%2FYxO4tbO4so4I7ZUvG5Lpl3PssprUws392JD%2Fut%2FwCbAOh6sLuU6VqYMF4hKRu2x5DrFID0YY7rsh4tMn06%2FkspjzjniaS1nQfDIE%2BJWXwdafEv7Od0ugnmL8tmYipWEOB1oGWp%2FwCH5Zy4hoT9WuU5BTzj7FHp%2FeIe3%2BX%2FADrnR%2FysvRfaHeaJIwZ4RJGo8QDzQ%2F8AAy5OPVBfPNxbpMpifYqaBu4IwFYWlx9fEIJU7lmH7S%2F83ZJ9e0x7K9unRg8azMkoHWJySVR%2F9YH4W%2Bzj%2FKtvFdaxBDLQKzAVO243G%2F8ArYPJUXb2NvDZtdyWxe3B4meCQ%2BtG37Jkjb9lafaXHRXl9q2r6etnWJ7Yxx28h%2B1UNyMjt%2FlH4myX6lp1poPm57CIFLG8jikCsKisqqXWncc2%2BzhLZW0Om35hCuLiCeSgIHFY67KT9rl4YaV63IwMVSakg1pt27eGRuzvrTWILdYbnlcIWXSdQc8W5pXlp91%2FLIP91M%2F94mG9tcfW3AQ14xgsPH4c4Zp%2Btz6HqN2rJ61nNIy3loTTkA54yIf91zR%2FailyRNUinpV0iXqyyxw%2BjdW5Md%2FaN1iPdqf74f8A5Jtka1ZZ4LWYxsySKpaCRT8QI%2BIxkj2Hw5KLe6%2FTsUF%2Fp1wsmrRRk2l0QAL6Fft290g%2Fu7uP7Lo395hFquqaL9XZmmWCRwySWTgs8MgG6Oo%2FY5fZbAUhC%2BZ9JsfMl1SKYW%2BqyQRT2DMf3UvJeTwt4M5%2BJchKyrOr6D5gQwTQEpFK%2FwDeQv8Ayn%2BZDk%2B1Gyj1ew0SaC4gS5ntmCtX0ELQtQAM%2FEcv5Fwv1bQxr%2Bnhr4oms2ymP6xuCStSIbof6v8AdzYOaGAQ%2BXdTm1NdLjj5SN8QkG6FOvqBu%2BTjTrS2sbZLCMcVUmkpFGLn7Rf%2FAFv%2BFxTTme102DTuILIwZpKksW%2BzxWtW41%2FZyU2XlW41HhcahytOQJZVHKaXw%2Fd%2F7rY%2FzviAqQ21ldXtytrDG0k524qKmnj8sO736roNmNIltfVv7hFeaMnkUqTwpxNA2S2MaT5etS9w6WdtEvx1b42A7Symh%2F2C5zTzb%2BZun3swOjacGVKwrqMgoajeiJSr%2FOTDVdVZPYa3pOj%2BW7u31uKKzuZGP1dePP1Iz1UovxtJ%2FlYU%2Fl%2F5hOtyS%2BWrlis1Gm0pyfiR4%2Fi9Kv7SumcrvNYnu5DcXH7yToamrfSf6YL8ratLZ%2BZtJvoT6bwXMbEjuCwVh8uJauPFuFe%2Bx%2BYJ4NMawQiOFzyZOlDX4h%2FwQyQeVtAn1BY9R1QN9WU1toG%2Fa3qGYeGE3lnRLPVfM19LdAyWkTtcW8a7xOGaq1kGz7k%2FAM6moAACigA2AxJ7lojn72wANgKAbDLzZsirs2bNirs2bNirs2bNirs2bNir%2F9Hv%2BbNmxV2bNmxV2Qzz3p3P6rqKbFKwyt%2Fkn4lJPseWTPOVfnX5gmstNtdBtnMbajykuXHX0YyPh%2F2b%2FwDEcbrfm24MRzZI447GR59w6pMfNyWl7EdD1KM3Cp6dwoKldj%2B1zHE%2FRko0rz1rUtz9SvLe2lm2Hp8jC7BhWqV5xvUb%2FDnn01jPEin8pHgckemXmsTaHcxANw05o57O7IIKxliJYll%2FaRT%2B84%2FsNkIZhIkSjRDl6zs%2FwIjJCXFHkb5gp3%2Ba2r%2BbdPuEvYLVbLRpiFL27c2EvWkj0%2Fd8v2VpkGsfN2p3Kra3V2XicgEsQeQJ3qSNs7d%2BkdO1rSbKw1LhN%2BlbRXVJKFZyq8ZV3%2F3YGHLOEecvKFx5TvTLDym0idv3EvdG%2FwB9yf5a%2Fst%2B3lpHUOvsua4iNwbWCruHK%2FB0qN9j0zoy%2BarW48r2Oq3l1Le2sSizuGkqk6zgUVXMda8F%2FwCDTOO2M01rIs0XCRQ4kHqCqGnZl74NgkTlMqSOIZpPVNrXjGG8Qvfj2wXzSyqz1eO%2FtNSjQM8sVubhFbbkIpPiI%2BUR5Yca3q17IdPmtZ2js7yztriONNhyAKSciPtMJUfIZaXDWL%2Bra0jcq0ZYAV4yAq6mv8ynJf5Yex1PRjpUsfO%2F0pnuLJWNPUgkNZ4l%2Fm9Nh6vH%2BVpMRa2uvBcXej2Gp2rSC5tJJbOZkqW4v%2B%2FgY%2Bx%2FeJhtBB5sOi313boLa9ihMlo3EB5ClGkULy%2B16XJk4rhlZarBFAbZljtrS5Tg%2FoIF4fyOT1JjbC1L%2BbSbowyMwu4WrzJryX9h1PQpxw%2FFWFXNut9PB6srS6frlqI4JZHJ%2Br30bCsbOf5phx3%2FAN1T%2FwCRgPQbtreVrEFrW8gfnDU0dJUPF09unHD%2FAFeygjjvbi2haTQbpxcXlpHvLp9x9n6xClfjtjXi1P2P3b%2FGseE99ol9qtyjIKamYlktb6Mn0L5QKJ8Zp6d3x%2BH4vt8OD%2FvciTXPZaZ%2Bkdp5%2Bgt19Yad5p0%2Fe0vU2Y8T0NPiaM%2FtJ%2FuvOj6lcGysFaRlYxJWUjfkUXk3z5MM5p%2BV2l3U2pPdalFHK1sOLyqQxSZaFOdDWOT7W%2F7eTTzK8UdlcxREhXBTf%2BdzWg%2B7JxNji70HnTz%2B51GXyrrFxaapD69nfcRqSftqxHL1oj%2FOjPjtQ0GNYCfrCXWi3QMtlelhyjkYbFfdvsyx4M87Wy6zqemTq4X9KwQnmegkK%2Bmf%2BSi5G9M1PVfLUt3od3CJIGDEQS7iKUD4Zov8%2FjyErqrruPNkK58%2FJN7Sx0%2B0sDe3k0k9xA6JBJACGqwPHiKp8S8fts2F0mrDS3nubPThH6ZLa5YcQsnBvs3kSsCnJeXJuK%2FB%2FwAYmwQ%2FrXdjEsQRizhvjKCrUp8If4uW%2FwCyuK3Fs893p1nezfVdeDCOxmHFi8ZH%2B89wSeBjP2YuX%2BVFiYggA70Pmt9QzfyjdxXeiPeaY3rNYt9ZjdKUkgP2gyj7L8ftL%2FPgTz2tuLdJIT6cN1JFcxzJ2U7Sb%2Fs%2Fa5BsKfLEMvlPXby2sJI40uF%2F0izVuUcb05n0z3gkTlw%2F3237vHeddXN5pEMdnbmOys4BFcy0qDcF6GJa%2FwDFacmyX8Pu2R1QOi6Yupabd2F78dj6xjLVFUlpySaHwkUf7F0%2BDIrrvl6dr42N0eOtRIHs7ldkv4B9ijf8tKgfB%2FP%2FAHX28mPluyb0Vs7OdTL6SSReoK8%2FTZqN%2FrqOPL%2FJwTqdhFr9m1lqUhjnhcm2uv8AdltN%2FMKf7pb9tP8AZrjVhWHaHrEWqxjTb9gt9GKQytsXptxNf2sl35d3J0jznJbCojuQlR26cT%2Btc55qthdyXcyyJ9X8wWfx3ESbC6UbrcwfzS8fjfj%2FAHyfvPt88N%2FKuq3F3q1hqBIEyc4pWZuCkoAwck9MA50VPJV%2FMq2k0TznfCJQUmPJom%2BzJG%2FxcW9t%2FwDY4Q6cILa6ZY2eKRwslv6goaDfjy%2FaK4f%2FAJleadM80a8t1pcZ9GCJYmuG2MrD7TAfsqv2V%2Fmwr0WyuNXUw%2FVxci1%2BKLt%2Fw23wrg6qGVanr1vrK2t3csIry1aESBjxJEYYOQff4cjvm3zLbpZXLWTGC5vr%2FwCswRjfjBGoC8yfi%2BIjCPX2vNMu%2FqZDRNGKSAgUqxqgqKruF%2BH4sjsyPPIZZSXJ7knfG1e9%2Flxrdt5ksbqdSFu7eMLcW5O4%2BE0cV6xvnF75uVzcb14yyBW7%2FaO2KeS9RutC8yWd7DMYLfn6d3L%2Bx6D7SLJ7YhdkNd3UkRDRSXErxMDUFGclT9IwndCtpWrz6ZN8MskcDsGdojxkjcfZmip%2B2v8AL%2B3iWoySfXbiHUmFy14frKXqH4nL14zK%2FVuX7cbYEeJWDPQVpU0Pj0x6yqLL0pJUW4tXE1o5O9SaSRbeP28Cob1Z1T6vPKzxjeNSSQK9aV%2BznXLfUrdtDt9T1U%2BjeRxpa3iMvF5V41jcg%2Ftrx%2BLOd6VbaTqJlS7lMl5Iri3hHwRiQj920snhzODppWsii6zcGeSZg3po3NSy%2FD%2Fen4K9V542r0LyheaXcm5uaCCWNhxkl%2FvClOqcvs4rrn5g2enI9tpKi4uqlWck0rSvxP8Atf7HITazpccriNaLGD%2FozGnBVIJkZP261%2BFuWE4juLqSZo2pbJMjyitAQeVPwrjxbUE01reuXGvCKPUZJ2Cs0iqgUgFtunw%2FCKfDhWLXSzayW73jxuGElWgJUbUoSDhkLcXEjJDSJqkoSd%2BH7OGlh5a1HzDONIit%2FWvZEMkJRlQtGlKsCxVX4%2Fy%2FawWtILTLTTmeMmbTrm1jXe0nmMLOwH2nZlrWv%2BVhlBb6Lql09zpemrbx2cJe5WNqiRi3EKrfyj%2BZR8WJ6x%2BVPnXQrK41i9sljsrUB5pBLGzAVCgiNWLNufs4I8h6hp738sNxRBexei5BoBJWscg%2F2Xw4VZjoMllFbx635Y52k9mvPUdKaRjHIF%2B08PInhJQV6%2Fa%2BHOq6d5%2F0G7toZzK%2FBwKzBCVrTo3GpQ%2F62cYW21DQ%2FMUVrFH%2B5vyInPSIqzfE1f2afawkmtp42klt0f6uzuInWoDqrFQRQjkMJV9Q2ms6VfitpeRS%2BwYV%2FwCBNDg2ozzr5Y1vTI7V9L1PnbTDk8E4Qs7M1KKHBDp9GTfQfOdosSxw6txkp8UN0DxBG37WNea09UzZBNY%2FM3SfLcVrLrDIyXZKwm2bmW40DsF3%2BFa%2FF8eTW1uoby2iu7ducMyCSNh3VhUYEK2bNXNirs2bNirs2bNir%2F%2FS7%2FmzZsVdmzZsVdnE%2FwA9rKYXmj6jT9w0ctuT4OpEgB%2F1lOdsyO%2Bd%2FLUfmvy%2FcaXstxtNZyHosybofk32G%2F1sB5ORpMoxZ4TPIGj7pbPl%2BNPrERi%2FaG6H38MkPlXzh%2BjhHo%2BrmunCqwXFKmDmfiSRf27d%2FwBv9pMJBDcWN7JaXcRhuYHKTRPsVdT0P%2FNWB9RiWO6ag%2FdygSL7cuv3HKpkwImPcXoM2KGaHhy3B3BHf3h6B5qslXQooNOVof0a7Xdi6fDxBPKQQ78vT35xNl6Fr1l5s0yXSNZRHujHxlRhtMv%2B%2FE8JB9r%2FAIfILo2pT6XfLd8mmiKehPAzE8oT%2ByvKtOPWP%2BXKvrafSpl1XT2L6ez84LqLrE1a8WH7PH7NDlkMoluPiHRarRz05F%2BqJ5SHf1CT%2BZ%2FLlz5WvvTUmXT5jW2nP%2FEH%2FwCLB%2Fw2FauD8Sn6c67a3Vh5x0p7G%2BRfrAWskY6EHpLFXp%2FxpnLdc0S88t3ptrir27msE3ZlH6nX9pcmR1HJxFS3uA4oftDBlvcz2lxHc2sjQzxMGjlTYgjCVG6Mh%2BWD4ZRIvv3GC1Z7pdxHraNJCViukq13aswVAO9xCW%2F3Uf8Adkf%2B6%2F8AVw1t5NPkKabe3SyqKG2uIquYDX7Jc7ei37a%2FZ%2Fazm9tdT2dxHdWzcJYzyQ9R4UI7qw%2BFhkwhvDqECTaZbExyHhPbqKmCYivBf%2BKZPtR%2F8DgIkSDxVHqK%2FSyBFbjfvR8k1xY6gVtLb6vJbVWZbhvVLo38yAemYZF%2F4XCjW0McX986%2BX72Uem6V56bekVAYLu0D%2F8AJSL%2FAItjw7W0utVgj0%2B7k9DULc%2BnaSMaFkrvaz%2F8yXP%2Bpgq00iztbkaVcP8AWBer6NzEworLWjRsoJ4yRv8AEjr9jGOMDbn5y3UyJ8vcyfyHpl1puhy31zEE1e9cJfKxXjK8JMaTqfGZCrt%2FN9rCX8wda%2BqyabAGAM85aYA9FUcV%2FwCGyZXElvb20VgHb1LeNDEKmpKDhGS%2FfOK%2BcrhtU1G8vLZ%2BYsz6M0Q%2B0nBh6cwH%2B%2B5af7F8slsAAxG5tkesTm68saddo3GbTZ5bb1B1G%2FrRfrxGTW7LzPpcrahALfzDpir9biI4tJESAZowftJ8Svx%2FY%2F1cD6VMb3RNTsShJmhju4gVP2ovtH%2FgG%2BLBVnb6b5g0ux1JGWDXNDj4SEiongQcGjk%2F34hX7LfsYBukt%2BWba4gWHWmj%2BsWdpK0Dwqy%2BoWkonJE%2FvHZOfNcKdasnk80288TN6EEqxOXNWSOJiS7n2p8eB4bySw1ilv8Au4OQk47kKo%2BLv%2FLX7WK6jr4u9Uee1jItndiIiasyv9sV%2Fl3wWAFZBbvYajqsV%2FDSP1yITPUgSKH9SSinYcIRw%2BH7TSJhr%2BYOr2sOkWmnhHSK6YiRolFF5A%2Bn6ngJif8AWyP2ML6dbiRI%2FrqsAI3SRAsMY%2BxHKrlGhfl%2FefB%2B8%2F3XgXXYppJG1m2unkbj6Op2pUSKVAqksSNsif5L%2FFH9vHoUlrytdtDCgdmV7d5I2boU%2BINGT%2FLhpr3nLTKJ9UT19TB4zNHtCe3Jj3b2XIssp1Y8bUfFOQsix7SCSlAXpTmj4av5OnsdM%2BuzB3Zm9OWaNGD2%2FMgRTolP3sXP4Zv92fyYi6oITDVrG28x6NBeBvqWoWS1ivSeIikU8jFI%2BzLA6%2FGn%2B%2B3yFaq1pcXix28ZUGi3U2wadx%2FuziAEWv8Aq%2FH9vDLV7TzXeWrqbaWO4VhHeNHtFdCP7EhX5D4v9%2BfbwpC3liGgvIoVdSqt6pLPGr0pTj%2Bz8Xw4CUkEGil7RwRu9ZzwjPx8UZiKePShyb%2BRfMdhHM2kLEV%2BsDlDLQFncUCxcRX7WEF1DCs63zCs6r6c1Nkencr7rgXSpFsb5zCForepC9PiUEfZr4DADRVD%2BddUkPmPU4lUgVNtNG4qDw2Bof2kP2GyK1LECpY%2BAqTnUFhsta1Ce%2B1G3inuZCrySsgqwqFNRkwu9R8m%2BWIwscEHrharDBGjSH5tT4f9kcIoop4da6Vqt2GW0tbiQHbikbkH57UwHNFLbSvb3CNFLExSSJxxKsuzKwPQjOu3%2Fmq%2B1mFo1C2NmFMjhTQLGu%2FKRtumQfU%2FMWh3uoTXx0r1pJePKSWSnJlVU5lQNufHnhQxmOYxklSPiFCDvUZQKUIpUn7Jr0%2B7D8eYrBP7nRbUf61W%2FhmPmtkBFvptnEezCMkj78CpRZOY5w%2FBmWhBCjseuSyyn0pIpIr2CSr2gtbYTQt6aylufPp8PBWbCZPNurROskYgVkYOv7tSAVNRtg2780%2BYPNd5CdWuRJHEWkWONFjRaih%2BFAPljsqLeRrVh%2BjQt3GBRoXBBKAfFwag%2BLJ15B17yfealE2taTHcXMG63Eq%2FvbYL%2FvyH%2B6eFK%2F3iJyT9vAXkPygPNEuoxx3KQ3VrAHtYWP8AeOxpv%2FxWqijMv2WZMLdS8vBLx4pg9jqVqTFJIvwup6fvP5lP%2FDLillv5i%2BStPtL867pEEcem6hxkivLWjIk%2FevHb05ftZF9Okd5Et5mayvoWEtrdRmnCUfYmhYfzftp%2B3k3%2FAC68zLfx%2FwCFdWt1eElrbUoW%2BwGIrDPGv7CyU%2BLj%2B18eIeavKM3lq7WqfWNOkYizuXFeNesE9P8AiX7WNbqCncfm2fzRoUmg6zEqa7ZPDPPCv93eQRMC00A%2Fa%2FZaWLCHzf8Al7oOra5b6h5KuBHe3rj61Ywr%2B5Xlu0%2FL%2FdDfzx%2F8Qwpup4VWBrORhfW7iS0YkrJbkdRLKv2om%2FY4fbzsvkXU7fWNHju3jhj1ZAI9UEKhSJvEmg%2BF1%2BPEhXgPm%2B98y%2BWriTyxrM0iCMBo5VVW9aDoJIZm%2BLif5ftRthN5X8zxWXm%2FTZ9dHq6HH%2Foz2z1eNLd1Kq4TvwY%2Bo2el%2FPXkjTPPGkNY3n7q7hq9jeqPjikp%2FwANE%2F8AuxM8mazo%2Bq%2BV9UuNI1KL07y2PFlbdWU%2FZljP7UTfstih9Jy6d%2BVV3f2dja3ltHe6ipezS2mrzAFfh%2B2iMf2K%2FE2Xcfk5ojoTZ311bz1JEjFXG%2FitE%2F4lnlh3USLKCY7lSGLQ7UI3DA9mz0L%2BVP5wLqxi8s%2BaZRHqIpHZX0hAE9NljlPaf%2FK%2F3Z%2FrYqqal%2BS%2BoXb2EYvraWG3kcyvLG3IRuwY%2BkgPFW2PfJjZ%2BRruwuFa21iRYFACKFKsKf6r%2Bmf%2BReTUDxy6Yqo20LQQpE8rzso3lkpyb58QBi2bNirs2bNirs2bNir%2FAP%2FT7%2FmzZsVdmzZsVdlEA9cvNirzb8zvIf6bhOu6REP0tbL%2B%2BjUb3EQ%2FZ%2F4yp%2Bx%2FP9jOHagvqWsMw%2B1ETGwPWh33z1znI%2FzL8hqEutf0qP4JRzvrdR9lwR%2FpCDw%2F37%2FweRnGxTtNBreExw5DsD6Sen9F4gpphto2prYSSQ3Ketp11RbyAiu3QSoP50%2F4ZcK5YzG5Q7eGPEUyRpMykRSEhH7MV2IzGAkDY5h3OSEMkDjyCxL8beaZaglxoOpxTW7qLSWkmn3cX2eJ34nx%2FwApclMsen%2BcdIeC4QCZQPXjH2kanwyxe2RjT7%2B2e0bRtVHLT5SWhl%2Fat5Ozr%2FxWx%2B2uJWtxe6HqSw8wtxDvBL%2BxLGegP8yOMyceQEbcuo7nnNVpZ4JmMtwd4y%2FnD9bENU0y80C%2BazuRyTrHIPsunZh%2FTKil4kSIaj%2BGdZ1HTtM846SXUenKv2gN3glp1%2FykP%2FD5yO9s7vRb2SxvU4sh3I6MOzp7NkyOo5OKmSuGUMvQ9MMdF1m40S%2FjvoAHUfDPA32ZIz9pGH%2FDIf2Gwjt5Qh3%2Bw3%2BdcFnrir0GW5Kyx6hav6ltfqDHddW5Dbi1do5V%2Bw6%2FzZLPLbQ6nP8AWrpAby3oJGI2kXqkrfyyqR9rOU6HrKWHq2N8Gl0y6p66Lu0bjZZ4v%2BLF%2FaX%2FAHYmda8p2i2tk8sjrI7miTITxkj%2B1HJQ7huJ%2BLJR3K3snuq3fo2ruoqQC3idvsgD%2FWpnMJ%2FJ%2Fme4Msq2jC9tuUaXcXER3drJ1japH72NW%2BFmX%2Fit%2FwB5nTdIksyzW0c%2FryxVZgzBmCsa4eCZAKHDIWgbPDvK9jquh6xaW%2BtW81rDL6lozTKaFJQVAB%2BKtfhwvs47myvL%2BNQfTs2kD9UK8gQKlv2Xp8StnR%2FzQ1GW08u%2BlZO0d3dTxxQshKsKHm7hh9jiq%2Fazi6MxEiI5YOa3NyxJMjVrux%2BIrlctmQ3TS91E3jCG2jHI09R1qOdOgFf91r%2Fw2CLKFYxyJBIFZJOwHgMC2NryG3wRnq56t%2FZhusZiihu7cMWt5ir2zpyikUAU9T%2BYcuWR5pdPqiacQrWqXCzR1USVBAb%2FAHYp%2FnwutdVhtdTa%2FgWVBOAk8BcNG9BTqyk4c6rc2dxe2F3c2kcKJGI7qx4kUjpyR0B6Vr%2FNhBqc9haSxpHERaXRaWIt%2FuvcoTRa75PlyQn%2FAJZvYI%2FMUS2SNELiVTEygUjnVW9OlftI8hVWVsnSXVxfTGS6laSViCS2wodjsNs5%2Fo8rrL69qwd6pIrqAw%2Fd%2FYPw9OmSua51GVJ762swQXLCD1AiryNQrMfi2%2FyVwxKkIjW7%2BezjR7PTpr%2BUbv6WyUH8zfa6duOEPnjT7SEWmrxxkSXatazowA5KY%2BScgD9qIrxwQ3mPX7EetqFhAtqPtegzySAewA45F%2FMHm1dcRIGjMdvC%2FqRIKcuVCvJ2%2BR%2BzglLY2yhjnLaESfcEquburW8M5HpzKvIr1FaDl79cRbRr6S8%2Br245GCoac%2FCjKp67%2FwAww%2F8AL3li01qBLgPJO8f7t4wP7s%2FsE9fhOHer%2BVtfgg%2BvabcJe1FJrPjwZVUU%2Fdy%2FR9llyIFixuEzjKEjCYojmGM6jaajZ28RjBiEisJzULTi3iSKR0wboHkTXdWt21OLT5ru2A5RlN1k8SrsVWT%2FAGD5ena1aXA%2Bo6pD%2B9J2WcenL%2FlBJR8D%2FwCrnSfy880W%2Flyzl0m7uJZtJR%2BVmXSsltyNXhYD7cP7ScfiT%2BXDXcxosR1D8rvP2qWUdlZadHZW7kPcPcXEYeSn2EKR8%2BKJ9rh%2FNkRu%2FwArtc026ey1FooJo9yKs4IP7StQclz0brf5jeXtKhR7eYX0sieoiQHYL29Rv2Cf5T8WQ7W%2FMj%2BZrOKd0t1Ct%2B6eDk7xMaj05CaNxb%2FVwEgCy249PlyECMefJ4235fXQ63Uf%2FAt%2FXC7VvKsmkfV7iab1dPlISW7jTeFz%2BxJHWoPg37edQ5kyNDIvpyx%2FbQ%2B%2FQg91OB7hFCurIssUq8JoHFUdD%2Byw%2FU2IIIsMMmKeOZhkiYyHMF5g%2BhCNipcyFf3nwD7cH%2B%2FoT%2B1x%2FwB2J%2BziNyW01%2BNseVtMoMc4G7VH82TCbyxfWMxtUMlnDIPrOli55JIh%2FwCK2ZeMqthYYOMVxBNb%2Fu2HK%2Fsl6JU7Xlp%2B1xDfbT%2FdeFgmPknW7q0eC%2FsZ%2BOoWTck5ftRnrG9PtIfstnatY0iw%2FMfQ4%2FMWh8bfXbdSjxnbmyD47S4%2F5ky55gWcaZqDPp1yZo42Ppz8SvIePHOn%2BTfO2qaXML%2FSHiPqcRf2M9fTYf78Xj8XIfs4qoWsGsQ64Tpp%2Bo31zGbK9WYfFE8Z9TceI4FP9lnpNbWDVdGit9RRbiO4gT1g3QkqCW9jX4lzksE1z5382DU7WzEKoqq5UVAoOIeV9uR%2BL%2Fgc7RDGsMMcS%2FZjUIPkopj0UvHtQ%2FKnV9H1F9X8vXCajyYt9TuQoYLSijkx9OTj%2FlcMS8q%2BaLny95guoNYs2s%2FrfEXsLLxKyJXjLH4pxOdpwl1zy1p%2BvCOSctDcw19K5ioHAOxU1HxJ%2Fk4otNoZYriJJoWDxSKGRh0II2IyF%2FmN%2BXll550z4OMGs2oJsLynfr6Mv80L%2FwDCfayU6RYNplhDYNMZxACqyEBSVqSoIH8uDj0xV8NappV7pd7Np%2BoQNbX9qxSaJuoYfrr9pWwuSNg4NSHBqKbEHxrnrL80Py2g86WX1%2BwCw69aJS3kOyzoN%2Fq8x%2F5NP%2Bw2eXm0q8guZoblGglicpNA4pIroaMjfy0OKvpf8lvMusa5ol1ZazOLqXS3jiiuTvIyOvJRIf2ylKc86eM8y%2Fk35usfK2r3FtfyMtjqoRZZ3qRFNGT6bP4IQ7I%2F%2BxztF1%2BaHke01SDR31aN7mZ%2FTLRVeKNu3qTD4F5dMVZlmylIZQwNQdwRuMvFXZs2bFXZs2bFX%2F%2FU7%2FmzZsVdmzZsVdmzZsVdjXVXVlYclIIIPQg47GSOsaNI5oqgsx9h1xV4R%2BZvkA6VK2r6VH%2FuPlarIv8Aulz%2Bz%2Fxjb%2Fdf%2FA5EoJLK%2FwBFh0yOJU1AyrH6oABboqJItebtyJkjZP8AKVs73qnm%2FwAsyW89les7xzwtSMxMRKG%2BHghpx55w%2FwAy%2BWpbIHWNNjeGKFkDjlVw5%2BIPGwp8S%2FtcfsZGUauQHvdvpNWJxjhyyMZA%2BiXQ%2BUmMXNtcWVzJaXSGOeJisiHsRtll1uLf6rcMeKb20vVoW%2F6pP%2B0v7GHsLr5ptvq7mOPU4ElmEhHH1eCII4kp%2Bz6aO7%2FtephBNBPbytDcRtFNGaPG4oR7EZVKJHrhz%2B92JjDPA4cw3HMdR%2FSiiNG1m7028EiEC4j%2BGWMn4JU7qf8AjVslmvaBp3nLSFvbCiTKCY2P2opP24pKfs%2F9d5BTFFIys5ZGX9pKch9B65JvKmrSaVdevxea1m%2BCcEFQyj9tR%2FvxMtxZBIV8x3Oi1eknp5Ud4n6Zd%2F7XmskVxp11JY3sZikjbiyHsf8Amk%2Fs4caPay6peQadE6rJMwVXboF6k%2B%2FEDOn%2BdPIkfmSyGp6QA12qc4JFpSVPtcG9%2FwDjfI35a8qzaFNa3urKrXLmscfI1gP7NSv2n%2FmywxouIN1DWPKDW95DY6K0t5IfgnaUKgD05cl49I6H7TZ0bynBAmmjSVvhcz2qEM60%2BHnVRw9om%2BH%2FAFsItfhndg1q%2FpNdxmIuDQVU%2FECf8oYReSLk6R5nWBw1bmT6sSD8FGBb4h%2FNyXDyPvbsWITE9%2FVGJkB30jPKV1PpOsrBduRLFcPb3JY9VditW%2F2XxZ0%2B91O2spIoJWL3E54wW0Y5SOf9UdF%2Fmdvhzm%2Fm2w%2BoeapJlBVdRjWdCDsZE2cfeuHdvfQW0BngZptVvEH1u%2FlG6gj%2B5hH7Kr0xHc0pD%2BYg8wa1NDFZWjSWtsrCRoiGJcn46b1YdF2yFWemzxsW1CN4Ej%2FZlUofubOs2WpxpNGl2oMP2eSbEe5yTXGkadqFpJaT26T2860YEVBB6EHx8MeCzdpunlEtpHYQ38GqRrFNaPA8EgJcFa%2FGqhadao2XBGlmbe2uCzevbGeRo2PcNJx%2F2S%2FDht51s3sLpYLoH6ncIq21wRU1UceLt9Hxf8Hhj5f8pXerWc%2Bq2VrHqE1oqQWdvNLwUoijmJB%2FO9W4%2Fs8cjVH3LezEvM2mXWqyabNozB7SS1WJ5SwBrHWnLv8A3ZUbfy4onlazmtrKC%2BJlNoHFFNFbmQaHvtTJ9rPlzU7GOO6vrRbeNioQQMrRxmn938IXj4YE0fQLnXBdQ2062l1CP3SSry9Xav7og0%2F4LDsikps7C3s1WK2iWJB%2Bygpg5lNOX0YAVr7TL0xXsxcxsVlhktwpFPtLVWwbf%2Ban0e8tp7LTYrmyILlpyT6hoRw2%2Fu%2FTbdlw3taoK4v7a1dY2dW5GjQ1qVHj%2FwA24Vah5a03VlNxZEQTtvyX7LH%2FAClyfjyfo3nrTrbV9PRNI1i6gM8qRENC8gb05OUI%2BNR6nR1%2F4HIRqOj655Vu%2FQ1SBogT%2B7mU1ikA7pJ0%2FwBi3xZGwdizhOeOXFCRBSPSZNX8maml1MhNsfgldPijdD1H%2BS37S8s6ZLrEEZt2uEe1W6VWtLsg%2BlKGHIGOReS%2F6wOR9dWtRp11dSQ%2FWHt4Wk%2Br93IGyU%2Fysk%2Fk%2FW5INATT9Z0eAWczGT6gr%2BoIlfcgBxsf2vT%2FAGMYxMSeE2D07m7PqBmjEzjWSO3EOUo%2BY72M%2BYfLcd3FJeQRxyo7EzQU792GQ60bU9Hkf9HTAwts1jdVeNl%2FyX%2B2n%2FEc7ZL5XstVSS58p6kYZqVbTrysif6tWPrRr%2FspVzmuvaDq2jzsup2JtA5NCfjtmNf91Tjb%2FYyccJ8tmgJPqV4JJLJpI3sYLqJ2uY%2FtBgP91q6jpIy8Vw0sNNn8tG11m8KWNnqTrAums5YhTvyct%2B0R8fw%2FYb%2FWwLdRpfaVEkkPO50r1HhjJALRSg9HpX92%2FwCzgSW4TV9Hga5uppdVspFt7a0FOMNshHJm3VuZY8fV%2B0zfBlZHq3BNigOnm52LIfDHCYwMCJmVXI8IqMYnzI4f85l%2BsRQc7eSKVRPHKts4ruUkPAcv9R%2FiwbZaY1p5gtrHUoipjnjWZOo4lhRv8pGwcPL9pcROZo2S6ZkaXgyqoaFVk4hn%2BJ9uPqNxwVrmsWupvYzalF9S1K1lTi6GscsYYMpV%2FFWUNx%2FlwYoSjd8jybO0suLMcUsdmUQROxRrnH75PQtd8vaT5itBZapAJI0YPE6njJGw%2FajcfZzg%2FwCZWh3GhW%2Fr2wH1vSZ0DXlPia1m%2FuZuK7MV%2FuLj%2BbPQ1pdR3ltHcxGqyCtOtD3H0HIF%2BZFnb3k8EMqhxPBJHOh6MnIUDfSdssDq3zfqWmR6lFJqenRCO6iHK%2Fsk3FD0ng%2FmjfC3SNVudJuUvrKQJdQOrxc1DoafaSRGqrK%2FRlyU32i3XlnU4RHOYrF2Isr1hy9Jm%2F495%2F8AimT%2FACsKte0Zp%2FW1Kxg9KaL%2FAI6Nipr6bH%2Fd0X88En2lfFX0%2FwDlz5x0fzbpAlsIYrO%2BgAF%2FYxBVCOR%2FeIBTlE%2F7Lf7HJpniLyz5m1TyrqVtq2ly8ZYzuvVWX9qOQftI37WeufJnnHTfOekJqViwSZaLd2pNXhk7qf8AJP7D%2FtYFZJmzZsVdmzZsVaOct%2FNj8sx5ntJNb0NfT1uBayxrt9ajUfY%2F4zqP7tv2vsZ1PKOKvhz0CsbwMzqSSHPRgw2%2BL3%2FmwrlheB%2BD9exHQjxGeqfPH5Oad5lupdW0icabqU3xTqV5QTN%2FOyr8SOf2nX%2FgM59H%2BQfmu5nWC9ns4rbl8U6OzMB%2FkpwGKr%2Fyk%2FOBtOMPlnzTMWsyRHY6g5qYq9I5j3i%2Flf8A3XnoxWV1VkIZWAKsNwQehGc68sfkv5Q8vBZriE6rdrQ%2BpdAemCO6wj4f%2BD550VVCqFUUVRQAbAAYq3mzZsVdmzZsVf%2FV7%2FmzZsVdmzZsVdmzZsVdiNzNDbwSTXLBIUUmRj0A98WwNfPaJbSm%2BKi2pSUv9mh2pirAPON5pMWiStoiPJfM1bVI4yy1G8jDkCnwrhDrD6jFb%2BWbC7Wt36Ecl%2BCP%2BKiZHbtyJajf5WDvP9xZRaNCfKtxKb4zj04IOR403JKkfAOfH%2FWws84ajNd3EEklFuSOUtK%2FDRVXiKduQOFIeeatpk0F5PeaUj%2BlATLIYwSYhXd%2Fh%2ByleuLP9U12ykmiWO2vrWOW4n3PxgcAFXmaty3cf77%2Bx9jO1%2Flvo0EejT388SvJqDMjFxXlEvw8TX9ls5h%2BY%2FkSTyne%2FpHTVY6LdsVUj%2FdDt1hf%2Fitv91t%2FsMgRW4dxo9X4vDiySqcfon%2FO%2FoyYJsQGHfJL5FsrXUdZk0m6uTbR3UTvHQAgzRjkONSODla5GgcX0%2FUJNL1S0v41DPFINm8G%2BE%2Frym%2BHMD0k52txeLppjqPUPfF6l5X8xWlm995evZlguLSdkjR9gTWjrXovP4XTEPM6wwzKSKCT4tuoIO%2B3vke80xXU2tW13ZQ%2Bo15bq8npJ8UlELVp%2B3sMBWOsXOtWy2iEyXNuCyIw5Myf8VsaseFP7tvizL4tq%2BTzNUfvT%2BOU6npky9ZrciVAO5Xw%2Ba8sis01rp%2FmC01O5YrSWJ4kXcyFiB9nsK%2Ft5fljzLXzMlg0bRQujQESbVlDcl5IenL4kyeebPIp8y6j5e1DSEjX0XWO4QsqKYk%2FfRgV%2Fb5gx8cHMe5lGUom4mjuPnzSW%2F1OfVL6R7sJW2keKFUGygGhIJ3PKmNVy%2FTAk1pdWc9zBdrwuElcTIezA%2FEMj%2FmjXbvQtaW3tEBgCJM6PuCHFQExtizSKI1DHJv5VuxKrWMp%2BwC8JPh%2B0v0ZCNLvrfUrGG9tjWOVQfkf2lP%2BqcPdHme3vIZV24sKn2Ox%2FDJBSyzzBplrqWnSQSxLI0P76EsoIDqO3%2BsMgmlaxceUdRXU7ZS9jJRb62XoUP7QH80f2k%2F4HOgXdyVYgHp1%2BWQW8g4TzQyoQpJIVh1VuhGJCh7FG9hrempKhW4sryMMp6hlYV%2B%2FOWa%2FbXfk%2FU0ZJOEL1ayu2%2By1P91udv3if8OuCPJ%2FmhPL1xaaJetx0y4rFFMx2imJJSvhHLXj%2Fr4N%2FO9G%2FwAJ28o%2FYvY69%2BqOMgdlYzqPmWy8xr615aouoxAA3dk4dJB0%2FfRGjofBl54U%2BnCySRNSa1l%2B2vQq3861%2By65GPKcztqxjY7NEw4jYbU32zoNppF1qfrw2JX6yierHExoJivWIf5fH7DYYnZaY3pN9N5N1n9JJEbuIwyRoisY1kDbpVu3F%2FidM7xpl3Y%2BadFt7m5tle3vIUkaCYBx8Qowof5WB%2BPONG0jvLY28qlRUgqwoyOOu37LpiXl3XbjyZqk7XglmtZIfTEKNQGh5REFvsxr8XLjgIrdWQedvy7GiQPrfl4SNaxVe5s1q7Rp3ki%2FaaNf20%2B1kT07WGCqwcMjdJFNQc9C2N9BqNvHc29WiljSVGI2KyCopkFv%2FwAotHu9cOq2d1JYW0x53dhAo4O53JQt8MYf9ocMFyB23ZwGOQIkeCQGxqwfIsatNQSQrIrmOUbrIhoQfEEb5LLHzPP6X1TV4RqFo4ozFVL0%2FwApD8EmFms%2FlxeWBNxoEpuIhubSQj1B%2FqPsHwo0%2B4ltUmW7Ro5ojRo3BVhT2OWCi1t%2BbPJ1pFCfMXlJgtl%2Fx92VCyRHuwjb4kX%2Bdf2ciOlG2huhcX2n8JI2BE8FHHzMTb%2F8TVc6FpWutaySPblJEmHGeCQVVwP1ZGtcsIbe5N3p0Ui2MlWaM7mB%2FwBpQ4%2B1F%2FLkTGulhnDIYnYprf6tYaxaxxCDlxIaG7iko8Z7kGtWr%2B3ywxsdGl1WwitzGbkgkRzugDGnRm34R0%2FZbIJBZwxv9YhAVx8Qp0JPiBnafJl1eXekLNdyrKOXGNgADQD4g3Gg%2BE7Y8WyTLfiHNJbbQvNHle%2Fe50eQX%2BnXAD3Vg53EoWnKImhQmn7Pwv8AyYRX9xql1dmXWOS3XHiqMnp0QEkKq%2BA5fazrQwHqOmWmqQGC7j5D9lxsynxVsFsLeO39hb39tJbXMYlhlHGSNuhB%2FUffOZXy3%2Fl7UV05n5MgrpN1LTjLCT8VncEj%2FYf5Odt1nQbzR3JcerasfgnA2%2BT%2FAMrYB%2F5V3Y%2BdtAvhefubrn%2FuNux1jkQUYkD7Ubnir%2F6uEq8D1rTFk9XUdNgaHix%2Bu2XVoHPcD%2FfbfstnQfyAeb%2FFktC%2FptaSF1VgFNGTiXFfiA%2BLj%2FlZC9WuNd8p6kNM1e04anYsY2mY7TW%2FZG2pKjfsS4K003mkCHzl5WmDLazercWoB9WCvX1FB%2FeQt9lv8nIofYNRl5FPI3njTfO2li7taRXkQUXlp3Rj0Zf5on%2FYbJXirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F%2FW7%2FmzZsVdmzZsVdmzZsVdgHVY9PmtHi1JlW3cgEuQBUbr1wdgPUdNttTtzb3IJWoZSOoYdxirzHztP%2Bi20aTyvdm6uDcJSCMJIPTVhRWKj7Bbbi2B9b8keZrnV3FvberbsQsVwHUKFJ6tU8hSvhk%2B%2FwAPGy1GwvLOkqK5ju0cIP3YQ%2BnItAPiif8A4nkkphKQaQOk6eml6ba6fGQVt4whPif2j%2FwWO1LTbPVrGfTr%2BITWtyhSWMjqD%2FEfs4MzEVwIsg2Ni%2BVPOHlm58p65NpU1XhP72znP%2B7IWPwn%2FXX7En%2BVgW28s63f2kWr29m76Ys6xyXQpxUggNUfa4iv2uOen9c8t6J5it0t9as0uo4zyjLVDKf8l1oy5EL7TriGW30Lytp7Q6Dbs0d7xI4Oz%2FFWPlycmNvib%2BfKziufETs7WXahOn8Ph9dVIn6a7%2FewDzPd3ukNplzYtxtEhW2adVBKzRktTkalOdf%2BBw60bydZ%2BaNR%2FwARaPq0VjOIw509bcLIkw%2B16yh%2FjRm%2F3an28de6dLYTXGk6xDztpx8aHoyn7Lqfb9nI3YX%2BreQ9ThS3pcWhcvazMBWSE%2FbgaQ%2FF8P8AJlrrDvunvmryB9buFvzAtnrkQD84z%2B6mKHkGRtu%2F82HWnO%2BoadLpU5aC4ZeUL9Gil%2B0jKR0aOYZOLO80jzho0d3Zv6kMn2WFPUhlGxVh%2By6H7WQWaO80rV1hvwBMjcfUAoskTbLIP%2BNsIr5oSu10tfOt7Ikt4th5ijQC6hkSsdz6fwfWIiCCr7UmTj%2Fl5JG%2FKTSdTjg%2FxDILh4UCUgHD7P8Axafj45GPMdkYtRkvbWf6jewEXNhcr9ppT9pEVatJWnxJx%2BznRPIfmz%2FFujfW5Y%2FSvLZ%2Fq94gBCmQCvNQfiVX%2B1xb7OA2NleaX%2Fkr%2FA19Jb2rO%2BlXbc7UvuFbuvL%2Bb%2FiWC7M7HOuarpdrq9jLY3i8o5Bse6t2df8AKXOP6jbaj5dvpLK5USBd0fcB07Ov8cMT3qkP5l6trkWp2kNi8sVr6CShoyVDyH7RJHXj8OQpfNfmCN1%2BsXcsnBeCrOfUAUdFHP4uP052ny%2Fq2g6veR6T5hgX94Almsp%2Fdsw%2F3W%2F%2BU37GSHUfyp8n6hUpavZse8DkD%2FgH5p%2BGCXNeTwM%2BbzeWxtr%2B3SVHFGMZKMP8oBqr%2FwANh9qv5gpr3kVfLV9zk1O3uIjFOwrzt4w1GZhUeqn2P8rJD5i%2FJJrK2lvNKufrSx%2FEYCnCXiOtCpKPx%2F1M503lq7tpDxDNUUUGlPwyJtWQflfpMWseaFtLgssZtZnDJ1VhxCn3651e38qanp%2Bs2ZX95brKG%2BtR7UVfio6%2Fs16YS%2Fk3pGmWxvNR%2BspJqfEW7WnR4kryLFW3PqN%2FL%2FLnW8I2UsW8x%2BVo71n1LT0CXp3mjGwlp3%2F4yD%2Fhsgl9oyXUaxX1u8LoCY2dStV%2FaG%2Fgc7JTEbiztrr0zcRrIYmDxlhWjDJCXQoeVeW9Rv8Ay1PKL25kuNO9ELEo%2BIjgaoKk%2FAqJyzqtrdQXkCXNs3qQyqGjcdCCK7ZFPMPl1Yg95aLWA7zQjfjXqw%2FycB6JrNzY3BW7uAbARKgUqSQVIC8KfCq8K8vhxq9wlntK4DvtJ07UgBfW6TU6FhvTwqN8FRyRyoHiYOjCoZTUEHH5FDC9Y%2FL6wuSZ9Ic2M3X0tzEfo6p%2FscL9B%2FLy8XVJNS8y3Yuo41aKz0%2BEkQrGwoWlO3N2zomVTDZV4l5n8s3Plq94oS%2BnTk%2FVZj27%2Bk5%2FnX%2FhsMNI893Wj2ht2thNVSEp8PGSlBK%2F%2BSf286nqOnWmqWcllexiSGQbg9QezKf2WGco13yhdaVMUT95A39zL0DDwPg%2BIZc3pPlzU5dStGa4IaeNgGZaAFWHJWoMOc5v5E1B7Wf6tcAoD%2B5PLb3jP0fYzo4xIYrZIo5UaOVQ6MKMrCoIPtidrZ29lAttaoI4UrxQdBU1OL5sCsO8%2FwD5e6V5708RXNINQgB%2Bp3oFStf2JAKc4m%2F4XOIWX5R%2FmVoOpummwIySAxNMssZhZG2q3Mqen80eeoKZqYq8l8h%2Fk%2FceW9Qt9Z1DU3W4gPJLS0JCEGvwTytT1U3%2Bx6arnWs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F1%2B%2F5s2bFXZs2bFXZs2bFXZs2bFXUzZs2KuzZs2KoXUjILG49E0k4EKetK43TYkhsoQp5EqGZvEnvgp1DKVbdW2I9jhDdaDqT%2FuLLWJbOzP8AulEVnAPVVkb4hiqB8wWUXmS%2BXTYWQi3ikZphuUlqOKmn7P8ANnOtRsiPW0TWUKqD9ofajYfZkQ%2B3%2FDLnYtH0Wy0W3aC0BJc8pJHPJ2J8ThV5u8tJrdoZrdQL6EH0z0Lj%2BQ%2F8a4bTbxXQPMGq%2Fl9r7mSs9jcUN3Cv2Zo%2B1xD4Sp%2FzZnaNYhtfNmgRaho7JcMwE1pKCASP246%2Fsn%2BZf58gsugWWtaPFp1xRL21qFkFCyP3U96YR%2BUfMt7%2BX%2BqvpmqEtpUsnGeOtQjE0FxD%2FwAzUxOy%2B5kHmGCOa1s7%2BVeEsFYpHI3SvwyBvDi647RtH1by3qEXmPT5VlgnWmo2C1Amj6rIrH4RMnVcCz3FvJ5x1pIqX2h30cVy3ouv2nQEeka9XZW5ZJNPult7dbZpTLasP9HlYUalP7uVf2JlH2v%2BEw891ZzY3tvqNtHdWz84pBUHoQe6sP2WX9rC7zJoEOu2Xp7LdRVa3lPY90b%2FACHwi0IahBqtdOo9hKf9LjY0Vf8ALU%2F78ybZFD591jS3R3gnRo5Ymp4MjA9snHkr8weRi0LzJII7pQEtdQc0SYdFWQ%2Fsy%2F5X7eH%2FAJw8uLqMDX9qg%2BtRL%2B8Uf7sQf8bLnJbuyjcNFKnND1U%2Fw8Dh5pfQo3Gc487eVFhMmq2KfuHNbmNR%2Fdsf92KP5G%2Fa%2FlyPeXPM%2Fmfy2Y4pmk1PRqfBBKpNwiDr6E3%2B7OH8j51%2B1ubXU7JLiKklvcJWjDqrDdWH%2FEsV5PntoFiuUuQOFxFvHKpKsPpWnLJZ5b8w65p1xEguXuIp3q8d07P1%2FZVm%2Bx%2Fkccbr%2Bhm2125sbPggX95AZq8FRhy3p%2FL8Srk181aDHqempqtvRLu2hDngPheMDmV%2F2P2ozjsrKLW5ju4Eni%2By4rQ9Qe4OL5D%2FACp5hgu7iOwkkUXFxD6yx9DWM8HNMmGJFFDRFRQioPUHIhrmgrAXubdK2z19WOleFepH%2BRkwymCkUIqD1BxBpWIaNq09o31WURrZoiem32ePEheCIB8VV%2BLJcGB3BBHtka1LQXVne0XnC9eUQ%2B0tevHxXA2lxazHfW620rx2atW4gkSqFOPCgdvjDClVwkA7hWYZswzZFXYnNBFPG0UyCSNvtKwqDimbFWE6voE1hIbu0Be3BrQVLIP8rxXJRo92b3T4Z2%2B1Ti3zG1cGkAih3B7ZSRpGvCNQqjsooN8NquzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Dv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxViPmbyo91cfpvQiINVT%2B%2Bh6RXSD9iXwl%2F33N%2FwWQzW9AXXo%2FXaFg7L%2B%2BhK0mjdBuafzACjrnYcDTWNrPKk8kYMsZDI42NR40w3tRW3z1p2l6ho%2BotLAfXVE5OgDVljA%2BLj%2FxZGv7OS2BbeaRbscpI5YuLQq3ETIfiQcv2ZVP90%2F8AsMk3mi1FjqBnt0CpcIHIUU%2BNT8RFP5tuWRDTFEulwPaP9YjVSfVjBIFGPM%2F7BvHCOSXqXlu6trnTI1t7Y2Zj%2BGW1YhmQ%2BJcfb5fzYcZBPKl1I7GRXHKGi3AY%2FajPRvdsnQNd8BFFDjnL%2FPWhJaXguIKRw33KngklPi%2F2Pxcs6jkY892Ul3onqwoZJLWRZeAHIlT8D7fJsAVBaLpUeueS9PtriYPcWykQXkfZ4yVDD%2FJP2WwX5X1NJ2NrQIGXkoXpzQ8JP65GPJfmOTR5G0zXLmGCzlYmzhZPSeE05ceP7UTjfliOhagkWs3CW0gkUXbPZjoZVkY8kQHr8OSHUJZD540vmYtVRBIAvoTo260JrGzU7cvhzaJ5jgm0t9O1u7giu%2FRdY%2BJ48oyCg%2Fd9QyfZ%2FwArJhPBFcwvbzqHikBV0PQg9sgt%2FwCSLpJy9pxniPRiQsgH8rfzYB5ql%2Fkq1hXU4ZigNwjsizftemU3Uf5Jzp%2BRXyloF%2Fpj3FzqYiR3YraxREsyx%2FzTOaL6j%2Fyp8CZKsSUOzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Hv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVg35iPrMEFld6XbR3CRM%2Fro1fUIIHwx%2Fs4U%2FlrqOkz3c1lYQmENFJI8DCgRzIDIg5b1JavTOiajZLf2cls1KsKofBh9k5ydpJPL%2FmzT9ZcBIWY2t5sFoD8Ls5C1%2FwCDkyQ5KyjWdLGi6ml3brSyvKxuo6I58P8AJPVclOiXZurPi5rLAfTf5fsn7sE3drBqFq9vLR4pV2I7dwy%2B%2BF2j6Xf2F5dS3EqNbyhViVa8iV%2Fbfag6%2FZXBdj3KnWURXLzYFYV5n8n3us6ut7btEITGsbc6hl418B8WFd75Ym8svHqVqPrQiCsJSPiikH2jQfsNnScayhwVYBlOxB3BGG1Sby7rf6YgZ5KCZftIBSmHeFllodlp95LeWqlDMKGIfYB7kDDPE%2BSuzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F%2F0u%2F5s2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZFfM%2BipLW%2BSJZAf71CtaMPsygf8SyVZRAIIO4PbCDW6pP5YvDd6Wiu1Zbc%2Bi%2B9Tt9mv%2Bxw5xG3tbe1Vlt41jDHkwUUqfE4tgPNXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Pv%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV%2F%2FZ + ); + background-position:center center; + border:1px solid #00aa00; + } + + .yui-skin-sam .yui-h-slider { + background: url(bg-h.gif) no-repeat 5px 0; + height: 28px; + width: 228px; + } + """ + + output = """.yui3-skin-night .yui3-dial-ring-vml,.yui3-skin-night .yui3-dial-center-button-vml,.yui3-skin-night .yui3-dial-marker v\:oval.yui3-dial-marker-max-min,.yui3-skin-night v\:oval.yui3-dial-marker-max-min,.yui3-skin-night .yui3-dial-marker-vml,.yui3-skin-night .yui3-dial-handle-vml{background:0;opacity:1}div.base64-noquotes{width:100px;height:100px;background-image:url(data:image/jpeg;base64,%2F9j%2F4AAQSkZJRgABAgEAZABkAAD%2F4RfJRXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAABAAAAagEoAAMAAAABAAIAAAExAAIAAAAeAAAAcgEyAAIAAAAUAAAAkIdpAAQAAAABAAAApAAAANAAD0JAAAAnEAAPQkAAACcQQWRvYmUgUGhvdG9zaG9wIENTMiBNYWNpbnRvc2gAMjAwODowNzoxOSAxNDo1ODowNQAAA6ABAAMAAAAB%2F%2F8AAKACAAQAAAABAAABwqADAAQAAAABAAABRQAAAAAAAAAGAQMAAwAAAAEABgAAARoABQAAAAEAAAEeARsABQAAAAEAAAEmASgAAwAAAAEAAgAAAgEABAAAAAEAAAEuAgIABAAAAAEAABaTAAAAAAAAAEgAAAABAAAASAAAAAH%2F2P%2FgABBKRklGAAECAABIAEgAAP%2FtAAxBZG9iZV9DTQAC%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQADAgICAkIDAkJDBELCgsRFQ8MDA8VGBMTFRMTGBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAENCwsNDg0QDg4QFA4ODhQUDg4ODhQRDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgAdACgAwEiAAIRAQMRAf%2FdAAQACv%2FEAT8AAAEFAQEBAQEBAAAAAAAAAAMAAQIEBQYHCAkKCwEAAQUBAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAABBAEDAgQCBQcGCAUDDDMBAAIRAwQhEjEFQVFhEyJxgTIGFJGhsUIjJBVSwWIzNHKC0UMHJZJT8OHxY3M1FqKygyZEk1RkRcKjdDYX0lXiZfKzhMPTdePzRieUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm9jdHV2d3h5ent8fX5%2FcRAAICAQIEBAMEBQYHBwYFNQEAAhEDITESBEFRYXEiEwUygZEUobFCI8FS0fAzJGLhcoKSQ1MVY3M08SUGFqKygwcmNcLSRJNUoxdkRVU2dGXi8rOEw9N14%2FNGlKSFtJXE1OT0pbXF1eX1VmZ2hpamtsbW5vYnN0dXZ3eHl6e3x%2F%2FaAAwDAQACEQMRAD8A9VSSSSUpJJJJSkkkklKSSSSUs4uDSWiXRoOJKFi3Ovx2Wvbse4e5gMwRo5soypdOyN9mTSRHpWvj4Oc4%2FwDVf9WkpupJJJKUkkkkpSSSSSlJJJJKUkkkkp%2F%2F0PVUklCqxttbbG%2FReA4fApKZpJJJKUo2WNrrdY8w1gLnHyAlVOsWvowXZDZ%2FQOZa8D9xrmm3%2FNr3PUswm%2FpmQGamyl4bHiWuhJTYqsFtTLBw9od94U1Q6FkfaOk4tszuqYfwV9JSll1%2FoOt2sOguDXgeTm7f%2FPmN%2FwCCLUWZ1VhrysbKA%2Bjua7zgtuZ%2F1FqIUXTSQcvKZi47sh7XOY0tBDYJhzms3akfR3blVd13prMtuI%2Bwse%2F6L3Ahm7n0%2FU%2Bi2zafzkFOgq9%2Bdj0ZFOM4k3ZDtrGNEnhzt7v3WbWPU8bJpyavVpduZuc2eNWONb2%2F2XtXOY2VU7rA6hnO9RuJS%2Br1hO1lguGJbuYz2extvv3fzLPU%2FwCESU9QkkkkpSSSSSlJJJJKf%2F%2FR9VWf0iwiu3Ff9LGsewT%2B6D7P%2FAyxy0FmunH6xPDMpgd%2FbZFT%2FwDoOx%2F%2B20Qp0lC02CtxqALwDtB4J8FNJBTVovx%2Bo4jvbLLGlltTuRMssrf%2FANJiz%2BhZDqMO%2FEyCX2dOc6t%2Bkuc1gmt%2B3%2FhaPTeoZ5s6R1IZrBGDlkNvI%2BjXcYYyx%2F7tWV9B%2FwDw%2FwDwlyD1LqWH0jq2N1O55rxOogY1rwC4C9uuP6mzdt9Wl1v6T%2Bb%2FAFdFS%2F1Myq%2F2VThusa%2B2tst2k6sPuY73Brm%2Fo3Vv2fy%2F3F0S4TAzq%2Bm%2FWDquKGnbhWOya62DUUlrcmyulktZ6f2ey19Wz8%2F2Ls2Z%2BFYx1jL6yxgBe%2FcIAI3CXfNIqZX3ursqqrZ6j7TqJgNY2PUsPP0dypfWJ%2Fo9NOT2xrarHTxs3tZkf%2By77VR6v9bejdNsbkb3Zbqm2sfVjgOOhrdZFljqsffXs%2FmvV9V%2F%2BDXHdZ%2BuvU8zA6i%2Bj02Y1zawaZLt2LY21tltb7XOb6j6%2FwDR1V%2FzV36v6rPVYFU%2BiCs5PS7cd43O2WUOB77d1X%2FT27lwvVnNyKK8d15q6hij207o9dg3DHdW9m9zLv8AQ%2F4Wu71K%2FTtr9aqzJ%2BuHV8tmR0zNoyLvsXU8Nr7cdtjmtN1J9K%2B2podsbk1%2Fo%2F8A0YsnG6te%2BnLZbdXXY6pttADNwucwmhv2cWbv0tnr%2FpKnfpKbKf8AtsqD1DvrjnfV70cKlldgzgbqjf7QHB3p2sG0sb6lv0%2FfZ6TEcZg630xz2vezK6jVbfhVtf6biQ%2BxltmNY7a2z9Js3Vu%2FwayvrHh1%2BhhdKv3XubjBhY9wDbHtPpOdXkOH6t1Dd%2FRsl%2F6C%2FwDomV7Fg9Wc%2FF%2Bq3RL6biben5WVjh4lljdxbexltf06La9vvp%2FM%2FwAFZ%2FhEuqn0r6lfXM9WYzpvVoo6wxvcbBcGj9Ltr09LLoj9bxf%2BvU%2Fof5mz9avrJd0%2B2jBwHAZTrcd17iA7bVZa2nY0H%2FCXw9v%2FAAdf%2FWl5zidQZ9ZcY3l3oddwg2z1mHZ6zWfQv3t2%2Bnk0%2FwCn%2FwDRf8xf6Df1DqvXGZ%2Be5tzsi7Gd6lm2kPsqNbqqOG1%2F4Cuxnpt%2FS7%2F9I9ALjR1Gh7f96%2BuJJpA1KZj2WNDmODmnhwMhJaySSSSU%2FwD%2F0vVVn9airFGYSGjEcLXuOkV%2Fzd5J%2FdZU%2FwBX%2Fra0EHM9D7Jf9pE4%2Fpv9YHX2bT6mn9RIKcE9U6lgXnJDv2l0jN%2FS497S39C530sOyxn%2BBc%2F%2Bh5P83%2F2kyfS%2FR5NlPrv16uwczExsPHFozLBRWHz6rriWM9Ouv6Hs9T3vseue6B9buoZF5xBU84BDC%2BtoZYW1PczG%2B02Gxm71Gb2XP2fod7P5mtct167q%2FT87Gdfk%2FbscWHI6V1NrQNGu3O2NZ6fp5FN9dTcrH3epR6f9RKwRYXzhKEuGYo%2FxfSMvM6lZlF%2FVfSobU11Q9MGyqLfa%2BrP9PJsr9Kz09n6bGq%2F7dWN1z9VxC3KfZT0%2B%2BPUyGg5NIaPdU7ezZe7KxPR%2FR5Gyr1sX9Db6%2FwDguZx%2Bv352be4WNwft%2B9tb2n21mza%2F07Xn2%2Fp7qmVep6dbP0vqfolqZD7mY91eDmXh9pBAZDGjT9awvb%2Bkp3W%2Fp2Mdkfpav1b%2BXUb0WulTeP23h5Di3JL3UYWTYzlzbKn1sc5rN7baM3FZ9pw8xrvSvr%2FQf0im6uvH6vY7G6hfS2mx1WFYaq7bnk%2Bo2r0rKPTa7bustxX20%2Bz%2Blf4RW%2FqPXeM403s%2FQ9LpdbQ4v97Wl4DcW1nud6PqfrH2e9nszMavNx%2Fs9%2F2hA6i5r77HZLH24Tn1W4%2BUwj2Pva3Jdj5bfzaHb8n7Fk%2F4P9YxP0tX8wulqaWfb1CzZ6GaM30622WDD%2FSV1mp3sfY6tjWOsY1tf8776a%2FTQ7s27qFNVIrqwAfUsbZRXt9S0N9Ky1tX83%2Bgre%2F1sej%2FAE1lzKltdDtbi9ZJsqqbTaBXdS0TbW68PycUxNj8qrIbR%2FpPU%2FSb6%2FTyv6VWf0qvOrzmsYasnGzHmk7gx1Za6K2XR7sd%2B5v6K79JXi3bPU%2FV8i71RRU5%2FWaLMv6kYj7SGX9L6g%2BqK%2Fosrymeux1cf4B17fYsrpuPmNxftz63Pcwl9DWDQ2sIqZkNJ%2FNZe6luR6f%2FAAX%2FAAS1f2pXX0vq%2BJln7LfkVVta3ZAfl4929myn%2FA76rLq86r%2Bbo%2F4qyhVruoZfSek4ldmM12Pe227D9XQBtvptu3%2B7d6Vr66Xf2PU%2FwiSnsuqnDyLMfFtMZORj%2BrXSPeHFseo2l7g31La%2FV%2Bh%2F2oq%2FwfqfpFznVcd9uDZivaxzDZXa1zztZaXB1LW%2Bv%2Fgb%2FZVXi5tv83%2FRsj9AsLqfXv2j0%2FpXqPFedgNurfa10F3uqsx8lv8Ao3Obvrf%2FAMT6n%2BFQquq59%2FURec1zb7CHXMc8htmw7n47v8BtucPUb6v6P9Ld69iNoS9H6Vk0Zzcpj3tqqeW1AAtuc%2F8Am7Meyhv6WvZ%2FNZX%2FAIEugzuk14eLiN69fbi49u6wgBjyWNb6dP6IOa52V6vt247P0dD%2FAFP0ajjdc6kBeekYQd1DIs2tsZVZbdVXt9tYqsG9%2B3%2FTPZ%2Bl%2FwBCsajpXV%2BtX2Zwo%2Fal7Wuvv9S0Mea2%2FT2%2BvZ7m1e1j6GV76PZ%2FpK0NE7avpH1Z69ndU6Ti4mP6mVZktBdbkP3uqbWTRkjKuYGb9t1e6v2776rP9Iuyw8VuJjtoaS8tkueeXOcdz3%2F2nLyb6vdZv6DjDKwmVt3lzbsZz3VsZcHluTgX499hfRYxuzJwn%2FznsyKrLLv8H2vT%2Fr9hODK%2BsM%2FZuSWkvrtFjC1wLmvr%2FWKqW%2Fm%2BzbZ%2BkRR4vVpKn03q%2FTeq47Mnp%2BQy%2Bt7WvG0%2B4Bw3N9Ss%2FpK%2F%2BuNVxBT%2FAP%2FT9VTOa17SxwDmuBDmnUEHsU6SSnxf6x9CzPqt1ECix7GMeben5Q0caneyylzvouto3MruZ%2Fhav0np%2FpFUryaMjFvrzps6dkPbZmUsHvx7zFber4f7rbLP6Wz%2FAEln%2BivYvY%2Bu9L6f1Xpl2H1GG0OG71SQ01uH0b2Pd9B7P%2FUb%2FwBGvH8vpF3T7%2FQpcH5NIea7az6leXSd2%2BzHjdW70qHbMnE%2FwlSZrE2NuzfjKHMY%2BGfpzR2n%2FL9H95wM%2FByOk5hxsgiyuwb6rm613VH6N1X%2FAKMrW10jqbrmHDyHl8MhjY3G6sHd6PO318dv6eiz07fW9P0v%2BOHiPxc6n9j552Ydh3YmVy7FtP5%2Bvvfiu%2Fw%2F%2FB%2Fpf5zehYv1a6pVk5WLnh%2BJZhML6rmiWvsBY6t1Fn0n1NY77Tvp%2FSf8WnijqNWnOEscjGQ4ZB7Oig0fV7Lf01rr8rqR2tez3XPbBx67XsYzayxjbMl9m38%2F9J%2Bl%2FS%2BpzrvUxMWqvqNVuJksx3402zW5oosbdScig725OHd9qY6v%2Bc2ZOLT6Nn88y3e6X1uyk9NexznYd%2BPdi2Mc1lTxl4ztznWu3bKfWZZXbs9T06f0ypdY6J1Hr%2FVH9Wrtpsx6mMqFDS%2F1WisF30XNa5%2B577bf0eyz%2FRV%2BqnHwW1RotDF6xay49Tviks9OHNBAc9g2sfc1m7Zu2%2FoG%2FQxv3PST9Lw2dTblM%2B1W1sdDixoM2vu3f0936Ot3pOZsurp%2FQ5H%2Bj%2FwaJj4GXf052ZjepRjbmOrywNzNjHulh9Meg5zN%2B1lrn%2F4R9fp%2FpFYxcHD6ey6nItdSGAOtdlFwhlh%2FRuHpN3Ppbv8A5xnqemgFOZ1%2FDyMW77T1DIOQy4vra91bqC00en%2BiYx%2F06Nrqnev%2FAIf%2FAEj1WxaG2sxKOo9Mu6iK32UYeOLHML3vLrNrrmu3%2Blv%2FAPRi1cvpl1PUqW3Nfh5VJ34%2BNmO9XEurHu%2FV7t1jGMsYf0noP9n%2FAANqv9Q6W84w630hn2cuFd1jcYGysjS6qzLxmiu3Fyadv896NX%2Bl%2B2eqhSSBQ1u%2Fwed6z9X%2BrtFJH1dZj1agENsqZuIB9EPsvbkW7Wj6eS%2F6fq%2Bj%2BjWOcbNw8hnqdNxq3tizZYXOa4Ty7fkWbmNd%2FObfof4RdpT1fqHUs1rc%2FLfktyRU2vGa4bSHNePtFL3el6VmLlVejZ7f0m%2F1cj%2BZ%2B0WbvSfqn0%2Fq1eTjdRIdlVspsqcxwBqc8PbfW%2BrX376%2FTtf%2FAKH0bcS6v2WJXqulj4YCXEDZqurxn1dxXdRosuw78hnXcR5dSWu22OrY2bqsdn03ZFTt932Oz2ZWL6lVLPWoXS19Hzup9Pq%2BsfTMYty8kn7ZRXDCy5hdVb1DEre%2F9NU79LvxHbLclj7Kf5rIspXE5mPm9J6q%2FLqa82YrmvfVJHqUsd%2BgyqLatln0a%2F03pP8AWxLmerV%2B5j%2Bz%2FU7qvR%2Bp9Cot6QNlLJbZQ5xdZXb9O1l73S99jnu3%2Bq7%2Be%2FnEmN57qv1X6b9c%2Fq99oxWNx%2BuYxcyx7g9rjc2PXxM5t36x6djmt9H1v02L%2Bi9L%2FCVWcz9VPr51npPWX9H%2Bsxuy8bItFNoyDvtot9tIcN%2F06He316P%2FAEKo%2FwAJ9p9hbXW17ntaA98b3AQXQIbuP5y43%2FGB9Q2dfp%2FaXTWtZ1iloEGGjIY3%2FAPe7%2BayG%2F8AafJ%2F6zb%2Bi%2FSUJT2TK66xtraGAAABoAEAbW8fyVJecfUbqP8AjHPV31dXxMjJ6c%2Fay27LaMd1JaNgsoDxX9o9rf0zaW2%2Br%2FPep%2FpPR0lP%2F9T1VJJJJSDNL%2Fsr9geXGABW1rnakD6F36P%2BsuAwumV%2FWXNrxHvcymqh2S7IqLnRa%2B7Jfj2UPcGtqdvyfVspZ7K%2FQ%2By1%2FoaV6Ffj05FZqvYLKyQS13Eg7m%2F9JKimvHprorEV1NDGDwDRtakkEg2NCNnxDqdGb0Lru3qFDHX41rLTWGxVeyR7626tdVmNa%2F1P3LPWr9i2cmzrpY5tdB6gzpL7Kq7WGXPqqf6D8HIc1gZ9upo%2F7T%2FzmVX%2FADXqr0brOF0%2B4135bWVFgc05kD1WMP06ari17q25P81Z%2B%2FX%2Bi%2BnauTysfqnQeoM6h06l91eW2H4jwf1uhgc70Lq3DdV1XGxm78d7%2FwCfo%2FQXfpK7GIRFX4s2fN7vAeGpRjwk93IyOj5VDnN6U5%2FUMa%2FflYlLGuLm%2BqxnpfadrG1tdk302Ufav%2Bs2en%2FNrQ%2Brd9%2BNlNdfS%2FDuDXMy8e7R1bmjfJ%2FkNhl9Nv59KtOeLbMDO6FktJte%2B%2FCstLh%2Bhcaxm4mY5oc5tLMr08XK3st9H7TRd%2FSqPWW11fA%2Fa%2BOOpdOc6jqeMNllYjcdvudjWt9zfVr3b8d%2F%2Fom%2F1E%2B2F5jpf1u6Dj9RyLKcqu3peaf1rF1aGh%2Bhym0XMr91TfZksr%2Fncb%2FSWY9bFTwftfUOpdQwRZvxsDLOLh2uBexrLH21YzLLAHv9K3ZX%2FwAH%2FOLUw%2BnfU%2F6xVDB6lg4%2FTutEFrLaG%2BgLXD%2FC42zY2x%2F%2Blw7f01f8ur9On%2BroyPqllZfThhMsqBZblWhxFjahFLLWE%2B27HZv3sbsr9H9N63%2FBjW1L9J9PDa%2F6udcx%2FU6Ta7bSH6uxLT7W0Oc0%2Fo6HT6mFl1fzX%2BnV9n1R6v07bbhZv2za4w149K0Vk6enkMeWPu2fzn8xVb%2Fwf0F1GVh4mdSW3MD2vbAfA3AH9xyoYmRZ0o%2FY894%2BztBNGS7QbR%2Ba8n6Oz%2FwH%2FttLyRbyPUcXMqzznY2Nfh9TrbZbkkVyxzQBvzm%2Bm04vr7f0d%2Fo2frX%2BD%2FTV2rc%2BrmH0bqHT8fcW%2Fb2Ned1FrmWsaXOZLfRLH01W%2Bpu9L%2BaXUiCJHBWGegM6d1IdU6WzbJi%2FFbAaWuPvdV%2FV%2FnPTStN6U4%2FWfqPk5jhRj2BorDrMXLOjqnge2m1rNrnV3e1lnpf8b%2FO1Urm%2FqLlZH1a%2BseT03IwLa7s97K8nFrY%2Bx9fp%2Bp6OfjupZ6VvT3%2BrY3I%2F0P6Oyn9F%2BjXrKSCFJJJJKUkkkkp%2F%2F9X1VJJJJSkkkklKQM3Dpzcd2PbuAdBa9hLXscNWWVPb9CxjkdJJTxHVfq4Ol5bc%2Bq55dlvLLq2Dax1lprY7IFbP5i63b77Kf9H%2Bk%2FQrpPq%2FcLMAVlgrupcWXM4duBj9I0y7f%2F1f00vrB0hnVunPo%2BjkMmzFtGjq7QC1tlb9Njvd%2FqxS6dWMj0upH9FfbXsyqwIabGwx30%2Ff%2BifW9n9RHopwvrBh4eF1V%2BXkU%2FqmZQQLSB6deYxwux7HAfpGvs9L%2FB%2F8J6ivWVZ2dj4vV8FgfbkY%2Fo5WO8hosrdr3%2Bi6t7nrefWx8b2h20hzZEwR9Fw%2FlIOLg4%2BI652OCxt7vUfWD7Q4%2FScxv5m%2F89K1IujYj8PpWLjWDa%2BusB7SZIcfc5u6XfnFW31seIe0OEzBE6%2FNSSQUpJJJJSkkkklKSSSSUpJJJJT%2FAP%2FW9VSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2F%2FZ%2F%2B01IlBob3Rvc2hvcCAzLjAAOEJJTQQlAAAAAAAQAAAAAAAAAAAAAAAAAAAAADhCSU0D6gAAAAAYEDw%2FeG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04Ij8%2BCjwhRE9DVFlQRSBwbGlzdCBQVUJMSUMgIi0vL0FwcGxlLy9EVEQgUExJU1QgMS4wLy9FTiIgImh0dHA6Ly93d3cuYXBwbGUuY29tL0RURHMvUHJvcGVydHlMaXN0LTEuMC5kdGQiPgo8cGxpc3QgdmVyc2lvbj0iMS4wIj4KPGRpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNSG9yaXpvbnRhbFJlczwva2V5PgoJPGRpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJPGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1Ib3Jpem9udGFsUmVzPC9rZXk%2BCgkJCQk8cmVhbD43MjwvcmVhbD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTU9yaWVudGF0aW9uPC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk%2BCgkJCTxkaWN0PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTU9yaWVudGF0aW9uPC9rZXk%2BCgkJCQk8aW50ZWdlcj4xPC9pbnRlZ2VyPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNU2NhbGluZzwva2V5PgoJPGRpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJPGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1TY2FsaW5nPC9rZXk%2BCgkJCQk8cmVhbD4xPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxSZXM8L2tleT4KCTxkaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk%2BCgkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCTxhcnJheT4KCQkJPGRpY3Q%2BCgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxSZXM8L2tleT4KCQkJCTxyZWFsPjcyPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxTY2FsaW5nPC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk%2BCgkJCTxkaWN0PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTVZlcnRpY2FsU2NhbGluZzwva2V5PgoJCQkJPHJlYWw%2BMTwvcmVhbD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuc3ViVGlja2V0LnBhcGVyX2luZm9fdGlja2V0PC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PlBNUFBEUGFwZXJDb2RlTmFtZTwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PlBNUFBEUGFwZXJDb2RlTmFtZTwva2V5PgoJCQkJCTxzdHJpbmc%2BTGV0dGVyPC9zdHJpbmc%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BUE1UaW9nYVBhcGVyTmFtZTwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PlBNVGlvZ2FQYXBlck5hbWU8L2tleT4KCQkJCQk8c3RyaW5nPm5hLWxldHRlcjwvc3RyaW5nPgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BMC4wPC9yZWFsPgoJCQkJCQk8cmVhbD4wLjA8L3JlYWw%2BCgkJCQkJCTxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTc2PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1BZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQkJCQk8YXJyYXk%2BCgkJCQkJCTxyZWFsPi0xODwvcmVhbD4KCQkJCQkJPHJlYWw%2BLTE4PC9yZWFsPgoJCQkJCQk8cmVhbD43NzQ8L3JlYWw%2BCgkJCQkJCTxyZWFsPjU5NDwvcmVhbD4KCQkJCQk8L2FycmF5PgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYXBlckluZm8uUE1QYXBlck5hbWU8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNUGFwZXJOYW1lPC9rZXk%2BCgkJCQkJPHN0cmluZz5uYS1sZXR0ZXI8L3N0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVGbGFnPC9rZXk%2BCgkJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJCTwvZGljdD4KCQkJPC9hcnJheT4KCQk8L2RpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNVW5hZGp1c3RlZFBhZ2VSZWN0PC9rZXk%2BCgkJPGRpY3Q%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk%2BCgkJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BMC4wPC9yZWFsPgoJCQkJCQk8cmVhbD4wLjA8L3JlYWw%2BCgkJCQkJCTxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTc2PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNVW5hZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BLTE4PC9yZWFsPgoJCQkJCQk8cmVhbD4tMTg8L3JlYWw%2BCgkJCQkJCTxyZWFsPjc3NDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTk0PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5wcGQuUE1QYXBlck5hbWU8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLnBwZC5QTVBhcGVyTmFtZTwva2V5PgoJCQkJCTxzdHJpbmc%2BVVMgTGV0dGVyPC9zdHJpbmc%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5BUElWZXJzaW9uPC9rZXk%2BCgkJPHN0cmluZz4wMC4yMDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC50eXBlPC9rZXk%2BCgkJPHN0cmluZz5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvVGlja2V0PC9zdHJpbmc%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuQVBJVmVyc2lvbjwva2V5PgoJPHN0cmluZz4wMC4yMDwvc3RyaW5nPgoJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnR5cGU8L2tleT4KCTxzdHJpbmc%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXRUaWNrZXQ8L3N0cmluZz4KPC9kaWN0Pgo8L3BsaXN0Pgo4QklNA%2BkAAAAAAHgAAwAAAEgASAAAAAAC3gJA%2F%2B7%2F7gMGAlIDZwUoA%2FwAAgAAAEgASAAAAAAC2AIoAAEAAABkAAAAAQADAwMAAAABf%2F8AAQABAAAAAAAAAAAAAAAAaAgAGQGQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4QklNA%2B0AAAAAABAAZAAAAAEAAQBkAAAAAQABOEJJTQQmAAAAAAAOAAAAAAAAAAAAAD%2BAAAA4QklNBA0AAAAAAAQAAAAeOEJJTQQZAAAAAAAEAAAAHjhCSU0D8wAAAAAACQAAAAAAAAAAAQA4QklNBAoAAAAAAAEAADhCSU0nEAAAAAAACgABAAAAAAAAAAE4QklNA%2FUAAAAAAEgAL2ZmAAEAbGZmAAYAAAAAAAEAL2ZmAAEAoZmaAAYAAAAAAAEAMgAAAAEAWgAAAAYAAAAAAAEANQAAAAEALQAAAAYAAAAAAAE4QklNA%2FgAAAAAAHAAAP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8D6AAAAAD%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FA%2BgAAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwPoAAAAAP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8D6AAAOEJJTQQIAAAAAAAQAAAAAQAAAkAAAAJAAAAAADhCSU0EHgAAAAAABAAAAAA4QklNBBoAAAAAA1UAAAAGAAAAAAAAAAAAAAFFAAABwgAAABAAcwB3AGkAcwBzAF8AYQByAG0AeQBfAGsAbgBpAGYAZQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABwgAAAUUAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAQAAAAAAAG51bGwAAAACAAAABmJvdW5kc09iamMAAAABAAAAAAAAUmN0MQAAAAQAAAAAVG9wIGxvbmcAAAAAAAAAAExlZnRsb25nAAAAAAAAAABCdG9tbG9uZwAAAUUAAAAAUmdodGxvbmcAAAHCAAAABnNsaWNlc1ZsTHMAAAABT2JqYwAAAAEAAAAAAAVzbGljZQAAABIAAAAHc2xpY2VJRGxvbmcAAAAAAAAAB2dyb3VwSURsb25nAAAAAAAAAAZvcmlnaW5lbnVtAAAADEVTbGljZU9yaWdpbgAAAA1hdXRvR2VuZXJhdGVkAAAAAFR5cGVlbnVtAAAACkVTbGljZVR5cGUAAAAASW1nIAAAAAZib3VuZHNPYmpjAAAAAQAAAAAAAFJjdDEAAAAEAAAAAFRvcCBsb25nAAAAAAAAAABMZWZ0bG9uZwAAAAAAAAAAQnRvbWxvbmcAAAFFAAAAAFJnaHRsb25nAAABwgAAAAN1cmxURVhUAAAAAQAAAAAAAG51bGxURVhUAAAAAQAAAAAAAE1zZ2VURVhUAAAAAQAAAAAABmFsdFRhZ1RFWFQAAAABAAAAAAAOY2VsbFRleHRJc0hUTUxib29sAQAAAAhjZWxsVGV4dFRFWFQAAAABAAAAAAAJaG9yekFsaWduZW51bQAAAA9FU2xpY2VIb3J6QWxpZ24AAAAHZGVmYXVsdAAAAAl2ZXJ0QWxpZ25lbnVtAAAAD0VTbGljZVZlcnRBbGlnbgAAAAdkZWZhdWx0AAAAC2JnQ29sb3JUeXBlZW51bQAAABFFU2xpY2VCR0NvbG9yVHlwZQAAAABOb25lAAAACXRvcE91dHNldGxvbmcAAAAAAAAACmxlZnRPdXRzZXRsb25nAAAAAAAAAAxib3R0b21PdXRzZXRsb25nAAAAAAAAAAtyaWdodE91dHNldGxvbmcAAAAAADhCSU0EKAAAAAAADAAAAAE%2F8AAAAAAAADhCSU0EEQAAAAAAAQEAOEJJTQQUAAAAAAAEAAAAAThCSU0EDAAAAAAWrwAAAAEAAACgAAAAdAAAAeAAANmAAAAWkwAYAAH%2F2P%2FgABBKRklGAAECAABIAEgAAP%2FtAAxBZG9iZV9DTQAC%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQADAgICAkIDAkJDBELCgsRFQ8MDA8VGBMTFRMTGBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAENCwsNDg0QDg4QFA4ODhQUDg4ODhQRDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgAdACgAwEiAAIRAQMRAf%2FdAAQACv%2FEAT8AAAEFAQEBAQEBAAAAAAAAAAMAAQIEBQYHCAkKCwEAAQUBAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAABBAEDAgQCBQcGCAUDDDMBAAIRAwQhEjEFQVFhEyJxgTIGFJGhsUIjJBVSwWIzNHKC0UMHJZJT8OHxY3M1FqKygyZEk1RkRcKjdDYX0lXiZfKzhMPTdePzRieUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm9jdHV2d3h5ent8fX5%2FcRAAICAQIEBAMEBQYHBwYFNQEAAhEDITESBEFRYXEiEwUygZEUobFCI8FS0fAzJGLhcoKSQ1MVY3M08SUGFqKygwcmNcLSRJNUoxdkRVU2dGXi8rOEw9N14%2FNGlKSFtJXE1OT0pbXF1eX1VmZ2hpamtsbW5vYnN0dXZ3eHl6e3x%2F%2FaAAwDAQACEQMRAD8A9VSSSSUpJJJJSkkkklKSSSSUs4uDSWiXRoOJKFi3Ovx2Wvbse4e5gMwRo5soypdOyN9mTSRHpWvj4Oc4%2FwDVf9WkpupJJJKUkkkkpSSSSSlJJJJKUkkkkp%2F%2F0PVUklCqxttbbG%2FReA4fApKZpJJJKUo2WNrrdY8w1gLnHyAlVOsWvowXZDZ%2FQOZa8D9xrmm3%2FNr3PUswm%2FpmQGamyl4bHiWuhJTYqsFtTLBw9od94U1Q6FkfaOk4tszuqYfwV9JSll1%2FoOt2sOguDXgeTm7f%2FPmN%2FwCCLUWZ1VhrysbKA%2Bjua7zgtuZ%2F1FqIUXTSQcvKZi47sh7XOY0tBDYJhzms3akfR3blVd13prMtuI%2Bwse%2F6L3Ahm7n0%2FU%2Bi2zafzkFOgq9%2Bdj0ZFOM4k3ZDtrGNEnhzt7v3WbWPU8bJpyavVpduZuc2eNWONb2%2F2XtXOY2VU7rA6hnO9RuJS%2Br1hO1lguGJbuYz2extvv3fzLPU%2FwCESU9QkkkkpSSSSSlJJJJKf%2F%2FR9VWf0iwiu3Ff9LGsewT%2B6D7P%2FAyxy0FmunH6xPDMpgd%2FbZFT%2FwDoOx%2F%2B20Qp0lC02CtxqALwDtB4J8FNJBTVovx%2Bo4jvbLLGlltTuRMssrf%2FANJiz%2BhZDqMO%2FEyCX2dOc6t%2Bkuc1gmt%2B3%2FhaPTeoZ5s6R1IZrBGDlkNvI%2BjXcYYyx%2F7tWV9B%2FwDw%2FwDwlyD1LqWH0jq2N1O55rxOogY1rwC4C9uuP6mzdt9Wl1v6T%2Bb%2FAFdFS%2F1Myq%2F2VThusa%2B2tst2k6sPuY73Brm%2Fo3Vv2fy%2F3F0S4TAzq%2Bm%2FWDquKGnbhWOya62DUUlrcmyulktZ6f2ey19Wz8%2F2Ls2Z%2BFYx1jL6yxgBe%2FcIAI3CXfNIqZX3ursqqrZ6j7TqJgNY2PUsPP0dypfWJ%2Fo9NOT2xrarHTxs3tZkf%2By77VR6v9bejdNsbkb3Zbqm2sfVjgOOhrdZFljqsffXs%2FmvV9V%2F%2BDXHdZ%2BuvU8zA6i%2Bj02Y1zawaZLt2LY21tltb7XOb6j6%2FwDR1V%2FzV36v6rPVYFU%2BiCs5PS7cd43O2WUOB77d1X%2FT27lwvVnNyKK8d15q6hij207o9dg3DHdW9m9zLv8AQ%2F4Wu71K%2FTtr9aqzJ%2BuHV8tmR0zNoyLvsXU8Nr7cdtjmtN1J9K%2B2podsbk1%2Fo%2F8A0YsnG6te%2BnLZbdXXY6pttADNwucwmhv2cWbv0tnr%2FpKnfpKbKf8AtsqD1DvrjnfV70cKlldgzgbqjf7QHB3p2sG0sb6lv0%2FfZ6TEcZg630xz2vezK6jVbfhVtf6biQ%2BxltmNY7a2z9Js3Vu%2FwayvrHh1%2BhhdKv3XubjBhY9wDbHtPpOdXkOH6t1Dd%2FRsl%2F6C%2FwDomV7Fg9Wc%2FF%2Bq3RL6biben5WVjh4lljdxbexltf06La9vvp%2FM%2FwAFZ%2FhEuqn0r6lfXM9WYzpvVoo6wxvcbBcGj9Ltr09LLoj9bxf%2BvU%2Fof5mz9avrJd0%2B2jBwHAZTrcd17iA7bVZa2nY0H%2FCXw9v%2FAAdf%2FWl5zidQZ9ZcY3l3oddwg2z1mHZ6zWfQv3t2%2Bnk0%2FwCn%2FwDRf8xf6Df1DqvXGZ%2Be5tzsi7Gd6lm2kPsqNbqqOG1%2F4Cuxnpt%2FS7%2F9I9ALjR1Gh7f96%2BuJJpA1KZj2WNDmODmnhwMhJaySSSSU%2FwD%2F0vVVn9airFGYSGjEcLXuOkV%2Fzd5J%2FdZU%2FwBX%2Fra0EHM9D7Jf9pE4%2Fpv9YHX2bT6mn9RIKcE9U6lgXnJDv2l0jN%2FS497S39C530sOyxn%2BBc%2F%2Bh5P83%2F2kyfS%2FR5NlPrv16uwczExsPHFozLBRWHz6rriWM9Ouv6Hs9T3vseue6B9buoZF5xBU84BDC%2BtoZYW1PczG%2B02Gxm71Gb2XP2fod7P5mtct167q%2FT87Gdfk%2FbscWHI6V1NrQNGu3O2NZ6fp5FN9dTcrH3epR6f9RKwRYXzhKEuGYo%2FxfSMvM6lZlF%2FVfSobU11Q9MGyqLfa%2BrP9PJsr9Kz09n6bGq%2F7dWN1z9VxC3KfZT0%2B%2BPUyGg5NIaPdU7ezZe7KxPR%2FR5Gyr1sX9Db6%2FwDguZx%2Bv352be4WNwft%2B9tb2n21mza%2F07Xn2%2Fp7qmVep6dbP0vqfolqZD7mY91eDmXh9pBAZDGjT9awvb%2Bkp3W%2Fp2Mdkfpav1b%2BXUb0WulTeP23h5Di3JL3UYWTYzlzbKn1sc5rN7baM3FZ9pw8xrvSvr%2FQf0im6uvH6vY7G6hfS2mx1WFYaq7bnk%2Bo2r0rKPTa7bustxX20%2Bz%2Blf4RW%2FqPXeM403s%2FQ9LpdbQ4v97Wl4DcW1nud6PqfrH2e9nszMavNx%2Fs9%2F2hA6i5r77HZLH24Tn1W4%2BUwj2Pva3Jdj5bfzaHb8n7Fk%2F4P9YxP0tX8wulqaWfb1CzZ6GaM30622WDD%2FSV1mp3sfY6tjWOsY1tf8776a%2FTQ7s27qFNVIrqwAfUsbZRXt9S0N9Ky1tX83%2Bgre%2F1sej%2FAE1lzKltdDtbi9ZJsqqbTaBXdS0TbW68PycUxNj8qrIbR%2FpPU%2FSb6%2FTyv6VWf0qvOrzmsYasnGzHmk7gx1Za6K2XR7sd%2B5v6K79JXi3bPU%2FV8i71RRU5%2FWaLMv6kYj7SGX9L6g%2BqK%2Fosrymeux1cf4B17fYsrpuPmNxftz63Pcwl9DWDQ2sIqZkNJ%2FNZe6luR6f%2FAAX%2FAAS1f2pXX0vq%2BJln7LfkVVta3ZAfl4929myn%2FA76rLq86r%2Bbo%2F4qyhVruoZfSek4ldmM12Pe227D9XQBtvptu3%2B7d6Vr66Xf2PU%2FwiSnsuqnDyLMfFtMZORj%2BrXSPeHFseo2l7g31La%2FV%2Bh%2F2oq%2FwfqfpFznVcd9uDZivaxzDZXa1zztZaXB1LW%2Bv%2Fgb%2FZVXi5tv83%2FRsj9AsLqfXv2j0%2FpXqPFedgNurfa10F3uqsx8lv8Ao3Obvrf%2FAMT6n%2BFQquq59%2FURec1zb7CHXMc8htmw7n47v8BtucPUb6v6P9Ld69iNoS9H6Vk0Zzcpj3tqqeW1AAtuc%2F8Am7Meyhv6WvZ%2FNZX%2FAIEugzuk14eLiN69fbi49u6wgBjyWNb6dP6IOa52V6vt247P0dD%2FAFP0ajjdc6kBeekYQd1DIs2tsZVZbdVXt9tYqsG9%2B3%2FTPZ%2Bl%2FwBCsajpXV%2BtX2Zwo%2Fal7Wuvv9S0Mea2%2FT2%2BvZ7m1e1j6GV76PZ%2FpK0NE7avpH1Z69ndU6Ti4mP6mVZktBdbkP3uqbWTRkjKuYGb9t1e6v2776rP9Iuyw8VuJjtoaS8tkueeXOcdz3%2F2nLyb6vdZv6DjDKwmVt3lzbsZz3VsZcHluTgX499hfRYxuzJwn%2FznsyKrLLv8H2vT%2Fr9hODK%2BsM%2FZuSWkvrtFjC1wLmvr%2FWKqW%2Fm%2BzbZ%2BkRR4vVpKn03q%2FTeq47Mnp%2BQy%2Bt7WvG0%2B4Bw3N9Ss%2FpK%2F%2BuNVxBT%2FAP%2FT9VTOa17SxwDmuBDmnUEHsU6SSnxf6x9CzPqt1ECix7GMeben5Q0caneyylzvouto3MruZ%2Fhav0np%2FpFUryaMjFvrzps6dkPbZmUsHvx7zFber4f7rbLP6Wz%2FAEln%2BivYvY%2Bu9L6f1Xpl2H1GG0OG71SQ01uH0b2Pd9B7P%2FUb%2FwBGvH8vpF3T7%2FQpcH5NIea7az6leXSd2%2BzHjdW70qHbMnE%2FwlSZrE2NuzfjKHMY%2BGfpzR2n%2FL9H95wM%2FByOk5hxsgiyuwb6rm613VH6N1X%2FAKMrW10jqbrmHDyHl8MhjY3G6sHd6PO318dv6eiz07fW9P0v%2BOHiPxc6n9j552Ydh3YmVy7FtP5%2Bvvfiu%2Fw%2F%2FB%2Fpf5zehYv1a6pVk5WLnh%2BJZhML6rmiWvsBY6t1Fn0n1NY77Tvp%2FSf8WnijqNWnOEscjGQ4ZB7Oig0fV7Lf01rr8rqR2tez3XPbBx67XsYzayxjbMl9m38%2F9J%2Bl%2FS%2BpzrvUxMWqvqNVuJksx3402zW5oosbdScig725OHd9qY6v%2Bc2ZOLT6Nn88y3e6X1uyk9NexznYd%2BPdi2Mc1lTxl4ztznWu3bKfWZZXbs9T06f0ypdY6J1Hr%2FVH9Wrtpsx6mMqFDS%2F1WisF30XNa5%2B577bf0eyz%2FRV%2BqnHwW1RotDF6xay49Tviks9OHNBAc9g2sfc1m7Zu2%2FoG%2FQxv3PST9Lw2dTblM%2B1W1sdDixoM2vu3f0936Ot3pOZsurp%2FQ5H%2Bj%2FwaJj4GXf052ZjepRjbmOrywNzNjHulh9Meg5zN%2B1lrn%2F4R9fp%2FpFYxcHD6ey6nItdSGAOtdlFwhlh%2FRuHpN3Ppbv8A5xnqemgFOZ1%2FDyMW77T1DIOQy4vra91bqC00en%2BiYx%2F06Nrqnev%2FAIf%2FAEj1WxaG2sxKOo9Mu6iK32UYeOLHML3vLrNrrmu3%2Blv%2FAPRi1cvpl1PUqW3Nfh5VJ34%2BNmO9XEurHu%2FV7t1jGMsYf0noP9n%2FAANqv9Q6W84w630hn2cuFd1jcYGysjS6qzLxmiu3Fyadv896NX%2Bl%2B2eqhSSBQ1u%2Fwed6z9X%2BrtFJH1dZj1agENsqZuIB9EPsvbkW7Wj6eS%2F6fq%2Bj%2BjWOcbNw8hnqdNxq3tizZYXOa4Ty7fkWbmNd%2FObfof4RdpT1fqHUs1rc%2FLfktyRU2vGa4bSHNePtFL3el6VmLlVejZ7f0m%2F1cj%2BZ%2B0WbvSfqn0%2Fq1eTjdRIdlVspsqcxwBqc8PbfW%2BrX376%2FTtf%2FAKH0bcS6v2WJXqulj4YCXEDZqurxn1dxXdRosuw78hnXcR5dSWu22OrY2bqsdn03ZFTt932Oz2ZWL6lVLPWoXS19Hzup9Pq%2BsfTMYty8kn7ZRXDCy5hdVb1DEre%2F9NU79LvxHbLclj7Kf5rIspXE5mPm9J6q%2FLqa82YrmvfVJHqUsd%2BgyqLatln0a%2F03pP8AWxLmerV%2B5j%2Bz%2FU7qvR%2Bp9Cot6QNlLJbZQ5xdZXb9O1l73S99jnu3%2Bq7%2Be%2FnEmN57qv1X6b9c%2Fq99oxWNx%2BuYxcyx7g9rjc2PXxM5t36x6djmt9H1v02L%2Bi9L%2FCVWcz9VPr51npPWX9H%2Bsxuy8bItFNoyDvtot9tIcN%2F06He316P%2FAEKo%2FwAJ9p9hbXW17ntaA98b3AQXQIbuP5y43%2FGB9Q2dfp%2FaXTWtZ1iloEGGjIY3%2FAPe7%2BayG%2F8AafJ%2F6zb%2Bi%2FSUJT2TK66xtraGAAABoAEAbW8fyVJecfUbqP8AjHPV31dXxMjJ6c%2Fay27LaMd1JaNgsoDxX9o9rf0zaW2%2Br%2FPep%2FpPR0lP%2F9T1VJJJJSDNL%2Fsr9geXGABW1rnakD6F36P%2BsuAwumV%2FWXNrxHvcymqh2S7IqLnRa%2B7Jfj2UPcGtqdvyfVspZ7K%2FQ%2By1%2FoaV6Ffj05FZqvYLKyQS13Eg7m%2F9JKimvHprorEV1NDGDwDRtakkEg2NCNnxDqdGb0Lru3qFDHX41rLTWGxVeyR7626tdVmNa%2F1P3LPWr9i2cmzrpY5tdB6gzpL7Kq7WGXPqqf6D8HIc1gZ9upo%2F7T%2FzmVX%2FADXqr0brOF0%2B4135bWVFgc05kD1WMP06ari17q25P81Z%2B%2FX%2Bi%2BnauTysfqnQeoM6h06l91eW2H4jwf1uhgc70Lq3DdV1XGxm78d7%2FwCfo%2FQXfpK7GIRFX4s2fN7vAeGpRjwk93IyOj5VDnN6U5%2FUMa%2FflYlLGuLm%2BqxnpfadrG1tdk302Ufav%2Bs2en%2FNrQ%2Brd9%2BNlNdfS%2FDuDXMy8e7R1bmjfJ%2FkNhl9Nv59KtOeLbMDO6FktJte%2B%2FCstLh%2Bhcaxm4mY5oc5tLMr08XK3st9H7TRd%2FSqPWW11fA%2Fa%2BOOpdOc6jqeMNllYjcdvudjWt9zfVr3b8d%2F%2Fom%2F1E%2B2F5jpf1u6Dj9RyLKcqu3peaf1rF1aGh%2Bhym0XMr91TfZksr%2Fncb%2FSWY9bFTwftfUOpdQwRZvxsDLOLh2uBexrLH21YzLLAHv9K3ZX%2FwAH%2FOLUw%2BnfU%2F6xVDB6lg4%2FTutEFrLaG%2BgLXD%2FC42zY2x%2F%2Blw7f01f8ur9On%2BroyPqllZfThhMsqBZblWhxFjahFLLWE%2B27HZv3sbsr9H9N63%2FBjW1L9J9PDa%2F6udcx%2FU6Ta7bSH6uxLT7W0Oc0%2Fo6HT6mFl1fzX%2BnV9n1R6v07bbhZv2za4w149K0Vk6enkMeWPu2fzn8xVb%2Fwf0F1GVh4mdSW3MD2vbAfA3AH9xyoYmRZ0o%2FY894%2BztBNGS7QbR%2Ba8n6Oz%2FwH%2FttLyRbyPUcXMqzznY2Nfh9TrbZbkkVyxzQBvzm%2Bm04vr7f0d%2Fo2frX%2BD%2FTV2rc%2BrmH0bqHT8fcW%2Fb2Ned1FrmWsaXOZLfRLH01W%2Bpu9L%2BaXUiCJHBWGegM6d1IdU6WzbJi%2FFbAaWuPvdV%2FV%2FnPTStN6U4%2FWfqPk5jhRj2BorDrMXLOjqnge2m1rNrnV3e1lnpf8b%2FO1Urm%2FqLlZH1a%2BseT03IwLa7s97K8nFrY%2Bx9fp%2Bp6OfjupZ6VvT3%2BrY3I%2F0P6Oyn9F%2BjXrKSCFJJJJKUkkkkp%2F%2F9X1VJJJJSkkkklKQM3Dpzcd2PbuAdBa9hLXscNWWVPb9CxjkdJJTxHVfq4Ol5bc%2Bq55dlvLLq2Dax1lprY7IFbP5i63b77Kf9H%2Bk%2FQrpPq%2FcLMAVlgrupcWXM4duBj9I0y7f%2F1f00vrB0hnVunPo%2BjkMmzFtGjq7QC1tlb9Njvd%2FqxS6dWMj0upH9FfbXsyqwIabGwx30%2Ff%2BifW9n9RHopwvrBh4eF1V%2BXkU%2FqmZQQLSB6deYxwux7HAfpGvs9L%2FB%2F8J6ivWVZ2dj4vV8FgfbkY%2Fo5WO8hosrdr3%2Bi6t7nrefWx8b2h20hzZEwR9Fw%2FlIOLg4%2BI652OCxt7vUfWD7Q4%2FScxv5m%2F89K1IujYj8PpWLjWDa%2BusB7SZIcfc5u6XfnFW31seIe0OEzBE6%2FNSSQUpJJJJSkkkklKSSSSUpJJJJT%2FAP%2FW9VSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2F%2FZADhCSU0EIQAAAAAAVQAAAAEBAAAADwBBAGQAbwBiAGUAIABQAGgAbwB0AG8AcwBoAG8AcAAAABMAQQBkAG8AYgBlACAAUABoAG8AdABvAHMAaABvAHAAIABDAFMAMgAAAAEAOEJJTQQGAAAAAAAHAAIAAAABAQD%2F4TkjaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu%2B7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI%2FPgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSIzLjEuMS0xMTIiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4YXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyI%2BCiAgICAgICAgIDx4YXBNTTpEb2N1bWVudElEPnV1aWQ6RTcxOTVFNTY1NzMzMTFERDlFNzJGQ0E2QjkwQUZBRjU8L3hhcE1NOkRvY3VtZW50SUQ%2BCiAgICAgICAgIDx4YXBNTTpJbnN0YW5jZUlEPnV1aWQ6RTcxOTVFNTc1NzMzMTFERDlFNzJGQ0E2QjkwQUZBRjU8L3hhcE1NOkluc3RhbmNlSUQ%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4YXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPgogICAgICAgICA8eGFwOkNyZWF0ZURhdGU%2BMjAwOC0wNy0xOVQxNDo1Nzo0MS0wNTowMDwveGFwOkNyZWF0ZURhdGU%2BCiAgICAgICAgIDx4YXA6TW9kaWZ5RGF0ZT4yMDA4LTA3LTE5VDE0OjU4OjA1LTA1OjAwPC94YXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhhcDpNZXRhZGF0YURhdGU%2BMjAwOC0wNy0xOVQxNDo1ODowNS0wNTowMDwveGFwOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhhcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ1MyIE1hY2ludG9zaDwveGFwOkNyZWF0b3JUb29sPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIj4KICAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9qcGVnPC9kYzpmb3JtYXQ%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iPgogICAgICAgICA8cGhvdG9zaG9wOkNvbG9yTW9kZT4zPC9waG90b3Nob3A6Q29sb3JNb2RlPgogICAgICAgICA8cGhvdG9zaG9wOkhpc3RvcnkvPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICAgICA8dGlmZjpYUmVzb2x1dGlvbj4xMDAwMDAwLzEwMDAwPC90aWZmOlhSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj4xMDAwMDAwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8dGlmZjpOYXRpdmVEaWdlc3Q%2BMjU2LDI1NywyNTgsMjU5LDI2MiwyNzQsMjc3LDI4NCw1MzAsNTMxLDI4MiwyODMsMjk2LDMwMSwzMTgsMzE5LDUyOSw1MzIsMzA2LDI3MCwyNzEsMjcyLDMwNSwzMTUsMzM0MzI7QzA1QTE5MDRGRjAwQUJEQzA1MUJERkFGMDIwNEVBNTE8L3RpZmY6TmF0aXZlRGlnZXN0PgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24%2BNDUwPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjMyNTwvZXhpZjpQaXhlbFlEaW1lbnNpb24%2BCiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U%2BLTE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6TmF0aXZlRGlnZXN0PjM2ODY0LDQwOTYwLDQwOTYxLDM3MTIxLDM3MTIyLDQwOTYyLDQwOTYzLDM3NTEwLDQwOTY0LDM2ODY3LDM2ODY4LDMzNDM0LDMzNDM3LDM0ODUwLDM0ODUyLDM0ODU1LDM0ODU2LDM3Mzc3LDM3Mzc4LDM3Mzc5LDM3MzgwLDM3MzgxLDM3MzgyLDM3MzgzLDM3Mzg0LDM3Mzg1LDM3Mzg2LDM3Mzk2LDQxNDgzLDQxNDg0LDQxNDg2LDQxNDg3LDQxNDg4LDQxNDkyLDQxNDkzLDQxNDk1LDQxNzI4LDQxNzI5LDQxNzMwLDQxOTg1LDQxOTg2LDQxOTg3LDQxOTg4LDQxOTg5LDQxOTkwLDQxOTkxLDQxOTkyLDQxOTkzLDQxOTk0LDQxOTk1LDQxOTk2LDQyMDE2LDAsMiw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNSwxNiwxNywxOCwyMCwyMiwyMywyNCwyNSwyNiwyNywyOCwzMDtENDYzN0NCOUQ0MUExMEJBN0VGNUVCQ0RCNjMxODMyOTwvZXhpZjpOYXRpdmVEaWdlc3Q%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY%2BCjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8%2B%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQACAYGBgYGCAYGCAwIBwgMDgoICAoOEA0NDg0NEBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAEJCAgJCgkLCQkLDgsNCw4RDg4ODhERDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgBRQHCAwEiAAIRAQMRAf%2FdAAQAHf%2FEAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPBUtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4%2FPE1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1%2Bf3OEhYaHiImKi4yNjo%2BCk5SVlpeYmZqbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEyobHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp0%2BPzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1%2Bf3OEhYaHiImKi4yNjo%2BDlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq%2Bv%2FaAAwDAQACEQMRAD8A7%2FmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmrlHI2utXn6WltnFESb0RFSp41WjD5q3LCBaslzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Dv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2E2rj0LuzvF7PxftUf8AXPLDnC3XI0ewdpOkTK4P08T%2FAMK2Ec1TLNgaxmE9pFIDy%2BEAn3GxwTgV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV%2F%2F0e%2F5s2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXYUeZpfR0a4kpUAx19h6i74b4X65CbjR76FV5FoJKKe5C1GIVLdDuGhuXs2IKOX4gfsvGdx%2FskKtkiyD6bctHHbXgPImOC4qOpXj6E1ff4Mm4Ndxkpc770BvNmzZFLs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F9Lv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVokKCT0G%2F3ZgaioNQemANZuhaWLyE0LlY1%2BbGn6sWspA8QA%2FZ%2FUcVRWbNmxV2bNmxV2bNmxV2NZQwKnodj9OOzYqwDT4iiPZsa%2FVLm5sj7I%2F76L%2BOTXTpjPZQSt9pkAb5r8JyKXqfUvMOpIQOFxFDfoP8qFvTl%2F4Q5INDkrFPAf8AdUhI%2FwBV%2FiH8cnLeAPcxHMprmzZsgydmzZsVdmzZsVdmzZsVdmzYyWRIo2kkYKiirMdgAMVX5sjTeaGl1CzsbSAOLuX0w7k14KCzyAD%2BUZJBirebNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV%2F9Pv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVo4CXUFa9e0CbJRWkr%2B0RWlMHZEJ5WtPMtzE5%2BGYJKnyK0%2F4kuEC1ZeM2IW0okjFT8S7H%2BuL4FYz52mMWn2tDTlcpX6FbDHR5ecCH%2BZevywl%2FMM8dJtn7LdJ%2BKvgry9ccrOJ69KVw9E9GS5s2bAh2bNmxV2bNmxV2bNmxVi3miFU1LSbxvsStJYzH%2FJnSi%2F8ADYpoEzLcxq1T68FGr%2FvyE8GrgjzdC0mhXEsY%2Fe2pS5i%2F1omDf8RwttJRHdrcDaNbhZVI6endIG%2F4mcnHeJCDzDL82bNkEuzZs2KuzZs2KuzZs2KurkZ843Zhs4bVTQzvVqfyrvg3zHNPb2UMsLtGBPGJSpIqjEqRUb9ch%2BvSSvp0JlcvJCLyMuxJb4JNvi%2F1SuEBVvkaX9L%2BZbm8Sv1fTbf0Y6%2F78lPxH%2FgFzpeRvyPbWcXlyyubWBIXuoxJOUG7uKjk5%2FaOSSoxPNXZs2bArs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F%2FU7%2FmzZsVQ2osyWM7o3FlQkMOxGPtZhPBHKCDyUE08e%2BM1Bedjcr4xPT%2FgThV5cvBJbpGergEfMDcYa2PkqfZs2bArs2bNirs2bNirs2bNirshfnRGtb7T9TX7LcoJD2qDzSv3vk0wl81WDajodzDGKzRj1oR35R%2FFT6RthHNXaVcrKsUtftDi304dZz7yrqPrQCIncdMnsEnqRK%2Fcjf54yVjP5gwtJ5ckcD%2B5mik%2BivH%2FAI3wD5RnEtpwrWgw983RGby1qiqKlYGkA%2F4x%2FvP%2BNchnke5BIUGoPQ%2FPEJHJ6XG3JFbxAOPxOA1iX7sUwIdmzZsVdmzZsVdmzZsVUbqBbq2mtm%2BzMjRn5MKZDLP4rC2UVq9qYj7SWshWn%2BtxOTnIVG4ttWvdNaiiK7FxGDtWG6Ti%2FwDwMuShzRLky%2B1lE9vFMP8AdiK33gYscKtBmDWhtq%2FHbOyMp68SeSH5UOGpyJ5pYde3moWPmW5KzH0pIo2WNt0A3Wv0ts2D73zJ6FgzxqqXpKpHG9eDMx7EYXed45VeC4gPCdYyqua0I5q5VgOv2cAW8sWo2QMqhwwoyHcVyQFhVOLzvrNrdf6fFHLFX4kVeBH%2Br1%2F4bJlp%2Bt2GqWrXFnICyKWeJtnUgV3XOP67qFjaFLHUrprecy%2BlZajxPDmN1jn8Dv8Aa%2By%2BIaXrclrdtZ34NrfR1USofgeo2K06c%2F8AgcBCXr%2FlvXRrdnzk4rcoSJFXYfMbnDzOV%2BTr82eqAOQIpaBq7AV2r%2BOdTriRSEp8z8RoN9I5AWJPV5HYD0yHr%2BGQg3Ntq1lcLazx3CiZviiYOKzQo9Kjb7YzoWqRevpt5AOskEij5lCOmc60%2BWCK0j9ONYo5IYp%2BEahQWBeNjRafEcMVTvTb%2B80fyHZ3FugE9vSORJBUqPVZW%2BEHw3yVaXeC%2FsobulGkX41HQMNmH%2FBZzC%2B1iTTbadZUkjtJjzkQqSKqNm5fsdMPfyr1%2FRdT8vra2E5N5C8kl3bSMTKpkctz%2BL7SP9peOA7fFkIkgkCwOfkz7NlA1y8DF2bNmxV2bEJLy1injtpJVWeavpREjk3Hc8V64virs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F9Xv%2BbNmxVplDqUbcMKH5HIPokjWsstqx%2BK2lZPf4Wpk5yF6nF9R8xswHGO8QSj%2FAFh8D%2Fqrkoc6VmSMHUOOjAEfTjsAaZMXhMRPxRnYf5J6YPyJV2bNmxV2bNmxVQuzOtu729PVUclBFa07U98DafqsF6i9EkI6ePywwyF6vG2i6oJRUWl6xZCP2JOrr%2FsvtLhG6s0yiK4X6Zfi4QI7Vb9lv5h%2FXDHrgV5c8B0LzLc2Q%2BGF29WDw4SfF%2Fwp%2BHOg6ZMHi4j2YfTkZ%2FMGwpFaazGDytm9GYj%2FAH25%2BEn%2FAFX%2FAOJ4N8uXwliicnps305LmFZDewrc2dxbt0lieNvkylc45%2BX10UCQzHjLATE4P%2BQeNc6L55e5i0J57WVonikRmZDSq7jf2yB2Op%2FVYor2%2FwBNjktpAGjv7ZvU%2BInoy8Vao%2Fa44xCQ9as25RVBqKnfBGRby9rBEsljcIY0aQ%2Bg5FCCQD6bj9lslGAikN5s2bArs2bGyOI0aRuiAsfoFcVS%2B%2F1q2sJltyrSytuyp%2ByDsC3zwxBqAfHfI%2Fodmt2z6pcjnJK5dAexPT%2FgRkhxV2c782mSy82WUgNIdUhNs4PTmlXQ%2FwDBDOiZAvzLb6nHpOrlFkjs7lWdG9yvxVG%2FwiuGJ3Up3YTJFr7op%2BC8t1ljHjxodv8AYtkiyHbxx6XfKx%2FcXBilJ3%2BAsVUV8FRlyYA4Z80DkkPmq3EtlHJSpR%2BJPgHBH66ZBtIufqt9Np8pALHnEpO5p1oM6Xq0P1jTriPoePIfNdxnLtZtrGZluBcG3vF%2BFJYuJfx40IIxHJKzzb5et9ZtZYp1LwygcgPtKy%2FZkT%2FKWuRKKCOC6tNH1CblcWiILDUWHH6wg6wyDejx9Fw4sfOMjEBY5biH1HgLy8VYulOfJV%2By38q4ndH9LlL219K4iilHrWzLxlhI6SgE1%2F2SnFKo0Czxy28zMqcSzspIYBPj2I%2F1cd%2FytnVZdNQ2D1I%2BESMoPw%2FzMftdMG20TSQyyblmjenzoc4TY6ld6ddyKwMUsbFZoHqNwd1KnGXRXs9n5%2Fv7lg98GlWhMjwk1A%2Fm4k%2FF7jDSwSPULTTJI3PGBphGNwG4OHVW%2BgnONX%2BsWFpDFPaSSJcTESG3TcRlf2uRK%2Fazq%2BkR3aeT7e%2BjnX6yoW8ibht%2B9i9Tg%2B%2FQ98QVZHqstutowuSPSdSpV91IPVWzif1i98t%2BZ7m40FjbSWUrNCqEmkex4EH7acT9lv2c6lonmbTvMUTWdwgiumWktrLQhge6H9of6uRLzd5ZurDVP8Q2KetYFUW7jXeSKi%2Bm0hH7UZH7X7OCY4g2YM0sMrjvexB5SHcXsnkPz3Y%2BcdPqKQ6lAB9bta%2F8lI69Y2%2F4XJeDXPJlvPe6JqMeq6NMYLuE842To6ntToyuv2k%2Faz0J5E8%2B2HnGyI2t9VgA%2Bt2ZP0epH%2FNEf%2BEyESeUuff3tmbFAx8bB9H8Uf4sZ8%2F6P82TMMLNb1iDRrQ3EpBkkPC3j7s5%2FwCNV%2B0%2BGVc4n5m8yfpnzJcsr%2F6HYsLe232PBv3j0%2Fy3%2BH%2FY5JxkLourX175%2Ftb28maSZrloqsdlQ1Tgo%2FZUDO755%2B8uxlvPcMI6i6LAe2zZ6BwlS7NmzYFdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVf%2FW7%2FmzZsVdkb83W5%2BrW1%2Bg%2BK1kAc%2F5D%2FCf%2BG45JMC6jare2FxaEf3qMo%2BdPh%2F4bCNiqV6VOOcTV%2BGReB37%2FaX9WHoyFaJMz23BqiWE7juCh%2FsyaRsHRXHRgCPpwy52gLs2bNkUuzZs2Kuwu1rS49W06Wzb4XI5Qv8Ayuu6nDHMemKvONE1KWGVrG7rHPCxRgeqsppk7sb0XC8W2lUbjxHiMhnnnTHtLmPXrZfgakd1TsR9iQ%2FP7LYtomq%2FWI0Iakybof4H55LmFZdqNlFqNjcWMwqk6FD8z9k%2FQc515XuZbW4l0%2B5%2BGWFzE6n%2BZTTOlW063EQkXY9GXwPhnO%2FNsS6T5qtbofBHqaniexmi%2B2v%2BsU4v%2FlfFgHcrPTFbanZNb3caywyApLE3Qj3zjutwW%2FljzBc6Pp6fV9JHpsLNSSgR1HIorV3Vvizq2jXQk%2BAndlBA9xnNvzRiWPzJBJWnr2gJP%2FGNyv8AHAdikJ3YGf0o5pZDIboSD1GJJ9aAgh6%2F5aUzoFnN9YtYZ6gmRFY06VI3zlXk%2FVI5bi2hnlBgdBuxFFahhfr0rSNsnHlXU4JI5NJaSt1avIQm%2B8RaquD0I%2BLjkpbgIZJmzZsirsDagC1jcqvUxOB%2FwJwTjXUMrK32WBB%2BWKpfofE6Xb8f5d%2FnhjkMPmvR%2FKlrJBqkxBSVkjC0PIDpTfI15s%2FMNbu3jbR5plsZVp69swRi9N0eSjMnH%2BRcVpm669cWWtXGnatwjgKma2uPsj0x%2FNXw%2FayOfmLr%2BhX3l%2BPTYrkXM%2BpGtm1uPUUceX7yRx8KLtx%2F1s5xoN%2FefpVr6C8muVVfUuLO8kMrEIeTem7faRk5Iy5FdTuZ9B169trGZvqkc5eFD8SmJzzUcTtsrceWHzTT3fRJv0p5UZvtOYYrgCu%2FJV4P%2FwANHnPrj83vM2lX9zpWoBJYInaJ540pOi1%2BGRQDxf4cln5Z3qT2stjWrK0iexEgE60%2F2XLOUfmHYNZeZbhWP94Ayn%2FV%2BD%2FjVclLlbEdQn955q1wTiU3huLadeQCsfSljO9VNev%2FAA0bYHub1Z7V5yWkhHxpcVHJNwGiuP8AKStVk%2FbyGafqLQxyafO5S2lPJWUAmGQ%2F7uiB8f8Ad0X%2B7F%2Fy8Ctd6jpl3Lb3v%2BkQTpxniqTDcQn7LoR%2FwUb%2FALDZCyyekWl9ZyaFLPelZGN2tqDEtZJHenEs67ckpVXwq1bU7G1iKWvqtcCR4bkkhFYx%2FCHXhvvX48C%2BU7H1SbK3k9Wya5hv4HchSBF8FxBL2WeOJ1k%2F4s9P4cu%2F0j%2Fc1FphmFxPLeOhZBxAiL8jt40%2Fa5YegV6PprvLpcE8sfpTNbh3QVoGK12zluoQ2PnZih42fmWIVt5z8Md4q9I5P5J17Z2A8YrWVl%2BERoaDwoNvupnLtb02LzREdR0hBba7bVe4tEPFZwpr6sH%2FABb%2FAJOSkgPNLqGaG7e3v42iuoTwlicUIK9iM755KnGoeT7ZX6m1CH%2FYSNCf%2BFOctnuIfNkSWeqEWuu249OC8ccfW47elcd%2Bf%2BVnQ%2FyyS5t9Dk067jMVzaSXMLK3hRJlp4r9rjkQl5PbalcaRd%2BlI7PbwysI5lNJIirU%2BH%2FmnOueV%2FOUeo%2BnY6hIpuWX9xPtwnU%2Fhz%2FnTOPeY4zY%2BYNTgpVBcSVQ9CrHkP14Gsb9rFgRWSzY1K1%2BKNh%2B0p%2FZcY2Qdlev%2BZ%2FLKxK19pcdYBUzWqdYx1Lwj%2BT%2BaP8AZ%2FZyHW9xe6ZfQ6ppk7QXsBDRTIevsR%2B0p%2FlyY%2BUPNR1KJLK5lD3KrW3m6esg67f79T9pMD%2BZtCjTnqVilIW%2BKeFRshPWVAP2P9%2BJ%2BzkZRB3DPFlljlxRPz5Edx8mcw%2Fm3aXvkzULyUC3122jET2oNA8kh9NZYT%2FJvzb%2BTOZabIhnWkgaOQcwxO577%2F5fjhKIYndoZvgaUD05v5SN1J%2FyG6YI0RZ4tS%2BoyKQS1HTwYHsffALrdOQwM%2BLGOEHfhu6PWvJ6f5FsfrXnqW8K%2FDBEZD4cmAVf1Z2fOSeSLmfT9RrHEWlvDSZaVagNR0%2BztnW8mWsuzZs2BDs2bNirs2bNirs2bNirs2bNirs2bNir%2F9fv%2BbNmxV2Y5so4qw6WL9HeYLiLpFcUnjHQfH9r%2FhuWSbTnBgMXeJiv0H4l%2FA4T%2Ba4fTW01FRvDJ6chA%2FYfpX5MMGaXcBmHxbSrsCdi6%2F2f8RyZ3j7kdU4zZzrTfO2qQa3eaHrkax6haux%2Br9FkgJ%2FdT2790ZP%2BGydWWoWt%2BnK3erAfHGdmX5rkSEovNm2yiQASTQDqcCt5sL5dZ06Ko9YSMNqRAv8Aiu2Iv5i0qJecsrKo%2FwAkk%2FctTho9yo69htbi2e2vApgmHB1cgA1%2BffOYX%2BnXflXUghJezkNbabxH8jH%2BdcPPN82l65b2j213DN9WdmmtefCRkdeJaNSVb1I%2FtYXWM8cVk2javM95pUv9zcPvLB%2FI3LvwwhIT2x12KCBrxjyjRS06jrxG5YD%2BZcrzzpkXmjyhLPpzCSaFVv8ATZozuXjHP4GH%2B%2FE5JkUmju9BujaTsJYHHKC4G6Sxn9r%2FAJqwJH5g1fyjDaw2KrcaE0zSNE27RiQmtuXPSJ6%2FuW%2FYkxI6hUg8ia9qulatHe3s0k8DgxGKZywBk%2By3%2BTkn%2FN63a8ttG1a3rHL%2B9hIB%2BIdGK%2F8ABA5DNbS0tL%2B4ksJOenzETWrjYiOT4gjD9l4WqjL%2FAJOTTU9XtNc8r6dDP8E8rc4iSP71RwkTfcP%2FALsX%2Bflgq09zFvKeoTMwV2NeCsB0BaNqf8a5N9Zu59K83W2pWb%2Bn9cjBo24ZXUNxI%2F1lyCWsL6fNb3LLxid24tSgINA9Puyb67pd%2Fr2kaPc6bxN5b8owWYKAYW%2BGpP8Ak4RyQU8ufOF7JLD9XRLaAj96ZPiYv4A9An%2FDYOfz9oEbtA0kjXSqG%2Brqh5NtU%2BkW4iQL%2Fk5yK%2FiuNagjEcpTU7Rj6YqaSAbtG3%2FFin4om%2Fa%2BxhXcGeWxlDgpPbfvUFaPDKpFXiI%2FYk%2Fk%2FmwFXr0n5hfXBJFpVtxmC84jOa81%2FaKqv7Sfy5GL7zJq2rwXFhqF40AlWiSQfAEPVW%2BH7Sfz%2FwCRkTl1dLW3t7xi8108C3g9EBArCnI8jtVvtNRcR1zzTdJcQvYwRW7y28dwzFfUblIvI05fAo%2F2OGxSt3zSW9LPXITMlCjiuzofsyRt2YfsPkd0iabTtQksp1Zra4U84X6SJ%2Bw4%2FwAoD7Ei5dzqV7LFCt7KZnVAY%2BRqyg9aj%2BX%2BTJdouuaHrltb2PmCNfrkKmO2vKcT%2FkgsP2v5cjtapfo0Jt%2FMllFGHltZvVeK4ptwWNi6yU%2BzIvRlwm1mUarpNtJHEWutOkuIbh0FSbfkGiZ2%2Fb4MxH%2Bpk6uZNM8saddywStNK605tsKnYcF%2FmbOZy3s2nWaLE%2FG4e4%2BsK43HELxowOzK%2FL4lx5fFWb%2FlprP1TWLQMaLMFRh%2FlRtxr%2FwLYn%2BfNjLp%2Bp2WpwghWLxuf2SGoRX%2FAIDI9peo2kMtjfWQMUi3JM9u24j5rT4D%2B1G5qyfyfYzp%2FwCcdimr%2BUra%2FG%2FwpISOoIpXJDcIPN4HDcR3Scl2I6qeoOGNvcQyw%2Fo%2B%2FJWGpNvP1aBz%2B0PGFv8Adkf%2BzXIxSaym2NCOh7MMNYLhLlOS7H9pfA5FU30vUNQ8uaqjIqsaqHiO8UsZ6EeKsrfA%2BS3yUr6p5iudTl2S1RvRTchTKeCgV%2FlTlkOsvUveOn8VkdatauxoyN%2FID%2B0r%2FwAmTfyYHsrnTdPasc1%2FNczXMZArwhT04g3%2FAD05Yjml6Fditlcmu%2FpvX%2FgTnK5bS4sp1mDtFID6kLioPXZgc61NGTBNH4o1PpUjIpNbyX0Ulvcw%2FV9Tst7i2IqHWn99EP2kZf7xF%2F18nMX1pANMR1TTYfMkRuYYlt%2FMEY5EfYW7UftL%2FwAXjDf8u%2FMFzM8mnX6MJLWRFkMh4yiqtGUYU%2FZP82IzW%2FH4DVQG%2FduPtRP1FG%2Flb9lsG6ZJDJfPcvGI9WCoJZwPhmRGHF2X%2Ffi4AP2peafmBE8Hmq8LgATCOVaeBUL%2BtcjauUPip6qe%2BdJ82x293Pb2GtyL9ZmWRoL5QAUZZGQK47p9nOd39hdaZctaXScXXdSN1YHoynupwFCL0zUX0y4SVGf6uWDqybPG46On%2BWv%2FAA652nRtY%2FTdqJbfjLdqnK4gjpSVen1iBf5G%2FwB3Rf7qfOCxycaqwqjfaH8ck%2Fk%2B61K11AC1kZYIT6gmB%2Bxy2oP%2BMi%2FC6f7LEGlZCbBr%2B%2Bu%2FqqpBCjP6cbtxHwn4ljrX%2FgckOj2EMcC6lK6yTRKYmkIoYl6gE9xT7L5HhcTJqM1jJCEjjQSQsK7qT3rgi%2FuJ7PTDKQ31S5f03ZPi3SrfGP5RiKG6bej%2BQPzB0CG%2BuNHvB9VeZx9WvZKBX7cCf2P8jOvghgGU1B3BHfPHaQrfuEt3DHkAJakKhP8AO37A%2BedX8i%2BYPOen6dNp2qK5t4JFjs5HClyoNJfTdieUaL8S%2FwDCZEcRNEc%2BrfOOKWMTgeGUaEoHfi%2FpRP8Aunt2Ab3UorWqAc5QK8egHzOPhuoTYrdesJYgnJptgDTqdu%2FtkT1C9XnLPIwofjLduJ6fhkgLcYsi0vVRfSSRSUWRd1UeGGgyN%2BVrCSkmrXKlXnHC3RtuMXXlT%2BaTJKMTV7JdmzZsCuzZs2KuzZs2KuzZs2Kv%2F9Dv%2BbNmxV2bNmxVB6rafXtOuLXvIh4HwYbr%2BORbRL%2BsMTMaSQsOSnrseLZNDnIdfXXtP8zXtnZGC2tJWEsUz8pH4yAE8Y14rs3JfibJRPMKyf8AMXye3mKyh1LTH%2Bra1px9S0ulFSV6mNwPtRnIb5e8wXlw%2FwCj9VR9N1u12PGoD9vUgfpJG%2F7UedN8uapcXsH1e7C%2BrAi%2FvV6OPs1K%2FsttgLzP5Ysr%2B3NzHFxmjPMlNj%2FrLTdWHtiNtlU9I82sxa11lAssf2bmMfA9PFf2G%2F4XIt501y81SykEEjRQRMGEaMV5L0%2BKn2sq4ke3j9O4asuyLNSnMduX%2BV%2FxLCfVZ%2FS0u8cDkVhYhfGgxodGQDDZPMculusTzpwryMT7mneh%2B0uTVNL1e%2Bsob1tLmmtJlEkbFW5FGFR8NfUzi2iQ%2FXNXtLjUAXWe6hE3L%2BQyKHG%2FbjnqzzP5ok0H0baxgWVyodi5IRU6KBx74ASrzOzsfL1tcH69ppMtdxM0h4%2F883OS2GLS7239K1pGpGwj%2BEr%2FALHphVcecBcyrc6vBFdabL%2B7lV0UPbt12KDnx%2FlYYGvrWKwP1nTp3kirsT0BpWgcdf8AZDJg%2FFjSanSb8xC2ursXtpG5kt7dkVRGaU4g%2FETy%2FwBbIzc60xSeylsgphDJeafNtIY%2BnqREfCygYcWPmV14x3qkqQDzAo4X%2BYr%2B0ME6h%2BidZZImjM90FLQzQj40BFNn2%2B1X7DYkd2yi%2BrHNIh8uXbLp7yLJdToZbZJDX1EU0NOXxCeL9tf%2BJ4PhbTLya58sXqqt3EoKlV9MSoRUMn8ssf7XH%2FXyD%2BcfKFzoWnRatFqUUU%2BnSGSGAsBcFXZaFeHJVZDu2HOn3See9Kt1%2BsCx8x2y%2BpaXabB2TxH8pP21%2FZwAnu3VQuV1CC2iW9cx%2FwCkS28kMtA5dKLHL1%2B00TJyZPhk%2B1nS%2FKN59Z0GSNno6yI6e1V4v%2Fwy5z9pv8YWj6Hrcf6P836EWmiiHSfiv2ox%2B2kwH7P2ftLk28uxrbeXYLhYXV7gMzxkE0IqPiqNq%2Fy4gb%2BSk7PNdX1m%2BHmqaytJ%2FTtTdx%2FBGoSu6swZqcj8YbCnRmM97drKxKXcslu7tXb1%2BSBq%2FwCSzZK9c06xuNUutStkBls5IFkAIAMoHqUUfzNRkbCPW5Xs70rZcVtL0%2BtCoAFVc1oK%2FwArni2RIShdD0rU9Z0g2UNW1DR5JLG%2BirRlQuXhmp1ZP72M%2FwCph75g0K2sra21G8ch5baGJUpt6yARuvL6OWEQk1eJrq9s55YLxBzuGhJEjr3LKvxSqv7WISanq2oQRXGryvd2MtVhuSQ8aNQch8OyNjsqElhK1hbcr%2Fcv3K%2FyV%2FycRSQBmUiifsfQM6FpfljSbrR0mCy3EjDlDIzN9sGnphU%2FZ%2FlbIrqnl%2F6jM3Cb1VRqOoU8078XoOHPISIiQCd5ckgE3tyS4StK6xSy%2FDJ%2B7HqseO%2FYVw0W2thatp10he2b7YH21cfZljP86f8AD%2FYxO4tbO4so4I7ZUvG5Lpl3PssprUws392JD%2Fut%2FwCbAOh6sLuU6VqYMF4hKRu2x5DrFID0YY7rsh4tMn06%2FkspjzjniaS1nQfDIE%2BJWXwdafEv7Od0ugnmL8tmYipWEOB1oGWp%2FwCH5Zy4hoT9WuU5BTzj7FHp%2FeIe3%2BX%2FADrnR%2FysvRfaHeaJIwZ4RJGo8QDzQ%2F8AAy5OPVBfPNxbpMpifYqaBu4IwFYWlx9fEIJU7lmH7S%2F83ZJ9e0x7K9unRg8azMkoHWJySVR%2F9YH4W%2Bzj%2FKtvFdaxBDLQKzAVO243G%2F8ArYPJUXb2NvDZtdyWxe3B4meCQ%2BtG37Jkjb9lafaXHRXl9q2r6etnWJ7Yxx28h%2B1UNyMjt%2FlH4myX6lp1poPm57CIFLG8jikCsKisqqXWncc2%2BzhLZW0Om35hCuLiCeSgIHFY67KT9rl4YaV63IwMVSakg1pt27eGRuzvrTWILdYbnlcIWXSdQc8W5pXlp91%2FLIP91M%2F94mG9tcfW3AQ14xgsPH4c4Zp%2Btz6HqN2rJ61nNIy3loTTkA54yIf91zR%2FailyRNUinpV0iXqyyxw%2BjdW5Md%2FaN1iPdqf74f8A5Jtka1ZZ4LWYxsySKpaCRT8QI%2BIxkj2Hw5KLe6%2FTsUF%2Fp1wsmrRRk2l0QAL6Fft290g%2Fu7uP7Lo395hFquqaL9XZmmWCRwySWTgs8MgG6Oo%2FY5fZbAUhC%2BZ9JsfMl1SKYW%2BqyQRT2DMf3UvJeTwt4M5%2BJchKyrOr6D5gQwTQEpFK%2FwDeQv8Ayn%2BZDk%2B1Gyj1ew0SaC4gS5ntmCtX0ELQtQAM%2FEcv5Fwv1bQxr%2Bnhr4oms2ymP6xuCStSIbof6v8AdzYOaGAQ%2BXdTm1NdLjj5SN8QkG6FOvqBu%2BTjTrS2sbZLCMcVUmkpFGLn7Rf%2FAFv%2BFxTTme102DTuILIwZpKksW%2BzxWtW41%2FZyU2XlW41HhcahytOQJZVHKaXw%2Fd%2F7rY%2FzviAqQ21ldXtytrDG0k524qKmnj8sO736roNmNIltfVv7hFeaMnkUqTwpxNA2S2MaT5etS9w6WdtEvx1b42A7Symh%2F2C5zTzb%2BZun3swOjacGVKwrqMgoajeiJSr%2FOTDVdVZPYa3pOj%2BW7u31uKKzuZGP1dePP1Iz1UovxtJ%2FlYU%2Fl%2F5hOtyS%2BWrlis1Gm0pyfiR4%2Fi9Kv7SumcrvNYnu5DcXH7yToamrfSf6YL8ratLZ%2BZtJvoT6bwXMbEjuCwVh8uJauPFuFe%2Bx%2BYJ4NMawQiOFzyZOlDX4h%2FwQyQeVtAn1BY9R1QN9WU1toG%2Fa3qGYeGE3lnRLPVfM19LdAyWkTtcW8a7xOGaq1kGz7k%2FAM6moAACigA2AxJ7lojn72wANgKAbDLzZsirs2bNirs2bNirs2bNirs2bNir%2F9Hv%2BbNmxV2bNmxV2Qzz3p3P6rqKbFKwyt%2Fkn4lJPseWTPOVfnX5gmstNtdBtnMbajykuXHX0YyPh%2F2b%2FwDEcbrfm24MRzZI447GR59w6pMfNyWl7EdD1KM3Cp6dwoKldj%2B1zHE%2FRko0rz1rUtz9SvLe2lm2Hp8jC7BhWqV5xvUb%2FDnn01jPEin8pHgckemXmsTaHcxANw05o57O7IIKxliJYll%2FaRT%2B84%2FsNkIZhIkSjRDl6zs%2FwIjJCXFHkb5gp3%2Ba2r%2BbdPuEvYLVbLRpiFL27c2EvWkj0%2Fd8v2VpkGsfN2p3Kra3V2XicgEsQeQJ3qSNs7d%2BkdO1rSbKw1LhN%2BlbRXVJKFZyq8ZV3%2F3YGHLOEecvKFx5TvTLDym0idv3EvdG%2FwB9yf5a%2Fst%2B3lpHUOvsua4iNwbWCruHK%2FB0qN9j0zoy%2BarW48r2Oq3l1Le2sSizuGkqk6zgUVXMda8F%2FwCDTOO2M01rIs0XCRQ4kHqCqGnZl74NgkTlMqSOIZpPVNrXjGG8Qvfj2wXzSyqz1eO%2FtNSjQM8sVubhFbbkIpPiI%2BUR5Yca3q17IdPmtZ2js7yztriONNhyAKSciPtMJUfIZaXDWL%2Bra0jcq0ZYAV4yAq6mv8ynJf5Yex1PRjpUsfO%2F0pnuLJWNPUgkNZ4l%2Fm9Nh6vH%2BVpMRa2uvBcXej2Gp2rSC5tJJbOZkqW4v%2B%2FgY%2Bx%2FeJhtBB5sOi313boLa9ihMlo3EB5ClGkULy%2B16XJk4rhlZarBFAbZljtrS5Tg%2FoIF4fyOT1JjbC1L%2BbSbowyMwu4WrzJryX9h1PQpxw%2FFWFXNut9PB6srS6frlqI4JZHJ%2Br30bCsbOf5phx3%2FAN1T%2FwCRgPQbtreVrEFrW8gfnDU0dJUPF09unHD%2FAFeygjjvbi2haTQbpxcXlpHvLp9x9n6xClfjtjXi1P2P3b%2FGseE99ol9qtyjIKamYlktb6Mn0L5QKJ8Zp6d3x%2BH4vt8OD%2FvciTXPZaZ%2Bkdp5%2Bgt19Yad5p0%2Fe0vU2Y8T0NPiaM%2FtJ%2FuvOj6lcGysFaRlYxJWUjfkUXk3z5MM5p%2BV2l3U2pPdalFHK1sOLyqQxSZaFOdDWOT7W%2F7eTTzK8UdlcxREhXBTf%2BdzWg%2B7JxNji70HnTz%2B51GXyrrFxaapD69nfcRqSftqxHL1oj%2FOjPjtQ0GNYCfrCXWi3QMtlelhyjkYbFfdvsyx4M87Wy6zqemTq4X9KwQnmegkK%2Bmf%2BSi5G9M1PVfLUt3od3CJIGDEQS7iKUD4Zov8%2FjyErqrruPNkK58%2FJN7Sx0%2B0sDe3k0k9xA6JBJACGqwPHiKp8S8fts2F0mrDS3nubPThH6ZLa5YcQsnBvs3kSsCnJeXJuK%2FB%2FwAYmwQ%2FrXdjEsQRizhvjKCrUp8If4uW%2FwCyuK3Fs893p1nezfVdeDCOxmHFi8ZH%2B89wSeBjP2YuX%2BVFiYggA70Pmt9QzfyjdxXeiPeaY3rNYt9ZjdKUkgP2gyj7L8ftL%2FPgTz2tuLdJIT6cN1JFcxzJ2U7Sb%2Fs%2Fa5BsKfLEMvlPXby2sJI40uF%2F0izVuUcb05n0z3gkTlw%2F3237vHeddXN5pEMdnbmOys4BFcy0qDcF6GJa%2FwDFacmyX8Pu2R1QOi6Yupabd2F78dj6xjLVFUlpySaHwkUf7F0%2BDIrrvl6dr42N0eOtRIHs7ldkv4B9ijf8tKgfB%2FP%2FAHX28mPluyb0Vs7OdTL6SSReoK8%2FTZqN%2FrqOPL%2FJwTqdhFr9m1lqUhjnhcm2uv8AdltN%2FMKf7pb9tP8AZrjVhWHaHrEWqxjTb9gt9GKQytsXptxNf2sl35d3J0jznJbCojuQlR26cT%2Btc55qthdyXcyyJ9X8wWfx3ESbC6UbrcwfzS8fjfj%2FAHyfvPt88N%2FKuq3F3q1hqBIEyc4pWZuCkoAwck9MA50VPJV%2FMq2k0TznfCJQUmPJom%2BzJG%2FxcW9t%2FwDY4Q6cILa6ZY2eKRwslv6goaDfjy%2FaK4f%2FAJleadM80a8t1pcZ9GCJYmuG2MrD7TAfsqv2V%2Fmwr0WyuNXUw%2FVxci1%2BKLt%2Fw23wrg6qGVanr1vrK2t3csIry1aESBjxJEYYOQff4cjvm3zLbpZXLWTGC5vr%2FwCswRjfjBGoC8yfi%2BIjCPX2vNMu%2FqZDRNGKSAgUqxqgqKruF%2BH4sjsyPPIZZSXJ7knfG1e9%2Flxrdt5ksbqdSFu7eMLcW5O4%2BE0cV6xvnF75uVzcb14yyBW7%2FaO2KeS9RutC8yWd7DMYLfn6d3L%2Bx6D7SLJ7YhdkNd3UkRDRSXErxMDUFGclT9IwndCtpWrz6ZN8MskcDsGdojxkjcfZmip%2B2v8AL%2B3iWoySfXbiHUmFy14frKXqH4nL14zK%2FVuX7cbYEeJWDPQVpU0Pj0x6yqLL0pJUW4tXE1o5O9SaSRbeP28Cob1Z1T6vPKzxjeNSSQK9aV%2BznXLfUrdtDt9T1U%2BjeRxpa3iMvF5V41jcg%2Ftrx%2BLOd6VbaTqJlS7lMl5Iri3hHwRiQj920snhzODppWsii6zcGeSZg3po3NSy%2FD%2Fen4K9V542r0LyheaXcm5uaCCWNhxkl%2FvClOqcvs4rrn5g2enI9tpKi4uqlWck0rSvxP8Atf7HITazpccriNaLGD%2FozGnBVIJkZP261%2BFuWE4juLqSZo2pbJMjyitAQeVPwrjxbUE01reuXGvCKPUZJ2Cs0iqgUgFtunw%2FCKfDhWLXSzayW73jxuGElWgJUbUoSDhkLcXEjJDSJqkoSd%2BH7OGlh5a1HzDONIit%2FWvZEMkJRlQtGlKsCxVX4%2Fy%2FawWtILTLTTmeMmbTrm1jXe0nmMLOwH2nZlrWv%2BVhlBb6Lql09zpemrbx2cJe5WNqiRi3EKrfyj%2BZR8WJ6x%2BVPnXQrK41i9sljsrUB5pBLGzAVCgiNWLNufs4I8h6hp738sNxRBexei5BoBJWscg%2F2Xw4VZjoMllFbx635Y52k9mvPUdKaRjHIF%2B08PInhJQV6%2Fa%2BHOq6d5%2F0G7toZzK%2FBwKzBCVrTo3GpQ%2F62cYW21DQ%2FMUVrFH%2B5vyInPSIqzfE1f2afawkmtp42klt0f6uzuInWoDqrFQRQjkMJV9Q2ms6VfitpeRS%2BwYV%2FwCBNDg2ozzr5Y1vTI7V9L1PnbTDk8E4Qs7M1KKHBDp9GTfQfOdosSxw6txkp8UN0DxBG37WNea09UzZBNY%2FM3SfLcVrLrDIyXZKwm2bmW40DsF3%2BFa%2FF8eTW1uoby2iu7ducMyCSNh3VhUYEK2bNXNirs2bNirs2bNir%2F%2FS7%2FmzZsVdmzZsVdnE%2FwA9rKYXmj6jT9w0ctuT4OpEgB%2F1lOdsyO%2Bd%2FLUfmvy%2FcaXstxtNZyHosybofk32G%2F1sB5ORpMoxZ4TPIGj7pbPl%2BNPrERi%2FaG6H38MkPlXzh%2BjhHo%2BrmunCqwXFKmDmfiSRf27d%2FwBv9pMJBDcWN7JaXcRhuYHKTRPsVdT0P%2FNWB9RiWO6ag%2FdygSL7cuv3HKpkwImPcXoM2KGaHhy3B3BHf3h6B5qslXQooNOVof0a7Xdi6fDxBPKQQ78vT35xNl6Fr1l5s0yXSNZRHujHxlRhtMv%2B%2FE8JB9r%2FAIfILo2pT6XfLd8mmiKehPAzE8oT%2ByvKtOPWP%2BXKvrafSpl1XT2L6ez84LqLrE1a8WH7PH7NDlkMoluPiHRarRz05F%2BqJ5SHf1CT%2BZ%2FLlz5WvvTUmXT5jW2nP%2FEH%2FwCLB%2Fw2FauD8Sn6c67a3Vh5x0p7G%2BRfrAWskY6EHpLFXp%2FxpnLdc0S88t3ptrir27msE3ZlH6nX9pcmR1HJxFS3uA4oftDBlvcz2lxHc2sjQzxMGjlTYgjCVG6Mh%2BWD4ZRIvv3GC1Z7pdxHraNJCViukq13aswVAO9xCW%2F3Uf8Adkf%2B6%2F8AVw1t5NPkKabe3SyqKG2uIquYDX7Jc7ei37a%2FZ%2Fazm9tdT2dxHdWzcJYzyQ9R4UI7qw%2BFhkwhvDqECTaZbExyHhPbqKmCYivBf%2BKZPtR%2F8DgIkSDxVHqK%2FSyBFbjfvR8k1xY6gVtLb6vJbVWZbhvVLo38yAemYZF%2F4XCjW0McX986%2BX72Uem6V56bekVAYLu0D%2F8AJSL%2FAItjw7W0utVgj0%2B7k9DULc%2BnaSMaFkrvaz%2F8yXP%2Bpgq00iztbkaVcP8AWBer6NzEworLWjRsoJ4yRv8AEjr9jGOMDbn5y3UyJ8vcyfyHpl1puhy31zEE1e9cJfKxXjK8JMaTqfGZCrt%2FN9rCX8wda%2BqyabAGAM85aYA9FUcV%2FwCGyZXElvb20VgHb1LeNDEKmpKDhGS%2FfOK%2BcrhtU1G8vLZ%2BYsz6M0Q%2B0nBh6cwH%2B%2B5af7F8slsAAxG5tkesTm68saddo3GbTZ5bb1B1G%2FrRfrxGTW7LzPpcrahALfzDpir9biI4tJESAZowftJ8Svx%2FY%2F1cD6VMb3RNTsShJmhju4gVP2ovtH%2FgG%2BLBVnb6b5g0ux1JGWDXNDj4SEiongQcGjk%2F34hX7LfsYBukt%2BWba4gWHWmj%2BsWdpK0Dwqy%2BoWkonJE%2FvHZOfNcKdasnk80288TN6EEqxOXNWSOJiS7n2p8eB4bySw1ilv8Au4OQk47kKo%2BLv%2FLX7WK6jr4u9Uee1jItndiIiasyv9sV%2Fl3wWAFZBbvYajqsV%2FDSP1yITPUgSKH9SSinYcIRw%2BH7TSJhr%2BYOr2sOkWmnhHSK6YiRolFF5A%2Bn6ngJif8AWyP2ML6dbiRI%2FrqsAI3SRAsMY%2BxHKrlGhfl%2FefB%2B8%2F3XgXXYppJG1m2unkbj6Op2pUSKVAqksSNsif5L%2FFH9vHoUlrytdtDCgdmV7d5I2boU%2BINGT%2FLhpr3nLTKJ9UT19TB4zNHtCe3Jj3b2XIssp1Y8bUfFOQsix7SCSlAXpTmj4av5OnsdM%2BuzB3Zm9OWaNGD2%2FMgRTolP3sXP4Zv92fyYi6oITDVrG28x6NBeBvqWoWS1ivSeIikU8jFI%2BzLA6%2FGn%2B%2B3yFaq1pcXix28ZUGi3U2wadx%2FuziAEWv8Aq%2FH9vDLV7TzXeWrqbaWO4VhHeNHtFdCP7EhX5D4v9%2BfbwpC3liGgvIoVdSqt6pLPGr0pTj%2Bz8Xw4CUkEGil7RwRu9ZzwjPx8UZiKePShyb%2BRfMdhHM2kLEV%2BsDlDLQFncUCxcRX7WEF1DCs63zCs6r6c1Nkencr7rgXSpFsb5zCForepC9PiUEfZr4DADRVD%2BddUkPmPU4lUgVNtNG4qDw2Bof2kP2GyK1LECpY%2BAqTnUFhsta1Ce%2B1G3inuZCrySsgqwqFNRkwu9R8m%2BWIwscEHrharDBGjSH5tT4f9kcIoop4da6Vqt2GW0tbiQHbikbkH57UwHNFLbSvb3CNFLExSSJxxKsuzKwPQjOu3%2Fmq%2B1mFo1C2NmFMjhTQLGu%2FKRtumQfU%2FMWh3uoTXx0r1pJePKSWSnJlVU5lQNufHnhQxmOYxklSPiFCDvUZQKUIpUn7Jr0%2B7D8eYrBP7nRbUf61W%2FhmPmtkBFvptnEezCMkj78CpRZOY5w%2FBmWhBCjseuSyyn0pIpIr2CSr2gtbYTQt6aylufPp8PBWbCZPNurROskYgVkYOv7tSAVNRtg2780%2BYPNd5CdWuRJHEWkWONFjRaih%2BFAPljsqLeRrVh%2BjQt3GBRoXBBKAfFwag%2BLJ15B17yfealE2taTHcXMG63Eq%2FvbYL%2FvyH%2B6eFK%2F3iJyT9vAXkPygPNEuoxx3KQ3VrAHtYWP8AeOxpv%2FxWqijMv2WZMLdS8vBLx4pg9jqVqTFJIvwup6fvP5lP%2FDLillv5i%2BStPtL867pEEcem6hxkivLWjIk%2FevHb05ftZF9Okd5Et5mayvoWEtrdRmnCUfYmhYfzftp%2B3k3%2FAC68zLfx%2FwCFdWt1eElrbUoW%2BwGIrDPGv7CyU%2BLj%2B18eIeavKM3lq7WqfWNOkYizuXFeNesE9P8AiX7WNbqCncfm2fzRoUmg6zEqa7ZPDPPCv93eQRMC00A%2Fa%2FZaWLCHzf8Al7oOra5b6h5KuBHe3rj61Ywr%2B5Xlu0%2FL%2FdDfzx%2F8Qwpup4VWBrORhfW7iS0YkrJbkdRLKv2om%2FY4fbzsvkXU7fWNHju3jhj1ZAI9UEKhSJvEmg%2BF1%2BPEhXgPm%2B98y%2BWriTyxrM0iCMBo5VVW9aDoJIZm%2BLif5ftRthN5X8zxWXm%2FTZ9dHq6HH%2Foz2z1eNLd1Kq4TvwY%2Bo2el%2FPXkjTPPGkNY3n7q7hq9jeqPjikp%2FwANE%2F8AuxM8mazo%2Bq%2BV9UuNI1KL07y2PFlbdWU%2FZljP7UTfstih9Jy6d%2BVV3f2dja3ltHe6ipezS2mrzAFfh%2B2iMf2K%2FE2Xcfk5ojoTZ311bz1JEjFXG%2FitE%2F4lnlh3USLKCY7lSGLQ7UI3DA9mz0L%2BVP5wLqxi8s%2BaZRHqIpHZX0hAE9NljlPaf%2FK%2F3Z%2FrYqqal%2BS%2BoXb2EYvraWG3kcyvLG3IRuwY%2BkgPFW2PfJjZ%2BRruwuFa21iRYFACKFKsKf6r%2Bmf%2BReTUDxy6Yqo20LQQpE8rzso3lkpyb58QBi2bNirs2bNirs2bNir%2FAP%2FT7%2FmzZsVdmzZsVdlEA9cvNirzb8zvIf6bhOu6REP0tbL%2B%2BjUb3EQ%2FZ%2F4yp%2Bx%2FP9jOHagvqWsMw%2B1ETGwPWh33z1znI%2FzL8hqEutf0qP4JRzvrdR9lwR%2FpCDw%2F37%2FweRnGxTtNBreExw5DsD6Sen9F4gpphto2prYSSQ3Ketp11RbyAiu3QSoP50%2F4ZcK5YzG5Q7eGPEUyRpMykRSEhH7MV2IzGAkDY5h3OSEMkDjyCxL8beaZaglxoOpxTW7qLSWkmn3cX2eJ34nx%2FwApclMsen%2BcdIeC4QCZQPXjH2kanwyxe2RjT7%2B2e0bRtVHLT5SWhl%2Fat5Ozr%2FxWx%2B2uJWtxe6HqSw8wtxDvBL%2BxLGegP8yOMyceQEbcuo7nnNVpZ4JmMtwd4y%2FnD9bENU0y80C%2BazuRyTrHIPsunZh%2FTKil4kSIaj%2BGdZ1HTtM846SXUenKv2gN3glp1%2FykP%2FD5yO9s7vRb2SxvU4sh3I6MOzp7NkyOo5OKmSuGUMvQ9MMdF1m40S%2FjvoAHUfDPA32ZIz9pGH%2FDIf2Gwjt5Qh3%2Bw3%2BdcFnrir0GW5Kyx6hav6ltfqDHddW5Dbi1do5V%2Bw6%2FzZLPLbQ6nP8AWrpAby3oJGI2kXqkrfyyqR9rOU6HrKWHq2N8Gl0y6p66Lu0bjZZ4v%2BLF%2FaX%2FAHYmda8p2i2tk8sjrI7miTITxkj%2B1HJQ7huJ%2BLJR3K3snuq3fo2ruoqQC3idvsgD%2FWpnMJ%2FJ%2Fme4Msq2jC9tuUaXcXER3drJ1japH72NW%2BFmX%2Fit%2FwB5nTdIksyzW0c%2FryxVZgzBmCsa4eCZAKHDIWgbPDvK9jquh6xaW%2BtW81rDL6lozTKaFJQVAB%2BKtfhwvs47myvL%2BNQfTs2kD9UK8gQKlv2Xp8StnR%2FzQ1GW08u%2BlZO0d3dTxxQshKsKHm7hh9jiq%2Fazi6MxEiI5YOa3NyxJMjVrux%2BIrlctmQ3TS91E3jCG2jHI09R1qOdOgFf91r%2Fw2CLKFYxyJBIFZJOwHgMC2NryG3wRnq56t%2FZhusZiihu7cMWt5ir2zpyikUAU9T%2BYcuWR5pdPqiacQrWqXCzR1USVBAb%2FAHYp%2FnwutdVhtdTa%2FgWVBOAk8BcNG9BTqyk4c6rc2dxe2F3c2kcKJGI7qx4kUjpyR0B6Vr%2FNhBqc9haSxpHERaXRaWIt%2FuvcoTRa75PlyQn%2FAJZvYI%2FMUS2SNELiVTEygUjnVW9OlftI8hVWVsnSXVxfTGS6laSViCS2wodjsNs5%2Fo8rrL69qwd6pIrqAw%2Fd%2FYPw9OmSua51GVJ762swQXLCD1AiryNQrMfi2%2FyVwxKkIjW7%2BezjR7PTpr%2BUbv6WyUH8zfa6duOEPnjT7SEWmrxxkSXatazowA5KY%2BScgD9qIrxwQ3mPX7EetqFhAtqPtegzySAewA45F%2FMHm1dcRIGjMdvC%2FqRIKcuVCvJ2%2BR%2BzglLY2yhjnLaESfcEquburW8M5HpzKvIr1FaDl79cRbRr6S8%2Br245GCoac%2FCjKp67%2FwAww%2F8AL3li01qBLgPJO8f7t4wP7s%2FsE9fhOHer%2BVtfgg%2BvabcJe1FJrPjwZVUU%2Fdy%2FR9llyIFixuEzjKEjCYojmGM6jaajZ28RjBiEisJzULTi3iSKR0wboHkTXdWt21OLT5ru2A5RlN1k8SrsVWT%2FAGD5ena1aXA%2Bo6pD%2B9J2WcenL%2FlBJR8D%2FwCrnSfy880W%2Flyzl0m7uJZtJR%2BVmXSsltyNXhYD7cP7ScfiT%2BXDXcxosR1D8rvP2qWUdlZadHZW7kPcPcXEYeSn2EKR8%2BKJ9rh%2FNkRu%2FwArtc026ey1FooJo9yKs4IP7StQclz0brf5jeXtKhR7eYX0sieoiQHYL29Rv2Cf5T8WQ7W%2FMj%2BZrOKd0t1Ct%2B6eDk7xMaj05CaNxb%2FVwEgCy249PlyECMefJ4235fXQ63Uf%2FAt%2FXC7VvKsmkfV7iab1dPlISW7jTeFz%2BxJHWoPg37edQ5kyNDIvpyx%2FbQ%2B%2FQg91OB7hFCurIssUq8JoHFUdD%2Byw%2FU2IIIsMMmKeOZhkiYyHMF5g%2BhCNipcyFf3nwD7cH%2B%2FoT%2B1x%2FwB2J%2BziNyW01%2BNseVtMoMc4G7VH82TCbyxfWMxtUMlnDIPrOli55JIh%2FwCK2ZeMqthYYOMVxBNb%2Fu2HK%2Fsl6JU7Xlp%2B1xDfbT%2FdeFgmPknW7q0eC%2FsZ%2BOoWTck5ftRnrG9PtIfstnatY0iw%2FMfQ4%2FMWh8bfXbdSjxnbmyD47S4%2F5ky55gWcaZqDPp1yZo42Ppz8SvIePHOn%2BTfO2qaXML%2FSHiPqcRf2M9fTYf78Xj8XIfs4qoWsGsQ64Tpp%2Bo31zGbK9WYfFE8Z9TceI4FP9lnpNbWDVdGit9RRbiO4gT1g3QkqCW9jX4lzksE1z5382DU7WzEKoqq5UVAoOIeV9uR%2BL%2Fgc7RDGsMMcS%2FZjUIPkopj0UvHtQ%2FKnV9H1F9X8vXCajyYt9TuQoYLSijkx9OTj%2FlcMS8q%2BaLny95guoNYs2s%2FrfEXsLLxKyJXjLH4pxOdpwl1zy1p%2BvCOSctDcw19K5ioHAOxU1HxJ%2Fk4otNoZYriJJoWDxSKGRh0II2IyF%2FmN%2BXll550z4OMGs2oJsLynfr6Mv80L%2FwDCfayU6RYNplhDYNMZxACqyEBSVqSoIH8uDj0xV8NappV7pd7Np%2BoQNbX9qxSaJuoYfrr9pWwuSNg4NSHBqKbEHxrnrL80Py2g86WX1%2BwCw69aJS3kOyzoN%2Fq8x%2F5NP%2Bw2eXm0q8guZoblGglicpNA4pIroaMjfy0OKvpf8lvMusa5ol1ZazOLqXS3jiiuTvIyOvJRIf2ylKc86eM8y%2Fk35usfK2r3FtfyMtjqoRZZ3qRFNGT6bP4IQ7I%2F%2BxztF1%2BaHke01SDR31aN7mZ%2FTLRVeKNu3qTD4F5dMVZlmylIZQwNQdwRuMvFXZs2bFXZs2bFX%2F%2FU7%2FmzZsVdmzZsVdmzZsVdjXVXVlYclIIIPQg47GSOsaNI5oqgsx9h1xV4R%2BZvkA6VK2r6VH%2FuPlarIv8Aulz%2Bz%2Fxjb%2Fdf%2FA5EoJLK%2FwBFh0yOJU1AyrH6oABboqJItebtyJkjZP8AKVs73qnm%2FwAsyW89les7xzwtSMxMRKG%2BHghpx55w%2FwAy%2BWpbIHWNNjeGKFkDjlVw5%2BIPGwp8S%2FtcfsZGUauQHvdvpNWJxjhyyMZA%2BiXQ%2BUmMXNtcWVzJaXSGOeJisiHsRtll1uLf6rcMeKb20vVoW%2F6pP%2B0v7GHsLr5ptvq7mOPU4ElmEhHH1eCII4kp%2Bz6aO7%2FtephBNBPbytDcRtFNGaPG4oR7EZVKJHrhz%2B92JjDPA4cw3HMdR%2FSiiNG1m7028EiEC4j%2BGWMn4JU7qf8AjVslmvaBp3nLSFvbCiTKCY2P2opP24pKfs%2F9d5BTFFIys5ZGX9pKch9B65JvKmrSaVdevxea1m%2BCcEFQyj9tR%2FvxMtxZBIV8x3Oi1eknp5Ud4n6Zd%2F7XmskVxp11JY3sZikjbiyHsf8Amk%2Fs4caPay6peQadE6rJMwVXboF6k%2B%2FEDOn%2BdPIkfmSyGp6QA12qc4JFpSVPtcG9%2FwDjfI35a8qzaFNa3urKrXLmscfI1gP7NSv2n%2FmywxouIN1DWPKDW95DY6K0t5IfgnaUKgD05cl49I6H7TZ0bynBAmmjSVvhcz2qEM60%2BHnVRw9om%2BH%2FAFsItfhndg1q%2FpNdxmIuDQVU%2FECf8oYReSLk6R5nWBw1bmT6sSD8FGBb4h%2FNyXDyPvbsWITE9%2FVGJkB30jPKV1PpOsrBduRLFcPb3JY9VditW%2F2XxZ0%2B91O2spIoJWL3E54wW0Y5SOf9UdF%2Fmdvhzm%2Fm2w%2BoeapJlBVdRjWdCDsZE2cfeuHdvfQW0BngZptVvEH1u%2FlG6gj%2B5hH7Kr0xHc0pD%2BYg8wa1NDFZWjSWtsrCRoiGJcn46b1YdF2yFWemzxsW1CN4Ej%2FZlUofubOs2WpxpNGl2oMP2eSbEe5yTXGkadqFpJaT26T2860YEVBB6EHx8MeCzdpunlEtpHYQ38GqRrFNaPA8EgJcFa%2FGqhadao2XBGlmbe2uCzevbGeRo2PcNJx%2F2S%2FDht51s3sLpYLoH6ncIq21wRU1UceLt9Hxf8Hhj5f8pXerWc%2Bq2VrHqE1oqQWdvNLwUoijmJB%2FO9W4%2Fs8cjVH3LezEvM2mXWqyabNozB7SS1WJ5SwBrHWnLv8A3ZUbfy4onlazmtrKC%2BJlNoHFFNFbmQaHvtTJ9rPlzU7GOO6vrRbeNioQQMrRxmn938IXj4YE0fQLnXBdQ2062l1CP3SSry9Xav7og0%2F4LDsikps7C3s1WK2iWJB%2Bygpg5lNOX0YAVr7TL0xXsxcxsVlhktwpFPtLVWwbf%2Ban0e8tp7LTYrmyILlpyT6hoRw2%2Fu%2FTbdlw3taoK4v7a1dY2dW5GjQ1qVHj%2FwA24Vah5a03VlNxZEQTtvyX7LH%2FAClyfjyfo3nrTrbV9PRNI1i6gM8qRENC8gb05OUI%2BNR6nR1%2F4HIRqOj655Vu%2FQ1SBogT%2B7mU1ikA7pJ0%2FwBi3xZGwdizhOeOXFCRBSPSZNX8maml1MhNsfgldPijdD1H%2BS37S8s6ZLrEEZt2uEe1W6VWtLsg%2BlKGHIGOReS%2F6wOR9dWtRp11dSQ%2FWHt4Wk%2Br93IGyU%2Fysk%2Fk%2FW5INATT9Z0eAWczGT6gr%2BoIlfcgBxsf2vT%2FAGMYxMSeE2D07m7PqBmjEzjWSO3EOUo%2BY72M%2BYfLcd3FJeQRxyo7EzQU792GQ60bU9Hkf9HTAwts1jdVeNl%2FyX%2B2n%2FEc7ZL5XstVSS58p6kYZqVbTrysif6tWPrRr%2FspVzmuvaDq2jzsup2JtA5NCfjtmNf91Tjb%2FYyccJ8tmgJPqV4JJLJpI3sYLqJ2uY%2FtBgP91q6jpIy8Vw0sNNn8tG11m8KWNnqTrAums5YhTvyct%2B0R8fw%2FYb%2FWwLdRpfaVEkkPO50r1HhjJALRSg9HpX92%2FwCzgSW4TV9Hga5uppdVspFt7a0FOMNshHJm3VuZY8fV%2B0zfBlZHq3BNigOnm52LIfDHCYwMCJmVXI8IqMYnzI4f85l%2BsRQc7eSKVRPHKts4ruUkPAcv9R%2FiwbZaY1p5gtrHUoipjnjWZOo4lhRv8pGwcPL9pcROZo2S6ZkaXgyqoaFVk4hn%2BJ9uPqNxwVrmsWupvYzalF9S1K1lTi6GscsYYMpV%2FFWUNx%2FlwYoSjd8jybO0suLMcUsdmUQROxRrnH75PQtd8vaT5itBZapAJI0YPE6njJGw%2FajcfZzg%2FwCZWh3GhW%2Fr2wH1vSZ0DXlPia1m%2FuZuK7MV%2FuLj%2BbPQ1pdR3ltHcxGqyCtOtD3H0HIF%2BZFnb3k8EMqhxPBJHOh6MnIUDfSdssDq3zfqWmR6lFJqenRCO6iHK%2Fsk3FD0ng%2FmjfC3SNVudJuUvrKQJdQOrxc1DoafaSRGqrK%2FRlyU32i3XlnU4RHOYrF2Isr1hy9Jm%2F495%2F8AimT%2FACsKte0Zp%2FW1Kxg9KaL%2FAI6Nipr6bH%2Fd0X88En2lfFX0%2FwDlz5x0fzbpAlsIYrO%2BgAF%2FYxBVCOR%2FeIBTlE%2F7Lf7HJpniLyz5m1TyrqVtq2ly8ZYzuvVWX9qOQftI37WeufJnnHTfOekJqViwSZaLd2pNXhk7qf8AJP7D%2FtYFZJmzZsVdmzZsVaOct%2FNj8sx5ntJNb0NfT1uBayxrt9ajUfY%2F4zqP7tv2vsZ1PKOKvhz0CsbwMzqSSHPRgw2%2BL3%2FmwrlheB%2BD9exHQjxGeqfPH5Oad5lupdW0icabqU3xTqV5QTN%2FOyr8SOf2nX%2FgM59H%2BQfmu5nWC9ns4rbl8U6OzMB%2FkpwGKr%2Fyk%2FOBtOMPlnzTMWsyRHY6g5qYq9I5j3i%2Flf8A3XnoxWV1VkIZWAKsNwQehGc68sfkv5Q8vBZriE6rdrQ%2BpdAemCO6wj4f%2BD550VVCqFUUVRQAbAAYq3mzZsVdmzZsVf%2FV7%2FmzZsVdmzZsVdmzZsVdiNzNDbwSTXLBIUUmRj0A98WwNfPaJbSm%2BKi2pSUv9mh2pirAPON5pMWiStoiPJfM1bVI4yy1G8jDkCnwrhDrD6jFb%2BWbC7Wt36Ecl%2BCP%2BKiZHbtyJajf5WDvP9xZRaNCfKtxKb4zj04IOR403JKkfAOfH%2FWws84ajNd3EEklFuSOUtK%2FDRVXiKduQOFIeeatpk0F5PeaUj%2BlATLIYwSYhXd%2Fh%2ByleuLP9U12ykmiWO2vrWOW4n3PxgcAFXmaty3cf77%2Bx9jO1%2Flvo0EejT388SvJqDMjFxXlEvw8TX9ls5h%2BY%2FkSTyne%2FpHTVY6LdsVUj%2FdDt1hf%2Fitv91t%2FsMgRW4dxo9X4vDiySqcfon%2FO%2FoyYJsQGHfJL5FsrXUdZk0m6uTbR3UTvHQAgzRjkONSODla5GgcX0%2FUJNL1S0v41DPFINm8G%2BE%2Frym%2BHMD0k52txeLppjqPUPfF6l5X8xWlm995evZlguLSdkjR9gTWjrXovP4XTEPM6wwzKSKCT4tuoIO%2B3vke80xXU2tW13ZQ%2Bo15bq8npJ8UlELVp%2B3sMBWOsXOtWy2iEyXNuCyIw5Myf8VsaseFP7tvizL4tq%2BTzNUfvT%2BOU6npky9ZrciVAO5Xw%2Ba8sis01rp%2FmC01O5YrSWJ4kXcyFiB9nsK%2Ft5fljzLXzMlg0bRQujQESbVlDcl5IenL4kyeebPIp8y6j5e1DSEjX0XWO4QsqKYk%2FfRgV%2Fb5gx8cHMe5lGUom4mjuPnzSW%2F1OfVL6R7sJW2keKFUGygGhIJ3PKmNVy%2FTAk1pdWc9zBdrwuElcTIezA%2FEMj%2FmjXbvQtaW3tEBgCJM6PuCHFQExtizSKI1DHJv5VuxKrWMp%2BwC8JPh%2B0v0ZCNLvrfUrGG9tjWOVQfkf2lP%2BqcPdHme3vIZV24sKn2Ox%2FDJBSyzzBplrqWnSQSxLI0P76EsoIDqO3%2BsMgmlaxceUdRXU7ZS9jJRb62XoUP7QH80f2k%2F4HOgXdyVYgHp1%2BWQW8g4TzQyoQpJIVh1VuhGJCh7FG9hrempKhW4sryMMp6hlYV%2B%2FOWa%2FbXfk%2FU0ZJOEL1ayu2%2By1P91udv3if8OuCPJ%2FmhPL1xaaJetx0y4rFFMx2imJJSvhHLXj%2Fr4N%2FO9G%2FwAJ28o%2FYvY69%2BqOMgdlYzqPmWy8xr615aouoxAA3dk4dJB0%2FfRGjofBl54U%2BnCySRNSa1l%2B2vQq3861%2By65GPKcztqxjY7NEw4jYbU32zoNppF1qfrw2JX6yierHExoJivWIf5fH7DYYnZaY3pN9N5N1n9JJEbuIwyRoisY1kDbpVu3F%2FidM7xpl3Y%2BadFt7m5tle3vIUkaCYBx8Qowof5WB%2BPONG0jvLY28qlRUgqwoyOOu37LpiXl3XbjyZqk7XglmtZIfTEKNQGh5REFvsxr8XLjgIrdWQedvy7GiQPrfl4SNaxVe5s1q7Rp3ki%2FaaNf20%2B1kT07WGCqwcMjdJFNQc9C2N9BqNvHc29WiljSVGI2KyCopkFv%2FwAotHu9cOq2d1JYW0x53dhAo4O53JQt8MYf9ocMFyB23ZwGOQIkeCQGxqwfIsatNQSQrIrmOUbrIhoQfEEb5LLHzPP6X1TV4RqFo4ozFVL0%2FwApD8EmFms%2FlxeWBNxoEpuIhubSQj1B%2FqPsHwo0%2B4ltUmW7Ro5ojRo3BVhT2OWCi1t%2BbPJ1pFCfMXlJgtl%2Fx92VCyRHuwjb4kX%2Bdf2ciOlG2huhcX2n8JI2BE8FHHzMTb%2F8TVc6FpWutaySPblJEmHGeCQVVwP1ZGtcsIbe5N3p0Ui2MlWaM7mB%2FwBpQ4%2B1F%2FLkTGulhnDIYnYprf6tYaxaxxCDlxIaG7iko8Z7kGtWr%2B3ywxsdGl1WwitzGbkgkRzugDGnRm34R0%2FZbIJBZwxv9YhAVx8Qp0JPiBnafJl1eXekLNdyrKOXGNgADQD4g3Gg%2BE7Y8WyTLfiHNJbbQvNHle%2Fe50eQX%2BnXAD3Vg53EoWnKImhQmn7Pwv8AyYRX9xql1dmXWOS3XHiqMnp0QEkKq%2BA5fazrQwHqOmWmqQGC7j5D9lxsynxVsFsLeO39hb39tJbXMYlhlHGSNuhB%2FUffOZXy3%2Fl7UV05n5MgrpN1LTjLCT8VncEj%2FYf5Odt1nQbzR3JcerasfgnA2%2BT%2FAMrYB%2F5V3Y%2BdtAvhefubrn%2FuNux1jkQUYkD7Ubnir%2F6uEq8D1rTFk9XUdNgaHix%2Bu2XVoHPcD%2FfbfstnQfyAeb%2FFktC%2FptaSF1VgFNGTiXFfiA%2BLj%2FlZC9WuNd8p6kNM1e04anYsY2mY7TW%2FZG2pKjfsS4K003mkCHzl5WmDLazercWoB9WCvX1FB%2FeQt9lv8nIofYNRl5FPI3njTfO2li7taRXkQUXlp3Rj0Zf5on%2FYbJXirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F%2FW7%2FmzZsVdmzZsVdmzZsVdgHVY9PmtHi1JlW3cgEuQBUbr1wdgPUdNttTtzb3IJWoZSOoYdxirzHztP%2Bi20aTyvdm6uDcJSCMJIPTVhRWKj7Bbbi2B9b8keZrnV3FvberbsQsVwHUKFJ6tU8hSvhk%2B%2FwAPGy1GwvLOkqK5ju0cIP3YQ%2BnItAPiif8A4nkkphKQaQOk6eml6ba6fGQVt4whPif2j%2FwWO1LTbPVrGfTr%2BITWtyhSWMjqD%2FEfs4MzEVwIsg2Ni%2BVPOHlm58p65NpU1XhP72znP%2B7IWPwn%2FXX7En%2BVgW28s63f2kWr29m76Ys6xyXQpxUggNUfa4iv2uOen9c8t6J5it0t9as0uo4zyjLVDKf8l1oy5EL7TriGW30Lytp7Q6Dbs0d7xI4Oz%2FFWPlycmNvib%2BfKziufETs7WXahOn8Ph9dVIn6a7%2FewDzPd3ukNplzYtxtEhW2adVBKzRktTkalOdf%2BBw60bydZ%2BaNR%2FwARaPq0VjOIw509bcLIkw%2B16yh%2FjRm%2F3an28de6dLYTXGk6xDztpx8aHoyn7Lqfb9nI3YX%2BreQ9ThS3pcWhcvazMBWSE%2FbgaQ%2FF8P8AJlrrDvunvmryB9buFvzAtnrkQD84z%2B6mKHkGRtu%2F82HWnO%2BoadLpU5aC4ZeUL9Gil%2B0jKR0aOYZOLO80jzho0d3Zv6kMn2WFPUhlGxVh%2By6H7WQWaO80rV1hvwBMjcfUAoskTbLIP%2BNsIr5oSu10tfOt7Ikt4th5ijQC6hkSsdz6fwfWIiCCr7UmTj%2Fl5JG%2FKTSdTjg%2FxDILh4UCUgHD7P8Axafj45GPMdkYtRkvbWf6jewEXNhcr9ppT9pEVatJWnxJx%2BznRPIfmz%2FFujfW5Y%2FSvLZ%2Fq94gBCmQCvNQfiVX%2B1xb7OA2NleaX%2Fkr%2FA19Jb2rO%2BlXbc7UvuFbuvL%2Bb%2FiWC7M7HOuarpdrq9jLY3i8o5Bse6t2df8AKXOP6jbaj5dvpLK5USBd0fcB07Ov8cMT3qkP5l6trkWp2kNi8sVr6CShoyVDyH7RJHXj8OQpfNfmCN1%2BsXcsnBeCrOfUAUdFHP4uP052ny%2Fq2g6veR6T5hgX94Almsp%2Fdsw%2F3W%2F%2BU37GSHUfyp8n6hUpavZse8DkD%2FgH5p%2BGCXNeTwM%2BbzeWxtr%2B3SVHFGMZKMP8oBqr%2FwANh9qv5gpr3kVfLV9zk1O3uIjFOwrzt4w1GZhUeqn2P8rJD5i%2FJJrK2lvNKufrSx%2FEYCnCXiOtCpKPx%2F1M503lq7tpDxDNUUUGlPwyJtWQflfpMWseaFtLgssZtZnDJ1VhxCn3651e38qanp%2Bs2ZX95brKG%2BtR7UVfio6%2Fs16YS%2Fk3pGmWxvNR%2BspJqfEW7WnR4kryLFW3PqN%2FL%2FLnW8I2UsW8x%2BVo71n1LT0CXp3mjGwlp3%2F4yD%2Fhsgl9oyXUaxX1u8LoCY2dStV%2FaG%2Fgc7JTEbiztrr0zcRrIYmDxlhWjDJCXQoeVeW9Rv8Ay1PKL25kuNO9ELEo%2BIjgaoKk%2FAqJyzqtrdQXkCXNs3qQyqGjcdCCK7ZFPMPl1Yg95aLWA7zQjfjXqw%2FycB6JrNzY3BW7uAbARKgUqSQVIC8KfCq8K8vhxq9wlntK4DvtJ07UgBfW6TU6FhvTwqN8FRyRyoHiYOjCoZTUEHH5FDC9Y%2FL6wuSZ9Ic2M3X0tzEfo6p%2FscL9B%2FLy8XVJNS8y3Yuo41aKz0%2BEkQrGwoWlO3N2zomVTDZV4l5n8s3Plq94oS%2BnTk%2FVZj27%2Bk5%2FnX%2FhsMNI893Wj2ht2thNVSEp8PGSlBK%2F%2BSf286nqOnWmqWcllexiSGQbg9QezKf2WGco13yhdaVMUT95A39zL0DDwPg%2BIZc3pPlzU5dStGa4IaeNgGZaAFWHJWoMOc5v5E1B7Wf6tcAoD%2B5PLb3jP0fYzo4xIYrZIo5UaOVQ6MKMrCoIPtidrZ29lAttaoI4UrxQdBU1OL5sCsO8%2FwD5e6V5708RXNINQgB%2Bp3oFStf2JAKc4m%2F4XOIWX5R%2FmVoOpummwIySAxNMssZhZG2q3Mqen80eeoKZqYq8l8h%2Fk%2FceW9Qt9Z1DU3W4gPJLS0JCEGvwTytT1U3%2Bx6arnWs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F1%2B%2F5s2bFXZs2bFXZs2bFXZs2bFXUzZs2KuzZs2KoXUjILG49E0k4EKetK43TYkhsoQp5EqGZvEnvgp1DKVbdW2I9jhDdaDqT%2FuLLWJbOzP8AulEVnAPVVkb4hiqB8wWUXmS%2BXTYWQi3ikZphuUlqOKmn7P8ANnOtRsiPW0TWUKqD9ofajYfZkQ%2B3%2FDLnYtH0Wy0W3aC0BJc8pJHPJ2J8ThV5u8tJrdoZrdQL6EH0z0Lj%2BQ%2F8a4bTbxXQPMGq%2Fl9r7mSs9jcUN3Cv2Zo%2B1xD4Sp%2FzZnaNYhtfNmgRaho7JcMwE1pKCASP246%2Fsn%2BZf58gsugWWtaPFp1xRL21qFkFCyP3U96YR%2BUfMt7%2BX%2BqvpmqEtpUsnGeOtQjE0FxD%2FwAzUxOy%2B5kHmGCOa1s7%2BVeEsFYpHI3SvwyBvDi647RtH1by3qEXmPT5VlgnWmo2C1Amj6rIrH4RMnVcCz3FvJ5x1pIqX2h30cVy3ouv2nQEeka9XZW5ZJNPult7dbZpTLasP9HlYUalP7uVf2JlH2v%2BEw891ZzY3tvqNtHdWz84pBUHoQe6sP2WX9rC7zJoEOu2Xp7LdRVa3lPY90b%2FACHwi0IahBqtdOo9hKf9LjY0Vf8ALU%2F78ybZFD591jS3R3gnRo5Ymp4MjA9snHkr8weRi0LzJII7pQEtdQc0SYdFWQ%2Fsy%2F5X7eH%2FAJw8uLqMDX9qg%2BtRL%2B8Uf7sQf8bLnJbuyjcNFKnND1U%2Fw8Dh5pfQo3Gc487eVFhMmq2KfuHNbmNR%2Fdsf92KP5G%2Fa%2FlyPeXPM%2Fmfy2Y4pmk1PRqfBBKpNwiDr6E3%2B7OH8j51%2B1ubXU7JLiKklvcJWjDqrDdWH%2FEsV5PntoFiuUuQOFxFvHKpKsPpWnLJZ5b8w65p1xEguXuIp3q8d07P1%2FZVm%2Bx%2Fkccbr%2Bhm2125sbPggX95AZq8FRhy3p%2FL8Srk181aDHqempqtvRLu2hDngPheMDmV%2F2P2ozjsrKLW5ju4Eni%2By4rQ9Qe4OL5D%2FACp5hgu7iOwkkUXFxD6yx9DWM8HNMmGJFFDRFRQioPUHIhrmgrAXubdK2z19WOleFepH%2BRkwymCkUIqD1BxBpWIaNq09o31WURrZoiem32ePEheCIB8VV%2BLJcGB3BBHtka1LQXVne0XnC9eUQ%2B0tevHxXA2lxazHfW620rx2atW4gkSqFOPCgdvjDClVwkA7hWYZswzZFXYnNBFPG0UyCSNvtKwqDimbFWE6voE1hIbu0Be3BrQVLIP8rxXJRo92b3T4Z2%2B1Ti3zG1cGkAih3B7ZSRpGvCNQqjsooN8NquzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Dv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxViPmbyo91cfpvQiINVT%2B%2Bh6RXSD9iXwl%2F33N%2FwWQzW9AXXo%2FXaFg7L%2B%2BhK0mjdBuafzACjrnYcDTWNrPKk8kYMsZDI42NR40w3tRW3z1p2l6ho%2BotLAfXVE5OgDVljA%2BLj%2FxZGv7OS2BbeaRbscpI5YuLQq3ETIfiQcv2ZVP90%2F8AsMk3mi1FjqBnt0CpcIHIUU%2BNT8RFP5tuWRDTFEulwPaP9YjVSfVjBIFGPM%2F7BvHCOSXqXlu6trnTI1t7Y2Zj%2BGW1YhmQ%2BJcfb5fzYcZBPKl1I7GRXHKGi3AY%2FajPRvdsnQNd8BFFDjnL%2FPWhJaXguIKRw33KngklPi%2F2Pxcs6jkY892Ul3onqwoZJLWRZeAHIlT8D7fJsAVBaLpUeueS9PtriYPcWykQXkfZ4yVDD%2FJP2WwX5X1NJ2NrQIGXkoXpzQ8JP65GPJfmOTR5G0zXLmGCzlYmzhZPSeE05ceP7UTjfliOhagkWs3CW0gkUXbPZjoZVkY8kQHr8OSHUJZD540vmYtVRBIAvoTo260JrGzU7cvhzaJ5jgm0t9O1u7giu%2FRdY%2BJ48oyCg%2Fd9QyfZ%2FwArJhPBFcwvbzqHikBV0PQg9sgt%2FwCSLpJy9pxniPRiQsgH8rfzYB5ql%2Fkq1hXU4ZigNwjsizftemU3Uf5Jzp%2BRXyloF%2Fpj3FzqYiR3YraxREsyx%2FzTOaL6j%2Fyp8CZKsSUOzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Hv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVg35iPrMEFld6XbR3CRM%2Fro1fUIIHwx%2Fs4U%2FlrqOkz3c1lYQmENFJI8DCgRzIDIg5b1JavTOiajZLf2cls1KsKofBh9k5ydpJPL%2FmzT9ZcBIWY2t5sFoD8Ls5C1%2FwCDkyQ5KyjWdLGi6ml3brSyvKxuo6I58P8AJPVclOiXZurPi5rLAfTf5fsn7sE3drBqFq9vLR4pV2I7dwy%2B%2BF2j6Xf2F5dS3EqNbyhViVa8iV%2Fbfag6%2FZXBdj3KnWURXLzYFYV5n8n3us6ut7btEITGsbc6hl418B8WFd75Ym8svHqVqPrQiCsJSPiikH2jQfsNnScayhwVYBlOxB3BGG1Sby7rf6YgZ5KCZftIBSmHeFllodlp95LeWqlDMKGIfYB7kDDPE%2BSuzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F%2F0u%2F5s2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZFfM%2BipLW%2BSJZAf71CtaMPsygf8SyVZRAIIO4PbCDW6pP5YvDd6Wiu1Zbc%2Bi%2B9Tt9mv%2Bxw5xG3tbe1Vlt41jDHkwUUqfE4tgPNXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Pv%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV%2F%2FZ);background-position:center center;border:1px solid #0a0}.yui-skin-sam .yui-h-slider{background:url(bg-h.gif) no-repeat 5px 0;height:28px;width:228px}""" + + self._test(input, output) + + def test_yui_dataurl_base64_singlequotes(self): + input = """ + .yui3-skin-night .yui3-dial-ring-vml, + .yui3-skin-night .yui3-dial-center-button-vml, + .yui3-skin-night .yui3-dial-marker v\:oval.yui3-dial-marker-max-min, + .yui3-skin-night v\:oval.yui3-dial-marker-max-min, + .yui3-skin-night .yui3-dial-marker-vml, + .yui3-skin-night .yui3-dial-handle-vml { + background: none; + opacity:1; + } + + div.base64-singlequotes { + width:100px; + height:100px; + background-image:url('data:image/jpeg;base64,%2F9j%2F4AAQSkZJRgABAgEAZABkAAD%2F4RfJRXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAABAAAAagEoAAMAAAABAAIAAAExAAIAAAAeAAAAcgEyAAIAAAAUAAAAkIdpAAQAAAABAAAApAAAANAAD0JAAAAnEAAPQkAAACcQQWRvYmUgUGhvdG9zaG9wIENTMiBNYWNpbnRvc2gAMjAwODowNzoxOSAxNDo1ODowNQAAA6ABAAMAAAAB%2F%2F8AAKACAAQAAAABAAABwqADAAQAAAABAAABRQAAAAAAAAAGAQMAAwAAAAEABgAAARoABQAAAAEAAAEeARsABQAAAAEAAAEmASgAAwAAAAEAAgAAAgEABAAAAAEAAAEuAgIABAAAAAEAABaTAAAAAAAAAEgAAAABAAAASAAAAAH%2F2P%2FgABBKRklGAAECAABIAEgAAP%2FtAAxBZG9iZV9DTQAC%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQADAgICAkIDAkJDBELCgsRFQ8MDA8VGBMTFRMTGBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAENCwsNDg0QDg4QFA4ODhQUDg4ODhQRDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgAdACgAwEiAAIRAQMRAf%2FdAAQACv%2FEAT8AAAEFAQEBAQEBAAAAAAAAAAMAAQIEBQYHCAkKCwEAAQUBAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAABBAEDAgQCBQcGCAUDDDMBAAIRAwQhEjEFQVFhEyJxgTIGFJGhsUIjJBVSwWIzNHKC0UMHJZJT8OHxY3M1FqKygyZEk1RkRcKjdDYX0lXiZfKzhMPTdePzRieUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm9jdHV2d3h5ent8fX5%2FcRAAICAQIEBAMEBQYHBwYFNQEAAhEDITESBEFRYXEiEwUygZEUobFCI8FS0fAzJGLhcoKSQ1MVY3M08SUGFqKygwcmNcLSRJNUoxdkRVU2dGXi8rOEw9N14%2FNGlKSFtJXE1OT0pbXF1eX1VmZ2hpamtsbW5vYnN0dXZ3eHl6e3x%2F%2FaAAwDAQACEQMRAD8A9VSSSSUpJJJJSkkkklKSSSSUs4uDSWiXRoOJKFi3Ovx2Wvbse4e5gMwRo5soypdOyN9mTSRHpWvj4Oc4%2FwDVf9WkpupJJJKUkkkkpSSSSSlJJJJKUkkkkp%2F%2F0PVUklCqxttbbG%2FReA4fApKZpJJJKUo2WNrrdY8w1gLnHyAlVOsWvowXZDZ%2FQOZa8D9xrmm3%2FNr3PUswm%2FpmQGamyl4bHiWuhJTYqsFtTLBw9od94U1Q6FkfaOk4tszuqYfwV9JSll1%2FoOt2sOguDXgeTm7f%2FPmN%2FwCCLUWZ1VhrysbKA%2Bjua7zgtuZ%2F1FqIUXTSQcvKZi47sh7XOY0tBDYJhzms3akfR3blVd13prMtuI%2Bwse%2F6L3Ahm7n0%2FU%2Bi2zafzkFOgq9%2Bdj0ZFOM4k3ZDtrGNEnhzt7v3WbWPU8bJpyavVpduZuc2eNWONb2%2F2XtXOY2VU7rA6hnO9RuJS%2Br1hO1lguGJbuYz2extvv3fzLPU%2FwCESU9QkkkkpSSSSSlJJJJKf%2F%2FR9VWf0iwiu3Ff9LGsewT%2B6D7P%2FAyxy0FmunH6xPDMpgd%2FbZFT%2FwDoOx%2F%2B20Qp0lC02CtxqALwDtB4J8FNJBTVovx%2Bo4jvbLLGlltTuRMssrf%2FANJiz%2BhZDqMO%2FEyCX2dOc6t%2Bkuc1gmt%2B3%2FhaPTeoZ5s6R1IZrBGDlkNvI%2BjXcYYyx%2F7tWV9B%2FwDw%2FwDwlyD1LqWH0jq2N1O55rxOogY1rwC4C9uuP6mzdt9Wl1v6T%2Bb%2FAFdFS%2F1Myq%2F2VThusa%2B2tst2k6sPuY73Brm%2Fo3Vv2fy%2F3F0S4TAzq%2Bm%2FWDquKGnbhWOya62DUUlrcmyulktZ6f2ey19Wz8%2F2Ls2Z%2BFYx1jL6yxgBe%2FcIAI3CXfNIqZX3ursqqrZ6j7TqJgNY2PUsPP0dypfWJ%2Fo9NOT2xrarHTxs3tZkf%2By77VR6v9bejdNsbkb3Zbqm2sfVjgOOhrdZFljqsffXs%2FmvV9V%2F%2BDXHdZ%2BuvU8zA6i%2Bj02Y1zawaZLt2LY21tltb7XOb6j6%2FwDR1V%2FzV36v6rPVYFU%2BiCs5PS7cd43O2WUOB77d1X%2FT27lwvVnNyKK8d15q6hij207o9dg3DHdW9m9zLv8AQ%2F4Wu71K%2FTtr9aqzJ%2BuHV8tmR0zNoyLvsXU8Nr7cdtjmtN1J9K%2B2podsbk1%2Fo%2F8A0YsnG6te%2BnLZbdXXY6pttADNwucwmhv2cWbv0tnr%2FpKnfpKbKf8AtsqD1DvrjnfV70cKlldgzgbqjf7QHB3p2sG0sb6lv0%2FfZ6TEcZg630xz2vezK6jVbfhVtf6biQ%2BxltmNY7a2z9Js3Vu%2FwayvrHh1%2BhhdKv3XubjBhY9wDbHtPpOdXkOH6t1Dd%2FRsl%2F6C%2FwDomV7Fg9Wc%2FF%2Bq3RL6biben5WVjh4lljdxbexltf06La9vvp%2FM%2FwAFZ%2FhEuqn0r6lfXM9WYzpvVoo6wxvcbBcGj9Ltr09LLoj9bxf%2BvU%2Fof5mz9avrJd0%2B2jBwHAZTrcd17iA7bVZa2nY0H%2FCXw9v%2FAAdf%2FWl5zidQZ9ZcY3l3oddwg2z1mHZ6zWfQv3t2%2Bnk0%2FwCn%2FwDRf8xf6Df1DqvXGZ%2Be5tzsi7Gd6lm2kPsqNbqqOG1%2F4Cuxnpt%2FS7%2F9I9ALjR1Gh7f96%2BuJJpA1KZj2WNDmODmnhwMhJaySSSSU%2FwD%2F0vVVn9airFGYSGjEcLXuOkV%2Fzd5J%2FdZU%2FwBX%2Fra0EHM9D7Jf9pE4%2Fpv9YHX2bT6mn9RIKcE9U6lgXnJDv2l0jN%2FS497S39C530sOyxn%2BBc%2F%2Bh5P83%2F2kyfS%2FR5NlPrv16uwczExsPHFozLBRWHz6rriWM9Ouv6Hs9T3vseue6B9buoZF5xBU84BDC%2BtoZYW1PczG%2B02Gxm71Gb2XP2fod7P5mtct167q%2FT87Gdfk%2FbscWHI6V1NrQNGu3O2NZ6fp5FN9dTcrH3epR6f9RKwRYXzhKEuGYo%2FxfSMvM6lZlF%2FVfSobU11Q9MGyqLfa%2BrP9PJsr9Kz09n6bGq%2F7dWN1z9VxC3KfZT0%2B%2BPUyGg5NIaPdU7ezZe7KxPR%2FR5Gyr1sX9Db6%2FwDguZx%2Bv352be4WNwft%2B9tb2n21mza%2F07Xn2%2Fp7qmVep6dbP0vqfolqZD7mY91eDmXh9pBAZDGjT9awvb%2Bkp3W%2Fp2Mdkfpav1b%2BXUb0WulTeP23h5Di3JL3UYWTYzlzbKn1sc5rN7baM3FZ9pw8xrvSvr%2FQf0im6uvH6vY7G6hfS2mx1WFYaq7bnk%2Bo2r0rKPTa7bustxX20%2Bz%2Blf4RW%2FqPXeM403s%2FQ9LpdbQ4v97Wl4DcW1nud6PqfrH2e9nszMavNx%2Fs9%2F2hA6i5r77HZLH24Tn1W4%2BUwj2Pva3Jdj5bfzaHb8n7Fk%2F4P9YxP0tX8wulqaWfb1CzZ6GaM30622WDD%2FSV1mp3sfY6tjWOsY1tf8776a%2FTQ7s27qFNVIrqwAfUsbZRXt9S0N9Ky1tX83%2Bgre%2F1sej%2FAE1lzKltdDtbi9ZJsqqbTaBXdS0TbW68PycUxNj8qrIbR%2FpPU%2FSb6%2FTyv6VWf0qvOrzmsYasnGzHmk7gx1Za6K2XR7sd%2B5v6K79JXi3bPU%2FV8i71RRU5%2FWaLMv6kYj7SGX9L6g%2BqK%2Fosrymeux1cf4B17fYsrpuPmNxftz63Pcwl9DWDQ2sIqZkNJ%2FNZe6luR6f%2FAAX%2FAAS1f2pXX0vq%2BJln7LfkVVta3ZAfl4929myn%2FA76rLq86r%2Bbo%2F4qyhVruoZfSek4ldmM12Pe227D9XQBtvptu3%2B7d6Vr66Xf2PU%2FwiSnsuqnDyLMfFtMZORj%2BrXSPeHFseo2l7g31La%2FV%2Bh%2F2oq%2FwfqfpFznVcd9uDZivaxzDZXa1zztZaXB1LW%2Bv%2Fgb%2FZVXi5tv83%2FRsj9AsLqfXv2j0%2FpXqPFedgNurfa10F3uqsx8lv8Ao3Obvrf%2FAMT6n%2BFQquq59%2FURec1zb7CHXMc8htmw7n47v8BtucPUb6v6P9Ld69iNoS9H6Vk0Zzcpj3tqqeW1AAtuc%2F8Am7Meyhv6WvZ%2FNZX%2FAIEugzuk14eLiN69fbi49u6wgBjyWNb6dP6IOa52V6vt247P0dD%2FAFP0ajjdc6kBeekYQd1DIs2tsZVZbdVXt9tYqsG9%2B3%2FTPZ%2Bl%2FwBCsajpXV%2BtX2Zwo%2Fal7Wuvv9S0Mea2%2FT2%2BvZ7m1e1j6GV76PZ%2FpK0NE7avpH1Z69ndU6Ti4mP6mVZktBdbkP3uqbWTRkjKuYGb9t1e6v2776rP9Iuyw8VuJjtoaS8tkueeXOcdz3%2F2nLyb6vdZv6DjDKwmVt3lzbsZz3VsZcHluTgX499hfRYxuzJwn%2FznsyKrLLv8H2vT%2Fr9hODK%2BsM%2FZuSWkvrtFjC1wLmvr%2FWKqW%2Fm%2BzbZ%2BkRR4vVpKn03q%2FTeq47Mnp%2BQy%2Bt7WvG0%2B4Bw3N9Ss%2FpK%2F%2BuNVxBT%2FAP%2FT9VTOa17SxwDmuBDmnUEHsU6SSnxf6x9CzPqt1ECix7GMeben5Q0caneyylzvouto3MruZ%2Fhav0np%2FpFUryaMjFvrzps6dkPbZmUsHvx7zFber4f7rbLP6Wz%2FAEln%2BivYvY%2Bu9L6f1Xpl2H1GG0OG71SQ01uH0b2Pd9B7P%2FUb%2FwBGvH8vpF3T7%2FQpcH5NIea7az6leXSd2%2BzHjdW70qHbMnE%2FwlSZrE2NuzfjKHMY%2BGfpzR2n%2FL9H95wM%2FByOk5hxsgiyuwb6rm613VH6N1X%2FAKMrW10jqbrmHDyHl8MhjY3G6sHd6PO318dv6eiz07fW9P0v%2BOHiPxc6n9j552Ydh3YmVy7FtP5%2Bvvfiu%2Fw%2F%2FB%2Fpf5zehYv1a6pVk5WLnh%2BJZhML6rmiWvsBY6t1Fn0n1NY77Tvp%2FSf8WnijqNWnOEscjGQ4ZB7Oig0fV7Lf01rr8rqR2tez3XPbBx67XsYzayxjbMl9m38%2F9J%2Bl%2FS%2BpzrvUxMWqvqNVuJksx3402zW5oosbdScig725OHd9qY6v%2Bc2ZOLT6Nn88y3e6X1uyk9NexznYd%2BPdi2Mc1lTxl4ztznWu3bKfWZZXbs9T06f0ypdY6J1Hr%2FVH9Wrtpsx6mMqFDS%2F1WisF30XNa5%2B577bf0eyz%2FRV%2BqnHwW1RotDF6xay49Tviks9OHNBAc9g2sfc1m7Zu2%2FoG%2FQxv3PST9Lw2dTblM%2B1W1sdDixoM2vu3f0936Ot3pOZsurp%2FQ5H%2Bj%2FwaJj4GXf052ZjepRjbmOrywNzNjHulh9Meg5zN%2B1lrn%2F4R9fp%2FpFYxcHD6ey6nItdSGAOtdlFwhlh%2FRuHpN3Ppbv8A5xnqemgFOZ1%2FDyMW77T1DIOQy4vra91bqC00en%2BiYx%2F06Nrqnev%2FAIf%2FAEj1WxaG2sxKOo9Mu6iK32UYeOLHML3vLrNrrmu3%2Blv%2FAPRi1cvpl1PUqW3Nfh5VJ34%2BNmO9XEurHu%2FV7t1jGMsYf0noP9n%2FAANqv9Q6W84w630hn2cuFd1jcYGysjS6qzLxmiu3Fyadv896NX%2Bl%2B2eqhSSBQ1u%2Fwed6z9X%2BrtFJH1dZj1agENsqZuIB9EPsvbkW7Wj6eS%2F6fq%2Bj%2BjWOcbNw8hnqdNxq3tizZYXOa4Ty7fkWbmNd%2FObfof4RdpT1fqHUs1rc%2FLfktyRU2vGa4bSHNePtFL3el6VmLlVejZ7f0m%2F1cj%2BZ%2B0WbvSfqn0%2Fq1eTjdRIdlVspsqcxwBqc8PbfW%2BrX376%2FTtf%2FAKH0bcS6v2WJXqulj4YCXEDZqurxn1dxXdRosuw78hnXcR5dSWu22OrY2bqsdn03ZFTt932Oz2ZWL6lVLPWoXS19Hzup9Pq%2BsfTMYty8kn7ZRXDCy5hdVb1DEre%2F9NU79LvxHbLclj7Kf5rIspXE5mPm9J6q%2FLqa82YrmvfVJHqUsd%2BgyqLatln0a%2F03pP8AWxLmerV%2B5j%2Bz%2FU7qvR%2Bp9Cot6QNlLJbZQ5xdZXb9O1l73S99jnu3%2Bq7%2Be%2FnEmN57qv1X6b9c%2Fq99oxWNx%2BuYxcyx7g9rjc2PXxM5t36x6djmt9H1v02L%2Bi9L%2FCVWcz9VPr51npPWX9H%2Bsxuy8bItFNoyDvtot9tIcN%2F06He316P%2FAEKo%2FwAJ9p9hbXW17ntaA98b3AQXQIbuP5y43%2FGB9Q2dfp%2FaXTWtZ1iloEGGjIY3%2FAPe7%2BayG%2F8AafJ%2F6zb%2Bi%2FSUJT2TK66xtraGAAABoAEAbW8fyVJecfUbqP8AjHPV31dXxMjJ6c%2Fay27LaMd1JaNgsoDxX9o9rf0zaW2%2Br%2FPep%2FpPR0lP%2F9T1VJJJJSDNL%2Fsr9geXGABW1rnakD6F36P%2BsuAwumV%2FWXNrxHvcymqh2S7IqLnRa%2B7Jfj2UPcGtqdvyfVspZ7K%2FQ%2By1%2FoaV6Ffj05FZqvYLKyQS13Eg7m%2F9JKimvHprorEV1NDGDwDRtakkEg2NCNnxDqdGb0Lru3qFDHX41rLTWGxVeyR7626tdVmNa%2F1P3LPWr9i2cmzrpY5tdB6gzpL7Kq7WGXPqqf6D8HIc1gZ9upo%2F7T%2FzmVX%2FADXqr0brOF0%2B4135bWVFgc05kD1WMP06ari17q25P81Z%2B%2FX%2Bi%2BnauTysfqnQeoM6h06l91eW2H4jwf1uhgc70Lq3DdV1XGxm78d7%2FwCfo%2FQXfpK7GIRFX4s2fN7vAeGpRjwk93IyOj5VDnN6U5%2FUMa%2FflYlLGuLm%2BqxnpfadrG1tdk302Ufav%2Bs2en%2FNrQ%2Brd9%2BNlNdfS%2FDuDXMy8e7R1bmjfJ%2FkNhl9Nv59KtOeLbMDO6FktJte%2B%2FCstLh%2Bhcaxm4mY5oc5tLMr08XK3st9H7TRd%2FSqPWW11fA%2Fa%2BOOpdOc6jqeMNllYjcdvudjWt9zfVr3b8d%2F%2Fom%2F1E%2B2F5jpf1u6Dj9RyLKcqu3peaf1rF1aGh%2Bhym0XMr91TfZksr%2Fncb%2FSWY9bFTwftfUOpdQwRZvxsDLOLh2uBexrLH21YzLLAHv9K3ZX%2FwAH%2FOLUw%2BnfU%2F6xVDB6lg4%2FTutEFrLaG%2BgLXD%2FC42zY2x%2F%2Blw7f01f8ur9On%2BroyPqllZfThhMsqBZblWhxFjahFLLWE%2B27HZv3sbsr9H9N63%2FBjW1L9J9PDa%2F6udcx%2FU6Ta7bSH6uxLT7W0Oc0%2Fo6HT6mFl1fzX%2BnV9n1R6v07bbhZv2za4w149K0Vk6enkMeWPu2fzn8xVb%2Fwf0F1GVh4mdSW3MD2vbAfA3AH9xyoYmRZ0o%2FY894%2BztBNGS7QbR%2Ba8n6Oz%2FwH%2FttLyRbyPUcXMqzznY2Nfh9TrbZbkkVyxzQBvzm%2Bm04vr7f0d%2Fo2frX%2BD%2FTV2rc%2BrmH0bqHT8fcW%2Fb2Ned1FrmWsaXOZLfRLH01W%2Bpu9L%2BaXUiCJHBWGegM6d1IdU6WzbJi%2FFbAaWuPvdV%2FV%2FnPTStN6U4%2FWfqPk5jhRj2BorDrMXLOjqnge2m1rNrnV3e1lnpf8b%2FO1Urm%2FqLlZH1a%2BseT03IwLa7s97K8nFrY%2Bx9fp%2Bp6OfjupZ6VvT3%2BrY3I%2F0P6Oyn9F%2BjXrKSCFJJJJKUkkkkp%2F%2F9X1VJJJJSkkkklKQM3Dpzcd2PbuAdBa9hLXscNWWVPb9CxjkdJJTxHVfq4Ol5bc%2Bq55dlvLLq2Dax1lprY7IFbP5i63b77Kf9H%2Bk%2FQrpPq%2FcLMAVlgrupcWXM4duBj9I0y7f%2F1f00vrB0hnVunPo%2BjkMmzFtGjq7QC1tlb9Njvd%2FqxS6dWMj0upH9FfbXsyqwIabGwx30%2Ff%2BifW9n9RHopwvrBh4eF1V%2BXkU%2FqmZQQLSB6deYxwux7HAfpGvs9L%2FB%2F8J6ivWVZ2dj4vV8FgfbkY%2Fo5WO8hosrdr3%2Bi6t7nrefWx8b2h20hzZEwR9Fw%2FlIOLg4%2BI652OCxt7vUfWD7Q4%2FScxv5m%2F89K1IujYj8PpWLjWDa%2BusB7SZIcfc5u6XfnFW31seIe0OEzBE6%2FNSSQUpJJJJSkkkklKSSSSUpJJJJT%2FAP%2FW9VSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2F%2FZ%2F%2B01IlBob3Rvc2hvcCAzLjAAOEJJTQQlAAAAAAAQAAAAAAAAAAAAAAAAAAAAADhCSU0D6gAAAAAYEDw%2FeG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04Ij8%2BCjwhRE9DVFlQRSBwbGlzdCBQVUJMSUMgIi0vL0FwcGxlLy9EVEQgUExJU1QgMS4wLy9FTiIgImh0dHA6Ly93d3cuYXBwbGUuY29tL0RURHMvUHJvcGVydHlMaXN0LTEuMC5kdGQiPgo8cGxpc3QgdmVyc2lvbj0iMS4wIj4KPGRpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNSG9yaXpvbnRhbFJlczwva2V5PgoJPGRpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJPGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1Ib3Jpem9udGFsUmVzPC9rZXk%2BCgkJCQk8cmVhbD43MjwvcmVhbD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTU9yaWVudGF0aW9uPC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk%2BCgkJCTxkaWN0PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTU9yaWVudGF0aW9uPC9rZXk%2BCgkJCQk8aW50ZWdlcj4xPC9pbnRlZ2VyPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNU2NhbGluZzwva2V5PgoJPGRpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJPGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1TY2FsaW5nPC9rZXk%2BCgkJCQk8cmVhbD4xPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxSZXM8L2tleT4KCTxkaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk%2BCgkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCTxhcnJheT4KCQkJPGRpY3Q%2BCgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxSZXM8L2tleT4KCQkJCTxyZWFsPjcyPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxTY2FsaW5nPC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk%2BCgkJCTxkaWN0PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTVZlcnRpY2FsU2NhbGluZzwva2V5PgoJCQkJPHJlYWw%2BMTwvcmVhbD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuc3ViVGlja2V0LnBhcGVyX2luZm9fdGlja2V0PC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PlBNUFBEUGFwZXJDb2RlTmFtZTwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PlBNUFBEUGFwZXJDb2RlTmFtZTwva2V5PgoJCQkJCTxzdHJpbmc%2BTGV0dGVyPC9zdHJpbmc%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BUE1UaW9nYVBhcGVyTmFtZTwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PlBNVGlvZ2FQYXBlck5hbWU8L2tleT4KCQkJCQk8c3RyaW5nPm5hLWxldHRlcjwvc3RyaW5nPgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BMC4wPC9yZWFsPgoJCQkJCQk8cmVhbD4wLjA8L3JlYWw%2BCgkJCQkJCTxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTc2PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1BZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQkJCQk8YXJyYXk%2BCgkJCQkJCTxyZWFsPi0xODwvcmVhbD4KCQkJCQkJPHJlYWw%2BLTE4PC9yZWFsPgoJCQkJCQk8cmVhbD43NzQ8L3JlYWw%2BCgkJCQkJCTxyZWFsPjU5NDwvcmVhbD4KCQkJCQk8L2FycmF5PgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYXBlckluZm8uUE1QYXBlck5hbWU8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNUGFwZXJOYW1lPC9rZXk%2BCgkJCQkJPHN0cmluZz5uYS1sZXR0ZXI8L3N0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVGbGFnPC9rZXk%2BCgkJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJCTwvZGljdD4KCQkJPC9hcnJheT4KCQk8L2RpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNVW5hZGp1c3RlZFBhZ2VSZWN0PC9rZXk%2BCgkJPGRpY3Q%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk%2BCgkJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BMC4wPC9yZWFsPgoJCQkJCQk8cmVhbD4wLjA8L3JlYWw%2BCgkJCQkJCTxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTc2PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNVW5hZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BLTE4PC9yZWFsPgoJCQkJCQk8cmVhbD4tMTg8L3JlYWw%2BCgkJCQkJCTxyZWFsPjc3NDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTk0PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5wcGQuUE1QYXBlck5hbWU8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLnBwZC5QTVBhcGVyTmFtZTwva2V5PgoJCQkJCTxzdHJpbmc%2BVVMgTGV0dGVyPC9zdHJpbmc%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5BUElWZXJzaW9uPC9rZXk%2BCgkJPHN0cmluZz4wMC4yMDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC50eXBlPC9rZXk%2BCgkJPHN0cmluZz5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvVGlja2V0PC9zdHJpbmc%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuQVBJVmVyc2lvbjwva2V5PgoJPHN0cmluZz4wMC4yMDwvc3RyaW5nPgoJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnR5cGU8L2tleT4KCTxzdHJpbmc%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXRUaWNrZXQ8L3N0cmluZz4KPC9kaWN0Pgo8L3BsaXN0Pgo4QklNA%2BkAAAAAAHgAAwAAAEgASAAAAAAC3gJA%2F%2B7%2F7gMGAlIDZwUoA%2FwAAgAAAEgASAAAAAAC2AIoAAEAAABkAAAAAQADAwMAAAABf%2F8AAQABAAAAAAAAAAAAAAAAaAgAGQGQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4QklNA%2B0AAAAAABAAZAAAAAEAAQBkAAAAAQABOEJJTQQmAAAAAAAOAAAAAAAAAAAAAD%2BAAAA4QklNBA0AAAAAAAQAAAAeOEJJTQQZAAAAAAAEAAAAHjhCSU0D8wAAAAAACQAAAAAAAAAAAQA4QklNBAoAAAAAAAEAADhCSU0nEAAAAAAACgABAAAAAAAAAAE4QklNA%2FUAAAAAAEgAL2ZmAAEAbGZmAAYAAAAAAAEAL2ZmAAEAoZmaAAYAAAAAAAEAMgAAAAEAWgAAAAYAAAAAAAEANQAAAAEALQAAAAYAAAAAAAE4QklNA%2FgAAAAAAHAAAP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8D6AAAAAD%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FA%2BgAAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwPoAAAAAP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8D6AAAOEJJTQQIAAAAAAAQAAAAAQAAAkAAAAJAAAAAADhCSU0EHgAAAAAABAAAAAA4QklNBBoAAAAAA1UAAAAGAAAAAAAAAAAAAAFFAAABwgAAABAAcwB3AGkAcwBzAF8AYQByAG0AeQBfAGsAbgBpAGYAZQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABwgAAAUUAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAQAAAAAAAG51bGwAAAACAAAABmJvdW5kc09iamMAAAABAAAAAAAAUmN0MQAAAAQAAAAAVG9wIGxvbmcAAAAAAAAAAExlZnRsb25nAAAAAAAAAABCdG9tbG9uZwAAAUUAAAAAUmdodGxvbmcAAAHCAAAABnNsaWNlc1ZsTHMAAAABT2JqYwAAAAEAAAAAAAVzbGljZQAAABIAAAAHc2xpY2VJRGxvbmcAAAAAAAAAB2dyb3VwSURsb25nAAAAAAAAAAZvcmlnaW5lbnVtAAAADEVTbGljZU9yaWdpbgAAAA1hdXRvR2VuZXJhdGVkAAAAAFR5cGVlbnVtAAAACkVTbGljZVR5cGUAAAAASW1nIAAAAAZib3VuZHNPYmpjAAAAAQAAAAAAAFJjdDEAAAAEAAAAAFRvcCBsb25nAAAAAAAAAABMZWZ0bG9uZwAAAAAAAAAAQnRvbWxvbmcAAAFFAAAAAFJnaHRsb25nAAABwgAAAAN1cmxURVhUAAAAAQAAAAAAAG51bGxURVhUAAAAAQAAAAAAAE1zZ2VURVhUAAAAAQAAAAAABmFsdFRhZ1RFWFQAAAABAAAAAAAOY2VsbFRleHRJc0hUTUxib29sAQAAAAhjZWxsVGV4dFRFWFQAAAABAAAAAAAJaG9yekFsaWduZW51bQAAAA9FU2xpY2VIb3J6QWxpZ24AAAAHZGVmYXVsdAAAAAl2ZXJ0QWxpZ25lbnVtAAAAD0VTbGljZVZlcnRBbGlnbgAAAAdkZWZhdWx0AAAAC2JnQ29sb3JUeXBlZW51bQAAABFFU2xpY2VCR0NvbG9yVHlwZQAAAABOb25lAAAACXRvcE91dHNldGxvbmcAAAAAAAAACmxlZnRPdXRzZXRsb25nAAAAAAAAAAxib3R0b21PdXRzZXRsb25nAAAAAAAAAAtyaWdodE91dHNldGxvbmcAAAAAADhCSU0EKAAAAAAADAAAAAE%2F8AAAAAAAADhCSU0EEQAAAAAAAQEAOEJJTQQUAAAAAAAEAAAAAThCSU0EDAAAAAAWrwAAAAEAAACgAAAAdAAAAeAAANmAAAAWkwAYAAH%2F2P%2FgABBKRklGAAECAABIAEgAAP%2FtAAxBZG9iZV9DTQAC%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQADAgICAkIDAkJDBELCgsRFQ8MDA8VGBMTFRMTGBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAENCwsNDg0QDg4QFA4ODhQUDg4ODhQRDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgAdACgAwEiAAIRAQMRAf%2FdAAQACv%2FEAT8AAAEFAQEBAQEBAAAAAAAAAAMAAQIEBQYHCAkKCwEAAQUBAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAABBAEDAgQCBQcGCAUDDDMBAAIRAwQhEjEFQVFhEyJxgTIGFJGhsUIjJBVSwWIzNHKC0UMHJZJT8OHxY3M1FqKygyZEk1RkRcKjdDYX0lXiZfKzhMPTdePzRieUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm9jdHV2d3h5ent8fX5%2FcRAAICAQIEBAMEBQYHBwYFNQEAAhEDITESBEFRYXEiEwUygZEUobFCI8FS0fAzJGLhcoKSQ1MVY3M08SUGFqKygwcmNcLSRJNUoxdkRVU2dGXi8rOEw9N14%2FNGlKSFtJXE1OT0pbXF1eX1VmZ2hpamtsbW5vYnN0dXZ3eHl6e3x%2F%2FaAAwDAQACEQMRAD8A9VSSSSUpJJJJSkkkklKSSSSUs4uDSWiXRoOJKFi3Ovx2Wvbse4e5gMwRo5soypdOyN9mTSRHpWvj4Oc4%2FwDVf9WkpupJJJKUkkkkpSSSSSlJJJJKUkkkkp%2F%2F0PVUklCqxttbbG%2FReA4fApKZpJJJKUo2WNrrdY8w1gLnHyAlVOsWvowXZDZ%2FQOZa8D9xrmm3%2FNr3PUswm%2FpmQGamyl4bHiWuhJTYqsFtTLBw9od94U1Q6FkfaOk4tszuqYfwV9JSll1%2FoOt2sOguDXgeTm7f%2FPmN%2FwCCLUWZ1VhrysbKA%2Bjua7zgtuZ%2F1FqIUXTSQcvKZi47sh7XOY0tBDYJhzms3akfR3blVd13prMtuI%2Bwse%2F6L3Ahm7n0%2FU%2Bi2zafzkFOgq9%2Bdj0ZFOM4k3ZDtrGNEnhzt7v3WbWPU8bJpyavVpduZuc2eNWONb2%2F2XtXOY2VU7rA6hnO9RuJS%2Br1hO1lguGJbuYz2extvv3fzLPU%2FwCESU9QkkkkpSSSSSlJJJJKf%2F%2FR9VWf0iwiu3Ff9LGsewT%2B6D7P%2FAyxy0FmunH6xPDMpgd%2FbZFT%2FwDoOx%2F%2B20Qp0lC02CtxqALwDtB4J8FNJBTVovx%2Bo4jvbLLGlltTuRMssrf%2FANJiz%2BhZDqMO%2FEyCX2dOc6t%2Bkuc1gmt%2B3%2FhaPTeoZ5s6R1IZrBGDlkNvI%2BjXcYYyx%2F7tWV9B%2FwDw%2FwDwlyD1LqWH0jq2N1O55rxOogY1rwC4C9uuP6mzdt9Wl1v6T%2Bb%2FAFdFS%2F1Myq%2F2VThusa%2B2tst2k6sPuY73Brm%2Fo3Vv2fy%2F3F0S4TAzq%2Bm%2FWDquKGnbhWOya62DUUlrcmyulktZ6f2ey19Wz8%2F2Ls2Z%2BFYx1jL6yxgBe%2FcIAI3CXfNIqZX3ursqqrZ6j7TqJgNY2PUsPP0dypfWJ%2Fo9NOT2xrarHTxs3tZkf%2By77VR6v9bejdNsbkb3Zbqm2sfVjgOOhrdZFljqsffXs%2FmvV9V%2F%2BDXHdZ%2BuvU8zA6i%2Bj02Y1zawaZLt2LY21tltb7XOb6j6%2FwDR1V%2FzV36v6rPVYFU%2BiCs5PS7cd43O2WUOB77d1X%2FT27lwvVnNyKK8d15q6hij207o9dg3DHdW9m9zLv8AQ%2F4Wu71K%2FTtr9aqzJ%2BuHV8tmR0zNoyLvsXU8Nr7cdtjmtN1J9K%2B2podsbk1%2Fo%2F8A0YsnG6te%2BnLZbdXXY6pttADNwucwmhv2cWbv0tnr%2FpKnfpKbKf8AtsqD1DvrjnfV70cKlldgzgbqjf7QHB3p2sG0sb6lv0%2FfZ6TEcZg630xz2vezK6jVbfhVtf6biQ%2BxltmNY7a2z9Js3Vu%2FwayvrHh1%2BhhdKv3XubjBhY9wDbHtPpOdXkOH6t1Dd%2FRsl%2F6C%2FwDomV7Fg9Wc%2FF%2Bq3RL6biben5WVjh4lljdxbexltf06La9vvp%2FM%2FwAFZ%2FhEuqn0r6lfXM9WYzpvVoo6wxvcbBcGj9Ltr09LLoj9bxf%2BvU%2Fof5mz9avrJd0%2B2jBwHAZTrcd17iA7bVZa2nY0H%2FCXw9v%2FAAdf%2FWl5zidQZ9ZcY3l3oddwg2z1mHZ6zWfQv3t2%2Bnk0%2FwCn%2FwDRf8xf6Df1DqvXGZ%2Be5tzsi7Gd6lm2kPsqNbqqOG1%2F4Cuxnpt%2FS7%2F9I9ALjR1Gh7f96%2BuJJpA1KZj2WNDmODmnhwMhJaySSSSU%2FwD%2F0vVVn9airFGYSGjEcLXuOkV%2Fzd5J%2FdZU%2FwBX%2Fra0EHM9D7Jf9pE4%2Fpv9YHX2bT6mn9RIKcE9U6lgXnJDv2l0jN%2FS497S39C530sOyxn%2BBc%2F%2Bh5P83%2F2kyfS%2FR5NlPrv16uwczExsPHFozLBRWHz6rriWM9Ouv6Hs9T3vseue6B9buoZF5xBU84BDC%2BtoZYW1PczG%2B02Gxm71Gb2XP2fod7P5mtct167q%2FT87Gdfk%2FbscWHI6V1NrQNGu3O2NZ6fp5FN9dTcrH3epR6f9RKwRYXzhKEuGYo%2FxfSMvM6lZlF%2FVfSobU11Q9MGyqLfa%2BrP9PJsr9Kz09n6bGq%2F7dWN1z9VxC3KfZT0%2B%2BPUyGg5NIaPdU7ezZe7KxPR%2FR5Gyr1sX9Db6%2FwDguZx%2Bv352be4WNwft%2B9tb2n21mza%2F07Xn2%2Fp7qmVep6dbP0vqfolqZD7mY91eDmXh9pBAZDGjT9awvb%2Bkp3W%2Fp2Mdkfpav1b%2BXUb0WulTeP23h5Di3JL3UYWTYzlzbKn1sc5rN7baM3FZ9pw8xrvSvr%2FQf0im6uvH6vY7G6hfS2mx1WFYaq7bnk%2Bo2r0rKPTa7bustxX20%2Bz%2Blf4RW%2FqPXeM403s%2FQ9LpdbQ4v97Wl4DcW1nud6PqfrH2e9nszMavNx%2Fs9%2F2hA6i5r77HZLH24Tn1W4%2BUwj2Pva3Jdj5bfzaHb8n7Fk%2F4P9YxP0tX8wulqaWfb1CzZ6GaM30622WDD%2FSV1mp3sfY6tjWOsY1tf8776a%2FTQ7s27qFNVIrqwAfUsbZRXt9S0N9Ky1tX83%2Bgre%2F1sej%2FAE1lzKltdDtbi9ZJsqqbTaBXdS0TbW68PycUxNj8qrIbR%2FpPU%2FSb6%2FTyv6VWf0qvOrzmsYasnGzHmk7gx1Za6K2XR7sd%2B5v6K79JXi3bPU%2FV8i71RRU5%2FWaLMv6kYj7SGX9L6g%2BqK%2Fosrymeux1cf4B17fYsrpuPmNxftz63Pcwl9DWDQ2sIqZkNJ%2FNZe6luR6f%2FAAX%2FAAS1f2pXX0vq%2BJln7LfkVVta3ZAfl4929myn%2FA76rLq86r%2Bbo%2F4qyhVruoZfSek4ldmM12Pe227D9XQBtvptu3%2B7d6Vr66Xf2PU%2FwiSnsuqnDyLMfFtMZORj%2BrXSPeHFseo2l7g31La%2FV%2Bh%2F2oq%2FwfqfpFznVcd9uDZivaxzDZXa1zztZaXB1LW%2Bv%2Fgb%2FZVXi5tv83%2FRsj9AsLqfXv2j0%2FpXqPFedgNurfa10F3uqsx8lv8Ao3Obvrf%2FAMT6n%2BFQquq59%2FURec1zb7CHXMc8htmw7n47v8BtucPUb6v6P9Ld69iNoS9H6Vk0Zzcpj3tqqeW1AAtuc%2F8Am7Meyhv6WvZ%2FNZX%2FAIEugzuk14eLiN69fbi49u6wgBjyWNb6dP6IOa52V6vt247P0dD%2FAFP0ajjdc6kBeekYQd1DIs2tsZVZbdVXt9tYqsG9%2B3%2FTPZ%2Bl%2FwBCsajpXV%2BtX2Zwo%2Fal7Wuvv9S0Mea2%2FT2%2BvZ7m1e1j6GV76PZ%2FpK0NE7avpH1Z69ndU6Ti4mP6mVZktBdbkP3uqbWTRkjKuYGb9t1e6v2776rP9Iuyw8VuJjtoaS8tkueeXOcdz3%2F2nLyb6vdZv6DjDKwmVt3lzbsZz3VsZcHluTgX499hfRYxuzJwn%2FznsyKrLLv8H2vT%2Fr9hODK%2BsM%2FZuSWkvrtFjC1wLmvr%2FWKqW%2Fm%2BzbZ%2BkRR4vVpKn03q%2FTeq47Mnp%2BQy%2Bt7WvG0%2B4Bw3N9Ss%2FpK%2F%2BuNVxBT%2FAP%2FT9VTOa17SxwDmuBDmnUEHsU6SSnxf6x9CzPqt1ECix7GMeben5Q0caneyylzvouto3MruZ%2Fhav0np%2FpFUryaMjFvrzps6dkPbZmUsHvx7zFber4f7rbLP6Wz%2FAEln%2BivYvY%2Bu9L6f1Xpl2H1GG0OG71SQ01uH0b2Pd9B7P%2FUb%2FwBGvH8vpF3T7%2FQpcH5NIea7az6leXSd2%2BzHjdW70qHbMnE%2FwlSZrE2NuzfjKHMY%2BGfpzR2n%2FL9H95wM%2FByOk5hxsgiyuwb6rm613VH6N1X%2FAKMrW10jqbrmHDyHl8MhjY3G6sHd6PO318dv6eiz07fW9P0v%2BOHiPxc6n9j552Ydh3YmVy7FtP5%2Bvvfiu%2Fw%2F%2FB%2Fpf5zehYv1a6pVk5WLnh%2BJZhML6rmiWvsBY6t1Fn0n1NY77Tvp%2FSf8WnijqNWnOEscjGQ4ZB7Oig0fV7Lf01rr8rqR2tez3XPbBx67XsYzayxjbMl9m38%2F9J%2Bl%2FS%2BpzrvUxMWqvqNVuJksx3402zW5oosbdScig725OHd9qY6v%2Bc2ZOLT6Nn88y3e6X1uyk9NexznYd%2BPdi2Mc1lTxl4ztznWu3bKfWZZXbs9T06f0ypdY6J1Hr%2FVH9Wrtpsx6mMqFDS%2F1WisF30XNa5%2B577bf0eyz%2FRV%2BqnHwW1RotDF6xay49Tviks9OHNBAc9g2sfc1m7Zu2%2FoG%2FQxv3PST9Lw2dTblM%2B1W1sdDixoM2vu3f0936Ot3pOZsurp%2FQ5H%2Bj%2FwaJj4GXf052ZjepRjbmOrywNzNjHulh9Meg5zN%2B1lrn%2F4R9fp%2FpFYxcHD6ey6nItdSGAOtdlFwhlh%2FRuHpN3Ppbv8A5xnqemgFOZ1%2FDyMW77T1DIOQy4vra91bqC00en%2BiYx%2F06Nrqnev%2FAIf%2FAEj1WxaG2sxKOo9Mu6iK32UYeOLHML3vLrNrrmu3%2Blv%2FAPRi1cvpl1PUqW3Nfh5VJ34%2BNmO9XEurHu%2FV7t1jGMsYf0noP9n%2FAANqv9Q6W84w630hn2cuFd1jcYGysjS6qzLxmiu3Fyadv896NX%2Bl%2B2eqhSSBQ1u%2Fwed6z9X%2BrtFJH1dZj1agENsqZuIB9EPsvbkW7Wj6eS%2F6fq%2Bj%2BjWOcbNw8hnqdNxq3tizZYXOa4Ty7fkWbmNd%2FObfof4RdpT1fqHUs1rc%2FLfktyRU2vGa4bSHNePtFL3el6VmLlVejZ7f0m%2F1cj%2BZ%2B0WbvSfqn0%2Fq1eTjdRIdlVspsqcxwBqc8PbfW%2BrX376%2FTtf%2FAKH0bcS6v2WJXqulj4YCXEDZqurxn1dxXdRosuw78hnXcR5dSWu22OrY2bqsdn03ZFTt932Oz2ZWL6lVLPWoXS19Hzup9Pq%2BsfTMYty8kn7ZRXDCy5hdVb1DEre%2F9NU79LvxHbLclj7Kf5rIspXE5mPm9J6q%2FLqa82YrmvfVJHqUsd%2BgyqLatln0a%2F03pP8AWxLmerV%2B5j%2Bz%2FU7qvR%2Bp9Cot6QNlLJbZQ5xdZXb9O1l73S99jnu3%2Bq7%2Be%2FnEmN57qv1X6b9c%2Fq99oxWNx%2BuYxcyx7g9rjc2PXxM5t36x6djmt9H1v02L%2Bi9L%2FCVWcz9VPr51npPWX9H%2Bsxuy8bItFNoyDvtot9tIcN%2F06He316P%2FAEKo%2FwAJ9p9hbXW17ntaA98b3AQXQIbuP5y43%2FGB9Q2dfp%2FaXTWtZ1iloEGGjIY3%2FAPe7%2BayG%2F8AafJ%2F6zb%2Bi%2FSUJT2TK66xtraGAAABoAEAbW8fyVJecfUbqP8AjHPV31dXxMjJ6c%2Fay27LaMd1JaNgsoDxX9o9rf0zaW2%2Br%2FPep%2FpPR0lP%2F9T1VJJJJSDNL%2Fsr9geXGABW1rnakD6F36P%2BsuAwumV%2FWXNrxHvcymqh2S7IqLnRa%2B7Jfj2UPcGtqdvyfVspZ7K%2FQ%2By1%2FoaV6Ffj05FZqvYLKyQS13Eg7m%2F9JKimvHprorEV1NDGDwDRtakkEg2NCNnxDqdGb0Lru3qFDHX41rLTWGxVeyR7626tdVmNa%2F1P3LPWr9i2cmzrpY5tdB6gzpL7Kq7WGXPqqf6D8HIc1gZ9upo%2F7T%2FzmVX%2FADXqr0brOF0%2B4135bWVFgc05kD1WMP06ari17q25P81Z%2B%2FX%2Bi%2BnauTysfqnQeoM6h06l91eW2H4jwf1uhgc70Lq3DdV1XGxm78d7%2FwCfo%2FQXfpK7GIRFX4s2fN7vAeGpRjwk93IyOj5VDnN6U5%2FUMa%2FflYlLGuLm%2BqxnpfadrG1tdk302Ufav%2Bs2en%2FNrQ%2Brd9%2BNlNdfS%2FDuDXMy8e7R1bmjfJ%2FkNhl9Nv59KtOeLbMDO6FktJte%2B%2FCstLh%2Bhcaxm4mY5oc5tLMr08XK3st9H7TRd%2FSqPWW11fA%2Fa%2BOOpdOc6jqeMNllYjcdvudjWt9zfVr3b8d%2F%2Fom%2F1E%2B2F5jpf1u6Dj9RyLKcqu3peaf1rF1aGh%2Bhym0XMr91TfZksr%2Fncb%2FSWY9bFTwftfUOpdQwRZvxsDLOLh2uBexrLH21YzLLAHv9K3ZX%2FwAH%2FOLUw%2BnfU%2F6xVDB6lg4%2FTutEFrLaG%2BgLXD%2FC42zY2x%2F%2Blw7f01f8ur9On%2BroyPqllZfThhMsqBZblWhxFjahFLLWE%2B27HZv3sbsr9H9N63%2FBjW1L9J9PDa%2F6udcx%2FU6Ta7bSH6uxLT7W0Oc0%2Fo6HT6mFl1fzX%2BnV9n1R6v07bbhZv2za4w149K0Vk6enkMeWPu2fzn8xVb%2Fwf0F1GVh4mdSW3MD2vbAfA3AH9xyoYmRZ0o%2FY894%2BztBNGS7QbR%2Ba8n6Oz%2FwH%2FttLyRbyPUcXMqzznY2Nfh9TrbZbkkVyxzQBvzm%2Bm04vr7f0d%2Fo2frX%2BD%2FTV2rc%2BrmH0bqHT8fcW%2Fb2Ned1FrmWsaXOZLfRLH01W%2Bpu9L%2BaXUiCJHBWGegM6d1IdU6WzbJi%2FFbAaWuPvdV%2FV%2FnPTStN6U4%2FWfqPk5jhRj2BorDrMXLOjqnge2m1rNrnV3e1lnpf8b%2FO1Urm%2FqLlZH1a%2BseT03IwLa7s97K8nFrY%2Bx9fp%2Bp6OfjupZ6VvT3%2BrY3I%2F0P6Oyn9F%2BjXrKSCFJJJJKUkkkkp%2F%2F9X1VJJJJSkkkklKQM3Dpzcd2PbuAdBa9hLXscNWWVPb9CxjkdJJTxHVfq4Ol5bc%2Bq55dlvLLq2Dax1lprY7IFbP5i63b77Kf9H%2Bk%2FQrpPq%2FcLMAVlgrupcWXM4duBj9I0y7f%2F1f00vrB0hnVunPo%2BjkMmzFtGjq7QC1tlb9Njvd%2FqxS6dWMj0upH9FfbXsyqwIabGwx30%2Ff%2BifW9n9RHopwvrBh4eF1V%2BXkU%2FqmZQQLSB6deYxwux7HAfpGvs9L%2FB%2F8J6ivWVZ2dj4vV8FgfbkY%2Fo5WO8hosrdr3%2Bi6t7nrefWx8b2h20hzZEwR9Fw%2FlIOLg4%2BI652OCxt7vUfWD7Q4%2FScxv5m%2F89K1IujYj8PpWLjWDa%2BusB7SZIcfc5u6XfnFW31seIe0OEzBE6%2FNSSQUpJJJJSkkkklKSSSSUpJJJJT%2FAP%2FW9VSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2F%2FZADhCSU0EIQAAAAAAVQAAAAEBAAAADwBBAGQAbwBiAGUAIABQAGgAbwB0AG8AcwBoAG8AcAAAABMAQQBkAG8AYgBlACAAUABoAG8AdABvAHMAaABvAHAAIABDAFMAMgAAAAEAOEJJTQQGAAAAAAAHAAIAAAABAQD%2F4TkjaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu%2B7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI%2FPgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSIzLjEuMS0xMTIiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4YXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyI%2BCiAgICAgICAgIDx4YXBNTTpEb2N1bWVudElEPnV1aWQ6RTcxOTVFNTY1NzMzMTFERDlFNzJGQ0E2QjkwQUZBRjU8L3hhcE1NOkRvY3VtZW50SUQ%2BCiAgICAgICAgIDx4YXBNTTpJbnN0YW5jZUlEPnV1aWQ6RTcxOTVFNTc1NzMzMTFERDlFNzJGQ0E2QjkwQUZBRjU8L3hhcE1NOkluc3RhbmNlSUQ%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4YXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPgogICAgICAgICA8eGFwOkNyZWF0ZURhdGU%2BMjAwOC0wNy0xOVQxNDo1Nzo0MS0wNTowMDwveGFwOkNyZWF0ZURhdGU%2BCiAgICAgICAgIDx4YXA6TW9kaWZ5RGF0ZT4yMDA4LTA3LTE5VDE0OjU4OjA1LTA1OjAwPC94YXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhhcDpNZXRhZGF0YURhdGU%2BMjAwOC0wNy0xOVQxNDo1ODowNS0wNTowMDwveGFwOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhhcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ1MyIE1hY2ludG9zaDwveGFwOkNyZWF0b3JUb29sPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIj4KICAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9qcGVnPC9kYzpmb3JtYXQ%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iPgogICAgICAgICA8cGhvdG9zaG9wOkNvbG9yTW9kZT4zPC9waG90b3Nob3A6Q29sb3JNb2RlPgogICAgICAgICA8cGhvdG9zaG9wOkhpc3RvcnkvPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICAgICA8dGlmZjpYUmVzb2x1dGlvbj4xMDAwMDAwLzEwMDAwPC90aWZmOlhSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj4xMDAwMDAwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8dGlmZjpOYXRpdmVEaWdlc3Q%2BMjU2LDI1NywyNTgsMjU5LDI2MiwyNzQsMjc3LDI4NCw1MzAsNTMxLDI4MiwyODMsMjk2LDMwMSwzMTgsMzE5LDUyOSw1MzIsMzA2LDI3MCwyNzEsMjcyLDMwNSwzMTUsMzM0MzI7QzA1QTE5MDRGRjAwQUJEQzA1MUJERkFGMDIwNEVBNTE8L3RpZmY6TmF0aXZlRGlnZXN0PgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24%2BNDUwPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjMyNTwvZXhpZjpQaXhlbFlEaW1lbnNpb24%2BCiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U%2BLTE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6TmF0aXZlRGlnZXN0PjM2ODY0LDQwOTYwLDQwOTYxLDM3MTIxLDM3MTIyLDQwOTYyLDQwOTYzLDM3NTEwLDQwOTY0LDM2ODY3LDM2ODY4LDMzNDM0LDMzNDM3LDM0ODUwLDM0ODUyLDM0ODU1LDM0ODU2LDM3Mzc3LDM3Mzc4LDM3Mzc5LDM3MzgwLDM3MzgxLDM3MzgyLDM3MzgzLDM3Mzg0LDM3Mzg1LDM3Mzg2LDM3Mzk2LDQxNDgzLDQxNDg0LDQxNDg2LDQxNDg3LDQxNDg4LDQxNDkyLDQxNDkzLDQxNDk1LDQxNzI4LDQxNzI5LDQxNzMwLDQxOTg1LDQxOTg2LDQxOTg3LDQxOTg4LDQxOTg5LDQxOTkwLDQxOTkxLDQxOTkyLDQxOTkzLDQxOTk0LDQxOTk1LDQxOTk2LDQyMDE2LDAsMiw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNSwxNiwxNywxOCwyMCwyMiwyMywyNCwyNSwyNiwyNywyOCwzMDtENDYzN0NCOUQ0MUExMEJBN0VGNUVCQ0RCNjMxODMyOTwvZXhpZjpOYXRpdmVEaWdlc3Q%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY%2BCjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8%2B%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQACAYGBgYGCAYGCAwIBwgMDgoICAoOEA0NDg0NEBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAEJCAgJCgkLCQkLDgsNCw4RDg4ODhERDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgBRQHCAwEiAAIRAQMRAf%2FdAAQAHf%2FEAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPBUtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4%2FPE1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1%2Bf3OEhYaHiImKi4yNjo%2BCk5SVlpeYmZqbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEyobHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp0%2BPzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1%2Bf3OEhYaHiImKi4yNjo%2BDlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq%2Bv%2FaAAwDAQACEQMRAD8A7%2FmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmrlHI2utXn6WltnFESb0RFSp41WjD5q3LCBaslzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Dv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2E2rj0LuzvF7PxftUf8AXPLDnC3XI0ewdpOkTK4P08T%2FAMK2Ec1TLNgaxmE9pFIDy%2BEAn3GxwTgV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV%2F%2F0e%2F5s2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXYUeZpfR0a4kpUAx19h6i74b4X65CbjR76FV5FoJKKe5C1GIVLdDuGhuXs2IKOX4gfsvGdx%2FskKtkiyD6bctHHbXgPImOC4qOpXj6E1ff4Mm4Ndxkpc770BvNmzZFLs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F9Lv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVokKCT0G%2F3ZgaioNQemANZuhaWLyE0LlY1%2BbGn6sWspA8QA%2FZ%2FUcVRWbNmxV2bNmxV2bNmxV2NZQwKnodj9OOzYqwDT4iiPZsa%2FVLm5sj7I%2F76L%2BOTXTpjPZQSt9pkAb5r8JyKXqfUvMOpIQOFxFDfoP8qFvTl%2F4Q5INDkrFPAf8AdUhI%2FwBV%2FiH8cnLeAPcxHMprmzZsgydmzZsVdmzZsVdmzZsVdmzYyWRIo2kkYKiirMdgAMVX5sjTeaGl1CzsbSAOLuX0w7k14KCzyAD%2BUZJBirebNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV%2F9Pv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVo4CXUFa9e0CbJRWkr%2B0RWlMHZEJ5WtPMtzE5%2BGYJKnyK0%2F4kuEC1ZeM2IW0okjFT8S7H%2BuL4FYz52mMWn2tDTlcpX6FbDHR5ecCH%2BZevywl%2FMM8dJtn7LdJ%2BKvgry9ccrOJ69KVw9E9GS5s2bAh2bNmxV2bNmxV2bNmxVi3miFU1LSbxvsStJYzH%2FJnSi%2F8ADYpoEzLcxq1T68FGr%2FvyE8GrgjzdC0mhXEsY%2Fe2pS5i%2F1omDf8RwttJRHdrcDaNbhZVI6endIG%2F4mcnHeJCDzDL82bNkEuzZs2KuzZs2KuzZs2KurkZ843Zhs4bVTQzvVqfyrvg3zHNPb2UMsLtGBPGJSpIqjEqRUb9ch%2BvSSvp0JlcvJCLyMuxJb4JNvi%2F1SuEBVvkaX9L%2BZbm8Sv1fTbf0Y6%2F78lPxH%2FgFzpeRvyPbWcXlyyubWBIXuoxJOUG7uKjk5%2FaOSSoxPNXZs2bArs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F%2FU7%2FmzZsVQ2osyWM7o3FlQkMOxGPtZhPBHKCDyUE08e%2BM1Bedjcr4xPT%2FgThV5cvBJbpGergEfMDcYa2PkqfZs2bArs2bNirs2bNirs2bNirshfnRGtb7T9TX7LcoJD2qDzSv3vk0wl81WDajodzDGKzRj1oR35R%2FFT6RthHNXaVcrKsUtftDi304dZz7yrqPrQCIncdMnsEnqRK%2Fcjf54yVjP5gwtJ5ckcD%2B5mik%2BivH%2FAI3wD5RnEtpwrWgw983RGby1qiqKlYGkA%2F4x%2FvP%2BNchnke5BIUGoPQ%2FPEJHJ6XG3JFbxAOPxOA1iX7sUwIdmzZsVdmzZsVdmzZsVUbqBbq2mtm%2BzMjRn5MKZDLP4rC2UVq9qYj7SWshWn%2BtxOTnIVG4ttWvdNaiiK7FxGDtWG6Ti%2FwDwMuShzRLky%2B1lE9vFMP8AdiK33gYscKtBmDWhtq%2FHbOyMp68SeSH5UOGpyJ5pYde3moWPmW5KzH0pIo2WNt0A3Wv0ts2D73zJ6FgzxqqXpKpHG9eDMx7EYXed45VeC4gPCdYyqua0I5q5VgOv2cAW8sWo2QMqhwwoyHcVyQFhVOLzvrNrdf6fFHLFX4kVeBH%2Br1%2F4bJlp%2Bt2GqWrXFnICyKWeJtnUgV3XOP67qFjaFLHUrprecy%2BlZajxPDmN1jn8Dv8Aa%2By%2BIaXrclrdtZ34NrfR1USofgeo2K06c%2F8AgcBCXr%2FlvXRrdnzk4rcoSJFXYfMbnDzOV%2BTr82eqAOQIpaBq7AV2r%2BOdTriRSEp8z8RoN9I5AWJPV5HYD0yHr%2BGQg3Ntq1lcLazx3CiZviiYOKzQo9Kjb7YzoWqRevpt5AOskEij5lCOmc60%2BWCK0j9ONYo5IYp%2BEahQWBeNjRafEcMVTvTb%2B80fyHZ3FugE9vSORJBUqPVZW%2BEHw3yVaXeC%2FsobulGkX41HQMNmH%2FBZzC%2B1iTTbadZUkjtJjzkQqSKqNm5fsdMPfyr1%2FRdT8vra2E5N5C8kl3bSMTKpkctz%2BL7SP9peOA7fFkIkgkCwOfkz7NlA1y8DF2bNmxV2bEJLy1injtpJVWeavpREjk3Hc8V64virs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F9Xv%2BbNmxVplDqUbcMKH5HIPokjWsstqx%2BK2lZPf4Wpk5yF6nF9R8xswHGO8QSj%2FAFh8D%2Fqrkoc6VmSMHUOOjAEfTjsAaZMXhMRPxRnYf5J6YPyJV2bNmxV2bNmxVQuzOtu729PVUclBFa07U98DafqsF6i9EkI6ePywwyF6vG2i6oJRUWl6xZCP2JOrr%2FsvtLhG6s0yiK4X6Zfi4QI7Vb9lv5h%2FXDHrgV5c8B0LzLc2Q%2BGF29WDw4SfF%2Fwp%2BHOg6ZMHi4j2YfTkZ%2FMGwpFaazGDytm9GYj%2FAH25%2BEn%2FAFX%2FAOJ4N8uXwliicnps305LmFZDewrc2dxbt0lieNvkylc45%2BX10UCQzHjLATE4P%2BQeNc6L55e5i0J57WVonikRmZDSq7jf2yB2Op%2FVYor2%2FwBNjktpAGjv7ZvU%2BInoy8Vao%2Fa44xCQ9as25RVBqKnfBGRby9rBEsljcIY0aQ%2Bg5FCCQD6bj9lslGAikN5s2bArs2bGyOI0aRuiAsfoFcVS%2B%2F1q2sJltyrSytuyp%2ByDsC3zwxBqAfHfI%2Fodmt2z6pcjnJK5dAexPT%2FgRkhxV2c782mSy82WUgNIdUhNs4PTmlXQ%2FwDBDOiZAvzLb6nHpOrlFkjs7lWdG9yvxVG%2FwiuGJ3Up3YTJFr7op%2BC8t1ljHjxodv8AYtkiyHbxx6XfKx%2FcXBilJ3%2BAsVUV8FRlyYA4Z80DkkPmq3EtlHJSpR%2BJPgHBH66ZBtIufqt9Np8pALHnEpO5p1oM6Xq0P1jTriPoePIfNdxnLtZtrGZluBcG3vF%2BFJYuJfx40IIxHJKzzb5et9ZtZYp1LwygcgPtKy%2FZkT%2FKWuRKKCOC6tNH1CblcWiILDUWHH6wg6wyDejx9Fw4sfOMjEBY5biH1HgLy8VYulOfJV%2By38q4ndH9LlL219K4iilHrWzLxlhI6SgE1%2F2SnFKo0Czxy28zMqcSzspIYBPj2I%2F1cd%2FytnVZdNQ2D1I%2BESMoPw%2FzMftdMG20TSQyyblmjenzoc4TY6ld6ddyKwMUsbFZoHqNwd1KnGXRXs9n5%2Fv7lg98GlWhMjwk1A%2Fm4k%2FF7jDSwSPULTTJI3PGBphGNwG4OHVW%2BgnONX%2BsWFpDFPaSSJcTESG3TcRlf2uRK%2Fazq%2BkR3aeT7e%2BjnX6yoW8ibht%2B9i9Tg%2B%2FQ98QVZHqstutowuSPSdSpV91IPVWzif1i98t%2BZ7m40FjbSWUrNCqEmkex4EH7acT9lv2c6lonmbTvMUTWdwgiumWktrLQhge6H9of6uRLzd5ZurDVP8Q2KetYFUW7jXeSKi%2Bm0hH7UZH7X7OCY4g2YM0sMrjvexB5SHcXsnkPz3Y%2BcdPqKQ6lAB9bta%2F8lI69Y2%2F4XJeDXPJlvPe6JqMeq6NMYLuE842To6ntToyuv2k%2Faz0J5E8%2B2HnGyI2t9VgA%2Bt2ZP0epH%2FNEf%2BEyESeUuff3tmbFAx8bB9H8Uf4sZ8%2F6P82TMMLNb1iDRrQ3EpBkkPC3j7s5%2FwCNV%2B0%2BGVc4n5m8yfpnzJcsr%2F6HYsLe232PBv3j0%2Fy3%2BH%2FY5JxkLourX175%2Ftb28maSZrloqsdlQ1Tgo%2FZUDO755%2B8uxlvPcMI6i6LAe2zZ6BwlS7NmzYFdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVf%2FW7%2FmzZsVdkb83W5%2BrW1%2Bg%2BK1kAc%2F5D%2FCf%2BG45JMC6jare2FxaEf3qMo%2BdPh%2F4bCNiqV6VOOcTV%2BGReB37%2FaX9WHoyFaJMz23BqiWE7juCh%2FsyaRsHRXHRgCPpwy52gLs2bNkUuzZs2Kuwu1rS49W06Wzb4XI5Qv8Ayuu6nDHMemKvONE1KWGVrG7rHPCxRgeqsppk7sb0XC8W2lUbjxHiMhnnnTHtLmPXrZfgakd1TsR9iQ%2FP7LYtomq%2FWI0Iakybof4H55LmFZdqNlFqNjcWMwqk6FD8z9k%2FQc515XuZbW4l0%2B5%2BGWFzE6n%2BZTTOlW063EQkXY9GXwPhnO%2FNsS6T5qtbofBHqaniexmi%2B2v%2BsU4v%2FlfFgHcrPTFbanZNb3caywyApLE3Qj3zjutwW%2FljzBc6Pp6fV9JHpsLNSSgR1HIorV3Vvizq2jXQk%2BAndlBA9xnNvzRiWPzJBJWnr2gJP%2FGNyv8AHAdikJ3YGf0o5pZDIboSD1GJJ9aAgh6%2F5aUzoFnN9YtYZ6gmRFY06VI3zlXk%2FVI5bi2hnlBgdBuxFFahhfr0rSNsnHlXU4JI5NJaSt1avIQm%2B8RaquD0I%2BLjkpbgIZJmzZsirsDagC1jcqvUxOB%2FwJwTjXUMrK32WBB%2BWKpfofE6Xb8f5d%2FnhjkMPmvR%2FKlrJBqkxBSVkjC0PIDpTfI15s%2FMNbu3jbR5plsZVp69swRi9N0eSjMnH%2BRcVpm669cWWtXGnatwjgKma2uPsj0x%2FNXw%2FayOfmLr%2BhX3l%2BPTYrkXM%2BpGtm1uPUUceX7yRx8KLtx%2F1s5xoN%2FefpVr6C8muVVfUuLO8kMrEIeTem7faRk5Iy5FdTuZ9B169trGZvqkc5eFD8SmJzzUcTtsrceWHzTT3fRJv0p5UZvtOYYrgCu%2FJV4P%2FwANHnPrj83vM2lX9zpWoBJYInaJ540pOi1%2BGRQDxf4cln5Z3qT2stjWrK0iexEgE60%2F2XLOUfmHYNZeZbhWP94Ayn%2FV%2BD%2FjVclLlbEdQn955q1wTiU3huLadeQCsfSljO9VNev%2FAA0bYHub1Z7V5yWkhHxpcVHJNwGiuP8AKStVk%2FbyGafqLQxyafO5S2lPJWUAmGQ%2F7uiB8f8Ad0X%2B7F%2Fy8Ctd6jpl3Lb3v%2BkQTpxniqTDcQn7LoR%2FwUb%2FALDZCyyekWl9ZyaFLPelZGN2tqDEtZJHenEs67ckpVXwq1bU7G1iKWvqtcCR4bkkhFYx%2FCHXhvvX48C%2BU7H1SbK3k9Wya5hv4HchSBF8FxBL2WeOJ1k%2F4s9P4cu%2F0j%2Fc1FphmFxPLeOhZBxAiL8jt40%2Fa5YegV6PprvLpcE8sfpTNbh3QVoGK12zluoQ2PnZih42fmWIVt5z8Md4q9I5P5J17Z2A8YrWVl%2BERoaDwoNvupnLtb02LzREdR0hBba7bVe4tEPFZwpr6sH%2FABb%2FAJOSkgPNLqGaG7e3v42iuoTwlicUIK9iM755KnGoeT7ZX6m1CH%2FYSNCf%2BFOctnuIfNkSWeqEWuu249OC8ccfW47elcd%2Bf%2BVnQ%2FyyS5t9Dk067jMVzaSXMLK3hRJlp4r9rjkQl5PbalcaRd%2BlI7PbwysI5lNJIirU%2BH%2FmnOueV%2FOUeo%2BnY6hIpuWX9xPtwnU%2Fhz%2FnTOPeY4zY%2BYNTgpVBcSVQ9CrHkP14Gsb9rFgRWSzY1K1%2BKNh%2B0p%2FZcY2Qdlev%2BZ%2FLKxK19pcdYBUzWqdYx1Lwj%2BT%2BaP8AZ%2FZyHW9xe6ZfQ6ppk7QXsBDRTIevsR%2B0p%2FlyY%2BUPNR1KJLK5lD3KrW3m6esg67f79T9pMD%2BZtCjTnqVilIW%2BKeFRshPWVAP2P9%2BJ%2BzkZRB3DPFlljlxRPz5Edx8mcw%2Fm3aXvkzULyUC3122jET2oNA8kh9NZYT%2FJvzb%2BTOZabIhnWkgaOQcwxO577%2F5fjhKIYndoZvgaUD05v5SN1J%2FyG6YI0RZ4tS%2BoyKQS1HTwYHsffALrdOQwM%2BLGOEHfhu6PWvJ6f5FsfrXnqW8K%2FDBEZD4cmAVf1Z2fOSeSLmfT9RrHEWlvDSZaVagNR0%2BztnW8mWsuzZs2BDs2bNirs2bNirs2bNirs2bNirs2bNir%2F9fv%2BbNmxV2Y5so4qw6WL9HeYLiLpFcUnjHQfH9r%2FhuWSbTnBgMXeJiv0H4l%2FA4T%2Ba4fTW01FRvDJ6chA%2FYfpX5MMGaXcBmHxbSrsCdi6%2F2f8RyZ3j7kdU4zZzrTfO2qQa3eaHrkax6haux%2Br9FkgJ%2FdT2790ZP%2BGydWWoWt%2BnK3erAfHGdmX5rkSEovNm2yiQASTQDqcCt5sL5dZ06Ko9YSMNqRAv8Aiu2Iv5i0qJecsrKo%2FwAkk%2FctTho9yo69htbi2e2vApgmHB1cgA1%2BffOYX%2BnXflXUghJezkNbabxH8jH%2BdcPPN82l65b2j213DN9WdmmtefCRkdeJaNSVb1I%2FtYXWM8cVk2javM95pUv9zcPvLB%2FI3LvwwhIT2x12KCBrxjyjRS06jrxG5YD%2BZcrzzpkXmjyhLPpzCSaFVv8ATZozuXjHP4GH%2B%2FE5JkUmju9BujaTsJYHHKC4G6Sxn9r%2FAJqwJH5g1fyjDaw2KrcaE0zSNE27RiQmtuXPSJ6%2FuW%2FYkxI6hUg8ia9qulatHe3s0k8DgxGKZywBk%2By3%2BTkn%2FN63a8ttG1a3rHL%2B9hIB%2BIdGK%2F8ABA5DNbS0tL%2B4ksJOenzETWrjYiOT4gjD9l4WqjL%2FAJOTTU9XtNc8r6dDP8E8rc4iSP71RwkTfcP%2FALsX%2Bflgq09zFvKeoTMwV2NeCsB0BaNqf8a5N9Zu59K83W2pWb%2Bn9cjBo24ZXUNxI%2F1lyCWsL6fNb3LLxid24tSgINA9Puyb67pd%2Fr2kaPc6bxN5b8owWYKAYW%2BGpP8Ak4RyQU8ufOF7JLD9XRLaAj96ZPiYv4A9An%2FDYOfz9oEbtA0kjXSqG%2Brqh5NtU%2BkW4iQL%2Fk5yK%2FiuNagjEcpTU7Rj6YqaSAbtG3%2FFin4om%2Fa%2BxhXcGeWxlDgpPbfvUFaPDKpFXiI%2FYk%2Fk%2FmwFXr0n5hfXBJFpVtxmC84jOa81%2FaKqv7Sfy5GL7zJq2rwXFhqF40AlWiSQfAEPVW%2BH7Sfz%2FwCRkTl1dLW3t7xi8108C3g9EBArCnI8jtVvtNRcR1zzTdJcQvYwRW7y28dwzFfUblIvI05fAo%2F2OGxSt3zSW9LPXITMlCjiuzofsyRt2YfsPkd0iabTtQksp1Zra4U84X6SJ%2Bw4%2FwAoD7Ei5dzqV7LFCt7KZnVAY%2BRqyg9aj%2BX%2BTJdouuaHrltb2PmCNfrkKmO2vKcT%2FkgsP2v5cjtapfo0Jt%2FMllFGHltZvVeK4ptwWNi6yU%2BzIvRlwm1mUarpNtJHEWutOkuIbh0FSbfkGiZ2%2Fb4MxH%2Bpk6uZNM8saddywStNK605tsKnYcF%2FmbOZy3s2nWaLE%2FG4e4%2BsK43HELxowOzK%2FL4lx5fFWb%2FlprP1TWLQMaLMFRh%2FlRtxr%2FwLYn%2BfNjLp%2Bp2WpwghWLxuf2SGoRX%2FAIDI9peo2kMtjfWQMUi3JM9u24j5rT4D%2B1G5qyfyfYzp%2FwCcdimr%2BUra%2FG%2FwpISOoIpXJDcIPN4HDcR3Scl2I6qeoOGNvcQyw%2Fo%2B%2FJWGpNvP1aBz%2B0PGFv8Adkf%2BzXIxSaym2NCOh7MMNYLhLlOS7H9pfA5FU30vUNQ8uaqjIqsaqHiO8UsZ6EeKsrfA%2BS3yUr6p5iudTl2S1RvRTchTKeCgV%2FlTlkOsvUveOn8VkdatauxoyN%2FID%2B0r%2FwAmTfyYHsrnTdPasc1%2FNczXMZArwhT04g3%2FAD05Yjml6Fditlcmu%2FpvX%2FgTnK5bS4sp1mDtFID6kLioPXZgc61NGTBNH4o1PpUjIpNbyX0Ulvcw%2FV9Tst7i2IqHWn99EP2kZf7xF%2F18nMX1pANMR1TTYfMkRuYYlt%2FMEY5EfYW7UftL%2FwAXjDf8u%2FMFzM8mnX6MJLWRFkMh4yiqtGUYU%2FZP82IzW%2FH4DVQG%2FduPtRP1FG%2Flb9lsG6ZJDJfPcvGI9WCoJZwPhmRGHF2X%2Ffi4AP2peafmBE8Hmq8LgATCOVaeBUL%2BtcjauUPip6qe%2BdJ82x293Pb2GtyL9ZmWRoL5QAUZZGQK47p9nOd39hdaZctaXScXXdSN1YHoynupwFCL0zUX0y4SVGf6uWDqybPG46On%2BWv%2FAA652nRtY%2FTdqJbfjLdqnK4gjpSVen1iBf5G%2FwB3Rf7qfOCxycaqwqjfaH8ck%2Fk%2B61K11AC1kZYIT6gmB%2Bxy2oP%2BMi%2FC6f7LEGlZCbBr%2B%2Bu%2FqqpBCjP6cbtxHwn4ljrX%2FgckOj2EMcC6lK6yTRKYmkIoYl6gE9xT7L5HhcTJqM1jJCEjjQSQsK7qT3rgi%2FuJ7PTDKQ31S5f03ZPi3SrfGP5RiKG6bej%2BQPzB0CG%2BuNHvB9VeZx9WvZKBX7cCf2P8jOvghgGU1B3BHfPHaQrfuEt3DHkAJakKhP8AO37A%2BedX8i%2BYPOen6dNp2qK5t4JFjs5HClyoNJfTdieUaL8S%2FwDCZEcRNEc%2BrfOOKWMTgeGUaEoHfi%2FpRP8Aunt2Ab3UorWqAc5QK8egHzOPhuoTYrdesJYgnJptgDTqdu%2FtkT1C9XnLPIwofjLduJ6fhkgLcYsi0vVRfSSRSUWRd1UeGGgyN%2BVrCSkmrXKlXnHC3RtuMXXlT%2BaTJKMTV7JdmzZsCuzZs2KuzZs2KuzZs2Kv%2F9Dv%2BbNmxV2bNmxVB6rafXtOuLXvIh4HwYbr%2BORbRL%2BsMTMaSQsOSnrseLZNDnIdfXXtP8zXtnZGC2tJWEsUz8pH4yAE8Y14rs3JfibJRPMKyf8AMXye3mKyh1LTH%2Bra1px9S0ulFSV6mNwPtRnIb5e8wXlw%2FwCj9VR9N1u12PGoD9vUgfpJG%2F7UedN8uapcXsH1e7C%2BrAi%2FvV6OPs1K%2FsttgLzP5Ysr%2B3NzHFxmjPMlNj%2FrLTdWHtiNtlU9I82sxa11lAssf2bmMfA9PFf2G%2F4XIt501y81SykEEjRQRMGEaMV5L0%2BKn2sq4ke3j9O4asuyLNSnMduX%2BV%2FxLCfVZ%2FS0u8cDkVhYhfGgxodGQDDZPMculusTzpwryMT7mneh%2B0uTVNL1e%2Bsob1tLmmtJlEkbFW5FGFR8NfUzi2iQ%2FXNXtLjUAXWe6hE3L%2BQyKHG%2FbjnqzzP5ok0H0baxgWVyodi5IRU6KBx74ASrzOzsfL1tcH69ppMtdxM0h4%2F883OS2GLS7239K1pGpGwj%2BEr%2FALHphVcecBcyrc6vBFdabL%2B7lV0UPbt12KDnx%2FlYYGvrWKwP1nTp3kirsT0BpWgcdf8AZDJg%2FFjSanSb8xC2ursXtpG5kt7dkVRGaU4g%2FETy%2FwBbIzc60xSeylsgphDJeafNtIY%2BnqREfCygYcWPmV14x3qkqQDzAo4X%2BYr%2B0ME6h%2BidZZImjM90FLQzQj40BFNn2%2B1X7DYkd2yi%2BrHNIh8uXbLp7yLJdToZbZJDX1EU0NOXxCeL9tf%2BJ4PhbTLya58sXqqt3EoKlV9MSoRUMn8ssf7XH%2FXyD%2BcfKFzoWnRatFqUUU%2BnSGSGAsBcFXZaFeHJVZDu2HOn3See9Kt1%2BsCx8x2y%2BpaXabB2TxH8pP21%2FZwAnu3VQuV1CC2iW9cx%2FwCkS28kMtA5dKLHL1%2B00TJyZPhk%2B1nS%2FKN59Z0GSNno6yI6e1V4v%2Fwy5z9pv8YWj6Hrcf6P836EWmiiHSfiv2ox%2B2kwH7P2ftLk28uxrbeXYLhYXV7gMzxkE0IqPiqNq%2Fy4gb%2BSk7PNdX1m%2BHmqaytJ%2FTtTdx%2FBGoSu6swZqcj8YbCnRmM97drKxKXcslu7tXb1%2BSBq%2FwCSzZK9c06xuNUutStkBls5IFkAIAMoHqUUfzNRkbCPW5Xs70rZcVtL0%2BtCoAFVc1oK%2FwArni2RIShdD0rU9Z0g2UNW1DR5JLG%2BirRlQuXhmp1ZP72M%2FwCph75g0K2sra21G8ch5baGJUpt6yARuvL6OWEQk1eJrq9s55YLxBzuGhJEjr3LKvxSqv7WISanq2oQRXGryvd2MtVhuSQ8aNQch8OyNjsqElhK1hbcr%2Fcv3K%2FyV%2FycRSQBmUiifsfQM6FpfljSbrR0mCy3EjDlDIzN9sGnphU%2FZ%2FlbIrqnl%2F6jM3Cb1VRqOoU8078XoOHPISIiQCd5ckgE3tyS4StK6xSy%2FDJ%2B7HqseO%2FYVw0W2thatp10he2b7YH21cfZljP86f8AD%2FYxO4tbO4so4I7ZUvG5Lpl3PssprUws392JD%2Fut%2FwCbAOh6sLuU6VqYMF4hKRu2x5DrFID0YY7rsh4tMn06%2FkspjzjniaS1nQfDIE%2BJWXwdafEv7Od0ugnmL8tmYipWEOB1oGWp%2FwCH5Zy4hoT9WuU5BTzj7FHp%2FeIe3%2BX%2FADrnR%2FysvRfaHeaJIwZ4RJGo8QDzQ%2F8AAy5OPVBfPNxbpMpifYqaBu4IwFYWlx9fEIJU7lmH7S%2F83ZJ9e0x7K9unRg8azMkoHWJySVR%2F9YH4W%2Bzj%2FKtvFdaxBDLQKzAVO243G%2F8ArYPJUXb2NvDZtdyWxe3B4meCQ%2BtG37Jkjb9lafaXHRXl9q2r6etnWJ7Yxx28h%2B1UNyMjt%2FlH4myX6lp1poPm57CIFLG8jikCsKisqqXWncc2%2BzhLZW0Om35hCuLiCeSgIHFY67KT9rl4YaV63IwMVSakg1pt27eGRuzvrTWILdYbnlcIWXSdQc8W5pXlp91%2FLIP91M%2F94mG9tcfW3AQ14xgsPH4c4Zp%2Btz6HqN2rJ61nNIy3loTTkA54yIf91zR%2FailyRNUinpV0iXqyyxw%2BjdW5Md%2FaN1iPdqf74f8A5Jtka1ZZ4LWYxsySKpaCRT8QI%2BIxkj2Hw5KLe6%2FTsUF%2Fp1wsmrRRk2l0QAL6Fft290g%2Fu7uP7Lo395hFquqaL9XZmmWCRwySWTgs8MgG6Oo%2FY5fZbAUhC%2BZ9JsfMl1SKYW%2BqyQRT2DMf3UvJeTwt4M5%2BJchKyrOr6D5gQwTQEpFK%2FwDeQv8Ayn%2BZDk%2B1Gyj1ew0SaC4gS5ntmCtX0ELQtQAM%2FEcv5Fwv1bQxr%2Bnhr4oms2ymP6xuCStSIbof6v8AdzYOaGAQ%2BXdTm1NdLjj5SN8QkG6FOvqBu%2BTjTrS2sbZLCMcVUmkpFGLn7Rf%2FAFv%2BFxTTme102DTuILIwZpKksW%2BzxWtW41%2FZyU2XlW41HhcahytOQJZVHKaXw%2Fd%2F7rY%2FzviAqQ21ldXtytrDG0k524qKmnj8sO736roNmNIltfVv7hFeaMnkUqTwpxNA2S2MaT5etS9w6WdtEvx1b42A7Symh%2F2C5zTzb%2BZun3swOjacGVKwrqMgoajeiJSr%2FOTDVdVZPYa3pOj%2BW7u31uKKzuZGP1dePP1Iz1UovxtJ%2FlYU%2Fl%2F5hOtyS%2BWrlis1Gm0pyfiR4%2Fi9Kv7SumcrvNYnu5DcXH7yToamrfSf6YL8ratLZ%2BZtJvoT6bwXMbEjuCwVh8uJauPFuFe%2Bx%2BYJ4NMawQiOFzyZOlDX4h%2FwQyQeVtAn1BY9R1QN9WU1toG%2Fa3qGYeGE3lnRLPVfM19LdAyWkTtcW8a7xOGaq1kGz7k%2FAM6moAACigA2AxJ7lojn72wANgKAbDLzZsirs2bNirs2bNirs2bNirs2bNir%2F9Hv%2BbNmxV2bNmxV2Qzz3p3P6rqKbFKwyt%2Fkn4lJPseWTPOVfnX5gmstNtdBtnMbajykuXHX0YyPh%2F2b%2FwDEcbrfm24MRzZI447GR59w6pMfNyWl7EdD1KM3Cp6dwoKldj%2B1zHE%2FRko0rz1rUtz9SvLe2lm2Hp8jC7BhWqV5xvUb%2FDnn01jPEin8pHgckemXmsTaHcxANw05o57O7IIKxliJYll%2FaRT%2B84%2FsNkIZhIkSjRDl6zs%2FwIjJCXFHkb5gp3%2Ba2r%2BbdPuEvYLVbLRpiFL27c2EvWkj0%2Fd8v2VpkGsfN2p3Kra3V2XicgEsQeQJ3qSNs7d%2BkdO1rSbKw1LhN%2BlbRXVJKFZyq8ZV3%2F3YGHLOEecvKFx5TvTLDym0idv3EvdG%2FwB9yf5a%2Fst%2B3lpHUOvsua4iNwbWCruHK%2FB0qN9j0zoy%2BarW48r2Oq3l1Le2sSizuGkqk6zgUVXMda8F%2FwCDTOO2M01rIs0XCRQ4kHqCqGnZl74NgkTlMqSOIZpPVNrXjGG8Qvfj2wXzSyqz1eO%2FtNSjQM8sVubhFbbkIpPiI%2BUR5Yca3q17IdPmtZ2js7yztriONNhyAKSciPtMJUfIZaXDWL%2Bra0jcq0ZYAV4yAq6mv8ynJf5Yex1PRjpUsfO%2F0pnuLJWNPUgkNZ4l%2Fm9Nh6vH%2BVpMRa2uvBcXej2Gp2rSC5tJJbOZkqW4v%2B%2FgY%2Bx%2FeJhtBB5sOi313boLa9ihMlo3EB5ClGkULy%2B16XJk4rhlZarBFAbZljtrS5Tg%2FoIF4fyOT1JjbC1L%2BbSbowyMwu4WrzJryX9h1PQpxw%2FFWFXNut9PB6srS6frlqI4JZHJ%2Br30bCsbOf5phx3%2FAN1T%2FwCRgPQbtreVrEFrW8gfnDU0dJUPF09unHD%2FAFeygjjvbi2haTQbpxcXlpHvLp9x9n6xClfjtjXi1P2P3b%2FGseE99ol9qtyjIKamYlktb6Mn0L5QKJ8Zp6d3x%2BH4vt8OD%2FvciTXPZaZ%2Bkdp5%2Bgt19Yad5p0%2Fe0vU2Y8T0NPiaM%2FtJ%2FuvOj6lcGysFaRlYxJWUjfkUXk3z5MM5p%2BV2l3U2pPdalFHK1sOLyqQxSZaFOdDWOT7W%2F7eTTzK8UdlcxREhXBTf%2BdzWg%2B7JxNji70HnTz%2B51GXyrrFxaapD69nfcRqSftqxHL1oj%2FOjPjtQ0GNYCfrCXWi3QMtlelhyjkYbFfdvsyx4M87Wy6zqemTq4X9KwQnmegkK%2Bmf%2BSi5G9M1PVfLUt3od3CJIGDEQS7iKUD4Zov8%2FjyErqrruPNkK58%2FJN7Sx0%2B0sDe3k0k9xA6JBJACGqwPHiKp8S8fts2F0mrDS3nubPThH6ZLa5YcQsnBvs3kSsCnJeXJuK%2FB%2FwAYmwQ%2FrXdjEsQRizhvjKCrUp8If4uW%2FwCyuK3Fs893p1nezfVdeDCOxmHFi8ZH%2B89wSeBjP2YuX%2BVFiYggA70Pmt9QzfyjdxXeiPeaY3rNYt9ZjdKUkgP2gyj7L8ftL%2FPgTz2tuLdJIT6cN1JFcxzJ2U7Sb%2Fs%2Fa5BsKfLEMvlPXby2sJI40uF%2F0izVuUcb05n0z3gkTlw%2F3237vHeddXN5pEMdnbmOys4BFcy0qDcF6GJa%2FwDFacmyX8Pu2R1QOi6Yupabd2F78dj6xjLVFUlpySaHwkUf7F0%2BDIrrvl6dr42N0eOtRIHs7ldkv4B9ijf8tKgfB%2FP%2FAHX28mPluyb0Vs7OdTL6SSReoK8%2FTZqN%2FrqOPL%2FJwTqdhFr9m1lqUhjnhcm2uv8AdltN%2FMKf7pb9tP8AZrjVhWHaHrEWqxjTb9gt9GKQytsXptxNf2sl35d3J0jznJbCojuQlR26cT%2Btc55qthdyXcyyJ9X8wWfx3ESbC6UbrcwfzS8fjfj%2FAHyfvPt88N%2FKuq3F3q1hqBIEyc4pWZuCkoAwck9MA50VPJV%2FMq2k0TznfCJQUmPJom%2BzJG%2FxcW9t%2FwDY4Q6cILa6ZY2eKRwslv6goaDfjy%2FaK4f%2FAJleadM80a8t1pcZ9GCJYmuG2MrD7TAfsqv2V%2Fmwr0WyuNXUw%2FVxci1%2BKLt%2Fw23wrg6qGVanr1vrK2t3csIry1aESBjxJEYYOQff4cjvm3zLbpZXLWTGC5vr%2FwCswRjfjBGoC8yfi%2BIjCPX2vNMu%2FqZDRNGKSAgUqxqgqKruF%2BH4sjsyPPIZZSXJ7knfG1e9%2Flxrdt5ksbqdSFu7eMLcW5O4%2BE0cV6xvnF75uVzcb14yyBW7%2FaO2KeS9RutC8yWd7DMYLfn6d3L%2Bx6D7SLJ7YhdkNd3UkRDRSXErxMDUFGclT9IwndCtpWrz6ZN8MskcDsGdojxkjcfZmip%2B2v8AL%2B3iWoySfXbiHUmFy14frKXqH4nL14zK%2FVuX7cbYEeJWDPQVpU0Pj0x6yqLL0pJUW4tXE1o5O9SaSRbeP28Cob1Z1T6vPKzxjeNSSQK9aV%2BznXLfUrdtDt9T1U%2BjeRxpa3iMvF5V41jcg%2Ftrx%2BLOd6VbaTqJlS7lMl5Iri3hHwRiQj920snhzODppWsii6zcGeSZg3po3NSy%2FD%2Fen4K9V542r0LyheaXcm5uaCCWNhxkl%2FvClOqcvs4rrn5g2enI9tpKi4uqlWck0rSvxP8Atf7HITazpccriNaLGD%2FozGnBVIJkZP261%2BFuWE4juLqSZo2pbJMjyitAQeVPwrjxbUE01reuXGvCKPUZJ2Cs0iqgUgFtunw%2FCKfDhWLXSzayW73jxuGElWgJUbUoSDhkLcXEjJDSJqkoSd%2BH7OGlh5a1HzDONIit%2FWvZEMkJRlQtGlKsCxVX4%2Fy%2FawWtILTLTTmeMmbTrm1jXe0nmMLOwH2nZlrWv%2BVhlBb6Lql09zpemrbx2cJe5WNqiRi3EKrfyj%2BZR8WJ6x%2BVPnXQrK41i9sljsrUB5pBLGzAVCgiNWLNufs4I8h6hp738sNxRBexei5BoBJWscg%2F2Xw4VZjoMllFbx635Y52k9mvPUdKaRjHIF%2B08PInhJQV6%2Fa%2BHOq6d5%2F0G7toZzK%2FBwKzBCVrTo3GpQ%2F62cYW21DQ%2FMUVrFH%2B5vyInPSIqzfE1f2afawkmtp42klt0f6uzuInWoDqrFQRQjkMJV9Q2ms6VfitpeRS%2BwYV%2FwCBNDg2ozzr5Y1vTI7V9L1PnbTDk8E4Qs7M1KKHBDp9GTfQfOdosSxw6txkp8UN0DxBG37WNea09UzZBNY%2FM3SfLcVrLrDIyXZKwm2bmW40DsF3%2BFa%2FF8eTW1uoby2iu7ducMyCSNh3VhUYEK2bNXNirs2bNirs2bNir%2F%2FS7%2FmzZsVdmzZsVdnE%2FwA9rKYXmj6jT9w0ctuT4OpEgB%2F1lOdsyO%2Bd%2FLUfmvy%2FcaXstxtNZyHosybofk32G%2F1sB5ORpMoxZ4TPIGj7pbPl%2BNPrERi%2FaG6H38MkPlXzh%2BjhHo%2BrmunCqwXFKmDmfiSRf27d%2FwBv9pMJBDcWN7JaXcRhuYHKTRPsVdT0P%2FNWB9RiWO6ag%2FdygSL7cuv3HKpkwImPcXoM2KGaHhy3B3BHf3h6B5qslXQooNOVof0a7Xdi6fDxBPKQQ78vT35xNl6Fr1l5s0yXSNZRHujHxlRhtMv%2B%2FE8JB9r%2FAIfILo2pT6XfLd8mmiKehPAzE8oT%2ByvKtOPWP%2BXKvrafSpl1XT2L6ez84LqLrE1a8WH7PH7NDlkMoluPiHRarRz05F%2BqJ5SHf1CT%2BZ%2FLlz5WvvTUmXT5jW2nP%2FEH%2FwCLB%2Fw2FauD8Sn6c67a3Vh5x0p7G%2BRfrAWskY6EHpLFXp%2FxpnLdc0S88t3ptrir27msE3ZlH6nX9pcmR1HJxFS3uA4oftDBlvcz2lxHc2sjQzxMGjlTYgjCVG6Mh%2BWD4ZRIvv3GC1Z7pdxHraNJCViukq13aswVAO9xCW%2F3Uf8Adkf%2B6%2F8AVw1t5NPkKabe3SyqKG2uIquYDX7Jc7ei37a%2FZ%2Fazm9tdT2dxHdWzcJYzyQ9R4UI7qw%2BFhkwhvDqECTaZbExyHhPbqKmCYivBf%2BKZPtR%2F8DgIkSDxVHqK%2FSyBFbjfvR8k1xY6gVtLb6vJbVWZbhvVLo38yAemYZF%2F4XCjW0McX986%2BX72Uem6V56bekVAYLu0D%2F8AJSL%2FAItjw7W0utVgj0%2B7k9DULc%2BnaSMaFkrvaz%2F8yXP%2Bpgq00iztbkaVcP8AWBer6NzEworLWjRsoJ4yRv8AEjr9jGOMDbn5y3UyJ8vcyfyHpl1puhy31zEE1e9cJfKxXjK8JMaTqfGZCrt%2FN9rCX8wda%2BqyabAGAM85aYA9FUcV%2FwCGyZXElvb20VgHb1LeNDEKmpKDhGS%2FfOK%2BcrhtU1G8vLZ%2BYsz6M0Q%2B0nBh6cwH%2B%2B5af7F8slsAAxG5tkesTm68saddo3GbTZ5bb1B1G%2FrRfrxGTW7LzPpcrahALfzDpir9biI4tJESAZowftJ8Svx%2FY%2F1cD6VMb3RNTsShJmhju4gVP2ovtH%2FgG%2BLBVnb6b5g0ux1JGWDXNDj4SEiongQcGjk%2F34hX7LfsYBukt%2BWba4gWHWmj%2BsWdpK0Dwqy%2BoWkonJE%2FvHZOfNcKdasnk80288TN6EEqxOXNWSOJiS7n2p8eB4bySw1ilv8Au4OQk47kKo%2BLv%2FLX7WK6jr4u9Uee1jItndiIiasyv9sV%2Fl3wWAFZBbvYajqsV%2FDSP1yITPUgSKH9SSinYcIRw%2BH7TSJhr%2BYOr2sOkWmnhHSK6YiRolFF5A%2Bn6ngJif8AWyP2ML6dbiRI%2FrqsAI3SRAsMY%2BxHKrlGhfl%2FefB%2B8%2F3XgXXYppJG1m2unkbj6Op2pUSKVAqksSNsif5L%2FFH9vHoUlrytdtDCgdmV7d5I2boU%2BINGT%2FLhpr3nLTKJ9UT19TB4zNHtCe3Jj3b2XIssp1Y8bUfFOQsix7SCSlAXpTmj4av5OnsdM%2BuzB3Zm9OWaNGD2%2FMgRTolP3sXP4Zv92fyYi6oITDVrG28x6NBeBvqWoWS1ivSeIikU8jFI%2BzLA6%2FGn%2B%2B3yFaq1pcXix28ZUGi3U2wadx%2FuziAEWv8Aq%2FH9vDLV7TzXeWrqbaWO4VhHeNHtFdCP7EhX5D4v9%2BfbwpC3liGgvIoVdSqt6pLPGr0pTj%2Bz8Xw4CUkEGil7RwRu9ZzwjPx8UZiKePShyb%2BRfMdhHM2kLEV%2BsDlDLQFncUCxcRX7WEF1DCs63zCs6r6c1Nkencr7rgXSpFsb5zCForepC9PiUEfZr4DADRVD%2BddUkPmPU4lUgVNtNG4qDw2Bof2kP2GyK1LECpY%2BAqTnUFhsta1Ce%2B1G3inuZCrySsgqwqFNRkwu9R8m%2BWIwscEHrharDBGjSH5tT4f9kcIoop4da6Vqt2GW0tbiQHbikbkH57UwHNFLbSvb3CNFLExSSJxxKsuzKwPQjOu3%2Fmq%2B1mFo1C2NmFMjhTQLGu%2FKRtumQfU%2FMWh3uoTXx0r1pJePKSWSnJlVU5lQNufHnhQxmOYxklSPiFCDvUZQKUIpUn7Jr0%2B7D8eYrBP7nRbUf61W%2FhmPmtkBFvptnEezCMkj78CpRZOY5w%2FBmWhBCjseuSyyn0pIpIr2CSr2gtbYTQt6aylufPp8PBWbCZPNurROskYgVkYOv7tSAVNRtg2780%2BYPNd5CdWuRJHEWkWONFjRaih%2BFAPljsqLeRrVh%2BjQt3GBRoXBBKAfFwag%2BLJ15B17yfealE2taTHcXMG63Eq%2FvbYL%2FvyH%2B6eFK%2F3iJyT9vAXkPygPNEuoxx3KQ3VrAHtYWP8AeOxpv%2FxWqijMv2WZMLdS8vBLx4pg9jqVqTFJIvwup6fvP5lP%2FDLillv5i%2BStPtL867pEEcem6hxkivLWjIk%2FevHb05ftZF9Okd5Et5mayvoWEtrdRmnCUfYmhYfzftp%2B3k3%2FAC68zLfx%2FwCFdWt1eElrbUoW%2BwGIrDPGv7CyU%2BLj%2B18eIeavKM3lq7WqfWNOkYizuXFeNesE9P8AiX7WNbqCncfm2fzRoUmg6zEqa7ZPDPPCv93eQRMC00A%2Fa%2FZaWLCHzf8Al7oOra5b6h5KuBHe3rj61Ywr%2B5Xlu0%2FL%2FdDfzx%2F8Qwpup4VWBrORhfW7iS0YkrJbkdRLKv2om%2FY4fbzsvkXU7fWNHju3jhj1ZAI9UEKhSJvEmg%2BF1%2BPEhXgPm%2B98y%2BWriTyxrM0iCMBo5VVW9aDoJIZm%2BLif5ftRthN5X8zxWXm%2FTZ9dHq6HH%2Foz2z1eNLd1Kq4TvwY%2Bo2el%2FPXkjTPPGkNY3n7q7hq9jeqPjikp%2FwANE%2F8AuxM8mazo%2Bq%2BV9UuNI1KL07y2PFlbdWU%2FZljP7UTfstih9Jy6d%2BVV3f2dja3ltHe6ipezS2mrzAFfh%2B2iMf2K%2FE2Xcfk5ojoTZ311bz1JEjFXG%2FitE%2F4lnlh3USLKCY7lSGLQ7UI3DA9mz0L%2BVP5wLqxi8s%2BaZRHqIpHZX0hAE9NljlPaf%2FK%2F3Z%2FrYqqal%2BS%2BoXb2EYvraWG3kcyvLG3IRuwY%2BkgPFW2PfJjZ%2BRruwuFa21iRYFACKFKsKf6r%2Bmf%2BReTUDxy6Yqo20LQQpE8rzso3lkpyb58QBi2bNirs2bNirs2bNir%2FAP%2FT7%2FmzZsVdmzZsVdlEA9cvNirzb8zvIf6bhOu6REP0tbL%2B%2BjUb3EQ%2FZ%2F4yp%2Bx%2FP9jOHagvqWsMw%2B1ETGwPWh33z1znI%2FzL8hqEutf0qP4JRzvrdR9lwR%2FpCDw%2F37%2FweRnGxTtNBreExw5DsD6Sen9F4gpphto2prYSSQ3Ketp11RbyAiu3QSoP50%2F4ZcK5YzG5Q7eGPEUyRpMykRSEhH7MV2IzGAkDY5h3OSEMkDjyCxL8beaZaglxoOpxTW7qLSWkmn3cX2eJ34nx%2FwApclMsen%2BcdIeC4QCZQPXjH2kanwyxe2RjT7%2B2e0bRtVHLT5SWhl%2Fat5Ozr%2FxWx%2B2uJWtxe6HqSw8wtxDvBL%2BxLGegP8yOMyceQEbcuo7nnNVpZ4JmMtwd4y%2FnD9bENU0y80C%2BazuRyTrHIPsunZh%2FTKil4kSIaj%2BGdZ1HTtM846SXUenKv2gN3glp1%2FykP%2FD5yO9s7vRb2SxvU4sh3I6MOzp7NkyOo5OKmSuGUMvQ9MMdF1m40S%2FjvoAHUfDPA32ZIz9pGH%2FDIf2Gwjt5Qh3%2Bw3%2BdcFnrir0GW5Kyx6hav6ltfqDHddW5Dbi1do5V%2Bw6%2FzZLPLbQ6nP8AWrpAby3oJGI2kXqkrfyyqR9rOU6HrKWHq2N8Gl0y6p66Lu0bjZZ4v%2BLF%2FaX%2FAHYmda8p2i2tk8sjrI7miTITxkj%2B1HJQ7huJ%2BLJR3K3snuq3fo2ruoqQC3idvsgD%2FWpnMJ%2FJ%2Fme4Msq2jC9tuUaXcXER3drJ1japH72NW%2BFmX%2Fit%2FwB5nTdIksyzW0c%2FryxVZgzBmCsa4eCZAKHDIWgbPDvK9jquh6xaW%2BtW81rDL6lozTKaFJQVAB%2BKtfhwvs47myvL%2BNQfTs2kD9UK8gQKlv2Xp8StnR%2FzQ1GW08u%2BlZO0d3dTxxQshKsKHm7hh9jiq%2Fazi6MxEiI5YOa3NyxJMjVrux%2BIrlctmQ3TS91E3jCG2jHI09R1qOdOgFf91r%2Fw2CLKFYxyJBIFZJOwHgMC2NryG3wRnq56t%2FZhusZiihu7cMWt5ir2zpyikUAU9T%2BYcuWR5pdPqiacQrWqXCzR1USVBAb%2FAHYp%2FnwutdVhtdTa%2FgWVBOAk8BcNG9BTqyk4c6rc2dxe2F3c2kcKJGI7qx4kUjpyR0B6Vr%2FNhBqc9haSxpHERaXRaWIt%2FuvcoTRa75PlyQn%2FAJZvYI%2FMUS2SNELiVTEygUjnVW9OlftI8hVWVsnSXVxfTGS6laSViCS2wodjsNs5%2Fo8rrL69qwd6pIrqAw%2Fd%2FYPw9OmSua51GVJ762swQXLCD1AiryNQrMfi2%2FyVwxKkIjW7%2BezjR7PTpr%2BUbv6WyUH8zfa6duOEPnjT7SEWmrxxkSXatazowA5KY%2BScgD9qIrxwQ3mPX7EetqFhAtqPtegzySAewA45F%2FMHm1dcRIGjMdvC%2FqRIKcuVCvJ2%2BR%2BzglLY2yhjnLaESfcEquburW8M5HpzKvIr1FaDl79cRbRr6S8%2Br245GCoac%2FCjKp67%2FwAww%2F8AL3li01qBLgPJO8f7t4wP7s%2FsE9fhOHer%2BVtfgg%2BvabcJe1FJrPjwZVUU%2Fdy%2FR9llyIFixuEzjKEjCYojmGM6jaajZ28RjBiEisJzULTi3iSKR0wboHkTXdWt21OLT5ru2A5RlN1k8SrsVWT%2FAGD5ena1aXA%2Bo6pD%2B9J2WcenL%2FlBJR8D%2FwCrnSfy880W%2Flyzl0m7uJZtJR%2BVmXSsltyNXhYD7cP7ScfiT%2BXDXcxosR1D8rvP2qWUdlZadHZW7kPcPcXEYeSn2EKR8%2BKJ9rh%2FNkRu%2FwArtc026ey1FooJo9yKs4IP7StQclz0brf5jeXtKhR7eYX0sieoiQHYL29Rv2Cf5T8WQ7W%2FMj%2BZrOKd0t1Ct%2B6eDk7xMaj05CaNxb%2FVwEgCy249PlyECMefJ4235fXQ63Uf%2FAt%2FXC7VvKsmkfV7iab1dPlISW7jTeFz%2BxJHWoPg37edQ5kyNDIvpyx%2FbQ%2B%2FQg91OB7hFCurIssUq8JoHFUdD%2Byw%2FU2IIIsMMmKeOZhkiYyHMF5g%2BhCNipcyFf3nwD7cH%2B%2FoT%2B1x%2FwB2J%2BziNyW01%2BNseVtMoMc4G7VH82TCbyxfWMxtUMlnDIPrOli55JIh%2FwCK2ZeMqthYYOMVxBNb%2Fu2HK%2Fsl6JU7Xlp%2B1xDfbT%2FdeFgmPknW7q0eC%2FsZ%2BOoWTck5ftRnrG9PtIfstnatY0iw%2FMfQ4%2FMWh8bfXbdSjxnbmyD47S4%2F5ky55gWcaZqDPp1yZo42Ppz8SvIePHOn%2BTfO2qaXML%2FSHiPqcRf2M9fTYf78Xj8XIfs4qoWsGsQ64Tpp%2Bo31zGbK9WYfFE8Z9TceI4FP9lnpNbWDVdGit9RRbiO4gT1g3QkqCW9jX4lzksE1z5382DU7WzEKoqq5UVAoOIeV9uR%2BL%2Fgc7RDGsMMcS%2FZjUIPkopj0UvHtQ%2FKnV9H1F9X8vXCajyYt9TuQoYLSijkx9OTj%2FlcMS8q%2BaLny95guoNYs2s%2FrfEXsLLxKyJXjLH4pxOdpwl1zy1p%2BvCOSctDcw19K5ioHAOxU1HxJ%2Fk4otNoZYriJJoWDxSKGRh0II2IyF%2FmN%2BXll550z4OMGs2oJsLynfr6Mv80L%2FwDCfayU6RYNplhDYNMZxACqyEBSVqSoIH8uDj0xV8NappV7pd7Np%2BoQNbX9qxSaJuoYfrr9pWwuSNg4NSHBqKbEHxrnrL80Py2g86WX1%2BwCw69aJS3kOyzoN%2Fq8x%2F5NP%2Bw2eXm0q8guZoblGglicpNA4pIroaMjfy0OKvpf8lvMusa5ol1ZazOLqXS3jiiuTvIyOvJRIf2ylKc86eM8y%2Fk35usfK2r3FtfyMtjqoRZZ3qRFNGT6bP4IQ7I%2F%2BxztF1%2BaHke01SDR31aN7mZ%2FTLRVeKNu3qTD4F5dMVZlmylIZQwNQdwRuMvFXZs2bFXZs2bFX%2F%2FU7%2FmzZsVdmzZsVdmzZsVdjXVXVlYclIIIPQg47GSOsaNI5oqgsx9h1xV4R%2BZvkA6VK2r6VH%2FuPlarIv8Aulz%2Bz%2Fxjb%2Fdf%2FA5EoJLK%2FwBFh0yOJU1AyrH6oABboqJItebtyJkjZP8AKVs73qnm%2FwAsyW89les7xzwtSMxMRKG%2BHghpx55w%2FwAy%2BWpbIHWNNjeGKFkDjlVw5%2BIPGwp8S%2FtcfsZGUauQHvdvpNWJxjhyyMZA%2BiXQ%2BUmMXNtcWVzJaXSGOeJisiHsRtll1uLf6rcMeKb20vVoW%2F6pP%2B0v7GHsLr5ptvq7mOPU4ElmEhHH1eCII4kp%2Bz6aO7%2FtephBNBPbytDcRtFNGaPG4oR7EZVKJHrhz%2B92JjDPA4cw3HMdR%2FSiiNG1m7028EiEC4j%2BGWMn4JU7qf8AjVslmvaBp3nLSFvbCiTKCY2P2opP24pKfs%2F9d5BTFFIys5ZGX9pKch9B65JvKmrSaVdevxea1m%2BCcEFQyj9tR%2FvxMtxZBIV8x3Oi1eknp5Ud4n6Zd%2F7XmskVxp11JY3sZikjbiyHsf8Amk%2Fs4caPay6peQadE6rJMwVXboF6k%2B%2FEDOn%2BdPIkfmSyGp6QA12qc4JFpSVPtcG9%2FwDjfI35a8qzaFNa3urKrXLmscfI1gP7NSv2n%2FmywxouIN1DWPKDW95DY6K0t5IfgnaUKgD05cl49I6H7TZ0bynBAmmjSVvhcz2qEM60%2BHnVRw9om%2BH%2FAFsItfhndg1q%2FpNdxmIuDQVU%2FECf8oYReSLk6R5nWBw1bmT6sSD8FGBb4h%2FNyXDyPvbsWITE9%2FVGJkB30jPKV1PpOsrBduRLFcPb3JY9VditW%2F2XxZ0%2B91O2spIoJWL3E54wW0Y5SOf9UdF%2Fmdvhzm%2Fm2w%2BoeapJlBVdRjWdCDsZE2cfeuHdvfQW0BngZptVvEH1u%2FlG6gj%2B5hH7Kr0xHc0pD%2BYg8wa1NDFZWjSWtsrCRoiGJcn46b1YdF2yFWemzxsW1CN4Ej%2FZlUofubOs2WpxpNGl2oMP2eSbEe5yTXGkadqFpJaT26T2860YEVBB6EHx8MeCzdpunlEtpHYQ38GqRrFNaPA8EgJcFa%2FGqhadao2XBGlmbe2uCzevbGeRo2PcNJx%2F2S%2FDht51s3sLpYLoH6ncIq21wRU1UceLt9Hxf8Hhj5f8pXerWc%2Bq2VrHqE1oqQWdvNLwUoijmJB%2FO9W4%2Fs8cjVH3LezEvM2mXWqyabNozB7SS1WJ5SwBrHWnLv8A3ZUbfy4onlazmtrKC%2BJlNoHFFNFbmQaHvtTJ9rPlzU7GOO6vrRbeNioQQMrRxmn938IXj4YE0fQLnXBdQ2062l1CP3SSry9Xav7og0%2F4LDsikps7C3s1WK2iWJB%2Bygpg5lNOX0YAVr7TL0xXsxcxsVlhktwpFPtLVWwbf%2Ban0e8tp7LTYrmyILlpyT6hoRw2%2Fu%2FTbdlw3taoK4v7a1dY2dW5GjQ1qVHj%2FwA24Vah5a03VlNxZEQTtvyX7LH%2FAClyfjyfo3nrTrbV9PRNI1i6gM8qRENC8gb05OUI%2BNR6nR1%2F4HIRqOj655Vu%2FQ1SBogT%2B7mU1ikA7pJ0%2FwBi3xZGwdizhOeOXFCRBSPSZNX8maml1MhNsfgldPijdD1H%2BS37S8s6ZLrEEZt2uEe1W6VWtLsg%2BlKGHIGOReS%2F6wOR9dWtRp11dSQ%2FWHt4Wk%2Br93IGyU%2Fysk%2Fk%2FW5INATT9Z0eAWczGT6gr%2BoIlfcgBxsf2vT%2FAGMYxMSeE2D07m7PqBmjEzjWSO3EOUo%2BY72M%2BYfLcd3FJeQRxyo7EzQU792GQ60bU9Hkf9HTAwts1jdVeNl%2FyX%2B2n%2FEc7ZL5XstVSS58p6kYZqVbTrysif6tWPrRr%2FspVzmuvaDq2jzsup2JtA5NCfjtmNf91Tjb%2FYyccJ8tmgJPqV4JJLJpI3sYLqJ2uY%2FtBgP91q6jpIy8Vw0sNNn8tG11m8KWNnqTrAums5YhTvyct%2B0R8fw%2FYb%2FWwLdRpfaVEkkPO50r1HhjJALRSg9HpX92%2FwCzgSW4TV9Hga5uppdVspFt7a0FOMNshHJm3VuZY8fV%2B0zfBlZHq3BNigOnm52LIfDHCYwMCJmVXI8IqMYnzI4f85l%2BsRQc7eSKVRPHKts4ruUkPAcv9R%2FiwbZaY1p5gtrHUoipjnjWZOo4lhRv8pGwcPL9pcROZo2S6ZkaXgyqoaFVk4hn%2BJ9uPqNxwVrmsWupvYzalF9S1K1lTi6GscsYYMpV%2FFWUNx%2FlwYoSjd8jybO0suLMcUsdmUQROxRrnH75PQtd8vaT5itBZapAJI0YPE6njJGw%2FajcfZzg%2FwCZWh3GhW%2Fr2wH1vSZ0DXlPia1m%2FuZuK7MV%2FuLj%2BbPQ1pdR3ltHcxGqyCtOtD3H0HIF%2BZFnb3k8EMqhxPBJHOh6MnIUDfSdssDq3zfqWmR6lFJqenRCO6iHK%2Fsk3FD0ng%2FmjfC3SNVudJuUvrKQJdQOrxc1DoafaSRGqrK%2FRlyU32i3XlnU4RHOYrF2Isr1hy9Jm%2F495%2F8AimT%2FACsKte0Zp%2FW1Kxg9KaL%2FAI6Nipr6bH%2Fd0X88En2lfFX0%2FwDlz5x0fzbpAlsIYrO%2BgAF%2FYxBVCOR%2FeIBTlE%2F7Lf7HJpniLyz5m1TyrqVtq2ly8ZYzuvVWX9qOQftI37WeufJnnHTfOekJqViwSZaLd2pNXhk7qf8AJP7D%2FtYFZJmzZsVdmzZsVaOct%2FNj8sx5ntJNb0NfT1uBayxrt9ajUfY%2F4zqP7tv2vsZ1PKOKvhz0CsbwMzqSSHPRgw2%2BL3%2FmwrlheB%2BD9exHQjxGeqfPH5Oad5lupdW0icabqU3xTqV5QTN%2FOyr8SOf2nX%2FgM59H%2BQfmu5nWC9ns4rbl8U6OzMB%2FkpwGKr%2Fyk%2FOBtOMPlnzTMWsyRHY6g5qYq9I5j3i%2Flf8A3XnoxWV1VkIZWAKsNwQehGc68sfkv5Q8vBZriE6rdrQ%2BpdAemCO6wj4f%2BD550VVCqFUUVRQAbAAYq3mzZsVdmzZsVf%2FV7%2FmzZsVdmzZsVdmzZsVdiNzNDbwSTXLBIUUmRj0A98WwNfPaJbSm%2BKi2pSUv9mh2pirAPON5pMWiStoiPJfM1bVI4yy1G8jDkCnwrhDrD6jFb%2BWbC7Wt36Ecl%2BCP%2BKiZHbtyJajf5WDvP9xZRaNCfKtxKb4zj04IOR403JKkfAOfH%2FWws84ajNd3EEklFuSOUtK%2FDRVXiKduQOFIeeatpk0F5PeaUj%2BlATLIYwSYhXd%2Fh%2ByleuLP9U12ykmiWO2vrWOW4n3PxgcAFXmaty3cf77%2Bx9jO1%2Flvo0EejT388SvJqDMjFxXlEvw8TX9ls5h%2BY%2FkSTyne%2FpHTVY6LdsVUj%2FdDt1hf%2Fitv91t%2FsMgRW4dxo9X4vDiySqcfon%2FO%2FoyYJsQGHfJL5FsrXUdZk0m6uTbR3UTvHQAgzRjkONSODla5GgcX0%2FUJNL1S0v41DPFINm8G%2BE%2Frym%2BHMD0k52txeLppjqPUPfF6l5X8xWlm995evZlguLSdkjR9gTWjrXovP4XTEPM6wwzKSKCT4tuoIO%2B3vke80xXU2tW13ZQ%2Bo15bq8npJ8UlELVp%2B3sMBWOsXOtWy2iEyXNuCyIw5Myf8VsaseFP7tvizL4tq%2BTzNUfvT%2BOU6npky9ZrciVAO5Xw%2Ba8sis01rp%2FmC01O5YrSWJ4kXcyFiB9nsK%2Ft5fljzLXzMlg0bRQujQESbVlDcl5IenL4kyeebPIp8y6j5e1DSEjX0XWO4QsqKYk%2FfRgV%2Fb5gx8cHMe5lGUom4mjuPnzSW%2F1OfVL6R7sJW2keKFUGygGhIJ3PKmNVy%2FTAk1pdWc9zBdrwuElcTIezA%2FEMj%2FmjXbvQtaW3tEBgCJM6PuCHFQExtizSKI1DHJv5VuxKrWMp%2BwC8JPh%2B0v0ZCNLvrfUrGG9tjWOVQfkf2lP%2BqcPdHme3vIZV24sKn2Ox%2FDJBSyzzBplrqWnSQSxLI0P76EsoIDqO3%2BsMgmlaxceUdRXU7ZS9jJRb62XoUP7QH80f2k%2F4HOgXdyVYgHp1%2BWQW8g4TzQyoQpJIVh1VuhGJCh7FG9hrempKhW4sryMMp6hlYV%2B%2FOWa%2FbXfk%2FU0ZJOEL1ayu2%2By1P91udv3if8OuCPJ%2FmhPL1xaaJetx0y4rFFMx2imJJSvhHLXj%2Fr4N%2FO9G%2FwAJ28o%2FYvY69%2BqOMgdlYzqPmWy8xr615aouoxAA3dk4dJB0%2FfRGjofBl54U%2BnCySRNSa1l%2B2vQq3861%2By65GPKcztqxjY7NEw4jYbU32zoNppF1qfrw2JX6yierHExoJivWIf5fH7DYYnZaY3pN9N5N1n9JJEbuIwyRoisY1kDbpVu3F%2FidM7xpl3Y%2BadFt7m5tle3vIUkaCYBx8Qowof5WB%2BPONG0jvLY28qlRUgqwoyOOu37LpiXl3XbjyZqk7XglmtZIfTEKNQGh5REFvsxr8XLjgIrdWQedvy7GiQPrfl4SNaxVe5s1q7Rp3ki%2FaaNf20%2B1kT07WGCqwcMjdJFNQc9C2N9BqNvHc29WiljSVGI2KyCopkFv%2FwAotHu9cOq2d1JYW0x53dhAo4O53JQt8MYf9ocMFyB23ZwGOQIkeCQGxqwfIsatNQSQrIrmOUbrIhoQfEEb5LLHzPP6X1TV4RqFo4ozFVL0%2FwApD8EmFms%2FlxeWBNxoEpuIhubSQj1B%2FqPsHwo0%2B4ltUmW7Ro5ojRo3BVhT2OWCi1t%2BbPJ1pFCfMXlJgtl%2Fx92VCyRHuwjb4kX%2Bdf2ciOlG2huhcX2n8JI2BE8FHHzMTb%2F8TVc6FpWutaySPblJEmHGeCQVVwP1ZGtcsIbe5N3p0Ui2MlWaM7mB%2FwBpQ4%2B1F%2FLkTGulhnDIYnYprf6tYaxaxxCDlxIaG7iko8Z7kGtWr%2B3ywxsdGl1WwitzGbkgkRzugDGnRm34R0%2FZbIJBZwxv9YhAVx8Qp0JPiBnafJl1eXekLNdyrKOXGNgADQD4g3Gg%2BE7Y8WyTLfiHNJbbQvNHle%2Fe50eQX%2BnXAD3Vg53EoWnKImhQmn7Pwv8AyYRX9xql1dmXWOS3XHiqMnp0QEkKq%2BA5fazrQwHqOmWmqQGC7j5D9lxsynxVsFsLeO39hb39tJbXMYlhlHGSNuhB%2FUffOZXy3%2Fl7UV05n5MgrpN1LTjLCT8VncEj%2FYf5Odt1nQbzR3JcerasfgnA2%2BT%2FAMrYB%2F5V3Y%2BdtAvhefubrn%2FuNux1jkQUYkD7Ubnir%2F6uEq8D1rTFk9XUdNgaHix%2Bu2XVoHPcD%2FfbfstnQfyAeb%2FFktC%2FptaSF1VgFNGTiXFfiA%2BLj%2FlZC9WuNd8p6kNM1e04anYsY2mY7TW%2FZG2pKjfsS4K003mkCHzl5WmDLazercWoB9WCvX1FB%2FeQt9lv8nIofYNRl5FPI3njTfO2li7taRXkQUXlp3Rj0Zf5on%2FYbJXirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F%2FW7%2FmzZsVdmzZsVdmzZsVdgHVY9PmtHi1JlW3cgEuQBUbr1wdgPUdNttTtzb3IJWoZSOoYdxirzHztP%2Bi20aTyvdm6uDcJSCMJIPTVhRWKj7Bbbi2B9b8keZrnV3FvberbsQsVwHUKFJ6tU8hSvhk%2B%2FwAPGy1GwvLOkqK5ju0cIP3YQ%2BnItAPiif8A4nkkphKQaQOk6eml6ba6fGQVt4whPif2j%2FwWO1LTbPVrGfTr%2BITWtyhSWMjqD%2FEfs4MzEVwIsg2Ni%2BVPOHlm58p65NpU1XhP72znP%2B7IWPwn%2FXX7En%2BVgW28s63f2kWr29m76Ys6xyXQpxUggNUfa4iv2uOen9c8t6J5it0t9as0uo4zyjLVDKf8l1oy5EL7TriGW30Lytp7Q6Dbs0d7xI4Oz%2FFWPlycmNvib%2BfKziufETs7WXahOn8Ph9dVIn6a7%2FewDzPd3ukNplzYtxtEhW2adVBKzRktTkalOdf%2BBw60bydZ%2BaNR%2FwARaPq0VjOIw509bcLIkw%2B16yh%2FjRm%2F3an28de6dLYTXGk6xDztpx8aHoyn7Lqfb9nI3YX%2BreQ9ThS3pcWhcvazMBWSE%2FbgaQ%2FF8P8AJlrrDvunvmryB9buFvzAtnrkQD84z%2B6mKHkGRtu%2F82HWnO%2BoadLpU5aC4ZeUL9Gil%2B0jKR0aOYZOLO80jzho0d3Zv6kMn2WFPUhlGxVh%2By6H7WQWaO80rV1hvwBMjcfUAoskTbLIP%2BNsIr5oSu10tfOt7Ikt4th5ijQC6hkSsdz6fwfWIiCCr7UmTj%2Fl5JG%2FKTSdTjg%2FxDILh4UCUgHD7P8Axafj45GPMdkYtRkvbWf6jewEXNhcr9ppT9pEVatJWnxJx%2BznRPIfmz%2FFujfW5Y%2FSvLZ%2Fq94gBCmQCvNQfiVX%2B1xb7OA2NleaX%2Fkr%2FA19Jb2rO%2BlXbc7UvuFbuvL%2Bb%2FiWC7M7HOuarpdrq9jLY3i8o5Bse6t2df8AKXOP6jbaj5dvpLK5USBd0fcB07Ov8cMT3qkP5l6trkWp2kNi8sVr6CShoyVDyH7RJHXj8OQpfNfmCN1%2BsXcsnBeCrOfUAUdFHP4uP052ny%2Fq2g6veR6T5hgX94Almsp%2Fdsw%2F3W%2F%2BU37GSHUfyp8n6hUpavZse8DkD%2FgH5p%2BGCXNeTwM%2BbzeWxtr%2B3SVHFGMZKMP8oBqr%2FwANh9qv5gpr3kVfLV9zk1O3uIjFOwrzt4w1GZhUeqn2P8rJD5i%2FJJrK2lvNKufrSx%2FEYCnCXiOtCpKPx%2F1M503lq7tpDxDNUUUGlPwyJtWQflfpMWseaFtLgssZtZnDJ1VhxCn3651e38qanp%2Bs2ZX95brKG%2BtR7UVfio6%2Fs16YS%2Fk3pGmWxvNR%2BspJqfEW7WnR4kryLFW3PqN%2FL%2FLnW8I2UsW8x%2BVo71n1LT0CXp3mjGwlp3%2F4yD%2Fhsgl9oyXUaxX1u8LoCY2dStV%2FaG%2Fgc7JTEbiztrr0zcRrIYmDxlhWjDJCXQoeVeW9Rv8Ay1PKL25kuNO9ELEo%2BIjgaoKk%2FAqJyzqtrdQXkCXNs3qQyqGjcdCCK7ZFPMPl1Yg95aLWA7zQjfjXqw%2FycB6JrNzY3BW7uAbARKgUqSQVIC8KfCq8K8vhxq9wlntK4DvtJ07UgBfW6TU6FhvTwqN8FRyRyoHiYOjCoZTUEHH5FDC9Y%2FL6wuSZ9Ic2M3X0tzEfo6p%2FscL9B%2FLy8XVJNS8y3Yuo41aKz0%2BEkQrGwoWlO3N2zomVTDZV4l5n8s3Plq94oS%2BnTk%2FVZj27%2Bk5%2FnX%2FhsMNI893Wj2ht2thNVSEp8PGSlBK%2F%2BSf286nqOnWmqWcllexiSGQbg9QezKf2WGco13yhdaVMUT95A39zL0DDwPg%2BIZc3pPlzU5dStGa4IaeNgGZaAFWHJWoMOc5v5E1B7Wf6tcAoD%2B5PLb3jP0fYzo4xIYrZIo5UaOVQ6MKMrCoIPtidrZ29lAttaoI4UrxQdBU1OL5sCsO8%2FwD5e6V5708RXNINQgB%2Bp3oFStf2JAKc4m%2F4XOIWX5R%2FmVoOpummwIySAxNMssZhZG2q3Mqen80eeoKZqYq8l8h%2Fk%2FceW9Qt9Z1DU3W4gPJLS0JCEGvwTytT1U3%2Bx6arnWs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F1%2B%2F5s2bFXZs2bFXZs2bFXZs2bFXUzZs2KuzZs2KoXUjILG49E0k4EKetK43TYkhsoQp5EqGZvEnvgp1DKVbdW2I9jhDdaDqT%2FuLLWJbOzP8AulEVnAPVVkb4hiqB8wWUXmS%2BXTYWQi3ikZphuUlqOKmn7P8ANnOtRsiPW0TWUKqD9ofajYfZkQ%2B3%2FDLnYtH0Wy0W3aC0BJc8pJHPJ2J8ThV5u8tJrdoZrdQL6EH0z0Lj%2BQ%2F8a4bTbxXQPMGq%2Fl9r7mSs9jcUN3Cv2Zo%2B1xD4Sp%2FzZnaNYhtfNmgRaho7JcMwE1pKCASP246%2Fsn%2BZf58gsugWWtaPFp1xRL21qFkFCyP3U96YR%2BUfMt7%2BX%2BqvpmqEtpUsnGeOtQjE0FxD%2FwAzUxOy%2B5kHmGCOa1s7%2BVeEsFYpHI3SvwyBvDi647RtH1by3qEXmPT5VlgnWmo2C1Amj6rIrH4RMnVcCz3FvJ5x1pIqX2h30cVy3ouv2nQEeka9XZW5ZJNPult7dbZpTLasP9HlYUalP7uVf2JlH2v%2BEw891ZzY3tvqNtHdWz84pBUHoQe6sP2WX9rC7zJoEOu2Xp7LdRVa3lPY90b%2FACHwi0IahBqtdOo9hKf9LjY0Vf8ALU%2F78ybZFD591jS3R3gnRo5Ymp4MjA9snHkr8weRi0LzJII7pQEtdQc0SYdFWQ%2Fsy%2F5X7eH%2FAJw8uLqMDX9qg%2BtRL%2B8Uf7sQf8bLnJbuyjcNFKnND1U%2Fw8Dh5pfQo3Gc487eVFhMmq2KfuHNbmNR%2Fdsf92KP5G%2Fa%2FlyPeXPM%2Fmfy2Y4pmk1PRqfBBKpNwiDr6E3%2B7OH8j51%2B1ubXU7JLiKklvcJWjDqrDdWH%2FEsV5PntoFiuUuQOFxFvHKpKsPpWnLJZ5b8w65p1xEguXuIp3q8d07P1%2FZVm%2Bx%2Fkccbr%2Bhm2125sbPggX95AZq8FRhy3p%2FL8Srk181aDHqempqtvRLu2hDngPheMDmV%2F2P2ozjsrKLW5ju4Eni%2By4rQ9Qe4OL5D%2FACp5hgu7iOwkkUXFxD6yx9DWM8HNMmGJFFDRFRQioPUHIhrmgrAXubdK2z19WOleFepH%2BRkwymCkUIqD1BxBpWIaNq09o31WURrZoiem32ePEheCIB8VV%2BLJcGB3BBHtka1LQXVne0XnC9eUQ%2B0tevHxXA2lxazHfW620rx2atW4gkSqFOPCgdvjDClVwkA7hWYZswzZFXYnNBFPG0UyCSNvtKwqDimbFWE6voE1hIbu0Be3BrQVLIP8rxXJRo92b3T4Z2%2B1Ti3zG1cGkAih3B7ZSRpGvCNQqjsooN8NquzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Dv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxViPmbyo91cfpvQiINVT%2B%2Bh6RXSD9iXwl%2F33N%2FwWQzW9AXXo%2FXaFg7L%2B%2BhK0mjdBuafzACjrnYcDTWNrPKk8kYMsZDI42NR40w3tRW3z1p2l6ho%2BotLAfXVE5OgDVljA%2BLj%2FxZGv7OS2BbeaRbscpI5YuLQq3ETIfiQcv2ZVP90%2F8AsMk3mi1FjqBnt0CpcIHIUU%2BNT8RFP5tuWRDTFEulwPaP9YjVSfVjBIFGPM%2F7BvHCOSXqXlu6trnTI1t7Y2Zj%2BGW1YhmQ%2BJcfb5fzYcZBPKl1I7GRXHKGi3AY%2FajPRvdsnQNd8BFFDjnL%2FPWhJaXguIKRw33KngklPi%2F2Pxcs6jkY892Ul3onqwoZJLWRZeAHIlT8D7fJsAVBaLpUeueS9PtriYPcWykQXkfZ4yVDD%2FJP2WwX5X1NJ2NrQIGXkoXpzQ8JP65GPJfmOTR5G0zXLmGCzlYmzhZPSeE05ceP7UTjfliOhagkWs3CW0gkUXbPZjoZVkY8kQHr8OSHUJZD540vmYtVRBIAvoTo260JrGzU7cvhzaJ5jgm0t9O1u7giu%2FRdY%2BJ48oyCg%2Fd9QyfZ%2FwArJhPBFcwvbzqHikBV0PQg9sgt%2FwCSLpJy9pxniPRiQsgH8rfzYB5ql%2Fkq1hXU4ZigNwjsizftemU3Uf5Jzp%2BRXyloF%2Fpj3FzqYiR3YraxREsyx%2FzTOaL6j%2Fyp8CZKsSUOzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Hv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVg35iPrMEFld6XbR3CRM%2Fro1fUIIHwx%2Fs4U%2FlrqOkz3c1lYQmENFJI8DCgRzIDIg5b1JavTOiajZLf2cls1KsKofBh9k5ydpJPL%2FmzT9ZcBIWY2t5sFoD8Ls5C1%2FwCDkyQ5KyjWdLGi6ml3brSyvKxuo6I58P8AJPVclOiXZurPi5rLAfTf5fsn7sE3drBqFq9vLR4pV2I7dwy%2B%2BF2j6Xf2F5dS3EqNbyhViVa8iV%2Fbfag6%2FZXBdj3KnWURXLzYFYV5n8n3us6ut7btEITGsbc6hl418B8WFd75Ym8svHqVqPrQiCsJSPiikH2jQfsNnScayhwVYBlOxB3BGG1Sby7rf6YgZ5KCZftIBSmHeFllodlp95LeWqlDMKGIfYB7kDDPE%2BSuzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F%2F0u%2F5s2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZFfM%2BipLW%2BSJZAf71CtaMPsygf8SyVZRAIIO4PbCDW6pP5YvDd6Wiu1Zbc%2Bi%2B9Tt9mv%2Bxw5xG3tbe1Vlt41jDHkwUUqfE4tgPNXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Pv%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV%2F%2FZ'); + background-position:center center; + border:1px solid #00aa00; + } + + .yui-skin-sam .yui-h-slider { + background: url(bg-h.gif) no-repeat 5px 0; + height: 28px; + width: 228px; + } + """ + + output = """.yui3-skin-night .yui3-dial-ring-vml,.yui3-skin-night .yui3-dial-center-button-vml,.yui3-skin-night .yui3-dial-marker v\:oval.yui3-dial-marker-max-min,.yui3-skin-night v\:oval.yui3-dial-marker-max-min,.yui3-skin-night .yui3-dial-marker-vml,.yui3-skin-night .yui3-dial-handle-vml{background:0;opacity:1}div.base64-singlequotes{width:100px;height:100px;background-image:url('data:image/jpeg;base64,%2F9j%2F4AAQSkZJRgABAgEAZABkAAD%2F4RfJRXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAABAAAAagEoAAMAAAABAAIAAAExAAIAAAAeAAAAcgEyAAIAAAAUAAAAkIdpAAQAAAABAAAApAAAANAAD0JAAAAnEAAPQkAAACcQQWRvYmUgUGhvdG9zaG9wIENTMiBNYWNpbnRvc2gAMjAwODowNzoxOSAxNDo1ODowNQAAA6ABAAMAAAAB%2F%2F8AAKACAAQAAAABAAABwqADAAQAAAABAAABRQAAAAAAAAAGAQMAAwAAAAEABgAAARoABQAAAAEAAAEeARsABQAAAAEAAAEmASgAAwAAAAEAAgAAAgEABAAAAAEAAAEuAgIABAAAAAEAABaTAAAAAAAAAEgAAAABAAAASAAAAAH%2F2P%2FgABBKRklGAAECAABIAEgAAP%2FtAAxBZG9iZV9DTQAC%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQADAgICAkIDAkJDBELCgsRFQ8MDA8VGBMTFRMTGBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAENCwsNDg0QDg4QFA4ODhQUDg4ODhQRDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgAdACgAwEiAAIRAQMRAf%2FdAAQACv%2FEAT8AAAEFAQEBAQEBAAAAAAAAAAMAAQIEBQYHCAkKCwEAAQUBAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAABBAEDAgQCBQcGCAUDDDMBAAIRAwQhEjEFQVFhEyJxgTIGFJGhsUIjJBVSwWIzNHKC0UMHJZJT8OHxY3M1FqKygyZEk1RkRcKjdDYX0lXiZfKzhMPTdePzRieUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm9jdHV2d3h5ent8fX5%2FcRAAICAQIEBAMEBQYHBwYFNQEAAhEDITESBEFRYXEiEwUygZEUobFCI8FS0fAzJGLhcoKSQ1MVY3M08SUGFqKygwcmNcLSRJNUoxdkRVU2dGXi8rOEw9N14%2FNGlKSFtJXE1OT0pbXF1eX1VmZ2hpamtsbW5vYnN0dXZ3eHl6e3x%2F%2FaAAwDAQACEQMRAD8A9VSSSSUpJJJJSkkkklKSSSSUs4uDSWiXRoOJKFi3Ovx2Wvbse4e5gMwRo5soypdOyN9mTSRHpWvj4Oc4%2FwDVf9WkpupJJJKUkkkkpSSSSSlJJJJKUkkkkp%2F%2F0PVUklCqxttbbG%2FReA4fApKZpJJJKUo2WNrrdY8w1gLnHyAlVOsWvowXZDZ%2FQOZa8D9xrmm3%2FNr3PUswm%2FpmQGamyl4bHiWuhJTYqsFtTLBw9od94U1Q6FkfaOk4tszuqYfwV9JSll1%2FoOt2sOguDXgeTm7f%2FPmN%2FwCCLUWZ1VhrysbKA%2Bjua7zgtuZ%2F1FqIUXTSQcvKZi47sh7XOY0tBDYJhzms3akfR3blVd13prMtuI%2Bwse%2F6L3Ahm7n0%2FU%2Bi2zafzkFOgq9%2Bdj0ZFOM4k3ZDtrGNEnhzt7v3WbWPU8bJpyavVpduZuc2eNWONb2%2F2XtXOY2VU7rA6hnO9RuJS%2Br1hO1lguGJbuYz2extvv3fzLPU%2FwCESU9QkkkkpSSSSSlJJJJKf%2F%2FR9VWf0iwiu3Ff9LGsewT%2B6D7P%2FAyxy0FmunH6xPDMpgd%2FbZFT%2FwDoOx%2F%2B20Qp0lC02CtxqALwDtB4J8FNJBTVovx%2Bo4jvbLLGlltTuRMssrf%2FANJiz%2BhZDqMO%2FEyCX2dOc6t%2Bkuc1gmt%2B3%2FhaPTeoZ5s6R1IZrBGDlkNvI%2BjXcYYyx%2F7tWV9B%2FwDw%2FwDwlyD1LqWH0jq2N1O55rxOogY1rwC4C9uuP6mzdt9Wl1v6T%2Bb%2FAFdFS%2F1Myq%2F2VThusa%2B2tst2k6sPuY73Brm%2Fo3Vv2fy%2F3F0S4TAzq%2Bm%2FWDquKGnbhWOya62DUUlrcmyulktZ6f2ey19Wz8%2F2Ls2Z%2BFYx1jL6yxgBe%2FcIAI3CXfNIqZX3ursqqrZ6j7TqJgNY2PUsPP0dypfWJ%2Fo9NOT2xrarHTxs3tZkf%2By77VR6v9bejdNsbkb3Zbqm2sfVjgOOhrdZFljqsffXs%2FmvV9V%2F%2BDXHdZ%2BuvU8zA6i%2Bj02Y1zawaZLt2LY21tltb7XOb6j6%2FwDR1V%2FzV36v6rPVYFU%2BiCs5PS7cd43O2WUOB77d1X%2FT27lwvVnNyKK8d15q6hij207o9dg3DHdW9m9zLv8AQ%2F4Wu71K%2FTtr9aqzJ%2BuHV8tmR0zNoyLvsXU8Nr7cdtjmtN1J9K%2B2podsbk1%2Fo%2F8A0YsnG6te%2BnLZbdXXY6pttADNwucwmhv2cWbv0tnr%2FpKnfpKbKf8AtsqD1DvrjnfV70cKlldgzgbqjf7QHB3p2sG0sb6lv0%2FfZ6TEcZg630xz2vezK6jVbfhVtf6biQ%2BxltmNY7a2z9Js3Vu%2FwayvrHh1%2BhhdKv3XubjBhY9wDbHtPpOdXkOH6t1Dd%2FRsl%2F6C%2FwDomV7Fg9Wc%2FF%2Bq3RL6biben5WVjh4lljdxbexltf06La9vvp%2FM%2FwAFZ%2FhEuqn0r6lfXM9WYzpvVoo6wxvcbBcGj9Ltr09LLoj9bxf%2BvU%2Fof5mz9avrJd0%2B2jBwHAZTrcd17iA7bVZa2nY0H%2FCXw9v%2FAAdf%2FWl5zidQZ9ZcY3l3oddwg2z1mHZ6zWfQv3t2%2Bnk0%2FwCn%2FwDRf8xf6Df1DqvXGZ%2Be5tzsi7Gd6lm2kPsqNbqqOG1%2F4Cuxnpt%2FS7%2F9I9ALjR1Gh7f96%2BuJJpA1KZj2WNDmODmnhwMhJaySSSSU%2FwD%2F0vVVn9airFGYSGjEcLXuOkV%2Fzd5J%2FdZU%2FwBX%2Fra0EHM9D7Jf9pE4%2Fpv9YHX2bT6mn9RIKcE9U6lgXnJDv2l0jN%2FS497S39C530sOyxn%2BBc%2F%2Bh5P83%2F2kyfS%2FR5NlPrv16uwczExsPHFozLBRWHz6rriWM9Ouv6Hs9T3vseue6B9buoZF5xBU84BDC%2BtoZYW1PczG%2B02Gxm71Gb2XP2fod7P5mtct167q%2FT87Gdfk%2FbscWHI6V1NrQNGu3O2NZ6fp5FN9dTcrH3epR6f9RKwRYXzhKEuGYo%2FxfSMvM6lZlF%2FVfSobU11Q9MGyqLfa%2BrP9PJsr9Kz09n6bGq%2F7dWN1z9VxC3KfZT0%2B%2BPUyGg5NIaPdU7ezZe7KxPR%2FR5Gyr1sX9Db6%2FwDguZx%2Bv352be4WNwft%2B9tb2n21mza%2F07Xn2%2Fp7qmVep6dbP0vqfolqZD7mY91eDmXh9pBAZDGjT9awvb%2Bkp3W%2Fp2Mdkfpav1b%2BXUb0WulTeP23h5Di3JL3UYWTYzlzbKn1sc5rN7baM3FZ9pw8xrvSvr%2FQf0im6uvH6vY7G6hfS2mx1WFYaq7bnk%2Bo2r0rKPTa7bustxX20%2Bz%2Blf4RW%2FqPXeM403s%2FQ9LpdbQ4v97Wl4DcW1nud6PqfrH2e9nszMavNx%2Fs9%2F2hA6i5r77HZLH24Tn1W4%2BUwj2Pva3Jdj5bfzaHb8n7Fk%2F4P9YxP0tX8wulqaWfb1CzZ6GaM30622WDD%2FSV1mp3sfY6tjWOsY1tf8776a%2FTQ7s27qFNVIrqwAfUsbZRXt9S0N9Ky1tX83%2Bgre%2F1sej%2FAE1lzKltdDtbi9ZJsqqbTaBXdS0TbW68PycUxNj8qrIbR%2FpPU%2FSb6%2FTyv6VWf0qvOrzmsYasnGzHmk7gx1Za6K2XR7sd%2B5v6K79JXi3bPU%2FV8i71RRU5%2FWaLMv6kYj7SGX9L6g%2BqK%2Fosrymeux1cf4B17fYsrpuPmNxftz63Pcwl9DWDQ2sIqZkNJ%2FNZe6luR6f%2FAAX%2FAAS1f2pXX0vq%2BJln7LfkVVta3ZAfl4929myn%2FA76rLq86r%2Bbo%2F4qyhVruoZfSek4ldmM12Pe227D9XQBtvptu3%2B7d6Vr66Xf2PU%2FwiSnsuqnDyLMfFtMZORj%2BrXSPeHFseo2l7g31La%2FV%2Bh%2F2oq%2FwfqfpFznVcd9uDZivaxzDZXa1zztZaXB1LW%2Bv%2Fgb%2FZVXi5tv83%2FRsj9AsLqfXv2j0%2FpXqPFedgNurfa10F3uqsx8lv8Ao3Obvrf%2FAMT6n%2BFQquq59%2FURec1zb7CHXMc8htmw7n47v8BtucPUb6v6P9Ld69iNoS9H6Vk0Zzcpj3tqqeW1AAtuc%2F8Am7Meyhv6WvZ%2FNZX%2FAIEugzuk14eLiN69fbi49u6wgBjyWNb6dP6IOa52V6vt247P0dD%2FAFP0ajjdc6kBeekYQd1DIs2tsZVZbdVXt9tYqsG9%2B3%2FTPZ%2Bl%2FwBCsajpXV%2BtX2Zwo%2Fal7Wuvv9S0Mea2%2FT2%2BvZ7m1e1j6GV76PZ%2FpK0NE7avpH1Z69ndU6Ti4mP6mVZktBdbkP3uqbWTRkjKuYGb9t1e6v2776rP9Iuyw8VuJjtoaS8tkueeXOcdz3%2F2nLyb6vdZv6DjDKwmVt3lzbsZz3VsZcHluTgX499hfRYxuzJwn%2FznsyKrLLv8H2vT%2Fr9hODK%2BsM%2FZuSWkvrtFjC1wLmvr%2FWKqW%2Fm%2BzbZ%2BkRR4vVpKn03q%2FTeq47Mnp%2BQy%2Bt7WvG0%2B4Bw3N9Ss%2FpK%2F%2BuNVxBT%2FAP%2FT9VTOa17SxwDmuBDmnUEHsU6SSnxf6x9CzPqt1ECix7GMeben5Q0caneyylzvouto3MruZ%2Fhav0np%2FpFUryaMjFvrzps6dkPbZmUsHvx7zFber4f7rbLP6Wz%2FAEln%2BivYvY%2Bu9L6f1Xpl2H1GG0OG71SQ01uH0b2Pd9B7P%2FUb%2FwBGvH8vpF3T7%2FQpcH5NIea7az6leXSd2%2BzHjdW70qHbMnE%2FwlSZrE2NuzfjKHMY%2BGfpzR2n%2FL9H95wM%2FByOk5hxsgiyuwb6rm613VH6N1X%2FAKMrW10jqbrmHDyHl8MhjY3G6sHd6PO318dv6eiz07fW9P0v%2BOHiPxc6n9j552Ydh3YmVy7FtP5%2Bvvfiu%2Fw%2F%2FB%2Fpf5zehYv1a6pVk5WLnh%2BJZhML6rmiWvsBY6t1Fn0n1NY77Tvp%2FSf8WnijqNWnOEscjGQ4ZB7Oig0fV7Lf01rr8rqR2tez3XPbBx67XsYzayxjbMl9m38%2F9J%2Bl%2FS%2BpzrvUxMWqvqNVuJksx3402zW5oosbdScig725OHd9qY6v%2Bc2ZOLT6Nn88y3e6X1uyk9NexznYd%2BPdi2Mc1lTxl4ztznWu3bKfWZZXbs9T06f0ypdY6J1Hr%2FVH9Wrtpsx6mMqFDS%2F1WisF30XNa5%2B577bf0eyz%2FRV%2BqnHwW1RotDF6xay49Tviks9OHNBAc9g2sfc1m7Zu2%2FoG%2FQxv3PST9Lw2dTblM%2B1W1sdDixoM2vu3f0936Ot3pOZsurp%2FQ5H%2Bj%2FwaJj4GXf052ZjepRjbmOrywNzNjHulh9Meg5zN%2B1lrn%2F4R9fp%2FpFYxcHD6ey6nItdSGAOtdlFwhlh%2FRuHpN3Ppbv8A5xnqemgFOZ1%2FDyMW77T1DIOQy4vra91bqC00en%2BiYx%2F06Nrqnev%2FAIf%2FAEj1WxaG2sxKOo9Mu6iK32UYeOLHML3vLrNrrmu3%2Blv%2FAPRi1cvpl1PUqW3Nfh5VJ34%2BNmO9XEurHu%2FV7t1jGMsYf0noP9n%2FAANqv9Q6W84w630hn2cuFd1jcYGysjS6qzLxmiu3Fyadv896NX%2Bl%2B2eqhSSBQ1u%2Fwed6z9X%2BrtFJH1dZj1agENsqZuIB9EPsvbkW7Wj6eS%2F6fq%2Bj%2BjWOcbNw8hnqdNxq3tizZYXOa4Ty7fkWbmNd%2FObfof4RdpT1fqHUs1rc%2FLfktyRU2vGa4bSHNePtFL3el6VmLlVejZ7f0m%2F1cj%2BZ%2B0WbvSfqn0%2Fq1eTjdRIdlVspsqcxwBqc8PbfW%2BrX376%2FTtf%2FAKH0bcS6v2WJXqulj4YCXEDZqurxn1dxXdRosuw78hnXcR5dSWu22OrY2bqsdn03ZFTt932Oz2ZWL6lVLPWoXS19Hzup9Pq%2BsfTMYty8kn7ZRXDCy5hdVb1DEre%2F9NU79LvxHbLclj7Kf5rIspXE5mPm9J6q%2FLqa82YrmvfVJHqUsd%2BgyqLatln0a%2F03pP8AWxLmerV%2B5j%2Bz%2FU7qvR%2Bp9Cot6QNlLJbZQ5xdZXb9O1l73S99jnu3%2Bq7%2Be%2FnEmN57qv1X6b9c%2Fq99oxWNx%2BuYxcyx7g9rjc2PXxM5t36x6djmt9H1v02L%2Bi9L%2FCVWcz9VPr51npPWX9H%2Bsxuy8bItFNoyDvtot9tIcN%2F06He316P%2FAEKo%2FwAJ9p9hbXW17ntaA98b3AQXQIbuP5y43%2FGB9Q2dfp%2FaXTWtZ1iloEGGjIY3%2FAPe7%2BayG%2F8AafJ%2F6zb%2Bi%2FSUJT2TK66xtraGAAABoAEAbW8fyVJecfUbqP8AjHPV31dXxMjJ6c%2Fay27LaMd1JaNgsoDxX9o9rf0zaW2%2Br%2FPep%2FpPR0lP%2F9T1VJJJJSDNL%2Fsr9geXGABW1rnakD6F36P%2BsuAwumV%2FWXNrxHvcymqh2S7IqLnRa%2B7Jfj2UPcGtqdvyfVspZ7K%2FQ%2By1%2FoaV6Ffj05FZqvYLKyQS13Eg7m%2F9JKimvHprorEV1NDGDwDRtakkEg2NCNnxDqdGb0Lru3qFDHX41rLTWGxVeyR7626tdVmNa%2F1P3LPWr9i2cmzrpY5tdB6gzpL7Kq7WGXPqqf6D8HIc1gZ9upo%2F7T%2FzmVX%2FADXqr0brOF0%2B4135bWVFgc05kD1WMP06ari17q25P81Z%2B%2FX%2Bi%2BnauTysfqnQeoM6h06l91eW2H4jwf1uhgc70Lq3DdV1XGxm78d7%2FwCfo%2FQXfpK7GIRFX4s2fN7vAeGpRjwk93IyOj5VDnN6U5%2FUMa%2FflYlLGuLm%2BqxnpfadrG1tdk302Ufav%2Bs2en%2FNrQ%2Brd9%2BNlNdfS%2FDuDXMy8e7R1bmjfJ%2FkNhl9Nv59KtOeLbMDO6FktJte%2B%2FCstLh%2Bhcaxm4mY5oc5tLMr08XK3st9H7TRd%2FSqPWW11fA%2Fa%2BOOpdOc6jqeMNllYjcdvudjWt9zfVr3b8d%2F%2Fom%2F1E%2B2F5jpf1u6Dj9RyLKcqu3peaf1rF1aGh%2Bhym0XMr91TfZksr%2Fncb%2FSWY9bFTwftfUOpdQwRZvxsDLOLh2uBexrLH21YzLLAHv9K3ZX%2FwAH%2FOLUw%2BnfU%2F6xVDB6lg4%2FTutEFrLaG%2BgLXD%2FC42zY2x%2F%2Blw7f01f8ur9On%2BroyPqllZfThhMsqBZblWhxFjahFLLWE%2B27HZv3sbsr9H9N63%2FBjW1L9J9PDa%2F6udcx%2FU6Ta7bSH6uxLT7W0Oc0%2Fo6HT6mFl1fzX%2BnV9n1R6v07bbhZv2za4w149K0Vk6enkMeWPu2fzn8xVb%2Fwf0F1GVh4mdSW3MD2vbAfA3AH9xyoYmRZ0o%2FY894%2BztBNGS7QbR%2Ba8n6Oz%2FwH%2FttLyRbyPUcXMqzznY2Nfh9TrbZbkkVyxzQBvzm%2Bm04vr7f0d%2Fo2frX%2BD%2FTV2rc%2BrmH0bqHT8fcW%2Fb2Ned1FrmWsaXOZLfRLH01W%2Bpu9L%2BaXUiCJHBWGegM6d1IdU6WzbJi%2FFbAaWuPvdV%2FV%2FnPTStN6U4%2FWfqPk5jhRj2BorDrMXLOjqnge2m1rNrnV3e1lnpf8b%2FO1Urm%2FqLlZH1a%2BseT03IwLa7s97K8nFrY%2Bx9fp%2Bp6OfjupZ6VvT3%2BrY3I%2F0P6Oyn9F%2BjXrKSCFJJJJKUkkkkp%2F%2F9X1VJJJJSkkkklKQM3Dpzcd2PbuAdBa9hLXscNWWVPb9CxjkdJJTxHVfq4Ol5bc%2Bq55dlvLLq2Dax1lprY7IFbP5i63b77Kf9H%2Bk%2FQrpPq%2FcLMAVlgrupcWXM4duBj9I0y7f%2F1f00vrB0hnVunPo%2BjkMmzFtGjq7QC1tlb9Njvd%2FqxS6dWMj0upH9FfbXsyqwIabGwx30%2Ff%2BifW9n9RHopwvrBh4eF1V%2BXkU%2FqmZQQLSB6deYxwux7HAfpGvs9L%2FB%2F8J6ivWVZ2dj4vV8FgfbkY%2Fo5WO8hosrdr3%2Bi6t7nrefWx8b2h20hzZEwR9Fw%2FlIOLg4%2BI652OCxt7vUfWD7Q4%2FScxv5m%2F89K1IujYj8PpWLjWDa%2BusB7SZIcfc5u6XfnFW31seIe0OEzBE6%2FNSSQUpJJJJSkkkklKSSSSUpJJJJT%2FAP%2FW9VSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2F%2FZ%2F%2B01IlBob3Rvc2hvcCAzLjAAOEJJTQQlAAAAAAAQAAAAAAAAAAAAAAAAAAAAADhCSU0D6gAAAAAYEDw%2FeG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04Ij8%2BCjwhRE9DVFlQRSBwbGlzdCBQVUJMSUMgIi0vL0FwcGxlLy9EVEQgUExJU1QgMS4wLy9FTiIgImh0dHA6Ly93d3cuYXBwbGUuY29tL0RURHMvUHJvcGVydHlMaXN0LTEuMC5kdGQiPgo8cGxpc3QgdmVyc2lvbj0iMS4wIj4KPGRpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNSG9yaXpvbnRhbFJlczwva2V5PgoJPGRpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJPGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1Ib3Jpem9udGFsUmVzPC9rZXk%2BCgkJCQk8cmVhbD43MjwvcmVhbD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTU9yaWVudGF0aW9uPC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk%2BCgkJCTxkaWN0PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTU9yaWVudGF0aW9uPC9rZXk%2BCgkJCQk8aW50ZWdlcj4xPC9pbnRlZ2VyPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNU2NhbGluZzwva2V5PgoJPGRpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJPGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1TY2FsaW5nPC9rZXk%2BCgkJCQk8cmVhbD4xPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxSZXM8L2tleT4KCTxkaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk%2BCgkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCTxhcnJheT4KCQkJPGRpY3Q%2BCgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxSZXM8L2tleT4KCQkJCTxyZWFsPjcyPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJPC9kaWN0PgoJCTwvYXJyYXk%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNVmVydGljYWxTY2FsaW5nPC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk%2BCgkJCTxkaWN0PgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTVZlcnRpY2FsU2NhbGluZzwva2V5PgoJCQkJPHJlYWw%2BMTwvcmVhbD4KCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuc3ViVGlja2V0LnBhcGVyX2luZm9fdGlja2V0PC9rZXk%2BCgk8ZGljdD4KCQk8a2V5PlBNUFBEUGFwZXJDb2RlTmFtZTwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PlBNUFBEUGFwZXJDb2RlTmFtZTwva2V5PgoJCQkJCTxzdHJpbmc%2BTGV0dGVyPC9zdHJpbmc%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BUE1UaW9nYVBhcGVyTmFtZTwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PlBNVGlvZ2FQYXBlck5hbWU8L2tleT4KCQkJCQk8c3RyaW5nPm5hLWxldHRlcjwvc3RyaW5nPgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BMC4wPC9yZWFsPgoJCQkJCQk8cmVhbD4wLjA8L3JlYWw%2BCgkJCQkJCTxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTc2PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1BZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5qb2J0aWNrZXQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk%2BCgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQkJCQk8YXJyYXk%2BCgkJCQkJCTxyZWFsPi0xODwvcmVhbD4KCQkJCQkJPHJlYWw%2BLTE4PC9yZWFsPgoJCQkJCQk8cmVhbD43NzQ8L3JlYWw%2BCgkJCQkJCTxyZWFsPjU5NDwvcmVhbD4KCQkJCQk8L2FycmF5PgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYXBlckluZm8uUE1QYXBlck5hbWU8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNUGFwZXJOYW1lPC9rZXk%2BCgkJCQkJPHN0cmluZz5uYS1sZXR0ZXI8L3N0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVGbGFnPC9rZXk%2BCgkJCQkJPGludGVnZXI%2BMDwvaW50ZWdlcj4KCQkJCTwvZGljdD4KCQkJPC9hcnJheT4KCQk8L2RpY3Q%2BCgkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNVW5hZGp1c3RlZFBhZ2VSZWN0PC9rZXk%2BCgkJPGRpY3Q%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk%2BCgkJCTxzdHJpbmc%2BY29tLmFwcGxlLmpvYnRpY2tldDwvc3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk%2BCgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BMC4wPC9yZWFsPgoJCQkJCQk8cmVhbD4wLjA8L3JlYWw%2BCgkJCQkJCTxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTc2PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLlBNVW5hZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw%2BLTE4PC9yZWFsPgoJCQkJCQk8cmVhbD4tMTg8L3JlYWw%2BCgkJCQkJCTxyZWFsPjc3NDwvcmVhbD4KCQkJCQkJPHJlYWw%2BNTk0PC9yZWFsPgoJCQkJCTwvYXJyYXk%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LlBhcGVySW5mby5wcGQuUE1QYXBlck5hbWU8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuam9idGlja2V0PC9zdHJpbmc%2BCgkJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQkJPGFycmF5PgoJCQkJPGRpY3Q%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvLnBwZC5QTVBhcGVyTmFtZTwva2V5PgoJCQkJCTxzdHJpbmc%2BVVMgTGV0dGVyPC9zdHJpbmc%2BCgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI%2BCgkJCQk8L2RpY3Q%2BCgkJCTwvYXJyYXk%2BCgkJPC9kaWN0PgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC5BUElWZXJzaW9uPC9rZXk%2BCgkJPHN0cmluZz4wMC4yMDwvc3RyaW5nPgoJCTxrZXk%2BY29tLmFwcGxlLnByaW50LnRpY2tldC50eXBlPC9rZXk%2BCgkJPHN0cmluZz5jb20uYXBwbGUucHJpbnQuUGFwZXJJbmZvVGlja2V0PC9zdHJpbmc%2BCgk8L2RpY3Q%2BCgk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuQVBJVmVyc2lvbjwva2V5PgoJPHN0cmluZz4wMC4yMDwvc3RyaW5nPgoJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnR5cGU8L2tleT4KCTxzdHJpbmc%2BY29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXRUaWNrZXQ8L3N0cmluZz4KPC9kaWN0Pgo8L3BsaXN0Pgo4QklNA%2BkAAAAAAHgAAwAAAEgASAAAAAAC3gJA%2F%2B7%2F7gMGAlIDZwUoA%2FwAAgAAAEgASAAAAAAC2AIoAAEAAABkAAAAAQADAwMAAAABf%2F8AAQABAAAAAAAAAAAAAAAAaAgAGQGQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4QklNA%2B0AAAAAABAAZAAAAAEAAQBkAAAAAQABOEJJTQQmAAAAAAAOAAAAAAAAAAAAAD%2BAAAA4QklNBA0AAAAAAAQAAAAeOEJJTQQZAAAAAAAEAAAAHjhCSU0D8wAAAAAACQAAAAAAAAAAAQA4QklNBAoAAAAAAAEAADhCSU0nEAAAAAAACgABAAAAAAAAAAE4QklNA%2FUAAAAAAEgAL2ZmAAEAbGZmAAYAAAAAAAEAL2ZmAAEAoZmaAAYAAAAAAAEAMgAAAAEAWgAAAAYAAAAAAAEANQAAAAEALQAAAAYAAAAAAAE4QklNA%2FgAAAAAAHAAAP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8D6AAAAAD%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FA%2BgAAAAA%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwPoAAAAAP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8D6AAAOEJJTQQIAAAAAAAQAAAAAQAAAkAAAAJAAAAAADhCSU0EHgAAAAAABAAAAAA4QklNBBoAAAAAA1UAAAAGAAAAAAAAAAAAAAFFAAABwgAAABAAcwB3AGkAcwBzAF8AYQByAG0AeQBfAGsAbgBpAGYAZQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABwgAAAUUAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAQAAAAAAAG51bGwAAAACAAAABmJvdW5kc09iamMAAAABAAAAAAAAUmN0MQAAAAQAAAAAVG9wIGxvbmcAAAAAAAAAAExlZnRsb25nAAAAAAAAAABCdG9tbG9uZwAAAUUAAAAAUmdodGxvbmcAAAHCAAAABnNsaWNlc1ZsTHMAAAABT2JqYwAAAAEAAAAAAAVzbGljZQAAABIAAAAHc2xpY2VJRGxvbmcAAAAAAAAAB2dyb3VwSURsb25nAAAAAAAAAAZvcmlnaW5lbnVtAAAADEVTbGljZU9yaWdpbgAAAA1hdXRvR2VuZXJhdGVkAAAAAFR5cGVlbnVtAAAACkVTbGljZVR5cGUAAAAASW1nIAAAAAZib3VuZHNPYmpjAAAAAQAAAAAAAFJjdDEAAAAEAAAAAFRvcCBsb25nAAAAAAAAAABMZWZ0bG9uZwAAAAAAAAAAQnRvbWxvbmcAAAFFAAAAAFJnaHRsb25nAAABwgAAAAN1cmxURVhUAAAAAQAAAAAAAG51bGxURVhUAAAAAQAAAAAAAE1zZ2VURVhUAAAAAQAAAAAABmFsdFRhZ1RFWFQAAAABAAAAAAAOY2VsbFRleHRJc0hUTUxib29sAQAAAAhjZWxsVGV4dFRFWFQAAAABAAAAAAAJaG9yekFsaWduZW51bQAAAA9FU2xpY2VIb3J6QWxpZ24AAAAHZGVmYXVsdAAAAAl2ZXJ0QWxpZ25lbnVtAAAAD0VTbGljZVZlcnRBbGlnbgAAAAdkZWZhdWx0AAAAC2JnQ29sb3JUeXBlZW51bQAAABFFU2xpY2VCR0NvbG9yVHlwZQAAAABOb25lAAAACXRvcE91dHNldGxvbmcAAAAAAAAACmxlZnRPdXRzZXRsb25nAAAAAAAAAAxib3R0b21PdXRzZXRsb25nAAAAAAAAAAtyaWdodE91dHNldGxvbmcAAAAAADhCSU0EKAAAAAAADAAAAAE%2F8AAAAAAAADhCSU0EEQAAAAAAAQEAOEJJTQQUAAAAAAAEAAAAAThCSU0EDAAAAAAWrwAAAAEAAACgAAAAdAAAAeAAANmAAAAWkwAYAAH%2F2P%2FgABBKRklGAAECAABIAEgAAP%2FtAAxBZG9iZV9DTQAC%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQADAgICAkIDAkJDBELCgsRFQ8MDA8VGBMTFRMTGBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAENCwsNDg0QDg4QFA4ODhQUDg4ODhQRDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgAdACgAwEiAAIRAQMRAf%2FdAAQACv%2FEAT8AAAEFAQEBAQEBAAAAAAAAAAMAAQIEBQYHCAkKCwEAAQUBAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAABBAEDAgQCBQcGCAUDDDMBAAIRAwQhEjEFQVFhEyJxgTIGFJGhsUIjJBVSwWIzNHKC0UMHJZJT8OHxY3M1FqKygyZEk1RkRcKjdDYX0lXiZfKzhMPTdePzRieUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm9jdHV2d3h5ent8fX5%2FcRAAICAQIEBAMEBQYHBwYFNQEAAhEDITESBEFRYXEiEwUygZEUobFCI8FS0fAzJGLhcoKSQ1MVY3M08SUGFqKygwcmNcLSRJNUoxdkRVU2dGXi8rOEw9N14%2FNGlKSFtJXE1OT0pbXF1eX1VmZ2hpamtsbW5vYnN0dXZ3eHl6e3x%2F%2FaAAwDAQACEQMRAD8A9VSSSSUpJJJJSkkkklKSSSSUs4uDSWiXRoOJKFi3Ovx2Wvbse4e5gMwRo5soypdOyN9mTSRHpWvj4Oc4%2FwDVf9WkpupJJJKUkkkkpSSSSSlJJJJKUkkkkp%2F%2F0PVUklCqxttbbG%2FReA4fApKZpJJJKUo2WNrrdY8w1gLnHyAlVOsWvowXZDZ%2FQOZa8D9xrmm3%2FNr3PUswm%2FpmQGamyl4bHiWuhJTYqsFtTLBw9od94U1Q6FkfaOk4tszuqYfwV9JSll1%2FoOt2sOguDXgeTm7f%2FPmN%2FwCCLUWZ1VhrysbKA%2Bjua7zgtuZ%2F1FqIUXTSQcvKZi47sh7XOY0tBDYJhzms3akfR3blVd13prMtuI%2Bwse%2F6L3Ahm7n0%2FU%2Bi2zafzkFOgq9%2Bdj0ZFOM4k3ZDtrGNEnhzt7v3WbWPU8bJpyavVpduZuc2eNWONb2%2F2XtXOY2VU7rA6hnO9RuJS%2Br1hO1lguGJbuYz2extvv3fzLPU%2FwCESU9QkkkkpSSSSSlJJJJKf%2F%2FR9VWf0iwiu3Ff9LGsewT%2B6D7P%2FAyxy0FmunH6xPDMpgd%2FbZFT%2FwDoOx%2F%2B20Qp0lC02CtxqALwDtB4J8FNJBTVovx%2Bo4jvbLLGlltTuRMssrf%2FANJiz%2BhZDqMO%2FEyCX2dOc6t%2Bkuc1gmt%2B3%2FhaPTeoZ5s6R1IZrBGDlkNvI%2BjXcYYyx%2F7tWV9B%2FwDw%2FwDwlyD1LqWH0jq2N1O55rxOogY1rwC4C9uuP6mzdt9Wl1v6T%2Bb%2FAFdFS%2F1Myq%2F2VThusa%2B2tst2k6sPuY73Brm%2Fo3Vv2fy%2F3F0S4TAzq%2Bm%2FWDquKGnbhWOya62DUUlrcmyulktZ6f2ey19Wz8%2F2Ls2Z%2BFYx1jL6yxgBe%2FcIAI3CXfNIqZX3ursqqrZ6j7TqJgNY2PUsPP0dypfWJ%2Fo9NOT2xrarHTxs3tZkf%2By77VR6v9bejdNsbkb3Zbqm2sfVjgOOhrdZFljqsffXs%2FmvV9V%2F%2BDXHdZ%2BuvU8zA6i%2Bj02Y1zawaZLt2LY21tltb7XOb6j6%2FwDR1V%2FzV36v6rPVYFU%2BiCs5PS7cd43O2WUOB77d1X%2FT27lwvVnNyKK8d15q6hij207o9dg3DHdW9m9zLv8AQ%2F4Wu71K%2FTtr9aqzJ%2BuHV8tmR0zNoyLvsXU8Nr7cdtjmtN1J9K%2B2podsbk1%2Fo%2F8A0YsnG6te%2BnLZbdXXY6pttADNwucwmhv2cWbv0tnr%2FpKnfpKbKf8AtsqD1DvrjnfV70cKlldgzgbqjf7QHB3p2sG0sb6lv0%2FfZ6TEcZg630xz2vezK6jVbfhVtf6biQ%2BxltmNY7a2z9Js3Vu%2FwayvrHh1%2BhhdKv3XubjBhY9wDbHtPpOdXkOH6t1Dd%2FRsl%2F6C%2FwDomV7Fg9Wc%2FF%2Bq3RL6biben5WVjh4lljdxbexltf06La9vvp%2FM%2FwAFZ%2FhEuqn0r6lfXM9WYzpvVoo6wxvcbBcGj9Ltr09LLoj9bxf%2BvU%2Fof5mz9avrJd0%2B2jBwHAZTrcd17iA7bVZa2nY0H%2FCXw9v%2FAAdf%2FWl5zidQZ9ZcY3l3oddwg2z1mHZ6zWfQv3t2%2Bnk0%2FwCn%2FwDRf8xf6Df1DqvXGZ%2Be5tzsi7Gd6lm2kPsqNbqqOG1%2F4Cuxnpt%2FS7%2F9I9ALjR1Gh7f96%2BuJJpA1KZj2WNDmODmnhwMhJaySSSSU%2FwD%2F0vVVn9airFGYSGjEcLXuOkV%2Fzd5J%2FdZU%2FwBX%2Fra0EHM9D7Jf9pE4%2Fpv9YHX2bT6mn9RIKcE9U6lgXnJDv2l0jN%2FS497S39C530sOyxn%2BBc%2F%2Bh5P83%2F2kyfS%2FR5NlPrv16uwczExsPHFozLBRWHz6rriWM9Ouv6Hs9T3vseue6B9buoZF5xBU84BDC%2BtoZYW1PczG%2B02Gxm71Gb2XP2fod7P5mtct167q%2FT87Gdfk%2FbscWHI6V1NrQNGu3O2NZ6fp5FN9dTcrH3epR6f9RKwRYXzhKEuGYo%2FxfSMvM6lZlF%2FVfSobU11Q9MGyqLfa%2BrP9PJsr9Kz09n6bGq%2F7dWN1z9VxC3KfZT0%2B%2BPUyGg5NIaPdU7ezZe7KxPR%2FR5Gyr1sX9Db6%2FwDguZx%2Bv352be4WNwft%2B9tb2n21mza%2F07Xn2%2Fp7qmVep6dbP0vqfolqZD7mY91eDmXh9pBAZDGjT9awvb%2Bkp3W%2Fp2Mdkfpav1b%2BXUb0WulTeP23h5Di3JL3UYWTYzlzbKn1sc5rN7baM3FZ9pw8xrvSvr%2FQf0im6uvH6vY7G6hfS2mx1WFYaq7bnk%2Bo2r0rKPTa7bustxX20%2Bz%2Blf4RW%2FqPXeM403s%2FQ9LpdbQ4v97Wl4DcW1nud6PqfrH2e9nszMavNx%2Fs9%2F2hA6i5r77HZLH24Tn1W4%2BUwj2Pva3Jdj5bfzaHb8n7Fk%2F4P9YxP0tX8wulqaWfb1CzZ6GaM30622WDD%2FSV1mp3sfY6tjWOsY1tf8776a%2FTQ7s27qFNVIrqwAfUsbZRXt9S0N9Ky1tX83%2Bgre%2F1sej%2FAE1lzKltdDtbi9ZJsqqbTaBXdS0TbW68PycUxNj8qrIbR%2FpPU%2FSb6%2FTyv6VWf0qvOrzmsYasnGzHmk7gx1Za6K2XR7sd%2B5v6K79JXi3bPU%2FV8i71RRU5%2FWaLMv6kYj7SGX9L6g%2BqK%2Fosrymeux1cf4B17fYsrpuPmNxftz63Pcwl9DWDQ2sIqZkNJ%2FNZe6luR6f%2FAAX%2FAAS1f2pXX0vq%2BJln7LfkVVta3ZAfl4929myn%2FA76rLq86r%2Bbo%2F4qyhVruoZfSek4ldmM12Pe227D9XQBtvptu3%2B7d6Vr66Xf2PU%2FwiSnsuqnDyLMfFtMZORj%2BrXSPeHFseo2l7g31La%2FV%2Bh%2F2oq%2FwfqfpFznVcd9uDZivaxzDZXa1zztZaXB1LW%2Bv%2Fgb%2FZVXi5tv83%2FRsj9AsLqfXv2j0%2FpXqPFedgNurfa10F3uqsx8lv8Ao3Obvrf%2FAMT6n%2BFQquq59%2FURec1zb7CHXMc8htmw7n47v8BtucPUb6v6P9Ld69iNoS9H6Vk0Zzcpj3tqqeW1AAtuc%2F8Am7Meyhv6WvZ%2FNZX%2FAIEugzuk14eLiN69fbi49u6wgBjyWNb6dP6IOa52V6vt247P0dD%2FAFP0ajjdc6kBeekYQd1DIs2tsZVZbdVXt9tYqsG9%2B3%2FTPZ%2Bl%2FwBCsajpXV%2BtX2Zwo%2Fal7Wuvv9S0Mea2%2FT2%2BvZ7m1e1j6GV76PZ%2FpK0NE7avpH1Z69ndU6Ti4mP6mVZktBdbkP3uqbWTRkjKuYGb9t1e6v2776rP9Iuyw8VuJjtoaS8tkueeXOcdz3%2F2nLyb6vdZv6DjDKwmVt3lzbsZz3VsZcHluTgX499hfRYxuzJwn%2FznsyKrLLv8H2vT%2Fr9hODK%2BsM%2FZuSWkvrtFjC1wLmvr%2FWKqW%2Fm%2BzbZ%2BkRR4vVpKn03q%2FTeq47Mnp%2BQy%2Bt7WvG0%2B4Bw3N9Ss%2FpK%2F%2BuNVxBT%2FAP%2FT9VTOa17SxwDmuBDmnUEHsU6SSnxf6x9CzPqt1ECix7GMeben5Q0caneyylzvouto3MruZ%2Fhav0np%2FpFUryaMjFvrzps6dkPbZmUsHvx7zFber4f7rbLP6Wz%2FAEln%2BivYvY%2Bu9L6f1Xpl2H1GG0OG71SQ01uH0b2Pd9B7P%2FUb%2FwBGvH8vpF3T7%2FQpcH5NIea7az6leXSd2%2BzHjdW70qHbMnE%2FwlSZrE2NuzfjKHMY%2BGfpzR2n%2FL9H95wM%2FByOk5hxsgiyuwb6rm613VH6N1X%2FAKMrW10jqbrmHDyHl8MhjY3G6sHd6PO318dv6eiz07fW9P0v%2BOHiPxc6n9j552Ydh3YmVy7FtP5%2Bvvfiu%2Fw%2F%2FB%2Fpf5zehYv1a6pVk5WLnh%2BJZhML6rmiWvsBY6t1Fn0n1NY77Tvp%2FSf8WnijqNWnOEscjGQ4ZB7Oig0fV7Lf01rr8rqR2tez3XPbBx67XsYzayxjbMl9m38%2F9J%2Bl%2FS%2BpzrvUxMWqvqNVuJksx3402zW5oosbdScig725OHd9qY6v%2Bc2ZOLT6Nn88y3e6X1uyk9NexznYd%2BPdi2Mc1lTxl4ztznWu3bKfWZZXbs9T06f0ypdY6J1Hr%2FVH9Wrtpsx6mMqFDS%2F1WisF30XNa5%2B577bf0eyz%2FRV%2BqnHwW1RotDF6xay49Tviks9OHNBAc9g2sfc1m7Zu2%2FoG%2FQxv3PST9Lw2dTblM%2B1W1sdDixoM2vu3f0936Ot3pOZsurp%2FQ5H%2Bj%2FwaJj4GXf052ZjepRjbmOrywNzNjHulh9Meg5zN%2B1lrn%2F4R9fp%2FpFYxcHD6ey6nItdSGAOtdlFwhlh%2FRuHpN3Ppbv8A5xnqemgFOZ1%2FDyMW77T1DIOQy4vra91bqC00en%2BiYx%2F06Nrqnev%2FAIf%2FAEj1WxaG2sxKOo9Mu6iK32UYeOLHML3vLrNrrmu3%2Blv%2FAPRi1cvpl1PUqW3Nfh5VJ34%2BNmO9XEurHu%2FV7t1jGMsYf0noP9n%2FAANqv9Q6W84w630hn2cuFd1jcYGysjS6qzLxmiu3Fyadv896NX%2Bl%2B2eqhSSBQ1u%2Fwed6z9X%2BrtFJH1dZj1agENsqZuIB9EPsvbkW7Wj6eS%2F6fq%2Bj%2BjWOcbNw8hnqdNxq3tizZYXOa4Ty7fkWbmNd%2FObfof4RdpT1fqHUs1rc%2FLfktyRU2vGa4bSHNePtFL3el6VmLlVejZ7f0m%2F1cj%2BZ%2B0WbvSfqn0%2Fq1eTjdRIdlVspsqcxwBqc8PbfW%2BrX376%2FTtf%2FAKH0bcS6v2WJXqulj4YCXEDZqurxn1dxXdRosuw78hnXcR5dSWu22OrY2bqsdn03ZFTt932Oz2ZWL6lVLPWoXS19Hzup9Pq%2BsfTMYty8kn7ZRXDCy5hdVb1DEre%2F9NU79LvxHbLclj7Kf5rIspXE5mPm9J6q%2FLqa82YrmvfVJHqUsd%2BgyqLatln0a%2F03pP8AWxLmerV%2B5j%2Bz%2FU7qvR%2Bp9Cot6QNlLJbZQ5xdZXb9O1l73S99jnu3%2Bq7%2Be%2FnEmN57qv1X6b9c%2Fq99oxWNx%2BuYxcyx7g9rjc2PXxM5t36x6djmt9H1v02L%2Bi9L%2FCVWcz9VPr51npPWX9H%2Bsxuy8bItFNoyDvtot9tIcN%2F06He316P%2FAEKo%2FwAJ9p9hbXW17ntaA98b3AQXQIbuP5y43%2FGB9Q2dfp%2FaXTWtZ1iloEGGjIY3%2FAPe7%2BayG%2F8AafJ%2F6zb%2Bi%2FSUJT2TK66xtraGAAABoAEAbW8fyVJecfUbqP8AjHPV31dXxMjJ6c%2Fay27LaMd1JaNgsoDxX9o9rf0zaW2%2Br%2FPep%2FpPR0lP%2F9T1VJJJJSDNL%2Fsr9geXGABW1rnakD6F36P%2BsuAwumV%2FWXNrxHvcymqh2S7IqLnRa%2B7Jfj2UPcGtqdvyfVspZ7K%2FQ%2By1%2FoaV6Ffj05FZqvYLKyQS13Eg7m%2F9JKimvHprorEV1NDGDwDRtakkEg2NCNnxDqdGb0Lru3qFDHX41rLTWGxVeyR7626tdVmNa%2F1P3LPWr9i2cmzrpY5tdB6gzpL7Kq7WGXPqqf6D8HIc1gZ9upo%2F7T%2FzmVX%2FADXqr0brOF0%2B4135bWVFgc05kD1WMP06ari17q25P81Z%2B%2FX%2Bi%2BnauTysfqnQeoM6h06l91eW2H4jwf1uhgc70Lq3DdV1XGxm78d7%2FwCfo%2FQXfpK7GIRFX4s2fN7vAeGpRjwk93IyOj5VDnN6U5%2FUMa%2FflYlLGuLm%2BqxnpfadrG1tdk302Ufav%2Bs2en%2FNrQ%2Brd9%2BNlNdfS%2FDuDXMy8e7R1bmjfJ%2FkNhl9Nv59KtOeLbMDO6FktJte%2B%2FCstLh%2Bhcaxm4mY5oc5tLMr08XK3st9H7TRd%2FSqPWW11fA%2Fa%2BOOpdOc6jqeMNllYjcdvudjWt9zfVr3b8d%2F%2Fom%2F1E%2B2F5jpf1u6Dj9RyLKcqu3peaf1rF1aGh%2Bhym0XMr91TfZksr%2Fncb%2FSWY9bFTwftfUOpdQwRZvxsDLOLh2uBexrLH21YzLLAHv9K3ZX%2FwAH%2FOLUw%2BnfU%2F6xVDB6lg4%2FTutEFrLaG%2BgLXD%2FC42zY2x%2F%2Blw7f01f8ur9On%2BroyPqllZfThhMsqBZblWhxFjahFLLWE%2B27HZv3sbsr9H9N63%2FBjW1L9J9PDa%2F6udcx%2FU6Ta7bSH6uxLT7W0Oc0%2Fo6HT6mFl1fzX%2BnV9n1R6v07bbhZv2za4w149K0Vk6enkMeWPu2fzn8xVb%2Fwf0F1GVh4mdSW3MD2vbAfA3AH9xyoYmRZ0o%2FY894%2BztBNGS7QbR%2Ba8n6Oz%2FwH%2FttLyRbyPUcXMqzznY2Nfh9TrbZbkkVyxzQBvzm%2Bm04vr7f0d%2Fo2frX%2BD%2FTV2rc%2BrmH0bqHT8fcW%2Fb2Ned1FrmWsaXOZLfRLH01W%2Bpu9L%2BaXUiCJHBWGegM6d1IdU6WzbJi%2FFbAaWuPvdV%2FV%2FnPTStN6U4%2FWfqPk5jhRj2BorDrMXLOjqnge2m1rNrnV3e1lnpf8b%2FO1Urm%2FqLlZH1a%2BseT03IwLa7s97K8nFrY%2Bx9fp%2Bp6OfjupZ6VvT3%2BrY3I%2F0P6Oyn9F%2BjXrKSCFJJJJKUkkkkp%2F%2F9X1VJJJJSkkkklKQM3Dpzcd2PbuAdBa9hLXscNWWVPb9CxjkdJJTxHVfq4Ol5bc%2Bq55dlvLLq2Dax1lprY7IFbP5i63b77Kf9H%2Bk%2FQrpPq%2FcLMAVlgrupcWXM4duBj9I0y7f%2F1f00vrB0hnVunPo%2BjkMmzFtGjq7QC1tlb9Njvd%2FqxS6dWMj0upH9FfbXsyqwIabGwx30%2Ff%2BifW9n9RHopwvrBh4eF1V%2BXkU%2FqmZQQLSB6deYxwux7HAfpGvs9L%2FB%2F8J6ivWVZ2dj4vV8FgfbkY%2Fo5WO8hosrdr3%2Bi6t7nrefWx8b2h20hzZEwR9Fw%2FlIOLg4%2BI652OCxt7vUfWD7Q4%2FScxv5m%2F89K1IujYj8PpWLjWDa%2BusB7SZIcfc5u6XfnFW31seIe0OEzBE6%2FNSSQUpJJJJSkkkklKSSSSUpJJJJT%2FAP%2FW9VSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2Bqkl8qpJKfqpJfKqSSn6qSXyqkkp%2F%2FZADhCSU0EIQAAAAAAVQAAAAEBAAAADwBBAGQAbwBiAGUAIABQAGgAbwB0AG8AcwBoAG8AcAAAABMAQQBkAG8AYgBlACAAUABoAG8AdABvAHMAaABvAHAAIABDAFMAMgAAAAEAOEJJTQQGAAAAAAAHAAIAAAABAQD%2F4TkjaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu%2B7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI%2FPgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSIzLjEuMS0xMTIiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4YXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyI%2BCiAgICAgICAgIDx4YXBNTTpEb2N1bWVudElEPnV1aWQ6RTcxOTVFNTY1NzMzMTFERDlFNzJGQ0E2QjkwQUZBRjU8L3hhcE1NOkRvY3VtZW50SUQ%2BCiAgICAgICAgIDx4YXBNTTpJbnN0YW5jZUlEPnV1aWQ6RTcxOTVFNTc1NzMzMTFERDlFNzJGQ0E2QjkwQUZBRjU8L3hhcE1NOkluc3RhbmNlSUQ%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4YXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPgogICAgICAgICA8eGFwOkNyZWF0ZURhdGU%2BMjAwOC0wNy0xOVQxNDo1Nzo0MS0wNTowMDwveGFwOkNyZWF0ZURhdGU%2BCiAgICAgICAgIDx4YXA6TW9kaWZ5RGF0ZT4yMDA4LTA3LTE5VDE0OjU4OjA1LTA1OjAwPC94YXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhhcDpNZXRhZGF0YURhdGU%2BMjAwOC0wNy0xOVQxNDo1ODowNS0wNTowMDwveGFwOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHhhcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ1MyIE1hY2ludG9zaDwveGFwOkNyZWF0b3JUb29sPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIj4KICAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9qcGVnPC9kYzpmb3JtYXQ%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iPgogICAgICAgICA8cGhvdG9zaG9wOkNvbG9yTW9kZT4zPC9waG90b3Nob3A6Q29sb3JNb2RlPgogICAgICAgICA8cGhvdG9zaG9wOkhpc3RvcnkvPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICAgICA8dGlmZjpYUmVzb2x1dGlvbj4xMDAwMDAwLzEwMDAwPC90aWZmOlhSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj4xMDAwMDAwLzEwMDAwPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yPC90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8dGlmZjpOYXRpdmVEaWdlc3Q%2BMjU2LDI1NywyNTgsMjU5LDI2MiwyNzQsMjc3LDI4NCw1MzAsNTMxLDI4MiwyODMsMjk2LDMwMSwzMTgsMzE5LDUyOSw1MzIsMzA2LDI3MCwyNzEsMjcyLDMwNSwzMTUsMzM0MzI7QzA1QTE5MDRGRjAwQUJEQzA1MUJERkFGMDIwNEVBNTE8L3RpZmY6TmF0aXZlRGlnZXN0PgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24%2BNDUwPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjMyNTwvZXhpZjpQaXhlbFlEaW1lbnNpb24%2BCiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U%2BLTE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6TmF0aXZlRGlnZXN0PjM2ODY0LDQwOTYwLDQwOTYxLDM3MTIxLDM3MTIyLDQwOTYyLDQwOTYzLDM3NTEwLDQwOTY0LDM2ODY3LDM2ODY4LDMzNDM0LDMzNDM3LDM0ODUwLDM0ODUyLDM0ODU1LDM0ODU2LDM3Mzc3LDM3Mzc4LDM3Mzc5LDM3MzgwLDM3MzgxLDM3MzgyLDM3MzgzLDM3Mzg0LDM3Mzg1LDM3Mzg2LDM3Mzk2LDQxNDgzLDQxNDg0LDQxNDg2LDQxNDg3LDQxNDg4LDQxNDkyLDQxNDkzLDQxNDk1LDQxNzI4LDQxNzI5LDQxNzMwLDQxOTg1LDQxOTg2LDQxOTg3LDQxOTg4LDQxOTg5LDQxOTkwLDQxOTkxLDQxOTkyLDQxOTkzLDQxOTk0LDQxOTk1LDQxOTk2LDQyMDE2LDAsMiw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNSwxNiwxNywxOCwyMCwyMiwyMywyNCwyNSwyNiwyNywyOCwzMDtENDYzN0NCOUQ0MUExMEJBN0VGNUVCQ0RCNjMxODMyOTwvZXhpZjpOYXRpdmVEaWdlc3Q%2BCiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY%2BCjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8%2B%2F%2B4ADkFkb2JlAGSAAAAAAf%2FbAIQACAYGBgYGCAYGCAwIBwgMDgoICAoOEA0NDg0NEBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAEJCAgJCgkLCQkLDgsNCw4RDg4ODhERDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM%2F8AAEQgBRQHCAwEiAAIRAQMRAf%2FdAAQAHf%2FEAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPBUtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4%2FPE1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1%2Bf3OEhYaHiImKi4yNjo%2BCk5SVlpeYmZqbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEyobHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp0%2BPzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1%2Bf3OEhYaHiImKi4yNjo%2BDlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq%2Bv%2FaAAwDAQACEQMRAD8A7%2FmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVdmrlHI2utXn6WltnFESb0RFSp41WjD5q3LCBaslzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Dv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2E2rj0LuzvF7PxftUf8AXPLDnC3XI0ewdpOkTK4P08T%2FAMK2Ec1TLNgaxmE9pFIDy%2BEAn3GxwTgV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV%2F%2F0e%2F5s2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXYUeZpfR0a4kpUAx19h6i74b4X65CbjR76FV5FoJKKe5C1GIVLdDuGhuXs2IKOX4gfsvGdx%2FskKtkiyD6bctHHbXgPImOC4qOpXj6E1ff4Mm4Ndxkpc770BvNmzZFLs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F9Lv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVokKCT0G%2F3ZgaioNQemANZuhaWLyE0LlY1%2BbGn6sWspA8QA%2FZ%2FUcVRWbNmxV2bNmxV2bNmxV2NZQwKnodj9OOzYqwDT4iiPZsa%2FVLm5sj7I%2F76L%2BOTXTpjPZQSt9pkAb5r8JyKXqfUvMOpIQOFxFDfoP8qFvTl%2F4Q5INDkrFPAf8AdUhI%2FwBV%2FiH8cnLeAPcxHMprmzZsgydmzZsVdmzZsVdmzZsVdmzYyWRIo2kkYKiirMdgAMVX5sjTeaGl1CzsbSAOLuX0w7k14KCzyAD%2BUZJBirebNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV%2F9Pv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVo4CXUFa9e0CbJRWkr%2B0RWlMHZEJ5WtPMtzE5%2BGYJKnyK0%2F4kuEC1ZeM2IW0okjFT8S7H%2BuL4FYz52mMWn2tDTlcpX6FbDHR5ecCH%2BZevywl%2FMM8dJtn7LdJ%2BKvgry9ccrOJ69KVw9E9GS5s2bAh2bNmxV2bNmxV2bNmxVi3miFU1LSbxvsStJYzH%2FJnSi%2F8ADYpoEzLcxq1T68FGr%2FvyE8GrgjzdC0mhXEsY%2Fe2pS5i%2F1omDf8RwttJRHdrcDaNbhZVI6endIG%2F4mcnHeJCDzDL82bNkEuzZs2KuzZs2KuzZs2KurkZ843Zhs4bVTQzvVqfyrvg3zHNPb2UMsLtGBPGJSpIqjEqRUb9ch%2BvSSvp0JlcvJCLyMuxJb4JNvi%2F1SuEBVvkaX9L%2BZbm8Sv1fTbf0Y6%2F78lPxH%2FgFzpeRvyPbWcXlyyubWBIXuoxJOUG7uKjk5%2FaOSSoxPNXZs2bArs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F%2FU7%2FmzZsVQ2osyWM7o3FlQkMOxGPtZhPBHKCDyUE08e%2BM1Bedjcr4xPT%2FgThV5cvBJbpGergEfMDcYa2PkqfZs2bArs2bNirs2bNirs2bNirshfnRGtb7T9TX7LcoJD2qDzSv3vk0wl81WDajodzDGKzRj1oR35R%2FFT6RthHNXaVcrKsUtftDi304dZz7yrqPrQCIncdMnsEnqRK%2Fcjf54yVjP5gwtJ5ckcD%2B5mik%2BivH%2FAI3wD5RnEtpwrWgw983RGby1qiqKlYGkA%2F4x%2FvP%2BNchnke5BIUGoPQ%2FPEJHJ6XG3JFbxAOPxOA1iX7sUwIdmzZsVdmzZsVdmzZsVUbqBbq2mtm%2BzMjRn5MKZDLP4rC2UVq9qYj7SWshWn%2BtxOTnIVG4ttWvdNaiiK7FxGDtWG6Ti%2FwDwMuShzRLky%2B1lE9vFMP8AdiK33gYscKtBmDWhtq%2FHbOyMp68SeSH5UOGpyJ5pYde3moWPmW5KzH0pIo2WNt0A3Wv0ts2D73zJ6FgzxqqXpKpHG9eDMx7EYXed45VeC4gPCdYyqua0I5q5VgOv2cAW8sWo2QMqhwwoyHcVyQFhVOLzvrNrdf6fFHLFX4kVeBH%2Br1%2F4bJlp%2Bt2GqWrXFnICyKWeJtnUgV3XOP67qFjaFLHUrprecy%2BlZajxPDmN1jn8Dv8Aa%2By%2BIaXrclrdtZ34NrfR1USofgeo2K06c%2F8AgcBCXr%2FlvXRrdnzk4rcoSJFXYfMbnDzOV%2BTr82eqAOQIpaBq7AV2r%2BOdTriRSEp8z8RoN9I5AWJPV5HYD0yHr%2BGQg3Ntq1lcLazx3CiZviiYOKzQo9Kjb7YzoWqRevpt5AOskEij5lCOmc60%2BWCK0j9ONYo5IYp%2BEahQWBeNjRafEcMVTvTb%2B80fyHZ3FugE9vSORJBUqPVZW%2BEHw3yVaXeC%2FsobulGkX41HQMNmH%2FBZzC%2B1iTTbadZUkjtJjzkQqSKqNm5fsdMPfyr1%2FRdT8vra2E5N5C8kl3bSMTKpkctz%2BL7SP9peOA7fFkIkgkCwOfkz7NlA1y8DF2bNmxV2bEJLy1injtpJVWeavpREjk3Hc8V64virs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F9Xv%2BbNmxVplDqUbcMKH5HIPokjWsstqx%2BK2lZPf4Wpk5yF6nF9R8xswHGO8QSj%2FAFh8D%2Fqrkoc6VmSMHUOOjAEfTjsAaZMXhMRPxRnYf5J6YPyJV2bNmxV2bNmxVQuzOtu729PVUclBFa07U98DafqsF6i9EkI6ePywwyF6vG2i6oJRUWl6xZCP2JOrr%2FsvtLhG6s0yiK4X6Zfi4QI7Vb9lv5h%2FXDHrgV5c8B0LzLc2Q%2BGF29WDw4SfF%2Fwp%2BHOg6ZMHi4j2YfTkZ%2FMGwpFaazGDytm9GYj%2FAH25%2BEn%2FAFX%2FAOJ4N8uXwliicnps305LmFZDewrc2dxbt0lieNvkylc45%2BX10UCQzHjLATE4P%2BQeNc6L55e5i0J57WVonikRmZDSq7jf2yB2Op%2FVYor2%2FwBNjktpAGjv7ZvU%2BInoy8Vao%2Fa44xCQ9as25RVBqKnfBGRby9rBEsljcIY0aQ%2Bg5FCCQD6bj9lslGAikN5s2bArs2bGyOI0aRuiAsfoFcVS%2B%2F1q2sJltyrSytuyp%2ByDsC3zwxBqAfHfI%2Fodmt2z6pcjnJK5dAexPT%2FgRkhxV2c782mSy82WUgNIdUhNs4PTmlXQ%2FwDBDOiZAvzLb6nHpOrlFkjs7lWdG9yvxVG%2FwiuGJ3Up3YTJFr7op%2BC8t1ljHjxodv8AYtkiyHbxx6XfKx%2FcXBilJ3%2BAsVUV8FRlyYA4Z80DkkPmq3EtlHJSpR%2BJPgHBH66ZBtIufqt9Np8pALHnEpO5p1oM6Xq0P1jTriPoePIfNdxnLtZtrGZluBcG3vF%2BFJYuJfx40IIxHJKzzb5et9ZtZYp1LwygcgPtKy%2FZkT%2FKWuRKKCOC6tNH1CblcWiILDUWHH6wg6wyDejx9Fw4sfOMjEBY5biH1HgLy8VYulOfJV%2By38q4ndH9LlL219K4iilHrWzLxlhI6SgE1%2F2SnFKo0Czxy28zMqcSzspIYBPj2I%2F1cd%2FytnVZdNQ2D1I%2BESMoPw%2FzMftdMG20TSQyyblmjenzoc4TY6ld6ddyKwMUsbFZoHqNwd1KnGXRXs9n5%2Fv7lg98GlWhMjwk1A%2Fm4k%2FF7jDSwSPULTTJI3PGBphGNwG4OHVW%2BgnONX%2BsWFpDFPaSSJcTESG3TcRlf2uRK%2Fazq%2BkR3aeT7e%2BjnX6yoW8ibht%2B9i9Tg%2B%2FQ98QVZHqstutowuSPSdSpV91IPVWzif1i98t%2BZ7m40FjbSWUrNCqEmkex4EH7acT9lv2c6lonmbTvMUTWdwgiumWktrLQhge6H9of6uRLzd5ZurDVP8Q2KetYFUW7jXeSKi%2Bm0hH7UZH7X7OCY4g2YM0sMrjvexB5SHcXsnkPz3Y%2BcdPqKQ6lAB9bta%2F8lI69Y2%2F4XJeDXPJlvPe6JqMeq6NMYLuE842To6ntToyuv2k%2Faz0J5E8%2B2HnGyI2t9VgA%2Bt2ZP0epH%2FNEf%2BEyESeUuff3tmbFAx8bB9H8Uf4sZ8%2F6P82TMMLNb1iDRrQ3EpBkkPC3j7s5%2FwCNV%2B0%2BGVc4n5m8yfpnzJcsr%2F6HYsLe232PBv3j0%2Fy3%2BH%2FY5JxkLourX175%2Ftb28maSZrloqsdlQ1Tgo%2FZUDO755%2B8uxlvPcMI6i6LAe2zZ6BwlS7NmzYFdmzZsVdmzZsVdmzZsVdmzZsVdmzZsVf%2FW7%2FmzZsVdkb83W5%2BrW1%2Bg%2BK1kAc%2F5D%2FCf%2BG45JMC6jare2FxaEf3qMo%2BdPh%2F4bCNiqV6VOOcTV%2BGReB37%2FaX9WHoyFaJMz23BqiWE7juCh%2FsyaRsHRXHRgCPpwy52gLs2bNkUuzZs2Kuwu1rS49W06Wzb4XI5Qv8Ayuu6nDHMemKvONE1KWGVrG7rHPCxRgeqsppk7sb0XC8W2lUbjxHiMhnnnTHtLmPXrZfgakd1TsR9iQ%2FP7LYtomq%2FWI0Iakybof4H55LmFZdqNlFqNjcWMwqk6FD8z9k%2FQc515XuZbW4l0%2B5%2BGWFzE6n%2BZTTOlW063EQkXY9GXwPhnO%2FNsS6T5qtbofBHqaniexmi%2B2v%2BsU4v%2FlfFgHcrPTFbanZNb3caywyApLE3Qj3zjutwW%2FljzBc6Pp6fV9JHpsLNSSgR1HIorV3Vvizq2jXQk%2BAndlBA9xnNvzRiWPzJBJWnr2gJP%2FGNyv8AHAdikJ3YGf0o5pZDIboSD1GJJ9aAgh6%2F5aUzoFnN9YtYZ6gmRFY06VI3zlXk%2FVI5bi2hnlBgdBuxFFahhfr0rSNsnHlXU4JI5NJaSt1avIQm%2B8RaquD0I%2BLjkpbgIZJmzZsirsDagC1jcqvUxOB%2FwJwTjXUMrK32WBB%2BWKpfofE6Xb8f5d%2FnhjkMPmvR%2FKlrJBqkxBSVkjC0PIDpTfI15s%2FMNbu3jbR5plsZVp69swRi9N0eSjMnH%2BRcVpm669cWWtXGnatwjgKma2uPsj0x%2FNXw%2FayOfmLr%2BhX3l%2BPTYrkXM%2BpGtm1uPUUceX7yRx8KLtx%2F1s5xoN%2FefpVr6C8muVVfUuLO8kMrEIeTem7faRk5Iy5FdTuZ9B169trGZvqkc5eFD8SmJzzUcTtsrceWHzTT3fRJv0p5UZvtOYYrgCu%2FJV4P%2FwANHnPrj83vM2lX9zpWoBJYInaJ540pOi1%2BGRQDxf4cln5Z3qT2stjWrK0iexEgE60%2F2XLOUfmHYNZeZbhWP94Ayn%2FV%2BD%2FjVclLlbEdQn955q1wTiU3huLadeQCsfSljO9VNev%2FAA0bYHub1Z7V5yWkhHxpcVHJNwGiuP8AKStVk%2FbyGafqLQxyafO5S2lPJWUAmGQ%2F7uiB8f8Ad0X%2B7F%2Fy8Ctd6jpl3Lb3v%2BkQTpxniqTDcQn7LoR%2FwUb%2FALDZCyyekWl9ZyaFLPelZGN2tqDEtZJHenEs67ckpVXwq1bU7G1iKWvqtcCR4bkkhFYx%2FCHXhvvX48C%2BU7H1SbK3k9Wya5hv4HchSBF8FxBL2WeOJ1k%2F4s9P4cu%2F0j%2Fc1FphmFxPLeOhZBxAiL8jt40%2Fa5YegV6PprvLpcE8sfpTNbh3QVoGK12zluoQ2PnZih42fmWIVt5z8Md4q9I5P5J17Z2A8YrWVl%2BERoaDwoNvupnLtb02LzREdR0hBba7bVe4tEPFZwpr6sH%2FABb%2FAJOSkgPNLqGaG7e3v42iuoTwlicUIK9iM755KnGoeT7ZX6m1CH%2FYSNCf%2BFOctnuIfNkSWeqEWuu249OC8ccfW47elcd%2Bf%2BVnQ%2FyyS5t9Dk067jMVzaSXMLK3hRJlp4r9rjkQl5PbalcaRd%2BlI7PbwysI5lNJIirU%2BH%2FmnOueV%2FOUeo%2BnY6hIpuWX9xPtwnU%2Fhz%2FnTOPeY4zY%2BYNTgpVBcSVQ9CrHkP14Gsb9rFgRWSzY1K1%2BKNh%2B0p%2FZcY2Qdlev%2BZ%2FLKxK19pcdYBUzWqdYx1Lwj%2BT%2BaP8AZ%2FZyHW9xe6ZfQ6ppk7QXsBDRTIevsR%2B0p%2FlyY%2BUPNR1KJLK5lD3KrW3m6esg67f79T9pMD%2BZtCjTnqVilIW%2BKeFRshPWVAP2P9%2BJ%2BzkZRB3DPFlljlxRPz5Edx8mcw%2Fm3aXvkzULyUC3122jET2oNA8kh9NZYT%2FJvzb%2BTOZabIhnWkgaOQcwxO577%2F5fjhKIYndoZvgaUD05v5SN1J%2FyG6YI0RZ4tS%2BoyKQS1HTwYHsffALrdOQwM%2BLGOEHfhu6PWvJ6f5FsfrXnqW8K%2FDBEZD4cmAVf1Z2fOSeSLmfT9RrHEWlvDSZaVagNR0%2BztnW8mWsuzZs2BDs2bNirs2bNirs2bNirs2bNirs2bNir%2F9fv%2BbNmxV2Y5so4qw6WL9HeYLiLpFcUnjHQfH9r%2FhuWSbTnBgMXeJiv0H4l%2FA4T%2Ba4fTW01FRvDJ6chA%2FYfpX5MMGaXcBmHxbSrsCdi6%2F2f8RyZ3j7kdU4zZzrTfO2qQa3eaHrkax6haux%2Br9FkgJ%2FdT2790ZP%2BGydWWoWt%2BnK3erAfHGdmX5rkSEovNm2yiQASTQDqcCt5sL5dZ06Ko9YSMNqRAv8Aiu2Iv5i0qJecsrKo%2FwAkk%2FctTho9yo69htbi2e2vApgmHB1cgA1%2BffOYX%2BnXflXUghJezkNbabxH8jH%2BdcPPN82l65b2j213DN9WdmmtefCRkdeJaNSVb1I%2FtYXWM8cVk2javM95pUv9zcPvLB%2FI3LvwwhIT2x12KCBrxjyjRS06jrxG5YD%2BZcrzzpkXmjyhLPpzCSaFVv8ATZozuXjHP4GH%2B%2FE5JkUmju9BujaTsJYHHKC4G6Sxn9r%2FAJqwJH5g1fyjDaw2KrcaE0zSNE27RiQmtuXPSJ6%2FuW%2FYkxI6hUg8ia9qulatHe3s0k8DgxGKZywBk%2By3%2BTkn%2FN63a8ttG1a3rHL%2B9hIB%2BIdGK%2F8ABA5DNbS0tL%2B4ksJOenzETWrjYiOT4gjD9l4WqjL%2FAJOTTU9XtNc8r6dDP8E8rc4iSP71RwkTfcP%2FALsX%2Bflgq09zFvKeoTMwV2NeCsB0BaNqf8a5N9Zu59K83W2pWb%2Bn9cjBo24ZXUNxI%2F1lyCWsL6fNb3LLxid24tSgINA9Puyb67pd%2Fr2kaPc6bxN5b8owWYKAYW%2BGpP8Ak4RyQU8ufOF7JLD9XRLaAj96ZPiYv4A9An%2FDYOfz9oEbtA0kjXSqG%2Brqh5NtU%2BkW4iQL%2Fk5yK%2FiuNagjEcpTU7Rj6YqaSAbtG3%2FFin4om%2Fa%2BxhXcGeWxlDgpPbfvUFaPDKpFXiI%2FYk%2Fk%2FmwFXr0n5hfXBJFpVtxmC84jOa81%2FaKqv7Sfy5GL7zJq2rwXFhqF40AlWiSQfAEPVW%2BH7Sfz%2FwCRkTl1dLW3t7xi8108C3g9EBArCnI8jtVvtNRcR1zzTdJcQvYwRW7y28dwzFfUblIvI05fAo%2F2OGxSt3zSW9LPXITMlCjiuzofsyRt2YfsPkd0iabTtQksp1Zra4U84X6SJ%2Bw4%2FwAoD7Ei5dzqV7LFCt7KZnVAY%2BRqyg9aj%2BX%2BTJdouuaHrltb2PmCNfrkKmO2vKcT%2FkgsP2v5cjtapfo0Jt%2FMllFGHltZvVeK4ptwWNi6yU%2BzIvRlwm1mUarpNtJHEWutOkuIbh0FSbfkGiZ2%2Fb4MxH%2Bpk6uZNM8saddywStNK605tsKnYcF%2FmbOZy3s2nWaLE%2FG4e4%2BsK43HELxowOzK%2FL4lx5fFWb%2FlprP1TWLQMaLMFRh%2FlRtxr%2FwLYn%2BfNjLp%2Bp2WpwghWLxuf2SGoRX%2FAIDI9peo2kMtjfWQMUi3JM9u24j5rT4D%2B1G5qyfyfYzp%2FwCcdimr%2BUra%2FG%2FwpISOoIpXJDcIPN4HDcR3Scl2I6qeoOGNvcQyw%2Fo%2B%2FJWGpNvP1aBz%2B0PGFv8Adkf%2BzXIxSaym2NCOh7MMNYLhLlOS7H9pfA5FU30vUNQ8uaqjIqsaqHiO8UsZ6EeKsrfA%2BS3yUr6p5iudTl2S1RvRTchTKeCgV%2FlTlkOsvUveOn8VkdatauxoyN%2FID%2B0r%2FwAmTfyYHsrnTdPasc1%2FNczXMZArwhT04g3%2FAD05Yjml6Fditlcmu%2FpvX%2FgTnK5bS4sp1mDtFID6kLioPXZgc61NGTBNH4o1PpUjIpNbyX0Ulvcw%2FV9Tst7i2IqHWn99EP2kZf7xF%2F18nMX1pANMR1TTYfMkRuYYlt%2FMEY5EfYW7UftL%2FwAXjDf8u%2FMFzM8mnX6MJLWRFkMh4yiqtGUYU%2FZP82IzW%2FH4DVQG%2FduPtRP1FG%2Flb9lsG6ZJDJfPcvGI9WCoJZwPhmRGHF2X%2Ffi4AP2peafmBE8Hmq8LgATCOVaeBUL%2BtcjauUPip6qe%2BdJ82x293Pb2GtyL9ZmWRoL5QAUZZGQK47p9nOd39hdaZctaXScXXdSN1YHoynupwFCL0zUX0y4SVGf6uWDqybPG46On%2BWv%2FAA652nRtY%2FTdqJbfjLdqnK4gjpSVen1iBf5G%2FwB3Rf7qfOCxycaqwqjfaH8ck%2Fk%2B61K11AC1kZYIT6gmB%2Bxy2oP%2BMi%2FC6f7LEGlZCbBr%2B%2Bu%2FqqpBCjP6cbtxHwn4ljrX%2FgckOj2EMcC6lK6yTRKYmkIoYl6gE9xT7L5HhcTJqM1jJCEjjQSQsK7qT3rgi%2FuJ7PTDKQ31S5f03ZPi3SrfGP5RiKG6bej%2BQPzB0CG%2BuNHvB9VeZx9WvZKBX7cCf2P8jOvghgGU1B3BHfPHaQrfuEt3DHkAJakKhP8AO37A%2BedX8i%2BYPOen6dNp2qK5t4JFjs5HClyoNJfTdieUaL8S%2FwDCZEcRNEc%2BrfOOKWMTgeGUaEoHfi%2FpRP8Aunt2Ab3UorWqAc5QK8egHzOPhuoTYrdesJYgnJptgDTqdu%2FtkT1C9XnLPIwofjLduJ6fhkgLcYsi0vVRfSSRSUWRd1UeGGgyN%2BVrCSkmrXKlXnHC3RtuMXXlT%2BaTJKMTV7JdmzZsCuzZs2KuzZs2KuzZs2Kv%2F9Dv%2BbNmxV2bNmxVB6rafXtOuLXvIh4HwYbr%2BORbRL%2BsMTMaSQsOSnrseLZNDnIdfXXtP8zXtnZGC2tJWEsUz8pH4yAE8Y14rs3JfibJRPMKyf8AMXye3mKyh1LTH%2Bra1px9S0ulFSV6mNwPtRnIb5e8wXlw%2FwCj9VR9N1u12PGoD9vUgfpJG%2F7UedN8uapcXsH1e7C%2BrAi%2FvV6OPs1K%2FsttgLzP5Ysr%2B3NzHFxmjPMlNj%2FrLTdWHtiNtlU9I82sxa11lAssf2bmMfA9PFf2G%2F4XIt501y81SykEEjRQRMGEaMV5L0%2BKn2sq4ke3j9O4asuyLNSnMduX%2BV%2FxLCfVZ%2FS0u8cDkVhYhfGgxodGQDDZPMculusTzpwryMT7mneh%2B0uTVNL1e%2Bsob1tLmmtJlEkbFW5FGFR8NfUzi2iQ%2FXNXtLjUAXWe6hE3L%2BQyKHG%2FbjnqzzP5ok0H0baxgWVyodi5IRU6KBx74ASrzOzsfL1tcH69ppMtdxM0h4%2F883OS2GLS7239K1pGpGwj%2BEr%2FALHphVcecBcyrc6vBFdabL%2B7lV0UPbt12KDnx%2FlYYGvrWKwP1nTp3kirsT0BpWgcdf8AZDJg%2FFjSanSb8xC2ursXtpG5kt7dkVRGaU4g%2FETy%2FwBbIzc60xSeylsgphDJeafNtIY%2BnqREfCygYcWPmV14x3qkqQDzAo4X%2BYr%2B0ME6h%2BidZZImjM90FLQzQj40BFNn2%2B1X7DYkd2yi%2BrHNIh8uXbLp7yLJdToZbZJDX1EU0NOXxCeL9tf%2BJ4PhbTLya58sXqqt3EoKlV9MSoRUMn8ssf7XH%2FXyD%2BcfKFzoWnRatFqUUU%2BnSGSGAsBcFXZaFeHJVZDu2HOn3See9Kt1%2BsCx8x2y%2BpaXabB2TxH8pP21%2FZwAnu3VQuV1CC2iW9cx%2FwCkS28kMtA5dKLHL1%2B00TJyZPhk%2B1nS%2FKN59Z0GSNno6yI6e1V4v%2Fwy5z9pv8YWj6Hrcf6P836EWmiiHSfiv2ox%2B2kwH7P2ftLk28uxrbeXYLhYXV7gMzxkE0IqPiqNq%2Fy4gb%2BSk7PNdX1m%2BHmqaytJ%2FTtTdx%2FBGoSu6swZqcj8YbCnRmM97drKxKXcslu7tXb1%2BSBq%2FwCSzZK9c06xuNUutStkBls5IFkAIAMoHqUUfzNRkbCPW5Xs70rZcVtL0%2BtCoAFVc1oK%2FwArni2RIShdD0rU9Z0g2UNW1DR5JLG%2BirRlQuXhmp1ZP72M%2FwCph75g0K2sra21G8ch5baGJUpt6yARuvL6OWEQk1eJrq9s55YLxBzuGhJEjr3LKvxSqv7WISanq2oQRXGryvd2MtVhuSQ8aNQch8OyNjsqElhK1hbcr%2Fcv3K%2FyV%2FycRSQBmUiifsfQM6FpfljSbrR0mCy3EjDlDIzN9sGnphU%2FZ%2FlbIrqnl%2F6jM3Cb1VRqOoU8078XoOHPISIiQCd5ckgE3tyS4StK6xSy%2FDJ%2B7HqseO%2FYVw0W2thatp10he2b7YH21cfZljP86f8AD%2FYxO4tbO4so4I7ZUvG5Lpl3PssprUws392JD%2Fut%2FwCbAOh6sLuU6VqYMF4hKRu2x5DrFID0YY7rsh4tMn06%2FkspjzjniaS1nQfDIE%2BJWXwdafEv7Od0ugnmL8tmYipWEOB1oGWp%2FwCH5Zy4hoT9WuU5BTzj7FHp%2FeIe3%2BX%2FADrnR%2FysvRfaHeaJIwZ4RJGo8QDzQ%2F8AAy5OPVBfPNxbpMpifYqaBu4IwFYWlx9fEIJU7lmH7S%2F83ZJ9e0x7K9unRg8azMkoHWJySVR%2F9YH4W%2Bzj%2FKtvFdaxBDLQKzAVO243G%2F8ArYPJUXb2NvDZtdyWxe3B4meCQ%2BtG37Jkjb9lafaXHRXl9q2r6etnWJ7Yxx28h%2B1UNyMjt%2FlH4myX6lp1poPm57CIFLG8jikCsKisqqXWncc2%2BzhLZW0Om35hCuLiCeSgIHFY67KT9rl4YaV63IwMVSakg1pt27eGRuzvrTWILdYbnlcIWXSdQc8W5pXlp91%2FLIP91M%2F94mG9tcfW3AQ14xgsPH4c4Zp%2Btz6HqN2rJ61nNIy3loTTkA54yIf91zR%2FailyRNUinpV0iXqyyxw%2BjdW5Md%2FaN1iPdqf74f8A5Jtka1ZZ4LWYxsySKpaCRT8QI%2BIxkj2Hw5KLe6%2FTsUF%2Fp1wsmrRRk2l0QAL6Fft290g%2Fu7uP7Lo395hFquqaL9XZmmWCRwySWTgs8MgG6Oo%2FY5fZbAUhC%2BZ9JsfMl1SKYW%2BqyQRT2DMf3UvJeTwt4M5%2BJchKyrOr6D5gQwTQEpFK%2FwDeQv8Ayn%2BZDk%2B1Gyj1ew0SaC4gS5ntmCtX0ELQtQAM%2FEcv5Fwv1bQxr%2Bnhr4oms2ymP6xuCStSIbof6v8AdzYOaGAQ%2BXdTm1NdLjj5SN8QkG6FOvqBu%2BTjTrS2sbZLCMcVUmkpFGLn7Rf%2FAFv%2BFxTTme102DTuILIwZpKksW%2BzxWtW41%2FZyU2XlW41HhcahytOQJZVHKaXw%2Fd%2F7rY%2FzviAqQ21ldXtytrDG0k524qKmnj8sO736roNmNIltfVv7hFeaMnkUqTwpxNA2S2MaT5etS9w6WdtEvx1b42A7Symh%2F2C5zTzb%2BZun3swOjacGVKwrqMgoajeiJSr%2FOTDVdVZPYa3pOj%2BW7u31uKKzuZGP1dePP1Iz1UovxtJ%2FlYU%2Fl%2F5hOtyS%2BWrlis1Gm0pyfiR4%2Fi9Kv7SumcrvNYnu5DcXH7yToamrfSf6YL8ratLZ%2BZtJvoT6bwXMbEjuCwVh8uJauPFuFe%2Bx%2BYJ4NMawQiOFzyZOlDX4h%2FwQyQeVtAn1BY9R1QN9WU1toG%2Fa3qGYeGE3lnRLPVfM19LdAyWkTtcW8a7xOGaq1kGz7k%2FAM6moAACigA2AxJ7lojn72wANgKAbDLzZsirs2bNirs2bNirs2bNirs2bNir%2F9Hv%2BbNmxV2bNmxV2Qzz3p3P6rqKbFKwyt%2Fkn4lJPseWTPOVfnX5gmstNtdBtnMbajykuXHX0YyPh%2F2b%2FwDEcbrfm24MRzZI447GR59w6pMfNyWl7EdD1KM3Cp6dwoKldj%2B1zHE%2FRko0rz1rUtz9SvLe2lm2Hp8jC7BhWqV5xvUb%2FDnn01jPEin8pHgckemXmsTaHcxANw05o57O7IIKxliJYll%2FaRT%2B84%2FsNkIZhIkSjRDl6zs%2FwIjJCXFHkb5gp3%2Ba2r%2BbdPuEvYLVbLRpiFL27c2EvWkj0%2Fd8v2VpkGsfN2p3Kra3V2XicgEsQeQJ3qSNs7d%2BkdO1rSbKw1LhN%2BlbRXVJKFZyq8ZV3%2F3YGHLOEecvKFx5TvTLDym0idv3EvdG%2FwB9yf5a%2Fst%2B3lpHUOvsua4iNwbWCruHK%2FB0qN9j0zoy%2BarW48r2Oq3l1Le2sSizuGkqk6zgUVXMda8F%2FwCDTOO2M01rIs0XCRQ4kHqCqGnZl74NgkTlMqSOIZpPVNrXjGG8Qvfj2wXzSyqz1eO%2FtNSjQM8sVubhFbbkIpPiI%2BUR5Yca3q17IdPmtZ2js7yztriONNhyAKSciPtMJUfIZaXDWL%2Bra0jcq0ZYAV4yAq6mv8ynJf5Yex1PRjpUsfO%2F0pnuLJWNPUgkNZ4l%2Fm9Nh6vH%2BVpMRa2uvBcXej2Gp2rSC5tJJbOZkqW4v%2B%2FgY%2Bx%2FeJhtBB5sOi313boLa9ihMlo3EB5ClGkULy%2B16XJk4rhlZarBFAbZljtrS5Tg%2FoIF4fyOT1JjbC1L%2BbSbowyMwu4WrzJryX9h1PQpxw%2FFWFXNut9PB6srS6frlqI4JZHJ%2Br30bCsbOf5phx3%2FAN1T%2FwCRgPQbtreVrEFrW8gfnDU0dJUPF09unHD%2FAFeygjjvbi2haTQbpxcXlpHvLp9x9n6xClfjtjXi1P2P3b%2FGseE99ol9qtyjIKamYlktb6Mn0L5QKJ8Zp6d3x%2BH4vt8OD%2FvciTXPZaZ%2Bkdp5%2Bgt19Yad5p0%2Fe0vU2Y8T0NPiaM%2FtJ%2FuvOj6lcGysFaRlYxJWUjfkUXk3z5MM5p%2BV2l3U2pPdalFHK1sOLyqQxSZaFOdDWOT7W%2F7eTTzK8UdlcxREhXBTf%2BdzWg%2B7JxNji70HnTz%2B51GXyrrFxaapD69nfcRqSftqxHL1oj%2FOjPjtQ0GNYCfrCXWi3QMtlelhyjkYbFfdvsyx4M87Wy6zqemTq4X9KwQnmegkK%2Bmf%2BSi5G9M1PVfLUt3od3CJIGDEQS7iKUD4Zov8%2FjyErqrruPNkK58%2FJN7Sx0%2B0sDe3k0k9xA6JBJACGqwPHiKp8S8fts2F0mrDS3nubPThH6ZLa5YcQsnBvs3kSsCnJeXJuK%2FB%2FwAYmwQ%2FrXdjEsQRizhvjKCrUp8If4uW%2FwCyuK3Fs893p1nezfVdeDCOxmHFi8ZH%2B89wSeBjP2YuX%2BVFiYggA70Pmt9QzfyjdxXeiPeaY3rNYt9ZjdKUkgP2gyj7L8ftL%2FPgTz2tuLdJIT6cN1JFcxzJ2U7Sb%2Fs%2Fa5BsKfLEMvlPXby2sJI40uF%2F0izVuUcb05n0z3gkTlw%2F3237vHeddXN5pEMdnbmOys4BFcy0qDcF6GJa%2FwDFacmyX8Pu2R1QOi6Yupabd2F78dj6xjLVFUlpySaHwkUf7F0%2BDIrrvl6dr42N0eOtRIHs7ldkv4B9ijf8tKgfB%2FP%2FAHX28mPluyb0Vs7OdTL6SSReoK8%2FTZqN%2FrqOPL%2FJwTqdhFr9m1lqUhjnhcm2uv8AdltN%2FMKf7pb9tP8AZrjVhWHaHrEWqxjTb9gt9GKQytsXptxNf2sl35d3J0jznJbCojuQlR26cT%2Btc55qthdyXcyyJ9X8wWfx3ESbC6UbrcwfzS8fjfj%2FAHyfvPt88N%2FKuq3F3q1hqBIEyc4pWZuCkoAwck9MA50VPJV%2FMq2k0TznfCJQUmPJom%2BzJG%2FxcW9t%2FwDY4Q6cILa6ZY2eKRwslv6goaDfjy%2FaK4f%2FAJleadM80a8t1pcZ9GCJYmuG2MrD7TAfsqv2V%2Fmwr0WyuNXUw%2FVxci1%2BKLt%2Fw23wrg6qGVanr1vrK2t3csIry1aESBjxJEYYOQff4cjvm3zLbpZXLWTGC5vr%2FwCswRjfjBGoC8yfi%2BIjCPX2vNMu%2FqZDRNGKSAgUqxqgqKruF%2BH4sjsyPPIZZSXJ7knfG1e9%2Flxrdt5ksbqdSFu7eMLcW5O4%2BE0cV6xvnF75uVzcb14yyBW7%2FaO2KeS9RutC8yWd7DMYLfn6d3L%2Bx6D7SLJ7YhdkNd3UkRDRSXErxMDUFGclT9IwndCtpWrz6ZN8MskcDsGdojxkjcfZmip%2B2v8AL%2B3iWoySfXbiHUmFy14frKXqH4nL14zK%2FVuX7cbYEeJWDPQVpU0Pj0x6yqLL0pJUW4tXE1o5O9SaSRbeP28Cob1Z1T6vPKzxjeNSSQK9aV%2BznXLfUrdtDt9T1U%2BjeRxpa3iMvF5V41jcg%2Ftrx%2BLOd6VbaTqJlS7lMl5Iri3hHwRiQj920snhzODppWsii6zcGeSZg3po3NSy%2FD%2Fen4K9V542r0LyheaXcm5uaCCWNhxkl%2FvClOqcvs4rrn5g2enI9tpKi4uqlWck0rSvxP8Atf7HITazpccriNaLGD%2FozGnBVIJkZP261%2BFuWE4juLqSZo2pbJMjyitAQeVPwrjxbUE01reuXGvCKPUZJ2Cs0iqgUgFtunw%2FCKfDhWLXSzayW73jxuGElWgJUbUoSDhkLcXEjJDSJqkoSd%2BH7OGlh5a1HzDONIit%2FWvZEMkJRlQtGlKsCxVX4%2Fy%2FawWtILTLTTmeMmbTrm1jXe0nmMLOwH2nZlrWv%2BVhlBb6Lql09zpemrbx2cJe5WNqiRi3EKrfyj%2BZR8WJ6x%2BVPnXQrK41i9sljsrUB5pBLGzAVCgiNWLNufs4I8h6hp738sNxRBexei5BoBJWscg%2F2Xw4VZjoMllFbx635Y52k9mvPUdKaRjHIF%2B08PInhJQV6%2Fa%2BHOq6d5%2F0G7toZzK%2FBwKzBCVrTo3GpQ%2F62cYW21DQ%2FMUVrFH%2B5vyInPSIqzfE1f2afawkmtp42klt0f6uzuInWoDqrFQRQjkMJV9Q2ms6VfitpeRS%2BwYV%2FwCBNDg2ozzr5Y1vTI7V9L1PnbTDk8E4Qs7M1KKHBDp9GTfQfOdosSxw6txkp8UN0DxBG37WNea09UzZBNY%2FM3SfLcVrLrDIyXZKwm2bmW40DsF3%2BFa%2FF8eTW1uoby2iu7ducMyCSNh3VhUYEK2bNXNirs2bNirs2bNir%2F%2FS7%2FmzZsVdmzZsVdnE%2FwA9rKYXmj6jT9w0ctuT4OpEgB%2F1lOdsyO%2Bd%2FLUfmvy%2FcaXstxtNZyHosybofk32G%2F1sB5ORpMoxZ4TPIGj7pbPl%2BNPrERi%2FaG6H38MkPlXzh%2BjhHo%2BrmunCqwXFKmDmfiSRf27d%2FwBv9pMJBDcWN7JaXcRhuYHKTRPsVdT0P%2FNWB9RiWO6ag%2FdygSL7cuv3HKpkwImPcXoM2KGaHhy3B3BHf3h6B5qslXQooNOVof0a7Xdi6fDxBPKQQ78vT35xNl6Fr1l5s0yXSNZRHujHxlRhtMv%2B%2FE8JB9r%2FAIfILo2pT6XfLd8mmiKehPAzE8oT%2ByvKtOPWP%2BXKvrafSpl1XT2L6ez84LqLrE1a8WH7PH7NDlkMoluPiHRarRz05F%2BqJ5SHf1CT%2BZ%2FLlz5WvvTUmXT5jW2nP%2FEH%2FwCLB%2Fw2FauD8Sn6c67a3Vh5x0p7G%2BRfrAWskY6EHpLFXp%2FxpnLdc0S88t3ptrir27msE3ZlH6nX9pcmR1HJxFS3uA4oftDBlvcz2lxHc2sjQzxMGjlTYgjCVG6Mh%2BWD4ZRIvv3GC1Z7pdxHraNJCViukq13aswVAO9xCW%2F3Uf8Adkf%2B6%2F8AVw1t5NPkKabe3SyqKG2uIquYDX7Jc7ei37a%2FZ%2Fazm9tdT2dxHdWzcJYzyQ9R4UI7qw%2BFhkwhvDqECTaZbExyHhPbqKmCYivBf%2BKZPtR%2F8DgIkSDxVHqK%2FSyBFbjfvR8k1xY6gVtLb6vJbVWZbhvVLo38yAemYZF%2F4XCjW0McX986%2BX72Uem6V56bekVAYLu0D%2F8AJSL%2FAItjw7W0utVgj0%2B7k9DULc%2BnaSMaFkrvaz%2F8yXP%2Bpgq00iztbkaVcP8AWBer6NzEworLWjRsoJ4yRv8AEjr9jGOMDbn5y3UyJ8vcyfyHpl1puhy31zEE1e9cJfKxXjK8JMaTqfGZCrt%2FN9rCX8wda%2BqyabAGAM85aYA9FUcV%2FwCGyZXElvb20VgHb1LeNDEKmpKDhGS%2FfOK%2BcrhtU1G8vLZ%2BYsz6M0Q%2B0nBh6cwH%2B%2B5af7F8slsAAxG5tkesTm68saddo3GbTZ5bb1B1G%2FrRfrxGTW7LzPpcrahALfzDpir9biI4tJESAZowftJ8Svx%2FY%2F1cD6VMb3RNTsShJmhju4gVP2ovtH%2FgG%2BLBVnb6b5g0ux1JGWDXNDj4SEiongQcGjk%2F34hX7LfsYBukt%2BWba4gWHWmj%2BsWdpK0Dwqy%2BoWkonJE%2FvHZOfNcKdasnk80288TN6EEqxOXNWSOJiS7n2p8eB4bySw1ilv8Au4OQk47kKo%2BLv%2FLX7WK6jr4u9Uee1jItndiIiasyv9sV%2Fl3wWAFZBbvYajqsV%2FDSP1yITPUgSKH9SSinYcIRw%2BH7TSJhr%2BYOr2sOkWmnhHSK6YiRolFF5A%2Bn6ngJif8AWyP2ML6dbiRI%2FrqsAI3SRAsMY%2BxHKrlGhfl%2FefB%2B8%2F3XgXXYppJG1m2unkbj6Op2pUSKVAqksSNsif5L%2FFH9vHoUlrytdtDCgdmV7d5I2boU%2BINGT%2FLhpr3nLTKJ9UT19TB4zNHtCe3Jj3b2XIssp1Y8bUfFOQsix7SCSlAXpTmj4av5OnsdM%2BuzB3Zm9OWaNGD2%2FMgRTolP3sXP4Zv92fyYi6oITDVrG28x6NBeBvqWoWS1ivSeIikU8jFI%2BzLA6%2FGn%2B%2B3yFaq1pcXix28ZUGi3U2wadx%2FuziAEWv8Aq%2FH9vDLV7TzXeWrqbaWO4VhHeNHtFdCP7EhX5D4v9%2BfbwpC3liGgvIoVdSqt6pLPGr0pTj%2Bz8Xw4CUkEGil7RwRu9ZzwjPx8UZiKePShyb%2BRfMdhHM2kLEV%2BsDlDLQFncUCxcRX7WEF1DCs63zCs6r6c1Nkencr7rgXSpFsb5zCForepC9PiUEfZr4DADRVD%2BddUkPmPU4lUgVNtNG4qDw2Bof2kP2GyK1LECpY%2BAqTnUFhsta1Ce%2B1G3inuZCrySsgqwqFNRkwu9R8m%2BWIwscEHrharDBGjSH5tT4f9kcIoop4da6Vqt2GW0tbiQHbikbkH57UwHNFLbSvb3CNFLExSSJxxKsuzKwPQjOu3%2Fmq%2B1mFo1C2NmFMjhTQLGu%2FKRtumQfU%2FMWh3uoTXx0r1pJePKSWSnJlVU5lQNufHnhQxmOYxklSPiFCDvUZQKUIpUn7Jr0%2B7D8eYrBP7nRbUf61W%2FhmPmtkBFvptnEezCMkj78CpRZOY5w%2FBmWhBCjseuSyyn0pIpIr2CSr2gtbYTQt6aylufPp8PBWbCZPNurROskYgVkYOv7tSAVNRtg2780%2BYPNd5CdWuRJHEWkWONFjRaih%2BFAPljsqLeRrVh%2BjQt3GBRoXBBKAfFwag%2BLJ15B17yfealE2taTHcXMG63Eq%2FvbYL%2FvyH%2B6eFK%2F3iJyT9vAXkPygPNEuoxx3KQ3VrAHtYWP8AeOxpv%2FxWqijMv2WZMLdS8vBLx4pg9jqVqTFJIvwup6fvP5lP%2FDLillv5i%2BStPtL867pEEcem6hxkivLWjIk%2FevHb05ftZF9Okd5Et5mayvoWEtrdRmnCUfYmhYfzftp%2B3k3%2FAC68zLfx%2FwCFdWt1eElrbUoW%2BwGIrDPGv7CyU%2BLj%2B18eIeavKM3lq7WqfWNOkYizuXFeNesE9P8AiX7WNbqCncfm2fzRoUmg6zEqa7ZPDPPCv93eQRMC00A%2Fa%2FZaWLCHzf8Al7oOra5b6h5KuBHe3rj61Ywr%2B5Xlu0%2FL%2FdDfzx%2F8Qwpup4VWBrORhfW7iS0YkrJbkdRLKv2om%2FY4fbzsvkXU7fWNHju3jhj1ZAI9UEKhSJvEmg%2BF1%2BPEhXgPm%2B98y%2BWriTyxrM0iCMBo5VVW9aDoJIZm%2BLif5ftRthN5X8zxWXm%2FTZ9dHq6HH%2Foz2z1eNLd1Kq4TvwY%2Bo2el%2FPXkjTPPGkNY3n7q7hq9jeqPjikp%2FwANE%2F8AuxM8mazo%2Bq%2BV9UuNI1KL07y2PFlbdWU%2FZljP7UTfstih9Jy6d%2BVV3f2dja3ltHe6ipezS2mrzAFfh%2B2iMf2K%2FE2Xcfk5ojoTZ311bz1JEjFXG%2FitE%2F4lnlh3USLKCY7lSGLQ7UI3DA9mz0L%2BVP5wLqxi8s%2BaZRHqIpHZX0hAE9NljlPaf%2FK%2F3Z%2FrYqqal%2BS%2BoXb2EYvraWG3kcyvLG3IRuwY%2BkgPFW2PfJjZ%2BRruwuFa21iRYFACKFKsKf6r%2Bmf%2BReTUDxy6Yqo20LQQpE8rzso3lkpyb58QBi2bNirs2bNirs2bNir%2FAP%2FT7%2FmzZsVdmzZsVdlEA9cvNirzb8zvIf6bhOu6REP0tbL%2B%2BjUb3EQ%2FZ%2F4yp%2Bx%2FP9jOHagvqWsMw%2B1ETGwPWh33z1znI%2FzL8hqEutf0qP4JRzvrdR9lwR%2FpCDw%2F37%2FweRnGxTtNBreExw5DsD6Sen9F4gpphto2prYSSQ3Ketp11RbyAiu3QSoP50%2F4ZcK5YzG5Q7eGPEUyRpMykRSEhH7MV2IzGAkDY5h3OSEMkDjyCxL8beaZaglxoOpxTW7qLSWkmn3cX2eJ34nx%2FwApclMsen%2BcdIeC4QCZQPXjH2kanwyxe2RjT7%2B2e0bRtVHLT5SWhl%2Fat5Ozr%2FxWx%2B2uJWtxe6HqSw8wtxDvBL%2BxLGegP8yOMyceQEbcuo7nnNVpZ4JmMtwd4y%2FnD9bENU0y80C%2BazuRyTrHIPsunZh%2FTKil4kSIaj%2BGdZ1HTtM846SXUenKv2gN3glp1%2FykP%2FD5yO9s7vRb2SxvU4sh3I6MOzp7NkyOo5OKmSuGUMvQ9MMdF1m40S%2FjvoAHUfDPA32ZIz9pGH%2FDIf2Gwjt5Qh3%2Bw3%2BdcFnrir0GW5Kyx6hav6ltfqDHddW5Dbi1do5V%2Bw6%2FzZLPLbQ6nP8AWrpAby3oJGI2kXqkrfyyqR9rOU6HrKWHq2N8Gl0y6p66Lu0bjZZ4v%2BLF%2FaX%2FAHYmda8p2i2tk8sjrI7miTITxkj%2B1HJQ7huJ%2BLJR3K3snuq3fo2ruoqQC3idvsgD%2FWpnMJ%2FJ%2Fme4Msq2jC9tuUaXcXER3drJ1japH72NW%2BFmX%2Fit%2FwB5nTdIksyzW0c%2FryxVZgzBmCsa4eCZAKHDIWgbPDvK9jquh6xaW%2BtW81rDL6lozTKaFJQVAB%2BKtfhwvs47myvL%2BNQfTs2kD9UK8gQKlv2Xp8StnR%2FzQ1GW08u%2BlZO0d3dTxxQshKsKHm7hh9jiq%2Fazi6MxEiI5YOa3NyxJMjVrux%2BIrlctmQ3TS91E3jCG2jHI09R1qOdOgFf91r%2Fw2CLKFYxyJBIFZJOwHgMC2NryG3wRnq56t%2FZhusZiihu7cMWt5ir2zpyikUAU9T%2BYcuWR5pdPqiacQrWqXCzR1USVBAb%2FAHYp%2FnwutdVhtdTa%2FgWVBOAk8BcNG9BTqyk4c6rc2dxe2F3c2kcKJGI7qx4kUjpyR0B6Vr%2FNhBqc9haSxpHERaXRaWIt%2FuvcoTRa75PlyQn%2FAJZvYI%2FMUS2SNELiVTEygUjnVW9OlftI8hVWVsnSXVxfTGS6laSViCS2wodjsNs5%2Fo8rrL69qwd6pIrqAw%2Fd%2FYPw9OmSua51GVJ762swQXLCD1AiryNQrMfi2%2FyVwxKkIjW7%2BezjR7PTpr%2BUbv6WyUH8zfa6duOEPnjT7SEWmrxxkSXatazowA5KY%2BScgD9qIrxwQ3mPX7EetqFhAtqPtegzySAewA45F%2FMHm1dcRIGjMdvC%2FqRIKcuVCvJ2%2BR%2BzglLY2yhjnLaESfcEquburW8M5HpzKvIr1FaDl79cRbRr6S8%2Br245GCoac%2FCjKp67%2FwAww%2F8AL3li01qBLgPJO8f7t4wP7s%2FsE9fhOHer%2BVtfgg%2BvabcJe1FJrPjwZVUU%2Fdy%2FR9llyIFixuEzjKEjCYojmGM6jaajZ28RjBiEisJzULTi3iSKR0wboHkTXdWt21OLT5ru2A5RlN1k8SrsVWT%2FAGD5ena1aXA%2Bo6pD%2B9J2WcenL%2FlBJR8D%2FwCrnSfy880W%2Flyzl0m7uJZtJR%2BVmXSsltyNXhYD7cP7ScfiT%2BXDXcxosR1D8rvP2qWUdlZadHZW7kPcPcXEYeSn2EKR8%2BKJ9rh%2FNkRu%2FwArtc026ey1FooJo9yKs4IP7StQclz0brf5jeXtKhR7eYX0sieoiQHYL29Rv2Cf5T8WQ7W%2FMj%2BZrOKd0t1Ct%2B6eDk7xMaj05CaNxb%2FVwEgCy249PlyECMefJ4235fXQ63Uf%2FAt%2FXC7VvKsmkfV7iab1dPlISW7jTeFz%2BxJHWoPg37edQ5kyNDIvpyx%2FbQ%2B%2FQg91OB7hFCurIssUq8JoHFUdD%2Byw%2FU2IIIsMMmKeOZhkiYyHMF5g%2BhCNipcyFf3nwD7cH%2B%2FoT%2B1x%2FwB2J%2BziNyW01%2BNseVtMoMc4G7VH82TCbyxfWMxtUMlnDIPrOli55JIh%2FwCK2ZeMqthYYOMVxBNb%2Fu2HK%2Fsl6JU7Xlp%2B1xDfbT%2FdeFgmPknW7q0eC%2FsZ%2BOoWTck5ftRnrG9PtIfstnatY0iw%2FMfQ4%2FMWh8bfXbdSjxnbmyD47S4%2F5ky55gWcaZqDPp1yZo42Ppz8SvIePHOn%2BTfO2qaXML%2FSHiPqcRf2M9fTYf78Xj8XIfs4qoWsGsQ64Tpp%2Bo31zGbK9WYfFE8Z9TceI4FP9lnpNbWDVdGit9RRbiO4gT1g3QkqCW9jX4lzksE1z5382DU7WzEKoqq5UVAoOIeV9uR%2BL%2Fgc7RDGsMMcS%2FZjUIPkopj0UvHtQ%2FKnV9H1F9X8vXCajyYt9TuQoYLSijkx9OTj%2FlcMS8q%2BaLny95guoNYs2s%2FrfEXsLLxKyJXjLH4pxOdpwl1zy1p%2BvCOSctDcw19K5ioHAOxU1HxJ%2Fk4otNoZYriJJoWDxSKGRh0II2IyF%2FmN%2BXll550z4OMGs2oJsLynfr6Mv80L%2FwDCfayU6RYNplhDYNMZxACqyEBSVqSoIH8uDj0xV8NappV7pd7Np%2BoQNbX9qxSaJuoYfrr9pWwuSNg4NSHBqKbEHxrnrL80Py2g86WX1%2BwCw69aJS3kOyzoN%2Fq8x%2F5NP%2Bw2eXm0q8guZoblGglicpNA4pIroaMjfy0OKvpf8lvMusa5ol1ZazOLqXS3jiiuTvIyOvJRIf2ylKc86eM8y%2Fk35usfK2r3FtfyMtjqoRZZ3qRFNGT6bP4IQ7I%2F%2BxztF1%2BaHke01SDR31aN7mZ%2FTLRVeKNu3qTD4F5dMVZlmylIZQwNQdwRuMvFXZs2bFXZs2bFX%2F%2FU7%2FmzZsVdmzZsVdmzZsVdjXVXVlYclIIIPQg47GSOsaNI5oqgsx9h1xV4R%2BZvkA6VK2r6VH%2FuPlarIv8Aulz%2Bz%2Fxjb%2Fdf%2FA5EoJLK%2FwBFh0yOJU1AyrH6oABboqJItebtyJkjZP8AKVs73qnm%2FwAsyW89les7xzwtSMxMRKG%2BHghpx55w%2FwAy%2BWpbIHWNNjeGKFkDjlVw5%2BIPGwp8S%2FtcfsZGUauQHvdvpNWJxjhyyMZA%2BiXQ%2BUmMXNtcWVzJaXSGOeJisiHsRtll1uLf6rcMeKb20vVoW%2F6pP%2B0v7GHsLr5ptvq7mOPU4ElmEhHH1eCII4kp%2Bz6aO7%2FtephBNBPbytDcRtFNGaPG4oR7EZVKJHrhz%2B92JjDPA4cw3HMdR%2FSiiNG1m7028EiEC4j%2BGWMn4JU7qf8AjVslmvaBp3nLSFvbCiTKCY2P2opP24pKfs%2F9d5BTFFIys5ZGX9pKch9B65JvKmrSaVdevxea1m%2BCcEFQyj9tR%2FvxMtxZBIV8x3Oi1eknp5Ud4n6Zd%2F7XmskVxp11JY3sZikjbiyHsf8Amk%2Fs4caPay6peQadE6rJMwVXboF6k%2B%2FEDOn%2BdPIkfmSyGp6QA12qc4JFpSVPtcG9%2FwDjfI35a8qzaFNa3urKrXLmscfI1gP7NSv2n%2FmywxouIN1DWPKDW95DY6K0t5IfgnaUKgD05cl49I6H7TZ0bynBAmmjSVvhcz2qEM60%2BHnVRw9om%2BH%2FAFsItfhndg1q%2FpNdxmIuDQVU%2FECf8oYReSLk6R5nWBw1bmT6sSD8FGBb4h%2FNyXDyPvbsWITE9%2FVGJkB30jPKV1PpOsrBduRLFcPb3JY9VditW%2F2XxZ0%2B91O2spIoJWL3E54wW0Y5SOf9UdF%2Fmdvhzm%2Fm2w%2BoeapJlBVdRjWdCDsZE2cfeuHdvfQW0BngZptVvEH1u%2FlG6gj%2B5hH7Kr0xHc0pD%2BYg8wa1NDFZWjSWtsrCRoiGJcn46b1YdF2yFWemzxsW1CN4Ej%2FZlUofubOs2WpxpNGl2oMP2eSbEe5yTXGkadqFpJaT26T2860YEVBB6EHx8MeCzdpunlEtpHYQ38GqRrFNaPA8EgJcFa%2FGqhadao2XBGlmbe2uCzevbGeRo2PcNJx%2F2S%2FDht51s3sLpYLoH6ncIq21wRU1UceLt9Hxf8Hhj5f8pXerWc%2Bq2VrHqE1oqQWdvNLwUoijmJB%2FO9W4%2Fs8cjVH3LezEvM2mXWqyabNozB7SS1WJ5SwBrHWnLv8A3ZUbfy4onlazmtrKC%2BJlNoHFFNFbmQaHvtTJ9rPlzU7GOO6vrRbeNioQQMrRxmn938IXj4YE0fQLnXBdQ2062l1CP3SSry9Xav7og0%2F4LDsikps7C3s1WK2iWJB%2Bygpg5lNOX0YAVr7TL0xXsxcxsVlhktwpFPtLVWwbf%2Ban0e8tp7LTYrmyILlpyT6hoRw2%2Fu%2FTbdlw3taoK4v7a1dY2dW5GjQ1qVHj%2FwA24Vah5a03VlNxZEQTtvyX7LH%2FAClyfjyfo3nrTrbV9PRNI1i6gM8qRENC8gb05OUI%2BNR6nR1%2F4HIRqOj655Vu%2FQ1SBogT%2B7mU1ikA7pJ0%2FwBi3xZGwdizhOeOXFCRBSPSZNX8maml1MhNsfgldPijdD1H%2BS37S8s6ZLrEEZt2uEe1W6VWtLsg%2BlKGHIGOReS%2F6wOR9dWtRp11dSQ%2FWHt4Wk%2Br93IGyU%2Fysk%2Fk%2FW5INATT9Z0eAWczGT6gr%2BoIlfcgBxsf2vT%2FAGMYxMSeE2D07m7PqBmjEzjWSO3EOUo%2BY72M%2BYfLcd3FJeQRxyo7EzQU792GQ60bU9Hkf9HTAwts1jdVeNl%2FyX%2B2n%2FEc7ZL5XstVSS58p6kYZqVbTrysif6tWPrRr%2FspVzmuvaDq2jzsup2JtA5NCfjtmNf91Tjb%2FYyccJ8tmgJPqV4JJLJpI3sYLqJ2uY%2FtBgP91q6jpIy8Vw0sNNn8tG11m8KWNnqTrAums5YhTvyct%2B0R8fw%2FYb%2FWwLdRpfaVEkkPO50r1HhjJALRSg9HpX92%2FwCzgSW4TV9Hga5uppdVspFt7a0FOMNshHJm3VuZY8fV%2B0zfBlZHq3BNigOnm52LIfDHCYwMCJmVXI8IqMYnzI4f85l%2BsRQc7eSKVRPHKts4ruUkPAcv9R%2FiwbZaY1p5gtrHUoipjnjWZOo4lhRv8pGwcPL9pcROZo2S6ZkaXgyqoaFVk4hn%2BJ9uPqNxwVrmsWupvYzalF9S1K1lTi6GscsYYMpV%2FFWUNx%2FlwYoSjd8jybO0suLMcUsdmUQROxRrnH75PQtd8vaT5itBZapAJI0YPE6njJGw%2FajcfZzg%2FwCZWh3GhW%2Fr2wH1vSZ0DXlPia1m%2FuZuK7MV%2FuLj%2BbPQ1pdR3ltHcxGqyCtOtD3H0HIF%2BZFnb3k8EMqhxPBJHOh6MnIUDfSdssDq3zfqWmR6lFJqenRCO6iHK%2Fsk3FD0ng%2FmjfC3SNVudJuUvrKQJdQOrxc1DoafaSRGqrK%2FRlyU32i3XlnU4RHOYrF2Isr1hy9Jm%2F495%2F8AimT%2FACsKte0Zp%2FW1Kxg9KaL%2FAI6Nipr6bH%2Fd0X88En2lfFX0%2FwDlz5x0fzbpAlsIYrO%2BgAF%2FYxBVCOR%2FeIBTlE%2F7Lf7HJpniLyz5m1TyrqVtq2ly8ZYzuvVWX9qOQftI37WeufJnnHTfOekJqViwSZaLd2pNXhk7qf8AJP7D%2FtYFZJmzZsVdmzZsVaOct%2FNj8sx5ntJNb0NfT1uBayxrt9ajUfY%2F4zqP7tv2vsZ1PKOKvhz0CsbwMzqSSHPRgw2%2BL3%2FmwrlheB%2BD9exHQjxGeqfPH5Oad5lupdW0icabqU3xTqV5QTN%2FOyr8SOf2nX%2FgM59H%2BQfmu5nWC9ns4rbl8U6OzMB%2FkpwGKr%2Fyk%2FOBtOMPlnzTMWsyRHY6g5qYq9I5j3i%2Flf8A3XnoxWV1VkIZWAKsNwQehGc68sfkv5Q8vBZriE6rdrQ%2BpdAemCO6wj4f%2BD550VVCqFUUVRQAbAAYq3mzZsVdmzZsVf%2FV7%2FmzZsVdmzZsVdmzZsVdiNzNDbwSTXLBIUUmRj0A98WwNfPaJbSm%2BKi2pSUv9mh2pirAPON5pMWiStoiPJfM1bVI4yy1G8jDkCnwrhDrD6jFb%2BWbC7Wt36Ecl%2BCP%2BKiZHbtyJajf5WDvP9xZRaNCfKtxKb4zj04IOR403JKkfAOfH%2FWws84ajNd3EEklFuSOUtK%2FDRVXiKduQOFIeeatpk0F5PeaUj%2BlATLIYwSYhXd%2Fh%2ByleuLP9U12ykmiWO2vrWOW4n3PxgcAFXmaty3cf77%2Bx9jO1%2Flvo0EejT388SvJqDMjFxXlEvw8TX9ls5h%2BY%2FkSTyne%2FpHTVY6LdsVUj%2FdDt1hf%2Fitv91t%2FsMgRW4dxo9X4vDiySqcfon%2FO%2FoyYJsQGHfJL5FsrXUdZk0m6uTbR3UTvHQAgzRjkONSODla5GgcX0%2FUJNL1S0v41DPFINm8G%2BE%2Frym%2BHMD0k52txeLppjqPUPfF6l5X8xWlm995evZlguLSdkjR9gTWjrXovP4XTEPM6wwzKSKCT4tuoIO%2B3vke80xXU2tW13ZQ%2Bo15bq8npJ8UlELVp%2B3sMBWOsXOtWy2iEyXNuCyIw5Myf8VsaseFP7tvizL4tq%2BTzNUfvT%2BOU6npky9ZrciVAO5Xw%2Ba8sis01rp%2FmC01O5YrSWJ4kXcyFiB9nsK%2Ft5fljzLXzMlg0bRQujQESbVlDcl5IenL4kyeebPIp8y6j5e1DSEjX0XWO4QsqKYk%2FfRgV%2Fb5gx8cHMe5lGUom4mjuPnzSW%2F1OfVL6R7sJW2keKFUGygGhIJ3PKmNVy%2FTAk1pdWc9zBdrwuElcTIezA%2FEMj%2FmjXbvQtaW3tEBgCJM6PuCHFQExtizSKI1DHJv5VuxKrWMp%2BwC8JPh%2B0v0ZCNLvrfUrGG9tjWOVQfkf2lP%2BqcPdHme3vIZV24sKn2Ox%2FDJBSyzzBplrqWnSQSxLI0P76EsoIDqO3%2BsMgmlaxceUdRXU7ZS9jJRb62XoUP7QH80f2k%2F4HOgXdyVYgHp1%2BWQW8g4TzQyoQpJIVh1VuhGJCh7FG9hrempKhW4sryMMp6hlYV%2B%2FOWa%2FbXfk%2FU0ZJOEL1ayu2%2By1P91udv3if8OuCPJ%2FmhPL1xaaJetx0y4rFFMx2imJJSvhHLXj%2Fr4N%2FO9G%2FwAJ28o%2FYvY69%2BqOMgdlYzqPmWy8xr615aouoxAA3dk4dJB0%2FfRGjofBl54U%2BnCySRNSa1l%2B2vQq3861%2By65GPKcztqxjY7NEw4jYbU32zoNppF1qfrw2JX6yierHExoJivWIf5fH7DYYnZaY3pN9N5N1n9JJEbuIwyRoisY1kDbpVu3F%2FidM7xpl3Y%2BadFt7m5tle3vIUkaCYBx8Qowof5WB%2BPONG0jvLY28qlRUgqwoyOOu37LpiXl3XbjyZqk7XglmtZIfTEKNQGh5REFvsxr8XLjgIrdWQedvy7GiQPrfl4SNaxVe5s1q7Rp3ki%2FaaNf20%2B1kT07WGCqwcMjdJFNQc9C2N9BqNvHc29WiljSVGI2KyCopkFv%2FwAotHu9cOq2d1JYW0x53dhAo4O53JQt8MYf9ocMFyB23ZwGOQIkeCQGxqwfIsatNQSQrIrmOUbrIhoQfEEb5LLHzPP6X1TV4RqFo4ozFVL0%2FwApD8EmFms%2FlxeWBNxoEpuIhubSQj1B%2FqPsHwo0%2B4ltUmW7Ro5ojRo3BVhT2OWCi1t%2BbPJ1pFCfMXlJgtl%2Fx92VCyRHuwjb4kX%2Bdf2ciOlG2huhcX2n8JI2BE8FHHzMTb%2F8TVc6FpWutaySPblJEmHGeCQVVwP1ZGtcsIbe5N3p0Ui2MlWaM7mB%2FwBpQ4%2B1F%2FLkTGulhnDIYnYprf6tYaxaxxCDlxIaG7iko8Z7kGtWr%2B3ywxsdGl1WwitzGbkgkRzugDGnRm34R0%2FZbIJBZwxv9YhAVx8Qp0JPiBnafJl1eXekLNdyrKOXGNgADQD4g3Gg%2BE7Y8WyTLfiHNJbbQvNHle%2Fe50eQX%2BnXAD3Vg53EoWnKImhQmn7Pwv8AyYRX9xql1dmXWOS3XHiqMnp0QEkKq%2BA5fazrQwHqOmWmqQGC7j5D9lxsynxVsFsLeO39hb39tJbXMYlhlHGSNuhB%2FUffOZXy3%2Fl7UV05n5MgrpN1LTjLCT8VncEj%2FYf5Odt1nQbzR3JcerasfgnA2%2BT%2FAMrYB%2F5V3Y%2BdtAvhefubrn%2FuNux1jkQUYkD7Ubnir%2F6uEq8D1rTFk9XUdNgaHix%2Bu2XVoHPcD%2FfbfstnQfyAeb%2FFktC%2FptaSF1VgFNGTiXFfiA%2BLj%2FlZC9WuNd8p6kNM1e04anYsY2mY7TW%2FZG2pKjfsS4K003mkCHzl5WmDLazercWoB9WCvX1FB%2FeQt9lv8nIofYNRl5FPI3njTfO2li7taRXkQUXlp3Rj0Zf5on%2FYbJXirs2bNirs2bNirs2bNirs2bNirs2bNirs2bNir%2F%2FW7%2FmzZsVdmzZsVdmzZsVdgHVY9PmtHi1JlW3cgEuQBUbr1wdgPUdNttTtzb3IJWoZSOoYdxirzHztP%2Bi20aTyvdm6uDcJSCMJIPTVhRWKj7Bbbi2B9b8keZrnV3FvberbsQsVwHUKFJ6tU8hSvhk%2B%2FwAPGy1GwvLOkqK5ju0cIP3YQ%2BnItAPiif8A4nkkphKQaQOk6eml6ba6fGQVt4whPif2j%2FwWO1LTbPVrGfTr%2BITWtyhSWMjqD%2FEfs4MzEVwIsg2Ni%2BVPOHlm58p65NpU1XhP72znP%2B7IWPwn%2FXX7En%2BVgW28s63f2kWr29m76Ys6xyXQpxUggNUfa4iv2uOen9c8t6J5it0t9as0uo4zyjLVDKf8l1oy5EL7TriGW30Lytp7Q6Dbs0d7xI4Oz%2FFWPlycmNvib%2BfKziufETs7WXahOn8Ph9dVIn6a7%2FewDzPd3ukNplzYtxtEhW2adVBKzRktTkalOdf%2BBw60bydZ%2BaNR%2FwARaPq0VjOIw509bcLIkw%2B16yh%2FjRm%2F3an28de6dLYTXGk6xDztpx8aHoyn7Lqfb9nI3YX%2BreQ9ThS3pcWhcvazMBWSE%2FbgaQ%2FF8P8AJlrrDvunvmryB9buFvzAtnrkQD84z%2B6mKHkGRtu%2F82HWnO%2BoadLpU5aC4ZeUL9Gil%2B0jKR0aOYZOLO80jzho0d3Zv6kMn2WFPUhlGxVh%2By6H7WQWaO80rV1hvwBMjcfUAoskTbLIP%2BNsIr5oSu10tfOt7Ikt4th5ijQC6hkSsdz6fwfWIiCCr7UmTj%2Fl5JG%2FKTSdTjg%2FxDILh4UCUgHD7P8Axafj45GPMdkYtRkvbWf6jewEXNhcr9ppT9pEVatJWnxJx%2BznRPIfmz%2FFujfW5Y%2FSvLZ%2Fq94gBCmQCvNQfiVX%2B1xb7OA2NleaX%2Fkr%2FA19Jb2rO%2BlXbc7UvuFbuvL%2Bb%2FiWC7M7HOuarpdrq9jLY3i8o5Bse6t2df8AKXOP6jbaj5dvpLK5USBd0fcB07Ov8cMT3qkP5l6trkWp2kNi8sVr6CShoyVDyH7RJHXj8OQpfNfmCN1%2BsXcsnBeCrOfUAUdFHP4uP052ny%2Fq2g6veR6T5hgX94Almsp%2Fdsw%2F3W%2F%2BU37GSHUfyp8n6hUpavZse8DkD%2FgH5p%2BGCXNeTwM%2BbzeWxtr%2B3SVHFGMZKMP8oBqr%2FwANh9qv5gpr3kVfLV9zk1O3uIjFOwrzt4w1GZhUeqn2P8rJD5i%2FJJrK2lvNKufrSx%2FEYCnCXiOtCpKPx%2F1M503lq7tpDxDNUUUGlPwyJtWQflfpMWseaFtLgssZtZnDJ1VhxCn3651e38qanp%2Bs2ZX95brKG%2BtR7UVfio6%2Fs16YS%2Fk3pGmWxvNR%2BspJqfEW7WnR4kryLFW3PqN%2FL%2FLnW8I2UsW8x%2BVo71n1LT0CXp3mjGwlp3%2F4yD%2Fhsgl9oyXUaxX1u8LoCY2dStV%2FaG%2Fgc7JTEbiztrr0zcRrIYmDxlhWjDJCXQoeVeW9Rv8Ay1PKL25kuNO9ELEo%2BIjgaoKk%2FAqJyzqtrdQXkCXNs3qQyqGjcdCCK7ZFPMPl1Yg95aLWA7zQjfjXqw%2FycB6JrNzY3BW7uAbARKgUqSQVIC8KfCq8K8vhxq9wlntK4DvtJ07UgBfW6TU6FhvTwqN8FRyRyoHiYOjCoZTUEHH5FDC9Y%2FL6wuSZ9Ic2M3X0tzEfo6p%2FscL9B%2FLy8XVJNS8y3Yuo41aKz0%2BEkQrGwoWlO3N2zomVTDZV4l5n8s3Plq94oS%2BnTk%2FVZj27%2Bk5%2FnX%2FhsMNI893Wj2ht2thNVSEp8PGSlBK%2F%2BSf286nqOnWmqWcllexiSGQbg9QezKf2WGco13yhdaVMUT95A39zL0DDwPg%2BIZc3pPlzU5dStGa4IaeNgGZaAFWHJWoMOc5v5E1B7Wf6tcAoD%2B5PLb3jP0fYzo4xIYrZIo5UaOVQ6MKMrCoIPtidrZ29lAttaoI4UrxQdBU1OL5sCsO8%2FwD5e6V5708RXNINQgB%2Bp3oFStf2JAKc4m%2F4XOIWX5R%2FmVoOpummwIySAxNMssZhZG2q3Mqen80eeoKZqYq8l8h%2Fk%2FceW9Qt9Z1DU3W4gPJLS0JCEGvwTytT1U3%2Bx6arnWs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F1%2B%2F5s2bFXZs2bFXZs2bFXZs2bFXUzZs2KuzZs2KoXUjILG49E0k4EKetK43TYkhsoQp5EqGZvEnvgp1DKVbdW2I9jhDdaDqT%2FuLLWJbOzP8AulEVnAPVVkb4hiqB8wWUXmS%2BXTYWQi3ikZphuUlqOKmn7P8ANnOtRsiPW0TWUKqD9ofajYfZkQ%2B3%2FDLnYtH0Wy0W3aC0BJc8pJHPJ2J8ThV5u8tJrdoZrdQL6EH0z0Lj%2BQ%2F8a4bTbxXQPMGq%2Fl9r7mSs9jcUN3Cv2Zo%2B1xD4Sp%2FzZnaNYhtfNmgRaho7JcMwE1pKCASP246%2Fsn%2BZf58gsugWWtaPFp1xRL21qFkFCyP3U96YR%2BUfMt7%2BX%2BqvpmqEtpUsnGeOtQjE0FxD%2FwAzUxOy%2B5kHmGCOa1s7%2BVeEsFYpHI3SvwyBvDi647RtH1by3qEXmPT5VlgnWmo2C1Amj6rIrH4RMnVcCz3FvJ5x1pIqX2h30cVy3ouv2nQEeka9XZW5ZJNPult7dbZpTLasP9HlYUalP7uVf2JlH2v%2BEw891ZzY3tvqNtHdWz84pBUHoQe6sP2WX9rC7zJoEOu2Xp7LdRVa3lPY90b%2FACHwi0IahBqtdOo9hKf9LjY0Vf8ALU%2F78ybZFD591jS3R3gnRo5Ymp4MjA9snHkr8weRi0LzJII7pQEtdQc0SYdFWQ%2Fsy%2F5X7eH%2FAJw8uLqMDX9qg%2BtRL%2B8Uf7sQf8bLnJbuyjcNFKnND1U%2Fw8Dh5pfQo3Gc487eVFhMmq2KfuHNbmNR%2Fdsf92KP5G%2Fa%2FlyPeXPM%2Fmfy2Y4pmk1PRqfBBKpNwiDr6E3%2B7OH8j51%2B1ubXU7JLiKklvcJWjDqrDdWH%2FEsV5PntoFiuUuQOFxFvHKpKsPpWnLJZ5b8w65p1xEguXuIp3q8d07P1%2FZVm%2Bx%2Fkccbr%2Bhm2125sbPggX95AZq8FRhy3p%2FL8Srk181aDHqempqtvRLu2hDngPheMDmV%2F2P2ozjsrKLW5ju4Eni%2By4rQ9Qe4OL5D%2FACp5hgu7iOwkkUXFxD6yx9DWM8HNMmGJFFDRFRQioPUHIhrmgrAXubdK2z19WOleFepH%2BRkwymCkUIqD1BxBpWIaNq09o31WURrZoiem32ePEheCIB8VV%2BLJcGB3BBHtka1LQXVne0XnC9eUQ%2B0tevHxXA2lxazHfW620rx2atW4gkSqFOPCgdvjDClVwkA7hWYZswzZFXYnNBFPG0UyCSNvtKwqDimbFWE6voE1hIbu0Be3BrQVLIP8rxXJRo92b3T4Z2%2B1Ti3zG1cGkAih3B7ZSRpGvCNQqjsooN8NquzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Dv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxViPmbyo91cfpvQiINVT%2B%2Bh6RXSD9iXwl%2F33N%2FwWQzW9AXXo%2FXaFg7L%2B%2BhK0mjdBuafzACjrnYcDTWNrPKk8kYMsZDI42NR40w3tRW3z1p2l6ho%2BotLAfXVE5OgDVljA%2BLj%2FxZGv7OS2BbeaRbscpI5YuLQq3ETIfiQcv2ZVP90%2F8AsMk3mi1FjqBnt0CpcIHIUU%2BNT8RFP5tuWRDTFEulwPaP9YjVSfVjBIFGPM%2F7BvHCOSXqXlu6trnTI1t7Y2Zj%2BGW1YhmQ%2BJcfb5fzYcZBPKl1I7GRXHKGi3AY%2FajPRvdsnQNd8BFFDjnL%2FPWhJaXguIKRw33KngklPi%2F2Pxcs6jkY892Ul3onqwoZJLWRZeAHIlT8D7fJsAVBaLpUeueS9PtriYPcWykQXkfZ4yVDD%2FJP2WwX5X1NJ2NrQIGXkoXpzQ8JP65GPJfmOTR5G0zXLmGCzlYmzhZPSeE05ceP7UTjfliOhagkWs3CW0gkUXbPZjoZVkY8kQHr8OSHUJZD540vmYtVRBIAvoTo260JrGzU7cvhzaJ5jgm0t9O1u7giu%2FRdY%2BJ48oyCg%2Fd9QyfZ%2FwArJhPBFcwvbzqHikBV0PQg9sgt%2FwCSLpJy9pxniPRiQsgH8rfzYB5ql%2Fkq1hXU4ZigNwjsizftemU3Uf5Jzp%2BRXyloF%2Fpj3FzqYiR3YraxREsyx%2FzTOaL6j%2Fyp8CZKsSUOzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Hv%2BbNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxV2bNmxVg35iPrMEFld6XbR3CRM%2Fro1fUIIHwx%2Fs4U%2FlrqOkz3c1lYQmENFJI8DCgRzIDIg5b1JavTOiajZLf2cls1KsKofBh9k5ydpJPL%2FmzT9ZcBIWY2t5sFoD8Ls5C1%2FwCDkyQ5KyjWdLGi6ml3brSyvKxuo6I58P8AJPVclOiXZurPi5rLAfTf5fsn7sE3drBqFq9vLR4pV2I7dwy%2B%2BF2j6Xf2F5dS3EqNbyhViVa8iV%2Fbfag6%2FZXBdj3KnWURXLzYFYV5n8n3us6ut7btEITGsbc6hl418B8WFd75Ym8svHqVqPrQiCsJSPiikH2jQfsNnScayhwVYBlOxB3BGG1Sby7rf6YgZ5KCZftIBSmHeFllodlp95LeWqlDMKGIfYB7kDDPE%2BSuzZs2BXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F%2F0u%2F5s2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZFfM%2BipLW%2BSJZAf71CtaMPsygf8SyVZRAIIO4PbCDW6pP5YvDd6Wiu1Zbc%2Bi%2B9Tt9mv%2Bxw5xG3tbe1Vlt41jDHkwUUqfE4tgPNXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFXZs2bFX%2F9Pv%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV9%2F5s8AZsVff%2BbPAGbFX3%2FmzwBmxV%2F%2FZ');background-position:center center;border:1px solid #0a0}.yui-skin-sam .yui-h-slider{background:url(bg-h.gif) no-repeat 5px 0;height:28px;width:228px}""" + + self._test(input, output) + + def test_yui_dataurl_base64_twourls(self): + input = """ + .yui3-skin-night .yui3-dial-ring-vml, + .yui3-skin-night .yui3-dial-center-button-vml, + .yui3-skin-night .yui3-dial-marker v\:oval.yui3-dial-marker-max-min, + .yui3-skin-night v\:oval.yui3-dial-marker-max-min, + .yui3-skin-night .yui3-dial-marker-vml, + .yui3-skin-night .yui3-dial-handle-vml { + background: none; + opacity:1; + } + + div.base64-singlequotes { + width:100px; + height:100px; + background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QAAAAAAAD5Q7t%2FAAAACXBIWXMAAA3WAAAN1gGQb3mcAAAFrUlEQVRYw%2B2Xz28kRxXHP%2B9Vdc8vj2fG9uIkm2yUeFGEhNCKQwBpj1yRUBAnpJU4ceZP4MxfkBxy2GO45bQXuEGQohUKigQ%2BsBBE1sbYXv8Yz%2FRMd9fjUN09PV5nFZA4kZZa79W3quu9er%2FqNfy%2FPwJgZty%2Ffz%2FZ29vrpmmqhlWTgmFg1UpbfWhYhG6Yq2cFi%2FNrj9nJyWnx%2BPHjeafbMTEzPvjVB9%2B6d%2B%2FezweDwV1BfHubivkC3lZya%2F4m7Np8UZYXhweHH7733rvvC0Kyv7%2F%2F7p07d34qIjyvr63RNb4l4CbsRUrNrq6OfvfRRz%2FxDx486A%2F6g7vXhX9ZIf%2Bp4JomaToZj8d7Pk1SFRFXb1aWodq09l%2F9YZu%2FCWMVL9e%2FaVFVQVUREEG8r3VUEZ4cX%2FHL30%2B5shRtAhFq4wggNb6GrXgVa2K2jYtACIHv7Sz54TfHjWWagBOBy0Xgt%2BcjLtwQxVCpNwURQat1DdYaT7pwdwKDFA6ncHgFizJmSb3WLPDa7PMIVlb2daqZxdONUnAetEojEdBGiXXhTmAjhZ0%2B3N6AlzeEROH1TXiWGf%2BcwmkWZYnAfAkdx1pq%2BhUbT9z3IElUJlVhI4WsgCLUyggi4MQYd4VXNmCYRqXOspV7UoHbQ3hpEMeLAk5mkFYRYlWI%2BHZ6CTDwkKSQqrA7gFeHMF3C6VyYLmFZGiKCE6GrMFsaWb7yuVOh66N1FkUUpBJ3TyvFovToBl9XMbNY2bZ70BsKfW94hbyErosmXpQwy4VgkCjMizjvRRik0EtiLNwagBf465lwOqtkETEn60XT1%2BlTazrpKlR%2BKkooy3Xfb3Wj3wGWJRQhnni7B53q5FqZ4%2FUR7PYhGMxyuMwgWVqTxrYWAwZYQX78J5i8ie9txSAkoOqqQAqk3pBgKLDhlY5XVKPCi3w97YRoKSFaceCgF2hqSXRBPQAIOcXRH8hP%2F0y6%2BRpWLrEyg3SbS9tmzggzJQSLQZo4xoOE8UbCsJfQ7zi6iaIizZ5OBVfXDKPireWC%2BvAYWOBqeszl9Jg03afbH6OqGH%2FBSUrqbrFIXiHoiEDC%2FPySi2d9nnZHJE7opY7dcY%2FdcZej84zpPGfY9Uw2ErYGCf2O4mUVEzELWtqUxZKjf3zK2cURzicMRy%2BzvbuH8ylFPqXIPqNczClKY1mUZFcnSDKkv%2FUNuptbWG%2BLw%2FMh%2FzrYpKBHKV3ONOXzYxeV20z49qhs7gaMWIjqQQiBxVXB4irg05yz4u%2FMpieoJuTLjDJfUJYBzBAFUUHkhMvLvzFPuvQ2JvQ3duh0R%2FjuiDQdIn6MJdvkxTafZUPuaN6UaOo6UNtgNl9weboguwqoA5fCjEusNCzE8LJQFRuNrzpQD%2BrmZNM55%2F4A5z1J2iHtDEg6fZK0j0uHlDrh2N4i3Ptuc%2BH5tjp5HliWQmmOUAhlYe0rqUqWSEOhcZ05AvE1UdColSQJaHWlSUYgJy%2FO2fFfo%2F34piphFOZ4OrvNdL5EJZbcmE8OQzFxIJEGEgIeE1%2FhrjJLxZcOrIVpVC7TW00WmLWyAMDEs%2Bi9wVJDtG17Y21vVmNxLKqIOkQqqoq2qHORmgj9UQcza7mgFm4GmiDjO5C72DSoQ6uPVTW%2BzuFUcQ2teKd4pzhd8b6Nu6jg7vZsrb1rClEIxhu7Q37x47fIy3j%2Fi8iqqlUuEWTFN%2FOrMU3fEEuhUM8FIPDqZgcRIVhsAXxtjGDGS5M%2BP%2Fv%2Bm7yopVrRVsPabsW%2BYG1T%2Fy3KahqSg6cHRb5cTmt5ZXhxQ7nS6yZsVWOajDFbw2JSCSGEIsuyC%2F%2Fo0aPp%2Fv7%2Br9NO5%2F5gMOhJLNn%2FpQLrLfw6tkKKorCDg4NPP%2Fnkjx%2FLOz96h2enzyZvf%2BftH9za2fm6qLrnO9tGk2vY86f%2FMliWZRdPnjz5zcOHDz%2B%2B%2Fifimorzv31C9X718G%2FYrCYSNJa5LgAAACJ6VFh0U29mdHdhcmUAAHjaKy8v18vMyy5OTixI1csvSgcANtgGWBBTylwAAAAASUVORK5CYII%3D'); + background-position:center center; + border:1px solid #00aa00; + } + + div.otherdataurl { + background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kFBQ0WDWwqwjwAAANMSURBVEjHrdZbaFxVFAbgb2aSTG6GTi6mVIwxNxF9qFI0RQnFUqiYamutVutLa2t9EY0oPggFoYgPRR%2FaghYviA%2BiIAYvmBJKoYWiiBCigVTT1FisbUhrEtNkJpc5PuQkjGEmJqkLFmdz2Hv%2Fa%2F3rX3tvlm95oS%2FLokuZtIpbdvAs7KFtL22wjb3V1C41upy2ke1DXC2k%2FBjv1HHXDrbkEamg7lX2P8QTldQ2UtfOB8uiJsHNiB%2Fik0GmO%2BgZIxgnGA59nGCMoJPeQaYP047iBDXZEohkAYof4%2FNyKlZRdR%2F1ASZCnoOMhWkUheMz9F1laJSRZ3gEqVw1ipZQjcoBRrbT3Ez9OJLhZkHG5CD8l8Q47qXhMZp%2FYxhVFaxBLBtQejdtA%2FTtZPMIJnOknI2WSYzicTYN8OtTvICZbECxdr5Pkm6iPL0C3c%2BgkfIJgi%2F4LnP%2FudRKD3K4jf1VJGLEAiuz6VnA4AGam1h7gpNIzSFe66D3NurLKVhJNkHo07N9V9BE3XHOYmyeuirqG1l9mdHgOkDSGd8%2FGWtg9Roa56lrYdchDtRQPLlCkEywKVRScDfrurnwC2diiPTRe47iVtbnLZDxckGCkKYpPM%2FRr3kbyRhsYOtRDiQonFoBSHrBOI18rOeOPvr76YrCeUbf5fTvjOddJ0gQ1uMPku9z6hwjiEZhgOMn%2BaaUeHQJIOkMD7KMA5QQP01HP18hPbfvDTvZPETqb2YiS1BWrozM6jk9SPJJHkZZ5qFagtoaNnbyZg1FE4sUPRdlAQpwkdSjvDZAJ%2FoxNt%2Bw6NlGbQVFl5iKLKKsXCAwyFQZ8S3ciu65ho1lBJ5%2FkZk3OBGjpJWGmVCmsjTkQvA8JHCUU7s5eImevzg%2Fd7BGFhzCARIf8uVN3J5Heh1VM%2BHlko2y%2FHBxF0NJolfo38eDuJJxbf0ro%2FnMurh8hM%2FupH4tdT8zciOFsTC8SAgQoJfRxKzCuvfw0k%2F8MDx7xqUyez%2BS48ESIN7Ky6OUpfAtrxzhx03UR4m0c%2FZF7tnKW2mma4l9yuthTSIZIvxPi6EcpZW0PM0xtOzjo%2Bf4GPfv4r1qNqAUFYs9diJLBC1CIa7FZx8fUlwI22LuNv%2FfLbKMAOftH9TwRXg6%2FiCDAAAAAElFTkSuQmCC"); + } + + .yui-skin-sam .yui-h-slider { + background: url(bg-h.gif) no-repeat 5px 0; + height: 28px; + width: 228px; + } + """ + + output = """.yui3-skin-night .yui3-dial-ring-vml,.yui3-skin-night .yui3-dial-center-button-vml,.yui3-skin-night .yui3-dial-marker v\:oval.yui3-dial-marker-max-min,.yui3-skin-night v\:oval.yui3-dial-marker-max-min,.yui3-skin-night .yui3-dial-marker-vml,.yui3-skin-night .yui3-dial-handle-vml{background:0;opacity:1}div.base64-singlequotes{width:100px;height:100px;background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QAAAAAAAD5Q7t%2FAAAACXBIWXMAAA3WAAAN1gGQb3mcAAAFrUlEQVRYw%2B2Xz28kRxXHP%2B9Vdc8vj2fG9uIkm2yUeFGEhNCKQwBpj1yRUBAnpJU4ceZP4MxfkBxy2GO45bQXuEGQohUKigQ%2BsBBE1sbYXv8Yz%2FRMd9fjUN09PV5nFZA4kZZa79W3quu9er%2FqNfy%2FPwJgZty%2Ffz%2FZ29vrpmmqhlWTgmFg1UpbfWhYhG6Yq2cFi%2FNrj9nJyWnx%2BPHjeafbMTEzPvjVB9%2B6d%2B%2FezweDwV1BfHubivkC3lZya%2F4m7Np8UZYXhweHH7733rvvC0Kyv7%2F%2F7p07d34qIjyvr63RNb4l4CbsRUrNrq6OfvfRRz%2FxDx486A%2F6g7vXhX9ZIf%2Bp4JomaToZj8d7Pk1SFRFXb1aWodq09l%2F9YZu%2FCWMVL9e%2FaVFVQVUREEG8r3VUEZ4cX%2FHL30%2B5shRtAhFq4wggNb6GrXgVa2K2jYtACIHv7Sz54TfHjWWagBOBy0Xgt%2BcjLtwQxVCpNwURQat1DdYaT7pwdwKDFA6ncHgFizJmSb3WLPDa7PMIVlb2daqZxdONUnAetEojEdBGiXXhTmAjhZ0%2B3N6AlzeEROH1TXiWGf%2BcwmkWZYnAfAkdx1pq%2BhUbT9z3IElUJlVhI4WsgCLUyggi4MQYd4VXNmCYRqXOspV7UoHbQ3hpEMeLAk5mkFYRYlWI%2BHZ6CTDwkKSQqrA7gFeHMF3C6VyYLmFZGiKCE6GrMFsaWb7yuVOh66N1FkUUpBJ3TyvFovToBl9XMbNY2bZ70BsKfW94hbyErosmXpQwy4VgkCjMizjvRRik0EtiLNwagBf465lwOqtkETEn60XT1%2BlTazrpKlR%2BKkooy3Xfb3Wj3wGWJRQhnni7B53q5FqZ4%2FUR7PYhGMxyuMwgWVqTxrYWAwZYQX78J5i8ie9txSAkoOqqQAqk3pBgKLDhlY5XVKPCi3w97YRoKSFaceCgF2hqSXRBPQAIOcXRH8hP%2F0y6%2BRpWLrEyg3SbS9tmzggzJQSLQZo4xoOE8UbCsJfQ7zi6iaIizZ5OBVfXDKPireWC%2BvAYWOBqeszl9Jg03afbH6OqGH%2FBSUrqbrFIXiHoiEDC%2FPySi2d9nnZHJE7opY7dcY%2FdcZej84zpPGfY9Uw2ErYGCf2O4mUVEzELWtqUxZKjf3zK2cURzicMRy%2BzvbuH8ylFPqXIPqNczClKY1mUZFcnSDKkv%2FUNuptbWG%2BLw%2FMh%2FzrYpKBHKV3ONOXzYxeV20z49qhs7gaMWIjqQQiBxVXB4irg05yz4u%2FMpieoJuTLjDJfUJYBzBAFUUHkhMvLvzFPuvQ2JvQ3duh0R%2FjuiDQdIn6MJdvkxTafZUPuaN6UaOo6UNtgNl9weboguwqoA5fCjEusNCzE8LJQFRuNrzpQD%2BrmZNM55%2F4A5z1J2iHtDEg6fZK0j0uHlDrh2N4i3Ptuc%2BH5tjp5HliWQmmOUAhlYe0rqUqWSEOhcZ05AvE1UdColSQJaHWlSUYgJy%2FO2fFfo%2F34piphFOZ4OrvNdL5EJZbcmE8OQzFxIJEGEgIeE1%2FhrjJLxZcOrIVpVC7TW00WmLWyAMDEs%2Bi9wVJDtG17Y21vVmNxLKqIOkQqqoq2qHORmgj9UQcza7mgFm4GmiDjO5C72DSoQ6uPVTW%2BzuFUcQ2teKd4pzhd8b6Nu6jg7vZsrb1rClEIxhu7Q37x47fIy3j%2Fi8iqqlUuEWTFN%2FOrMU3fEEuhUM8FIPDqZgcRIVhsAXxtjGDGS5M%2BP%2Fv%2Bm7yopVrRVsPabsW%2BYG1T%2Fy3KahqSg6cHRb5cTmt5ZXhxQ7nS6yZsVWOajDFbw2JSCSGEIsuyC%2F%2Fo0aPp%2Fv7%2Br9NO5%2F5gMOhJLNn%2FpQLrLfw6tkKKorCDg4NPP%2Fnkjx%2FLOz96h2enzyZvf%2BftH9za2fm6qLrnO9tGk2vY86f%2FMliWZRdPnjz5zcOHDz%2B%2B%2Fifimorzv31C9X718G%2FYrCYSNJa5LgAAACJ6VFh0U29mdHdhcmUAAHjaKy8v18vMyy5OTixI1csvSgcANtgGWBBTylwAAAAASUVORK5CYII%3D');background-position:center center;border:1px solid #0a0}div.otherdataurl{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kFBQ0WDWwqwjwAAANMSURBVEjHrdZbaFxVFAbgb2aSTG6GTi6mVIwxNxF9qFI0RQnFUqiYamutVutLa2t9EY0oPggFoYgPRR%2FaghYviA%2BiIAYvmBJKoYWiiBCigVTT1FisbUhrEtNkJpc5PuQkjGEmJqkLFmdz2Hv%2Fa%2F3rX3tvlm95oS%2FLokuZtIpbdvAs7KFtL22wjb3V1C41upy2ke1DXC2k%2FBjv1HHXDrbkEamg7lX2P8QTldQ2UtfOB8uiJsHNiB%2Fik0GmO%2BgZIxgnGA59nGCMoJPeQaYP047iBDXZEohkAYof4%2FNyKlZRdR%2F1ASZCnoOMhWkUheMz9F1laJSRZ3gEqVw1ipZQjcoBRrbT3Ez9OJLhZkHG5CD8l8Q47qXhMZp%2FYxhVFaxBLBtQejdtA%2FTtZPMIJnOknI2WSYzicTYN8OtTvICZbECxdr5Pkm6iPL0C3c%2BgkfIJgi%2F4LnP%2FudRKD3K4jf1VJGLEAiuz6VnA4AGam1h7gpNIzSFe66D3NurLKVhJNkHo07N9V9BE3XHOYmyeuirqG1l9mdHgOkDSGd8%2FGWtg9Roa56lrYdchDtRQPLlCkEywKVRScDfrurnwC2diiPTRe47iVtbnLZDxckGCkKYpPM%2FRr3kbyRhsYOtRDiQonFoBSHrBOI18rOeOPvr76YrCeUbf5fTvjOddJ0gQ1uMPku9z6hwjiEZhgOMn%2BaaUeHQJIOkMD7KMA5QQP01HP18hPbfvDTvZPETqb2YiS1BWrozM6jk9SPJJHkZZ5qFagtoaNnbyZg1FE4sUPRdlAQpwkdSjvDZAJ%2FoxNt%2Bw6NlGbQVFl5iKLKKsXCAwyFQZ8S3ciu65ho1lBJ5%2FkZk3OBGjpJWGmVCmsjTkQvA8JHCUU7s5eImevzg%2Fd7BGFhzCARIf8uVN3J5Heh1VM%2BHlko2y%2FHBxF0NJolfo38eDuJJxbf0ro%2FnMurh8hM%2FupH4tdT8zciOFsTC8SAgQoJfRxKzCuvfw0k%2F8MDx7xqUyez%2BS48ESIN7Ky6OUpfAtrxzhx03UR4m0c%2FZF7tnKW2mma4l9yuthTSIZIvxPi6EcpZW0PM0xtOzjo%2Bf4GPfv4r1qNqAUFYs9diJLBC1CIa7FZx8fUlwI22LuNv%2FfLbKMAOftH9TwRXg6%2FiCDAAAAAElFTkSuQmCC")}.yui-skin-sam .yui-h-slider{background:url(bg-h.gif) no-repeat 5px 0;height:28px;width:228px}""" + + self._test(input, output) + + def test_yui_dataurl_dbquote_font(self): + input = """ + /*csslint fontfamily: true*/ + + /** + * Foo + */ + + .y-ff-1 { + font-family:"Foo Bar",Helvetica,Arial; + text-rendering: optimizeLegibility; + } + + .ua-op .y-ff-1 { + /* Some Comment */ + font-family:Helvetica,Arial; + } + + /* + Foo + + Bar + */ + + @font-face { + font-family: "Foo Bar"; + src: url("data:font/truetype;base64,gRbIUFAIrsQNGditEWbAUKwAA") format("truetype"), + url("http://yuilibrary.com/fonts/foo-bar.svg#webfontse22fewwr") format("svg"); + font-weight: normal; + font-style: normal; + } + + + """ + + output = """.y-ff-1{font-family:"Foo Bar",Helvetica,Arial;text-rendering:optimizeLegibility}.ua-op .y-ff-1{font-family:Helvetica,Arial}@font-face{font-family:"Foo Bar";src:url("data:font/truetype;base64,gRbIUFAIrsQNGditEWbAUKwAA") format("truetype"),url("http://yuilibrary.com/fonts/foo-bar.svg#webfontse22fewwr") format("svg");font-weight:normal;font-style:normal}""" + + self._test(input, output) + + def test_yui_dataurl_nonbase64_doublequotes(self): + input = """ + div.nonbase64-doublequotes { + width:100px; + height:100px; + background-image:url( + "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%0C%00%00%00%0E%08%03%00%00%00%2Cc%0D%DE%00%00%00%A2PLTEQQQ%FA%FA%FA%FC%FC%FC%EE%EE%EE%A9%A9%A9%E9%E9%E9%0A%0A%0A%0D%0D%0D444PPP%CD%CD%CD%CC%CC%CC%F5%F5%F5UUU%D0%D0%D0'''%F9%F9%F9%A6%A6%A6%40%40%40FFF%A0%A0%A0%89%89%89%8D%8D%8D%20%20%20%14%14%14%DA%DA%DA%B6%B6%B6%02%02%02%87%87%87%81%81%81%AC%AC%AC%0E%0E%0E111%7D%7D%7D%92%92%92333%B9%B9%B9%BC%BC%BChhh)))%E1%E1%E1%03%03%03%CB%CB%CB%EB%EB%EB%FD%FD%FD%A3%A3%A3(((%04%04%04%CA%CA%CAttt%2C%2C%2C%F4%F4%F4%00%00%00%FF%FF%FF%D6%DE%02%C3%00%00%006tRNS%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%00%A1%8FN1%00%00%00iIDAT%08%D7E%C7E%16%02Q%10%C5%D0j%C3%DD%BDqw%5E%F6%BF5%06%D4%3Fd%94kx%BDf%DE6%FFIA%AB%C8qYj%1F%E3Xk%93%E0%C8JZ%10%90%9E%3A1%60%BBY%85%A8%AE%14%C0%5E%1A6%8E%C5w%02%60%99%C9%FA%9A%03%60%8C%EFz%8C%CE%0EnSu%3F%01%AD%B2%06%04%F0%3CT%FF%B8nk%3F%7C%01%C5z%1B%F9%26%2F%3Az%00%00%00%00IEND%AEB%60%82" + ); + border:1px solid #00aa00; + } + + span.othercss { + font-family:"Times New Roman"; + font-weight:inherit; + } + + """ + + output = """div.nonbase64-doublequotes{width:100px;height:100px;background-image:url("data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%0C%00%00%00%0E%08%03%00%00%00%2Cc%0D%DE%00%00%00%A2PLTEQQQ%FA%FA%FA%FC%FC%FC%EE%EE%EE%A9%A9%A9%E9%E9%E9%0A%0A%0A%0D%0D%0D444PPP%CD%CD%CD%CC%CC%CC%F5%F5%F5UUU%D0%D0%D0'''%F9%F9%F9%A6%A6%A6%40%40%40FFF%A0%A0%A0%89%89%89%8D%8D%8D%20%20%20%14%14%14%DA%DA%DA%B6%B6%B6%02%02%02%87%87%87%81%81%81%AC%AC%AC%0E%0E%0E111%7D%7D%7D%92%92%92333%B9%B9%B9%BC%BC%BChhh)))%E1%E1%E1%03%03%03%CB%CB%CB%EB%EB%EB%FD%FD%FD%A3%A3%A3(((%04%04%04%CA%CA%CAttt%2C%2C%2C%F4%F4%F4%00%00%00%FF%FF%FF%D6%DE%02%C3%00%00%006tRNS%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%00%A1%8FN1%00%00%00iIDAT%08%D7E%C7E%16%02Q%10%C5%D0j%C3%DD%BDqw%5E%F6%BF5%06%D4%3Fd%94kx%BDf%DE6%FFIA%AB%C8qYj%1F%E3Xk%93%E0%C8JZ%10%90%9E%3A1%60%BBY%85%A8%AE%14%C0%5E%1A6%8E%C5w%02%60%99%C9%FA%9A%03%60%8C%EFz%8C%CE%0EnSu%3F%01%AD%B2%06%04%F0%3CT%FF%B8nk%3F%7C%01%C5z%1B%F9%26%2F%3Az%00%00%00%00IEND%AEB%60%82");border:1px solid #0a0}span.othercss{font-family:"Times New Roman";font-weight:inherit}""" + + self._test(input, output) + + def test_yui_dataurl_nonbase64_noquotes(self): + input = """ + div.nonbase64-noquotes { + width:100px; + height:100px; + background-image:url( data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%0C%00%00%00%0E%08%03%00%00%00%2Cc%0D%DE%00%00%00%A2PLTEQQQ%FA%FA%FA%FC%FC%FC%EE%EE%EE%A9%A9%A9%E9%E9%E9%0A%0A%0A%0D%0D%0D444PPP%CD%CD%CD%CC%CC%CC%F5%F5%F5UUU%D0%D0%D0\'\'\'%F9%F9%F9%A6%A6%A6%40%40%40FFF%A0%A0%A0%89%89%89%8D%8D%8D%20%20%20%14%14%14%DA%DA%DA%B6%B6%B6%02%02%02%87%87%87%81%81%81%AC%AC%AC%0E%0E%0E111%7D%7D%7D%92%92%92333%B9%B9%B9%BC%BC%BChhh\)\)\)%E1%E1%E1%03%03%03%CB%CB%CB%EB%EB%EB%FD%FD%FD%A3%A3%A3\(\(\(%04%04%04%CA%CA%CAttt%2C%2C%2C%F4%F4%F4%00%00%00%FF%FF%FF%D6%DE%02%C3%00%00%006tRNS%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%00%A1%8FN1%00%00%00iIDAT%08%D7E%C7E%16%02Q%10%C5%D0j%C3%DD%BDqw%5E%F6%BF5%06%D4%3Fd%94kx%BDf%DE6%FFIA%AB%C8qYj%1F%E3Xk%93%E0%C8JZ%10%90%9E%3A1%60%BBY%85%A8%AE%14%C0%5E%1A6%8E%C5w%02%60%99%C9%FA%9A%03%60%8C%EFz%8C%CE%0EnSu%3F%01%AD%B2%06%04%F0%3CT%FF%B8nk%3F%7C%01%C5z%1B%F9%26%2F%3Az%00%00%00%00IEND%AEB%60%82 ); + border:1px solid red; + } + + span.othercss { + font-family:"Times New Roman"; + font-weight:inherit; + } + + """ + + output = """div.nonbase64-noquotes{width:100px;height:100px;background-image:url(data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%0C%00%00%00%0E%08%03%00%00%00%2Cc%0D%DE%00%00%00%A2PLTEQQQ%FA%FA%FA%FC%FC%FC%EE%EE%EE%A9%A9%A9%E9%E9%E9%0A%0A%0A%0D%0D%0D444PPP%CD%CD%CD%CC%CC%CC%F5%F5%F5UUU%D0%D0%D0\'\'\'%F9%F9%F9%A6%A6%A6%40%40%40FFF%A0%A0%A0%89%89%89%8D%8D%8D%20%20%20%14%14%14%DA%DA%DA%B6%B6%B6%02%02%02%87%87%87%81%81%81%AC%AC%AC%0E%0E%0E111%7D%7D%7D%92%92%92333%B9%B9%B9%BC%BC%BChhh\)\)\)%E1%E1%E1%03%03%03%CB%CB%CB%EB%EB%EB%FD%FD%FD%A3%A3%A3\(\(\(%04%04%04%CA%CA%CAttt%2C%2C%2C%F4%F4%F4%00%00%00%FF%FF%FF%D6%DE%02%C3%00%00%006tRNS%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%00%A1%8FN1%00%00%00iIDAT%08%D7E%C7E%16%02Q%10%C5%D0j%C3%DD%BDqw%5E%F6%BF5%06%D4%3Fd%94kx%BDf%DE6%FFIA%AB%C8qYj%1F%E3Xk%93%E0%C8JZ%10%90%9E%3A1%60%BBY%85%A8%AE%14%C0%5E%1A6%8E%C5w%02%60%99%C9%FA%9A%03%60%8C%EFz%8C%CE%0EnSu%3F%01%AD%B2%06%04%F0%3CT%FF%B8nk%3F%7C%01%C5z%1B%F9%26%2F%3Az%00%00%00%00IEND%AEB%60%82);border:1px solid red}span.othercss{font-family:"Times New Roman";font-weight:inherit}""" + + self._test(input, output) + + def test_yui_dataurl_noquote_multiline_font(self): + input = """ + /*csslint fontfamily: true*/ + + /** + * Foo + */ + + .y-ff-1 { + font-family:"Foo Bar",Helvetica,Arial; + text-rendering: optimizeLegibility; + } + + .ua-op .y-ff-1 { + /* Some Comment */ + font-family:Helvetica,Arial; + } + + /* + Foo + + Bar + */ + + @font-face { + font-family: "Foo Bar"; + src: url( + data:font/truetype;base64,gRbIUFAIrsQNGditEWbAUKwAA) format("truetype"), + url("http://yuilibrary.com/fonts/foo-bar.svg#webfontse22fewwr") format("svg"); + font-weight: normal; + font-style: normal; + } + + + """ + + output = """.y-ff-1{font-family:"Foo Bar",Helvetica,Arial;text-rendering:optimizeLegibility}.ua-op .y-ff-1{font-family:Helvetica,Arial}@font-face{font-family:"Foo Bar";src:url(data:font/truetype;base64,gRbIUFAIrsQNGditEWbAUKwAA) format("truetype"),url("http://yuilibrary.com/fonts/foo-bar.svg#webfontse22fewwr") format("svg");font-weight:normal;font-style:normal} + +""" + + self._test(input, output) + + def test_yui_dataurl_realdata_doublequotes(self): + input = """ + .yui3-skin-sam .yui3-scrollview-scrollbar { + -webkit-transform: translate3d(0, 0, 0); + -moz-transform: translate(0, 0); + } + + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-first, + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-middle, + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last { + border-radius:3px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAABCAYAAAD9yd/wAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABJJREFUeNpiZGBgSGPAAgACDAAIkABoFyloZQAAAABJRU5ErkJggg=="); + } + + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-first, + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last { + border-bottom-right-radius:0; + border-bottom-left-radius:0; + + -webkit-border-bottom-right-radius:0; + -webkit-border-bottom-left-radius:0; + + -moz-border-radius-bottomright:0; + -moz-border-radius-bottomleft:0; + } + + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last { + border-radius:0; + border-bottom-right-radius:3px; + border-bottom-left-radius:3px; + + -webkit-border-radius:0; + -webkit-border-bottom-right-radius:3px; + -webkit-border-bottom-left-radius:3px; + -webkit-transform: translate3d(0, 0, 0); + + -moz-border-radius:0; + -moz-border-radius-bottomright:3px; + -moz-border-radius-bottomleft:3px; + -moz-transform: translate(0, 0); + } + + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-middle { + border-radius:0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + + -webkit-transform: translate3d(0,0,0) scaleY(1); + -webkit-transform-origin-y: 0; + + -moz-transform: translate(0,0) scaleY(1); + -moz-transform-origin: 0 0; + } + + .yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-first, + .yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-last { + border-top-right-radius: 0; + border-bottom-left-radius: 3px; + + -webkit-border-top-right-radius: 0; + -webkit-border-bottom-left-radius: 3px; + + -moz-border-radius-topright: 0; + -moz-border-radius-bottomleft: 3px; + } + + .yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-last { + border-bottom-left-radius: 0; + border-top-right-radius: 3px; + + -webkit-border-bottom-left-radius: 0; + -webkit-border-top-right-radius: 3px; + + -moz-border-radius-bottomleft: 0; + -moz-border-radius-topright: 3px; + } + + .yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-middle { + -webkit-transform: translate3d(0,0,0) scaleX(1); + -webkit-transform-origin: 0 0; + + -moz-transform: translate(0,0) scaleX(1); + -moz-transform-origin: 0 0; + } + + .yui3-skin-sam .yui3-scrollview-scrollbar-vert-basic .yui3-scrollview-child, + .yui3-skin-sam .yui3-scrollview-scrollbar-horiz-basic .yui3-scrollview-child { + background-color: #aaa; + background-image: none; + } + """ + + output = """.yui3-skin-sam .yui3-scrollview-scrollbar{-webkit-transform:translate3d(0,0,0);-moz-transform:translate(0,0)}.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-first,.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-middle,.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last{border-radius:3px;-webkit-border-radius:3px;-moz-border-radius:3px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAABCAYAAAD9yd/wAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABJJREFUeNpiZGBgSGPAAgACDAAIkABoFyloZQAAAABJRU5ErkJggg==")}.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-first,.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last{border-bottom-right-radius:0;border-bottom-left-radius:0;-webkit-border-bottom-right-radius:0;-webkit-border-bottom-left-radius:0;-moz-border-radius-bottomright:0;-moz-border-radius-bottomleft:0}.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last{border-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px;-webkit-border-radius:0;-webkit-border-bottom-right-radius:3px;-webkit-border-bottom-left-radius:3px;-webkit-transform:translate3d(0,0,0);-moz-border-radius:0;-moz-border-radius-bottomright:3px;-moz-border-radius-bottomleft:3px;-moz-transform:translate(0,0)}.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-middle{border-radius:0;-webkit-border-radius:0;-moz-border-radius:0;-webkit-transform:translate3d(0,0,0) scaleY(1);-webkit-transform-origin-y:0;-moz-transform:translate(0,0) scaleY(1);-moz-transform-origin:0 0}.yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-first,.yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-last{border-top-right-radius:0;border-bottom-left-radius:3px;-webkit-border-top-right-radius:0;-webkit-border-bottom-left-radius:3px;-moz-border-radius-topright:0;-moz-border-radius-bottomleft:3px}.yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-last{border-bottom-left-radius:0;border-top-right-radius:3px;-webkit-border-bottom-left-radius:0;-webkit-border-top-right-radius:3px;-moz-border-radius-bottomleft:0;-moz-border-radius-topright:3px}.yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-middle{-webkit-transform:translate3d(0,0,0) scaleX(1);-webkit-transform-origin:0 0;-moz-transform:translate(0,0) scaleX(1);-moz-transform-origin:0 0}.yui3-skin-sam .yui3-scrollview-scrollbar-vert-basic .yui3-scrollview-child,.yui3-skin-sam .yui3-scrollview-scrollbar-horiz-basic .yui3-scrollview-child{background-color:#aaa;background-image:none}""" + + self._test(input, output) + + def test_yui_dataurl_realdata_noquotes(self): + input = """ + .yui3-skin-sam .yui3-scrollview-scrollbar { + -webkit-transform: translate3d(0, 0, 0); + -moz-transform: translate(0, 0); + } + + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-first, + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-middle, + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last { + border-radius:3px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAABCAYAAAD9yd/wAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABJJREFUeNpiZGBgSGPAAgACDAAIkABoFyloZQAAAABJRU5ErkJggg==); + } + + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-first, + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last { + border-bottom-right-radius:0; + border-bottom-left-radius:0; + + -webkit-border-bottom-right-radius:0; + -webkit-border-bottom-left-radius:0; + + -moz-border-radius-bottomright:0; + -moz-border-radius-bottomleft:0; + } + + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last { + border-radius:0; + border-bottom-right-radius:3px; + border-bottom-left-radius:3px; + + -webkit-border-radius:0; + -webkit-border-bottom-right-radius:3px; + -webkit-border-bottom-left-radius:3px; + -webkit-transform: translate3d(0, 0, 0); + + -moz-border-radius:0; + -moz-border-radius-bottomright:3px; + -moz-border-radius-bottomleft:3px; + -moz-transform: translate(0, 0); + } + + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-middle { + border-radius:0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + + -webkit-transform: translate3d(0,0,0) scaleY(1); + -webkit-transform-origin-y: 0; + + -moz-transform: translate(0,0) scaleY(1); + -moz-transform-origin: 0 0; + } + + .yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-first, + .yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-last { + border-top-right-radius: 0; + border-bottom-left-radius: 3px; + + -webkit-border-top-right-radius: 0; + -webkit-border-bottom-left-radius: 3px; + + -moz-border-radius-topright: 0; + -moz-border-radius-bottomleft: 3px; + } + + .yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-last { + border-bottom-left-radius: 0; + border-top-right-radius: 3px; + + -webkit-border-bottom-left-radius: 0; + -webkit-border-top-right-radius: 3px; + + -moz-border-radius-bottomleft: 0; + -moz-border-radius-topright: 3px; + } + + .yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-middle { + -webkit-transform: translate3d(0,0,0) scaleX(1); + -webkit-transform-origin: 0 0; + + -moz-transform: translate(0,0) scaleX(1); + -moz-transform-origin: 0 0; + } + + .yui3-skin-sam .yui3-scrollview-scrollbar-vert-basic .yui3-scrollview-child, + .yui3-skin-sam .yui3-scrollview-scrollbar-horiz-basic .yui3-scrollview-child { + background-color: #aaa; + background-image: none; + } + """ + + output = """.yui3-skin-sam .yui3-scrollview-scrollbar{-webkit-transform:translate3d(0,0,0);-moz-transform:translate(0,0)}.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-first,.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-middle,.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last{border-radius:3px;-webkit-border-radius:3px;-moz-border-radius:3px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAABCAYAAAD9yd/wAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABJJREFUeNpiZGBgSGPAAgACDAAIkABoFyloZQAAAABJRU5ErkJggg==)}.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-first,.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last{border-bottom-right-radius:0;border-bottom-left-radius:0;-webkit-border-bottom-right-radius:0;-webkit-border-bottom-left-radius:0;-moz-border-radius-bottomright:0;-moz-border-radius-bottomleft:0}.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last{border-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px;-webkit-border-radius:0;-webkit-border-bottom-right-radius:3px;-webkit-border-bottom-left-radius:3px;-webkit-transform:translate3d(0,0,0);-moz-border-radius:0;-moz-border-radius-bottomright:3px;-moz-border-radius-bottomleft:3px;-moz-transform:translate(0,0)}.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-middle{border-radius:0;-webkit-border-radius:0;-moz-border-radius:0;-webkit-transform:translate3d(0,0,0) scaleY(1);-webkit-transform-origin-y:0;-moz-transform:translate(0,0) scaleY(1);-moz-transform-origin:0 0}.yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-first,.yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-last{border-top-right-radius:0;border-bottom-left-radius:3px;-webkit-border-top-right-radius:0;-webkit-border-bottom-left-radius:3px;-moz-border-radius-topright:0;-moz-border-radius-bottomleft:3px}.yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-last{border-bottom-left-radius:0;border-top-right-radius:3px;-webkit-border-bottom-left-radius:0;-webkit-border-top-right-radius:3px;-moz-border-radius-bottomleft:0;-moz-border-radius-topright:3px}.yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-middle{-webkit-transform:translate3d(0,0,0) scaleX(1);-webkit-transform-origin:0 0;-moz-transform:translate(0,0) scaleX(1);-moz-transform-origin:0 0}.yui3-skin-sam .yui3-scrollview-scrollbar-vert-basic .yui3-scrollview-child,.yui3-skin-sam .yui3-scrollview-scrollbar-horiz-basic .yui3-scrollview-child{background-color:#aaa;background-image:none}""" + + self._test(input, output) + + def test_yui_dataurl_realdata_singlequotes(self): + input = """ + .yui3-skin-sam .yui3-scrollview-scrollbar { + -webkit-transform: translate3d(0, 0, 0); + -moz-transform: translate(0, 0); + } + + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-first, + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-middle, + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last { + border-radius:3px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAABCAYAAAD9yd/wAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABJJREFUeNpiZGBgSGPAAgACDAAIkABoFyloZQAAAABJRU5ErkJggg=='); + } + + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-first, + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last { + border-bottom-right-radius:0; + border-bottom-left-radius:0; + + -webkit-border-bottom-right-radius:0; + -webkit-border-bottom-left-radius:0; + + -moz-border-radius-bottomright:0; + -moz-border-radius-bottomleft:0; + } + + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last { + border-radius:0; + border-bottom-right-radius:3px; + border-bottom-left-radius:3px; + + -webkit-border-radius:0; + -webkit-border-bottom-right-radius:3px; + -webkit-border-bottom-left-radius:3px; + -webkit-transform: translate3d(0, 0, 0); + + -moz-border-radius:0; + -moz-border-radius-bottomright:3px; + -moz-border-radius-bottomleft:3px; + -moz-transform: translate(0, 0); + } + + .yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-middle { + border-radius:0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + + -webkit-transform: translate3d(0,0,0) scaleY(1); + -webkit-transform-origin-y: 0; + + -moz-transform: translate(0,0) scaleY(1); + -moz-transform-origin: 0 0; + } + + .yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-first, + .yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-last { + border-top-right-radius: 0; + border-bottom-left-radius: 3px; + + -webkit-border-top-right-radius: 0; + -webkit-border-bottom-left-radius: 3px; + + -moz-border-radius-topright: 0; + -moz-border-radius-bottomleft: 3px; + } + + .yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-last { + border-bottom-left-radius: 0; + border-top-right-radius: 3px; + + -webkit-border-bottom-left-radius: 0; + -webkit-border-top-right-radius: 3px; + + -moz-border-radius-bottomleft: 0; + -moz-border-radius-topright: 3px; + } + + .yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-middle { + -webkit-transform: translate3d(0,0,0) scaleX(1); + -webkit-transform-origin: 0 0; + + -moz-transform: translate(0,0) scaleX(1); + -moz-transform-origin: 0 0; + } + + .yui3-skin-sam .yui3-scrollview-scrollbar-vert-basic .yui3-scrollview-child, + .yui3-skin-sam .yui3-scrollview-scrollbar-horiz-basic .yui3-scrollview-child { + background-color: #aaa; + background-image: none; + } + """ + + output = """.yui3-skin-sam .yui3-scrollview-scrollbar{-webkit-transform:translate3d(0,0,0);-moz-transform:translate(0,0)}.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-first,.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-middle,.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last{border-radius:3px;-webkit-border-radius:3px;-moz-border-radius:3px;background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAABCAYAAAD9yd/wAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABJJREFUeNpiZGBgSGPAAgACDAAIkABoFyloZQAAAABJRU5ErkJggg==')}.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-first,.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last{border-bottom-right-radius:0;border-bottom-left-radius:0;-webkit-border-bottom-right-radius:0;-webkit-border-bottom-left-radius:0;-moz-border-radius-bottomright:0;-moz-border-radius-bottomleft:0}.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-last{border-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px;-webkit-border-radius:0;-webkit-border-bottom-right-radius:3px;-webkit-border-bottom-left-radius:3px;-webkit-transform:translate3d(0,0,0);-moz-border-radius:0;-moz-border-radius-bottomright:3px;-moz-border-radius-bottomleft:3px;-moz-transform:translate(0,0)}.yui3-skin-sam .yui3-scrollview-scrollbar .yui3-scrollview-middle{border-radius:0;-webkit-border-radius:0;-moz-border-radius:0;-webkit-transform:translate3d(0,0,0) scaleY(1);-webkit-transform-origin-y:0;-moz-transform:translate(0,0) scaleY(1);-moz-transform-origin:0 0}.yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-first,.yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-last{border-top-right-radius:0;border-bottom-left-radius:3px;-webkit-border-top-right-radius:0;-webkit-border-bottom-left-radius:3px;-moz-border-radius-topright:0;-moz-border-radius-bottomleft:3px}.yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-last{border-bottom-left-radius:0;border-top-right-radius:3px;-webkit-border-bottom-left-radius:0;-webkit-border-top-right-radius:3px;-moz-border-radius-bottomleft:0;-moz-border-radius-topright:3px}.yui3-skin-sam .yui3-scrollview-scrollbar-horiz .yui3-scrollview-middle{-webkit-transform:translate3d(0,0,0) scaleX(1);-webkit-transform-origin:0 0;-moz-transform:translate(0,0) scaleX(1);-moz-transform-origin:0 0}.yui3-skin-sam .yui3-scrollview-scrollbar-vert-basic .yui3-scrollview-child,.yui3-skin-sam .yui3-scrollview-scrollbar-horiz-basic .yui3-scrollview-child{background-color:#aaa;background-image:none}""" + + self._test(input, output) + + def test_yui_dataurl_realdata_yuiapp(self): + input = """ + html { + background: #fff; + color: #555; + height: 100%; + } + + #hd, #bd, #ft { + padding: 0 50px; + } + + #bd { + padding-bottom: 50px; + border-bottom: 1px solid #006e9c; + } + + #ft { + background: transparent no-repeat 0% 100%; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADcAAAAeCAMAAAC/pnaKAAAAwFBMVEUkXIAoYYQoYYQoYYQyao02bpE6cpQ6cpREe51LgqNUiqtUiqtUiqtZj7BckrNlmrtlmrtlmrtroMB3qsp3qsp3qsp+sdCHudiHudiHudiHudiPwN+YyOaYyOaYyOaf0O2g0O6n1vOn1vOn1vOn1vOw3vu45f245f245f3B7f7K9f7K9f7K9f7X/v7X/v7p//7p//79//79//4AAABhAGMAbwBzAC4AcwB0AHIAaQBuAGcAcwABAAAAwIwmGwEAAABQQKFUAAAAQHRSTlP///////////////////////////////////////////////////////////////////8A////////////////0pfSbwAAAdtJREFUOI2NlNGqgzAMhv/ClDlQQcFdKOx4McFCKcH3f7eTtLWr07mFiUvbzz9Nk2LZGRH5937qZTiGyEx0Cr5xQpEd6xzNueCGc9SzUmAzP8cplO1zgRRutBAlM9EhZwknWj1LKXkwbOVojTqMEpKZOfeUD3OnR24VWeJf1OOBTnTgw8zZr3LyOlbdrWrdGlpG6Alz4Ni3RcT4Ldm8wHpOo9Yo5CPsdngM6D3HmLlESrgHD+YvrtEuUeLeMf7x7j23GJVgbJoHs0/cI3B7TNkfON5btsEUrvSdY694U0P9nXMp2mDsdbLmY14CN8XDjtzgo9CuqpYnGoMscDUmn08qsbOn/3Lte6rgP1eM7qQ1YMP+yPhiTmx2laDQspytgJI1MbIzZ7yHfj2/hYYtZ1xMM4dc3iTukjgJyKsrUBEH4uqFXNUh3aXxtWjbC3DpH8LRVPJ8MXLoA8fsz51ue73Q+1z7RkTEF0dWa1o5a8001C6ZbGuzh0bSTo9iS8X+C/NWbfTW5uZ9Vu4QaL20Xv3uhgyrZUXVtI2NfS3fbzgP75dNek/YbrL+k/EykecuZf6ZOzDJYsf9PCV3xhFHq7043mw+H1ylW+5Ab5jpADuPM2Z1P3POLWnUG/sHnIDsqkOjlqoAAAAASUVORK5CYII=); + /* image width: 55px */ + padding: 0 0 40px 0; + margin: 50px; + } + + #hd, #bd { + background: #f9f9f9; + } + + body { + margin: 0; + padding: 0; + font: 12px "Helvetica Nueue", Arial, sans-serif; + } + + #hd { + color: #fff; + padding-top: 50px; + margin: 0; + } + + #hd, h1, h2, p, .color { + margin: auto; + } + + h1, h2, a { + color: #006e9c; + } + + h1, h2 { + margin-top: 0; + } + + h4 .title { + font-weight: bold; + letter-spacing: -2px; + font-size: 47px; + text-shadow: 0 1px 0 #369; + background: #006e9d; + color: #fff; + padding: 0 10px; + } + + h4 { + display: block; + float: right; + margin: 0 0 0 20px; + } + + h4 .what { + display: block; + padding: 4px; + text-align: center; + font-weight: normal; + } + + h4 .version { + font-size: 11px; + color: #ccc; + } + + h2 { + font-size: 40px; + font-family: "HelveticaNeue-Light", "Helvetica Neue Light", + "Helvetica Neue", sans-serif; + font-weight: 300; + } + + h4, p { + padding: 6px 0 6px; + } + + #ft p.fine, #ft p.fine a { + color: #999; + } + + #ft p.intro { + font-size: 12px; + } + + #bd { + font-size: 14px; + color: #666; + } + + #ft p { + font-size: 11px; + } + + """ + + output = """html{background:#fff;color:#555;height:100%}#hd,#bd,#ft{padding:0 50px}#bd{padding-bottom:50px;border-bottom:1px solid #006e9c}#ft{background:transparent no-repeat 0 100%;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADcAAAAeCAMAAAC/pnaKAAAAwFBMVEUkXIAoYYQoYYQoYYQyao02bpE6cpQ6cpREe51LgqNUiqtUiqtUiqtZj7BckrNlmrtlmrtlmrtroMB3qsp3qsp3qsp+sdCHudiHudiHudiHudiPwN+YyOaYyOaYyOaf0O2g0O6n1vOn1vOn1vOn1vOw3vu45f245f245f3B7f7K9f7K9f7K9f7X/v7X/v7p//7p//79//79//4AAABhAGMAbwBzAC4AcwB0AHIAaQBuAGcAcwABAAAAwIwmGwEAAABQQKFUAAAAQHRSTlP///////////////////////////////////////////////////////////////////8A////////////////0pfSbwAAAdtJREFUOI2NlNGqgzAMhv/ClDlQQcFdKOx4McFCKcH3f7eTtLWr07mFiUvbzz9Nk2LZGRH5937qZTiGyEx0Cr5xQpEd6xzNueCGc9SzUmAzP8cplO1zgRRutBAlM9EhZwknWj1LKXkwbOVojTqMEpKZOfeUD3OnR24VWeJf1OOBTnTgw8zZr3LyOlbdrWrdGlpG6Alz4Ni3RcT4Ldm8wHpOo9Yo5CPsdngM6D3HmLlESrgHD+YvrtEuUeLeMf7x7j23GJVgbJoHs0/cI3B7TNkfON5btsEUrvSdY694U0P9nXMp2mDsdbLmY14CN8XDjtzgo9CuqpYnGoMscDUmn08qsbOn/3Lte6rgP1eM7qQ1YMP+yPhiTmx2laDQspytgJI1MbIzZ7yHfj2/hYYtZ1xMM4dc3iTukjgJyKsrUBEH4uqFXNUh3aXxtWjbC3DpH8LRVPJ8MXLoA8fsz51ue73Q+1z7RkTEF0dWa1o5a8001C6ZbGuzh0bSTo9iS8X+C/NWbfTW5uZ9Vu4QaL20Xv3uhgyrZUXVtI2NfS3fbzgP75dNek/YbrL+k/EykecuZf6ZOzDJYsf9PCV3xhFHq7043mw+H1ylW+5Ab5jpADuPM2Z1P3POLWnUG/sHnIDsqkOjlqoAAAAASUVORK5CYII=);padding:0 0 40px 0;margin:50px}#hd,#bd{background:#f9f9f9}body{margin:0;padding:0;font:12px "Helvetica Nueue",Arial,sans-serif}#hd{color:#fff;padding-top:50px;margin:0}#hd,h1,h2,p,.color{margin:auto}h1,h2,a{color:#006e9c}h1,h2{margin-top:0}h4 .title{font-weight:bold;letter-spacing:-2px;font-size:47px;text-shadow:0 1px 0 #369;background:#006e9d;color:#fff;padding:0 10px}h4{display:block;float:right;margin:0 0 0 20px}h4 .what{display:block;padding:4px;text-align:center;font-weight:normal}h4 .version{font-size:11px;color:#ccc}h2{font-size:40px;font-family:"HelveticaNeue-Light","Helvetica Neue Light","Helvetica Neue",sans-serif;font-weight:300}h4,p{padding:6px 0 6px}#ft p.fine,#ft p.fine a{color:#999}#ft p.intro{font-size:12px}#bd{font-size:14px;color:#666}#ft p{font-size:11px}""" + + self._test(input, output) + + def test_yui_dataurl_singlequote_font(self): + input = """ + /*csslint fontfamily: true*/ + + /** + * Foo + */ + + .y-ff-1 { + font-family:"Foo Bar",Helvetica,Arial; + text-rendering: optimizeLegibility; + } + + .ua-op .y-ff-1 { + /* Some Comment */ + font-family:Helvetica,Arial; + } + + /* + Foo + + Bar + */ + + @font-face { + font-family: "Foo Bar"; + src: URL('data:font/truetype;base64,gRbIUFAIrsQNGditEWbAUKwAA') format("truetype"), + url("http://yuilibrary.com/fonts/foo-bar.svg#webfontse22fewwr") format("svg"); + font-weight: normal; + font-style: normal; + } + + + """ + + output = """.y-ff-1{font-family:"Foo Bar",Helvetica,Arial;text-rendering:optimizeLegibility}.ua-op .y-ff-1{font-family:Helvetica,Arial}@font-face{font-family:"Foo Bar";src:url('data:font/truetype;base64,gRbIUFAIrsQNGditEWbAUKwAA') format("truetype"),url("http://yuilibrary.com/fonts/foo-bar.svg#webfontse22fewwr") format("svg");font-weight:normal;font-style:normal} + +""" + + self._test(input, output) + + def test_yui_decimals(self): + input = """ + ::selection { + margin: 0.6px 0.333pt 1.2em 8.8cm; + } + + """ + + output = """::selection{margin:.6px .333pt 1.2em 8.8cm}""" + + self._test(input, output) + + def test_yui_neg_decimals(self): + input = """ + ::selection { + margin: -0.6px -0.333pt -1.2em -8.8cm; + } + + """ + + output = """::selection{margin:-.6px -.333pt -1.2em -8.8cm}""" + + self._test(input, output) + + def test_yui_pos_decimals(self): + input = """ + ::selection { + margin: +0.6px +0.333pt +1.2em +8.8cm; + } + + """ + + output = """::selection{margin:.6px .333pt 1.2em 8.8cm}""" + + self._test(input, output) + + def test_yui_dollar_header(self): + input = """ +/*! +$Header: /temp/dirname/filename.css 3 2/02/08 3:37p JSmith $ +*/ + +foo { + bar: baz +} + """ + + output = """/*! +$Header: /temp/dirname/filename.css 3 2/02/08 3:37p JSmith $ +*/foo{bar:baz}""" + + self._test(input, output) + + def test_yui_font_face(self): + input = """ + @font-face { + font-family: 'gzipper'; + src: url(yanone.eot); + src: local('gzipper'), + url(yanone.ttf) format('truetype'); + } + + """ + + output = """@font-face{font-family:'gzipper';src:url(yanone.eot);src:local('gzipper'),url(yanone.ttf) format('truetype')}""" + + self._test(input, output) + + def test_yui_ie5mac(self): + input = """ + /* Ignore the next rule in IE mac \*/ + .selector { + color: khaki; + } + /* Stop ignoring in IE mac */ + + """ + + output = """/*\*/.selector{color:khaki}/**/""" + + self._test(input, output) + + def test_yui_lowercasing(self): + input = """ + @CHARSET "UTF-8"; + + @FONT-FACE { + FONT-FAMILY: "YOUR FACE"; + } + + @IMPORT "HTTP://DOMAIN.TLD/OTHER.CSS"; + + @MEDIA print { + BACKGROUND: NONE; + BACKGROUND-POSITION: 0 0; + } + + @PAGE { + CONTENT: ATTR(HREF); + HEIGHT: MAX(0, MIN(10, 20)); + WIDTH: CALC(50% - 10PX); + } + + @NAMESPACE XHTML "HTTP://WWW.W3.ORG/1999/XHTML"; + + /* pseudos */ + a:ACTIVE, + a:AFTER, + a:BEFORE, + a:CHECKED, + a:DISABLED, + a:EMPTY, + a:ENABLED, + a:FIRST-CHILD, + a:FIRST-LETTER, + a:FIRST-LINE, + a:FIRST-OF-TYPE, + a:FOCUS, + a:HOVER, + a:LAST-CHILD, + a:LAST-OF-TYPE, + a:LINK, + a:ONLY-CHILD, + a:ONLY-OF-TYPE, + a:ROOT, + a::SELECTION, + a:TARGET, + a:VISITED, + + /* pseudo functions */ + a:ANY(A, B, I) STRONG, + a:LANG(FR), + a:NOT([HIDDEN]), + a:NTH-CHILD(2), + a:NTH-LAST-CHILD(2), + a:NTH-LAST-OF-TYPE(2), + a:NTH-OF-TYPE(2) { + BACKGROUND: URL(PROTO://DOMAIN.TLD/PATH), + REPEATING-LINEAR-GRADIENT(20DEG, GRAY, GREEN, 20PX, WHITE 40PX), + -ATSC-LINEAR-GRADIENT(LEFT, BLACK, WHITE), + -KHTML-RADIAL-GRADIENT(CENTER 50DEG, CIRCLE CLOSEST-SIDE, BLACK 0, GREEN 100%), + -MOZ-RADIAL-GRADIENT(CENTER 45DEG, CIRCLE CLOSEST-SIDE, ORANGE 0%, RED 100%), + -MS-LINEAR-GRaDiEnT(LEFT, BLUE, BLACK), + -O-REPEATING-RADIAL-GRADIENT(CENTER, CIRCLE CLOSEST-SIDE, PAPAYAWHIP, RED 50%, GAINSBORO), + -WAP-LINEAR-GRADIENT(LEFT, BLACK, WHITE), + -WEBKIT-GRADIENT(LINEAR, LEFT, FROM(WHITE), TO(RGBA(1,2,3,.4))); + } + + """ + + output = """@charset "UTF-8";@font-face{FONT-FAMILY:"YOUR FACE"}@import "HTTP://DOMAIN.TLD/OTHER.CSS";@media print{background:0;background-position:0 0}@page{CONTENT:attr(HREF);HEIGHT:max(0,min(10,20));WIDTH:calc(50% - 10PX)}@namespace XHTML "HTTP://WWW.W3.ORG/1999/XHTML";a:active,a:after,a:before,a:checked,a:disabled,a:empty,a:enabled,a:first-child,a:first-letter ,a:first-line ,a:first-of-type,a:focus,a:hover,a:last-child,a:last-of-type,a:link,a:only-child,a:only-of-type,a:root,a::selection,a:target,a:visited,a:any(A,B,I) STRONG,a:lang(FR),a:not([HIDDEN]),a:nth-child(2),a:nth-last-child(2),a:nth-last-of-type(2),a:nth-of-type(2){BACKGROUND:url(PROTO://DOMAIN.TLD/PATH),repeating-linear-gradient(20DEG,GRAY,GREEN,20PX,WHITE 40PX),-atsc-linear-gradient(LEFT,BLACK,WHITE),-khtml-radial-gradient(CENTER 50DEG,CIRCLE CLOSEST-SIDE,BLACK 0,GREEN 100%),-moz-radial-gradient(CENTER 45DEG,CIRCLE CLOSEST-SIDE,ORANGE 0,RED 100%),-ms-linear-gradient(LEFT,BLUE,BLACK),-o-repeating-radial-gradient(CENTER,CIRCLE CLOSEST-SIDE,PAPAYAWHIP,RED 50%,GAINSBORO),-wap-linear-gradient(LEFT,BLACK,WHITE),-webkit-gradient(LINEAR,LEFT,from(WHITE),to(rgba(1,2,3,.4)))}""" + + self._test(input, output) + + def test_yui_media_empty_class(self): + input = """ + /*! preserved */ + emptiness {} + + @import "another.css"; + /* I'm empty - delete me */ + empty { ;} + + @media print { + .noprint { display: none; } + } + + @media screen { + /* this rule should be removed, not simply minified.*/ + .breakme {} + .printonly { display: none; } + } + """ + + output = """/*! preserved */@import "another.css";@media print{.noprint{display:none}}@media screen{.printonly{display:none}}""" + + self._test(input, output) + + def test_yui_media_multi(self): + input = """ + @media only all and (max-width:50em), only all and (max-device-width:800px), only all and (max-width:780px) { + some-css : here + } + + """ + + output = """@media only all and (max-width:50em),only all and (max-device-width:800px),only all and (max-width:780px){some-css:here}""" + + self._test(input, output) + + def test_yui_media_test(self): + input = """ + @media screen AND (-webkit-min-device-pixel-ratio:0) { + some-css : here + } + + """ + + output = """@media screen and (-webkit-min-device-pixel-ratio:0){some-css:here}""" + + self._test(input, output) + + def test_yui_old_ie_filter_matrix(self): + input = """ + .a { + filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.9914448613738104, M12=-0.13052619222005157, M21=0.13052619222005157, M22=0.9914448613738104, sizingMethod='auto expand'); + } + .b { + display: none; + filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.9914448613738104, M12=-0.13052619222005157, M21=0.13052619222005157, M22=0.9914448613738104, sizingMethod='auto expand'); + vertical-align: bottom; + } + + """ + + output = """.a{filter:progid:DXImageTransform.Microsoft.Matrix(M11=0.9914448613738104, M12=-0.13052619222005157, M21=0.13052619222005157, M22=0.9914448613738104, sizingMethod='auto expand')}.b{display:none;filter:progid:DXImageTransform.Microsoft.Matrix(M11=0.9914448613738104, M12=-0.13052619222005157, M21=0.13052619222005157, M22=0.9914448613738104, sizingMethod='auto expand');vertical-align:bottom}""" + + self._test(input, output) + + def test_yui_opacity_filter(self): + input = """ + /* example from https://developer.mozilla.org/en/CSS/opacity */ + pre { /* make the box translucent (80% opaque) */ + border: solid red; + opacity: 0.8; /* Firefox, Safari(WebKit), Opera */ + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; /* IE 8 */ + filter: PROGID:DXImageTransform.Microsoft.Alpha(Opacity=80); /* IE 4-7 */ + zoom: 1; /* set "zoom", "width" or "height" to trigger "hasLayout" in IE 7 and lower */ + } + + /** and again */ + code { + -ms-filter: "PROGID:DXImageTransform.Microsoft.Alpha(Opacity=80)"; /* IE 8 */ + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80); /* IE 4-7 */ + } + """ + + output = """pre{border:solid red;opacity:.8;-ms-filter:"alpha(opacity=80)";filter:alpha(opacity=80);zoom:1}code{-ms-filter:"alpha(opacity=80)";filter:alpha(opacity=80)}""" + + self._test(input, output) + + def test_yui_opera_pixel_ratio(self): + input = """ + @media + (-o-min-device-pixel-ratio:10/4), + (-o-max-device-pixel-ratio: 5/4), + (-o-device-pixel-ratio: 1/1), + (-o-device-pixel-ratio: 1/10), + (-o-device-pixel-ratio: 1.25), + (device-pixel-ratio:1.5) { + /* some:prop; */ + /* An empty property with a fraction in query would break previously */ + } + + .something { + foo: bar; + } + """ + + output = """.something{foo:bar}""" + + self._test(input, output) + + def test_yui_preserve_case(self): + input = """ + #AddAddressForm { + padding: 0; + } + #AddAddressForm .messageBoxNeutral { + padding: 0; + } + #FeedbackMailForm{ + padding: 0; + } + #FeedbackMailForm .classe{ + margin: 0; + } + .classes, #FeedBackMailForm { + margin: 0; + } + + """ + + output = """#AddAddressForm{padding:0}#AddAddressForm .messageBoxNeutral{padding:0}#FeedbackMailForm{padding:0}#FeedbackMailForm .classe{margin:0}.classes,#FeedBackMailForm{margin:0}""" + + self._test(input, output) + + def test_yui_preserve_important(self): + input = """ + .red { color: red !important; } + + """ + + output = """.red{color:red !important}""" + + self._test(input, output) + + def test_yui_preserve_new_line(self): + input = r""" +#sel-o { + content: "on\"ce upon \ +a time"; + content: 'once upon \ +a ti\'me'; +} + """ + + output = r"""#sel-o{content:"on\"ce upon \ +a time";content:'once upon \ +a ti\'me'}""" + + self._test(input, output) + + def test_yui_preserve_strings(self): + input = r""" + /* preserving strings */ + .sele { + content: "\"keep \" me"; + something: '\\\' . . '; + else: 'empty{}'; + content: "/* test */"; /* <---- this is not a comment, should be be kept */ + } + """ + + output = r""".sele{content:"\"keep \" me";something:'\\\' . . ';else:'empty{}';content:"/* test */"}""" + + self._test(input, output) + + def test_yui_pseudo_first(self): + input = """ + /* + because of IE6 first-letter and first-line + must be followed by a space + http://reference.sitepoint.com/css/pseudoelement-firstletter + Thanks: P.Sorokin comment at http://www.phpied.com/cssmin-js/ + */ + p:first-letter{ + buh: hum; + } + p:FIRST-LINE{ + baa: 1; + } + + p:first-line,a,p:first-letter,b{ + color: red; + } + + """ + + output = """p:first-letter {buh:hum}p:first-line {baa:1}p:first-line ,a,p:first-letter ,b{color:red}""" + + self._test(input, output) + + def test_yui_pseudo(self): + input = """ + p :link { + ba:zinga;;; + foo: bar;;; + } + """ + + output = """p :link{ba:zinga;foo:bar}""" + + self._test(input, output) + + def test_yui_special_comments(self): + input = """ +/*!************88**** + Preserving comments + as they are + ******************** + Keep the initial ! + *******************/ +#yo { + ma: "ma"; +} +/*! +I said +pre- +serve! */ + """ + + output = """/*!************88**** + Preserving comments + as they are + ******************** + Keep the initial ! + *******************/#yo{ma:"ma"}/*! +I said +pre- +serve! */""" + + self._test(input, output) + + def test_yui_remove_special_comments(self): + input = """ +/*!************88**** + Preserving comments + as they are + ******************** + Keep the initial ! + *******************/ +#yo { + ma: "ma"; +} +/*! +I said +pre- +serve! */ + """ + + output = """#yo{ma:"ma"}""" + + from csscompressor import compress + res = compress(input, preserve_exclamation_comments=False) + self.assertEqual(res, output) + + def test_yui_star_underscore_hacks(self): + input = """ + #elementarr { + width: 1px; + *width: 3pt; + _width: 2em; + } + """ + + output = """#elementarr{width:1px;*width:3pt;_width:2em}""" + + self._test(input, output) + + def test_yui_string_in_comment(self): + input = """ + /* te " st */ + a{a:1} + /*!"preserve" me*/ + b{content: "/**/"} + /* quite " quote ' \' \" */ + /* ie mac \*/ + c {c : 3} + /* end hiding */ + """ + + output = """a{a:1}/*!"preserve" me*/b{content:"/**/"}/*\*/c{c:3}/**/""" + + self._test(input, output) + + def test_yui_webkit_transform(self): + input = """ + c {-webkit-transform-origin: 0 0;} + d {-MOZ-TRANSFORM-ORIGIN: 0 0 } + """ + + output = """c{-webkit-transform-origin:0 0}d{-moz-transform-origin:0 0}""" + + self._test(input, output) + + def test_yui_zeros(self): + input = """ + a { + margin: 0px 0pt 0em 0%; + _padding-top: 0ex; + background-position: 0 0; + padding: 0in 0cm 0mm 0pc; + transition: opacity .0s; + transition-delay: 0.0ms; + transform: rotate3d(0grad, 0rad, 0deg); + pitch: 0KHZ; + pitch: + 0hz; /* intentionally on next line */ + } + + """ + + output = """a{margin:0;_padding-top:0;background-position:0 0;padding:0;transition:opacity 0s;transition-delay:0ms;transform:rotate3d(0,0,0);pitch:0;pitch:0}""" + + self._test(input, output) + + def test_yui_dataurl_nonbase64_singlequotes(self): + input = r""" + /* Some Comment */ + + div.nonbase64-singlequotes { + width:100px; + height:100px; + background-image:url('data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%0C%00%00%00%0E%08%03%00%00%00%2Cc%0D%DE%00%00%00%A2PLTEQQQ%FA%FA%FA%FC%FC%FC%EE%EE%EE%A9%A9%A9%E9%E9%E9%0A%0A%0A%0D%0D%0D444PPP%CD%CD%CD%CC%CC%CC%F5%F5%F5UUU%D0%D0%D0\'\'\'%F9%F9%F9%A6%A6%A6%40%40%40FFF%A0%A0%A0%89%89%89%8D%8D%8D%20%20%20%14%14%14%DA%DA%DA%B6%B6%B6%02%02%02%87%87%87%81%81%81%AC%AC%AC%0E%0E%0E111%7D%7D%7D%92%92%92333%B9%B9%B9%BC%BC%BChhh)))%E1%E1%E1%03%03%03%CB%CB%CB%EB%EB%EB%FD%FD%FD%A3%A3%A3(((%04%04%04%CA%CA%CAttt%2C%2C%2C%F4%F4%F4%00%00%00%FF%FF%FF%D6%DE%02%C3%00%00%006tRNS%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%00%A1%8FN1%00%00%00iIDAT%08%D7E%C7E%16%02Q%10%C5%D0j%C3%DD%BDqw%5E%F6%BF5%06%D4%3Fd%94kx%BDf%DE6%FFIA%AB%C8qYj%1F%E3Xk%93%E0%C8JZ%10%90%9E%3A1%60%BBY%85%A8%AE%14%C0%5E%1A6%8E%C5w%02%60%99%C9%FA%9A%03%60%8C%EFz%8C%CE%0EnSu%3F%01%AD%B2%06%04%F0%3CT%FF%B8nk%3F%7C%01%C5z%1B%F9%26%2F%3Az%00%00%00%00IEND%AEB%60%82'); + border:1px solid #0000aa; + } + + /* Some Other Comment */ + + span.othercss { + font-family:"Times New Roman"; + font-weight:inherit; + } + + """ + + output = r"""div.nonbase64-singlequotes{width:100px;height:100px;background-image:url('data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%0C%00%00%00%0E%08%03%00%00%00%2Cc%0D%DE%00%00%00%A2PLTEQQQ%FA%FA%FA%FC%FC%FC%EE%EE%EE%A9%A9%A9%E9%E9%E9%0A%0A%0A%0D%0D%0D444PPP%CD%CD%CD%CC%CC%CC%F5%F5%F5UUU%D0%D0%D0\'\'\'%F9%F9%F9%A6%A6%A6%40%40%40FFF%A0%A0%A0%89%89%89%8D%8D%8D%20%20%20%14%14%14%DA%DA%DA%B6%B6%B6%02%02%02%87%87%87%81%81%81%AC%AC%AC%0E%0E%0E111%7D%7D%7D%92%92%92333%B9%B9%B9%BC%BC%BChhh)))%E1%E1%E1%03%03%03%CB%CB%CB%EB%EB%EB%FD%FD%FD%A3%A3%A3(((%04%04%04%CA%CA%CAttt%2C%2C%2C%F4%F4%F4%00%00%00%FF%FF%FF%D6%DE%02%C3%00%00%006tRNS%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%00%A1%8FN1%00%00%00iIDAT%08%D7E%C7E%16%02Q%10%C5%D0j%C3%DD%BDqw%5E%F6%BF5%06%D4%3Fd%94kx%BDf%DE6%FFIA%AB%C8qYj%1F%E3Xk%93%E0%C8JZ%10%90%9E%3A1%60%BBY%85%A8%AE%14%C0%5E%1A6%8E%C5w%02%60%99%C9%FA%9A%03%60%8C%EFz%8C%CE%0EnSu%3F%01%AD%B2%06%04%F0%3CT%FF%B8nk%3F%7C%01%C5z%1B%F9%26%2F%3Az%00%00%00%00IEND%AEB%60%82');border:1px solid #00a}span.othercss{font-family:"Times New Roman";font-weight:inherit}""" + + self._test(input, output) diff --git a/.venv/lib/python3.9/site-packages/dateutil/__init__.py b/.venv/lib/python3.9/site-packages/dateutil/__init__.py new file mode 100644 index 00000000..a2c19c06 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +import sys + +try: + from ._version import version as __version__ +except ImportError: + __version__ = 'unknown' + +__all__ = ['easter', 'parser', 'relativedelta', 'rrule', 'tz', + 'utils', 'zoneinfo'] + +def __getattr__(name): + import importlib + + if name in __all__: + return importlib.import_module("." + name, __name__) + raise AttributeError( + "module {!r} has not attribute {!r}".format(__name__, name) + ) + + +def __dir__(): + # __dir__ should include all the lazy-importable modules as well. + return [x for x in globals() if x not in sys.modules] + __all__ diff --git a/.venv/lib/python3.9/site-packages/dateutil/_common.py b/.venv/lib/python3.9/site-packages/dateutil/_common.py new file mode 100644 index 00000000..4eb2659b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/_common.py @@ -0,0 +1,43 @@ +""" +Common code used in multiple modules. +""" + + +class weekday(object): + __slots__ = ["weekday", "n"] + + def __init__(self, weekday, n=None): + self.weekday = weekday + self.n = n + + def __call__(self, n): + if n == self.n: + return self + else: + return self.__class__(self.weekday, n) + + def __eq__(self, other): + try: + if self.weekday != other.weekday or self.n != other.n: + return False + except AttributeError: + return False + return True + + def __hash__(self): + return hash(( + self.weekday, + self.n, + )) + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday] + if not self.n: + return s + else: + return "%s(%+d)" % (s, self.n) + +# vim:ts=4:sw=4:et diff --git a/.venv/lib/python3.9/site-packages/dateutil/_version.py b/.venv/lib/python3.9/site-packages/dateutil/_version.py new file mode 100644 index 00000000..ddda9809 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/_version.py @@ -0,0 +1,4 @@ +# file generated by setuptools_scm +# don't change, don't track in version control +__version__ = version = '2.9.0.post0' +__version_tuple__ = version_tuple = (2, 9, 0) diff --git a/.venv/lib/python3.9/site-packages/dateutil/easter.py b/.venv/lib/python3.9/site-packages/dateutil/easter.py new file mode 100644 index 00000000..f74d1f74 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/easter.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +""" +This module offers a generic Easter computing method for any given year, using +Western, Orthodox or Julian algorithms. +""" + +import datetime + +__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"] + +EASTER_JULIAN = 1 +EASTER_ORTHODOX = 2 +EASTER_WESTERN = 3 + + +def easter(year, method=EASTER_WESTERN): + """ + This method was ported from the work done by GM Arts, + on top of the algorithm by Claus Tondering, which was + based in part on the algorithm of Ouding (1940), as + quoted in "Explanatory Supplement to the Astronomical + Almanac", P. Kenneth Seidelmann, editor. + + This algorithm implements three different Easter + calculation methods: + + 1. Original calculation in Julian calendar, valid in + dates after 326 AD + 2. Original method, with date converted to Gregorian + calendar, valid in years 1583 to 4099 + 3. Revised method, in Gregorian calendar, valid in + years 1583 to 4099 as well + + These methods are represented by the constants: + + * ``EASTER_JULIAN = 1`` + * ``EASTER_ORTHODOX = 2`` + * ``EASTER_WESTERN = 3`` + + The default method is method 3. + + More about the algorithm may be found at: + + `GM Arts: Easter Algorithms `_ + + and + + `The Calendar FAQ: Easter `_ + + """ + + if not (1 <= method <= 3): + raise ValueError("invalid method") + + # g - Golden year - 1 + # c - Century + # h - (23 - Epact) mod 30 + # i - Number of days from March 21 to Paschal Full Moon + # j - Weekday for PFM (0=Sunday, etc) + # p - Number of days from March 21 to Sunday on or before PFM + # (-6 to 28 methods 1 & 3, to 56 for method 2) + # e - Extra days to add for method 2 (converting Julian + # date to Gregorian date) + + y = year + g = y % 19 + e = 0 + if method < 3: + # Old method + i = (19*g + 15) % 30 + j = (y + y//4 + i) % 7 + if method == 2: + # Extra dates to convert Julian to Gregorian date + e = 10 + if y > 1600: + e = e + y//100 - 16 - (y//100 - 16)//4 + else: + # New method + c = y//100 + h = (c - c//4 - (8*c + 13)//25 + 19*g + 15) % 30 + i = h - (h//28)*(1 - (h//28)*(29//(h + 1))*((21 - g)//11)) + j = (y + y//4 + i + 2 - c + c//4) % 7 + + # p can be from -6 to 56 corresponding to dates 22 March to 23 May + # (later dates apply to method 2, although 23 May never actually occurs) + p = i - j + e + d = 1 + (p + 27 + (p + 6)//40) % 31 + m = 3 + (p + 26)//30 + return datetime.date(int(y), int(m), int(d)) diff --git a/.venv/lib/python3.9/site-packages/dateutil/parser/__init__.py b/.venv/lib/python3.9/site-packages/dateutil/parser/__init__.py new file mode 100644 index 00000000..d174b0e4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/parser/__init__.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +from ._parser import parse, parser, parserinfo, ParserError +from ._parser import DEFAULTPARSER, DEFAULTTZPARSER +from ._parser import UnknownTimezoneWarning + +from ._parser import __doc__ + +from .isoparser import isoparser, isoparse + +__all__ = ['parse', 'parser', 'parserinfo', + 'isoparse', 'isoparser', + 'ParserError', + 'UnknownTimezoneWarning'] + + +### +# Deprecate portions of the private interface so that downstream code that +# is improperly relying on it is given *some* notice. + + +def __deprecated_private_func(f): + from functools import wraps + import warnings + + msg = ('{name} is a private function and may break without warning, ' + 'it will be moved and or renamed in future versions.') + msg = msg.format(name=f.__name__) + + @wraps(f) + def deprecated_func(*args, **kwargs): + warnings.warn(msg, DeprecationWarning) + return f(*args, **kwargs) + + return deprecated_func + +def __deprecate_private_class(c): + import warnings + + msg = ('{name} is a private class and may break without warning, ' + 'it will be moved and or renamed in future versions.') + msg = msg.format(name=c.__name__) + + class private_class(c): + __doc__ = c.__doc__ + + def __init__(self, *args, **kwargs): + warnings.warn(msg, DeprecationWarning) + super(private_class, self).__init__(*args, **kwargs) + + private_class.__name__ = c.__name__ + + return private_class + + +from ._parser import _timelex, _resultbase +from ._parser import _tzparser, _parsetz + +_timelex = __deprecate_private_class(_timelex) +_tzparser = __deprecate_private_class(_tzparser) +_resultbase = __deprecate_private_class(_resultbase) +_parsetz = __deprecated_private_func(_parsetz) diff --git a/.venv/lib/python3.9/site-packages/dateutil/parser/_parser.py b/.venv/lib/python3.9/site-packages/dateutil/parser/_parser.py new file mode 100644 index 00000000..37d1663b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/parser/_parser.py @@ -0,0 +1,1613 @@ +# -*- coding: utf-8 -*- +""" +This module offers a generic date/time string parser which is able to parse +most known formats to represent a date and/or time. + +This module attempts to be forgiving with regards to unlikely input formats, +returning a datetime object even for dates which are ambiguous. If an element +of a date/time stamp is omitted, the following rules are applied: + +- If AM or PM is left unspecified, a 24-hour clock is assumed, however, an hour + on a 12-hour clock (``0 <= hour <= 12``) *must* be specified if AM or PM is + specified. +- If a time zone is omitted, a timezone-naive datetime is returned. + +If any other elements are missing, they are taken from the +:class:`datetime.datetime` object passed to the parameter ``default``. If this +results in a day number exceeding the valid number of days per month, the +value falls back to the end of the month. + +Additional resources about date/time string formats can be found below: + +- `A summary of the international standard date and time notation + `_ +- `W3C Date and Time Formats `_ +- `Time Formats (Planetary Rings Node) `_ +- `CPAN ParseDate module + `_ +- `Java SimpleDateFormat Class + `_ +""" +from __future__ import unicode_literals + +import datetime +import re +import string +import time +import warnings + +from calendar import monthrange +from io import StringIO + +import six +from six import integer_types, text_type + +from decimal import Decimal + +from warnings import warn + +from .. import relativedelta +from .. import tz + +__all__ = ["parse", "parserinfo", "ParserError"] + + +# TODO: pandas.core.tools.datetimes imports this explicitly. Might be worth +# making public and/or figuring out if there is something we can +# take off their plate. +class _timelex(object): + # Fractional seconds are sometimes split by a comma + _split_decimal = re.compile("([.,])") + + def __init__(self, instream): + if isinstance(instream, (bytes, bytearray)): + instream = instream.decode() + + if isinstance(instream, text_type): + instream = StringIO(instream) + elif getattr(instream, 'read', None) is None: + raise TypeError('Parser must be a string or character stream, not ' + '{itype}'.format(itype=instream.__class__.__name__)) + + self.instream = instream + self.charstack = [] + self.tokenstack = [] + self.eof = False + + def get_token(self): + """ + This function breaks the time string into lexical units (tokens), which + can be parsed by the parser. Lexical units are demarcated by changes in + the character set, so any continuous string of letters is considered + one unit, any continuous string of numbers is considered one unit. + + The main complication arises from the fact that dots ('.') can be used + both as separators (e.g. "Sep.20.2009") or decimal points (e.g. + "4:30:21.447"). As such, it is necessary to read the full context of + any dot-separated strings before breaking it into tokens; as such, this + function maintains a "token stack", for when the ambiguous context + demands that multiple tokens be parsed at once. + """ + if self.tokenstack: + return self.tokenstack.pop(0) + + seenletters = False + token = None + state = None + + while not self.eof: + # We only realize that we've reached the end of a token when we + # find a character that's not part of the current token - since + # that character may be part of the next token, it's stored in the + # charstack. + if self.charstack: + nextchar = self.charstack.pop(0) + else: + nextchar = self.instream.read(1) + while nextchar == '\x00': + nextchar = self.instream.read(1) + + if not nextchar: + self.eof = True + break + elif not state: + # First character of the token - determines if we're starting + # to parse a word, a number or something else. + token = nextchar + if self.isword(nextchar): + state = 'a' + elif self.isnum(nextchar): + state = '0' + elif self.isspace(nextchar): + token = ' ' + break # emit token + else: + break # emit token + elif state == 'a': + # If we've already started reading a word, we keep reading + # letters until we find something that's not part of a word. + seenletters = True + if self.isword(nextchar): + token += nextchar + elif nextchar == '.': + token += nextchar + state = 'a.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == '0': + # If we've already started reading a number, we keep reading + # numbers until we find something that doesn't fit. + if self.isnum(nextchar): + token += nextchar + elif nextchar == '.' or (nextchar == ',' and len(token) >= 2): + token += nextchar + state = '0.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == 'a.': + # If we've seen some letters and a dot separator, continue + # parsing, and the tokens will be broken up later. + seenletters = True + if nextchar == '.' or self.isword(nextchar): + token += nextchar + elif self.isnum(nextchar) and token[-1] == '.': + token += nextchar + state = '0.' + else: + self.charstack.append(nextchar) + break # emit token + elif state == '0.': + # If we've seen at least one dot separator, keep going, we'll + # break up the tokens later. + if nextchar == '.' or self.isnum(nextchar): + token += nextchar + elif self.isword(nextchar) and token[-1] == '.': + token += nextchar + state = 'a.' + else: + self.charstack.append(nextchar) + break # emit token + + if (state in ('a.', '0.') and (seenletters or token.count('.') > 1 or + token[-1] in '.,')): + l = self._split_decimal.split(token) + token = l[0] + for tok in l[1:]: + if tok: + self.tokenstack.append(tok) + + if state == '0.' and token.count('.') == 0: + token = token.replace(',', '.') + + return token + + def __iter__(self): + return self + + def __next__(self): + token = self.get_token() + if token is None: + raise StopIteration + + return token + + def next(self): + return self.__next__() # Python 2.x support + + @classmethod + def split(cls, s): + return list(cls(s)) + + @classmethod + def isword(cls, nextchar): + """ Whether or not the next character is part of a word """ + return nextchar.isalpha() + + @classmethod + def isnum(cls, nextchar): + """ Whether the next character is part of a number """ + return nextchar.isdigit() + + @classmethod + def isspace(cls, nextchar): + """ Whether the next character is whitespace """ + return nextchar.isspace() + + +class _resultbase(object): + + def __init__(self): + for attr in self.__slots__: + setattr(self, attr, None) + + def _repr(self, classname): + l = [] + for attr in self.__slots__: + value = getattr(self, attr) + if value is not None: + l.append("%s=%s" % (attr, repr(value))) + return "%s(%s)" % (classname, ", ".join(l)) + + def __len__(self): + return (sum(getattr(self, attr) is not None + for attr in self.__slots__)) + + def __repr__(self): + return self._repr(self.__class__.__name__) + + +class parserinfo(object): + """ + Class which handles what inputs are accepted. Subclass this to customize + the language and acceptable values for each parameter. + + :param dayfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM + and YMD. Default is ``False``. + + :param yearfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the year. If ``True``, the first number is taken + to be the year, otherwise the last number is taken to be the year. + Default is ``False``. + """ + + # m from a.m/p.m, t from ISO T separator + JUMP = [" ", ".", ",", ";", "-", "/", "'", + "at", "on", "and", "ad", "m", "t", "of", + "st", "nd", "rd", "th"] + + WEEKDAYS = [("Mon", "Monday"), + ("Tue", "Tuesday"), # TODO: "Tues" + ("Wed", "Wednesday"), + ("Thu", "Thursday"), # TODO: "Thurs" + ("Fri", "Friday"), + ("Sat", "Saturday"), + ("Sun", "Sunday")] + MONTHS = [("Jan", "January"), + ("Feb", "February"), # TODO: "Febr" + ("Mar", "March"), + ("Apr", "April"), + ("May", "May"), + ("Jun", "June"), + ("Jul", "July"), + ("Aug", "August"), + ("Sep", "Sept", "September"), + ("Oct", "October"), + ("Nov", "November"), + ("Dec", "December")] + HMS = [("h", "hour", "hours"), + ("m", "minute", "minutes"), + ("s", "second", "seconds")] + AMPM = [("am", "a"), + ("pm", "p")] + UTCZONE = ["UTC", "GMT", "Z", "z"] + PERTAIN = ["of"] + TZOFFSET = {} + # TODO: ERA = ["AD", "BC", "CE", "BCE", "Stardate", + # "Anno Domini", "Year of Our Lord"] + + def __init__(self, dayfirst=False, yearfirst=False): + self._jump = self._convert(self.JUMP) + self._weekdays = self._convert(self.WEEKDAYS) + self._months = self._convert(self.MONTHS) + self._hms = self._convert(self.HMS) + self._ampm = self._convert(self.AMPM) + self._utczone = self._convert(self.UTCZONE) + self._pertain = self._convert(self.PERTAIN) + + self.dayfirst = dayfirst + self.yearfirst = yearfirst + + self._year = time.localtime().tm_year + self._century = self._year // 100 * 100 + + def _convert(self, lst): + dct = {} + for i, v in enumerate(lst): + if isinstance(v, tuple): + for v in v: + dct[v.lower()] = i + else: + dct[v.lower()] = i + return dct + + def jump(self, name): + return name.lower() in self._jump + + def weekday(self, name): + try: + return self._weekdays[name.lower()] + except KeyError: + pass + return None + + def month(self, name): + try: + return self._months[name.lower()] + 1 + except KeyError: + pass + return None + + def hms(self, name): + try: + return self._hms[name.lower()] + except KeyError: + return None + + def ampm(self, name): + try: + return self._ampm[name.lower()] + except KeyError: + return None + + def pertain(self, name): + return name.lower() in self._pertain + + def utczone(self, name): + return name.lower() in self._utczone + + def tzoffset(self, name): + if name in self._utczone: + return 0 + + return self.TZOFFSET.get(name) + + def convertyear(self, year, century_specified=False): + """ + Converts two-digit years to year within [-50, 49] + range of self._year (current local time) + """ + + # Function contract is that the year is always positive + assert year >= 0 + + if year < 100 and not century_specified: + # assume current century to start + year += self._century + + if year >= self._year + 50: # if too far in future + year -= 100 + elif year < self._year - 50: # if too far in past + year += 100 + + return year + + def validate(self, res): + # move to info + if res.year is not None: + res.year = self.convertyear(res.year, res.century_specified) + + if ((res.tzoffset == 0 and not res.tzname) or + (res.tzname == 'Z' or res.tzname == 'z')): + res.tzname = "UTC" + res.tzoffset = 0 + elif res.tzoffset != 0 and res.tzname and self.utczone(res.tzname): + res.tzoffset = 0 + return True + + +class _ymd(list): + def __init__(self, *args, **kwargs): + super(self.__class__, self).__init__(*args, **kwargs) + self.century_specified = False + self.dstridx = None + self.mstridx = None + self.ystridx = None + + @property + def has_year(self): + return self.ystridx is not None + + @property + def has_month(self): + return self.mstridx is not None + + @property + def has_day(self): + return self.dstridx is not None + + def could_be_day(self, value): + if self.has_day: + return False + elif not self.has_month: + return 1 <= value <= 31 + elif not self.has_year: + # Be permissive, assume leap year + month = self[self.mstridx] + return 1 <= value <= monthrange(2000, month)[1] + else: + month = self[self.mstridx] + year = self[self.ystridx] + return 1 <= value <= monthrange(year, month)[1] + + def append(self, val, label=None): + if hasattr(val, '__len__'): + if val.isdigit() and len(val) > 2: + self.century_specified = True + if label not in [None, 'Y']: # pragma: no cover + raise ValueError(label) + label = 'Y' + elif val > 100: + self.century_specified = True + if label not in [None, 'Y']: # pragma: no cover + raise ValueError(label) + label = 'Y' + + super(self.__class__, self).append(int(val)) + + if label == 'M': + if self.has_month: + raise ValueError('Month is already set') + self.mstridx = len(self) - 1 + elif label == 'D': + if self.has_day: + raise ValueError('Day is already set') + self.dstridx = len(self) - 1 + elif label == 'Y': + if self.has_year: + raise ValueError('Year is already set') + self.ystridx = len(self) - 1 + + def _resolve_from_stridxs(self, strids): + """ + Try to resolve the identities of year/month/day elements using + ystridx, mstridx, and dstridx, if enough of these are specified. + """ + if len(self) == 3 and len(strids) == 2: + # we can back out the remaining stridx value + missing = [x for x in range(3) if x not in strids.values()] + key = [x for x in ['y', 'm', 'd'] if x not in strids] + assert len(missing) == len(key) == 1 + key = key[0] + val = missing[0] + strids[key] = val + + assert len(self) == len(strids) # otherwise this should not be called + out = {key: self[strids[key]] for key in strids} + return (out.get('y'), out.get('m'), out.get('d')) + + def resolve_ymd(self, yearfirst, dayfirst): + len_ymd = len(self) + year, month, day = (None, None, None) + + strids = (('y', self.ystridx), + ('m', self.mstridx), + ('d', self.dstridx)) + + strids = {key: val for key, val in strids if val is not None} + if (len(self) == len(strids) > 0 or + (len(self) == 3 and len(strids) == 2)): + return self._resolve_from_stridxs(strids) + + mstridx = self.mstridx + + if len_ymd > 3: + raise ValueError("More than three YMD values") + elif len_ymd == 1 or (mstridx is not None and len_ymd == 2): + # One member, or two members with a month string + if mstridx is not None: + month = self[mstridx] + # since mstridx is 0 or 1, self[mstridx-1] always + # looks up the other element + other = self[mstridx - 1] + else: + other = self[0] + + if len_ymd > 1 or mstridx is None: + if other > 31: + year = other + else: + day = other + + elif len_ymd == 2: + # Two members with numbers + if self[0] > 31: + # 99-01 + year, month = self + elif self[1] > 31: + # 01-99 + month, year = self + elif dayfirst and self[1] <= 12: + # 13-01 + day, month = self + else: + # 01-13 + month, day = self + + elif len_ymd == 3: + # Three members + if mstridx == 0: + if self[1] > 31: + # Apr-2003-25 + month, year, day = self + else: + month, day, year = self + elif mstridx == 1: + if self[0] > 31 or (yearfirst and self[2] <= 31): + # 99-Jan-01 + year, month, day = self + else: + # 01-Jan-01 + # Give precedence to day-first, since + # two-digit years is usually hand-written. + day, month, year = self + + elif mstridx == 2: + # WTF!? + if self[1] > 31: + # 01-99-Jan + day, year, month = self + else: + # 99-01-Jan + year, day, month = self + + else: + if (self[0] > 31 or + self.ystridx == 0 or + (yearfirst and self[1] <= 12 and self[2] <= 31)): + # 99-01-01 + if dayfirst and self[2] <= 12: + year, day, month = self + else: + year, month, day = self + elif self[0] > 12 or (dayfirst and self[1] <= 12): + # 13-01-01 + day, month, year = self + else: + # 01-13-01 + month, day, year = self + + return year, month, day + + +class parser(object): + def __init__(self, info=None): + self.info = info or parserinfo() + + def parse(self, timestr, default=None, + ignoretz=False, tzinfos=None, **kwargs): + """ + Parse the date/time string into a :class:`datetime.datetime` object. + + :param timestr: + Any date/time string using the supported formats. + + :param default: + The default datetime object, if this is a datetime object and not + ``None``, elements specified in ``timestr`` replace elements in the + default object. + + :param ignoretz: + If set ``True``, time zones in parsed strings are ignored and a + naive :class:`datetime.datetime` object is returned. + + :param tzinfos: + Additional time zone names / aliases which may be present in the + string. This argument maps time zone names (and optionally offsets + from those time zones) to time zones. This parameter can be a + dictionary with timezone aliases mapping time zone names to time + zones or a function taking two parameters (``tzname`` and + ``tzoffset``) and returning a time zone. + + The timezones to which the names are mapped can be an integer + offset from UTC in seconds or a :class:`tzinfo` object. + + .. doctest:: + :options: +NORMALIZE_WHITESPACE + + >>> from dateutil.parser import parse + >>> from dateutil.tz import gettz + >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")} + >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200)) + >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, + tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) + + This parameter is ignored if ``ignoretz`` is set. + + :param \\*\\*kwargs: + Keyword arguments as passed to ``_parse()``. + + :return: + Returns a :class:`datetime.datetime` object or, if the + ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the + first element being a :class:`datetime.datetime` object, the second + a tuple containing the fuzzy tokens. + + :raises ParserError: + Raised for invalid or unknown string format, if the provided + :class:`tzinfo` is not in a valid format, or if an invalid date + would be created. + + :raises TypeError: + Raised for non-string or character stream input. + + :raises OverflowError: + Raised if the parsed date exceeds the largest valid C integer on + your system. + """ + + if default is None: + default = datetime.datetime.now().replace(hour=0, minute=0, + second=0, microsecond=0) + + res, skipped_tokens = self._parse(timestr, **kwargs) + + if res is None: + raise ParserError("Unknown string format: %s", timestr) + + if len(res) == 0: + raise ParserError("String does not contain a date: %s", timestr) + + try: + ret = self._build_naive(res, default) + except ValueError as e: + six.raise_from(ParserError(str(e) + ": %s", timestr), e) + + if not ignoretz: + ret = self._build_tzaware(ret, res, tzinfos) + + if kwargs.get('fuzzy_with_tokens', False): + return ret, skipped_tokens + else: + return ret + + class _result(_resultbase): + __slots__ = ["year", "month", "day", "weekday", + "hour", "minute", "second", "microsecond", + "tzname", "tzoffset", "ampm","any_unused_tokens"] + + def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False, + fuzzy_with_tokens=False): + """ + Private method which performs the heavy lifting of parsing, called from + ``parse()``, which passes on its ``kwargs`` to this function. + + :param timestr: + The string to parse. + + :param dayfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM + and YMD. If set to ``None``, this value is retrieved from the + current :class:`parserinfo` object (which itself defaults to + ``False``). + + :param yearfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the year. If ``True``, the first number is taken + to be the year, otherwise the last number is taken to be the year. + If this is set to ``None``, the value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). + + :param fuzzy: + Whether to allow fuzzy parsing, allowing for string like "Today is + January 1, 2047 at 8:21:00AM". + + :param fuzzy_with_tokens: + If ``True``, ``fuzzy`` is automatically set to True, and the parser + will return a tuple where the first element is the parsed + :class:`datetime.datetime` datetimestamp and the second element is + a tuple containing the portions of the string which were ignored: + + .. doctest:: + + >>> from dateutil.parser import parse + >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) + (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) + + """ + if fuzzy_with_tokens: + fuzzy = True + + info = self.info + + if dayfirst is None: + dayfirst = info.dayfirst + + if yearfirst is None: + yearfirst = info.yearfirst + + res = self._result() + l = _timelex.split(timestr) # Splits the timestr into tokens + + skipped_idxs = [] + + # year/month/day list + ymd = _ymd() + + len_l = len(l) + i = 0 + try: + while i < len_l: + + # Check if it's a number + value_repr = l[i] + try: + value = float(value_repr) + except ValueError: + value = None + + if value is not None: + # Numeric token + i = self._parse_numeric_token(l, i, info, ymd, res, fuzzy) + + # Check weekday + elif info.weekday(l[i]) is not None: + value = info.weekday(l[i]) + res.weekday = value + + # Check month name + elif info.month(l[i]) is not None: + value = info.month(l[i]) + ymd.append(value, 'M') + + if i + 1 < len_l: + if l[i + 1] in ('-', '/'): + # Jan-01[-99] + sep = l[i + 1] + ymd.append(l[i + 2]) + + if i + 3 < len_l and l[i + 3] == sep: + # Jan-01-99 + ymd.append(l[i + 4]) + i += 2 + + i += 2 + + elif (i + 4 < len_l and l[i + 1] == l[i + 3] == ' ' and + info.pertain(l[i + 2])): + # Jan of 01 + # In this case, 01 is clearly year + if l[i + 4].isdigit(): + # Convert it here to become unambiguous + value = int(l[i + 4]) + year = str(info.convertyear(value)) + ymd.append(year, 'Y') + else: + # Wrong guess + pass + # TODO: not hit in tests + i += 4 + + # Check am/pm + elif info.ampm(l[i]) is not None: + value = info.ampm(l[i]) + val_is_ampm = self._ampm_valid(res.hour, res.ampm, fuzzy) + + if val_is_ampm: + res.hour = self._adjust_ampm(res.hour, value) + res.ampm = value + + elif fuzzy: + skipped_idxs.append(i) + + # Check for a timezone name + elif self._could_be_tzname(res.hour, res.tzname, res.tzoffset, l[i]): + res.tzname = l[i] + res.tzoffset = info.tzoffset(res.tzname) + + # Check for something like GMT+3, or BRST+3. Notice + # that it doesn't mean "I am 3 hours after GMT", but + # "my time +3 is GMT". If found, we reverse the + # logic so that timezone parsing code will get it + # right. + if i + 1 < len_l and l[i + 1] in ('+', '-'): + l[i + 1] = ('+', '-')[l[i + 1] == '+'] + res.tzoffset = None + if info.utczone(res.tzname): + # With something like GMT+3, the timezone + # is *not* GMT. + res.tzname = None + + # Check for a numbered timezone + elif res.hour is not None and l[i] in ('+', '-'): + signal = (-1, 1)[l[i] == '+'] + len_li = len(l[i + 1]) + + # TODO: check that l[i + 1] is integer? + if len_li == 4: + # -0300 + hour_offset = int(l[i + 1][:2]) + min_offset = int(l[i + 1][2:]) + elif i + 2 < len_l and l[i + 2] == ':': + # -03:00 + hour_offset = int(l[i + 1]) + min_offset = int(l[i + 3]) # TODO: Check that l[i+3] is minute-like? + i += 2 + elif len_li <= 2: + # -[0]3 + hour_offset = int(l[i + 1][:2]) + min_offset = 0 + else: + raise ValueError(timestr) + + res.tzoffset = signal * (hour_offset * 3600 + min_offset * 60) + + # Look for a timezone name between parenthesis + if (i + 5 < len_l and + info.jump(l[i + 2]) and l[i + 3] == '(' and + l[i + 5] == ')' and + 3 <= len(l[i + 4]) and + self._could_be_tzname(res.hour, res.tzname, + None, l[i + 4])): + # -0300 (BRST) + res.tzname = l[i + 4] + i += 4 + + i += 1 + + # Check jumps + elif not (info.jump(l[i]) or fuzzy): + raise ValueError(timestr) + + else: + skipped_idxs.append(i) + i += 1 + + # Process year/month/day + year, month, day = ymd.resolve_ymd(yearfirst, dayfirst) + + res.century_specified = ymd.century_specified + res.year = year + res.month = month + res.day = day + + except (IndexError, ValueError): + return None, None + + if not info.validate(res): + return None, None + + if fuzzy_with_tokens: + skipped_tokens = self._recombine_skipped(l, skipped_idxs) + return res, tuple(skipped_tokens) + else: + return res, None + + def _parse_numeric_token(self, tokens, idx, info, ymd, res, fuzzy): + # Token is a number + value_repr = tokens[idx] + try: + value = self._to_decimal(value_repr) + except Exception as e: + six.raise_from(ValueError('Unknown numeric token'), e) + + len_li = len(value_repr) + + len_l = len(tokens) + + if (len(ymd) == 3 and len_li in (2, 4) and + res.hour is None and + (idx + 1 >= len_l or + (tokens[idx + 1] != ':' and + info.hms(tokens[idx + 1]) is None))): + # 19990101T23[59] + s = tokens[idx] + res.hour = int(s[:2]) + + if len_li == 4: + res.minute = int(s[2:]) + + elif len_li == 6 or (len_li > 6 and tokens[idx].find('.') == 6): + # YYMMDD or HHMMSS[.ss] + s = tokens[idx] + + if not ymd and '.' not in tokens[idx]: + ymd.append(s[:2]) + ymd.append(s[2:4]) + ymd.append(s[4:]) + else: + # 19990101T235959[.59] + + # TODO: Check if res attributes already set. + res.hour = int(s[:2]) + res.minute = int(s[2:4]) + res.second, res.microsecond = self._parsems(s[4:]) + + elif len_li in (8, 12, 14): + # YYYYMMDD + s = tokens[idx] + ymd.append(s[:4], 'Y') + ymd.append(s[4:6]) + ymd.append(s[6:8]) + + if len_li > 8: + res.hour = int(s[8:10]) + res.minute = int(s[10:12]) + + if len_li > 12: + res.second = int(s[12:]) + + elif self._find_hms_idx(idx, tokens, info, allow_jump=True) is not None: + # HH[ ]h or MM[ ]m or SS[.ss][ ]s + hms_idx = self._find_hms_idx(idx, tokens, info, allow_jump=True) + (idx, hms) = self._parse_hms(idx, tokens, info, hms_idx) + if hms is not None: + # TODO: checking that hour/minute/second are not + # already set? + self._assign_hms(res, value_repr, hms) + + elif idx + 2 < len_l and tokens[idx + 1] == ':': + # HH:MM[:SS[.ss]] + res.hour = int(value) + value = self._to_decimal(tokens[idx + 2]) # TODO: try/except for this? + (res.minute, res.second) = self._parse_min_sec(value) + + if idx + 4 < len_l and tokens[idx + 3] == ':': + res.second, res.microsecond = self._parsems(tokens[idx + 4]) + + idx += 2 + + idx += 2 + + elif idx + 1 < len_l and tokens[idx + 1] in ('-', '/', '.'): + sep = tokens[idx + 1] + ymd.append(value_repr) + + if idx + 2 < len_l and not info.jump(tokens[idx + 2]): + if tokens[idx + 2].isdigit(): + # 01-01[-01] + ymd.append(tokens[idx + 2]) + else: + # 01-Jan[-01] + value = info.month(tokens[idx + 2]) + + if value is not None: + ymd.append(value, 'M') + else: + raise ValueError() + + if idx + 3 < len_l and tokens[idx + 3] == sep: + # We have three members + value = info.month(tokens[idx + 4]) + + if value is not None: + ymd.append(value, 'M') + else: + ymd.append(tokens[idx + 4]) + idx += 2 + + idx += 1 + idx += 1 + + elif idx + 1 >= len_l or info.jump(tokens[idx + 1]): + if idx + 2 < len_l and info.ampm(tokens[idx + 2]) is not None: + # 12 am + hour = int(value) + res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 2])) + idx += 1 + else: + # Year, month or day + ymd.append(value) + idx += 1 + + elif info.ampm(tokens[idx + 1]) is not None and (0 <= value < 24): + # 12am + hour = int(value) + res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 1])) + idx += 1 + + elif ymd.could_be_day(value): + ymd.append(value) + + elif not fuzzy: + raise ValueError() + + return idx + + def _find_hms_idx(self, idx, tokens, info, allow_jump): + len_l = len(tokens) + + if idx+1 < len_l and info.hms(tokens[idx+1]) is not None: + # There is an "h", "m", or "s" label following this token. We take + # assign the upcoming label to the current token. + # e.g. the "12" in 12h" + hms_idx = idx + 1 + + elif (allow_jump and idx+2 < len_l and tokens[idx+1] == ' ' and + info.hms(tokens[idx+2]) is not None): + # There is a space and then an "h", "m", or "s" label. + # e.g. the "12" in "12 h" + hms_idx = idx + 2 + + elif idx > 0 and info.hms(tokens[idx-1]) is not None: + # There is a "h", "m", or "s" preceding this token. Since neither + # of the previous cases was hit, there is no label following this + # token, so we use the previous label. + # e.g. the "04" in "12h04" + hms_idx = idx-1 + + elif (1 < idx == len_l-1 and tokens[idx-1] == ' ' and + info.hms(tokens[idx-2]) is not None): + # If we are looking at the final token, we allow for a + # backward-looking check to skip over a space. + # TODO: Are we sure this is the right condition here? + hms_idx = idx - 2 + + else: + hms_idx = None + + return hms_idx + + def _assign_hms(self, res, value_repr, hms): + # See GH issue #427, fixing float rounding + value = self._to_decimal(value_repr) + + if hms == 0: + # Hour + res.hour = int(value) + if value % 1: + res.minute = int(60*(value % 1)) + + elif hms == 1: + (res.minute, res.second) = self._parse_min_sec(value) + + elif hms == 2: + (res.second, res.microsecond) = self._parsems(value_repr) + + def _could_be_tzname(self, hour, tzname, tzoffset, token): + return (hour is not None and + tzname is None and + tzoffset is None and + len(token) <= 5 and + (all(x in string.ascii_uppercase for x in token) + or token in self.info.UTCZONE)) + + def _ampm_valid(self, hour, ampm, fuzzy): + """ + For fuzzy parsing, 'a' or 'am' (both valid English words) + may erroneously trigger the AM/PM flag. Deal with that + here. + """ + val_is_ampm = True + + # If there's already an AM/PM flag, this one isn't one. + if fuzzy and ampm is not None: + val_is_ampm = False + + # If AM/PM is found and hour is not, raise a ValueError + if hour is None: + if fuzzy: + val_is_ampm = False + else: + raise ValueError('No hour specified with AM or PM flag.') + elif not 0 <= hour <= 12: + # If AM/PM is found, it's a 12 hour clock, so raise + # an error for invalid range + if fuzzy: + val_is_ampm = False + else: + raise ValueError('Invalid hour specified for 12-hour clock.') + + return val_is_ampm + + def _adjust_ampm(self, hour, ampm): + if hour < 12 and ampm == 1: + hour += 12 + elif hour == 12 and ampm == 0: + hour = 0 + return hour + + def _parse_min_sec(self, value): + # TODO: Every usage of this function sets res.second to the return + # value. Are there any cases where second will be returned as None and + # we *don't* want to set res.second = None? + minute = int(value) + second = None + + sec_remainder = value % 1 + if sec_remainder: + second = int(60 * sec_remainder) + return (minute, second) + + def _parse_hms(self, idx, tokens, info, hms_idx): + # TODO: Is this going to admit a lot of false-positives for when we + # just happen to have digits and "h", "m" or "s" characters in non-date + # text? I guess hex hashes won't have that problem, but there's plenty + # of random junk out there. + if hms_idx is None: + hms = None + new_idx = idx + elif hms_idx > idx: + hms = info.hms(tokens[hms_idx]) + new_idx = hms_idx + else: + # Looking backwards, increment one. + hms = info.hms(tokens[hms_idx]) + 1 + new_idx = idx + + return (new_idx, hms) + + # ------------------------------------------------------------------ + # Handling for individual tokens. These are kept as methods instead + # of functions for the sake of customizability via subclassing. + + def _parsems(self, value): + """Parse a I[.F] seconds value into (seconds, microseconds).""" + if "." not in value: + return int(value), 0 + else: + i, f = value.split(".") + return int(i), int(f.ljust(6, "0")[:6]) + + def _to_decimal(self, val): + try: + decimal_value = Decimal(val) + # See GH 662, edge case, infinite value should not be converted + # via `_to_decimal` + if not decimal_value.is_finite(): + raise ValueError("Converted decimal value is infinite or NaN") + except Exception as e: + msg = "Could not convert %s to decimal" % val + six.raise_from(ValueError(msg), e) + else: + return decimal_value + + # ------------------------------------------------------------------ + # Post-Parsing construction of datetime output. These are kept as + # methods instead of functions for the sake of customizability via + # subclassing. + + def _build_tzinfo(self, tzinfos, tzname, tzoffset): + if callable(tzinfos): + tzdata = tzinfos(tzname, tzoffset) + else: + tzdata = tzinfos.get(tzname) + # handle case where tzinfo is paased an options that returns None + # eg tzinfos = {'BRST' : None} + if isinstance(tzdata, datetime.tzinfo) or tzdata is None: + tzinfo = tzdata + elif isinstance(tzdata, text_type): + tzinfo = tz.tzstr(tzdata) + elif isinstance(tzdata, integer_types): + tzinfo = tz.tzoffset(tzname, tzdata) + else: + raise TypeError("Offset must be tzinfo subclass, tz string, " + "or int offset.") + return tzinfo + + def _build_tzaware(self, naive, res, tzinfos): + if (callable(tzinfos) or (tzinfos and res.tzname in tzinfos)): + tzinfo = self._build_tzinfo(tzinfos, res.tzname, res.tzoffset) + aware = naive.replace(tzinfo=tzinfo) + aware = self._assign_tzname(aware, res.tzname) + + elif res.tzname and res.tzname in time.tzname: + aware = naive.replace(tzinfo=tz.tzlocal()) + + # Handle ambiguous local datetime + aware = self._assign_tzname(aware, res.tzname) + + # This is mostly relevant for winter GMT zones parsed in the UK + if (aware.tzname() != res.tzname and + res.tzname in self.info.UTCZONE): + aware = aware.replace(tzinfo=tz.UTC) + + elif res.tzoffset == 0: + aware = naive.replace(tzinfo=tz.UTC) + + elif res.tzoffset: + aware = naive.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset)) + + elif not res.tzname and not res.tzoffset: + # i.e. no timezone information was found. + aware = naive + + elif res.tzname: + # tz-like string was parsed but we don't know what to do + # with it + warnings.warn("tzname {tzname} identified but not understood. " + "Pass `tzinfos` argument in order to correctly " + "return a timezone-aware datetime. In a future " + "version, this will raise an " + "exception.".format(tzname=res.tzname), + category=UnknownTimezoneWarning) + aware = naive + + return aware + + def _build_naive(self, res, default): + repl = {} + for attr in ("year", "month", "day", "hour", + "minute", "second", "microsecond"): + value = getattr(res, attr) + if value is not None: + repl[attr] = value + + if 'day' not in repl: + # If the default day exceeds the last day of the month, fall back + # to the end of the month. + cyear = default.year if res.year is None else res.year + cmonth = default.month if res.month is None else res.month + cday = default.day if res.day is None else res.day + + if cday > monthrange(cyear, cmonth)[1]: + repl['day'] = monthrange(cyear, cmonth)[1] + + naive = default.replace(**repl) + + if res.weekday is not None and not res.day: + naive = naive + relativedelta.relativedelta(weekday=res.weekday) + + return naive + + def _assign_tzname(self, dt, tzname): + if dt.tzname() != tzname: + new_dt = tz.enfold(dt, fold=1) + if new_dt.tzname() == tzname: + return new_dt + + return dt + + def _recombine_skipped(self, tokens, skipped_idxs): + """ + >>> tokens = ["foo", " ", "bar", " ", "19June2000", "baz"] + >>> skipped_idxs = [0, 1, 2, 5] + >>> _recombine_skipped(tokens, skipped_idxs) + ["foo bar", "baz"] + """ + skipped_tokens = [] + for i, idx in enumerate(sorted(skipped_idxs)): + if i > 0 and idx - 1 == skipped_idxs[i - 1]: + skipped_tokens[-1] = skipped_tokens[-1] + tokens[idx] + else: + skipped_tokens.append(tokens[idx]) + + return skipped_tokens + + +DEFAULTPARSER = parser() + + +def parse(timestr, parserinfo=None, **kwargs): + """ + + Parse a string in one of the supported formats, using the + ``parserinfo`` parameters. + + :param timestr: + A string containing a date/time stamp. + + :param parserinfo: + A :class:`parserinfo` object containing parameters for the parser. + If ``None``, the default arguments to the :class:`parserinfo` + constructor are used. + + The ``**kwargs`` parameter takes the following keyword arguments: + + :param default: + The default datetime object, if this is a datetime object and not + ``None``, elements specified in ``timestr`` replace elements in the + default object. + + :param ignoretz: + If set ``True``, time zones in parsed strings are ignored and a naive + :class:`datetime` object is returned. + + :param tzinfos: + Additional time zone names / aliases which may be present in the + string. This argument maps time zone names (and optionally offsets + from those time zones) to time zones. This parameter can be a + dictionary with timezone aliases mapping time zone names to time + zones or a function taking two parameters (``tzname`` and + ``tzoffset``) and returning a time zone. + + The timezones to which the names are mapped can be an integer + offset from UTC in seconds or a :class:`tzinfo` object. + + .. doctest:: + :options: +NORMALIZE_WHITESPACE + + >>> from dateutil.parser import parse + >>> from dateutil.tz import gettz + >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")} + >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200)) + >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) + datetime.datetime(2012, 1, 19, 17, 21, + tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) + + This parameter is ignored if ``ignoretz`` is set. + + :param dayfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the day (``True``) or month (``False``). If + ``yearfirst`` is set to ``True``, this distinguishes between YDM and + YMD. If set to ``None``, this value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). + + :param yearfirst: + Whether to interpret the first value in an ambiguous 3-integer date + (e.g. 01/05/09) as the year. If ``True``, the first number is taken to + be the year, otherwise the last number is taken to be the year. If + this is set to ``None``, the value is retrieved from the current + :class:`parserinfo` object (which itself defaults to ``False``). + + :param fuzzy: + Whether to allow fuzzy parsing, allowing for string like "Today is + January 1, 2047 at 8:21:00AM". + + :param fuzzy_with_tokens: + If ``True``, ``fuzzy`` is automatically set to True, and the parser + will return a tuple where the first element is the parsed + :class:`datetime.datetime` datetimestamp and the second element is + a tuple containing the portions of the string which were ignored: + + .. doctest:: + + >>> from dateutil.parser import parse + >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) + (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) + + :return: + Returns a :class:`datetime.datetime` object or, if the + ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the + first element being a :class:`datetime.datetime` object, the second + a tuple containing the fuzzy tokens. + + :raises ParserError: + Raised for invalid or unknown string formats, if the provided + :class:`tzinfo` is not in a valid format, or if an invalid date would + be created. + + :raises OverflowError: + Raised if the parsed date exceeds the largest valid C integer on + your system. + """ + if parserinfo: + return parser(parserinfo).parse(timestr, **kwargs) + else: + return DEFAULTPARSER.parse(timestr, **kwargs) + + +class _tzparser(object): + + class _result(_resultbase): + + __slots__ = ["stdabbr", "stdoffset", "dstabbr", "dstoffset", + "start", "end"] + + class _attr(_resultbase): + __slots__ = ["month", "week", "weekday", + "yday", "jyday", "day", "time"] + + def __repr__(self): + return self._repr("") + + def __init__(self): + _resultbase.__init__(self) + self.start = self._attr() + self.end = self._attr() + + def parse(self, tzstr): + res = self._result() + l = [x for x in re.split(r'([,:.]|[a-zA-Z]+|[0-9]+)',tzstr) if x] + used_idxs = list() + try: + + len_l = len(l) + + i = 0 + while i < len_l: + # BRST+3[BRDT[+2]] + j = i + while j < len_l and not [x for x in l[j] + if x in "0123456789:,-+"]: + j += 1 + if j != i: + if not res.stdabbr: + offattr = "stdoffset" + res.stdabbr = "".join(l[i:j]) + else: + offattr = "dstoffset" + res.dstabbr = "".join(l[i:j]) + + for ii in range(j): + used_idxs.append(ii) + i = j + if (i < len_l and (l[i] in ('+', '-') or l[i][0] in + "0123456789")): + if l[i] in ('+', '-'): + # Yes, that's right. See the TZ variable + # documentation. + signal = (1, -1)[l[i] == '+'] + used_idxs.append(i) + i += 1 + else: + signal = -1 + len_li = len(l[i]) + if len_li == 4: + # -0300 + setattr(res, offattr, (int(l[i][:2]) * 3600 + + int(l[i][2:]) * 60) * signal) + elif i + 1 < len_l and l[i + 1] == ':': + # -03:00 + setattr(res, offattr, + (int(l[i]) * 3600 + + int(l[i + 2]) * 60) * signal) + used_idxs.append(i) + i += 2 + elif len_li <= 2: + # -[0]3 + setattr(res, offattr, + int(l[i][:2]) * 3600 * signal) + else: + return None + used_idxs.append(i) + i += 1 + if res.dstabbr: + break + else: + break + + + if i < len_l: + for j in range(i, len_l): + if l[j] == ';': + l[j] = ',' + + assert l[i] == ',' + + i += 1 + + if i >= len_l: + pass + elif (8 <= l.count(',') <= 9 and + not [y for x in l[i:] if x != ',' + for y in x if y not in "0123456789+-"]): + # GMT0BST,3,0,30,3600,10,0,26,7200[,3600] + for x in (res.start, res.end): + x.month = int(l[i]) + used_idxs.append(i) + i += 2 + if l[i] == '-': + value = int(l[i + 1]) * -1 + used_idxs.append(i) + i += 1 + else: + value = int(l[i]) + used_idxs.append(i) + i += 2 + if value: + x.week = value + x.weekday = (int(l[i]) - 1) % 7 + else: + x.day = int(l[i]) + used_idxs.append(i) + i += 2 + x.time = int(l[i]) + used_idxs.append(i) + i += 2 + if i < len_l: + if l[i] in ('-', '+'): + signal = (-1, 1)[l[i] == "+"] + used_idxs.append(i) + i += 1 + else: + signal = 1 + used_idxs.append(i) + res.dstoffset = (res.stdoffset + int(l[i]) * signal) + + # This was a made-up format that is not in normal use + warn(('Parsed time zone "%s"' % tzstr) + + 'is in a non-standard dateutil-specific format, which ' + + 'is now deprecated; support for parsing this format ' + + 'will be removed in future versions. It is recommended ' + + 'that you switch to a standard format like the GNU ' + + 'TZ variable format.', tz.DeprecatedTzFormatWarning) + elif (l.count(',') == 2 and l[i:].count('/') <= 2 and + not [y for x in l[i:] if x not in (',', '/', 'J', 'M', + '.', '-', ':') + for y in x if y not in "0123456789"]): + for x in (res.start, res.end): + if l[i] == 'J': + # non-leap year day (1 based) + used_idxs.append(i) + i += 1 + x.jyday = int(l[i]) + elif l[i] == 'M': + # month[-.]week[-.]weekday + used_idxs.append(i) + i += 1 + x.month = int(l[i]) + used_idxs.append(i) + i += 1 + assert l[i] in ('-', '.') + used_idxs.append(i) + i += 1 + x.week = int(l[i]) + if x.week == 5: + x.week = -1 + used_idxs.append(i) + i += 1 + assert l[i] in ('-', '.') + used_idxs.append(i) + i += 1 + x.weekday = (int(l[i]) - 1) % 7 + else: + # year day (zero based) + x.yday = int(l[i]) + 1 + + used_idxs.append(i) + i += 1 + + if i < len_l and l[i] == '/': + used_idxs.append(i) + i += 1 + # start time + len_li = len(l[i]) + if len_li == 4: + # -0300 + x.time = (int(l[i][:2]) * 3600 + + int(l[i][2:]) * 60) + elif i + 1 < len_l and l[i + 1] == ':': + # -03:00 + x.time = int(l[i]) * 3600 + int(l[i + 2]) * 60 + used_idxs.append(i) + i += 2 + if i + 1 < len_l and l[i + 1] == ':': + used_idxs.append(i) + i += 2 + x.time += int(l[i]) + elif len_li <= 2: + # -[0]3 + x.time = (int(l[i][:2]) * 3600) + else: + return None + used_idxs.append(i) + i += 1 + + assert i == len_l or l[i] == ',' + + i += 1 + + assert i >= len_l + + except (IndexError, ValueError, AssertionError): + return None + + unused_idxs = set(range(len_l)).difference(used_idxs) + res.any_unused_tokens = not {l[n] for n in unused_idxs}.issubset({",",":"}) + return res + + +DEFAULTTZPARSER = _tzparser() + + +def _parsetz(tzstr): + return DEFAULTTZPARSER.parse(tzstr) + + +class ParserError(ValueError): + """Exception subclass used for any failure to parse a datetime string. + + This is a subclass of :py:exc:`ValueError`, and should be raised any time + earlier versions of ``dateutil`` would have raised ``ValueError``. + + .. versionadded:: 2.8.1 + """ + def __str__(self): + try: + return self.args[0] % self.args[1:] + except (TypeError, IndexError): + return super(ParserError, self).__str__() + + def __repr__(self): + args = ", ".join("'%s'" % arg for arg in self.args) + return "%s(%s)" % (self.__class__.__name__, args) + + +class UnknownTimezoneWarning(RuntimeWarning): + """Raised when the parser finds a timezone it cannot parse into a tzinfo. + + .. versionadded:: 2.7.0 + """ +# vim:ts=4:sw=4:et diff --git a/.venv/lib/python3.9/site-packages/dateutil/parser/isoparser.py b/.venv/lib/python3.9/site-packages/dateutil/parser/isoparser.py new file mode 100644 index 00000000..7060087d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/parser/isoparser.py @@ -0,0 +1,416 @@ +# -*- coding: utf-8 -*- +""" +This module offers a parser for ISO-8601 strings + +It is intended to support all valid date, time and datetime formats per the +ISO-8601 specification. + +..versionadded:: 2.7.0 +""" +from datetime import datetime, timedelta, time, date +import calendar +from dateutil import tz + +from functools import wraps + +import re +import six + +__all__ = ["isoparse", "isoparser"] + + +def _takes_ascii(f): + @wraps(f) + def func(self, str_in, *args, **kwargs): + # If it's a stream, read the whole thing + str_in = getattr(str_in, 'read', lambda: str_in)() + + # If it's unicode, turn it into bytes, since ISO-8601 only covers ASCII + if isinstance(str_in, six.text_type): + # ASCII is the same in UTF-8 + try: + str_in = str_in.encode('ascii') + except UnicodeEncodeError as e: + msg = 'ISO-8601 strings should contain only ASCII characters' + six.raise_from(ValueError(msg), e) + + return f(self, str_in, *args, **kwargs) + + return func + + +class isoparser(object): + def __init__(self, sep=None): + """ + :param sep: + A single character that separates date and time portions. If + ``None``, the parser will accept any single character. + For strict ISO-8601 adherence, pass ``'T'``. + """ + if sep is not None: + if (len(sep) != 1 or ord(sep) >= 128 or sep in '0123456789'): + raise ValueError('Separator must be a single, non-numeric ' + + 'ASCII character') + + sep = sep.encode('ascii') + + self._sep = sep + + @_takes_ascii + def isoparse(self, dt_str): + """ + Parse an ISO-8601 datetime string into a :class:`datetime.datetime`. + + An ISO-8601 datetime string consists of a date portion, followed + optionally by a time portion - the date and time portions are separated + by a single character separator, which is ``T`` in the official + standard. Incomplete date formats (such as ``YYYY-MM``) may *not* be + combined with a time portion. + + Supported date formats are: + + Common: + + - ``YYYY`` + - ``YYYY-MM`` + - ``YYYY-MM-DD`` or ``YYYYMMDD`` + + Uncommon: + + - ``YYYY-Www`` or ``YYYYWww`` - ISO week (day defaults to 0) + - ``YYYY-Www-D`` or ``YYYYWwwD`` - ISO week and day + + The ISO week and day numbering follows the same logic as + :func:`datetime.date.isocalendar`. + + Supported time formats are: + + - ``hh`` + - ``hh:mm`` or ``hhmm`` + - ``hh:mm:ss`` or ``hhmmss`` + - ``hh:mm:ss.ssssss`` (Up to 6 sub-second digits) + + Midnight is a special case for `hh`, as the standard supports both + 00:00 and 24:00 as a representation. The decimal separator can be + either a dot or a comma. + + + .. caution:: + + Support for fractional components other than seconds is part of the + ISO-8601 standard, but is not currently implemented in this parser. + + Supported time zone offset formats are: + + - `Z` (UTC) + - `±HH:MM` + - `±HHMM` + - `±HH` + + Offsets will be represented as :class:`dateutil.tz.tzoffset` objects, + with the exception of UTC, which will be represented as + :class:`dateutil.tz.tzutc`. Time zone offsets equivalent to UTC (such + as `+00:00`) will also be represented as :class:`dateutil.tz.tzutc`. + + :param dt_str: + A string or stream containing only an ISO-8601 datetime string + + :return: + Returns a :class:`datetime.datetime` representing the string. + Unspecified components default to their lowest value. + + .. warning:: + + As of version 2.7.0, the strictness of the parser should not be + considered a stable part of the contract. Any valid ISO-8601 string + that parses correctly with the default settings will continue to + parse correctly in future versions, but invalid strings that + currently fail (e.g. ``2017-01-01T00:00+00:00:00``) are not + guaranteed to continue failing in future versions if they encode + a valid date. + + .. versionadded:: 2.7.0 + """ + components, pos = self._parse_isodate(dt_str) + + if len(dt_str) > pos: + if self._sep is None or dt_str[pos:pos + 1] == self._sep: + components += self._parse_isotime(dt_str[pos + 1:]) + else: + raise ValueError('String contains unknown ISO components') + + if len(components) > 3 and components[3] == 24: + components[3] = 0 + return datetime(*components) + timedelta(days=1) + + return datetime(*components) + + @_takes_ascii + def parse_isodate(self, datestr): + """ + Parse the date portion of an ISO string. + + :param datestr: + The string portion of an ISO string, without a separator + + :return: + Returns a :class:`datetime.date` object + """ + components, pos = self._parse_isodate(datestr) + if pos < len(datestr): + raise ValueError('String contains unknown ISO ' + + 'components: {!r}'.format(datestr.decode('ascii'))) + return date(*components) + + @_takes_ascii + def parse_isotime(self, timestr): + """ + Parse the time portion of an ISO string. + + :param timestr: + The time portion of an ISO string, without a separator + + :return: + Returns a :class:`datetime.time` object + """ + components = self._parse_isotime(timestr) + if components[0] == 24: + components[0] = 0 + return time(*components) + + @_takes_ascii + def parse_tzstr(self, tzstr, zero_as_utc=True): + """ + Parse a valid ISO time zone string. + + See :func:`isoparser.isoparse` for details on supported formats. + + :param tzstr: + A string representing an ISO time zone offset + + :param zero_as_utc: + Whether to return :class:`dateutil.tz.tzutc` for zero-offset zones + + :return: + Returns :class:`dateutil.tz.tzoffset` for offsets and + :class:`dateutil.tz.tzutc` for ``Z`` and (if ``zero_as_utc`` is + specified) offsets equivalent to UTC. + """ + return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc) + + # Constants + _DATE_SEP = b'-' + _TIME_SEP = b':' + _FRACTION_REGEX = re.compile(b'[\\.,]([0-9]+)') + + def _parse_isodate(self, dt_str): + try: + return self._parse_isodate_common(dt_str) + except ValueError: + return self._parse_isodate_uncommon(dt_str) + + def _parse_isodate_common(self, dt_str): + len_str = len(dt_str) + components = [1, 1, 1] + + if len_str < 4: + raise ValueError('ISO string too short') + + # Year + components[0] = int(dt_str[0:4]) + pos = 4 + if pos >= len_str: + return components, pos + + has_sep = dt_str[pos:pos + 1] == self._DATE_SEP + if has_sep: + pos += 1 + + # Month + if len_str - pos < 2: + raise ValueError('Invalid common month') + + components[1] = int(dt_str[pos:pos + 2]) + pos += 2 + + if pos >= len_str: + if has_sep: + return components, pos + else: + raise ValueError('Invalid ISO format') + + if has_sep: + if dt_str[pos:pos + 1] != self._DATE_SEP: + raise ValueError('Invalid separator in ISO string') + pos += 1 + + # Day + if len_str - pos < 2: + raise ValueError('Invalid common day') + components[2] = int(dt_str[pos:pos + 2]) + return components, pos + 2 + + def _parse_isodate_uncommon(self, dt_str): + if len(dt_str) < 4: + raise ValueError('ISO string too short') + + # All ISO formats start with the year + year = int(dt_str[0:4]) + + has_sep = dt_str[4:5] == self._DATE_SEP + + pos = 4 + has_sep # Skip '-' if it's there + if dt_str[pos:pos + 1] == b'W': + # YYYY-?Www-?D? + pos += 1 + weekno = int(dt_str[pos:pos + 2]) + pos += 2 + + dayno = 1 + if len(dt_str) > pos: + if (dt_str[pos:pos + 1] == self._DATE_SEP) != has_sep: + raise ValueError('Inconsistent use of dash separator') + + pos += has_sep + + dayno = int(dt_str[pos:pos + 1]) + pos += 1 + + base_date = self._calculate_weekdate(year, weekno, dayno) + else: + # YYYYDDD or YYYY-DDD + if len(dt_str) - pos < 3: + raise ValueError('Invalid ordinal day') + + ordinal_day = int(dt_str[pos:pos + 3]) + pos += 3 + + if ordinal_day < 1 or ordinal_day > (365 + calendar.isleap(year)): + raise ValueError('Invalid ordinal day' + + ' {} for year {}'.format(ordinal_day, year)) + + base_date = date(year, 1, 1) + timedelta(days=ordinal_day - 1) + + components = [base_date.year, base_date.month, base_date.day] + return components, pos + + def _calculate_weekdate(self, year, week, day): + """ + Calculate the day of corresponding to the ISO year-week-day calendar. + + This function is effectively the inverse of + :func:`datetime.date.isocalendar`. + + :param year: + The year in the ISO calendar + + :param week: + The week in the ISO calendar - range is [1, 53] + + :param day: + The day in the ISO calendar - range is [1 (MON), 7 (SUN)] + + :return: + Returns a :class:`datetime.date` + """ + if not 0 < week < 54: + raise ValueError('Invalid week: {}'.format(week)) + + if not 0 < day < 8: # Range is 1-7 + raise ValueError('Invalid weekday: {}'.format(day)) + + # Get week 1 for the specific year: + jan_4 = date(year, 1, 4) # Week 1 always has January 4th in it + week_1 = jan_4 - timedelta(days=jan_4.isocalendar()[2] - 1) + + # Now add the specific number of weeks and days to get what we want + week_offset = (week - 1) * 7 + (day - 1) + return week_1 + timedelta(days=week_offset) + + def _parse_isotime(self, timestr): + len_str = len(timestr) + components = [0, 0, 0, 0, None] + pos = 0 + comp = -1 + + if len_str < 2: + raise ValueError('ISO time too short') + + has_sep = False + + while pos < len_str and comp < 5: + comp += 1 + + if timestr[pos:pos + 1] in b'-+Zz': + # Detect time zone boundary + components[-1] = self._parse_tzstr(timestr[pos:]) + pos = len_str + break + + if comp == 1 and timestr[pos:pos+1] == self._TIME_SEP: + has_sep = True + pos += 1 + elif comp == 2 and has_sep: + if timestr[pos:pos+1] != self._TIME_SEP: + raise ValueError('Inconsistent use of colon separator') + pos += 1 + + if comp < 3: + # Hour, minute, second + components[comp] = int(timestr[pos:pos + 2]) + pos += 2 + + if comp == 3: + # Fraction of a second + frac = self._FRACTION_REGEX.match(timestr[pos:]) + if not frac: + continue + + us_str = frac.group(1)[:6] # Truncate to microseconds + components[comp] = int(us_str) * 10**(6 - len(us_str)) + pos += len(frac.group()) + + if pos < len_str: + raise ValueError('Unused components in ISO string') + + if components[0] == 24: + # Standard supports 00:00 and 24:00 as representations of midnight + if any(component != 0 for component in components[1:4]): + raise ValueError('Hour may only be 24 at 24:00:00.000') + + return components + + def _parse_tzstr(self, tzstr, zero_as_utc=True): + if tzstr == b'Z' or tzstr == b'z': + return tz.UTC + + if len(tzstr) not in {3, 5, 6}: + raise ValueError('Time zone offset must be 1, 3, 5 or 6 characters') + + if tzstr[0:1] == b'-': + mult = -1 + elif tzstr[0:1] == b'+': + mult = 1 + else: + raise ValueError('Time zone offset requires sign') + + hours = int(tzstr[1:3]) + if len(tzstr) == 3: + minutes = 0 + else: + minutes = int(tzstr[(4 if tzstr[3:4] == self._TIME_SEP else 3):]) + + if zero_as_utc and hours == 0 and minutes == 0: + return tz.UTC + else: + if minutes > 59: + raise ValueError('Invalid minutes in time zone offset') + + if hours > 23: + raise ValueError('Invalid hours in time zone offset') + + return tz.tzoffset(None, mult * (hours * 60 + minutes) * 60) + + +DEFAULT_ISOPARSER = isoparser() +isoparse = DEFAULT_ISOPARSER.isoparse diff --git a/.venv/lib/python3.9/site-packages/dateutil/relativedelta.py b/.venv/lib/python3.9/site-packages/dateutil/relativedelta.py new file mode 100644 index 00000000..cd323a54 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/relativedelta.py @@ -0,0 +1,599 @@ +# -*- coding: utf-8 -*- +import datetime +import calendar + +import operator +from math import copysign + +from six import integer_types +from warnings import warn + +from ._common import weekday + +MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7)) + +__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"] + + +class relativedelta(object): + """ + The relativedelta type is designed to be applied to an existing datetime and + can replace specific components of that datetime, or represents an interval + of time. + + It is based on the specification of the excellent work done by M.-A. Lemburg + in his + `mx.DateTime `_ extension. + However, notice that this type does *NOT* implement the same algorithm as + his work. Do *NOT* expect it to behave like mx.DateTime's counterpart. + + There are two different ways to build a relativedelta instance. The + first one is passing it two date/datetime classes:: + + relativedelta(datetime1, datetime2) + + The second one is passing it any number of the following keyword arguments:: + + relativedelta(arg1=x,arg2=y,arg3=z...) + + year, month, day, hour, minute, second, microsecond: + Absolute information (argument is singular); adding or subtracting a + relativedelta with absolute information does not perform an arithmetic + operation, but rather REPLACES the corresponding value in the + original datetime with the value(s) in relativedelta. + + years, months, weeks, days, hours, minutes, seconds, microseconds: + Relative information, may be negative (argument is plural); adding + or subtracting a relativedelta with relative information performs + the corresponding arithmetic operation on the original datetime value + with the information in the relativedelta. + + weekday: + One of the weekday instances (MO, TU, etc) available in the + relativedelta module. These instances may receive a parameter N, + specifying the Nth weekday, which could be positive or negative + (like MO(+1) or MO(-2)). Not specifying it is the same as specifying + +1. You can also use an integer, where 0=MO. This argument is always + relative e.g. if the calculated date is already Monday, using MO(1) + or MO(-1) won't change the day. To effectively make it absolute, use + it in combination with the day argument (e.g. day=1, MO(1) for first + Monday of the month). + + leapdays: + Will add given days to the date found, if year is a leap + year, and the date found is post 28 of february. + + yearday, nlyearday: + Set the yearday or the non-leap year day (jump leap days). + These are converted to day/month/leapdays information. + + There are relative and absolute forms of the keyword + arguments. The plural is relative, and the singular is + absolute. For each argument in the order below, the absolute form + is applied first (by setting each attribute to that value) and + then the relative form (by adding the value to the attribute). + + The order of attributes considered when this relativedelta is + added to a datetime is: + + 1. Year + 2. Month + 3. Day + 4. Hours + 5. Minutes + 6. Seconds + 7. Microseconds + + Finally, weekday is applied, using the rule described above. + + For example + + >>> from datetime import datetime + >>> from dateutil.relativedelta import relativedelta, MO + >>> dt = datetime(2018, 4, 9, 13, 37, 0) + >>> delta = relativedelta(hours=25, day=1, weekday=MO(1)) + >>> dt + delta + datetime.datetime(2018, 4, 2, 14, 37) + + First, the day is set to 1 (the first of the month), then 25 hours + are added, to get to the 2nd day and 14th hour, finally the + weekday is applied, but since the 2nd is already a Monday there is + no effect. + + """ + + def __init__(self, dt1=None, dt2=None, + years=0, months=0, days=0, leapdays=0, weeks=0, + hours=0, minutes=0, seconds=0, microseconds=0, + year=None, month=None, day=None, weekday=None, + yearday=None, nlyearday=None, + hour=None, minute=None, second=None, microsecond=None): + + if dt1 and dt2: + # datetime is a subclass of date. So both must be date + if not (isinstance(dt1, datetime.date) and + isinstance(dt2, datetime.date)): + raise TypeError("relativedelta only diffs datetime/date") + + # We allow two dates, or two datetimes, so we coerce them to be + # of the same type + if (isinstance(dt1, datetime.datetime) != + isinstance(dt2, datetime.datetime)): + if not isinstance(dt1, datetime.datetime): + dt1 = datetime.datetime.fromordinal(dt1.toordinal()) + elif not isinstance(dt2, datetime.datetime): + dt2 = datetime.datetime.fromordinal(dt2.toordinal()) + + self.years = 0 + self.months = 0 + self.days = 0 + self.leapdays = 0 + self.hours = 0 + self.minutes = 0 + self.seconds = 0 + self.microseconds = 0 + self.year = None + self.month = None + self.day = None + self.weekday = None + self.hour = None + self.minute = None + self.second = None + self.microsecond = None + self._has_time = 0 + + # Get year / month delta between the two + months = (dt1.year - dt2.year) * 12 + (dt1.month - dt2.month) + self._set_months(months) + + # Remove the year/month delta so the timedelta is just well-defined + # time units (seconds, days and microseconds) + dtm = self.__radd__(dt2) + + # If we've overshot our target, make an adjustment + if dt1 < dt2: + compare = operator.gt + increment = 1 + else: + compare = operator.lt + increment = -1 + + while compare(dt1, dtm): + months += increment + self._set_months(months) + dtm = self.__radd__(dt2) + + # Get the timedelta between the "months-adjusted" date and dt1 + delta = dt1 - dtm + self.seconds = delta.seconds + delta.days * 86400 + self.microseconds = delta.microseconds + else: + # Check for non-integer values in integer-only quantities + if any(x is not None and x != int(x) for x in (years, months)): + raise ValueError("Non-integer years and months are " + "ambiguous and not currently supported.") + + # Relative information + self.years = int(years) + self.months = int(months) + self.days = days + weeks * 7 + self.leapdays = leapdays + self.hours = hours + self.minutes = minutes + self.seconds = seconds + self.microseconds = microseconds + + # Absolute information + self.year = year + self.month = month + self.day = day + self.hour = hour + self.minute = minute + self.second = second + self.microsecond = microsecond + + if any(x is not None and int(x) != x + for x in (year, month, day, hour, + minute, second, microsecond)): + # For now we'll deprecate floats - later it'll be an error. + warn("Non-integer value passed as absolute information. " + + "This is not a well-defined condition and will raise " + + "errors in future versions.", DeprecationWarning) + + if isinstance(weekday, integer_types): + self.weekday = weekdays[weekday] + else: + self.weekday = weekday + + yday = 0 + if nlyearday: + yday = nlyearday + elif yearday: + yday = yearday + if yearday > 59: + self.leapdays = -1 + if yday: + ydayidx = [31, 59, 90, 120, 151, 181, 212, + 243, 273, 304, 334, 366] + for idx, ydays in enumerate(ydayidx): + if yday <= ydays: + self.month = idx+1 + if idx == 0: + self.day = yday + else: + self.day = yday-ydayidx[idx-1] + break + else: + raise ValueError("invalid year day (%d)" % yday) + + self._fix() + + def _fix(self): + if abs(self.microseconds) > 999999: + s = _sign(self.microseconds) + div, mod = divmod(self.microseconds * s, 1000000) + self.microseconds = mod * s + self.seconds += div * s + if abs(self.seconds) > 59: + s = _sign(self.seconds) + div, mod = divmod(self.seconds * s, 60) + self.seconds = mod * s + self.minutes += div * s + if abs(self.minutes) > 59: + s = _sign(self.minutes) + div, mod = divmod(self.minutes * s, 60) + self.minutes = mod * s + self.hours += div * s + if abs(self.hours) > 23: + s = _sign(self.hours) + div, mod = divmod(self.hours * s, 24) + self.hours = mod * s + self.days += div * s + if abs(self.months) > 11: + s = _sign(self.months) + div, mod = divmod(self.months * s, 12) + self.months = mod * s + self.years += div * s + if (self.hours or self.minutes or self.seconds or self.microseconds + or self.hour is not None or self.minute is not None or + self.second is not None or self.microsecond is not None): + self._has_time = 1 + else: + self._has_time = 0 + + @property + def weeks(self): + return int(self.days / 7.0) + + @weeks.setter + def weeks(self, value): + self.days = self.days - (self.weeks * 7) + value * 7 + + def _set_months(self, months): + self.months = months + if abs(self.months) > 11: + s = _sign(self.months) + div, mod = divmod(self.months * s, 12) + self.months = mod * s + self.years = div * s + else: + self.years = 0 + + def normalized(self): + """ + Return a version of this object represented entirely using integer + values for the relative attributes. + + >>> relativedelta(days=1.5, hours=2).normalized() + relativedelta(days=+1, hours=+14) + + :return: + Returns a :class:`dateutil.relativedelta.relativedelta` object. + """ + # Cascade remainders down (rounding each to roughly nearest microsecond) + days = int(self.days) + + hours_f = round(self.hours + 24 * (self.days - days), 11) + hours = int(hours_f) + + minutes_f = round(self.minutes + 60 * (hours_f - hours), 10) + minutes = int(minutes_f) + + seconds_f = round(self.seconds + 60 * (minutes_f - minutes), 8) + seconds = int(seconds_f) + + microseconds = round(self.microseconds + 1e6 * (seconds_f - seconds)) + + # Constructor carries overflow back up with call to _fix() + return self.__class__(years=self.years, months=self.months, + days=days, hours=hours, minutes=minutes, + seconds=seconds, microseconds=microseconds, + leapdays=self.leapdays, year=self.year, + month=self.month, day=self.day, + weekday=self.weekday, hour=self.hour, + minute=self.minute, second=self.second, + microsecond=self.microsecond) + + def __add__(self, other): + if isinstance(other, relativedelta): + return self.__class__(years=other.years + self.years, + months=other.months + self.months, + days=other.days + self.days, + hours=other.hours + self.hours, + minutes=other.minutes + self.minutes, + seconds=other.seconds + self.seconds, + microseconds=(other.microseconds + + self.microseconds), + leapdays=other.leapdays or self.leapdays, + year=(other.year if other.year is not None + else self.year), + month=(other.month if other.month is not None + else self.month), + day=(other.day if other.day is not None + else self.day), + weekday=(other.weekday if other.weekday is not None + else self.weekday), + hour=(other.hour if other.hour is not None + else self.hour), + minute=(other.minute if other.minute is not None + else self.minute), + second=(other.second if other.second is not None + else self.second), + microsecond=(other.microsecond if other.microsecond + is not None else + self.microsecond)) + if isinstance(other, datetime.timedelta): + return self.__class__(years=self.years, + months=self.months, + days=self.days + other.days, + hours=self.hours, + minutes=self.minutes, + seconds=self.seconds + other.seconds, + microseconds=self.microseconds + other.microseconds, + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + if not isinstance(other, datetime.date): + return NotImplemented + elif self._has_time and not isinstance(other, datetime.datetime): + other = datetime.datetime.fromordinal(other.toordinal()) + year = (self.year or other.year)+self.years + month = self.month or other.month + if self.months: + assert 1 <= abs(self.months) <= 12 + month += self.months + if month > 12: + year += 1 + month -= 12 + elif month < 1: + year -= 1 + month += 12 + day = min(calendar.monthrange(year, month)[1], + self.day or other.day) + repl = {"year": year, "month": month, "day": day} + for attr in ["hour", "minute", "second", "microsecond"]: + value = getattr(self, attr) + if value is not None: + repl[attr] = value + days = self.days + if self.leapdays and month > 2 and calendar.isleap(year): + days += self.leapdays + ret = (other.replace(**repl) + + datetime.timedelta(days=days, + hours=self.hours, + minutes=self.minutes, + seconds=self.seconds, + microseconds=self.microseconds)) + if self.weekday: + weekday, nth = self.weekday.weekday, self.weekday.n or 1 + jumpdays = (abs(nth) - 1) * 7 + if nth > 0: + jumpdays += (7 - ret.weekday() + weekday) % 7 + else: + jumpdays += (ret.weekday() - weekday) % 7 + jumpdays *= -1 + ret += datetime.timedelta(days=jumpdays) + return ret + + def __radd__(self, other): + return self.__add__(other) + + def __rsub__(self, other): + return self.__neg__().__radd__(other) + + def __sub__(self, other): + if not isinstance(other, relativedelta): + return NotImplemented # In case the other object defines __rsub__ + return self.__class__(years=self.years - other.years, + months=self.months - other.months, + days=self.days - other.days, + hours=self.hours - other.hours, + minutes=self.minutes - other.minutes, + seconds=self.seconds - other.seconds, + microseconds=self.microseconds - other.microseconds, + leapdays=self.leapdays or other.leapdays, + year=(self.year if self.year is not None + else other.year), + month=(self.month if self.month is not None else + other.month), + day=(self.day if self.day is not None else + other.day), + weekday=(self.weekday if self.weekday is not None else + other.weekday), + hour=(self.hour if self.hour is not None else + other.hour), + minute=(self.minute if self.minute is not None else + other.minute), + second=(self.second if self.second is not None else + other.second), + microsecond=(self.microsecond if self.microsecond + is not None else + other.microsecond)) + + def __abs__(self): + return self.__class__(years=abs(self.years), + months=abs(self.months), + days=abs(self.days), + hours=abs(self.hours), + minutes=abs(self.minutes), + seconds=abs(self.seconds), + microseconds=abs(self.microseconds), + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + + def __neg__(self): + return self.__class__(years=-self.years, + months=-self.months, + days=-self.days, + hours=-self.hours, + minutes=-self.minutes, + seconds=-self.seconds, + microseconds=-self.microseconds, + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + + def __bool__(self): + return not (not self.years and + not self.months and + not self.days and + not self.hours and + not self.minutes and + not self.seconds and + not self.microseconds and + not self.leapdays and + self.year is None and + self.month is None and + self.day is None and + self.weekday is None and + self.hour is None and + self.minute is None and + self.second is None and + self.microsecond is None) + # Compatibility with Python 2.x + __nonzero__ = __bool__ + + def __mul__(self, other): + try: + f = float(other) + except TypeError: + return NotImplemented + + return self.__class__(years=int(self.years * f), + months=int(self.months * f), + days=int(self.days * f), + hours=int(self.hours * f), + minutes=int(self.minutes * f), + seconds=int(self.seconds * f), + microseconds=int(self.microseconds * f), + leapdays=self.leapdays, + year=self.year, + month=self.month, + day=self.day, + weekday=self.weekday, + hour=self.hour, + minute=self.minute, + second=self.second, + microsecond=self.microsecond) + + __rmul__ = __mul__ + + def __eq__(self, other): + if not isinstance(other, relativedelta): + return NotImplemented + if self.weekday or other.weekday: + if not self.weekday or not other.weekday: + return False + if self.weekday.weekday != other.weekday.weekday: + return False + n1, n2 = self.weekday.n, other.weekday.n + if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)): + return False + return (self.years == other.years and + self.months == other.months and + self.days == other.days and + self.hours == other.hours and + self.minutes == other.minutes and + self.seconds == other.seconds and + self.microseconds == other.microseconds and + self.leapdays == other.leapdays and + self.year == other.year and + self.month == other.month and + self.day == other.day and + self.hour == other.hour and + self.minute == other.minute and + self.second == other.second and + self.microsecond == other.microsecond) + + def __hash__(self): + return hash(( + self.weekday, + self.years, + self.months, + self.days, + self.hours, + self.minutes, + self.seconds, + self.microseconds, + self.leapdays, + self.year, + self.month, + self.day, + self.hour, + self.minute, + self.second, + self.microsecond, + )) + + def __ne__(self, other): + return not self.__eq__(other) + + def __div__(self, other): + try: + reciprocal = 1 / float(other) + except TypeError: + return NotImplemented + + return self.__mul__(reciprocal) + + __truediv__ = __div__ + + def __repr__(self): + l = [] + for attr in ["years", "months", "days", "leapdays", + "hours", "minutes", "seconds", "microseconds"]: + value = getattr(self, attr) + if value: + l.append("{attr}={value:+g}".format(attr=attr, value=value)) + for attr in ["year", "month", "day", "weekday", + "hour", "minute", "second", "microsecond"]: + value = getattr(self, attr) + if value is not None: + l.append("{attr}={value}".format(attr=attr, value=repr(value))) + return "{classname}({attrs})".format(classname=self.__class__.__name__, + attrs=", ".join(l)) + + +def _sign(x): + return int(copysign(1, x)) + +# vim:ts=4:sw=4:et diff --git a/.venv/lib/python3.9/site-packages/dateutil/rrule.py b/.venv/lib/python3.9/site-packages/dateutil/rrule.py new file mode 100644 index 00000000..571a0d2b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/rrule.py @@ -0,0 +1,1737 @@ +# -*- coding: utf-8 -*- +""" +The rrule module offers a small, complete, and very fast, implementation of +the recurrence rules documented in the +`iCalendar RFC `_, +including support for caching of results. +""" +import calendar +import datetime +import heapq +import itertools +import re +import sys +from functools import wraps +# For warning about deprecation of until and count +from warnings import warn + +from six import advance_iterator, integer_types + +from six.moves import _thread, range + +from ._common import weekday as weekdaybase + +try: + from math import gcd +except ImportError: + from fractions import gcd + +__all__ = ["rrule", "rruleset", "rrulestr", + "YEARLY", "MONTHLY", "WEEKLY", "DAILY", + "HOURLY", "MINUTELY", "SECONDLY", + "MO", "TU", "WE", "TH", "FR", "SA", "SU"] + +# Every mask is 7 days longer to handle cross-year weekly periods. +M366MASK = tuple([1]*31+[2]*29+[3]*31+[4]*30+[5]*31+[6]*30 + + [7]*31+[8]*31+[9]*30+[10]*31+[11]*30+[12]*31+[1]*7) +M365MASK = list(M366MASK) +M29, M30, M31 = list(range(1, 30)), list(range(1, 31)), list(range(1, 32)) +MDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) +MDAY365MASK = list(MDAY366MASK) +M29, M30, M31 = list(range(-29, 0)), list(range(-30, 0)), list(range(-31, 0)) +NMDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) +NMDAY365MASK = list(NMDAY366MASK) +M366RANGE = (0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366) +M365RANGE = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365) +WDAYMASK = [0, 1, 2, 3, 4, 5, 6]*55 +del M29, M30, M31, M365MASK[59], MDAY365MASK[59], NMDAY365MASK[31] +MDAY365MASK = tuple(MDAY365MASK) +M365MASK = tuple(M365MASK) + +FREQNAMES = ['YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY', 'HOURLY', 'MINUTELY', 'SECONDLY'] + +(YEARLY, + MONTHLY, + WEEKLY, + DAILY, + HOURLY, + MINUTELY, + SECONDLY) = list(range(7)) + +# Imported on demand. +easter = None +parser = None + + +class weekday(weekdaybase): + """ + This version of weekday does not allow n = 0. + """ + def __init__(self, wkday, n=None): + if n == 0: + raise ValueError("Can't create weekday with n==0") + + super(weekday, self).__init__(wkday, n) + + +MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7)) + + +def _invalidates_cache(f): + """ + Decorator for rruleset methods which may invalidate the + cached length. + """ + @wraps(f) + def inner_func(self, *args, **kwargs): + rv = f(self, *args, **kwargs) + self._invalidate_cache() + return rv + + return inner_func + + +class rrulebase(object): + def __init__(self, cache=False): + if cache: + self._cache = [] + self._cache_lock = _thread.allocate_lock() + self._invalidate_cache() + else: + self._cache = None + self._cache_complete = False + self._len = None + + def __iter__(self): + if self._cache_complete: + return iter(self._cache) + elif self._cache is None: + return self._iter() + else: + return self._iter_cached() + + def _invalidate_cache(self): + if self._cache is not None: + self._cache = [] + self._cache_complete = False + self._cache_gen = self._iter() + + if self._cache_lock.locked(): + self._cache_lock.release() + + self._len = None + + def _iter_cached(self): + i = 0 + gen = self._cache_gen + cache = self._cache + acquire = self._cache_lock.acquire + release = self._cache_lock.release + while gen: + if i == len(cache): + acquire() + if self._cache_complete: + break + try: + for j in range(10): + cache.append(advance_iterator(gen)) + except StopIteration: + self._cache_gen = gen = None + self._cache_complete = True + break + release() + yield cache[i] + i += 1 + while i < self._len: + yield cache[i] + i += 1 + + def __getitem__(self, item): + if self._cache_complete: + return self._cache[item] + elif isinstance(item, slice): + if item.step and item.step < 0: + return list(iter(self))[item] + else: + return list(itertools.islice(self, + item.start or 0, + item.stop or sys.maxsize, + item.step or 1)) + elif item >= 0: + gen = iter(self) + try: + for i in range(item+1): + res = advance_iterator(gen) + except StopIteration: + raise IndexError + return res + else: + return list(iter(self))[item] + + def __contains__(self, item): + if self._cache_complete: + return item in self._cache + else: + for i in self: + if i == item: + return True + elif i > item: + return False + return False + + # __len__() introduces a large performance penalty. + def count(self): + """ Returns the number of recurrences in this set. It will have go + through the whole recurrence, if this hasn't been done before. """ + if self._len is None: + for x in self: + pass + return self._len + + def before(self, dt, inc=False): + """ Returns the last recurrence before the given datetime instance. The + inc keyword defines what happens if dt is an occurrence. With + inc=True, if dt itself is an occurrence, it will be returned. """ + if self._cache_complete: + gen = self._cache + else: + gen = self + last = None + if inc: + for i in gen: + if i > dt: + break + last = i + else: + for i in gen: + if i >= dt: + break + last = i + return last + + def after(self, dt, inc=False): + """ Returns the first recurrence after the given datetime instance. The + inc keyword defines what happens if dt is an occurrence. With + inc=True, if dt itself is an occurrence, it will be returned. """ + if self._cache_complete: + gen = self._cache + else: + gen = self + if inc: + for i in gen: + if i >= dt: + return i + else: + for i in gen: + if i > dt: + return i + return None + + def xafter(self, dt, count=None, inc=False): + """ + Generator which yields up to `count` recurrences after the given + datetime instance, equivalent to `after`. + + :param dt: + The datetime at which to start generating recurrences. + + :param count: + The maximum number of recurrences to generate. If `None` (default), + dates are generated until the recurrence rule is exhausted. + + :param inc: + If `dt` is an instance of the rule and `inc` is `True`, it is + included in the output. + + :yields: Yields a sequence of `datetime` objects. + """ + + if self._cache_complete: + gen = self._cache + else: + gen = self + + # Select the comparison function + if inc: + comp = lambda dc, dtc: dc >= dtc + else: + comp = lambda dc, dtc: dc > dtc + + # Generate dates + n = 0 + for d in gen: + if comp(d, dt): + if count is not None: + n += 1 + if n > count: + break + + yield d + + def between(self, after, before, inc=False, count=1): + """ Returns all the occurrences of the rrule between after and before. + The inc keyword defines what happens if after and/or before are + themselves occurrences. With inc=True, they will be included in the + list, if they are found in the recurrence set. """ + if self._cache_complete: + gen = self._cache + else: + gen = self + started = False + l = [] + if inc: + for i in gen: + if i > before: + break + elif not started: + if i >= after: + started = True + l.append(i) + else: + l.append(i) + else: + for i in gen: + if i >= before: + break + elif not started: + if i > after: + started = True + l.append(i) + else: + l.append(i) + return l + + +class rrule(rrulebase): + """ + That's the base of the rrule operation. It accepts all the keywords + defined in the RFC as its constructor parameters (except byday, + which was renamed to byweekday) and more. The constructor prototype is:: + + rrule(freq) + + Where freq must be one of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, + or SECONDLY. + + .. note:: + Per RFC section 3.3.10, recurrence instances falling on invalid dates + and times are ignored rather than coerced: + + Recurrence rules may generate recurrence instances with an invalid + date (e.g., February 30) or nonexistent local time (e.g., 1:30 AM + on a day where the local time is moved forward by an hour at 1:00 + AM). Such recurrence instances MUST be ignored and MUST NOT be + counted as part of the recurrence set. + + This can lead to possibly surprising behavior when, for example, the + start date occurs at the end of the month: + + >>> from dateutil.rrule import rrule, MONTHLY + >>> from datetime import datetime + >>> start_date = datetime(2014, 12, 31) + >>> list(rrule(freq=MONTHLY, count=4, dtstart=start_date)) + ... # doctest: +NORMALIZE_WHITESPACE + [datetime.datetime(2014, 12, 31, 0, 0), + datetime.datetime(2015, 1, 31, 0, 0), + datetime.datetime(2015, 3, 31, 0, 0), + datetime.datetime(2015, 5, 31, 0, 0)] + + Additionally, it supports the following keyword arguments: + + :param dtstart: + The recurrence start. Besides being the base for the recurrence, + missing parameters in the final recurrence instances will also be + extracted from this date. If not given, datetime.now() will be used + instead. + :param interval: + The interval between each freq iteration. For example, when using + YEARLY, an interval of 2 means once every two years, but with HOURLY, + it means once every two hours. The default interval is 1. + :param wkst: + The week start day. Must be one of the MO, TU, WE constants, or an + integer, specifying the first day of the week. This will affect + recurrences based on weekly periods. The default week start is got + from calendar.firstweekday(), and may be modified by + calendar.setfirstweekday(). + :param count: + If given, this determines how many occurrences will be generated. + + .. note:: + As of version 2.5.0, the use of the keyword ``until`` in conjunction + with ``count`` is deprecated, to make sure ``dateutil`` is fully + compliant with `RFC-5545 Sec. 3.3.10 `_. Therefore, ``until`` and ``count`` + **must not** occur in the same call to ``rrule``. + :param until: + If given, this must be a datetime instance specifying the upper-bound + limit of the recurrence. The last recurrence in the rule is the greatest + datetime that is less than or equal to the value specified in the + ``until`` parameter. + + .. note:: + As of version 2.5.0, the use of the keyword ``until`` in conjunction + with ``count`` is deprecated, to make sure ``dateutil`` is fully + compliant with `RFC-5545 Sec. 3.3.10 `_. Therefore, ``until`` and ``count`` + **must not** occur in the same call to ``rrule``. + :param bysetpos: + If given, it must be either an integer, or a sequence of integers, + positive or negative. Each given integer will specify an occurrence + number, corresponding to the nth occurrence of the rule inside the + frequency period. For example, a bysetpos of -1 if combined with a + MONTHLY frequency, and a byweekday of (MO, TU, WE, TH, FR), will + result in the last work day of every month. + :param bymonth: + If given, it must be either an integer, or a sequence of integers, + meaning the months to apply the recurrence to. + :param bymonthday: + If given, it must be either an integer, or a sequence of integers, + meaning the month days to apply the recurrence to. + :param byyearday: + If given, it must be either an integer, or a sequence of integers, + meaning the year days to apply the recurrence to. + :param byeaster: + If given, it must be either an integer, or a sequence of integers, + positive or negative. Each integer will define an offset from the + Easter Sunday. Passing the offset 0 to byeaster will yield the Easter + Sunday itself. This is an extension to the RFC specification. + :param byweekno: + If given, it must be either an integer, or a sequence of integers, + meaning the week numbers to apply the recurrence to. Week numbers + have the meaning described in ISO8601, that is, the first week of + the year is that containing at least four days of the new year. + :param byweekday: + If given, it must be either an integer (0 == MO), a sequence of + integers, one of the weekday constants (MO, TU, etc), or a sequence + of these constants. When given, these variables will define the + weekdays where the recurrence will be applied. It's also possible to + use an argument n for the weekday instances, which will mean the nth + occurrence of this weekday in the period. For example, with MONTHLY, + or with YEARLY and BYMONTH, using FR(+1) in byweekday will specify the + first friday of the month where the recurrence happens. Notice that in + the RFC documentation, this is specified as BYDAY, but was renamed to + avoid the ambiguity of that keyword. + :param byhour: + If given, it must be either an integer, or a sequence of integers, + meaning the hours to apply the recurrence to. + :param byminute: + If given, it must be either an integer, or a sequence of integers, + meaning the minutes to apply the recurrence to. + :param bysecond: + If given, it must be either an integer, or a sequence of integers, + meaning the seconds to apply the recurrence to. + :param cache: + If given, it must be a boolean value specifying to enable or disable + caching of results. If you will use the same rrule instance multiple + times, enabling caching will improve the performance considerably. + """ + def __init__(self, freq, dtstart=None, + interval=1, wkst=None, count=None, until=None, bysetpos=None, + bymonth=None, bymonthday=None, byyearday=None, byeaster=None, + byweekno=None, byweekday=None, + byhour=None, byminute=None, bysecond=None, + cache=False): + super(rrule, self).__init__(cache) + global easter + if not dtstart: + if until and until.tzinfo: + dtstart = datetime.datetime.now(tz=until.tzinfo).replace(microsecond=0) + else: + dtstart = datetime.datetime.now().replace(microsecond=0) + elif not isinstance(dtstart, datetime.datetime): + dtstart = datetime.datetime.fromordinal(dtstart.toordinal()) + else: + dtstart = dtstart.replace(microsecond=0) + self._dtstart = dtstart + self._tzinfo = dtstart.tzinfo + self._freq = freq + self._interval = interval + self._count = count + + # Cache the original byxxx rules, if they are provided, as the _byxxx + # attributes do not necessarily map to the inputs, and this can be + # a problem in generating the strings. Only store things if they've + # been supplied (the string retrieval will just use .get()) + self._original_rule = {} + + if until and not isinstance(until, datetime.datetime): + until = datetime.datetime.fromordinal(until.toordinal()) + self._until = until + + if self._dtstart and self._until: + if (self._dtstart.tzinfo is not None) != (self._until.tzinfo is not None): + # According to RFC5545 Section 3.3.10: + # https://tools.ietf.org/html/rfc5545#section-3.3.10 + # + # > If the "DTSTART" property is specified as a date with UTC + # > time or a date with local time and time zone reference, + # > then the UNTIL rule part MUST be specified as a date with + # > UTC time. + raise ValueError( + 'RRULE UNTIL values must be specified in UTC when DTSTART ' + 'is timezone-aware' + ) + + if count is not None and until: + warn("Using both 'count' and 'until' is inconsistent with RFC 5545" + " and has been deprecated in dateutil. Future versions will " + "raise an error.", DeprecationWarning) + + if wkst is None: + self._wkst = calendar.firstweekday() + elif isinstance(wkst, integer_types): + self._wkst = wkst + else: + self._wkst = wkst.weekday + + if bysetpos is None: + self._bysetpos = None + elif isinstance(bysetpos, integer_types): + if bysetpos == 0 or not (-366 <= bysetpos <= 366): + raise ValueError("bysetpos must be between 1 and 366, " + "or between -366 and -1") + self._bysetpos = (bysetpos,) + else: + self._bysetpos = tuple(bysetpos) + for pos in self._bysetpos: + if pos == 0 or not (-366 <= pos <= 366): + raise ValueError("bysetpos must be between 1 and 366, " + "or between -366 and -1") + + if self._bysetpos: + self._original_rule['bysetpos'] = self._bysetpos + + if (byweekno is None and byyearday is None and bymonthday is None and + byweekday is None and byeaster is None): + if freq == YEARLY: + if bymonth is None: + bymonth = dtstart.month + self._original_rule['bymonth'] = None + bymonthday = dtstart.day + self._original_rule['bymonthday'] = None + elif freq == MONTHLY: + bymonthday = dtstart.day + self._original_rule['bymonthday'] = None + elif freq == WEEKLY: + byweekday = dtstart.weekday() + self._original_rule['byweekday'] = None + + # bymonth + if bymonth is None: + self._bymonth = None + else: + if isinstance(bymonth, integer_types): + bymonth = (bymonth,) + + self._bymonth = tuple(sorted(set(bymonth))) + + if 'bymonth' not in self._original_rule: + self._original_rule['bymonth'] = self._bymonth + + # byyearday + if byyearday is None: + self._byyearday = None + else: + if isinstance(byyearday, integer_types): + byyearday = (byyearday,) + + self._byyearday = tuple(sorted(set(byyearday))) + self._original_rule['byyearday'] = self._byyearday + + # byeaster + if byeaster is not None: + if not easter: + from dateutil import easter + if isinstance(byeaster, integer_types): + self._byeaster = (byeaster,) + else: + self._byeaster = tuple(sorted(byeaster)) + + self._original_rule['byeaster'] = self._byeaster + else: + self._byeaster = None + + # bymonthday + if bymonthday is None: + self._bymonthday = () + self._bynmonthday = () + else: + if isinstance(bymonthday, integer_types): + bymonthday = (bymonthday,) + + bymonthday = set(bymonthday) # Ensure it's unique + + self._bymonthday = tuple(sorted(x for x in bymonthday if x > 0)) + self._bynmonthday = tuple(sorted(x for x in bymonthday if x < 0)) + + # Storing positive numbers first, then negative numbers + if 'bymonthday' not in self._original_rule: + self._original_rule['bymonthday'] = tuple( + itertools.chain(self._bymonthday, self._bynmonthday)) + + # byweekno + if byweekno is None: + self._byweekno = None + else: + if isinstance(byweekno, integer_types): + byweekno = (byweekno,) + + self._byweekno = tuple(sorted(set(byweekno))) + + self._original_rule['byweekno'] = self._byweekno + + # byweekday / bynweekday + if byweekday is None: + self._byweekday = None + self._bynweekday = None + else: + # If it's one of the valid non-sequence types, convert to a + # single-element sequence before the iterator that builds the + # byweekday set. + if isinstance(byweekday, integer_types) or hasattr(byweekday, "n"): + byweekday = (byweekday,) + + self._byweekday = set() + self._bynweekday = set() + for wday in byweekday: + if isinstance(wday, integer_types): + self._byweekday.add(wday) + elif not wday.n or freq > MONTHLY: + self._byweekday.add(wday.weekday) + else: + self._bynweekday.add((wday.weekday, wday.n)) + + if not self._byweekday: + self._byweekday = None + elif not self._bynweekday: + self._bynweekday = None + + if self._byweekday is not None: + self._byweekday = tuple(sorted(self._byweekday)) + orig_byweekday = [weekday(x) for x in self._byweekday] + else: + orig_byweekday = () + + if self._bynweekday is not None: + self._bynweekday = tuple(sorted(self._bynweekday)) + orig_bynweekday = [weekday(*x) for x in self._bynweekday] + else: + orig_bynweekday = () + + if 'byweekday' not in self._original_rule: + self._original_rule['byweekday'] = tuple(itertools.chain( + orig_byweekday, orig_bynweekday)) + + # byhour + if byhour is None: + if freq < HOURLY: + self._byhour = {dtstart.hour} + else: + self._byhour = None + else: + if isinstance(byhour, integer_types): + byhour = (byhour,) + + if freq == HOURLY: + self._byhour = self.__construct_byset(start=dtstart.hour, + byxxx=byhour, + base=24) + else: + self._byhour = set(byhour) + + self._byhour = tuple(sorted(self._byhour)) + self._original_rule['byhour'] = self._byhour + + # byminute + if byminute is None: + if freq < MINUTELY: + self._byminute = {dtstart.minute} + else: + self._byminute = None + else: + if isinstance(byminute, integer_types): + byminute = (byminute,) + + if freq == MINUTELY: + self._byminute = self.__construct_byset(start=dtstart.minute, + byxxx=byminute, + base=60) + else: + self._byminute = set(byminute) + + self._byminute = tuple(sorted(self._byminute)) + self._original_rule['byminute'] = self._byminute + + # bysecond + if bysecond is None: + if freq < SECONDLY: + self._bysecond = ((dtstart.second,)) + else: + self._bysecond = None + else: + if isinstance(bysecond, integer_types): + bysecond = (bysecond,) + + self._bysecond = set(bysecond) + + if freq == SECONDLY: + self._bysecond = self.__construct_byset(start=dtstart.second, + byxxx=bysecond, + base=60) + else: + self._bysecond = set(bysecond) + + self._bysecond = tuple(sorted(self._bysecond)) + self._original_rule['bysecond'] = self._bysecond + + if self._freq >= HOURLY: + self._timeset = None + else: + self._timeset = [] + for hour in self._byhour: + for minute in self._byminute: + for second in self._bysecond: + self._timeset.append( + datetime.time(hour, minute, second, + tzinfo=self._tzinfo)) + self._timeset.sort() + self._timeset = tuple(self._timeset) + + def __str__(self): + """ + Output a string that would generate this RRULE if passed to rrulestr. + This is mostly compatible with RFC5545, except for the + dateutil-specific extension BYEASTER. + """ + + output = [] + h, m, s = [None] * 3 + if self._dtstart: + output.append(self._dtstart.strftime('DTSTART:%Y%m%dT%H%M%S')) + h, m, s = self._dtstart.timetuple()[3:6] + + parts = ['FREQ=' + FREQNAMES[self._freq]] + if self._interval != 1: + parts.append('INTERVAL=' + str(self._interval)) + + if self._wkst: + parts.append('WKST=' + repr(weekday(self._wkst))[0:2]) + + if self._count is not None: + parts.append('COUNT=' + str(self._count)) + + if self._until: + parts.append(self._until.strftime('UNTIL=%Y%m%dT%H%M%S')) + + if self._original_rule.get('byweekday') is not None: + # The str() method on weekday objects doesn't generate + # RFC5545-compliant strings, so we should modify that. + original_rule = dict(self._original_rule) + wday_strings = [] + for wday in original_rule['byweekday']: + if wday.n: + wday_strings.append('{n:+d}{wday}'.format( + n=wday.n, + wday=repr(wday)[0:2])) + else: + wday_strings.append(repr(wday)) + + original_rule['byweekday'] = wday_strings + else: + original_rule = self._original_rule + + partfmt = '{name}={vals}' + for name, key in [('BYSETPOS', 'bysetpos'), + ('BYMONTH', 'bymonth'), + ('BYMONTHDAY', 'bymonthday'), + ('BYYEARDAY', 'byyearday'), + ('BYWEEKNO', 'byweekno'), + ('BYDAY', 'byweekday'), + ('BYHOUR', 'byhour'), + ('BYMINUTE', 'byminute'), + ('BYSECOND', 'bysecond'), + ('BYEASTER', 'byeaster')]: + value = original_rule.get(key) + if value: + parts.append(partfmt.format(name=name, vals=(','.join(str(v) + for v in value)))) + + output.append('RRULE:' + ';'.join(parts)) + return '\n'.join(output) + + def replace(self, **kwargs): + """Return new rrule with same attributes except for those attributes given new + values by whichever keyword arguments are specified.""" + new_kwargs = {"interval": self._interval, + "count": self._count, + "dtstart": self._dtstart, + "freq": self._freq, + "until": self._until, + "wkst": self._wkst, + "cache": False if self._cache is None else True } + new_kwargs.update(self._original_rule) + new_kwargs.update(kwargs) + return rrule(**new_kwargs) + + def _iter(self): + year, month, day, hour, minute, second, weekday, yearday, _ = \ + self._dtstart.timetuple() + + # Some local variables to speed things up a bit + freq = self._freq + interval = self._interval + wkst = self._wkst + until = self._until + bymonth = self._bymonth + byweekno = self._byweekno + byyearday = self._byyearday + byweekday = self._byweekday + byeaster = self._byeaster + bymonthday = self._bymonthday + bynmonthday = self._bynmonthday + bysetpos = self._bysetpos + byhour = self._byhour + byminute = self._byminute + bysecond = self._bysecond + + ii = _iterinfo(self) + ii.rebuild(year, month) + + getdayset = {YEARLY: ii.ydayset, + MONTHLY: ii.mdayset, + WEEKLY: ii.wdayset, + DAILY: ii.ddayset, + HOURLY: ii.ddayset, + MINUTELY: ii.ddayset, + SECONDLY: ii.ddayset}[freq] + + if freq < HOURLY: + timeset = self._timeset + else: + gettimeset = {HOURLY: ii.htimeset, + MINUTELY: ii.mtimeset, + SECONDLY: ii.stimeset}[freq] + if ((freq >= HOURLY and + self._byhour and hour not in self._byhour) or + (freq >= MINUTELY and + self._byminute and minute not in self._byminute) or + (freq >= SECONDLY and + self._bysecond and second not in self._bysecond)): + timeset = () + else: + timeset = gettimeset(hour, minute, second) + + total = 0 + count = self._count + while True: + # Get dayset with the right frequency + dayset, start, end = getdayset(year, month, day) + + # Do the "hard" work ;-) + filtered = False + for i in dayset[start:end]: + if ((bymonth and ii.mmask[i] not in bymonth) or + (byweekno and not ii.wnomask[i]) or + (byweekday and ii.wdaymask[i] not in byweekday) or + (ii.nwdaymask and not ii.nwdaymask[i]) or + (byeaster and not ii.eastermask[i]) or + ((bymonthday or bynmonthday) and + ii.mdaymask[i] not in bymonthday and + ii.nmdaymask[i] not in bynmonthday) or + (byyearday and + ((i < ii.yearlen and i+1 not in byyearday and + -ii.yearlen+i not in byyearday) or + (i >= ii.yearlen and i+1-ii.yearlen not in byyearday and + -ii.nextyearlen+i-ii.yearlen not in byyearday)))): + dayset[i] = None + filtered = True + + # Output results + if bysetpos and timeset: + poslist = [] + for pos in bysetpos: + if pos < 0: + daypos, timepos = divmod(pos, len(timeset)) + else: + daypos, timepos = divmod(pos-1, len(timeset)) + try: + i = [x for x in dayset[start:end] + if x is not None][daypos] + time = timeset[timepos] + except IndexError: + pass + else: + date = datetime.date.fromordinal(ii.yearordinal+i) + res = datetime.datetime.combine(date, time) + if res not in poslist: + poslist.append(res) + poslist.sort() + for res in poslist: + if until and res > until: + self._len = total + return + elif res >= self._dtstart: + if count is not None: + count -= 1 + if count < 0: + self._len = total + return + total += 1 + yield res + else: + for i in dayset[start:end]: + if i is not None: + date = datetime.date.fromordinal(ii.yearordinal + i) + for time in timeset: + res = datetime.datetime.combine(date, time) + if until and res > until: + self._len = total + return + elif res >= self._dtstart: + if count is not None: + count -= 1 + if count < 0: + self._len = total + return + + total += 1 + yield res + + # Handle frequency and interval + fixday = False + if freq == YEARLY: + year += interval + if year > datetime.MAXYEAR: + self._len = total + return + ii.rebuild(year, month) + elif freq == MONTHLY: + month += interval + if month > 12: + div, mod = divmod(month, 12) + month = mod + year += div + if month == 0: + month = 12 + year -= 1 + if year > datetime.MAXYEAR: + self._len = total + return + ii.rebuild(year, month) + elif freq == WEEKLY: + if wkst > weekday: + day += -(weekday+1+(6-wkst))+self._interval*7 + else: + day += -(weekday-wkst)+self._interval*7 + weekday = wkst + fixday = True + elif freq == DAILY: + day += interval + fixday = True + elif freq == HOURLY: + if filtered: + # Jump to one iteration before next day + hour += ((23-hour)//interval)*interval + + if byhour: + ndays, hour = self.__mod_distance(value=hour, + byxxx=self._byhour, + base=24) + else: + ndays, hour = divmod(hour+interval, 24) + + if ndays: + day += ndays + fixday = True + + timeset = gettimeset(hour, minute, second) + elif freq == MINUTELY: + if filtered: + # Jump to one iteration before next day + minute += ((1439-(hour*60+minute))//interval)*interval + + valid = False + rep_rate = (24*60) + for j in range(rep_rate // gcd(interval, rep_rate)): + if byminute: + nhours, minute = \ + self.__mod_distance(value=minute, + byxxx=self._byminute, + base=60) + else: + nhours, minute = divmod(minute+interval, 60) + + div, hour = divmod(hour+nhours, 24) + if div: + day += div + fixday = True + filtered = False + + if not byhour or hour in byhour: + valid = True + break + + if not valid: + raise ValueError('Invalid combination of interval and ' + + 'byhour resulting in empty rule.') + + timeset = gettimeset(hour, minute, second) + elif freq == SECONDLY: + if filtered: + # Jump to one iteration before next day + second += (((86399 - (hour * 3600 + minute * 60 + second)) + // interval) * interval) + + rep_rate = (24 * 3600) + valid = False + for j in range(0, rep_rate // gcd(interval, rep_rate)): + if bysecond: + nminutes, second = \ + self.__mod_distance(value=second, + byxxx=self._bysecond, + base=60) + else: + nminutes, second = divmod(second+interval, 60) + + div, minute = divmod(minute+nminutes, 60) + if div: + hour += div + div, hour = divmod(hour, 24) + if div: + day += div + fixday = True + + if ((not byhour or hour in byhour) and + (not byminute or minute in byminute) and + (not bysecond or second in bysecond)): + valid = True + break + + if not valid: + raise ValueError('Invalid combination of interval, ' + + 'byhour and byminute resulting in empty' + + ' rule.') + + timeset = gettimeset(hour, minute, second) + + if fixday and day > 28: + daysinmonth = calendar.monthrange(year, month)[1] + if day > daysinmonth: + while day > daysinmonth: + day -= daysinmonth + month += 1 + if month == 13: + month = 1 + year += 1 + if year > datetime.MAXYEAR: + self._len = total + return + daysinmonth = calendar.monthrange(year, month)[1] + ii.rebuild(year, month) + + def __construct_byset(self, start, byxxx, base): + """ + If a `BYXXX` sequence is passed to the constructor at the same level as + `FREQ` (e.g. `FREQ=HOURLY,BYHOUR={2,4,7},INTERVAL=3`), there are some + specifications which cannot be reached given some starting conditions. + + This occurs whenever the interval is not coprime with the base of a + given unit and the difference between the starting position and the + ending position is not coprime with the greatest common denominator + between the interval and the base. For example, with a FREQ of hourly + starting at 17:00 and an interval of 4, the only valid values for + BYHOUR would be {21, 1, 5, 9, 13, 17}, because 4 and 24 are not + coprime. + + :param start: + Specifies the starting position. + :param byxxx: + An iterable containing the list of allowed values. + :param base: + The largest allowable value for the specified frequency (e.g. + 24 hours, 60 minutes). + + This does not preserve the type of the iterable, returning a set, since + the values should be unique and the order is irrelevant, this will + speed up later lookups. + + In the event of an empty set, raises a :exception:`ValueError`, as this + results in an empty rrule. + """ + + cset = set() + + # Support a single byxxx value. + if isinstance(byxxx, integer_types): + byxxx = (byxxx, ) + + for num in byxxx: + i_gcd = gcd(self._interval, base) + # Use divmod rather than % because we need to wrap negative nums. + if i_gcd == 1 or divmod(num - start, i_gcd)[1] == 0: + cset.add(num) + + if len(cset) == 0: + raise ValueError("Invalid rrule byxxx generates an empty set.") + + return cset + + def __mod_distance(self, value, byxxx, base): + """ + Calculates the next value in a sequence where the `FREQ` parameter is + specified along with a `BYXXX` parameter at the same "level" + (e.g. `HOURLY` specified with `BYHOUR`). + + :param value: + The old value of the component. + :param byxxx: + The `BYXXX` set, which should have been generated by + `rrule._construct_byset`, or something else which checks that a + valid rule is present. + :param base: + The largest allowable value for the specified frequency (e.g. + 24 hours, 60 minutes). + + If a valid value is not found after `base` iterations (the maximum + number before the sequence would start to repeat), this raises a + :exception:`ValueError`, as no valid values were found. + + This returns a tuple of `divmod(n*interval, base)`, where `n` is the + smallest number of `interval` repetitions until the next specified + value in `byxxx` is found. + """ + accumulator = 0 + for ii in range(1, base + 1): + # Using divmod() over % to account for negative intervals + div, value = divmod(value + self._interval, base) + accumulator += div + if value in byxxx: + return (accumulator, value) + + +class _iterinfo(object): + __slots__ = ["rrule", "lastyear", "lastmonth", + "yearlen", "nextyearlen", "yearordinal", "yearweekday", + "mmask", "mrange", "mdaymask", "nmdaymask", + "wdaymask", "wnomask", "nwdaymask", "eastermask"] + + def __init__(self, rrule): + for attr in self.__slots__: + setattr(self, attr, None) + self.rrule = rrule + + def rebuild(self, year, month): + # Every mask is 7 days longer to handle cross-year weekly periods. + rr = self.rrule + if year != self.lastyear: + self.yearlen = 365 + calendar.isleap(year) + self.nextyearlen = 365 + calendar.isleap(year + 1) + firstyday = datetime.date(year, 1, 1) + self.yearordinal = firstyday.toordinal() + self.yearweekday = firstyday.weekday() + + wday = datetime.date(year, 1, 1).weekday() + if self.yearlen == 365: + self.mmask = M365MASK + self.mdaymask = MDAY365MASK + self.nmdaymask = NMDAY365MASK + self.wdaymask = WDAYMASK[wday:] + self.mrange = M365RANGE + else: + self.mmask = M366MASK + self.mdaymask = MDAY366MASK + self.nmdaymask = NMDAY366MASK + self.wdaymask = WDAYMASK[wday:] + self.mrange = M366RANGE + + if not rr._byweekno: + self.wnomask = None + else: + self.wnomask = [0]*(self.yearlen+7) + # no1wkst = firstwkst = self.wdaymask.index(rr._wkst) + no1wkst = firstwkst = (7-self.yearweekday+rr._wkst) % 7 + if no1wkst >= 4: + no1wkst = 0 + # Number of days in the year, plus the days we got + # from last year. + wyearlen = self.yearlen+(self.yearweekday-rr._wkst) % 7 + else: + # Number of days in the year, minus the days we + # left in last year. + wyearlen = self.yearlen-no1wkst + div, mod = divmod(wyearlen, 7) + numweeks = div+mod//4 + for n in rr._byweekno: + if n < 0: + n += numweeks+1 + if not (0 < n <= numweeks): + continue + if n > 1: + i = no1wkst+(n-1)*7 + if no1wkst != firstwkst: + i -= 7-firstwkst + else: + i = no1wkst + for j in range(7): + self.wnomask[i] = 1 + i += 1 + if self.wdaymask[i] == rr._wkst: + break + if 1 in rr._byweekno: + # Check week number 1 of next year as well + # TODO: Check -numweeks for next year. + i = no1wkst+numweeks*7 + if no1wkst != firstwkst: + i -= 7-firstwkst + if i < self.yearlen: + # If week starts in next year, we + # don't care about it. + for j in range(7): + self.wnomask[i] = 1 + i += 1 + if self.wdaymask[i] == rr._wkst: + break + if no1wkst: + # Check last week number of last year as + # well. If no1wkst is 0, either the year + # started on week start, or week number 1 + # got days from last year, so there are no + # days from last year's last week number in + # this year. + if -1 not in rr._byweekno: + lyearweekday = datetime.date(year-1, 1, 1).weekday() + lno1wkst = (7-lyearweekday+rr._wkst) % 7 + lyearlen = 365+calendar.isleap(year-1) + if lno1wkst >= 4: + lno1wkst = 0 + lnumweeks = 52+(lyearlen + + (lyearweekday-rr._wkst) % 7) % 7//4 + else: + lnumweeks = 52+(self.yearlen-no1wkst) % 7//4 + else: + lnumweeks = -1 + if lnumweeks in rr._byweekno: + for i in range(no1wkst): + self.wnomask[i] = 1 + + if (rr._bynweekday and (month != self.lastmonth or + year != self.lastyear)): + ranges = [] + if rr._freq == YEARLY: + if rr._bymonth: + for month in rr._bymonth: + ranges.append(self.mrange[month-1:month+1]) + else: + ranges = [(0, self.yearlen)] + elif rr._freq == MONTHLY: + ranges = [self.mrange[month-1:month+1]] + if ranges: + # Weekly frequency won't get here, so we may not + # care about cross-year weekly periods. + self.nwdaymask = [0]*self.yearlen + for first, last in ranges: + last -= 1 + for wday, n in rr._bynweekday: + if n < 0: + i = last+(n+1)*7 + i -= (self.wdaymask[i]-wday) % 7 + else: + i = first+(n-1)*7 + i += (7-self.wdaymask[i]+wday) % 7 + if first <= i <= last: + self.nwdaymask[i] = 1 + + if rr._byeaster: + self.eastermask = [0]*(self.yearlen+7) + eyday = easter.easter(year).toordinal()-self.yearordinal + for offset in rr._byeaster: + self.eastermask[eyday+offset] = 1 + + self.lastyear = year + self.lastmonth = month + + def ydayset(self, year, month, day): + return list(range(self.yearlen)), 0, self.yearlen + + def mdayset(self, year, month, day): + dset = [None]*self.yearlen + start, end = self.mrange[month-1:month+1] + for i in range(start, end): + dset[i] = i + return dset, start, end + + def wdayset(self, year, month, day): + # We need to handle cross-year weeks here. + dset = [None]*(self.yearlen+7) + i = datetime.date(year, month, day).toordinal()-self.yearordinal + start = i + for j in range(7): + dset[i] = i + i += 1 + # if (not (0 <= i < self.yearlen) or + # self.wdaymask[i] == self.rrule._wkst): + # This will cross the year boundary, if necessary. + if self.wdaymask[i] == self.rrule._wkst: + break + return dset, start, i + + def ddayset(self, year, month, day): + dset = [None] * self.yearlen + i = datetime.date(year, month, day).toordinal() - self.yearordinal + dset[i] = i + return dset, i, i + 1 + + def htimeset(self, hour, minute, second): + tset = [] + rr = self.rrule + for minute in rr._byminute: + for second in rr._bysecond: + tset.append(datetime.time(hour, minute, second, + tzinfo=rr._tzinfo)) + tset.sort() + return tset + + def mtimeset(self, hour, minute, second): + tset = [] + rr = self.rrule + for second in rr._bysecond: + tset.append(datetime.time(hour, minute, second, tzinfo=rr._tzinfo)) + tset.sort() + return tset + + def stimeset(self, hour, minute, second): + return (datetime.time(hour, minute, second, + tzinfo=self.rrule._tzinfo),) + + +class rruleset(rrulebase): + """ The rruleset type allows more complex recurrence setups, mixing + multiple rules, dates, exclusion rules, and exclusion dates. The type + constructor takes the following keyword arguments: + + :param cache: If True, caching of results will be enabled, improving + performance of multiple queries considerably. """ + + class _genitem(object): + def __init__(self, genlist, gen): + try: + self.dt = advance_iterator(gen) + genlist.append(self) + except StopIteration: + pass + self.genlist = genlist + self.gen = gen + + def __next__(self): + try: + self.dt = advance_iterator(self.gen) + except StopIteration: + if self.genlist[0] is self: + heapq.heappop(self.genlist) + else: + self.genlist.remove(self) + heapq.heapify(self.genlist) + + next = __next__ + + def __lt__(self, other): + return self.dt < other.dt + + def __gt__(self, other): + return self.dt > other.dt + + def __eq__(self, other): + return self.dt == other.dt + + def __ne__(self, other): + return self.dt != other.dt + + def __init__(self, cache=False): + super(rruleset, self).__init__(cache) + self._rrule = [] + self._rdate = [] + self._exrule = [] + self._exdate = [] + + @_invalidates_cache + def rrule(self, rrule): + """ Include the given :py:class:`rrule` instance in the recurrence set + generation. """ + self._rrule.append(rrule) + + @_invalidates_cache + def rdate(self, rdate): + """ Include the given :py:class:`datetime` instance in the recurrence + set generation. """ + self._rdate.append(rdate) + + @_invalidates_cache + def exrule(self, exrule): + """ Include the given rrule instance in the recurrence set exclusion + list. Dates which are part of the given recurrence rules will not + be generated, even if some inclusive rrule or rdate matches them. + """ + self._exrule.append(exrule) + + @_invalidates_cache + def exdate(self, exdate): + """ Include the given datetime instance in the recurrence set + exclusion list. Dates included that way will not be generated, + even if some inclusive rrule or rdate matches them. """ + self._exdate.append(exdate) + + def _iter(self): + rlist = [] + self._rdate.sort() + self._genitem(rlist, iter(self._rdate)) + for gen in [iter(x) for x in self._rrule]: + self._genitem(rlist, gen) + exlist = [] + self._exdate.sort() + self._genitem(exlist, iter(self._exdate)) + for gen in [iter(x) for x in self._exrule]: + self._genitem(exlist, gen) + lastdt = None + total = 0 + heapq.heapify(rlist) + heapq.heapify(exlist) + while rlist: + ritem = rlist[0] + if not lastdt or lastdt != ritem.dt: + while exlist and exlist[0] < ritem: + exitem = exlist[0] + advance_iterator(exitem) + if exlist and exlist[0] is exitem: + heapq.heapreplace(exlist, exitem) + if not exlist or ritem != exlist[0]: + total += 1 + yield ritem.dt + lastdt = ritem.dt + advance_iterator(ritem) + if rlist and rlist[0] is ritem: + heapq.heapreplace(rlist, ritem) + self._len = total + + + + +class _rrulestr(object): + """ Parses a string representation of a recurrence rule or set of + recurrence rules. + + :param s: + Required, a string defining one or more recurrence rules. + + :param dtstart: + If given, used as the default recurrence start if not specified in the + rule string. + + :param cache: + If set ``True`` caching of results will be enabled, improving + performance of multiple queries considerably. + + :param unfold: + If set ``True`` indicates that a rule string is split over more + than one line and should be joined before processing. + + :param forceset: + If set ``True`` forces a :class:`dateutil.rrule.rruleset` to + be returned. + + :param compatible: + If set ``True`` forces ``unfold`` and ``forceset`` to be ``True``. + + :param ignoretz: + If set ``True``, time zones in parsed strings are ignored and a naive + :class:`datetime.datetime` object is returned. + + :param tzids: + If given, a callable or mapping used to retrieve a + :class:`datetime.tzinfo` from a string representation. + Defaults to :func:`dateutil.tz.gettz`. + + :param tzinfos: + Additional time zone names / aliases which may be present in a string + representation. See :func:`dateutil.parser.parse` for more + information. + + :return: + Returns a :class:`dateutil.rrule.rruleset` or + :class:`dateutil.rrule.rrule` + """ + + _freq_map = {"YEARLY": YEARLY, + "MONTHLY": MONTHLY, + "WEEKLY": WEEKLY, + "DAILY": DAILY, + "HOURLY": HOURLY, + "MINUTELY": MINUTELY, + "SECONDLY": SECONDLY} + + _weekday_map = {"MO": 0, "TU": 1, "WE": 2, "TH": 3, + "FR": 4, "SA": 5, "SU": 6} + + def _handle_int(self, rrkwargs, name, value, **kwargs): + rrkwargs[name.lower()] = int(value) + + def _handle_int_list(self, rrkwargs, name, value, **kwargs): + rrkwargs[name.lower()] = [int(x) for x in value.split(',')] + + _handle_INTERVAL = _handle_int + _handle_COUNT = _handle_int + _handle_BYSETPOS = _handle_int_list + _handle_BYMONTH = _handle_int_list + _handle_BYMONTHDAY = _handle_int_list + _handle_BYYEARDAY = _handle_int_list + _handle_BYEASTER = _handle_int_list + _handle_BYWEEKNO = _handle_int_list + _handle_BYHOUR = _handle_int_list + _handle_BYMINUTE = _handle_int_list + _handle_BYSECOND = _handle_int_list + + def _handle_FREQ(self, rrkwargs, name, value, **kwargs): + rrkwargs["freq"] = self._freq_map[value] + + def _handle_UNTIL(self, rrkwargs, name, value, **kwargs): + global parser + if not parser: + from dateutil import parser + try: + rrkwargs["until"] = parser.parse(value, + ignoretz=kwargs.get("ignoretz"), + tzinfos=kwargs.get("tzinfos")) + except ValueError: + raise ValueError("invalid until date") + + def _handle_WKST(self, rrkwargs, name, value, **kwargs): + rrkwargs["wkst"] = self._weekday_map[value] + + def _handle_BYWEEKDAY(self, rrkwargs, name, value, **kwargs): + """ + Two ways to specify this: +1MO or MO(+1) + """ + l = [] + for wday in value.split(','): + if '(' in wday: + # If it's of the form TH(+1), etc. + splt = wday.split('(') + w = splt[0] + n = int(splt[1][:-1]) + elif len(wday): + # If it's of the form +1MO + for i in range(len(wday)): + if wday[i] not in '+-0123456789': + break + n = wday[:i] or None + w = wday[i:] + if n: + n = int(n) + else: + raise ValueError("Invalid (empty) BYDAY specification.") + + l.append(weekdays[self._weekday_map[w]](n)) + rrkwargs["byweekday"] = l + + _handle_BYDAY = _handle_BYWEEKDAY + + def _parse_rfc_rrule(self, line, + dtstart=None, + cache=False, + ignoretz=False, + tzinfos=None): + if line.find(':') != -1: + name, value = line.split(':') + if name != "RRULE": + raise ValueError("unknown parameter name") + else: + value = line + rrkwargs = {} + for pair in value.split(';'): + name, value = pair.split('=') + name = name.upper() + value = value.upper() + try: + getattr(self, "_handle_"+name)(rrkwargs, name, value, + ignoretz=ignoretz, + tzinfos=tzinfos) + except AttributeError: + raise ValueError("unknown parameter '%s'" % name) + except (KeyError, ValueError): + raise ValueError("invalid '%s': %s" % (name, value)) + return rrule(dtstart=dtstart, cache=cache, **rrkwargs) + + def _parse_date_value(self, date_value, parms, rule_tzids, + ignoretz, tzids, tzinfos): + global parser + if not parser: + from dateutil import parser + + datevals = [] + value_found = False + TZID = None + + for parm in parms: + if parm.startswith("TZID="): + try: + tzkey = rule_tzids[parm.split('TZID=')[-1]] + except KeyError: + continue + if tzids is None: + from . import tz + tzlookup = tz.gettz + elif callable(tzids): + tzlookup = tzids + else: + tzlookup = getattr(tzids, 'get', None) + if tzlookup is None: + msg = ('tzids must be a callable, mapping, or None, ' + 'not %s' % tzids) + raise ValueError(msg) + + TZID = tzlookup(tzkey) + continue + + # RFC 5445 3.8.2.4: The VALUE parameter is optional, but may be found + # only once. + if parm not in {"VALUE=DATE-TIME", "VALUE=DATE"}: + raise ValueError("unsupported parm: " + parm) + else: + if value_found: + msg = ("Duplicate value parameter found in: " + parm) + raise ValueError(msg) + value_found = True + + for datestr in date_value.split(','): + date = parser.parse(datestr, ignoretz=ignoretz, tzinfos=tzinfos) + if TZID is not None: + if date.tzinfo is None: + date = date.replace(tzinfo=TZID) + else: + raise ValueError('DTSTART/EXDATE specifies multiple timezone') + datevals.append(date) + + return datevals + + def _parse_rfc(self, s, + dtstart=None, + cache=False, + unfold=False, + forceset=False, + compatible=False, + ignoretz=False, + tzids=None, + tzinfos=None): + global parser + if compatible: + forceset = True + unfold = True + + TZID_NAMES = dict(map( + lambda x: (x.upper(), x), + re.findall('TZID=(?P[^:]+):', s) + )) + s = s.upper() + if not s.strip(): + raise ValueError("empty string") + if unfold: + lines = s.splitlines() + i = 0 + while i < len(lines): + line = lines[i].rstrip() + if not line: + del lines[i] + elif i > 0 and line[0] == " ": + lines[i-1] += line[1:] + del lines[i] + else: + i += 1 + else: + lines = s.split() + if (not forceset and len(lines) == 1 and (s.find(':') == -1 or + s.startswith('RRULE:'))): + return self._parse_rfc_rrule(lines[0], cache=cache, + dtstart=dtstart, ignoretz=ignoretz, + tzinfos=tzinfos) + else: + rrulevals = [] + rdatevals = [] + exrulevals = [] + exdatevals = [] + for line in lines: + if not line: + continue + if line.find(':') == -1: + name = "RRULE" + value = line + else: + name, value = line.split(':', 1) + parms = name.split(';') + if not parms: + raise ValueError("empty property name") + name = parms[0] + parms = parms[1:] + if name == "RRULE": + for parm in parms: + raise ValueError("unsupported RRULE parm: "+parm) + rrulevals.append(value) + elif name == "RDATE": + for parm in parms: + if parm != "VALUE=DATE-TIME": + raise ValueError("unsupported RDATE parm: "+parm) + rdatevals.append(value) + elif name == "EXRULE": + for parm in parms: + raise ValueError("unsupported EXRULE parm: "+parm) + exrulevals.append(value) + elif name == "EXDATE": + exdatevals.extend( + self._parse_date_value(value, parms, + TZID_NAMES, ignoretz, + tzids, tzinfos) + ) + elif name == "DTSTART": + dtvals = self._parse_date_value(value, parms, TZID_NAMES, + ignoretz, tzids, tzinfos) + if len(dtvals) != 1: + raise ValueError("Multiple DTSTART values specified:" + + value) + dtstart = dtvals[0] + else: + raise ValueError("unsupported property: "+name) + if (forceset or len(rrulevals) > 1 or rdatevals + or exrulevals or exdatevals): + if not parser and (rdatevals or exdatevals): + from dateutil import parser + rset = rruleset(cache=cache) + for value in rrulevals: + rset.rrule(self._parse_rfc_rrule(value, dtstart=dtstart, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in rdatevals: + for datestr in value.split(','): + rset.rdate(parser.parse(datestr, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in exrulevals: + rset.exrule(self._parse_rfc_rrule(value, dtstart=dtstart, + ignoretz=ignoretz, + tzinfos=tzinfos)) + for value in exdatevals: + rset.exdate(value) + if compatible and dtstart: + rset.rdate(dtstart) + return rset + else: + return self._parse_rfc_rrule(rrulevals[0], + dtstart=dtstart, + cache=cache, + ignoretz=ignoretz, + tzinfos=tzinfos) + + def __call__(self, s, **kwargs): + return self._parse_rfc(s, **kwargs) + + +rrulestr = _rrulestr() + +# vim:ts=4:sw=4:et diff --git a/.venv/lib/python3.9/site-packages/dateutil/tz/__init__.py b/.venv/lib/python3.9/site-packages/dateutil/tz/__init__.py new file mode 100644 index 00000000..af1352c4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/tz/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +from .tz import * +from .tz import __doc__ + +__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange", + "tzstr", "tzical", "tzwin", "tzwinlocal", "gettz", + "enfold", "datetime_ambiguous", "datetime_exists", + "resolve_imaginary", "UTC", "DeprecatedTzFormatWarning"] + + +class DeprecatedTzFormatWarning(Warning): + """Warning raised when time zones are parsed from deprecated formats.""" diff --git a/.venv/lib/python3.9/site-packages/dateutil/tz/_common.py b/.venv/lib/python3.9/site-packages/dateutil/tz/_common.py new file mode 100644 index 00000000..e6ac1183 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/tz/_common.py @@ -0,0 +1,419 @@ +from six import PY2 + +from functools import wraps + +from datetime import datetime, timedelta, tzinfo + + +ZERO = timedelta(0) + +__all__ = ['tzname_in_python2', 'enfold'] + + +def tzname_in_python2(namefunc): + """Change unicode output into bytestrings in Python 2 + + tzname() API changed in Python 3. It used to return bytes, but was changed + to unicode strings + """ + if PY2: + @wraps(namefunc) + def adjust_encoding(*args, **kwargs): + name = namefunc(*args, **kwargs) + if name is not None: + name = name.encode() + + return name + + return adjust_encoding + else: + return namefunc + + +# The following is adapted from Alexander Belopolsky's tz library +# https://github.com/abalkin/tz +if hasattr(datetime, 'fold'): + # This is the pre-python 3.6 fold situation + def enfold(dt, fold=1): + """ + Provides a unified interface for assigning the ``fold`` attribute to + datetimes both before and after the implementation of PEP-495. + + :param fold: + The value for the ``fold`` attribute in the returned datetime. This + should be either 0 or 1. + + :return: + Returns an object for which ``getattr(dt, 'fold', 0)`` returns + ``fold`` for all versions of Python. In versions prior to + Python 3.6, this is a ``_DatetimeWithFold`` object, which is a + subclass of :py:class:`datetime.datetime` with the ``fold`` + attribute added, if ``fold`` is 1. + + .. versionadded:: 2.6.0 + """ + return dt.replace(fold=fold) + +else: + class _DatetimeWithFold(datetime): + """ + This is a class designed to provide a PEP 495-compliant interface for + Python versions before 3.6. It is used only for dates in a fold, so + the ``fold`` attribute is fixed at ``1``. + + .. versionadded:: 2.6.0 + """ + __slots__ = () + + def replace(self, *args, **kwargs): + """ + Return a datetime with the same attributes, except for those + attributes given new values by whichever keyword arguments are + specified. Note that tzinfo=None can be specified to create a naive + datetime from an aware datetime with no conversion of date and time + data. + + This is reimplemented in ``_DatetimeWithFold`` because pypy3 will + return a ``datetime.datetime`` even if ``fold`` is unchanged. + """ + argnames = ( + 'year', 'month', 'day', 'hour', 'minute', 'second', + 'microsecond', 'tzinfo' + ) + + for arg, argname in zip(args, argnames): + if argname in kwargs: + raise TypeError('Duplicate argument: {}'.format(argname)) + + kwargs[argname] = arg + + for argname in argnames: + if argname not in kwargs: + kwargs[argname] = getattr(self, argname) + + dt_class = self.__class__ if kwargs.get('fold', 1) else datetime + + return dt_class(**kwargs) + + @property + def fold(self): + return 1 + + def enfold(dt, fold=1): + """ + Provides a unified interface for assigning the ``fold`` attribute to + datetimes both before and after the implementation of PEP-495. + + :param fold: + The value for the ``fold`` attribute in the returned datetime. This + should be either 0 or 1. + + :return: + Returns an object for which ``getattr(dt, 'fold', 0)`` returns + ``fold`` for all versions of Python. In versions prior to + Python 3.6, this is a ``_DatetimeWithFold`` object, which is a + subclass of :py:class:`datetime.datetime` with the ``fold`` + attribute added, if ``fold`` is 1. + + .. versionadded:: 2.6.0 + """ + if getattr(dt, 'fold', 0) == fold: + return dt + + args = dt.timetuple()[:6] + args += (dt.microsecond, dt.tzinfo) + + if fold: + return _DatetimeWithFold(*args) + else: + return datetime(*args) + + +def _validate_fromutc_inputs(f): + """ + The CPython version of ``fromutc`` checks that the input is a ``datetime`` + object and that ``self`` is attached as its ``tzinfo``. + """ + @wraps(f) + def fromutc(self, dt): + if not isinstance(dt, datetime): + raise TypeError("fromutc() requires a datetime argument") + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + return f(self, dt) + + return fromutc + + +class _tzinfo(tzinfo): + """ + Base class for all ``dateutil`` ``tzinfo`` objects. + """ + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + + dt = dt.replace(tzinfo=self) + + wall_0 = enfold(dt, fold=0) + wall_1 = enfold(dt, fold=1) + + same_offset = wall_0.utcoffset() == wall_1.utcoffset() + same_dt = wall_0.replace(tzinfo=None) == wall_1.replace(tzinfo=None) + + return same_dt and not same_offset + + def _fold_status(self, dt_utc, dt_wall): + """ + Determine the fold status of a "wall" datetime, given a representation + of the same datetime as a (naive) UTC datetime. This is calculated based + on the assumption that ``dt.utcoffset() - dt.dst()`` is constant for all + datetimes, and that this offset is the actual number of hours separating + ``dt_utc`` and ``dt_wall``. + + :param dt_utc: + Representation of the datetime as UTC + + :param dt_wall: + Representation of the datetime as "wall time". This parameter must + either have a `fold` attribute or have a fold-naive + :class:`datetime.tzinfo` attached, otherwise the calculation may + fail. + """ + if self.is_ambiguous(dt_wall): + delta_wall = dt_wall - dt_utc + _fold = int(delta_wall == (dt_utc.utcoffset() - dt_utc.dst())) + else: + _fold = 0 + + return _fold + + def _fold(self, dt): + return getattr(dt, 'fold', 0) + + def _fromutc(self, dt): + """ + Given a timezone-aware datetime in a given timezone, calculates a + timezone-aware datetime in a new timezone. + + Since this is the one time that we *know* we have an unambiguous + datetime object, we take this opportunity to determine whether the + datetime is ambiguous and in a "fold" state (e.g. if it's the first + occurrence, chronologically, of the ambiguous datetime). + + :param dt: + A timezone-aware :class:`datetime.datetime` object. + """ + + # Re-implement the algorithm from Python's datetime.py + dtoff = dt.utcoffset() + if dtoff is None: + raise ValueError("fromutc() requires a non-None utcoffset() " + "result") + + # The original datetime.py code assumes that `dst()` defaults to + # zero during ambiguous times. PEP 495 inverts this presumption, so + # for pre-PEP 495 versions of python, we need to tweak the algorithm. + dtdst = dt.dst() + if dtdst is None: + raise ValueError("fromutc() requires a non-None dst() result") + delta = dtoff - dtdst + + dt += delta + # Set fold=1 so we can default to being in the fold for + # ambiguous dates. + dtdst = enfold(dt, fold=1).dst() + if dtdst is None: + raise ValueError("fromutc(): dt.dst gave inconsistent " + "results; cannot convert") + return dt + dtdst + + @_validate_fromutc_inputs + def fromutc(self, dt): + """ + Given a timezone-aware datetime in a given timezone, calculates a + timezone-aware datetime in a new timezone. + + Since this is the one time that we *know* we have an unambiguous + datetime object, we take this opportunity to determine whether the + datetime is ambiguous and in a "fold" state (e.g. if it's the first + occurrence, chronologically, of the ambiguous datetime). + + :param dt: + A timezone-aware :class:`datetime.datetime` object. + """ + dt_wall = self._fromutc(dt) + + # Calculate the fold status given the two datetimes. + _fold = self._fold_status(dt, dt_wall) + + # Set the default fold value for ambiguous dates + return enfold(dt_wall, fold=_fold) + + +class tzrangebase(_tzinfo): + """ + This is an abstract base class for time zones represented by an annual + transition into and out of DST. Child classes should implement the following + methods: + + * ``__init__(self, *args, **kwargs)`` + * ``transitions(self, year)`` - this is expected to return a tuple of + datetimes representing the DST on and off transitions in standard + time. + + A fully initialized ``tzrangebase`` subclass should also provide the + following attributes: + * ``hasdst``: Boolean whether or not the zone uses DST. + * ``_dst_offset`` / ``_std_offset``: :class:`datetime.timedelta` objects + representing the respective UTC offsets. + * ``_dst_abbr`` / ``_std_abbr``: Strings representing the timezone short + abbreviations in DST and STD, respectively. + * ``_hasdst``: Whether or not the zone has DST. + + .. versionadded:: 2.6.0 + """ + def __init__(self): + raise NotImplementedError('tzrangebase is an abstract base class') + + def utcoffset(self, dt): + isdst = self._isdst(dt) + + if isdst is None: + return None + elif isdst: + return self._dst_offset + else: + return self._std_offset + + def dst(self, dt): + isdst = self._isdst(dt) + + if isdst is None: + return None + elif isdst: + return self._dst_base_offset + else: + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + if self._isdst(dt): + return self._dst_abbr + else: + return self._std_abbr + + def fromutc(self, dt): + """ Given a datetime in UTC, return local time """ + if not isinstance(dt, datetime): + raise TypeError("fromutc() requires a datetime argument") + + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + # Get transitions - if there are none, fixed offset + transitions = self.transitions(dt.year) + if transitions is None: + return dt + self.utcoffset(dt) + + # Get the transition times in UTC + dston, dstoff = transitions + + dston -= self._std_offset + dstoff -= self._std_offset + + utc_transitions = (dston, dstoff) + dt_utc = dt.replace(tzinfo=None) + + isdst = self._naive_isdst(dt_utc, utc_transitions) + + if isdst: + dt_wall = dt + self._dst_offset + else: + dt_wall = dt + self._std_offset + + _fold = int(not isdst and self.is_ambiguous(dt_wall)) + + return enfold(dt_wall, fold=_fold) + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + if not self.hasdst: + return False + + start, end = self.transitions(dt.year) + + dt = dt.replace(tzinfo=None) + return (end <= dt < end + self._dst_base_offset) + + def _isdst(self, dt): + if not self.hasdst: + return False + elif dt is None: + return None + + transitions = self.transitions(dt.year) + + if transitions is None: + return False + + dt = dt.replace(tzinfo=None) + + isdst = self._naive_isdst(dt, transitions) + + # Handle ambiguous dates + if not isdst and self.is_ambiguous(dt): + return not self._fold(dt) + else: + return isdst + + def _naive_isdst(self, dt, transitions): + dston, dstoff = transitions + + dt = dt.replace(tzinfo=None) + + if dston < dstoff: + isdst = dston <= dt < dstoff + else: + isdst = not dstoff <= dt < dston + + return isdst + + @property + def _dst_base_offset(self): + return self._dst_offset - self._std_offset + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(...)" % self.__class__.__name__ + + __reduce__ = object.__reduce__ diff --git a/.venv/lib/python3.9/site-packages/dateutil/tz/_factories.py b/.venv/lib/python3.9/site-packages/dateutil/tz/_factories.py new file mode 100644 index 00000000..f8a65891 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/tz/_factories.py @@ -0,0 +1,80 @@ +from datetime import timedelta +import weakref +from collections import OrderedDict + +from six.moves import _thread + + +class _TzSingleton(type): + def __init__(cls, *args, **kwargs): + cls.__instance = None + super(_TzSingleton, cls).__init__(*args, **kwargs) + + def __call__(cls): + if cls.__instance is None: + cls.__instance = super(_TzSingleton, cls).__call__() + return cls.__instance + + +class _TzFactory(type): + def instance(cls, *args, **kwargs): + """Alternate constructor that returns a fresh instance""" + return type.__call__(cls, *args, **kwargs) + + +class _TzOffsetFactory(_TzFactory): + def __init__(cls, *args, **kwargs): + cls.__instances = weakref.WeakValueDictionary() + cls.__strong_cache = OrderedDict() + cls.__strong_cache_size = 8 + + cls._cache_lock = _thread.allocate_lock() + + def __call__(cls, name, offset): + if isinstance(offset, timedelta): + key = (name, offset.total_seconds()) + else: + key = (name, offset) + + instance = cls.__instances.get(key, None) + if instance is None: + instance = cls.__instances.setdefault(key, + cls.instance(name, offset)) + + # This lock may not be necessary in Python 3. See GH issue #901 + with cls._cache_lock: + cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance) + + # Remove an item if the strong cache is overpopulated + if len(cls.__strong_cache) > cls.__strong_cache_size: + cls.__strong_cache.popitem(last=False) + + return instance + + +class _TzStrFactory(_TzFactory): + def __init__(cls, *args, **kwargs): + cls.__instances = weakref.WeakValueDictionary() + cls.__strong_cache = OrderedDict() + cls.__strong_cache_size = 8 + + cls.__cache_lock = _thread.allocate_lock() + + def __call__(cls, s, posix_offset=False): + key = (s, posix_offset) + instance = cls.__instances.get(key, None) + + if instance is None: + instance = cls.__instances.setdefault(key, + cls.instance(s, posix_offset)) + + # This lock may not be necessary in Python 3. See GH issue #901 + with cls.__cache_lock: + cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance) + + # Remove an item if the strong cache is overpopulated + if len(cls.__strong_cache) > cls.__strong_cache_size: + cls.__strong_cache.popitem(last=False) + + return instance + diff --git a/.venv/lib/python3.9/site-packages/dateutil/tz/tz.py b/.venv/lib/python3.9/site-packages/dateutil/tz/tz.py new file mode 100644 index 00000000..61759144 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/tz/tz.py @@ -0,0 +1,1849 @@ +# -*- coding: utf-8 -*- +""" +This module offers timezone implementations subclassing the abstract +:py:class:`datetime.tzinfo` type. There are classes to handle tzfile format +files (usually are in :file:`/etc/localtime`, :file:`/usr/share/zoneinfo`, +etc), TZ environment string (in all known formats), given ranges (with help +from relative deltas), local machine timezone, fixed offset timezone, and UTC +timezone. +""" +import datetime +import struct +import time +import sys +import os +import bisect +import weakref +from collections import OrderedDict + +import six +from six import string_types +from six.moves import _thread +from ._common import tzname_in_python2, _tzinfo +from ._common import tzrangebase, enfold +from ._common import _validate_fromutc_inputs + +from ._factories import _TzSingleton, _TzOffsetFactory +from ._factories import _TzStrFactory +try: + from .win import tzwin, tzwinlocal +except ImportError: + tzwin = tzwinlocal = None + +# For warning about rounding tzinfo +from warnings import warn + +ZERO = datetime.timedelta(0) +EPOCH = datetime.datetime(1970, 1, 1, 0, 0) +EPOCHORDINAL = EPOCH.toordinal() + + +@six.add_metaclass(_TzSingleton) +class tzutc(datetime.tzinfo): + """ + This is a tzinfo object that represents the UTC time zone. + + **Examples:** + + .. doctest:: + + >>> from datetime import * + >>> from dateutil.tz import * + + >>> datetime.now() + datetime.datetime(2003, 9, 27, 9, 40, 1, 521290) + + >>> datetime.now(tzutc()) + datetime.datetime(2003, 9, 27, 12, 40, 12, 156379, tzinfo=tzutc()) + + >>> datetime.now(tzutc()).tzname() + 'UTC' + + .. versionchanged:: 2.7.0 + ``tzutc()`` is now a singleton, so the result of ``tzutc()`` will + always return the same object. + + .. doctest:: + + >>> from dateutil.tz import tzutc, UTC + >>> tzutc() is tzutc() + True + >>> tzutc() is UTC + True + """ + def utcoffset(self, dt): + return ZERO + + def dst(self, dt): + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return "UTC" + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + return False + + @_validate_fromutc_inputs + def fromutc(self, dt): + """ + Fast track version of fromutc() returns the original ``dt`` object for + any valid :py:class:`datetime.datetime` object. + """ + return dt + + def __eq__(self, other): + if not isinstance(other, (tzutc, tzoffset)): + return NotImplemented + + return (isinstance(other, tzutc) or + (isinstance(other, tzoffset) and other._offset == ZERO)) + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s()" % self.__class__.__name__ + + __reduce__ = object.__reduce__ + + +#: Convenience constant providing a :class:`tzutc()` instance +#: +#: .. versionadded:: 2.7.0 +UTC = tzutc() + + +@six.add_metaclass(_TzOffsetFactory) +class tzoffset(datetime.tzinfo): + """ + A simple class for representing a fixed offset from UTC. + + :param name: + The timezone name, to be returned when ``tzname()`` is called. + :param offset: + The time zone offset in seconds, or (since version 2.6.0, represented + as a :py:class:`datetime.timedelta` object). + """ + def __init__(self, name, offset): + self._name = name + + try: + # Allow a timedelta + offset = offset.total_seconds() + except (TypeError, AttributeError): + pass + + self._offset = datetime.timedelta(seconds=_get_supported_offset(offset)) + + def utcoffset(self, dt): + return self._offset + + def dst(self, dt): + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return self._name + + @_validate_fromutc_inputs + def fromutc(self, dt): + return dt + self._offset + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + return False + + def __eq__(self, other): + if not isinstance(other, tzoffset): + return NotImplemented + + return self._offset == other._offset + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(%s, %s)" % (self.__class__.__name__, + repr(self._name), + int(self._offset.total_seconds())) + + __reduce__ = object.__reduce__ + + +class tzlocal(_tzinfo): + """ + A :class:`tzinfo` subclass built around the ``time`` timezone functions. + """ + def __init__(self): + super(tzlocal, self).__init__() + + self._std_offset = datetime.timedelta(seconds=-time.timezone) + if time.daylight: + self._dst_offset = datetime.timedelta(seconds=-time.altzone) + else: + self._dst_offset = self._std_offset + + self._dst_saved = self._dst_offset - self._std_offset + self._hasdst = bool(self._dst_saved) + self._tznames = tuple(time.tzname) + + def utcoffset(self, dt): + if dt is None and self._hasdst: + return None + + if self._isdst(dt): + return self._dst_offset + else: + return self._std_offset + + def dst(self, dt): + if dt is None and self._hasdst: + return None + + if self._isdst(dt): + return self._dst_offset - self._std_offset + else: + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return self._tznames[self._isdst(dt)] + + def is_ambiguous(self, dt): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + naive_dst = self._naive_is_dst(dt) + return (not naive_dst and + (naive_dst != self._naive_is_dst(dt - self._dst_saved))) + + def _naive_is_dst(self, dt): + timestamp = _datetime_to_timestamp(dt) + return time.localtime(timestamp + time.timezone).tm_isdst + + def _isdst(self, dt, fold_naive=True): + # We can't use mktime here. It is unstable when deciding if + # the hour near to a change is DST or not. + # + # timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour, + # dt.minute, dt.second, dt.weekday(), 0, -1)) + # return time.localtime(timestamp).tm_isdst + # + # The code above yields the following result: + # + # >>> import tz, datetime + # >>> t = tz.tzlocal() + # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + # 'BRDT' + # >>> datetime.datetime(2003,2,16,0,tzinfo=t).tzname() + # 'BRST' + # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + # 'BRST' + # >>> datetime.datetime(2003,2,15,22,tzinfo=t).tzname() + # 'BRDT' + # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() + # 'BRDT' + # + # Here is a more stable implementation: + # + if not self._hasdst: + return False + + # Check for ambiguous times: + dstval = self._naive_is_dst(dt) + fold = getattr(dt, 'fold', None) + + if self.is_ambiguous(dt): + if fold is not None: + return not self._fold(dt) + else: + return True + + return dstval + + def __eq__(self, other): + if isinstance(other, tzlocal): + return (self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset) + elif isinstance(other, tzutc): + return (not self._hasdst and + self._tznames[0] in {'UTC', 'GMT'} and + self._std_offset == ZERO) + elif isinstance(other, tzoffset): + return (not self._hasdst and + self._tznames[0] == other._name and + self._std_offset == other._offset) + else: + return NotImplemented + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s()" % self.__class__.__name__ + + __reduce__ = object.__reduce__ + + +class _ttinfo(object): + __slots__ = ["offset", "delta", "isdst", "abbr", + "isstd", "isgmt", "dstoffset"] + + def __init__(self): + for attr in self.__slots__: + setattr(self, attr, None) + + def __repr__(self): + l = [] + for attr in self.__slots__: + value = getattr(self, attr) + if value is not None: + l.append("%s=%s" % (attr, repr(value))) + return "%s(%s)" % (self.__class__.__name__, ", ".join(l)) + + def __eq__(self, other): + if not isinstance(other, _ttinfo): + return NotImplemented + + return (self.offset == other.offset and + self.delta == other.delta and + self.isdst == other.isdst and + self.abbr == other.abbr and + self.isstd == other.isstd and + self.isgmt == other.isgmt and + self.dstoffset == other.dstoffset) + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __getstate__(self): + state = {} + for name in self.__slots__: + state[name] = getattr(self, name, None) + return state + + def __setstate__(self, state): + for name in self.__slots__: + if name in state: + setattr(self, name, state[name]) + + +class _tzfile(object): + """ + Lightweight class for holding the relevant transition and time zone + information read from binary tzfiles. + """ + attrs = ['trans_list', 'trans_list_utc', 'trans_idx', 'ttinfo_list', + 'ttinfo_std', 'ttinfo_dst', 'ttinfo_before', 'ttinfo_first'] + + def __init__(self, **kwargs): + for attr in self.attrs: + setattr(self, attr, kwargs.get(attr, None)) + + +class tzfile(_tzinfo): + """ + This is a ``tzinfo`` subclass that allows one to use the ``tzfile(5)`` + format timezone files to extract current and historical zone information. + + :param fileobj: + This can be an opened file stream or a file name that the time zone + information can be read from. + + :param filename: + This is an optional parameter specifying the source of the time zone + information in the event that ``fileobj`` is a file object. If omitted + and ``fileobj`` is a file stream, this parameter will be set either to + ``fileobj``'s ``name`` attribute or to ``repr(fileobj)``. + + See `Sources for Time Zone and Daylight Saving Time Data + `_ for more information. + Time zone files can be compiled from the `IANA Time Zone database files + `_ with the `zic time zone compiler + `_ + + .. note:: + + Only construct a ``tzfile`` directly if you have a specific timezone + file on disk that you want to read into a Python ``tzinfo`` object. + If you want to get a ``tzfile`` representing a specific IANA zone, + (e.g. ``'America/New_York'``), you should call + :func:`dateutil.tz.gettz` with the zone identifier. + + + **Examples:** + + Using the US Eastern time zone as an example, we can see that a ``tzfile`` + provides time zone information for the standard Daylight Saving offsets: + + .. testsetup:: tzfile + + from dateutil.tz import gettz + from datetime import datetime + + .. doctest:: tzfile + + >>> NYC = gettz('America/New_York') + >>> NYC + tzfile('/usr/share/zoneinfo/America/New_York') + + >>> print(datetime(2016, 1, 3, tzinfo=NYC)) # EST + 2016-01-03 00:00:00-05:00 + + >>> print(datetime(2016, 7, 7, tzinfo=NYC)) # EDT + 2016-07-07 00:00:00-04:00 + + + The ``tzfile`` structure contains a fully history of the time zone, + so historical dates will also have the right offsets. For example, before + the adoption of the UTC standards, New York used local solar mean time: + + .. doctest:: tzfile + + >>> print(datetime(1901, 4, 12, tzinfo=NYC)) # LMT + 1901-04-12 00:00:00-04:56 + + And during World War II, New York was on "Eastern War Time", which was a + state of permanent daylight saving time: + + .. doctest:: tzfile + + >>> print(datetime(1944, 2, 7, tzinfo=NYC)) # EWT + 1944-02-07 00:00:00-04:00 + + """ + + def __init__(self, fileobj, filename=None): + super(tzfile, self).__init__() + + file_opened_here = False + if isinstance(fileobj, string_types): + self._filename = fileobj + fileobj = open(fileobj, 'rb') + file_opened_here = True + elif filename is not None: + self._filename = filename + elif hasattr(fileobj, "name"): + self._filename = fileobj.name + else: + self._filename = repr(fileobj) + + if fileobj is not None: + if not file_opened_here: + fileobj = _nullcontext(fileobj) + + with fileobj as file_stream: + tzobj = self._read_tzfile(file_stream) + + self._set_tzdata(tzobj) + + def _set_tzdata(self, tzobj): + """ Set the time zone data of this object from a _tzfile object """ + # Copy the relevant attributes over as private attributes + for attr in _tzfile.attrs: + setattr(self, '_' + attr, getattr(tzobj, attr)) + + def _read_tzfile(self, fileobj): + out = _tzfile() + + # From tzfile(5): + # + # The time zone information files used by tzset(3) + # begin with the magic characters "TZif" to identify + # them as time zone information files, followed by + # sixteen bytes reserved for future use, followed by + # six four-byte values of type long, written in a + # ``standard'' byte order (the high-order byte + # of the value is written first). + if fileobj.read(4).decode() != "TZif": + raise ValueError("magic not found") + + fileobj.read(16) + + ( + # The number of UTC/local indicators stored in the file. + ttisgmtcnt, + + # The number of standard/wall indicators stored in the file. + ttisstdcnt, + + # The number of leap seconds for which data is + # stored in the file. + leapcnt, + + # The number of "transition times" for which data + # is stored in the file. + timecnt, + + # The number of "local time types" for which data + # is stored in the file (must not be zero). + typecnt, + + # The number of characters of "time zone + # abbreviation strings" stored in the file. + charcnt, + + ) = struct.unpack(">6l", fileobj.read(24)) + + # The above header is followed by tzh_timecnt four-byte + # values of type long, sorted in ascending order. + # These values are written in ``standard'' byte order. + # Each is used as a transition time (as returned by + # time(2)) at which the rules for computing local time + # change. + + if timecnt: + out.trans_list_utc = list(struct.unpack(">%dl" % timecnt, + fileobj.read(timecnt*4))) + else: + out.trans_list_utc = [] + + # Next come tzh_timecnt one-byte values of type unsigned + # char; each one tells which of the different types of + # ``local time'' types described in the file is associated + # with the same-indexed transition time. These values + # serve as indices into an array of ttinfo structures that + # appears next in the file. + + if timecnt: + out.trans_idx = struct.unpack(">%dB" % timecnt, + fileobj.read(timecnt)) + else: + out.trans_idx = [] + + # Each ttinfo structure is written as a four-byte value + # for tt_gmtoff of type long, in a standard byte + # order, followed by a one-byte value for tt_isdst + # and a one-byte value for tt_abbrind. In each + # structure, tt_gmtoff gives the number of + # seconds to be added to UTC, tt_isdst tells whether + # tm_isdst should be set by localtime(3), and + # tt_abbrind serves as an index into the array of + # time zone abbreviation characters that follow the + # ttinfo structure(s) in the file. + + ttinfo = [] + + for i in range(typecnt): + ttinfo.append(struct.unpack(">lbb", fileobj.read(6))) + + abbr = fileobj.read(charcnt).decode() + + # Then there are tzh_leapcnt pairs of four-byte + # values, written in standard byte order; the + # first value of each pair gives the time (as + # returned by time(2)) at which a leap second + # occurs; the second gives the total number of + # leap seconds to be applied after the given time. + # The pairs of values are sorted in ascending order + # by time. + + # Not used, for now (but seek for correct file position) + if leapcnt: + fileobj.seek(leapcnt * 8, os.SEEK_CUR) + + # Then there are tzh_ttisstdcnt standard/wall + # indicators, each stored as a one-byte value; + # they tell whether the transition times associated + # with local time types were specified as standard + # time or wall clock time, and are used when + # a time zone file is used in handling POSIX-style + # time zone environment variables. + + if ttisstdcnt: + isstd = struct.unpack(">%db" % ttisstdcnt, + fileobj.read(ttisstdcnt)) + + # Finally, there are tzh_ttisgmtcnt UTC/local + # indicators, each stored as a one-byte value; + # they tell whether the transition times associated + # with local time types were specified as UTC or + # local time, and are used when a time zone file + # is used in handling POSIX-style time zone envi- + # ronment variables. + + if ttisgmtcnt: + isgmt = struct.unpack(">%db" % ttisgmtcnt, + fileobj.read(ttisgmtcnt)) + + # Build ttinfo list + out.ttinfo_list = [] + for i in range(typecnt): + gmtoff, isdst, abbrind = ttinfo[i] + gmtoff = _get_supported_offset(gmtoff) + tti = _ttinfo() + tti.offset = gmtoff + tti.dstoffset = datetime.timedelta(0) + tti.delta = datetime.timedelta(seconds=gmtoff) + tti.isdst = isdst + tti.abbr = abbr[abbrind:abbr.find('\x00', abbrind)] + tti.isstd = (ttisstdcnt > i and isstd[i] != 0) + tti.isgmt = (ttisgmtcnt > i and isgmt[i] != 0) + out.ttinfo_list.append(tti) + + # Replace ttinfo indexes for ttinfo objects. + out.trans_idx = [out.ttinfo_list[idx] for idx in out.trans_idx] + + # Set standard, dst, and before ttinfos. before will be + # used when a given time is before any transitions, + # and will be set to the first non-dst ttinfo, or to + # the first dst, if all of them are dst. + out.ttinfo_std = None + out.ttinfo_dst = None + out.ttinfo_before = None + if out.ttinfo_list: + if not out.trans_list_utc: + out.ttinfo_std = out.ttinfo_first = out.ttinfo_list[0] + else: + for i in range(timecnt-1, -1, -1): + tti = out.trans_idx[i] + if not out.ttinfo_std and not tti.isdst: + out.ttinfo_std = tti + elif not out.ttinfo_dst and tti.isdst: + out.ttinfo_dst = tti + + if out.ttinfo_std and out.ttinfo_dst: + break + else: + if out.ttinfo_dst and not out.ttinfo_std: + out.ttinfo_std = out.ttinfo_dst + + for tti in out.ttinfo_list: + if not tti.isdst: + out.ttinfo_before = tti + break + else: + out.ttinfo_before = out.ttinfo_list[0] + + # Now fix transition times to become relative to wall time. + # + # I'm not sure about this. In my tests, the tz source file + # is setup to wall time, and in the binary file isstd and + # isgmt are off, so it should be in wall time. OTOH, it's + # always in gmt time. Let me know if you have comments + # about this. + lastdst = None + lastoffset = None + lastdstoffset = None + lastbaseoffset = None + out.trans_list = [] + + for i, tti in enumerate(out.trans_idx): + offset = tti.offset + dstoffset = 0 + + if lastdst is not None: + if tti.isdst: + if not lastdst: + dstoffset = offset - lastoffset + + if not dstoffset and lastdstoffset: + dstoffset = lastdstoffset + + tti.dstoffset = datetime.timedelta(seconds=dstoffset) + lastdstoffset = dstoffset + + # If a time zone changes its base offset during a DST transition, + # then you need to adjust by the previous base offset to get the + # transition time in local time. Otherwise you use the current + # base offset. Ideally, I would have some mathematical proof of + # why this is true, but I haven't really thought about it enough. + baseoffset = offset - dstoffset + adjustment = baseoffset + if (lastbaseoffset is not None and baseoffset != lastbaseoffset + and tti.isdst != lastdst): + # The base DST has changed + adjustment = lastbaseoffset + + lastdst = tti.isdst + lastoffset = offset + lastbaseoffset = baseoffset + + out.trans_list.append(out.trans_list_utc[i] + adjustment) + + out.trans_idx = tuple(out.trans_idx) + out.trans_list = tuple(out.trans_list) + out.trans_list_utc = tuple(out.trans_list_utc) + + return out + + def _find_last_transition(self, dt, in_utc=False): + # If there's no list, there are no transitions to find + if not self._trans_list: + return None + + timestamp = _datetime_to_timestamp(dt) + + # Find where the timestamp fits in the transition list - if the + # timestamp is a transition time, it's part of the "after" period. + trans_list = self._trans_list_utc if in_utc else self._trans_list + idx = bisect.bisect_right(trans_list, timestamp) + + # We want to know when the previous transition was, so subtract off 1 + return idx - 1 + + def _get_ttinfo(self, idx): + # For no list or after the last transition, default to _ttinfo_std + if idx is None or (idx + 1) >= len(self._trans_list): + return self._ttinfo_std + + # If there is a list and the time is before it, return _ttinfo_before + if idx < 0: + return self._ttinfo_before + + return self._trans_idx[idx] + + def _find_ttinfo(self, dt): + idx = self._resolve_ambiguous_time(dt) + + return self._get_ttinfo(idx) + + def fromutc(self, dt): + """ + The ``tzfile`` implementation of :py:func:`datetime.tzinfo.fromutc`. + + :param dt: + A :py:class:`datetime.datetime` object. + + :raises TypeError: + Raised if ``dt`` is not a :py:class:`datetime.datetime` object. + + :raises ValueError: + Raised if this is called with a ``dt`` which does not have this + ``tzinfo`` attached. + + :return: + Returns a :py:class:`datetime.datetime` object representing the + wall time in ``self``'s time zone. + """ + # These isinstance checks are in datetime.tzinfo, so we'll preserve + # them, even if we don't care about duck typing. + if not isinstance(dt, datetime.datetime): + raise TypeError("fromutc() requires a datetime argument") + + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + # First treat UTC as wall time and get the transition we're in. + idx = self._find_last_transition(dt, in_utc=True) + tti = self._get_ttinfo(idx) + + dt_out = dt + datetime.timedelta(seconds=tti.offset) + + fold = self.is_ambiguous(dt_out, idx=idx) + + return enfold(dt_out, fold=int(fold)) + + def is_ambiguous(self, dt, idx=None): + """ + Whether or not the "wall time" of a given datetime is ambiguous in this + zone. + + :param dt: + A :py:class:`datetime.datetime`, naive or time zone aware. + + + :return: + Returns ``True`` if ambiguous, ``False`` otherwise. + + .. versionadded:: 2.6.0 + """ + if idx is None: + idx = self._find_last_transition(dt) + + # Calculate the difference in offsets from current to previous + timestamp = _datetime_to_timestamp(dt) + tti = self._get_ttinfo(idx) + + if idx is None or idx <= 0: + return False + + od = self._get_ttinfo(idx - 1).offset - tti.offset + tt = self._trans_list[idx] # Transition time + + return timestamp < tt + od + + def _resolve_ambiguous_time(self, dt): + idx = self._find_last_transition(dt) + + # If we have no transitions, return the index + _fold = self._fold(dt) + if idx is None or idx == 0: + return idx + + # If it's ambiguous and we're in a fold, shift to a different index. + idx_offset = int(not _fold and self.is_ambiguous(dt, idx)) + + return idx - idx_offset + + def utcoffset(self, dt): + if dt is None: + return None + + if not self._ttinfo_std: + return ZERO + + return self._find_ttinfo(dt).delta + + def dst(self, dt): + if dt is None: + return None + + if not self._ttinfo_dst: + return ZERO + + tti = self._find_ttinfo(dt) + + if not tti.isdst: + return ZERO + + # The documentation says that utcoffset()-dst() must + # be constant for every dt. + return tti.dstoffset + + @tzname_in_python2 + def tzname(self, dt): + if not self._ttinfo_std or dt is None: + return None + return self._find_ttinfo(dt).abbr + + def __eq__(self, other): + if not isinstance(other, tzfile): + return NotImplemented + return (self._trans_list == other._trans_list and + self._trans_idx == other._trans_idx and + self._ttinfo_list == other._ttinfo_list) + + __hash__ = None + + def __ne__(self, other): + return not (self == other) + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self._filename)) + + def __reduce__(self): + return self.__reduce_ex__(None) + + def __reduce_ex__(self, protocol): + return (self.__class__, (None, self._filename), self.__dict__) + + +class tzrange(tzrangebase): + """ + The ``tzrange`` object is a time zone specified by a set of offsets and + abbreviations, equivalent to the way the ``TZ`` variable can be specified + in POSIX-like systems, but using Python delta objects to specify DST + start, end and offsets. + + :param stdabbr: + The abbreviation for standard time (e.g. ``'EST'``). + + :param stdoffset: + An integer or :class:`datetime.timedelta` object or equivalent + specifying the base offset from UTC. + + If unspecified, +00:00 is used. + + :param dstabbr: + The abbreviation for DST / "Summer" time (e.g. ``'EDT'``). + + If specified, with no other DST information, DST is assumed to occur + and the default behavior or ``dstoffset``, ``start`` and ``end`` is + used. If unspecified and no other DST information is specified, it + is assumed that this zone has no DST. + + If this is unspecified and other DST information is *is* specified, + DST occurs in the zone but the time zone abbreviation is left + unchanged. + + :param dstoffset: + A an integer or :class:`datetime.timedelta` object or equivalent + specifying the UTC offset during DST. If unspecified and any other DST + information is specified, it is assumed to be the STD offset +1 hour. + + :param start: + A :class:`relativedelta.relativedelta` object or equivalent specifying + the time and time of year that daylight savings time starts. To + specify, for example, that DST starts at 2AM on the 2nd Sunday in + March, pass: + + ``relativedelta(hours=2, month=3, day=1, weekday=SU(+2))`` + + If unspecified and any other DST information is specified, the default + value is 2 AM on the first Sunday in April. + + :param end: + A :class:`relativedelta.relativedelta` object or equivalent + representing the time and time of year that daylight savings time + ends, with the same specification method as in ``start``. One note is + that this should point to the first time in the *standard* zone, so if + a transition occurs at 2AM in the DST zone and the clocks are set back + 1 hour to 1AM, set the ``hours`` parameter to +1. + + + **Examples:** + + .. testsetup:: tzrange + + from dateutil.tz import tzrange, tzstr + + .. doctest:: tzrange + + >>> tzstr('EST5EDT') == tzrange("EST", -18000, "EDT") + True + + >>> from dateutil.relativedelta import * + >>> range1 = tzrange("EST", -18000, "EDT") + >>> range2 = tzrange("EST", -18000, "EDT", -14400, + ... relativedelta(hours=+2, month=4, day=1, + ... weekday=SU(+1)), + ... relativedelta(hours=+1, month=10, day=31, + ... weekday=SU(-1))) + >>> tzstr('EST5EDT') == range1 == range2 + True + + """ + def __init__(self, stdabbr, stdoffset=None, + dstabbr=None, dstoffset=None, + start=None, end=None): + + global relativedelta + from dateutil import relativedelta + + self._std_abbr = stdabbr + self._dst_abbr = dstabbr + + try: + stdoffset = stdoffset.total_seconds() + except (TypeError, AttributeError): + pass + + try: + dstoffset = dstoffset.total_seconds() + except (TypeError, AttributeError): + pass + + if stdoffset is not None: + self._std_offset = datetime.timedelta(seconds=stdoffset) + else: + self._std_offset = ZERO + + if dstoffset is not None: + self._dst_offset = datetime.timedelta(seconds=dstoffset) + elif dstabbr and stdoffset is not None: + self._dst_offset = self._std_offset + datetime.timedelta(hours=+1) + else: + self._dst_offset = ZERO + + if dstabbr and start is None: + self._start_delta = relativedelta.relativedelta( + hours=+2, month=4, day=1, weekday=relativedelta.SU(+1)) + else: + self._start_delta = start + + if dstabbr and end is None: + self._end_delta = relativedelta.relativedelta( + hours=+1, month=10, day=31, weekday=relativedelta.SU(-1)) + else: + self._end_delta = end + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = bool(self._start_delta) + + def transitions(self, year): + """ + For a given year, get the DST on and off transition times, expressed + always on the standard time side. For zones with no transitions, this + function returns ``None``. + + :param year: + The year whose transitions you would like to query. + + :return: + Returns a :class:`tuple` of :class:`datetime.datetime` objects, + ``(dston, dstoff)`` for zones with an annual DST transition, or + ``None`` for fixed offset zones. + """ + if not self.hasdst: + return None + + base_year = datetime.datetime(year, 1, 1) + + start = base_year + self._start_delta + end = base_year + self._end_delta + + return (start, end) + + def __eq__(self, other): + if not isinstance(other, tzrange): + return NotImplemented + + return (self._std_abbr == other._std_abbr and + self._dst_abbr == other._dst_abbr and + self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset and + self._start_delta == other._start_delta and + self._end_delta == other._end_delta) + + @property + def _dst_base_offset(self): + return self._dst_base_offset_ + + +@six.add_metaclass(_TzStrFactory) +class tzstr(tzrange): + """ + ``tzstr`` objects are time zone objects specified by a time-zone string as + it would be passed to a ``TZ`` variable on POSIX-style systems (see + the `GNU C Library: TZ Variable`_ for more details). + + There is one notable exception, which is that POSIX-style time zones use an + inverted offset format, so normally ``GMT+3`` would be parsed as an offset + 3 hours *behind* GMT. The ``tzstr`` time zone object will parse this as an + offset 3 hours *ahead* of GMT. If you would like to maintain the POSIX + behavior, pass a ``True`` value to ``posix_offset``. + + The :class:`tzrange` object provides the same functionality, but is + specified using :class:`relativedelta.relativedelta` objects. rather than + strings. + + :param s: + A time zone string in ``TZ`` variable format. This can be a + :class:`bytes` (2.x: :class:`str`), :class:`str` (2.x: + :class:`unicode`) or a stream emitting unicode characters + (e.g. :class:`StringIO`). + + :param posix_offset: + Optional. If set to ``True``, interpret strings such as ``GMT+3`` or + ``UTC+3`` as being 3 hours *behind* UTC rather than ahead, per the + POSIX standard. + + .. caution:: + + Prior to version 2.7.0, this function also supported time zones + in the format: + + * ``EST5EDT,4,0,6,7200,10,0,26,7200,3600`` + * ``EST5EDT,4,1,0,7200,10,-1,0,7200,3600`` + + This format is non-standard and has been deprecated; this function + will raise a :class:`DeprecatedTZFormatWarning` until + support is removed in a future version. + + .. _`GNU C Library: TZ Variable`: + https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html + """ + def __init__(self, s, posix_offset=False): + global parser + from dateutil.parser import _parser as parser + + self._s = s + + res = parser._parsetz(s) + if res is None or res.any_unused_tokens: + raise ValueError("unknown string format") + + # Here we break the compatibility with the TZ variable handling. + # GMT-3 actually *means* the timezone -3. + if res.stdabbr in ("GMT", "UTC") and not posix_offset: + res.stdoffset *= -1 + + # We must initialize it first, since _delta() needs + # _std_offset and _dst_offset set. Use False in start/end + # to avoid building it two times. + tzrange.__init__(self, res.stdabbr, res.stdoffset, + res.dstabbr, res.dstoffset, + start=False, end=False) + + if not res.dstabbr: + self._start_delta = None + self._end_delta = None + else: + self._start_delta = self._delta(res.start) + if self._start_delta: + self._end_delta = self._delta(res.end, isend=1) + + self.hasdst = bool(self._start_delta) + + def _delta(self, x, isend=0): + from dateutil import relativedelta + kwargs = {} + if x.month is not None: + kwargs["month"] = x.month + if x.weekday is not None: + kwargs["weekday"] = relativedelta.weekday(x.weekday, x.week) + if x.week > 0: + kwargs["day"] = 1 + else: + kwargs["day"] = 31 + elif x.day: + kwargs["day"] = x.day + elif x.yday is not None: + kwargs["yearday"] = x.yday + elif x.jyday is not None: + kwargs["nlyearday"] = x.jyday + if not kwargs: + # Default is to start on first sunday of april, and end + # on last sunday of october. + if not isend: + kwargs["month"] = 4 + kwargs["day"] = 1 + kwargs["weekday"] = relativedelta.SU(+1) + else: + kwargs["month"] = 10 + kwargs["day"] = 31 + kwargs["weekday"] = relativedelta.SU(-1) + if x.time is not None: + kwargs["seconds"] = x.time + else: + # Default is 2AM. + kwargs["seconds"] = 7200 + if isend: + # Convert to standard time, to follow the documented way + # of working with the extra hour. See the documentation + # of the tzinfo class. + delta = self._dst_offset - self._std_offset + kwargs["seconds"] -= delta.seconds + delta.days * 86400 + return relativedelta.relativedelta(**kwargs) + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self._s)) + + +class _tzicalvtzcomp(object): + def __init__(self, tzoffsetfrom, tzoffsetto, isdst, + tzname=None, rrule=None): + self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom) + self.tzoffsetto = datetime.timedelta(seconds=tzoffsetto) + self.tzoffsetdiff = self.tzoffsetto - self.tzoffsetfrom + self.isdst = isdst + self.tzname = tzname + self.rrule = rrule + + +class _tzicalvtz(_tzinfo): + def __init__(self, tzid, comps=[]): + super(_tzicalvtz, self).__init__() + + self._tzid = tzid + self._comps = comps + self._cachedate = [] + self._cachecomp = [] + self._cache_lock = _thread.allocate_lock() + + def _find_comp(self, dt): + if len(self._comps) == 1: + return self._comps[0] + + dt = dt.replace(tzinfo=None) + + try: + with self._cache_lock: + return self._cachecomp[self._cachedate.index( + (dt, self._fold(dt)))] + except ValueError: + pass + + lastcompdt = None + lastcomp = None + + for comp in self._comps: + compdt = self._find_compdt(comp, dt) + + if compdt and (not lastcompdt or lastcompdt < compdt): + lastcompdt = compdt + lastcomp = comp + + if not lastcomp: + # RFC says nothing about what to do when a given + # time is before the first onset date. We'll look for the + # first standard component, or the first component, if + # none is found. + for comp in self._comps: + if not comp.isdst: + lastcomp = comp + break + else: + lastcomp = comp[0] + + with self._cache_lock: + self._cachedate.insert(0, (dt, self._fold(dt))) + self._cachecomp.insert(0, lastcomp) + + if len(self._cachedate) > 10: + self._cachedate.pop() + self._cachecomp.pop() + + return lastcomp + + def _find_compdt(self, comp, dt): + if comp.tzoffsetdiff < ZERO and self._fold(dt): + dt -= comp.tzoffsetdiff + + compdt = comp.rrule.before(dt, inc=True) + + return compdt + + def utcoffset(self, dt): + if dt is None: + return None + + return self._find_comp(dt).tzoffsetto + + def dst(self, dt): + comp = self._find_comp(dt) + if comp.isdst: + return comp.tzoffsetdiff + else: + return ZERO + + @tzname_in_python2 + def tzname(self, dt): + return self._find_comp(dt).tzname + + def __repr__(self): + return "" % repr(self._tzid) + + __reduce__ = object.__reduce__ + + +class tzical(object): + """ + This object is designed to parse an iCalendar-style ``VTIMEZONE`` structure + as set out in `RFC 5545`_ Section 4.6.5 into one or more `tzinfo` objects. + + :param `fileobj`: + A file or stream in iCalendar format, which should be UTF-8 encoded + with CRLF endings. + + .. _`RFC 5545`: https://tools.ietf.org/html/rfc5545 + """ + def __init__(self, fileobj): + global rrule + from dateutil import rrule + + if isinstance(fileobj, string_types): + self._s = fileobj + # ical should be encoded in UTF-8 with CRLF + fileobj = open(fileobj, 'r') + else: + self._s = getattr(fileobj, 'name', repr(fileobj)) + fileobj = _nullcontext(fileobj) + + self._vtz = {} + + with fileobj as fobj: + self._parse_rfc(fobj.read()) + + def keys(self): + """ + Retrieves the available time zones as a list. + """ + return list(self._vtz.keys()) + + def get(self, tzid=None): + """ + Retrieve a :py:class:`datetime.tzinfo` object by its ``tzid``. + + :param tzid: + If there is exactly one time zone available, omitting ``tzid`` + or passing :py:const:`None` value returns it. Otherwise a valid + key (which can be retrieved from :func:`keys`) is required. + + :raises ValueError: + Raised if ``tzid`` is not specified but there are either more + or fewer than 1 zone defined. + + :returns: + Returns either a :py:class:`datetime.tzinfo` object representing + the relevant time zone or :py:const:`None` if the ``tzid`` was + not found. + """ + if tzid is None: + if len(self._vtz) == 0: + raise ValueError("no timezones defined") + elif len(self._vtz) > 1: + raise ValueError("more than one timezone available") + tzid = next(iter(self._vtz)) + + return self._vtz.get(tzid) + + def _parse_offset(self, s): + s = s.strip() + if not s: + raise ValueError("empty offset") + if s[0] in ('+', '-'): + signal = (-1, +1)[s[0] == '+'] + s = s[1:] + else: + signal = +1 + if len(s) == 4: + return (int(s[:2]) * 3600 + int(s[2:]) * 60) * signal + elif len(s) == 6: + return (int(s[:2]) * 3600 + int(s[2:4]) * 60 + int(s[4:])) * signal + else: + raise ValueError("invalid offset: " + s) + + def _parse_rfc(self, s): + lines = s.splitlines() + if not lines: + raise ValueError("empty string") + + # Unfold + i = 0 + while i < len(lines): + line = lines[i].rstrip() + if not line: + del lines[i] + elif i > 0 and line[0] == " ": + lines[i-1] += line[1:] + del lines[i] + else: + i += 1 + + tzid = None + comps = [] + invtz = False + comptype = None + for line in lines: + if not line: + continue + name, value = line.split(':', 1) + parms = name.split(';') + if not parms: + raise ValueError("empty property name") + name = parms[0].upper() + parms = parms[1:] + if invtz: + if name == "BEGIN": + if value in ("STANDARD", "DAYLIGHT"): + # Process component + pass + else: + raise ValueError("unknown component: "+value) + comptype = value + founddtstart = False + tzoffsetfrom = None + tzoffsetto = None + rrulelines = [] + tzname = None + elif name == "END": + if value == "VTIMEZONE": + if comptype: + raise ValueError("component not closed: "+comptype) + if not tzid: + raise ValueError("mandatory TZID not found") + if not comps: + raise ValueError( + "at least one component is needed") + # Process vtimezone + self._vtz[tzid] = _tzicalvtz(tzid, comps) + invtz = False + elif value == comptype: + if not founddtstart: + raise ValueError("mandatory DTSTART not found") + if tzoffsetfrom is None: + raise ValueError( + "mandatory TZOFFSETFROM not found") + if tzoffsetto is None: + raise ValueError( + "mandatory TZOFFSETFROM not found") + # Process component + rr = None + if rrulelines: + rr = rrule.rrulestr("\n".join(rrulelines), + compatible=True, + ignoretz=True, + cache=True) + comp = _tzicalvtzcomp(tzoffsetfrom, tzoffsetto, + (comptype == "DAYLIGHT"), + tzname, rr) + comps.append(comp) + comptype = None + else: + raise ValueError("invalid component end: "+value) + elif comptype: + if name == "DTSTART": + # DTSTART in VTIMEZONE takes a subset of valid RRULE + # values under RFC 5545. + for parm in parms: + if parm != 'VALUE=DATE-TIME': + msg = ('Unsupported DTSTART param in ' + + 'VTIMEZONE: ' + parm) + raise ValueError(msg) + rrulelines.append(line) + founddtstart = True + elif name in ("RRULE", "RDATE", "EXRULE", "EXDATE"): + rrulelines.append(line) + elif name == "TZOFFSETFROM": + if parms: + raise ValueError( + "unsupported %s parm: %s " % (name, parms[0])) + tzoffsetfrom = self._parse_offset(value) + elif name == "TZOFFSETTO": + if parms: + raise ValueError( + "unsupported TZOFFSETTO parm: "+parms[0]) + tzoffsetto = self._parse_offset(value) + elif name == "TZNAME": + if parms: + raise ValueError( + "unsupported TZNAME parm: "+parms[0]) + tzname = value + elif name == "COMMENT": + pass + else: + raise ValueError("unsupported property: "+name) + else: + if name == "TZID": + if parms: + raise ValueError( + "unsupported TZID parm: "+parms[0]) + tzid = value + elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"): + pass + else: + raise ValueError("unsupported property: "+name) + elif name == "BEGIN" and value == "VTIMEZONE": + tzid = None + comps = [] + invtz = True + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, repr(self._s)) + + +if sys.platform != "win32": + TZFILES = ["/etc/localtime", "localtime"] + TZPATHS = ["/usr/share/zoneinfo", + "/usr/lib/zoneinfo", + "/usr/share/lib/zoneinfo", + "/etc/zoneinfo"] +else: + TZFILES = [] + TZPATHS = [] + + +def __get_gettz(): + tzlocal_classes = (tzlocal,) + if tzwinlocal is not None: + tzlocal_classes += (tzwinlocal,) + + class GettzFunc(object): + """ + Retrieve a time zone object from a string representation + + This function is intended to retrieve the :py:class:`tzinfo` subclass + that best represents the time zone that would be used if a POSIX + `TZ variable`_ were set to the same value. + + If no argument or an empty string is passed to ``gettz``, local time + is returned: + + .. code-block:: python3 + + >>> gettz() + tzfile('/etc/localtime') + + This function is also the preferred way to map IANA tz database keys + to :class:`tzfile` objects: + + .. code-block:: python3 + + >>> gettz('Pacific/Kiritimati') + tzfile('/usr/share/zoneinfo/Pacific/Kiritimati') + + On Windows, the standard is extended to include the Windows-specific + zone names provided by the operating system: + + .. code-block:: python3 + + >>> gettz('Egypt Standard Time') + tzwin('Egypt Standard Time') + + Passing a GNU ``TZ`` style string time zone specification returns a + :class:`tzstr` object: + + .. code-block:: python3 + + >>> gettz('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3') + tzstr('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3') + + :param name: + A time zone name (IANA, or, on Windows, Windows keys), location of + a ``tzfile(5)`` zoneinfo file or ``TZ`` variable style time zone + specifier. An empty string, no argument or ``None`` is interpreted + as local time. + + :return: + Returns an instance of one of ``dateutil``'s :py:class:`tzinfo` + subclasses. + + .. versionchanged:: 2.7.0 + + After version 2.7.0, any two calls to ``gettz`` using the same + input strings will return the same object: + + .. code-block:: python3 + + >>> tz.gettz('America/Chicago') is tz.gettz('America/Chicago') + True + + In addition to improving performance, this ensures that + `"same zone" semantics`_ are used for datetimes in the same zone. + + + .. _`TZ variable`: + https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html + + .. _`"same zone" semantics`: + https://blog.ganssle.io/articles/2018/02/aware-datetime-arithmetic.html + """ + def __init__(self): + + self.__instances = weakref.WeakValueDictionary() + self.__strong_cache_size = 8 + self.__strong_cache = OrderedDict() + self._cache_lock = _thread.allocate_lock() + + def __call__(self, name=None): + with self._cache_lock: + rv = self.__instances.get(name, None) + + if rv is None: + rv = self.nocache(name=name) + if not (name is None + or isinstance(rv, tzlocal_classes) + or rv is None): + # tzlocal is slightly more complicated than the other + # time zone providers because it depends on environment + # at construction time, so don't cache that. + # + # We also cannot store weak references to None, so we + # will also not store that. + self.__instances[name] = rv + else: + # No need for strong caching, return immediately + return rv + + self.__strong_cache[name] = self.__strong_cache.pop(name, rv) + + if len(self.__strong_cache) > self.__strong_cache_size: + self.__strong_cache.popitem(last=False) + + return rv + + def set_cache_size(self, size): + with self._cache_lock: + self.__strong_cache_size = size + while len(self.__strong_cache) > size: + self.__strong_cache.popitem(last=False) + + def cache_clear(self): + with self._cache_lock: + self.__instances = weakref.WeakValueDictionary() + self.__strong_cache.clear() + + @staticmethod + def nocache(name=None): + """A non-cached version of gettz""" + tz = None + if not name: + try: + name = os.environ["TZ"] + except KeyError: + pass + if name is None or name in ("", ":"): + for filepath in TZFILES: + if not os.path.isabs(filepath): + filename = filepath + for path in TZPATHS: + filepath = os.path.join(path, filename) + if os.path.isfile(filepath): + break + else: + continue + if os.path.isfile(filepath): + try: + tz = tzfile(filepath) + break + except (IOError, OSError, ValueError): + pass + else: + tz = tzlocal() + else: + try: + if name.startswith(":"): + name = name[1:] + except TypeError as e: + if isinstance(name, bytes): + new_msg = "gettz argument should be str, not bytes" + six.raise_from(TypeError(new_msg), e) + else: + raise + if os.path.isabs(name): + if os.path.isfile(name): + tz = tzfile(name) + else: + tz = None + else: + for path in TZPATHS: + filepath = os.path.join(path, name) + if not os.path.isfile(filepath): + filepath = filepath.replace(' ', '_') + if not os.path.isfile(filepath): + continue + try: + tz = tzfile(filepath) + break + except (IOError, OSError, ValueError): + pass + else: + tz = None + if tzwin is not None: + try: + tz = tzwin(name) + except (WindowsError, UnicodeEncodeError): + # UnicodeEncodeError is for Python 2.7 compat + tz = None + + if not tz: + from dateutil.zoneinfo import get_zonefile_instance + tz = get_zonefile_instance().get(name) + + if not tz: + for c in name: + # name is not a tzstr unless it has at least + # one offset. For short values of "name", an + # explicit for loop seems to be the fastest way + # To determine if a string contains a digit + if c in "0123456789": + try: + tz = tzstr(name) + except ValueError: + pass + break + else: + if name in ("GMT", "UTC"): + tz = UTC + elif name in time.tzname: + tz = tzlocal() + return tz + + return GettzFunc() + + +gettz = __get_gettz() +del __get_gettz + + +def datetime_exists(dt, tz=None): + """ + Given a datetime and a time zone, determine whether or not a given datetime + would fall in a gap. + + :param dt: + A :class:`datetime.datetime` (whose time zone will be ignored if ``tz`` + is provided.) + + :param tz: + A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If + ``None`` or not provided, the datetime's own time zone will be used. + + :return: + Returns a boolean value whether or not the "wall time" exists in + ``tz``. + + .. versionadded:: 2.7.0 + """ + if tz is None: + if dt.tzinfo is None: + raise ValueError('Datetime is naive and no time zone provided.') + tz = dt.tzinfo + + dt = dt.replace(tzinfo=None) + + # This is essentially a test of whether or not the datetime can survive + # a round trip to UTC. + dt_rt = dt.replace(tzinfo=tz).astimezone(UTC).astimezone(tz) + dt_rt = dt_rt.replace(tzinfo=None) + + return dt == dt_rt + + +def datetime_ambiguous(dt, tz=None): + """ + Given a datetime and a time zone, determine whether or not a given datetime + is ambiguous (i.e if there are two times differentiated only by their DST + status). + + :param dt: + A :class:`datetime.datetime` (whose time zone will be ignored if ``tz`` + is provided.) + + :param tz: + A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If + ``None`` or not provided, the datetime's own time zone will be used. + + :return: + Returns a boolean value whether or not the "wall time" is ambiguous in + ``tz``. + + .. versionadded:: 2.6.0 + """ + if tz is None: + if dt.tzinfo is None: + raise ValueError('Datetime is naive and no time zone provided.') + + tz = dt.tzinfo + + # If a time zone defines its own "is_ambiguous" function, we'll use that. + is_ambiguous_fn = getattr(tz, 'is_ambiguous', None) + if is_ambiguous_fn is not None: + try: + return tz.is_ambiguous(dt) + except Exception: + pass + + # If it doesn't come out and tell us it's ambiguous, we'll just check if + # the fold attribute has any effect on this particular date and time. + dt = dt.replace(tzinfo=tz) + wall_0 = enfold(dt, fold=0) + wall_1 = enfold(dt, fold=1) + + same_offset = wall_0.utcoffset() == wall_1.utcoffset() + same_dst = wall_0.dst() == wall_1.dst() + + return not (same_offset and same_dst) + + +def resolve_imaginary(dt): + """ + Given a datetime that may be imaginary, return an existing datetime. + + This function assumes that an imaginary datetime represents what the + wall time would be in a zone had the offset transition not occurred, so + it will always fall forward by the transition's change in offset. + + .. doctest:: + + >>> from dateutil import tz + >>> from datetime import datetime + >>> NYC = tz.gettz('America/New_York') + >>> print(tz.resolve_imaginary(datetime(2017, 3, 12, 2, 30, tzinfo=NYC))) + 2017-03-12 03:30:00-04:00 + + >>> KIR = tz.gettz('Pacific/Kiritimati') + >>> print(tz.resolve_imaginary(datetime(1995, 1, 1, 12, 30, tzinfo=KIR))) + 1995-01-02 12:30:00+14:00 + + As a note, :func:`datetime.astimezone` is guaranteed to produce a valid, + existing datetime, so a round-trip to and from UTC is sufficient to get + an extant datetime, however, this generally "falls back" to an earlier time + rather than falling forward to the STD side (though no guarantees are made + about this behavior). + + :param dt: + A :class:`datetime.datetime` which may or may not exist. + + :return: + Returns an existing :class:`datetime.datetime`. If ``dt`` was not + imaginary, the datetime returned is guaranteed to be the same object + passed to the function. + + .. versionadded:: 2.7.0 + """ + if dt.tzinfo is not None and not datetime_exists(dt): + + curr_offset = (dt + datetime.timedelta(hours=24)).utcoffset() + old_offset = (dt - datetime.timedelta(hours=24)).utcoffset() + + dt += curr_offset - old_offset + + return dt + + +def _datetime_to_timestamp(dt): + """ + Convert a :class:`datetime.datetime` object to an epoch timestamp in + seconds since January 1, 1970, ignoring the time zone. + """ + return (dt.replace(tzinfo=None) - EPOCH).total_seconds() + + +if sys.version_info >= (3, 6): + def _get_supported_offset(second_offset): + return second_offset +else: + def _get_supported_offset(second_offset): + # For python pre-3.6, round to full-minutes if that's not the case. + # Python's datetime doesn't accept sub-minute timezones. Check + # http://python.org/sf/1447945 or https://bugs.python.org/issue5288 + # for some information. + old_offset = second_offset + calculated_offset = 60 * ((second_offset + 30) // 60) + return calculated_offset + + +try: + # Python 3.7 feature + from contextlib import nullcontext as _nullcontext +except ImportError: + class _nullcontext(object): + """ + Class for wrapping contexts so that they are passed through in a + with statement. + """ + def __init__(self, context): + self.context = context + + def __enter__(self): + return self.context + + def __exit__(*args, **kwargs): + pass + +# vim:ts=4:sw=4:et diff --git a/.venv/lib/python3.9/site-packages/dateutil/tz/win.py b/.venv/lib/python3.9/site-packages/dateutil/tz/win.py new file mode 100644 index 00000000..cde07ba7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/tz/win.py @@ -0,0 +1,370 @@ +# -*- coding: utf-8 -*- +""" +This module provides an interface to the native time zone data on Windows, +including :py:class:`datetime.tzinfo` implementations. + +Attempting to import this module on a non-Windows platform will raise an +:py:obj:`ImportError`. +""" +# This code was originally contributed by Jeffrey Harris. +import datetime +import struct + +from six.moves import winreg +from six import text_type + +try: + import ctypes + from ctypes import wintypes +except ValueError: + # ValueError is raised on non-Windows systems for some horrible reason. + raise ImportError("Running tzwin on non-Windows system") + +from ._common import tzrangebase + +__all__ = ["tzwin", "tzwinlocal", "tzres"] + +ONEWEEK = datetime.timedelta(7) + +TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones" +TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones" +TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" + + +def _settzkeyname(): + handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) + try: + winreg.OpenKey(handle, TZKEYNAMENT).Close() + TZKEYNAME = TZKEYNAMENT + except WindowsError: + TZKEYNAME = TZKEYNAME9X + handle.Close() + return TZKEYNAME + + +TZKEYNAME = _settzkeyname() + + +class tzres(object): + """ + Class for accessing ``tzres.dll``, which contains timezone name related + resources. + + .. versionadded:: 2.5.0 + """ + p_wchar = ctypes.POINTER(wintypes.WCHAR) # Pointer to a wide char + + def __init__(self, tzres_loc='tzres.dll'): + # Load the user32 DLL so we can load strings from tzres + user32 = ctypes.WinDLL('user32') + + # Specify the LoadStringW function + user32.LoadStringW.argtypes = (wintypes.HINSTANCE, + wintypes.UINT, + wintypes.LPWSTR, + ctypes.c_int) + + self.LoadStringW = user32.LoadStringW + self._tzres = ctypes.WinDLL(tzres_loc) + self.tzres_loc = tzres_loc + + def load_name(self, offset): + """ + Load a timezone name from a DLL offset (integer). + + >>> from dateutil.tzwin import tzres + >>> tzr = tzres() + >>> print(tzr.load_name(112)) + 'Eastern Standard Time' + + :param offset: + A positive integer value referring to a string from the tzres dll. + + .. note:: + + Offsets found in the registry are generally of the form + ``@tzres.dll,-114``. The offset in this case is 114, not -114. + + """ + resource = self.p_wchar() + lpBuffer = ctypes.cast(ctypes.byref(resource), wintypes.LPWSTR) + nchar = self.LoadStringW(self._tzres._handle, offset, lpBuffer, 0) + return resource[:nchar] + + def name_from_string(self, tzname_str): + """ + Parse strings as returned from the Windows registry into the time zone + name as defined in the registry. + + >>> from dateutil.tzwin import tzres + >>> tzr = tzres() + >>> print(tzr.name_from_string('@tzres.dll,-251')) + 'Dateline Daylight Time' + >>> print(tzr.name_from_string('Eastern Standard Time')) + 'Eastern Standard Time' + + :param tzname_str: + A timezone name string as returned from a Windows registry key. + + :return: + Returns the localized timezone string from tzres.dll if the string + is of the form `@tzres.dll,-offset`, else returns the input string. + """ + if not tzname_str.startswith('@'): + return tzname_str + + name_splt = tzname_str.split(',-') + try: + offset = int(name_splt[1]) + except: + raise ValueError("Malformed timezone string.") + + return self.load_name(offset) + + +class tzwinbase(tzrangebase): + """tzinfo class based on win32's timezones available in the registry.""" + def __init__(self): + raise NotImplementedError('tzwinbase is an abstract base class') + + def __eq__(self, other): + # Compare on all relevant dimensions, including name. + if not isinstance(other, tzwinbase): + return NotImplemented + + return (self._std_offset == other._std_offset and + self._dst_offset == other._dst_offset and + self._stddayofweek == other._stddayofweek and + self._dstdayofweek == other._dstdayofweek and + self._stdweeknumber == other._stdweeknumber and + self._dstweeknumber == other._dstweeknumber and + self._stdhour == other._stdhour and + self._dsthour == other._dsthour and + self._stdminute == other._stdminute and + self._dstminute == other._dstminute and + self._std_abbr == other._std_abbr and + self._dst_abbr == other._dst_abbr) + + @staticmethod + def list(): + """Return a list of all time zones known to the system.""" + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + with winreg.OpenKey(handle, TZKEYNAME) as tzkey: + result = [winreg.EnumKey(tzkey, i) + for i in range(winreg.QueryInfoKey(tzkey)[0])] + return result + + def display(self): + """ + Return the display name of the time zone. + """ + return self._display + + def transitions(self, year): + """ + For a given year, get the DST on and off transition times, expressed + always on the standard time side. For zones with no transitions, this + function returns ``None``. + + :param year: + The year whose transitions you would like to query. + + :return: + Returns a :class:`tuple` of :class:`datetime.datetime` objects, + ``(dston, dstoff)`` for zones with an annual DST transition, or + ``None`` for fixed offset zones. + """ + + if not self.hasdst: + return None + + dston = picknthweekday(year, self._dstmonth, self._dstdayofweek, + self._dsthour, self._dstminute, + self._dstweeknumber) + + dstoff = picknthweekday(year, self._stdmonth, self._stddayofweek, + self._stdhour, self._stdminute, + self._stdweeknumber) + + # Ambiguous dates default to the STD side + dstoff -= self._dst_base_offset + + return dston, dstoff + + def _get_hasdst(self): + return self._dstmonth != 0 + + @property + def _dst_base_offset(self): + return self._dst_base_offset_ + + +class tzwin(tzwinbase): + """ + Time zone object created from the zone info in the Windows registry + + These are similar to :py:class:`dateutil.tz.tzrange` objects in that + the time zone data is provided in the format of a single offset rule + for either 0 or 2 time zone transitions per year. + + :param: name + The name of a Windows time zone key, e.g. "Eastern Standard Time". + The full list of keys can be retrieved with :func:`tzwin.list`. + """ + + def __init__(self, name): + self._name = name + + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + tzkeyname = text_type("{kn}\\{name}").format(kn=TZKEYNAME, name=name) + with winreg.OpenKey(handle, tzkeyname) as tzkey: + keydict = valuestodict(tzkey) + + self._std_abbr = keydict["Std"] + self._dst_abbr = keydict["Dlt"] + + self._display = keydict["Display"] + + # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm + tup = struct.unpack("=3l16h", keydict["TZI"]) + stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1 + dstoffset = stdoffset-tup[2] # + DaylightBias * -1 + self._std_offset = datetime.timedelta(minutes=stdoffset) + self._dst_offset = datetime.timedelta(minutes=dstoffset) + + # for the meaning see the win32 TIME_ZONE_INFORMATION structure docs + # http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx + (self._stdmonth, + self._stddayofweek, # Sunday = 0 + self._stdweeknumber, # Last = 5 + self._stdhour, + self._stdminute) = tup[4:9] + + (self._dstmonth, + self._dstdayofweek, # Sunday = 0 + self._dstweeknumber, # Last = 5 + self._dsthour, + self._dstminute) = tup[12:17] + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = self._get_hasdst() + + def __repr__(self): + return "tzwin(%s)" % repr(self._name) + + def __reduce__(self): + return (self.__class__, (self._name,)) + + +class tzwinlocal(tzwinbase): + """ + Class representing the local time zone information in the Windows registry + + While :class:`dateutil.tz.tzlocal` makes system calls (via the :mod:`time` + module) to retrieve time zone information, ``tzwinlocal`` retrieves the + rules directly from the Windows registry and creates an object like + :class:`dateutil.tz.tzwin`. + + Because Windows does not have an equivalent of :func:`time.tzset`, on + Windows, :class:`dateutil.tz.tzlocal` instances will always reflect the + time zone settings *at the time that the process was started*, meaning + changes to the machine's time zone settings during the run of a program + on Windows will **not** be reflected by :class:`dateutil.tz.tzlocal`. + Because ``tzwinlocal`` reads the registry directly, it is unaffected by + this issue. + """ + def __init__(self): + with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: + with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey: + keydict = valuestodict(tzlocalkey) + + self._std_abbr = keydict["StandardName"] + self._dst_abbr = keydict["DaylightName"] + + try: + tzkeyname = text_type('{kn}\\{sn}').format(kn=TZKEYNAME, + sn=self._std_abbr) + with winreg.OpenKey(handle, tzkeyname) as tzkey: + _keydict = valuestodict(tzkey) + self._display = _keydict["Display"] + except OSError: + self._display = None + + stdoffset = -keydict["Bias"]-keydict["StandardBias"] + dstoffset = stdoffset-keydict["DaylightBias"] + + self._std_offset = datetime.timedelta(minutes=stdoffset) + self._dst_offset = datetime.timedelta(minutes=dstoffset) + + # For reasons unclear, in this particular key, the day of week has been + # moved to the END of the SYSTEMTIME structure. + tup = struct.unpack("=8h", keydict["StandardStart"]) + + (self._stdmonth, + self._stdweeknumber, # Last = 5 + self._stdhour, + self._stdminute) = tup[1:5] + + self._stddayofweek = tup[7] + + tup = struct.unpack("=8h", keydict["DaylightStart"]) + + (self._dstmonth, + self._dstweeknumber, # Last = 5 + self._dsthour, + self._dstminute) = tup[1:5] + + self._dstdayofweek = tup[7] + + self._dst_base_offset_ = self._dst_offset - self._std_offset + self.hasdst = self._get_hasdst() + + def __repr__(self): + return "tzwinlocal()" + + def __str__(self): + # str will return the standard name, not the daylight name. + return "tzwinlocal(%s)" % repr(self._std_abbr) + + def __reduce__(self): + return (self.__class__, ()) + + +def picknthweekday(year, month, dayofweek, hour, minute, whichweek): + """ dayofweek == 0 means Sunday, whichweek 5 means last instance """ + first = datetime.datetime(year, month, 1, hour, minute) + + # This will work if dayofweek is ISO weekday (1-7) or Microsoft-style (0-6), + # Because 7 % 7 = 0 + weekdayone = first.replace(day=((dayofweek - first.isoweekday()) % 7) + 1) + wd = weekdayone + ((whichweek - 1) * ONEWEEK) + if (wd.month != month): + wd -= ONEWEEK + + return wd + + +def valuestodict(key): + """Convert a registry key's values to a dictionary.""" + dout = {} + size = winreg.QueryInfoKey(key)[1] + tz_res = None + + for i in range(size): + key_name, value, dtype = winreg.EnumValue(key, i) + if dtype == winreg.REG_DWORD or dtype == winreg.REG_DWORD_LITTLE_ENDIAN: + # If it's a DWORD (32-bit integer), it's stored as unsigned - convert + # that to a proper signed integer + if value & (1 << 31): + value = value - (1 << 32) + elif dtype == winreg.REG_SZ: + # If it's a reference to the tzres DLL, load the actual string + if value.startswith('@tzres'): + tz_res = tz_res or tzres() + value = tz_res.name_from_string(value) + + value = value.rstrip('\x00') # Remove trailing nulls + + dout[key_name] = value + + return dout diff --git a/.venv/lib/python3.9/site-packages/dateutil/tzwin.py b/.venv/lib/python3.9/site-packages/dateutil/tzwin.py new file mode 100644 index 00000000..cebc673e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/tzwin.py @@ -0,0 +1,2 @@ +# tzwin has moved to dateutil.tz.win +from .tz.win import * diff --git a/.venv/lib/python3.9/site-packages/dateutil/utils.py b/.venv/lib/python3.9/site-packages/dateutil/utils.py new file mode 100644 index 00000000..dd2d245a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/utils.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +""" +This module offers general convenience and utility functions for dealing with +datetimes. + +.. versionadded:: 2.7.0 +""" +from __future__ import unicode_literals + +from datetime import datetime, time + + +def today(tzinfo=None): + """ + Returns a :py:class:`datetime` representing the current day at midnight + + :param tzinfo: + The time zone to attach (also used to determine the current day). + + :return: + A :py:class:`datetime.datetime` object representing the current day + at midnight. + """ + + dt = datetime.now(tzinfo) + return datetime.combine(dt.date(), time(0, tzinfo=tzinfo)) + + +def default_tzinfo(dt, tzinfo): + """ + Sets the ``tzinfo`` parameter on naive datetimes only + + This is useful for example when you are provided a datetime that may have + either an implicit or explicit time zone, such as when parsing a time zone + string. + + .. doctest:: + + >>> from dateutil.tz import tzoffset + >>> from dateutil.parser import parse + >>> from dateutil.utils import default_tzinfo + >>> dflt_tz = tzoffset("EST", -18000) + >>> print(default_tzinfo(parse('2014-01-01 12:30 UTC'), dflt_tz)) + 2014-01-01 12:30:00+00:00 + >>> print(default_tzinfo(parse('2014-01-01 12:30'), dflt_tz)) + 2014-01-01 12:30:00-05:00 + + :param dt: + The datetime on which to replace the time zone + + :param tzinfo: + The :py:class:`datetime.tzinfo` subclass instance to assign to + ``dt`` if (and only if) it is naive. + + :return: + Returns an aware :py:class:`datetime.datetime`. + """ + if dt.tzinfo is not None: + return dt + else: + return dt.replace(tzinfo=tzinfo) + + +def within_delta(dt1, dt2, delta): + """ + Useful for comparing two datetimes that may have a negligible difference + to be considered equal. + """ + delta = abs(delta) + difference = dt1 - dt2 + return -delta <= difference <= delta diff --git a/.venv/lib/python3.9/site-packages/dateutil/zoneinfo/__init__.py b/.venv/lib/python3.9/site-packages/dateutil/zoneinfo/__init__.py new file mode 100644 index 00000000..34f11ad6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/zoneinfo/__init__.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +import warnings +import json + +from tarfile import TarFile +from pkgutil import get_data +from io import BytesIO + +from dateutil.tz import tzfile as _tzfile + +__all__ = ["get_zonefile_instance", "gettz", "gettz_db_metadata"] + +ZONEFILENAME = "dateutil-zoneinfo.tar.gz" +METADATA_FN = 'METADATA' + + +class tzfile(_tzfile): + def __reduce__(self): + return (gettz, (self._filename,)) + + +def getzoneinfofile_stream(): + try: + return BytesIO(get_data(__name__, ZONEFILENAME)) + except IOError as e: # TODO switch to FileNotFoundError? + warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror)) + return None + + +class ZoneInfoFile(object): + def __init__(self, zonefile_stream=None): + if zonefile_stream is not None: + with TarFile.open(fileobj=zonefile_stream) as tf: + self.zones = {zf.name: tzfile(tf.extractfile(zf), filename=zf.name) + for zf in tf.getmembers() + if zf.isfile() and zf.name != METADATA_FN} + # deal with links: They'll point to their parent object. Less + # waste of memory + links = {zl.name: self.zones[zl.linkname] + for zl in tf.getmembers() if + zl.islnk() or zl.issym()} + self.zones.update(links) + try: + metadata_json = tf.extractfile(tf.getmember(METADATA_FN)) + metadata_str = metadata_json.read().decode('UTF-8') + self.metadata = json.loads(metadata_str) + except KeyError: + # no metadata in tar file + self.metadata = None + else: + self.zones = {} + self.metadata = None + + def get(self, name, default=None): + """ + Wrapper for :func:`ZoneInfoFile.zones.get`. This is a convenience method + for retrieving zones from the zone dictionary. + + :param name: + The name of the zone to retrieve. (Generally IANA zone names) + + :param default: + The value to return in the event of a missing key. + + .. versionadded:: 2.6.0 + + """ + return self.zones.get(name, default) + + +# The current API has gettz as a module function, although in fact it taps into +# a stateful class. So as a workaround for now, without changing the API, we +# will create a new "global" class instance the first time a user requests a +# timezone. Ugly, but adheres to the api. +# +# TODO: Remove after deprecation period. +_CLASS_ZONE_INSTANCE = [] + + +def get_zonefile_instance(new_instance=False): + """ + This is a convenience function which provides a :class:`ZoneInfoFile` + instance using the data provided by the ``dateutil`` package. By default, it + caches a single instance of the ZoneInfoFile object and returns that. + + :param new_instance: + If ``True``, a new instance of :class:`ZoneInfoFile` is instantiated and + used as the cached instance for the next call. Otherwise, new instances + are created only as necessary. + + :return: + Returns a :class:`ZoneInfoFile` object. + + .. versionadded:: 2.6 + """ + if new_instance: + zif = None + else: + zif = getattr(get_zonefile_instance, '_cached_instance', None) + + if zif is None: + zif = ZoneInfoFile(getzoneinfofile_stream()) + + get_zonefile_instance._cached_instance = zif + + return zif + + +def gettz(name): + """ + This retrieves a time zone from the local zoneinfo tarball that is packaged + with dateutil. + + :param name: + An IANA-style time zone name, as found in the zoneinfo file. + + :return: + Returns a :class:`dateutil.tz.tzfile` time zone object. + + .. warning:: + It is generally inadvisable to use this function, and it is only + provided for API compatibility with earlier versions. This is *not* + equivalent to ``dateutil.tz.gettz()``, which selects an appropriate + time zone based on the inputs, favoring system zoneinfo. This is ONLY + for accessing the dateutil-specific zoneinfo (which may be out of + date compared to the system zoneinfo). + + .. deprecated:: 2.6 + If you need to use a specific zoneinfofile over the system zoneinfo, + instantiate a :class:`dateutil.zoneinfo.ZoneInfoFile` object and call + :func:`dateutil.zoneinfo.ZoneInfoFile.get(name)` instead. + + Use :func:`get_zonefile_instance` to retrieve an instance of the + dateutil-provided zoneinfo. + """ + warnings.warn("zoneinfo.gettz() will be removed in future versions, " + "to use the dateutil-provided zoneinfo files, instantiate a " + "ZoneInfoFile object and use ZoneInfoFile.zones.get() " + "instead. See the documentation for details.", + DeprecationWarning) + + if len(_CLASS_ZONE_INSTANCE) == 0: + _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) + return _CLASS_ZONE_INSTANCE[0].zones.get(name) + + +def gettz_db_metadata(): + """ Get the zonefile metadata + + See `zonefile_metadata`_ + + :returns: + A dictionary with the database metadata + + .. deprecated:: 2.6 + See deprecation warning in :func:`zoneinfo.gettz`. To get metadata, + query the attribute ``zoneinfo.ZoneInfoFile.metadata``. + """ + warnings.warn("zoneinfo.gettz_db_metadata() will be removed in future " + "versions, to use the dateutil-provided zoneinfo files, " + "ZoneInfoFile object and query the 'metadata' attribute " + "instead. See the documentation for details.", + DeprecationWarning) + + if len(_CLASS_ZONE_INSTANCE) == 0: + _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) + return _CLASS_ZONE_INSTANCE[0].metadata diff --git a/.venv/lib/python3.9/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz b/.venv/lib/python3.9/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz new file mode 100644 index 00000000..1461f8c8 Binary files /dev/null and b/.venv/lib/python3.9/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz differ diff --git a/.venv/lib/python3.9/site-packages/dateutil/zoneinfo/rebuild.py b/.venv/lib/python3.9/site-packages/dateutil/zoneinfo/rebuild.py new file mode 100644 index 00000000..684c6586 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/dateutil/zoneinfo/rebuild.py @@ -0,0 +1,75 @@ +import logging +import os +import tempfile +import shutil +import json +from subprocess import check_call, check_output +from tarfile import TarFile + +from dateutil.zoneinfo import METADATA_FN, ZONEFILENAME + + +def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None): + """Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar* + + filename is the timezone tarball from ``ftp.iana.org/tz``. + + """ + tmpdir = tempfile.mkdtemp() + zonedir = os.path.join(tmpdir, "zoneinfo") + moduledir = os.path.dirname(__file__) + try: + with TarFile.open(filename) as tf: + for name in zonegroups: + tf.extract(name, tmpdir) + filepaths = [os.path.join(tmpdir, n) for n in zonegroups] + + _run_zic(zonedir, filepaths) + + # write metadata file + with open(os.path.join(zonedir, METADATA_FN), 'w') as f: + json.dump(metadata, f, indent=4, sort_keys=True) + target = os.path.join(moduledir, ZONEFILENAME) + with TarFile.open(target, "w:%s" % format) as tf: + for entry in os.listdir(zonedir): + entrypath = os.path.join(zonedir, entry) + tf.add(entrypath, entry) + finally: + shutil.rmtree(tmpdir) + + +def _run_zic(zonedir, filepaths): + """Calls the ``zic`` compiler in a compatible way to get a "fat" binary. + + Recent versions of ``zic`` default to ``-b slim``, while older versions + don't even have the ``-b`` option (but default to "fat" binaries). The + current version of dateutil does not support Version 2+ TZif files, which + causes problems when used in conjunction with "slim" binaries, so this + function is used to ensure that we always get a "fat" binary. + """ + + try: + help_text = check_output(["zic", "--help"]) + except OSError as e: + _print_on_nosuchfile(e) + raise + + if b"-b " in help_text: + bloat_args = ["-b", "fat"] + else: + bloat_args = [] + + check_call(["zic"] + bloat_args + ["-d", zonedir] + filepaths) + + +def _print_on_nosuchfile(e): + """Print helpful troubleshooting message + + e is an exception raised by subprocess.check_call() + + """ + if e.errno == 2: + logging.error( + "Could not find zic. Perhaps you need to install " + "libc-bin or some other package that provides it, " + "or it's not in your PATH?") diff --git a/.venv/lib/python3.9/site-packages/distutils-precedence.pth b/.venv/lib/python3.9/site-packages/distutils-precedence.pth new file mode 100644 index 00000000..6de4198f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/distutils-precedence.pth @@ -0,0 +1 @@ +import os; var = 'SETUPTOOLS_USE_DISTUTILS'; enabled = os.environ.get(var, 'stdlib') == 'local'; enabled and __import__('_distutils_hack').add_shim(); diff --git a/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/LICENSE b/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/LICENSE new file mode 100644 index 00000000..756d048c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2020] [Paul Davis ] + + 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. diff --git a/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/METADATA b/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/METADATA new file mode 100644 index 00000000..70c12664 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/METADATA @@ -0,0 +1,154 @@ +Metadata-Version: 2.1 +Name: ghp-import +Version: 2.1.0 +Summary: Copy your docs directly to the gh-pages branch. +Home-page: https://github.com/c-w/ghp-import +Author: Paul Joseph Davis +Author-email: paul.joseph.davis@gmail.com +License: Apache Software License +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: python-dateutil (>=2.8.1) +Provides-Extra: dev +Requires-Dist: twine ; extra == 'dev' +Requires-Dist: markdown ; extra == 'dev' +Requires-Dist: flake8 ; extra == 'dev' +Requires-Dist: wheel ; extra == 'dev' + +GitHub Pages Import +=================== + +[![CI status](https://github.com/davisp/ghp-import/workflows/CI/badge.svg)](https://github.com/davisp/ghp-import/actions?query=workflow%3Aci) +[![CircleCI](https://circleci.com/gh/c-w/ghp-import/tree/master.svg?style=svg)](https://circleci.com/gh/c-w/ghp-import/tree/master) +[![TravisCI](https://travis-ci.org/c-w/ghp-import.svg?branch=master)](https://travis-ci.org/c-w/ghp-import) + +[![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://opensource.org/licenses/Apache-2.0) +[![Version](https://img.shields.io/pypi/v/ghp-import.svg)](https://pypi.org/project/ghp-import/) + +As part of [gunicorn][gunicorn], [Benoit Chesneau][benoit] and [Paul Davis][davisp] +were looking at how to host documentation. There's the obvious method of +using [GitHub's post-receive hook][github-post] to trigger doc builds and rsync +to a webserver, but we ended up wanting to try out github's hosting to make the +whole interface a bit more robust. + +[GitHub Pages][gh-pages] is a pretty awesome service that GitHub provides for +hosting project documentation. The only thing is that it requires a +`gh-pages` branch that is the site's document root. This means that keeping +documentation sources in the branch with code is a bit difficult. And it really +turns into a head scratcher for things like [Sphinx][sphinx] that want to +access documentation sources and code sources at the same time. + +Then we stumbled across an interesting looking package called +[github-tools][github-tools] that looked almost like what we wanted. It was a tad +complicated and more involved than we wanted but it gave us an idea. Why not +just write a script that can copy a directory to the `gh-pages` branch of the +repository. This saves us from even having to think about the branch and +everything becomes magical. + +This is what `ghp-import` was written for. + +[gunicorn]: http://www.gunicorn.com/ "Gunicorn" +[benoit]: http://github.com/benoitc "Benoît Chesneau" +[davisp]: http://github.com/davisp "Paul J. Davis" +[github-post]: https://help.github.com/articles/post-receive-hooks "GitHub Post-Receive Hook" +[gh-pages]: http://pages.github.com/ "GitHub Pages" +[sphinx]: http://sphinx.pocoo.org/ "Sphinx Documentation" +[github-tools]: http://dinoboff.github.com/github-tools/ "github-tools" + + +Big Fat Warning +--------------- + +This will **DESTROY** your `gh-pages` branch. If you love it, you'll want to +take backups before playing with this. This script assumes that `gh-pages` is +100% derivative. You should never edit files in your `gh-pages` branch by hand +if you're using this script because you will lose your work. + +When used with a prefix, only files below the set prefix will be destroyed, limiting the +above warning to just that directory and everything below it. + +Usage +----- + +``` +Usage: ghp-import [OPTIONS] DIRECTORY + +Options: + -n, --no-jekyll Include a .nojekyll file in the branch. + -c CNAME, --cname=CNAME + Write a CNAME file with the given CNAME. + -m MESG, --message=MESG + The commit message to use on the target branch. + -p, --push Push the branch to origin/{branch} after committing. + -x PREFIX, --prefix=PREFIX + The prefix to add to each file that gets pushed to the + remote. Only files below this prefix will be cleared + out. [none] + -f, --force Force the push to the repository. + -o, --no-history Force new commit without parent history. + -r REMOTE, --remote=REMOTE + The name of the remote to push to. [origin] + -b BRANCH, --branch=BRANCH + Name of the branch to write to. [gh-pages] + -s, --shell Use the shell when invoking Git. [False] + -l, --follow-links Follow symlinks when adding files. [False] + -h, --help show this help message and exit +``` + +Its pretty simple. Inside your repository just run `ghp-import $DOCS_DIR` +where `$DOCS_DIR` is the path to the **built** documentation. This will write a +commit to your `gh-pages` branch with the current documents in it. + +If you specify `-p` it will also attempt to push the `gh-pages` branch to +GitHub. By default it'll just run `git push origin gh-pages`. You can specify +a different remote using the `-r` flag. + +The `-o` option will discard any previous history and ensure that only a +single commit is always pushed to the `gh-pages` branch. This is useful to +avoid bloating the repository size and is **highly recommended**. + +You can specify a different branch with `-b`. This is useful for user and +organization page, which are served from the `master` branch. + +Some Windows users report needing to pass Git commands through the shell which can be accomplished by passing `-s`. + +The `-l` option will cause the import to follow symlinks for users that have odd configurations that include symlinking outside of their documentation directory. + +Python Usage +------------ + +You can also call ghp_import directly from your Python code as a library. The +library has one public function `ghp_import.ghp_import`, which accepts the +following arguments: + +* `srcdir`: The path to the **built** documentation (required). +* `remote`: The name of the remote to push to. Default: `origin`. +* `branch`: Name of the branch to write to. Default: `gh-pages`. +* `mesg`: The commit message to use on the target branch. Default: `Update documentation`. +* `push`: Push the branch to {remote}/{branch} after committing. Default: `False`. +* `prefix`: The prefix to add to each file that gets pushed to the remote. Default: `None`. +* `force`: Force the push to the repository. Default: `False`. +* `no_history`: Force new commit without parent history. Default: `False`. +* `use_shell`: Default: Use the shell when invoking Git. `False`. +* `followlinks`: Follow symlinks when adding files. Default: `False`. +* `cname`: Write a CNAME file with the given CNAME. Default: `None`. +* `nojekyll`: Include a .nojekyll file in the branch. Default: `False`. + +With Python's current working directory (cwd) inside your repository, do the +following: + +```python +from ghp_import import ghp_import +ghp_import('docs', push=True, cname='example.com') +``` + + diff --git a/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/RECORD b/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/RECORD new file mode 100644 index 00000000..cc9e845c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/RECORD @@ -0,0 +1,10 @@ +../../../../../../../Library/Caches/com.apple.python/Users/orni/MKdocs/DockerLabs/DockerLabs/.venv/lib/python3.9/site-packages/ghp_import.cpython-39.pyc,, +../../../bin/ghp-import,sha256=MJs0TrvReN2uAKyTRkwkW_arKBc12Ueb4QGlcnLlJYs,224 +ghp_import-2.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ghp_import-2.1.0.dist-info/LICENSE,sha256=C8j_tF8m7dHNDeT1BCWuLLRsWMbYrBE5hNQSC-NVr6k,11374 +ghp_import-2.1.0.dist-info/METADATA,sha256=PCrYmDTJ2XjIuUkYM33d1t8Fva95SG2UphvN-t9b6y8,7177 +ghp_import-2.1.0.dist-info/RECORD,, +ghp_import-2.1.0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +ghp_import-2.1.0.dist-info/entry_points.txt,sha256=mk55YA2cS0KmQK9APJFk_1ny9zFWFOZDtOBZMO0cu1Y,48 +ghp_import-2.1.0.dist-info/top_level.txt,sha256=QGVcxjaCFAMEV3ZX7ADAlIMIlsiyfplHNQi-JwrTgow,11 +ghp_import.py,sha256=zvDcFrdka_GzgEkD1BjJrBfDHD3sCExSbnJHmBE1igU,9234 diff --git a/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/WHEEL new file mode 100644 index 00000000..becc9a66 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/entry_points.txt b/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/entry_points.txt new file mode 100644 index 00000000..6f2959a8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +ghp-import = ghp_import:main + diff --git a/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/top_level.txt new file mode 100644 index 00000000..a780cead --- /dev/null +++ b/.venv/lib/python3.9/site-packages/ghp_import-2.1.0.dist-info/top_level.txt @@ -0,0 +1 @@ +ghp_import diff --git a/.venv/lib/python3.9/site-packages/ghp_import.py b/.venv/lib/python3.9/site-packages/ghp_import.py new file mode 100644 index 00000000..328422bd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/ghp_import.py @@ -0,0 +1,306 @@ +#! /usr/bin/env python + +import errno +import os +import subprocess as sp +import sys +import time +from dateutil import tz +from datetime import datetime + +try: + from shlex import quote +except ImportError: + from pipes import quote + +__all__ = ['ghp_import'] +__version__ = "2.1.0" + + +class GhpError(Exception): + def __init__(self, message): + self.message = message + + +if sys.version_info[0] == 3: + def enc(text): + if isinstance(text, bytes): + return text + return text.encode() + + def dec(text): + if isinstance(text, bytes): + return text.decode('utf-8') + return text + + def write(pipe, data): + try: + pipe.stdin.write(data) + except IOError as e: + if e.errno != errno.EPIPE: + raise +else: + def enc(text): + if isinstance(text, unicode): # noqa F821 + return text.encode('utf-8') + return text + + def dec(text): + if isinstance(text, unicode): # noqa F821 + return text + return text.decode('utf-8') + + def write(pipe, data): + pipe.stdin.write(data) + + +class Git(object): + def __init__(self, use_shell=False): + self.use_shell = use_shell + + self.cmd = None + self.pipe = None + self.stderr = None + self.stdout = None + + def check_repo(self): + if self.call('rev-parse') != 0: + error = self.stderr + if not error: + error = "Unknown Git error" + error = dec(error) + if error.startswith("fatal: "): + error = error[len("fatal: "):] + raise GhpError(error) + + def try_rebase(self, remote, branch, no_history=False): + rc = self.call('rev-list', '--max-count=1', '%s/%s' % (remote, branch)) + if rc != 0: + return True + rev = dec(self.stdout.strip()) + if no_history: + rc = self.call('update-ref', '-d', 'refs/heads/%s' % branch) + else: + rc = self.call('update-ref', 'refs/heads/%s' % branch, rev) + if rc != 0: + return False + return True + + def get_config(self, key): + self.call('config', key) + return self.stdout.strip() + + def get_prev_commit(self, branch): + rc = self.call('rev-list', '--max-count=1', branch, '--') + if rc != 0: + return None + return dec(self.stdout).strip() + + def open(self, *args, **kwargs): + if self.use_shell: + self.cmd = 'git ' + ' '.join(map(quote, args)) + else: + self.cmd = ['git'] + list(args) + if sys.version_info >= (3, 2, 0): + kwargs['universal_newlines'] = False + for k in 'stdin stdout stderr'.split(): + kwargs.setdefault(k, sp.PIPE) + kwargs['shell'] = self.use_shell + self.pipe = sp.Popen(self.cmd, **kwargs) + return self.pipe + + def call(self, *args, **kwargs): + self.open(*args, **kwargs) + (self.stdout, self.stderr) = self.pipe.communicate() + return self.pipe.wait() + + def check_call(self, *args, **kwargs): + kwargs["shell"] = self.use_shell + sp.check_call(['git'] + list(args), **kwargs) + + +def mk_when(timestamp=None): + if timestamp is None: + timestamp = int(time.time()) + currtz = datetime.now(tz.tzlocal()).strftime('%z') + return "%s %s" % (timestamp, currtz) + + +def start_commit(pipe, git, branch, message, prefix=None): + uname = os.getenv('GIT_COMMITTER_NAME', dec(git.get_config('user.name'))) + email = os.getenv('GIT_COMMITTER_EMAIL', dec(git.get_config('user.email'))) + when = os.getenv('GIT_COMMITTER_DATE', mk_when()) + write(pipe, enc('commit refs/heads/%s\n' % branch)) + write(pipe, enc('committer %s <%s> %s\n' % (uname, email, when))) + write(pipe, enc('data %d\n%s\n' % (len(enc(message)), message))) + head = git.get_prev_commit(branch) + if head: + write(pipe, enc('from %s\n' % head)) + if prefix: + write(pipe, enc('D %s\n' % prefix)) + else: + write(pipe, enc('deleteall\n')) + + +def add_file(pipe, srcpath, tgtpath): + with open(srcpath, "rb") as handle: + if os.access(srcpath, os.X_OK): + write(pipe, enc('M 100755 inline %s\n' % tgtpath)) + else: + write(pipe, enc('M 100644 inline %s\n' % tgtpath)) + data = handle.read() + write(pipe, enc('data %d\n' % len(data))) + write(pipe, enc(data)) + write(pipe, enc('\n')) + + +def add_nojekyll(pipe, prefix=None): + if prefix: + fpath = os.path.join(prefix, '.nojekyll') + else: + fpath = '.nojekyll' + write(pipe, enc('M 100644 inline %s\n' % fpath)) + write(pipe, enc('data 0\n')) + write(pipe, enc('\n')) + + +def add_cname(pipe, cname): + write(pipe, enc('M 100644 inline CNAME\n')) + write(pipe, enc('data %d\n%s\n' % (len(enc(cname)), cname))) + + +def gitpath(fname): + norm = os.path.normpath(fname) + return "/".join(norm.split(os.path.sep)) + + +def run_import(git, srcdir, **opts): + srcdir = dec(srcdir) + pipe = git.open('fast-import', '--date-format=rfc2822', '--quiet', + stdin=sp.PIPE, stdout=None, stderr=None) + start_commit(pipe, git, opts['branch'], opts['mesg'], opts['prefix']) + for path, _, fnames in os.walk(srcdir, followlinks=opts['followlinks']): + for fn in fnames: + fpath = os.path.join(path, fn) + gpath = gitpath(os.path.relpath(fpath, start=srcdir)) + if opts['prefix']: + gpath = os.path.join(opts['prefix'], gpath) + add_file(pipe, fpath, gpath) + if opts['nojekyll']: + add_nojekyll(pipe, opts['prefix']) + if opts['cname'] is not None: + add_cname(pipe, opts['cname']) + write(pipe, enc('\n')) + pipe.stdin.close() + if pipe.wait() != 0: + sys.stdout.write(enc("Failed to process commit.\n")) + + +def options(): + return [ + (('-n', '--no-jekyll'), dict( + dest='nojekyll', + default=False, + action="store_true", + help='Include a .nojekyll file in the branch.', + )), + (('-c', '--cname'), dict( + dest='cname', + default=None, + help='Write a CNAME file with the given CNAME.', + )), + (('-m', '--message'), dict( + dest='mesg', + default='Update documentation', + help='The commit message to use on the target branch.', + )), + (('-p', '--push'), dict( + dest='push', + default=False, + action='store_true', + help='Push the branch to origin/{branch} after committing.', + )), + (('-x', '--prefix'), dict( + dest='prefix', + default=None, + help='The prefix to add to each file that gets pushed to the ' + 'remote. Only files below this prefix will be cleared ' + 'out. [%(default)s]', + )), + (('-f', '--force'), dict( + dest='force', + default=False, action='store_true', + help='Force the push to the repository.', + )), + (('-o', '--no-history'), dict( + dest='no_history', + default=False, + action='store_true', + help='Force new commit without parent history.', + )), + (('-r', '--remote'), dict( + dest='remote', + default='origin', + help='The name of the remote to push to. [%(default)s]', + )), + (('-b', '--branch'), dict( + dest='branch', + default='gh-pages', + help='Name of the branch to write to. [%(default)s]', + )), + (('-s', '--shell'), dict( + dest='use_shell', + default=False, + action='store_true', + help='Use the shell when invoking Git. [%(default)s]', + )), + (('-l', '--follow-links'), dict( + dest='followlinks', + default=False, + action='store_true', + help='Follow symlinks when adding files. [%(default)s]', + )) + ] + + +def ghp_import(srcdir, **kwargs): + if not os.path.isdir(srcdir): + raise GhpError("Not a directory: %s" % srcdir) + + opts = {kwargs["dest"]: kwargs["default"] for _, kwargs in options()} + opts.update(kwargs) + + git = Git(use_shell=opts['use_shell']) + git.check_repo() + + if not git.try_rebase(opts['remote'], opts['branch'], opts['no_history']): + raise GhpError("Failed to rebase %s branch." % opts['branch']) + + run_import(git, srcdir, **opts) + + if opts['push']: + if opts['force'] or opts['no_history']: + git.check_call('push', opts['remote'], opts['branch'], '--force') + else: + git.check_call('push', opts['remote'], opts['branch']) + + +def main(): + from argparse import ArgumentParser + + parser = ArgumentParser() + parser.add_argument("--version", action="version", version=__version__) + parser.add_argument("directory") + for args, kwargs in options(): + parser.add_argument(*args, **kwargs) + + args = parser.parse_args().__dict__ + + try: + ghp_import(args.pop("directory"), **args) + except GhpError as e: + parser.error(e.message) + + +if __name__ == '__main__': + main() diff --git a/.venv/lib/python3.9/site-packages/git/__init__.py b/.venv/lib/python3.9/site-packages/git/__init__.py new file mode 100644 index 00000000..ff9ce1dd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/git/__init__.py @@ -0,0 +1,300 @@ +# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors +# +# This module is part of GitPython and is released under the +# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ + +# @PydevCodeAnalysisIgnore + +__all__ = [ + "Actor", + "AmbiguousObjectName", + "BadName", + "BadObject", + "BadObjectType", + "BaseIndexEntry", + "Blob", + "BlobFilter", + "BlockingLockFile", + "CacheError", + "CheckoutError", + "CommandError", + "Commit", + "Diff", + "DiffConstants", + "DiffIndex", + "Diffable", + "FetchInfo", + "Git", + "GitCmdObjectDB", + "GitCommandError", + "GitCommandNotFound", + "GitConfigParser", + "GitDB", + "GitError", + "HEAD", + "Head", + "HookExecutionError", + "INDEX", + "IndexEntry", + "IndexFile", + "IndexObject", + "InvalidDBRoot", + "InvalidGitRepositoryError", + "List", # Deprecated - import this from `typing` instead. + "LockFile", + "NULL_TREE", + "NoSuchPathError", + "ODBError", + "Object", + "Optional", # Deprecated - import this from `typing` instead. + "ParseError", + "PathLike", + "PushInfo", + "RefLog", + "RefLogEntry", + "Reference", + "Remote", + "RemoteProgress", + "RemoteReference", + "Repo", + "RepositoryDirtyError", + "RootModule", + "RootUpdateProgress", + "Sequence", # Deprecated - import from `typing`, or `collections.abc` in 3.9+. + "StageType", + "Stats", + "Submodule", + "SymbolicReference", + "TYPE_CHECKING", # Deprecated - import this from `typing` instead. + "Tag", + "TagObject", + "TagReference", + "Tree", + "TreeModifier", + "Tuple", # Deprecated - import this from `typing` instead. + "Union", # Deprecated - import this from `typing` instead. + "UnmergedEntriesError", + "UnsafeOptionError", + "UnsafeProtocolError", + "UnsupportedOperation", + "UpdateProgress", + "WorkTreeRepositoryUnsupported", + "refresh", + "remove_password_if_present", + "rmtree", + "safe_decode", + "to_hex_sha", +] + +__version__ = '3.1.45' + +from typing import Any, List, Optional, Sequence, TYPE_CHECKING, Tuple, Union + +if TYPE_CHECKING: + from types import ModuleType + +import warnings + +from gitdb.util import to_hex_sha + +from git.exc import ( + AmbiguousObjectName, + BadName, + BadObject, + BadObjectType, + CacheError, + CheckoutError, + CommandError, + GitCommandError, + GitCommandNotFound, + GitError, + HookExecutionError, + InvalidDBRoot, + InvalidGitRepositoryError, + NoSuchPathError, + ODBError, + ParseError, + RepositoryDirtyError, + UnmergedEntriesError, + UnsafeOptionError, + UnsafeProtocolError, + UnsupportedOperation, + WorkTreeRepositoryUnsupported, +) +from git.types import PathLike + +try: + from git.compat import safe_decode # @NoMove + from git.config import GitConfigParser # @NoMove + from git.objects import ( # @NoMove + Blob, + Commit, + IndexObject, + Object, + RootModule, + RootUpdateProgress, + Submodule, + TagObject, + Tree, + TreeModifier, + UpdateProgress, + ) + from git.refs import ( # @NoMove + HEAD, + Head, + RefLog, + RefLogEntry, + Reference, + RemoteReference, + SymbolicReference, + Tag, + TagReference, + ) + from git.diff import ( # @NoMove + INDEX, + NULL_TREE, + Diff, + DiffConstants, + DiffIndex, + Diffable, + ) + from git.db import GitCmdObjectDB, GitDB # @NoMove + from git.cmd import Git # @NoMove + from git.repo import Repo # @NoMove + from git.remote import FetchInfo, PushInfo, Remote, RemoteProgress # @NoMove + from git.index import ( # @NoMove + BaseIndexEntry, + BlobFilter, + CheckoutError, + IndexEntry, + IndexFile, + StageType, + # NOTE: This tells type checkers what util resolves to. We delete it, and it is + # really resolved by __getattr__, which warns. See below on what to use instead. + util, + ) + from git.util import ( # @NoMove + Actor, + BlockingLockFile, + LockFile, + Stats, + remove_password_if_present, + rmtree, + ) +except GitError as _exc: + raise ImportError("%s: %s" % (_exc.__class__.__name__, _exc)) from _exc + + +def _warned_import(message: str, fullname: str) -> "ModuleType": + import importlib + + warnings.warn(message, DeprecationWarning, stacklevel=3) + return importlib.import_module(fullname) + + +def _getattr(name: str) -> Any: + # TODO: If __version__ is made dynamic and lazily fetched, put that case right here. + + if name == "util": + return _warned_import( + "The expression `git.util` and the import `from git import util` actually " + "reference git.index.util, and not the git.util module accessed in " + '`from git.util import XYZ` or `sys.modules["git.util"]`. This potentially ' + "confusing behavior is currently preserved for compatibility, but may be " + "changed in the future and should not be relied on.", + fullname="git.index.util", + ) + + for names, prefix in ( + ({"head", "log", "reference", "symbolic", "tag"}, "git.refs"), + ({"base", "fun", "typ"}, "git.index"), + ): + if name not in names: + continue + + fullname = f"{prefix}.{name}" + + return _warned_import( + f"{__name__}.{name} is a private alias of {fullname} and subject to " + f"immediate removal. Use {fullname} instead.", + fullname=fullname, + ) + + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +if not TYPE_CHECKING: + # NOTE: The expression `git.util` gives git.index.util and `from git import util` + # imports git.index.util, NOT git.util. It may not be feasible to change this until + # the next major version, to avoid breaking code inadvertently relying on it. + # + # - If git.index.util *is* what you want, use (or import from) that, to avoid + # confusion. + # + # - To use the "real" git.util module, write `from git.util import ...`, or if + # necessary access it as `sys.modules["git.util"]`. + # + # Note also that `import git.util` technically imports the "real" git.util... but + # the *expression* `git.util` after doing so is still git.index.util! + # + # (This situation differs from that of other indirect-submodule imports that are + # unambiguously non-public and subject to immediate removal. Here, the public + # git.util module, though different, makes less discoverable that the expression + # `git.util` refers to a non-public attribute of the git module.) + # + # This had originally come about by a wildcard import. Now that all intended imports + # are explicit, the intuitive but potentially incompatible binding occurs due to the + # usual rules for Python submodule bindings. So for now we replace that binding with + # git.index.util, delete that, and let __getattr__ handle it and issue a warning. + # + # For the same runtime behavior, it would be enough to forgo importing util, and + # delete util as created naturally; __getattr__ would behave the same. But type + # checkers would not know what util refers to when accessed as an attribute of git. + del util + + # This is "hidden" to preserve static checking for undefined/misspelled attributes. + __getattr__ = _getattr + +# { Initialize git executable path + +GIT_OK = None + + +def refresh(path: Optional[PathLike] = None) -> None: + """Convenience method for setting the git executable path. + + :param path: + Optional path to the Git executable. If not absolute, it is resolved + immediately, relative to the current directory. + + :note: + The `path` parameter is usually omitted and cannot be used to specify a custom + command whose location is looked up in a path search on each call. See + :meth:`Git.refresh ` for details on how to achieve this. + + :note: + This calls :meth:`Git.refresh ` and sets other global + configuration according to the effect of doing so. As such, this function should + usually be used instead of using :meth:`Git.refresh ` or + :meth:`FetchInfo.refresh ` directly. + + :note: + This function is called automatically, with no arguments, at import time. + """ + global GIT_OK + GIT_OK = False + + if not Git.refresh(path=path): + return + if not FetchInfo.refresh(): # noqa: F405 + return # type: ignore[unreachable] + + GIT_OK = True + + +try: + refresh() +except Exception as _exc: + raise ImportError("Failed to initialize: {0}".format(_exc)) from _exc + +# } END initialize git executable path diff --git a/.venv/lib/python3.9/site-packages/git/cmd.py b/.venv/lib/python3.9/site-packages/git/cmd.py new file mode 100644 index 00000000..15d7820d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/git/cmd.py @@ -0,0 +1,1741 @@ +# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors +# +# This module is part of GitPython and is released under the +# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ + +from __future__ import annotations + +__all__ = ["GitMeta", "Git"] + +import contextlib +import io +import itertools +import logging +import os +import re +import signal +import subprocess +from subprocess import DEVNULL, PIPE, Popen +import sys +from textwrap import dedent +import threading +import warnings + +from git.compat import defenc, force_bytes, safe_decode +from git.exc import ( + CommandError, + GitCommandError, + GitCommandNotFound, + UnsafeOptionError, + UnsafeProtocolError, +) +from git.util import ( + cygpath, + expand_path, + is_cygwin_git, + patch_env, + remove_password_if_present, + stream_copy, +) + +# typing --------------------------------------------------------------------------- + +from typing import ( + Any, + AnyStr, + BinaryIO, + Callable, + Dict, + IO, + Iterator, + List, + Mapping, + Optional, + Sequence, + TYPE_CHECKING, + TextIO, + Tuple, + Union, + cast, + overload, +) + +if sys.version_info >= (3, 10): + from typing import TypeAlias +else: + from typing_extensions import TypeAlias + +from git.types import Literal, PathLike, TBD + +if TYPE_CHECKING: + from git.diff import DiffIndex + from git.repo.base import Repo + +# --------------------------------------------------------------------------------- + +execute_kwargs = { + "istream", + "with_extended_output", + "with_exceptions", + "as_process", + "output_stream", + "stdout_as_string", + "kill_after_timeout", + "with_stdout", + "universal_newlines", + "shell", + "env", + "max_chunk_size", + "strip_newline_in_stdout", +} + +_logger = logging.getLogger(__name__) + + +# ============================================================================== +## @name Utilities +# ------------------------------------------------------------------------------ +# Documentation +## @{ + + +def handle_process_output( + process: "Git.AutoInterrupt" | Popen, + stdout_handler: Union[ + None, + Callable[[AnyStr], None], + Callable[[List[AnyStr]], None], + Callable[[bytes, "Repo", "DiffIndex"], None], + ], + stderr_handler: Union[None, Callable[[AnyStr], None], Callable[[List[AnyStr]], None]], + finalizer: Union[None, Callable[[Union[Popen, "Git.AutoInterrupt"]], None]] = None, + decode_streams: bool = True, + kill_after_timeout: Union[None, float] = None, +) -> None: + R"""Register for notifications to learn that process output is ready to read, and + dispatch lines to the respective line handlers. + + This function returns once the finalizer returns. + + :param process: + :class:`subprocess.Popen` instance. + + :param stdout_handler: + f(stdout_line_string), or ``None``. + + :param stderr_handler: + f(stderr_line_string), or ``None``. + + :param finalizer: + f(proc) - wait for proc to finish. + + :param decode_streams: + Assume stdout/stderr streams are binary and decode them before pushing their + contents to handlers. + + This defaults to ``True``. Set it to ``False`` if: + + - ``universal_newlines == True``, as then streams are in text mode, or + - decoding must happen later, such as for :class:`~git.diff.Diff`\s. + + :param kill_after_timeout: + :class:`float` or ``None``, Default = ``None`` + + To specify a timeout in seconds for the git command, after which the process + should be killed. + """ + + # Use 2 "pump" threads and wait for both to finish. + def pump_stream( + cmdline: List[str], + name: str, + stream: Union[BinaryIO, TextIO], + is_decode: bool, + handler: Union[None, Callable[[Union[bytes, str]], None]], + ) -> None: + try: + for line in stream: + if handler: + if is_decode: + assert isinstance(line, bytes) + line_str = line.decode(defenc) + handler(line_str) + else: + handler(line) + + except Exception as ex: + _logger.error(f"Pumping {name!r} of cmd({remove_password_if_present(cmdline)}) failed due to: {ex!r}") + if "I/O operation on closed file" not in str(ex): + # Only reraise if the error was not due to the stream closing. + raise CommandError([f"<{name}-pump>"] + remove_password_if_present(cmdline), ex) from ex + finally: + stream.close() + + if hasattr(process, "proc"): + process = cast("Git.AutoInterrupt", process) + cmdline: str | Tuple[str, ...] | List[str] = getattr(process.proc, "args", "") + p_stdout = process.proc.stdout if process.proc else None + p_stderr = process.proc.stderr if process.proc else None + else: + process = cast(Popen, process) # type: ignore[redundant-cast] + cmdline = getattr(process, "args", "") + p_stdout = process.stdout + p_stderr = process.stderr + + if not isinstance(cmdline, (tuple, list)): + cmdline = cmdline.split() + + pumps: List[Tuple[str, IO, Callable[..., None] | None]] = [] + if p_stdout: + pumps.append(("stdout", p_stdout, stdout_handler)) + if p_stderr: + pumps.append(("stderr", p_stderr, stderr_handler)) + + threads: List[threading.Thread] = [] + + for name, stream, handler in pumps: + t = threading.Thread(target=pump_stream, args=(cmdline, name, stream, decode_streams, handler)) + t.daemon = True + t.start() + threads.append(t) + + # FIXME: Why join? Will block if stdin needs feeding... + for t in threads: + t.join(timeout=kill_after_timeout) + if t.is_alive(): + if isinstance(process, Git.AutoInterrupt): + process._terminate() + else: # Don't want to deal with the other case. + raise RuntimeError( + "Thread join() timed out in cmd.handle_process_output()." + f" kill_after_timeout={kill_after_timeout} seconds" + ) + if stderr_handler: + error_str: Union[str, bytes] = ( + f"error: process killed because it timed out. kill_after_timeout={kill_after_timeout} seconds" + ) + if not decode_streams and isinstance(p_stderr, BinaryIO): + # Assume stderr_handler needs binary input. + error_str = cast(str, error_str) + error_str = error_str.encode() + # We ignore typing on the next line because mypy does not like the way + # we inferred that stderr takes str or bytes. + stderr_handler(error_str) # type: ignore[arg-type] + + if finalizer: + finalizer(process) + + +safer_popen: Callable[..., Popen] + +if sys.platform == "win32": + + def _safer_popen_windows( + command: Union[str, Sequence[Any]], + *, + shell: bool = False, + env: Optional[Mapping[str, str]] = None, + **kwargs: Any, + ) -> Popen: + """Call :class:`subprocess.Popen` on Windows but don't include a CWD in the + search. + + This avoids an untrusted search path condition where a file like ``git.exe`` in + a malicious repository would be run when GitPython operates on the repository. + The process using GitPython may have an untrusted repository's working tree as + its current working directory. Some operations may temporarily change to that + directory before running a subprocess. In addition, while by default GitPython + does not run external commands with a shell, it can be made to do so, in which + case the CWD of the subprocess, which GitPython usually sets to a repository + working tree, can itself be searched automatically by the shell. This wrapper + covers all those cases. + + :note: + This currently works by setting the + :envvar:`NoDefaultCurrentDirectoryInExePath` environment variable during + subprocess creation. It also takes care of passing Windows-specific process + creation flags, but that is unrelated to path search. + + :note: + The current implementation contains a race condition on :attr:`os.environ`. + GitPython isn't thread-safe, but a program using it on one thread should + ideally be able to mutate :attr:`os.environ` on another, without + unpredictable results. See comments in: + https://github.com/gitpython-developers/GitPython/pull/1650 + """ + # CREATE_NEW_PROCESS_GROUP is needed for some ways of killing it afterwards. + # https://docs.python.org/3/library/subprocess.html#subprocess.Popen.send_signal + # https://docs.python.org/3/library/subprocess.html#subprocess.CREATE_NEW_PROCESS_GROUP + creationflags = subprocess.CREATE_NO_WINDOW | subprocess.CREATE_NEW_PROCESS_GROUP + + # When using a shell, the shell is the direct subprocess, so the variable must + # be set in its environment, to affect its search behavior. + if shell: + # The original may be immutable, or the caller may reuse it. Mutate a copy. + env = {} if env is None else dict(env) + env["NoDefaultCurrentDirectoryInExePath"] = "1" # The "1" can be any value. + + # When not using a shell, the current process does the search in a + # CreateProcessW API call, so the variable must be set in our environment. With + # a shell, that's unnecessary if https://github.com/python/cpython/issues/101283 + # is patched. In Python versions where it is unpatched, in the rare case the + # ComSpec environment variable is unset, the search for the shell itself is + # unsafe. Setting NoDefaultCurrentDirectoryInExePath in all cases, as done here, + # is simpler and protects against that. (As above, the "1" can be any value.) + with patch_env("NoDefaultCurrentDirectoryInExePath", "1"): + return Popen( + command, + shell=shell, + env=env, + creationflags=creationflags, + **kwargs, + ) + + safer_popen = _safer_popen_windows +else: + safer_popen = Popen + + +def dashify(string: str) -> str: + return string.replace("_", "-") + + +def slots_to_dict(self: "Git", exclude: Sequence[str] = ()) -> Dict[str, Any]: + return {s: getattr(self, s) for s in self.__slots__ if s not in exclude} + + +def dict_to_slots_and__excluded_are_none(self: object, d: Mapping[str, Any], excluded: Sequence[str] = ()) -> None: + for k, v in d.items(): + setattr(self, k, v) + for k in excluded: + setattr(self, k, None) + + +## -- End Utilities -- @} + + +class _AutoInterrupt: + """Process wrapper that terminates the wrapped process on finalization. + + This kills/interrupts the stored process instance once this instance goes out of + scope. It is used to prevent processes piling up in case iterators stop reading. + + All attributes are wired through to the contained process object. + + The wait method is overridden to perform automatic status code checking and possibly + raise. + """ + + __slots__ = ("proc", "args", "status") + + # If this is non-zero it will override any status code during _terminate, used + # to prevent race conditions in testing. + _status_code_if_terminate: int = 0 + + def __init__(self, proc: Union[None, subprocess.Popen], args: Any) -> None: + self.proc = proc + self.args = args + self.status: Union[int, None] = None + + def _terminate(self) -> None: + """Terminate the underlying process.""" + if self.proc is None: + return + + proc = self.proc + self.proc = None + if proc.stdin: + proc.stdin.close() + if proc.stdout: + proc.stdout.close() + if proc.stderr: + proc.stderr.close() + # Did the process finish already so we have a return code? + try: + if proc.poll() is not None: + self.status = self._status_code_if_terminate or proc.poll() + return + except OSError as ex: + _logger.info("Ignored error after process had died: %r", ex) + + # It can be that nothing really exists anymore... + if os is None or getattr(os, "kill", None) is None: + return + + # Try to kill it. + try: + proc.terminate() + status = proc.wait() # Ensure the process goes away. + + self.status = self._status_code_if_terminate or status + except OSError as ex: + _logger.info("Ignored error after process had died: %r", ex) + # END exception handling + + def __del__(self) -> None: + self._terminate() + + def __getattr__(self, attr: str) -> Any: + return getattr(self.proc, attr) + + # TODO: Bad choice to mimic `proc.wait()` but with different args. + def wait(self, stderr: Union[None, str, bytes] = b"") -> int: + """Wait for the process and return its status code. + + :param stderr: + Previously read value of stderr, in case stderr is already closed. + + :warn: + May deadlock if output or error pipes are used and not handled separately. + + :raise git.exc.GitCommandError: + If the return status is not 0. + """ + if stderr is None: + stderr_b = b"" + stderr_b = force_bytes(data=stderr, encoding="utf-8") + status: Union[int, None] + if self.proc is not None: + status = self.proc.wait() + p_stderr = self.proc.stderr + else: # Assume the underlying proc was killed earlier or never existed. + status = self.status + p_stderr = None + + def read_all_from_possibly_closed_stream(stream: Union[IO[bytes], None]) -> bytes: + if stream: + try: + return stderr_b + force_bytes(stream.read()) + except (OSError, ValueError): + return stderr_b or b"" + else: + return stderr_b or b"" + + # END status handling + + if status != 0: + errstr = read_all_from_possibly_closed_stream(p_stderr) + _logger.debug("AutoInterrupt wait stderr: %r" % (errstr,)) + raise GitCommandError(remove_password_if_present(self.args), status, errstr) + return status + + +_AutoInterrupt.__name__ = "AutoInterrupt" +_AutoInterrupt.__qualname__ = "Git.AutoInterrupt" + + +class _CatFileContentStream: + """Object representing a sized read-only stream returning the contents of + an object. + + This behaves like a stream, but counts the data read and simulates an empty stream + once our sized content region is empty. + + If not all data are read to the end of the object's lifetime, we read the rest to + ensure the underlying stream continues to work. + """ + + __slots__ = ("_stream", "_nbr", "_size") + + def __init__(self, size: int, stream: IO[bytes]) -> None: + self._stream = stream + self._size = size + self._nbr = 0 # Number of bytes read. + + # Special case: If the object is empty, has null bytes, get the final + # newline right away. + if size == 0: + stream.read(1) + # END handle empty streams + + def read(self, size: int = -1) -> bytes: + bytes_left = self._size - self._nbr + if bytes_left == 0: + return b"" + if size > -1: + # Ensure we don't try to read past our limit. + size = min(bytes_left, size) + else: + # They try to read all, make sure it's not more than what remains. + size = bytes_left + # END check early depletion + data = self._stream.read(size) + self._nbr += len(data) + + # Check for depletion, read our final byte to make the stream usable by + # others. + if self._size - self._nbr == 0: + self._stream.read(1) # final newline + # END finish reading + return data + + def readline(self, size: int = -1) -> bytes: + if self._nbr == self._size: + return b"" + + # Clamp size to lowest allowed value. + bytes_left = self._size - self._nbr + if size > -1: + size = min(bytes_left, size) + else: + size = bytes_left + # END handle size + + data = self._stream.readline(size) + self._nbr += len(data) + + # Handle final byte. + if self._size - self._nbr == 0: + self._stream.read(1) + # END finish reading + + return data + + def readlines(self, size: int = -1) -> List[bytes]: + if self._nbr == self._size: + return [] + + # Leave all additional logic to our readline method, we just check the size. + out = [] + nbr = 0 + while True: + line = self.readline() + if not line: + break + out.append(line) + if size > -1: + nbr += len(line) + if nbr > size: + break + # END handle size constraint + # END readline loop + return out + + # skipcq: PYL-E0301 + def __iter__(self) -> "Git.CatFileContentStream": + return self + + def __next__(self) -> bytes: + line = self.readline() + if not line: + raise StopIteration + + return line + + next = __next__ + + def __del__(self) -> None: + bytes_left = self._size - self._nbr + if bytes_left: + # Read and discard - seeking is impossible within a stream. + # This includes any terminating newline. + self._stream.read(bytes_left + 1) + # END handle incomplete read + + +_CatFileContentStream.__name__ = "CatFileContentStream" +_CatFileContentStream.__qualname__ = "Git.CatFileContentStream" + + +_USE_SHELL_DEFAULT_MESSAGE = ( + "Git.USE_SHELL is deprecated, because only its default value of False is safe. " + "It will be removed in a future release." +) + +_USE_SHELL_DANGER_MESSAGE = ( + "Setting Git.USE_SHELL to True is unsafe and insecure, as the effect of special " + "shell syntax cannot usually be accounted for. This can result in a command " + "injection vulnerability and arbitrary code execution. Git.USE_SHELL is deprecated " + "and will be removed in a future release." +) + + +def _warn_use_shell(*, extra_danger: bool) -> None: + warnings.warn( + _USE_SHELL_DANGER_MESSAGE if extra_danger else _USE_SHELL_DEFAULT_MESSAGE, + DeprecationWarning, + stacklevel=3, + ) + + +class _GitMeta(type): + """Metaclass for :class:`Git`. + + This helps issue :class:`DeprecationWarning` if :attr:`Git.USE_SHELL` is used. + """ + + def __getattribute(cls, name: str) -> Any: + if name == "USE_SHELL": + _warn_use_shell(extra_danger=False) + return super().__getattribute__(name) + + def __setattr(cls, name: str, value: Any) -> Any: + if name == "USE_SHELL": + _warn_use_shell(extra_danger=value) + super().__setattr__(name, value) + + if not TYPE_CHECKING: + # To preserve static checking for undefined/misspelled attributes while letting + # the methods' bodies be type-checked, these are defined as non-special methods, + # then bound to special names out of view of static type checkers. (The original + # names invoke name mangling (leading "__") to avoid confusion in other scopes.) + __getattribute__ = __getattribute + __setattr__ = __setattr + + +GitMeta = _GitMeta +"""Alias of :class:`Git`'s metaclass, whether it is :class:`type` or a custom metaclass. + +Whether the :class:`Git` class has the default :class:`type` as its metaclass or uses a +custom metaclass is not documented and may change at any time. This statically checkable +metaclass alias is equivalent at runtime to ``type(Git)``. This should almost never be +used. Code that benefits from it is likely to be remain brittle even if it is used. + +In view of the :class:`Git` class's intended use and :class:`Git` objects' dynamic +callable attributes representing git subcommands, it rarely makes sense to inherit from +:class:`Git` at all. Using :class:`Git` in multiple inheritance can be especially tricky +to do correctly. Attempting uses of :class:`Git` where its metaclass is relevant, such +as when a sibling class has an unrelated metaclass and a shared lower bound metaclass +might have to be introduced to solve a metaclass conflict, is not recommended. + +:note: + The correct static type of the :class:`Git` class itself, and any subclasses, is + ``Type[Git]``. (This can be written as ``type[Git]`` in Python 3.9 later.) + + :class:`GitMeta` should never be used in any annotation where ``Type[Git]`` is + intended or otherwise possible to use. This alias is truly only for very rare and + inherently precarious situations where it is necessary to deal with the metaclass + explicitly. +""" + + +class Git(metaclass=_GitMeta): + """The Git class manages communication with the Git binary. + + It provides a convenient interface to calling the Git binary, such as in:: + + g = Git( git_dir ) + g.init() # calls 'git init' program + rval = g.ls_files() # calls 'git ls-files' program + + Debugging: + + * Set the :envvar:`GIT_PYTHON_TRACE` environment variable to print each invocation + of the command to stdout. + * Set its value to ``full`` to see details about the returned values. + """ + + __slots__ = ( + "_working_dir", + "cat_file_all", + "cat_file_header", + "_version_info", + "_version_info_token", + "_git_options", + "_persistent_git_options", + "_environment", + ) + + _excluded_ = ( + "cat_file_all", + "cat_file_header", + "_version_info", + "_version_info_token", + ) + + re_unsafe_protocol = re.compile(r"(.+)::.+") + + def __getstate__(self) -> Dict[str, Any]: + return slots_to_dict(self, exclude=self._excluded_) + + def __setstate__(self, d: Dict[str, Any]) -> None: + dict_to_slots_and__excluded_are_none(self, d, excluded=self._excluded_) + + # CONFIGURATION + + git_exec_name = "git" + """Default git command that should work on Linux, Windows, and other systems.""" + + GIT_PYTHON_TRACE = os.environ.get("GIT_PYTHON_TRACE", False) + """Enables debugging of GitPython's git commands.""" + + USE_SHELL: bool = False + """Deprecated. If set to ``True``, a shell will be used when executing git commands. + + Code that uses ``USE_SHELL = True`` or that passes ``shell=True`` to any GitPython + functions should be updated to use the default value of ``False`` instead. ``True`` + is unsafe unless the effect of syntax treated specially by the shell is fully + considered and accounted for, which is not possible under most circumstances. As + detailed below, it is also no longer needed, even where it had been in the past. + + It is in many if not most cases a command injection vulnerability for an application + to set :attr:`USE_SHELL` to ``True``. Any attacker who can cause a specially crafted + fragment of text to make its way into any part of any argument to any git command + (including paths, branch names, etc.) can cause the shell to read and write + arbitrary files and execute arbitrary commands. Innocent input may also accidentally + contain special shell syntax, leading to inadvertent malfunctions. + + In addition, how a value of ``True`` interacts with some aspects of GitPython's + operation is not precisely specified and may change without warning, even before + GitPython 4.0.0 when :attr:`USE_SHELL` may be removed. This includes: + + * Whether or how GitPython automatically customizes the shell environment. + + * Whether, outside of Windows (where :class:`subprocess.Popen` supports lists of + separate arguments even when ``shell=True``), this can be used with any GitPython + functionality other than direct calls to the :meth:`execute` method. + + * Whether any GitPython feature that runs git commands ever attempts to partially + sanitize data a shell may treat specially. Currently this is not done. + + Prior to GitPython 2.0.8, this had a narrow purpose in suppressing console windows + in graphical Windows applications. In 2.0.8 and higher, it provides no benefit, as + GitPython solves that problem more robustly and safely by using the + ``CREATE_NO_WINDOW`` process creation flag on Windows. + + Because Windows path search differs subtly based on whether a shell is used, in rare + cases changing this from ``True`` to ``False`` may keep an unusual git "executable", + such as a batch file, from being found. To fix this, set the command name or full + path in the :envvar:`GIT_PYTHON_GIT_EXECUTABLE` environment variable or pass the + full path to :func:`git.refresh` (or invoke the script using a ``.exe`` shim). + + Further reading: + + * :meth:`Git.execute` (on the ``shell`` parameter). + * https://github.com/gitpython-developers/GitPython/commit/0d9390866f9ce42870d3116094cd49e0019a970a + * https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags + * https://github.com/python/cpython/issues/91558#issuecomment-1100942950 + * https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw + """ + + _git_exec_env_var = "GIT_PYTHON_GIT_EXECUTABLE" + _refresh_env_var = "GIT_PYTHON_REFRESH" + + GIT_PYTHON_GIT_EXECUTABLE = None + """Provide the full path to the git executable. Otherwise it assumes git is in the + executable search path. + + :note: + The git executable is actually found during the refresh step in the top level + ``__init__``. It can also be changed by explicitly calling :func:`git.refresh`. + """ + + _refresh_token = object() # Since None would match an initial _version_info_token. + + @classmethod + def refresh(cls, path: Union[None, PathLike] = None) -> bool: + """Update information about the git executable :class:`Git` objects will use. + + Called by the :func:`git.refresh` function in the top level ``__init__``. + + :param path: + Optional path to the git executable. If not absolute, it is resolved + immediately, relative to the current directory. (See note below.) + + :note: + The top-level :func:`git.refresh` should be preferred because it calls this + method and may also update other state accordingly. + + :note: + There are three different ways to specify the command that refreshing causes + to be used for git: + + 1. Pass no `path` argument and do not set the + :envvar:`GIT_PYTHON_GIT_EXECUTABLE` environment variable. The command + name ``git`` is used. It is looked up in a path search by the system, in + each command run (roughly similar to how git is found when running + ``git`` commands manually). This is usually the desired behavior. + + 2. Pass no `path` argument but set the :envvar:`GIT_PYTHON_GIT_EXECUTABLE` + environment variable. The command given as the value of that variable is + used. This may be a simple command or an arbitrary path. It is looked up + in each command run. Setting :envvar:`GIT_PYTHON_GIT_EXECUTABLE` to + ``git`` has the same effect as not setting it. + + 3. Pass a `path` argument. This path, if not absolute, is immediately + resolved, relative to the current directory. This resolution occurs at + the time of the refresh. When git commands are run, they are run using + that previously resolved path. If a `path` argument is passed, the + :envvar:`GIT_PYTHON_GIT_EXECUTABLE` environment variable is not + consulted. + + :note: + Refreshing always sets the :attr:`Git.GIT_PYTHON_GIT_EXECUTABLE` class + attribute, which can be read on the :class:`Git` class or any of its + instances to check what command is used to run git. This attribute should + not be confused with the related :envvar:`GIT_PYTHON_GIT_EXECUTABLE` + environment variable. The class attribute is set no matter how refreshing is + performed. + """ + # Discern which path to refresh with. + if path is not None: + new_git = os.path.expanduser(path) + new_git = os.path.abspath(new_git) + else: + new_git = os.environ.get(cls._git_exec_env_var, cls.git_exec_name) + + # Keep track of the old and new git executable path. + old_git = cls.GIT_PYTHON_GIT_EXECUTABLE + old_refresh_token = cls._refresh_token + cls.GIT_PYTHON_GIT_EXECUTABLE = new_git + cls._refresh_token = object() + + # Test if the new git executable path is valid. A GitCommandNotFound error is + # raised by us. A PermissionError is raised if the git executable cannot be + # executed for whatever reason. + has_git = False + try: + cls().version() + has_git = True + except (GitCommandNotFound, PermissionError): + pass + + # Warn or raise exception if test failed. + if not has_git: + err = ( + dedent( + """\ + Bad git executable. + The git executable must be specified in one of the following ways: + - be included in your $PATH + - be set via $%s + - explicitly set via git.refresh() + """ + ) + % cls._git_exec_env_var + ) + + # Revert to whatever the old_git was. + cls.GIT_PYTHON_GIT_EXECUTABLE = old_git + cls._refresh_token = old_refresh_token + + if old_git is None: + # On the first refresh (when GIT_PYTHON_GIT_EXECUTABLE is None) we only + # are quiet, warn, or error depending on the GIT_PYTHON_REFRESH value. + + # Determine what the user wants to happen during the initial refresh. We + # expect GIT_PYTHON_REFRESH to either be unset or be one of the + # following values: + # + # 0|q|quiet|s|silence|silent|n|none + # 1|w|warn|warning|l|log + # 2|r|raise|e|error|exception + + mode = os.environ.get(cls._refresh_env_var, "raise").lower() + + quiet = ["quiet", "q", "silence", "s", "silent", "none", "n", "0"] + warn = ["warn", "w", "warning", "log", "l", "1"] + error = ["error", "e", "exception", "raise", "r", "2"] + + if mode in quiet: + pass + elif mode in warn or mode in error: + err = dedent( + """\ + %s + All git commands will error until this is rectified. + + This initial message can be silenced or aggravated in the future by setting the + $%s environment variable. Use one of the following values: + - %s: for no message or exception + - %s: for a warning message (logging level CRITICAL, displayed by default) + - %s: for a raised exception + + Example: + export %s=%s + """ + ) % ( + err, + cls._refresh_env_var, + "|".join(quiet), + "|".join(warn), + "|".join(error), + cls._refresh_env_var, + quiet[0], + ) + + if mode in warn: + _logger.critical(err) + else: + raise ImportError(err) + else: + err = dedent( + """\ + %s environment variable has been set but it has been set with an invalid value. + + Use only the following values: + - %s: for no message or exception + - %s: for a warning message (logging level CRITICAL, displayed by default) + - %s: for a raised exception + """ + ) % ( + cls._refresh_env_var, + "|".join(quiet), + "|".join(warn), + "|".join(error), + ) + raise ImportError(err) + + # We get here if this was the initial refresh and the refresh mode was + # not error. Go ahead and set the GIT_PYTHON_GIT_EXECUTABLE such that we + # discern the difference between the first refresh at import time + # and subsequent calls to git.refresh or this refresh method. + cls.GIT_PYTHON_GIT_EXECUTABLE = cls.git_exec_name + else: + # After the first refresh (when GIT_PYTHON_GIT_EXECUTABLE is no longer + # None) we raise an exception. + raise GitCommandNotFound(new_git, err) + + return has_git + + @classmethod + def is_cygwin(cls) -> bool: + return is_cygwin_git(cls.GIT_PYTHON_GIT_EXECUTABLE) + + @overload + @classmethod + def polish_url(cls, url: str, is_cygwin: Literal[False] = ...) -> str: ... + + @overload + @classmethod + def polish_url(cls, url: str, is_cygwin: Union[None, bool] = None) -> str: ... + + @classmethod + def polish_url(cls, url: str, is_cygwin: Union[None, bool] = None) -> PathLike: + """Remove any backslashes from URLs to be written in config files. + + Windows might create config files containing paths with backslashes, but git + stops liking them as it will escape the backslashes. Hence we undo the escaping + just to be sure. + """ + if is_cygwin is None: + is_cygwin = cls.is_cygwin() + + if is_cygwin: + url = cygpath(url) + else: + url = os.path.expandvars(url) + if url.startswith("~"): + url = os.path.expanduser(url) + url = url.replace("\\\\", "\\").replace("\\", "/") + return url + + @classmethod + def check_unsafe_protocols(cls, url: str) -> None: + """Check for unsafe protocols. + + Apart from the usual protocols (http, git, ssh), Git allows "remote helpers" + that have the form ``::
``. One of these helpers (``ext::``) + can be used to invoke any arbitrary command. + + See: + + - https://git-scm.com/docs/gitremote-helpers + - https://git-scm.com/docs/git-remote-ext + """ + match = cls.re_unsafe_protocol.match(url) + if match: + protocol = match.group(1) + raise UnsafeProtocolError( + f"The `{protocol}::` protocol looks suspicious, use `allow_unsafe_protocols=True` to allow it." + ) + + @classmethod + def check_unsafe_options(cls, options: List[str], unsafe_options: List[str]) -> None: + """Check for unsafe options. + + Some options that are passed to ``git `` can be used to execute + arbitrary commands. These are blocked by default. + """ + # Options can be of the form `foo`, `--foo bar`, or `--foo=bar`, so we need to + # check if they start with "--foo" or if they are equal to "foo". + bare_unsafe_options = [option.lstrip("-") for option in unsafe_options] + for option in options: + for unsafe_option, bare_option in zip(unsafe_options, bare_unsafe_options): + if option.startswith(unsafe_option) or option == bare_option: + raise UnsafeOptionError( + f"{unsafe_option} is not allowed, use `allow_unsafe_options=True` to allow it." + ) + + AutoInterrupt: TypeAlias = _AutoInterrupt + + CatFileContentStream: TypeAlias = _CatFileContentStream + + def __init__(self, working_dir: Union[None, PathLike] = None) -> None: + """Initialize this instance with: + + :param working_dir: + Git directory we should work in. If ``None``, we always work in the current + directory as returned by :func:`os.getcwd`. + This is meant to be the working tree directory if available, or the + ``.git`` directory in case of bare repositories. + """ + super().__init__() + self._working_dir = expand_path(working_dir) + self._git_options: Union[List[str], Tuple[str, ...]] = () + self._persistent_git_options: List[str] = [] + + # Extra environment variables to pass to git commands + self._environment: Dict[str, str] = {} + + # Cached version slots + self._version_info: Union[Tuple[int, ...], None] = None + self._version_info_token: object = None + + # Cached command slots + self.cat_file_header: Union[None, TBD] = None + self.cat_file_all: Union[None, TBD] = None + + def __getattribute__(self, name: str) -> Any: + if name == "USE_SHELL": + _warn_use_shell(extra_danger=False) + return super().__getattribute__(name) + + def __getattr__(self, name: str) -> Any: + """A convenience method as it allows to call the command as if it was an object. + + :return: + Callable object that will execute call :meth:`_call_process` with your + arguments. + """ + if name.startswith("_"): + return super().__getattribute__(name) + return lambda *args, **kwargs: self._call_process(name, *args, **kwargs) + + def set_persistent_git_options(self, **kwargs: Any) -> None: + """Specify command line options to the git executable for subsequent + subcommand calls. + + :param kwargs: + A dict of keyword arguments. + These arguments are passed as in :meth:`_call_process`, but will be passed + to the git command rather than the subcommand. + """ + + self._persistent_git_options = self.transform_kwargs(split_single_char_options=True, **kwargs) + + @property + def working_dir(self) -> Union[None, PathLike]: + """:return: Git directory we are working on""" + return self._working_dir + + @property + def version_info(self) -> Tuple[int, ...]: + """ + :return: Tuple with integers representing the major, minor and additional + version numbers as parsed from :manpage:`git-version(1)`. Up to four fields + are used. + + This value is generated on demand and is cached. + """ + # Refreshing is global, but version_info caching is per-instance. + refresh_token = self._refresh_token # Copy token in case of concurrent refresh. + + # Use the cached version if obtained after the most recent refresh. + if self._version_info_token is refresh_token: + assert self._version_info is not None, "Bug: corrupted token-check state" + return self._version_info + + # Run "git version" and parse it. + process_version = self._call_process("version") + version_string = process_version.split(" ")[2] + version_fields = version_string.split(".")[:4] + leading_numeric_fields = itertools.takewhile(str.isdigit, version_fields) + self._version_info = tuple(map(int, leading_numeric_fields)) + + # This value will be considered valid until the next refresh. + self._version_info_token = refresh_token + return self._version_info + + @overload + def execute( + self, + command: Union[str, Sequence[Any]], + *, + as_process: Literal[True], + ) -> "AutoInterrupt": ... + + @overload + def execute( + self, + command: Union[str, Sequence[Any]], + *, + as_process: Literal[False] = False, + stdout_as_string: Literal[True], + ) -> Union[str, Tuple[int, str, str]]: ... + + @overload + def execute( + self, + command: Union[str, Sequence[Any]], + *, + as_process: Literal[False] = False, + stdout_as_string: Literal[False] = False, + ) -> Union[bytes, Tuple[int, bytes, str]]: ... + + @overload + def execute( + self, + command: Union[str, Sequence[Any]], + *, + with_extended_output: Literal[False], + as_process: Literal[False], + stdout_as_string: Literal[True], + ) -> str: ... + + @overload + def execute( + self, + command: Union[str, Sequence[Any]], + *, + with_extended_output: Literal[False], + as_process: Literal[False], + stdout_as_string: Literal[False], + ) -> bytes: ... + + def execute( + self, + command: Union[str, Sequence[Any]], + istream: Union[None, BinaryIO] = None, + with_extended_output: bool = False, + with_exceptions: bool = True, + as_process: bool = False, + output_stream: Union[None, BinaryIO] = None, + stdout_as_string: bool = True, + kill_after_timeout: Union[None, float] = None, + with_stdout: bool = True, + universal_newlines: bool = False, + shell: Union[None, bool] = None, + env: Union[None, Mapping[str, str]] = None, + max_chunk_size: int = io.DEFAULT_BUFFER_SIZE, + strip_newline_in_stdout: bool = True, + **subprocess_kwargs: Any, + ) -> Union[str, bytes, Tuple[int, Union[str, bytes], str], AutoInterrupt]: + R"""Handle executing the command, and consume and return the returned + information (stdout). + + :param command: + The command argument list to execute. + It should be a sequence of program arguments, or a string. The + program to execute is the first item in the args sequence or string. + + :param istream: + Standard input filehandle passed to :class:`subprocess.Popen`. + + :param with_extended_output: + Whether to return a (status, stdout, stderr) tuple. + + :param with_exceptions: + Whether to raise an exception when git returns a non-zero status. + + :param as_process: + Whether to return the created process instance directly from which + streams can be read on demand. This will render `with_extended_output` + and `with_exceptions` ineffective - the caller will have to deal with + the details. It is important to note that the process will be placed + into an :class:`AutoInterrupt` wrapper that will interrupt the process + once it goes out of scope. If you use the command in iterators, you + should pass the whole process instance instead of a single stream. + + :param output_stream: + If set to a file-like object, data produced by the git command will be + copied to the given stream instead of being returned as a string. + This feature only has any effect if `as_process` is ``False``. + + :param stdout_as_string: + If ``False``, the command's standard output will be bytes. Otherwise, it + will be decoded into a string using the default encoding (usually UTF-8). + The latter can fail, if the output contains binary data. + + :param kill_after_timeout: + Specifies a timeout in seconds for the git command, after which the process + should be killed. This will have no effect if `as_process` is set to + ``True``. It is set to ``None`` by default and will let the process run + until the timeout is explicitly specified. Uses of this feature should be + carefully considered, due to the following limitations: + + 1. This feature is not supported at all on Windows. + 2. Effectiveness may vary by operating system. ``ps --ppid`` is used to + enumerate child processes, which is available on most GNU/Linux systems + but not most others. + 3. Deeper descendants do not receive signals, though they may sometimes + terminate as a consequence of their parent processes being killed. + 4. `kill_after_timeout` uses ``SIGKILL``, which can have negative side + effects on a repository. For example, stale locks in case of + :manpage:`git-gc(1)` could render the repository incapable of accepting + changes until the lock is manually removed. + + :param with_stdout: + If ``True``, default ``True``, we open stdout on the created process. + + :param universal_newlines: + If ``True``, pipes will be opened as text, and lines are split at all known + line endings. + + :param shell: + Whether to invoke commands through a shell + (see :class:`Popen(..., shell=True) `). + If this is not ``None``, it overrides :attr:`USE_SHELL`. + + Passing ``shell=True`` to this or any other GitPython function should be + avoided, as it is unsafe under most circumstances. This is because it is + typically not feasible to fully consider and account for the effect of shell + expansions, especially when passing ``shell=True`` to other methods that + forward it to :meth:`Git.execute`. Passing ``shell=True`` is also no longer + needed (nor useful) to work around any known operating system specific + issues. + + :param env: + A dictionary of environment variables to be passed to + :class:`subprocess.Popen`. + + :param max_chunk_size: + Maximum number of bytes in one chunk of data passed to the `output_stream` + in one invocation of its ``write()`` method. If the given number is not + positive then the default value is used. + + :param strip_newline_in_stdout: + Whether to strip the trailing ``\n`` of the command stdout. + + :param subprocess_kwargs: + Keyword arguments to be passed to :class:`subprocess.Popen`. Please note + that some of the valid kwargs are already set by this method; the ones you + specify may not be the same ones. + + :return: + * str(output), if `extended_output` is ``False`` (Default) + * tuple(int(status), str(stdout), str(stderr)), + if `extended_output` is ``True`` + + If `output_stream` is ``True``, the stdout value will be your output stream: + + * output_stream, if `extended_output` is ``False`` + * tuple(int(status), output_stream, str(stderr)), + if `extended_output` is ``True`` + + Note that git is executed with ``LC_MESSAGES="C"`` to ensure consistent + output regardless of system language. + + :raise git.exc.GitCommandError: + + :note: + If you add additional keyword arguments to the signature of this method, you + must update the ``execute_kwargs`` variable housed in this module. + """ + # Remove password for the command if present. + redacted_command = remove_password_if_present(command) + if self.GIT_PYTHON_TRACE and (self.GIT_PYTHON_TRACE != "full" or as_process): + _logger.info(" ".join(redacted_command)) + + # Allow the user to have the command executed in their working dir. + try: + cwd = self._working_dir or os.getcwd() # type: Union[None, str] + if not os.access(str(cwd), os.X_OK): + cwd = None + except FileNotFoundError: + cwd = None + + # Start the process. + inline_env = env + env = os.environ.copy() + # Attempt to force all output to plain ASCII English, which is what some parsing + # code may expect. + # According to https://askubuntu.com/a/311796, we are setting LANGUAGE as well + # just to be sure. + env["LANGUAGE"] = "C" + env["LC_ALL"] = "C" + env.update(self._environment) + if inline_env is not None: + env.update(inline_env) + + if sys.platform == "win32": + if kill_after_timeout is not None: + raise GitCommandError( + redacted_command, + '"kill_after_timeout" feature is not supported on Windows.', + ) + cmd_not_found_exception = OSError + else: + cmd_not_found_exception = FileNotFoundError + # END handle + + stdout_sink = PIPE if with_stdout else getattr(subprocess, "DEVNULL", None) or open(os.devnull, "wb") + if shell is None: + # Get the value of USE_SHELL with no deprecation warning. Do this without + # warnings.catch_warnings, to avoid a race condition with application code + # configuring warnings. The value could be looked up in type(self).__dict__ + # or Git.__dict__, but those can break under some circumstances. This works + # the same as self.USE_SHELL in more situations; see Git.__getattribute__. + shell = super().__getattribute__("USE_SHELL") + _logger.debug( + "Popen(%s, cwd=%s, stdin=%s, shell=%s, universal_newlines=%s)", + redacted_command, + cwd, + "" if istream else "None", + shell, + universal_newlines, + ) + try: + proc = safer_popen( + command, + env=env, + cwd=cwd, + bufsize=-1, + stdin=(istream or DEVNULL), + stderr=PIPE, + stdout=stdout_sink, + shell=shell, + universal_newlines=universal_newlines, + encoding=defenc if universal_newlines else None, + **subprocess_kwargs, + ) + except cmd_not_found_exception as err: + raise GitCommandNotFound(redacted_command, err) from err + else: + # Replace with a typeguard for Popen[bytes]? + proc.stdout = cast(BinaryIO, proc.stdout) + proc.stderr = cast(BinaryIO, proc.stderr) + + if as_process: + return self.AutoInterrupt(proc, command) + + if sys.platform != "win32" and kill_after_timeout is not None: + # Help mypy figure out this is not None even when used inside communicate(). + timeout = kill_after_timeout + + def kill_process(pid: int) -> None: + """Callback to kill a process. + + This callback implementation would be ineffective and unsafe on Windows. + """ + p = Popen(["ps", "--ppid", str(pid)], stdout=PIPE) + child_pids = [] + if p.stdout is not None: + for line in p.stdout: + if len(line.split()) > 0: + local_pid = (line.split())[0] + if local_pid.isdigit(): + child_pids.append(int(local_pid)) + try: + os.kill(pid, signal.SIGKILL) + for child_pid in child_pids: + try: + os.kill(child_pid, signal.SIGKILL) + except OSError: + pass + # Tell the main routine that the process was killed. + kill_check.set() + except OSError: + # It is possible that the process gets completed in the duration + # after timeout happens and before we try to kill the process. + pass + return + + def communicate() -> Tuple[AnyStr, AnyStr]: + watchdog.start() + out, err = proc.communicate() + watchdog.cancel() + if kill_check.is_set(): + err = 'Timeout: the command "%s" did not complete in %d secs.' % ( + " ".join(redacted_command), + timeout, + ) + if not universal_newlines: + err = err.encode(defenc) + return out, err + + # END helpers + + kill_check = threading.Event() + watchdog = threading.Timer(timeout, kill_process, args=(proc.pid,)) + else: + communicate = proc.communicate + + # Wait for the process to return. + status = 0 + stdout_value: Union[str, bytes] = b"" + stderr_value: Union[str, bytes] = b"" + newline = "\n" if universal_newlines else b"\n" + try: + if output_stream is None: + stdout_value, stderr_value = communicate() + # Strip trailing "\n". + if stdout_value.endswith(newline) and strip_newline_in_stdout: # type: ignore[arg-type] + stdout_value = stdout_value[:-1] + if stderr_value.endswith(newline): # type: ignore[arg-type] + stderr_value = stderr_value[:-1] + + status = proc.returncode + else: + max_chunk_size = max_chunk_size if max_chunk_size and max_chunk_size > 0 else io.DEFAULT_BUFFER_SIZE + stream_copy(proc.stdout, output_stream, max_chunk_size) + stdout_value = proc.stdout.read() + stderr_value = proc.stderr.read() + # Strip trailing "\n". + if stderr_value.endswith(newline): # type: ignore[arg-type] + stderr_value = stderr_value[:-1] + status = proc.wait() + # END stdout handling + finally: + proc.stdout.close() + proc.stderr.close() + + if self.GIT_PYTHON_TRACE == "full": + cmdstr = " ".join(redacted_command) + + def as_text(stdout_value: Union[bytes, str]) -> str: + return not output_stream and safe_decode(stdout_value) or "" + + # END as_text + + if stderr_value: + _logger.info( + "%s -> %d; stdout: '%s'; stderr: '%s'", + cmdstr, + status, + as_text(stdout_value), + safe_decode(stderr_value), + ) + elif stdout_value: + _logger.info("%s -> %d; stdout: '%s'", cmdstr, status, as_text(stdout_value)) + else: + _logger.info("%s -> %d", cmdstr, status) + # END handle debug printing + + if with_exceptions and status != 0: + raise GitCommandError(redacted_command, status, stderr_value, stdout_value) + + if isinstance(stdout_value, bytes) and stdout_as_string: # Could also be output_stream. + stdout_value = safe_decode(stdout_value) + + # Allow access to the command's status code. + if with_extended_output: + return (status, stdout_value, safe_decode(stderr_value)) + else: + return stdout_value + + def environment(self) -> Dict[str, str]: + return self._environment + + def update_environment(self, **kwargs: Any) -> Dict[str, Union[str, None]]: + """Set environment variables for future git invocations. Return all changed + values in a format that can be passed back into this function to revert the + changes. + + Examples:: + + old_env = self.update_environment(PWD='/tmp') + self.update_environment(**old_env) + + :param kwargs: + Environment variables to use for git processes. + + :return: + Dict that maps environment variables to their old values + """ + old_env = {} + for key, value in kwargs.items(): + # Set value if it is None. + if value is not None: + old_env[key] = self._environment.get(key) + self._environment[key] = value + # Remove key from environment if its value is None. + elif key in self._environment: + old_env[key] = self._environment[key] + del self._environment[key] + return old_env + + @contextlib.contextmanager + def custom_environment(self, **kwargs: Any) -> Iterator[None]: + """A context manager around the above :meth:`update_environment` method to + restore the environment back to its previous state after operation. + + Examples:: + + with self.custom_environment(GIT_SSH='/bin/ssh_wrapper'): + repo.remotes.origin.fetch() + + :param kwargs: + See :meth:`update_environment`. + """ + old_env = self.update_environment(**kwargs) + try: + yield + finally: + self.update_environment(**old_env) + + def transform_kwarg(self, name: str, value: Any, split_single_char_options: bool) -> List[str]: + if len(name) == 1: + if value is True: + return ["-%s" % name] + elif value not in (False, None): + if split_single_char_options: + return ["-%s" % name, "%s" % value] + else: + return ["-%s%s" % (name, value)] + else: + if value is True: + return ["--%s" % dashify(name)] + elif value is not False and value is not None: + return ["--%s=%s" % (dashify(name), value)] + return [] + + def transform_kwargs(self, split_single_char_options: bool = True, **kwargs: Any) -> List[str]: + """Transform Python-style kwargs into git command line options.""" + args = [] + for k, v in kwargs.items(): + if isinstance(v, (list, tuple)): + for value in v: + args += self.transform_kwarg(k, value, split_single_char_options) + else: + args += self.transform_kwarg(k, v, split_single_char_options) + return args + + @classmethod + def _unpack_args(cls, arg_list: Sequence[str]) -> List[str]: + outlist = [] + if isinstance(arg_list, (list, tuple)): + for arg in arg_list: + outlist.extend(cls._unpack_args(arg)) + else: + outlist.append(str(arg_list)) + + return outlist + + def __call__(self, **kwargs: Any) -> "Git": + """Specify command line options to the git executable for a subcommand call. + + :param kwargs: + A dict of keyword arguments. + These arguments are passed as in :meth:`_call_process`, but will be passed + to the git command rather than the subcommand. + + Examples:: + + git(work_tree='/tmp').difftool() + """ + self._git_options = self.transform_kwargs(split_single_char_options=True, **kwargs) + return self + + @overload + def _call_process( + self, method: str, *args: None, **kwargs: None + ) -> str: ... # If no args were given, execute the call with all defaults. + + @overload + def _call_process( + self, + method: str, + istream: int, + as_process: Literal[True], + *args: Any, + **kwargs: Any, + ) -> "Git.AutoInterrupt": ... + + @overload + def _call_process( + self, method: str, *args: Any, **kwargs: Any + ) -> Union[str, bytes, Tuple[int, Union[str, bytes], str], "Git.AutoInterrupt"]: ... + + def _call_process( + self, method: str, *args: Any, **kwargs: Any + ) -> Union[str, bytes, Tuple[int, Union[str, bytes], str], "Git.AutoInterrupt"]: + """Run the given git command with the specified arguments and return the result + as a string. + + :param method: + The command. Contained ``_`` characters will be converted to hyphens, such + as in ``ls_files`` to call ``ls-files``. + + :param args: + The list of arguments. If ``None`` is included, it will be pruned. + This allows your commands to call git more conveniently, as ``None`` is + realized as non-existent. + + :param kwargs: + Contains key-values for the following: + + - The :meth:`execute()` kwds, as listed in ``execute_kwargs``. + - "Command options" to be converted by :meth:`transform_kwargs`. + - The ``insert_kwargs_after`` key which its value must match one of + ``*args``. + + It also contains any command options, to be appended after the matched arg. + + Examples:: + + git.rev_list('master', max_count=10, header=True) + + turns into:: + + git rev-list max-count 10 --header master + + :return: + Same as :meth:`execute`. If no args are given, used :meth:`execute`'s + default (especially ``as_process = False``, ``stdout_as_string = True``) and + return :class:`str`. + """ + # Handle optional arguments prior to calling transform_kwargs. + # Otherwise these'll end up in args, which is bad. + exec_kwargs = {k: v for k, v in kwargs.items() if k in execute_kwargs} + opts_kwargs = {k: v for k, v in kwargs.items() if k not in execute_kwargs} + + insert_after_this_arg = opts_kwargs.pop("insert_kwargs_after", None) + + # Prepare the argument list. + + opt_args = self.transform_kwargs(**opts_kwargs) + ext_args = self._unpack_args([a for a in args if a is not None]) + + if insert_after_this_arg is None: + args_list = opt_args + ext_args + else: + try: + index = ext_args.index(insert_after_this_arg) + except ValueError as err: + raise ValueError( + "Couldn't find argument '%s' in args %s to insert cmd options after" + % (insert_after_this_arg, str(ext_args)) + ) from err + # END handle error + args_list = ext_args[: index + 1] + opt_args + ext_args[index + 1 :] + # END handle opts_kwargs + + call = [self.GIT_PYTHON_GIT_EXECUTABLE] + + # Add persistent git options. + call.extend(self._persistent_git_options) + + # Add the git options, then reset to empty to avoid side effects. + call.extend(self._git_options) + self._git_options = () + + call.append(dashify(method)) + call.extend(args_list) + + return self.execute(call, **exec_kwargs) + + def _parse_object_header(self, header_line: str) -> Tuple[str, str, int]: + """ + :param header_line: + A line of the form:: + + type_string size_as_int + + :return: + (hex_sha, type_string, size_as_int) + + :raise ValueError: + If the header contains indication for an error due to incorrect input sha. + """ + tokens = header_line.split() + if len(tokens) != 3: + if not tokens: + err_msg = ( + f"SHA is empty, possible dubious ownership in the repository " + f"""at {self._working_dir}.\n If this is unintended run:\n\n """ + f""" "git config --global --add safe.directory {self._working_dir}" """ + ) + raise ValueError(err_msg) + else: + raise ValueError("SHA %s could not be resolved, git returned: %r" % (tokens[0], header_line.strip())) + # END handle actual return value + # END error handling + + if len(tokens[0]) != 40: + raise ValueError("Failed to parse header: %r" % header_line) + return (tokens[0], tokens[1], int(tokens[2])) + + def _prepare_ref(self, ref: AnyStr) -> bytes: + # Required for command to separate refs on stdin, as bytes. + if isinstance(ref, bytes): + # Assume 40 bytes hexsha - bin-to-ascii for some reason returns bytes, not text. + refstr: str = ref.decode("ascii") + elif not isinstance(ref, str): + refstr = str(ref) # Could be ref-object. + else: + refstr = ref + + if not refstr.endswith("\n"): + refstr += "\n" + return refstr.encode(defenc) + + def _get_persistent_cmd(self, attr_name: str, cmd_name: str, *args: Any, **kwargs: Any) -> "Git.AutoInterrupt": + cur_val = getattr(self, attr_name) + if cur_val is not None: + return cur_val + + options = {"istream": PIPE, "as_process": True} + options.update(kwargs) + + cmd = self._call_process(cmd_name, *args, **options) + setattr(self, attr_name, cmd) + cmd = cast("Git.AutoInterrupt", cmd) + return cmd + + def __get_object_header(self, cmd: "Git.AutoInterrupt", ref: AnyStr) -> Tuple[str, str, int]: + if cmd.stdin and cmd.stdout: + cmd.stdin.write(self._prepare_ref(ref)) + cmd.stdin.flush() + return self._parse_object_header(cmd.stdout.readline()) + else: + raise ValueError("cmd stdin was empty") + + def get_object_header(self, ref: str) -> Tuple[str, str, int]: + """Use this method to quickly examine the type and size of the object behind the + given ref. + + :note: + The method will only suffer from the costs of command invocation once and + reuses the command in subsequent calls. + + :return: + (hexsha, type_string, size_as_int) + """ + cmd = self._get_persistent_cmd("cat_file_header", "cat_file", batch_check=True) + return self.__get_object_header(cmd, ref) + + def get_object_data(self, ref: str) -> Tuple[str, str, int, bytes]: + """Similar to :meth:`get_object_header`, but returns object data as well. + + :return: + (hexsha, type_string, size_as_int, data_string) + + :note: + Not threadsafe. + """ + hexsha, typename, size, stream = self.stream_object_data(ref) + data = stream.read(size) + del stream + return (hexsha, typename, size, data) + + def stream_object_data(self, ref: str) -> Tuple[str, str, int, "Git.CatFileContentStream"]: + """Similar to :meth:`get_object_data`, but returns the data as a stream. + + :return: + (hexsha, type_string, size_as_int, stream) + + :note: + This method is not threadsafe. You need one independent :class:`Git` + instance per thread to be safe! + """ + cmd = self._get_persistent_cmd("cat_file_all", "cat_file", batch=True) + hexsha, typename, size = self.__get_object_header(cmd, ref) + cmd_stdout = cmd.stdout if cmd.stdout is not None else io.BytesIO() + return (hexsha, typename, size, self.CatFileContentStream(size, cmd_stdout)) + + def clear_cache(self) -> "Git": + """Clear all kinds of internal caches to release resources. + + Currently persistent commands will be interrupted. + + :return: + self + """ + for cmd in (self.cat_file_all, self.cat_file_header): + if cmd: + cmd.__del__() + + self.cat_file_all = None + self.cat_file_header = None + return self diff --git a/.venv/lib/python3.9/site-packages/git/compat.py b/.venv/lib/python3.9/site-packages/git/compat.py new file mode 100644 index 00000000..d7d9a55a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/git/compat.py @@ -0,0 +1,165 @@ +# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors +# +# This module is part of GitPython and is released under the +# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ + +"""Utilities to help provide compatibility with Python 3. + +This module exists for historical reasons. Code outside GitPython may make use of public +members of this module, but is unlikely to benefit from doing so. GitPython continues to +use some of these utilities, in some cases for compatibility across different platforms. +""" + +import locale +import os +import sys +import warnings + +from gitdb.utils.encoding import force_bytes, force_text # noqa: F401 + +# typing -------------------------------------------------------------------- + +from typing import ( + Any, # noqa: F401 + AnyStr, + Dict, # noqa: F401 + IO, # noqa: F401 + List, + Optional, + TYPE_CHECKING, + Tuple, # noqa: F401 + Type, # noqa: F401 + Union, + overload, +) + +# --------------------------------------------------------------------------- + + +_deprecated_platform_aliases = { + "is_win": os.name == "nt", + "is_posix": os.name == "posix", + "is_darwin": sys.platform == "darwin", +} + + +def _getattr(name: str) -> Any: + try: + value = _deprecated_platform_aliases[name] + except KeyError: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None + + warnings.warn( + f"{__name__}.{name} and other is_ aliases are deprecated. " + "Write the desired os.name or sys.platform check explicitly instead.", + DeprecationWarning, + stacklevel=2, + ) + return value + + +if not TYPE_CHECKING: # Preserve static checking for undefined/misspelled attributes. + __getattr__ = _getattr + + +def __dir__() -> List[str]: + return [*globals(), *_deprecated_platform_aliases] + + +is_win: bool +"""Deprecated alias for ``os.name == "nt"`` to check for native Windows. + +This is deprecated because it is clearer to write out :attr:`os.name` or +:attr:`sys.platform` checks explicitly, especially in cases where it matters which is +used. + +:note: + ``is_win`` is ``False`` on Cygwin, but is often wrongly assumed ``True``. To detect + Cygwin, use ``sys.platform == "cygwin"``. +""" + +is_posix: bool +"""Deprecated alias for ``os.name == "posix"`` to check for Unix-like ("POSIX") systems. + +This is deprecated because it clearer to write out :attr:`os.name` or +:attr:`sys.platform` checks explicitly, especially in cases where it matters which is +used. + +:note: + For POSIX systems, more detailed information is available in :attr:`sys.platform`, + while :attr:`os.name` is always ``"posix"`` on such systems, including macOS + (Darwin). +""" + +is_darwin: bool +"""Deprecated alias for ``sys.platform == "darwin"`` to check for macOS (Darwin). + +This is deprecated because it clearer to write out :attr:`os.name` or +:attr:`sys.platform` checks explicitly. + +:note: + For macOS (Darwin), ``os.name == "posix"`` as in other Unix-like systems, while + ``sys.platform == "darwin"``. +""" + +defenc = sys.getfilesystemencoding() +"""The encoding used to convert between Unicode and bytes filenames.""" + + +@overload +def safe_decode(s: None) -> None: ... + + +@overload +def safe_decode(s: AnyStr) -> str: ... + + +def safe_decode(s: Union[AnyStr, None]) -> Optional[str]: + """Safely decode a binary string to Unicode.""" + if isinstance(s, str): + return s + elif isinstance(s, bytes): + return s.decode(defenc, "surrogateescape") + elif s is None: + return None + else: + raise TypeError("Expected bytes or text, but got %r" % (s,)) + + +@overload +def safe_encode(s: None) -> None: ... + + +@overload +def safe_encode(s: AnyStr) -> bytes: ... + + +def safe_encode(s: Optional[AnyStr]) -> Optional[bytes]: + """Safely encode a binary string to Unicode.""" + if isinstance(s, str): + return s.encode(defenc) + elif isinstance(s, bytes): + return s + elif s is None: + return None + else: + raise TypeError("Expected bytes or text, but got %r" % (s,)) + + +@overload +def win_encode(s: None) -> None: ... + + +@overload +def win_encode(s: AnyStr) -> bytes: ... + + +def win_encode(s: Optional[AnyStr]) -> Optional[bytes]: + """Encode Unicode strings for process arguments on Windows.""" + if isinstance(s, str): + return s.encode(locale.getpreferredencoding(False)) + elif isinstance(s, bytes): + return s + elif s is not None: + raise TypeError("Expected bytes or text, but got %r" % (s,)) + return None diff --git a/.venv/lib/python3.9/site-packages/git/config.py b/.venv/lib/python3.9/site-packages/git/config.py new file mode 100644 index 00000000..345290a3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/git/config.py @@ -0,0 +1,951 @@ +# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors +# +# This module is part of GitPython and is released under the +# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ + +"""Parser for reading and writing configuration files.""" + +__all__ = ["GitConfigParser", "SectionConstraint"] + +import abc +import configparser as cp +import fnmatch +from functools import wraps +import inspect +from io import BufferedReader, IOBase +import logging +import os +import os.path as osp +import re +import sys + +from git.compat import defenc, force_text +from git.util import LockFile + +# typing------------------------------------------------------- + +from typing import ( + Any, + Callable, + Generic, + IO, + List, + Dict, + Sequence, + TYPE_CHECKING, + Tuple, + TypeVar, + Union, + cast, +) + +from git.types import Lit_config_levels, ConfigLevels_Tup, PathLike, assert_never, _T + +if TYPE_CHECKING: + from io import BytesIO + + from git.repo.base import Repo + +T_ConfigParser = TypeVar("T_ConfigParser", bound="GitConfigParser") +T_OMD_value = TypeVar("T_OMD_value", str, bytes, int, float, bool) + +if sys.version_info[:3] < (3, 7, 2): + # typing.Ordereddict not added until Python 3.7.2. + from collections import OrderedDict + + OrderedDict_OMD = OrderedDict +else: + from typing import OrderedDict + + OrderedDict_OMD = OrderedDict[str, List[T_OMD_value]] # type: ignore[assignment, misc] + +# ------------------------------------------------------------- + +_logger = logging.getLogger(__name__) + +CONFIG_LEVELS: ConfigLevels_Tup = ("system", "user", "global", "repository") +"""The configuration level of a configuration file.""" + +CONDITIONAL_INCLUDE_REGEXP = re.compile(r"(?<=includeIf )\"(gitdir|gitdir/i|onbranch):(.+)\"") +"""Section pattern to detect conditional includes. + +See: https://git-scm.com/docs/git-config#_conditional_includes +""" + + +class MetaParserBuilder(abc.ABCMeta): # noqa: B024 + """Utility class wrapping base-class methods into decorators that assure read-only + properties.""" + + def __new__(cls, name: str, bases: Tuple, clsdict: Dict[str, Any]) -> "MetaParserBuilder": + """Equip all base-class methods with a needs_values decorator, and all non-const + methods with a :func:`set_dirty_and_flush_changes` decorator in addition to + that. + """ + kmm = "_mutating_methods_" + if kmm in clsdict: + mutating_methods = clsdict[kmm] + for base in bases: + methods = (t for t in inspect.getmembers(base, inspect.isroutine) if not t[0].startswith("_")) + for method_name, method in methods: + if method_name in clsdict: + continue + method_with_values = needs_values(method) + if method_name in mutating_methods: + method_with_values = set_dirty_and_flush_changes(method_with_values) + # END mutating methods handling + + clsdict[method_name] = method_with_values + # END for each name/method pair + # END for each base + # END if mutating methods configuration is set + + new_type = super().__new__(cls, name, bases, clsdict) + return new_type + + +def needs_values(func: Callable[..., _T]) -> Callable[..., _T]: + """Return a method for ensuring we read values (on demand) before we try to access + them.""" + + @wraps(func) + def assure_data_present(self: "GitConfigParser", *args: Any, **kwargs: Any) -> _T: + self.read() + return func(self, *args, **kwargs) + + # END wrapper method + return assure_data_present + + +def set_dirty_and_flush_changes(non_const_func: Callable[..., _T]) -> Callable[..., _T]: + """Return a method that checks whether given non constant function may be called. + + If so, the instance will be set dirty. Additionally, we flush the changes right to + disk. + """ + + def flush_changes(self: "GitConfigParser", *args: Any, **kwargs: Any) -> _T: + rval = non_const_func(self, *args, **kwargs) + self._dirty = True + self.write() + return rval + + # END wrapper method + flush_changes.__name__ = non_const_func.__name__ + return flush_changes + + +class SectionConstraint(Generic[T_ConfigParser]): + """Constrains a ConfigParser to only option commands which are constrained to + always use the section we have been initialized with. + + It supports all ConfigParser methods that operate on an option. + + :note: + If used as a context manager, will release the wrapped ConfigParser. + """ + + __slots__ = ("_config", "_section_name") + + _valid_attrs_ = ( + "get_value", + "set_value", + "get", + "set", + "getint", + "getfloat", + "getboolean", + "has_option", + "remove_section", + "remove_option", + "options", + ) + + def __init__(self, config: T_ConfigParser, section: str) -> None: + self._config = config + self._section_name = section + + def __del__(self) -> None: + # Yes, for some reason, we have to call it explicitly for it to work in PY3 ! + # Apparently __del__ doesn't get call anymore if refcount becomes 0 + # Ridiculous ... . + self._config.release() + + def __getattr__(self, attr: str) -> Any: + if attr in self._valid_attrs_: + return lambda *args, **kwargs: self._call_config(attr, *args, **kwargs) + return super().__getattribute__(attr) + + def _call_config(self, method: str, *args: Any, **kwargs: Any) -> Any: + """Call the configuration at the given method which must take a section name as + first argument.""" + return getattr(self._config, method)(self._section_name, *args, **kwargs) + + @property + def config(self) -> T_ConfigParser: + """return: ConfigParser instance we constrain""" + return self._config + + def release(self) -> None: + """Equivalent to :meth:`GitConfigParser.release`, which is called on our + underlying parser instance.""" + return self._config.release() + + def __enter__(self) -> "SectionConstraint[T_ConfigParser]": + self._config.__enter__() + return self + + def __exit__(self, exception_type: str, exception_value: str, traceback: str) -> None: + self._config.__exit__(exception_type, exception_value, traceback) + + +class _OMD(OrderedDict_OMD): + """Ordered multi-dict.""" + + def __setitem__(self, key: str, value: _T) -> None: + super().__setitem__(key, [value]) + + def add(self, key: str, value: Any) -> None: + if key not in self: + super().__setitem__(key, [value]) + return + + super().__getitem__(key).append(value) + + def setall(self, key: str, values: List[_T]) -> None: + super().__setitem__(key, values) + + def __getitem__(self, key: str) -> Any: + return super().__getitem__(key)[-1] + + def getlast(self, key: str) -> Any: + return super().__getitem__(key)[-1] + + def setlast(self, key: str, value: Any) -> None: + if key not in self: + super().__setitem__(key, [value]) + return + + prior = super().__getitem__(key) + prior[-1] = value + + def get(self, key: str, default: Union[_T, None] = None) -> Union[_T, None]: + return super().get(key, [default])[-1] + + def getall(self, key: str) -> List[_T]: + return super().__getitem__(key) + + def items(self) -> List[Tuple[str, _T]]: # type: ignore[override] + """List of (key, last value for key).""" + return [(k, self[k]) for k in self] + + def items_all(self) -> List[Tuple[str, List[_T]]]: + """List of (key, list of values for key).""" + return [(k, self.getall(k)) for k in self] + + +def get_config_path(config_level: Lit_config_levels) -> str: + # We do not support an absolute path of the gitconfig on Windows. + # Use the global config instead. + if sys.platform == "win32" and config_level == "system": + config_level = "global" + + if config_level == "system": + return "/etc/gitconfig" + elif config_level == "user": + config_home = os.environ.get("XDG_CONFIG_HOME") or osp.join(os.environ.get("HOME", "~"), ".config") + return osp.normpath(osp.expanduser(osp.join(config_home, "git", "config"))) + elif config_level == "global": + return osp.normpath(osp.expanduser("~/.gitconfig")) + elif config_level == "repository": + raise ValueError("No repo to get repository configuration from. Use Repo._get_config_path") + else: + # Should not reach here. Will raise ValueError if does. Static typing will warn + # about missing elifs. + assert_never( # type: ignore[unreachable] + config_level, + ValueError(f"Invalid configuration level: {config_level!r}"), + ) + + +class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder): + """Implements specifics required to read git style configuration files. + + This variation behaves much like the :manpage:`git-config(1)` command, such that the + configuration will be read on demand based on the filepath given during + initialization. + + The changes will automatically be written once the instance goes out of scope, but + can be triggered manually as well. + + The configuration file will be locked if you intend to change values preventing + other instances to write concurrently. + + :note: + The config is case-sensitive even when queried, hence section and option names + must match perfectly. + + :note: + If used as a context manager, this will release the locked file. + """ + + # { Configuration + t_lock = LockFile + """The lock type determines the type of lock to use in new configuration readers. + + They must be compatible to the :class:`~git.util.LockFile` interface. + A suitable alternative would be the :class:`~git.util.BlockingLockFile`. + """ + + re_comment = re.compile(r"^\s*[#;]") + # } END configuration + + optvalueonly_source = r"\s*(?P