Sunday, 31 January 2016

HTML Preview

What is HTML Preview

HTML Preview is a tool answering a need I have got for a couple of years, actually since I started storing tons of HTML archives on my hard drives. My problem is likely to be yours : how to previewHTML pages without actually double-clicking on each item thus waiting for Internet Explorer to render it.
The amazing thing is that out there on the Internet, you have hundreds if not thousands of image thumbnailer sharewares, but you don't have a single tool to build automatically and with ease an aesthetic preview of all HTML pages in a given folder.
So let it be there.

Playing with HTML Preview

Before you intend to read the remainder of this article (if you like, or if you can), you may play with the provided HTML source code by doing the following. Once downloaded htmlpreview.html:
  • copy this file in the target folder
  • of course you need html pages to play with,
  • double-click on it
  • answer Yes when IE asks you if ActiveX scripting should be let run
  • just watch!

Building the HTML Preview from scratch

The story begins with the Windows Explorer web folders feature. You know, that space hungry thing anyone disables after a couple of days, which shows a zoomed out view of a selected picture, html or even doc file. Well, as anyone certainly guesses, this feature is full of HTML behind the wheel. Readthis MS article for more info. Namely, when the web folders are activated, the operating system looks for either a <user profile>\folder.htt file or for the default <windowsdir>\web\folder.htt file. This .htt file is in fact simple HTML with ActiveX scripting inside (remember to enable ActiveX scripting in your Internet configuration panel).
What I've done is figure out the actual elementary ActiveX component that builds the preview of one picture or document, and then put some application logic to reflect the fact that I want not a single preview in the output, but the preview of all HTML pages.
Below is the key data for this ActiveX :
  • ActiveX friendly name: ThumbCl class
  • CLSID : 1D2B4F40-1F10-11D1-9E88-00C04FDCAB92
  • Automation-enabled
  • Exposed methods:
    HRESULT displayFile([IN] BSTR filepath)
    HRESULT haveThumbnail([OUT] BOOL *bThumbnail)
  • Exposed properties :
    [propget] HRESULT freeSpace(/* [retval][out] */ long *);
    [propget] HRESULT usedSpace(/* [retval][out] */ long *);
    [propget] HRESULT totalSpace(/* [retval][out] */ long *);
Of course as anyone guesses the one and only method that is worth mentioning is thatdisplayFile([IN] BSTR filepath). It uses the client area declared in the HTML <object...> tag to render a preview of the passed file.
In fact, in HTML, that's simple code like follows :

<table>
  <tr>
    <td>
      <object id=Thumbnail_0
              classid="clsid:1D2B4F40-1F10-11D1-9E88-00C04FDCAB92"
              width=120 height=100>
      </object>
    </td>
    <td>&nbsp;</td>
   </tr>
</table>
<script>
  function Init_0()
  {
     Thumbnail_0.displayFile( 'D:\\new_stuff\\codeproject_homepage.htm' )
  }
</script>
So far that's easy. Now let's put this in true dirty Javascript code. In order to do this, we assume the following (to keep things simpler for the moment) :
  • we have an file enumerator called fc to browse all files of the current folder.fc.item() returns the current file, and fc.moveNext() switches to next.
  • we are working with a variable curdir, which holds the current folder being previewed.
  • thisfilename = htmlpreview.html
The Javascript code to do the previewing of all *.htm(l) pages is as follows :
<script language="javascript">
// purpose : use directory file enumerator to list all *.htm* files
//           and prepare a table cell to preview it using the preview ActiveX

// put the following line in comment if you don't want scaling
var imgsize = " width=160 height=140";
var clsid = " classid='clsid:1D2B4F40-1F10-11D1-9E88-00C04FDCAB92'";


var fso = new ActiveXObject("Scripting.FileSystemObject"), f, fc;

f = fso.GetFolder(curdir);
fc = new Enumerator(f.files); 
s = "";
sf = "<script language="'javascript'"> function Init() { ";
i = 0;

