TheDashboard.psm1

function Format-StringToSentence { 
    <#
    .SYNOPSIS
    Formats a given string by adding spaces before uppercase letters, digits, and non-word characters.
 
    .DESCRIPTION
    The Format-AddSpaceToSentence function takes a string or an array of strings
    and adds a space before each uppercase letter, digit, and non-word character (excluding dots, spaces, and underscores).
    It also provides options to convert the string to lowercase, remove certain characters before or after the formatting, and remove double spaces.
 
    .PARAMETER Text
    The string or array of strings to be formatted.
 
    .PARAMETER RemoveCharsBefore
    An array of characters to be removed from the string before the formatting is applied.
 
    .PARAMETER RemoveCharsAfter
    An array of characters to be removed from the string after the formatting is applied.
 
    .PARAMETER ToLowerCase
    If this switch is present, the function will convert the string to lowercase.
 
    .PARAMETER RemoveDoubleSpaces
    If this switch is present, the function will remove any double spaces from the string.
 
    .PARAMETER MakeWordsUpperCase
    An array of words that should be converted to uppercase after the formatting is applied.
 
    .PARAMETER DisableAddingSpace
    If this switch is present, the function will not add spaces before uppercase letters, digits, and non-word characters.
 
    .EXAMPLE
    $test = @(
        'OnceUponATime',
        'OnceUponATime1',
        'Money@Risk',
        'OnceUponATime123',
        'AHappyMan2014'
        'OnceUponATime_123'
        'Domain test.com'
    )
 
    Format-StringToSentence -Text $Test -RemoveCharsAfter '_' -RemoveDoubleSpaces
 
    This example formats each string in the $test array, removes any underscores after the formatting, and removes any double spaces.
 
    .EXAMPLE
    $test = @(
        'OnceUponATime',
        'OnceUponATime1',
        'Money@Risk',
        'OnceUponATime123',
        'AHappyMan2014'
        'OnceUponATime_123'
        'Domain test.com'
    )
 
    $Test | Format-StringToSentence -ToLowerCase -RemoveCharsAfter '_' -RemoveDoubleSpaces
 
    This example does the same as the previous one, but also converts each string to lowercase.
 
    .EXAMPLE
    $test = @(
        'OnceUponATime',
        'OnceUponATime1',
        'Money@Risk',
        'OnceUponATime123',
        'AHappyMan2014'
        'OnceUponATime_123'
        'Domain test.com'
    )
 
    Format-StringToSentence -Text $Test -RemoveCharsAfter '_' -RemoveDoubleSpaces -MakeWordsUpperCase 'test.com', 'money'
 
    .NOTES
    The function uses the -creplace operator to add spaces, which is case-insensitive. Therefore, it will add spaces before both uppercase and lowercase letters if they are specified in the RemoveCharsBefore or RemoveCharsAfter parameters.
    #>

    [alias('Format-AddSpaceToSentence')]
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, ValueFromPipeline = $true, Position = 0)][string[]] $Text,
        [string[]] $RemoveCharsBefore,
        [string[]] $RemoveCharsAfter,
        [switch] $ToLowerCase,
        [switch] $RemoveDoubleSpaces,
        [string[]] $MakeWordsUpperCase,
        [switch] $DisableAddingSpace
    )
    Process {
        foreach ($T in $Text) {
            if ($RemoveCharsBefore) {
                foreach ($R in $RemoveCharsBefore) {
                    $T = $T -ireplace [regex]::Escape($R), ""
                }
            }
            if (-not $DisableAddingSpace) {
                $T = $T -creplace '([A-Z]|[^a-zA-Z0-9_.\s]|_|\d+)(?<![a-z])', ' $&'
            }
            if ($ToLowerCase) {
                $T = $T.ToLower()
            }
            if ($RemoveCharsAfter) {
                foreach ($R in $RemoveCharsAfter) {
                    $T = $T -ireplace [regex]::Escape($R), ""
                }
            }
            if ($RemoveDoubleSpaces) {
                $T = $T -creplace '\s+', ' '
            }
            if ($MakeWordsUpperCase) {
                foreach ($W in $MakeWordsUpperCase) {
                    $T = $T -ireplace [regex]::Escape($W), $W.ToUpper()
                }
            }
            $T.Trim()
        }
    }
}
function Get-FileEncoding { 
    <#
    .SYNOPSIS
    Get the encoding of a file (ASCII, UTF8, UTF8BOM, Unicode, BigEndianUnicode, UTF7)
 
    .DESCRIPTION
    Get the encoding of a file (ASCII, UTF8, UTF8BOM, Unicode, BigEndianUnicode, UTF7).
    Encoding is determined by the first few bytes of the file or by the presence of non-ASCII characters.
 
    .PARAMETER Path
    Path to the file to check
 
    .EXAMPLE
    Get-FileEncoding -Path 'C:\Users\pklys\Desktop\test.txt'
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [string] $Path
    )
    if (-not (Test-Path -Path $Path)) {
        if ($ErrorActionPreference -eq 'Stop') {
            throw "Get-FileEncoding - File not found: $Path"
        }
        Write-Warning -Message "Get-FileEncoding - File not found: $Path"
        return
    }
    $fileStream = [System.IO.FileStream]::new($Path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
    $byte = [byte[]]::new(4)
    $null = $fileStream.Read($byte, 0, 4)
    $fileStream.Close()

    if ($byte[0] -eq 0xef -and $byte[1] -eq 0xbb -and $byte[2] -eq 0xbf) {
        return 'UTF8BOM'
    } elseif ($byte[0] -eq 0xff -and $byte[1] -eq 0xfe) {
        return 'Unicode'
    } elseif ($byte[0] -eq 0xfe -and $byte[1] -eq 0xff) {
        return 'BigEndianUnicode'
    } elseif ($byte[0] -eq 0x2b -and $byte[1] -eq 0x2f -and $byte[2] -eq 0x76) {
        return 'UTF7'
    } else {

        $fileStream = [System.IO.FileStream]::new($Path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
        $byte = [byte[]]::new(1)
        while ($fileStream.Read($byte, 0, 1) -gt 0) {
            if ($byte[0] -gt 0x7F) {
                $fileStream.Close()
                return 'UTF8'
            }
        }
        $fileStream.Close()
        return 'ASCII'
    }
}
function Get-GitHubLatestRelease { 
    <#
    .SYNOPSIS
    Gets one or more releases from GitHub repository
 
    .DESCRIPTION
    Gets one or more releases from GitHub repository
 
    .PARAMETER Url
    Url to github repository
 
    .EXAMPLE
    Get-GitHubLatestRelease -Url "https://api.github.com/repos/evotecit/Testimo/releases" | Format-Table
 
    .NOTES
    General notes
    #>

    [CmdLetBinding()]
    param(
        [parameter(Mandatory)][alias('ReleasesUrl')][uri] $Url
    )
    $ProgressPreference = 'SilentlyContinue'

    $Responds = Test-Connection -ComputerName $URl.Host -Quiet -Count 1
    if ($Responds) {
        Try {
            [Array] $JsonOutput = (Invoke-WebRequest -Uri $Url -ErrorAction Stop | ConvertFrom-Json)
            foreach ($JsonContent in $JsonOutput) {
                [PSCustomObject] @{
                    PublishDate = [DateTime]  $JsonContent.published_at
                    CreatedDate = [DateTime] $JsonContent.created_at
                    PreRelease  = [bool] $JsonContent.prerelease
                    Version     = [version] ($JsonContent.name -replace 'v', '')
                    Tag         = $JsonContent.tag_name
                    Branch      = $JsonContent.target_commitish
                    Errors      = ''
                }
            }
        } catch {
            [PSCustomObject] @{
                PublishDate = $null
                CreatedDate = $null
                PreRelease  = $null
                Version     = $null
                Tag         = $null
                Branch      = $null
                Errors      = $_.Exception.Message
            }
        }
    } else {
        [PSCustomObject] @{
            PublishDate = $null
            CreatedDate = $null
            PreRelease  = $null
            Version     = $null
            Tag         = $null
            Branch      = $null
            Errors      = "No connection (ping) to $($Url.Host)"
        }
    }
    $ProgressPreference = 'Continue'
}
function Remove-EmptyValue { 
    <#
    .SYNOPSIS
    Removes empty values from a hashtable recursively.
 
    .DESCRIPTION
    This function removes empty values from a given hashtable. It can be used to clean up a hashtable by removing keys with null, empty string, empty array, or empty dictionary values. The function supports recursive removal of empty values.
 
    .PARAMETER Hashtable
    The hashtable from which empty values will be removed.
 
    .PARAMETER ExcludeParameter
    An array of keys to exclude from the removal process.
 
    .PARAMETER Recursive
    Indicates whether to recursively remove empty values from nested hashtables.
 
    .PARAMETER Rerun
    Specifies the number of times to rerun the removal process recursively.
 
    .PARAMETER DoNotRemoveNull
    If specified, null values will not be removed.
 
    .PARAMETER DoNotRemoveEmpty
    If specified, empty string values will not be removed.
 
    .PARAMETER DoNotRemoveEmptyArray
    If specified, empty array values will not be removed.
 
    .PARAMETER DoNotRemoveEmptyDictionary
    If specified, empty dictionary values will not be removed.
 
    .EXAMPLE
    $hashtable = @{
        'Key1' = '';
        'Key2' = $null;
        'Key3' = @();
        'Key4' = @{}
    }
    Remove-EmptyValue -Hashtable $hashtable -Recursive
 
    Description
    -----------
    This example removes empty values from the $hashtable recursively.
 
    #>

    [alias('Remove-EmptyValues')]
    [CmdletBinding()]
    param(
        [alias('Splat', 'IDictionary')][Parameter(Mandatory)][System.Collections.IDictionary] $Hashtable,
        [string[]] $ExcludeParameter,
        [switch] $Recursive,
        [int] $Rerun,
        [switch] $DoNotRemoveNull,
        [switch] $DoNotRemoveEmpty,
        [switch] $DoNotRemoveEmptyArray,
        [switch] $DoNotRemoveEmptyDictionary
    )
    foreach ($Key in [string[]] $Hashtable.Keys) {
        if ($Key -notin $ExcludeParameter) {
            if ($Recursive) {
                if ($Hashtable[$Key] -is [System.Collections.IDictionary]) {
                    if ($Hashtable[$Key].Count -eq 0) {
                        if (-not $DoNotRemoveEmptyDictionary) {
                            $Hashtable.Remove($Key)
                        }
                    } else {
                        Remove-EmptyValue -Hashtable $Hashtable[$Key] -Recursive:$Recursive -ExcludeParameter $ExcludeParameter -DoNotRemoveNull:$DoNotRemoveNull -DoNotRemoveEmpty:$DoNotRemoveEmpty -DoNotRemoveEmptyArray:$DoNotRemoveEmptyArray -DoNotRemoveEmptyDictionary:$DoNotRemoveEmptyDictionary
                    }
                } else {
                    if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) {
                        $Hashtable.Remove($Key)
                    } elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') {
                        $Hashtable.Remove($Key)
                    } elseif (-not $DoNotRemoveEmptyArray -and $Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0) {
                        $Hashtable.Remove($Key)
                    }
                }
            } else {
                if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) {
                    $Hashtable.Remove($Key)
                } elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') {
                    $Hashtable.Remove($Key)
                } elseif (-not $DoNotRemoveEmptyArray -and $Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0) {
                    $Hashtable.Remove($Key)
                }
            }
        }
    }
    if ($Rerun) {
        for ($i = 0; $i -lt $Rerun; $i++) {
            Remove-EmptyValue -Hashtable $Hashtable -Recursive:$Recursive -ExcludeParameter $ExcludeParameter -DoNotRemoveNull:$DoNotRemoveNull -DoNotRemoveEmpty:$DoNotRemoveEmpty -DoNotRemoveEmptyArray:$DoNotRemoveEmptyArray -DoNotRemoveEmptyDictionary:$DoNotRemoveEmptyDictionary
        }
    }
}
function Remove-FileItem { 
    <#
    .SYNOPSIS
    Removes one or more files and/or folders using one of three methods:
    Remove-Item, .NET Delete(), or move to Recycle Bin.
 
    .DESCRIPTION
    - If the path is a file, removes it in one shot.
    - If the path is a folder and -SkipFolder is NOT used, removes that folder
      in one shot (recursively if -Recursive is specified).
    - If -SkipFolder is used (and the path is a folder), calls Remove-ChildItems
      to remove only the folder's contents, leaving the folder itself.
 
    Supports:
     - RemoveItem, DotNetDelete, RecycleBin
     - Retries
     - WhatIf/Confirm
     - Passthru (detailed objects)
     - SimpleReturn (boolean overall result)
 
    .PARAMETER Paths
    One or more paths to files/folders.
 
    .PARAMETER DeleteMethod
    "RemoveItem", "DotNetDelete", or "RecycleBin". Defaults to "RemoveItem".
 
    .PARAMETER Recursive
    If set, allows recursive removal for folder contents.
 
    .PARAMETER SkipTopFolder
    If set for a folder, removes the contents only, not the folder itself.
 
    .PARAMETER Retries
    Specifies how many times to attempt removal before giving up. Defaults to 1.
 
    .PARAMETER Include
    Patterns (e.g., *.log) to include if removing child items individually.
    Will work only if SkipTopFolder is used.
 
    .PARAMETER Exclude
    Patterns (e.g., *.log) to exclude if removing child items individually.
    Will work only if SkipTopFolder is used.
 
    .PARAMETER Filter
    Filter for Get-ChildItem when removing child items individually.
    Will work only if SkipTopFolder is used.
 
    .PARAMETER Passthru
    Outputs an object for each path with details on success or failure.
 
    .PARAMETER SimpleReturn
    Outputs a single boolean indicating overall success or failure.
 
    .EXAMPLE
    Remove-FileItem -Paths "C:\SomeFolder" -DeleteMethod RemoveItem -Recursive -WhatIf
 
    Shows which items would be removed, but doesn't actually remove them,
    because -WhatIf is in effect.
 
    .EXAMPLE
    Remove-FileItem -Paths "C:\SomeFolder", "C:\SomeFile.txt" -Passthru -DeleteMethod DotNetDelete
 
    Removes both the folder (and everything in it) and the file, then returns details on each removal attempt.
 
    #>

    [Alias('Remove-ItemAlternative')]
    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Include')]
    param(
        [Parameter(Mandatory, ValueFromPipeline, Position = 0)][Alias('LiteralPath', 'Path')][string[]] $Paths,

        [ValidateSet("RemoveItem", "DotNetDelete", "RecycleBin")]
        [string] $DeleteMethod = "RecycleBin",

        [switch] $Recursive,

        [alias('SkipFolder')][switch] $SkipTopFolder,

        [int] $Retries = 1,

        [Parameter(ParameterSetName = 'Include')][string[]] $Include,

        [Parameter(ParameterSetName = 'Exclude')][string[]] $Exclude,

        [string] $Filter,

        [switch] $Passthru,

        [switch] $SimpleReturn
    )

    begin {
        $Results = [System.Collections.Generic.List[PSObject]]::new()
        $AllSucceeded = $true
    }

    process {
        foreach ($Path in $Paths) {

            $UnresolvedFullPath = $null
            try {
                $UnresolvedFullPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
            } catch {
                $UnresolvedFullPath = $null
            }

            if (-not $UnresolvedFullPath) {
                $Message = "Remove-FileItem - Unable to resolve path '$Path'."
                if ($PSCmdlet.ErrorAction -eq 'Stop') {
                    throw $Message
                } else {
                    Write-Warning -Message $Message
                }
                $AllSucceeded = $false

                if ($Passthru) {
                    $Results.Add(
                        [PSCustomObject]@{
                            Path         = $Path
                            Status       = $false
                            Reason       = "Path could not be resolved."
                            AttemptCount = 0
                            WhatIf       = $WhatIfPreference
                        }
                    )
                }
                continue
            }

            if (-not (Test-Path -LiteralPath $UnresolvedFullPath)) {
                $Message = "Remove-FileItem - Path '$UnresolvedFullPath' does not exist."
                if ($PSCmdlet.ErrorAction -eq 'Stop') {
                    throw $Message
                } else {
                    Write-Warning -Message $Message
                }
                $AllSucceeded = $false
                if ($Passthru) {
                    $Results.Add(
                        [PSCustomObject]@{
                            Path         = $UnresolvedFullPath
                            Status       = $false
                            Reason       = "Path does not exist."
                            AttemptCount = 0
                            WhatIf       = $WhatIfPreference
                        }
                    )
                }
                continue
            }

            $ItemInfo = Get-Item -LiteralPath $UnresolvedFullPath -Force
            $IsDirectory = $ItemInfo.PSIsContainer

            if ($IsDirectory -and $SkipTopFolder) {

                $removeChildItemsSplat = @{
                    TopLevelPath    = $UnresolvedFullPath
                    DeleteMethod    = $DeleteMethod
                    Recursive       = $Recursive
                    Retries         = $Retries
                    Include         = $Include
                    Exclude         = $Exclude
                    Filter          = $Filter
                    Passthru        = $Passthru
                    Results         = $Results
                    AllSucceededRef = ([ref]$AllSucceeded)
                }
                Remove-ChildItems @removeChildItemsSplat
            } else {

                $Attempt = 0
                $Deleted = $false
                $LastError = $null
                $WhatIfUsed = $WhatIfPreference

                while ($Attempt -lt $Retries -and -not $Deleted) {
                    $Attempt++
                    Write-Verbose -Message "Remove-FileItem - Attempting to remove '$UnresolvedFullPath' (Attempt $Attempt of $Retries)."

                    if ($WhatIfUsed) {
                        $LastError = "Skipped because -WhatIf was used."
                    }
                    if ($PSCmdlet.ShouldProcess($UnresolvedFullPath, "Remove top-level")) {

                        try {
                            switch ($DeleteMethod) {
                                'RemoveItem' {
                                    if ($IsDirectory -and $Recursive) {
                                        Remove-Item -LiteralPath $UnresolvedFullPath -Recurse -Force -ErrorAction Stop
                                    } else {
                                        Remove-Item -LiteralPath $UnresolvedFullPath -Force -ErrorAction Stop
                                    }
                                }
                                'DotNetDelete' {
                                    if ($IsDirectory) {
                                        $DirInfo = [System.IO.DirectoryInfo] $UnresolvedFullPath
                                        if ($Recursive) {
                                            $DirInfo.Delete($true)
                                        } else {
                                            $DirInfo.Delete()
                                        }
                                    } else {
                                        $FileInfo = [System.IO.FileInfo] $UnresolvedFullPath
                                        $FileInfo.Delete()
                                    }
                                }
                                'RecycleBin' {
                                    $Shell = [Activator]::CreateInstance([Type]::GetTypeFromProgID("Shell.Application"))
                                    $FolderPath = Split-Path $UnresolvedFullPath
                                    $Leaf = Split-Path $UnresolvedFullPath -Leaf
                                    $Folder = $Shell.NameSpace($FolderPath)
                                    if (-not $Folder) {
                                        throw "Could not open folder: $FolderPath"
                                    }
                                    $ShellItem = $Folder.ParseName($Leaf)
                                    if (-not $ShellItem) {
                                        throw "Item '$Leaf' not found in folder: $FolderPath"
                                    }
                                    $ShellItem.InvokeVerb("delete")
                                }
                            }
                            $Deleted = $true
                        } catch {
                            $LastError = $_.Exception.Message
                            Write-Verbose -Message "Remove-FileItem - Error deleting '$UnresolvedFullPath': $LastError"

                            if ($PSCmdlet.ErrorAction -eq 'Stop') {
                                if ($Attempt -ge $Retries) {
                                    throw "Remove-FileItem - Couldn't delete '$UnresolvedFullPath' after $Retries attempts. Error: $LastError"
                                }
                            } else {
                                if ($Attempt -ge $Retries) {
                                    Write-Warning -Message "Remove-FileItem - Couldn't delete '$UnresolvedFullPath' after $Retries attempts. Error: $LastError"
                                }
                            }
                        }
                    }
                }

                if (-not $Deleted -and -not $WhatIfUsed) {
                    $AllSucceeded = $false
                }

                if ($WhatIfUsed) {
                    $AllSucceeded = $false
                }

                if ($Passthru) {
                    $Results.Add(
                        [PSCustomObject]@{
                            Path         = $UnresolvedFullPath
                            Status       = $Deleted
                            Reason       = if ($Deleted) {
                                $null 
                            } else {
                                $LastError 
                            }
                            AttemptCount = $Attempt
                            WhatIf       = $WhatIfUsed
                        }
                    )
                }
            }
        }
    }
    end {
        if ($Passthru) {
            $Results
        } elseif ($SimpleReturn) {
            $AllSucceeded
        }
    }
}
function Set-LoggingCapabilities { 
    <#
    .SYNOPSIS
    Sets up logging capabilities by managing log files.
 
    .DESCRIPTION
    This function sets up logging capabilities by creating the necessary directories and managing the number of log files based on the specified maximum.
 
    .PARAMETER LogPath
    The path where the log files will be stored.
 
    .PARAMETER ScriptPath
    The path of the script that generates the logs.
 
    .PARAMETER LogMaximum
    The maximum number of log files to keep. Older files will be deleted if this limit is exceeded.
 
    .PARAMETER ShowTime
    Switch to include timestamps in the log entries.
 
    .PARAMETER TimeFormat
    The format of the timestamps in the log entries.
 
    .PARAMETER ParameterPSDefaultParameterValues
    The hashtable of default parameter values for the Write-Color function.
    If this parameter is not provided, the function will create a new hashtable.
    This will only work properly if the function is nested as private function in another module.
    It's advised to provide the hashtable from the parent function for this to work always.
 
    .EXAMPLE
    Set-LoggingCapabilities -LogPath "C:\Logs\log.log" -ScriptPath "C:\Scripts\script.ps1" -LogMaximum 10 -ShowTime -TimeFormat "yyyy-MM-dd HH:mm:ss" -ParameterPSDefaultParameterValues $Script:PSDefaultParameterValues
 
    .EXAMPLE
    Set-LoggingCapabilities -LogPath "C:\Logs\log.log" -ScriptPath "C:\Scripts\script.ps1" -LogMaximum 10 -ShowTime -TimeFormat "yyyy-MM-dd HH:mm:ss"
 
    .NOTES
    This function is used in:
    - CleanupMonster
    - PasswordSolution
    - SharePointEssentials
 
    And many other scripts.
    #>

    [CmdletBinding()]
    param(
        [Alias('Path', 'Log', 'Folder', 'LiteralPath', 'FilePath')][string] $LogPath,
        [string] $ScriptPath,
        [Alias('Maximum')][int] $LogMaximum,
        [switch] $ShowTime,
        [string] $TimeFormat,
        [System.Collections.IDictionary] $ParameterPSDefaultParameterValues
    )
    if (-not $ParameterPSDefaultParameterValues) {
        $Script:PSDefaultParameterValues = @{
            "Write-Color:LogFile"    = $LogPath
            "Write-Color:ShowTime"   = if ($PSBoundParameters.ContainsKey('ShowTime')) {
                $ShowTime.IsPresent 
            } else {
                $null 
            }
            "Write-Color:TimeFormat" = $TimeFormat
        }
    } else {
        $ParameterPSDefaultParameterValues["Write-Color:LogFile"] = $LogPath
        $ParameterPSDefaultParameterValues["Write-Color:ShowTime"] = if ($PSBoundParameters.ContainsKey('ShowTime')) {
            $ShowTime.IsPresent 
        } else {
            $null 
        }
        $ParameterPSDefaultParameterValues["Write-Color:TimeFormat"] = $TimeFormat
    }
    if ($LogPath) {
        try {
            $FolderPath = [io.path]::GetDirectoryName($LogPath)
            if (-not (Test-Path -LiteralPath $FolderPath)) {
                $null = New-Item -Path $FolderPath -ItemType Directory -Force -WhatIf:$false
            }
            if ($LogMaximum -gt 0) {
                if ($ScriptPath) {
                    $ScriptPathFolder = [io.path]::GetDirectoryName($ScriptPath)
                    if ($ScriptPathFolder -eq $FolderPath) {
                        Write-Color -Text '[i] ', "LogMaximum is set to ", $LogMaximum, " but log files are in the same folder as the script. Cleanup disabled." -Color Yellow, White, DarkCyan, White
                        return
                    }

                    $LogPathExtension = [io.path]::GetExtension($LogPath)

                    if ($LogPathExtension) {

                        $CurrentLogs = Get-ChildItem -LiteralPath $FolderPath -Filter "*$LogPathExtension" -ErrorAction Stop | Sort-Object -Property CreationTime -Descending | Select-Object -Skip $LogMaximum
                    } else {
                        $CurrentLogs = $null
                        Write-Color -Text '[i] ', "Log file has no extension (?!). Cleanup disabled." -Color Yellow, White, DarkCyan, White
                    }
                    if ($CurrentLogs) {
                        Write-Color -Text '[i] ', "Logs directory has more than ", $LogMaximum, " log files. Cleanup required..." -Color Yellow, DarkCyan, Red, DarkCyan
                        foreach ($Log in $CurrentLogs) {
                            try {
                                Remove-Item -LiteralPath $Log.FullName -Confirm:$false -WhatIf:$false
                                Write-Color -Text '[+] ', "Deleted ", "$($Log.FullName)" -Color Yellow, White, Green
                            } catch {
                                Write-Color -Text '[-] ', "Couldn't delete log file $($Log.FullName). Error: ', "$($_.Exception.Message) -Color Yellow, White, Red
                            }
                        }
                    }
                } else {
                    Write-Color -Text '[i] ', "LogMaximum is set to ", $LogMaximum, " but no script path detected. Most likely running interactively. Cleanup disabled." -Color Yellow, White, DarkCyan, White
                }
            } else {
                Write-Color -Text '[i] ', "LogMaximum is set to 0 (Unlimited). No log files will be deleted." -Color Yellow, DarkCyan
            }
        } catch {
            Write-Color -Text "[e] ", "Couldn't create the log directory. Error: $($_.Exception.Message)" -Color Yellow, Red
            $Script:PSDefaultParameterValues["Write-Color:LogFile"] = $null
        }
    } else {
        $Script:PSDefaultParameterValues["Write-Color:LogFile"] = $null
    }
    Remove-EmptyValue -Hashtable $Script:PSDefaultParameterValues
}
function Start-TimeLog { 
    <#
    .SYNOPSIS
    Starts a new stopwatch for logging time.
 
    .DESCRIPTION
    This function starts a new stopwatch that can be used for logging time durations.
 
    .EXAMPLE
    Start-TimeLog
    Starts a new stopwatch for logging time.
 
    #>

    [CmdletBinding()]
    param()
    [System.Diagnostics.Stopwatch]::StartNew()
}
function Stop-TimeLog { 
    <#
    .SYNOPSIS
    Stops the stopwatch and returns the elapsed time in a specified format.
 
    .DESCRIPTION
    The Stop-TimeLog function stops the provided stopwatch and returns the elapsed time in a specified format. The function can output the elapsed time as a single string or an array of days, hours, minutes, seconds, and milliseconds.
 
    .PARAMETER Time
    Specifies the stopwatch object to stop and retrieve the elapsed time from.
 
    .PARAMETER Option
    Specifies the format in which the elapsed time should be returned. Valid values are 'OneLiner' (default) or 'Array'.
 
    .PARAMETER Continue
    Indicates whether the stopwatch should continue running after retrieving the elapsed time.
 
    .EXAMPLE
    $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
    # Perform some operations
    Stop-TimeLog -Time $stopwatch
    # Output: "0 days, 0 hours, 0 minutes, 5 seconds, 123 milliseconds"
 
    .EXAMPLE
    $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
    # Perform some operations
    Stop-TimeLog -Time $stopwatch -Option Array
    # Output: ["0 days", "0 hours", "0 minutes", "5 seconds", "123 milliseconds"]
    #>

    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true)][System.Diagnostics.Stopwatch] $Time,
        [ValidateSet('OneLiner', 'Array')][string] $Option = 'OneLiner',
        [switch] $Continue
    )
    Begin {
    }
    Process {
        if ($Option -eq 'Array') {
            $TimeToExecute = "$($Time.Elapsed.Days) days", "$($Time.Elapsed.Hours) hours", "$($Time.Elapsed.Minutes) minutes", "$($Time.Elapsed.Seconds) seconds", "$($Time.Elapsed.Milliseconds) milliseconds"
        } else {
            $TimeToExecute = "$($Time.Elapsed.Days) days, $($Time.Elapsed.Hours) hours, $($Time.Elapsed.Minutes) minutes, $($Time.Elapsed.Seconds) seconds, $($Time.Elapsed.Milliseconds) milliseconds"
        }
    }
    End {
        if (-not $Continue) {
            $Time.Stop()
        }
        return $TimeToExecute
    }
}
function Write-Color { 
    <#
    .SYNOPSIS
    Write-Color is a wrapper around Write-Host delivering a lot of additional features for easier color options.
 
    .DESCRIPTION
    Write-Color is a wrapper around Write-Host delivering a lot of additional features for easier color options.
 
    It provides:
    - Easy manipulation of colors,
    - Logging output to file (log)
    - Nice formatting options out of the box.
    - Ability to use aliases for parameters
 
    .PARAMETER Text
    Text to display on screen and write to log file if specified.
    Accepts an array of strings.
 
    .PARAMETER Color
    Color of the text. Accepts an array of colors. If more than one color is specified it will loop through colors for each string.
    If there are more strings than colors it will start from the beginning.
    Available colors are: Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White
 
    .PARAMETER BackGroundColor
    Color of the background. Accepts an array of colors. If more than one color is specified it will loop through colors for each string.
    If there are more strings than colors it will start from the beginning.
    Available colors are: Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White
 
    .PARAMETER StartTab
    Number of tabs to add before text. Default is 0.
 
    .PARAMETER LinesBefore
    Number of empty lines before text. Default is 0.
 
    .PARAMETER LinesAfter
    Number of empty lines after text. Default is 0.
 
    .PARAMETER StartSpaces
    Number of spaces to add before text. Default is 0.
 
    .PARAMETER LogFile
    Path to log file. If not specified no log file will be created.
 
    .PARAMETER DateTimeFormat
    Custom date and time format string. Default is yyyy-MM-dd HH:mm:ss
 
    .PARAMETER LogTime
    If set to $true it will add time to log file. Default is $true.
 
    .PARAMETER LogRetry
    Number of retries to write to log file, in case it can't write to it for some reason, before skipping. Default is 2.
 
    .PARAMETER Encoding
    Encoding of the log file. Default is Unicode.
 
    .PARAMETER ShowTime
    Switch to add time to console output. Default is not set.
 
    .PARAMETER NoNewLine
    Switch to not add new line at the end of the output. Default is not set.
 
    .PARAMETER NoConsoleOutput
    Switch to not output to console. Default all output goes to console.
 
    .EXAMPLE
    Write-Color -Text "Red ", "Green ", "Yellow " -Color Red,Green,Yellow
 
    .EXAMPLE
    Write-Color -Text "This is text in Green ",
                      "followed by red ",
                      "and then we have Magenta... ",
                      "isn't it fun? ",
                      "Here goes DarkCyan" -Color Green,Red,Magenta,White,DarkCyan
 
    .EXAMPLE
    Write-Color -Text "This is text in Green ",
                      "followed by red ",
                      "and then we have Magenta... ",
                      "isn't it fun? ",
                      "Here goes DarkCyan" -Color Green,Red,Magenta,White,DarkCyan -StartTab 3 -LinesBefore 1 -LinesAfter 1
 
    .EXAMPLE
    Write-Color "1. ", "Option 1" -Color Yellow, Green
    Write-Color "2. ", "Option 2" -Color Yellow, Green
    Write-Color "3. ", "Option 3" -Color Yellow, Green
    Write-Color "4. ", "Option 4" -Color Yellow, Green
    Write-Color "9. ", "Press 9 to exit" -Color Yellow, Gray -LinesBefore 1
 
    .EXAMPLE
    Write-Color -LinesBefore 2 -Text "This little ","message is ", "written to log ", "file as well." `
                -Color Yellow, White, Green, Red, Red -LogFile "C:\testing.txt" -TimeFormat "yyyy-MM-dd HH:mm:ss"
    Write-Color -Text "This can get ","handy if ", "want to display things, and log actions to file ", "at the same time." `
                -Color Yellow, White, Green, Red, Red -LogFile "C:\testing.txt"
 
    .EXAMPLE
    Write-Color -T "My text", " is ", "all colorful" -C Yellow, Red, Green -B Green, Green, Yellow
    Write-Color -t "my text" -c yellow -b green
    Write-Color -text "my text" -c red
 
    .EXAMPLE
    Write-Color -Text "Testuję czy się ładnie zapisze, czy będą problemy" -Encoding unicode -LogFile 'C:\temp\testinggg.txt' -Color Red -NoConsoleOutput
 
    .NOTES
    Understanding Custom date and time format strings: https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings
    Project support: https://github.com/EvotecIT/PSWriteColor
    Original idea: Josh (https://stackoverflow.com/users/81769/josh)
 
    #>

    [alias('Write-Colour')]
    [CmdletBinding()]
    param (
        [alias ('T')] [String[]]$Text,
        [alias ('C', 'ForegroundColor', 'FGC')] [ConsoleColor[]]$Color = [ConsoleColor]::White,
        [alias ('B', 'BGC')] [ConsoleColor[]]$BackGroundColor = $null,
        [alias ('Indent')][int] $StartTab = 0,
        [int] $LinesBefore = 0,
        [int] $LinesAfter = 0,
        [int] $StartSpaces = 0,
        [alias ('L')] [string] $LogFile = '',
        [Alias('DateFormat', 'TimeFormat')][string] $DateTimeFormat = 'yyyy-MM-dd HH:mm:ss',
        [alias ('LogTimeStamp')][bool] $LogTime = $true,
        [int] $LogRetry = 2,
        [ValidateSet('unknown', 'string', 'unicode', 'bigendianunicode', 'utf8', 'utf7', 'utf32', 'ascii', 'default', 'oem')][string]$Encoding = 'Unicode',
        [switch] $ShowTime,
        [switch] $NoNewLine,
        [alias('HideConsole')][switch] $NoConsoleOutput
    )
    if (-not $NoConsoleOutput) {
        $DefaultColor = $Color[0]
        if ($null -ne $BackGroundColor -and $BackGroundColor.Count -ne $Color.Count) {
            Write-Error "Colors, BackGroundColors parameters count doesn't match. Terminated."
            return
        }
        if ($LinesBefore -ne 0) {
            for ($i = 0; $i -lt $LinesBefore; $i++) {
                Write-Host -Object "`n" -NoNewline 
            } 
        } 
        if ($StartTab -ne 0) {
            for ($i = 0; $i -lt $StartTab; $i++) {
                Write-Host -Object "`t" -NoNewline 
            } 
        }  
        if ($StartSpaces -ne 0) {
            for ($i = 0; $i -lt $StartSpaces; $i++) {
                Write-Host -Object ' ' -NoNewline 
            } 
        }  
        if ($ShowTime) {
            Write-Host -Object "[$([datetime]::Now.ToString($DateTimeFormat))] " -NoNewline 
        } 
        if ($Text.Count -ne 0) {
            if ($Color.Count -ge $Text.Count) {

                if ($null -eq $BackGroundColor) {
                    for ($i = 0; $i -lt $Text.Length; $i++) {
                        Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -NoNewline 
                    }
                } else {
                    for ($i = 0; $i -lt $Text.Length; $i++) {
                        Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackGroundColor[$i] -NoNewline 
                    }
                }
            } else {
                if ($null -eq $BackGroundColor) {
                    for ($i = 0; $i -lt $Color.Length ; $i++) {
                        Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -NoNewline 
                    }
                    for ($i = $Color.Length; $i -lt $Text.Length; $i++) {
                        Write-Host -Object $Text[$i] -ForegroundColor $DefaultColor -NoNewline 
                    }
                } else {
                    for ($i = 0; $i -lt $Color.Length ; $i++) {
                        Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackGroundColor[$i] -NoNewline 
                    }
                    for ($i = $Color.Length; $i -lt $Text.Length; $i++) {
                        Write-Host -Object $Text[$i] -ForegroundColor $DefaultColor -BackgroundColor $BackGroundColor[0] -NoNewline 
                    }
                }
            }
        }
        if ($NoNewLine -eq $true) {
            Write-Host -NoNewline 
        } else {
            Write-Host 
        } 
        if ($LinesAfter -ne 0) {
            for ($i = 0; $i -lt $LinesAfter; $i++) {
                Write-Host -Object "`n" -NoNewline 
            } 
        }  
    }
    if ($Text.Count -and $LogFile) {

        $TextToFile = ""
        for ($i = 0; $i -lt $Text.Length; $i++) {
            $TextToFile += $Text[$i]
        }
        $Saved = $false
        $Retry = 0
        Do {
            $Retry++
            try {
                if ($LogTime) {
                    "[$([datetime]::Now.ToString($DateTimeFormat))] $TextToFile" | Out-File -FilePath $LogFile -Encoding $Encoding -Append -ErrorAction Stop -WhatIf:$false
                } else {
                    "$TextToFile" | Out-File -FilePath $LogFile -Encoding $Encoding -Append -ErrorAction Stop -WhatIf:$false
                }
                $Saved = $true
            } catch {
                if ($Saved -eq $false -and $Retry -eq $LogRetry) {
                    Write-Warning "Write-Color - Couldn't write to log file $($_.Exception.Message). Tried ($Retry/$LogRetry))"
                } else {
                    Write-Warning "Write-Color - Couldn't write to log file $($_.Exception.Message). Retrying... ($Retry/$LogRetry)"
                }
            }
        } Until ($Saved -eq $true -or $Retry -ge $LogRetry)
    }
}
function Remove-ChildItems { 
    <#
    .SYNOPSIS
    Helper function that removes the child items of a given folder.
 
    .DESCRIPTION
    - Enumerates the folder's contents (recursively if requested).
    - Removes each child item individually, respecting the chosen DeleteMethod.
    - Respects -WhatIf and -Confirm (because of CmdletBinding with SupportsShouldProcess).
    - Records attempts in Passthru objects if desired.
 
    .PARAMETER TopLevelPath
    Specifies the path to the folder whose contents are to be removed.
 
    .PARAMETER DeleteMethod
    Specifies the removal method: RemoveItem, DotNetDelete, or RecycleBin.
 
    .PARAMETER Recursive
    Specifies whether to enumerate child items recursively.
 
    .PARAMETER Retries
    Number of retry attempts for each item.
 
    .PARAMETER Include
    Include patterns for Get-ChildItem.
 
    .PARAMETER Exclude
    Exclude patterns for Get-ChildItem.
 
    .PARAMETER Filter
    Filter for Get-ChildItem.
 
    .PARAMETER Passthru
    If true, records results in $Results.
 
    .PARAMETER Results
    [System.Collections.Generic.List[PSObject]] passed in from the parent function.
 
    .PARAMETER AllSucceededRef
    [ref] reference to a boolean tracking overall success/failure.
 
    .EXAMPLE
    Remove-ChildItems -TopLevelPath "C:\Folder" -DeleteMethod RemoveItem -Recursive -WhatIf
 
    Shows (via WhatIf) which items would be removed inside "C:\Folder".
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)][string] $TopLevelPath,

        [Parameter(Mandatory)][ValidateSet("RemoveItem", "DotNetDelete", "RecycleBin")]
        [string] $DeleteMethod,

        [switch] $Recursive,

        [int] $Retries,

        [string[]] $Include,

        [string[]] $Exclude,

        [string] $Filter,

        [switch] $Passthru,

        [System.Collections.Generic.List[PSObject]] $Results,

        [ref] $AllSucceededRef
    )
    begin {
        Write-Verbose -Message "Remove-ItemAlternative - Removing contents of '$TopLevelPath' (SkipFolder)."
        $getChildItemSplat = @{
            LiteralPath = $TopLevelPath
            Force       = $true
            Include     = $Include
            Exclude     = $Exclude
            Filter      = $Filter
            ErrorAction = 'SilentlyContinue'
        }
        Remove-EmptyValue -Hashtable $getChildItemSplat

        $Items = Get-ChildItem @getChildItemSplat
        if ($Recursive) {
            $Items = $Items | Sort-Object -Property FullName -Descending
        }

        $WhatIfUsed = $WhatIfPreference
    }
    process {
        foreach ($Item in $Items) {
            $Attempt = 0
            $Deleted = $false
            $LastError = $null

            while ($Attempt -lt $Retries -and -not $Deleted) {
                $Attempt++
                Write-Verbose -Message "Remove-ItemAlternative - Attempting to remove '$($Item.FullName)' (Attempt $Attempt of $Retries)."

                if ($WhatIfUsed) {

                    $LastError = "Skipped because -WhatIf was used."
                }
                if ($PSCmdlet.ShouldProcess($Item.FullName, "Remove child item")) {
                    try {
                        switch ($DeleteMethod) {
                            'RemoveItem' {
                                if ($Recursive -and $Item.PSIsContainer) {
                                    Remove-Item -LiteralPath $Item.FullName -Recurse -Force -ErrorAction Stop
                                } else {
                                    Remove-Item -LiteralPath $Item.FullName -Force -ErrorAction Stop
                                }
                            }
                            'DotNetDelete' {
                                if ($Item.PSIsContainer) {
                                    $Dir = [System.IO.DirectoryInfo] $Item.FullName
                                    if ($Recursive) {
                                        $Dir.Delete($true)
                                    } else {
                                        $Dir.Delete()
                                    }
                                } else {
                                    $File = [System.IO.FileInfo] $Item.FullName
                                    $File.Delete()
                                }
                            }
                            'RecycleBin' {
                                $Shell = [Activator]::CreateInstance([Type]::GetTypeFromProgID("Shell.Application"))
                                $FolderPath = Split-Path $Item.FullName
                                $Leaf = Split-Path $Item.FullName -Leaf
                                $Folder = $Shell.NameSpace($FolderPath)
                                if (-not $Folder) {
                                    throw "Could not open folder: $FolderPath"
                                }
                                $ShellItem = $Folder.ParseName($Leaf)
                                if (-not $ShellItem) {
                                    throw "Item '$Leaf' not found in folder: $FolderPath"
                                }
                                $ShellItem.InvokeVerb("delete")
                            }
                        }
                        $Deleted = $true
                    } catch {
                        $LastError = $_.Exception.Message
                        Write-Verbose -Message "Remove-ItemAlternative - Error deleting '$($Item.FullName)': $LastError"

                        if ($PSCmdlet.ErrorAction -eq 'Stop') {
                            if ($Attempt -ge $Retries) {
                                throw "Remove-ItemAlternative - Couldn't delete '$($Item.FullName)' after $Retries attempts. Error: $LastError"
                            }
                        } else {
                            if ($Attempt -ge $Retries) {
                                Write-Warning -Message "Remove-ItemAlternative - Couldn't delete '$($Item.FullName)' after $Retries attempts. Error: $LastError"
                            }
                        }
                    }
                }
            }

            if (-not $Deleted -and -not $WhatIfUsed) {
                $AllSucceededRef.Value = $false
            }

            if ($Passthru) {
                $Results.Add(
                    [PSCustomObject]@{
                        Path         = $Item.FullName
                        Status       = $Deleted
                        Reason       = if ($Deleted) {
                            $null 
                        } else {
                            $LastError 
                        }
                        AttemptCount = $Attempt
                        WhatIf       = $WhatIfUsed
                    }
                )
            }
        }
    }
}
function Convert-FilesToKeepOrRemove {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Array] $FilePathsGenerated,
        [Array] $Files,
        [switch] $RemoveNotIncluded,
        [ValidateSet("RemoveItem", "DotNetDelete", "RecycleBin")]
        [string] $DeleteMethod = "RecycleBin"
    )
    [Array] $FullSummary = @(
        foreach ($File in $FilePathsGenerated) {
            try {
                $FileInfo = Get-Item -LiteralPath $File -ErrorAction Stop
                $Output = [PSCustomObject] @{
                    Type    = 'Dashboard'
                    Path    = $File
                    Include = $true
                    Date    = ($FileInfo).LastWriteTime
                    Status  = 'Included'
                }
            } catch {
                $Output = [PSCustomObject] @{
                    Type    = 'Dashboard'
                    Path    = $File
                    Include = $false
                    Date    = $null
                    Status  = 'Included (WhatIf)'
                }
            }
            if ($WhatIfPreference) {
                $Output.Status = 'Included (WhatIf)'
            }
            $Output
        }
        foreach ($File in $Files) {
            [PSCustomObject] @{
                Type    = 'Report'
                Path    = $File.FullPath
                Include = $File.Include
                Date    = $File.Date
                Status  = if ($File.Include) {
                    'Included' } else {
                    'Excluded' }
            }
        }
    )
    if ($RemoveNotIncluded) {
        foreach ($File in $FullSummary) {
            if (-not $File.Include) {
                Remove-FileItem -Paths $File.Path -DeleteMethod $DeleteMethod -WhatIf:$WhatIfPreference
                if ($WhatIfPreference) {
                    $File.Status = 'Removed (WhatIf)'
                } else {
                    $File.Status = 'Removed'
                }
            }
        }
    }
    $FullSummary
}
function Convert-FilesToMenu {
    <#
    .SYNOPSIS
    Generates a structured menu from a collection of files and folders.
 
    .DESCRIPTION
    Builds ordered folders and file entries, applies filtering and history logic.
 
    .PARAMETER Folders
    Dictionary defining folder metadata and optional limits configuration.
 
    .PARAMETER Files
    Array of file data, each containing attributes such as Date, Menu, and Name.
 
    .PARAMETER ExportData
    Dictionary containing exported data from previous runs.
 
    .PARAMETER Force
    Forces the regeneration of the menu.
 
    .EXAMPLE
    Convert-FilesToMenu -Folders $Folders -Files $FileList
 
    .NOTES
    Part of TheDashboard module, creates the navigation menu from report files.
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Folders,
        [Array] $Files,
        [System.Collections.IDictionary] $ExportData,
        [switch] $Force
    )
    $CurrentDate = Get-Date

    $MenuBuilder = [ordered] @{}

    foreach ($Folder in $Folders.Keys) {
        if (-not $MenuBuilder[$Folder]) {
            $MenuBuilder[$Folder] = [ordered] @{}
        }
    }

    foreach ($Entry in $Files | Sort-Object { $_.Date } -Descending) {
        $LimitsConfiguration = $Folders[$Entry.Menu].LimitsConfiguration
        if ($LimitsConfiguration) {
            $Limits = $LimitsConfiguration[$Entry.Name]
            if (-not $Limits) {
                if ($LimitsConfiguration['*']) {
                    $Limits = $LimitsConfiguration['*']
                } else {
                    $Limits = $null
                }
            }
        } else {
            $Limits = $null
        }

        if (-not $MenuBuilder[$Entry.Menu][$Entry.Name]) {
            $Entry.Include = $true
            $MenuBuilder[$Entry.Menu][$Entry.Name] = @{
                Current = $Entry
                Full    = [System.Collections.Generic.List[Object]]::new()
                History = [System.Collections.Generic.List[Object]]::new()
            }
        }
        if ($Entry.Include -ne $true) {
            if ($Limits.LimitItem) {
                if ($MenuBuilder[$Entry.Menu][$Entry.Name]['Full'].Count -ge $Limits.LimitItem) {
                    $Entry.Include = $true

                    Limit-FilesHistory -MenuBuilder $MenuBuilder -Entry $Entry -Limits $Limits -CurrentDate $CurrentDate
                    continue
                }
            } elseif ($Limits.LimitDate) {
                if ($Entry.Date -lt $Limits.LimitDate) {
                    $Entry.Include = $true

                    Limit-FilesHistory -MenuBuilder $MenuBuilder -Entry $Entry -Limits $Limits -CurrentDate $CurrentDate
                    continue
                }
            } elseif ($Limits.LimitDays) {
                if ($Entry.Date -lt ($CurrentDate).AddDays(-$Limits.LimitDays)) {
                    $Entry.Include = $true

                    Limit-FilesHistory -MenuBuilder $MenuBuilder -Entry $Entry -Limits $Limits -CurrentDate $CurrentDate
                    continue
                }
            }
        }
        $Entry.Include = $true
        $MenuBuilder[$Entry.Menu][$Entry.Name]['Full'].Add($Entry)
    }

    if ($Force) {
        return $MenuBuilder
    }

    if ($ExportData.MenuBuilder.Keys.Count -eq 0) {

        return $MenuBuilder
    }
    if ($MenuBuilder.Keys -and $ExportData.MenuBuilder.Keys -and $MenuBuilder.Keys.Count -ne $ExportData.MenuBuilder.Keys.Count) {

        return $MenuBuilder
    }
    $Comparison = Compare-Object -ReferenceObject $MenuBuilder.Keys -DifferenceObject $ExportData.MenuBuilder.Keys
    if ($Comparison) {

        return $MenuBuilder
    }

    foreach ($Section in $MenuBuilder.Keys) {
        if ($ExportData.MenuBuilder[$Section].Count -ne $MenuBuilder[$Section].Count) {

            return $MenuBuilder
        }
        $Comparison = Compare-Object -ReferenceObject $MenuBuilder[$Section].Keys -DifferenceObject $ExportData.MenuBuilder[$Section].Keys
        if ($Comparison) {

            return $MenuBuilder
        }
    }

    foreach ($Section in $MenuBuilder.Keys) {
        foreach ($Name in $MenuBuilder[$Section].Keys) {
            $MenuData = $MenuBuilder[$Section][$Name]
            $MenuDataFromImport = $ExportData.MenuBuilder[$Section][$Name]

            $RegenerateNotRequired = $true
            $Comparison = Compare-Object -ReferenceObject $MenuData.Current -DifferenceObject $MenuDataFromImport.Current -Property Date, Name, Href
            if ($Comparison) {
                $RegenerateNotRequired = $false
            }
            $Comparison = Compare-Object -ReferenceObject $MenuData.Full -DifferenceObject $MenuDataFromImport.Full -Property Date, Name, Href
            if ($Comparison) {
                $RegenerateNotRequired = $false
            }
            $Comparison = Compare-Object -ReferenceObject $MenuData.History -DifferenceObject $MenuDataFromImport.History -Property Date, Name, Href
            if ($Comparison) {
                $RegenerateNotRequired = $false
            }
            if ($RegenerateNotRequired -eq $true) {
                $MenuData.Current.SkipGeneration = $true
                foreach ($Entry in $MenuData.Full) {
                    $Entry.SkipGeneration = $true
                }
                foreach ($Entry in $MenuData.History) {
                    $Entry.SkipGeneration = $true
                }
            }
        }
    }
    $MenuBuilder
}
function Convert-FilesToMenuData {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Folders,
        [System.Collections.IDictionary] $Replacements,
        [string] $Extension
    )
    Write-Color -Text '[i]', "[TheDashboard] ", 'Creating Menu from files', ' [Informative] ' -Color Yellow, DarkGray, Yellow, DarkGray, Magenta
    foreach ($FolderName in $Folders.Keys) {
        $Folder = $Folders[$FolderName]
        $FilesInFolder = Get-ChildItem -LiteralPath $Folders[$FolderName].Path -ErrorAction SilentlyContinue -Filter "*$Extension" | Sort-Object -Property Name
        Write-Color -Text '[i]', "[TheDashboard] ", "Creating Menu from files in folder ", "'$FolderName'", " files in folder ", $($FilesInFolder.Count), ' [Informative] ' -Color Yellow, DarkGray, Yellow, Magenta, Yellow, Magenta, DarkGray
        foreach ($File in $FilesInFolder) {
            $RelativeFolder = Split-Path -Path $Folders[$FolderName].Path -Leaf
            $Href = "$($RelativeFolder)/$($File.Name)"
            $MenuName = $File.BaseName

            $ResultsFile = [ordered] @{
                File     = $File.Name
                Href     = $Href
                MenuName = $MenuName
            }
            $ResultsFile['ReplaceCaseInsenstive'] = $Replacements.ReplaceCaseInsenstive
            $ResultsFile['ReplaceSkipRegex'] = $Replacements.ReplaceSkipRegex

            if ($Folder.ReplacementsGlobal -eq $true) {
                foreach ($Replace in $Replacements.BeforeSplit.Keys) {
                    if ($Replacements.ReplaceCaseInsenstive -and $Replacements.ReplaceSkipRegex) {
                        $MenuName = $MenuName -ireplace $Replace, $Replacements.BeforeSplit[$Replace]
                    } elseif ($Replacements.ReplaceCaseInsenstive) {
                        $MenuName = $MenuName -ireplace [regex]::Escape($Replace), $Replacements.BeforeSplit[$Replace]
                    } else {
                        $MenuName = $MenuName.Replace($Replace, $Replacements.BeforeSplit[$Replace])
                    }
                }
                $ResultsFile['MenuNameStep2AfterBeforeSplitReplacements'] = $MenuName
                if ($Replacements.SplitOn) {
                    $Splitted = $MenuName -split $Replacements.SplitOn
                    if ($null -ne $Replacements.AfterSplitPositionName) {
                        $PositionPlace = @(0)
                        $Name = ''
                        if ($Replacements.AfterSplitPositionName -is [System.Collections.IDictionary]) {
                            foreach ($FileNameToFind in $Replacements.AfterSplitPositionName.Keys) {
                                if ($MenuName -like $FileNameToFind) {
                                    [Array] $PositionPlace = $Replacements.AfterSplitPositionName[$FileNameToFind]
                                    break
                                }
                            }
                        } else {
                            [Array] $PositionPlace = $Replacements.AfterSplitPositionName
                        }
                        $NameParts = foreach ($Position in $PositionPlace) {
                            $Splitted[$Position]
                        }
                        $Name = $NameParts -join ' '
                        $ResultsFile['SplitPosition'] = $PositionPlace -join ', '
                        $ResultsFile['SplitPositionName'] = $Name
                    } else {
                        $Name = $Splitted[0]
                        $ResultsFile['SplitPosition'] = 0
                        $ResultsFile['SplitPositionName'] = $Name
                    }
                } else {
                    $Name = $MenuName
                    $ResultsFile['SplitPosition'] = $null
                    $ResultsFile['SplitPositionName'] = $Name
                }
                $ResultsFile['MenuNameStep3AfterSplittingPosition'] = $Name

                $formatStringToSentenceSplat = @{
                    Text               = $Name
                    RemoveCharsBefore  = $Replacements.BeforeRemoveChars
                    RemoveCharsAfter   = $Replacements.AfterRemoveChars
                    RemoveDoubleSpaces = $Replacements.AfterRemoveDoubleSpaces
                    MakeWordsUpperCase = $Replacements.AfterUpperChars
                    DisableAddingSpace = -not $Replacements.AddSpaceToName
                }
                $Name = Format-StringToSentence @formatStringToSentenceSplat

                $ResultsFile['BeforeRemoveChars'] = $Replacements.BeforeRemoveChars
                $ResultsFile['AfterRemoveChars'] = $Replacements.AfterRemoveChars
                $ResultsFile['AfterRemoveDoubleSpaces'] = $Replacements.AfterRemoveDoubleSpaces
                $ResultsFile['AfterUpperChars'] = $Replacements.AfterUpperChars
                $ResultsFile['AddSpaceToName'] = $Replacements.AddSpaceToName
                $ResultsFile['MenuNameStep4AfterFormat'] = $Name
                foreach ($Replace in $Replacements.AfterSplit.Keys) {
                    if ($Replacements.ReplaceCaseInsenstive -and $Replacements.ReplaceSkipRegex) {
                        $Name = $Name -ireplace $Replace, $Replacements.AfterSplit[$Replace]
                    } elseif ($Replacements.ReplaceCaseInsenstive) {
                        $Name = $Name -ireplace [regex]::Escape($Replace), $Replacements.AfterSplit[$Replace]
                    } else {
                        $Name = $Name.Replace($Replace, $Replacements.AfterSplit[$Replace])
                    }
                }
                $ResultsFile['MenuNameFinal'] = $Name

                $Type = 'global replacements'

                $ResultsFile['Type'] = $Type
                if ($DebugPreference) {
                    [PSCustomObject] $ResultsFile | Out-String | Write-Debug
                }

            } elseif ($Folder.Replacements) {
                foreach ($Replace in $Folder.Replacements.BeforeSplit.Keys) {
                    if ($Folder.Replacements.ReplaceCaseInsenstive -and $Folder.Replacements.ReplaceSkipRegex) {
                        $MenuName = $MenuName -ireplace $Replace, $Folder.Replacements.BeforeSplit[$Replace]
                    } elseif ($Folder.Replacements.ReplaceCaseInsenstive) {
                        $MenuName = $MenuName -ireplace [regex]::Escape($Replace), $Folder.Replacements.BeforeSplit[$Replace]
                    } else {
                        $MenuName = $MenuName.Replace($Replace, $Folder.Replacements.BeforeSplit[$Replace])
                    }
                }
                $ResultsFile['MenuNameStep2AfterBeforeSplitReplacements'] = $MenuName
                if ($Folder.Replacements.SplitOn) {
                    $Splitted = $MenuName -split $Folder.Replacements.SplitOn
                    if ($null -ne $Folder.Replacements.AfterSplitPositionName) {
                        $PositionPlace = @(0)
                        $Name = ''
                        if ($Folder.Replacements.AfterSplitPositionName -is [System.Collections.IDictionary]) {
                            foreach ($FileNameToFind in $Folder.Replacements.AfterSplitPositionName.Keys) {
                                if ($MenuName -like $FileNameToFind) {
                                    [Array] $PositionPlace = $Folder.Replacements.AfterSplitPositionName[$FileNameToFind]
                                    break
                                }
                            }
                        } else {
                            [Array] $PositionPlace = $Folder.Replacements.AfterSplitPositionName
                        }
                        $NameParts = foreach ($Position in $PositionPlace) {
                            $Splitted[$Position]
                        }
                        $Name = $NameParts -join ' '
                        $ResultsFile['SplitPosition'] = $PositionPlace -join ', '
                        $ResultsFile['SplitPositionName'] = $Name
                    } else {
                        $Name = $Splitted[0]
                        $ResultsFile['SplitPosition'] = 0
                        $ResultsFile['SplitPositionName'] = $Name
                    }
                } else {
                    $Name = $MenuName
                    $ResultsFile['SplitPosition'] = $null
                    $ResultsFile['SplitPositionName'] = $Name
                }

                $ResultsFile['MenuNameStep3AfterSplittingPosition'] = $Name

                $formatStringToSentenceSplat = @{
                    Text               = $Name
                    RemoveCharsBefore  = $Folder.Replacements.BeforeRemoveChars
                    RemoveCharsAfter   = $Folder.Replacements.AfterRemoveChars
                    RemoveDoubleSpaces = $Folder.Replacements.AfterRemoveDoubleSpaces
                    MakeWordsUpperCase = $Folder.Replacements.AfterUpperChars
                    DisableAddingSpace = -not $Folder.Replacements.AddSpaceToName
                }

                $Name = Format-StringToSentence @formatStringToSentenceSplat

                $ResultsFile['BeforeRemoveChars'] = $Folder.Replacements.BeforeRemoveChars
                $ResultsFile['AfterRemoveChars'] = $Folder.Replacements.AfterRemoveChars
                $ResultsFile['AfterRemoveDoubleSpaces'] = $Folder.Replacements.AfterRemoveDoubleSpaces
                $ResultsFile['AfterUpperChars'] = $Folder.Replacements.AfterUpperChars
                $ResultsFile['AddSpaceToName'] = $Folder.Replacements.AddSpaceToName
                $ResultsFile['MenuNameStep4AfterFormat'] = $Name

                foreach ($Replace in $Folder.Replacements.AfterSplit.Keys) {
                    if ($Folder.Replacements.ReplaceCaseInsenstive -and $Folder.Replacements.ReplaceSkipRegex) {
                        $Name = $Name -ireplace $Replace, $Folder.Replacements.AfterSplit[$Replace]
                    } elseif ($Folder.Replacements.ReplaceCaseInsenstive) {
                        $Name = $Name -ireplace [regex]::Escape($Replace), $Folder.Replacements.AfterSplit[$Replace]
                    } else {
                        $Name = $Name.Replace($Replace, $Folder.Replacements.AfterSplit[$Replace])
                    }
                }

                $ResultsFile['MenuNameFinal'] = $Name
                $Type = 'folder replacements'
                $ResultsFile['Type'] = $Type

                if ($DebugPreference) {
                    [PSCustomObject] $ResultsFile | Out-String | Write-Debug
                }

            } else {
                $Name = $MenuName
                $Type = 'no replacements applied'
            }
            if ($Name) {
                [PSCustomObject] @{
                    Name           = $Name
                    Href           = $Href
                    FileName       = "$($Folder.Url)_$($File.Name)"
                    Menu           = $FolderName
                    MenuLink       = $Folder.Url
                    Date           = $File.LastWriteTime

                    FullPath       = $File.FullName

                    Include        = $false

                    SkipGeneration = $false

                    Debug          = [PSCustomObject] $ResultsFile
                }
            } else {
                Write-Color -Text "[e]", "[TheDashboard] ", "Creating Menu ", "[error] ", "Couldn't create menu item for $($File.FullName). Problem with $Type" -Color Red, DarkGray, Red, DarkGray, Red
            }
        }
    }
}
function Convert-MultipleReplacements {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $Replacements,
        [Array] $ReplacementConfiguration
    )
    Write-Color -Text '[i]', "[TheDashboard] ", 'Converting Multiple Replacements', ' [Informative] ' -Color Yellow, DarkGray, Yellow, DarkGray, Magenta

    if (-not $Replacements) {
        $ReplacementSetting = [ordered] @{
            SplitOn                 = $null
            BeforeSplit             = [ordered] @{}
            AfterSplit              = [ordered] @{}
            AddSpaceToName          = $null
            AfterSplitPositionName  = $null
            AfterRemoveChars        = $null
            AfterUpperChars         = $null
            AfterRemoveDoubleSpaces = $null
            BeforeRemoveChars       = $null
        }
    } else {
        $ReplacementSetting = $Replacements
    }

    foreach ($Replacement in $ReplacementConfiguration) {
        Remove-EmptyValue -Hashtable $Replacement
        foreach ($R in $Replacement.Keys) {
            if ($R -eq 'SplitOn') {
                if ($null -ne $Replacement[$R]) {
                    $ReplacementSetting['SplitOn'] = $Replacement[$R]
                }
            } elseif ($R -eq 'beforeSplit') {
                foreach ($Before in $Replacement[$R]) {
                    foreach ($Key in $Before.Keys) {
                        $ReplacementSetting['BeforeSplit'][$Key] = $Before[$Key]
                    }
                }
            } elseif ($R -eq 'afterSplit') {
                foreach ($After in $Replacement[$R]) {
                    foreach ($Key in $After.Keys) {
                        $ReplacementSetting['AfterSplit'][$Key] = $After[$Key]
                    }
                }
            } elseif ($R -eq 'addSpaceToName') {
                if ($null -ne $Replacement[$R]) {
                    $ReplacementSetting['AddSpaceToName'] = $Replacement[$R]
                }
            } elseif ($R -eq 'AfterSplitPositionName') {
                if ($null -ne $Replacement[$R]) {
                    $ReplacementSetting['AfterSplitPositionName'] = $Replacement[$R]
                }
            } else {
                if ($null -ne $Replacement[$R]) {
                    if ($Replacement[$R].GetType().Name -eq 'SwitchParameter') {
                        $ReplacementSetting[$R] = $Replacement[$R].IsPresent
                    } else {
                        $ReplacementSetting[$R] = $Replacement[$R]
                    }
                }
            }
        }
    }
    $ReplacementSetting
}
function Convert-NestedElements {
    [CmdletBinding()]
    param(
        [ScriptBlock] $Elements,
        [System.Collections.Generic.List[System.Collections.IDictionary]] $GageConfiguration,
        [System.Collections.Generic.List[System.Collections.IDictionary]] $FoldersConfiguration,
        [System.Collections.Generic.List[System.Collections.IDictionary]] $ReplacementConfiguration,
        [System.Collections.IDictionary] $FolderLimit
    )

    if ($Elements) {
        $TimeLogElements = Start-TimeLog
        Write-Color -Text '[i]', "[TheDashboard] ", 'Executing nested elements (data gathering/conversions)', ' [Informative] ' -Color Yellow, DarkGray, Yellow, DarkGray, Magenta

        try {
            $OutputElements = & $Elements
        } catch {
            Write-Color -Text '[e]', "[TheDashboard] ", 'Failed to execute nested elements', ' [Error] ', $_.Exception.Message -Color Yellow, DarkGray, Yellow, DarkGray, Red
            return
        }
        foreach ($E in $OutputElements) {
            if ($E.Type -eq 'Gage') {
                $GageConfiguration.Add($E.Settings)
            } elseif ($E.Type -eq 'Folder') {
                $FoldersConfiguration.Add($E.Settings)
            } elseif ($E.Type -eq 'Replacement') {
                $ReplacementConfiguration.Add($E.Settings)
            } elseif ($E.Type -eq 'FolderLimit') {
                foreach ($Setting in $E.Settings.Keys) {
                    $FolderLimit[$Setting] = $E.Settings[$Setting]
                }
            }
        }

        $TimeLogElements = Stop-TimeLog -Time $TimeLogElements -Option OneLiner
        Write-Color -Text '[i]', "[TheDashboard] ", 'Executing nested elements (data gathering/conversions)', ' [Time to execute: ', $TimeLogElements, ']' -Color Yellow, DarkGray, Yellow, DarkGray, Magenta
    }
}
function Copy-HTMLFiles {
    [cmdletbinding()]
    param(
        [System.Collections.IDictionary] $Folders,
        [string] $Extension
    )

    Write-Color -Text '[i]', "[TheDashboard] ", 'Copying or moving HTML files', ' [Informative] ' -Color Yellow, DarkGray, Yellow, DarkGray, Magenta
    foreach ($FolderName in $Folders.Keys) {
        if ($Folders[$FolderName].CopyFrom) {
            foreach ($Path in $Folders[$FolderName].CopyFrom) {
                Write-Color -Text '[i]', "[TheDashboard] ", "Copying from $Path to $($Folders[$FolderName].Path) using '$Extension'", ' [Informative] ' -Color Yellow, DarkGray, Yellow, DarkGray, Magenta
                foreach ($File in Get-ChildItem -LiteralPath $Path -Filter "*$Extension" -Recurse) {
                    $Destination = $File.FullName.Replace($Path, $Folders[$FolderName].Path)
                    $DIrectoryName = [io.path]::GetDirectoryName($Destination)
                    $null = New-Item -Path $DIrectoryName -ItemType Directory -Force
                    Copy-Item -LiteralPath $File.FullName -Destination $Destination -Force -ErrorAction Stop
                }
                foreach ($Ext in $Folders[$FolderName].Extension) {
                    Write-Color -Text '[i]', "[TheDashboard] ", "Copying from $Path to $($Folders[$FolderName].Path) using '$Ext'", ' [Informative] ' -Color Yellow, DarkGray, Yellow, DarkGray, Magenta
                    foreach ($File in Get-ChildItem -LiteralPath $Path -Filter "*$Ext" -Recurse) {
                        $Destination = $File.FullName.Replace($Path, $Folders[$FolderName].Path)
                        $DIrectoryName = [io.path]::GetDirectoryName($Destination)
                        $null = New-Item -Path $DIrectoryName -ItemType Directory -Force
                        Copy-Item -LiteralPath $File.FullName -Destination $Destination -Force -ErrorAction Stop
                    }
                }
            }
        }
        if ($Folders[$FolderName].MoveFrom) {
            foreach ($Path in $Folders[$FolderName].MoveFrom) {
                Write-Color -Text '[i]', "[TheDashboard] ", "Moving from $Path to $($Folders[$FolderName].Path) using '$Extension'", ' [Informative] ' -Color Yellow, DarkGray, Yellow, DarkGray, Magenta
                foreach ($File in Get-ChildItem -LiteralPath $Path -Filter "*$Extension" -Recurse) {
                    $Destination = $File.FullName.Replace($Path, $Folders[$FolderName].Path)
                    $DIrectoryName = [io.path]::GetDirectoryName($Destination)
                    $null = New-Item -Path $DIrectoryName -ItemType Directory -Force
                    Move-Item -LiteralPath $File.FullName -Destination $Destination -Force -ErrorAction Stop
                }
                foreach ($Ext in $Folders[$FolderName].Extension) {
                    Write-Color -Text '[i]', "[TheDashboard] ", "Moving from $Path to $($Folders[$FolderName].Path) using '$Ext'", ' [Informative] ' -Color Yellow, DarkGray, Yellow, DarkGray, Magenta
                    foreach ($File in Get-ChildItem -LiteralPath $Path -Filter "*$Ext" -Recurse) {
                        $Destination = $File.FullName.Replace($Path, $Folders[$FolderName].Path)
                        $DIrectoryName = [io.path]::GetDirectoryName($Destination)
                        $null = New-Item -Path $DIrectoryName -ItemType Directory -Force
                        Move-Item -LiteralPath $File.FullName -Destination $Destination -Force -ErrorAction Stop
                    }
                }
            }
        }
    }
}
function Get-GitHubVersion {
    [cmdletBinding()]
    param(
        [Parameter(Mandatory)][string] $Cmdlet,
        [Parameter(Mandatory)][string] $RepositoryOwner,
        [Parameter(Mandatory)][string] $RepositoryName
    )
    $App = Get-Command -Name $Cmdlet -ErrorAction SilentlyContinue
    if ($App) {
        [Array] $GitHubReleases = (Get-GitHubLatestRelease -Url "https://api.github.com/repos/$RepositoryOwner/$RepositoryName/releases" -Verbose:$false)
        $LatestVersion = $GitHubReleases[0]
        if (-not $LatestVersion.Errors) {
            if ($App.Version -eq $LatestVersion.Version) {
                "Current/Latest: $($LatestVersion.Version) at $($LatestVersion.PublishDate)"
            } elseif ($App.Version -lt $LatestVersion.Version) {
                "Current: $($App.Version), Published: $($LatestVersion.Version) at $($LatestVersion.PublishDate). Update?"
            } elseif ($App.Version -gt $LatestVersion.Version) {
                "Current: $($App.Version), Published: $($LatestVersion.Version) at $($LatestVersion.PublishDate). Lucky you!"
            }
        } else {
            "Current: $($App.Version)"
        }
    }
}
function Import-DashboardStatistics {
    [CmdletBinding()]
    param(
        [string] $StatisticsPath
    )
    $ExportData = [ordered] @{
        MenuBuilder = [ordered] @{}
        Statistics  = [ordered] @{}
    }
    if ($StatisticsPath -and (Test-Path -LiteralPath $StatisticsPath)) {
        Write-Color -Text '[i]', "[TheDashboard] ", 'Importing Statistics', ' [Informative] ', $StatisticsPath -Color Yellow, DarkGray, Yellow, DarkGray, Magenta
        $ImportedData = Import-Clixml -LiteralPath $StatisticsPath
        if (-not $ImportedData.Statistics) {
            $ExportData.Statistics = $ImportedData
        } else {
            $ExportData = $ImportedData
        }
    }
    foreach ($E in $GageConfiguration) {
        $ExportData['Statistics'][$E.Date] = [ordered] @{}
        $ExportData['Statistics'][$E.Date].Date = $E.Date
    }
    $ExportData
}
function Limit-FilesHistory {
    <#
    .SYNOPSIS
    Controls how files are moved into history based on specified limits.
 
    .DESCRIPTION
    Checks and applies IncludeHistory settings, filtering by date or count.
 
    .PARAMETER MenuBuilder
    Contains the menu structure to which entries will be added.
 
    .PARAMETER Entry
    Represents the current file entry under evaluation.
 
    .PARAMETER Limits
    Holds data about various limits for item, days, and date filtering.
 
    .PARAMETER CurrentDate
    Specifies the current date for time-based comparisons.
 
    .EXAMPLE
    Limit-FilesHistory -MenuBuilder $Builder -Entry $File -Limits $Limits -CurrentDate (Get-Date)
 
    .NOTES
    Part of TheDashboard module, handles history-limit logic for file entries.
    #>

    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $MenuBuilder,
        [PSCustomObject] $Entry,
        [System.Collections.IDictionary] $Limits,
        [DateTime] $CurrentDate
    )
    if ($Limits.IncludeHistory) {
        if ($null -ne $Limits.IncludeHistoryLimit) {
            if ($MenuBuilder[$Entry.Menu][$Entry.Name]['History'].Count -ge $Limits.IncludeHistoryLimit) {
                $Entry.Include = $false
                return
            }
        }
        if ($null -ne $Limits.IncludeHistoryLimitDate) {
            if ($Entry.Date -lt $Limits.IncludeHistoryLimitDate) {
                $Entry.Include = $false
                return
            }
        }
        if ($null -ne $Limits.IncludeHistoryLimitDays) {
            if ($Entry.Date -lt ($CurrentDate).AddDays(-$Limits.IncludeHistoryLimitDays)) {
                $Entry.Include = $false
                return
            }
        }
        $Entry.Include = $true
        $MenuBuilder[$Entry.Menu][$Entry.Name]['History'].Add($Entry)
    }
}
function New-HTMLReport {
    [cmdletBinding()]
    param(
        [Array] $OutputElements,
        [string] $Logo,
        [string] $Extension,
        [System.Collections.IDictionary] $MenuBuilder,
        [System.Collections.IDictionary] $Configuration,
        [System.Collections.IDictionary] $ExportData,
        [Array] $Files,
        [string] $HTMLPath,
        [switch] $ShowHTML,
        [switch] $Online,
        [Uri] $UrlPath,
        [switch] $Force,
        [switch] $Pretend
    )
    $TimeLogHTML = Start-TimeLog
    Write-Color -Text '[i]', '[HTML ] ', "Generating HTML report ($HTMLPath)" -Color Yellow, DarkGray, Yellow

    $FilePathsGenerated = [System.Collections.Generic.List[string]]::new()
    $FilePathsGenerated.Add($HTMLPath) 

    if ($Pretend) {
        foreach ($Menu in $MenuBuilder.Keys) {
            Write-Color -Text '[i]', '[HTML ] ', "Building Menu for ", $Menu -Color Yellow, DarkGray, Yellow, DarkCyan
            $TopMenuSplat = @{
                Name = $Menu
            }
            if ($Configuration.Folders.$Menu.IconType) {
                $TopMenuSplat[$Configuration.Folders.$Menu.IconType] = $Configuration.Folders.$Menu.Icon
            }

            foreach ($MenuReport in $MenuBuilder[$Menu].Keys | Sort-Object) {
                $MenuLink = $MenuBuilder[$Menu][$MenuReport]['Current'].MenuLink
                $PathToSubReports = [io.path]::GetDirectoryName($HTMLPath)
                $PageName = ($MenuBuilder[$Menu][$MenuReport]['Current'].Name).Replace(":", "_").Replace(" ", "_")
                $FullPath = [io.path]::Combine($PathToSubReports, "$($MenuLink)_$PageName$($Extension)")

                $CurrentReport = $MenuBuilder[$Menu][$MenuReport]['Current']
                [Array] $FullReports = $MenuBuilder[$Menu][$MenuReport]['Full']
                [Array] $HistoryReports = $MenuBuilder[$Menu][$MenuReport]['History']

                $Name = $CurrentReport.Name
                $FilePathsGenerated.Add($FullPath)  

                foreach ($Report in $FullReports) {
                    $FullPathOther = [io.path]::Combine($PathToSubReports, $Report.FileName)
                    $Name = $Report.Name + ' - ' + $Report.Date
                    $FilePathsGenerated.Add($FullPathOther) 
                }
            }
            Write-Color -Text '[i]', '[HTML ] ', "Ending Menu for ", $Menu -Color Yellow, DarkGray, Yellow, DarkCyan
        }
    } else {

        New-HTML {
            $newHTMLNavTopSplat = @{
                Logo            = $Logo
                MenuItemsWidth  = '250px'
                NavigationLinks = {
                    foreach ($Menu in $MenuBuilder.Keys) {
                        $TopMenuSplat = @{
                            Name = $Menu
                        }
                        if ($Configuration.Folders.$Menu.IconType) {
                            $TopMenuSplat[$Configuration.Folders.$Menu.IconType] = $Configuration.Folders.$Menu.Icon
                        }
                        New-NavTopMenu @TopMenuSplat {
                            foreach ($MenuReport in $MenuBuilder[$Menu].Keys | Sort-Object) {
                                $MenuLink = $MenuBuilder[$Menu][$MenuReport]['Current'].MenuLink
                                $PageName = (( -join ($MenuBuilder[$Menu][$MenuReport]['Current'].Name)).Replace(":", "_").Replace(" ", "_"))
                                if ($UrlPath) {
                                    New-NavLink -IconRegular calendar-check -Name $MenuBuilder[$Menu][$MenuReport]['Current'].Name -Href "$UrlPath/$($MenuLink)_$($PageName)$($Extension)"
                                } else {
                                    New-NavLink -IconRegular calendar-check -Name $MenuBuilder[$Menu][$MenuReport]['Current'].Name -Href "$($MenuLink)_$($PageName)$($Extension)"
                                }
                            }
                        }
                    }
                }
            }
            if ($UrlPath) {
                $FileNameHome = [System.IO.Path]::GetFileName($HTMLPath)
                $newHTMLNavTopSplat['HomeLink'] = "$UrlPath/$FileNameHome"
            } else {
                $newHTMLNavTopSplat['HomeLinkHome'] = $true
            }
            New-HTMLNavTop @newHTMLNavTopSplat

            New-HTMLSectionStyle -BorderRadius 0px -HeaderBackGroundColor Grey -RemoveShadow
            New-HTMLPanelStyle -BorderRadius 0px

            New-HTMLSection -Invisible {
                foreach ($E in $OutputElements) {
                    New-HTMLPanel {
                        New-HTMLGage -Label $E.Label -MinValue $E.MinValue -MaxValue $E.MaxValue -Value $E.Value -Counter
                        $ExportData['Statistics'][$E.Date][$($E.Label)] = $E.Value

                    }
                }
            }
            New-HTMLSection -Invisible {
                New-HTMLPanel {
                    $StatisticsKeys = $ExportData['Statistics'].Keys | Sort-Object | Select-Object -Last 50
                    [Array] $Dates = foreach ($Day in $StatisticsKeys) {
                        $ExportData['Statistics'][$Day].Date
                    }

                    foreach ($UserInput in $Dates) {
                        $ExportData['Statistics'][$Day].$($UserInput.Label) = $UserInput.Value
                    }
                    New-HTMLChart -Title 'Domain Summary' -TitleAlignment center {
                        New-ChartAxisX -Type datetime -Names $Dates
                        New-ChartAxisY -TitleText 'Numbers' -Show

                        foreach ($UserInput in $OutputElements) {
                            $Values = foreach ($Day in $StatisticsKeys) {
                                $ExportData['Statistics'][$Day].$($UserInput.Label)
                            }
                            New-ChartLine -Name $UserInput.Label -Value $Values
                        }
                    }
                }
                New-HTMLPanel {
                    New-HTMLCalendar {
                        foreach ($Menu in $MenuBuilder.Keys) {
                            foreach ($MenuReport in $MenuBuilder[$Menu].Keys) {
                                [Array] $FullReports = $MenuBuilder[$Menu][$MenuReport]['Full']
                                foreach ($CalendarEntry in $FullReports) {

                                    if ($CalendarEntry.Include) {
                                        if ($($CalendarEntry.Date).Day -eq $($($CalendarEntry.Date).AddMinutes(30)).Day) {
                                            New-CalendarEvent -Title $CalendarEntry.Name -StartDate $CalendarEntry.Date -EndDate $($CalendarEntry.Date).AddMinutes(30) -Url $CalendarEntry.FileName
                                        } else {
                                            New-CalendarEvent -Title $CalendarEntry.Name -StartDate $CalendarEntry.Date.AddMinutes(-30) -EndDate $($CalendarEntry.Date) -Url $CalendarEntry.FileName
                                        }
                                    }
                                }

                            }
                        }
                    } -HeaderRight @('dayGridMonth', 'timeGridWeek', 'timeGridDay', 'listMonth', 'listYear')
                }
            }
            foreach ($Menu in $MenuBuilder.Keys) {
                Write-Color -Text '[i]', '[HTML ] ', "Building Menu for ", $Menu -Color Yellow, DarkGray, Yellow, DarkCyan
                $TopMenuSplat = @{
                    Name = $Menu
                }
                if ($Configuration.Folders.$Menu.IconType) {
                    $TopMenuSplat[$Configuration.Folders.$Menu.IconType] = $Configuration.Folders.$Menu.Icon
                }

                foreach ($MenuReport in $MenuBuilder[$Menu].Keys | Sort-Object) {
                    $MenuLink = $MenuBuilder[$Menu][$MenuReport]['Current'].MenuLink
                    $PathToSubReports = [io.path]::GetDirectoryName($HTMLPath)
                    $PageName = ($MenuBuilder[$Menu][$MenuReport]['Current'].Name).Replace(":", "_").Replace(" ", "_")
                    $FullPath = [io.path]::Combine($PathToSubReports, "$($MenuLink)_$PageName$($Extension)")

                    $CurrentReport = $MenuBuilder[$Menu][$MenuReport]['Current']
                    [Array] $FullReports = $MenuBuilder[$Menu][$MenuReport]['Full']
                    [Array] $HistoryReports = $MenuBuilder[$Menu][$MenuReport]['History']

                    $Name = $CurrentReport.Name
                    if (-not $CurrentReport.SkipGeneration) {
                        New-HTMLReportPage -Report $CurrentReport -FullReports $FullReports -HistoryReports $HistoryReports -FilePath $FullPath -PathToSubReports $PathToSubReports -Name $Name
                    } else {
                        Write-Color -Text '[i]', '[HTML ] ', "Skipping generation of ", $FullPath, ". generation not required..." -Color Yellow, DarkGray, Yellow
                    }
                    $FilePathsGenerated.Add($FullPath)  

                    foreach ($Report in $FullReports) {
                        if ($Report.Include) {
                            $FullPathOther = [io.path]::Combine($PathToSubReports, $Report.FileName)
                            $Name = $Report.Name + ' - ' + $Report.Date
                            $FilePathsGenerated.Add($FullPathOther) 
                            if (-not $Report.SkipGeneration) {
                                New-HTMLReportPage -SubReport -Report $Report -FullReports $FullReports -FilePath $FullPathOther -PathToSubReports $PathToSubReports -Name $Name -HistoryReports $HistoryReports
                            } else {
                                Write-Color -Text '[i]', '[HTML ] ', "Skipping generation of ", $FullPathOther, ". generation not required..." -Color Yellow, DarkGray, Yellow
                            }
                        }
                    }
                }
                Write-Color -Text '[i]', '[HTML ] ', "Ending Menu for ", $Menu -Color Yellow, DarkGray, Yellow, DarkCyan
            }
            Write-Color -Text '[i]', '[HTML ] ', "Saving HTML reports (this may take a while...)" -Color Yellow, DarkGray, Yellow
        } -FilePath $HTMLPath -ShowHTML:$ShowHTML.IsPresent -TitleText 'The Dashboard' -Online:$Online.IsPresent -Author "Przemyslaw Klys @ Evotec"
    }
    $TimeLogEndHTML = Stop-TimeLog -Time $TimeLogHTML -Option OneLiner
    Write-Color -Text '[i]', '[HTML ] ', 'Generating HTML report', " [Time to execute: $TimeLogEndHTML]" -Color Yellow, DarkGray, Yellow, DarkGray

    $FilePathsGenerated
}
function New-HTMLReportPage {
    [cmdletBinding()]
    param(
        [PSCustomObject] $Report,
        [Array] $FullReports,
        [Array] $HistoryReports,
        [string] $FilePath,
        [string] $PathToSubReports,
        [string] $Name,
        [switch] $SubReport
    )
    if ($SubReport) {
        Write-Color -Text '[i]', '[HTML ] ', "Generating HTML page ($MenuReport) sub report ($FilePath)" -Color Yellow, DarkGray, Yellow
    } else {
        Write-Color -Text '[i]', '[HTML ] ', "Generating HTML page ($MenuReport) report ($FilePath)" -Color Yellow, DarkGray, Yellow
    }
    New-HTMLPage -Name $Name -Title "The Dashboard - $Name" {
        New-HTMLSection -HeaderText "Summary for $($Report.Name)" -HeaderBackGroundColor Black {
            New-HTMLSection -Invisible {
                New-HTMLPanel {
                    New-HTMLText -Text "Report name: ", $CurrentReport.Name -FontSize 12px
                    New-HTMLText -Text "Report date: ", $CurrentReport.Date -FontSize 12px

                    New-HTMLText -Text "All reports in this catagory: ", $FullReports.Count -FontSize 12px
                    New-HTMLList {
                        if ($FullReports.Count -eq 1) {
                            New-HTMLListItem -Text "Date in report: ", $FullReports[0].Date -FontSize 12px -FontWeight normal, bold -TextDecoration none, underline
                        } else {
                            New-HTMLListItem -Text "Date ranges from: ", $FullReports[$FullReports.Count - 1].Date, " to ", $FullReports[0].Date -FontSize 12px -FontWeight normal, bold, normal, bold -TextDecoration none, underline, none, underline
                        }
                    }
                    if ($HistoryReports.Count) {
                        New-HTMLText -Text "History reports in this catagory: ", $HistoryReports.Count -FontSize 12px
                        New-HTMLList {
                            if ($HistoryReports.Count -gt 1) {
                                New-HTMLListItem -Text "Date ranges from: ", $HistoryReports[$FullReports.Count - 1].Date, " to ", $HistoryReports[0].Date -FontSize 12px -FontWeight normal, bold, normal, bold -TextDecoration none, underline, none, underline
                            } else {
                                New-HTMLListItem -Text "Date in report: ", $HistoryReports[0].Date -FontSize 12px -FontWeight normal, bold -TextDecoration none, underline
                            }
                        }
                    }
                } -Invisible
            }
            New-HTMLSection -Invisible {
                New-HTMLCalendar {
                    foreach ($CalendarEntry in $FullReports) {

                        if ($($CalendarEntry.Date).Day -eq $($($CalendarEntry.Date).AddMinutes(30)).Day) {
                            New-CalendarEvent -Title $CalendarEntry.Name -StartDate $CalendarEntry.Date -EndDate $($CalendarEntry.Date).AddMinutes(30) -Url $CalendarEntry.FileName
                        } else {
                            New-CalendarEvent -Title $CalendarEntry.Name -StartDate $CalendarEntry.Date.AddMinutes(-30) -EndDate $($CalendarEntry.Date) -Url $CalendarEntry.FileName
                        }
                    }

                    foreach ($CalendarEntry in $HistoryReports) {

                        if ($($CalendarEntry.Date).Day -eq $($($CalendarEntry.Date).AddMinutes(30)).Day) {
                            New-CalendarEvent -Title $CalendarEntry.Name -StartDate $CalendarEntry.Date -EndDate $($CalendarEntry.Date).AddMinutes(30) -Url $CalendarEntry.Href -TargetName 'iFrameWithContent'
                        } else {
                            New-CalendarEvent -Title $CalendarEntry.Name -StartDate $CalendarEntry.Date.AddMinutes(-30) -EndDate $($CalendarEntry.Date) -Url $CalendarEntry.Href -TargetName 'iFrameWithContent'
                        }
                    }
                } -HeaderRight @('dayGridMonth', 'timeGridWeek', 'timeGridDay', 'listMonth', 'listYear')
            }
        } -Height 300px
        New-HTMLSection -Invisible {

        } -Height 15px
        New-HTMLFrame -SourcePath $Report.Href -Scrolling Auto -Height 2000px -Name 'iFrameWithContent'
    } -FilePath $FilePath
}

