PoshCode Logo PowerShell Code Repository

Get-HtmlHelp 3.0 by Joel Bennett 3 weeks ago
View followups from Joel Bennett | embed code: <script type="text/javascript" src="http://PoshCode.org/embed/3187"></script>download | new post

A script to convert MAML help to HTML.

Note: This is still very rough but good enough to share, and to be useful with ShowUI (see the “Cool Example” inline).

  1. ## Get-HtmlHelp - by Joel Bennett
  2. ## version 3.0
  3. #####################################################################
  4. ## Cool Example, using ShowUI:
  5. ##    Import-Module HtmlHelp
  6. ##    Import-Module ShowUI
  7. ##    function Show-Help { [CmdletBinding()]param([String]$Name)  
  8. ##       Window { WebBrowser -Name wb } -On_Loaded {
  9. ##          $wb.NavigateToString((Get-HtmlHelp $Name))
  10. ##          $this.Title = "Get-Help $Name"
  11. ##       } -Show
  12. ##    }
  13. ##    Show-Help Get-Help
  14. ##
  15. #####################################################################
  16. #Import System.Web in order to use HtmlEncode functionality
  17. Add-Type -Assembly System.Web
  18.  
  19. function ConvertTo-Dictionary([hashtable]$ht) {
  20.    $kvd = new-object "System.Collections.Generic.Dictionary``2[[System.String],[System.String]]"
  21.    foreach($kv in $ht.GetEnumerator()) { $kvd.Add($kv.Key,$kv.Value) }
  22.    return $kvd
  23. }
  24.  
  25.  
  26. function htmlEncode {
  27.    param([Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)][String]$Text)
  28.    process{ [System.Web.HttpUtility]::HtmlEncode($Text) }
  29. }
  30.  
  31. function trim {
  32.    param([Parameter(ValueFromPipeline=$true,Mandatory=$true)][String]$string)
  33.    process{ $string.Trim() }
  34. }
  35.  
  36. function trimEncode{
  37.    param([Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)][String]$Text)
  38.    process{ [System.Web.HttpUtility]::HtmlEncode($Text).Trim() }
  39. }
  40.  
  41. function HHSplit {
  42.    param(
  43.       $Separator="\s*\r?\n",
  44.       [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
  45.       [String]$inputObject
  46.    )
  47.    process{
  48.       foreach($item in [regex]::Split($inputObject,$Separator)) {
  49.          $item.Trim() | Where {$_.Length}
  50.       }
  51.    }
  52. }
  53.  
  54. function HHjoin {
  55.    param(
  56.       [Parameter(Position=0)]
  57.       $Separator=$ofs,
  58.      
  59.       [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
  60.       [String[]]
  61.       $inputObject
  62.    )
  63.    begin   { [string[]]$array = $inputObject }
  64.    process { $array += $inputObject }
  65.    end     {
  66.       Write-Verbose $Array.Length
  67.       if($array.Length -gt 1) {
  68.          [string]::Join($Separator,$array)
  69.       } else {
  70.          $array
  71.       }
  72.    }
  73. }
  74. function Out-HtmlTag {
  75.    param([Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)][String]$Text, $Tag="p")
  76.    process{
  77.       $html = $Text | out-string -width ([int]::MaxValue) | HHSplit | trimEncode | HHjoin "</$tag>`n<$tag>"
  78.       $html = $html -replace '(\S+)(http://.*?)([\s\p{P}](?:\s|$))','<a href="$2">$1</a>$3'
  79.       "<$tag>$html</$tag>"
  80.    }
  81. }
  82. function Out-HtmlList {
  83.    param([Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)][String]$Text)
  84.    process{
  85.       "<li>$($Text | out-string -width ([int]::MaxValue) | HHSplit | trimEncode | HHjoin "</li>`n<li>")</li>"
  86.    }
  87. }
  88. function Out-HtmlDefList {
  89.    param(
  90.       [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)]$Node,
  91.       [String[]]$Term,
  92.       [String[]]$Definition
  93.    )
  94.    # begin { "<dl>"}
  95.    process{
  96.       $tx = $Node
  97.       foreach($t in $Term) { $tx = $tx.$t; Write-Verbose "${t}: $tx" }
  98.       $dx = $Node
  99.       foreach($d in $Definition) { $dx = $dx.$d; Write-Verbose "${t}: $dx"  }
  100.       "<dt>{0}</dt><dd>{1}</dd>" -f ($tx | trimEncode),($dx | trimEncode)
  101.    }
  102.    # end { "</dl>"}
  103. }
  104. function Out-HtmlBr {
  105.    param([Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)][String]$Text)
  106.    process{
  107.       $Text | out-string -width ([int]::MaxValue) | HHSplit | trimEncode | HHjoin "<br />"
  108.    }
  109. }
  110.  
  111. ## Utility Functions
  112. function Get-UtcTime {
  113.    Param($Format="")
  114.    [DateTime]::Now.ToUniversalTime().ToString($Format)
  115. }
  116. function Encode-Twice {
  117.    param([Parameter(ValueFromPipeline=$true,Mandatory=$true)][String]$string)
  118.    process {
  119.       [System.Web.HttpUtility]::UrlEncode( [System.Web.HttpUtility]::UrlEncode( $string ) )
  120.    }
  121. }
  122.  
  123.  
  124. ## Get-HtmlHelp - A Helper function for generating help:
  125. ## Usage:  Get-HtmlHelp Get-*
  126. function Get-HtmlHelp {
  127.    param([string[]]$commands, [string]$baseUrl = "http://http://technet.microsoft.com/en-us/library/")
  128.    $commands | Get-Command -EA "SilentlyContinue" | Get-Help -Full | Convert-HelpToHtml $baseUrl
  129. }
  130.  
  131. function textile {
  132. param([string]$text)
  133.  
  134. $text -replace '(?<=\r\n\r\n|^)\*\s(.*)(?=\r\n)',"<ul>`r`n<li>`$1</li>"                     <# start of a list      #>`
  135.       -replace '(?<=\r\n)\*\s+((?:.|\r\n(?![\*\r]))+)(?=\r\n\r\n|\r\n\*|$)',"<li>`$1</li>"  <# middle of a list     #>`
  136.       -replace '</li>(?=\r\n\r\n|$)',"</li>`r`n</ul>"                                       <# end of the list      #>`
  137.       -replace '(?<=\r\n\r\n|^)([^\n]*)(?=\r\n\r\n|$)',"<p>`r`n`$1`r`n</p>"                 <# regular paragraphs   #>`
  138.       -replace '(?<=\r\n\r\n)([^\r\n]*\s+[^\r\n]*)\r\n(-+)(?=\r\n\r\n)',"<h3>`$1</h3>"      <# headers              #>`
  139.       -replace '(?<=[^\r\n>])(\r\n)(?=[^\r\n]+)',"<br />`$1"                                <# remaining linebreaks #>`
  140.       -replace "  "," &nbsp;"  # Because the content is originally for get-help, preserve whitespace
  141. }
  142.  
  143. function Convert-ParameterHelp {
  144. param([Parameter(ValueFromPipeline=$true,Mandatory=$true)]$ParameterItem)
  145. process {
  146.    $name = $(
  147.       if($ParameterItem.position -ne "named") {
  148.          "[-<strong>{0}</strong>]" -f $ParameterItem.name
  149.       } else {
  150.          "-<strong>{0}</strong>" -f $ParameterItem.name
  151.       }
  152.    )
  153.  
  154.    if($ParameterItem.required -eq "false") {
  155.       "<nobr>[{0} {1}]</nobr>" -f $name, $ParameterItem.parameterValue
  156.    } else {
  157.       "<nobr>{0} {1}</nobr>" -f $name, $ParameterItem.parameterValue
  158.    }
  159. }
  160. }
  161.  
  162. function Convert-SyntaxItem {
  163. param([Parameter(ValueFromPipeline=$true,Mandatory=$true)]$SyntaxItem)
  164. process {
  165.    "<li>{0} {1}</li>" -f $SyntaxItem.name, $($SyntaxItem.parameter | Convert-ParameterHelp | join " ")
  166. }
  167. }
  168.  
  169. function Convert-HelpToHtml {
  170. param(
  171. [string]$baseUrl,
  172.  
  173. [Parameter(ValueFromPipeline=$true)]
  174. $Help
  175. )
  176. PROCESS {
  177.       #  throw "Can only process -Full view help output"
  178.  
  179.       # Name isn't needed, since this is going as the body, but ...
  180.       # $data = "<html><head><title>$(trimEncode($help.Name))</title></head><body>";
  181.       # $data += "<h1>$(trimEncode($help.Name))</h1>"
  182. $data = @"
  183. <html>
  184. <head>
  185. <title>$(trimEncode($help.Name))</title>
  186. <style type="text/css">
  187.   h1, h2, h3, h4, h5, h6 { color: #369 }
  188.   ul.syntax {
  189.      list-style: none outside;
  190.      font-size: .9em;
  191.      font-family: Consolas, "DejaVu Sans Mono", "Lucida Console", monospace;
  192.   }
  193.   ul.syntax li {
  194.      margin: 3px 0px;
  195.   }
  196.   table.parameters th {
  197.      text-align: left;
  198.   }
  199. </style>
  200. </head>
  201. <body>
  202. <h1>$(trimEncode($help.Name))</h1>
  203. "@
  204.       # Synopsis
  205.       $data += "<h2>Synopsis</h2>$($help.Synopsis | Out-HtmlTag -Tag p)"
  206.      
  207.       # Syntax
  208.       $data += "<h2 class='syntax'>Syntax</h2><ul class='syntax'>$($help.Syntax.syntaxItem | Convert-SyntaxItem)</ul>"
  209.    
  210.       # Related Commands
  211.       $data += "<h2>Related Commands</h2>"
  212.       foreach ($relatedLink in $help.relatedLinks.navigationLink) {
  213.          if($relatedLink.linkText -and !$relatedLink.linkText.StartsWith("about")) {
  214.             $uri = ""
  215.             if( $relatedLink.uri -ne "" ) {
  216.                $uri = $relatedLink.uri
  217.             } else {
  218.                $uri = "$baseUrl/$((get-command $relatedLink.linkText -EA "SilentlyContinue").PSSnapin.Name)/$($relatedLink.linkText)"
  219.             }
  220.             $data += "<a href='$(trimEncode($uri))'>$(trimEncode($relatedLink.linkText))</a><br>"
  221.          }
  222.       }
  223.    
  224.       # Detailed Description
  225.       $data += "<h2>Detailed Description</h2>$($help.Description | Out-HtmlTag -Tag p)"
  226.    
  227.       # Parameters
  228.       $data += "<h2>Parameters</h2>"
  229.       foreach($param in $help.parameters.parameter) {
  230.          $data += "<h4>-$(trimEncode($param.Name)) [&lt;$(trimEncode($param.type.name))&gt;]</h4>"
  231.          $data += $param.Description | Out-HtmlTag -Tag p
  232.          $data += "<table class='parameters'>"
  233.          $data += "<tr><th>Required? &nbsp;</th><td> $(trimEncode($param.Required))</td></tr>"
  234.          $data += "<tr><th>Position? &nbsp;</th><td> $(trimEncode($param.Position))</td></tr>"
  235.          if($param.DefaultValue) {
  236.             $data += "<tr><th>Default value? &nbsp;</th><td> $(trimEncode($param.defaultValue))</td></tr>"
  237.          }
  238.          $data += "<tr><th>Accept pipeline input? &nbsp;</th><td> $(trimEncode($param.pipelineInput))</td></tr>"
  239.          $data += "<tr><th>Accept wildcard characters? &nbsp;</th><td> $(trimEncode($param.globbing))</td></tr></table>"
  240.       }
  241.    
  242.       if($help.inputTypes) {
  243.          # Input Type
  244.          $data += "<h3>Input Type</h3><dl class='input'>$(
  245.            $help.inputTypes.inputType | Out-HtmlDefList -Term type, name -Definition description
  246.         )</dl>"
  247.       }
  248.       if($help.returnValues) {
  249.          # Return Type
  250.          $data += "<h3>Output Type</h3><dl class='output'>$(
  251.            $help.returnValues.returnValue | Out-HtmlDefList -Term type, name -Definition description
  252.         )</dl>"
  253.       }
  254.       # Notes
  255.       $data += "<h2>Notes</h2>$($help.alertSet.alert | Out-HtmlTag -Tag p)"
  256.    
  257.       # Examples
  258.       if($help.Examples.example -and $help.Examples.example.Length) {
  259.          $data += "<h2>Examples</h2>"
  260.          
  261.          foreach($example in $help.Examples.example){
  262.             if($example.title) {
  263.                $data += "<h4>$(trimEncode($example.title.trim(' -')))</h4>"
  264.             }
  265.             $data += "<code><strong>C:\PS&gt;</strong>&nbsp;$(($example.code | Out-HtmlBr) -replace "<br />C:\\PS&gt;","<br /><strong>C:\PS&gt;</strong>&nbsp;")</code>"
  266.             $data += "<p>$($example.remarks | out-string -width ([int]::MaxValue) | Out-HtmlTag -Tag p)</p>"
  267.          }
  268.       }
  269.       $data += "</body></html>"
  270.  
  271.       write-output $data
  272. }}
  273.  
  274. # Export-ModuleMember Get-HtmlHelp

Submit a correction or amendment below (
click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:


Remember me