<!--

/*  *************************************************  */
/*  For inclusion in the HEAD of each "document" page  */
/*  *************************************************  */

/*  A "document" is an essentially arbitrary page in the web. It is written 
    as its own content but may be presented in some external context (such 
    as a frameset). 

    It cannot be stressed enough how important is the preceding design 
    requirement. Pages at this site must be prepare-able as naturally as 
    possible in an HTML editor for self-standing presentation, especially to 
    anticipate being browsed without running scripts. 

    The implementation here requires that every document page should have a 
    HEAD that loads two scripts and two stylesheets. The reason for having 
    two of each is that the first contains whatever is imagined might be 
    used in the second (or more). Even substantial changes to the scheme may 
    then be accommodated just in the scripts and stylesheets. 

    The presentation here loads each document into a frameset, to be seen in 
    a frame along with a banner and a table of contents (TOC) in other 
    frames.  */

/*  *************  */
/*  Configuration  */

/*  Frame and frameset properties - but note that the name and id properties 
    for each frame are defined in MASTER.JS  */

var sBannerFrameHeight = "120";         // must match BANNER.CSS 
var sTopFramesetBorder = "8";
var sTopFramesetBorderColor = "#DDDDEE";
var sTopFramesetFrameBorder = "1";
var sTopFramesetFrameSpacing = "8";

var sBannerFrameFrameBorder = "0";
var sBannerFrameNoResize = "1";
var sBannerFrameScrolling = "no";

var sTocFrameWidth = "25%";
var sTocFrameFrameBorder = "0";

var sDocFrameFrameBorder = "0";

/*  Images to decorate various types of link  */

/*  Note for future: if decoration is instead done by specifying the images 
    as backgrounds for the relevant tags, then the configuration will 
    instead be of class names.  */

var sExternalLinkImage = "/_images/external.gif";
var sEmailLinkImage = "/_images/email.gif";

/*  *******************  */
/*  Decoration of Links  */

/*  Follow the practice - established long ago by the MSDN, if not before, 
    and very widely adopted by now - of indicating graphically that a link 
    has some external effect such as leaving the site.  */

function AddImageToLink (Link, Image, Pathname)
{
    if (Image == null) {
        Image = window.document.createElement ("IMG");
        Image.src = new LocalUrl (Pathname);
    }
    var parent = Link.parentNode;
    parent.insertBefore (Image.cloneNode (false), Link);
    parent.insertBefore (window.document.createTextNode ("\xA0"), Link);
    return Image;
}

function DecorateLinks ()
{
    var extern = null;
    var email = null;

    var hostname = window.location.hostname;
    var links = window.document.getElementsByTagName ("A");
    var numlinks = links.length;
    for (var n = 0; n < numlinks; n ++) {
        var a = links [n];
        switch (a.protocol) {
            case "http:":
            case "https:": {
                if (a.hostname != hostname) {
                    extern = AddImageToLink (a, extern, sExternalLinkImage);

                    /*  Take the opportunity to configure the link so that 
                        the target, which knows nothing of our frameset, is 
                        loaded into a new window.  */

                    a.target = "_top";
                }
                break;
            }
            case "mailto:": {
                email = AddImageToLink (a, email, sEmailLinkImage);
                break;
            }
        }
    }
}

/*  **********************************************  */
/*  Tool tips for elements with "character styles"  */

/*  A general principle for all document pages is that any inline formatting 
    should show a tooltip to explain the formatting. 
    
    Of course, the sorts of things to be explained may vary from one subweb 
    to another. Some common handling is here, using three functions that may 
    be provided by scripts for each subweb: GetCharStyleToolTip, 
    GetCharStyleToolTipBase and GetCharStyleToolTipQualifier  */

function ComposeCharStyleToolTip (Span)
{
    var tip = null;

    /*  Allow a subweb's script to take complete control of deciding the 
        tooltip.  */

    if (typeof (GetCharStyleToolTip) == "function") {
        tip = GetCharStyleToolTip (Span);
        if (tip != null) return tip;
    }

    /*  In general, compose the tooltip from the SPAN element's classes.  */

    var cls = Span.className;
    if (cls == null) return null;

    var classes = cls.split (" ");
    var n = classes.length;
    if (n -- == 0) return null;

    cls = classes [n];
    if (cls == "") return null;

    /*  The last class provides the base - as is, unless the subweb's script 
        provides an override.  */

    var gotbasefunc = typeof (GetCharStyleToolTipBase) == "function";
    var gotqualfunc = typeof (GetCharStyleToolTipQualifier) == "function";

    if (gotbasefunc) tip = GetCharStyleToolTipBase (cls);
    if (tip == null && gotqualfunc) tip = GetCharStyleToolTipQualifier (cls);
    if (tip == null) tip = cls;

    /*  The base may be qualified by preceding classes, if the subweb's 
        script translates.  */

    while (n -- != 0) {
        cls = classes [n];
        var qual = gotqualfunc ? GetCharStyleToolTipQualifier (cls) : cls;
        if (qual != null) tip += ", " + qual;
    }
    return tip;
}

