PSWriteHTML.psm1
function Add-CustomFormatForDatetimeSorting { <# .SYNOPSIS .DESCRIPTION This function adds code to make the datatable columns sortable with different datetime formats. Formatting: Day (of Month) D - 1 2 ... 30 31 Do - 1st 2nd ... 30th 31st DD - 01 02 ... 30 31 Month M - 1 2 ... 11 12 Mo - 1st 2nd ... 11th 12th MM - 01 02 ... 11 12 MMM - Jan Feb ... Nov Dec MMMM - January February ... November December Year YY - 70 71 ... 29 30 YYYY - 1970 1971 ... 2029 2030 Hour H - 0 1 ... 22 23 HH - 00 01 ... 22 23 h - 1 2 ... 11 12 hh - 01 02 ... 11 12 Minute m - 0 1 ... 58 59 mm - 00 01 ... 58 59 Second s - 0 1 ... 58 59 ss - 00 01 ... 58 59 More formats http://momentjs.com/docs/#/displaying/ .PARAMETER CustomDateTimeFormat Array with strings of custom datetime format. The string is build from two parts. Format and locale. Locale is optional. format explanation: http://momentjs.com/docs/#/displaying/ locale explanation: http://momentjs.com/docs/#/i18n/ .LINK format explanation: http://momentjs.com/docs/#/displaying/ locale explanation: http://momentjs.com/docs/#/i18n/ .Example Add-CustomFormatForDatetimeSorting -CustomDateFormat 'dddd, MMMM Do, YYYY','HH:mm MMM D, YY' .Example Add-CustomFormatForDatetimeSorting -CustomDateFormat 'DD.MM.YYYY HH:mm:ss' #> [CmdletBinding()] param( [array]$DateTimeSortingFormat ) if ($Script:Output) { Remove-Variable Output -Scope Script } if ($DateTimeSortingFormat) { [array]$Script:Output = foreach ($format in $DateTimeSortingFormat) { "$.fn.dataTable.moment( '$format' );" } } else { # Default localized format $Script:Output = "$.fn.dataTable.moment( 'L' );" } return $Script:Output } function Add-TableContent { [CmdletBinding()] param( [System.Collections.Generic.List[PSCustomObject]] $ContentRows, [System.Collections.Generic.List[PSCUstomObject]] $ContentStyle, [System.Collections.Generic.List[PSCUstomObject]] $ContentTop, [System.Collections.Generic.List[PSCUstomObject]] $ContentFormattingInline, [string[]] $HeaderNames, [Array] $Table ) # This converts inline conditonal formatting into style. It's intensive because it actually scans whole Table # During scan it tries to match things and when it finds a match it prepares it for ContentStyling feature if ($ContentFormattingInline.Count -gt 0) { [Array] $AddStyles = for ($RowCount = 1; $RowCount -lt $Table.Count; $RowCount++) { [string[]] $RowData = $Table[$RowCount] -replace '</td></tr>' -replace '<tr><td>' -split '</td><td>' for ($ColumnCount = 0; $ColumnCount -lt $RowData.Count; $ColumnCount++) { foreach ($ConditionalFormatting in $ContentFormattingInline) { $ColumnIndexHeader = [array]::indexof($HeaderNames.ToUpper(), $($ConditionalFormatting.Name).ToUpper()) if ($ColumnIndexHeader -eq $ColumnCount) { if ($ConditionalFormatting.Type -eq 'number' -or $ConditionalFormatting.Type -eq 'decimal') { [decimal] $returnedValueLeft = 0 [bool]$resultLeft = [int]::TryParse($RowData[$ColumnCount], [ref]$returnedValueLeft) [decimal]$returnedValueRight = 0 [bool]$resultRight = [int]::TryParse($ConditionalFormatting.Value, [ref]$returnedValueRight) if ($resultLeft -and $resultRight) { $SideLeft = $returnedValueLeft $SideRight = $returnedValueRight } else { $SideLeft = $RowData[$ColumnCount] $SideRight = $ConditionalFormatting.Value } } elseif ($ConditionalFormatting.Type -eq 'int') { # Leaving this in althought only NUMBER is used. [int] $returnedValueLeft = 0 [bool]$resultLeft = [int]::TryParse($RowData[$ColumnCount], [ref]$returnedValueLeft) [int]$returnedValueRight = 0 [bool]$resultRight = [int]::TryParse($ConditionalFormatting.Value, [ref]$returnedValueRight) if ($resultLeft -and $resultRight) { $SideLeft = $returnedValueLeft $SideRight = $returnedValueRight } else { $SideLeft = $RowData[$ColumnCount] $SideRight = $ConditionalFormatting.Value } } else { $SideLeft = $RowData[$ColumnCount] $SideRight = $ConditionalFormatting.Value } if ($ConditionalFormatting.Operator -eq 'gt') { $Pass = $SideLeft -gt $SideRight } elseif ($ConditionalFormatting.Operator -eq 'lt') { $Pass = $SideLeft -lt $SideRight } elseif ($ConditionalFormatting.Operator -eq 'eq') { $Pass = $SideLeft -eq $SideRight } elseif ($ConditionalFormatting.Operator -eq 'le') { $Pass = $SideLeft -le $SideRight } elseif ($ConditionalFormatting.Operator -eq 'ge') { $Pass = $SideLeft -ge $SideRight } elseif ($ConditionalFormatting.Operator -eq 'ne') { $Pass = $SideLeft -ne $SideRight } elseif ($ConditionalFormatting.Operator -eq 'like') { $Pass = $SideLeft -like $SideRight } elseif ($ConditionalFormatting.Operator -eq 'contains') { $Pass = $SideLeft -contains $SideRight } # This is generally risky, alternative way to do it, so doing above instead # if (Invoke-Expression -Command "`"$($RowData[$ColumnCount])`" -$($ConditionalFormatting.Operator) `"$($ConditionalFormatting.Value)`"") { if ($Pass) { # if ($RowData[$ColumnCount] -eq $ConditionalFormatting.Value) { # If we want to make conditional formatting for for row it requires a bit diff approach if ($ConditionalFormatting.Row) { for ($i = 0; $i -lt $RowData.Count; $i++) { [PSCustomObject]@{ RowIndex = $RowCount ColumnIndex = ($i + 1) # Since it's 0 based index and we count from 1 we need to add 1 Style = $ConditionalFormatting.Style } } } else { [PSCustomObject]@{ RowIndex = $RowCount ColumnIndex = ($ColumnIndexHeader + 1) # Since it's 0 based index and we count from 1 we need to add 1 Style = $ConditionalFormatting.Style } } } } } } } # This makes conditional forwarding a ContentStyle foreach ($Style in $AddStyles) { $ContentStyle.Add($Style) } } # Prepopulate hashtable with rows $TableRows = @{ } if ($ContentStyle) { for ($RowIndex = 0; $RowIndex -lt $Table.Count; $RowIndex++) { $TableRows[$RowIndex] = @{ } } } # Find rows in hashtable and add column to it foreach ($Content in $ContentStyle) { if ($Content.RowIndex -and $Content.ColumnIndex) { # ROWINDEX and COLUMNINDEX - ARRAYS # This takes care of Content by Column Nr foreach ($ColumnIndex in $Content.ColumnIndex) { # Column Index given by user is from 1 to infinity, Column Index is counted from 0 # We need to address this by doing - 1 foreach ($RowIndex in $Content.RowIndex) { $TableRows[$RowIndex][$ColumnIndex - 1] = @{ Style = $Content.Style } if ($Content.Text) { if ($Content.Used) { $TableRows[$RowIndex][$ColumnIndex - 1]['Text'] = '' $TableRows[$RowIndex][$ColumnIndex - 1]['Remove'] = $true } else { $TableRows[$RowIndex][$ColumnIndex - 1]['Text'] = $Content.Text $TableRows[$RowIndex][$ColumnIndex - 1]['Remove'] = $false $TableRows[$RowIndex][$ColumnIndex - 1]['ColSpan'] = $($Content.ColumnIndex).Count $TableRows[$RowIndex][$ColumnIndex - 1]['RowSpan'] = $($Content.RowIndex).Count $Content.Used = $true } } } } } elseif ($Content.RowIndex -and $Content.Name) { # ROWINDEX AND COLUMN NAMES - ARRAYS # This takes care of Content by Column Names (Header Names) foreach ($ColumnName in $Content.Name) { $ColumnIndex = ([array]::indexof($HeaderNames.ToUpper(), $ColumnName.ToUpper())) foreach ($RowIndex in $Content.RowIndex) { $TableRows[$RowIndex][$ColumnIndex] = @{ Style = $Content.Style } if ($Content.Text) { if ($Content.Used) { $TableRows[$RowIndex][$ColumnIndex]['Text'] = '' $TableRows[$RowIndex][$ColumnIndex]['Remove'] = $true } else { $TableRows[$RowIndex][$ColumnIndex]['Text'] = $Content.Text $TableRows[$RowIndex][$ColumnIndex]['Remove'] = $false $TableRows[$RowIndex][$ColumnIndex]['ColSpan'] = $($Content.ColumnIndex).Count $TableRows[$RowIndex][$ColumnIndex]['RowSpan'] = $($Content.RowIndex).Count $Content.Used = $true } } } } } elseif ($Content.RowIndex -and (-not $Content.ColumnIndex -and -not $Content.Name)) { # Just ROW INDEX for ($ColumnIndex = 0; $ColumnIndex -lt $HeaderNames.Count; $ColumnIndex++) { foreach ($RowIndex in $Content.RowIndex) { $TableRows[$RowIndex][$ColumnIndex] = @{ Style = $Content.Style } } } } elseif (-not $Content.RowIndex -and ($Content.ColumnIndex -or $Content.Name)) { # JUST COLUMNINDEX or COLUMNNAMES for ($RowIndex = 1; $RowIndex -lt $($Table.Count); $RowIndex++) { if ($Content.ColumnIndex) { # JUST COLUMN INDEX foreach ($ColumnIndex in $Content.ColumnIndex) { $TableRows[$RowIndex][$ColumnIndex - 1] = @{ Style = $Content.Style } } } else { # JUST COLUMN NAMES foreach ($ColumnName in $Content.Name) { $ColumnIndex = [array]::indexof($HeaderNames.ToUpper(), $ColumnName.ToUpper()) $TableRows[$RowIndex][$ColumnIndex] = @{ Style = $Content.Style } } } } } } # Row 0 = Table Header # This builds table from scratch, skipping rows untouched by styling [Array] $NewTable = for ($RowCount = 0; $RowCount -lt $Table.Count; $RowCount++) { # No conditional formatting we can process just styling since we don't need values # We have column index and row index and that's enough # In case of conditional formatting it's different as it works on values if ($TableRows[$RowCount]) { [string[]] $RowData = $Table[$RowCount] -replace '</td></tr>' -replace '<tr><td>' -split '</td><td>' New-HTMLTag -Tag 'tr' { for ($ColumnCount = 0; $ColumnCount -lt $RowData.Count; $ColumnCount++) { if ($TableRows[$RowCount][$ColumnCount]) { if (-not $TableRows[$RowCount][$ColumnCount]['Remove']) { if ($TableRows[$RowCount][$ColumnCount]['Text']) { New-HTMLTag -Tag 'td' -Value { $TableRows[$RowCount][$ColumnCount]['Text'] } -Attributes @{ style = $TableRows[$RowCount][$ColumnCount]['Style'] colspan = if ($TableRows[$RowCount][$ColumnCount]['ColSpan'] -gt 1) { $TableRows[$RowCount][$ColumnCount]['ColSpan'] } else { } rowspan = if ($TableRows[$RowCount][$ColumnCount]['RowSpan'] -gt 1) { $TableRows[$RowCount][$ColumnCount]['RowSpan'] } else { } } # Version 1 - Alternative version to workaround DataTables.NET # New-HTMLTag -Tag 'td' -Value { $TableRows[$RowCount][$ColumnCount]['Text'] } -Attributes @{ # style = $TableRows[$RowCount][$ColumnCount]['Style'] # } } else { New-HTMLTag -Tag 'td' -Value { $RowData[$ColumnCount] } -Attributes @{ style = $TableRows[$RowCount][$ColumnCount]['Style'] } } } else { # RowSpan/ColSpan doesn't work in DataTables.net for content. # This means that this functionality is only good for Non-JS. # Normally you would just remove TD/TD and everything shopuld work # And it does work but only for NON-JS solution # Version 1 # Alternative Approach - this assumes the text will be zeroed # From visibility side it will look like an empty cells # However content will be stored only in first cell. # requires removal of colspan/rowspan # New-HTMLTag -Tag 'td' -Value { '' } -Attributes @{ # style = $TableRows[$RowCount][$ColumnCount]['Style'] # } # Version 2 # Below code was suggested as a workaround - it doesn't wrok # New-HTMLTag -Tag 'td' -Value { } -Attributes @{ # style = "display: none;" # } } } else { New-HTMLTag -Tag 'td' -Value { $RowData[$ColumnCount] } } } } } else { $Table[$RowCount] } } $NewTable } function Add-TableFiltering { [CmdletBinding()] param( [bool] $Filtering, [ValidateSet('Top', 'Bottom', 'Both')][string]$FilteringLocation = 'Bottom', [string] $DataTableName ) $Output = @{ } if ($Filtering) { # https://datatables.net/examples/api/multi_filter.html if ($FilteringLocation -eq 'Bottom') { $Output.FilteringTopCode = @" // Setup - add a text input to each footer cell `$('#$DataTableName tfoot th').each(function () { var title = `$(this).text(); `$(this).html('<input type="text" placeholder="' + title + '" />'); }); "@ $Output.FilteringBottomCode = @" // Apply the search for footer cells table.columns().every(function () { var that = this; `$('input', this.footer()).on('keyup change', function () { if (that.search() !== this.value) { that.search(this.value).draw(); } }); }); "@ } elseif ($FilteringLocation -eq 'Both') { $Output.FilteringTopCode = @" // Setup - add a text input to each header cell `$('#$DataTableName thead th').each(function () { var title = `$(this).text(); `$(this).html('<input type="text" placeholder="' + title + '" />'); }); // Setup - add a text input to each footer cell `$('#$DataTableName tfoot th').each(function () { var title = `$(this).text(); `$(this).html('<input type="text" placeholder="' + title + '" />'); }); "@ $Output.FilteringBottomCode = @" // Apply the search for header cells table.columns().eq(0).each(function (colIdx) { `$('input', table.column(colIdx).header()).on('keyup change', function () { table .column(colIdx) .search(this.value) .draw(); }); `$('input', table.column(colIdx).header()).on('click', function (e) { e.stopPropagation(); }); }); // Apply the search for footer cells table.columns().every(function () { var that = this; `$('input', this.footer()).on('keyup change', function () { if (that.search() !== this.value) { that.search(this.value).draw(); } }); }); "@ } else { # top headers $Output.FilteringTopCode = @" // Setup - add a text input to each header cell `$('#$DataTableName thead th').each(function () { var title = `$(this).text(); `$(this).html('<input type="text" placeholder="' + title + '" />'); }); "@ $Output.FilteringBottomCode = @" // Apply the search for header cells table.columns().eq(0).each(function (colIdx) { `$('input', table.column(colIdx).header()).on('keyup change', function () { table .column(colIdx) .search(this.value) .draw(); }); `$('input', table.column(colIdx).header()).on('click', function (e) { e.stopPropagation(); }); }); "@ } } else { $Output.FilteringTopCode = $Output.FilteringBottomCode = '' # assign multiple same values trick } return $Output } function Add-TableHeader { [CmdletBinding()] param( [System.Collections.Generic.List[PSCustomObject]] $HeaderRows, [System.Collections.Generic.List[PSCUstomObject]] $HeaderStyle, [System.Collections.Generic.List[PSCUstomObject]] $HeaderTop, [System.Collections.Generic.List[PSCUstomObject]] $HeaderResponsiveOperations, [string[]] $HeaderNames ) if ($HeaderRows.Count -eq 0 -and $HeaderStyle.Count -eq 0 -and $HeaderTop.Count -eq 0 -and $HeaderResponsiveOperations.Count -eq 0) { return } # Prepares for styles to merged headers [Array] $MergeColumns = foreach ($Row in $HeaderRows) { $Index = foreach ($R in $Row.Names) { [array]::indexof($HeaderNames.ToUpper(), $R.ToUpper()) } if ($Index -contains -1) { Write-Warning -Message "Table Header can't be processed properly. Names on the list to merge were not found in Table Header." } else { @{ Index = $Index Title = $Row.Title Count = $Index.Count Style = $Row.Style Used = $false } } } $ResponsiveOperations = @{ } foreach ($Row in $HeaderResponsiveOperations) { foreach ($_ in $Row.Names) { $Index = [array]::indexof($HeaderNames.ToUpper(), $_.ToUpper()) $ResponsiveOperations[$Index] = @{ Index = $Index ResponsiveOperations = $Row.ResponsiveOperations Used = $false } } } # Prepares for styles to standard header rows $Styles = @{ } foreach ($Row in $HeaderStyle) { foreach ($_ in $Row.Names) { $Index = [array]::indexof($HeaderNames.ToUpper(), $_.ToUpper()) $Styles[$Index] = @{ Index = $Index Title = $Row.Title Count = $Index.Count Style = $Row.Style Used = $false } } } if ($HeaderTop.Count -gt 0) { $UsedColumns = 0 $ColumnsTotal = $HeaderNames.Count $TopHeader = New-HTMLTag -Tag 'tr' { foreach ($_ in $HeaderTop) { if ($_.ColumnCount -eq 0) { $UsedColumns = $ColumnsTotal - $UsedColumns New-HTMLTag -Tag 'th' -Attributes @{ colspan = $UsedColumns; style = ($_.Style) } -Value { $_.Title } } else { if ($_.ColumnCount -le $ColumnsTotal) { $UsedColumns = $UsedColumns + $_.ColumnCount } else { $UsedColumns = - ($ColumnsTotal - $_.ColumnCount) } New-HTMLTag -Tag 'th' -Attributes @{ colspan = $_.ColumnCount; style = ($_.Style) } -Value { $_.Title } } } } } $AddedHeader = @( $NewHeader = [System.Collections.Generic.List[string]]::new() $TopHeader New-HTMLTag -Tag 'tr' { for ($i = 0; $i -lt $HeaderNames.Count; $i++) { $Found = $false foreach ($_ in $MergeColumns) { if ($_.Index -contains $i) { if ($_.Used -eq $false) { New-HTMLTag -Tag 'th' -Attributes @{ colspan = $_.Count; style = ($_.Style); class = $ResponsiveOperations[$i] } -Value { $_.Title } $_.Used = $true $Found = $true } else { $Found = $true # Do Nothing } } } if (-not $Found) { if ($MergeColumns.Count -eq 0) { # if there are no columns that are supposed to get a Title (merged Title over 2 or more columns) we remove rowspan completly and just apply style # the style will apply, however if Style will be empty it will be removed by New-HTMLTag function New-HTMLTag -Tag 'th' { $HeaderNames[$i] } -Attributes @{ style = $Styles[$i].Style; class = $ResponsiveOperations[$i].ResponsiveOperations } } else { # Since we're adding Title we need to use Rowspan. Rowspan = 2 means spaning row over 2 rows New-HTMLTag -Tag 'th' { $HeaderNames[$i] } -Attributes @{ rowspan = 2; style = $Styles[$i].Style; class = $ResponsiveOperations[$i].ResponsiveOperations } } } else { $Head = New-HTMLTag -Tag 'th' { $HeaderNames[$i] } -Attributes @{ style = $Styles[$i].Style; class = $ResponsiveOperations[$i].ResponsiveOperations } $NewHeader.Add($Head) } } } if ($NewHeader.Count) { New-HTMLTag -Tag 'tr' { $NewHeader } } ) return $AddedHeader } function Add-TableRowGrouping { [CmdletBinding()] param( [string] $DataTableName, [System.Collections.IDictionary] $Settings, [switch] $Top, [switch] $Bottom ) if ($Settings.Count -gt 0) { if ($Top) { $Output = "var collapsedGroups = {};" } if ($Bottom) { $Output = @" `$('#$DataTableName tbody').on('click', 'tr.dtrg-start', function () { var name = `$(this).data('name'); collapsedGroups[name] = !collapsedGroups[name]; table.draw(false); }); "@ } $Output } } function Add-TableState { [CmdletBinding()] param( [bool] $Filtering, [bool] $SavedState, [string] $DataTableName, [ValidateSet('Top', 'Bottom', 'Both')][string]$FilteringLocation = 'Bottom' ) if ($Filtering -and $SavedState) { if ($FilteringLocation -eq 'Top') { $Output = @" // Setup - Looading text input from SavedState `$('#$DataTableName').on('stateLoaded.dt', function(e, settings, data) { settings.aoPreSearchCols.forEach(function(col, index) { if (col.sSearch) setTimeout(function() { `$('#$DataTableName thead th:eq('+index+') input').val(col.sSearch) }, 50) }) }); "@ } elseif ($FilteringLocation -eq 'Both') { $Output = @" // Setup - Looading text input from SavedState `$('#$DataTableName').on('stateLoaded.dt', function(e, settings, data) { settings.aoPreSearchCols.forEach(function(col, index) { if (col.sSearch) setTimeout(function() { `$('#$DataTableName thead th:eq('+index+') input').val(col.sSearch) }, 50) }) }); // Setup - Looading text input from SavedState `$('#$DataTableName').on('stateLoaded.dt', function(e, settings, data) { settings.aoPreSearchCols.forEach(function(col, index) { if (col.sSearch) setTimeout(function() { `$('#$DataTableName tfoot th:eq('+index+') input').val(col.sSearch) }, 50) }) }); "@ } else { $Output = @" // Setup - Looading text input from SavedState `$('#$DataTableName').on('stateLoaded.dt', function(e, settings, data) { settings.aoPreSearchCols.forEach(function(col, index) { if (col.sSearch) setTimeout(function() { `$('#$DataTableName tfoot th:eq('+index+') input').val(col.sSearch) }, 50) }) }) "@ } } else { $Output = '' } return $Output } function Convert-Image { [CmdletBinding()] param( [string] $Image ) $ImageFile = Get-ImageFile -Image $Image if ($ImageFile) { Convert-ImageToBinary -ImageFile $ImageFile } } function Convert-ImagesToBinary { [CmdLetBinding()] param( [string[]] $Content, [string] $Search, [string] $ReplacePath ) if ($Content -like "*$Search*") { $Replace = "data:image/$FileType;base64," + [Convert]::ToBase64String((Get-Content -LiteralPath $ReplacePath -Encoding Byte)) $Content = $Content.Replace($Search, $Replace) } $Content } function Convert-ImageToBinary { [CmdletBinding()] param( [System.IO.FileInfo] $ImageFile ) if ($ImageFile.Extension -eq '.jpg') { $FileType = 'jpeg' } else { $FileType = $ImageFile.Extension.Replace('.', '') } Write-Verbose "Converting $($ImageFile.FullName) to base64 ($FileType)" if ($PSEdition -eq 'Core') { $ImageContent = Get-Content -AsByteStream -LiteralPath $ImageFile.FullName } else { $ImageContent = Get-Content -LiteralPath $ImageFile.FullName -Encoding Byte } $Output = "data:image/$FileType;base64," + [Convert]::ToBase64String(($ImageContent)) $Output } function Convert-StyleContent { [CmdLetBinding()] param( [string[]] $CSS, [string] $ImagesPath, [string] $SearchPath ) #Get-ObjectType -Object $CSS -VerboseOnly -Verbose $ImageFiles = Get-ChildItem -Path (Join-Path $ImagesPath '\*') -Include *.jpg, *.png, *.bmp #-Recurse foreach ($Image in $ImageFiles) { #$Image.FullName #$Image.Name $CSS = Convert-ImagesToBinary -Content $CSS -Search "$SearchPath$($Image.Name)" -ReplacePath $Image.FullName } return $CSS } # #Convert-StyleContent -ImagesPath "$PSScriptRoot\Resources\Images\DataTables" -SearchPath "DataTables-1.10.18/images/" function Convert-StyleContent1 { param( [PSCustomObject] $Options ) # Replace PNG / JPG files in Styles if ($null -ne $Options.StyleContent) { Write-Verbose "Logos: $($Options.Logos.Keys -join ',')" foreach ($Logo in $Options.Logos.Keys) { $Search = "../images/$Logo.png", "DataTables-1.10.18/images/$Logo.png" $Replace = $Options.Logos[$Logo] foreach ($S in $Search) { Write-Verbose "Logos - replacing $S with binary representation" $Options.StyleContent = ($Options.StyleContent).Replace($S, $Replace) } } } } function Convert-TableRowGrouping { [CmdletBinding()] param( [string] $Options, [int] $RowGroupingColumnID ) if ($RowGroupingColumnID -gt -1) { $TextToReplace = @" rowGroup: { // Uses the 'row group' plugin dataSrc: $RowGroupingColumnID, startRender: function (rows, group) { var collapsed = !!collapsedGroups[group]; rows.nodes().each(function (r) { r.style.display = collapsed ? 'none' : ''; }); var toggleClass = collapsed ? 'fa-plus-square' : 'fa-minus-square'; // Add group name to <tr> return `$('<tr/>') .append('<td colspan="' + rows.columns()[0].length + '">' + '<span class="fa fa-fw ' + toggleClass + ' toggler"/> ' + group + ' (' + rows.count() + ')</td>') .attr('data-name', group) .toggleClass('collapsed', collapsed); }, }, "@ } else { $TextToReplace = '' } if ($PSEdition -eq 'Desktop') { $TextToFind = '"rowGroup": "",' } else { $TextToFind = '"rowGroup": "",' } $Options = $Options -Replace ($TextToFind, $TextToReplace) $Options } function ConvertTo-CSS { [CmdletBinding()] param( [string] $ID, [string] $ClassName, [System.Collections.IDictionary] $Attributes, [switch] $Group ) $Css = @( if ($Group) { '<style>' } if ($ID) { "#$ID $ClassName {" } else { ".$ClassName {" } foreach ($_ in $Attributes.Keys) { if ($null -ne $Attributes[$_]) { "$($_): $($Attributes[$_]);" } } '}' if ($Group) { '</style>' } ) -join "`n" $CSS } function ConvertTo-HTMLStyle { [CmdletBinding()] param( [nullable[RGBColors]] $Color, [nullable[RGBColors]] $BackGroundColor, [int] $FontSize, [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeight, [ValidateSet('normal', 'italic', 'oblique')][string] $FontStyle, [ValidateSet('normal', 'small-caps')][string] $FontVariant, [string] $FontFamily, [ValidateSet('left', 'center', 'right', 'justify')][string] $Alignment, [ValidateSet('none', 'line-through', 'overline', 'underline')][string] $TextDecoration, [ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $TextTransform, [ValidateSet('rtl')][string] $Direction, [switch] $LineBreak ) if ($FontSize -eq 0) { $Size = '' } else { $size = "$($FontSize)px" } $Style = @{ 'color' = ConvertFrom-Color -Color $Color 'background-color' = ConvertFrom-Color -Color $BackGroundColor 'font-size' = $Size 'font-weight' = $FontWeight 'font-variant' = $FontVariant 'font-family' = $FontFamily 'font-style' = $FontStyle 'text-align' = $Alignment 'text-decoration' = $TextDecoration 'text-transform' = $TextTransform } # Removes empty, not needed values from hashtable. It's much easier then using if/else to verify for null/empty string Remove-EmptyValues -Hashtable $Style return $Style } function Get-FeaturesInUse { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER PriorityFeatures Define priority features - important for ordering when CSS or JS has to be processed in certain order .EXAMPLE Get-FeaturesInUse -PriorityFeatures 'Jquery', 'DataTables', 'Tabs', 'Test' .NOTES General notes #> [CmdletBinding()] param( [string[]] $PriorityFeatures ) [Array] $Features = foreach ($Key in $Script:HTMLSchema.Features.Keys) { if ($Script:HTMLSchema.Features[$Key]) { $Key } } [Array] $TopFeatures = foreach ($Feature in $PriorityFeatures) { if ($Features -contains $Feature) { $Feature } } [Array] $RemainingFeatures = foreach ($Feature in $Features) { if ($TopFeatures -notcontains $Feature) { $Feature } } [Array] $AllFeatures = $TopFeatures + $RemainingFeatures $AllFeatures } Function Get-HTMLLogos { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string]$LeftLogoName = "Sample", [string]$RightLogoName = "Alternate", [string]$LeftLogoString, [string]$RightLogoString ) $LogoSources = [ordered] @{ } $LogoPath = @( if ([String]::IsNullOrEmpty($RightLogoString) -eq $false -or [String]::IsNullOrEmpty($LeftLogoString) -eq $false) { if ([String]::IsNullOrEmpty($RightLogoString) -eq $false) { $LogoSources.Add($RightLogoName, $RightLogoString) } if ([String]::IsNullOrEmpty($LeftLogoString) -eq $false) { $LogoSources.Add($LeftLogoName, $LeftLogoString) } } else { "$PSScriptRoot\Resources\Images\Other" } "$PSScriptRoot\Resources\Images\DataTables" ) $ImageFiles = Get-ChildItem -Path (Join-Path $LogoPath '\*') -Include *.jpg, *.png, *.bmp -Recurse foreach ($ImageFile in $ImageFiles) { <# if ($ImageFile.Extension -eq '.jpg') { $FileType = 'jpeg' } else { $FileType = $ImageFile.Extension.Replace('.', '') } Write-Verbose "Converting $($ImageFile.FullName) to base64 ($FileType)" if ($PSEdition -eq 'Core') { $ImageContent = Get-Content -AsByteStream -LiteralPath $ImageFile.FullName } else { $ImageContent = Get-Content -LiteralPath $ImageFile.FullName -Encoding Byte } #> $ImageBinary = Convert-ImageToBinary -ImageFile $ImageFile #$LogoSources.Add($ImageFile.BaseName, "data:image/$FileType;base64," + [Convert]::ToBase64String(($ImageContent))) $LogoSources.Add($ImageFile.BaseName, $ImageBinary) } $LogoSources } #$t = 'C:\Support\GitHub\PSWriteHTML\Private\Get-HTMLLogos.ps1' #$t -as [System.IO.FileInfo] function Get-HTMLPartContent { param( [Array] $Content, [string] $Start, [string] $End, [ValidateSet('Before', 'Between', 'After')] $Type = 'Between' ) $NrStart = $Content.IndexOf($Start) $NrEnd = $Content.IndexOf($End) #Write-Color $NrStart, $NrEnd, $Type -Color White, Yellow, Blue if ($Type -eq 'Between') { if ($NrStart -eq -1) { # return nothing return } $Content[$NrStart..$NrEnd] } if ($Type -eq 'After') { if ($NrStart -eq -1) { # Returns untouched content return $Content } $Content[($NrEnd + 1)..($Content.Count - 1)] } if ($Type -eq 'Before') { if ($NrStart -eq -1) { # return nothing return } $Content[0..$NrStart] } } function Get-ImageFile { [CmdletBinding()] param( [uri] $Image ) if (-not $Image.IsFile) { $Extension = ($Image.OriginalString).Substring(($Image.OriginalString).Length - 4) if ($Extension -notin @('.png', '.jpg', 'jpeg', '.svg')) { return } $Extension = $Extension.Replace('.', '') $ImageFile = Get-FileName -Extension $Extension -Temporary Invoke-WebRequest -Uri $Image -OutFile $ImageFile $ImageFile } else { } } function Get-Resources { [CmdLetBinding()] param( [switch] $UseCssLinks, [switch] $UseJavaScriptLinks, [switch] $NoScript, [ValidateSet('Header', 'Footer', 'HeaderAlways', 'FooterAlways')][string] $Location ) DynamicParam { # Defines Features Parameter Dynamically $Names = $Script:Configuration.Features.Keys $ParamAttrib = New-Object System.Management.Automation.ParameterAttribute $ParamAttrib.Mandatory = $true $ParamAttrib.ParameterSetName = '__AllParameterSets' $ReportAttrib = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $ReportAttrib.Add($ParamAttrib) $ReportAttrib.Add((New-Object System.Management.Automation.ValidateSetAttribute($Names))) $ReportRuntimeParam = New-Object System.Management.Automation.RuntimeDefinedParameter('Features', [string[]], $ReportAttrib) $RuntimeParamDic = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary $RuntimeParamDic.Add('Features', $ReportRuntimeParam) return $RuntimeParamDic } Process { [string[]] $Features = $PSBoundParameters.Features foreach ($Feature in $Features) { Write-Verbose "Get-Resources - Location: $Location - Feature: $Feature UseCssLinks: $UseCssLinks UseJavaScriptLinks: $UseJavaScriptLinks" if ($UseCssLinks) { New-HTMLResourceCSS -Link $Script:Configuration.Features.$Feature.$Location.'CssLink' -ResourceComment $Script:Configuration.Features.$Feature.Comment } else { $CSSOutput = New-HTMLResourceCSS ` -FilePath $Script:Configuration.Features.$Feature.$Location.'Css' ` -ResourceComment $Script:Configuration.Features.$Feature.Comment ` -Replace $Script:Configuration.Features.$Feature.CustomActionsReplace Convert-StyleContent -CSS $CSSOutput -ImagesPath "$PSScriptRoot\Resources\Images\DataTables" -SearchPath "../images/" } if ($UseJavaScriptLinks) { New-HTMLResourceJS -Link $Script:Configuration.Features.$Feature.$Location.'JsLink' -ResourceComment $Script:Configuration.Features.$Feature.Comment } else { New-HTMLResourceJS -FilePath $Script:Configuration.Features.$Feature.$Location.'Js' -ResourceComment $Script:Configuration.Features.$Feature.Comment -ReplaceData $Script:Configuration.Features.$Feature.CustomActionsReplace } if ($NoScript) { [Array] $Output = @( if ($UseCssLinks) { New-HTMLResourceCSS -Link $Script:Configuration.Features.$Feature.$Location.'CssLinkNoScript' -ResourceComment $Script:Configuration.Features.$Feature.Comment } else { $CSSOutput = New-HTMLResourceCSS -FilePath $Script:Configuration.Features.$Feature.$Location.'CssNoScript' -ResourceComment $Script:Configuration.Features.$Feature.Comment -ReplaceData $Script:Configuration.Features.$Feature.CustomActionsReplace Convert-StyleContent -CSS $CSSOutput -ImagesPath "$PSScriptRoot\Resources\Images\DataTables" -SearchPath "../images/" } ) if (($Output.Count -gt 0) -and ($null -ne $Output[0])) { New-HTMLTag -Tag 'noscript' { $Output } } } } } } function New-ApexChart { [CmdletBinding()] param( [System.Collections.IDictionary] $Options ) $Script:HTMLSchema.Features.ChartsApex = $true [string] $ID = "ChartID-" + (Get-RandomStringName -Size 8) $Div = New-HTMLTag -Tag 'div' -Attributes @{ id = $ID; } $Script = New-HTMLTag -Tag 'script' -Value { # Convert Dictionary to JSON and return chart within SCRIPT tag # Make sure to return with additional empty string $JSON = $Options | ConvertTo-Json -Depth 5 | ForEach-Object { [System.Text.RegularExpressions.Regex]::Unescape($_) } "var options = $JSON" "var chart = new ApexCharts(document.querySelector('#$ID'), options );" "chart.render();" } -NewLine $Div # we need to move it to the end of the code therefore using additional vesel $Script:HTMLSchema.Charts.Add($Script) } function New-CalendarEvent { [alias('CalendarEvent')] [CmdletBinding()] param( [string] $Title, [string] $Description, [DateTime] $StartDate, [nullable[DateTime]] $EndDate, [string] $Constraint, [RGBColors] $Color ) $Object = [PSCustomObject] @{ Type = 'CalendarEvent' Settings = [ordered] @{ title = $Title description = $Description constraint = $Constraint # url: 'http://google.com/', color = ConvertFrom-Color -Color $Color } } if ($StartDate) { $Object.Settings.start = Get-Date -Date ($StartDate) -Format "yyyy-MM-ddTHH:mm:ss" } if ($EndDate) { $Object.Settings.end = Get-Date -Date ($EndDate) -Format "yyyy-MM-ddTHH:mm:ss" } Remove-EmptyValues -Hashtable $Object.Settings -Recursive #-Rerun 2 $Object <# events: [ { title: 'rrule event', rrule: { dtstart: '2019-08-09T13:00:00', // until: '2019-08-01', freq: 'weekly' }, duration: '02:00' } ], events: [{ title: 'Business Lunch', start: '2019-08-03T13:00:00', constraint: 'businessHours' }, { title: 'Meeting', start: '2019-08-13T11:00:00', constraint: 'availableForMeeting', // defined below color: '#257e4a' }, { title: 'Conference', start: '2019-08-18', end: '2019-08-20' }, { title: 'Party', start: '2019-08-29T20:00:00' }, // areas where "Meeting" must be dropped { groupId: 'availableForMeeting', start: '2019-08-11T10:00:00', end: '2019-08-11T16:00:00', rendering: 'background' }, { groupId: 'availableForMeeting', start: '2019-08-13T10:00:00', end: '2019-08-13T16:00:00', rendering: 'background' }, // red areas where no events can be dropped { start: '2019-08-24', end: '2019-08-28', overlap: false, rendering: 'background', color: '#ff9f89' }, { start: '2019-08-06', end: '2019-08-08', overlap: false, rendering: 'background', color: '#ff9f89' } ] #> } function New-ChartAxisX { [alias('ChartCategory', 'ChartAxisX', 'New-ChartCategory')] [CmdletBinding()] param( [alias('Name')][Array] $Names, [alias('Title')][string] $TitleText, [ValidateSet('datetime', 'category', 'numeric')][string] $Type = 'category', [int] $MinValue, [int] $MaxValue #[ValidateSet('top', 'topRight', 'left', 'right', 'bottom', '')][string] $LegendPosition = '', # [RGBColors[]] $Color ) [PSCustomObject] @{ ObjectType = 'ChartAxisX' ChartAxisX = @{ Names = $Names Type = $Type TitleText = $TitleText Min = $MinValue Max = $MaxValue } # LegendPosition = $LegendPosition # Color = $Color } # https://apexcharts.com/docs/options/xaxis/ } <# We can build this: xaxis: { type: 'category', categories: [], labels: { show: true, rotate: -45, rotateAlways: false, hideOverlappingLabels: true, showDuplicates: false, trim: true, minHeight: undefined, maxHeight: 120, style: { colors: [], fontSize: '12px', fontFamily: 'Helvetica, Arial, sans-serif', cssClass: 'apexcharts-xaxis-label', }, offsetX: 0, offsetY: 0, format: undefined, formatter: undefined, datetimeFormatter: { year: 'yyyy', month: "MMM 'yy", day: 'dd MMM', hour: 'HH:mm', }, }, axisBorder: { show: true, color: '#78909C', height: 1, width: '100%', offsetX: 0, offsetY: 0 }, axisTicks: { show: true, borderType: 'solid', color: '#78909C', height: 6, offsetX: 0, offsetY: 0 }, tickAmount: undefined, tickPlacement: 'between', min: undefined, max: undefined, range: undefined, floating: false, position: 'bottom', title: { text: undefined, offsetX: 0, offsetY: 0, style: { color: undefined, fontSize: '12px', fontFamily: 'Helvetica, Arial, sans-serif', cssClass: 'apexcharts-xaxis-title', }, }, crosshairs: { show: true, width: 1, position: 'back', opacity: 0.9, stroke: { color: '#b6b6b6', width: 0, dashArray: 0, }, fill: { type: 'solid', color: '#B1B9C4', gradient: { colorFrom: '#D8E3F0', colorTo: '#BED1E6', stops: [0, 100], opacityFrom: 0.4, opacityTo: 0.5, }, }, dropShadow: { enabled: false, top: 0, left: 0, blur: 1, opacity: 0.4, }, }, tooltip: { enabled: true, formatter: undefined, offsetY: 0, }, } #> function New-ChartAxisY { [alias('ChartAxisY')] [CmdletBinding()] param( [switch] $Show, [switch] $ShowAlways, [string] $TitleText, [ValidateSet('90', '270')][string] $TitleRotate = '90', [int] $TitleOffsetX = 0, [int] $TitleOffsetY = 0, [RGBColors] $TitleStyleColor = [RGBColors]::None, [int] $TitleStyleFontSize = 12, [string] $TitleStylefontFamily = 'Helvetica, Arial, sans-serif', [int] $MinValue, [int] $MaxValue #[ValidateSet('top', 'topRight', 'left', 'right', 'bottom', '')][string] $LegendPosition = '', # [RGBColors[]] $Color ) [PSCustomObject] @{ ObjectType = 'ChartAxisY' ChartAxisY = @{ Show = $Show.IsPresent ShowAlways = $ShowAlways.IsPresent TitleText = $TitleText TitleRotate = $TitleRotate TitleOffsetX = $TitleOffsetX TitleOffsetY = $TitleOffsetY TitleStyleColor = $TitleStyleColor TitleStyleFontSize = $TitleStyleFontSize TitleStylefontFamily = $TitleStylefontFamily Min = $MinValue Max = $MaxValue } } # https://apexcharts.com/docs/options/yaxis/ } <# We can build this yaxis: { show: true, showAlways: true, seriesName: undefined, opposite: false, reversed: false, logarithmic: false, tickAmount: 6, min: 6, max: 6, forceNiceScale: false, floating: false, decimalsInFloat: undefined, labels: { show: true, align: 'right', minWidth: 0, maxWidth: 160, style: { color: undefined, fontSize: '12px', fontFamily: 'Helvetica, Arial, sans-serif', cssClass: 'apexcharts-yaxis-label', }, offsetX: 0, offsetY: 0, rotate: 0, formatter: (value) => { return val }, }, axisBorder: { show: true, color: '#78909C', offsetX: 0, offsetY: 0 }, axisTicks: { show: true, borderType: 'solid', color: '#78909C', width: 6, offsetX: 0, offsetY: 0 }, title: { text: undefined, rotate: -90, offsetX: 0, offsetY: 0, style: { color: undefined, fontSize: '12px', fontFamily: 'Helvetica, Arial, sans-serif', cssClass: 'apexcharts-yaxis-title', }, }, crosshairs: { show: true, position: 'back', stroke: { color: '#b6b6b6', width: 1, dashArray: 0, }, }, tooltip: { enabled: true, offsetX: 0, }, } #> function New-ChartBar { [alias('ChartBar')] [CmdletBinding()] param( [string] $Name, [object] $Value ) [PSCustomObject] @{ ObjectType = 'Bar' Name = $Name Value = $Value } } function New-ChartBarOptions { [alias('ChartBarOptions')] [CmdletBinding()] param( [ValidateSet('bar', 'barStacked', 'barStacked100Percent')] $Type = 'bar', [bool] $DataLabelsEnabled = $true, [int] $DataLabelsOffsetX = -6, [string] $DataLabelsFontSize = '12px', [nullable[RGBColors]] $DataLabelsColor, [alias('PatternedColors')][switch] $Patterned, [alias('GradientColors')][switch] $Gradient, [switch] $Distributed, [switch] $Vertical ) if ($null -ne $PSBoundParameters.Patterned) { $PatternedColors = $Patterned.IsPresent } else { $PatternedColors = $null } if ($null -ne $PSBoundParameters.Gradient) { $GradientColors = $Gradient.IsPresent } else { $GradientColors = $null } [PSCustomObject] @{ ObjectType = 'BarOptions' Type = $Type Title = $Title TitleAlignment = $TitleAlignment Horizontal = -not $Vertical.IsPresent DataLabelsEnabled = $DataLabelsEnabled DataLabelsOffsetX = $DataLabelsOffsetX DataLabelsFontSize = $DataLabelsFontSize DataLabelsColor = $DataLabelsColor PatternedColors = $PatternedColors GradientColors = $GradientColors Distributed = $Distributed.IsPresent } } function New-ChartDonut { [alias('ChartDonut')] [CmdletBinding()] param( [string] $Name, [object] $Value, [RGBColors] $Color ) [PSCustomObject] @{ ObjectType = 'Donut' Name = $Name Value = $Value Color = $Color } } function New-ChartGrid { [alias('ChartGrid')] [CmdletBinding()] param( [switch] $Show, [RGBColors] $BorderColor = [RGBColors]::None, [int] $StrokeDash, #: 0, [ValidateSet('front', 'back', 'default')][string] $Position = 'default', [switch] $xAxisLinesShow, [switch] $yAxisLinesShow, [RGBColors[]] $RowColors, [double] $RowOpacity = 0.5, # valid range 0 - 1 [RGBColors[]] $ColumnColors, [double] $ColumnOpacity = 0.5, # valid range 0 - 1 [int] $PaddingTop, [int] $PaddingRight, [int] $PaddingBottom, [int] $PaddingLeft ) [PSCustomObject] @{ ObjectType = 'ChartGrid' Grid = @{ Show = $Show.IsPresent BorderColor = $BorderColor StrokeDash = $StrokeDash Position = $Position xAxisLinesShow = $xAxisLinesShow.IsPresent yAxisLinesShow = $yAxisLinesShow.IsPresent RowColors = $RowColors RowOpacity = $RowOpacity ColumnColors = $ColumnColors ColumnOpacity = $ColumnOpacity PaddingTop = $PaddingTop PaddingRight = $PaddingRight PaddingBottom = $PaddingBottom PaddingLeft = $PaddingLeft } } # https://apexcharts.com/docs/options/xaxis/ } function New-ChartInternalArea { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [Array] $Data, [Array] $DataNames, [Array] $DataLegend, #[bool] $DataLabelsEnabled = $true, #[int] $DataLabelsOffsetX = -6, #[string] $DataLabelsFontSize = '12px', #[nullable[RGBColors]] $DataLabelsColor, [ValidateSet('datetime', 'category', 'numeric')][string] $DataCategoriesType = 'category' #$Type ) # Chart defintion type, size $Options.chart = @{ type = 'area' } $Options.series = @( New-ChartInternalDataSet -Data $Data -DataNames $DataNames ) # X AXIS - CATEGORIES $Options.xaxis = [ordered] @{ } if ($DataCategoriesType -ne '') { $Options.xaxis.type = $DataCategoriesType } if ($DataCategories.Count -gt 0) { $Options.xaxis.categories = $DataCategories } } function New-ChartInternalAxisX { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [string] $TitleText, [int] $Min, [int] $Max, [ValidateSet('datetime', 'category', 'numeric')][string] $Type = 'category', [Array] $Names ) if (-not $Options.Contains('xaxis')) { $Options.xaxis = @{ } } if ($TitleText -ne '') { $Options.xaxis.title = @{ } $Options.xaxis.title.text = $TitleText } if ($MinValue -gt 0) { $Options.xaxis.min = $Min } if ($MinValue -gt 0) { $Options.xaxis.max = $Max } if ($Type -ne '') { $Options.xaxis.type = $Type } if ($Names.Count -gt 0) { $Options.xaxis.categories = $Names } } function New-ChartInternalAxisY { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [string] $TitleText, [int] $Min, [int] $Max, [bool] $Show, [bool] $ShowAlways, [ValidateSet('90', '270')][string] $TitleRotate = '90', [int] $TitleOffsetX = 0, [int] $TitleOffsetY = 0, [RGBColors] $TitleStyleColor = [RGBColors]::None, [int] $TitleStyleFontSize = 12, [string] $TitleStylefontFamily = 'Helvetica, Arial, sans-serif' ) if (-not $Options.Contains('yaxis')) { $Options.yaxis = @{ } } #if ($Show) { $Options.yaxis.show = $Show $Options.yaxis.showAlways = $ShowAlways # } if ($TitleText -ne '') { $Options.yaxis.title = [ordered] @{ } $Options.yaxis.title.text = $TitleText $Options.yaxis.title.rotate = [int] $TitleRotate $Options.yaxis.title.offsetX = $TitleOffsetX $Options.yaxis.title.offsetY = $TitleOffsetY $Options.yaxis.title.style = [ordered] @{ } $Color = ConvertFrom-Color -Color $TitleStyleColor if ($null -ne $Color) { $Options.yaxis.title.style.color = $Coor } $Options.yaxis.title.style.fontSize = $TitleStyleFontSize $Options.yaxis.title.style.fontFamily = $TitleStylefontFamily } if ($Min -gt 0) { $Options.yaxis.min = $Min } if ($Min -gt 0) { $Options.yaxis.max = $Max } } <# We can build this yaxis: { show: true, showAlways: true, seriesName: undefined, opposite: false, reversed: false, logarithmic: false, tickAmount: 6, min: 6, max: 6, forceNiceScale: false, floating: false, decimalsInFloat: undefined, labels: { show: true, align: 'right', minWidth: 0, maxWidth: 160, style: { color: undefined, fontSize: '12px', fontFamily: 'Helvetica, Arial, sans-serif', cssClass: 'apexcharts-yaxis-label', }, offsetX: 0, offsetY: 0, rotate: 0, formatter: (value) => { return val }, }, axisBorder: { show: true, color: '#78909C', offsetX: 0, offsetY: 0 }, axisTicks: { show: true, borderType: 'solid', color: '#78909C', width: 6, offsetX: 0, offsetY: 0 }, title: { text: undefined, rotate: -90, offsetX: 0, offsetY: 0, style: { color: undefined, fontSize: '12px', fontFamily: 'Helvetica, Arial, sans-serif', cssClass: 'apexcharts-yaxis-title', }, }, crosshairs: { show: true, position: 'back', stroke: { color: '#b6b6b6', width: 1, dashArray: 0, }, }, tooltip: { enabled: true, offsetX: 0, }, } #> Function New-ChartInternalBar { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [bool] $Horizontal = $true, [bool] $DataLabelsEnabled = $true, [int] $DataLabelsOffsetX = -6, [string] $DataLabelsFontSize = '12px', [RGBColors[]] $DataLabelsColor, [string] $Title, [ValidateSet('center', 'left', 'right', 'default')][string] $TitleAlignment = 'default', [string] $Formatter, [ValidateSet('bar', 'barStacked', 'barStacked100Percent')] $Type = 'bar', #[RGBColors[]] $Colors, [switch] $Distributed, [Array] $Data, [Array] $DataNames, [Array] $DataLegend ) if ($Type -eq 'bar') { $Options.chart = [ordered] @{ type = 'bar' } } elseif ($Type -eq 'barStacked') { $Options.chart = [ordered] @{ type = 'bar' stacked = $true } } else { $Options.chart = [ordered] @{ type = 'bar' stacked = $true stackType = '100%' } } $Options.plotOptions = @{ bar = @{ horizontal = $Horizontal } } if ($Distributed) { $Options.plotOptions.bar.distributed = $Distributed.IsPresent } $Options.dataLabels = [ordered] @{ enabled = $DataLabelsEnabled offsetX = $DataLabelsOffsetX style = @{ fontSize = $DataLabelsFontSize } } if ($null -ne $DataLabelsColor) { $RGBColorLabel = ConvertFrom-Color -Color $DataLabelsColor $Options.dataLabels.style.colors = @($RGBColorLabel) } $Options.series = @(New-ChartInternalDataSet -Data $Data -DataNames $DataLegend) # X AXIS - CATEGORIES $Options.xaxis = [ordered] @{ } # if ($DataCategoriesType -ne '') { # $Options.xaxis.type = $DataCategoriesType #} if ($DataNames.Count -gt 0) { $Options.xaxis.categories = $DataNames # Need to figure out how to conver to json and leave function without "" #if ($Formatter -ne '') { #$Options.xaxis.labels = @{ #formatter = "function(val) { return val + `"$Formatter`" }" #} #} } } function New-ChartInternalColors { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [RGBColors[]]$Colors ) if ($Colors.Count -gt 0) { $RGBColor = ConvertFrom-Color -Color $Colors $Options.colors = @($RGBColor) } } function New-ChartInternalDataLabels { param( [System.Collections.IDictionary] $Options, [bool] $DataLabelsEnabled = $true, [int] $DataLabelsOffsetX = -6, [string] $DataLabelsFontSize = '12px', [RGBColors[]] $DataLabelsColor ) $Options.dataLabels = [ordered] @{ enabled = $DataLabelsEnabled offsetX = $DataLabelsOffsetX style = @{ fontSize = $DataLabelsFontSize } } if ($DataLabelsColor.Count -gt 0) { $Options.dataLabels.style.colors = @(ConvertFrom-Color -Color $DataLabelsColor) } } function New-ChartInternalDataSet { [CmdletBinding()] param( [Array] $Data, [Array] $DataNames ) if ($null -ne $Data -and $null -ne $DataNames) { if ($Data[0] -is [System.Collections.ICollection]) { # If it's array of Arrays if ($Data[0].Count -eq $DataNames.Count) { for ($a = 0; $a -lt $Data.Count; $a++) { [ordered] @{ name = $DataNames[$a] data = $Data[$a] } } } elseif ($Data.Count -eq $DataNames.Count) { for ($a = 0; $a -lt $Data.Count; $a++) { [ordered] @{ name = $DataNames[$a] data = $Data[$a] } } } else { # rerun with just data (so it checks another if) New-ChartInternalDataSet -Data $Data } } else { if ($null -ne $DataNames) { # If it's just int in Array [ordered] @{ name = $DataNames data = $Data } } else { [ordered] @{ data = $Data } } } } elseif ($null -ne $Data) { # No names given if ($Data[0] -is [System.Collections.ICollection]) { # If it's array of Arrays foreach ($D in $Data) { [ordered] @{ data = $D } } } else { # If it's just int in Array [ordered] @{ data = $Data } } } else { Write-Warning -Message "New-ChartInternalDataSet - No Data provided. Unabled to create dataset." return [ordered] @{ } } } function New-ChartInternalGradient { [CmdletBinding()] param( ) $Options.fill = [ordered] @{ type = 'gradient' gradient = [ordered] @{ shade = 'dark' type = 'horizontal' shadeIntensity = 0.5 gradientToColors = @('#ABE5A1') inverseColors = $true opacityFrom = 1 opacityTo = 1 stops = @(0, 100) } } } function New-ChartInternalGrid { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [bool] $Show, [RGBColors] $BorderColor = [RGBColors]::None, [int] $StrokeDash, #: 0, [ValidateSet('front', 'back', 'default')][string] $Position = 'default', [nullable[bool]] $xAxisLinesShow = $null, [nullable[bool]] $yAxisLinesShow = $null, [alias('GridColors')][RGBColors[]] $RowColors, [alias('GridOpacity')][double] $RowOpacity = 0.5, # valid range 0 - 1 [RGBColors[]] $ColumnColors , [double] $ColumnOpacity = 0.5, # valid range 0 - 1 [int] $PaddingTop, [int] $PaddingRight, [int] $PaddingBottom, [int] $PaddingLeft ) <# Build this https://apexcharts.com/docs/options/grid/ grid: { show: true, borderColor: '#90A4AE', strokeDashArray: 0, position: 'back', xaxis: { lines: {, show: false } }, yaxis: { lines: {, show: false } }, row: { colors: undefined, opacity: 0.5 }, column: { colors: undefined, opacity: 0.5 }, padding: { top: 0, right: 0, bottom: 0, left: 0 }, } #> $Options.grid = [ordered] @{ } $Options.grid.Show = $Show if ($BorderColor -ne [RGBColors]::None) { $options.grid.borderColor = @(ConvertFrom-Color -Color $BorderColor) } if ($StrokeDash -gt 0) { $Options.grid.strokeDashArray = $StrokeDash } if ($Position -ne 'Default') { $Options.grid.position = $Position } if ($null -ne $xAxisLinesShow) { $Options.grid.xaxis = @{ } $Options.grid.xaxis.lines = @{ } $Options.grid.xaxis.lines.show = $xAxisLinesShow } if ($null -ne $yAxisLinesShow) { $Options.grid.yaxis = @{ } $Options.grid.yaxis.lines = @{ } $Options.grid.yaxis.lines.show = $yAxisLinesShow } if ($RowColors.Count -gt 0 -or $RowOpacity -ne 0) { $Options.grid.row = @{ } if ($RowColors.Count -gt 0) { $Options.grid.row.colors = @(ConvertFrom-Color -Color $RowColors) } if ($RowOpacity -ne 0) { $Options.grid.row.opacity = $RowOpacity } } if ($ColumnColors.Count -gt 0 -or $ColumnOpacity -ne 0) { $Options.grid.column = @{ } if ($ColumnColors.Count -gt 0) { $Options.grid.column.colors = @(ConvertFrom-Color -Color $ColumnColors) } if ($ColumnOpacity -ne 0) { $Options.grid.column.opacity = $ColumnOpacitys } } if ($PaddingTop -gt 0 -or $PaddingRight -gt 0 -or $PaddingBottom -gt 0 -or $PaddingLeft -gt 0) { # Padding options $Options.grid.padding = @{ } if ($PaddingTop -gt 0) { $Options.grid.padding.PaddingTop = $PaddingTop } if ($PaddingRight -gt 0) { $Options.grid.padding.PaddingRight = $PaddingRight } if ($PaddingBottom -gt 0) { $Options.grid.padding.PaddingBottom = $PaddingBottom } if ($PaddingLeft -gt 0) { $Options.grid.padding.PaddingLeft = $PaddingLeft } } } function New-ChartInternalLegend { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [ValidateSet('top', 'topRight', 'left', 'right', 'bottom', 'default')][string] $LegendPosition = 'default' ) # legend if ($LegendPosition -eq 'default' -or $LegendPosition -eq 'bottom') { # Do nothing } elseif ($LegendPosition -eq 'right') { $Options.legend = [ordered]@{ position = 'right' offsetY = 100 height = 230 } } elseif ($LegendPosition -eq 'top') { $Options.legend = [ordered]@{ position = 'top' horizontalAlign = 'left' offsetX = 40 } } elseif ($LegendPosition -eq 'topRight') { $Options.legend = [ordered]@{ position = 'top' horizontalAlign = 'right' floating = true offsetY = -25 offsetX = -5 } } } function New-ChartInternalLine { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [Array] $Data, [Array] $DataNames, #[Array] $DataLegend, # [bool] $DataLabelsEnabled = $true, #[int] $DataLabelsOffsetX = -6, #[string] $DataLabelsFontSize = '12px', # [nullable[RGBColors]] $DataLabelsColor, [ValidateSet('datetime', 'category', 'numeric')][string] $DataCategoriesType = 'category' # $Type ) # Chart defintion type, size $Options.chart = @{ type = 'line' } $Options.series = @( New-ChartInternalDataSet -Data $Data -DataNames $DataNames ) # X AXIS - CATEGORIES $Options.xaxis = [ordered] @{ } if ($DataCategoriesType -ne '') { $Options.xaxis.type = $DataCategoriesType } if ($DataCategories.Count -gt 0) { $Options.xaxis.categories = $DataCategories } } function New-ChartInternalMarker { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [int] $MarkerSize ) if ($MarkerSize -gt 0) { $Options.markers = @{ size = $MarkerSize } } } function New-ChartInternalPattern { [CmdletBinding()] param( ) $Options.fill = [ordered]@{ type = 'pattern' opacity = 1 pattern = [ordered]@{ style = @('circles', 'slantedLines', 'verticalLines', 'horizontalLines') } } } function New-ChartInternalPie { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [Array] $Values, [Array] $Names, [string] $Type ) # Chart defintion type, size $Options.chart = @{ type = $Type.ToLower() } $Options.series = @($Values) $Options.labels = @($Names) } function New-ChartInternalRadial { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [Array] $Values, [Array] $Names, $Type ) # Chart defintion type, size $Options.chart = @{ type = 'radialBar' } if ($Type -eq '1') { New-ChartInternalRadialType1 -Options $Options } elseif ($Type -eq '2') { New-ChartInternalRadialType2 -Options $Options } $Options.series = @($Values) $Options.labels = @($Names) } function New-ChartInternalRadialCircleType { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [ValidateSet('FullCircleTop', 'FullCircleBottom', 'FullCircleBottomLeft', 'FullCircleLeft', 'Speedometer', 'SemiCircleGauge')] $CircleType ) if ($CircleType -eq 'SemiCircleGauge') { $Options.plotOptions.radialBar = [ordered] @{ startAngle = -90 endAngle = 90 } } elseif ($CircleType -eq 'FullCircleBottom') { $Options.plotOptions.radialBar = [ordered] @{ startAngle = -180 endAngle = 180 } } elseif ($CircleType -eq 'FullCircleLeft') { $Options.plotOptions.radialBar = [ordered] @{ startAngle = -90 endAngle = 270 } } elseif ($CircleType -eq 'FullCircleBottomLeft') { $Options.plotOptions.radialBar = [ordered] @{ startAngle = -135 endAngle = 225 } } elseif ($CircleType -eq 'Speedometer') { $Options.plotOptions.radialBar = [ordered] @{ startAngle = -135 endAngle = 135 } } else { #FullCircleTop } } function New-ChartInternalRadialDataLabels { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [string] $LabelAverage = 'Average' ) if ($LabelAverage -ne '') { $Options.plotOptions.radialBar.dataLabels = @{ showOn = 'always' name = @{ # fontSize = '16px' # color = 'undefined' #offsetY = 120 } value = @{ #offsetY = 76 # fontSize = '22px' # color = 'undefined' # formatter = 'function (val) { return val + "%" }' } total = @{ show = $true label = $LabelAverage } } } } function New-ChartInternalRadialType1 { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [Array] $Values, [Array] $Names ) $Options.plotOptions = @{ radialBar = [ordered] @{ #startAngle = -135 #endAngle = 225 #startAngle = -135 #endAngle = 135 hollow = [ordered] @{ margin = 0 size = '70%' background = '#fff' image = 'undefined' imageOffsetX = 0 imageOffsetY = 0 position = 'front' dropShadow = @{ enabled = $true top = 3 left = 0 blur = 4 opacity = 0.24 } } track = [ordered] @{ background = '#fff' strokeWidth = '70%' margin = 0 #// margin is in pixels dropShadow = [ordered] @{ enabled = $true top = -3 left = 0 blur = 4 opacity = 0.35 } } <# dataLabels = @{ showOn = 'always' name = @{ # fontSize = '16px' # color = 'undefined' #offsetY = 120 } value = @{ #offsetY = 76 # fontSize = '22px' # color = 'undefined' # formatter = 'function (val) { return val + "%" }' } total = @{ show = $false label = 'Average' } } #> } } $Options.fill = [ordered] @{ type = 'gradient' gradient = [ordered] @{ shade = 'dark' type = 'horizontal' shadeIntensity = 0.5 gradientToColors = @('#ABE5A1') inverseColors = $true opacityFrom = 1 opacityTo = 1 stops = @(0, 100) } } <# Gradient $Options.stroke = @{ lineCap = 'round' } #> <# $Options.fill = @{ type = 'gradient' gradient = @{ shade = 'dark' shadeIntensity = 0.15 inverseColors = $false opacityFrom = 1 opacityTo = 1 stops = @(0, 50, 65, 91) } } #> $Options.stroke = [ordered] @{ dashArray = 4 } } function New-ChartInternalRadialType2 { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [Array] $Values, [Array] $Names ) $Options.plotOptions = @{ radialBar = [ordered] @{ #startAngle = -135 #endAngle = 225 #startAngle = -135 #endAngle = 135 hollow = [ordered] @{ margin = 0 size = '70%' background = '#fff' image = 'undefined' imageOffsetX = 0 imageOffsetY = 0 position = 'front' dropShadow = @{ enabled = $true top = 3 left = 0 blur = 4 opacity = 0.24 } } <# track = @{ background = '#fff' strokeWidth = '70%' margin = 0 #// margin is in pixels dropShadow = @{ enabled = $true top = -3 left = 0 blur = 4 opacity = 0.35 } } dataLabels = @{ showOn = 'always' name = @{ # fontSize = '16px' # color = 'undefined' offsetY = 120 } value = @{ offsetY = 76 # fontSize = '22px' # color = 'undefined' # formatter = 'function (val) { return val + "%" }' } total = @{ show = $false label = 'Average' } } #> } } } function New-ChartInternalSize { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [nullable[int]] $Height = 350, [nullable[int]] $Width ) if ($null -ne $Height) { $Options.chart.height = $Height } if ($null -ne $Width) { $Options.chart.width = $Width } } function New-ChartInternalSpark { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [nullable[RGBColors]] $Color, [string] $Title, [string] $SubTitle, [int] $FontSizeTitle = 24, [int] $FontSizeSubtitle = 14, [Array] $Values ) if ($Values.Count -eq 0) { Write-Warning 'Get-ChartSpark - Values Empty' } if ($null -ne $Color) { $ColorRGB = ConvertFrom-Color -Color $Color $Options.colors = @($ColorRGB) } $Options.chart = [ordered] @{ type = 'area' sparkline = @{ enabled = $true } } $Options.stroke = @{ curve = 'straight' } $Options.title = [ordered] @{ text = $Title offsetX = 0 style = @{ fontSize = "$($FontSizeTitle)px" cssClass = 'apexcharts-yaxis-title' } } $Options.subtitle = [ordered] @{ text = $SubTitle offsetX = 0 style = @{ fontSize = "$($FontSizeSubtitle)px" cssClass = 'apexcharts-yaxis-title' } } $Options.yaxis = @{ min = 0 } $Options.fill = @{ opacity = 0.3 } $Options.series = @( # Checks if it's multiple array passed or just one. If one it will draw one line, if more then one it will draw line per each array if ($Values[0] -is [Array]) { foreach ($Value in $Values) { @{ data = @($Value) } } } else { @{ data = @($Values) } } ) } function New-ChartInternalStrokeDefinition { param( [System.Collections.IDictionary] $Options, [bool] $LineShow = $true, [ValidateSet('straight', 'smooth', 'stepline')][string[]] $LineCurve, [int[]] $LineWidth, [ValidateSet('butt', 'square', 'round')][string[]] $LineCap, [RGBColors[]] $LineColor, [int[]] $LineDash ) # LINE Definition $Options.stroke = [ordered] @{ show = $LineShow } if ($LineCurve.Count -gt 0) { $Options.stroke.curve = $LineCurve } if ($LineWidth.Count -gt 0) { $Options.stroke.width = $LineWidth } if ($LineColor.Count -gt 0) { $Options.stroke.colors = @(ConvertFrom-Color -Color $LineColor) } if ($LineCap.Count -gt 0) { $Options.stroke.lineCap = $LineCap } if ($LineDash.Count -gt 0) { $Options.stroke.dashArray = $LineDash } } <# theme: { mode: 'light', palette: 'palette1', monochrome: { enabled: false, color: '#255aee', shadeTo: 'light', shadeIntensity: 0.65 }, } #> function New-ChartInternalTheme { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [ValidateSet('light', 'dark')][string] $Mode, [ValidateSet( 'palette1', 'palette2', 'palette3', 'palette4', 'palette5', 'palette6', 'palette7', 'palette8', 'palette9', 'palette10' ) ][string] $Palette = 'palette1', [switch] $Monochrome, [RGBColors] $Color = [RGBColors]::DodgerBlue, [ValidateSet('light', 'dark')][string] $ShadeTo = 'light', [double] $ShadeIntensity = 0.65 ) $RGBColor = ConvertFrom-Color -Color $Color $Options.theme = [ordered] @{ mode = $Mode palette = $Palette monochrome = [ordered] @{ enabled = $Monochrome.IsPresent color = $RGBColor shadeTo = $ShadeTo shadeIntensity = $ShadeIntensity } } } function New-ChartInternalTitle { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [string] $Title, [ValidateSet('center', 'left', 'right', 'default')][string] $TitleAlignment = 'default' ) # title $Options.title = [ordered] @{ } if ($TitleText -ne '') { $Options.title.text = $Title } if ($TitleAlignment -ne 'default') { $Options.title.align = $TitleAlignment } } function New-ChartInternalToolbar { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [bool] $Show = $false, [bool] $Download = $false, [bool] $Selection = $false, [bool] $Zoom = $false, [bool] $ZoomIn = $false, [bool] $ZoomOut = $false, [bool] $Pan = $false, [bool] $Reset = $false, [ValidateSet('zoom', 'selection', 'pan')][string] $AutoSelected = 'zoom' ) $Options.chart.toolbar = [ordered] @{ show = $show tools = [ordered] @{ download = $Download selection = $Selection zoom = $Zoom zoomin = $ZoomIn zoomout = $ZoomOut pan = $Pan reset = $Reset } autoSelected = $AutoSelected } } function New-ChartInternalZoom { [CmdletBinding()] param( [System.Collections.IDictionary] $Options, [switch] $Enabled ) if ($Enabled) { $Options.chart.zoom = @{ type = 'x' enabled = $Enabled.IsPresent } } } function New-ChartLegend { [alias('ChartLegend')] [CmdletBinding()] param( [Array] $Names, [ValidateSet('top', 'topRight', 'left', 'right', 'bottom', 'default')][string] $LegendPosition = 'default', [RGBColors[]] $Color ) #$Colors = [System.Collections.Generic.List[RGBColors]]::new() #foreach ($_ in $Color) { # $Colors.Add($Color) #} [PSCustomObject] @{ ObjectType = 'Legend' Names = $Names LegendPosition = $LegendPosition Color = $Color } } function New-ChartLine { [alias('ChartLine')] [CmdletBinding()] param( [string] $Name, [object] $Value, [RGBColors] $Color, [ValidateSet('straight', 'smooth', 'stepline')] $Curve = 'straight', [int] $Width = 6, [ValidateSet('butt', 'square', 'round')][string] $Cap = 'butt', [int] $Dash = 0 ) [PSCustomObject] @{ ObjectType = 'Line' Name = $Name Value = $Value LineColor = $Color LineCurve = $Curve LineWidth = $Width LineCap = $Cap LineDash = $Dash } } function New-ChartPie { [alias('ChartPie')] [CmdletBinding()] param( [string] $Name, [object] $Value, [RGBColors] $Color ) [PSCustomObject] @{ ObjectType = 'Pie' Name = $Name Value = $Value Color = $Color } } function New-ChartRadial { [alias('ChartRadial')] [CmdletBinding()] param( [string] $Name, [object] $Value, [RGBColors] $Color ) [PSCustomObject] @{ ObjectType = 'Radial' Name = $Name Value = $Value Color = $Color } } function New-ChartSpark { [alias('ChartSpark')] [CmdletBinding()] param( [string] $Name, [object] $Value, [RGBColors] $Color ) [PSCustomObject] @{ ObjectType = 'Spark' Name = $Name Value = $Value Color = $Color } } function New-ChartTheme { [alias('ChartTheme')] [CmdletBinding()] param( [ValidateSet('light', 'dark')][string] $Mode = 'light', [ValidateSet( 'palette1', 'palette2', 'palette3', 'palette4', 'palette5', 'palette6', 'palette7', 'palette8', 'palette9', 'palette10' ) ][string] $Palette = 'palette1', [switch] $Monochrome, [RGBColors] $Color = [RGBColors]::DodgerBlue, [ValidateSet('light', 'dark')][string] $ShadeTo = 'light', [double] $ShadeIntensity = 0.65 ) [PSCustomObject] @{ ObjectType = 'Theme' Theme = @{ Mode = $Mode Palette = $Palette Monochrome = $Monochrome.IsPresent Color = $Color ShadeTo = $ShadeTo ShadeIntensity = $ShadeIntensity } } } function New-ChartToolbar { [alias('ChartToolbar')] [CmdletBinding()] param( [switch] $Download, [switch] $Selection, [switch] $Zoom, [switch] $ZoomIn, [switch] $ZoomOut, [switch] $Pan, [switch] $Reset, [ValidateSet('zoom', 'selection', 'pan')][string] $AutoSelected = 'zoom' ) [PSCustomObject] @{ ObjectType = 'Toolbar' Toolbar = @{ #Show = $Show.IsPresent #tools = [ordered] @{ download = $Download.IsPresent selection = $Selection.IsPresent zoom = $Zoom.IsPresent zoomin = $ZoomIn.IsPresent zoomout = $ZoomOut.IsPresent pan = $Pan.IsPresent reset = $Reset.IsPresent #} autoSelected = $AutoSelected } } } function New-DiagramEvent { [CmdletBinding()] param( #[switch] $FadeSearch, [string] $ID, [nullable[int]] $ColumnID ) $Object = [PSCustomObject] @{ Type = 'DiagramEvent' Settings = @{ # OnClick = $OnClick.IsPresent ID = $ID # FadeSearch = $FadeSearch.IsPresent ColumnID = $ColumnID } } $Object } function New-DiagramInternalEvent { [CmdletBinding()] param( #[switch] $OnClick, [string] $ID, #[switch] $FadeSearch, [nullable[int]] $ColumnID ) # not ready $FadeSearch = $false if ($FadeSearch) { $Event = @" var table = `$('#$ID').DataTable(); //table.search(params.nodes).draw(); table.rows(':visible').every(function (rowIdx, tableLoop, rowLoop) { var present = true; if (params.nodes) { present = table.row(rowIdx).data().some(function (v) { return v.match(new RegExp(params.nodes, 'i')) != null; }); } `$(table.row(rowIdx).node()).toggleClass('notMatched', !present); }); "@ } else { if ($null -ne $ColumnID) { $Event = @" var table = `$('#$ID').DataTable(); if (findValue != '') { table.columns($ColumnID).search("^" + findValue + "$", true, false, true).draw(); } else { table.columns($ColumnID).search('').draw(); } if (table.page.info().recordsDisplay == 0) { table.columns($ColumnID).search('').draw(); } "@ } else { $Event = @" var table = `$('#$ID').DataTable(); if (findValue != '') { table.search("^" + findValue + "$", true, false, true).draw(); } else { table.search('').draw(); } if (table.page.info().recordsDisplay == 0) { table.search('').draw(); } "@ } } $Event } function New-DiagramLink { [alias('DiagramEdge', 'DiagramEdges', 'New-DiagramEdge', 'DiagramLink')] [CmdletBinding()] param( [string[]] $From, [string[]] $To, [string] $Label, [nullable[bool]] $ArrowsToEnabled, [nullable[int]] $ArrowsToScaleFacto, [ValidateSet('arrow', 'bar', 'circle')][string] $ArrowsToType, [nullable[bool]] $ArrowsMiddleEnabled, [nullable[int]]$ArrowsMiddleScaleFactor, [ValidateSet('arrow', 'bar', 'circle')][string] $ArrowsMiddleType, [nullable[bool]] $ArrowsFromEnabled, [nullable[int]] $ArrowsFromScaleFactor, [ValidateSet('arrow', 'bar', 'circle')][string] $ArrowsFromType, [nullable[bool]]$ArrowStrikethrough, [nullable[bool]] $Chosen, [RGBColors] $Color = [RGBColors]::None, [RGBColors] $ColorHighlight = [RGBColors]::None, [RGBColors] $ColorHover = [RGBColors]::None, [ValidateSet('true', 'false', 'from', 'to', 'both')][string]$ColorInherit, [nullable[double]] $ColorOpacity, # range between 0 and 1 [nullable[bool]] $Dashes, [string] $Length, [RGBColors] $FontColor = [RGBColors]::None, [nullable[int]] $FontSize, #// px [string] $FontName, [RGBColors] $FontBackground = [RGBColors]::None, [nullable[int]] $FontStrokeWidth, #// px [RGBColors] $FontStrokeColor = [RGBColors]::None, [ValidateSet('center', 'left')][string] $FontAlign, [ValidateSet('false', 'true', 'markdown', 'html')][string]$FontMulti, [nullable[int]] $FontVAdjust, [nullable[int]] $WidthConstraint ) $Object = [PSCustomObject] @{ Type = 'DiagramLink' Settings = @{ from = $From to = $To } Edges = @{ label = $Label length = $Length arrows = [ordered]@{ to = [ordered]@{ enabled = $ArrowsToEnabled scaleFactor = $ArrowsToScaleFactor type = $ArrowsToType } middle = [ordered]@{ enabled = $ArrowsMiddleEnabled scaleFactor = $ArrowsMiddleScaleFactor type = $ArrowsMiddleType } from = [ordered]@{ enabled = $ArrowsFromEnabled scaleFactor = $ArrowsFromScaleFactor type = $ArrowsFromType } } arrowStrikethrough = $ArrowStrikethrough chosen = $Chosen color = [ordered]@{ color = ConvertFrom-Color -Color $Color highlight = ConvertFrom-Color -Color $ColorHighlight hover = ConvertFrom-Color -Color $ColorHover inherit = $ColorInherit opacity = $ColorOpacity } font = [ordered]@{ color = ConvertFrom-Color -Color $FontColor size = $FontSize face = $FontName background = ConvertFrom-Color -Color $FontBackground strokeWidth = $FontStrokeWidth strokeColor = ConvertFrom-Color -Color $FontStrokeColor align = $FontAlign multi = $FontMulti vadjust = $FontVAdjust } dashes = $Dashes widthConstraint = $WidthConstraint } } Remove-EmptyValues -Hashtable $Object.Settings -Recursive Remove-EmptyValues -Hashtable $Object.Edges -Recursive -Rerun 2 $Object } <# // these are all options in full. var options = { edges:{ arrows: { to: {enabled: false, scaleFactor:1, type:'arrow'}, middle: {enabled: false, scaleFactor:1, type:'arrow'}, from: {enabled: false, scaleFactor:1, type:'arrow'} }, arrowStrikethrough: true, chosen: true, color: { color:'#848484', highlight:'#848484', hover: '#848484', inherit: 'from', opacity:1.0 }, dashes: false, font: { color: '#343434', size: 14, // px face: 'arial', background: 'none', strokeWidth: 2, // px strokeColor: '#ffffff', align: 'horizontal', multi: false, vadjust: 0, bold: { color: '#343434', size: 14, // px face: 'arial', vadjust: 0, mod: 'bold' }, ital: { color: '#343434', size: 14, // px face: 'arial', vadjust: 0, mod: 'italic', }, boldital: { color: '#343434', size: 14, // px face: 'arial', vadjust: 0, mod: 'bold italic' }, mono: { color: '#343434', size: 15, // px face: 'courier new', vadjust: 2, mod: '' } }, hidden: false, hoverWidth: 1.5, label: undefined, labelHighlightBold: true, length: undefined, physics: true, scaling:{ min: 1, max: 15, label: { enabled: true, min: 14, max: 30, maxVisible: 30, drawThreshold: 5 }, customScalingFunction: function (min,max,total,value) { if (max === min) { return 0.5; } else { var scale = 1 / (max - min); return Math.max(0,(value - min)*scale); } } }, selectionWidth: 1, selfReferenceSize:20, shadow:{ enabled: false, color: 'rgba(0,0,0,0.5)', size:10, x:5, y:5 }, smooth: { enabled: true, type: "dynamic", roundness: 0.5 }, title:undefined, value: undefined, width: 1, widthConstraint: false } } network.setOptions(options); #> function New-DiagramNode { [alias('DiagramNode')] [CmdLetBinding(DefaultParameterSetName = 'Shape')] param( [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][ScriptBlock] $TextBox, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][string] $Id, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")] [string] $Label, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][string[]] $To, [parameter(ParameterSetName = "Shape")][string][ValidateSet( 'circle', 'dot', 'diamond', 'ellipse', 'database', 'box', 'square', 'triangle', 'triangleDown', 'text', 'star', 'hexagon')] $Shape, [parameter(ParameterSetName = "Image")][ValidateSet('squareImage', 'circularImage')][string] $ImageType, [parameter(ParameterSetName = "Image")][uri] $Image, #[string] $BrokenImage, #[string] $ImagePadding, #[string] $ImagePaddingLeft, #[string] $ImagePaddingRight, #[string] $ImagePaddingTop, #[string] $ImagePaddingBottom, #[string] $UseImageSize, #[alias('BackgroundColor')][RGBColors] $Color, #[RGBColors] $Border, #[RGBColors] $HighlightBackground, #[RGBColors] $HighlightBorder, #[RGBColors] $HoverBackground, #[RGBColors] $HoverBorder, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][nullable[int]] $BorderWidth, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][nullable[int]] $BorderWidthSelected, [parameter(ParameterSetName = "Image")][string] $BrokenImages, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][nullable[bool]] $Chosen, [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][RGBColors] $ColorBorder = [RGBColors]::None, [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][RGBColors] $ColorBackground = [RGBColors]::None, [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][RGBColors] $ColorHighlightBorder = [RGBColors]::None, [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][RGBColors] $ColorHighlightBackground = [RGBColors]::None, [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][RGBColors] $ColorHoverBorder = [RGBColors]::None, [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][RGBColors] $ColorHoverBackground = [RGBColors]::None, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][nullable[bool]]$FixedX, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][nullable[bool]]$FixedY, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][RGBColors] $FontColor = [RGBColors]::None, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][nullable[int]] $FontSize, #// px [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][string] $FontName, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][RGBColors] $FontBackground = [RGBColors]::None, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][nullable[int]] $FontStrokeWidth, #// px [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][RGBColors] $FontStrokeColor = [RGBColors]::None, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][ValidateSet('center', 'left')][string] $FontAlign, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][ValidateSet('false', 'true', 'markdown', 'html')][string]$FontMulti, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][nullable[int]] $FontVAdjust, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][nullable[int]] $Size, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][nullable[int]] $X, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][nullable[int]] $Y, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")][switch] $IconAsImage, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")][RGBColors] $IconColor = [RGBColors]::None, # 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, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][nullable[int]] $Level, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][nullable[int]] $HeightConstraintMinimum, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][ValidateSet('top', 'middle', 'bottom')][string] $HeightConstraintVAlign, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][nullable[int]] $WidthConstraintMinimum, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [parameter(ParameterSetName = "Image")] [parameter(ParameterSetName = "Shape")][nullable[int]] $WidthConstraintMaximum ) if (-not $Label) { Write-Warning 'New-DiagramNode - Label is required. Skipping node.' return } $Object = [PSCustomObject] @{ Type = 'DiagramNode' Settings = @{ } Edges = @{ } } $Icon = @{ } # Reset value, just in case # If ID is not defined use label if (-not $ID) { $ID = $Label } if ($IconBrands -or $IconRegular -or $IconSolid) { if ($IconBrands) { if (-not $IconAsImage) { # Workaround using image for Fonts # https://use.fontawesome.com/releases/v5.11.2/svgs/brands/accessible-icon.svg <# Until all Icons work, using images instead. Currently only Brands work fine / Solid/Regular is weird #> $NodeShape = 'icon' $icon = @{ face = '"Font Awesome 5 Brands"' code = -join ('\u', $Global:HTMLIcons.FontAwesomeBrands[$IconBrands]) # "\uf007" color = ConvertFrom-Color -Color $IconColor weight = 'bold' } } else { $NodeShape = 'image' $Image = -join ($Script:Configuration.Features.FontsAwesome.Other.Link, 'brands/', $IconBrands, '.svg') } } elseif ($IconRegular) { if (-not $IconAsImage) { $NodeShape = 'icon' $icon = @{ face = '"Font Awesome 5 Free"' code = -join ('\u', $Global:HTMLIcons.FontAwesomeRegular[$IconRegular]) # "\uf007" color = ConvertFrom-Color -Color $IconColor weight = 'bold' } } else { $NodeShape = 'image' $Image = -join ($Script:Configuration.Features.FontsAwesome.Other.Link, 'regular/', $IconRegular, '.svg') } } else { if (-not $IconAsImage) { $NodeShape = 'icon' $icon = @{ face = '"Font Awesome 5 Free"' code = -join ('\u', $Global:HTMLIcons.FontAwesomeSolid[$IconSolid]) # "\uf007" color = ConvertFrom-Color -Color $IconColor weight = 'bold' } } else { $NodeShape = 'image' $Image = -join ($Script:Configuration.Features.FontsAwesome.Other.Link, 'solid/', $IconSolid, '.svg') } } } elseif ($Image) { if ($ImageType -eq 'squareImage') { $NodeShape = 'image' } else { $NodeShape = 'circularImage' } } else { $NodeShape = $Shape } if ($To) { $Object.Edges = @{ from = if ($To) { $Id } else { '' } to = if ($To) { $To } else { '' } } } $Object.Settings = [ordered] @{ id = $Id label = $Label shape = $NodeShape image = $Image icon = $icon level = $Level borderWidth = $BorderWidth borderWidthSelected = $BorderWidthSelected brokenImage = $BrokenImage chosen = $Chosen color = [ordered]@{ border = ConvertFrom-Color -Color $ColorBorder background = ConvertFrom-Color -Color $ColorBackground highlight = [ordered]@{ border = ConvertFrom-Color -Color $ColorHighlightBorder background = ConvertFrom-Color -Color $ColorHighlightBackground } hover = [ordered]@{ border = ConvertFrom-Color -Color $ColorHoverBorder background = ConvertFrom-Color -Color $ColorHoverBackground } } fixed = [ordered]@{ x = $FixedX y = $FixedY } font = [ordered]@{ color = ConvertFrom-Color -Color $FontColor size = $FontSize face = $FontName background = ConvertFrom-Color -Color $FontBackground strokeWidth = $FontStrokeWidth strokeColor = ConvertFrom-Color -Color $FontStrokeColor align = $FontAlign multi = $FontMulti vadjust = $FontVAdjust } size = $Size heightConstraint = @{ minimum = $HeightConstraintMinimum valign = $HeightConstraintVAlign } widthConstraint = @{ minimum = $WidthConstraintMinimum maximum = $WidthConstraintMaximum } x = $X y = $Y } Remove-EmptyValues -Hashtable $Object.Settings -Recursive -Rerun 2 Remove-EmptyValues -Hashtable $Object.Edges -Recursive $Object } <# // these are all options in full. var options = { nodes:{ borderWidth: 1, borderWidthSelected: 2, brokenImage:undefined, chosen: true, color: { border: '#2B7CE9', background: '#97C2FC', highlight: { border: '#2B7CE9', background: '#D2E5FF' }, hover: { border: '#2B7CE9', background: '#D2E5FF' } }, fixed: { x:false, y:false }, font: { color: '#343434', size: 14, // px face: 'arial', background: 'none', strokeWidth: 0, // px strokeColor: '#ffffff', align: 'center', multi: false, vadjust: 0, bold: { color: '#343434', size: 14, // px face: 'arial', vadjust: 0, mod: 'bold' }, ital: { color: '#343434', size: 14, // px face: 'arial', vadjust: 0, mod: 'italic', }, boldital: { color: '#343434', size: 14, // px face: 'arial', vadjust: 0, mod: 'bold italic' }, mono: { color: '#343434', size: 15, // px face: 'courier new', vadjust: 2, mod: '' } }, group: undefined, heightConstraint: false, hidden: false, icon: { face: 'FontAwesome', code: undefined, size: 50, //50, color:'#2B7CE9' }, image: undefined, imagePadding: { left: 0, top: 0, bottom: 0, right: 0 }, label: undefined, labelHighlightBold: true, level: undefined, mass: 1, physics: true, scaling: { min: 10, max: 30, label: { enabled: false, min: 14, max: 30, maxVisible: 30, drawThreshold: 5 }, customScalingFunction: function (min,max,total,value) { if (max === min) { return 0.5; } else { let scale = 1 / (max - min); return Math.max(0,(value - min)*scale); } } }, shadow:{ enabled: false, color: 'rgba(0,0,0,0.5)', size:10, x:5, y:5 }, shape: 'ellipse', shapeProperties: { borderDashes: false, // only for borders borderRadius: 6, // only for box shape interpolation: false, // only for image and circularImage shapes useImageSize: false, // only for image and circularImage shapes useBorderWithImage: false // only for image shape } size: 25, title: undefined, value: undefined, widthConstraint: false, x: undefined, y: undefined } } network.setOptions(options) #> function New-DiagramOptionsInteraction { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER DragNodes Parameter description .PARAMETER DragView Parameter description .PARAMETER HideEdgesOnDrag Parameter description .PARAMETER HideEdgesOnZoom Parameter description .PARAMETER HideNodesOnDrag Parameter description .PARAMETER Hover Parameter description .PARAMETER HoverConnectedEdges Parameter description .PARAMETER KeyboardEnabled Parameter description .PARAMETER KeyboardSpeedX Parameter description .PARAMETER KeyboardSpeedY Parameter description .PARAMETER KeyboardSpeedZoom Parameter description .PARAMETER KeyboardBindToWindow Parameter description .PARAMETER Multiselect Parameter description .PARAMETER NavigationButtons Parameter description .PARAMETER Selectable Parameter description .PARAMETER SelectConnectedEdges Parameter description .PARAMETER TooltipDelay Parameter description .PARAMETER ZoomView Parameter description .EXAMPLE An example .NOTES Based on options https://visjs.github.io/vis-network/docs/network/interaction.html# #> [alias('DiagramOptionsInteraction')] [CmdletBinding()] param( [nullable[bool]] $DragNodes, [nullable[bool]] $DragView, [nullable[bool]] $HideEdgesOnDrag, [nullable[bool]] $HideEdgesOnZoom, [nullable[bool]] $HideNodesOnDrag, [nullable[bool]] $Hover, [nullable[bool]] $HoverConnectedEdges, [nullable[bool]] $KeyboardEnabled, [nullable[int]] $KeyboardSpeedX, [nullable[int]] $KeyboardSpeedY, [nullable[decimal]] $KeyboardSpeedZoom, [nullable[bool]] $KeyboardBindToWindow, [nullable[bool]] $Multiselect, [nullable[bool]] $NavigationButtons, [nullable[bool]] $Selectable, [nullable[bool]] $SelectConnectedEdges, [nullable[int]] $TooltipDelay, [nullable[bool]] $ZoomView ) $Object = [PSCustomObject] @{ Type = 'DiagramOptionsInteraction' Settings = @{ interaction = [ordered] @{ dragNodes = $DragNodes dragView = $DragView hideEdgesOnDrag = $HideEdgesOnDrag hideEdgesOnZoom = $HideEdgesOnZoom hideNodesOnDrag = $HideNodesOnDrag hover = $Hover hoverConnectedEdges = $HoverConnectedEdges keyboard = @{ enabled = $KeyboardEnabled speed = @{ x = $KeyboardSpeedX y = $KeyboardSpeedY zoom = $KeyboardSpeedZoom } bindToWindow = $KeyboardBindToWindow } multiselect = $Multiselect navigationButtons = $NavigationButtons selectable = $Selectable selectConnectedEdges = $SelectConnectedEdges tooltipDelay = $TooltipDelay zoomView = $ZoomView } } } Remove-EmptyValues -Hashtable $Object.Settings -Recursive -Rerun 2 $Object } <# var options = { nodes: { borderWidth:2, borderWidthSelected: 8, size:24, color: { border: 'white', background: 'black', highlight: { border: 'black', background: 'white' }, hover: { border: 'orange', background: 'grey' } }, font:{color:'#eeeeee'}, shapeProperties: { useBorderWithImage:true } }, edges: { color: 'lightgray' } }; #> # https://visjs.github.io/vis-network/docs/network/edges.html# function New-DiagramOptionsLayout { [alias('DiagramOptionsLayout')] [CmdletBinding()] param( [nullable[int]] $RandomSeed, [nullable[bool]] $ImprovedLayout, [nullable[int]] $ClusterThreshold , [nullable[bool]] $HierarchicalEnabled, [nullable[int]] $HierarchicalLevelSeparation, [nullable[int]] $HierarchicalNodeSpacing, [nullable[int]] $HierarchicalTreeSpacing, [nullable[bool]] $HierarchicalBlockShifting, [nullable[bool]] $HierarchicalEdgeMinimization, [nullable[bool]] $HierarchicalParentCentralization, [ValidateSet('FromUpToDown', 'FromDownToUp', 'FromLeftToRight', 'FromRigthToLeft')][string] $HierarchicalDirection, [ValidateSet('hubsize', 'directed')][string] $HierarchicalSortMethod ) $Direction = @{ FromUpToDown = 'UD' FromDownToUp = 'DU' FromLeftToRight = 'LR' FromRigthToLeft = 'RL' } $Object = [PSCustomObject] @{ Type = 'DiagramOptionsLayout' Settings = @{ layout = [ordered] @{ randomSeed = $RandomSeed improvedLayout = $ImprovedLayout clusterThreshold = $ClusterThreshold hierarchical = @{ enabled = $HierarchicalEnabled levelSeparation = $HierarchicalLevelSeparation nodeSpacing = $HierarchicalNodeSpacing treeSpacing = $HierarchicalTreeSpacing blockShifting = $HierarchicalBlockShifting edgeMinimization = $HierarchicalEdgeMinimization parentCentralization = $HierarchicalParentCentralization direction = $Direction[$HierarchicalDirection] # // UD, DU, LR, RL sortMethod = $HierarchicalSortMethod #// hubsize, directed } } } } Remove-EmptyValues -Hashtable $Object.Settings -Recursive -Rerun 2 $Object } <# // these are all options in full. var options = { layout = { randomSeed = undefined, improvedLayout = true, clusterThreshold = 150, hierarchical = { enabled = false, levelSeparation = 150, nodeSpacing = 100, treeSpacing = 200, blockShifting = true, edgeMinimization = true, parentCentralization = true, direction = 'UD', // UD, DU, LR, RL sortMethod = 'hubsize' // hubsize, directed } } } network.setOptions(options); #> function New-DiagramOptionsLinks { [alias('DiagramOptionsEdges', 'New-DiagramOptionsEdges', 'DiagramOptionsLinks')] [CmdletBinding()] param( [nullable[bool]] $ArrowsToEnabled, [nullable[int]] $ArrowsToScaleFactor, [ValidateSet('arrow', 'bar', 'circle')][string] $ArrowsToType, [nullable[bool]] $ArrowsMiddleEnabled, [nullable[int]] $ArrowsMiddleScaleFactor, [ValidateSet('arrow', 'bar', 'circle')][string] $ArrowsMiddleType, [nullable[bool]] $ArrowsFromEnabled, [nullable[int]] $ArrowsFromScaleFactor, [ValidateSet('arrow', 'bar', 'circle')][string] $ArrowsFromType, [nullable[bool]] $ArrowStrikethrough, [nullable[bool]] $Chosen, [RGBColors] $Color = [RGBColors]::None, [RGBColors] $ColorHighlight = [RGBColors]::None, [RGBColors] $ColorHover = [RGBColors]::None, [ValidateSet('true', 'false', 'from', 'to', 'both')][string]$ColorInherit, [nullable[double]] $ColorOpacity, # range between 0 and 1 [nullable[bool]] $Dashes, [string] $Length, [RGBColors] $FontColor = [RGBColors]::None, [nullable[int]] $FontSize, #// px [string] $FontName, [RGBColors] $FontBackground = [RGBColors]::None, [nullable[int]] $FontStrokeWidth, #// px [RGBColors] $FontStrokeColor = [RGBColors]::None, [ValidateSet('center', 'left')][string] $FontAlign, [ValidateSet('false', 'true', 'markdown', 'html')][string]$FontMulti, [nullable[int]] $FontVAdjust, [nullable[int]] $WidthConstraint ) $Object = [PSCustomObject] @{ Type = 'DiagramOptionsEdges' Settings = @{ edges = [ordered] @{ length = $Length arrows = [ordered]@{ to = [ordered]@{ enabled = $ArrowsToEnabled scaleFactor = $ArrowsToScaleFactor type = $ArrowsToType } middle = [ordered]@{ enabled = $ArrowsMiddleEnabled scaleFactor = $ArrowsMiddleScaleFactor type = $ArrowsMiddleType } from = [ordered]@{ enabled = $ArrowsFromEnabled scaleFactor = $ArrowsFromScaleFactor type = $ArrowsFromType } } arrowStrikethrough = $ArrowStrikethrough chosen = $Chosen color = [ordered]@{ color = ConvertFrom-Color -Color $Color highlight = ConvertFrom-Color -Color $ColorHighlight hover = ConvertFrom-Color -Color $ColorHover inherit = $ColorInherit opacity = $ColorOpacity } font = [ordered]@{ color = ConvertFrom-Color -Color $FontColor size = $FontSize face = $FontName background = ConvertFrom-Color -Color $FontBackground strokeWidth = $FontStrokeWidth strokeColor = ConvertFrom-Color -Color $FontStrokeColor align = $FontAlign multi = $FontMulti vadjust = $FontVAdjust } dashes = $Dashes widthConstraint = $WidthConstraint } } } Remove-EmptyValues -Hashtable $Object.Settings -Recursive -Rerun 2 $Object } <# // these are all options in full. var options = { edges:{ arrows: { to: {enabled: false, scaleFactor:1, type:'arrow'}, middle: {enabled: false, scaleFactor:1, type:'arrow'}, from: {enabled: false, scaleFactor:1, type:'arrow'} }, arrowStrikethrough: true, chosen: true, color: { color:'#848484', highlight:'#848484', hover: '#848484', inherit: 'from', opacity:1.0 }, dashes: false, font: { color: '#343434', size: 14, // px face: 'arial', background: 'none', strokeWidth: 2, // px strokeColor: '#ffffff', align: 'horizontal', multi: false, vadjust: 0, bold: { color: '#343434', size: 14, // px face: 'arial', vadjust: 0, mod: 'bold' }, ital: { color: '#343434', size: 14, // px face: 'arial', vadjust: 0, mod: 'italic', }, boldital: { color: '#343434', size: 14, // px face: 'arial', vadjust: 0, mod: 'bold italic' }, mono: { color: '#343434', size: 15, // px face: 'courier new', vadjust: 2, mod: '' } }, hidden: false, hoverWidth: 1.5, label: undefined, labelHighlightBold: true, length: undefined, physics: true, scaling:{ min: 1, max: 15, label: { enabled: true, min: 14, max: 30, maxVisible: 30, drawThreshold: 5 }, customScalingFunction: function (min,max,total,value) { if (max === min) { return 0.5; } else { var scale = 1 / (max - min); return Math.max(0,(value - min)*scale); } } }, selectionWidth: 1, selfReferenceSize:20, shadow:{ enabled: false, color: 'rgba(0,0,0,0.5)', size:10, x:5, y:5 }, smooth: { enabled: true, type: "dynamic", roundness: 0.5 }, title:undefined, value: undefined, width: 1, widthConstraint: false } } network.setOptions(options); #> function New-DiagramOptionsManipulation { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER InitiallyActive Parameter description .PARAMETER AddNode Parameter description .PARAMETER AddEdge Parameter description .PARAMETER EditNode Parameter description .PARAMETER EditEdge Parameter description .PARAMETER DeleteNode Parameter description .PARAMETER DeleteEdge Parameter description .EXAMPLE An example .NOTES Based on https://visjs.github.io/vis-network/docs/network/manipulation.html# It's incomplete #> [alias('DiagramOptionsManipulation')] [CmdletBinding()] param( [nullable[bool]] $InitiallyActive, [nullable[bool]] $AddNode, [nullable[bool]] $AddEdge, [nullable[bool]] $EditNode, [nullable[bool]] $EditEdge, [nullable[bool]] $DeleteNode, [nullable[bool]] $DeleteEdge ) $Object = [PSCustomObject] @{ Type = 'DiagramOptionsManipulation' Settings = @{ manipulation = [ordered] @{ enabled = $true initiallyActive = $InitiallyActive addNode = $AddNode addEdge = $AddEdge editNode = $EditNode editEdge = $EditEdge deleteNode = $DeleteNode deleteEdge = $DeleteEdge } } } Remove-EmptyValues -Hashtable $Object.Settings -Recursive $Object } function New-DiagramOptionsNodes { [alias('DiagramOptionsNodes')] [CmdletBinding()] param( [nullable[int]] $BorderWidth, [nullable[int]] $BorderWidthSelected, [string] $BrokenImage, [nullable[bool]] $Chosen, [RGBColors] $ColorBorder = [RGBColors]::None, [RGBColors] $ColorBackground = [RGBColors]::None, [RGBColors] $ColorHighlightBorder = [RGBColors]::None, [RGBColors] $ColorHighlightBackground = [RGBColors]::None, [RGBColors] $ColorHoverBorder = [RGBColors]::None, [RGBColors] $ColorHoverBackground = [RGBColors]::None, [nullable[bool]] $FixedX, [nullable[bool]] $FixedY, [RGBColors] $FontColor = [RGBColors]::None, [nullable[int]] $FontSize, #// px [string] $FontName, [RGBColors] $FontBackground = [RGBColors]::None, [nullable[int]] $FontStrokeWidth, #// px [RGBColors] $FontStrokeColor = [RGBColors]::None, [ValidateSet('center', 'left')][string] $FontAlign, [ValidateSet('false', 'true', 'markdown', 'html')][string]$FontMulti, [nullable[int]] $FontVAdjust, [nullable[int]] $Size, [parameter(ParameterSetName = "Shape")][string][ValidateSet( 'circle', 'dot', 'diamond', 'ellipse', 'database', 'box', 'square', 'triangle', 'triangleDown', 'text', 'star', 'hexagon')] $Shape, [nullable[int]] $HeightConstraintMinimum, [ValidateSet('top', 'middle', 'bottom')][string] $HeightConstraintVAlign, [nullable[int]] $WidthConstraintMinimum, [nullable[int]] $WidthConstraintMaximum, [nullable[int]] $Margin, [nullable[int]] $MarginTop, [nullable[int]] $MarginRight, [nullable[int]] $MarginBottom, [nullable[int]] $MarginLeft ) $Object = [PSCustomObject] @{ Type = 'DiagramOptionsNodes' Settings = @{ nodes = [ordered] @{ borderWidth = $BorderWidth borderWidthSelected = $BorderWidthSelected brokenImage = $BrokenImage chosen = $Chosen color = [ordered]@{ border = ConvertFrom-Color -Color $ColorBorder background = ConvertFrom-Color -Color $ColorBackground highlight = [ordered]@{ border = ConvertFrom-Color -Color $ColorHighlightBorder background = ConvertFrom-Color -Color $ColorHighlightBackground } hover = [ordered]@{ border = ConvertFrom-Color -Color $ColorHoverBorder background = ConvertFrom-Color -Color $ColorHoverBackground } } fixed = [ordered]@{ x = $FixedX y = $FixedY } font = [ordered]@{ color = ConvertFrom-Color -Color $FontColor size = $FontSize #// px face = $FontName background = ConvertFrom-Color -Color $FontBackground strokeWidth = $FontStrokeWidth #// px strokeColor = ConvertFrom-Color -Color $FontStrokeColor align = $FontAlign multi = $FontMulti vadjust = $FontVAdjust } heightConstraint = @{ minimum = $HeightConstraintMinimum valign = $HeightConstraintVAlign } size = $Size shape = $Shape widthConstraint = @{ minimum = $WidthConstraintMinimum maximum = $WidthConstraintMaximum } } } } if ($Margin) { $Object.Settings.nodes.margin = $Margin } else { $Object.Settings.nodes.margin = @{ top = $MarginTop right = $MarginRight bottom = $MarginBottom left = $MarginLeft } } Remove-EmptyValues -Hashtable $Object.Settings -Recursive -Rerun 2 $Object } <# // these are all options in full. var options = { nodes:{ borderWidth: 1, borderWidthSelected: 2, brokenImage:undefined, chosen: true, color: { border: '#2B7CE9', background: '#97C2FC', highlight: { border: '#2B7CE9', background: '#D2E5FF' }, hover: { border: '#2B7CE9', background: '#D2E5FF' } }, fixed: { x:false, y:false }, font: { color: '#343434', size: 14, // px face: 'arial', background: 'none', strokeWidth: 0, // px strokeColor: '#ffffff', align: 'center', multi: false, vadjust: 0, bold: { color: '#343434', size: 14, // px face: 'arial', vadjust: 0, mod: 'bold' }, ital: { color: '#343434', size: 14, // px face: 'arial', vadjust: 0, mod: 'italic', }, boldital: { color: '#343434', size: 14, // px face: 'arial', vadjust: 0, mod: 'bold italic' }, mono: { color: '#343434', size: 15, // px face: 'courier new', vadjust: 2, mod: '' } }, group: undefined, heightConstraint: false, hidden: false, icon: { face: 'FontAwesome', code: undefined, size: 50, //50, color:'#2B7CE9' }, image: undefined, imagePadding: { left: 0, top: 0, bottom: 0, right: 0 }, label: undefined, labelHighlightBold: true, level: undefined, mass: 1, physics: true, scaling: { min: 10, max: 30, label: { enabled: false, min: 14, max: 30, maxVisible: 30, drawThreshold: 5 }, customScalingFunction: function (min,max,total,value) { if (max === min) { return 0.5; } else { let scale = 1 / (max - min); return Math.max(0,(value - min)*scale); } } }, shadow:{ enabled: false, color: 'rgba(0,0,0,0.5)', size:10, x:5, y:5 }, shape: 'ellipse', shapeProperties: { borderDashes: false, // only for borders borderRadius: 6, // only for box shape interpolation: false, // only for image and circularImage shapes useImageSize: false, // only for image and circularImage shapes useBorderWithImage: false // only for image shape } size: 25, title: undefined, value: undefined, widthConstraint: false, x: undefined, y: undefined } } network.setOptions(options) #> function New-DiagramOptionsPhysics { [alias('DiagramOptionsPhysics')] [CmdletBinding()] param( [nullable[bool]] $Enabled, [nullable[bool]] $StabilizationEnabled, [nullable[int]] $Stabilizationiterations, [nullable[int]] $StabilizationupdateInterval, [nullable[bool]] $StabilizationonlyDynamicEdges, [nullable[bool]] $Stabilizationfit, [nullable[int]] $MaxVelocity, [nullable[int]] $MinVelocity, [nullable[int]] $Timestep, [nullable[bool]] $AdaptiveTimestep ) $Object = [PSCustomObject] @{ Type = 'DiagramOptionsPhysics' Settings = @{ physics = [ordered] @{ enabled = $Enabled stabilization = @{ enabled = $StabilizationEnabled iterations = $Stabilizationiterations updateInterval = $StabilizationupdateInterval onlyDynamicEdges = $StabilizationonlyDynamicEdges fit = $Stabilizationfit } maxVelocity = $MaxVelocity minVelocity = $MinVelocity timestep = $Timestep adaptiveTimestep = $AdaptiveTimestep } } } Remove-EmptyValues -Hashtable $Object.Settings -Recursive -Rerun 2 $Object } <# // these are all options in full. var options = { physics:{ enabled: true, barnesHut: { gravitationalConstant: -2000, centralGravity: 0.3, springLength: 95, springConstant: 0.04, damping: 0.09, avoidOverlap: 0 }, forceAtlas2Based: { gravitationalConstant: -50, centralGravity: 0.01, springConstant: 0.08, springLength: 100, damping: 0.4, avoidOverlap: 0 }, repulsion: { centralGravity: 0.2, springLength: 200, springConstant: 0.05, nodeDistance: 100, damping: 0.09 }, hierarchicalRepulsion: { centralGravity: 0.0, springLength: 100, springConstant: 0.01, nodeDistance: 120, damping: 0.09 }, maxVelocity: 50, minVelocity: 0.1, solver: 'barnesHut', stabilization: { enabled: true, iterations: 1000, updateInterval: 100, onlyDynamicEdges: false, fit: true }, timestep: 0.5, adaptiveTimestep: true } } network.setOptions(options); #> function New-HierarchicalTreeNode { [alias('New-HierarchicalTreeNode', 'HierarchicalTreeNode')] [CmdletBinding()] param( [string] $ID, [alias('Name')][string] $Label, [string] $Type = "Organism", [string] $Description, [string] $To ) if (-not $ID) { $ID = $Label } $Object = [PSCustomObject] @{ Type = 'TreeNode' Settings = [ordered] @{ "id" = $ID "parentId" = $To "name" = $Label #"type" = $Type "description" = $Description } } Remove-EmptyValues -Hashtable $Object.Settings -Recursive $Object } Function New-HTML { [alias('Dashboard')] [CmdletBinding()] param( [alias('Content')][Parameter(Position = 0)][ValidateNotNull()][ScriptBlock] $HtmlData = $(Throw "Have you put the open curly brace on the next line?"), [switch] $UseCssLinks, [switch] $UseJavaScriptLinks, [alias('Name', 'Title')][String] $TitleText, [string] $Author, [string] $DateFormat = 'yyyy-MM-dd HH:mm:ss', [int] $AutoRefresh, # save HTML options [Parameter(Mandatory = $false)][string]$FilePath, [alias('Show', 'Open')][Parameter(Mandatory = $false)][switch]$ShowHTML, [ValidateSet('Unknown', 'String', 'Unicode', 'Byte', 'BigEndianUnicode', 'UTF8', 'UTF7', 'UTF32', 'Ascii', 'Default', 'Oem', 'BigEndianUTF32')] $Encoding = 'UTF8' ) [string] $CurrentDate = (Get-Date).ToString($DateFormat) $Script:HTMLSchema = @{ TabsHeaders = [System.Collections.Generic.List[System.Collections.IDictionary]]::new() # tracks / stores headers TabsHeadersNested = [System.Collections.Generic.List[System.Collections.IDictionary]]::new() # tracks / stores headers Features = [ordered] @{ } # tracks features for CSS/JS implementation Charts = [System.Collections.Generic.List[string]]::new() Diagrams = [System.Collections.Generic.List[string]]::new() Logos = "" # Tab settings TabOptions = @{ SlimTabs = $false } CustomCSS = [System.Collections.Generic.List[Array]]::new() } [Array] $TempOutputHTML = Invoke-Command -ScriptBlock $HtmlData $Features = Get-FeaturesInUse -PriorityFeatures 'JQuery', 'DataTables', 'Tabs' # this gets rid of any non-strings # it's added here to track nested tabs $OutputHTML = foreach ($_ in $TempOutputHTML) { if ($_ -isnot [System.Collections.IDictionary]) { $_ } } # This removes Nested Tabs from primary Tabs foreach ($_ in $Script:HTMLSchema.TabsHeadersNested) { $null = $Script:HTMLSchema.TabsHeaders.Remove($_) } $HTML = @( '<!DOCTYPE html>' #"<!-- saved from url=(0016)http://localhost -->" + "`r`n" New-HTMLTag -Tag 'html' { '<!-- HEADER -->' New-HTMLTag -Tag 'head' { New-HTMLTag -Tag 'meta' -Attributes @{ charset = "utf-8" } -SelfClosing #New-HTMLTag -Tag 'meta' -Attributes @{ 'http-equiv' = 'X-UA-Compatible'; content = 'IE=8' } -SelfClosing New-HTMLTag -Tag 'meta' -Attributes @{ name = 'viewport'; content = 'width=device-width, initial-scale=1' } -SelfClosing New-HTMLTag -Tag 'meta' -Attributes @{ name = 'author'; content = $Author } -SelfClosing New-HTMLTag -Tag 'meta' -Attributes @{ name = 'revised'; content = $CurrentDate } -SelfClosing New-HTMLTag -Tag 'title' { $TitleText } if ($Autorefresh -gt 0) { New-HTMLTag -Tag 'meta' -Attributes @{ 'http-equiv' = 'refresh'; content = $Autorefresh } -SelfClosing } Get-Resources -UseCssLinks:$true -UseJavaScriptLinks:$true -Location 'HeaderAlways' -Features Default, DefaultHeadings, Fonts, FontsAwesome Get-Resources -UseCssLinks:$false -UseJavaScriptLinks:$false -Location 'HeaderAlways' -Features Default, DefaultHeadings if ($null -ne $Features) { Get-Resources -UseCssLinks:$true -UseJavaScriptLinks:$true -Location 'HeaderAlways' -Features $Features -NoScript Get-Resources -UseCssLinks:$false -UseJavaScriptLinks:$false -Location 'HeaderAlways' -Features $Features -NoScript Get-Resources -UseCssLinks:$UseCssLinks -UseJavaScriptLinks:$UseJavaScriptLinks -Location 'Header' -Features $Features } } New-HTMLCustomCSS -Css $Script:HTMLSchema.CustomCSS '<!-- END HEADER -->' '<!-- BODY -->' New-HTMLTag -Tag 'body' { # Add logo if there is one $Script:HTMLSchema.Logos # Add tabs header if there is one if ($Script:HTMLSchema.TabsHeaders) { New-HTMLTabHead New-HTMLTag -Tag 'div' -Attributes @{ 'data-panes' = 'true' } { # Add remaining data $OutputHTML } } else { # Add remaining data $OutputHTML } # Add charts scripts if those are there foreach ($Chart in $Script:HTMLSchema.Charts) { $Chart } foreach ($Diagram in $Script:HTMLSchema.Diagrams) { $Diagram } '<!-- END BODY -->' '<!-- FOOTER -->' #New-HTMLTag -Tag 'footer' { if ($null -ne $Features) { # FooterAlways means we're not able to provide consistent output with and without links and we prefer those to be included # either as links or from file per required features Get-Resources -UseCssLinks:$true -UseJavaScriptLinks:$true -Location 'FooterAlways' -Features $Features Get-Resources -UseCssLinks:$false -UseJavaScriptLinks:$false -Location 'FooterAlways' -Features $Features # standard footer features Get-Resources -UseCssLinks:$UseCssLinks -UseJavaScriptLinks:$UseJavaScriptLinks -Location 'Footer' -Features $Features } } '<!-- END FOOTER -->' } ) if ($FilePath -ne '') { Save-HTML -HTML $HTML -FilePath $FilePath -ShowHTML:$ShowHTML -Encoding $Encoding } else { $HTML } } function New-HTMLAnchor { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER Name Parameter description .PARAMETER Id Parameter description .PARAMETER Target Parameter description .PARAMETER Class Parameter description .PARAMETER HrefLink Parameter description .PARAMETER OnClick Parameter description .PARAMETER Style Parameter description .PARAMETER Text Parameter description .EXAMPLE New-HTMLAnchor -Target _parent New-HTMLAnchor -Id "show_$RandomNumber" -Href '#' -OnClick "show('$RandomNumber');" -Style "color: #ffffff; display:none;" -Text 'Show' Output: <a target = "_parent" /> .NOTES General notes #> param( [alias('AnchorName')][string] $Name, [string] $Id, [string] $Target, # "_blank|_self|_parent|_top|framename" [string] $Class, [alias('Url', 'Link', 'UrlLink', 'Href')][string] $HrefLink, [string] $OnClick, [string] $Style, [alias('AnchorText', 'Value')][string] $Text ) $Attributes = [ordered]@{ 'id' = $Id 'name' = $Name 'class' = $Class 'target' = $Target 'href' = $HrefLink 'onclick' = $OnClick 'style' = $Style } New-HTMLTag -Tag 'a' -Attributes $Attributes { $Text } } function New-HTMLCalendar { [alias('Calendar')] [CmdletBinding()] param( [ScriptBlock] $CalendarSettings, [ValidateSet('interaction', 'dayGrid', 'timeGrid', 'list', 'rrule')][string[]] $Plugins = @('interaction', 'dayGrid', 'timeGrid', 'list', 'rrule'), [ValidateSet( 'prev', 'next', 'today', 'prevYear', 'nextYear', 'dayGridDay', 'dayGridWeek', 'dayGridMonth', 'timeGridWeek', 'timeGridDay', 'listDay', 'listWeek', 'listMonth', 'title' )][string[]] $HeaderLeft = @('prev', 'next', 'today'), [ValidateSet( 'prev', 'next', 'today', 'prevYear', 'nextYear', 'dayGridDay', 'dayGridWeek', 'dayGridMonth', 'timeGridWeek', 'timeGridDay', 'listDay', 'listWeek', 'listMonth', 'title' )][string[]]$HeaderCenter = 'title', [ValidateSet( 'prev', 'next', 'today', 'prevYear', 'nextYear', 'dayGridDay', 'dayGridWeek', 'dayGridMonth', 'timeGridWeek', 'timeGridDay', 'listDay', 'listWeek', 'listMonth', 'title' )][string[]] $HeaderRight = @('dayGridMonth', 'timeGridWeek', 'timeGridDay', 'listMonth'), [DateTime] $DefaultDate = (Get-Date), [bool] $NavigationLinks = $true, [bool] $NowIndicator = $true, [bool] $EventLimit = $true, [bool] $WeekNumbers = $true, [bool] $WeekNumbersWithinDays = $true, [bool] $Selectable = $true, [bool] $SelectMirror = $true, [switch] $BusinessHours, [switch] $Editable ) if (-not $Script:HTMLSchema.Features) { Write-Warning 'New-HTMLCalendar - Creation of HTML aborted. Most likely New-HTML is missing.' Exit } $Script:HTMLSchema.Features.FullCalendar = $true $Script:HTMLSchema.Features.FullCalendarCore = $true $Script:HTMLSchema.Features.FullCalendarDayGrid = $true $Script:HTMLSchema.Features.FullCalendarInteraction = $true $Script:HTMLSchema.Features.FullCalendarList = $true $Script:HTMLSchema.Features.FullCalendarRRule = $true $Script:HTMLSchema.Features.FullCalendarTimeGrid = $true $Script:HTMLSchema.Features.FullCalendarTimeLine = $true $Script:HTMLSchema.Features.Popper = $true $CalendarEvents = [System.Collections.Generic.List[System.Collections.IDictionary]]::new() [Array] $Settings = & $CalendarSettings foreach ($Object in $Settings) { if ($Object.Type -eq 'CalendarEvent') { $CalendarEvents.Add($Object.Settings) } } # Define HTML/Script [string] $ID = "Calendar-" + (Get-RandomStringName -Size 8) $Calendar = [ordered] @{ plugins = $Plugins header = @{ left = $HeaderLeft -join ',' center = $HeaderCenter -join ',' right = $HeaderRight -join ',' } defaultDate = '{0:yyyy-MM-dd}' -f ($DefaultDate) nowIndicator = $NowIndicator #now: '2018-02-13T09:25:00' // just for demo navLinks = $NavigationLinks #// can click day/week names to navigate views businessHours = $BusinessHours.IsPresent #// display business hours editable = $Editable.IsPresent events = $CalendarEvents eventLimit = $EventLimit weekNumbers = $WeekNumbers weekNumbersWithinDays = $WeekNumbersWithinDays weekNumberCalculation = 'ISO' selectable = $Selectable selectMirror = $SelectMirror buttonIcons = $false # // show the prev/next text #// customize the button names, #// otherwise they'd all just say "list" views = @{ listDay = @{ buttonText = 'list day' } listWeek = @{ buttonText = 'list week' } listMonth = @{ buttonText = 'list month' } } eventRender = 'ReplaceMe' } Remove-EmptyValues -Hashtable $Calendar -Recursive $CalendarJSON = $Calendar | ConvertTo-Json -Depth 7 # Adding function for ToolTips / need cleaner way $EventRender = @" eventRender: function (info) { var tooltip = new Tooltip(info.el, { title: info.event.extendedProps.description, placement: 'top', trigger: 'hover', container: 'body' }); } "@ if ($PSEdition -eq 'Desktop') { $TextToFind = '"eventRender": "ReplaceMe"' } else { $TextToFind = '"eventRender": "ReplaceMe"' } $CalendarJSON = $CalendarJSON.Replace($TextToFind, $EventRender) $Div = New-HTMLTag -Tag 'div' -Attributes @{ id = $ID; class = 'calendarFullCalendar'; style = $Style } $Script = New-HTMLTag -Tag 'script' -Value { "document.addEventListener('DOMContentLoaded', function () {" "var calendarEl = document.getElementById('$ID');" 'var calendar = new FullCalendar.Calendar(calendarEl,' $CalendarJSON ');' 'calendar.render();' '}); ' } -NewLine # return HTML $Script $Div } function New-HTMLChart { [alias('Chart')] [CmdletBinding()] param( [ScriptBlock] $ChartSettings, [string] $Title, [ValidateSet('center', 'left', 'right', 'default')][string] $TitleAlignment = 'default', [nullable[int]] $Height = 350, [nullable[int]] $Width, [alias('GradientColors')][switch] $Gradient, [alias('PatternedColors')][switch] $Patterned ) # Datasets Bar/Line $DataSet = [System.Collections.Generic.List[object]]::new() $DataName = [System.Collections.Generic.List[object]]::new() # Legend Variables $Colors = [System.Collections.Generic.List[RGBColors]]::new() # Line Variables # $LineColors = [System.Collections.Generic.List[RGBColors]]::new() $LineCurves = [System.Collections.Generic.List[string]]::new() $LineWidths = [System.Collections.Generic.List[int]]::new() $LineDashes = [System.Collections.Generic.List[int]]::new() $LineCaps = [System.Collections.Generic.List[string]]::new() #$RadialColors = [System.Collections.Generic.List[RGBColors]]::new() #$SparkColors = [System.Collections.Generic.List[RGBColors]]::new() # Bar default definitions [bool] $BarHorizontal = $true [bool] $BarDataLabelsEnabled = $true [int] $BarDataLabelsOffsetX = -6 [string] $BarDataLabelsFontSize = '12px' [bool] $BarDistributed = $false [string] $LegendPosition = 'default' # [string] $Type = '' [Array] $Settings = & $ChartSettings foreach ($Setting in $Settings) { if ($Setting.ObjectType -eq 'Bar') { # For Bar Charts if (-not $Type) { # thiss makes sure type is not set if BarOptions is used which already set type to BarStacked or similar $Type = $Setting.ObjectType } $DataSet.Add($Setting.Value) $DataName.Add($Setting.Name) } elseif ($Setting.ObjectType -eq 'Pie' -or $Setting.ObjectType -eq 'Donut') { # For Pie Charts $Type = $Setting.ObjectType $DataSet.Add($Setting.Value) $DataName.Add($Setting.Name) if ($Setting.Color) { $Colors.Add($Setting.Color) } } elseif ($Setting.ObjectType -eq 'Spark') { # For Spark Charts $Type = $Setting.ObjectType $DataSet.Add($Setting.Value) $DataName.Add($Setting.Name) if ($Setting.Color) { $Colors.Add($Setting.Color) } } elseif ($Setting.ObjectType -eq 'Radial') { $Type = $Setting.ObjectType $DataSet.Add($Setting.Value) $DataName.Add($Setting.Name) if ($Setting.Color) { $Colors.Add($Setting.Color) } } elseif ($Setting.ObjectType -eq 'Legend') { # For Bar Charts $DataLegend = $Setting.Names $LegendPosition = $Setting.LegendPosition if ($null -ne $Setting.Color) { $Colors = $Setting.Color } } elseif ($Setting.ObjectType -eq 'BarOptions') { # For Bar Charts $Type = $Setting.Type $BarHorizontal = $Setting.Horizontal $BarDataLabelsEnabled = $Setting.DataLabelsEnabled $BarDataLabelsOffsetX = $Setting.DataLabelsOffsetX $BarDataLabelsFontSize = $Setting.DataLabelsFontSize $BarDataLabelsColor = $Setting.DataLabelsColor $BarDistributed = $Setting.Distributed # This is required to support legacy ChartBarOptions - Gradient -Patterned if ($null -ne $Setting.PatternedColors) { $Patterned = $Setting.PatternedColors } if ($null -ne $Setting.GradientColors) { $Gradient = $Setting.GradientColors } } elseif ($Setting.ObjectType -eq 'Toolbar') { # For All Charts $Toolbar = $Setting.Toolbar } elseif ($Setting.ObjectType -eq 'Theme') { # For All Charts $Theme = $Setting.Theme } elseif ($Setting.ObjectType -eq 'Line') { # For Line Charts $Type = $Setting.ObjectType $DataSet.Add($Setting.Value) $DataName.Add($Setting.Name) if ($Setting.LineColor) { $Colors.Add($Setting.LineColor) } if ($Setting.LineCurve) { $LineCurves.Add($Setting.LineCurve) } if ($Setting.LineWidth) { $LineWidths.Add($Setting.LineWidth) } if ($Setting.LineDash) { $LineDashes.Add($Setting.LineDash) } if ($Setting.LineCap) { $LineCaps.Add($Setting.LineCap) } } elseif ($Setting.ObjectType -eq 'ChartAxisX') { $ChartAxisX = $Setting.ChartAxisX #$DataCategory = $ChartAxisX.Names } elseif ($Setting.ObjectType -eq 'ChartGrid') { $GridOptions = $Setting.Grid } elseif ($Setting.ObjectType -eq 'ChartAxisY') { $ChartAxisY = $Setting.ChartAxisY } } if ($Type -in @('bar', 'barStacked', 'barStacked100Percent')) { if ($DataLegend.Count -lt $DataSet[0].Count) { Write-Warning -Message "Chart Legend count doesn't match values count. Skipping." } # Fixes dataset/dataname to format expected by New-HTMLChartBar $HashTable = [ordered] @{ } $ArrayCount = $DataSet[0].Count if ($ArrayCount -eq 1) { $HashTable.1 = $DataSet } else { for ($i = 0; $i -lt $ArrayCount; $i++) { $HashTable.$i = [System.Collections.Generic.List[object]]::new() } foreach ($Value in $DataSet) { for ($h = 0; $h -lt $Value.Count; $h++) { $HashTable[$h].Add($Value[$h]) } } } New-HTMLChartBar ` -Data $($HashTable.Values) ` -DataNames $DataName ` -DataLegend $DataLegend ` -LegendPosition $LegendPosition ` -Type $Type ` -Title $Title ` -TitleAlignment $TitleAlignment ` -Horizontal:$BarHorizontal ` -DataLabelsEnabled $BarDataLabelsEnabled ` -DataLabelsOffsetX $BarDataLabelsOffsetX ` -DataLabelsFontSize $BarDataLabelsFontSize ` -Distributed:$BarDistributed ` -DataLabelsColor $BarDataLabelsColor ` -Height $Height ` -Width $Width ` -Colors $Colors ` -Theme $Theme -Toolbar $Toolbar -GridOptions $GridOptions -PatternedColors:$Patterned -GradientColors:$Gradient } elseif ($Type -eq 'Line') { if (-not $ChartAxisX) { Write-Warning -Message 'Chart Category (Chart Axis X) is missing.' Exit } New-HTMLChartLine -Data $DataSet ` -DataNames $DataName ` -DataLabelsEnabled $BarDataLabelsEnabled ` -DataLabelsOffsetX $BarDataLabelsOffsetX ` -DataLabelsFontSize $BarDataLabelsFontSize ` -DataLabelsColor $BarDataLabelsColor ` -LineColor $Colors ` -LineCurve $LineCurves ` -LineWidth $LineWidths ` -LineDash $LineDashes ` -LineCap $LineCaps ` -ChartAxisX $ChartAxisX ` -ChartAxisY $ChartAxisY ` -Title $Title -TitleAlignment $TitleAlignment ` -Height $Height -Width $Width ` -Theme $Theme -Toolbar $Toolbar -GridOptions $GridOptions -PatternedColors:$Patterned -GradientColors:$Gradient } elseif ($Type -eq 'Pie' -or $Type -eq 'Donut') { New-HTMLChartPie ` -Type $Type ` -Data $DataSet ` -DataNames $DataName ` -Colors $Colors ` -Title $Title -TitleAlignment $TitleAlignment ` -Height $Height -Width $Width ` -Theme $Theme -Toolbar $Toolbar -GridOptions $GridOptions -PatternedColors:$Patterned -GradientColors:$Gradient } elseif ($Type -eq 'Spark') { New-HTMLChartSpark ` -Data $DataSet ` -DataNames $DataName ` -Colors $Colors ` -Title $Title -TitleAlignment $TitleAlignment ` -Height $Height -Width $Width ` -Theme $Theme -Toolbar $Toolbar -GridOptions $GridOptions -PatternedColors:$Patterned -GradientColors:$Gradient } elseif ($Type -eq 'Radial') { New-HTMLChartRadial ` -Data $DataSet ` -DataNames $DataName ` -Colors $Colors ` -Title $Title -TitleAlignment $TitleAlignment ` -Height $Height -Width $Width ` -Theme $Theme -Toolbar $Toolbar -GridOptions $GridOptions -PatternedColors:$Patterned -GradientColors:$Gradient } } function New-HTMLChartArea { [CmdletBinding()] param( [nullable[int]] $Height = 350, [bool] $DataLabelsEnabled = $true, [int] $DataLabelsOffsetX = -6, [string] $DataLabelsFontSize = '12px', [RGBColors[]] $DataLabelsColor, [ValidateSet('datetime', 'category', 'numeric')][string] $DataCategoriesType = 'category', [ValidateSet('straight', 'smooth', 'stepline')] $LineCurve = 'straight', [int] $LineWidth, [RGBColors[]] $LineColor, [RGBColors[]] $GridColors, [double] $GridOpacity, [ValidateSet('top', 'topRight', 'left', 'right', 'bottom', 'default')][string] $LegendPosition = 'default', [string] $TitleX, [string] $TitleY, [int] $MarkerSize, [Array] $Data, [Array] $DataNames, [Array] $DataLegend, [switch] $Zoom, [string] $Title, [ValidateSet('center', 'left', 'right', 'default')][string] $TitleAlignment = 'default', [switch] $PatternedColors, [switch] $GradientColors, [System.Collections.IDictionary] $GridOptions, [System.Collections.IDictionary] $Toolbar, [System.Collections.IDictionary] $Theme ) $Options = [ordered] @{ } New-ChartInternalArea -Options $Options -Data $Data -DataNames $DataNames New-ChartInternalStrokeDefinition -Options $Options ` -LineShow $true ` -LineCurve $LineCurve ` -LineWidth $LineWidth ` -LineColor $LineColor New-ChartInternalDataLabels -Options $Options ` -DataLabelsEnabled $DataLabelsEnabled ` -DataLabelsOffsetX $DataLabelsOffsetX ` -DataLabelsFontSize $DataLabelsFontSize ` -DataLabelsColor $DataLabelsColor New-ChartInternalAxisX -Options $Options ` -Title $TitleX ` -DataCategoriesType $DataCategoriesType ` -DataCategories $DataLegend New-ChartInternalAxisY -Options $Options -Title $TitleY New-ChartInternalMarker -Options $Options -MarkerSize $MarkerSize New-ChartInternalZoom -Options $Options -Enabled:$Zoom New-ChartInternalLegend -Options $Options -LegendPosition $LegendPosition # Default for all charts if ($PatternedColors) { New-ChartInternalPattern } if ($GradientColors) { New-ChartInternalGradient } New-ChartInternalTitle -Options $Options -Title $Title -TitleAlignment $TitleAlignment New-ChartInternalSize -Options $Options -Height $Height -Width $Width if ($GridOptions) { New-ChartInternalGrid -Options $Options @GridOptions } if ($Theme) { New-ChartInternalTheme -Options $Options @Theme } if ($Toolbar) { New-ChartInternalToolbar -Options $Options @Toolbar -Show $true } New-ApexChart -Options $Options } function New-HTMLChartBar { [CmdletBinding()] param( [nullable[int]] $Height = 350, [nullable[int]] $Width, [ValidateSet('bar', 'barStacked', 'barStacked100Percent')] $Type = 'bar', [RGBColors[]] $Colors, [bool] $Horizontal = $true, [bool] $DataLabelsEnabled = $true, [int] $DataLabelsOffsetX = -6, [string] $DataLabelsFontSize = '12px', [nullable[RGBColors]] $DataLabelsColor, [switch] $Distributed, [ValidateSet('top', 'topRight', 'left', 'right', 'bottom', 'default')][string] $LegendPosition = 'default', [Array] $Data, [Array] $DataNames, [Array] $DataLegend, [string] $Title, [ValidateSet('center', 'left', 'right', 'default')][string] $TitleAlignment = 'default', [switch] $PatternedColors, [switch] $GradientColors, [System.Collections.IDictionary] $GridOptions, [System.Collections.IDictionary] $Toolbar, [System.Collections.IDictionary] $Theme ) $Options = [ordered] @{ } New-ChartInternalBar -Options $Options -Horizontal $Horizontal -DataLabelsEnabled $DataLabelsEnabled ` -DataLabelsOffsetX $DataLabelsOffsetX -DataLabelsFontSize $DataLabelsFontSize -DataLabelsColor $DataLabelsColor ` -Data $Data -DataNames $DataNames -DataLegend $DataLegend -Title $Title -TitleAlignment $TitleAlignment ` -Type $Type -Distributed:$Distributed New-ChartInternalColors -Options $Options -Colors $Colors New-ChartInternalLegend -Options $Options -LegendPosition $LegendPosition # Default for all charts if ($PatternedColors) { New-ChartInternalPattern } if ($GradientColors) { New-ChartInternalGradient } New-ChartInternalTitle -Options $Options -Title $Title -TitleAlignment $TitleAlignment New-ChartInternalSize -Options $Options -Height $Height -Width $Width if ($GridOptions) { New-ChartInternalGrid -Options $Options @GridOptions } if ($Theme) { New-ChartInternalTheme -Options $Options @Theme } if ($Toolbar) { New-ChartInternalToolbar -Options $Options @Toolbar -Show $true } New-ApexChart -Options $Options } function New-HTMLChartLine { [CmdletBinding()] param( [nullable[int]] $Height = 350, [nullable[int]] $Width, [bool] $DataLabelsEnabled = $true, [int] $DataLabelsOffsetX = -6, [string] $DataLabelsFontSize = '12px', [RGBColors[]] $DataLabelsColor, # [ValidateSet('datetime', 'category', 'numeric')][string] $DataCategoriesType = 'category', [ValidateSet('straight', 'smooth', 'stepline')][string[]] $LineCurve, [int[]] $LineWidth, [RGBColors[]] $LineColor, [int[]] $LineDash, [ValidateSet('butt', 'square', 'round')][string[]] $LineCap, #[RGBColors[]] $GridColors, #[double] $GridOpacity, [ValidateSet('top', 'topRight', 'left', 'right', 'bottom', 'default')][string] $LegendPosition = 'default', #[string] $TitleX, #[string] $TitleY, [int] $MarkerSize, [Array] $Data, [Array] $DataNames, #[Array] $DataLegend, [System.Collections.IDictionary] $ChartAxisX, [System.Collections.IDictionary] $ChartAxisY, [string] $Title, [ValidateSet('center', 'left', 'right', 'default')][string] $TitleAlignment = 'default', [switch] $PatternedColors, [switch] $GradientColors, [System.Collections.IDictionary] $GridOptions, [System.Collections.IDictionary] $Toolbar, [System.Collections.IDictionary] $Theme ) $Options = [ordered] @{ } New-ChartInternalLine -Options $Options -Data $Data -DataNames $DataNames if ($LineCurve.Count -eq 0 -or ($LineCurve.Count -ne $DataNames.Count)) { $LineCurve = for ($i = $LineCurve.Count; $i -le $DataNames.Count; $i++) { 'straight' } } if ($LineCap.Count -eq 0 -or ($LineCap.Count -ne $DataNames.Count)) { $LineCap = for ($i = $LineCap.Count; $i -le $DataNames.Count; $i++) { 'butt' } } if ($LineDash.Count -eq 0) { } New-ChartInternalStrokeDefinition -Options $Options ` -LineShow $true ` -LineCurve $LineCurve ` -LineWidth $LineWidth ` -LineColor $LineColor ` -LineCap $LineCap ` -LineDash $LineDash # line colors (stroke colors ) doesn't cover legend - we need to make sure it's the same even thou lines are already colored New-ChartInternalColors -Options $Options -Colors $LineColor New-ChartInternalDataLabels -Options $Options ` -DataLabelsEnabled $DataLabelsEnabled ` -DataLabelsOffsetX $DataLabelsOffsetX ` -DataLabelsFontSize $DataLabelsFontSize ` -DataLabelsColor $DataLabelsColor if ($ChartAxisX) { New-ChartInternalAxisX -Options $Options @ChartAxisX } if ($ChartAxisY) { New-ChartInternalAxisY -Options $Options @ChartAxisY } New-ChartInternalMarker -Options $Options -MarkerSize $MarkerSize New-ChartInternalLegend -Options $Options -LegendPosition $LegendPosition # Default for all charts if ($PatternedColors) { New-ChartInternalPattern } if ($GradientColors) { New-ChartInternalGradient } New-ChartInternalTitle -Options $Options -Title $Title -TitleAlignment $TitleAlignment New-ChartInternalSize -Options $Options -Height $Height -Width $Width if ($GridOptions) { New-ChartInternalGrid -Options $Options @GridOptions } if ($Theme) { New-ChartInternalTheme -Options $Options @Theme } if ($Toolbar) { New-ChartInternalToolbar -Options $Options @Toolbar -Show $true } New-ApexChart -Options $Options } function New-HTMLChartPie { [CmdletBinding()] param( [string] $Type, [nullable[int]] $Height = 350, [nullable[int]] $Width, [bool] $DataLabelsEnabled = $true, [int] $DataLabelsOffsetX = -6, [string] $DataLabelsFontSize = '12px', [RGBColors[]] $DataLabelsColor, [Array] $Data, [Array] $DataNames, [ValidateSet('top', 'topRight', 'left', 'right', 'bottom', 'default')][string] $LegendPosition = 'default', [RGBColors[]] $Colors, [string] $Title, [ValidateSet('center', 'left', 'right', 'default')][string] $TitleAlignment = 'default', [switch] $PatternedColors, [switch] $GradientColors, [System.Collections.IDictionary] $GridOptions, [System.Collections.IDictionary] $Toolbar, [System.Collections.IDictionary] $Theme ) $Options = [ordered] @{ } New-ChartInternalPie -Options $Options -Names $DataNames -Values $Data -Type $Type New-ChartInternalColors -Options $Options -Colors $Colors # Default for all charts if ($PatternedColors) { New-ChartInternalPattern } if ($GradientColors) { New-ChartInternalGradient } New-ChartInternalTitle -Options $Options -Title $Title -TitleAlignment $TitleAlignment New-ChartInternalSize -Options $Options -Height $Height -Width $Width if ($GridOptions) { New-ChartInternalGrid -Options $Options @GridOptions } if ($Theme) { New-ChartInternalTheme -Options $Options @Theme } if ($Toolbar) { New-ChartInternalToolbar -Options $Options @Toolbar -Show $true } New-ApexChart -Options $Options } function New-HTMLChartRadial { [CmdletBinding()] param( [nullable[int]] $Height = 350, [nullable[int]] $Width, [Array] $DataNames, [Array] $Data, [string] $Type, [ValidateSet('FullCircleTop', 'FullCircleBottom', 'FullCircleBottomLeft', 'FullCircleLeft', 'Speedometer', 'SemiCircleGauge')] $CircleType = 'FullCircleTop', [string] $LabelAverage, [RGBColors[]] $Colors, [string] $Title, [ValidateSet('center', 'left', 'right', 'default')][string] $TitleAlignment = 'default', [switch] $PatternedColors, [switch] $GradientColors, [System.Collections.IDictionary] $GridOptions, [System.Collections.IDictionary] $Toolbar, [System.Collections.IDictionary] $Theme ) $Options = [ordered] @{ } New-ChartInternalRadial -Options $Options -Names $DataNames -Values $Data -Type $Type # This controls how the circle starts / left , right and so on New-ChartInternalRadialCircleType -Options $Options -CircleType $CircleType # This added label. It's useful if there's more then one data New-ChartInternalRadialDataLabels -Options $Options -Label $LabelAverage New-ChartInternalColors -Options $Options -Colors $Colors # Default for all charts if ($PatternedColors) { New-ChartInternalPattern } if ($GradientColors) { New-ChartInternalGradient } New-ChartInternalTitle -Options $Options -Title $Title -TitleAlignment $TitleAlignment New-ChartInternalSize -Options $Options -Height $Height -Width $Width if ($GridOptions) { New-ChartInternalGrid -Options $Options @GridOptions } if ($Theme) { New-ChartInternalTheme -Options $Options @Theme } if ($Toolbar) { New-ChartInternalToolbar -Options $Options @Toolbar -Show $true } New-ApexChart -Options $Options } function New-HTMLChartSpark { [CmdletBinding()] param( [nullable[int]] $Height = 350, [nullable[int]] $Width, [string] $Title, [ValidateSet('center', 'left', 'right', 'default')][string] $TitleAlignment = 'default', # Data to display in Spark [Array] $Data, [Array] $DataNames, [string] $TitleText, [string] $SubTitleText, [int] $FontSizeTitle = 24, [int] $FontSizeSubtitle = 14, [nullable[RGBColors]] $Color, [switch] $PatternedColors, [switch] $GradientColors, [System.Collections.IDictionary] $GridOptions, [System.Collections.IDictionary] $Toolbar, [System.Collections.IDictionary] $Theme ) $Options = [ordered] @{ } New-ChartInternalSpark -Options $Options -Color $Color -Title $TitleText -SubTitle $SubTitleText -FontSizeTitle $FontSizeTitle -FontSizeSubtitle $FontSizeSubtitle -Values $Data # Default for all charts if ($PatternedColors) { New-ChartInternalPattern } if ($GradientColors) { New-ChartInternalGradient } New-ChartInternalTitle -Options $Options -Title $Title -TitleAlignment $TitleAlignment New-ChartInternalSize -Options $Options -Height $Height -Width $Width if ($GridOptions) { New-ChartInternalGrid -Options $Options @GridOptions } if ($Theme) { New-ChartInternalTheme -Options $Options @Theme } if ($Toolbar) { New-ChartInternalToolbar -Options $Options @Toolbar -Show $true } New-ApexChart -Options $Options } Function New-HTMLCodeBlock { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)][String] $Code, [Parameter(Mandatory = $false)] [ValidateSet( 'assembly', 'asm', 'avrassembly', 'avrasm', 'c', 'cpp', 'c++', 'csharp', 'css', 'cython', 'cordpro', 'diff', 'docker', 'dockerfile', 'generic', 'standard', 'groovy', 'go', 'golang', 'html', 'ini', 'conf', 'java', 'js', 'javascript', 'jquery', 'mootools', 'ext.js', 'json', 'kotlin', 'less', 'lua', 'gfm', 'md', 'markdown', 'octave', 'matlab', 'nsis', 'php', 'powershell', 'prolog', 'py', 'python', 'raw', 'ruby', 'rust', 'scss', 'sass', 'shell', 'bash', 'sql', 'squirrel', 'swift', 'typescript', 'vhdl', 'visualbasic', 'vb', 'xml', 'yaml' )] [String] $Style = 'powershell', [Parameter(Mandatory = $false)] [ValidateSet( 'enlighter', 'beyond', 'classic', 'godzilla', 'atomic', 'droide', 'minimal', 'eclipse', 'mowtwo', 'rowhammer', 'bootstrap4', 'dracula', 'monokai' )][String] $Theme, [Parameter(Mandatory = $false)][String] $Group, [Parameter(Mandatory = $false)][String] $Title, [Parameter(Mandatory = $false)][String[]] $Highlight, [Parameter(Mandatory = $false)][nullable[bool]] $ShowLineNumbers, [Parameter(Mandatory = $false)][String] $LineOffset ) $Script:HTMLSchema.Features.CodeBlocks = $true <# Explanation to fields: data-enlighter-language (string) - The language of the codeblock - overrides the global default setting | Block+Inline Content option data-enlighter-theme (string) - The theme of the codeblock - overrides the global default setting | Block+Inline Content option data-enlighter-group (string) - The identifier of the codegroup where the codeblock belongs to | Block Content option data-enlighter-title (string) - The title/name of the tab | Block Content option data-enlighter-linenumbers (boolean) - Show/Hide the linenumbers of a codeblock (Values: "true", "false") | Block Content option data-enlighter-highlight (string) - A List of lines to point out, comma seperated (ranges are supported) e.g. "2,3,6-10" | Block Content option data-enlighter-lineoffset (number) - Start value of line-numbering e.g. "5" to start with line 5 - attribute start of the ol tag is set | Block Content option #> if ($null -eq $ShowLineNumbers -and $Highlight) { $ShowLineNumbers = $true } $Attributes = [ordered]@{ 'data-enlighter-language' = "$Style".ToLower() 'data-enlighter-theme' = "$Theme".ToLower() 'data-enlighter-group' = "$Group".ToLower() 'data-enlighter-title' = "$Title" 'data-enlighter-linenumbers' = "$ShowLineNumbers" 'data-enlighter-highlight' = "$Highlight" 'data-enlighter-lineoffset' = "$LineOffset".ToLower() } # Cleanup code (if there are spaces before code it fixes that) $ExtraCode = $Code.Split([System.Environment]::NewLine) [int] $Length = 5000 $NewCode = foreach ($Line in $ExtraCode) { if ($Line.Trim() -ne '') { [int] $TempLength = $Line.Length - (($Line -replace '^(\s+)').Length) #$TempLength = ($line -replace '^(\s+).+$', '$1').Length if ($TempLength -le $Length) { $Length = $TempLength } $Line } } $FixedCode = foreach ($Line in $NewCode) { $Line.Substring($Length) } $FinalCode = $FixedCode -join [System.Environment]::NewLine # Prepare HTML New-HTMLTag -Tag 'pre' -Attributes $Attributes { $FinalCode } } function New-HTMLContainer { [alias('Container')] [CmdletBinding()] param( [alias('Content')][Parameter(Mandatory = $false, Position = 0)][ScriptBlock] $HTML, [string] $Width, [string] $Margin ) if ($Width -or $Margin) { [string] $ClassName = "flexElement$(Get-RandomStringName -Size 8 -LettersOnly)" $Attributes = @{ 'flex-basis' = if ($Width) { $Width } else { '100%' } 'margin' = if ($Margin) { $Margin } } $Css = ConvertTo-CSS -ClassName $ClassName -Attributes $Attributes $Script:HTMLSchema.CustomCSS.Add($Css) [string] $Class = "$ClassName overflowHidden" } else { [string] $Class = 'flexElement overflowHidden' } New-HTMLTag -Tag 'div' -Attributes @{ class = $Class } { if ($HTML) { Invoke-Command -ScriptBlock $HTML } } } function New-HTMLCustomCSS { [CmdletBinding()] param( [System.Collections.IList] $CSS ) "<!-- CSS AUTOGENERATED on DEMAND START -->" foreach ($_ in $CSS) { if ($_) { New-HTMLTag -Tag 'style' -Attributes @{ type = 'text/css' } { $_ } -NewLine } } "<!-- CSS AUTOGENERATED on DEMAND END -->" } function New-HTMLDiagram { [alias('Diagram')] [CmdletBinding()] param( [ScriptBlock] $Diagram, [string] $Height, [string] $Width, [switch] $BundleImages, [uri] $BackGroundImage, [string] $BackgroundSize = '100% 100%' ) if (-not $Script:HTMLSchema.Features) { Write-Warning 'New-HTMLDiagram - Creation of HTML aborted. Most likely New-HTML is missing.' Exit } $DataEdges = [System.Collections.Generic.List[System.Collections.IDictionary]]::new() $DataNodes = [ordered] @{ } $DataEvents = [System.Collections.Generic.List[System.Collections.IDictionary]]::new() [Array] $Settings = & $Diagram foreach ($Node in $Settings) { if ($Node.Type -eq 'DiagramNode') { $ID = $Node.Settings['id'] $DataNodes[$ID] = $Node.Settings #$DataEdges.Add($Node.Edges) foreach ($From in $Node.Edges.From) { foreach ($To in $Node.Edges.To) { $Edge = $Node.Edges.Clone() $Edge['from'] = $From $Edge['to'] = $To $DataEdges.Add($Edge) } } } elseif ($Node.Type -eq 'DiagramOptionsInteraction') { $DiagramOptionsInteraction = $Node.Settings } elseif ($Node.Type -eq 'DiagramOptionsManipulation') { $DiagramOptionsManipulation = $Node.Settings } elseif ($Node.Type -eq 'DiagramOptionsPhysics') { $DiagramOptionsPhysics = $Node.Settings } elseif ($Node.Type -eq 'DiagramOptionsLayout') { $DiagramOptionsLayout = $Node.Settings } elseif ($Node.Type -eq 'DiagramOptionsNodes') { $DiagramOptionsNodes = $Node.Settings } elseif ($Node.Type -eq 'DiagramOptionsEdges') { $DiagramOptionsEdges = $Node.Settings } elseif ($Node.Type -eq 'DiagramLink') { if ($Node.Settings.From -and $Node.Settings.To) { foreach ($From in $Node.Settings.From) { foreach ($To in $Node.Settings.To) { $Edge = $Node.Edges.Clone() $Edge['from'] = $From $Edge['to'] = $To $DataEdges.Add($Edge) } } } $DataEdges.Add($Node.Edges) } elseif ($Node.Type -eq 'DiagramEvent') { $DataEvents.Add($Node.Settings) } } <# {id: 14, shape: 'circularImage', image: DIR + '14.png'}, {id: 15, shape: 'circularImage', image: DIR + 'missing.png', brokenImage: DIR + 'missingBrokenImage.png', label:"when images\nfail\nto load"}, {id: 16, shape: 'circularImage', image: DIR + 'anotherMissing.png', brokenImage: DIR + '9.png', label:"fallback image in action"} {id: 5, label:'colorObject', color: {background:'pink', border:'purple'}}, {id: 6, label:'colorObject + highlight', color: {background:'#F03967', border:'#713E7F',highlight:{background:'red',border:'black'}}}, {id: 7, label:'colorObject + highlight + hover', color: {background:'cyan', border:'blue',highlight:{background:'red',border:'blue'},hover:{background:'white',border:'red'}}} {id: 1,label: 'User 1',group: 'users'}, {id: 2,label: 'User 2',group: 'users'}, {id: 3,label: 'Usergroup 1',group: 'usergroups'} nodes.push({id: 1, label: 'Main', image: DIR + 'Network-Pipe-icon.png', shape: 'image'}); nodes.push({id: 2, label: 'Office', image: DIR + 'Network-Pipe-icon.png', shape: 'image'}); nodes.push({id: 3, label: 'Wireless', image: DIR + 'Network-Pipe-icon.png', shape: 'image'}); {id: 3, shape: 'image', image: DIR + '3.png', label: "imagePadding{2,10,8,20}+size", imagePadding: { left: 2, top: 10, right: 8, bottom: 20}, size: 40, color: { border: 'green', background: 'yellow', highlight: { border: 'yellow', background: 'green' }, hover: { border: 'orange', background: 'grey' } } }, {id: 9, shape: 'image', image: DIR + '9.png', label: "useImageSize + imagePadding:15", shapeProperties: { useImageSize: true }, imagePadding: 30, color: { border: 'blue', background: 'orange', highlight: { border: 'orange', background: 'blue' }, hover: { border: 'orange', background: 'grey' } } }, var url = "data:image/svg+xml;charset=utf-8,"+ encodeURIComponent(svg); {id: 2, label: 'Using SVG', image: url, shape: 'image'} #> [Array] $Nodes = foreach ($_ in $DataNodes.Keys) { if ($DataNodes[$_]['image']) { if ($BundleImages) { $DataNodes[$_]['image'] = Convert-Image -Image $DataNodes[$_]['image'] } } $NodeJson = $DataNodes[$_] | ConvertTo-Json -Depth 5 #| ForEach-Object { [System.Text.RegularExpressions.Regex]::Unescape($_) } # We need to fix wrong escaped chars, Unescape breaks other parts $Replace = @{ '"\"Font Awesome 5 Solid\""' = "'`"Font Awesome 5 Solid`"'" '"\"Font Awesome 5 Brands\""' = "'`"Font Awesome 5 Brands`"'" '"\"Font Awesome 5 Regular\""' = "'`"Font Awesome 5 Regular`"'" '"\"Font Awesome 5 Free\""' = "'`"Font Awesome 5 Free`"'" '"\"Font Awesome 5 Free Regular\""' = "'`"Font Awesome 5 Free Regular`"'" '"\"Font Awesome 5 Free Solid\""' = "'`"Font Awesome 5 Free Solid`"'" '"\"Font Awesome 5 Free Brands\""' = "'`"Font Awesome 5 Free Brands`"'" '"\\u' = '"\u' } foreach ($R in $Replace.Keys) { $NodeJson = $NodeJson.Replace($R, $Replace[$R]) } $NodeJson } [Array] $Edges = foreach ($_ in $DataEdges) { #if ($_.From -and $_.To) { # foreach ($SingleTo in $_.To) { # [ordered] @{ # from = $_.From # to = $SingleTo # } | ConvertTo-Json -Depth 5 $_ | ConvertTo-Json -Depth 5 #| ForEach-Object { [System.Text.RegularExpressions.Regex]::Unescape($_) } #} #} } $Options = @{ } if ($DiagramOptionsInteraction) { if ($DiagramOptionsInteraction['interaction']) { $Options['interaction'] = $DiagramOptionsInteraction['interaction'] } } if ($DiagramOptionsManipulation) { if ($DiagramOptionsManipulation['manipulation']) { $Options['manipulation'] = $DiagramOptionsManipulation['manipulation'] } } if ($DiagramOptionsPhysics) { if ($DiagramOptionsPhysics['physics']) { $Options['physics'] = $DiagramOptionsPhysics['physics'] } } if ($DiagramOptionsLayout) { if ($DiagramOptionsLayout['layout']) { $Options['layout'] = $DiagramOptionsLayout['layout'] } } if ($DiagramOptionsEdges) { if ($DiagramOptionsEdges['edges']) { $Options['edges'] = $DiagramOptionsEdges['edges'] } } if ($DiagramOptionsNodes) { if ($DiagramOptionsNodes['nodes']) { $Options['nodes'] = $DiagramOptionsNodes['nodes'] } } if ($BundleImages -and $BackGroundImage) { $Image = Convert-Image -Image $BackGroundImage } else { $Image = $BackGroundImage } New-InternalDiagram -Nodes $Nodes -Edges $Edges -Options $Options -Width $Width -Height $Height -BackgroundImage $Image -Events $DataEvents } Function New-HTMLHeading { [CmdletBinding()] Param ( [validateset('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7')][string]$Heading, [string]$HeadingText, # [validateset('default', 'central')][string] $Type = 'default', [switch] $Underline, [nullable[RGBColors]] $Color ) if ($null -ne $Color) { $RGBcolor = ConvertFrom-Color -Color $Color $Attributes = @{ style = "color: $RGBcolor;" } } else { $Attributes = @{ } } # if ($Type -eq 'central') { # $Attributes.Class = 'central' # } if ($Underline) { $Attributes.Class = "$($Attributes.Class) underline" } New-HTMLTag -Tag $Heading -Attributes $Attributes { $HeadingText } } function New-HTMLHierarchicalTree { param( [ScriptBlock] $TreeView ) $Script:HTMLSchema.Features.D3Mitch = $true [string] $ID = "HierarchicalTree-" + (Get-RandomStringName -Size 8) $TreeNodes = [System.Collections.Generic.List[System.Collections.IDictionary]]::new() [Array] $Settings = & $TreeView foreach ($Object in $Settings) { if ($Object.Type -eq 'TreeNode') { $TreeNodes.Add($Object.Settings) } } # Prepare NODES $Data = $TreeNodes | ConvertTo-Json -Depth 5 # Prepare HTML $Section = New-HTMLTag -Tag 'section' -Attributes @{ id = $ID; class = 'hierarchicalTree' } $Script = New-HTMLTag -Tag 'script' -Value { @" var data = $Data; var treePlugin = new d3.mitchTree.boxedTree() .setIsFlatData(true) .setData(data) .setElement(document.getElementById("$ID")) .setIdAccessor(function (data) { return data.id; }) .setParentIdAccessor(function (data) { return data.parentId; }) .setBodyDisplayTextAccessor(function (data) { return data.description; }) .setTitleDisplayTextAccessor(function (data) { return data.name; }) .initialize(); "@ } -NewLine # Send HTML $Section $Script } function New-HTMLHorizontalLine { [CmdletBinding()] param() New-HTMLTag -Tag 'hr' -SelfClosing } function New-HTMLImage { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER Source Parameter description .PARAMETER UrlLink Parameter description .PARAMETER AlternativeText Parameter description .PARAMETER Class Parameter description .PARAMETER Target Parameter description .PARAMETER Width Parameter description .PARAMETER Height Parameter description .EXAMPLE New-HTMLImage -Source 'https://evotec.pl/image.png' -UrlLink 'https://evotec.pl/' -AlternativeText 'My other text' -Class 'otehr' -Width '100%' .NOTES General notes #> [alias('Image')] [CmdletBinding()] param( [string] $Source, [Uri] $UrlLink = '', [string] $AlternativeText = '', [string] $Class = 'Logo', [string] $Target = '_blank', [string] $Width, [string] $Height ) New-HTMLTag -Tag 'div' -Attributes @{ class = $Class.ToLower() } { $AAttributes = [ordered]@{ 'target' = $Target 'href' = $UrlLink } New-HTMLTag -Tag 'a' -Attributes $AAttributes { $ImgAttributes = [ordered]@{ 'src' = "$Source" 'alt' = "$AlternativeText" 'width' = "$Height" 'height' = "$Width" } New-HTMLTag -Tag 'img' -Attributes $ImgAttributes } } } function New-HTMLList { [CmdletBinding()] param( [ScriptBlock]$ListItems, [ValidateSet('Unordered', 'Ordered')] [string] $Type = 'Unordered', [RGBColors] $Color, [RGBColors] $BackGroundColor, [int] $FontSize, [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeight, [ValidateSet('normal', 'italic', 'oblique')][string] $FontStyle, [ValidateSet('normal', 'small-caps')][string] $FontVariant, [string] $FontFamily, [ValidateSet('left', 'center', 'right', 'justify')][string] $Alignment, [ValidateSet('none', 'line-through', 'overline', 'underline')][string] $TextDecoration, [ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $TextTransform, [ValidateSet('rtl')][string] $Direction, [switch] $LineBreak ) $newHTMLSplat = @{ } if ($Alignment) { $newHTMLSplat.Alignment = $Alignment } if ($FontSize) { $newHTMLSplat.FontSize = $FontSize } if ($TextTransform) { $newHTMLSplat.TextTransform = $TextTransform } if ($Color) { $newHTMLSplat.Color = $Color } if ($FontFamily) { $newHTMLSplat.FontFamily = $FontFamily } if ($Direction) { $newHTMLSplat.Direction = $Direction } if ($FontStyle) { $newHTMLSplat.FontStyle = $FontStyle } if ($TextDecoration) { $newHTMLSplat.TextDecoration = $TextDecoration } if ($BackGroundColor) { $newHTMLSplat.BackGroundColor = $BackGroundColor } if ($FontVariant) { $newHTMLSplat.FontVariant = $FontVariant } if ($FontWeight) { $newHTMLSplat.FontWeight = $FontWeight } if ($LineBreak) { $newHTMLSplat.LineBreak = $LineBreak } [bool] $SpanRequired = $false foreach ($Entry in $newHTMLSplat.GetEnumerator()) { if ((Get-ObjectCount -Object $Entry.Value) -gt 0) { $SpanRequired = $true break } } if ($SpanRequired) { New-HTMLSpanStyle @newHTMLSplat { if ($Type -eq 'Unordered') { New-HTMLTag -Tag 'ul' { Invoke-Command -ScriptBlock $ListItems } } else { New-HTMLTag -Tag 'ol' { Invoke-Command -ScriptBlock $ListItems } } } } else { if ($Type -eq 'Unordered') { New-HTMLTag -Tag 'ul' { Invoke-Command -ScriptBlock $ListItems } } else { New-HTMLTag -Tag 'ol' { Invoke-Command -ScriptBlock $ListItems } } } } function New-HTMLListItem { [CmdletBinding()] param( [string[]] $Text, [RGBColors[]] $Color = @(), [RGBColors[]] $BackGroundColor = @(), [int[]] $FontSize = @(), [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string[]] $FontWeight = @(), [ValidateSet('normal', 'italic', 'oblique')][string[]] $FontStyle = @(), [ValidateSet('normal', 'small-caps')][string[]] $FontVariant = @(), [string[]] $FontFamily = @(), [ValidateSet('left', 'center', 'right', 'justify')][string[]] $Alignment = @(), [ValidateSet('none', 'line-through', 'overline', 'underline')][string[]] $TextDecoration = @(), [ValidateSet('uppercase', 'lowercase', 'capitalize')][string[]] $TextTransform = @(), [ValidateSet('rtl')][string[]] $Direction = @(), [switch] $LineBreak ) $newHTMLTextSplat = @{ Alignment = $Alignment FontSize = $FontSize TextTransform = $TextTransform Text = $Text Color = $Color FontFamily = $FontFamily Direction = $Direction FontStyle = $FontStyle TextDecoration = $TextDecoration BackGroundColor = $BackGroundColor FontVariant = $FontVariant FontWeight = $FontWeight LineBreak = $LineBreak } if (($FontSize.Count -eq 0) -or ($FontSize -eq 0)) { $Size = '' } else { $size = "$($FontSize)px" } $Style = @{ style = @{ 'color' = ConvertFrom-Color -Color $Color 'background-color' = ConvertFrom-Color -Color $BackGroundColor 'font-size' = $Size 'font-weight' = $FontWeight 'font-variant' = $FontVariant 'font-family' = $FontFamily 'font-style' = $FontStyle 'text-align' = $Alignment 'text-decoration' = $TextDecoration 'text-transform' = $TextTransform 'direction' = $Direction } } New-HTMLTag -Tag 'li' -Attributes $Style -Value { New-HTMLText @newHTMLTextSplat -SkipParagraph } } function New-HTMLLogo { [CmdletBinding()] param( [String] $LogoPath, [string] $LeftLogoName = "Sample", [string] $RightLogoName = "Alternate", [string] $LeftLogoString, [string] $RightLogoString, [switch] $HideLogos ) $LogoSources = Get-HTMLLogos ` -RightLogoName $RightLogoName ` -LeftLogoName $LeftLogoName ` -LeftLogoString $LeftLogoString ` -RightLogoString $RightLogoString #Convert-StyleContent1 -Options $Options $Options = [PSCustomObject] @{ Logos = $LogoSources ColorSchemes = $ColorSchemes } if ($HideLogos -eq $false) { $Leftlogo = $Options.Logos[$LeftLogoName] $Rightlogo = $Options.Logos[$RightLogoName] $Script:HTMLSchema.Logos = @( '<!-- START LOGO -->' New-HTMLTag -Tag 'div' -Attributes @{ class = 'logos' } { New-HTMLTag -Tag 'div' -Attributes @{ class = 'leftLogo' } { New-HTMLTag -Tag 'img' -Attributes @{ src = "$LeftLogo" } } New-HTMLTag -Tag 'div' -Attributes @{ class = 'rightLogo' } { New-HTMLTag -Tag 'img' -Attributes @{ src = "$RightLogo" } } } '<!-- END LOGO -->' ) -join '' } } <# <table><tbody> <tr> <td class="clientlogo"><img src="$Leftlogo" /></td> <td class="MainLogo"><img src="$Rightlogo" /></td> </tr> </tbody></table> #> <# $Attributes = @{ src = "https://$Rightlogo" style = @{ color = 'red' 'background-color' = 'yellow' } } New-HTMLTag -Tag 'Table' { New-HTMLTag -Tag 'tbody' { New-HTMLTag -Tag 'tr' { New-HTMLTag -Tag 'td' -Attributes @{ class = 'clientLogo' } { New-HTMLTag -Tag 'img' -Attributes $Attributes } New-HTMLTag -Tag 'td' -Attributes @{ class = 'MainLogo' } { New-HTMLTag -Tag 'img' -Attributes @{ src = "https://$Rightlogo" } } } } } #> Function New-HTMLPanel { [alias('New-HTMLColumn', 'Panel')] [CmdletBinding()] param ( [Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $Content = $(Throw "Open curly brace with Content"), #[alias('ColumnCount', 'Columns')][ValidateSet('1', '2', '3', '4', '5 ', '6', '7', '8', '9', '10', '11', '12')][string] $Count = 1, [alias('BackgroundShade')][RGBColors]$BackgroundColor = [RGBColors]::None, [switch] $Invisible, [alias('flex-basis')][string] $Width, [string] $Margin #, # [int] $Height ) #if ($Height -ne 0) { # $StyleHeight = "height: $($Height)px" #} # $StyleWidth = "width: calc(100% / $Count - 10px)" if ($BackgroundColor -ne [RGBColors]::None) { $BackGroundColorFromRGB = ConvertFrom-Color -Color $BackgroundColor $DivColumnStyle = "background-color:$BackGroundColorFromRGB;" } else { $DivColumnStyle = "" } if ($Invisible) { $DivColumnStyle = "$DivColumnStyle box-shadow: unset !important;" } if ($Width -or $Margin) { [string] $ClassName = "flexPanel$(Get-RandomStringName -Size 8 -LettersOnly)" $Attributes = @{ 'flex-basis' = if ($Width) { $Width } else { '100%' } 'margin' = if ($Margin) { $Margin } } $Css = ConvertTo-CSS -ClassName $ClassName -Attributes $Attributes $Script:HTMLSchema.CustomCSS.Add($Css) [string] $Class = "$ClassName overflowHidden" } else { [string] $Class = 'flexPanel overflowHidden' } # New-HTMLTag -Tag 'div' -Attributes @{ class = "flexPanel roundedCorners"; style = $DivColumnStyle } { New-HTMLTag -Tag 'div' -Attributes @{ class = "$Class roundedCorners overflowHidden"; style = $DivColumnStyle } { Invoke-Command -ScriptBlock $Content } } function New-HTMLResourceCSS { [alias('New-ResourceCSS', 'New-CSS')] [CmdletBinding()] param( [alias('ScriptContent')][Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $Content, [string[]] $Link, [string] $ResourceComment, [string[]] $FilePath, [System.Collections.IDictionary] $ReplaceData ) $Output = @( "<!-- CSS $ResourceComment START -->" foreach ($File in $FilePath) { if ($File -ne '') { if (Test-Path -LiteralPath $File) { New-HTMLTag -Tag 'style' -Attributes @{ type = 'text/css' } { Write-Verbose "New-HTMLResourceCSS - Reading file from $File" # Replaces stuff based on $Script:Configuration CustomActionReplace Entry $FileContent = Get-Content -LiteralPath $File if ($null -ne $ReplaceData) { foreach ($_ in $ReplaceData.Keys) { $FileContent = $FileContent -replace $_, $ReplaceData[$_] } } $FileContent -replace '@charset "UTF-8";' } -NewLine } } } foreach ($L in $Link) { if ($L -ne '') { Write-Verbose "New-HTMLResourceCSS - Adding link $L" New-HTMLTag -Tag 'link' -Attributes @{ rel = "stylesheet"; type = "text/css"; href = $L } -SelfClosing -NewLine } } "<!-- CSS $ResourceComment END -->" ) if ($Output.Count -gt 2) { # Outputs only if more than comments $Output } } function New-HTMLResourceJS { [alias('New-ResourceJS', 'New-JavaScript')] [CmdletBinding()] param( [alias('ScriptContent')][Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $Content, [string[]] $Link, [string] $ResourceComment, [string[]] $FilePath, [System.Collections.IDictionary] $ReplaceData ) $Output = @( "<!-- JS $ResourceComment START -->" foreach ($File in $FilePath) { if ($File -ne '') { if (Test-Path -LiteralPath $File) { New-HTMLTag -Tag 'script' -Attributes @{ type = 'text/javascript' } { # Replaces stuff based on $Script:Configuration CustomActionReplace Entry $FileContent = Get-Content -LiteralPath $File if ($null -ne $ReplaceData) { foreach ($_ in $ReplaceData.Keys) { $FileContent = $FileContent -replace $_, $ReplaceData[$_] } } $FileContent } -NewLine } else { return } } } foreach ($L in $Link) { if ($L -ne '') { New-HTMLTag -Tag 'script' -Attributes @{ type = "text/javascript"; src = $L } -NewLine } else { return } } "<!-- JS $ResourceComment END -->" ) if ($Output.Count -gt 2) { # Outputs only if more than comments $Output } } Function New-HTMLSection { [alias('New-HTMLContent', 'Section')] [CmdletBinding()] Param ( [Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $Content = $(Throw "Open curly brace"), [alias('Name')][Parameter(Mandatory = $false)][string]$HeaderText, [alias('TextColor')][RGBColors]$HeaderTextColor = [RGBColors]::White, [alias('TextAlignment')][string][ValidateSet('center', 'left', 'right', 'justify')] $HeaderTextAlignment = 'center', [alias('TextBackGroundColor')][RGBColors]$HeaderBackGroundColor = [RGBColors]::DeepSkyBlue, [alias('BackgroundShade')][RGBColors]$BackgroundColor = [RGBColors]::None, [alias('Collapsable')][Parameter(Mandatory = $false)][switch] $CanCollapse, [Parameter(Mandatory = $false)][switch] $IsHidden, [switch] $Collapsed, [int] $Height, [switch] $Invisible, # Following are based on https://css-tricks.com/snippets/css/a-guide-to-flexbox/ [string][ValidateSet('wrap', 'nowrap', 'wrap-reverse')] $Wrap, [string][ValidateSet('row', 'row-reverse', 'column', 'column-reverse')] $Direction, [string][ValidateSet('flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'stretch')] $AlignContent, [string][ValidateSet('stretch', 'flex-start', 'flex-end', 'center', 'baseline')] $AlignItems ) $RandomNumber = Get-Random $TextHeaderColorFromRGB = ConvertFrom-Color -Color $HeaderTextColor $HiddenDivStyle = @{ } if ($CanCollapse) { $Script:HTMLSchema.Features.HideSection = $true if ($IsHidden) { $ShowStyle = "color: $TextHeaderColorFromRGB;" # shows Show button $HideStyle = "color: $TextHeaderColorFromRGB; display:none;" # hides Hide button } else { if ($Collapsed) { $HideStyle = "color: $TextHeaderColorFromRGB; display:none;" # hides Show button $ShowStyle = "color: $TextHeaderColorFromRGB;" # shows Hide button # $HiddenDivStyle = 'display:none; ' $HiddenDivStyle['display'] = 'none' } else { $ShowStyle = "color: $TextHeaderColorFromRGB; display:none;" # hides Show button $HideStyle = "color: $TextHeaderColorFromRGB;" # shows Hide button } } } else { if ($IsHidden) { $ShowStyle = "color: $TextHeaderColorFromRGB;" # shows Show button $HideStyle = "color: $TextHeaderColorFromRGB; display:none;" # hides Hide button } else { $ShowStyle = "color: $TextHeaderColorFromRGB; display:none;" # hides Show button $HideStyle = "color: $TextHeaderColorFromRGB; display:none;" # hides Show button } } if ($IsHidden) { $DivContentStyle = @{ "display" = 'none' #"width" = "calc(100% / $Count - 15px)" #"height" = if ($Height -ne 0) { "$($Height)px" } else { '' } "background-color" = ConvertFrom-Color -Color $BackgroundColor } } else { $DivContentStyle = @{ # "width" = "calc(100% / $Count - 15px)" #"height" = if ($Height -ne 0) { "$($Height)px" } else { '' } "background-color" = ConvertFrom-Color -Color $BackgroundColor } } $HiddenDivStyle['height'] = if ($Height -ne 0) { "$($Height)px" } else { '' } <# .flexParent { display: flex; flex-wrap: nowrap; justify-content: space-between; padding: 2px; /* overflow: hidden; overflow-x: hidden; overflow-y: hidden; */ } #> if ($Wrap -or $Direction) { [string] $ClassName = "flexParent$(Get-RandomStringName -Size 8 -LettersOnly)" $Attributes = @{ 'display' = 'flex' 'flex-wrap' = if ($Wrap) { $Wrap } else { } 'flex-direction' = if ($Direction) { $Direction } else { } 'align-content' = if ($AlignContent) { $AlignContent } else { } 'align-items' = if ($AlignItems) { $AlignItems } else { } } $Css = ConvertTo-CSS -ClassName $ClassName -Attributes $Attributes $Script:HTMLSchema.CustomCSS.Add($Css) } else { [string] $ClassName = 'flexParent flexElement overflowHidden' } $DivHeaderStyle = @{ "text-align" = $HeaderTextAlignment "background-color" = ConvertFrom-Color -Color $HeaderBackGroundColor } $HeaderStyle = "color: $TextHeaderColorFromRGB;" if ($Invisible) { #New-HTMLTag -Tag 'div' -Attributes @{ class = 'flexParentInvisible' } -Value { New-HTMLTag -Tag 'div' -Attributes @{ class = $ClassName } -Value { New-HTMLTag -Tag 'div' -Attributes @{ class = $ClassName } -Value { # New-HTMLTag -Tag 'div' -Attributes @{ class = 'flexParentInvisible flexElement' } -Value { $Object = Invoke-Command -ScriptBlock $Content if ($null -ne $Object) { $Object } } } } else { # return this HTML New-HTMLTag -Tag 'div' -Attributes @{ 'class' = "defaultSection roundedCorners overflowHidden"; 'style' = $DivContentStyle } -Value { New-HTMLTag -Tag 'div' -Attributes @{ 'class' = "defaultHeader"; 'style' = $DivHeaderStyle } -Value { New-HTMLAnchor -Name $HeaderText -Text "$HeaderText " -Style $HeaderStyle New-HTMLAnchor -Id "show_$RandomNumber" -Href 'javascript:void(0)' -OnClick "show('$RandomNumber');" -Style $ShowStyle -Text '(Show)' New-HTMLAnchor -Id "hide_$RandomNumber" -Href 'javascript:void(0)' -OnClick "hide('$RandomNumber');" -Style $HideStyle -Text '(Hide)' } New-HTMLTag -Tag 'div' -Attributes @{ class = $ClassName; id = $RandomNumber; Style = $HiddenDivStyle } -Value { New-HTMLTag -Tag 'div' -Attributes @{ class = "$ClassName collapsable"; id = $RandomNumber; } -Value { $Object = Invoke-Command -ScriptBlock $Content if ($null -ne $Object) { $Object } } } } } } function New-HTMLSpanStyle { [CmdletBinding()] param( [ScriptBlock] $Content, [nullable[RGBColors]] $Color, [nullable[RGBColors]] $BackGroundColor, [int] $FontSize, [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeight, [ValidateSet('normal', 'italic', 'oblique')][string] $FontStyle, [ValidateSet('normal', 'small-caps')][string] $FontVariant, [string] $FontFamily, [ValidateSet('left', 'center', 'right', 'justify')][string] $Alignment, [ValidateSet('none', 'line-through', 'overline', 'underline')][string] $TextDecoration, [ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $TextTransform, [ValidateSet('rtl')][string] $Direction, [switch] $LineBreak ) if ($FontSize -eq 0) { $Size = '' } else { $size = "$($FontSize)px" } $Style = @{ style = @{ 'color' = ConvertFrom-Color -Color $Color 'background-color' = ConvertFrom-Color -Color $BackGroundColor 'font-size' = $Size 'font-weight' = $FontWeight 'font-variant' = $FontVariant 'font-family' = $FontFamily 'font-style' = $FontStyle 'text-align' = $Alignment 'text-decoration' = $TextDecoration 'text-transform' = $TextTransform 'direction' = $Direction } } if ($Alignment) { $StyleDiv = @{ } $StyleDiv.Align = $Alignment New-HTMLTag -Tag 'div' -Attributes $StyleDiv { New-HTMLTag -Tag 'span' -Attributes $Style { Invoke-Command -ScriptBlock $Content } } } else { New-HTMLTag -Tag 'span' -Attributes $Style { Invoke-Command -ScriptBlock $Content } } # if ($LineBreak) { # Write-Verbose 'New-SpanStyle - BR' # New-HTMLTag -Tag 'br' -SelfClosing # } } function New-HTMLStatus { [CmdletBinding()] param( [Parameter(Mandatory = $false, Position = 0)][alias('')][ScriptBlock] $Content ) $Script:HTMLSchema.Features.StatusButtonical = $true New-HTMLTag -Tag 'div' -Attributes @{ class = 'buttonicalService' } { #New-HTMLTag -Tag 'div' -Attributes @{ class = 'buttonical-align' } { Invoke-Command -ScriptBlock $Content # } } } function New-HTMLStatusItem { [CmdletBinding()] param( [string] $ServiceName, [string] $ServiceStatus, [ValidateSet('Dead', 'Bad', 'Good')]$Icon = 'Good', [ValidateSet('0%', '10%', '30%', '70%', '100%')][string] $Percentage = '100%' ) #$Script:HTMLSchema.Features.StatusButtonical = $true if ($Icon -eq 'Dead') { $IconType = 'performanceDead' } elseif ($Icon -eq 'Bad') { $IconType = 'performanceProblem' } elseif ($Icon -eq 'Good') { #$IconType = 'performance' } if ($Percentage -eq '100%') { $Colors = 'background-color: #0ef49b;' } elseif ($Percentage -eq '70%') { $Colors = 'background-color: #d2dc69;' } elseif ($Percentage -eq '30%') { $Colors = 'background-color: #faa04b;' } elseif ($Percentage -eq '10%') { $Colors = 'background-color: #ff9035;' } elseif ($Percentage -eq '0%') { $Colors = 'background-color: #ff5a64;' } New-HTMLTag -Tag 'div' -Attributes @{ class = 'buttonical'; style = $Colors } -Value { New-HTMLTag -Tag 'div' -Attributes @{ class = 'label' } { New-HTMLTag -Tag 'span' -Attributes @{ class = 'performance' } { $ServiceName } } New-HTMLTag -Tag 'div' -Attributes @{ class = 'middle' } New-HTMLTag -Tag 'div' -Attributes @{ class = 'status' } { New-HTMLTag -Tag 'input' -Attributes @{ name = Get-Random; type = 'radio'; value = 'other-item'; checked = 'true' } -SelfClosing New-HTMLTag -Tag 'span' -Attributes @{ class = "performance $IconType" } { $ServiceStatus } } } } function New-HTMLTab { [alias('Tab')] [CmdLetBinding(DefaultParameterSetName = 'FontAwesomeBrands')] param( [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [Parameter(Mandatory = $false, Position = 0)][ValidateNotNull()][ScriptBlock] $HtmlData = $(Throw "No curly brace?)"), [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [alias('TabHeading')][Parameter(Mandatory = $false, Position = 1)][String]$Heading, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")] [alias('TabName')][string] $Name = 'Tab', # 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, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")][int] $TextSize, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")][RGBColors] $TextColor = [RGBColors]::None, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")][int] $IconSize, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")][RGBColors] $IconColor = [RGBColors]::None ) if (-not $Script:HTMLSchema.Features) { Write-Warning 'New-HTMLTab - Creation of HTML aborted. Most likely New-HTML is missing.' Exit } [string] $Icon = '' if ($IconBrands) { $Icon = "fab fa-$IconBrands" # fa-$($FontSize)x" } elseif ($IconRegular) { $Icon = "far fa-$IconRegular" # fa-$($FontSize)x" } elseif ($IconSolid) { $Icon = "fas fa-$IconSolid" # fa-$($FontSize)x" } $StyleText = @{ } if ($TextSize -ne 0) { $StyleText.'font-size' = "$($TextSize)px" } if ($TextColor -ne [RGBColors]::None) { $StyleText.'color' = ConvertFrom-Color -Color $TextColor } $StyleIcon = @{ } if ($IconSize -ne 0) { $StyleIcon.'font-size' = "$($IconSize)px" } if ($IconColor -ne [RGBColors]::None) { $StyleIcon.'color' = ConvertFrom-Color -Color $IconColor } $Script:HTMLSchema.Features.Tabbis = $true # Reset all Tabs Headers to make sure there are no Current Tab Set # This is required for New-HTMLTable foreach ($Tab in $Script:HTMLSchema.TabsHeaders) { $Tab.Current = $false } # Start Tab Tracking $Tab = [ordered] @{ } $Tab.ID = "Tab-$(Get-RandomStringName -Size 8)" $Tab.Name = " $Name" $Tab.StyleIcon = $StyleIcon $Tab.StyleText = $StyleText #$Tab.Used = $true $Tab.Current = $true if ($Script:HTMLSchema.TabsHeaders | Where-Object { $_.Active -eq $true }) { $Tab.Active = $false } else { $Tab.Active = $true } # $Tab.Active = $true # $Tab.Active = $true $Tab.Icon = $Icon # End Tab Tracking # This is building HTML if ($Tab.Active) { $Class = 'active' } else { $Class = '' } #New-HTMLTag -Tag 'div' -Attributes @{ id = $Tab.ID; class = $Class } { New-HTMLTag -Tag 'div' -Attributes @{ id = $Tab.ID; class = $Class } { if (-not [string]::IsNullOrWhiteSpace($Heading)) { New-HTMLTag -Tag 'h7' { $Heading } } $OutputHTML = Invoke-Command -ScriptBlock $HtmlData [Array] $TabsCollection = foreach ($_ in $OutputHTML) { if ($_ -is [System.Collections.IDictionary]) { $_ $Script:HTMLSchema.TabsHeadersNested.Add($_) } } [Array] $HTML = foreach ($_ in $OutputHTML) { if ($_ -isnot [System.Collections.IDictionary]) { $_ } } if ($TabsCollection.Count -gt 0) { New-HTMLTabHead -TabsCollection $TabsCollection New-HTMLTag -Tag 'div' -Attributes @{ 'data-panes' = 'true' } { # Add remaining data $HTML } } else { $HTML } } $Script:HTMLSchema.TabsHeaders.Add($Tab) $Tab } function New-HTMLTabHead { [CmdletBinding()] Param ( [Array] $TabsCollection ) if ($Script:HTMLSchema.TabOptions.SlimTabs) { $Style = 'display: inline-block;' # makes tabs wrapperr slim/small } else { $Style = '' # makes it full-width } <# New-HTMLTag -Tag 'div' -Attributes @{ class = 'tabsWrapper' } { New-HTMLTag -Tag 'div' -Attributes @{ class = 'tabs' ; style = $Style } { New-HTMLTag -Tag 'div' -Attributes @{ class = 'selector' } foreach ($Tab in $Script:HTMLSchema.TabsHeaders) { $AttributesA = @{ 'href' = 'javascript:void(0)' 'data-id' = "$($Tab.Id)" } if ($Tab.Active) { $AttributesA.class = 'active' } else { $AttributesA.class = '' } New-HTMLTag -Tag 'a' -Attributes $AttributesA { New-HTMLTag -Tag 'div' -Attributes @{ class = $($Tab.Icon); style = $($Tab.StyleIcon) } New-HTMLTag -Tag 'span' -Attributes @{ style = $($Tab.StyleText ) } -Value { $Tab.Name } } } } } #> if ($TabsCollection.Count -gt 0) { $Tabs = $TabsCollection } else { $Tabs = $Script:HTMLSchema.TabsHeaders } New-HTMLTag -Tag 'div' -Attributes @{ class = 'tabsWrapper' } { New-HTMLTag -Tag 'div' -Attributes @{ class = 'tabs' ; style = $Style } { New-HTMLTag -Tag 'div' -Attributes @{ 'data-tabs' = 'true' } { foreach ($Tab in $Tabs) { if ($Tab.Active) { $TabActive = 'active' } else { $TabActive = '' } New-HTMLTag -Tag 'div' -Attributes @{ id = $Tab.ID; class = $TabActive } { New-HTMLTag -Tag 'div' -Attributes @{ class = $($Tab.Icon); style = $($Tab.StyleIcon) } New-HTMLTag -Tag 'span' -Attributes @{ style = $($Tab.StyleText ) } -Value { $Tab.Name } } } } } } } function New-HTMLTable { [alias('Table', 'EmailTable')] [CmdletBinding()] param( [Parameter(Mandatory = $false, Position = 0)][ScriptBlock] $HTML, [Parameter(Mandatory = $false, Position = 1)][ScriptBlock] $PreContent, [Parameter(Mandatory = $false, Position = 2)][ScriptBlock] $PostContent, [alias('ArrayOfObjects', 'Object', 'Table')][Array] $DataTable, [string[]][ValidateSet('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5', 'pageLength')] $Buttons = @('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5', 'pageLength'), [string[]][ValidateSet('numbers', 'simple', 'simple_numbers', 'full', 'full_numbers', 'first_last_numbers')] $PagingStyle = 'full_numbers', [int[]]$PagingOptions = @(15, 25, 50, 100), [switch]$DisablePaging, [switch]$DisableOrdering, [switch]$DisableInfo, [switch]$HideFooter, [switch]$DisableColumnReorder, [switch]$DisableProcessing, [switch]$DisableResponsiveTable, [switch]$DisableSelect, [switch]$DisableStateSave, [switch]$DisableSearch, [switch]$ScrollCollapse, [switch]$OrderMulti, [switch]$Filtering, [ValidateSet('Top', 'Bottom', 'Both')][string]$FilteringLocation = 'Bottom', [string[]][ValidateSet('display', 'cell-border', 'compact', 'hover', 'nowrap', 'order-column', 'row-border', 'stripe')] $Style = @('display', 'compact'), [switch]$Simplify, [string]$TextWhenNoData = 'No data available.', [int] $ScreenSizePercent = 0, [string[]] $DefaultSortColumn, [int[]] $DefaultSortIndex, [ValidateSet('Ascending', 'Descending')][string] $DefaultSortOrder = 'Ascending', [string[]] $DateTimeSortingFormat, [alias('Search')][string]$Find, [switch] $InvokeHTMLTags, [switch] $DisableNewLine, [switch] $ScrollX, [switch] $ScrollY, [int] $ScrollSizeY = 500, [int] $FreezeColumnsLeft, [int] $FreezeColumnsRight, [switch] $FixedHeader, [switch] $FixedFooter, [string[]] $ResponsivePriorityOrder, [int[]] $ResponsivePriorityOrderIndex, [string[]] $PriorityProperties, [alias('DataTableName')][string] $DataTableID, [switch] $ImmediatelyShowHiddenDetails, [alias('RemoveShowButton')][switch] $HideShowButton, [switch] $AllProperties, [switch] $Compare, [alias('CompareWithColors')][switch] $HighlightDifferences, [int] $First, [int] $Last, [alias('Replace')][Array] $CompareReplace ) if (-not $Script:HTMLSchema.Features) { Write-Warning 'New-HTMLTable - Creation of HTML aborted. Most likely New-HTML is missing.' Exit } # Theme creator https://datatables.net/manual/styling/theme-creator $ConditionalFormatting = [System.Collections.Generic.List[PSCustomObject]]::new() $CustomButtons = [System.Collections.Generic.List[PSCustomObject]]::new() $HeaderRows = [System.Collections.Generic.List[PSCustomObject]]::new() $HeaderStyle = [System.Collections.Generic.List[PSCustomObject]]::new() $HeaderTop = [System.Collections.Generic.List[PSCustomObject]]::new() $HeaderResponsiveOperations = [System.Collections.Generic.List[PSCustomObject]]::new() $ContentRows = [System.Collections.Generic.List[PSCustomObject]]::new() $ContentStyle = [System.Collections.Generic.List[PSCustomObject]]::new() $ContentTop = [System.Collections.Generic.List[PSCustomObject]]::new() $ContentFormattingInline = [System.Collections.Generic.List[PSCustomObject]]::new() $ReplaceCompare = [System.Collections.Generic.List[System.Collections.IDictionary]]::new() $RowGrouping = @{ } if ($HTML) { [Array] $Output = & $HTML if ($Output.Count -gt 0) { foreach ($Parameters in $Output) { if ($Parameters.Type -eq 'TableButtonPDF') { $CustomButtons.Add($Parameters.Output) } elseif ($Parameters.Type -eq 'TableButtonCSV') { $CustomButtons.Add($Parameters.Output) } elseif ($Parameters.Type -eq 'TableButtonPageLength') { $CustomButtons.Add($Parameters.Output) } elseif ($Parameters.Type -eq 'TableButtonExcel') { $CustomButtons.Add($Parameters.Output) } elseif ($Parameters.Type -eq 'TableButtonPDF') { $CustomButtons.Add($Parameters.Output) } elseif ($Parameters.Type -eq 'TableButtonPrint') { $CustomButtons.Add($Parameters.Output) } elseif ($Parameters.Type -eq 'TableButtonCopy') { $CustomButtons.Add($Parameters.Output) } elseif ($Parameters.Type -eq 'TableCondition') { $ConditionalFormatting.Add($Parameters.Output) } elseif ($Parameters.Type -eq 'TableHeaderMerge') { $HeaderRows.Add($Parameters.Output) } elseif ($Parameters.Type -eq 'TableHeaderStyle') { $HeaderStyle.Add($Parameters.Output) } elseif ($Parameters.Type -eq 'TableHeaderFullRow') { $HeaderTop.Add($Parameters.Output) } elseif ($Parameters.Type -eq 'TableContentMerge') { $ContentRows.Add($Parameters.Output) } elseif ($Parameters.Type -eq 'TableContentStyle') { $ContentStyle.Add($Parameters.Output) } elseif ($Parameters.Type -eq 'TableContentFullRow') { $ContentTop.Add($Parameters.Output) } elseif ($Parameters.Type -eq 'TableConditionInline') { $ContentFormattingInline.Add($Parameters.Output) } elseif ($Parameters.Type -eq 'TableHeaderResponsiveOperations') { $HeaderResponsiveOperations.Add($Parameters.Output) } elseif ($Parameters.Type -eq 'TableReplaceCompare') { $ReplaceCompare.Add($Parameters.Output) } elseif ($Parameters.Type -eq 'TableRowGrouping') { $RowGrouping = $Parameters.Output } } } } # Limit objects count First or Last if ($First -or $Last) { $DataTable = $DataTable | Select-Object -First $First -Last $Last } if ($Compare) { $Splitter = "`r`n" if ($ReplaceCompare) { foreach ($R in $CompareReplace) { $ReplaceCompare.Add($R) } } $DataTable = Compare-MultipleObjects -Objects $DataTable -Summary -Splitter $Splitter -FormatOutput -AllProperties:$AllProperties -Replace $ReplaceCompare if ($HighlightDifferences) { $Highlight = for ($i = 0; $i -lt $DataTable.Count; $i++) { if ($DataTable[$i].Status -eq $false) { # Different row foreach ($DifferenceColumn in $DataTable[$i].Different) { $DataSame = $DataTable[$i]."$DifferenceColumn-Same" -join $Splitter $DataAdd = $DataTable[$i]."$DifferenceColumn-Add" -join $Splitter $DataRemove = $DataTable[$i]."$DifferenceColumn-Remove" -join $Splitter if ($DataSame -ne '') { $DataSame = "$DataSame$Splitter" } if ($DataAdd -ne '') { $DataAdd = "$DataAdd$Splitter" } if ($DataRemove -ne '') { $DataRemove = "$DataRemove$Splitter" } $Text = New-HTMLText -Text $DataSame, $DataRemove, $DataAdd -Color Black, Red, Blue -TextDecoration none, line-through, none -FontWeight normal, bold, bold New-HTMLTableContent -ColumnName "$DifferenceColumn" -RowIndex ($i + 1) -Text "$Text" } } else { # Same row # New-HTMLTableContent -RowIndex ($i + 1) -BackGroundColor Green -Color White } } } $Properties = Select-Properties -Objects $DataTable -ExcludeProperty '*-*', 'Same', 'Different' $DataTable = $DataTable | Select-Object -Property $Properties if ($HighlightDifferences) { foreach ($Parameter in $Highlight.Output) { $ContentStyle.Add($Parameter) } } } if ($AllProperties) { $Properties = Select-Properties -Objects $DataTable -AllProperties:$AllProperties $DataTable = $DataTable | Select-Object -Property $Properties } # This is more direct way of PriorityProperties that will work also on Scroll and in other circumstances if ($PriorityProperties) { if ($DataTable.Count -gt 0) { $Properties = $DataTable[0].PSObject.Properties.Name # $Properties = Select-Properties -Objects $DataTable -AllProperties:$AllProperties $RemainingProperties = foreach ($Property in $Properties) { if ($PriorityProperties -notcontains $Property) { $Property } } $BoundedProperties = $PriorityProperties + $RemainingProperties $DataTable = $DataTable | Select-Object -Property $BoundedProperties } } # This option disable paging if number of elements is less or equal count of elements in DataTable $PagingOptions = $PagingOptions | Sort-Object -Unique if ($DataTable.Count -le $PagingOptions[0]) { $DisablePaging = $true } # Building HTML Table / Script if (-not $DataTableID) { # Only define this if user failed to deliver as per https://github.com/EvotecIT/PSWriteHTML/issues/29 $DataTableID = "DT-$(Get-RandomStringName -Size 8 -LettersOnly)" # this builds table ID } if ($null -eq $DataTable -or $DataTable.Count -eq 0) { #return '' $Filtering = $false # setting it to false because it's not nessecary $HideFooter = $true $DataTable = $TextWhenNoData } if ($DataTable[0] -is [System.Collections.IDictionary]) { Write-Verbose 'New-HTMLTable - Working with IDictionary' [Array ] $Table = $($DataTable).GetEnumerator() | Select-Object Name, Value | ConvertTo-Html -Fragment | Select-Object -SkipLast 1 | Select-Object -Skip 2 # This removes table tags (open/closing) } elseif ($DataTable[0] -is [string]) { [Array] $Table = $DataTable | ForEach-Object { [PSCustomObject]@{ 'Name' = $_ } } | ConvertTo-Html -Fragment | Select-Object -SkipLast 1 | Select-Object -Skip 2 } else { Write-Verbose 'New-HTMLTable - Working with Objects' [Array] $Table = $DataTable | ConvertTo-Html -Fragment | Select-Object -SkipLast 1 | Select-Object -Skip 2 # This removes table tags (open/closing) } [string] $Header = $Table | Select-Object -First 1 # this gets header [string[]] $HeaderNames = $Header -replace '</th></tr>' -replace '<tr><th>' -split '</th><th>' $AddedHeader = Add-TableHeader -HeaderRows $HeaderRows -HeaderNames $HeaderNames -HeaderStyle $HeaderStyle -HeaderTop $HeaderTop -HeaderResponsiveOperations $HeaderResponsiveOperations # This modifies Table content. # It basically goes thru every single row and checks if values to add styles or inline conditional formatting # It's heavier then JS, so use when nessecary if ($ContentRows.Capacity -gt 0 -or $ContentStyle.Count -gt 0 -or $ContentTop.Count -gt 0 -or $ContentFormattingInline.Count -gt 0) { $Table = Add-TableContent -ContentRows $ContentRows -ContentStyle $ContentStyle -ContentTop $ContentTop -ContentFormattingInline $ContentFormattingInline -Table $Table -HeaderNames $HeaderNames } $Table = $Table | Select-Object -Skip 1 # this gets actuall table content $Options = [ordered] @{ <# DOM Definition: https://datatables.net/reference/option/dom l - length changing input control f - filtering input t - The table! i - Table information summary p - pagination control r - processing display element B - Buttons S - Select F - FadeSeaerch #> dom = 'Bfrtip' #buttons = @($Buttons) buttons = @( if ($CustomButtons) { $CustomButtons } else { foreach ($button in $Buttons) { if ($button -ne 'pdfHtml5') { @{ extend = $button } } else { @{ extend = 'pdfHtml5' pageSize = 'A3' orientation = 'landscape' } } } } ) "searchFade" = $false "colReorder" = -not $DisableColumnReorder.IsPresent # https://datatables.net/examples/basic_init/scroll_y_dynamic.html "paging" = -not $DisablePaging "scrollCollapse" = $ScrollCollapse.IsPresent <# Paging Type numbers - Page number buttons only simple - 'Previous' and 'Next' buttons only simple_numbers - 'Previous' and 'Next' buttons, plus page numbers full - 'First', 'Previous', 'Next' and 'Last' buttons full_numbers - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers first_last_numbers - 'First' and 'Last' buttons, plus page numbers #> "pagingType" = $PagingStyle "lengthMenu" = @( , @($PagingOptions + (-1)) , @($PagingOptions + "All") ) "ordering" = -not $DisableOrdering.IsPresent "order" = @() # this makes sure there's no default ordering upon start (usually it would be 1st column) "rowGroup" = '' "info" = -not $DisableInfo.IsPresent "procesing" = -not $DisableProcessing.IsPresent "select" = -not $DisableSelect.IsPresent "searching" = -not $DisableSearch.IsPresent "stateSave" = -not $DisableStateSave.IsPresent } if ($ScrollX) { $Options.'scrollX' = $true # disabling responsive table because it won't work with ScrollX $DisableResponsiveTable = $true } if ($ScrollY) { $Options.'scrollY' = "$($ScrollSizeY)px" } if ($FreezeColumnsLeft -or $FreezeColumnsRight) { $Options.fixedColumns = [ordered] @{ } if ($FreezeColumnsLeft) { $Options.fixedColumns.leftColumns = $FreezeColumnsLeft } if ($FreezeColumnsRight) { $Options.fixedColumns.rightColumns = $FreezeColumnsRight } } if ($FixedHeader -or $FixedFooter) { # Using FixedHeader/FixedFooter won't work with ScrollY. $Options.fixedHeader = [ordered] @{ } if ($FixedHeader) { $Options.fixedHeader.header = $FixedHeader.IsPresent } if ($FixedFooter) { $Options.fixedHeader.footer = $FixedFooter.IsPresent } } #} # this was due to: https://github.com/DataTables/DataTablesSrc/issues/143 if (-not $DisableResponsiveTable) { $Options["responsive"] = @{ } $Options["responsive"]['details'] = @{ } if ($ImmediatelyShowHiddenDetails) { $Options["responsive"]['details']['display'] = '$.fn.dataTable.Responsive.display.childRowImmediate' } if ($HideShowButton) { $Options["responsive"]['details']['type'] = 'none' # this makes button invisible } else { $Options["responsive"]['details']['type'] = 'inline' # this adds a button } } else { # HideSHowButton doesn't work # ImmediatelyShowHiddenDetails doesn't work # Maybe I should communicate this?? # Better would be with parametersets but don't want to play now } if ($OrderMulti) { $Options.orderMulti = $OrderMulti.IsPresent } if ($Find -ne '') { $Options.search = @{ search = $Find } } [int] $RowGroupingColumnID = -1 if ($RowGrouping.Count -gt 0) { if ($RowGrouping.Name) { $RowGroupingColumnID = ($HeaderNames).ToLower().IndexOf($RowGrouping.Name.ToLower()) } else { $RowGroupingColumnID = $RowGrouping.ColumnID } if ($RowGroupingColumnID -ne -1) { $ColumnsOrder = , @($RowGroupingColumnID, $RowGrouping.Sorting) if ($DefaultSortColumn.Count -gt 0 -or $DefaultSortIndex.Count -gt 0) { Write-Warning 'New-HTMLTable - Row grouping sorting overwrites default sorting.' } } else { Write-Warning 'New-HTMLTable - Row grouping disabled. Column name/id not found.' } } else { # Sorting if ($DefaultSortOrder -eq 'Ascending') { $Sort = 'asc' } else { $Sort = 'desc' } if ($DefaultSortColumn.Count -gt 0) { $ColumnsOrder = foreach ($Column in $DefaultSortColumn) { $DefaultSortingNumber = ($HeaderNames).ToLower().IndexOf($Column.ToLower()) if ($DefaultSortingNumber -ne - 1) { , @($DefaultSortingNumber, $Sort) } } } if ($DefaultSortIndex.Count -gt 0 -and $DefaultSortColumn.Count -eq 0) { $ColumnsOrder = foreach ($Column in $DefaultSortIndex) { if ($Column -ne - 1) { , @($Column, $Sort) } } } } if ($ColumnsOrder.Count -gt 0) { $Options."order" = @($ColumnsOrder) # there seems to be a bug in ordering and colReorder plugin # Disabling colReorder $Options.colReorder = $false } # Overwriting table size - screen size in percent. With added Section/Panels it shouldn't be more than 90% if ($ScreenSizePercent -gt 0) { $Options."scrollY" = "$($ScreenSizePercent)vh" } if ($null -ne $ConditionalFormatting -and $ConditionalFormatting.Count -gt 0) { $Options.createdRow = '' } if ($ResponsivePriorityOrderIndex -or $ResponsivePriorityOrder) { $PriorityOrder = 0 [Array] $PriorityOrderBinding = @( foreach ($_ in $ResponsivePriorityOrder) { $Index = [array]::indexof($HeaderNames.ToUpper(), $_.ToUpper()) if ($Index -ne -1) { @{ responsivePriority = 0; targets = $Index } } } foreach ($_ in $ResponsivePriorityOrderIndex) { @{ responsivePriority = 0; targets = $_ } } ) $Options.columnDefs = @( foreach ($_ in $PriorityOrderBinding) { $PriorityOrder++ $_.responsivePriority = $PriorityOrder $_ } ) } $Options = $Options | ConvertTo-Json -Depth 6 # cleans up $Options for ImmediatelyShowHiddenDetails # Since it's JavaScript inside we're basically removing double quotes from JSON in favor of no quotes at all # Before: "display": "$.fn.dataTable.Responsive.display.childRowImmediate" # After: "display": $.fn.dataTable.Responsive.display.childRowImmediate $Options = $Options -replace '"(\$\.fn\.dataTable\.Responsive\.display\.childRowImmediate)"', '$1' # Process Conditional Formatting. Ugly JS building $Options = New-TableConditionalFormatting -Options $Options -ConditionalFormatting $ConditionalFormatting -Header $HeaderNames # Process Row Grouping. Ugly JS building if ($RowGroupingColumnID -ne -1) { $Options = Convert-TableRowGrouping -Options $Options -RowGroupingColumnID $RowGroupingColumnID $RowGroupingTop = Add-TableRowGrouping -DataTableName $DataTableID -Top -Settings $RowGrouping $RowGroupingBottom = Add-TableRowGrouping -DataTableName $DataTableID -Bottom -Settings $RowGrouping } [Array] $Tabs = ($Script:HTMLSchema.TabsHeaders | Where-Object { $_.Current -eq $true }) if ($Tabs.Count -eq 0) { # There are no tabs in use, pretend there is only one Active Tab $Tab = @{ Active = $true } } else { # Get First Tab $Tab = $Tabs[0] } # return data if (-not $Simplify) { $Script:HTMLSchema.Features.Jquery = $true $Script:HTMLSchema.Features.DataTables = $true $Script:HTMLSchema.Features.DataTablesPDF = $true $Script:HTMLSchema.Features.DataTablesExcel = $true #$Script:HTMLSchema.Features.DataTablesSearchFade = $true if ($ScrollX) { $TableAttributes = @{ id = $DataTableID; class = "$($Style -join ' ')"; width = '100%' } } else { $TableAttributes = @{ id = $DataTableID; class = "$($Style -join ' ')"; width = '100%' } } # Enable Custom Date fromat sorting $SortingFormatDateTime = Add-CustomFormatForDatetimeSorting -DateTimeSortingFormat $DateTimeSortingFormat $FilteringOutput = Add-TableFiltering -Filtering $Filtering -FilteringLocation $FilteringLocation -DataTableName $DataTableID $FilteringTopCode = $FilteringOutput.FilteringTopCode $FilteringBottomCode = $FilteringOutput.FilteringBottomCode $LoadSavedState = Add-TableState -DataTableName $DataTableID -Filtering $Filtering -FilteringLocation $FilteringLocation -SavedState (-not $DisableStateSave) if ($Tab.Active -eq $true) { New-HTMLTag -Tag 'script' { @" `$(document).ready(function() { $SortingFormatDateTime $RowGroupingTop $LoadSavedState $FilteringTopCode // Table code var table = `$('#$DataTableID').DataTable( $($Options) ); $FilteringBottomCode $RowGroupingBottom }); "@ } } else { [string] $TabName = $Tab.Id New-HTMLTag -Tag 'script' { @" `$(document).ready(function() { $SortingFormatDateTime $RowGroupingTop `$('.tabs').on('click', 'a', function (event) { if (`$(event.currentTarget).attr("data-id") == "$TabName" && !$.fn.dataTable.isDataTable("#$DataTableID")) { $LoadSavedState $FilteringTopCode // Table code var table = `$('#$DataTableID').DataTable( $($Options) ); $FilteringBottomCode }; }); $RowGroupingBottom }); "@ } } } else { $TableAttributes = @{ class = 'simplify' } $Script:HTMLSchema.Features.DataTablesSimplify = $true } if ($InvokeHTMLTags) { # By default HTML tags are displayed, in this case we're converting tags into real tags $Table = $Table -replace '<', '<' -replace '>', '>' -replace '&nbsp;', ' ' -replace '"', '"' -replace ''', "'" } if (-not $DisableNewLine) { # Finds new lines and adds HTML TAG BR #$Table = $Table -replace '(?m)\s+$', "`r`n<BR>" $Table = $Table -replace '(?m)\s+$', "<BR>" } if ($OtherHTML) { $BeforeTableCode = Invoke-Command -ScriptBlock $OtherHTML } else { $BeforeTableCode = '' } if ($PreContent) { $BeforeTable = Invoke-Command -ScriptBlock $PreContent } else { $BeforeTable = '' } if ($PostContent) { $AfterTable = Invoke-Command -ScriptBlock $PostContent } else { $AfterTable = '' } if ($RowGrouping.Attributes.Count -gt 0) { $RowGroupingCSS = ConvertTo-CSS -ID $DataTableID -ClassName 'tr.dtrg-group td' -Attributes $RowGrouping.Attributes -Group } else { $RowGroupingCSS = '' } New-HTMLTag -Tag 'div' -Attributes @{ class = 'flexElement overflowHidden' } -Value { $RowGroupingCSS $BeforeTableCode $BeforeTable # Build HTML TABLE New-HTMLTag -Tag 'table' -Attributes $TableAttributes { New-HTMLTag -Tag 'thead' { if ($AddedHeader) { $AddedHeader } else { $Header } } New-HTMLTag -Tag 'tbody' { $Table } if (-not $HideFooter) { New-HTMLTag -Tag 'tfoot' { $Header } } } $AfterTable } } function New-HTMLTabOptions { [alias('TabOptions')] [CmdletBinding()] param( [switch] $SlimTabs, [RGBColors] $SelectorColor = [RGBColors]::None, [RGBColors] $SelectorColorTarget = [RGBColors]::None, [switch] $Transition, [switch] $LinearGradient ) if (-not $Script:HTMLSchema) { Write-Warning 'New-HTMLTabOptions - Creation of HTML aborted. Most likely New-HTML is missing.' Exit } #$Script:HTMLSchema.TabOptions = @{ } $Script:HTMLSchema.TabOptions.SlimTabs = $SlimTabs.IsPresent if ($SelectorColor -ne [RGBColors]::None) { # $Script:HTMLSchema.TabOptions.SelectorColor = ConvertFrom-Color -Color $SelectorColor $Script:Configuration.Features.Tabbis.CustomActionsReplace.ColorSelector = ConvertFrom-Color -Color $SelectorColor $Script:Configuration.Features.TabbisGradient.CustomActionsReplace.ColorSelector = ConvertFrom-Color -Color $SelectorColor # $Script:Configuration.Features.TabsTransition.CustomActionsReplace.ColorSelector = ConvertFrom-Color -Color $SelectorColor } if ($SelectorColorTarget -ne [RGBColors]::None) { $Script:Configuration.Features.Tabbis.CustomActionsReplace.ColorTarget = ConvertFrom-Color -Color $SelectorColorTarget $Script:Configuration.Features.TabbisGradient.CustomActionsReplace.ColorTarget = ConvertFrom-Color -Color $SelectorColorTarget } $Script:HTMLSchema.Features.TabbisGradient = $LinearGradient.IsPresent $Script:HTMLSchema.Features.TabbisTransition = $Transition.IsPresent } function New-HTMLTag { [CmdletBinding()] param( [Parameter(Mandatory = $false, Position = 0)][alias('Content')][ScriptBlock] $Value, [Parameter(Mandatory = $true, Position = 1)][string] $Tag, [System.Collections.IDictionary] $Attributes, [switch] $SelfClosing, [switch] $NewLine ) $HTMLTag = [Ordered] @{ Tag = $Tag Attributes = $Attributes Value = if ($null -eq $Value) { '' } else { Invoke-Command -ScriptBlock $Value } SelfClosing = $SelfClosing } $HTML = Set-Tag -HtmlObject $HTMLTag -NewLine:$NewLine return $HTML } function New-HTMLText { [alias('HTMLText', 'Text')] [CmdletBinding()] param( [string[]] $Text, [RGBColors[]] $Color = @(), [RGBColors[]] $BackGroundColor = @(), [int[]] $FontSize = @(), [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string[]] $FontWeight = @(), [ValidateSet('normal', 'italic', 'oblique')][string[]] $FontStyle = @(), [ValidateSet('normal', 'small-caps')][string[]] $FontVariant = @(), [string[]] $FontFamily = @(), [ValidateSet('left', 'center', 'right', 'justify')][string[]] $Alignment = @(), [ValidateSet('none', 'line-through', 'overline', 'underline')][string[]] $TextDecoration = @(), [ValidateSet('uppercase', 'lowercase', 'capitalize')][string[]] $TextTransform = @(), [ValidateSet('rtl')][string[]] $Direction = @(), [switch] $LineBreak, [switch] $SkipParagraph #, #[bool[]] $NewLine = @() ) #Write-Verbose 'New-HTMLText - Processing...' $DefaultColor = $Color[0] $DefaultFontSize = $FontSize[0] $DefaultFontWeight = if ($null -eq $FontWeight[0] ) { '' } else { $FontWeight[0] } $DefaultBackGroundColor = $BackGroundColor[0] $DefaultFontFamily = if ($null -eq $FontFamily[0] ) { '' } else { $FontFamily[0] } $DefaultFontStyle = if ($null -eq $FontStyle[0] ) { '' } else { $FontStyle[0] } $DefaultTextDecoration = if ($null -eq $TextDecoration[0]) { '' } else { $TextDecoration[0] } $DefaultTextTransform = if ($null -eq $TextTransform[0]) { '' } else { $TextTransform[0] } $DefaultFontVariant = if ($null -eq $FontVariant[0]) { '' } else { $FontVariant } $DefaultDirection = if ($null -eq $Direction[0]) { '' } else { $Direction[0] } $DefaultAlignment = if ($null -eq $Alignment[0]) { '' } else { $Alignment[0] } # $DefaultNewLine = if ($null -eq $NewLine[0]) { $false } else { $NewLine[0] } $Output = for ($i = 0; $i -lt $Text.Count; $i++) { if ($null -eq $FontWeight[$i]) { $ParamFontWeight = $DefaultFontWeight } else { $ParamFontWeight = $FontWeight[$i] } if ($null -eq $FontSize[$i]) { $ParamFontSize = $DefaultFontSize } else { $ParamFontSize = $FontSize[$i] } if ($null -eq $Color[$i]) { $ParamColor = $DefaultColor } else { $ParamColor = $Color[$i] } if ($null -eq $BackGroundColor[$i]) { $ParamBackGroundColor = $DefaultBackGroundColor } else { $ParamBackGroundColor = $BackGroundColor[$i] } if ($null -eq $FontFamily[$i]) { $ParamFontFamily = $DefaultFontFamily } else { $ParamFontFamily = $FontFamily[$i] } if ($null -eq $FontStyle[$i]) { $ParamFontStyle = $DefaultFontStyle } else { $ParamFontStyle = $FontStyle[$i] } if ($null -eq $TextDecoration[$i]) { $ParamTextDecoration = $DefaultTextDecoration } else { $ParamTextDecoration = $TextDecoration[$i] } if ($null -eq $TextTransform[$i]) { $ParamTextTransform = $DefaultTextTransform } else { $ParamTextTransform = $TextTransform[$i] } if ($null -eq $FontVariant[$i]) { $ParamFontVariant = $DefaultFontVariant } else { $ParamFontVariant = $FontVariant[$i] } if ($null -eq $Direction[$i]) { $ParamDirection = $DefaultDirection } else { $ParamDirection = $Direction[$i] } if ($null -eq $Alignment[$i]) { $ParamAlignment = $DefaultAlignment } else { $ParamAlignment = $Alignment[$i] } $newSpanTextSplat = @{ } $newSpanTextSplat.Color = $ParamColor $newSpanTextSplat.BackGroundColor = $ParamBackGroundColor $newSpanTextSplat.FontSize = $ParamFontSize if ($ParamFontWeight -ne '') { $newSpanTextSplat.FontWeight = $ParamFontWeight } $newSpanTextSplat.FontFamily = $ParamFontFamily if ($ParamFontStyle -ne '') { $newSpanTextSplat.FontStyle = $ParamFontStyle } if ($ParamFontVariant -ne '') { $newSpanTextSplat.FontVariant = $ParamFontVariant } if ($ParamTextDecoration -ne '') { $newSpanTextSplat.TextDecoration = $ParamTextDecoration } if ($ParamTextTransform -ne '') { $newSpanTextSplat.TextTransform = $ParamTextTransform } if ($ParamDirection -ne '') { $newSpanTextSplat.Direction = $ParamDirection } if ($ParamAlignment -ne '') { $newSpanTextSplat.Alignment = $ParamAlignment } $newSpanTextSplat.LineBreak = $LineBreak New-HTMLSpanStyle @newSpanTextSplat { if ($Text[$i] -match "\[([^\[]*)\)") { # Covers markdown LINK "[somestring](https://evotec.xyz)" $RegexBrackets1 = [regex] "\[([^\[]*)\]" # catch 'sometstring' $RegexBrackets2 = [regex] "\(([^\[]*)\)" # catch link $RegexBrackets3 = [regex] "\[([^\[]*)\)" # catch both somestring and link $Text1 = $RegexBrackets1.match($Text[$i]).Groups[1].value $Text2 = $RegexBrackets2.match($Text[$i]).Groups[1].value $Text3 = $RegexBrackets3.match($Text[$i]).Groups[0].value if ($Text1 -ne '' -and $Text2 -ne '') { $Link = New-HTMLAnchor -HrefLink $Text2 -Text $Text1 $Text[$i].Replace($Text3, $Link) } } else { # Default $Text[$i] # if ($NewLine[$i]) { # '<br>' #} } } } if ($SkipParagraph) { $Output -join '' } else { New-HTMLTag -Tag 'div' { $Output } } if ($LineBreak) { New-HTMLTag -Tag 'br' -SelfClosing } } function New-HTMLTimeline { param( [Parameter(Mandatory = $false, Position = 0)][alias('TimeLineItems')][ScriptBlock] $Content ) $Script:HTMLSchema.Features.TimeLine = $true New-HTMLTag -Tag 'div' -Attributes @{ class = 'timelineSimpleContainer' } { if ($null -eq $Value) { '' } else { Invoke-Command -ScriptBlock $Content } } } function New-HTMLTimelineItem { [CmdletBinding()] param( [DateTime] $Date = (Get-Date), [string] $HeadingText, [string] $Text, [nullable[RGBColors]] $Color ) $Attributes = @{ class = 'timelineSimple-item' "date-is" = $Date } if ($null -ne $Color) { $RGBcolor = ConvertFrom-Color -Color $Color $Style = "color: $RGBcolor;" } else { $Style = '' } # $Script:HTMLSchema.Features.TimeLine = $true New-HTMLTag -Tag 'div' -Attributes $Attributes -Value { New-HTMLTag -Tag 'h1' -Attributes @{ class = 'timelineSimple'; style = $style } { $HeadingText } New-HTMLTag -Tag 'p' -Attributes @{ class = 'timelineSimple' } { $Text -Replace [Environment]::NewLine, '<br>' -replace '\n', '<br>' } } } function New-HTMLToast { [CmdletBinding()] param( [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")][string] $TextHeader, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")][RGBColors] $TextHeaderColor = [RGBColors]::None, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")][string] $Text, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")][RGBColors] $TextColor = [RGBColors]::None, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")][int] $IconSize = 30, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")][RGBColors] $IconColor = [RGBColors]::Blue, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")][RGBColors] $BarColorLeft = [RGBColors]::Blue, [parameter(ParameterSetName = "FontAwesomeBrands")] [parameter(ParameterSetName = "FontAwesomeRegular")] [parameter(ParameterSetName = "FontAwesomeSolid")][RGBColors] $BarColorRight = [RGBColors]::None, # 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 ) [string] $Icon = '' if ($IconBrands) { $Icon = "fab fa-$IconBrands" # fa-$($FontSize)x" } elseif ($IconRegular) { $Icon = "far fa-$IconRegular" # fa-$($FontSize)x" } elseif ($IconSolid) { $Icon = "fas fa-$IconSolid" # fa-$($FontSize)x" } $Script:HTMLSchema.Features.Toasts = $true [string] $DivClass = "toast" $StyleText = @{ } if ($TextColor -ne [RGBColors]::None) { $StyleText.'color' = ConvertFrom-Color -Color $TextColor } $StyleTextHeader = @{ } if ($TextHeaderColor -ne [RGBColors]::None) { $StyleTextHeader.'color' = ConvertFrom-Color -Color $TextHeaderColor } $StyleIcon = @{ } if ($IconSize -ne 0) { $StyleIcon.'font-size' = "$($IconSize)px" } if ($IconColor -ne [RGBColors]::None) { $StyleIcon.'color' = ConvertFrom-Color -Color $IconColor } $StyleBarLeft = @{ } if ($BarColorLeft -ne [RGBColors]::None) { $StyleBarLeft.'background-color' = ConvertFrom-Color -Color $BarColorLeft } $StyleBarRight = @{ } if ($BarColorRight -ne [RGBColors]::None) { $StyleBarRight.'background-color' = ConvertFrom-Color -Color $BarColorRight } New-HTMLTag -Tag 'div' -Attributes @{ class = $DivClass } { New-HTMLTag -Tag 'div' -Attributes @{ class = 'toastBorderLeft'; style = $StyleBarLeft } New-HTMLTag -Tag 'div' -Attributes @{ class = "toastIcon $Icon"; style = $StyleIcon } New-HTMLTag -Tag 'div' -Attributes @{ class = 'toastContent' } { New-HTMLTag -Tag 'p' -Attributes @{ class = 'toastTextHeader'; style = $StyleTextHeader } { $TextHeader } New-HTMLTag -Tag 'p' -Attributes @{ class = 'toastText'; style = $StyleText } { $Text } } New-HTMLTag -Tag 'div' -Attributes @{ class = 'toastBorderRight'; style = $StyleBarRight } } } function New-InternalDiagram { [CmdletBinding()] param( [System.Collections.IList] $Nodes, [System.Collections.IList] $Edges, [System.Collections.IList] $Events, [System.Collections.IDictionary] $Options, [string] $Height, [string] $Width, [string] $BackgroundImage, [string] $BackgroundSize = '100% 100%' ) $Script:HTMLSchema.Features.VisNetwork = $true $Style = @{ } if ($Width -or $Height) { $Style['width'] = $Width $Style['height'] = $Height } if ($BackgroundImage) { $Style['background'] = "url('$BackgroundImage')" $Style['background-size'] = $BackgroundSize } [string] $ID = "Diagram-" + (Get-RandomStringName -Size 8) $Div = New-HTMLTag -Tag 'div' -Attributes @{ id = $ID; class = 'diagram'; style = $Style } $ConvertedNodes = $Nodes -join ', ' $ConvertedEdges = $Edges -join ', ' if ($Events.Count -gt 0) { [Array] $PreparedEvents = @( # https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex' @' function escapeRegExp(string) { return string.toString().replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string }; '@ 'network.on("click", function (params) {' 'params.event = "[original event]";' 'var findValue = escapeRegExp(params.nodes);' foreach ($_ in $Events) { New-DiagramInternalEvent -ID $_.ID -ColumnID $_.ColumnID } '});' ) } $Script = New-HTMLTag -Tag 'script' -Value { # Convert Dictionary to JSON and return chart within SCRIPT tag # Make sure to return with additional empty string '// create an array with nodes' "var nodes = new vis.DataSet([$ConvertedNodes]); " '// create an array with edges' "var edges = new vis.DataSet([$ConvertedEdges]); " '// create a network' "var Container = document.getElementById('$ID'); " "var data = { " " nodes: nodes, " " edges: edges" " }; " if ($Options) { $ConvertedOptions = $Options | ConvertTo-Json -Depth 5 | ForEach-Object { [System.Text.RegularExpressions.Regex]::Unescape($_) } "var options = $ConvertedOptions; " } else { "var options = { }; " } 'var network = new vis.Network(Container, data, options); ' $PreparedEvents } -NewLine $Div $Script:HTMLSchema.Diagrams.Add($Script) } function New-TableButtonCopy { [alias('TableButtonCopy', 'EmailTableButtonCopy', 'New-HTMLTableButtonCopy')] [CmdletBinding()] param() [PSCustomObject] @{ Type = 'TableButtonCopy' Output = @{ extend = 'copyHtml5' } } } function New-TableButtonCSV { [alias('TableButtonCSV', 'EmailTableButtonCSV', 'New-HTMLTableButtonCSV')] [CmdletBinding()] param() [PSCustomObject] @{ Type = 'TableButtonCSV' Output = @{ extend = 'csvHtml5' } } } function New-TableButtonExcel { [alias('TableButtonExcel', 'EmailTableButtonExcel', 'New-HTMLTableButtonExcel')] [CmdletBinding()] param() [PSCustomObject] @{ Type = 'TableButtonExcel' Output = @{ extend = 'excelHtml5' } } } function New-TableButtonPageLength { [alias('TableButtonPageLength', 'EmailTableButtonPageLength', 'New-HTMLTableButtonPageLength')] [CmdletBinding()] param() [PSCustomObject] @{ Type = 'TableButtonPageLength' Output = @{ extend = 'pageLength' } } } function New-TableButtonPDF { <# .SYNOPSIS Allows more control when adding buttons to Table .DESCRIPTION Allows more control when adding buttons to Table. Works only within Table or New-HTMLTable scriptblock. .PARAMETER Title Document title (appears above the table in the generated PDF). The special character * is automatically replaced with the value read from the host document's title tag. .PARAMETER DisplayName The button's display text. The text can be configured using this option .PARAMETER MessageBottom Message to be shown at the bottom of the table, or the caption tag if displayed at the bottom of the table. .PARAMETER MessageTop Message to be shown at the top of the table, or the caption tag if displayed at the top of the table. .PARAMETER FileName File name to give the created file (plus the extension defined by the extension option). The special character * is automatically replaced with the value read from the host document's title tag. .PARAMETER Extension The extension to give the created file name. (default .pdf) .PARAMETER PageSize Paper size for the created PDF. This can be A3, A4, A5, LEGAL, LETTER or TABLOID. Other options are available. .PARAMETER Orientation Paper orientation for the created PDF. This can be portrait or landscape .PARAMETER Header Indicate if the table header should be included in the exported data or not. .PARAMETER Footer Indicate if the table footer should be included in the exported data or not. .EXAMPLE Dashboard -Name 'Dashimo Test' -FilePath $PSScriptRoot\DashboardEasy05.html -Show { Section -Name 'Test' -Collapsable { Container { Panel { Table -DataTable $Process { TableButtonPDF TableButtonCopy TableButtonExcel } -Buttons @() -DisableSearch -DisablePaging -HideFooter } Panel { Table -DataTable $Process -Buttons @() -DisableSearch -DisablePaging -HideFooter } Panel { Table -DataTable $Process { TableButtonPDF -PageSize A10 -Orientation landscape TableButtonCopy TableButtonExcel } -Buttons @() -DisableSearch -DisablePaging -HideFooter } } } } .NOTES Options are based on this URL: https://datatables.net/reference/button/pdfHtml5 #> [alias('TableButtonPDF', 'EmailTableButtonPDF', 'New-HTMLTableButtonPDF')] [CmdletBinding()] param( [string] $Title, [string] $DisplayName, [string] $MessageBottom, [string] $MessageTop, [string] $FileName, [string] $Extension, [string][ValidateSet('4A0', '2A0', 'A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10', 'B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9', 'B10', 'C0', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9', 'C10', 'RA0', 'RA1', 'RA2', 'RA3', 'RA4', 'SRA0', 'SRA1', 'SRA2', 'SRA3', 'SRA4', 'EXECUTIVE', 'FOLIO', 'LEGAL', 'LETTER', 'TABLOID')] $PageSize = 'A3', [string][ValidateSet('portrait', 'landscape')] $Orientation = 'landscape', [switch] $Header, [switch] $Footer ) $Button = @{ } $Button.extend = 'pdfHtml5' $Button.pageSize = $PageSize $Button.orientation = $Orientation if ($MessageBottom) { $Button.messageBottom = $MessageBottom } if ($MessageTop) { $Button.messageTop = $MessageTop } if ($DisplayName) { $Button.text = $DisplayName } if ($Title) { $Button.title = $Title } if ($FileName) { $Button.filename = $FileName } if ($Extension) { $Button.extension = $Extension } if ($Header) { $Button.header = $Header.IsPresent } if ($Footer) { $Button.footer = $Footer.IsPresent } [PSCustomObject] @{ Type = 'TableButtonPDF' Output = $Button } } function New-TableButtonPrint { [alias('TableButtonPrint', 'EmailTableButtonPrint', 'New-HTMLTableButtonPrint')] [CmdletBinding()] param() $Button = @{ extend = 'print' } [PSCustomObject] @{ Type = 'TableButtonPrint' Output = $Button } } function New-TableCondition { [alias('EmailTableCondition', 'TableConditionalFormatting', 'New-HTMLTableCondition')] [CmdletBinding()] param( [alias('ColumnName')][string] $Name, [alias('Type')][ValidateSet('number', 'string')][string] $ComparisonType, [ValidateSet('lt', 'le', 'eq', 'ge', 'gt', 'ne', 'contains', 'like')][string] $Operator, [Object] $Value, [switch] $Row, [nullable[RGBColors]] $Color, [nullable[RGBColors]] $BackgroundColor, [int] $FontSize, [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeight, [ValidateSet('normal', 'italic', 'oblique')][string] $FontStyle, [ValidateSet('normal', 'small-caps')][string] $FontVariant, [string] $FontFamily, [ValidateSet('left', 'center', 'right', 'justify')][string] $Alignment, [ValidateSet('none', 'line-through', 'overline', 'underline')][string] $TextDecoration, [ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $TextTransform, [ValidateSet('rtl')][string] $Direction, [switch] $Inline ) $Style = @{ Color = $Color BackGroundColor = $BackGroundColor FontSize = $FontSize FontWeight = $FontWeight FontStyle = $FontStyle FontVariant = $FontVariant FontFamily = $FontFamily Alignment = $Alignment TextDecoration = $TextDecoration TextTransform = $TextTransform Direction = $Direction } Remove-EmptyValues -Hashtable $Style $TableCondition = [PSCustomObject] @{ Row = $Row Type = if (-not $ComparisonType) { 'string' } else { $ComparisonType } Name = $Name Operator = if (-not $Operator) { 'eq' } else { $Operator } Value = $Value Color = $Color BackgroundColor = $BackgroundColor Style = ConvertTo-HTMLStyle @Style } [PSCustomObject] @{ Type = if ($Inline) { 'TableConditionInline' } else { 'TableCondition' } Output = $TableCondition } } function New-TableConditionalFormatting { [CmdletBinding()] param( [string] $Options, [Array] $ConditionalFormatting, [string[]] $Header ) if ($ConditionalFormatting.Count -gt 0) { # Conditional - changes PowerShellOperator into JS operator foreach ($Formatting in $ConditionalFormatting) { if ($Formatting.Operator -eq 'gt') { $Formatting.Operator = '>' } elseif ($Formatting.Operator -eq 'lt') { $Formatting.Operator = '<' } elseif ($Formatting.Operator -eq 'eq') { $Formatting.Operator = '==' } elseif ($Formatting.Operator -eq 'le') { $Formatting.Operator = '<=' } elseif ($Formatting.Operator -eq 'ge') { $Formatting.Operator = '>=' } elseif ($Formatting.Operator -eq 'ne') { $Formatting.Operator = '!=' } # Operator like/contains are taken care of below } $Condition = @( '"createdRow": function (row, data, dataIndex, column) {' foreach ($Condition in $ConditionalFormatting) { $ConditionHeaderNr = $Header.ToLower().IndexOf($($Condition.Name.ToLower())) $Style = $Condition.Style | ConvertTo-Json [string] $StyleDefinition = ".css($Style)" if ($null -eq $Condition.Type -or $Condition.Type -eq 'number' -or $Condition.Type -eq 'int' -or $Condition.Type -eq 'decimal') { "if (data[$ConditionHeaderNr] $($Condition.Operator) $($Condition.Value)) {" } elseif ($Condition.Type -eq 'string') { switch ($Condition.Operator) { "contains" { "if (data[$($ConditionHeaderNr)].includes('$($Condition.Value)')) {" } "like" { "if (data[$($ConditionHeaderNr)].includes('$($Condition.Value)')) {" } default { "if (data[$ConditionHeaderNr] $($Condition.Operator) '$($Condition.Value)') {" } } } elseif ($Condition.Type -eq 'date') { "if (new Date(data[$ConditionHeaderNr]) $($Condition.Operator) new Date('$($Condition.Value)')) {" } if ($null -ne $Condition.Row -and $Condition.Row -eq $true) { "`$(column)$($StyleDefinition);" } else { "`$(column[$ConditionHeaderNr])$($StyleDefinition);" } "}" } '}' ) if ($PSEdition -eq 'Desktop') { $TextToFind = '"createdRow": ""' } else { $TextToFind = '"createdRow": ""' } $Options = $Options -Replace ($TextToFind, $Condition) } return $Options } function New-TableContent { [alias('TableContent', 'EmailTableContent', 'New-HTMLTableContent')] [CmdletBinding()] param( [alias('ColumnNames', 'Names', 'Name')][string[]] $ColumnName, [int[]] $ColumnIndex, [int[]] $RowIndex, [string[]] $Text, [RGBColors] $Color, [RGBColors] $BackGroundColor, [int] $FontSize, [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeight, [ValidateSet('normal', 'italic', 'oblique')][string] $FontStyle, [ValidateSet('normal', 'small-caps')][string] $FontVariant, [string] $FontFamily, [ValidateSet('left', 'center', 'right', 'justify')][string] $Alignment, [ValidateSet('none', 'line-through', 'overline', 'underline')][string] $TextDecoration, [ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $TextTransform, [ValidateSet('rtl')][string] $Direction ) $Style = @{ Color = $Color BackGroundColor = $BackGroundColor FontSize = $FontSize FontWeight = $FontWeight FontStyle = $FontStyle FontVariant = $FontVariant FontFamily = $FontFamily Alignment = $Alignment TextDecoration = $TextDecoration TextTransform = $TextTransform Direction = $Direction } Remove-EmptyValues -Hashtable $Style [PSCustomObject]@{ Type = 'TableContentStyle' Output = @{ Name = $ColumnName Text = $Text RowIndex = $RowIndex | Sort-Object ColumnIndex = $ColumnIndex | Sort-Object Style = ConvertTo-HTMLStyle @Style Used = $false } } } function New-TableHeader { [alias('TableHeader', 'EmailTableHeader', 'New-HTMLTableHeader')] [CmdletBinding()] param( [string[]] $Names, [string] $Title, [RGBColors] $Color, [RGBColors] $BackGroundColor, [int] $FontSize, [ValidateSet('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900')][string] $FontWeight, [ValidateSet('normal', 'italic', 'oblique')][string] $FontStyle, [ValidateSet('normal', 'small-caps')][string] $FontVariant, [string] $FontFamily, [ValidateSet('left', 'center', 'right', 'justify')][string] $Alignment, [ValidateSet('none', 'line-through', 'overline', 'underline')][string] $TextDecoration, [ValidateSet('uppercase', 'lowercase', 'capitalize')][string] $TextTransform, [ValidateSet('rtl')][string] $Direction, [switch] $AddRow, [int] $ColumnCount, [ValidateSet( 'all', 'none', 'never', 'desktop', 'not-desktop', 'tablet-l', 'tablet-p', 'mobile-l', 'mobile-p', 'min-desktop', 'max-desktop', 'tablet', 'not-tablet', 'min-tablet', 'max-tablet', 'not-tablet-l', 'min-tablet-l', 'max-tablet-l', 'not-tablet-p', 'min-tablet-p', 'max-tablet-p', 'mobile', 'not-mobile', 'min-mobile', 'max-mobile', 'not-mobile-l', 'min-mobile-l', 'max-mobile-l', 'not-mobile-p', 'min-mobile-p', 'max-mobile-p' )][string] $ResponsiveOperations ) if ($AddRow) { Write-Warning "New-HTMLTableHeader - Using AddRow switch is deprecated. It's not nessecary anymore. Just use Title alone. It will be removed later on." } $Style = @{ Color = $Color BackGroundColor = $BackGroundColor FontSize = $FontSize FontWeight = $FontWeight FontStyle = $FontStyle FontVariant = $FontVariant FontFamily = $FontFamily Alignment = $Alignment TextDecoration = $TextDecoration TextTransform = $TextTransform Direction = $Direction } Remove-EmptyValues -Hashtable $Style if (($AddRow -and $Title) -or ($Title -and -not $Names)) { $Type = 'TableHeaderFullRow' } elseif ((-not $AddRow -and $Title) -or ($Title -and $Names)) { $Type = 'TableHeaderMerge' } elseif ($Names -and $ResponsiveOperations) { $Type = 'TableHeaderResponsiveOperations' } elseif ($ResponsiveOperations) { Write-Warning 'New-HTMLTableHeader - ResponsiveOperations require Names (ColumnNames) to apply operation to.' return } else { $Type = 'TableHeaderStyle' } [PSCustomObject]@{ Type = $Type Output = @{ Names = $Names ResponsiveOperations = $ResponsiveOperations Title = $Title Style = ConvertTo-HTMLStyle @Style ColumnCount = $ColumnCount } } } function New-TableReplace { [alias('TableReplace', 'EmailTableReplace', 'New-HTMLTableReplace')] [CmdletBinding()] param( [string] $FieldName, [string[]] $Replacements ) [PSCustomObject]@{ Type = 'TableReplaceCompare' Output = @{ $FieldName = $Replacements } } } function New-TableRowGrouping { [alias('TableRowGrouping', 'EmailTableRowGrouping', 'New-HTMLTableRowGrouping')] [CmdletBinding()] param( [alias('ColumnName')][string] $Name, [int] $ColumnID = -1, [ValidateSet('Ascending', 'Descending')][string] $SortOrder = 'Ascending', [RGBColors] $Color, [RGBColors] $BackgroundColor ) $Object = [PSCustomObject] @{ Type = 'TableRowGrouping' Output = [ordered] @{ Name = $Name ColumnID = $ColumnID Sorting = if ('Ascending') { 'asc' } else { 'desc' } Attributes = @{ 'color' = ConvertFrom-Color -Color $Color 'background-color' = ConvertFrom-Color -Color $BackgroundColor } } } Remove-EmptyValues -Hashtable $Object.Output $Object } function Out-HtmlView { <# .SYNOPSIS Small function that allows to send output to HTML .DESCRIPTION Small function that allows to send output to HTML. When displaying in HTML it allows data to output to EXCEL, CSV and PDF. It allows sorting, searching and so on. .PARAMETER Table Data you want to display .PARAMETER Title Title of HTML Window .PARAMETER DefaultSortColumn Sort by Column Name .PARAMETER DefaultSortIndex Sort by Column Index .EXAMPLE Get-Process | Select-Object -First 5 | Out-HtmlView .NOTES General notes #> [alias('Out-GridHtml', 'ohv')] [CmdletBinding()] param( [Parameter(Mandatory = $false, Position = 0)][ScriptBlock] $HTML, [Parameter(Mandatory = $false, Position = 1)][ScriptBlock] $PreContent, [Parameter(Mandatory = $false, Position = 2)][ScriptBlock] $PostContent, [alias('ArrayOfObjects', 'Object', 'DataTable')][Parameter(ValueFromPipeline = $true, Mandatory = $true)] $Table, [string] $FilePath, [string] $Title = 'Out-HTMLView', [switch] $PassThru, [string[]][ValidateSet('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5')] $Buttons = @('copyHtml5', 'excelHtml5', 'csvHtml5', 'pdfHtml5', 'pageLength'), [string[]][ValidateSet('numbers', 'simple', 'simple_numbers', 'full', 'full_numbers', 'first_last_numbers')] $PagingStyle = 'full_numbers', [int[]]$PagingOptions = @(15, 25, 50, 100), [switch]$DisablePaging, [switch]$DisableOrdering, [switch]$DisableInfo, [switch]$HideFooter, [switch]$DisableColumnReorder, [switch]$DisableProcessing, [switch]$DisableResponsiveTable, [switch]$DisableSelect, [switch]$DisableStateSave, [switch]$DisableSearch, [switch]$ScrollCollapse, [switch]$OrderMulti, [switch]$Filtering, [ValidateSet('Top', 'Bottom', 'Both')][string]$FilteringLocation = 'Bottom', [string[]][ValidateSet('display', 'cell-border', 'compact', 'hover', 'nowrap', 'order-column', 'row-border', 'stripe')] $Style = @('display', 'compact'), [switch]$Simplify, [string]$TextWhenNoData = 'No data available.', [int] $ScreenSizePercent = 0, [string[]] $DefaultSortColumn, [int[]] $DefaultSortIndex, [ValidateSet('Ascending', 'Descending')][string] $DefaultSortOrder = 'Ascending', [string[]]$DateTimeSortingFormat, [alias('Search')][string]$Find, [switch] $InvokeHTMLTags, [switch] $DisableNewLine, [switch] $ScrollX, [switch] $ScrollY, [int] $ScrollSizeY = 500, [int] $FreezeColumnsLeft, [int] $FreezeColumnsRight, [switch] $FixedHeader, [switch] $FixedFooter, [string[]] $ResponsivePriorityOrder, [int[]] $ResponsivePriorityOrderIndex, [string[]] $PriorityProperties, [switch] $ImmediatelyShowHiddenDetails, [alias('RemoveShowButton')][switch] $HideShowButton, [switch] $AllProperties, [switch] $Compare, [alias('CompareWithColors')][switch] $HighlightDifferences, [int] $First, [int] $Last, [alias('Replace')][Array] $CompareReplace ) Begin { $DataTable = [System.Collections.Generic.List[Object]]::new() if ($FilePath -eq '') { $FilePath = Get-FileName -Extension 'html' -Temporary } } Process { if ($null -ne $Table) { foreach ($T in $Table) { $DataTable.Add($T) } } } End { if ($null -ne $Table) { # HTML generation part New-HTML -FilePath $FilePath -UseCssLinks -UseJavaScriptLinks -TitleText $Title -ShowHTML { New-HTMLTable -DataTable $DataTable ` -HideFooter:$HideFooter ` -Buttons $Buttons -PagingStyle $PagingStyle -PagingOptions $PagingOptions ` -DisablePaging:$DisablePaging -DisableOrdering:$DisableOrdering -DisableInfo:$DisableInfo -DisableColumnReorder:$DisableColumnReorder -DisableProcessing:$DisableProcessing ` -DisableResponsiveTable:$DisableResponsiveTable -DisableSelect:$DisableSelect -DisableStateSave:$DisableStateSave -DisableSearch:$DisableSearch -ScrollCollapse:$ScrollCollapse ` -Style $Style -TextWhenNoData:$TextWhenNoData -ScreenSizePercent $ScreenSizePercent ` -HTML $HTML -PreContent $PreContent -PostContent $PostContent ` -DefaultSortColumn $DefaultSortColumn -DefaultSortIndex $DefaultSortIndex -DefaultSortOrder $DefaultSortOrder ` -DateTimeSortingFormat $DateTimeSortingFormat -Find $Find -OrderMulti:$OrderMulti ` -Filtering:$Filtering -FilteringLocation $FilteringLocation ` -InvokeHTMLTags:$InvokeHTMLTags -DisableNewLine:$DisableNewLine -ScrollX:$ScrollX -ScrollY:$ScrollY -ScrollSizeY $ScrollSizeY ` -FreezeColumnsLeft $FreezeColumnsLeft -FreezeColumnsRight $FreezeColumnsRight ` -FixedHeader:$FixedHeader -FixedFooter:$FixedFooter -ResponsivePriorityOrder $ResponsivePriorityOrder ` -ResponsivePriorityOrderIndex $ResponsivePriorityOrderIndex -PriorityProperties $PriorityProperties -AllProperties:$AllProperties ` -Compare:$Compare -HighlightDifferences:$HighlightDifferences -First $First -Last $Last ` -ImmediatelyShowHiddenDetails:$ImmediatelyShowHiddenDetails -Simplify:$Simplify -HideShowButton:$HideShowButton -CompareReplace $CompareReplace } if ($PassThru) { # This isn't really real PassThru but just passing final object further down the pipe when needed # real PassThru requires significant work - if you're up to it, let me know. $DataTable } } else { Write-Warning 'Out-HtmlView - No data available.' } } } $Script:Configuration = [ordered] @{ Features = [ordered] @{ Default = @{ Comment = 'Always Required Default Visual Settings' HeaderAlways = @{ Css = "$PSScriptRoot\Resources\CSS\default.css" } } DefaultHeadings = @{ Comment = 'Always Required Default Headings' HeaderAlways = @{ Css = "$PSScriptRoot\Resources\CSS\headings.css" } } Accordion = @{ Comment = 'Accordion' HeaderAlways = @{ Css = "$PSScriptRoot\Resources\CSS\accordion-1.css" } } CodeBlocks = @{ Comment = 'EnlighterJS CodeBlocks' Header = @{ CssLink = 'https://evotec.xyz/wp-content/uploads/pswritehtml/enlighterjs30/enlighterjs.min.css' Css = "$PSScriptRoot\Resources\CSS\enlighterjs.min.css" JsLink = 'https://evotec.xyz/wp-content/uploads/pswritehtml/enlighterjs30/enlighterjs.min.js' JS = "$PSScriptRoot\Resources\JS\enlighterjs.min.js" } Footer = @{ } HeaderAlways = @{ Css = "$PSScriptRoot\Resources\CSS\enlighterjs.css" } FooterAlways = @{ JS = "$PSScriptRoot\Resources\JS\enlighterjs-footer.js" } } ChartsApex = @{ Comment = 'Apex Charts' Header = @{ JsLink = 'https://cdn.jsdelivr.net/npm/apexcharts@latest' JS = "$PSScriptRoot\Resources\JS\apexcharts.min.js" } HeaderAlways = @{ #Css = "$PSScriptRoot\Resources\CSS\apexcharts.css" } } Jquery = @{ Comment = 'Jquery' Header = @{ JsLink = 'https://code.jquery.com/jquery-3.3.1.min.js' Js = "$PSScriptRoot\Resources\JS\jquery-3.3.1.min.js" } } DataTablesOld = @{ Comment = 'DataTables' HeaderAlways = @{ Css = "$PSScriptRoot\Resources\CSS\datatables.css" CssNoscript = "$PSScriptRoot\Resources\CSS\datatables.noscript.css" } Header = @{ CssLink = 'https://cdn.datatables.net/v/dt/jq-3.3.1/dt-1.10.18/af-2.3.2/b-1.5.4/b-colvis-1.5.4/b-html5-1.5.4/b-print-1.5.4/cr-1.5.0/fc-3.2.5/fh-3.1.4/kt-2.5.0/r-2.2.2/rg-1.1.0/rr-1.2.4/sc-1.5.0/sl-1.2.6/datatables.min.css' Css = "$PSScriptRoot\Resources\CSS\datatables.min.css" JsLink = @( "https://cdn.datatables.net/v/dt/jq-3.3.1/dt-1.10.18/af-2.3.2/b-1.5.4/b-colvis-1.5.4/b-html5-1.5.4/b-print-1.5.4/cr-1.5.0/fc-3.2.5/fh-3.1.4/kt-2.5.0/r-2.2.2/rg-1.1.0/rr-1.2.4/sc-1.5.0/sl-1.2.6/datatables.min.js" "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.4/moment.min.js" "https://cdn.datatables.net/plug-ins/1.10.19/sorting/datetime-moment.js" ) JS = @( "$PSScriptRoot\Resources\JS\datatables.min.js" "$PSScriptRoot\Resources\JS\moment.min.js" "$PSScriptRoot\Resources\JS\datetime-moment.js" ) } } DataTablesSearchFade = @{ Comment = 'DataTables SearchFade' Header = @{ CssLink = 'https://cdn.datatables.net/plug-ins/preview/searchFade/dataTables.searchFade.min.css' Css = "$PSScriptRoot\Resources\CSS\datatablesSearchFade.css" JsLink = "https://cdn.datatables.net/plug-ins/preview/searchFade/dataTables.searchFade.min.js" JS = "$PSScriptRoot\Resources\JS\datatables.SearchFade.min" } } DataTables = @{ Comment = 'DataTables' HeaderAlways = @{ Css = "$PSScriptRoot\Resources\CSS\datatables.css" CssNoscript = "$PSScriptRoot\Resources\CSS\datatables.noscript.css" } Header = @{ CssLink = @( "https://cdn.datatables.net/1.10.20/css/jquery.dataTables.min.css" "https://cdn.datatables.net/autofill/2.3.4/css/autoFill.dataTables.css" "https://cdn.datatables.net/buttons/1.6.1/css/buttons.dataTables.min.css" "https://cdn.datatables.net/colreorder/1.5.2/css/colReorder.dataTables.min.css" "https://cdn.datatables.net/fixedcolumns/3.3.0/css/fixedColumns.dataTables.min.css" "https://cdn.datatables.net/fixedheader/3.1.6/css/fixedHeader.dataTables.min.css" "https://cdn.datatables.net/keytable/2.5.1/css/keyTable.dataTables.min.css" "https://cdn.datatables.net/responsive/2.2.3/css/responsive.dataTables.min.css" "https://cdn.datatables.net/rowgroup/1.1.1/css/rowGroup.dataTables.min.css" "https://cdn.datatables.net/rowreorder/1.2.6/css/rowReorder.dataTables.min.css" "https://cdn.datatables.net/scroller/2.0.1/css/scroller.dataTables.min.css" "https://cdn.datatables.net/select/1.3.1/css/select.dataTables.min.css" ) Css = @( "$PSScriptRoot\Resources\CSS\jquery.dataTables.min.css" "$PSScriptRoot\Resources\CSS\autoFill.dataTables.css" "$PSScriptRoot\Resources\CSS\buttons.dataTables.min.css" "$PSScriptRoot\Resources\CSS\colReorder.dataTables.min.css" "$PSScriptRoot\Resources\CSS\fixedColumns.dataTables.min.css" "$PSScriptRoot\Resources\CSS\fixedHeader.dataTables.min.css" "$PSScriptRoot\Resources\CSS\keyTable.dataTables.min.css" "$PSScriptRoot\Resources\CSS\responsive.dataTables.min.css" "$PSScriptRoot\Resources\CSS\rowGroup.dataTables.min.css" "$PSScriptRoot\Resources\CSS\rowReorder.dataTables.min.css" "$PSScriptRoot\Resources\CSS\scroller.dataTables.min.css" "$PSScriptRoot\Resources\CSS\select.dataTables.min.css" ) JsLink = @( #"https://code.jquery.com/jquery-3.3.1.min.js" #"https://cdnjs.cloudflare.com/ajax/libs/jszip/2.5.0/jszip.min.js" #"https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.36/pdfmake.min.js" #"https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.36/vfs_fonts.js" "https://nightly.datatables.net/js/jquery.dataTables.min.js" "https://cdn.datatables.net/autofill/2.3.4/js/dataTables.autoFill.min.js" "https://cdn.datatables.net/buttons/1.6.1/js/dataTables.buttons.min.js" "https://cdn.datatables.net/buttons/1.6.1/js/buttons.colVis.min.js" "https://cdn.datatables.net/buttons/1.6.1/js/buttons.html5.min.js" "https://cdn.datatables.net/buttons/1.6.1/js/buttons.print.min.js" "https://cdn.datatables.net/colreorder/1.5.2/js/dataTables.colReorder.min.js" "https://cdn.datatables.net/fixedcolumns/3.3.0/js/dataTables.fixedColumns.min.js" "https://cdn.datatables.net/fixedheader/3.1.6/js/dataTables.fixedHeader.min.js" "https://cdn.datatables.net/keytable/2.5.1/js/dataTables.keyTable.min.js" "https://cdn.datatables.net/responsive/2.2.3/js/dataTables.responsive.min.js" "https://cdn.datatables.net/rowgroup/1.1.1/js/dataTables.rowGroup.min.js" "https://cdn.datatables.net/rowreorder/1.2.6/js/dataTables.rowReorder.min.js" "https://cdn.datatables.net/scroller/2.0.1/js/dataTables.scroller.min.js" "https://cdn.datatables.net/select/1.3.1/js/dataTables.select.min.js" "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js" "https://cdn.datatables.net/plug-ins/1.10.20/sorting/datetime-moment.js" ) JS = @( "$PSScriptRoot\Resources\JS\jquery.dataTables.min.js" "$PSScriptRoot\Resources\JS\dataTables.autoFill.min.js" "$PSScriptRoot\Resources\JS\dataTables.buttons.min.js" "$PSScriptRoot\Resources\JS\buttons.colVis.min.js" "$PSScriptRoot\Resources\JS\buttons.html5.min.js" "$PSScriptRoot\Resources\JS\buttons.print.min.js" "$PSScriptRoot\Resources\JS\dataTables.colReorder.min.js" "$PSScriptRoot\Resources\JS\dataTables.fixedColumns.min.js" "$PSScriptRoot\Resources\JS\dataTables.fixedHeader.min.js" "$PSScriptRoot\Resources\JS\dataTables.keyTable.min.js" "$PSScriptRoot\Resources\JS\dataTables.responsive.min.js" "$PSScriptRoot\Resources\JS\dataTables.rowGroup.min.js" "$PSScriptRoot\Resources\JS\dataTables.rowReorder.min.js" "$PSScriptRoot\Resources\JS\dataTables.scroller.min.js" "$PSScriptRoot\Resources\JS\dataTables.select.min.js" "$PSScriptRoot\Resources\JS\moment.min.js" "$PSScriptRoot\Resources\JS\datetime-moment.js" ) } } DataTablesPDF = @{ Comment = 'DataTables PDF Features' Header = @{ JsLink = @( 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.36/pdfmake.min.js' 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.36/vfs_fonts.js' ) Js = @( "$PSScriptRoot\Resources\JS\pdfmake.min.js" "$PSScriptRoot\Resources\JS\vfs_fonts.js" ) } } DataTablesExcel = @{ Comment = 'DataTables Excel Features' Header = @{ JsLink = @( 'https://cdnjs.cloudflare.com/ajax/libs/jszip/2.5.0/jszip.min.js' ) JS = @( "$PSScriptRoot\Resources\JS\jszip.min.js" ) } } DataTablesSimplify = @{ Comment = 'DataTables (not really) - Simplified' HeaderAlways = @{ Css = "$PSScriptRoot\Resources\CSS\datatables.simplify.css" } } D3Mitch = @{ Comment = 'D3Mitch Feature' Header = @{ JsLink = @( #'https://cdn.jsdelivr.net/npm/d3-mitch-tree@1.0.5/lib/d3-mitch-tree.min.js' 'https://cdn.jsdelivr.net/gh/deltoss/d3-mitch-tree@1.0.2/dist/js/d3-mitch-tree.min.js' ) CssLink = @( 'https://cdn.jsdelivr.net/gh/deltoss/d3-mitch-tree@1.0.2/dist/css/d3-mitch-tree.min.css' 'https://cdn.jsdelivr.net/gh/deltoss/d3-mitch-tree@1.0.2/dist/css/d3-mitch-tree-theme-default.min.css' ) } HeaderAlways = @{ Css = "$PSScriptRoot\Resources\CSS\hierarchicalTree.css" } } Fonts = @{ Comment = 'Default fonts' HeaderAlways = @{ CssLink = 'https://fonts.googleapis.com/css?family=Roboto|Hammersmith+One|Questrial|Oswald' } } FontsAwesome = @{ Comment = 'Default fonts icons' HeaderAlways = @{ CssLink = 'https://use.fontawesome.com/releases/v5.11.2/css/all.css' } Other = @{ Link = 'https://use.fontawesome.com/releases/v5.11.2/svgs/' } } FullCalendar = @{ Comment = 'FullCalendar Basic' HeaderAlways = @{ Css = "$PSScriptRoot\Resources\CSS\calendar.css" } } FullCalendarCore = @{ Comment = 'FullCalendar Core' Header = @{ CssLink = 'https://cdn.jsdelivr.net/npm/@fullcalendar/core@4.3.1/main.min.css' Css = "$PSScriptRoot\Resources\CSS\fullCalendarCore.css" JSLink = 'https://cdn.jsdelivr.net/npm/@fullcalendar/core@4.3.1/main.min.js' JS = "$PSScriptRoot\Resources\JS\fullCalendarCore.js" } } FullCalendarDayGrid = @{ Comment = 'FullCalendar DayGrid' Header = @{ CssLink = 'https://cdn.jsdelivr.net/npm/@fullcalendar/daygrid@4.3.0/main.min.css' Css = "$PSScriptRoot\Resources\CSS\fullCalendarDayGrid.css" JSLink = 'https://cdn.jsdelivr.net/npm/@fullcalendar/daygrid@4.3.0/main.min.js' JS = "$PSScriptRoot\Resources\JS\fullCalendarDayGrid.js" } } FullCalendarInteraction = @{ Comment = 'FullCalendar Interaction' Header = @{ JSLink = 'https://cdn.jsdelivr.net/npm/@fullcalendar/interaction@4.3.0/main.min.js' JS = "$PSScriptRoot\Resources\JS\FullCalendarInteraction.js" } } FullCalendarList = @{ Comment = 'FullCalendar List' Header = @{ CssLink = 'https://cdn.jsdelivr.net/npm/@fullcalendar/list@4.3.0/main.min.css' Css = "$PSScriptRoot\Resources\CSS\fullCalendarList.css" JSLink = 'https://cdn.jsdelivr.net/npm/@fullcalendar/list@4.3.0/main.min.js' JS = "$PSScriptRoot\Resources\JS\fullCalendarList.js" } } FullCalendarRRule = @{ Comment = 'FullCalendar RRule' Header = @{ JSLink = 'https://cdn.jsdelivr.net/npm/@fullcalendar/rrule' JS = "$PSScriptRoot\Resources\JS\fullCalendarRRule.js" #https://cdn.jsdelivr.net/npm/@fullcalendar/rrule@4.3.0/main.min.js } } FullCalendarTimeGrid = @{ Comment = 'FullCalendar TimeGrid' Header = @{ CssLink = 'https://cdn.jsdelivr.net/npm/@fullcalendar/timegrid@4.3.0/main.min.css' Css = "$PSScriptRoot\Resources\CSS\fullCalendarTimeGrid.css" JSLink = 'https://cdn.jsdelivr.net/npm/@fullcalendar/timegrid@4.3.0/main.min.js' JS = "$PSScriptRoot\Resources\JS\fullCalendarTimeGrid.js" } } FullCalendarTimeLine = @{ Comment = 'FullCalendar TimeLine' Header = @{ CssLink = 'https://cdn.jsdelivr.net/npm/@fullcalendar/timeline@4.3.0/main.min.css' Css = "$PSScriptRoot\Resources\CSS\fullCalendarTimeLine.css" JSLink = 'https://cdn.jsdelivr.net/npm/@fullcalendar/timeline@4.3.0/main.min.js' JS = "$PSScriptRoot\Resources\JS\fullCalendarTimeLine.js" } } HideSection = @{ Comment = 'Hide Section Code' HeaderAlways = @{ JS = "$PSScriptRoot\Resources\JS\HideSection.js" } } Popper = @{ Comment = 'Popper and Tooltip for FullCalendar' HeaderAlways = @{ Css = "$PSScriptRoot\Resources\CSS\popper.css" } Header = @{ JSLink = @( 'https://unpkg.com/popper.js/dist/umd/popper.min.js' 'https://unpkg.com/tooltip.js/dist/umd/tooltip.min.js' ) JS = @( "$PSScriptRoot\Resources\JS\popper.js" "$PSScriptRoot\Resources\JS\tooltip.js" ) } } Tabs = @{ Comment = 'Elastic Tabs' HeaderAlways = @{ Css = "$PSScriptRoot\Resources\CSS\tabs-elastic.css" } FooterAlways = @{ JS = "$PSScriptRoot\Resources\JS\tabs-elastic.js" } CustomActionsReplace = @{ 'ColorSelector' = ConvertFrom-Color -Color ([RGBColors]::DodgerBlue) 'ColorTarget' = ConvertFrom-Color -Color ([RGBColors]::MediumSlateBlue) } } Tabbis = @{ Comment = 'Elastic Tabbis' HeaderAlways = @{ Css = "$PSScriptRoot\Resources\CSS\tabbis.css" } FooterAlways = @{ JS = @( "$PSScriptRoot\Resources\JS\tabbis.js" "$PSScriptRoot\Resources\JS\tabbisAdditional.js" ) } CustomActionsReplace = @{ 'ColorSelector' = ConvertFrom-Color -Color ([RGBColors]::DodgerBlue) 'ColorTarget' = ConvertFrom-Color -Color ([RGBColors]::MediumSlateBlue) } } TabbisGradient = @{ Comment = 'Elastic Tabs Gradient' FooterAlways = @{ Css = "$PSScriptRoot\Resources\CSS\tabs-elastic.gradient.css" } CustomActionsReplace = @{ 'ColorSelector' = ConvertFrom-Color -Color ([RGBColors]::DodgerBlue) 'ColorTarget' = ConvertFrom-Color -Color ([RGBColors]::MediumSlateBlue) } } TabbisTransition = @{ Comment = 'Elastic Tabs Transition' FooterAlways = @{ Css = "$PSScriptRoot\Resources\CSS\tabs-elastic.transition.css" } } TimeLine = @{ Comment = 'Timeline Simple' HeaderAlways = @{ Css = "$PSScriptRoot\Resources\CSS\timeline-simple.css" } } Toasts = @{ Comment = 'Toasts Looking Messages' HeaderAlways = @{ Css = "$PSScriptRoot\Resources\CSS\Toasts.css" } } StatusButtonical = @{ Comment = 'Status Buttonical' HeaderAlways = @{ Css = "$PSScriptRoot\Resources\CSS\status.css" } } TuiGrid = @{ Comment = 'Tui Grid' Header = @{ Css = "$PSScriptRoot\Resources\CSS\tuigrid.css" CssLink = 'https://cdn.jsdelivr.net/npm/tui-grid@3.5.0/dist/tui-grid.css' } } VisNetwork = @{ Comment = 'VIS Network Dynamic, browser based visualization libraries' HeaderAlways = @{ Css = "$PSScriptRoot\Resources\CSS\vis-network.diagram.css" } Header = @{ JsLink = 'https://unpkg.com/vis-network@6.3.0/dist/vis-network.min.js' Js = "$PSScriptRoot\Resources\JS\vis-network.min.js" Css = "$PSScriptRoot\Resources\CSS\vis-network.min.css" CssLink = 'https://unpkg.com/vis-network@6.3.0/dist/vis-network.min.css' } FooterAlways = @{ JS = "$PSScriptRoot\Resources\JS\vis-networkFunctions.js" } } } } function Get-ResourcesContentFromWeb { param( [uri[]] $ResourceLinks, [ValidateSet('CSS', 'JS')][string] $Type ) $Output = foreach ($Link in $ResourceLinks) { # [uri] $Link = $File $Splitted = $Link.OriginalString -split '/' $FileName = $Splitted[-1] $FilePath = [IO.Path]::Combine('C:\Users\przemyslaw.klys\OneDrive - Evotec\Support\GitHub\PSWriteHTML\Resources', $Type, $FileName) $FilePathScriptRoot = -Join ('"', '$PSScriptRoot\Resources\', "$Type", '\', $FileName, '"') $FilePathScriptRoot #[IO.Path]::Combine('C:\Users\przemyslaw.klys\OneDrive - Evotec\Support\GitHub\PSWriteHTML\Resources\CSS', $FileName) Invoke-WebRequest -Uri $Link -OutFile $FilePath } $Output } #Get-ResourcesContentFromWeb -ResourceLinks $($Script:Configuration).Features.DataTables.Header.JsLink -Type 'JS' #Get-ResourcesContentFromWeb -ResourceLinks $($Script:Configuration).Features.DataTables.Header.CssLink -Type 'CSS' #Get-ResourcesContentFromWeb -ResourceLinks $($Script:Configuration).Features.VisNetwork.Header.JsLink -Type 'JS' #Get-ResourcesContentFromWeb -ResourceLinks $($Script:Configuration).Features.VisNetwork.Header.CssLink -Type 'CSS' # Another way to access # https://use.fontawesome.com/releases/v5.11.2/svgs/brands/accessible-icon.svg $Global:HTMLIcons = @{ FontAwesomeBrands = [ordered] @{ '500px' = 'f26e' 'accessible-icon' = 'f368' 'accusoft' = 'f369' 'acquisitions-incorporated' = 'f6af' 'adn' = 'f170' 'adobe' = 'f778' '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' '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' 'dashcube' = 'f210' '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' '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' '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-play' = 'f3ab' 'google-plus' = 'f2b3' 'google-plus-g' = 'f0d5' 'google-plus-square' = 'f0d4' 'google-wallet' = 'f1ee' 'gratipay' = 'f184' 'grav' = 'f2d6' 'gripfire' = 'f3ac' 'grunt' = 'f3ad' 'gulp' = 'f3ae' 'hacker-news' = 'f1d4' 'hacker-news-square' = 'f3af' 'hackerrank' = 'f5f7' 'hips' = 'f452' 'hire-a-helper' = 'f3b0' 'hooli' = 'f427' 'hornbill' = 'f592' 'hotjar' = 'f3b1' 'houzz' = 'f27c' 'html5' = 'f13b' 'hubspot' = 'f3b2' 'imdb' = 'f2d8' 'instagram' = 'f16d' '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' 'microsoft' = 'f3ca' 'mix' = 'f3cb' 'mixcloud' = 'f289' 'mizuni' = 'f3cc' 'modx' = 'f285' 'monero' = 'f3d0' 'napster' = 'f3d2' 'neos' = 'f612' 'nimblr' = 'f5a8' 'node' = 'f419' 'node-js' = 'f3d3' 'npm' = 'f3d4' 'ns8' = 'f3d5' 'nutritionix' = 'f3d6' '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' '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' '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' 'safari' = 'f267' 'salesforce' = 'f83b' 'sass' = 'f41e' 'schlix' = 'f3ea' 'scribd' = 'f28a' 'searchengin' = 'f3eb' 'sellcast' = 'f2da' 'sellsy' = 'f213' 'servicestack' = 'f3ec' 'shirtsinbulk' = 'f214' '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' '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' 'uniregistry' = 'f404' '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' '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' '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' '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' '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' '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-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' '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-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' '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-usd' = 'f4c0' '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-spock' = 'f259' 'hands' = 'f4c2' 'hands-helping' = 'f4c4' 'handshake' = 'f2b5' 'hanukiah' = 'f6e6' 'hard-hat' = 'f807' 'hashtag' = 'f292' 'hat-cowboy' = 'f8c0' 'hat-cowboy-side' = 'f8c1' 'hat-wizard' = 'f6e8' 'haykal' = 'f666' 'hdd' = 'f0a0' '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' 'hot-tub' = 'f593' 'hotdog' = 'f80f' 'hotel' = 'f594' 'hourglass' = 'f254' 'hourglass-end' = 'f253' 'hourglass-half' = 'f252' 'hourglass-start' = 'f251' 'house-damage' = 'f6f1' '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-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' '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-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' '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' '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' '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' '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' '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' 'store' = 'f54e' 'store-alt' = 'f54f' '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' 'toolbox' = 'f552' 'tools' = 'f7d9' 'tooth' = 'f5c9' 'torah' = 'f6a0' 'torii-gate' = 'f6a1' 'tractor' = 'f722' 'trademark' = 'f25c' 'traffic-light' = 'f637' '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' 'utensil-spoon' = 'f2e5' 'utensils' = 'f2e7' 'vector-square' = 'f5cb' 'venus' = 'f221' 'venus-double' = 'f226' 'venus-mars' = 'f228' 'vial' = 'f492' 'vials' = 'f493' 'video' = 'f03d' 'video-slash' = 'f4e2' 'vihara' = 'f6a7' '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' } } Function Save-HTML { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER FilePath Parameter description .PARAMETER HTML Parameter description .PARAMETER ShowReport Parameter description .EXAMPLE An example .NOTES General notes #> [CmdletBinding()] Param ( [Parameter(Mandatory = $false)][string]$FilePath, [Parameter(Mandatory = $true)][Array] $HTML, [alias('Show', 'Open')][Parameter(Mandatory = $false)][switch]$ShowHTML, [ValidateSet('Unknown', 'String', 'Unicode', 'Byte', 'BigEndianUnicode', 'UTF8', 'UTF7', 'UTF32', 'Ascii', 'Default', 'Oem', 'BigEndianUTF32')] $Encoding = 'UTF8', [bool] $Supress = $true ) if ([string]::IsNullOrEmpty($FilePath)) { $FilePath = Get-FileName -Temporary -Extension 'html' Write-Verbose "Save-HTML - FilePath parameter is empty, using Temporary $FilePath" } else { if (Test-Path -LiteralPath $FilePath) { Write-Verbose "Save-HTML - Path $FilePath already exists. Report will be overwritten." } } Write-Verbose "Save-HTML - Saving HTML to file $FilePath" try { $HTML | Set-Content -LiteralPath $FilePath -Force -Encoding $Encoding if (-not $Supress) { $FilePath } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " Write-Warning "Save-HTML - Failed with error: $ErrorMessage" } if ($ShowHTML) { try { Invoke-Item -LiteralPath $FilePath -ErrorAction Stop } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " Write-Verbose "Save-HTML - couldn't open file $FilePath in a browser. Error: $ErrorMessage" } } } function Set-Tag { [CmdletBinding()] param( [System.Collections.IDictionary] $HtmlObject, [switch] $NewLine # This is needed if code requires new lines such as JavaScript ) $HTML = [System.Text.StringBuilder]::new() [void] $HTML.Append("<$($HtmlObject.Tag)") foreach ($Property in $HtmlObject.Attributes.Keys) { $PropertyValue = $HtmlObject.Attributes[$Property] # This checks if property has any subproperties such as style having multiple options if ($PropertyValue -is [System.Collections.IDictionary]) { $OutputSubProperties = foreach ($SubAttributes in $PropertyValue.Keys) { $SubPropertyValue = $PropertyValue[$SubAttributes] # skip adding properties that are empty if ($null -ne $SubPropertyValue -and $SubPropertyValue -ne '') { "$($SubAttributes):$($SubPropertyValue)" } } $MyValue = $OutputSubProperties -join ';' if ($MyValue.Trim() -ne '') { [void] $HTML.Append(" $Property=`"$MyValue`"") } } else { # skip adding properties that are empty if ($null -ne $PropertyValue -and $PropertyValue -ne '') { [void] $HTML.Append(" $Property=`"$PropertyValue`"") } } } if (($null -ne $HtmlObject.Value) -and ($HtmlObject.Value -ne '')) { [void] $HTML.Append(">") if ($HtmlObject.Value.Count -eq 1) { if ($HtmlObject.Value -is [System.Collections.IDictionary]) { [string] $NewObject = Set-Tag -HtmlObject ($HtmlObject.Value) [void] $HTML.Append($NewObject) } else { [void] $HTML.Append([string] $HtmlObject.Value) } } else { foreach ($Entry in $HtmlObject.Value) { if ($Entry -is [System.Collections.IDictionary]) { [string] $NewObject = Set-Tag -HtmlObject ($Entry) [void] $HTML.Append($NewObject) } else { # This is needed if code requires new lines such as JavaScript if ($NewLine) { [void] $HTML.AppendLine([string] $Entry) } else { [void] $HTML.Append([string] $Entry) } } } } [void] $HTML.Append("</$($HtmlObject.Tag)>") } else { if ($HtmlObject.SelfClosing) { [void] $HTML.Append("/>") } else { [void] $HTML.Append("></$($HtmlObject.Tag)>") } } $HTML.ToString() } Export-ModuleMember ` -Function @('New-CalendarEvent', 'New-ChartAxisX', 'New-ChartAxisY', 'New-ChartBar', 'New-ChartBarOptions', 'New-ChartDonut', 'New-ChartGrid', 'New-ChartLegend', 'New-ChartLine', 'New-ChartPie', 'New-ChartRadial', 'New-ChartTheme', 'New-ChartToolbar', 'New-DiagramEvent', 'New-DiagramLink', 'New-DiagramNode', 'New-DiagramOptionsInteraction', 'New-DiagramOptionsLayout', 'New-DiagramOptionsLinks', 'New-DiagramOptionsManipulation', 'New-DiagramOptionsNodes', 'New-DiagramOptionsPhysics', 'New-HierarchicalTreeNode', 'New-HTML', 'New-HTMLCalendar', 'New-HTMLChart', 'New-HTMLCodeBlock', 'New-HTMLContainer', 'New-HTMLDiagram', 'New-HTMLHeading', 'New-HTMLHierarchicalTree', 'New-HTMLHorizontalLine', 'New-HTMLImage', 'New-HTMLList', 'New-HTMLListItem', 'New-HTMLLogo', 'New-HTMLPanel', 'New-HTMLSection', 'New-HTMLSpanStyle', 'New-HTMLStatus', 'New-HTMLStatusItem', 'New-HTMLTab', 'New-HTMLTable', 'New-HTMLTabOptions', 'New-HTMLTag', 'New-HTMLText', 'New-HTMLTimeline', 'New-HTMLTimelineItem', 'New-HTMLToast', 'New-TableButtonCopy', 'New-TableButtonCSV', 'New-TableButtonExcel', 'New-TableButtonPageLength', 'New-TableButtonPDF', 'New-TableButtonPrint', 'New-TableCondition', 'New-TableContent', 'New-TableHeader', 'New-TableReplace', 'New-TableRowGrouping', 'Out-HtmlView', 'Save-HTML') ` -Alias @('Calendar', 'CalendarEvent', 'Chart', 'ChartAxisX', 'ChartAxisY', 'ChartBar', 'ChartBarOptions', 'ChartCategory', 'ChartDonut', 'ChartGrid', 'ChartLegend', 'ChartLine', 'ChartPie', 'ChartRadial', 'ChartTheme', 'ChartToolbar', 'Container', 'Dashboard', 'Diagram', 'DiagramEdge', 'DiagramEdges', 'DiagramLink', 'DiagramNode', 'DiagramOptionsEdges', 'DiagramOptionsInteraction', 'DiagramOptionsLayout', 'DiagramOptionsLinks', 'DiagramOptionsManipulation', 'DiagramOptionsNodes', 'DiagramOptionsPhysics', 'EmailTable', 'EmailTableButtonCopy', 'EmailTableButtonCSV', 'EmailTableButtonExcel', 'EmailTableButtonPageLength', 'EmailTableButtonPDF', 'EmailTableButtonPrint', 'EmailTableCondition', 'EmailTableContent', 'EmailTableHeader', 'EmailTableReplace', 'EmailTableRowGrouping', 'HierarchicalTreeNode', 'HTMLText', 'Image', 'New-ChartCategory', 'New-DiagramEdge', 'New-DiagramOptionsEdges', 'New-HierarchicalTreeNode', 'New-HTMLColumn', 'New-HTMLContent', 'New-HTMLTableButtonCopy', 'New-HTMLTableButtonCSV', 'New-HTMLTableButtonExcel', 'New-HTMLTableButtonPageLength', 'New-HTMLTableButtonPDF', 'New-HTMLTableButtonPrint', 'New-HTMLTableCondition', 'New-HTMLTableContent', 'New-HTMLTableHeader', 'New-HTMLTableReplace', 'New-HTMLTableRowGrouping', 'ohv', 'Out-GridHtml', 'Panel', 'Section', 'Tab', 'Table', 'TableButtonCopy', 'TableButtonCSV', 'TableButtonExcel', 'TableButtonPageLength', 'TableButtonPDF', 'TableButtonPrint', 'TableConditionalFormatting', 'TableContent', 'TableHeader', 'TableReplace', 'TableRowGrouping', 'TabOptions', 'Text') |