for (; !fc.atEnd(); fc.moveNext())
{
  // create an ActiveX instance for the preview of this html file
  name = "Thumbnail"+i
  s = "<object id=" + name + clsid + imgsize + "></"+"object>";
  if (fc.item().Name.indexOf(".htm",0)>-1 && fc.item().Name!=thisfilename)
  {
    // and add an hyperlink to play with
    s = "<a href='"+fc.item().Name+"'>" + s + "</a>";
    document.write(s);
    // attach initialisation code to it
    // .replace(/\\/g, "\\\\") replaces simple backslashes with 
    // double-backslashes
    s = name + 
       ".displayFile( '" + fc.item().Path.replace(/\\/g, "\\\\") + "');";
    sf += s;
    i++;
  }
}
sf += "} </"+ "script>";
document.writeln(sf);
</script>
Again sorry for those who don't speak Javascript.
OK, now before providing the full uncensored listing, let's discuss the initial steps : When you double-click on htmlpreview.html in any folder, we begin with retrieving the full qualified path ofhtmlpreview.html, for instance c:\ Documents and Settings \ Administrator \ My Documents \htmlpreview.html. But instead of getting just that with :
var fullpath = location.href;
we rather get something of the form file:/// c:/ Documents%20and%20Settings / Administrator / My%20Documents / htmlpreview.html because javascript is cross-platform by design, thus returns a file://-protocol compliant URI.
We remove, strip down, replace some stuff out of this one to prepare URIs that are meaning something for the web browser when htmlpreview.html is being rendered. That's why you'll see the following code :

<script language="javascript">

  var fullpath, curdir, thisfilename;



  fullpath = location.href; // fully qualified filepath 
  // of the form "file:///c:/Documents%20and%20Settings/Administrator/
  // My%20Documents/htmlpreview.html"

  // regexp weirdo
  fullpath = fullpath.replace(/file:\/\/\//,""); // remove file:///
  fullpath = fullpath.replace(/\//g,"\\"); // replace slash with backslash 
                                           // (for use on Windows OS)
  fullpath = fullpath.replace(/%20/g," "); // replace escaped space

  // split path and filename
  var iLastBackslash = fullpath.lastIndexOf("\\");

  // = htmlpreview.html
  var thisfilename = fullpath.substring( iLastBackslash+1, fullpath.length); 
  // remove filename from full path, we are just interested in the path
  var curdir = fullpath.substring( 0, iLastBackslash );
</script>
Ok now for full listing of htmlpreview.html :
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE> HTML Preview </TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" onload="Init()">
<script language="javascript">

  var fullpath, curdir, thisfilename;



  fullpath = location.href; // fully qualified filepath 
  // of the form "file:///c:/Documents%20and%20Settings/Administrator/
  //  My%20Documents/htmlpreview.html"

  // regexp weirdo
  fullpath = fullpath.replace(/file:\/\/\//,""); // remove file:///
  fullpath = fullpath.replace(/\//g,"\\"); // replace slash with backslash 
                                           //(for use on Windows OS)
  fullpath = fullpath.replace(/%20/g," "); // replace escaped space

  // split path and filename
  var iLastBackslash = fullpath.lastIndexOf("\\");

  // = htmlpreview.html
  var thisfilename = fullpath.substring( iLastBackslash+1, fullpath.length); 
  // remove filename from full path, we are just interested in the path
  var curdir = fullpath.substring( 0, iLastBackslash );

  // purpose : use directory file enumerator to list all *.htm* files
  //           and prepare a table cell to preview it using the preview ActiveX

  // put the following line in comment if you don't want scaling
  var imgsize = " width=160 height=140";
  var clsid = " classid='clsid:1D2B4F40-1F10-11D1-9E88-00C04FDCAB92'";


  var fso = new ActiveXObject("Scripting.FileSystemObject"), f, fc;

  f = fso.GetFolder(curdir);
  fc = new Enumerator(f.files); 
  s = "";
  sf = "<script language="'javascript'"> function Init() { ";
  i = 0;

  for (; !fc.atEnd(); fc.moveNext())
  {
    // create an ActiveX instance for the preview of this html file
    name = "Thumbnail"+i
    s = "<object id=" + name + clsid + imgsize + "></"+"object>";
    if (fc.item().Name.indexOf(".htm",0)>-1 && fc.item().Name!=thisfilename)
    {
      // and add an hyperlink to play with
      s = "<a href='"+fc.item().Name+"'>" + s + "</a>";
      document.write(s);
      // attach initialisation code to it
      // .replace(/\\/g, "\\\\") replaces simple backslashes with double-backslashes
      s = name + ".displayFile( '" + fc.item().Path.replace(/\\/g, "\\\\") + "');";
      sf += s;
      i++;
    }
  }
  sf += "} </"+ "script>";
  document.writeln(sf);

</script>
The hyperlinks we have inserted while generating the HTML code are of course enabled : just right-click on previews.
Last remark, if you would like to change the size of thumbnails, just change the imgsize value.
The sample Code project preview then becomes (fraction-only to avoid full size) :
image, 141kb

No comments:

Post a Comment