$Global:HTMLIcons = [ordered] @{
    FontAwesomeBrands  = [ordered] @{
        '500px'                          = 'f26e'
        'accessible-icon'                = 'f368'
        'accusoft'                       = 'f369'
        'acquisitions-incorporated'      = 'f6af'
        'adn'                            = 'f170'
        'adversal'                       = 'f36a'
        'affiliatetheme'                 = 'f36b'
        'airbnb'                         = 'f834'
        'algolia'                        = 'f36c'
        'alipay'                         = 'f642'
        'amazon'                         = 'f270'
        'amazon-pay'                     = 'f42c'
        'amilia'                         = 'f36d'
        'android'                        = 'f17b'
        'angellist'                      = 'f209'
        'angrycreative'                  = 'f36e'
        'angular'                        = 'f420'
        'app-store'                      = 'f36f'
        'app-store-ios'                  = 'f370'
        'apper'                          = 'f371'
        'apple'                          = 'f179'
        'apple-pay'                      = 'f415'
        'artstation'                     = 'f77a'
        'asymmetrik'                     = 'f372'
        'atlassian'                      = 'f77b'
        'audible'                        = 'f373'
        'autoprefixer'                   = 'f41c'
        'avianex'                        = 'f374'
        'aviato'                         = 'f421'
        'aws'                            = 'f375'
        'bandcamp'                       = 'f2d5'
        'battle-net'                     = 'f835'
        'behance'                        = 'f1b4'
        'behance-square'                 = 'f1b5'
        'bimobject'                      = 'f378'
        'bitbucket'                      = 'f171'
        'bitcoin'                        = 'f379'
        'bity'                           = 'f37a'
        'black-tie'                      = 'f27e'
        'blackberry'                     = 'f37b'
        'blogger'                        = 'f37c'
        'blogger-b'                      = 'f37d'
        'bluetooth'                      = 'f293'
        'bluetooth-b'                    = 'f294'
        'bootstrap'                      = 'f836'
        'btc'                            = 'f15a'
        'buffer'                         = 'f837'
        'buromobelexperte'               = 'f37f'
        'buy-n-large'                    = 'f8a6'
        'canadian-maple-leaf'            = 'f785'
        'cc-amazon-pay'                  = 'f42d'
        'cc-amex'                        = 'f1f3'
        'cc-apple-pay'                   = 'f416'
        'cc-diners-club'                 = 'f24c'
        'cc-discover'                    = 'f1f2'
        'cc-jcb'                         = 'f24b'
        'cc-mastercard'                  = 'f1f1'
        'cc-paypal'                      = 'f1f4'
        'cc-stripe'                      = 'f1f5'
        'cc-visa'                        = 'f1f0'
        'centercode'                     = 'f380'
        'centos'                         = 'f789'
        'chrome'                         = 'f268'
        'chromecast'                     = 'f838'
        'cloudflare'                     = 'e07d'
        'cloudscale'                     = 'f383'
        'cloudsmith'                     = 'f384'
        'cloudversify'                   = 'f385'
        'codepen'                        = 'f1cb'
        'codiepie'                       = 'f284'
        'confluence'                     = 'f78d'
        'connectdevelop'                 = 'f20e'
        'contao'                         = 'f26d'
        'cotton-bureau'                  = 'f89e'
        'cpanel'                         = 'f388'
        'creative-commons'               = 'f25e'
        'creative-commons-by'            = 'f4e7'
        'creative-commons-nc'            = 'f4e8'
        'creative-commons-nc-eu'         = 'f4e9'
        'creative-commons-nc-jp'         = 'f4ea'
        'creative-commons-nd'            = 'f4eb'
        'creative-commons-pd'            = 'f4ec'
        'creative-commons-pd-alt'        = 'f4ed'
        'creative-commons-remix'         = 'f4ee'
        'creative-commons-sa'            = 'f4ef'
        'creative-commons-sampling'      = 'f4f0'
        'creative-commons-sampling-plus' = 'f4f1'
        'creative-commons-share'         = 'f4f2'
        'creative-commons-zero'          = 'f4f3'
        'critical-role'                  = 'f6c9'
        'css3'                           = 'f13c'
        'css3-alt'                       = 'f38b'
        'cuttlefish'                     = 'f38c'
        'd-and-d'                        = 'f38d'
        'd-and-d-beyond'                 = 'f6ca'
        'dailymotion'                    = 'e052'
        'dashcube'                       = 'f210'
        'deezer'                         = 'e077'
        'delicious'                      = 'f1a5'
        'deploydog'                      = 'f38e'
        'deskpro'                        = 'f38f'
        'dev'                            = 'f6cc'
        'deviantart'                     = 'f1bd'
        'dhl'                            = 'f790'
        'diaspora'                       = 'f791'
        'digg'                           = 'f1a6'
        'digital-ocean'                  = 'f391'
        'discord'                        = 'f392'
        'discourse'                      = 'f393'
        'dochub'                         = 'f394'
        'docker'                         = 'f395'
        'draft2digital'                  = 'f396'
        'dribbble'                       = 'f17d'
        'dribbble-square'                = 'f397'
        'dropbox'                        = 'f16b'
        'drupal'                         = 'f1a9'
        'dyalog'                         = 'f399'
        'earlybirds'                     = 'f39a'
        'ebay'                           = 'f4f4'
        'edge'                           = 'f282'
        'edge-legacy'                    = 'e078'
        'elementor'                      = 'f430'
        'ello'                           = 'f5f1'
        'ember'                          = 'f423'
        'empire'                         = 'f1d1'
        'envira'                         = 'f299'
        'erlang'                         = 'f39d'
        'ethereum'                       = 'f42e'
        'etsy'                           = 'f2d7'
        'evernote'                       = 'f839'
        'expeditedssl'                   = 'f23e'
        'facebook'                       = 'f09a'
        'facebook-f'                     = 'f39e'
        'facebook-messenger'             = 'f39f'
        'facebook-square'                = 'f082'
        'fantasy-flight-games'           = 'f6dc'
        'fedex'                          = 'f797'
        'fedora'                         = 'f798'
        'figma'                          = 'f799'
        'firefox'                        = 'f269'
        'firefox-browser'                = 'e007'
        'first-order'                    = 'f2b0'
        'first-order-alt'                = 'f50a'
        'firstdraft'                     = 'f3a1'
        'flickr'                         = 'f16e'
        'flipboard'                      = 'f44d'
        'fly'                            = 'f417'
        'font-awesome'                   = 'f2b4'
        'font-awesome-alt'               = 'f35c'
        'font-awesome-flag'              = 'f425'
        'fonticons'                      = 'f280'
        'fonticons-fi'                   = 'f3a2'
        'fort-awesome'                   = 'f286'
        'fort-awesome-alt'               = 'f3a3'
        'forumbee'                       = 'f211'
        'foursquare'                     = 'f180'
        'free-code-camp'                 = 'f2c5'
        'freebsd'                        = 'f3a4'
        'fulcrum'                        = 'f50b'
        'galactic-republic'              = 'f50c'
        'galactic-senate'                = 'f50d'
        'get-pocket'                     = 'f265'
        'gg'                             = 'f260'
        'gg-circle'                      = 'f261'
        'git'                            = 'f1d3'
        'git-alt'                        = 'f841'
        'git-square'                     = 'f1d2'
        'github'                         = 'f09b'
        'github-alt'                     = 'f113'
        'github-square'                  = 'f092'
        'gitkraken'                      = 'f3a6'
        'gitlab'                         = 'f296'
        'gitter'                         = 'f426'
        'glide'                          = 'f2a5'
        'glide-g'                        = 'f2a6'
        'gofore'                         = 'f3a7'
        'goodreads'                      = 'f3a8'
        'goodreads-g'                    = 'f3a9'
        'google'                         = 'f1a0'
        'google-drive'                   = 'f3aa'
        'google-pay'                     = 'e079'
        'google-play'                    = 'f3ab'
        'google-plus'                    = 'f2b3'
        'google-plus-g'                  = 'f0d5'
        'google-plus-square'             = 'f0d4'
        'google-wallet'                  = 'f1ee'
        'gratipay'                       = 'f184'
        'grav'                           = 'f2d6'
        'gripfire'                       = 'f3ac'
        'grunt'                          = 'f3ad'
        'guilded'                        = 'e07e'
        'gulp'                           = 'f3ae'
        'hacker-news'                    = 'f1d4'
        'hacker-news-square'             = 'f3af'
        'hackerrank'                     = 'f5f7'
        'hips'                           = 'f452'
        'hire-a-helper'                  = 'f3b0'
        'hive'                           = 'e07f'
        'hooli'                          = 'f427'
        'hornbill'                       = 'f592'
        'hotjar'                         = 'f3b1'
        'houzz'                          = 'f27c'
        'html5'                          = 'f13b'
        'hubspot'                        = 'f3b2'
        'ideal'                          = 'e013'
        'imdb'                           = 'f2d8'
        'innosoft'                       = 'e080'
        'instagram'                      = 'f16d'
        'instagram-square'               = 'e055'
        'instalod'                       = 'e081'
        'intercom'                       = 'f7af'
        'internet-explorer'              = 'f26b'
        'invision'                       = 'f7b0'
        'ioxhost'                        = 'f208'
        'itch-io'                        = 'f83a'
        'itunes'                         = 'f3b4'
        'itunes-note'                    = 'f3b5'
        'java'                           = 'f4e4'
        'jedi-order'                     = 'f50e'
        'jenkins'                        = 'f3b6'
        'jira'                           = 'f7b1'
        'joget'                          = 'f3b7'
        'joomla'                         = 'f1aa'
        'js'                             = 'f3b8'
        'js-square'                      = 'f3b9'
        'jsfiddle'                       = 'f1cc'
        'kaggle'                         = 'f5fa'
        'keybase'                        = 'f4f5'
        'keycdn'                         = 'f3ba'
        'kickstarter'                    = 'f3bb'
        'kickstarter-k'                  = 'f3bc'
        'korvue'                         = 'f42f'
        'laravel'                        = 'f3bd'
        'lastfm'                         = 'f202'
        'lastfm-square'                  = 'f203'
        'leanpub'                        = 'f212'
        'less'                           = 'f41d'
        'line'                           = 'f3c0'
        'linkedin'                       = 'f08c'
        'linkedin-in'                    = 'f0e1'
        'linode'                         = 'f2b8'
        'linux'                          = 'f17c'
        'lyft'                           = 'f3c3'
        'magento'                        = 'f3c4'
        'mailchimp'                      = 'f59e'
        'mandalorian'                    = 'f50f'
        'markdown'                       = 'f60f'
        'mastodon'                       = 'f4f6'
        'maxcdn'                         = 'f136'
        'mdb'                            = 'f8ca'
        'medapps'                        = 'f3c6'
        'medium'                         = 'f23a'
        'medium-m'                       = 'f3c7'
        'medrt'                          = 'f3c8'
        'meetup'                         = 'f2e0'
        'megaport'                       = 'f5a3'
        'mendeley'                       = 'f7b3'
        'microblog'                      = 'e01a'
        'microsoft'                      = 'f3ca'
        'mix'                            = 'f3cb'
        'mixcloud'                       = 'f289'
        'mixer'                          = 'e056'
        'mizuni'                         = 'f3cc'
        'modx'                           = 'f285'
        'monero'                         = 'f3d0'
        'napster'                        = 'f3d2'
        'neos'                           = 'f612'
        'nimblr'                         = 'f5a8'
        'node'                           = 'f419'
        'node-js'                        = 'f3d3'
        'npm'                            = 'f3d4'
        'ns8'                            = 'f3d5'
        'nutritionix'                    = 'f3d6'
        'octopus-deploy'                 = 'e082'
        'odnoklassniki'                  = 'f263'
        'odnoklassniki-square'           = 'f264'
        'old-republic'                   = 'f510'
        'opencart'                       = 'f23d'
        'openid'                         = 'f19b'
        'opera'                          = 'f26a'
        'optin-monster'                  = 'f23c'
        'orcid'                          = 'f8d2'
        'osi'                            = 'f41a'
        'page4'                          = 'f3d7'
        'pagelines'                      = 'f18c'
        'palfed'                         = 'f3d8'
        'patreon'                        = 'f3d9'
        'paypal'                         = 'f1ed'
        'penny-arcade'                   = 'f704'
        'perbyte'                        = 'e083'
        'periscope'                      = 'f3da'
        'phabricator'                    = 'f3db'
        'phoenix-framework'              = 'f3dc'
        'phoenix-squadron'               = 'f511'
        'php'                            = 'f457'
        'pied-piper'                     = 'f2ae'
        'pied-piper-alt'                 = 'f1a8'
        'pied-piper-hat'                 = 'f4e5'
        'pied-piper-pp'                  = 'f1a7'
        'pied-piper-square'              = 'e01e'
        'pinterest'                      = 'f0d2'
        'pinterest-p'                    = 'f231'
        'pinterest-square'               = 'f0d3'
        'playstation'                    = 'f3df'
        'product-hunt'                   = 'f288'
        'pushed'                         = 'f3e1'
        'python'                         = 'f3e2'
        'qq'                             = 'f1d6'
        'quinscape'                      = 'f459'
        'quora'                          = 'f2c4'
        'r-project'                      = 'f4f7'
        'raspberry-pi'                   = 'f7bb'
        'ravelry'                        = 'f2d9'
        'react'                          = 'f41b'
        'reacteurope'                    = 'f75d'
        'readme'                         = 'f4d5'
        'rebel'                          = 'f1d0'
        'red-river'                      = 'f3e3'
        'reddit'                         = 'f1a1'
        'reddit-alien'                   = 'f281'
        'reddit-square'                  = 'f1a2'
        'redhat'                         = 'f7bc'
        'renren'                         = 'f18b'
        'replyd'                         = 'f3e6'
        'researchgate'                   = 'f4f8'
        'resolving'                      = 'f3e7'
        'rev'                            = 'f5b2'
        'rocketchat'                     = 'f3e8'
        'rockrms'                        = 'f3e9'
        'rust'                           = 'e07a'
        'safari'                         = 'f267'
        'salesforce'                     = 'f83b'
        'sass'                           = 'f41e'
        'schlix'                         = 'f3ea'
        'scribd'                         = 'f28a'
        'searchengin'                    = 'f3eb'
        'sellcast'                       = 'f2da'
        'sellsy'                         = 'f213'
        'servicestack'                   = 'f3ec'
        'shirtsinbulk'                   = 'f214'
        'shopify'                        = 'e057'
        'shopware'                       = 'f5b5'
        'simplybuilt'                    = 'f215'
        'sistrix'                        = 'f3ee'
        'sith'                           = 'f512'
        'sketch'                         = 'f7c6'
        'skyatlas'                       = 'f216'
        'skype'                          = 'f17e'
        'slack'                          = 'f198'
        'slack-hash'                     = 'f3ef'
        'slideshare'                     = 'f1e7'
        'snapchat'                       = 'f2ab'
        'snapchat-ghost'                 = 'f2ac'
        'snapchat-square'                = 'f2ad'
        'soundcloud'                     = 'f1be'
        'sourcetree'                     = 'f7d3'
        'speakap'                        = 'f3f3'
        'speaker-deck'                   = 'f83c'
        'spotify'                        = 'f1bc'
        'squarespace'                    = 'f5be'
        'stack-exchange'                 = 'f18d'
        'stack-overflow'                 = 'f16c'
        'stackpath'                      = 'f842'
        'staylinked'                     = 'f3f5'
        'steam'                          = 'f1b6'
        'steam-square'                   = 'f1b7'
        'steam-symbol'                   = 'f3f6'
        'sticker-mule'                   = 'f3f7'
        'strava'                         = 'f428'
        'stripe'                         = 'f429'
        'stripe-s'                       = 'f42a'
        'studiovinari'                   = 'f3f8'
        'stumbleupon'                    = 'f1a4'
        'stumbleupon-circle'             = 'f1a3'
        'superpowers'                    = 'f2dd'
        'supple'                         = 'f3f9'
        'suse'                           = 'f7d6'
        'swift'                          = 'f8e1'
        'symfony'                        = 'f83d'
        'teamspeak'                      = 'f4f9'
        'telegram'                       = 'f2c6'
        'telegram-plane'                 = 'f3fe'
        'tencent-weibo'                  = 'f1d5'
        'the-red-yeti'                   = 'f69d'
        'themeco'                        = 'f5c6'
        'themeisle'                      = 'f2b2'
        'think-peaks'                    = 'f731'
        'tiktok'                         = 'e07b'
        'trade-federation'               = 'f513'
        'trello'                         = 'f181'
        'tripadvisor'                    = 'f262'
        'tumblr'                         = 'f173'
        'tumblr-square'                  = 'f174'
        'twitch'                         = 'f1e8'
        'twitter'                        = 'f099'
        'twitter-square'                 = 'f081'
        'typo3'                          = 'f42b'
        'uber'                           = 'f402'
        'ubuntu'                         = 'f7df'
        'uikit'                          = 'f403'
        'umbraco'                        = 'f8e8'
        'uncharted'                      = 'e084'
        'uniregistry'                    = 'f404'
        'unity'                          = 'e049'
        'unsplash'                       = 'e07c'
        'untappd'                        = 'f405'
        'ups'                            = 'f7e0'
        'usb'                            = 'f287'
        'usps'                           = 'f7e1'
        'ussunnah'                       = 'f407'
        'vaadin'                         = 'f408'
        'viacoin'                        = 'f237'
        'viadeo'                         = 'f2a9'
        'viadeo-square'                  = 'f2aa'
        'viber'                          = 'f409'
        'vimeo'                          = 'f40a'
        'vimeo-square'                   = 'f194'
        'vimeo-v'                        = 'f27d'
        'vine'                           = 'f1ca'
        'vk'                             = 'f189'
        'vnv'                            = 'f40b'
        'vuejs'                          = 'f41f'
        'watchman-monitoring'            = 'e087'
        'waze'                           = 'f83f'
        'weebly'                         = 'f5cc'
        'weibo'                          = 'f18a'
        'weixin'                         = 'f1d7'
        'whatsapp'                       = 'f232'
        'whatsapp-square'                = 'f40c'
        'whmcs'                          = 'f40d'
        'wikipedia-w'                    = 'f266'
        'windows'                        = 'f17a'
        'wix'                            = 'f5cf'
        'wizards-of-the-coast'           = 'f730'
        'wodu'                           = 'e088'
        'wolf-pack-battalion'            = 'f514'
        'wordpress'                      = 'f19a'
        'wordpress-simple'               = 'f411'
        'wpbeginner'                     = 'f297'
        'wpexplorer'                     = 'f2de'
        'wpforms'                        = 'f298'
        'wpressr'                        = 'f3e4'
        'xbox'                           = 'f412'
        'xing'                           = 'f168'
        'xing-square'                    = 'f169'
        'y-combinator'                   = 'f23b'
        'yahoo'                          = 'f19e'
        'yammer'                         = 'f840'
        'yandex'                         = 'f413'
        'yandex-international'           = 'f414'
        'yarn'                           = 'f7e3'
        'yelp'                           = 'f1e9'
        'yoast'                          = 'f2b1'
        'youtube'                        = 'f167'
        'youtube-square'                 = 'f431'
        'zhihu'                          = 'f63f'
    }
    FontAwesomeRegular = [ordered] @{
        'address-book'           = 'f2b9'
        'address-card'           = 'f2bb'
        'angry'                  = 'f556'
        'arrow-alt-circle-down'  = 'f358'
        'arrow-alt-circle-left'  = 'f359'
        'arrow-alt-circle-right' = 'f35a'
        'arrow-alt-circle-up'    = 'f35b'
        'bell'                   = 'f0f3'
        'bell-slash'             = 'f1f6'
        'bookmark'               = 'f02e'
        'building'               = 'f1ad'
        'calendar'               = 'f133'
        'calendar-alt'           = 'f073'
        'calendar-check'         = 'f274'
        'calendar-minus'         = 'f272'
        'calendar-plus'          = 'f271'
        'calendar-times'         = 'f273'
        'caret-square-down'      = 'f150'
        'caret-square-left'      = 'f191'
        'caret-square-right'     = 'f152'
        'caret-square-up'        = 'f151'
        'chart-bar'              = 'f080'
        'check-circle'           = 'f058'
        'check-square'           = 'f14a'
        'circle'                 = 'f111'
        'clipboard'              = 'f328'
        'clock'                  = 'f017'
        'clone'                  = 'f24d'
        'closed-captioning'      = 'f20a'
        'comment'                = 'f075'
        'comment-alt'            = 'f27a'
        'comment-dots'           = 'f4ad'
        'comments'               = 'f086'
        'compass'                = 'f14e'
        'copy'                   = 'f0c5'
        'copyright'              = 'f1f9'
        'credit-card'            = 'f09d'
        'dizzy'                  = 'f567'
        'dot-circle'             = 'f192'
        'edit'                   = 'f044'
        'envelope'               = 'f0e0'
        'envelope-open'          = 'f2b6'
        'eye'                    = 'f06e'
        'eye-slash'              = 'f070'
        'file'                   = 'f15b'
        'file-alt'               = 'f15c'
        'file-archive'           = 'f1c6'
        'file-audio'             = 'f1c7'
        'file-code'              = 'f1c9'
        'file-excel'             = 'f1c3'
        'file-image'             = 'f1c5'
        'file-pdf'               = 'f1c1'
        'file-powerpoint'        = 'f1c4'
        'file-video'             = 'f1c8'
        'file-word'              = 'f1c2'
        'flag'                   = 'f024'
        'flushed'                = 'f579'
        'folder'                 = 'f07b'
        'folder-open'            = 'f07c'
        'frown'                  = 'f119'
        'frown-open'             = 'f57a'
        'futbol'                 = 'f1e3'
        'gem'                    = 'f3a5'
        'grimace'                = 'f57f'
        'grin'                   = 'f580'
        'grin-alt'               = 'f581'
        'grin-beam'              = 'f582'
        'grin-beam-sweat'        = 'f583'
        'grin-hearts'            = 'f584'
        'grin-squint'            = 'f585'
        'grin-squint-tears'      = 'f586'
        'grin-stars'             = 'f587'
        'grin-tears'             = 'f588'
        'grin-tongue'            = 'f589'
        'grin-tongue-squint'     = 'f58a'
        'grin-tongue-wink'       = 'f58b'
        'grin-wink'              = 'f58c'
        'hand-lizard'            = 'f258'
        'hand-paper'             = 'f256'
        'hand-peace'             = 'f25b'
        'hand-point-down'        = 'f0a7'
        'hand-point-left'        = 'f0a5'
        'hand-point-right'       = 'f0a4'
        'hand-point-up'          = 'f0a6'
        'hand-pointer'           = 'f25a'
        'hand-rock'              = 'f255'
        'hand-scissors'          = 'f257'
        'hand-spock'             = 'f259'
        'handshake'              = 'f2b5'
        'hdd'                    = 'f0a0'
        'heart'                  = 'f004'
        'hospital'               = 'f0f8'
        'hourglass'              = 'f254'
        'id-badge'               = 'f2c1'
        'id-card'                = 'f2c2'
        'image'                  = 'f03e'
        'images'                 = 'f302'
        'keyboard'               = 'f11c'
        'kiss'                   = 'f596'
        'kiss-beam'              = 'f597'
        'kiss-wink-heart'        = 'f598'
        'laugh'                  = 'f599'
        'laugh-beam'             = 'f59a'
        'laugh-squint'           = 'f59b'
        'laugh-wink'             = 'f59c'
        'lemon'                  = 'f094'
        'life-ring'              = 'f1cd'
        'lightbulb'              = 'f0eb'
        'list-alt'               = 'f022'
        'map'                    = 'f279'
        'meh'                    = 'f11a'
        'meh-blank'              = 'f5a4'
        'meh-rolling-eyes'       = 'f5a5'
        'minus-square'           = 'f146'
        'money-bill-alt'         = 'f3d1'
        'moon'                   = 'f186'
        'newspaper'              = 'f1ea'
        'object-group'           = 'f247'
        'object-ungroup'         = 'f248'
        'paper-plane'            = 'f1d8'
        'pause-circle'           = 'f28b'
        'play-circle'            = 'f144'
        'plus-square'            = 'f0fe'
        'question-circle'        = 'f059'
        'registered'             = 'f25d'
        'sad-cry'                = 'f5b3'
        'sad-tear'               = 'f5b4'
        'save'                   = 'f0c7'
        'share-square'           = 'f14d'
        'smile'                  = 'f118'
        'smile-beam'             = 'f5b8'
        'smile-wink'             = 'f4da'
        'snowflake'              = 'f2dc'
        'square'                 = 'f0c8'
        'star'                   = 'f005'
        'star-half'              = 'f089'
        'sticky-note'            = 'f249'
        'stop-circle'            = 'f28d'
        'sun'                    = 'f185'
        'surprise'               = 'f5c2'
        'thumbs-down'            = 'f165'
        'thumbs-up'              = 'f164'
        'times-circle'           = 'f057'
        'tired'                  = 'f5c8'
        'trash-alt'              = 'f2ed'
        'user'                   = 'f007'
        'user-circle'            = 'f2bd'
        'window-close'           = 'f410'
        'window-maximize'        = 'f2d0'
        'window-minimize'        = 'f2d1'
        'window-restore'         = 'f2d2'
    }
    FontAwesomeSolid   = [ordered] @{
        'ad'                                  = 'f641'
        'address-book'                        = 'f2b9'
        'address-card'                        = 'f2bb'
        'adjust'                              = 'f042'
        'air-freshener'                       = 'f5d0'
        'align-center'                        = 'f037'
        'align-justify'                       = 'f039'
        'align-left'                          = 'f036'
        'align-right'                         = 'f038'
        'allergies'                           = 'f461'
        'ambulance'                           = 'f0f9'
        'american-sign-language-interpreting' = 'f2a3'
        'anchor'                              = 'f13d'
        'angle-double-down'                   = 'f103'
        'angle-double-left'                   = 'f100'
        'angle-double-right'                  = 'f101'
        'angle-double-up'                     = 'f102'
        'angle-down'                          = 'f107'
        'angle-left'                          = 'f104'
        'angle-right'                         = 'f105'
        'angle-up'                            = 'f106'
        'angry'                               = 'f556'
        'ankh'                                = 'f644'
        'apple-alt'                           = 'f5d1'
        'archive'                             = 'f187'
        'archway'                             = 'f557'
        'arrow-alt-circle-down'               = 'f358'
        'arrow-alt-circle-left'               = 'f359'
        'arrow-alt-circle-right'              = 'f35a'
        'arrow-alt-circle-up'                 = 'f35b'
        'arrow-circle-down'                   = 'f0ab'
        'arrow-circle-left'                   = 'f0a8'
        'arrow-circle-right'                  = 'f0a9'
        'arrow-circle-up'                     = 'f0aa'
        'arrow-down'                          = 'f063'
        'arrow-left'                          = 'f060'
        'arrow-right'                         = 'f061'
        'arrow-up'                            = 'f062'
        'arrows-alt'                          = 'f0b2'
        'arrows-alt-h'                        = 'f337'
        'arrows-alt-v'                        = 'f338'
        'assistive-listening-systems'         = 'f2a2'
        'asterisk'                            = 'f069'
        'at'                                  = 'f1fa'
        'atlas'                               = 'f558'
        'atom'                                = 'f5d2'
        'audio-description'                   = 'f29e'
        'award'                               = 'f559'
        'baby'                                = 'f77c'
        'baby-carriage'                       = 'f77d'
        'backspace'                           = 'f55a'
        'backward'                            = 'f04a'
        'bacon'                               = 'f7e5'
        'bacteria'                            = 'e059'
        'bacterium'                           = 'e05a'
        'bahai'                               = 'f666'
        'balance-scale'                       = 'f24e'
        'balance-scale-left'                  = 'f515'
        'balance-scale-right'                 = 'f516'
        'ban'                                 = 'f05e'
        'band-aid'                            = 'f462'
        'barcode'                             = 'f02a'
        'bars'                                = 'f0c9'
        'baseball-ball'                       = 'f433'
        'basketball-ball'                     = 'f434'
        'bath'                                = 'f2cd'
        'battery-empty'                       = 'f244'
        'battery-full'                        = 'f240'
        'battery-half'                        = 'f242'
        'battery-quarter'                     = 'f243'
        'battery-three-quarters'              = 'f241'
        'bed'                                 = 'f236'
        'beer'                                = 'f0fc'
        'bell'                                = 'f0f3'
        'bell-slash'                          = 'f1f6'
        'bezier-curve'                        = 'f55b'
        'bible'                               = 'f647'
        'bicycle'                             = 'f206'
        'biking'                              = 'f84a'
        'binoculars'                          = 'f1e5'
        'biohazard'                           = 'f780'
        'birthday-cake'                       = 'f1fd'
        'blender'                             = 'f517'
        'blender-phone'                       = 'f6b6'
        'blind'                               = 'f29d'
        'blog'                                = 'f781'
        'bold'                                = 'f032'
        'bolt'                                = 'f0e7'
        'bomb'                                = 'f1e2'
        'bone'                                = 'f5d7'
        'bong'                                = 'f55c'
        'book'                                = 'f02d'
        'book-dead'                           = 'f6b7'
        'book-medical'                        = 'f7e6'
        'book-open'                           = 'f518'
        'book-reader'                         = 'f5da'
        'bookmark'                            = 'f02e'
        'border-all'                          = 'f84c'
        'border-none'                         = 'f850'
        'border-style'                        = 'f853'
        'bowling-ball'                        = 'f436'
        'box'                                 = 'f466'
        'box-open'                            = 'f49e'
        'box-tissue'                          = 'e05b'
        'boxes'                               = 'f468'
        'braille'                             = 'f2a1'
        'brain'                               = 'f5dc'
        'bread-slice'                         = 'f7ec'
        'briefcase'                           = 'f0b1'
        'briefcase-medical'                   = 'f469'
        'broadcast-tower'                     = 'f519'
        'broom'                               = 'f51a'
        'brush'                               = 'f55d'
        'bug'                                 = 'f188'
        'building'                            = 'f1ad'
        'bullhorn'                            = 'f0a1'
        'bullseye'                            = 'f140'
        'burn'                                = 'f46a'
        'bus'                                 = 'f207'
        'bus-alt'                             = 'f55e'
        'business-time'                       = 'f64a'
        'calculator'                          = 'f1ec'
        'calendar'                            = 'f133'
        'calendar-alt'                        = 'f073'
        'calendar-check'                      = 'f274'
        'calendar-day'                        = 'f783'
        'calendar-minus'                      = 'f272'
        'calendar-plus'                       = 'f271'
        'calendar-times'                      = 'f273'
        'calendar-week'                       = 'f784'
        'camera'                              = 'f030'
        'camera-retro'                        = 'f083'
        'campground'                          = 'f6bb'
        'candy-cane'                          = 'f786'
        'cannabis'                            = 'f55f'
        'capsules'                            = 'f46b'
        'car'                                 = 'f1b9'
        'car-alt'                             = 'f5de'
        'car-battery'                         = 'f5df'
        'car-crash'                           = 'f5e1'
        'car-side'                            = 'f5e4'
        'caravan'                             = 'f8ff'
        'caret-down'                          = 'f0d7'
        'caret-left'                          = 'f0d9'
        'caret-right'                         = 'f0da'
        'caret-square-down'                   = 'f150'
        'caret-square-left'                   = 'f191'
        'caret-square-right'                  = 'f152'
        'caret-square-up'                     = 'f151'
        'caret-up'                            = 'f0d8'
        'carrot'                              = 'f787'
        'cart-arrow-down'                     = 'f218'
        'cart-plus'                           = 'f217'
        'cash-register'                       = 'f788'
        'cat'                                 = 'f6be'
        'certificate'                         = 'f0a3'
        'chair'                               = 'f6c0'
        'chalkboard'                          = 'f51b'
        'chalkboard-teacher'                  = 'f51c'
        'charging-station'                    = 'f5e7'
        'chart-area'                          = 'f1fe'
        'chart-bar'                           = 'f080'
        'chart-line'                          = 'f201'
        'chart-pie'                           = 'f200'
        'check'                               = 'f00c'
        'check-circle'                        = 'f058'
        'check-double'                        = 'f560'
        'check-square'                        = 'f14a'
        'cheese'                              = 'f7ef'
        'chess'                               = 'f439'
        'chess-bishop'                        = 'f43a'
        'chess-board'                         = 'f43c'
        'chess-king'                          = 'f43f'
        'chess-knight'                        = 'f441'
        'chess-pawn'                          = 'f443'
        'chess-queen'                         = 'f445'
        'chess-rook'                          = 'f447'
        'chevron-circle-down'                 = 'f13a'
        'chevron-circle-left'                 = 'f137'
        'chevron-circle-right'                = 'f138'
        'chevron-circle-up'                   = 'f139'
        'chevron-down'                        = 'f078'
        'chevron-left'                        = 'f053'
        'chevron-right'                       = 'f054'
        'chevron-up'                          = 'f077'
        'child'                               = 'f1ae'
        'church'                              = 'f51d'
        'circle'                              = 'f111'
        'circle-notch'                        = 'f1ce'
        'city'                                = 'f64f'
        'clinic-medical'                      = 'f7f2'
        'clipboard'                           = 'f328'
        'clipboard-check'                     = 'f46c'
        'clipboard-list'                      = 'f46d'
        'clock'                               = 'f017'
        'clone'                               = 'f24d'
        'closed-captioning'                   = 'f20a'
        'cloud'                               = 'f0c2'
        'cloud-download-alt'                  = 'f381'
        'cloud-meatball'                      = 'f73b'
        'cloud-moon'                          = 'f6c3'
        'cloud-moon-rain'                     = 'f73c'
        'cloud-rain'                          = 'f73d'
        'cloud-showers-heavy'                 = 'f740'
        'cloud-sun'                           = 'f6c4'
        'cloud-sun-rain'                      = 'f743'
        'cloud-upload-alt'                    = 'f382'
        'cocktail'                            = 'f561'
        'code'                                = 'f121'
        'code-branch'                         = 'f126'
        'coffee'                              = 'f0f4'
        'cog'                                 = 'f013'
        'cogs'                                = 'f085'
        'coins'                               = 'f51e'
        'columns'                             = 'f0db'
        'comment'                             = 'f075'
        'comment-alt'                         = 'f27a'
        'comment-dollar'                      = 'f651'
        'comment-dots'                        = 'f4ad'
        'comment-medical'                     = 'f7f5'
        'comment-slash'                       = 'f4b3'
        'comments'                            = 'f086'
        'comments-dollar'                     = 'f653'
        'compact-disc'                        = 'f51f'
        'compass'                             = 'f14e'
        'compress'                            = 'f066'
        'compress-alt'                        = 'f422'
        'compress-arrows-alt'                 = 'f78c'
        'concierge-bell'                      = 'f562'
        'cookie'                              = 'f563'
        'cookie-bite'                         = 'f564'
        'copy'                                = 'f0c5'
        'copyright'                           = 'f1f9'
        'couch'                               = 'f4b8'
        'credit-card'                         = 'f09d'
        'crop'                                = 'f125'
        'crop-alt'                            = 'f565'
        'cross'                               = 'f654'
        'crosshairs'                          = 'f05b'
        'crow'                                = 'f520'
        'crown'                               = 'f521'
        'crutch'                              = 'f7f7'
        'cube'                                = 'f1b2'
        'cubes'                               = 'f1b3'
        'cut'                                 = 'f0c4'
        'database'                            = 'f1c0'
        'deaf'                                = 'f2a4'
        'democrat'                            = 'f747'
        'desktop'                             = 'f108'
        'dharmachakra'                        = 'f655'
        'diagnoses'                           = 'f470'
        'dice'                                = 'f522'
        'dice-d20'                            = 'f6cf'
        'dice-d6'                             = 'f6d1'
        'dice-five'                           = 'f523'
        'dice-four'                           = 'f524'
        'dice-one'                            = 'f525'
        'dice-six'                            = 'f526'
        'dice-three'                          = 'f527'
        'dice-two'                            = 'f528'
        'digital-tachograph'                  = 'f566'
        'directions'                          = 'f5eb'
        'disease'                             = 'f7fa'
        'divide'                              = 'f529'
        'dizzy'                               = 'f567'
        'dna'                                 = 'f471'
        'dog'                                 = 'f6d3'
        'dollar-sign'                         = 'f155'
        'dolly'                               = 'f472'
        'dolly-flatbed'                       = 'f474'
        'donate'                              = 'f4b9'
        'door-closed'                         = 'f52a'
        'door-open'                           = 'f52b'
        'dot-circle'                          = 'f192'
        'dove'                                = 'f4ba'
        'download'                            = 'f019'
        'drafting-compass'                    = 'f568'
        'dragon'                              = 'f6d5'
        'draw-polygon'                        = 'f5ee'
        'drum'                                = 'f569'
        'drum-steelpan'                       = 'f56a'
        'drumstick-bite'                      = 'f6d7'
        'dumbbell'                            = 'f44b'
        'dumpster'                            = 'f793'
        'dumpster-fire'                       = 'f794'
        'dungeon'                             = 'f6d9'
        'edit'                                = 'f044'
        'egg'                                 = 'f7fb'
        'eject'                               = 'f052'
        'ellipsis-h'                          = 'f141'
        'ellipsis-v'                          = 'f142'
        'envelope'                            = 'f0e0'
        'envelope-open'                       = 'f2b6'
        'envelope-open-text'                  = 'f658'
        'envelope-square'                     = 'f199'
        'equals'                              = 'f52c'
        'eraser'                              = 'f12d'
        'ethernet'                            = 'f796'
        'euro-sign'                           = 'f153'
        'exchange-alt'                        = 'f362'
        'exclamation'                         = 'f12a'
        'exclamation-circle'                  = 'f06a'
        'exclamation-triangle'                = 'f071'
        'expand'                              = 'f065'
        'expand-alt'                          = 'f424'
        'expand-arrows-alt'                   = 'f31e'
        'external-link-alt'                   = 'f35d'
        'external-link-square-alt'            = 'f360'
        'eye'                                 = 'f06e'
        'eye-dropper'                         = 'f1fb'
        'eye-slash'                           = 'f070'
        'fan'                                 = 'f863'
        'fast-backward'                       = 'f049'
        'fast-forward'                        = 'f050'
        'faucet'                              = 'e005'
        'fax'                                 = 'f1ac'
        'feather'                             = 'f52d'
        'feather-alt'                         = 'f56b'
        'female'                              = 'f182'
        'fighter-jet'                         = 'f0fb'
        'file'                                = 'f15b'
        'file-alt'                            = 'f15c'
        'file-archive'                        = 'f1c6'
        'file-audio'                          = 'f1c7'
        'file-code'                           = 'f1c9'
        'file-contract'                       = 'f56c'
        'file-csv'                            = 'f6dd'
        'file-download'                       = 'f56d'
        'file-excel'                          = 'f1c3'
        'file-export'                         = 'f56e'
        'file-image'                          = 'f1c5'
        'file-import'                         = 'f56f'
        'file-invoice'                        = 'f570'
        'file-invoice-dollar'                 = 'f571'
        'file-medical'                        = 'f477'
        'file-medical-alt'                    = 'f478'
        'file-pdf'                            = 'f1c1'
        'file-powerpoint'                     = 'f1c4'
        'file-prescription'                   = 'f572'
        'file-signature'                      = 'f573'
        'file-upload'                         = 'f574'
        'file-video'                          = 'f1c8'
        'file-word'                           = 'f1c2'
        'fill'                                = 'f575'
        'fill-drip'                           = 'f576'
        'film'                                = 'f008'
        'filter'                              = 'f0b0'
        'fingerprint'                         = 'f577'
        'fire'                                = 'f06d'
        'fire-alt'                            = 'f7e4'
        'fire-extinguisher'                   = 'f134'
        'first-aid'                           = 'f479'
        'fish'                                = 'f578'
        'fist-raised'                         = 'f6de'
        'flag'                                = 'f024'
        'flag-checkered'                      = 'f11e'
        'flag-usa'                            = 'f74d'
        'flask'                               = 'f0c3'
        'flushed'                             = 'f579'
        'folder'                              = 'f07b'
        'folder-minus'                        = 'f65d'
        'folder-open'                         = 'f07c'
        'folder-plus'                         = 'f65e'
        'font'                                = 'f031'
        'football-ball'                       = 'f44e'
        'forward'                             = 'f04e'
        'frog'                                = 'f52e'
        'frown'                               = 'f119'
        'frown-open'                          = 'f57a'
        'funnel-dollar'                       = 'f662'
        'futbol'                              = 'f1e3'
        'gamepad'                             = 'f11b'
        'gas-pump'                            = 'f52f'
        'gavel'                               = 'f0e3'
        'gem'                                 = 'f3a5'
        'genderless'                          = 'f22d'
        'ghost'                               = 'f6e2'
        'gift'                                = 'f06b'
        'gifts'                               = 'f79c'
        'glass-cheers'                        = 'f79f'
        'glass-martini'                       = 'f000'
        'glass-martini-alt'                   = 'f57b'
        'glass-whiskey'                       = 'f7a0'
        'glasses'                             = 'f530'
        'globe'                               = 'f0ac'
        'globe-africa'                        = 'f57c'
        'globe-americas'                      = 'f57d'
        'globe-asia'                          = 'f57e'
        'globe-europe'                        = 'f7a2'
        'golf-ball'                           = 'f450'
        'gopuram'                             = 'f664'
        'graduation-cap'                      = 'f19d'
        'greater-than'                        = 'f531'
        'greater-than-equal'                  = 'f532'
        'grimace'                             = 'f57f'
        'grin'                                = 'f580'
        'grin-alt'                            = 'f581'
        'grin-beam'                           = 'f582'
        'grin-beam-sweat'                     = 'f583'
        'grin-hearts'                         = 'f584'
        'grin-squint'                         = 'f585'
        'grin-squint-tears'                   = 'f586'
        'grin-stars'                          = 'f587'
        'grin-tears'                          = 'f588'
        'grin-tongue'                         = 'f589'
        'grin-tongue-squint'                  = 'f58a'
        'grin-tongue-wink'                    = 'f58b'
        'grin-wink'                           = 'f58c'
        'grip-horizontal'                     = 'f58d'
        'grip-lines'                          = 'f7a4'
        'grip-lines-vertical'                 = 'f7a5'
        'grip-vertical'                       = 'f58e'
        'guitar'                              = 'f7a6'
        'h-square'                            = 'f0fd'
        'hamburger'                           = 'f805'
        'hammer'                              = 'f6e3'
        'hamsa'                               = 'f665'
        'hand-holding'                        = 'f4bd'
        'hand-holding-heart'                  = 'f4be'
        'hand-holding-medical'                = 'e05c'
        'hand-holding-usd'                    = 'f4c0'
        'hand-holding-water'                  = 'f4c1'
        'hand-lizard'                         = 'f258'
        'hand-middle-finger'                  = 'f806'
        'hand-paper'                          = 'f256'
        'hand-peace'                          = 'f25b'
        'hand-point-down'                     = 'f0a7'
        'hand-point-left'                     = 'f0a5'
        'hand-point-right'                    = 'f0a4'
        'hand-point-up'                       = 'f0a6'
        'hand-pointer'                        = 'f25a'
        'hand-rock'                           = 'f255'
        'hand-scissors'                       = 'f257'
        'hand-sparkles'                       = 'e05d'
        'hand-spock'                          = 'f259'
        'hands'                               = 'f4c2'
        'hands-helping'                       = 'f4c4'
        'hands-wash'                          = 'e05e'
        'handshake'                           = 'f2b5'
        'handshake-alt-slash'                 = 'e05f'
        'handshake-slash'                     = 'e060'
        'hanukiah'                            = 'f6e6'
        'hard-hat'                            = 'f807'
        'hashtag'                             = 'f292'
        'hat-cowboy'                          = 'f8c0'
        'hat-cowboy-side'                     = 'f8c1'
        'hat-wizard'                          = 'f6e8'
        'hdd'                                 = 'f0a0'
        'head-side-cough'                     = 'e061'
        'head-side-cough-slash'               = 'e062'
        'head-side-mask'                      = 'e063'
        'head-side-virus'                     = 'e064'
        'heading'                             = 'f1dc'
        'headphones'                          = 'f025'
        'headphones-alt'                      = 'f58f'
        'headset'                             = 'f590'
        'heart'                               = 'f004'
        'heart-broken'                        = 'f7a9'
        'heartbeat'                           = 'f21e'
        'helicopter'                          = 'f533'
        'highlighter'                         = 'f591'
        'hiking'                              = 'f6ec'
        'hippo'                               = 'f6ed'
        'history'                             = 'f1da'
        'hockey-puck'                         = 'f453'
        'holly-berry'                         = 'f7aa'
        'home'                                = 'f015'
        'horse'                               = 'f6f0'
        'horse-head'                          = 'f7ab'
        'hospital'                            = 'f0f8'
        'hospital-alt'                        = 'f47d'
        'hospital-symbol'                     = 'f47e'
        'hospital-user'                       = 'f80d'
        'hot-tub'                             = 'f593'
        'hotdog'                              = 'f80f'
        'hotel'                               = 'f594'
        'hourglass'                           = 'f254'
        'hourglass-end'                       = 'f253'
        'hourglass-half'                      = 'f252'
        'hourglass-start'                     = 'f251'
        'house-damage'                        = 'f6f1'
        'house-user'                          = 'e065'
        'hryvnia'                             = 'f6f2'
        'i-cursor'                            = 'f246'
        'ice-cream'                           = 'f810'
        'icicles'                             = 'f7ad'
        'icons'                               = 'f86d'
        'id-badge'                            = 'f2c1'
        'id-card'                             = 'f2c2'
        'id-card-alt'                         = 'f47f'
        'igloo'                               = 'f7ae'
        'image'                               = 'f03e'
        'images'                              = 'f302'
        'inbox'                               = 'f01c'
        'indent'                              = 'f03c'
        'industry'                            = 'f275'
        'infinity'                            = 'f534'
        'info'                                = 'f129'
        'info-circle'                         = 'f05a'
        'italic'                              = 'f033'
        'jedi'                                = 'f669'
        'joint'                               = 'f595'
        'journal-whills'                      = 'f66a'
        'kaaba'                               = 'f66b'
        'key'                                 = 'f084'
        'keyboard'                            = 'f11c'
        'khanda'                              = 'f66d'
        'kiss'                                = 'f596'
        'kiss-beam'                           = 'f597'
        'kiss-wink-heart'                     = 'f598'
        'kiwi-bird'                           = 'f535'
        'landmark'                            = 'f66f'
        'language'                            = 'f1ab'
        'laptop'                              = 'f109'
        'laptop-code'                         = 'f5fc'
        'laptop-house'                        = 'e066'
        'laptop-medical'                      = 'f812'
        'laugh'                               = 'f599'
        'laugh-beam'                          = 'f59a'
        'laugh-squint'                        = 'f59b'
        'laugh-wink'                          = 'f59c'
        'layer-group'                         = 'f5fd'
        'leaf'                                = 'f06c'
        'lemon'                               = 'f094'
        'less-than'                           = 'f536'
        'less-than-equal'                     = 'f537'
        'level-down-alt'                      = 'f3be'
        'level-up-alt'                        = 'f3bf'
        'life-ring'                           = 'f1cd'
        'lightbulb'                           = 'f0eb'
        'link'                                = 'f0c1'
        'lira-sign'                           = 'f195'
        'list'                                = 'f03a'
        'list-alt'                            = 'f022'
        'list-ol'                             = 'f0cb'
        'list-ul'                             = 'f0ca'
        'location-arrow'                      = 'f124'
        'lock'                                = 'f023'
        'lock-open'                           = 'f3c1'
        'long-arrow-alt-down'                 = 'f309'
        'long-arrow-alt-left'                 = 'f30a'
        'long-arrow-alt-right'                = 'f30b'
        'long-arrow-alt-up'                   = 'f30c'
        'low-vision'                          = 'f2a8'
        'luggage-cart'                        = 'f59d'
        'lungs'                               = 'f604'
        'lungs-virus'                         = 'e067'
        'magic'                               = 'f0d0'
        'magnet'                              = 'f076'
        'mail-bulk'                           = 'f674'
        'male'                                = 'f183'
        'map'                                 = 'f279'
        'map-marked'                          = 'f59f'
        'map-marked-alt'                      = 'f5a0'
        'map-marker'                          = 'f041'
        'map-marker-alt'                      = 'f3c5'
        'map-pin'                             = 'f276'
        'map-signs'                           = 'f277'
        'marker'                              = 'f5a1'
        'mars'                                = 'f222'
        'mars-double'                         = 'f227'
        'mars-stroke'                         = 'f229'
        'mars-stroke-h'                       = 'f22b'
        'mars-stroke-v'                       = 'f22a'
        'mask'                                = 'f6fa'
        'medal'                               = 'f5a2'
        'medkit'                              = 'f0fa'
        'meh'                                 = 'f11a'
        'meh-blank'                           = 'f5a4'
        'meh-rolling-eyes'                    = 'f5a5'
        'memory'                              = 'f538'
        'menorah'                             = 'f676'
        'mercury'                             = 'f223'
        'meteor'                              = 'f753'
        'microchip'                           = 'f2db'
        'microphone'                          = 'f130'
        'microphone-alt'                      = 'f3c9'
        'microphone-alt-slash'                = 'f539'
        'microphone-slash'                    = 'f131'
        'microscope'                          = 'f610'
        'minus'                               = 'f068'
        'minus-circle'                        = 'f056'
        'minus-square'                        = 'f146'
        'mitten'                              = 'f7b5'
        'mobile'                              = 'f10b'
        'mobile-alt'                          = 'f3cd'
        'money-bill'                          = 'f0d6'
        'money-bill-alt'                      = 'f3d1'
        'money-bill-wave'                     = 'f53a'
        'money-bill-wave-alt'                 = 'f53b'
        'money-check'                         = 'f53c'
        'money-check-alt'                     = 'f53d'
        'monument'                            = 'f5a6'
        'moon'                                = 'f186'
        'mortar-pestle'                       = 'f5a7'
        'mosque'                              = 'f678'
        'motorcycle'                          = 'f21c'
        'mountain'                            = 'f6fc'
        'mouse'                               = 'f8cc'
        'mouse-pointer'                       = 'f245'
        'mug-hot'                             = 'f7b6'
        'music'                               = 'f001'
        'network-wired'                       = 'f6ff'
        'neuter'                              = 'f22c'
        'newspaper'                           = 'f1ea'
        'not-equal'                           = 'f53e'
        'notes-medical'                       = 'f481'
        'object-group'                        = 'f247'
        'object-ungroup'                      = 'f248'
        'oil-can'                             = 'f613'
        'om'                                  = 'f679'
        'otter'                               = 'f700'
        'outdent'                             = 'f03b'
        'pager'                               = 'f815'
        'paint-brush'                         = 'f1fc'
        'paint-roller'                        = 'f5aa'
        'palette'                             = 'f53f'
        'pallet'                              = 'f482'
        'paper-plane'                         = 'f1d8'
        'paperclip'                           = 'f0c6'
        'parachute-box'                       = 'f4cd'
        'paragraph'                           = 'f1dd'
        'parking'                             = 'f540'
        'passport'                            = 'f5ab'
        'pastafarianism'                      = 'f67b'
        'paste'                               = 'f0ea'
        'pause'                               = 'f04c'
        'pause-circle'                        = 'f28b'
        'paw'                                 = 'f1b0'
        'peace'                               = 'f67c'
        'pen'                                 = 'f304'
        'pen-alt'                             = 'f305'
        'pen-fancy'                           = 'f5ac'
        'pen-nib'                             = 'f5ad'
        'pen-square'                          = 'f14b'
        'pencil-alt'                          = 'f303'
        'pencil-ruler'                        = 'f5ae'
        'people-arrows'                       = 'e068'
        'people-carry'                        = 'f4ce'
        'pepper-hot'                          = 'f816'
        'percent'                             = 'f295'
        'percentage'                          = 'f541'
        'person-booth'                        = 'f756'
        'phone'                               = 'f095'
        'phone-alt'                           = 'f879'
        'phone-slash'                         = 'f3dd'
        'phone-square'                        = 'f098'
        'phone-square-alt'                    = 'f87b'
        'phone-volume'                        = 'f2a0'
        'photo-video'                         = 'f87c'
        'piggy-bank'                          = 'f4d3'
        'pills'                               = 'f484'
        'pizza-slice'                         = 'f818'
        'place-of-worship'                    = 'f67f'
        'plane'                               = 'f072'
        'plane-arrival'                       = 'f5af'
        'plane-departure'                     = 'f5b0'
        'plane-slash'                         = 'e069'
        'play'                                = 'f04b'
        'play-circle'                         = 'f144'
        'plug'                                = 'f1e6'
        'plus'                                = 'f067'
        'plus-circle'                         = 'f055'
        'plus-square'                         = 'f0fe'
        'podcast'                             = 'f2ce'
        'poll'                                = 'f681'
        'poll-h'                              = 'f682'
        'poo'                                 = 'f2fe'
        'poo-storm'                           = 'f75a'
        'poop'                                = 'f619'
        'portrait'                            = 'f3e0'
        'pound-sign'                          = 'f154'
        'power-off'                           = 'f011'
        'pray'                                = 'f683'
        'praying-hands'                       = 'f684'
        'prescription'                        = 'f5b1'
        'prescription-bottle'                 = 'f485'
        'prescription-bottle-alt'             = 'f486'
        'print'                               = 'f02f'
        'procedures'                          = 'f487'
        'project-diagram'                     = 'f542'
        'pump-medical'                        = 'e06a'
        'pump-soap'                           = 'e06b'
        'puzzle-piece'                        = 'f12e'
        'qrcode'                              = 'f029'
        'question'                            = 'f128'
        'question-circle'                     = 'f059'
        'quidditch'                           = 'f458'
        'quote-left'                          = 'f10d'
        'quote-right'                         = 'f10e'
        'quran'                               = 'f687'
        'radiation'                           = 'f7b9'
        'radiation-alt'                       = 'f7ba'
        'rainbow'                             = 'f75b'
        'random'                              = 'f074'
        'receipt'                             = 'f543'
        'record-vinyl'                        = 'f8d9'
        'recycle'                             = 'f1b8'
        'redo'                                = 'f01e'
        'redo-alt'                            = 'f2f9'
        'registered'                          = 'f25d'
        'remove-format'                       = 'f87d'
        'reply'                               = 'f3e5'
        'reply-all'                           = 'f122'
        'republican'                          = 'f75e'
        'restroom'                            = 'f7bd'
        'retweet'                             = 'f079'
        'ribbon'                              = 'f4d6'
        'ring'                                = 'f70b'
        'road'                                = 'f018'
        'robot'                               = 'f544'
        'rocket'                              = 'f135'
        'route'                               = 'f4d7'
        'rss'                                 = 'f09e'
        'rss-square'                          = 'f143'
        'ruble-sign'                          = 'f158'
        'ruler'                               = 'f545'
        'ruler-combined'                      = 'f546'
        'ruler-horizontal'                    = 'f547'
        'ruler-vertical'                      = 'f548'
        'running'                             = 'f70c'
        'rupee-sign'                          = 'f156'
        'sad-cry'                             = 'f5b3'
        'sad-tear'                            = 'f5b4'
        'satellite'                           = 'f7bf'
        'satellite-dish'                      = 'f7c0'
        'save'                                = 'f0c7'
        'school'                              = 'f549'
        'screwdriver'                         = 'f54a'
        'scroll'                              = 'f70e'
        'sd-card'                             = 'f7c2'
        'search'                              = 'f002'
        'search-dollar'                       = 'f688'
        'search-location'                     = 'f689'
        'search-minus'                        = 'f010'
        'search-plus'                         = 'f00e'
        'seedling'                            = 'f4d8'
        'server'                              = 'f233'
        'shapes'                              = 'f61f'
        'share'                               = 'f064'
        'share-alt'                           = 'f1e0'
        'share-alt-square'                    = 'f1e1'
        'share-square'                        = 'f14d'
        'shekel-sign'                         = 'f20b'
        'shield-alt'                          = 'f3ed'
        'shield-virus'                        = 'e06c'
        'ship'                                = 'f21a'
        'shipping-fast'                       = 'f48b'
        'shoe-prints'                         = 'f54b'
        'shopping-bag'                        = 'f290'
        'shopping-basket'                     = 'f291'
        'shopping-cart'                       = 'f07a'
        'shower'                              = 'f2cc'
        'shuttle-van'                         = 'f5b6'
        'sign'                                = 'f4d9'
        'sign-in-alt'                         = 'f2f6'
        'sign-language'                       = 'f2a7'
        'sign-out-alt'                        = 'f2f5'
        'signal'                              = 'f012'
        'signature'                           = 'f5b7'
        'sim-card'                            = 'f7c4'
        'sink'                                = 'e06d'
        'sitemap'                             = 'f0e8'
        'skating'                             = 'f7c5'
        'skiing'                              = 'f7c9'
        'skiing-nordic'                       = 'f7ca'
        'skull'                               = 'f54c'
        'skull-crossbones'                    = 'f714'
        'slash'                               = 'f715'
        'sleigh'                              = 'f7cc'
        'sliders-h'                           = 'f1de'
        'smile'                               = 'f118'
        'smile-beam'                          = 'f5b8'
        'smile-wink'                          = 'f4da'
        'smog'                                = 'f75f'
        'smoking'                             = 'f48d'
        'smoking-ban'                         = 'f54d'
        'sms'                                 = 'f7cd'
        'snowboarding'                        = 'f7ce'
        'snowflake'                           = 'f2dc'
        'snowman'                             = 'f7d0'
        'snowplow'                            = 'f7d2'
        'soap'                                = 'e06e'
        'socks'                               = 'f696'
        'solar-panel'                         = 'f5ba'
        'sort'                                = 'f0dc'
        'sort-alpha-down'                     = 'f15d'
        'sort-alpha-down-alt'                 = 'f881'
        'sort-alpha-up'                       = 'f15e'
        'sort-alpha-up-alt'                   = 'f882'
        'sort-amount-down'                    = 'f160'
        'sort-amount-down-alt'                = 'f884'
        'sort-amount-up'                      = 'f161'
        'sort-amount-up-alt'                  = 'f885'
        'sort-down'                           = 'f0dd'
        'sort-numeric-down'                   = 'f162'
        'sort-numeric-down-alt'               = 'f886'
        'sort-numeric-up'                     = 'f163'
        'sort-numeric-up-alt'                 = 'f887'
        'sort-up'                             = 'f0de'
        'spa'                                 = 'f5bb'
        'space-shuttle'                       = 'f197'
        'spell-check'                         = 'f891'
        'spider'                              = 'f717'
        'spinner'                             = 'f110'
        'splotch'                             = 'f5bc'
        'spray-can'                           = 'f5bd'
        'square'                              = 'f0c8'
        'square-full'                         = 'f45c'
        'square-root-alt'                     = 'f698'
        'stamp'                               = 'f5bf'
        'star'                                = 'f005'
        'star-and-crescent'                   = 'f699'
        'star-half'                           = 'f089'
        'star-half-alt'                       = 'f5c0'
        'star-of-david'                       = 'f69a'
        'star-of-life'                        = 'f621'
        'step-backward'                       = 'f048'
        'step-forward'                        = 'f051'
        'stethoscope'                         = 'f0f1'
        'sticky-note'                         = 'f249'
        'stop'                                = 'f04d'
        'stop-circle'                         = 'f28d'
        'stopwatch'                           = 'f2f2'
        'stopwatch-20'                        = 'e06f'
        'store'                               = 'f54e'
        'store-alt'                           = 'f54f'
        'store-alt-slash'                     = 'e070'
        'store-slash'                         = 'e071'
        'stream'                              = 'f550'
        'street-view'                         = 'f21d'
        'strikethrough'                       = 'f0cc'
        'stroopwafel'                         = 'f551'
        'subscript'                           = 'f12c'
        'subway'                              = 'f239'
        'suitcase'                            = 'f0f2'
        'suitcase-rolling'                    = 'f5c1'
        'sun'                                 = 'f185'
        'superscript'                         = 'f12b'
        'surprise'                            = 'f5c2'
        'swatchbook'                          = 'f5c3'
        'swimmer'                             = 'f5c4'
        'swimming-pool'                       = 'f5c5'
        'synagogue'                           = 'f69b'
        'sync'                                = 'f021'
        'sync-alt'                            = 'f2f1'
        'syringe'                             = 'f48e'
        'table'                               = 'f0ce'
        'table-tennis'                        = 'f45d'
        'tablet'                              = 'f10a'
        'tablet-alt'                          = 'f3fa'
        'tablets'                             = 'f490'
        'tachometer-alt'                      = 'f3fd'
        'tag'                                 = 'f02b'
        'tags'                                = 'f02c'
        'tape'                                = 'f4db'
        'tasks'                               = 'f0ae'
        'taxi'                                = 'f1ba'
        'teeth'                               = 'f62e'
        'teeth-open'                          = 'f62f'
        'temperature-high'                    = 'f769'
        'temperature-low'                     = 'f76b'
        'tenge'                               = 'f7d7'
        'terminal'                            = 'f120'
        'text-height'                         = 'f034'
        'text-width'                          = 'f035'
        'th'                                  = 'f00a'
        'th-large'                            = 'f009'
        'th-list'                             = 'f00b'
        'theater-masks'                       = 'f630'
        'thermometer'                         = 'f491'
        'thermometer-empty'                   = 'f2cb'
        'thermometer-full'                    = 'f2c7'
        'thermometer-half'                    = 'f2c9'
        'thermometer-quarter'                 = 'f2ca'
        'thermometer-three-quarters'          = 'f2c8'
        'thumbs-down'                         = 'f165'
        'thumbs-up'                           = 'f164'
        'thumbtack'                           = 'f08d'
        'ticket-alt'                          = 'f3ff'
        'times'                               = 'f00d'
        'times-circle'                        = 'f057'
        'tint'                                = 'f043'
        'tint-slash'                          = 'f5c7'
        'tired'                               = 'f5c8'
        'toggle-off'                          = 'f204'
        'toggle-on'                           = 'f205'
        'toilet'                              = 'f7d8'
        'toilet-paper'                        = 'f71e'
        'toilet-paper-slash'                  = 'e072'
        'toolbox'                             = 'f552'
        'tools'                               = 'f7d9'
        'tooth'                               = 'f5c9'
        'torah'                               = 'f6a0'
        'torii-gate'                          = 'f6a1'
        'tractor'                             = 'f722'
        'trademark'                           = 'f25c'
        'traffic-light'                       = 'f637'
        'trailer'                             = 'e041'
        'train'                               = 'f238'
        'tram'                                = 'f7da'
        'transgender'                         = 'f224'
        'transgender-alt'                     = 'f225'
        'trash'                               = 'f1f8'
        'trash-alt'                           = 'f2ed'
        'trash-restore'                       = 'f829'
        'trash-restore-alt'                   = 'f82a'
        'tree'                                = 'f1bb'
        'trophy'                              = 'f091'
        'truck'                               = 'f0d1'
        'truck-loading'                       = 'f4de'
        'truck-monster'                       = 'f63b'
        'truck-moving'                        = 'f4df'
        'truck-pickup'                        = 'f63c'
        'tshirt'                              = 'f553'
        'tty'                                 = 'f1e4'
        'tv'                                  = 'f26c'
        'umbrella'                            = 'f0e9'
        'umbrella-beach'                      = 'f5ca'
        'underline'                           = 'f0cd'
        'undo'                                = 'f0e2'
        'undo-alt'                            = 'f2ea'
        'universal-access'                    = 'f29a'
        'university'                          = 'f19c'
        'unlink'                              = 'f127'
        'unlock'                              = 'f09c'
        'unlock-alt'                          = 'f13e'
        'upload'                              = 'f093'
        'user'                                = 'f007'
        'user-alt'                            = 'f406'
        'user-alt-slash'                      = 'f4fa'
        'user-astronaut'                      = 'f4fb'
        'user-check'                          = 'f4fc'
        'user-circle'                         = 'f2bd'
        'user-clock'                          = 'f4fd'
        'user-cog'                            = 'f4fe'
        'user-edit'                           = 'f4ff'
        'user-friends'                        = 'f500'
        'user-graduate'                       = 'f501'
        'user-injured'                        = 'f728'
        'user-lock'                           = 'f502'
        'user-md'                             = 'f0f0'
        'user-minus'                          = 'f503'
        'user-ninja'                          = 'f504'
        'user-nurse'                          = 'f82f'
        'user-plus'                           = 'f234'
        'user-secret'                         = 'f21b'
        'user-shield'                         = 'f505'
        'user-slash'                          = 'f506'
        'user-tag'                            = 'f507'
        'user-tie'                            = 'f508'
        'user-times'                          = 'f235'
        'users'                               = 'f0c0'
        'users-cog'                           = 'f509'
        'users-slash'                         = 'e073'
        'utensil-spoon'                       = 'f2e5'
        'utensils'                            = 'f2e7'
        'vector-square'                       = 'f5cb'
        'venus'                               = 'f221'
        'venus-double'                        = 'f226'
        'venus-mars'                          = 'f228'
        'vest'                                = 'e085'
        'vest-patches'                        = 'e086'
        'vial'                                = 'f492'
        'vials'                               = 'f493'
        'video'                               = 'f03d'
        'video-slash'                         = 'f4e2'
        'vihara'                              = 'f6a7'
        'virus'                               = 'e074'
        'virus-slash'                         = 'e075'
        'viruses'                             = 'e076'
        'voicemail'                           = 'f897'
        'volleyball-ball'                     = 'f45f'
        'volume-down'                         = 'f027'
        'volume-mute'                         = 'f6a9'
        'volume-off'                          = 'f026'
        'volume-up'                           = 'f028'
        'vote-yea'                            = 'f772'
        'vr-cardboard'                        = 'f729'
        'walking'                             = 'f554'
        'wallet'                              = 'f555'
        'warehouse'                           = 'f494'
        'water'                               = 'f773'
        'wave-square'                         = 'f83e'
        'weight'                              = 'f496'
        'weight-hanging'                      = 'f5cd'
        'wheelchair'                          = 'f193'
        'wifi'                                = 'f1eb'
        'wind'                                = 'f72e'
        'window-close'                        = 'f410'
        'window-maximize'                     = 'f2d0'
        'window-minimize'                     = 'f2d1'
        'window-restore'                      = 'f2d2'
        'wine-bottle'                         = 'f72f'
        'wine-glass'                          = 'f4e3'
        'wine-glass-alt'                      = 'f5ce'
        'won-sign'                            = 'f159'
        'wrench'                              = 'f0ad'
        'x-ray'                               = 'f497'
        'yen-sign'                            = 'f157'
        'yin-yang'                            = 'f6ad'
    }
    FontsMaterialIcon  = @(
        '3d-rotation'
        '500px'
        '8tracks'
        'account'
        'account-add'
        'account-box'
        'account-box-mail'
        'account-box-o'
        'account-box-phone'
        'account-calendar'
        'account-circle'
        'account-o'
        'accounts'
        'accounts-add'
        'accounts-alt'
        'accounts-list'
        'accounts-list-alt'
        'accounts-outline'
        'airline-seat-flat'
        'airline-seat-flat-angled'
        'airline-seat-individual-suite'
        'airline-seat-legroom-extra'
        'airline-seat-legroom-normal'
        'airline-seat-legroom-reduced'
        'airline-seat-recline-extra'
        'airline-seat-recline-normal'
        'airplane'
        'airplane-off'
        'airplay'
        'alarm'
        'alarm-check'
        'alarm-off'
        'alarm-plus'
        'alarm-snooze'
        'album'
        'alert-circle'
        'alert-circle-o'
        'alert-octagon'
        'alert-polygon'
        'alert-triangle'
        'amazon'
        'android'
        'android-alt'
        'apple'
        'apps'
        'archive'
        'arrow-left'
        'arrow-left-bottom'
        'arrow-merge'
        'arrow-missed'
        'arrow-right'
        'arrow-right-top'
        'arrows'
        'arrow-split'
        'aspect-ratio'
        'aspect-ratio-alt'
        'assignment'
        'assignment-account'
        'assignment-alert'
        'assignment-check'
        'assignment-o'
        'assignment-return'
        'assignment-returned'
        'attachment'
        'attachment-alt'
        'audio'
        'badge-check'
        'balance'
        'balance-wallet'
        'battery'
        'battery-alert'
        'battery-flash'
        'battery-unknown'
        'behance'
        'bike'
        'block'
        'block-alt'
        'blogger'
        'bluetooth'
        'bluetooth-connected'
        'bluetooth-off'
        'bluetooth-search'
        'bluetooth-setting'
        'blur'
        'blur-circular'
        'blur-linear'
        'blur-off'
        'boat'
        'book'
        'book-image'
        'bookmark'
        'bookmark-outline'
        'border-all'
        'border-bottom'
        'border-clear'
        'border-color'
        'border-horizontal'
        'border-inner'
        'border-left'
        'border-outer'
        'border-right'
        'border-style'
        'border-top'
        'border-vertical'
        'brightness-2'
        'brightness-3'
        'brightness-4'
        'brightness-5'
        'brightness-6'
        'brightness-7'
        'brightness-auto'
        'brightness-setting'
        'broken-image'
        'brush'
        'bug'
        'bus'
        'cake'
        'calendar'
        'calendar-alt'
        'calendar-check'
        'calendar-close'
        'calendar-note'
        'camera'
        'camera-add'
        'camera-alt'
        'camera-bw'
        'camera-front'
        'camera-mic'
        'camera-party-mode'
        'camera-rear'
        'camera-roll'
        'camera-switch'
        'car'
        'card'
        'card-alert'
        'card-giftcard'
        'card-membership'
        'card-off'
        'card-sd'
        'card-sim'
        'card-travel'
        'caret-down'
        'caret-down-circle'
        'caret-left'
        'caret-left-circle'
        'caret-right'
        'caret-right-circle'
        'caret-up'
        'caret-up-circle'
        'car-taxi'
        'car-wash'
        'case'
        'case-check'
        'case-download'
        'case-play'
        'cast'
        'cast-connected'
        'center-focus-strong'
        'center-focus-weak'
        'chart'
        'chart-donut'
        'check'
        'check-all'
        'check-circle'
        'check-circle-u'
        'check-square'
        'chevron-down'
        'chevron-left'
        'chevron-right'
        'chevron-up'
        'circle'
        'circle-o'
        'city'
        'city-alt'
        'close'
        'close-circle'
        'close-circle-o'
        'closed-caption'
        'cloud'
        'cloud-box'
        'cloud-circle'
        'cloud-done'
        'cloud-download'
        'cloud-off'
        'cloud-outline'
        'cloud-outline-alt'
        'cloud-upload'
        'cocktail'
        'code'
        'codepen'
        'code-setting'
        'code-smartphone'
        'coffee'
        'collection-bookmark'
        'collection-case-play'
        'collection-folder-image'
        'collection-image'
        'collection-image-o'
        'collection-item'
        'collection-item-1'
        'collection-item-2'
        'collection-item-3'
        'collection-item-4'
        'collection-item-5'
        'collection-item-6'
        'collection-item-7'
        'collection-item-8'
        'collection-item-9'
        'collection-item-9-plus'
        'collection-music'
        'collection-pdf'
        'collection-plus'
        'collection-speaker'
        'collection-text'
        'collection-video'
        'comment'
        'comment-alert'
        'comment-alt'
        'comment-alt-text'
        'comment-edit'
        'comment-image'
        'comment-list'
        'comment-more'
        'comment-outline'
        'comments'
        'comment-text'
        'comment-text-alt'
        'comment-video'
        'compare'
        'compass'
        'confirmation-number'
        'copy'
        'crop'
        'crop-16-9'
        'crop-3-2'
        'crop-5-4'
        'crop-7-5'
        'crop-din'
        'crop-free'
        'crop-landscape'
        'crop-portrait'
        'crop-square'
        'cutlery'
        'delete'
        'delicious'
        'desktop-mac'
        'desktop-windows'
        'developer-board'
        'device-hub'
        'devices'
        'devices-off'
        'dialpad'
        'disc-full'
        'disqus'
        'dns'
        'dock'
        'dot-circle'
        'dot-circle-alt'
        'download'
        'dribbble'
        'drink'
        'dropbox'
        'edit'
        'eject'
        'eject-alt'
        'email'
        'email-open'
        'equalizer'
        'evernote'
        'explicit'
        'exposure'
        'exposure-alt'
        'eye'
        'eyedropper'
        'eye-off'
        'face'
        'facebook'
        'facebook-box'
        'fast-forward'
        'fast-rewind'
        'favorite'
        'favorite-outline'
        'female'
        'file'
        'file-plus'
        'file-text'
        'filter-b-and-w'
        'filter-center-focus'
        'filter-frames'
        'filter-list'
        'filter-tilt-shift'
        'fire'
        'flag'
        'flare'
        'flash'
        'flash-auto'
        'flash-off'
        'flattr'
        'flickr'
        'flight-land'
        'flight-takeoff'
        'flip'
        'flip-to-back'
        'flip-to-front'
        'floppy'
        'flower'
        'flower-alt'
        'folder'
        'folder-outline'
        'folder-person'
        'folder-star'
        'folder-star-alt'
        'font'
        'format-align-center'
        'format-align-justify'
        'format-align-left'
        'format-align-right'
        'format-bold'
        'format-clear'
        'format-clear-all'
        'format-color-fill'
        'format-color-reset'
        'format-color-text'
        'format-indent-decrease'
        'format-indent-increase'
        'format-italic'
        'format-line-spacing'
        'format-list-bulleted'
        'format-list-numbered'
        'format-ltr'
        'format-rtl'
        'format-size'
        'format-strikethrough'
        'format-strikethrough-s'
        'format-subject'
        'format-underlined'
        'format-valign-bottom'
        'format-valign-center'
        'format-valign-top'
        'forward'
        'forward-10'
        'forward-30'
        'forward-5'
        'fullscreen'
        'fullscreen-alt'
        'fullscreen-exit'
        'functions'
        'gamepad'
        'gas-station'
        'gesture'
        'gif'
        'github'
        'github-alt'
        'github-box'
        'globe'
        'globe-alt'
        'globe-lock'
        'google'
        'google-drive'
        'google-earth'
        'google-glass'
        'google-maps'
        'google-old'
        'google-pages'
        'google-play'
        'google-plus'
        'google-plus-box'
        'gps'
        'gps-dot'
        'gps-off'
        'gradient'
        'graduation-cap'
        'grain'
        'graphic-eq'
        'grid'
        'grid-off'
        'group'
        'group-work'
        'hd'
        'hdr'
        'hdr-off'
        'hdr-strong'
        'hdr-weak'
        'headset'
        'headset-mic'
        'hearing'
        'help'
        'help-outline'
        'home'
        'hospital'
        'hospital-alt'
        'hotel'
        'hourglass'
        'hourglass-alt'
        'hourglass-outline'
        'hq'
        'http'
        'image'
        'image-alt'
        'image-o'
        'inbox'
        'info'
        'info-outline'
        'input-antenna'
        'input-composite'
        'input-hdmi'
        'input-power'
        'input-svideo'
        'instagram'
        'invert-colors'
        'invert-colors-off'
        'iridescent'
        'key'
        'keyboard'
        'keyboard-hide'
        'label'
        'label-alt'
        'label-alt-outline'
        'label-heart'
        'labels'
        'lamp'
        'landscape'
        'language-css3'
        'language-html5'
        'language-javascript'
        'language-python'
        'language-python-alt'
        'laptop'
        'laptop-chromebook'
        'laptop-mac'
        'lastfm'
        'layers'
        'layers-off'
        'leak'
        'leak-off'
        'library'
        'link'
        'linkedin'
        'linkedin-box'
        'lock'
        'lock-open'
        'lock-outline'
        'long-arrow-down'
        'long-arrow-left'
        'long-arrow-return'
        'long-arrow-right'
        'long-arrow-tab'
        'long-arrow-up'
        'looks'
        'loupe'
        'mail-reply'
        'mail-reply-all'
        'mail-send'
        'male'
        'male-alt'
        'male-female'
        'mall'
        'map'
        'markunread-mailbox'
        'memory'
        'menu'
        'mic'
        'mic-off'
        'mic-outline'
        'mic-setting'
        'minus'
        'minus-circle'
        'minus-circle-outline'
        'minus-square'
        'money'
        'money-box'
        'money-off'
        'mood'
        'mood-bad'
        'more'
        'more-vert'
        'mouse'
        'movie'
        'movie-alt'
        'n-1-square'
        'n-2-square'
        'n-3-square'
        'n-4-square'
        'n-5-square'
        'n-6-square'
        'nature'
        'nature-people'
        'navigation'
        'neg-1'
        'neg-2'
        'network'
        'network-alert'
        'network-locked'
        'network-off'
        'network-outline'
        'network-setting'
        'nfc'
        'notifications'
        'notifications-active'
        'notifications-add'
        'notifications-none'
        'notifications-off'
        'notifications-paused'
        'odnoklassniki'
        'open-in-browser'
        'open-in-new'
        'outlook'
        'palette'
        'panorama-horizontal'
        'panorama-vertical'
        'panorama-wide-angle'
        'parking'
        'pause'
        'pause-circle'
        'pause-circle-outline'
        'paypal'
        'paypal-alt'
        'phone'
        'phone-bluetooth'
        'phone-end'
        'phone-forwarded'
        'phone-in-talk'
        'phone-locked'
        'phone-missed'
        'phone-msg'
        'phone-paused'
        'phone-ring'
        'phone-setting'
        'phone-sip'
        'photo-size-select-large'
        'photo-size-select-small'
        'picture-in-picture'
        'pin'
        'pin-account'
        'pin-assistant'
        'pin-drop'
        'pin-help'
        'pin-off'
        'pinterest'
        'pinterest-box'
        'pizza'
        'plaster'
        'play'
        'play-circle'
        'play-circle-outline'
        'play-for-work'
        'playlist-audio'
        'playlist-plus'
        'playstation'
        'plus'
        'plus-1'
        'plus-2'
        'plus-circle'
        'plus-circle-o'
        'plus-circle-o-duplicate'
        'plus-square'
        'pocket'
        'polymer'
        'portable-wifi'
        'portable-wifi-changes'
        'portable-wifi-off'
        'power'
        'power-input'
        'power-setting'
        'present-to-all'
        'print'
        'puzzle-piece'
        'quote'
        'radio'
        'railway'
        'reader'
        'receipt'
        'reddit'
        'redo'
        'refresh'
        'refresh-alt'
        'refresh-sync'
        'refresh-sync-alert'
        'refresh-sync-off'
        'remote-control'
        'remote-control-alt'
        'repeat'
        'repeat-one'
        'replay'
        'replay-10'
        'replay-30'
        'replay-5'
        'roller'
        'rotate-ccw'
        'rotate-cw'
        'rotate-left'
        'rotate-right'
        'router'
        'rss'
        'ruler'
        'run'
        'satellite'
        'scanner'
        'scissors'
        'screen-rotation'
        'screen-rotation-lock'
        'search'
        'search-for'
        'search-in-file'
        'search-in-page'
        'search-replace'
        'seat'
        'sec-10'
        'sec-3'
        'select-all'
        'settings'
        'settings-square'
        'shape'
        'share'
        'shield-check'
        'shield-security'
        'shopping-basket'
        'shopping-cart'
        'shopping-cart-plus'
        'shuffle'
        'sign-in'
        'skip-next'
        'skip-previous'
        'skype'
        'slideshare'
        'slideshow'
        'smartphone'
        'smartphone-android'
        'smartphone-download'
        'smartphone-erase'
        'smartphone-info'
        'smartphone-iphone'
        'smartphone-landscape'
        'smartphone-landscape-lock'
        'smartphone-lock'
        'smartphone-portrait-lock'
        'smartphone-ring'
        'smartphone-setting'
        'smartphone-setup'
        'sort-amount-asc'
        'sort-amount-desc'
        'sort-asc'
        'sort-desc'
        'soundcloud'
        'space-bar'
        'speaker'
        'spellcheck'
        'spinner'
        'square-down'
        'square-o'
        'square-right'
        'stackoverflow'
        'star'
        'star-circle'
        'star-half'
        'star-outline'
        'steam'
        'steam-square'
        'stop'
        'storage'
        'store'
        'store-24'
        'subway'
        'sun'
        'surround-sound'
        'swap'
        'swap-alt'
        'swap-vertical'
        'swap-vertical-circle'
        'tab'
        'tablet'
        'tablet-android'
        'tablet-mac'
        'tab-unselected'
        'tag'
        'tag-close'
        'tag-more'
        'tap-and-play'
        'text-format'
        'texture'
        'thumb-down'
        'thumb-up'
        'thumb-up-down'
        'ticket-star'
        'time'
        'time-countdown'
        'time-interval'
        'timer'
        'time-restore'
        'time-restore-setting'
        'timer-off'
        'toll'
        'tonality'
        'toys'
        'traffic'
        'transform'
        'translate'
        'trending-down'
        'trending-flat'
        'trending-up'
        'triangle-down'
        'triangle-up'
        'truck'
        'tumblr'
        'tune'
        'turning-sign'
        'tv'
        'tv-alt-play'
        'tv-list'
        'tv-play'
        'twitch'
        'twitter'
        'twitter-box'
        'undo'
        'unfold-less'
        'unfold-more'
        'ungroup'
        'upload'
        'usb'
        'vibration'
        'videocam'
        'videocam-off'
        'videocam-switch'
        'view-agenda'
        'view-array'
        'view-carousel'
        'view-column'
        'view-comfy'
        'view-compact'
        'view-dashboard'
        'view-day'
        'view-headline'
        'view-list'
        'view-list-alt'
        'view-module'
        'view-quilt'
        'view-stream'
        'view-subtitles'
        'view-toc'
        'view-web'
        'view-week'
        'vignette'
        'vimeo'
        'vk'
        'voicemail'
        'volume-down'
        'volume-mute'
        'volume-off'
        'volume-up'
        'walk'
        'wallpaper'
        'washing-machine'
        'watch'
        'wb-auto'
        'whatsapp'
        'widgets'
        'wifi'
        'wifi-alt'
        'wifi-alt-2'
        'wifi-info'
        'wifi-lock'
        'wifi-off'
        'wifi-outline'
        'wikipedia'
        'window-maximize'
        'window-minimize'
        'window-restore'
        'windows'
        'wrap-text'
        'wrench'
        'xbox'
        'yahoo'
        'youtube'
        'youtube-play'
        'zero'
        'zoom-in'
        'zoom-out'
    )
    FontsSimple        = @(
        '1001tracklists'
        '1password'
        '3m'
        '42'
        '4d'
        '500px'
        'a-frame'
        'abbrobotstudio'
        'abbvie'
        'abletonlive'
        'about-dot-me'
        'abstract'
        'academia'
        'accenture'
        'acclaim'
        'accusoft'
        'acer'
        'acm'
        'actigraph'
        'activision'
        'adafruit'
        'adblock'
        'adblockplus'
        'addthis'
        'adguard'
        'adobe'
        'adobeacrobatreader'
        'adobeaftereffects'
        'adobeaudition'
        'adobecreativecloud'
        'adobedreamweaver'
        'adobefonts'
        'adobeillustrator'
        'adobeindesign'
        'adobelightroom'
        'adobelightroomclassic'
        'adobephonegap'
        'adobephotoshop'
        'adobepremierepro'
        'adobexd'
        'adonisjs'
        'aerlingus'
        'aeroflot'
        'aeromexico'
        'aerospike'
        'affinity'
        'affinitydesigner'
        'affinityphoto'
        'affinitypublisher'
        'aidungeon'
        'aiohttp'
        'aiqfome'
        'airasia'
        'airbnb'
        'airbus'
        'aircall'
        'aircanada'
        'airchina'
        'airfrance'
        'airplayaudio'
        'airplayvideo'
        'airtable'
        'alacritty'
        'alfaromeo'
        'algolia'
        'alibaba-dot-com'
        'alibabacloud'
        'aliexpress'
        'alipay'
        'alitalia'
        'alliedmodders'
        'allocine'
        'alltrails'
        'alpinedotjs'
        'alpinelinux'
        'altiumdesigner'
        'amazon'
        'amazonalexa'
        'amazonaws'
        'amazondynamodb'
        'amazonfiretv'
        'amazonlumberyard'
        'amazonpay'
        'amazonprime'
        'amazons3'
        'amd'
        'americanairlines'
        'americanexpress'
        'amp'
        'amul'
        'ana'
        'anaconda'
        'analogue'
        'anchor'
        'andela'
        'android'
        'androidauto'
        'androidstudio'
        'angellist'
        'angular'
        'angularjs'
        'angularuniversal'
        'anilist'
        'ansible'
        'ansys'
        'antdesign'
        'antena3'
        'anydesk'
        'aol'
        'apache'
        'apacheairflow'
        'apacheant'
        'apachecassandra'
        'apachecloudstack'
        'apachecordova'
        'apachedruid'
        'apacheecharts'
        'apacheflink'
        'apachegroovy'
        'apachehive'
        'apachejmeter'
        'apachekafka'
        'apachekylin'
        'apachemaven'
        'apachenetbeanside'
        'apacheopenoffice'
        'apachepulsar'
        'apacherocketmq'
        'apachesolr'
        'apachespark'
        'apachetomcat'
        'aparat'
        'apollographql'
        'apostrophe'
        'apple'
        'applearcade'
        'applemusic'
        'applepay'
        'applepodcasts'
        'appletv'
        'appsignal'
        'appstore'
        'appveyor'
        'aral'
        'arangodb'
        'archicad'
        'archiveofourown'
        'archlinux'
        'ardour'
        'arduino'
        'arkecosystem'
        'arlo'
        'artixlinux'
        'artstation'
        'arxiv'
        'asana'
        'asciidoctor'
        'asciinema'
        'aseprite'
        'askfm'
        'askubuntu'
        'assemblyscript'
        'asus'
        'at-and-t'
        'atari'
        'atlassian'
        'atom'
        'audacity'
        'audi'
        'audible'
        'audio-technica'
        'audioboom'
        'audiomack'
        'aurelia'
        'auth0'
        'authy'
        'autodesk'
        'autohotkey'
        'automatic'
        'automattic'
        'autotask'
        'aventrix'
        'awesomelists'
        'awesomewm'
        'awsamplify'
        'azureartifacts'
        'azuredataexplorer'
        'azuredevops'
        'azurefunctions'
        'azurepipelines'
        'b-and-rautomation'
        'babel'
        'badgr'
        'badoo'
        'baidu'
        'bamboo'
        'bancontact'
        'bandcamp'
        'bandlab'
        'bandsintown'
        'bankofamerica'
        'barclays'
        'baremetrics'
        'basecamp'
        'bata'
        'bathasu'
        'battle-dot-net'
        'bbc'
        'bbciplayer'
        'beatport'
        'beats'
        'beatsbydre'
        'behance'
        'beijingsubway'
        'bentley'
        'betfair'
        'bigbasket'
        'bigcartel'
        'bigcommerce'
        'bilibili'
        'bing'
        'bit'
        'bitbucket'
        'bitcoin'
        'bitcoincash'
        'bitcoinsv'
        'bitdefender'
        'bitly'
        'bitrise'
        'bitwarden'
        'bitwig'
        'blackberry'
        'blazemeter'
        'blazor'
        'blender'
        'blockchain-dot-com'
        'blogger'
        'bloglovin'
        'blueprint'
        'bluetooth'
        'bmcsoftware'
        'bmw'
        'boeing'
        'bookbub'
        'bookmeter'
        'bookstack'
        'boost'
        'bootstrap'
        'bosch'
        'bose'
        'bower'
        'box'
        'brand-dot-ai'
        'brandfolder'
        'brave'
        'breaker'
        'britishairways'
        'broadcom'
        'bt'
        'buddy'
        'buefy'
        'buffer'
        'bugatti'
        'bugcrowd'
        'bugsnag'
        'buildkite'
        'bulma'
        'bunq'
        'buymeacoffee'
        'buzzfeed'
        'byte'
        'c'
        'cachet'
        'cairometro'
        'cakephp'
        'campaignmonitor'
        'canonical'
        'canva'
        'capacitor'
        'carthrottle'
        'carto'
        'cashapp'
        'castbox'
        'castorama'
        'castro'
        'caterpillar'
        'cbs'
        'cdprojekt'
        'celery'
        'centos'
        'ceph'
        'cesium'
        'cevo'
        'chai'
        'chainlink'
        'chakraui'
        'chart-dot-js'
        'chartmogul'
        'chase'
        'chatbot'
        'checkio'
        'checkmarx'
        'chef'
        'chevrolet'
        'chinaeasternairlines'
        'chinasouthernairlines'
        'chocolatey'
        'chrysler'
        'chupachups'
        'cinema4d'
        'circle'
        'circleci'
        'cirrusci'
        'cisco'
        'citrix'
        'citroen'
        'civicrm'
        'ckeditor4'
        'claris'
        'clickup'
        'clion'
        'cliqz'
        'clockify'
        'clojure'
        'cloud66'
        'cloudbees'
        'cloudcannon'
        'cloudera'
        'cloudflare'
        'cloudsmith'
        'cloudways'
        'clubhouse'
        'clyp'
        'cmake'
        'cnn'
        'co-op'
        'cockroachlabs'
        'cocoapods'
        'cocos'
        'coda'
        'codacy'
        'codeberg'
        'codecademy'
        'codeceptjs'
        'codechef'
        'codeclimate'
        'codecov'
        'codefactor'
        'codeforces'
        'codeigniter'
        'codemagic'
        'codemirror'
        'codenewbie'
        'codepen'
        'codeproject'
        'codersrank'
        'coderwall'
        'codesandbox'
        'codeship'
        'codewars'
        'codingame'
        'codingninjas'
        'codio'
        'coffeescript'
        'cognizant'
        'coinbase'
        'commerzbank'
        'commonworkflowlanguage'
        'composer'
        'compropago'
        'concourse'
        'conda-forge'
        'conekta'
        'confluence'
        'consul'
        'contactlesspayment'
        'contentful'
        'convertio'
        'cookiecutter'
        'coronaengine'
        'coronarenderer'
        'corsair'
        'couchbase'
        'counter-strike'
        'countingworkspro'
        'coursera'
        'coveralls'
        'cpanel'
        'cplusplus'
        'craftcms'
        'creativecommons'
        'crehana'
        'crowdin'
        'crowdsource'
        'crunchbase'
        'crunchyroll'
        'cryengine'
        'crystal'
        'csharp'
        'css3'
        'csswizardry'
        'cucumber'
        'curl'
        'curseforge'
        'cycling74'
        'cypress'
        'd-wavesystems'
        'd3-dot-js'
        'dacia'
        'daf'
        'dailymotion'
        'daimler'
        'darkreader'
        'dart'
        'daserste'
        'dash'
        'dashlane'
        'dassaultsystemes'
        'databricks'
        'datacamp'
        'datadog'
        'datastax'
        'dataversioncontrol'
        'datocms'
        'datto'
        'dazn'
        'dblp'
        'dcentertainment'
        'debian'
        'deepin'
        'deepnote'
        'deezer'
        'delicious'
        'deliveroo'
        'dell'
        'delonghi'
        'delphi'
        'delta'
        'deno'
        'dependabot'
        'derspiegel'
        'designernews'
        'deutschebahn'
        'deutschebank'
        'dev-dot-to'
        'deviantart'
        'devpost'
        'devrant'
        'dgraph'
        'dhl'
        'diagrams-dot-net'
        'dialogflow'
        'diaspora'
        'digg'
        'digi-keyelectronics'
        'digitalocean'
        'dior'
        'directus'
        'discogs'
        'discord'
        'discourse'
        'discover'
        'disqus'
        'disroot'
        'django'
        'dlna'
        'docker'
        'docusign'
        'dogecoin'
        'dolby'
        'doordash'
        'dot-net'
        'douban'
        'draugiem-dot-lv'
        'dribbble'
        'drone'
        'drooble'
        'dropbox'
        'drupal'
        'dsautomobiles'
        'dtube'
        'duckduckgo'
        'dunked'
        'duolingo'
        'dwm'
        'dynamics365'
        'dynatrace'
        'ea'
        'eagle'
        'easyjet'
        'ebay'
        'eclipseche'
        'eclipseide'
        'eclipsejetty'
        'eclipsemosquitto'
        'eclipsevert-dot-x'
        'editorconfig'
        'edx'
        'egghead'
        'egnyte'
        'eightsleep'
        'elastic'
        'elasticcloud'
        'elasticsearch'
        'elasticstack'
        'electron'
        'element'
        'elementary'
        'eleventy'
        'elixir'
        'eljueves'
        'ello'
        'elm'
        'elsevier'
        'embarcadero'
        'ember-dot-js'
        'emby'
        'emirates'
        'emlakjet'
        'empirekred'
        'enpass'
        'envato'
        'epel'
        'epicgames'
        'epson'
        'erlang'
        'esea'
        'eslgaming'
        'eslint'
        'esphome'
        'espressif'
        'ethereum'
        'ethiopianairlines'
        'etihadairways'
        'etsy'
        'eventbrite'
        'eventstore'
        'evernote'
        'everplaces'
        'evry'
        'exercism'
        'expensify'
        'expertsexchange'
        'expo'
        'express'
        'eyeem'
        'f-droid'
        'f-secure'
        'facebook'
        'facebookgaming'
        'facebooklive'
        'faceit'
        'facepunch'
        'falcon'
        'fampay'
        'fandango'
        'fandom'
        'farfetch'
        'fastapi'
        'fastify'
        'fastlane'
        'fastly'
        'fathom'
        'favro'
        'feathub'
        'fedex'
        'fedora'
        'fedramp'
        'feedly'
        'ferrari'
        'ferrarin-dot-v-dot'
        'ffmpeg'
        'fiat'
        'fidoalliance'
        'fifa'
        'figma'
        'figshare'
        'fila'
        'files'
        'filezilla'
        'fing'
        'firebase'
        'firefox'
        'firefoxbrowser'
        'first'
        'fitbit'
        'fite'
        'fiverr'
        'flask'
        'flathub'
        'flattr'
        'flickr'
        'flipboard'
        'flipkart'
        'floatplane'
        'flood'
        'fluentd'
        'flutter'
        'fmod'
        'fnac'
        'folium'
        'fontawesome'
        'fontbase'
        'foodpanda'
        'ford'
        'forestry'
        'formstack'
        'fortinet'
        'fortran'
        'fossa'
        'fossilscm'
        'foursquare'
        'foxtel'
        'fozzy'
        'framer'
        'fraunhofer-gesellschaft'
        'freebsd'
        'freecodecamp'
        'freedesktop-dot-org'
        'freelancer'
        'freenas'
        'fujifilm'
        'fujitsu'
        'furaffinity'
        'furrynetwork'
        'futurelearn'
        'g2a'
        'gamejolt'
        'garmin'
        'gatling'
        'gatsby'
        'gauges'
        'geeksforgeeks'
        'generalelectric'
        'generalmotors'
        'genius'
        'gentoo'
        'geocaching'
        'gerrit'
        'ghost'
        'ghostery'
        'gimp'
        'giphy'
        'git'
        'gitbook'
        'gitea'
        'gitee'
        'gitextensions'
        'github'
        'githubactions'
        'githubsponsors'
        'gitkraken'
        'gitlab'
        'gitlfs'
        'gitpod'
        'gitter'
        'glassdoor'
        'glitch'
        'gmail'
        'gnome'
        'gnu'
        'gnubash'
        'gnuemacs'
        'gnuicecat'
        'gnuprivacyguard'
        'gnusocial'
        'go'
        'godotengine'
        'gofundme'
        'gog-dot-com'
        'goldenline'
        'goodreads'
        'google'
        'googleads'
        'googleadsense'
        'googleanalytics'
        'googleassistant'
        'googlecalendar'
        'googlecardboard'
        'googlecast'
        'googlechat'
        'googlechrome'
        'googleclassroom'
        'googlecloud'
        'googlecolab'
        'googledomains'
        'googledrive'
        'googleearth'
        'googlefit'
        'googlefonts'
        'googlehangouts'
        'googlekeep'
        'googlelens'
        'googlemaps'
        'googlemeet'
        'googlemessages'
        'googlemybusiness'
        'googlenearby'
        'googlenews'
        'googleoptimize'
        'googlepay'
        'googlephotos'
        'googleplay'
        'googlepodcasts'
        'googlescholar'
        'googlesearchconsole'
        'googlesheets'
        'googlestreetview'
        'googletagmanager'
        'googletranslate'
        'gotomeeting'
        'gov-dot-uk'
        'gradle'
        'grafana'
        'grammarly'
        'graphcool'
        'graphql'
        'grav'
        'gravatar'
        'graylog'
        'greensock'
        'griddotai'
        'gridsome'
        'groupon'
        'grubhub'
        'grunt'
        'guangzhoumetro'
        'gulp'
        'gumroad'
        'gumtree'
        'gutenberg'
        'habr'
        'hackaday'
        'hackclub'
        'hackerearth'
        'hackerone'
        'hackerrank'
        'hackhands'
        'hackster'
        'hackthebox'
        'handshake'
        'handshake_protocol'
        'happycow'
        'harbor'
        'hashnode'
        'haskell'
        'hasura'
        'hatenabookmark'
        'haveibeenpwned'
        'haxe'
        'hbo'
        'hcl'
        'headspace'
        'hellofresh'
        'hellyhansen'
        'helm'
        'helpdesk'
        'here'
        'heroku'
        'hexo'
        'hey'
        'hibernate'
        'highly'
        'hilton'
        'hipchat'
        'hitachi'
        'hive'
        'hive_blockchain'
        'hockeyapp'
        'homeadvisor'
        'homeassistant'
        'homeassistantcommunitystore'
        'homebrew'
        'homebridge'
        'homify'
        'honda'
        'hootsuite'
        'hoppscotch'
        'hotels-dot-com'
        'hotjar'
        'houdini'
        'houzz'
        'hp'
        'html5'
        'htmlacademy'
        'huawei'
        'hubspot'
        'hugo'
        'hulu'
        'humblebundle'
        'hungryjacks'
        'hurriyetemlak'
        'husqvarna'
        'hyper'
        'hyperledger'
        'hypothesis'
        'hyundai'
        'iata'
        'ibeacon'
        'ibm'
        'ibmwatson'
        'icinga'
        'icloud'
        'icomoon'
        'icon'
        'iconfinder'
        'iconify'
        'iconjar'
        'icq'
        'ideal'
        'ieee'
        'ifixit'
        'ifood'
        'ifttt'
        'iheartradio'
        'ikea'
        'imagej'
        'imdb'
        'imgur'
        'immer'
        'imou'
        'indeed'
        'infiniti'
        'influxdb'
        'informatica'
        'infosys'
        'ingress'
        'inkscape'
        'insomnia'
        'instacart'
        'instagram'
        'instapaper'
        'instructables'
        'integromat'
        'intel'
        'intellijidea'
        'intercom'
        'internetarchive'
        'internetexplorer'
        'intigriti'
        'invision'
        'invoiceninja'
        'iobroker'
        'ionic'
        'ios'
        'iota'
        'ipfs'
        'issuu'
        'itch-dot-io'
        'itunes'
        'iveco'
        'jabber'
        'jaguar'
        'jamboard'
        'jameson'
        'jamstack'
        'jasmine'
        'java'
        'javascript'
        'jbl'
        'jcb'
        'jeep'
        'jekyll'
        'jellyfin'
        'jenkins'
        'jenkinsx'
        'jest'
        'jet'
        'jetbrains'
        'jfrog'
        'jfrogbintray'
        'jinja'
        'jira'
        'jirasoftware'
        'jitsi'
        'johndeere'
        'joomla'
        'jpeg'
        'jquery'
        'jrgroup'
        'jsdelivr'
        'jsfiddle'
        'json'
        'jsonwebtokens'
        'jss'
        'julia'
        'junipernetworks'
        'junit5'
        'jupyter'
        'justeat'
        'justgiving'
        'kaggle'
        'kahoot'
        'kaios'
        'kakao'
        'kakaotalk'
        'kalilinux'
        'karlsruherverkehrsverbund'
        'kasasmart'
        'kashflow'
        'kaspersky'
        'katacoda'
        'katana'
        'kde'
        'kdenlive'
        'keepassxc'
        'kentico'
        'keras'
        'keybase'
        'keycdn'
        'khanacademy'
        'khronosgroup'
        'kia'
        'kibana'
        'kickstarter'
        'kik'
        'kirby'
        'kitsu'
        'klarna'
        'klm'
        'klook'
        'klout'
        'knowledgebase'
        'known'
        'ko-fi'
        'kodi'
        'koding'
        'kofax'
        'komoot'
        'kongregate'
        'kotlin'
        'krita'
        'ktm'
        'kubernetes'
        'kubuntu'
        'kyocera'
        'labview'
        'lada'
        'lamborghini'
        'landrover'
        'laragon'
        'laravel'
        'laravelhorizon'
        'laravelnova'
        'last-dot-fm'
        'lastpass'
        'latex'
        'launchpad'
        'lbry'
        'leaflet'
        'leanpub'
        'leetcode'
        'lenovo'
        'less'
        'letsencrypt'
        'letterboxd'
        'lg'
        'lgtm'
        'liberapay'
        'libraries-dot-io'
        'librarything'
        'libreoffice'
        'libuv'
        'lichess'
        'lifx'
        'lighthouse'
        'line'
        'lineageos'
        'linewebtoon'
        'linkedin'
        'linktree'
        'linode'
        'linux'
        'linuxcontainers'
        'linuxfoundation'
        'linuxmint'
        'lionair'
        'lit'
        'litecoin'
        'livechat'
        'livejournal'
        'livestream'
        'llvm'
        'lmms'
        'logitech'
        'logmein'
        'logstash'
        'looker'
        'loom'
        'loop'
        'lospec'
        'lotpolishairlines'
        'lua'
        'lubuntu'
        'lufthansa'
        'lumen'
        'lydia'
        'lyft'
        'maas'
        'macos'
        'macys'
        'magento'
        'magisk'
        'mail-dot-ru'
        'mailchimp'
        'majorleaguehacking'
        'makerbot'
        'man'
        'manageiq'
        'manjaro'
        'mapbox'
        'mariadb'
        'mariadbfoundation'
        'markdown'
        'marketo'
        'marriott'
        'maserati'
        'mastercard'
        'mastercomfig'
        'mastodon'
        'material-ui'
        'materialdesign'
        'materialdesignicons'
        'mathworks'
        'matomo'
        'matrix'
        'mattermost'
        'matternet'
        'max-planck-gesellschaft'
        'max'
        'maytag'
        'mazda'
        'mcafee'
        'mcdonalds'
        'mclaren'
        'mdnwebdocs'
        'mediafire'
        'mediatek'
        'mediatemple'
        'medium'
        'meetup'
        'mega'
        'mendeley'
        'mercedes'
        'mercurial'
        'messenger'
        'metabase'
        'metafilter'
        'meteor'
        'metro'
        'metrodelaciudaddemexico'
        'metrodemadrid'
        'metrodeparis'
        'mewe'
        'micro-dot-blog'
        'microbit'
        'microgenetics'
        'micropython'
        'microsoft'
        'microsoftacademic'
        'microsoftaccess'
        'microsoftazure'
        'microsoftedge'
        'microsoftexcel'
        'microsoftexchange'
        'microsoftoffice'
        'microsoftonedrive'
        'microsoftonenote'
        'microsoftoutlook'
        'microsoftpowerpoint'
        'microsoftsharepoint'
        'microsoftsqlserver'
        'microsoftteams'
        'microsoftvisio'
        'microsoftword'
        'microstrategy'
        'midi'
        'minds'
        'minecraft'
        'minetest'
        'mini'
        'minutemailer'
        'miro'
        'mitsubishi'
        'mix'
        'mixcloud'
        'mobx-state-tree'
        'mobx'
        'mocha'
        'modx'
        'mojangstudios'
        'moleculer'
        'momenteo'
        'monero'
        'mongodb'
        'monkeytie'
        'monogram'
        'monster'
        'monzo'
        'moo'
        'moscowmetro'
        'motorola'
        'mozilla'
        'mta'
        'mtr'
        'mumble'
        'musescore'
        'musicbrainz'
        'mxlinux'
        'myanimelist'
        'myob'
        'myspace'
        'mysql'
        'n26'
        'namebase'
        'namecheap'
        'nano'
        'nasa'
        'nationalgrid'
        'nativescript'
        'naver'
        'nba'
        'nbb'
        'ndr'
        'nec'
        'neo4j'
        'neovim'
        'nestjs'
        'netapp'
        'netflix'
        'netlify'
        'newjapanpro-wrestling'
        'newrelic'
        'newyorktimes'
        'next-dot-js'
        'nextcloud'
        'nextdoor'
        'nfc'
        'nginx'
        'ngrok'
        'niconico'
        'nim'
        'nintendo'
        'nintendo3ds'
        'nintendogamecube'
        'nintendonetwork'
        'nintendoswitch'
        'nissan'
        'nixos'
        'node-dot-js'
        'node-red'
        'nodemon'
        'nokia'
        'norwegian'
        'notepadplusplus'
        'notion'
        'notist'
        'nounproject'
        'npm'
        'nrwl'
        'nubank'
        'nucleo'
        'nuget'
        'nuke'
        'numba'
        'numpy'
        'nutanix'
        'nuxt-dot-js'
        'nvidia'
        'nx'
        'observable'
        'obsstudio'
        'ocaml'
        'octave'
        'octopusdeploy'
        'oculus'
        'odnoklassniki'
        'odysee'
        'okcupid'
        'okta'
        'oneplus'
        'onlyfans'
        'onnx'
        'onstar'
        'opel'
        'openaccess'
        'openai'
        'openaigym'
        'openapiinitiative'
        'openbadges'
        'openbsd'
        'openbugbounty'
        'opencollective'
        'opencontainersinitiative'
        'opencv'
        'openfaas'
        'opengl'
        'openid'
        'openlayers'
        'opennebula'
        'opensourceinitiative'
        'openssl'
        'openstack'
        'openstreetmap'
        'opensuse'
        'openvpn'
        'openwrt'
        'opera'
        'opnsense'
        'opsgenie'
        'opslevel'
        'oracle'
        'orcid'
        'org'
        'origin'
        'osano'
        'oshkosh'
        'osmc'
        'osu'
        'overcast'
        'overleaf'
        'ovh'
        'owasp'
        'oxygen'
        'oyo'
        'p5-dot-js'
        'packagist'
        'pagekit'
        'pagerduty'
        'pagespeedinsights'
        'pagseguro'
        'palantir'
        'paloaltosoftware'
        'pandas'
        'pandora'
        'pantheon'
        'paritysubstrate'
        'parse-dot-ly'
        'passport'
        'pastebin'
        'patreon'
        'payoneer'
        'paypal'
        'paytm'
        'pcgamingwiki'
        'peertube'
        'pegasusairlines'
        'pelican'
        'peloton'
        'pepsi'
        'perforce'
        'periscope'
        'perl'
        'peugeot'
        'pexels'
        'pfsense'
        'phabricator'
        'philipshue'
        'phonepe'
        'photobucket'
        'photocrowd'
        'photopea'
        'php'
        'phpstorm'
        'pi-hole'
        'picarto-dot-tv'
        'picnic'
        'picpay'
        'pimcore'
        'pinboard'
        'pingdom'
        'pingup'
        'pinterest'
        'pioneerdj'
        'pivotaltracker'
        'piwigo'
        'pixabay'
        'pixiv'
        'pjsip'
        'planet'
        'plangrid'
        'platzi'
        'playcanvas'
        'player-dot-me'
        'playerfm'
        'playstation'
        'playstation2'
        'playstation3'
        'playstation4'
        'playstation5'
        'playstationvita'
        'pleroma'
        'plesk'
        'plex'
        'plotly'
        'pluralsight'
        'plurk'
        'pluscodes'
        'pm2'
        'pnpm'
        'pocket'
        'pocketcasts'
        'podcastaddict'
        'podman'
        'pointy'
        'pokemon'
        'poly'
        'polymerproject'
        'pop_os'
        'porsche'
        'postcss'
        'postgresql'
        'postman'
        'postmates'
        'powerbi'
        'powers'
        'powershell'
        'pr-dot-co'
        'pre-commit'
        'premierleague'
        'prestashop'
        'presto'
        'prettier'
        'prezi'
        'prime'
        'primevideo'
        'prisma'
        'prismic'
        'privateinternetaccess'
        'probot'
        'processingfoundation'
        'processwire'
        'producthunt'
        'progate'
        'progress'
        'prometheus'
        'prosieben'
        'proto-dot-io'
        'protocols-dot-io'
        'protondb'
        'protonmail'
        'protonvpn'
        'protools'
        'proxmox'
        'publons'
        'pubmed'
        'pug'
        'puppet'
        'puppeteer'
        'purescript'
        'purgecss'
        'purism'
        'pusher'
        'pycharm'
        'pypi'
        'pypy'
        'python'
        'pytorch'
        'pytorchlightning'
        'pyup'
        'qantas'
        'qatarairways'
        'qemu'
        'qgis'
        'qi'
        'qiita'
        'qiskit'
        'qiwi'
        'qt'
        'qualcomm'
        'qualtrics'
        'quantcast'
        'quantconnect'
        'quantopian'
        'quarkus'
        'quasar'
        'qubesos'
        'quest'
        'quickbooks'
        'quicktime'
        'quip'
        'quora'
        'qwiklabs'
        'qzone'
        'r'
        'r3'
        'rabbitmq'
        'racket'
        'radar'
        'radiopublic'
        'rainmeter'
        'rakuten'
        'ram'
        'rancher'
        'raspberrypi'
        'razer'
        'react'
        'reactivex'
        'reactos'
        'reactrouter'
        'readthedocs'
        'realm'
        'reason'
        'reasonstudios'
        'redbubble'
        'reddit'
        'redhat'
        'redhatopenshift'
        'redis'
        'redux-saga'
        'redux'
        'redwoodjs'
        'relianceindustrieslimited'
        'renault'
        'renovatebot'
        'renpy'
        'renren'
        'repl-dot-it'
        'researchgate'
        'resurrectionremixos'
        'retroarch'
        'retropie'
        'reveal-dot-js'
        'reverbnation'
        'revolut'
        'revue'
        'rewe'
        'rezgo'
        'rhinoceros'
        'rider'
        'ring'
        'riotgames'
        'ripple'
        'riseup'
        'roamresearch'
        'roblox'
        'robotframework'
        'rocket-dot-chat'
        'roku'
        'rolls-royce'
        'rollup-dot-js'
        'roots'
        'rootsbedrock'
        'rootssage'
        'ros'
        'rottentomatoes'
        'roundcube'
        'rss'
        'rstudio'
        'rte'
        'rtl'
        'rtlzwei'
        'ruby'
        'rubygems'
        'rubyonrails'
        'runkeeper'
        'runkit'
        'rust'
        'rxdb'
        'ryanair'
        's7airlines'
        'safari'
        'sahibinden'
        'salesforce'
        'saltstack'
        'samsung'
        'samsungpay'
        'sanfranciscomunicipalrailway'
        'saopaulometro'
        'sap'
        'sass'
        'sat-dot-1'
        'saucelabs'
        'scala'
        'scaleway'
        'scania'
        'scikit-learn'
        'scipy'
        'scopus'
        'scratch'
        'screencastify'
        'scribd'
        'scrimba'
        'scrollreveal'
        'scrumalliance'
        'scrutinizerci'
        'seagate'
        'seat'
        'sefaria'
        'sega'
        'selenium'
        'sellfy'
        'semantic-release'
        'semanticuireact'
        'semanticweb'
        'semaphoreci'
        'semver'
        'sencha'
        'sennheiser'
        'sensu'
        'sentry'
        'sepa'
        'serverfault'
        'serverless'
        'sfml'
        'shanghaimetro'
        'sharp'
        'shazam'
        'shell'
        'shelly'
        'shenzhenmetro'
        'shields-dot-io'
        'shikimori'
        'shopify'
        'shopware'
        'shotcut'
        'showpad'
        'showtime'
        'shutterstock'
        'siemens'
        'signal'
        'simkl'
        'simpleanalytics'
        'simpleicons'
        'sinaweibo'
        'singlestore'
        'sitepoint'
        'sketch'
        'sketchfab'
        'sketchup'
        'skillshare'
        'skoda'
        'sky'
        'skyliner'
        'skype'
        'skypeforbusiness'
        'slack'
        'slackware'
        'slashdot'
        'slickpic'
        'slides'
        'slideshare'
        'smart'
        'smartthings'
        'smashingmagazine'
        'smrt'
        'smugmug'
        'snapchat'
        'snapcraft'
        'snowflake'
        'snyk'
        'society6'
        'socket-dot-io'
        'sogou'
        'solidity'
        'sololearn'
        'solus'
        'sonarcloud'
        'sonarlint'
        'sonarqube'
        'sonarsource'
        'songkick'
        'songoda'
        'sonicwall'
        'sonos'
        'soundcloud'
        'sourceengine'
        'sourceforge'
        'sourcegraph'
        'southwestairlines'
        'spacemacs'
        'spacex'
        'sparkar'
        'sparkasse'
        'sparkfun'
        'sparkpost'
        'spdx'
        'speakerdeck'
        'spectrum'
        'speedtest'
        'spinnaker'
        'spinrilla'
        'splunk'
        'spotify'
        'spotlight'
        'spreaker'
        'spring'
        'springboot'
        'sprint'
        'spyderide'
        'sqlite'
        'square'
        'squareenix'
        'squarespace'
        'ssrn'
        'stackbit'
        'stackexchange'
        'stackoverflow'
        'stackpath'
        'stackshare'
        'stadia'
        'staffbase'
        'starlingbank'
        'starship'
        'startrek'
        'starz'
        'statamic'
        'staticman'
        'statuspage'
        'statuspal'
        'steam'
        'steamdb'
        'steamworks'
        'steem'
        'steemit'
        'steinberg'
        'stellar'
        'stencyl'
        'stimulus'
        'stitcher'
        'stmicroelectronics'
        'storify'
        'storyblok'
        'storybook'
        'strapi'
        'strava'
        'streamlit'
        'stripe'
        'strongswan'
        'stubhub'
        'styled-components'
        'stylelint'
        'styleshare'
        'stylus'
        'subaru'
        'sublimetext'
        'substack'
        'subversion'
        'suckless'
        'sumologic'
        'supabase'
        'superuser'
        'surveymonkey'
        'suse'
        'suzuki'
        'svelte'
        'svg'
        'svgo'
        'swagger'
        'swarm'
        'swift'
        'swiggy'
        'swiper'
        'symantec'
        'symfony'
        'symphony'
        'sympy'
        'synology'
        't-mobile'
        'tableau'
        'tado'
        'tails'
        'tailwindcss'
        'talend'
        'tampermonkey'
        'taobao'
        'tapas'
        'tasmota'
        'tata'
        'taxbuzz'
        'teamcity'
        'teamspeak'
        'teamviewer'
        'ted'
        'teespring'
        'tekton'
        'tele5'
        'telegram'
        'telegraph'
        'tencentqq'
        'tencentweibo'
        'tensorflow'
        'teradata'
        'teratail'
        'terraform'
        'tesla'
        'testin'
        'testinglibrary'
        'textpattern'
        'theconversation'
        'theirishtimes'
        'themighty'
        'themodelsresource'
        'themoviedatabase'
        'theregister'
        'thesoundsresource'
        'thespritersresource'
        'thewashingtonpost'
        'thingiverse'
        'thinkpad'
        'three-dot-js'
        'threema'
        'thumbtack'
        'thunderbird'
        'thymeleaf'
        'ticketmaster'
        'tidal'
        'tide'
        'tiktok'
        'tile'
        'timescale'
        'tinder'
        'tinyletter'
        'tmux'
        'todoist'
        'toggl'
        'tokyometro'
        'tomorrowland'
        'topcoder'
        'toptal'
        'torbrowser'
        'torproject'
        'toshiba'
        'toyota'
        'tp-link'
        'tqdm'
        'trainerroad'
        'trakt'
        'transferwise'
        'transportforireland'
        'transportforlondon'
        'travisci'
        'treehouse'
        'trello'
        'trendmicro'
        'treyarch'
        'triller'
        'trino'
        'trip-dot-com'
        'tripadvisor'
        'trove'
        'truenas'
        'trulia'
        'trustedshops'
        'trustpilot'
        'tryhackme'
        'tryitonline'
        'ts-node'
        'tui'
        'tumblr'
        'tunein'
        'turbosquid'
        'turkishairlines'
        'tutanota'
        'tvtime'
        'twilio'
        'twitch'
        'twitter'
        'twoo'
        'typescript'
        'typo3'
        'uber'
        'ubereats'
        'ubiquiti'
        'ubisoft'
        'ublockorigin'
        'ubuntu'
        'udacity'
        'udemy'
        'ufc'
        'uikit'
        'ulule'
        'umbraco'
        'unacademy'
        'undertale'
        'unicode'
        'unilever'
        'unitedairlines'
        'unity'
        'unraid'
        'unrealengine'
        'unsplash'
        'untangle'
        'untappd'
        'upcloud'
        'uplabs'
        'uploaded'
        'ups'
        'upwork'
        'usps'
        'v'
        'v8'
        'vaadin'
        'vagrant'
        'valve'
        'vapor'
        'vault'
        'vauxhall'
        'vbulletin'
        'vectorlogozone'
        'vectorworks'
        'veeam'
        'veepee'
        'venmo'
        'vercel'
        'verdaccio'
        'veritas'
        'verizon'
        'vfairs'
        'viadeo'
        'viber'
        'vim'
        'vimeo'
        'vimeolivestream'
        'vine'
        'virb'
        'virtualbox'
        'virustotal'
        'visa'
        'visualstudio'
        'visualstudiocode'
        'vite'
        'vivaldi'
        'vivino'
        'vk'
        'vlcmediaplayer'
        'vmware'
        'vodafone'
        'volkswagen'
        'volvo'
        'vonage'
        'vox'
        'vsco'
        'vue-dot-js'
        'vuetify'
        'vulkan'
        'vultr'
        'w3c'
        'wagtail'
        'wakatime'
        'walkman'
        'wappalyzer'
        'warnerbros-dot'
        'wattpad'
        'waze'
        'wearos'
        'weasyl'
        'webassembly'
        'webauthn'
        'webcomponents-dot-org'
        'webdriverio'
        'webflow'
        'webgl'
        'webhint'
        'weblate'
        'webmin'
        'webmoney'
        'webpack'
        'webrtc'
        'webstorm'
        'wechat'
        'weights-and-biases'
        'wemo'
        'wetransfer'
        'whatsapp'
        'wheniwork'
        'whitesource'
        'wii'
        'wiiu'
        'wikidata'
        'wikimediacommons'
        'wikipedia'
        'wikiquote'
        'wikivoyage'
        'windows'
        'windows95'
        'windowsterminal'
        'windowsxp'
        'winmate'
        'wipro'
        'wire'
        'wireguard'
        'wireshark'
        'wish'
        'wistia'
        'wix'
        'wizzair'
        'wolfram'
        'wolframlanguage'
        'wolframmathematica'
        'woo'
        'woocommerce'
        'wordpress'
        'workplace'
        'worldhealthorganization'
        'wpengine'
        'wprocket'
        'write-dot-as'
        'wwe'
        'wwise'
        'x-dot-org'
        'x-pack'
        'xamarin'
        'xaml'
        'xampp'
        'xbox'
        'xcode'
        'xdadevelopers'
        'xero'
        'xfce'
        'xiaomi'
        'xilinx'
        'xing'
        'xmpp'
        'xrp'
        'xsplit'
        'xstate'
        'yahoo'
        'yale'
        'yamahacorporation'
        'yamahamotorcorporation'
        'yammer'
        'yandex'
        'yarn'
        'ycombinator'
        'yelp'
        'yoast'
        'yourtravel-dot-tv'
        'youtube'
        'youtubegaming'
        'youtubemusic'
        'youtubestudio'
        'youtubetv'
        'z-wave'
        'zalando'
        'zapier'
        'zdf'
        'zelle'
        'zend'
        'zendesk'
        'zendframework'
        'zenn'
        'zerodha'
        'zeromq'
        'zerply'
        'zhihu'
        'zigbee'
        'zillow'
        'zingat'
        'zoho'
        'zoiper'
        'zomato'
        'zoom'
        'zorin'
        'zotero'
        'zulip'
    )
}
function Remove-DiscardedReports {
    [CmdletBinding()]
    param(
        [Array] $FilePathsGenerated,
        [string] $FolderPath,
        [string] $Extension
    )
    $FilePathsCurrent = Get-ChildItem -LiteralPath $FolderPath -File -Filter "*$($Extension)" | Select-Object -ExpandProperty FullName
    foreach ($File in $FilePathsCurrent) {
        if ($File -notin $FilePathsGenerated) {
            Write-Color -Text '[i]', '[FILE ] ', "Removing discarded report: $File" -Color Yellow, DarkGray, Yellow
            Remove-Item -LiteralPath $File -Force
        }
    }
}
function Set-FolderConfiguration {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary]$Folders,
        [Array] $FoldersConfiguration,
        [System.Collections.IDictionary]$FolderLimit
    )

    Write-Color -Text '[i]', "[TheDashboard] ", 'Setting Folder Configuration', ' [Informative] ' -Color Yellow, DarkGray, Yellow, DarkGray, Magenta

    foreach ($Folder in $FoldersConfiguration) {
        if (-not $Folder.Name -and $Folder.Url) {
            $Folder.Name = $Folder.Url
        } elseif (-not $Folder.Url -and $Folder.Name) {
            $Folder.Url = $Folder.Name
        }

        if ($FolderLimit -and $FolderLimit.Name -and -not $Folder.DisableGlobalLimits) {
            $Folder.LimitsConfiguration[$FolderLimit.Name] = $FolderLimit
        }
        $Folders[$Folder.Name] = $Folder
    }
}
function New-DashboardEmailParameter {
    [cmdletBinding(DefaultParameterSetName = 'Compatibility', SupportsShouldProcess)]
    param(
        [Parameter(ParameterSetName = 'SecureString')]

        [Parameter(ParameterSetName = 'oAuth')]
        [Parameter(ParameterSetName = 'Compatibility')]
        [alias('SmtpServer')][string] $Server,

        [Parameter(ParameterSetName = 'SecureString')]

        [Parameter(ParameterSetName = 'oAuth')]
        [Parameter(ParameterSetName = 'Compatibility')]
        [int] $Port,

        [Parameter(Mandatory, ParameterSetName = 'SecureString')]
        [Parameter(Mandatory, ParameterSetName = 'oAuth')]
        [Parameter(Mandatory, ParameterSetName = 'Graph')]
        [Parameter(Mandatory, ParameterSetName = 'MgGraphRequest')]
        [Parameter(Mandatory, ParameterSetName = 'Compatibility')]
        [Parameter(Mandatory, ParameterSetName = 'SendGrid')]
        [object] $From,

        [Parameter(ParameterSetName = 'SecureString')]
        [Parameter(ParameterSetName = 'oAuth')]
        [Parameter(ParameterSetName = 'Graph')]
        [Parameter(ParameterSetName = 'MgGraphRequest')]
        [Parameter(ParameterSetName = 'Compatibility')]
        [Parameter(ParameterSetName = 'SendGrid')]
        [string] $ReplyTo,

        [Parameter(ParameterSetName = 'SecureString')]
        [Parameter(ParameterSetName = 'oAuth')]
        [Parameter(ParameterSetName = 'Graph')]
        [Parameter(ParameterSetName = 'MgGraphRequest')]
        [Parameter(ParameterSetName = 'Compatibility')]
        [Parameter(ParameterSetName = 'SendGrid')]
        [alias('Importance')][ValidateSet('Low', 'Normal', 'High')][string] $Priority,

        [Parameter(ParameterSetName = 'SecureString')]
        [Parameter(ParameterSetName = 'oAuth')]
        [Parameter(ParameterSetName = 'Compatibility')]
        [ValidateSet('None', 'OnSuccess', 'OnFailure', 'Delay', 'Never')][string[]] $DeliveryNotificationOption,

        [Parameter(ParameterSetName = 'SecureString')]
        [Parameter(ParameterSetName = 'oAuth')]
        [Parameter(ParameterSetName = 'Compatibility')]
        [MailKit.Net.Smtp.DeliveryStatusNotificationType] $DeliveryStatusNotificationType,

        [Parameter(ParameterSetName = 'oAuth')]
        [Parameter(Mandatory, ParameterSetName = 'Graph')]
        [Parameter(ParameterSetName = 'Compatibility')]
        [Parameter(Mandatory, ParameterSetName = 'SendGrid')]
        [pscredential] $Credential,

        [Parameter(ParameterSetName = 'SecureString')]
        [string] $Username,

        [Parameter(ParameterSetName = 'SecureString')]
        [string] $Password,

        [Parameter(ParameterSetName = 'SecureString')]
        [Parameter(ParameterSetName = 'oAuth')]
        [Parameter(ParameterSetName = 'Compatibility')]
        [MailKit.Security.SecureSocketOptions] $SecureSocketOptions,

        [Parameter(ParameterSetName = 'SecureString')]
        [Parameter(ParameterSetName = 'oAuth')]
        [Parameter(ParameterSetName = 'Compatibility')]
        [switch] $UseSsl,

        [Parameter(ParameterSetName = 'SecureString')]
        [Parameter(ParameterSetName = 'oAuth')]
        [Parameter(ParameterSetName = 'Compatibility')]
        [switch] $SkipCertificateRevocation,

        [Parameter(ParameterSetName = 'SecureString')]
        [Parameter(ParameterSetName = 'oAuth')]
        [Parameter(ParameterSetName = 'Compatibility')]
        [alias('SkipCertificateValidatation')][switch] $SkipCertificateValidation,

        [Parameter(ParameterSetName = 'SecureString')]
        [Parameter(ParameterSetName = 'oAuth')]
        [Parameter(ParameterSetName = 'Compatibility')]
        [int] $Timeout,

        [Parameter(ParameterSetName = 'oAuth')]
        [alias('oAuth')][switch] $oAuth2,

        [Parameter(ParameterSetName = 'Graph')]
        [Parameter(ParameterSetName = 'MgGraphRequest')]
        [switch] $RequestReadReceipt,

        [Parameter(ParameterSetName = 'Graph')]
        [Parameter(ParameterSetName = 'MgGraphRequest')]
        [switch] $RequestDeliveryReceipt,

        [Parameter(ParameterSetName = 'Graph')]
        [Parameter(ParameterSetName = 'MgGraphRequest')]
        [switch] $Graph,

        [Parameter(ParameterSetName = 'MgGraphRequest')]
        [switch] $MgGraphRequest,

        [Parameter(ParameterSetName = 'SecureString')]
        [switch] $AsSecureString,

        [Parameter(ParameterSetName = 'SendGrid')]
        [switch] $SendGrid,

        [Parameter(ParameterSetName = 'SendGrid')]
        [switch] $SeparateTo,

        [Parameter(ParameterSetName = 'Graph')]
        [Parameter(ParameterSetName = 'MgGraphRequest')]
        [switch] $DoNotSaveToSentItems,

        [Parameter(ParameterSetName = 'SecureString')]
        [Parameter(ParameterSetName = 'oAuth')]
        [Parameter(ParameterSetName = 'Compatibility')]
        [string] $LocalDomain
    )

    $Output = [ordered] @{
        Type     = 'DashboardConfigurationEmail'
        Settings = [ordered] @{
            Server                         = if ($PSBoundParameters.ContainsKey('Server')) {
                $Server } else {
                $null }
            Port                           = if ($PSBoundParameters.ContainsKey('Port')) {
                $Port } else {
                $null }
            From                           = if ($PSBoundParameters.ContainsKey('From')) {
                $From } else {
                $null }
            ReplyTo                        = if ($PSBoundParameters.ContainsKey('ReplyTo')) {
                $ReplyTo } else {
                $null }
            Priority                       = if ($PSBoundParameters.ContainsKey('Priority')) {
                $Priority } else {
                $null }
            DeliveryNotificationOption     = if ($PSBoundParameters.ContainsKey('DeliveryNotificationOption')) {
                $DeliveryNotificationOption } else {
                $null }
            DeliveryStatusNotificationType = if ($PSBoundParameters.ContainsKey('DeliveryStatusNotificationType')) {
                $DeliveryStatusNotificationType } else {
                $null }
            Credential                     = if ($PSBoundParameters.ContainsKey('Credential')) {
                $Credential } else {
                $null }
            Username                       = if ($PSBoundParameters.ContainsKey('Username')) {
                $Username } else {
                $null }
            Password                       = if ($PSBoundParameters.ContainsKey('Password')) {
                $Password } else {
                $null }
            SecureSocketOptions            = if ($PSBoundParameters.ContainsKey('SecureSocketOptions')) {
                $SecureSocketOptions } else {
                $null }
            UseSsl                         = if ($PSBoundParameters.ContainsKey('UseSsl')) {
                $UseSsl } else {
                $null }
            SkipCertificateRevocation      = if ($PSBoundParameters.ContainsKey('SkipCertificateRevocation')) {
                $SkipCertificateRevocation } else {
                $null }
            SkipCertificateValidation      = if ($PSBoundParameters.ContainsKey('SkipCertificateValidatation')) {
                $SkipCertificateValidation } else {
                $null }
            Timeout                        = if ($PSBoundParameters.ContainsKey('Timeout')) {
                $Timeout } else {
                $null }
            oAuth2                         = if ($PSBoundParameters.ContainsKey('oAuth2')) {
                $oAuth2 } else {
                $null }
            RequestReadReceipt             = if ($PSBoundParameters.ContainsKey('RequestReadReceipt')) {
                $RequestReadReceipt } else {
                $null }
            RequestDeliveryReceipt         = if ($PSBoundParameters.ContainsKey('RequestDeliveryReceipt')) {
                $RequestDeliveryReceipt } else {
                $null }
            Graph                          = if ($PSBoundParameters.ContainsKey('Graph')) {
                $Graph } else {
                $null }
            MgGraphRequest                 = if ($PSBoundParameters.ContainsKey('MgGraphRequest')) {
                $MgGraphRequest } else {
                $null }
            AsSecureString                 = if ($PSBoundParameters.ContainsKey('AsSecureString')) {
                $AsSecureString } else {
                $null }
            SendGrid                       = if ($PSBoundParameters.ContainsKey('SendGrid')) {
                $SendGrid } else {
                $null }
            SeparateTo                     = if ($PSBoundParameters.ContainsKey('SeparateTo')) {
                $SeparateTo } else {
                $null }
            DoNotSaveToSentItems           = if ($PSBoundParameters.ContainsKey('DoNotSaveToSentItems')) {
                $DoNotSaveToSentItems } else {
                $null }
            WhatIf                         = $WhatIfPreference
        }
    }
    Remove-EmptyValue -Hashtable $Output.Settings
    $Output
}
function New-DashboardFolder {
    <#
    .SYNOPSIS
        This function is used to define a new folder for the dashboard that will be displayed as a TopLevelMenu.
 
    .DESCRIPTION
    This function is used to define a new folder for the dashboard that will be displayed as a TopLevelMenu.
    It allows you to define the folder name, path, and URL name. You can also specify the icon that will be used for the folder.
 
    .PARAMETER Entries
    This parameter allows you to define the content of the folder. It should be a script block that contains the content of the folder.
    The content of the folder can be defined using the New-DashboardReplacement, New-DashboardLimit functions
 
    .PARAMETER Name
    The name of the folder that will be displayed in the dashboard.
 
    .PARAMETER Path
    Path to Reports folder where the reports are stored.
 
    .PARAMETER UrlName
    The URL name for the dashboard folder. This is used to create the URL for the folder.
 
    .PARAMETER CopyFrom
    Copy files from this location to the existing location that is specified by the Path parameter.
    This is useful for copying reports from different locations to the expected location.
 
    .PARAMETER MoveFrom
    Move files from this location to the existing location that is specified by the Path parameter.
    This is useful for moving reports from different locations to the expected location.
 
    .PARAMETER Extension
    This parameter specifies the file extensions to include when processing files.
    It will by default use the same extension as the root of the dashboard, but this one can include additional extensions.
 
    .PARAMETER DisableGlobalReplacement
    This parameter, when set, will disable global replacements for the dashboard for this particular folder.
    This means that the replacements defined in the root of the dashboard will not be applied to the content of this folder.
 
    .PARAMETER DisableGlobalLimits
    This parameter, when set, will disable global limits for the dashboard for this particular folder.
    This means that the limits defined in the root of the dashboard will not be applied to the content of this folder.
 
    .PARAMETER IconBrands
    Specifies the icon that will be used for the folder. This is a brand icon from FontAwesome.
 
    .PARAMETER IconRegular
    Specifies the icon that will be used for the folder. This is a regular icon from FontAwesome.
 
    .PARAMETER IconSolid
    Specifies the icon that will be used for the folder. This is a solid icon from FontAwesome.
 
    .EXAMPLE
    New-DashboardFolder -Name 'Group Policies' -IconBrands android -UrlName 'GPO' -Path $PSScriptRoot\Reports\TestPolicies
 
    .EXAMPLE
     New-DashboardFolder -Name 'Windows Updates' -IconBrands android -UrlName 'WindowsUpdates' -Path $PSScriptRoot\Reports\WindowsUpdates {
        $newDashboardReplacementSplat = @{
            BeforeSplit = @{
                'GroupMembership-' = ''
                '_Regional' = ' Regional'
                'GPOZaurr' = ''
                'Testimo' = ''
            }
            #AfterSplitPositionName = 1, 2
            AfterSplitPositionName = [ordered] @{
                'PingCastle-Domain-*' = 0, 2
                '*' = 0
                # '*' = 1, 2
            }
            AfterRemoveDoubleSpaces = $true
            AfterUpperChars = "evotec.com", "test.com"
            AfterSplit = @{
                'G P O' = 'GPO'
                'L D A P' = 'LDAP'
                'D H C P' = 'DHCP'
                'A D' = 'AD'
                'I T R X X' = 'ITRXX'
                'I N S' = 'INS'
                'L A P S' = 'LAPS'
                'K R B G T' = 'KRBGT'
            }
            SplitOn = "[_-]"
            AddSpaceToName = $true
        }
        New-DashboardReplacement @newDashboardReplacementSplat
        New-DashboardLimit -LimitItem 2 -IncludeHistory
    } -DisableGlobalReplacement
 
    .NOTES
    General notes
    #>

    [alias('New-TheDashboardFolder')]
    [CmdletBinding()]
    param(
        [Parameter(Position = 0)][scriptblock] $Entries,
        [Parameter(Mandatory)][string] $Name,
        [Parameter(Mandatory)][string] $Path,
        [Parameter(Mandatory)][string] $UrlName,
        [string[]] $CopyFrom,
        [string[]] $MoveFrom,
        [string[]] $Extension,
        [switch] $DisableGlobalReplacement,
        [switch] $DisableGlobalLimits,
        # ICON BRANDS
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
            ($Global:HTMLIcons.FontAwesomeBrands.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeBrands.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeBrands")][string] $IconBrands,

        # ICON REGULAR
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
            ($Global:HTMLIcons.FontAwesomeRegular.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeRegular.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeRegular")][string] $IconRegular,

        # ICON SOLID
        [ArgumentCompleter(
            {
                param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
            ($Global:HTMLIcons.FontAwesomeSolid.Keys)
            }
        )]
        [ValidateScript(
            {
                $_ -in (($Global:HTMLIcons.FontAwesomeSolid.Keys))
            }
        )]
        [parameter(ParameterSetName = "FontAwesomeSolid")][string] $IconSolid
    )

    if ($IconBrands) {
        $IconType = 'IconBrands'
        $Icon = $IconBrands
    } elseif ($IconRegular) {
        $IconType = 'IconRegular'
        $Icon = $IconRegular
    } elseif ($IconSolid) {
        $IconType = 'IconSolid'
        $Icon = $IconSolid
    } else {
        $IconType = 'IconSolid'
        $Icon = 'folder'
    }

    if ($Entries) {
        $ReplacementConfiguration = [System.Collections.Generic.List[System.Collections.IDictionary]]::new()
        $LimitsConfiguration = [ordered] @{}
        $OutputElements = & $Entries
        foreach ($E in $OutputElements) {
            if ($E.Type -eq 'Replacement') {
                $ReplacementConfiguration.Add($E.Settings)
            } elseif ($E.Type -eq 'FolderLimit') {
                $LimitsConfiguration[$E.Settings.Name] = $E.Settings
            }
        }
        $ReplacementsConfiguration = Convert-MultipleReplacements -ReplacementConfiguration $ReplacementConfiguration
    } else {
        $LimitsConfiguration = [ordered] @{}
        $ReplacementsConfiguration = $null
    }

    $Folder = [ordered] @{
        Type     = 'Folder'
        Settings = [ordered] @{
            Name                = $Name
            IconType            = $IconType
            Icon                = $Icon
            Path                = $Path
            Url                 = $UrlName
            CopyFrom            = $CopyFrom
            MoveFrom            = $MoveFrom
            Extension           = $Extension
            ReplacementsGlobal  = -not $DisableGlobalReplacement.IsPresent
            Replacements        = $ReplacementsConfiguration
            LimitsConfiguration = $LimitsConfiguration
            DisableGlobalLimits = $DisableGlobalLimits.IsPresent
        }
    }
    Remove-EmptyValue -Hashtable $Folder -Recursive -ExcludeParameter 'LimitsConfiguration', 'Replacements' -DoNotRemoveEmptyDictionary
    $Folder
}
function New-DashboardGage {
    [alias('New-TheDashboardGage')]
    [cmdletBinding()]
    param(
        [Parameter(Mandatory)][string] $Label,
        [Parameter()][int] $MinValue,
        [Parameter(Mandatory)][int] $MaxValue,
        [Parameter(Mandatory)][int] $Value,
        [Parameter(Mandatory)][DateTime] $Date
    )

    [ordered] @{
        Type     = 'Gage'
        Settings = [ordered] @{
            Label    = $Label
            Value    = $Value
            Date     = $Date
            MinValue = $MinValue
            MaxValue = $MaxValue
        }
    }
}
function New-DashboardLimit {
    <#
    .SYNOPSIS
    Sets limit rules (date, days, item count) for dashboard reports.
 
    .DESCRIPTION
    Creates a structured object defining limits for how many reports to keep,
    how many to store in history, and date-based handling when displaying files.
 
    .PARAMETER Name
    Specifies which reports the limit applies to.
 
    .PARAMETER LimitItem
    Limits how many newest files are kept before moving to history.
 
    .PARAMETER LimitDate
    Specifies date threshold for including files in the menu.
 
    .PARAMETER LimitDays
    Specifies how many days back reports are displayed.
 
    .PARAMETER IncludeHistory
    Indicates whether older files are added to a history list instead of excluded.
 
    .PARAMETER IncludeHistoryLimitDate
    Date threshold after which history entries should not be included.
 
    .PARAMETER IncludeHistoryLimitDays
    Number of days to preserve history entries.
 
    .PARAMETER IncludeHistoryLimit
    Limits how many history entries are kept.
 
    .EXAMPLE
    New-DashboardLimit -LimitItem 3 -LimitDays 10
 
    .NOTES
    Part of TheDashboard module, used to configure file retention rules.
    #>

    [CmdletBinding()]
    param(
        [string] $Name,
        [nullable[int]] $LimitItem,
        [nullable[DateTime]] $LimitDate,
        [nullable[int]] $LimitDays,
        [switch] $IncludeHistory,
        [nullable[DateTime]] $IncludeHistoryLimitDate,
        [nullable[int]] $IncludeHistoryLimitDays,
        [nullable[int]] $IncludeHistoryLimit
    )

    if (-not $Name) {
        $Name = '*'
    }

    if ($IncludeHistoryLimitDate -or $IncludeHistoryLimitDays -or $IncludeHistoryLimit) {
        $IncludeHistory = $true
    }

    $Limit = [ordered] @{
        Type     = 'FolderLimit'
        Settings = [ordered] @{
            Name                    = $Name
            LimitItem               = $LimitItem
            LimitDate               = $LimitDate
            LimitDays               = $LimitDays
            IncludeHistory          = $IncludeHistory
            IncludeHistoryLimit     = $IncludeHistoryLimit
            IncludeHistoryLimitDate = $IncludeHistoryLimitDate
            IncludeHistoryLimitDays = $IncludeHistoryLimitDays
        }
    }

    Remove-EmptyValue -Hashtable $Limit.Settings
    $Limit
}
function New-DashboardNotification {
    [CmdletBinding(DefaultParameterSetName = 'Report')]
    param(
        [Parameter(ParameterSetName = 'Report')][string] $Name,
        [Parameter(ParameterSetName = 'Report')][string] $Category,
        [string] $Type,
        [string] $Filter,
        [Parameter(Mandatory)][string] $EmailTo,
        [Parameter()][string] $EmailCC,
        [Parameter(Mandatory)][string] $EmailSubject
    )

    $Notification = [ordered] @{
        Type     = 'Notification'
        Settings = [ordered] @{
            Id           = [Guid]::NewGuid().ToString()
            Name         = $Name
            Category     = $Category
            Type         = $Type
            Filter       = $Filter
            EmailTo      = $EmailTo
            EmailCC      = $EmailCC
            EmailSubject = $EmailSubject
        }
    }
    Remove-EmptyValue -Hashtable $Notification
    $Notification
}
function New-DashboardReplacement {
    <#
    .SYNOPSIS
    Helps preparing Menu Names and Menu Entries for the dashboard from the file names
 
    .DESCRIPTION
    Helps preparing Menu Names and Menu Entries for the dashboard from the file names
 
    .PARAMETER SplitOn
    Character to split on, for example on '_' or in most cases [_-] which means either _ or -
 
    .PARAMETER BeforeSplit
    Dictionary of replacements to be done before splitting
 
    .PARAMETER BeforeRemoveChars
    Array of characters to remove from the name before splitting, such as specific prefixes or suffixes.
 
    .PARAMETER AfterSplit
    Dictionary of replacements to be done after splitting the file name.
 
    .PARAMETER AddSpaceToName
    Switch indicating whether to add spaces between words in the menu name.
 
    .PARAMETER AfterSplitPositionName
    Array indicating which positions to select from the split file name after splitting.
 
    .PARAMETER AfterRemoveChars
    Array of characters to remove from the name after splitting, such as specific unwanted characters.
 
    .PARAMETER AfterUpperChars
    Array of strings to convert to upper case after splitting.
 
    .PARAMETER AfterRemoveDoubleSpaces
    Switch indicating whether to remove double spaces from the formatted name.
 
    .PARAMETER ReplaceCaseInsenstive
    Switch indicating whether replacements should be case insensitive.
 
    .PARAMETER ReplaceSkipRegex
    Array of regex patterns to skip during the replacement process.
 
    .EXAMPLE
    An example of how to use the New-DashboardReplacement function.
    For example:
    ```powershell
    New-DashboardReplacement -SplitOn '_' -BeforeSplit @{ 'GPO' = '' } -AfterSplit @{ 'BlockedInheritance' = 'Blocked Inheritance' } -AddSpaceToName
    ```
 
    .NOTES
    The process of replacement is done in a specific order.
    - Before Split Replacements => to fix the name before splitting, say remove some parts we don't like
    - SplitOn => split on specific character or characters, for example on '_' or in most cases [_-] which means either _ or -
    - PositionName => Pick specific parts of the split based on the full file name after first replacements
    - FormatString => Format the string based on settings provided
    - After Split Replacements => Replacements after split
 
    The way it works is:
    - We take file 'GPOBlockedInheritance_2023-10-05_092149.aspx'
    - We first do BeforeSplit replacements:
        - It could be for example removing 'GPO' from the name or changing Inheritance to something else
    - We then split on specific character, for example on '_' or in most cases [_-] which means either _ or -
    - After splitting, we will select specific parts of the file name based on the split results, to create a formatted name for the output.
        - If we split on [_-] we get 'GPOBlockedInheritance', '2023', '10', '05', '092149.aspx'
        - However keep in mind we removed 'GPO' in BeforeSplit, so we would get 'BlockedInheritance', '2023', '10', '05', '092149.aspx'
        - So now we can select specific parts of the split, for example 0, 1, 2, 3, 4, 5
        - If we select 0, 1 we would get 'BlockedInheritance', '2023'
    - We then merge the selected parts into a single string, for example 'BlockedInheritance 2023' which will be our menu name
    - However at this point we also can do 'AddSpaceToName' which basically adds space between words, so 'BlockedInheritance 2023' becomes 'Blocked Inheritance 2023'
    - This step is necessary to improve readability and user experience.
    - However the way it works to add a space is to search for all high case letters and add space before them, so 'BlockedInheritance 2023' becomes 'Blocked Inheritance 2023'
    - This works fine for most cases, but sometimes we need to do additional replacements, for example 'GPO' becomes 'G P O', 'PingCastle' becomes 'Ping Castle'
    - We then do AfterSplit replacements, for example replacing 'G P O' with 'GPO' or 'Ping Castle' back to 'PingCastle'
 
    #>

    [alias('New-TheDashboardReplacement')]
    [CmdletBinding()]
    param(
        [string] $SplitOn,
        [Array] $BeforeSplit,
        [alias('RemoveCharsBefore')][string[]] $BeforeRemoveChars,
        [Array] $AfterSplit,
        [alias('AfterAddSpaceToName')][switch] $AddSpaceToName,
        [object] $AfterSplitPositionName,
        [alias('RemoveCharsAfter')][string[]] $AfterRemoveChars,
        [alias('ConvertToUpperChars')][string[]] $AfterUpperChars,
        [alias('RemoveDoubleSpaces')][switch] $AfterRemoveDoubleSpaces,
        [switch] $ReplaceCaseInsenstive,
        [switch] $ReplaceSkipRegex
    )

    $BeforeEntry = [ordered] @{}
    foreach ($Before in $BeforeSplit) {
        foreach ($Key in $Before.Keys) {
            $BeforeEntry[$Key] = $Before[$Key]
        }
    }
    $AfterEntry = [ordered] @{}
    foreach ($After in $AfterSplit) {
        foreach ($Key in $After.Keys) {
            $AfterEntry[$Key] = $After[$Key]
        }
    }

    $Replacements = [ordered] @{
        Type     = 'Replacement'
        Settings = @{
            SplitOn                 = if ($PSBoundParameters.ContainsKey('SplitOn')) {
                $SplitOn } else {
                $null }
            BeforeSplit             = if ($PSBoundParameters.ContainsKey('BeforeSplit')) {
                $BeforeEntry } else {
                $null }
            AfterSplit              = if ($PSBoundParameters.ContainsKey('AfterSplit')) {
                $AfterEntry } else {
                $null }
            AddSpaceToName          = if ($PSBoundParameters.ContainsKey('AddSpaceToName')) {
                $AddSpaceToName } else {
                $null }
            AfterSplitPositionName  = if ($PSBoundParameters.ContainsKey('AfterSplitPositionName')) {
                $AfterSplitPositionName } else {
                $null }
            AfterRemoveChars        = if ($PSBoundParameters.ContainsKey('AfterRemoveChars')) {
                $AfterRemoveChars } else {
                $null }
            AfterUpperChars         = if ($PSBoundParameters.ContainsKey('AfterUpperChars')) {
                $AfterUpperChars } else {
                $null }
            AfterRemoveDoubleSpaces = if ($PSBoundParameters.ContainsKey('AfterRemoveDoubleSpaces')) {
                $AfterRemoveDoubleSpaces } else {
                $null }
            BeforeRemoveChars       = if ($PSBoundParameters.ContainsKey('BeforeRemoveChars')) {
                $BeforeRemoveChars } else {
                $null }
            ReplaceCaseInsenstive   = if ($PSBoundParameters.ContainsKey('ReplaceCaseInsenstive')) {
                $ReplaceCaseInsenstive } else {
                $null }
            ReplaceSkipRegex        = if ($PSBoundParameters.ContainsKey('ReplaceSkipRegex')) {
                $ReplaceSkipRegex } else {
                $null }
        }
    }
    $Replacements
}
function Repair-DashboardContent {
    <#
    .SYNOPSIS
    This function helps to replace content in files with a specific extension in a specific directory with a new content.
 
    .DESCRIPTION
    This function helps to replace content in files with a specific extension in a specific directory with a new content.
 
    .PARAMETER Directory
    Directory where files are located
 
    .PARAMETER ExtensionFrom
    File extension to look for. Default is .aspx
 
    .EXAMPLE
    $SearchString = "_|:|@|#|<-|←|<:|<%|=|=>|⇒|>:"
    $ReplaceString = "_|:|@|#|<-|←|<:|<-%|=|=>|⇒|>:"
    $Directory = "C:\Users\przemyslaw.klys\Downloads"
 
    Repair-ReportContent -Directory $Directory -Search $SearchString -Replace $ReplaceString -EscapeRegex
 
    .EXAMPLE
    $SearchString = '<meta http-equiv="Content-Security-Policy".*?/>'
    $ReplaceString = ''
    $Directory = "C:\Support\GitHub\TheDashboard\Examples\Reports\Security"
 
    Repair-ReportContent -Directory $Directory -Search $SearchString -Replace $ReplaceString
 
    .NOTES
    This is a fix for a SharePoint error that occurs when a string contains "<%"
    - Sorry, something went wrong. An error occurred during the processing of /sites/TheDashboard/Shared Documents/Gpozaurr_GPOBrokenPartially_2024-03-27_025637.aspx. The server block is not well formed.
    The reports created by several scripts contain "<%" in the string, which is not allowed in SharePoint. This is used by the Enlighter.JS library and shows up when not using -Online switch.
    Since the enlighter.js library is put inside HTML directly it causes issues with SharePoint.
    The script will replace "<%" with "<-%" in all files with the .aspx extension in the Reports folder.
 
    This is also a fix for PingCastle reports that contain characters that block it from hosting on Sharepoint or IIS (<meta http-equiv="Content-Security-Policy".*?/>)
 
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [parameter(Mandatory)][string] $Directory,
        [string] $ExtensionFrom = '.aspx',
        [string] $Search,
        [string] $Replace,
        [switch] $EscapeRegex,
        [int] $OnlyNewerThan,
        [switch] $AddOneMinute
    )

    if ($EscapeRegex) {
        $SearchString = [regex]::Escape($Search)
        $ReplaceString = [regex]::Escape($Replace)
    } else {
        $SearchString = $Search
        $ReplaceString = $Replace
    }

    if (-not (Test-Path -Path $Directory)) {
        Write-Color -Text '[i]', "[TheDashboard] ", "Directory $Directory does not exist" -Color Yellow, DarkGray, Yellow, DarkGray, Magenta
        return
    }
    $Files = Get-ChildItem -Path $Directory -File -Recurse -Include "*$ExtensionFrom"
    foreach ($File in $Files) {
        if ($File.Extension -eq $ExtensionFrom) {
            if ($OnlyNewerThan -and $File.LastWriteTime -lt (Get-Date).AddDays(-$OnlyNewerThan)) {

                continue
            }
            Write-Color -Text '[i]', "[TheDashboard] ", "Processing fixes $($File.FullName) / $($File.LastWriteTime)" -Color Yellow, DarkGray, Yellow, DarkGray, Magenta

            $originalCreationTime = $File.CreationTime
            $originalLastWriteTime = $File.LastWriteTime

            $Encoding = Get-FileEncoding -Path $File.FullName
            if ($PSVersionTable.PSVersion.Major -ge 7) {
                $FileContent = Get-Content -Raw -Path $File.FullName -Encoding $Encoding
            } else {

                if ($Encoding -eq 'UTF8BOM') {
                    $Encoding = 'UTF8'
                }
                $FileContent = Get-Content -Raw -Path $File.FullName -Encoding $Encoding
            }
            if ($FileContent -match $SearchString) {
                Write-Color -Text '[i]', "[TheDashboard] ", "Processing fixes $($File.FullName) for ($SearchString)" -Color Green
                $NewContent = $FileContent -replace $SearchString, $ReplaceString
                $NewContent | Set-Content -Path $File.FullName -Encoding $Encoding

                $item = Get-Item $File.FullName
                $item.CreationTime = $originalCreationTime
                if ($AddOneMinute) {
                    $item.LastWriteTime = $originalLastWriteTime.AddMinutes(1)
                } else {
                    $item.LastWriteTime = $originalLastWriteTime
                }
            }
        }
    }
}
function Repair-DashboardExtension {
    <#
    .SYNOPSIS
    This function renames the extension of files in a given directory.
 
    .DESCRIPTION
    The Repair-DashboardExtension function is used to rename the extension of files in a specified directory from one extension to another.
    It's a part of the process of converting the reports to a SharePoint compatible format.
 
    .PARAMETER Path
    The path to the directory containing the files whose extensions are to be renamed.
 
    .PARAMETER ExtensionFrom
    The current extension of the files to be renamed.
 
    .PARAMETER ExtensionTo
    The new extension to be given to the files.
 
    .EXAMPLE
    Repair-DashboardExtension -Path "C:\Reports" -ExtensionFrom ".html" -ExtensionTo ".aspx"
    This command renames the extension of all .html files in the C:\Reports directory to .aspx.
 
    .NOTES
    If a file with the new name already exists in the directory, the function will skip renaming that file.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)][string] $Path,
        [Parameter(Mandatory)][string] $ExtensionFrom,
        [Parameter(Mandatory)][string] $ExtensionTo
    )

    if (-not (Test-Path -Path $Path)) {
        Write-Color -Text '[i]', "[TheDashboard] ", "Directory $Path does not exist" -Color Yellow, DarkGray, Yellow, DarkGray, Magenta
        return
    }
    $Files = Get-ChildItem -Path $Path -File -Recurse -Include "*$ExtensionFrom"
    foreach ($File in $Files) {
        if ($File.Extension -eq $ExtensionFrom) {
            Write-Color -Text '[i]', "[TheDashboard] ", "Processing rename $($File.FullName) / $($File.LastWriteTime)" -Color Yellow, DarkGray, Yellow, DarkGray, Magenta

            $NewName = $File.FullName -replace "$($ExtensionFrom)$", $ExtensionTo

            $ExpectedName = [io.path]::Combine($File.DirectoryName, $NewName)
            if (Test-Path -LiteralPath $ExpectedName) {
                Write-Color -Text "[i]", "[TheDashboard] ", "File already exists: $($ExpectedName). Skipping" -Color Yellow, DarkGray, Yellow, DarkGray, Magenta
                continue
            } else {
                if ($PSCmdlet.ShouldProcess($File.FullName, "Rename to $NewName")) {
                    Rename-Item -Path $File.FullName -NewName $NewName -Force
                }
            }
        }
    }
}
function Start-TheDashboard {
    <#
    .SYNOPSIS
    Generates TheDashboard from multiple provided reports in form of HTML files.
 
    .DESCRIPTION
    Generates TheDashboard from multiple provided reports in form of HTML files.
    It also generates statistics and charts based on the data provided by additional data.
 
    .PARAMETER Elements
    ScriptBlock that accepts New-Dashboard* functions, allowing for configuration of TheDashboard.
 
    .PARAMETER HTMLPath
    Path to HTML files that will be generated.
 
    .PARAMETER StatisticsPath
    Path to XML file that will save some of the data that is used to generate charts
 
    .PARAMETER Logo
    Path to logo that will be used in the header.
 
    .PARAMETER Folders
    Folders that will be used to generate TheDashboard.
    Can co-exist with Elements parameter, and configuration using both will be merged.
 
    .PARAMETER UrlPath
    URL that will be used as a starting point for TheDashboard, and used by all the links.
    By default the URL uses relative path, but it can be changed to absolute path.
    This is useful for example when you want to use TheDashboard on a SharePoint site, where the files are stored in a different place.
 
    .PARAMETER Replacements
    Replacements that will be used to replace names within TheDashboard.
 
    .PARAMETER RemoveNotIncluded
    Removes files that are not included in the menu.
    This is useful to clean up files that are not displayed, but be careful during testing.
 
    .PARAMETER DeleteMethod
    Specifies how files are removed when they are not included in the menu.
    By default, files are moved to the Recycle Bin.
    All options are: RemoveItem, DotNetDelete, RecycleBin.
 
    .PARAMETER TestMode
    Pretends to generate TheDashboard, but does not actually generate it.
    Other processes are executed, but no HTML files are created.
    This is useful for testing along with PassThru parameter, which will return all the data that would be used to generate TheDashboard.
 
    .PARAMETER ShowHTML
    Show TheDashboard in browser after generating it.
 
    .PARAMETER Online
    Tells Dashboard to use CSS/JS from CDN instead of local files.
 
    .PARAMETER PassThru
    Returns the object that was generated by the script.
 
    .PARAMETER LogPath
    Path to log file where all actions will be logged
 
    .PARAMETER LogMaximum
    Maximum number of log files to keep. If 0 then unlimited. Default unlimited.
    Please keep in mind that this will only work if the logs are in the dedicated folder.
    If you use the same folder as the script, then logging deletion will be disabled.
 
    .PARAMETER LogShowTime
    Show time in console output. Default $false. Logs will always have time.
 
    .PARAMETER LogTimeFormat
    Time format to use in log file. Default "yyyy-MM-dd HH:mm:ss"
 
    .PARAMETER Force
    Forces the script to regenerate existing files regardless if they need to be updated or not.
 
    .EXAMPLE
    An example
 
    .NOTES
    General notes
    #>

    [cmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Position = 0)][ScriptBlock] $Elements,
        [Parameter(Position = 1, Mandatory)][alias('FilePath')][string] $HTMLPath,
        [string] $StatisticsPath,
        [string] $Logo,
        [System.Collections.IDictionary] $Folders,
        [System.Collections.IDictionary] $Replacements,
        [switch] $RemoveNotIncluded,
        [ValidateSet("RemoveItem", "DotNetDelete", "RecycleBin")]
        [string] $DeleteMethod = "RecycleBin",
        [Uri] $UrlPath,
        [switch] $ShowHTML,
        [switch] $Online,
        [switch] $PassThru,
        [switch] $TestMode,
        [string] $LogPath,
        [int] $LogMaximum,
        [switch] $LogShowTime,
        [string] $LogTimeFormat,
        [switch] $Force
    )
    $Script:Reporting = [ordered] @{}
    $Script:Reporting['Version'] = Get-GitHubVersion -Cmdlet 'Start-TheDashboard' -RepositoryOwner 'evotecit' -RepositoryName 'TheDashboard'

    Set-LoggingCapabilities -LogPath $LogPath -LogMaximum $LogMaximum -ShowTime:$LogShowTime -TimeFormat $LogTimeFormat -ScriptPath $MyInvocation.ScriptName -ParameterPSDefaultParameterValues $Script:PSDefaultParameterValues

    Write-Color '[i]', "[TheDashboard] ", 'Version', ' [Informative] ', $Script:Reporting['Version'] -Color Yellow, DarkGray, Yellow, DarkGray, Magenta

    if ($TestMode) {
        Write-Color -Text '[i]', "[TheDashboard] ", 'TestMode enabled', ' [Informative] ' -Color Yellow, DarkGray, Yellow, DarkGray, Magenta
        $WhatIfPreference = $true
    }
    if (-not $Folders) {
        $Folders = [ordered] @{}
    }
    $GageConfiguration = [System.Collections.Generic.List[System.Collections.IDictionary]]::new()
    $FoldersConfiguration = [System.Collections.Generic.List[System.Collections.IDictionary]]::new()
    $ReplacementConfiguration = [System.Collections.Generic.List[System.Collections.IDictionary]]::new()
    $FolderLimit = [ordered] @{}

    $convertNestedElementsSplat = @{
        Elements                 = $Elements
        GageConfiguration        = $GageConfiguration
        FoldersConfiguration     = $FoldersConfiguration
        ReplacementConfiguration = $ReplacementConfiguration
        FolderLimit              = $FolderLimit
    }

    Convert-NestedElements @convertNestedElementsSplat

    $Extension = [io.path]::GetExtension($HTMLPath)
    $FolderPath = [io.path]::GetDirectoryName($HTMLPath)

    $ExportData = Import-DashboardStatistics -StatisticsPath $StatisticsPath

    $Replacements = Convert-MultipleReplacements -Replacements $Replacements -ReplacementConfiguration $ReplacementConfiguration

    Set-FolderConfiguration -Folders $Folders -FoldersConfiguration $FoldersConfiguration -FolderLimit $FolderLimit

    Copy-HTMLFiles -Folders $Folders -Extension $Extension

    $Files = Convert-FilesToMenuData -Folders $Folders -Replacements $Replacements -Extension $Extension

    $MenuBuilder = Convert-FilesToMenu -Files $Files -Folders $Folders -ExportData $ExportData -Force:$Force.IsPresent

    $FilePathsGenerated = New-HTMLReport -OutputElements $GageConfiguration -Logo $Logo -MenuBuilder $MenuBuilder -Configuration $Configuration -ExportData $ExportData -Files $Files -ShowHTML:$ShowHTML.IsPresent -HTMLPath $HTMLPath -Online:$Online.IsPresent -Force:$Force.IsPresent -Extension $Extension -UrlPath $UrlPath -Pretend:$TestMode.IsPresent
    Remove-DiscardedReports -FilePathsGenerated $FilePathsGenerated -FolderPath $FolderPath -Extension $Extension

    $FilesToKeepOrRemove = Convert-FilesToKeepOrRemove -FilePathsGenerated $FilePathsGenerated -Files $Files -DeleteMethod $DeleteMethod -RemoveNotIncluded:$RemoveNotIncluded.IsPresent -WhatIf:$WhatIfPreference

    if ($PassThru) {
        [ordered] @{
            FilesToKeepOrRemove = $FilesToKeepOrRemove
            Folders             = $Folders
            Extension           = $Extension
            Files               = $Files
            MenuBuilder         = $MenuBuilder
            Statistics          = $ExportData.Statistics
            StatisticsPath      = $StatisticsPath
            Replacements        = $Replacements
            FilePathsGenerated  = $FilePathsGenerated
        }
    }

    $ExportData['MenuBuilder'] = $MenuBuilder

    if ($StatisticsPath) {
        try {
            $ExportData | Export-Clixml -Depth 5 -LiteralPath $StatisticsPath -ErrorAction Stop -WhatIf:$false
        } catch {
            Write-Color -Text '[e]', "[TheDashboard] ", 'Failed to export statistics', ' [Error] ', $_.Exception.Message -Color Yellow, DarkGray, Yellow, DarkGray, Red
        }
    }
    Write-Color -Text '[i]', "[TheDashboard] ", 'Done', ' [Informative] ' -Color Yellow, DarkGray, Yellow, DarkGray, Magenta
}

