<HTML XMLNS:IE>
<HEAD>
<?import namespace="ie" implementation="#default">
<META HTTP-EQUIV="MSThemeCompatible" CONTENT="Yes">

<TITLE>Print Preview</TITLE>
#include "preview.h"

<STYLE>
.divPage
{
        position:       absolute;
        top:            -20000px;
		border-left:    1 solid black;
		border-top:     1 solid black;
		border-right:   4 solid black;
		border-bottom:  4 solid black;        
}
.page
{ 
        background:     white;          // pages are rendered over a white background
        width:          8.5in;
        height:         11in;
        margin:         0px;
        overflow:       hidden;
}
.mRect
{
        position: absolute; 
        margin: 1in;
        width:  6.5in;
        height: 9in;
        border: none;
        overflow : hidden;
}

.divHead
{
        position: absolute;
        overflow: hidden;
        top:      0in;
        left:     0in;
        width:    8.5in;
}
.divFoot
{
        position: absolute;
        overflow: hidden;
        bottom:   0in;
        left:     0in;
        width:    8.5in;
}



BODY            { overflow: hidden; padding: 0; margin: 0; background: threedface;     }
TABLE           { margin: 0px; padding: 0px; background: threedface; }
.THeader        { border: none; background: white; color: black; }    // white to match .page
.TFooter        { border: none; background: white; color: black; }    // white to match .page
TD              { padding: 0; margin: 0; border: none;               }
TD.UIPane       { width: 20px; border: none; font-family: 'ms sans serif'; font-size: 8pt; }
TD.UISeparator  { width:  1px; border-left: 2px threedhighlight ridge;   }
BUTTON          { border: 1px solid threedface; background: threedface; font-family: 'ms sans serif'; font-size: 8pt; color: buttontext;}
</STYLE>

<SCRIPT DEFER>
#define NUM_ABSTRACT_ZOOMS  3
#define ZOOM_PAGE_WIDTH     -1
#define ZOOM_WHOLE_PAGE     -2
#define ZOOM_TWO_PAGES      -3
#define FRAMESET_LAID_OUT    0
#define FRAMESET_SELECTED   1
#define FRAMESET_SEPARATE   2
#define STR_ZOOM_PAGE_WIDTH     "-1"
#define STR_ZOOM_WHOLE_PAGE     "-2"
#define STR_ZOOM_TWO_PAGES      "-3"
#define STR_FRAMESET_LAID_OUT   "0"
#define STR_FRAMESET_SELECTED   "1"
#define STR_FRAMESET_SEPARATE   "2"

#define HEADFOOTHEIGHT      27

var g_aDocTree      = new Object(); // Array of CPrintDoc's... using IDispatch expandos
var g_nDispPage     = -1;           // 0...n   Which of the pages is currently being previewed?
var g_nZoomFactor   = 0;            // What level of zoom is currently applying to the page?
var g_cLeftToPrint  = 0;            // How many documents are left to print? (Used in printing).
var g_fRTL;
var g_fPreview;

// HACKHACK (greglett) (IE6#17478)
// When printing WebOC apps from a nonbrowse thread (hyperlink "Print Target", PrintHTML API, File->Print + AllLinkedDocuments)
// we instantiate the WebOC on the background thread and call in IPrint::Print (via CTemplatePrinter::printNonNative).
// When that (asynchronously) returns, we close the printing thread... and the WebOC that is trying to print.
// This produces a vareity of interesting effects, all bad, and some dire.
// Note that we don't do this for Print Preview, Print All Linked Documents, because it would look very bad to the user.
var g_fDelayClose   = false;        // Delay window.close()

// Global value caches - mainly used to make sure we don't need to do any extra work:
// Require FULL REPAGINATION:
var g_nMarginTop            = 0;
var g_nMarginBottom         = 0;
var g_nMarginLeft           = 0;
var g_nMarginRight          = 0;
var g_nPageWidth            = 0;
var g_nPageHeight           = 0;
// Require only HEADER/FOOTER REAPPLICATION
var g_nUnprintTop           = 0;
var g_nUnprintBottom        = 0;
var g_strHeader             = "";
var g_strFooter             = "";
var g_fTableOfLinks         = false;

// keep track of page layout space
var g_cPagesDisplayed       = 0;
var g_cxDisplaySlots        = 1;
var g_cyDisplaySlots        = 1;

// keep track of whether the mouse is over any buttons
var g_imgUnderMouse         = null;

// keep track of frameset layout-related stuff
var g_nFramesetLayout       = FRAMESET_LAID_OUT;    // Which of the frameset layouts is currently active?
var g_strActiveFrame        = null;                 // Which frame "C_x_x," or null, is the active frame?
var g_nTotalPages           = 0;                    // How many total pages will be printed if the user
var g_nDocsToCalc           = 0;
var g_nFramesLeft           = 0;                    // # of frames currently being processed
                                                    // selects FRAMESET_SEPARATE?
#ifdef DEBUG
g_fReallyClose              = true;                 // Close template when Close() is called?
#endif
//----------------------------------------------------------------------
//  Functions  GetRuleFromSelector()
//
//  Synopsis:  Gets the first rule in the first stylesheet on the document
//             that applies to the given selector.
//             This will do the job for us so long as we have only one
//             stylesheet and only one rule per selector.
//
//  Returns:   the rule or null if no match was found.
//----------------------------------------------------------------------
function GetRuleFromSelector(strSelector)
{
    var i;
    var oRule;
    var oSS = document.styleSheets[0];  //  Assume one style sheet

    // Linear search.
    for (i=0;;i++)
    {
        oRule = oSS.rules[i];
                
        if (oRule == null)
            break;
                        
        if (oRule.selectorText == strSelector)
            break;            
    }

    // Either null (no match found) or a rule.
    return oRule;
}

function UnprintableURL(strLink)
{
    var fUnprintable = false;
    var cIndex;

    // Sniff the URL scheme prefix to make sure we can print it.
    cIndex = strLink.indexOf(":");
    switch (cIndex)
    {
        case 4:
            if (strLink.substr(0, cIndex)  == "news")
                fUnprintable = true;
            break;
            
        case 5:
            if (strLink.substr(0, cIndex)  == "snews")
                fUnprintable = true;
            break;

        case 6:
            if (    strLink.substr(0, cIndex)  == "telnet"
                ||  strLink.substr(0, cIndex)  == "mailto")
                fUnprintable = true;
            break;

        case 8:
            if (strLink.substr(0,cIndex)  == "vbscript")
                fUnprintable = true;
            break;

        case 10:
            if (strLink.substr(0,cIndex) == "javascript")
                fUnprintable = true;
            break;

    }

    // (greglett) If we ever choose to check MIME type from script (unlikely), 
    // this is the place to do it.
    return fUnprintable;
}

#ifdef DEBUG
function StatusToString(nStatus)
{
    var strStatus;    
    switch (nStatus)
    {
    case 0:
        strStatus="LOADING_OEHEADER";
        break;
    case 1:
        strStatus="LOADING_CONTENT";
        break;
    case 2:
        strStatus="LOADING_TABLEOFLINKS";
        break;
    case 3:
        strStatus="PAGING_COMPLETE";
        break;
    case 4:
        strStatus="READY_TO_PRINT";
        break;    
    default:
        strStatus="??? (" + nStatus + ")";
        break;
    }    
    return strStatus;
}

function Log(strEntry)
{
    var oDate = new Date();
    
    LogContainer.insertAdjacentHTML("beforeEnd", "[" + oDate.getTime() + "]  " + strEntry + "<BR>");
}

#endif

function OnKeyPress()
{
    if (event.keyCode == 27)    // esc
    {
        Close();
    }
#ifdef DEBUG
    else if (event.keyCode == 126)   // '~'
    {
        LogContainer.style.display = (LogContainer.currentStyle.display == "none") ? "block" : "none";
    }
    else if (event.keyCode == 33)   // '!'
    {
        var strDocNew = prompt("(DEBUG only!) Command to execute?", "");

        eval(strDocNew);
    }
    else if (event.keyCode == 64)   // '@'
    {
        g_fReallyClose = !g_fReallyClose;
        butClose.style.color = (g_fReallyClose) ? "" : "red";        
    }
#endif
}

function OnKeyDown()
{
    if (event.altKey)
    {
        switch (event.keyCode)
        {
        case 37:    // left arrow
            ChangeDispPage(g_nDispPage-1);
            break;

        case 39:    // right arrow
            ChangeDispPage(g_nDispPage+1);
            break;

        case 107:   // number pad +
        case 187:   // keyboard +
            HandleZoom(-1);
            break;

        case 109:   // number pad -
        case 189:   // keyboard -
            HandleZoom(1);
            break;

        case 35:    // end
            HandleLastPage();
            break;

        case 36:    //home
            HandleFirstPage();
            break;
        }
    }
}

function ShowHelp()
{
    window.showHelp("iexplore.chm::/print_preview.htm");
}

function AttachDialogEvents()
{
    // Attach events.  This is done here to:
    //  1. Prevent UI before we're able to handle it
    //  2. Load the dialog faster.
    // Attach button events
    butPrint.onclick        = HandlePrintClick;
    butPageSetup.onclick    = HandlePageSetup;
    butFirstPage.onclick    = HandleFirstPage;
    butBackPage.onclick     = HandleBackPage;
    butNextPage.onclick     = HandleForwardPage;
    butLastPage.onclick     = HandleLastPage;
    butZoomIn.onclick       = HandleZoomInButton;
    butZoomOut.onclick      = HandleZoomOutButton;
    butClose.onclick        = Close;
    butHelp.onclick         = ShowHelp;

    document.onhelp         = ShowHelp;

    butPrint.onmousedown        = buttonDown;
    butPageSetup.onmousedown    = buttonDown;
    butFirstPage.onmousedown    = buttonDown;
    butBackPage.onmousedown     = buttonDown;
    butNextPage.onmousedown     = buttonDown;
    butLastPage.onmousedown     = buttonDown;
    butZoomIn.onmousedown       = buttonDown;
    butZoomOut.onmousedown      = buttonDown;
    butClose.onmousedown        = buttonDown;
    butHelp.onmousedown         = buttonDown;

    // Attach image UI events
    printCtl.onmouseover    = buttonOver;
    printCtl.onmouseout     = buttonOut;
    begin.onmouseover       = buttonOver;
    begin.onmouseout        = buttonOut;
    prev.onmouseover        = buttonOver;
    prev.onmouseout         = buttonOut;
    next.onmouseover        = buttonOver;
    next.onmouseout         = buttonOut;
    end.onmouseover         = buttonOver;
    end.onmouseout          = buttonOut;
    zoomIn.onmouseover      = buttonOver;
    zoomIn.onmouseout       = buttonOut;
    zoomOut.onmouseover     = buttonOver;
    zoomOut.onmouseout      = buttonOut;
    butPrint.onmouseover    = new Function("buttonRaise(this);");
    butPrint.onmouseout     = new Function("buttonLower(this);");
    butClose.onmouseover    = new Function("buttonRaise(this);");
    butClose.onmouseout     = new Function("buttonLower(this);");
    butHelp.onmouseover     = new Function("buttonRaise(this);");
    butHelp.onmouseout      = new Function("buttonLower(this);");

    // Attach input events
    inputPageNum.onkeypress = HandleInputKeyPress;
    inputPageNum.onchange   = HandlePageSelect;
    selectZoom.onchange     = HandleZoomSelect;
    selectFrameset.onchange = HandleFramesetSelect;

    // Attach document events
    window.onresize         = OnResizeBody;
    window.onerror          = HandleError;

    // Focus maintenance: anytime we return from another dialog (page setup, help, etc.),
    // push the focus back onto the document body (for scrolling and accessibility).
    window.onfocus          = new Function("MasterContainer.focus()");

    document.body.onkeypress = OnKeyPress;
    document.body.onkeydown  = OnKeyDown;
}