/*  An alternative to the following is to call the preceding for every SPAN 
    (that doesn't already have a title) when the document loads.  */

function CheckForCharStyleToolTip (Event)       // onmouseover
{
    Event = EnsureEvent (Event);
    for (var x = GetEventSource (Event); x != null; x = x.parentNode) {
        if (x.nodeName == "SPAN") {
            if (x.title == null || x.title == "") {
                var tip = ComposeCharStyleToolTip (x);
                if (tip != null) {
                    x.title = tip;
                    break;
                }
            }
        }
    }
}

/*  *******************  */
/*  Frameset Dimensions  */

function ParseTocFrameWidth (Args)
{
    var width = Args.Get (sTocWidthArgumentName);
    return width != null ? unescape (width) : sTocFrameWidth;
}

function ComposeTocFrameWidth (Args)
{
    var tocframe = window.top.document.getElementById (sTocFrame);
    if (tocframe == null) return;

    var widths = tocframe.parentNode.cols.split (",", 3);
    if (widths.length != 2) return;

    var width = widths [0];
    if (width == null || width == sTocFrameWidth) return;

    Args.Add (sTocWidthArgumentName, escape (width));
}

/*  ***********************************************************  */
/*  Frameset Construction (global initialisation in top window)  */

/*  Supporting object for a single property of a FRAMESET or FRAME node  */

function Attribute (Name, Value)
{
    this.Name = Name;
    this.Value = Value;
}

/*  A method that composes an attribute's name and value for use in an HTML 
    tag  */

Attribute.prototype.toString = function () 
{
    return this.Name.toLowerCase () + "=\"" + this.Value + "\"";
};

/*  Supporting object to define a FRAMESET or FRAME node  */

function Component (Tag, Attributes)
{
    this.Tag = Tag;
    this.Attributes = new Array ();
    for (var n = 1; n < arguments.length; n ++) {
        this.Attributes.push (arguments [n]);
    }
}

/*  A mthod that composes all the attributes into a full HTML tag  */

Component.prototype.toString = function ()
{
    var str = "<" + this.Tag;
    var numattrs = this.Attributes.length;
    for (var n = 0; n < numattrs; n ++) {
        str += " " + this.Attributes [n];
    }
    return str + ">";
};

/*  A method that creates the FRAMESET or FRAME node from the definition  */

Component.prototype.CreateElement = function ()
{
    /*  Some browsers - well, Internet Explorer - extend the standard for 
        DOM node creation by allowing the createElement method to take as 
        its argument a whole HTML tag, complete with attributes.  */

    var e;
    try {
        return window.document.createElement (this);
    }
    catch (e) {
    }

    /*  For browsers that don't offer the preceding convenience - perhaps 
        out of bloody-mindedness in the name of standards compliance - 
        make multiple calls to the browser, not just to create the node but 
        also to add the attributes one by one.  */

    var node;
    try {
        node = window.document.createElement (this.Tag);
    }
    catch (e) {
        return null;
    }
    var numattrs = this.Attributes.length;
    for (var n = 0; n < numattrs; n ++) {
        var attr = this.Attributes [n];
        node.setAttribute (attr.Name, attr.Value);
    }
    return node;
};

function CreateFrameset (Args)
{
    /*  Create all the FRAMESET and FRAME elements. If the browser doesn't 
        allow this, we'll want to abandon all scripting before we make a 
        mess.  */

    var x = new Component (
            "FRAMESET", 
            new Attribute ("rows", sBannerFrameHeight + ",*"), 
            new Attribute ("border", sTopFramesetBorder), 
            new Attribute ("borderColor", sTopFramesetBorderColor), 
            new Attribute ("frameBorder", sTopFramesetFrameBorder), 
            new Attribute ("frameSpacing", sTopFramesetFrameSpacing));
    var topframeset = x.CreateElement ();
    if (topframeset == null) return null;

    x = new Component (
            "FRAMESET", 
            new Attribute ("cols", ParseTocFrameWidth (Args) + ",*"));
    var midframeset = x.CreateElement ();
    if (midframeset == null) return null;

    /*  Each frame will receive content from some other file. Frames other 
        than the document frame can be set from the start to have the 
        corresponding file as their content.  */

    x = new Component (
            "FRAME", 
            new Attribute ("src", new LocalUrl (sBannerPathname)), 
            new Attribute ("id", sBannerFrame), 
            new Attribute ("name", sBannerFrame), 
            new Attribute ("frameBorder", sBannerFrameFrameBorder), 
            new Attribute ("noResize", sBannerFrameNoResize), 
            new Attribute ("scrolling", sBannerFrameScrolling));
    var bannerframe = x.CreateElement ();
    if (bannerframe == null) return null;

    x = new Component (
            "FRAME", 
            new Attribute ("src", new LocalUrl (PathAppend (GetSubwebPath (), sTocFilename))), 
            new Attribute ("id", sTocFrame), 
            new Attribute ("name", sTocFrame), 
            new Attribute ("frameBorder", sTocFrameFrameBorder));
    var tocframe = x.CreateElement ();
    if (tocframe == null) return null;

    /*  The document frame is less straightforward. Settings its "src" here 
        would bring two unsatisfactory effects. One is that what we set here 
        would be exactly the same as loaded into the top window, and may - 
        indeed, typically will - be rejected by the browser, presumably to 
        avoid circularity. The other is that if we ensure the two URLs 
        cannot be the same, e.g., by appending to the search string, we will 
        have a lot of trouble stopping the browser from downloading the 
        document file afresh. Instead, we let the browser load a blank page 
        into the document frame. We can then get the intended document into 
        the frame when the blank has loaded.  */

    x = new Component (
            "FRAME", 
            new Attribute ("id", sDocFrame), 
            new Attribute ("name", sDocFrame), 
            new Attribute ("frameBorder", sDocFrameFrameBorder));
    var docframe = x.CreateElement ();
    if (docframe == null) return null;

    /*  Once we have all the nodes, build them into a tree.  */

    midframeset.appendChild (tocframe);
    midframeset.appendChild (docframe);

    topframeset.appendChild (bannerframe);
    topframeset.appendChild (midframeset);

    return topframeset;
}