Export-ModuleMember -Function @('New-DashboardEmailParameter', 'New-DashboardFolder', 'New-DashboardGage', 'New-DashboardLimit', 'New-DashboardNotification', 'New-DashboardReplacement', 'Repair-DashboardContent', 'Repair-DashboardExtension', 'Start-TheDashboard') -Alias @('New-TheDashboardFolder', 'New-TheDashboardGage', 'New-TheDashboardReplacement')
# SIG # Begin signature block
# MIItqwYJKoZIhvcNAQcCoIItnDCCLZgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCwyeM/1gpphIBM
# tlb/PXrp9iJftzbsyV3Vj1QwBVyhLaCCJq4wggWNMIIEdaADAgECAhAOmxiO+dAt
# 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa
# Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD
# ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E
# MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy
# unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF
# xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1
# 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB
# MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR
# WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6
# nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB
# YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S
# UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x
# q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB
# NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP
# TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC
# AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
# Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0
# aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB
# LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc
# Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov
# Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy
# oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW
# juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF
# mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z
# twGpn1eqXijiuZQwggWQMIIDeKADAgECAhAFmxtXno4hMuI5B72nd3VcMA0GCSqG
# SIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy
# dXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGIx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH
# NDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIw
# aTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLK
# EdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4Tm
# dDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembu
# d8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnD
# eMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1
# XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVld
# QnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTS
# YW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSm
# M9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzT
# QRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kx
# fgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
# VR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzANBgkq
# hkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNkaA9Wz3eucPn9mkqZucl4
# XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjSPMFDQK4dUPVS/JA7u5iZ
# aWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK7VB6fWIhCoDIc2bRoAVg
# X+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eBcg3AFDLvMFkuruBx8lbk
# apdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp5aPNoiBB19GcZNnqJqGL
# FNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msgdDDS4Dk0EIUhFQEI6FUy
# 3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vriRbgjU2wGb2dVf0a1TD9u
# KFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ79ARj6e/CVABRoIoqyc54
# zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5nLGbsQAe79APT0JsyQq8
# 7kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3i0objwG2J5VT6LaJbVu8
# aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0HEEcRrYc9B9F1vM/zZn4w
# ggauMIIElqADAgECAhAHNje3JFR82Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH
# NDAeFw0yMjAzMjMwMDAwMDBaFw0zNzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVT
# MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1
# c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqG
# SIb3DQEBAQUAA4ICDwAwggIKAoICAQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbS
# g9GeTKJtoLDMg/la9hGhRBVCX6SI82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9
# /UO0hNoR8XOxs+4rgISKIhjf69o9xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXn
# HwZljZQp09nsad/ZkIdGAHvbREGJ3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0
# VAshaG43IbtArF+y3kp9zvU5EmfvDqVjbOSmxR3NNg1c1eYbqMFkdECnwHLFuk4f
# sbVYTXn+149zk6wsOeKlSNbwsDETqVcplicu9Yemj052FVUmcJgmf6AaRyBD40Nj
# gHt1biclkJg6OBGz9vae5jtb7IHeIhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0
# QCirc0PO30qhHGs4xSnzyqqWc0Jon7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvv
# mz3+DrhkKvp1KCRB7UK/BZxmSVJQ9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T
# /jnA+bIwpUzX6ZhKWD7TA4j+s4/TXkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk
# 42PgpuE+9sJ0sj8eCXbsq11GdeJgo1gJASgADoRU7s7pXcheMBK9Rp6103a50g5r
# mQzSM7TNsQIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4E
# FgQUuhbZbU2FL3MpdpovdYxqII+eyG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5n
# P+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcG
# CCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQu
# Y29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGln
# aUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8v
# Y3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNV
# HSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIB
# AH1ZjsCTtm+YqUQiAX5m1tghQuGwGC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxp
# wc8dB+k+YMjYC+VcW9dth/qEICU0MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIl
# zpVpP0d3+3J0FNf/q0+KLHqrhc1DX+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQ
# cAp876i8dU+6WvepELJd6f8oVInw1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfe
# Kuv2nrF5mYGjVoarCkXJ38SNoOeY+/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+j
# Sbl3ZpHxcpzpSwJSpzd+k1OsOx0ISQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJsh
# IUDQtxMkzdwdeDrknq3lNHGS1yZr5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6
# OOmc4d0j/R0o08f56PGYX/sr2H7yRp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDw
# N7+YAN8gFk8n+2BnFqFmut1VwDophrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR
# 81fZvAT6gt4y3wSJ8ADNXcL50CN/AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2
# VVQrH4D6wPIOK+XW+6kvRBVK5xMOHds3OBqhK/bt1nz8MIIGsDCCBJigAwIBAgIQ
# CK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQGEwJVUzEV
# MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t
# MSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjEwNDI5MDAw
# MDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln
# aUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBT
# aWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMIICIjANBgkqhkiG9w0BAQEF
# AAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5WRuxiEL1M4zrPYGXcMW7xIUmMJ+k
# jmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJPDqFX/IiZwZHMgQM+TXAkZLON4gh9
# NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXzENOLsvsI8IrgnQnAZaf6mIBJNYc9
# URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bqHPNlaJGiTUyCEUhSaN4QvRRXXegY
# E2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTCfMjqGzLmysL0p6MDDnSlrzm2q2AS
# 4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaDG7dqZy3SvUQakhCBj7A7CdfHmzJa
# wv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urOkfW+0/tvk2E0XLyTRSiDNipmKF+w
# c86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7ADK5GyNnm+960IHnWmZcy740hQ83eR
# Gv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4R+Z1MI3sMJN2FKZbS110YU0/EpF2
# 3r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlNWdt4z4FKPkBHX8mBUHOFECMhWWCK
# ZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0IU0F8WD1Hs/q27IwyCQLMbDwMVhEC
# AwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFGg34Ou2
# O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9P
# MA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDAzB3BggrBgEFBQcB
# AQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggr
# BgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
# c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwHAYDVR0gBBUwEzAH
# BgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcNAQEMBQADggIBADojRD2NCHbuj7w6
# mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcTEp6QRJ9L/Z6jfCbVN7w6XUhtldU/
# SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WTauPrINHVUHmImoqKwba9oUgYftzY
# gBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9ntSZz0rdKOtfJqGVWEjVGv7XJz/9
# kNF2ht0csGBc8w2o7uCJob054ThO2m67Np375SFTWsPK6Wrxoj7bQ7gzyE84FJKZ
# 9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0HKKlS43Nb3Y3LIU/Gs4m6Ri+kAew
# Q3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL6TEa/y4ZXDlx4b6cpwoG1iZnt5Lm
# Tl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+16oh7cGvmoLr9Oj9FpsToFpFSi0HA
# SIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8M4+uKIw8y4+ICw2/O/TOHnuO77Xr
# y7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrFhsP2JjMMB0ug0wcCampAMEhLNKhR
# ILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy1lKQ/a+FSCH5Vzu0nAPthkX0tGFu
# v2jiJmCG6sivqf6UHedjGzqGVnhOMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnp
# BOMzBDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln
# aUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5
# NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEy
# NTIzNTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYD
# VQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQAD
# ggIPADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBD
# Er4IxHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo
# 76EO7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rO
# H3bpLEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9R
# eNZ8hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgX
# j3o5WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTV
# DSupWJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16J
# idj5XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/C
# acBqU0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93N
# Rxvd1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1X
# CB+1rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMB
# AAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUB
# Af8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1s
# BwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9X
# LAN3DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwz
# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
# U3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhho
# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNl
# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZU
# aW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQS
# R9lDkfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWB
# b0HvqT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDC
# zFzUy34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1
# UruJKlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3Wp
# ByXtgVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGE
# sshJmLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8
# a1u7cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNF
# YagLDBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7Q
# EY7MhKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgE
# deoHNHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/J
# ceENc2Sg8h3KeFUCS7tpFk7CrDqkMIIHXzCCBUegAwIBAgIQB8JSdCgUotar/iTq
# F+XdLjANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln
# aUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBT
# aWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMB4XDTIzMDQxNjAwMDAwMFoX
# DTI2MDcwNjIzNTk1OVowZzELMAkGA1UEBhMCUEwxEjAQBgNVBAcMCU1pa2/FgsOz
# dzEhMB8GA1UECgwYUHJ6ZW15c8WCYXcgS8WCeXMgRVZPVEVDMSEwHwYDVQQDDBhQ
# cnplbXlzxYJhdyBLxYJ5cyBFVk9URUMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
# ggIKAoICAQCUmgeXMQtIaKaSkKvbAt8GFZJ1ywOH8SwxlTus4McyrWmVOrRBVRQA
# 8ApF9FaeobwmkZxvkxQTFLHKm+8knwomEUslca8CqSOI0YwELv5EwTVEh0C/Daeh
# vxo6tkmNPF9/SP1KC3c0l1vO+M7vdNVGKQIQrhxq7EG0iezBZOAiukNdGVXRYOLn
# 47V3qL5PwG/ou2alJ/vifIDad81qFb+QkUh02Jo24SMjWdKDytdrMXi0235CN4Rr
# W+8gjfRJ+fKKjgMImbuceCsi9Iv1a66bUc9anAemObT4mF5U/yQBgAuAo3+jVB8w
# iUd87kUQO0zJCF8vq2YrVOz8OJmMX8ggIsEEUZ3CZKD0hVc3dm7cWSAw8/FNzGNP
# lAaIxzXX9qeD0EgaCLRkItA3t3eQW+IAXyS/9ZnnpFUoDvQGbK+Q4/bP0ib98XLf
# QpxVGRu0cCV0Ng77DIkRF+IyR1PcwVAq+OzVU3vKeo25v/rntiXCmCxiW4oHYO28
# eSQ/eIAcnii+3uKDNZrI15P7VxDrkUIc6FtiSvOhwc3AzY+vEfivUkFKRqwvSSr4
# fCrrkk7z2Qe72Zwlw2EDRVHyy0fUVGO9QMuh6E3RwnJL96ip0alcmhKABGoIqSW0
# 5nXdCUbkXmhPCTT5naQDuZ1UkAXbZPShKjbPwzdXP2b8I9nQ89VSgQIDAQABo4IC
# AzCCAf8wHwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYE
# FHrxaiVZuDJxxEk15bLoMuFI5233MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAK
# BggrBgEFBQcDAzCBtQYDVR0fBIGtMIGqMFOgUaBPhk1odHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEz
# ODQyMDIxQ0ExLmNybDBToFGgT4ZNaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0Rp
# Z2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5j
# cmwwPgYDVR0gBDcwNTAzBgZngQwBBAEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3
# dy5kaWdpY2VydC5jb20vQ1BTMIGUBggrBgEFBQcBAQSBhzCBhDAkBggrBgEFBQcw
# AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUFBzAChlBodHRwOi8v
# Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAAMA0GCSqGSIb3DQEB
# CwUAA4ICAQC3EeHXUPhpe31K2DL43Hfh6qkvBHyR1RlD9lVIklcRCR50ZHzoWs6E
# BlTFyohvkpclVCuRdQW33tS6vtKPOucpDDv4wsA+6zkJYI8fHouW6Tqa1W47YSrc
# 5AOShIcJ9+NpNbKNGih3doSlcio2mUKCX5I/ZrzJBkQpJ0kYha/pUST2CbE3JroJ
# f2vQWGUiI+J3LdiPNHmhO1l+zaQkSxv0cVDETMfQGZKKRVESZ6Fg61b0djvQSx51
# 0MdbxtKMjvS3ZtAytqnQHk1ipP+Rg+M5lFHrSkUlnpGa+f3nuQhxDb7N9E8hUVev
# xALTrFifg8zhslVRH5/Df/CxlMKXC7op30/AyQsOQxHW1uNx3tG1DMgizpwBasrx
# h6wa7iaA+Lp07q1I92eLhrYbtw3xC2vNIGdMdN7nd76yMIjdYnAn7r38wwtaJ3KY
# D0QTl77EB8u/5cCs3ShZdDdyg4K7NoJl8iEHrbqtooAHOMLiJpiL2i9Yn8kQMB6/
# Q6RMO3IUPLuycB9o6DNiwQHf6Jt5oW7P09k5NxxBEmksxwNbmZvNQ65Zn3exUAKq
# G+x31Egz5IZ4U/jPzRalElEIpS0rgrVg8R8pEOhd95mEzp5WERKFyXhe6nB6bSYH
# v8clLAV0iMku308rpfjMiQkqS3LLzfUJ5OHqtKKQNMLxz9z185UCszGCBlMwggZP
# AgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEw
# PwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2
# IFNIQTM4NCAyMDIxIENBMQIQB8JSdCgUotar/iTqF+XdLjANBglghkgBZQMEAgEF
# AKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgor
# BgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3
# DQEJBDEiBCAqSBIzJlo55+qsO5oHOvsaCJGAAwqjW2JtLkvxOszvJzANBgkqhkiG
# 9w0BAQEFAASCAgA76s96LwKGmkC31aCf3IzJ7pW4ZCzxxNugNlWZWG3L23u0OOe4
# uWnRE3xTi69yQclcOIobBgLxSeA7fwBanIc+p5vWl+CJSA4pvk4oEyWFv1enWDNy
# /nkV65TFTLR+LFWgnrT49fgiGlw3OQlKDNgj5klykH4npyZy7/yZgDOPGa4PhtCI
# y0b/8noAiJn821IMX26yq+dfxmqbukVX2lQ3irFiUBrGM6jqSPIl0M3B5kr46JvL
# uQgKJw3rH5Spedvq9RoQeUb0EWFMWA3D8ApM0ZuzRbAfspSoln2i9EvNVgMyGuW3
# 7d8VDUGDDDckf3VslmD4kkIZACw3MneX+m/pOut1f1dJ/j0EI94jZcpYisOOPTIR
# JtAHKqSOltF7RXfo2pSZCd+EqaonkTpTIP7Fn8L4Ia1uy5+ZE7skaFiX7ohJ3Xdm
# 4VIFKYo86GaFDgmMHIe75WdJ2/cUunNWhJNezStLEq+c0+Nu+sw9KY9QiwXnnDuz
# DnjXzV3htgzE+mEJPQ3vLelkzl8BMSuL9OwQLW8l3CYQqKN9IMvOk7SWcnoxCpib
# TDbRmEMWNjDAdkx050QodGO7YwUc65fmD448umVXU8jGbzzWtir4/2T3FuTLPk2c
# zdGcz1zhoQ9HTQLft9XlAXytzEySLMOSoru/R4gJtpGrJo8n8ipkwMjWXqGCAyAw
# ggMcBgkqhkiG9w0BCQYxggMNMIIDCQIBATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYD
# VQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBH
# NCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTj
# MwQwDQYJYIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwG
# CSqGSIb3DQEJBTEPFw0yNTAzMTcwODI5MjhaMC8GCSqGSIb3DQEJBDEiBCCoc2Uq
# xIkcZiNu2ldiziKzypOHJptjD9kirGnpJxbxxzANBgkqhkiG9w0BAQEFAASCAgC2
# vfQRfx0E1qcHrhkH3GCKeold1M+INhrDirJgbloMpehFvdY8wKC5Gk0YWom7USTV
# vH9uDGg92THKE05vounVpBjX1oNXmlYxxhnK3+qRkToE2lAKuyXj38nkYPbi3ZOv
# Xw6nPuQANL5UkvoS1VkUwLJ8XAP53vwK49O9Ts9vNSyfl5mpb1KexU/+g4ic0Ibu
# wkKD1l8gdpAITdEPmP4Jvn7tNGpHBbHUSVgewW6CMZ13FLLWfc0CBRJID33dw4tN
# bimz/pqRAqGzvG94dVwaofyq4G7giRaI0G+DvRgB2nrHr+s7T3jRNMePe4dvrU4N
# FPoCQu/N1kv08j36dTITsNT39YHAqU1lrpKp80OtcUwyZTTOKY8n7mOQoa2A7ZQe
# zqvp8I/dnLj6Kyw/x9IxKfVroMzoha4POsRuxzpJF9K+bDCP7E2QJcLDo6pJaBv1
# RBOrvngUmdq04D93KRmF9S2dM0ucHStZ5XbR0t9CmkFzFwAAd3AKSJZEZW5CUT8l
# vVac7FzAl9e6lr5jrmKbswKYYPdguIyaX2cDr8tfHzAvEtWUnia8tZ480Gr0YoEi
# daiGDnoHfNhvobLhtWe5LtVHv/AA0GJWod8ImsJo+2doQvZNOfsxnFwgNW45Eywh
# lVJ0acWxpaKBoA8dX+nkpOjZODz3huwlA6JrtT/Rkg==
# SIG # End signature block