//----------------------------------------------------------------------
//  Body event handlers.
//----------------------------------------------------------------------
function OnLoadBody()
{
    g_fRTL      = (document.body.currentStyle.direction.toLowerCase() == "rtl");
    g_fPreview  = dialogArguments.__IE_PrintType == "Preview";

    // For "printTarget" on an unprintable link URL on the body, we can have a bad URL.
    // Sniff it for printability, and bail if not so good. (greglett) (103361)
    if (UnprintableURL(dialogArguments.__IE_ContentDocumentUrl))
    {
        // JurgenE , variables for localization must be in the form L_someIDtext_Text_
        var L_Invalid_Text = "Unable to print URL.  Please navigate directly to this page and select Print.";
        alert(L_Invalid_Text);
        window.close();
    }

    // Set up "base" values for each of the arrow images - the image source will be different for LTR vs. RTL sy1stems.
    // This assumes all images will be initialized to *_inactive.gif
    // We could avoid doing all this string mangling by adding an inline "base" property on the IMG and localizing it.
    var str;
    str = begin.src;
    begin.base = str.slice(str.lastIndexOf("/")+1,str.indexOf("_"));
    str = end.src;
    end.base = str.slice(str.lastIndexOf("/")+1,str.indexOf("_"));
    str = next.src;
    next.base = str.slice(str.lastIndexOf("/")+1,str.indexOf("_"));
    str = prev.src;
    prev.base = str.slice(str.lastIndexOf("/")+1,str.indexOf("_"));

    ChangeZoom(75);

    // Do this H/F switch before EnsureDocuments
    if (dialogArguments.__IE_HeaderString)
        Printer.header = dialogArguments.__IE_HeaderString

    if (dialogArguments.__IE_FooterString)
        Printer.footer = dialogArguments.__IE_FooterString    

    EnsureDocuments();                      // Get defaults (no documents means little work)    
   

    // Make the cursor look like an hourglass while we wait for the frames to build
    window.document.body.style.cursor="wait";

    CreateDocument("document", "C", true);
    ChangeDispPage(1);

    // Set up the counter of remaning frames to load, and begin the iterative process
    g_nFramesLeft = 1;
    // Build those frames
    // (greglett) We may want to revise how and when we do this for the printing only,
    // i.e. non-preview situations.  This may have a significant perf hit on how long
    // it takes the print to happen, and most of the time, we don't really need to
    // generate all those frames for a printout.  We only do it this way so that we're
    // prepared if a user decides to try the FRAMESET_SEPARATE option in preview.
    OnBuildAllFrames("C");

    if (g_fPreview)
    {
        AttachDialogEvents();

        // make sure the display region is sized properly
        OverflowContainer.style.top = idDivToolbar.offsetHeight;
        OverflowContainer.style.height = document.body.clientHeight - idDivToolbar.offsetHeight;
    }
    else
    {
        PrintNow(dialogArguments.__IE_PrintType == "Prompt");
    }
}

function BuildAllFramesComplete()
{
    AssertSz(g_nFramesLeft == 0, "BuildAllFramesComplete when not complete?");    

    // Restore the cursor to its normal operation
    window.document.body.style.cursor="auto";

    // (t-svoida) Not sure this is the best or final place for this step to happen.
    // (greglett) It almost certainly isn't: we wait for all frames to be built to allow someone to
    //            do the frameset selection thing.
    UpdateFramesetSelect(); 
}

function CalcDocsComplete()
{
    AssertSz(g_nDocsToCalc == 0, "CalcDocsComplete when not complete?");    

    // If we're currently laid out as FRAMESET_SEPARATE, we need to recompute our frame/page table
    if (g_nFramesetLayout == FRAMESET_SEPARATE)
    {
        ChangeFramesetLayout(g_nFramesetLayout, true)   // FORCE update
    }
}

function OnResizeBody()
{
    // make sure the display region is sized properly
    OverflowContainer.style.height = Math.max(0, document.body.clientHeight - idDivToolbar.offsetHeight);

    // make sure zoom gets updated if we're in an abstract zoom mode
    HandleDynamicZoom();

    // make sure a relayout happens, because the layout space changed
    PositionPages(g_nDispPage);
}

//+-------------------------------------------------------------------
//
//  Synopsis:   Turns off error messages in dialogs
//
//  Arguments:  none
//
//  returns:    true (tells browser not to handle message)
//
//--------------------------------------------------------------------
function HandleError(message, url, line)
{
#ifdef DEBUG
    var str = "";
    if (url == document.URL)
        str += "Template ";

    // Debug only, no need to localize
    str += "Error (" + url + ":" + line + "): " + message;

    alert(str);
#else
    var L_Internal_ErrorMessage = "There was an internal error, and Internet Explorer is unable to print this document.";
    alert(L_Internal_ErrorMessage);
    window.close();
#endif

    return true;
}


// TODO (greglett) (TASK 5182)
// Q: So, why two RectComplete handlers, one called with a setTimeout from the other?
// A: Because otherwise we spend *all* of our time processing RectCompletes, and never give Trident a chance
//    to do any redrawing, &c...  In order to provide a somewhat interactive UI, we need this delay.
// Work to fix this should accompany incremental pagination.
function OnRectComplete( strDoc )
{
    if (!g_aDocTree[strDoc])
    {
        // No need to localize the string - it is only displayed in debug mode.
        HandleError("Document " + strDoc + " does not exist.", document.URL, "OnRectComplete");
        return;
    }

#ifdef DEBUG
    Log("OnRectComplete " + event.srcElement.id + " " + event.contentOverflow);
#endif
    window.setTimeout("OnRectCompleteNext('" + strDoc + "', " + event.contentOverflow + ",'" + event.srcElement.id + "');", 25);
}
function OnRectCompleteNext( strDoc, fOverflow, strElement)
{    
#ifdef DEBUG
    Log("OnRectCompleteNext " + strElement + " " + fOverflow);
#endif
    g_aDocTree[strDoc].RectComplete(fOverflow, strElement);
}

//----------------------------------------------------------------------
//  utilities for managing widget state
//----------------------------------------------------------------------

function enableButton(btn, img)
{
    btn.disabled = false;
    if (g_imgUnderMouse == img)
    {
        img.src = img.base + "_hilite.gif";
        buttonRaise(btn);
    }
    else
    {
        img.src = img.base + ".gif";
        buttonLower(btn);
    }
}

function disableButton(btn, img)
{
    btn.disabled = true;
    buttonLower(btn);

    // accept null because some buttons share
    // a common inactive image and this
    // generic naming scheme is broken

    if (img != null)
    {
        img.src = img.base + "_inactive.gif";
    }
}

function updateNavButtons()
{
    //
    // disable any nav buttons that need to be
    //

    if (g_nDispPage == 1)
    {
        disableButton(butFirstPage, begin);
        disableButton(butBackPage, prev);
    }
    else
    {
        enableButton(butFirstPage, begin);
        enableButton(butBackPage, prev);
    }

    if (TotalDisplayPages() - g_nDispPage < g_cxDisplaySlots * g_cyDisplaySlots)
    {
        disableButton(butNextPage, next);
        disableButton(butLastPage, end);
    }
    else
    {
        enableButton(butNextPage, next);
        enableButton(butLastPage, end);
    }
}

function updateZoomButtons()
{
    var fZoomOutDisabled = false;
    var fZoomInDisabled = false;

    //
    // disable any zoom buttons necessary
    //

    var oOptions = selectZoom.options;

    if (g_nZoomFactor >= parseInt(oOptions[0].value))
    {
        // we're on the largest zoom or larger, so disable zoom in
        disableButton(butZoomIn, null);
        zoomIn.src = "zoom_inactive.gif";
        fZoomInDisabled = true;
    }
    else if (g_nZoomFactor <= parseInt(oOptions[oOptions.length-1-NUM_ABSTRACT_ZOOMS].value))
    {
        // we're on the smallest zoom or smaller, so disable zoon out
        disableButton(butZoomOut, null);
        zoomOut.src = "zoom_inactive.gif";
        fZoomOutDisabled = true;
    }

    //
    // and enable any zoom buttons that are left
    //

    if (!fZoomOutDisabled)
    {
        enableButton(butZoomOut, zoomOut);
    }

    if (!fZoomInDisabled)
    {
        enableButton(butZoomIn, zoomIn);
    }
}

//----------------------------------------------------------------------
//  Function Synopsis
//  UpdateFramesetSelect()
//      Preps frameset layout select for user interaction, by
//      pruning off the SELECTED option if there's no selection
//      and hiding the control altogether if it's not a frameset
//      page.
//----------------------------------------------------------------------
function UpdateFramesetSelect()
{
    // Check main document's frameset status first
    if ( g_aDocTree["C"]._fFrameset )
    {
        // Pare the "Only the selected frame" option if there's no selected frame
        // (and the list is still at its original length)
        if (!g_strActiveFrame)
        {
            selectFrameset.options.remove(1);
        }

        // Show both the control and the preceding separator
        separatorFrameset.style.display = "inline";
        cellFrameset.style.display      = "inline";
    }
#ifdef DEBUG
    else 
    {
        // Hide both the control and the preceding separator
        // separatorFrameset.style.display = "none";
        // cellFrameset.style.display      = "none";

        // (greglett) They should already be hidden.  Let's make this an assert.
        AssertSz(   separatorFrameset.currentStyle.display == "none"
                &&  cellFrameset.currentStyle.display == "none", "UI Visible when it shouldn't be?");
    }
#endif
}

//----------------------------------------------------------------------
//  Function Synopses
//  getPageWidth, getPageHeight
//      Returns the global size of each page - assumes constant sized pages.
//      Return value is in screen CSS pixels.
//----------------------------------------------------------------------
function getPageWidth()
{
    return g_aDocTree["C"].Page(1).offsetWidth;
}

function getPageHeight()
{
    return g_aDocTree["C"].Page(1).offsetHeight;
}

//----------------------------------------------------------------------
//  UndisplayPages()
//      Undisplays all visible pages
//----------------------------------------------------------------------
function UndisplayPages()
{
    while (g_cPagesDisplayed > 0)
    {
        var oPage = DisplayPage(g_nDispPage + g_cPagesDisplayed - 1);
        if (oPage != null)
        {
            oPage.style.top = "-20000px";
            if (g_fRTL)
                oPage.style.right = "10px";
            else
                oPage.style.left = "10px";
        }

        g_cPagesDisplayed--;
    }
}