/*  *************************************  */
/*  Load-Time Initialisation (top window)  */

function OnLoadOriginalBody (Frameset, Location, Sheet, Rule)
{
    /*  Substitute our frameset for the document's original BODY. (But keep 
        the document's HEAD.) */

    var orgbody = window.document.body;
    var html = orgbody.parentNode;
    html.replaceChild (Frameset, orgbody);

    /*  Find the window object for the frame in which we want to re-present 
        the document. Point this window at the original document. Our hope 
        is that the browser realises it's being asked for content that it 
        only recently download and will now get it from a cache. 

        Better would be if we could fill the frame from the head and body 
        that we already know. This is readily doable, but apparently not 
        without leaving the frame to have the blank page as its location, 
        instead of the document that we have filled it with. Right-clicking 
        in the frame and asking to View Source would show source for the 
        wrong page.  */

    var docframe = GetFrame (sDocFrame);
    if (docframe != null) {
        docframe.location.replace (Location);
    }
    else {

        /*  If there seems to be no window object for the document frame, 
            try (admittedly only half-heartedly) to put things back.  */

        html.replaceChild (orgbody, Frameset);
        if (Sheet != null && Rule != null) RemoveRule (Sheet, Rule);
    }
}

/*  ******  */
/*  Public  */

/*  A parsing of window.top.location.search for common benefit - meaningful 
    in the top window  */

var oParsedSearch = null;

/*  Global function - use only from MASTER.JS (see 
    LocalUrl.prototype.LoadTocState)  */

function ComposeTocArguments (Args, Target)
{
    ComposeTocFrameWidth (Args);
}

/*  *********************  */
/*  Global Initialisation  */

if (IsBadHostName () || IsLowScriptSupport () || RedirectBadReferrer ()) {
    /* do nothing */
}

/*  When the document is loaded directly, try to rearrange the document to 
    re-present the content in a frameset, and at least get started on 
    loading content into those frames.  */

else if (window == window.top) {

    /*  Assign the window a name that is specific to the author and purpose. 
        Then, scripts that are run from pages that are loaded in a frame can 
        verify that their frame is the expected one with the expected top 
        window.  */

    window.name = sTopWindow;

    /*  Parse the search string once for common benefit.  */

    oParsedSearch = new ParsedSearch (window.location.search);

    /*  Create the FRAMESET and FRAME objects for the context in which we 
        will re-present the document, and specify files from which to load 
        content into frames other than the document frame.  */

    var frameset = CreateFrameset (oParsedSearch);
    if (frameset != null) {

        /*  We can't avoid letting the document's BODY load into the top 
            window even though we'll soon put a FRAMESET there instead. But 
            we can easily avoid letting it show.  */

        var sheet = GetLastStyleSheet ();
        var rule = sheet != null 
                    ? AppendRule (sheet, "BODY", "display:none") 
                    : null;

        /*  For frames other than the document frame, the browser will 
            already be loading content from the specified files, including 
            to pick up styles and scripts, even though the frameset is not 
            yet attached to any document. Content for the document frame is 
            what's still being loaded for this top-level window. When that 
            finishes loading, we can proceed with replacing the content by 
            the frameset.  */

        window.onload = function () {
            OnLoadOriginalBody (frameset, window.location, sheet, rule);
        };
    }
}

/*  When the document gets reloaded into the document frame, do whatever we 
    would have done without the re-presentation in a frameset.  */

else if (IsInFrameset (sDocFrame)) {

    HideNoScriptBlocks ();

    window.onload = DecorateLinks;

    /*  Ensure that all links to another page at this site will redraw the 
        frameset in the top window and pick up whatever needs to be 
        preserved of the TOC state.  */

    window.document.onclick = RedirectClickedLink;
    window.document.onmouseover = CheckForCharStyleToolTip;
}

/*  If the document is at a known host but is not in either the top window 
    or the expected frame, then force it into the top window and hope for 
    the best.  */

else {
    window.top.location.replace (window.location);
}

/*  Copyright © 2007-2012. Geoff Chappell. All rights reserved.  */

//-->
