Skip to content

berttejeda/bert.docs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

65 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Table of Contents generated with DocToc

Interactive HTA Documents - Powershell et, al

Why?

I needed a means of producing documents on the Windows platform that could better engage the reader and perform some advanced actions such as interacting with the system.

I used the boilerplate from this repo BlackrockDigital/startbootstrap-simple-sidebar to create the basis for the interactive document.

The layout is clean, and I managed to tweak it to my liking: check it out:

screenshot

Implementation

Here's how I accomplished my goal:

  • markdown for easy content creation, see default.markdown
  • pandoc for content rendering, and for creating the single, self-contained output
  • markdown pre-processing with pp
  • javascript ActiveX Objects for interaction with the Windows OS
    • powershell code can be embedded directly in your markdown and/or templates, enclosed in html comment tags
      <!-- -->
    • The same goes for cmd commands (although not as elegantly)
  • cmder for making the Windows commandline so much sweeter

Requirements

Features

The file is completely self-contained, with assets embedded as base64 objects.

I've incorporated HTAConsole into the source, which makes it substantially easier to troubleshoot bugs.

Simply press F12 to display the console. I use this feature throughout the codebase for printing helpful information to the console.

e.g. console.log('some message')

I plan on extending overall functionality to allow for wider integration of programming languages.

How to use

If you're on Windows, you should be able to install the project requirements using the bootstrap.bat script.

This will install chocolatey, pp, and pandoc on your system. It requires the 7z command, so it'll install that as well.

Example1: Build an HTA application from a markdown file

You can invoke the build process in any of 3 ways:

All of these act as a pandoc/pp wrapper to produce a single .hta/.html file as per specification.

  • Invoke the build script from commandline:
    • From cmder/git-bash:
      • Using the tasks command mentioned above
        tasks run -s _template/default.markdown -o default.hta -t _template/templates/default.html
      • Using the supplied build.sh bash script:
        ./build.sh -s _template/default.markdown -o default.hta -t _template/templates/default.html
      • Using the supplied powershell script:
        ./build.bat -s _template\\default.markdown -o default.hta -t _template\\templates\\default.html
    • Invoking any of the commands above with the --dry flag will display something similar to:
      pp _template/default.markdown | pandoc -o 'default.hta' -c '_common/templates/default.css' -H '_common/templates/header.html' --template _template/templates/default.html --self-contained --standalone
    • I'm also utilizing watchdog to monitor file changes and trigger a rebuild based on specified parameters
      Simply invoke any of the commands above with the --watch --patterns flags, e.g.
      --watch --patterns '*.markdown,*.md,*.js,*.css,*.html'
  • Invoke the build script from the hta:
    • Just click the Rebuild button located in the top navigation bar.
      This will invoke the powershell build script so long as it is located in the same directory as the HTA
      Press F5 to refresh the HTA application

Example2: Build an HTML file from the markdown file

  • Invoke the same build script from Example1, with just one difference: -o default.html

Example3: Add powershell to your document

<a href="#" id="testing" class="powershell">CLICKME: PowerShell
<!--         
Write-Host 'You executed PowerShell!'
&pause
-->
</a> 

As illustrated, you must enclose your commands/includes in comment tags <!-- -->

Do the same, this time with a non-interactive shell session:

<a href="#" id="testing" class="powershell" data-interactive="0">CLICKME: POWERSHELL
<!--         
'You executed powershell!'
-->
</a> 
  • Again, but using the !include macro:
<a href="#" id="include" class="powershell" data-interactive="0">CLICKME: PowerShell
<!--         
!include(src/include.ps1)
-->
</a>

Once the HTA file is rebuilt, you can refresh the HTA application by pressing F5. Your changes should have been rendered.

Again, you can access the javascript console by pressing F12.

You can review the STDOUT of non-interactive powershell command.

Example4: Invoke a cmd shell from your document

<a href="#" class="shell" data-shell="cmd">Start cmd!</a>

Once the HTA file is rebuilt, you can refresh the HTA application by pressing F5. Your changes should have been rendered.

Appendix

Under the hood

Powershell Invocation

The powershell invocation is done through a WScript.Shell ActiveX Object and clever Windows clipboard manipulation, so there is no need for intermediary scripts to handle the powershell code.

From shell.js:

/**
* Silently execute a powershell command. 
*/
function powershell(command_string, interactive, pause){
    start = 'start \"\"'
    if (pause == 1){
        pause = '&pause'
    } else {
        pause = ''
    }
    if (interactive == 0){
        hidden = '-w hidden -nologo'
        start = ''
    } else {
        hidden = ''
    }    
    var clipboardData_orig = window.clipboardData.getData("Text");
    window.clipboardData.setData("Text",command_string); 
    var clipboardData = window.clipboardData.getData("Text");
    console.log("Powershell command is: " + clipboardData)
    command = String.format("%comspec% /c {0} PowerShell -noprofile {1} -Command $commands=$(\"Set-ExecutionPolicy Bypass -Scope Process -Force;add-type -AssemblyName System.Windows.Forms;$clipboardData = [System.Windows.Forms.Clipboard]::GetText() -split '\\r\\n';[String]::Join( ';', $(  ( $clipboardData ) ))\");Invoke-Expression $commands 2>&1;{2} | clip", start, hidden, pause);
    console.log("Invoked Powershell command via: " + command)
    WshShell.run(command,0,true);
    console.log("STDOUT: " + window.clipboardData.getData("Text"));
    setTimeout(function(t){
    window.clipboardData.setData("Text",clipboardData_orig); 
    }, 2000);
    return               
}

The WshShell ActiveX Object is instantiated in default.html.

This is the jquery for handling hyperlinks with class 'powershell'

/**
* For all html links (a) with class 'powershell' invoke the powershell command encased in comment strings <!--POWERSHELLCODE-->
*/
$( "a.powershell" ).click(function() {
var command_string = ''
var test = this.innerHTML.match(/<!--[\s\S]*-->/)
if (test != null){
    var regex = /<!--|-->/gi;
    command_string = test[0].replace(regex,'')
}else {
    command_string = heredoc(function () {/*
    'You must specify powershell commands for this link by encasing these in comment strings'
        'e.g.'
        '<a href="#" class="powershell">'
        '<!--'
            'Hello World!'
        '-->'
        '</a>'
        &pause
    */}); 
    alert(command_string)   
    return
}
var pause=this.getAttribute('data-pause')
if (pause != 1){
    pause = 0
}
var interactive=this.getAttribute('data-interactive')
if (interactive != 0){
    interactive = 1
}
powershell(command_string, interactive, pause);
}); 

see ui.js:

Note the use of custom 'data-*' html attributes, keeping in compliance at least with HTML5.

Troubleshooting

If you encounter errors in your document(s), try building with the --no-aio/-a flag, as with:

  • tasks run -s _template/default.markdown -o default.hta -t _template/templates/default.html --no-aio

This will invoke the pandoc command without the --standalone and --self-contained flags, which results in a document that offers itself more willingly to inspection.

Happy debugging :)

Sources

About

Interactive Document Creation Using HTA, Markdown, Pandoc, PP, Javascript, ActiveXObjects, Powershell, et al

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published