//----------------------------------------------------------------------
//  PositionPages(nDispPage)
//      Sets the x and y positioning for the pages currently being
//      displayed. If the given # is outside valid bounds, the
//      nearest valid boundary is selected instead.
//      PERF (greglett) We may want to make redisplay smarter - right now, all
//      pages are redisplayed every time the window is resized.  We really should
//      have the funtion check if pages need to be redisplayed, with a force flag option.
//----------------------------------------------------------------------
function PositionPages(nDispPage)
{
    // Cache the current display doc
    var strDispDoc = DisplayDocument(nDispPage);

    if (    g_aDocTree != null
        &&  g_aDocTree[strDispDoc] != null
        &&  g_aDocTree[strDispDoc].Pages() > 0)
    {
        // first clear the old pages
        UndisplayPages();
        
        //
        // now draw the new pages with the new settings
        //

        // figure out how many pages we can show this time
        var xPageWidth = getPageWidth();
        var yPageHeight = getPageHeight();
        var nMaxPage = TotalDisplayPages();
        
        g_cxDisplaySlots = Math.max(1, Math.floor((OverflowContainer.offsetWidth*100)/(g_nZoomFactor*(xPageWidth+10))));
        g_cyDisplaySlots = Math.max(1, Math.floor((OverflowContainer.offsetHeight*100)/(g_nZoomFactor*(yPageHeight+10))));

        // Trim nDispPage to be inside a valid page range.
        var nMaxPageRequest = Math.max(nMaxPage - g_cxDisplaySlots * g_cyDisplaySlots + 1, 1);

        if ( nDispPage < 1 )
            nDispPage = 1;        
        else if (nDispPage > nMaxPageRequest)
            nDispPage = nMaxPageRequest;

        // now start using the new (bounded) page index
        g_nDispPage = nDispPage;

        // Update the page display to show the right numbers
        document.all.spanPageTotal.innerText = nMaxPage;
        document.all.inputPageNum.value = g_nDispPage;

        // fix the begin, prev, next, end arrows
        updateNavButtons();

        //
        // set up pages until we run out of pages
        // or run out of rows of slots to fill
        //

        var xDisplaySlot = 1;
        var yDisplaySlot = 1;
        var iPage = g_nDispPage;

        g_cPagesDisplayed = 0;

        // PERF We really don't need to do a DisplayPage each time
        //      We could loop through the DisplayDocuments, and loop through
        //      each of their pages to many multiple lookups inherent in calling
        //      DisplayPage each iteration of the loop (greglett)
        while (iPage <= nMaxPage && yDisplaySlot <= g_cyDisplaySlots)
        {
            var xPos    = xDisplaySlot*10 + (xDisplaySlot-1)*xPageWidth;
            var yPos    = yDisplaySlot*10 + (yDisplaySlot-1)*yPageHeight;
            var oPage   = DisplayPage(iPage);

            if (g_fRTL)
                oPage.style.right = xPos;
            else
                oPage.style.left = xPos;
           
            oPage.style.top = yPos;
    
            iPage++;
            if (++xDisplaySlot > g_cxDisplaySlots)
            {
                xDisplaySlot = 1;
                yDisplaySlot++;
            }

            g_cPagesDisplayed++;

        }
    }
}

function ChangeDispPage(nDispPageNew)
{
    if (isNaN(nDispPageNew))
    {
        // make sure the UI shows a valid number
        inputPageNum.value = g_nDispPage;
    }
    else
    {
        // Range check
        if (nDispPageNew < 1)
            nDispPageNew = 1;
        else if (nDispPageNew > TotalDisplayPages())
            nDispPageNew = TotalDisplayPages();
            
        // make sure the top of the page is showing
        OverflowContainer.scrollTop = 0;

        PositionPages(nDispPageNew);
    }

    return g_nDispPage;
}

//----------------------------------------------------------------------
//  Function Synopsis
//  DisplayDocument()
//      As below.  Only valid when not FRAMESET_SELECT.
//  DisplayDocument(nWhichPage)
//      Returns either the current display document (indicated by
//      g_nDispPage) in the format "C_x_x", or, when passed a page
//      number from 1...total pages (includes all frames in SEPARATE
//      frameset layout), returns the display document shown on that
//      page.  Will return null if the page number passed is out
//      of range.
//----------------------------------------------------------------------
function DisplayDocument(nWhichPage)
{
    switch (g_nFramesetLayout)
    {
        case FRAMESET_LAID_OUT:
            // In LAID_OUT case, display doc is always the master, "C"
            return "C";
            break;

        case FRAMESET_SELECTED:
            // In SELECTED casem display doc is always the active frame
            return g_strActiveFrame;
            break;

        case FRAMESET_SEPARATE:
            var i;

            if (!nWhichPage)
                return null;
                
            AssertSz(nWhichPage > 0 && nWhichPage <= g_nTotalPages, "DisplayDocument called with invalid page: " + nWhichPage);

            // Otherwise, count to find correct document
            for (i in g_aDocTree)
            {
                if (    nWhichPage >= g_aDocTree[i]._nStartingPage
                    &&  nWhichPage < (g_aDocTree[i]._nStartingPage + g_aDocTree[i].Pages()))
                    return i;
            }

        default:
            // Should never happen!
            // Don't localize this string, debug code only
            HandleError("Display document cannot be found!", document.URL, "DisplayDocument");
    }
}


//----------------------------------------------------------------------
//  Member    TotalDisplayPages
//
//  Synopsis: With multiple page arrays per CPrintDoc, these functions
//            are to provide the illusion of one array.  Unlike Pages(),
//            this returns the number of pages in this CPrintDoc if the
//            frameset layout is LAID_OUT or SELECTED, or the total
//            number of pages in all individual frames if the frameset
//            layout is SEPARATE.  To make this closer to user UI, this
//            virtual array is 1 based, not 0 based.
//----------------------------------------------------------------------
function TotalDisplayPages()
{
    if (g_nFramesetLayout == FRAMESET_SEPARATE)
        return g_nTotalPages;

    AssertSz(DisplayDocument(), "TotalDisplayPages: No DisplayDocument!")   // DEBUG only: don't localize.
    return g_aDocTree[DisplayDocument()].Pages();
}

//----------------------------------------------------------------------
//  DisplayPage(nWhichPage)
//      Returns the specified page of the displayed set
//
//      The only situation this is different than the DisplayDocument
//      is for "all frames individually."  Imagine two two page frames.
//      Then, the function would return:
//          nWhichPage      Return
//            1-2           Frame1.Page(nWhichPage)
//            3-4           Frame2.Page(nWhichPage - 2)
//----------------------------------------------------------------------
function DisplayPage(nWhichPage)
{
    AssertSz(nWhichPage >= 0, "Negative or null display page requested: " + nWhichPage);

    // Local page numbers are always the same as total page
    // numbers unless the layout is SEPARATE
    if (g_nFramesetLayout != FRAMESET_SEPARATE)
        return g_aDocTree[DisplayDocument(nWhichPage)].Page(nWhichPage);

    // Compute the page offset into the current document, and ask for that page.
    return g_aDocTree[DisplayDocument(nWhichPage)].Page(nWhichPage - g_aDocTree[DisplayDocument(nWhichPage)]._nStartingPage + 1);
}

//----------------------------------------------------------------------
//  Function ChangeZoom
//      Checks the bounds and changes the document zoom level to the new value.
//----------------------------------------------------------------------
function ChangeZoom(nNewVal)
{
    if (nNewVal < 10)
        nNewVal = 10;
    else if (nNewVal > 1000)
        nNewVal = 1000;
    else if (isNaN(nNewVal))
        nNewVal = g_nZoomFactor;

    if (nNewVal != g_nZoomFactor)
	{
		MasterContainer.style.zoom = nNewVal + "%";
		g_nZoomFactor = nNewVal;
        updateZoomButtons();
        PositionPages(g_nDispPage);
	}

    return g_nZoomFactor;
}

//----------------------------------------------------------------------
//  Function  BuildTableOfLinks
//
//  Synopsis:  These function takes a document, and uses its links collection
//             to create another document with a table of links.
//             Returns the document, or null if no links were encountered.
//----------------------------------------------------------------------
function BuildTableOfLinks( docSource )
{
    var aLinks = docSource.links;
    var nLinks = aLinks.length;    
    if (nLinks > 0)
    {
        var fDuplicate;
        var i;
        var j;
        var newHTM;
        var docLinkTable    = document.createElement("BODY");    
        var L_LINKSHEADER1_Text = "Shortcut Text";
        var L_LINKSHEADER2_Text = "Internet Address";

        newHTM  = "<CENTER><TABLE BORDER=1>";    
        newHTM += "<THEAD style=\"display:table-header-group\"><TR><TH>" 
                + L_LINKSHEADER1_Text 
                + "</TH><TH>" + L_LINKSHEADER2_Text + "</TH></TR></THEAD><TBODY>";

        // Old behvaior: Print each linked URL only once.  Do include a self link.
        // Ack! Yes, this is a dreaded N-squared algorithm.  This is what we used to do, though, so we're no worse.
        // If we deem it important enough, we can pare it down to NlogN via a more efficient sorting algorithm. (greglett)
        for (i = 0; i < nLinks; i++)
        {
            fDuplicate = false;
            
            for (j = 0; (!fDuplicate) && (j < i); j++)
            {
                if (aLinks[i].href == aLinks[j].href)
                    fDuplicate = true;
            }

            if (!fDuplicate)            
                newHTM += ("<TR><TD>" + aLinks[i].innerText + "</TD><TD>" + aLinks[i].href + "</TD></TR>");        
        }
                    
        newHTM += "</TBODY></TABLE></CENTER>";
    
        docLinkTable.innerHTML = newHTM;

        return docLinkTable.document;
    }

    return null;    
}

//----------------------------------------------------------------------
//  Function  CreateDocument
//
//  Synopsis:  Allocates and initializes a new CPrintDoc with the 
//             given contentURL and documentID.  Also passed: Should
//             we pay attention to any existing stream header.
//----------------------------------------------------------------------
function CreateDocument( docURL, strDocID, fUseStreamHeader )
{
    if (g_aDocTree[strDocID])
        return;    

    g_aDocTree[strDocID] = new CPrintDoc( strDocID, docURL );           
    g_aDocTree[strDocID].InitDocument( fUseStreamHeader );
}

function ChangeFramesetLayout( nNewLayout, fForce )
{
    // Don't do any work if none is needed
    if (    g_nFramesetLayout == nNewLayout
        &&  !fForce)
        return;

    // Hide the currently displayed page view
    UndisplayPages();

    // Grab the new frameset layout setting
    g_nFramesetLayout = nNewLayout;

    switch ( nNewLayout )
    {
        case FRAMESET_LAID_OUT:
        case FRAMESET_SELECTED:
            break;
            
        case FRAMESET_SEPARATE:
            // Recount all individual pages and assign starting page
            // numbers to each.  If a page has no starting page number,
            // i.e. _nStartingPage = 0, then it's either a frameset page
            // or the selection bit, and we don't want to include it in
            // the SEPARATE-type frameset previews.  We have to do this
            // computation each time, or else we can get errors after
            // repaginations.
            g_nTotalPages = 0;
            
            for (i in g_aDocTree)
            {
                if (    g_aDocTree[i]._fFrameset
                    ||  i == "S")
                    g_aDocTree[i]._nStartingPage = 0;
                else
                {
                    g_aDocTree[i]._nStartingPage = g_nTotalPages + 1;
                    g_nTotalPages += g_aDocTree[i].Pages();
                }
            }
            break;
            
        default:
            // Should never happen!
            //Don't localize this string, debug code only
            HandleError("Impossible frameset layout type: " + nNewLayout, document.URL, "ChangeFramesetLayout");
    }

    // Allow the logic in ChangeDispPage to select the correct page
    // and page number, and then display it.
    ChangeDispPage(1);
}

//----------------------------------------------------------------------
//  Function  EnsureDocuments()
//
//  Synopsis: This function picks up and applies style changes, resetting all
//            the documents states so they do enough work to reflow.
//
//  Returns:  nothing
//----------------------------------------------------------------------
function EnsureDocuments()
{
    var i;
    var tmp;
    
    //  Units received are 1/100"; convert to 1"
    //  Changes in these require repagination:
    var top         = Printer.marginTop         / 100;
    var bottom      = Printer.marginBottom      / 100;
    var left        = Printer.marginLeft        / 100;
    var right       = Printer.marginRight       / 100;
    var pageWidth   = Printer.pageWidth         / 100;
    var pageHeight  = Printer.pageHeight        / 100;

    //  Changes in these require repagination of the table of links:
    var linktable   = Printer.tableOfLinks;

    //  Changes in these require reapplication of headers/footers:
    var upTop       = Printer.unprintableTop    / 100;
    var upBottom    = Printer.unprintableBottom / 100;
    var header      = Printer.header;
    var footer      = Printer.footer;

    // Handle case where (unprintable) + (header/footer size) > (specified margin)
    // Just autoincrease the margin...
    // TODO (greglett) For now, use a guesstimation for the H/F size, as it is always one
    // line of the same font.  Frontload work to prevent reflow, and use a constant.
    // Fixing this is REQUIRED for HTML headers/footers.
    // TODO (greglett) (99287)    
    // On top of that, we are sizing the page slightly wrong - our "page size"
    // should not include the 1px/4px borders on the page for UI.  This could be amended by surrounding
    // the page in a DIV contiainer (the UI page), and printing/sizing the inner page, while positioning
    // the outer page.  This has some difficulties due to interaction of absolutely positioning stuff.
    if (header)    
    {
        tmp = upTop + (HEADFOOTHEIGHT / 100);
        if (tmp > top)
            top = tmp;
    }
    if (footer)
    {
        tmp = upBottom + (HEADFOOTHEIGHT / 100);
        if (tmp > bottom)
            bottom = tmp;
    }

    if (upTop != g_nUnprintTop)
    {
        g_nUnprintTop   = upTop;
        
        // Set styles on Header
        oRule = GetRuleFromSelector(".divHead");
        if (oRule == null)
        {
            // Should never happen!
            //Don't localize this string, debug code only
            HandleError("'.divHead' rule does not exist!", document.URL, "CPrintDoc::EnsureDocuments");
        }
        oRule.style.top            = upTop     + "in";
    }
    if (upBottom != g_nUnprintBottom)
    {
        g_nUnprintBottom= upBottom;

        // Set styles on Footer
        oRule = GetRuleFromSelector(".divFoot");
        if (oRule == null)
        {
            // Should never happen!
            //Don't localize this string, debug code only
            HandleError("'.divFoot' rule does not exist!", document.URL, "CPrintDoc::EnsureDocuments");
        }
        oRule.style.bottom         = upBottom  + "in";
    }

    //  Check if something has changed that will force work on the document.
    if (    top         != g_nMarginTop
        ||  bottom      != g_nMarginBottom
        ||  left        != g_nMarginLeft
        ||  right       != g_nMarginRight
        ||  pageWidth   != g_nPageWidth
        ||  pageHeight  != g_nPageHeight
        ||  header      != g_strHeader
        ||  footer      != g_strFooter    )
    {
        var conWidth    = pageWidth - left - right;
        var conHeight   = pageHeight - top - bottom;
        var oStyleSheet = document.styleSheets[0];
        var oRule;

        if (conWidth < 0)
            conWidth = 0;
        if (conHeight < 0)
            conHeight = 0;

        //  Hide currently displayed pages - they're about to become invalid.
        UndisplayPages();

        //  Clear page numbering for FRAMESET_SEPARATE layout
        g_nTotalPages = 0;
        g_nDocsToCalc = 0;

        //  All documents are about to be repaginated!
        for (i in g_aDocTree)
        {
            g_nDocsToCalc++;
            g_aDocTree[i].ResetDocument();
        }
        
        // Cache new values.
        g_nMarginTop    = top;
        g_nMarginBottom = bottom;
        g_nMarginLeft   = left;
        g_nMarginRight  = right;
        g_nPageWidth    = pageWidth;
        g_nPageHeight   = pageHeight;
        g_fTableofLinks = linktable;
        g_strHeader     = header;        
        g_strFooter     = footer;
        HeadFoot.textHead   = g_strHeader;
        HeadFoot.textFoot   = g_strFooter;
            
        // Set styles on pages.
        oRule = GetRuleFromSelector(".page");
        if (oRule == null)
        {
            // Should never happen!
            //Don't localize this string, debug code only
            HandleError("'.page' rule does not exist!", document.URL, "CPrintDoc::EnsureDocuments");
        }
        oRule.style.width       = pageWidth  + "in";
        oRule.style.height      = pageHeight + "in";
    
        // Set styles on layout rects.
        oRule = GetRuleFromSelector(".mRect");
        if (oRule == null)
        {
            // Should never happen!
            //Don't localize this string, debug code only
            HandleError("'.mRect' rule does not exist!", document.URL, "CPrintDoc::EnsureDocuments");
        }
        oRule.style.marginLeft     = left      + "in";
        oRule.style.marginRight    = right     + "in";
        oRule.style.marginTop      = top       + "in";
        oRule.style.marginBottom   = bottom    + "in";
        oRule.style.width          = conWidth  + "in";
        oRule.style.height         = conHeight + "in";

        // Set styles on Header
        oRule = GetRuleFromSelector(".divHead");
        if (oRule == null)
        {
            // Should never happen!
            //Don't localize this string, debug code only
            HandleError("'.divHead' rule does not exist!", document.URL, "CPrintDoc::EnsureDocuments");
        }
        oRule.style.left            = left     + "in";
        oRule.style.width           = conWidth + "in";
        // Set styles on Header
        oRule = GetRuleFromSelector(".divFoot");
        if (oRule == null)
        {
            // Should never happen!
            //Don't localize this string, debug code only
            HandleError("'.divHead' rule does not exist!", document.URL, "CPrintDoc::EnsureDocuments");
        }
        oRule.style.left            = left     + "in";
        oRule.style.width           = conWidth + "in";

        //  HACKHACK (greglett) 94479
        // The viewchain is not capable of recalcing if the layout rect properties are changed right now.
        // So, we unhook the LR, change the properties (ABOVE) and then reload/reparse the entire document (HERE)
        for (i in g_aDocTree)
        {
            // Reinit document - and pass whether or not we're using an OE header.
            g_aDocTree[i].InitDocument((g_aDocTree[i]._anMerge[LOADING_CONTENT] == 1));
        }

        //  Repagination screws up the delicate frame page table used to figure out how
        //  many pages are in which documents.  Force update.
        if (g_nFramesetLayout == FRAMESET_SEPARATE)
            ChangeFramesetLayout(g_nFramesetLayout, true);

        // Repaint the current page(s) that we hid above with UndisplayPages()
        PositionPages(g_nDispPage);
    }
    
    else if (linktable != g_fTableOfLinks)
    {

        // Cache new values.
        g_fTableOfLinks = linktable;
        
        //  All documents are about to be table of linked! (?)
        for (i in g_aDocTree)
        {
            g_aDocTree[i].ResetTableOfLinks();
        }        
    }    

}

//----------------------------------------------------------------------
//  Functions: Button utilities.
//
//  Synopsis:  These various & sundry functions are button UI utilities
//             and provide basic toolbar UI
//----------------------------------------------------------------------
function buttonRaise( elem )
{
	elem.style.borderStyle = "outset";
	elem.style.borderColor = "threedhighlight";
}
function buttonLower( elem )
{
	elem.style.borderStyle = "solid";
	elem.style.borderColor = "threedface";
}
function buttonDepress(elem)
{
    elem.style.borderStyle = "inset";
    elem.style.borderColor = "threedshadow";
}

// PERF (greglett)
// We don't need to do a whole updateXXXButtons!
function buttonOver()
{
    var imgSrc = event.srcElement;

    g_imgUnderMouse = imgSrc;

    if (imgSrc == begin ||
        imgSrc == prev ||
        imgSrc == next ||
        imgSrc == end)
    {
        updateNavButtons();
    }
    else if (imgSrc == zoomIn ||
             imgSrc == zoomOut)
    {
        updateZoomButtons();
    }
    else
    {
	    imgSrc.src= "" + imgSrc.id + "_hilite.gif";
	    buttonRaise( imgSrc.parentNode );
    }
}

// PERF (greglett)
// We don't need to do a whole updateXXXButtons!
function buttonOut()
{
    var imgSrc = event.srcElement;

    g_imgUnderMouse = null;

    if (imgSrc == begin ||
        imgSrc == prev ||
        imgSrc == next ||
        imgSrc == end)
    {
        updateNavButtons();
    }
    else if (imgSrc == zoomIn ||
             imgSrc == zoomOut)
    {
        updateZoomButtons();
    }
    else
    {
	    imgSrc.src= "" + imgSrc.id + ".gif";
	    buttonLower( imgSrc.parentNode );
    }
}

function buttonDown()
{
    buttonDepress(event.srcElement);
}

// --------------------------------------------------------
function HandlePageSelect()
{
    event.srcElement.value = ChangeDispPage(parseInt(inputPageNum.value));

    // Force focus back to the preview document so keyboard
    // accelerators work as expected
    MasterContainer.focus();
}
function HandlePageSetup()
{
    if (Printer.showPageSetupDialog())
        EnsureDocuments();
}        
function HandleForwardPage()
{
    ChangeDispPage(g_nDispPage + 1);

    // Force focus back to the preview document so keyboard
    // accelerators work as expected
    MasterContainer.focus();
}
function HandleBackPage()
{
    ChangeDispPage(g_nDispPage - 1);

    // Force focus back to the preview document so keyboard
    // accelerators work as expected
    MasterContainer.focus();
}
function HandleFirstPage()
{
    ChangeDispPage(1);

    // Force focus back to the preview document so keyboard
    // accelerators work as expected
    MasterContainer.focus();
}
function HandleLastPage()
{    
    ChangeDispPage(TotalDisplayPages());

    // Force focus back to the preview document so keyboard
    // accelerators work as expected
    MasterContainer.focus();
}

function NumericFromSpecialZoom(fnBounder)
{
    var iMaxNumericZoom = selectZoom.options.length-1-NUM_ABSTRACT_ZOOMS; // specials zooms follow numeric zooms
    var iBelow = -1;
    var nBelow = 0;
    var iAbove = iMaxNumericZoom + 1;
    var i;

    // establish iBelow and iAbove so that they are the selection indices just
    // below and above the current value of g_nZoomFactor. if there's an
    // exact match, then iBelow and iAbove have to both be the index whose
    // value matches

    // get iBelow
    for (i = 0; i <= iMaxNumericZoom; i++)
    {
        var nThisIndex = parseInt(selectZoom.options[i].value);
        if (nThisIndex >= g_nZoomFactor)
        {
            iBelow = i;
            nBelow = nThisIndex;
        }
        else
        {
            break;
        }
    }

    // get iAbove
    if (nBelow > g_nZoomFactor)
    {
        iAbove = iBelow + 1;
    }
    else
    {
        iAbove = iBelow;
    }

    return fnBounder(iBelow, iAbove);
}

function HandleZoom(nZoomIndexDelta)
{
    var iCurrZoom = selectZoom.selectedIndex;
    var iMaxNumericZoom = selectZoom.options.length-1-NUM_ABSTRACT_ZOOMS; // specials zooms follow numeric zooms

    if (iCurrZoom > iMaxNumericZoom)
    {
        var fnRemapBounder = null;

        // we're on a special zoom setting and now need to
        // remap back into numeric zoom mode

        if (nZoomIndexDelta == 1)
        {
            // we're going to be moving down the list
            // so the index returned by NFSZ needs
            // to be the vertically highest index
            // which is the min index

            fnRemapBounder = Math.min;
        }
        else
        {
            // moving up the list, need vertically lowest index
            fnRemapBounder = Math.max;
        }

        iCurrZoom = NumericFromSpecialZoom(fnRemapBounder);
    }

    // move up or down the selection list as indicated by nZoomIndexDelta
    selectZoom.selectedIndex = Math.min(Math.max(0, iCurrZoom + nZoomIndexDelta), iMaxNumericZoom);

    ChangeZoom(parseInt(selectZoom.options[selectZoom.selectedIndex].value));
}

function HandleDynamicZoom()
{
    var nZoomType = parseInt(selectZoom.options[selectZoom.selectedIndex].value);

    if (nZoomType < 0)
    {
        var nZoomFactor = 100;

        var xPageWidth = getPageWidth();

        switch (nZoomType)
        {
            // page width
            case ZOOM_PAGE_WIDTH:
                nZoomFactor = Math.floor(((OverflowContainer.offsetWidth - 20) * 100) / xPageWidth);
                break;

            // whole page
            case ZOOM_WHOLE_PAGE:
                var xZoom = Math.floor(((OverflowContainer.offsetWidth - 20) * 100) / xPageWidth);
                var yZoom = Math.floor(((OverflowContainer.offsetHeight - 20) * 100) / getPageHeight());
                nZoomFactor = Math.min(xZoom, yZoom);
                break;

            // two pages
            case ZOOM_TWO_PAGES:
                nZoomFactor = Math.floor(((OverflowContainer.offsetWidth - 30) * 100) / (2 * xPageWidth));
                break;

            // eh?
            default:
                nZoomFactor = 100;
                break;
        }

        ChangeZoom(nZoomFactor);
    }
}

function HandleZoomSelect()
{
    var nZoomFactor = parseInt(selectZoom.options[selectZoom.selectedIndex].value);

    if (nZoomFactor < 0)
    {
        HandleDynamicZoom();
    }
    else
    {
        ChangeZoom(nZoomFactor);
    }

    // The selects require a user to be able to arrow between values, selecting each in succession.
    // As such, we cannot refocus the master container here.
}

function HandleFramesetSelect()
{
    ChangeFramesetLayout(parseInt(selectFrameset.options[selectFrameset.selectedIndex].value), false);

    // The selects require a user to be able to arrow between values, selecting each in succession.
    // As such, we cannot refocus the master container here.    
}

function HandleZoomInButton()
{
    HandleZoom(-1);

    // Force focus back to the preview document so keyboard
    // accelerators work as expected
    MasterContainer.focus();
}

function HandleZoomOutButton()
{
    HandleZoom(1);

    // Force focus back to the preview document so keyboard
    // accelerators work as expected
    MasterContainer.focus();
}

function HandleInputKeyPress()
{
    var keyStroke = event.keyCode;

    if (keyStroke == 13)    // Enter
    {
        event.srcElement.onchange();   
    }
    else if (keyStroke < 48 || keyStroke > 57)
    {
        // only allow numeric input
        event.returnValue = false;
    }
}

function Close()
{
    // (greglett) The point of this is to give the host an update on the page# we're
    // printing.  Since it's slow, and noone uses it now anyway except as a boolean,
    // we just use it as an on/off for the printer status icon.
    Printer.updatePageStatus(-1);   // Display the printing icon in the browser        

    // HACKHACK (greglett) See defnition of this variable above for details.
    if (g_fDelayClose)
    {
        g_fDelayClose = false;
        window.setTimeout("Close()", 120000);  // Allow 2 minutes
        return;
    }

#ifdef DEBUG
    if (!g_fReallyClose)
        return;
#endif

    window.close();
}

//----------------------------------------------------------------------
//  Function PrintAll()
//
//  Synopsis:  This function ensures that all settings are current,
//             creates all linked documents (if the user has specified that
//             print option), and asks each CPrintDoc to print itself.
//----------------------------------------------------------------------
function PrintAll()
{
    var i;
    var nFirstDoc;

    //  (greglett) If we're printing frames in any way other than as shown, we should
    //  wait on all the little frameset documents to load.  Consider a better way?
    if (    g_nFramesLeft > 0
        &&  Printer.framesetDocument
        &&  !Printer.frameAsShown   )
    {
        // The OnBuildFrames timeout should be smaller than this one's - since this is gating on a variable that
        // only changes when OnBuildFrames has had a chance to spin.
        window.setTimeout("PrintAll()", 100);    // Poll every tenth of a second.
        return;
    }

    EnsureDocuments();    

    // Nothing to do if we have no copies
    if (Printer.copies <= 0)
    {
        Close();    
    }
    // Some OS's don't force this restriction in the Print dialog.  We do it posthumously.
    else if (   Printer.selectedPages
             && Printer.pageFrom > Printer.pageTo )
    {   
        var L_PAGERANGE_ErrorMessage = "The 'From' value cannot be greater than the 'To' value.";
        alert(L_PAGERANGE_ErrorMessage);

        // A "Preview" print should just exit the function.  The user can click print again.
        // A "NoPrompt" print should never be here.
        AssertSz(dialogArguments.__IE_PrintType != "NoPrompt", "From > To with no possible UI?");
        
        // A "Prompt" print will leak the template if we just let it leave the function!
        // Throw the dialog again.
        if (!g_fPreview)
            PrintNow(true);
    }
    else
    {
        // How do we know when to close the window (all docs done printing)?
        // Right now, we have each doc decrement the g_cLeftToPrint until
        // it hits zero; the last one then closes the window.    
        g_cLeftToPrint  = 1;
        
        // (greglett) The point of this is to give the host an update on the page# we're
        // printing.  Since it's slow, and noone uses it now anyway except as a boolean,
        // we just use it as an on/off for the printer status icon.
        Printer.updatePageStatus(1);    // Display the printing icon in the browser
        
        PrintSentinel(((Printer.selection)
                        ? "S"
                        : (Printer.frameActive && !!g_strActiveFrame)
                            ? g_strActiveFrame
                            : "C"),
                        true);
    }    
}

// No syncrohization mechanism exists in JScript except for brute force.  Instead of pure busy waiting,
// let's at least sleep for fixed time periods.
function PrintSentinel( strDoc, fRecursionOK )
{
    // Gate on the document being ready to print.
    if (    !g_aDocTree[strDoc]
        ||  !g_aDocTree[strDoc].ReadyToPrint()  )
    {
        window.setTimeout("PrintSentinel('" + strDoc + "'," + fRecursionOK + ");", 500);
        return;
    }    

    g_aDocTree[strDoc].Print(fRecursionOK);
}

function PrintDocumentComplete()
{
    g_cLeftToPrint--;

    if (g_cLeftToPrint <= 0)
    {
        Close();   
    }    
}


//----------------------------------------------------------------------
//    PrintNow()
//      We've either been called without preview or from the preview
//      helper function, HandlePrintClick().  In either case, just do
//      that printing thing now.  In preview situations, the user should
//      always get a print dialog and the template should NOT close
//      if the user clicks 'Cancel' in the dialog.  In straight-print
//      situations with a print dialog, a 'Cancel' selection will close
//      the template.
//----------------------------------------------------------------------
function PrintNow( fWithPrompt)
{

    // We have to gate on the loading of the original document right now for
    // frameset information.
    if (    !g_aDocTree["C"]
        ||  !g_aDocTree["C"]._aaRect[LOADING_CONTENT][0]
        ||  !g_aDocTree["C"]._aaRect[LOADING_CONTENT][0].contentDocument.body)   // 89002 - may not exist yet.
    {
        window.setTimeout("PrintNow('" + fWithPrompt + "');", 100);    // Poll every tenth of a second.
        return;
    }       

    var oDoc = g_aDocTree["C"]._aaRect[LOADING_CONTENT][0].contentDocument;
    var fConfirmed;
    var fFramesetDocument    = (oDoc.body.tagName.toUpperCase() == "FRAMESET");
    // fActiveFrame includes:
    // FRAMESET documents (which will have a g_strActiveFrame)
    // IFRAME documents (which will not!)
    var fActiveFrame         = (oDoc.all.tags("HTML").item(0).__IE_ActiveFrame != null);

    Printer.framesetDocument = fFramesetDocument;
    Printer.frameActiveEnabled  = fActiveFrame;

    if (g_fPreview)
    {
        Printer.frameActive         = (g_nFramesetLayout == FRAMESET_SELECTED);
        Printer.frameAsShown        = (g_nFramesetLayout == FRAMESET_LAID_OUT);
        Printer.currentPageAvail    = true;
    }    
    else
    {
        Printer.frameActive         = fActiveFrame;
        Printer.frameAsShown        = false;
        Printer.currentPageAvail    = false;
    }

    // The selection document may not be created yet.  Directly check the dialogArguments.
    Printer.selectionEnabled = !!(dialogArguments.__IE_ContentSelectionUrl);
    Printer.selection        = false;       // Never default to selection.

#ifdef DEBUG
    Log("ShowingPrintDialog " + eval(fWithPrompt));
#endif

    fConfirmed = (eval(fWithPrompt)) ? Printer.showPrintDialog() : Printer.ensurePrintDialogDefaults();

    if ( fConfirmed )
    {
        if (    Printer.selection
            &&  dialogArguments.__IE_ContentSelectionUrl )  // (greglett) Check this too, in case the dlg was mishandled
        {
            // NB: (greglett)
            // We always loaded the selection document in IE55
            CreateDocument(dialogArguments.__IE_ContentSelectionUrl, "S", true);
        }

#ifdef DEBUG
    Log("Printer: Copies:" + Printer.deviceSupports("copies") + " Collate:" + Printer.deviceSupports("collate"));
#endif

        Printer.usePrinterCopyCollate =    (    Printer.deviceSupports("copies") >= Printer.copies
                                            &&  (   !Printer.collate                                            
                                                ||  Printer.deviceSupports("collate") ));
        
        PrintAll();           
    }
    else
    {
        if ( !g_fPreview )
        {
            Close();
        }
        else
        {
            // TODO (greglett) (104826 - but really task level work)
            // We may theoretically have changes even with a Cancel for two reasons:
            //  1.  In NT5, the Apply button may have been clicked, followed by Cancel.
            //      Right now, this doesn't work because the DHUI doesn't deal with this correctly.
            //  2.  The user may be printing to file, have clicked OK, then Cancel on the
            //      file query dialog.  Right now, this doesn't work in CTemplatePrinter.
            //      Do we want this to work like this?
            // The template is not currently robust enough to survive this, even if it were to
            // work correctly.  TableOfLinks has problems, as does AllLinkedDocs.
            Printer.tableoflinks = false;
        }
    }
}  

//----------------------------------------------------------------------
//    HandlePrintClick()
//      this is just a helper fucntion so that we close the window properly
//      even when we are using this template to print with preview.
//----------------------------------------------------------------------
function HandlePrintClick()
{    
    // Call the PrintNow function, but indicate that we always want a
    // print dialog and never want to close preview on completion.
    PrintNow(true, true);
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
//  CLASS CPrintDoc FUNCTIONS BEGIN HERE
//----------------------------------------------------------------------
//----------------------------------------------------------------------

//----------------------------------------------------------------------
//  Member    CPrintDoc::ReadyToPrint
//
//  Synopsis: Is the print doc in a state where we can call print?
//----------------------------------------------------------------------
function CPrintDoc_ReadyToPrint()
{
    if (    this._nStatus == READY_TO_PRINT
        &&  this._aaRect[LOADING_CONTENT][0].contentDocument.readyState == "complete")
    {
        return true;
    }

    return false;    
}

//----------------------------------------------------------------------
//  Member    CPrintDoc::Print
//
//  Synopsis: Prints the document, taking into acount # of copies, a page
//            range, and collation.
//----------------------------------------------------------------------
function CPrintDoc_Print( fRecursionOK )
{    
    if (!this.ReadyToPrint())
    {
        // Don't localize this debug string
        HandleError("Printing when not ready!", document.URL, "CPrintDoc::Print");
        return;
    }       

    var nCount = (Printer.usePrinterCopyCollate) ? 1 : Printer.copies;
    var nFrom;
    var nTo;

    AssertSz(nCount > 0, "Printing 0 or less copies");  // We should have already checked this.

    // Attempt to recurse if necessary (activeFrame, allLinkedDocuments, &c...)
    if (fRecursionOK)
    {
        var fFrameset = (this._aaRect[LOADING_CONTENT][0].contentDocument.body.tagName.toUpperCase() == "FRAMESET");
        
        if (Printer.frameActive)        // Print active frame
        {
            var n = parseInt(this._aaRect[LOADING_CONTENT][0].contentDocument.all.tags("HTML").item(0).__IE_ActiveFrame);
            if (n >= 0)
            {
                var oTargetFrame = (fFrameset)
                        ? this._aaRect[LOADING_CONTENT][0].contentDocument.all.tags("FRAME").item(n)
                        : this._aaRect[LOADING_CONTENT][0].contentDocument.all.tags("IFRAME").item(n);

                // Load active frame                
                if (    IsPersistedDoc()
                   &&   (   oTargetFrame.src == "res://SHDOCLC.DLL/printnof.htm"    // FRAME
                        ||  oTargetFrame.src == "about:blank"   ))                  // IFRAME
                {
                    // Immediate subframe is a WebOC hosted document.  Use special
                    // interface to print it (Trident couldn't marshall it due to external bugs)
                    Printer.printNonNativeFrames(this._aaRect[LOADING_CONTENT][0].contentDocument, true);   // true for ActiveFrame
                }
                else
                {
                    // Immediate subframe is an HTML document.  Recurse.
                    this.CreateSubDocument(oTargetFrame.src, true);
                    this.PrintAllSubDocuments(true);
                }

                // Finish printing this document...
                PrintDocumentComplete();
                return;
            }
        }
        
        if (fFrameset)
        {
            if (!Printer.frameAsShown) // Print all frames
            {
                this.PrintAllSubDocuments(true);

                if (IsPersistedDoc())
                {
                    // Print all Word document frames, too
                    Printer.printNonNativeFrames(this._aaRect[LOADING_CONTENT][0].contentDocument, false);  // false for all frames
                }

                // Finish printing this document...
                PrintDocumentComplete();
                return;
            }
            else
            {
                // Print all Word document frames in addition to the as laid out
                Printer.printNonNativeFrames(this._aaRect[LOADING_CONTENT][0].contentDocument, false);  // false for all frames
            }
        }            
        else        // BODY document.
        {
            // Build all linked documents, if necessary.
            if (Printer.allLinkedDocuments)
            {
                this.BuildAllLinkedDocuments();
                this.PrintAllSubDocuments(false);
            }
        }                
    }

    // We may be a WebOC document.  If we are, and it prints itself, we're done.
    if (Printer.printNonNative(this._aaRect[LOADING_CONTENT][0].contentDocument) )
    {
        // HACKHACK (greglett) See defnition of this variable above for details.
        g_fDelayClose = !g_fPreview;
        PrintDocumentComplete();
        return;
    }   

    // Clip page range to this document/subdocument.
    // NB (greglett)
    // Selected Page/CurrentPage don't work as expected with AllLinkedDocuments.
    // (We don't print only the linked documents on the given page)
    // It can't unless we can get a collection with only with particular pages on it.
    // NB (greglett)
    // If asked to print a table of links and a page range, do we:
    //  1.  Always print a table of links (for the whole document).
    //  2.  Print whole TOL if the page range and the TOL range is nonintersecting
    //      or only the intersection if the range intersects.
    //  3.  Print only the part of the TOL that intersects the page range.
    // Remember, NT5+ has an apply button and should be able to preview the TOL (though
    // they can't yet, because we don't support Apply).
    // We currently take the path of least resistance: #3.
    if (Printer.selectedPages)
    {
        nFrom   = Printer.pageFrom;
        nTo     = Printer.pageTo;

        if (nFrom < 1)
            nFrom = 1;
        if (nTo > this.Pages())
            nTo = this.Pages();
    }    
    // CurrentPage only applies to the document being previewed.  Any subdocuments that are
    // printed should print in their entirity.
    else if (   Printer.currentPage
             && this._strDoc == DisplayDocument())
    {
        nFrom = (this.Pages() >= g_nDispPage) ? g_nDispPage : this.Pages();
        nTo = nFrom;
    }
    else
    {
        // Normal print job - print the whole kit n' kabootle
        nFrom = 1;
        nTo   = this.Pages();
    }

    // In this case, we've been asked to print a valid range, but the document does not have
    // any pages in the range. (ex: 2 to 3 of a 1 page document - nFrom = 2, nTo = 1)        
    // (We really only need to do this in selected pages above.)
    if (nTo < nFrom)
    {
        PrintDocumentComplete();
        return;
    }

    AssertSz(nFrom <= nTo, "pageTo is smaller than pageFrom!");
    AssertSz(nFrom > 0, "pageFrom value 0 or less!");
    AssertSz(nTo <= this.Pages(), "pageTo greater than number of pages in document!");    


    
    // The display URL goes onto the system spooler as a job title.
    if (Printer.startDoc(this._aaRect[LOADING_CONTENT][0].contentDocument.URL))
    {    
        if (Printer.collate)
        {
            // (greglett) (10084)
            // Printing collated, multiple documents, we need to ensure the document has an even # of pages;
            // This may require an extra page.
            // [1|2] [3| ] [1|2] [3| ]
            var fExtraPage = (      Printer.duplex
                              &&    ((nFrom - nTo) % 2 == 0) );

            for (j = 0; j < nCount; j++)
            {                   
                for (i = nFrom; i <= nTo; i++)
                {
                    Printer.printPage(this.Page(i).children[0]);
                }

                if (fExtraPage)
                    Printer.printBlankPage();                
            }            
        }
        else
        {
            // (greglett) (10084)
            // If we are printing duplex, multiple documents, we need to print pairs of pages.
            // We print front-to-back, producing the output (for a 3 page, 2 copies):
            // [1|2] [1|2] [3| ] [3| ]...
            var fDuplex    = Printer.duplex;
            
            for (i = nFrom; i <= nTo; i++)
            {
                for (j = 0; j < nCount; j++)
                {
                    Printer.printPage(this.Page(i).children[0]);
                    if (fDuplex)
                    {
                        if (i < nTo)
                            Printer.printPage(this.Page(i+1));
                        else
                            Printer.printBlankPage();
                    }
                } 

                if (fDuplex)
                    i++;
            }
         }

        Printer.stopDoc();
    }            
    
    //  All done.
    PrintDocumentComplete();
}

//----------------------------------------------------------------------
//  Member    CPrintDoc::RectComplete
//
//  Synopsis: Called whenever an onlayout complete fires for this document's
//            layout rects.  Requires a "callback" function OnRectComplete,
//            above.
//            This function determines when a document is complete, and adds
//            a table of links/headers/footers if necessary.
//----------------------------------------------------------------------
function CPrintDoc_RectComplete(fOverflow, strElement)
{
    var nStatus = parseInt(strElement.substr(5,1));
    var nPage   = parseInt(strElement.substr(strElement.lastIndexOf("p") + 1));

    AssertSz((nStatus >= LOADING_OEHEADER) && (nStatus <= LOADING_TABLEOFLINKS), "Status within recognized bounds");
    AssertSz(nPage < this._aaRect[nStatus].length, "Page # not created!");

    // Ignore premature event calls.  We'll deal with them when we're good and ready.
    if (nStatus > this._nStatus)
        return false;

    // Ignore event calls from pages before this one on the sheaf
    if (nPage != this._acPage[nStatus] - 1 + this._anMerge[nStatus])
        return false;

    // If this is from a previous document within this one...
    if (nStatus != this._nStatus)
    {
        // ...and it's just telling us again that we're done, ignore it...
        if (!fOverflow)
            return false;

        // ...but if there's new content, dynamically switch back into this pagination.
        this.StopFixupHF();                                         // Stop H/F fixup - we need to do it again.
        if (this._nStatus == READY_TO_PRINT)                        // Add ourselves back into the queue of repaginating docs
            g_nDocsToCalc++;
        Transition(nStatus, "Status backslide - RectComplete");     // Backslide status.
    }

    // Need another page...
    if (fOverflow)
    {
        this.AddPage();
    }
    else
    {
        // No more pages needed in this phase of document calculation.
        // Move into the next calculation phase.
        switch (this._nStatus)
        {
            case LOADING_OEHEADER:
                // Stream header has finished - begin content calculation.
                Transition(LOADING_CONTENT, "RectComplete");
                this.AddFirstPage();
                this._aaRect[this._nStatus][0].contentSrc = this._strDocURL;
                break;

            case LOADING_CONTENT:
                // Content has finished calcing - create a table of links if requested.
                Transition(LOADING_TABLEOFLINKS, "RectComplete");
                this.AddTableOfLinks();
                break;
            
            case LOADING_TABLEOFLINKS:
                // All done with pagination.  Headers and Footers, please.
                Transition(PAGING_COMPLETE, "RectComplete");               
                if (this._strDoc == DisplayDocument())
                    ChangeDispPage(g_nDispPage);       // Clip to the maximum page, in case we are reflowing & have fewer pages.
                this.FixupHF();
                break;
        }
    }

    // Update the display to reflect the correct # of pages.
    if (this._strDoc == DisplayDocument())
    {
        spanPageTotal.innerText = this.Pages();

        // make sure the nav buttons are in an appropriate state
        updateNavButtons();
    }
}
//----------------------------------------------------------------------
//  Member    CPrintDoc::AddPage
//
//  Synopsis: Adds a page to the end of the current document.
//----------------------------------------------------------------------
function CPrintDoc_AddPage()
{
    var newHTM  = "";
    var aPage   = this._aaPage[this._nStatus];
    var aRect   = this._aaRect[this._nStatus];

    AssertSz(this._nStatus < PAGING_COMPLETE, "Adding page when pagination done!");

    // Increment page count in this section of the print document
    (this._acPage[this._nStatus])++;    

    HeadFoot.URL        = this.EnsureURL();
    HeadFoot.title      = this.EnsureTitle();
    HeadFoot.pageTotal  = this.Pages();
    HeadFoot.page       = HeadFoot.pageTotal;

    // We conserve layout rects - if this page has already been created, use it.
    if (this._acPage[this._nStatus] <= aPage.length)
    {
        // Reset H/F - it may have changed.
        var oPage = aPage[this._acPage[this._nStatus] - 1];

        oPage.children("header").innerHTML = HeadFoot.HtmlHead;
        oPage.children("footer").innerHTML = HeadFoot.HtmlFoot;
    }
    else
    {

        // TODO: (greglett) (108939) We have to parse in the nextRect right now because it is
        // the CGenericElement::Notify (ENTERTREE) that hooks up the LayoutRect.  Changing
        // this value *after* the EnterTree has no effect.
        // NB (greglett)
        // Q: Why wrap the DeviceRect in a DIV instead of just using the DeviceRect?
        // A: Because the OM on the DeviceRect is messed up such that it does do a resolution change scale.
        //    Instead, it returns CSS pixels *within* the rect.  Because we also screw up sizing the rect, this
        //    turns out to work for a 96DPI screen.  However, for 120DPI (large fonts), we off by 1.25 (120/96).
        //    Since the DIV has no resolution issues, we don't have the problem when we wrap the DeviceRect in a DIV.
        newHTM  = "<DIV class=divPage><IE:DeviceRect media=\"print\" class=page id=mDiv"   + this._nStatus + this._strDoc + "p" + aPage.length + ">";
        newHTM += "<IE:LAYOUTRECT id=mRect"             + this._nStatus + this._strDoc + "p" + aRect.length;
        newHTM += " class=mRect nextRect=mRect"         + this._nStatus + this._strDoc + "p" + (aRect.length + 1);
        newHTM += " onlayoutcomplete=OnRectComplete('" + this._strDoc +  "')";
        newHTM += " tabindex=-1  onbeforefocusenter='event.returnValue=false;' ";
        newHTM +=  " /><DIV class=divHead id=header>";
        newHTM += HeadFoot.HtmlHead;
        newHTM += "</DIV><DIV class=divFoot id=footer>";
        newHTM += HeadFoot.HtmlFoot;
        newHTM += " </DIV></IE:DeviceRect></DIV>";

        MasterContainer.insertAdjacentHTML("beforeEnd", newHTM);

        aPage[aPage.length] = eval( "document.all.mDiv" + this._nStatus + this._strDoc + "p" + aPage.length);
        aRect[aRect.length] = eval("document.all.mRect" + this._nStatus + this._strDoc + "p" + aRect.length);
    }
}

//----------------------------------------------------------------------
//  Member    CPrintDoc::AddFirstPage
//
//  Synopsis: Adds a page to the end of the current document.
//----------------------------------------------------------------------
function CPrintDoc_AddFirstPage()
{
    this._acPage[this._nStatus] = 0;
    
    if (this._anMerge[this._nStatus] == 0)
    {
        this.AddPage();
    }
    else
    {
        var aRect   = this._aaRect[this._nStatus];
        var oPage   = this._aaPage[this._nStatus - 1][this._acPage[this._nStatus - 1] - 1];
        var oTop    = this._aaRect[this._nStatus - 1][this._acPage[this._nStatus - 1] + this._anMerge[this._nStatus - 1] - 1];
        var nTop    = oTop.offsetTop + oTop.scrollHeight;
        var nHeight = oTop.clientHeight - oTop.scrollHeight;

        AssertSz(this._nStatus < PAGING_COMPLETE,  "Merging page when pagination done!");
        AssertSz(this._anMerge[this._nStatus] > 0, "Merging page when not requested!");

        // We cannot dynamically get a stream header.  Ergo, if we have content layout rects,
        // we must be repaginating, and already have a content layout rect on a stream page.
        // We (may) simply need to resize and reparent it.
        if (aRect.length > 0)
        {
            var oNode       = aRect[0];

            oNode.style.marginTop     = nTop    + "px";
            oNode.style.pixelHeight   = nHeight;
            
            // Reparent, if necessary.
            if (oNode.parentElement != oPage)
                oPage.insertBefore(oNode);
        }
        // We need to actually size & create the content layout rect to merge onto the stream page.
        else
        {
            var newHTM;
            var oNode;
            
            // TODO: (greglett) (108939) We have to parse in the nextRect right now because it is
            // the CGenericElement::Notify (ENTERTREE) that hooks up the LayoutRect.  Changing
            // this value *after* the EnterTree has no effect.
            newHTM  = "<IE:LAYOUTRECT id=mRect"     + this._nStatus + this._strDoc + "p0";
            newHTM += " class=mRect nextRect=mRect" + this._nStatus + this._strDoc + "p1";
            newHTM += " onlayoutcomplete=OnRectComplete('" + this._strDoc +  "')";
            newHTM += " tabindex=-1 onbeforefocus='event.returnValue=false;' />";

            oPage.insertAdjacentHTML("beforeEnd", newHTM);

            oNode = eval("document.all.mRect" + this._nStatus + this._strDoc + "p0");

            aRect[0] = oNode;
            
            oNode.style.marginTop   = nTop    + "px";
            oNode.style.height      = nHeight + "px";        
        }
    }
}

//----------------------------------------------------------------------
//  Member    CPrintDoc::InitDocument
//
//  Synopsis: Begins pagination of document.  This could be merged with
//            the constructor.
//----------------------------------------------------------------------
function CPrintDoc_InitDocument( fUseStreamHeader )
{
    var fReallyUseStreamHeader = (fUseStreamHeader && (dialogArguments.__IE_OutlookHeader != null));
    this._anMerge[LOADING_CONTENT] = (fReallyUseStreamHeader) ? 1 : 0;
    
    Transition((fReallyUseStreamHeader) ? LOADING_OEHEADER : LOADING_CONTENT, "Initialize Status");
    
    this.AddFirstPage();

    this._aaRect[this._nStatus][0].contentSrc = (fReallyUseStreamHeader)
                                                ? dialogArguments.__IE_OutlookHeader
                                                : this._strDocURL;
}

function CPrintDoc_PrintAllSubDocuments( fRecursionOK )
{
    if (!this._aDoc)
        return;
        
    var nDocs = this._aDoc.length;
    var i;

    g_cLeftToPrint += nDocs;
    
    for (i = 0; i < nDocs; i++)
    {        
        PrintSentinel(this._aDoc[i]._strDoc, fRecursionOK);
    }                    
}

function CPrintDoc_BuildAllLinkedDocuments()
{
    var strURL = this._aaRect[LOADING_CONTENT][0].contentDocument.URL;
    var aLinks = this._aaRect[LOADING_CONTENT][0].contentDocument.links;
    var nLinks = aLinks.length;
    var i;
    var j;
    var strLink;
    
    for (i = 0; i < nLinks; i++)
    {        
        strLink = aLinks[i].href;

        // Check against our own URL.
        // Check to make sure we are not opening a bad URL
        if (    (strURL == strLink)
            ||  UnprintableURL(strLink) )
            continue;   
            
        // Check for a duplicate in the list.  Yes, this is n^2... as in IE4+
        for (j = 0; j < i; j++)
        {
            if (strLink == aLinks[j].href)
                break;
        }
        if (j < i)      // Duplicate found.
            continue;

        this.CreateSubDocument(strLink, false);
    }
}

function OnBuildAllFrames( strDoc )
{
    // We have to gate on the loading of the original document right now for
    // frameset information.
    if (    !g_aDocTree[strDoc]
        ||  !g_aDocTree[strDoc]._aaRect[LOADING_CONTENT][0]
        ||  !g_aDocTree[strDoc]._aaRect[LOADING_CONTENT][0].contentDocument.body)   // 89002 - may not exist yet.
    {
        window.setTimeout("OnBuildAllFrames('" + strDoc + "');", 100);
        return;
    }

    g_aDocTree[strDoc].BuildAllFrames();
}

function IsPersistedDoc()
{
    // Since this is only called while printing, we can be sure the following exists...
    return (!!g_aDocTree["C"]._aaRect[LOADING_CONTENT][0].contentDocument.all.tags("HTML")[0].__IE_DisplayURL);
}


function CPrintDoc_BuildAllFrames()
{
    var aFrames = this._aaRect[LOADING_CONTENT][0].contentDocument.all.tags("FRAME");
    var nFrames = aFrames.length;
    var nActive = parseInt(this._aaRect[LOADING_CONTENT][0].contentDocument.all.tags("HTML").item(0).__IE_ActiveFrame);
    var i;
    var strSrc;
    var strDoc;    

    if (nFrames > 0)
        this._fFrameset = true;

    for (i = 0; i < nFrames; i++)
    {
        strSrc = aFrames[i].src;

        // This URL was created by Trident replacing a WebOC frame that it couldn't marshall.
        // These will be printed via a call to "Printer.printNonNativeFrames(false)" in the caller.
        if (strSrc == "res://SHDOCLC.DLL/printnof.htm")
            continue;
        
        // Make a document for the new URL.
        // We can't use the markup because it's already slaved (to the frame)
        strDoc = this.CreateSubDocument(strSrc, true);
        if (i == nActive)
            g_strActiveFrame = strDoc;

        // Increment the semaphore, indicating that we're building a deeper branch of frames
        // (see OnLoadBody() for more info)
        g_nFramesLeft++;
        
        // And then recurse to build its children frames
        OnBuildAllFrames(strDoc);
    }
    
    // Decrement the semaphore, indicating that we've finished building this branch
    // (see OnLoadBody() for more info)
    g_nFramesLeft--;  
    if (g_nFramesLeft <= 0)
        BuildAllFramesComplete();    
}

function CPrintDoc_CreateSubDocument( docURL, fUseStreamHeader )
{
    if (!this._aDoc)
        this._aDoc = new Array();
        
    var nDoc   = this._aDoc.length;
    var strDoc = this._strDoc + "_" + nDoc;

    CreateDocument(docURL, strDoc, fUseStreamHeader);
    this._aDoc[nDoc] = g_aDocTree[strDoc];

    return (strDoc);
}

//----------------------------------------------------------------------
//  Member    CPrintDoc::AddTableOfLinks
//
//  Synopsis: Appends a table of links to the end of the current document
//            (this uses the documents current page value to determine the
//             "end" - do not call if this number is not determined).
//----------------------------------------------------------------------
function CPrintDoc_AddTableOfLinks()
{
    AssertSz(this._nStatus == LOADING_TABLEOFLINKS, "Call to AddTableOfLinks when not ready!");
    
    // No table of links requested.  Transition to header/footer work.
    if (!g_fTableOfLinks)
    {
        Transition(PAGING_COMPLETE, "AddTableOfLinks, no table of links requested");
        ChangeDispPage(g_nDispPage);       // Clip to the maximum page, in case we are reflowing & have fewer pages.
        this.FixupHF();
    }
    else
    {                    
        var oTableOfLinks = BuildTableOfLinks(this._aaRect[LOADING_CONTENT][0].contentDocument);
        if (oTableOfLinks != null)
        {
            // NB (greglett)
            // The markup for the oTableOfLinks document becomes null across the
            // AddFirstPage below.  The issue is timing related.  A workaround is to store the body.
            var oBody = oTableOfLinks.body;
            
            this.AddFirstPage()
            this._aaRect[this._nStatus][0].contentSrc = oBody.document;            
        }
        else
        {
            // Table of links does not exist.  Move straight to the HeadersAndFooters thing.
            Transition(PAGING_COMPLETE, "AddTableOfLinks, no table");
            ChangeDispPage(g_nDispPage);       // Clip to the maximum page, in case we are reflowing & have fewer pages.
            this.FixupHF();
        }        
    }
}


//----------------------------------------------------------------------
//  Member    CPrintDoc::FixupHF
//
//  Synopsis: Goes through all pages in the document, updating/creating
//            their header/footers.
//----------------------------------------------------------------------
function OnTickHF( strDoc )
{
    if (!g_aDocTree[strDoc])
    {
        // No need to localize the string - it is only displayed in debug mode.
        HandleError("Document " + strDoc + " does not exist.", document.URL, "OnRectComplete");
        return;
    }

    g_aDocTree[strDoc].TickHF();
}

// There are two magic numbers in this function - the n in the line iTo = nStartPage + n and the setTimeout delay.
// The first is directly proportional to the contiguous block of processor time we'll take at a time, but inversely proportional
// to the # of timeouts we'll have to set and the total amount of time required to do the job.
// The timeout delay is how long we allow Trident to update and process UI (button highlights, &c...).
function CPrintDoc_TickHF()
{
    var i, j;
    var iTo, jTo;
    var aTok;
    var oTok;
    var nStartPage  = this._nNextHF;
    var cPages      = this.Pages();

    iTo = nStartPage + 2;
    if (iTo > cPages)
        iTo = cPages;

    for (i = nStartPage; i <= iTo; i++)
    {
        // (greglett)  Why first child?  See comments in CPrintDoc_AddPage.
        aTok = this.Page(i).children[0].getElementsByTagName("SPAN");
        for (j=0, jTo = aTok.length; j < jTo; j++)
        {
            oTok = aTok[j];

            if (oTok.className == "hfPageTotal")
                oTok.innerText = cPages;

            else if (   oTok.className == "hfUrl"           
                     && oTok.innerText == ""      )
                oTok.innerText = this.EnsureURL();                                    
            else if (   oTok.className == "hfTitle"
                     && oTok.innerText == ""        )
                oTok.innerText = this.EnsureTitle();                                    
        }
    }

    // Update page # of next HF we need to fixup.
    this._nNextHF = i;

#ifdef DEBUG    
        // DEBUG ONLY - Track H/F fixup speed visually.
        spanDebugHF.innerText = this._nNextHF - 1;
#endif

    // If we're not done crowning & shoeing the document yet, we need to set a timeout to get back to it.
    // Timeout should be long enough to process UI.
    if (iTo == cPages)
    {
        Transition(READY_TO_PRINT, "AddHAndF");    
        if (--g_nDocsToCalc == 0)
            CalcDocsComplete();
    }
    else    
    {
        this._nTimerHF = window.setTimeout("OnTickHF('" + this._strDoc + "');", 250);
    }
}

//----------------------------------------------------------------------
//  Member    CPrintDoc::FixupHF
//
//  Synopsis: Kicks off the 
//            their header/footers.
//----------------------------------------------------------------------
function CPrintDoc_FixupHF()
{
    AssertSz(this._nStatus == PAGING_COMPLETE, "Call to FixupHF when not ready!");

#ifdef DEBUG
    // DEBUG ONLY - Track H/F fixup speed visually.
    spanDebugHF.innerText = this._nNextHF;
#endif

    //  Kick off the header/footer pass on the document.
    this.TickHF();
}

//----------------------------------------------------------------------
//  Member    CPrintDoc::Pages, Page
//
//  Synopsis: With multiple page arrays per CPrintDoc, these functions
//            are to provide the illusion of one array.  To make this
//            closer to user UI, this virtual array is 1 based, not 0 based.
//----------------------------------------------------------------------
function CPrintDoc_Pages()
{    
    var i;
    var c;

    for (i = 0, c = 0; i < PAGING_COMPLETE; i++)
    {
        c += this._acPage[i];
    }

    return c;
}
function CPrintDoc_Page(nPage)
{
    var i;
    var n = nPage;
    
    if (n <= 0)
        return null;        

    for (i = 0; i < PAGING_COMPLETE; i++)
    {
        // (greglett)  Why parentElement?  See comments in CPrintDoc_AddPage.
        if (n <= this._acPage[i])
            return this._aaPage[i][n - 1].parentElement;

        n -= this._acPage[i];
    }

    return null;
}

function CPrintDoc_EnsureURL()
{
    if (this._strURL == null)
    {
        if (   this._aaRect[LOADING_CONTENT][0]
            && this._aaRect[LOADING_CONTENT][0].contentDocument)
        {
            this._strURL = this._aaRect[LOADING_CONTENT][0].contentDocument.URL;
        }

        // We must return a string.
        if (this._strURL == null)
            return "";
    }

    return this._strURL;
}
function CPrintDoc_EnsureTitle()
{
    if (this._strTitle == null)
    {
        if (   this._aaRect[LOADING_CONTENT][0]
            && this._aaRect[LOADING_CONTENT][0].contentDocument)
        {
            this._strTitle = this._aaRect[LOADING_CONTENT][0].contentDocument.title;
        }

        // We must return a string.
        if (this._strTitle == null)
            return "";
    }

   return this._strTitle;
}

//----------------------------------------------------------------------
//  Member    CPrintDoc::ResetXXXX
//
//  Synopsis: If the given quantity (Document, TableOfLinks... &c...) has been
//            calculated, this will force it to be recalculated.  If not, the
//            functions essentially do nothing (because the quantity will be
//            created as the document finishes loading).
//----------------------------------------------------------------------
function CPrintDoc_ResetDocument()
{
    var i;
    
    for (i = 0; i < PAGING_COMPLETE; i++)
    {
        this._acPage[i] = 0;

        // HACKHACK (greglett) 94479
        // The viewchain is not capable of recalcing if the layout rect properties are changed right now.
        // So, we unhook the LR (HERE) change the properties, and then reload/reparse the entire document (LATER)
        if (this._aaRect[i][0])
            this._aaRect[i][0].contentSrc = "";
    }

    this.StopFixupHF();
}
function CPrintDoc_ResetTableOfLinks()
{
    //  A table of links will be generated as part of the document when we get to it.    
    if (this._nStatus <= LOADING_TABLEOFLINKS)
        return;

    this.StopFixupHF();
        
    Transition(LOADING_TABLEOFLINKS, "ResetTableOfLinks");
    this.AddTableOfLinks();
}

function CPrintDoc_StopFixupHF()
{
    if (this._nTimerHF != -1)
        window.clearTimeout(this._nTimerHF);
    this._nTimerHF = -1;
    this._nNextHF  =  1;  // Could be higher, to only reget the TOL pages.
}

//
// CPRINTDOC MEMBER FUNCTIONS
//

// This function is a constructor for the CPrintDoc Object.
// CPrintDoc contains per document information.
function CPrintDoc( nDocNum, strDocURL )
{
    var i;

    this._aDoc          = null;         // Array of frames/linked documents.
    this._strDoc        = nDocNum;      // Document id of this document (in form "C_2_3")
    this._strDocURL     = strDocURL;    // "Real" URL of document (for LayoutRect's contentSrc).

    // _nStatus stores the phase of pagination/flow completion that the document is in:
    // The values are defined in preview.h, and initialized in InitDocument
    this._nStatus       = 0;

    this._aaPage        = new Array(PAGING_COMPLETE);
    this._aaRect        = new Array(PAGING_COMPLETE);
    this._acPage        = new Array(PAGING_COMPLETE);
    this._anMerge       = new Array(PAGING_COMPLETE);
    for (i=0; i<PAGING_COMPLETE; i++)
    {
        this._aaPage[i]  = new Array();
        this._aaRect[i]  = new Array();
        this._acPage[i]  = 0;
        this._anMerge[i] = 0;
    }

    this._nNextHF       = 1;
    this._nTimerHF      = -1;

    this._strURL        = null;
    this._strTitle      = null;

    this._fFrameset     = false;
    this._nStartingPage = 0;
}

// Pagination functions
MEMBER(CPrintDoc, RectComplete);
MEMBER(CPrintDoc, AddPage);
MEMBER(CPrintDoc, AddFirstPage);
MEMBER(CPrintDoc, AddTableOfLinks);
MEMBER(CPrintDoc, FixupHF);

MEMBER(CPrintDoc, StopFixupHF);
MEMBER(CPrintDoc, TickHF);

// Document status functions
MEMBER(CPrintDoc, InitDocument);
MEMBER(CPrintDoc, ResetDocument);
MEMBER(CPrintDoc, ResetTableOfLinks);

// Recursive document functions
MEMBER(CPrintDoc, BuildAllLinkedDocuments);
MEMBER(CPrintDoc, BuildAllFrames);
MEMBER(CPrintDoc, CreateSubDocument);

// Printing functions
MEMBER(CPrintDoc, Print);
MEMBER(CPrintDoc, PrintAllSubDocuments);
MEMBER(CPrintDoc, ReadyToPrint);

// Data access functions
MEMBER(CPrintDoc, Page);
MEMBER(CPrintDoc, Pages);
MEMBER(CPrintDoc, EnsureURL);
MEMBER(CPrintDoc, EnsureTitle);
</SCRIPT>
</HEAD>

<BODY onload="setTimeout('OnLoadBody()', 400);">

<!-- Controls for printing  -->
<IE:TemplatePrinter id=Printer  />
<IE:HeaderFooter    id=HeadFoot />

<DIV id=idDivToolbar style="width:100%; overflow:hidden;">

    <DIV style="width=100%; border:'thin threedhighlight groove';">
    <TABLE><TR>
        <TD class="UIPane"> <BUTTON id="butPrint" title="Print Document (Alt+P)" accesskey=p><U>P</U>rint...</BUTTON></TD>
        <TD class="UISeparator"><IMG width=0 height=0></TD>
        <TD class="UIPane"> <BUTTON id="butPageSetup" accesskey=u><IMG id="printCtl" src="printctl.gif" alt="Page Setup (Alt+U)"></BUTTON></TD>
        <TD class="UISeparator"><IMG width=0 height=0></TD>
        <TD class="UIPane"> <BUTTON id="butFirstPage"><IMG id="begin"   src="begin_inactive.gif" alt="First Page (Alt+Home)"></BUTTON></TD>
        <TD class="UIPane"> <BUTTON id="butBackPage"> <IMG id="prev"    src="prev_inactive.gif"  alt="Previous Page (Alt+LeftArrow)"></BUTTON></TD>
        <TD class="UIPane"><SPAN style="color:windowtext;"><NOBR Loc>&nbsp;<ID id=idTdPageXofYLocText1>P<U>a</U>ge</ID>&nbsp;<INPUT type=text id="inputPageNum" title="Preview Page (Alt+A)" value="1" style="height:1.5em; width: 2em; color:windowtext;" accesskey=a><ID id=idTdPageXofYLocText2> of </ID><SPAN id="spanPageTotal"></SPAN>
#ifdef DEBUG
        <SPAN id="spanDebugHF" style="color: red"></SPAN>
#endif        
        &nbsp;</SPAN></NOBR></TD>
        <TD class="UIPane"> <BUTTON id="butNextPage"> <IMG id="next"    src="next_inactive.gif"           alt="Next Page (Alt+RightArrow)"></BUTTON></TD>
        <TD class="UIPane"> <BUTTON id="butLastPage"> <IMG id="end"     src="end_inactive.gif"            alt="Last Page (Alt+End)"></BUTTON></TD>
        <TD class="UISeparator"><IMG width=0 height=0></TD>
        <TD class="UIPane"> <BUTTON id="butZoomOut">  <IMG id="zoomOut" base="zoomOut"   src="zoomout.gif"    alt="Zoom Out (Alt+Minus)"></BUTTON></TD>
        <TD class="UIPane"> <BUTTON id="butZoomIn">   <IMG id="zoomIn"  base="zoomIn"    src="zoomin.gif"     alt="Zoom In (Alt+Plus)"></BUTTON></TD>
        <TD class="UIPane"> <SELECT id="selectZoom" accesskey=z>
                                <OPTION VALUE="500"             >500%
                                <OPTION VALUE="200"             >200%
                                <OPTION VALUE="150"             >150%
                                <OPTION VALUE="100"             >100%
                                <OPTION VALUE="75" SELECTED     >75%
                                <OPTION VALUE="50"              >50%
                                <OPTION VALUE="25"              >25%
                                <OPTION VALUE="10"              >10%
                                <!-- ID's are for localization  -->
                                <OPTION VALUE=STR_ZOOM_PAGE_WIDTH id="idPageWidth">Page Width</OPTION>
                                <OPTION VALUE=STR_ZOOM_WHOLE_PAGE id="idWholePage">Whole Page</OPTION>
                                <OPTION VALUE=STR_ZOOM_TWO_PAGES  id="idTwoPages" >Two Pages </OPTION>
                            </SELECT></TD>
        <TD class="UISeparator" id="separatorFrameset" style="display:none"><IMG width=0 height=0></TD>
        <TD class="UIPane" id="cellFrameset" style="display:none"> <SELECT id="selectFrameset" accesskey=f>
                                <OPTION VALUE=STR_FRAMESET_LAID_OUT id="idLaidOut">As laid out on screen</OPTION>
                                <OPTION VALUE=STR_FRAMESET_SELECTED id="idSelected">Only the selected frame</OPTION>
                                <OPTION VALUE=STR_FRAMESET_SEPARATE id="idSeparate">All frames individually</OPTION>
                            </SELECT></TD>
        <TD class="UISeparator"><IMG width=0 height=0></TD>
        <TD class="UIPane"> <BUTTON id="butHelp" title="Print Preview Help (Alt+H)" accesskey=h><U>H</U>elp</BUTTON></TD>
        <TD class="UISeparator"><IMG width=0 height=0></TD>
        <TD class="UIPane"> <BUTTON id="butClose" title="Close Print Preview (Alt+C)" accessKey=c><U>C</U>lose</BUTTON></TD>
    </TR></TABLE>
    </DIV>

</DIV>

<DIV id=OverflowContainer onclick="this.focus();" onfocus="butPrint.scrollIntoView();" tabindex=1 style="position:absolute; left:0; width:100%; overflow:auto; border:'thin threedhighlight inset'; background:threedshadow;">
    <DIV id=MasterContainer tabindex=0 style="width:100%; position:absolute;">
        <!-- Pages go here -->
    </DIV>

#ifdef DEBUG
<!-- Div used to log template events for debugging only.  No need to localize! -->
<DIV id=LogContainer style="display:none; position:absolute; background:C0FFC0; top:0px; left:0px; height:100%; width:100%; overflow-y:scroll; overflow-x:auto;"></DIV>
#endif
    
</DIV>

</BODY>
</HTML>