﻿/***************************************************************************************
Nested list collapsing script written by Mark Wilton-Jones - 21/11/2003
Version 2.3.0 - this script takes existing HTML nested UL or OL lists, and collapses them
Updated 13/02/2004 to allow links in root of expanding branch
Updated 09/09/2004 to allow state to be saved
Updated 07/10/2004 to allow page address links to be highlighted
Updated 28/11/2004 to allow you to force expand/collapse links to use just the extraHTML
Updated 23/09/2006 to add expandCollapseAll and to allow selfLink to locate custom links
****************************************************************************************

Collapsible list and cookie script provided for free by
<a href="http://www.howtocreate.co.uk">http://www.howtocreate.co.uk</a>.
Customers are not charged for use of th(is|ese) script(s).

Please see http://www.howtocreate.co.uk/jslibs/ for details and a demo of this script
Please see http://www.howtocreate.co.uk/jslibs/termsOfUse.html for terms of use
_________________________________________________________________________

You can put as many lists on the page as you like, each list may have a different format.

To use:
_________________________________________________________________________

Inbetween the <head> tags, put:

<script src="PATH TO SCRIPT/listCollapse.js" type="text/javascript" language="javascript1.2"></script>
_________________________________________________________________________

Define the HTML. Note that to correctly nest lists, child OLs or ULs should be children of an LI element,
not direct descendents of their parent OL/UL. The text used to expand the branch should be written
between the <li> tag and the <UL/OL/A> tag, and should only contain HTML that is permitted inside an 'A'
element. Note; Opera 7 will lose any style attributes you define in this text - use classes instead.

<ul id="someID">
<li>Book 1
<ul>
<li><a href="someHref">Chapter 1</a></li>
<li><a href="someHref">Chapter 2</a></li>
</ul>
</li>
<li><a href="elsewhere.html">Book 2</a>
<ul>
<li><a href="someHref">Chapter 1</a></li>
<li><a href="someHref">Chapter 2</a></li>
</ul>
</li>
<li>Book 3
<ul>
<li><a href="someHref">Chapter 1</a></li>
<li>Cha<span class="doMore">pt</span>er 2
<ul>
<li><a href="someHref">Sub 1</a></li>
<li><a href="someHref">Sub 2</a></li>
</ul>
</li>
</ul>
</li>
</ul>
________________________________________________________________________
Now you need to trigger the collapsing, using <body onload, window.onload or by putting the collapse
commands in a script just before the </body> tag. If using either onload technique, you must not use
any other scripts that rely on the onload event.

compactMenu(theRootID,shouldAutoCollapse,extraHTML[,useMinimalLink]);
oID = string: ID of root nest element, must be a UL or OL; this will not be collapsed, but any child
UL/OLs will be (note, if the root nest element is a UL, all child lists should be ULs - the same is
true for OLs; if the root nest element is OL, all child lists should be OLs)
shouldAutoCollapse = bool: auto-collapse unused branches
extraHTML = string: HTML to insert to collapsible branches - usually '&plusmn; '
useMinimalLink = bool: normally the expand/collapse link will use both extraHTML and the original list
item text - if the list item text is already a link, this will not be included - set this option to
true to force the script to use only the extraHTML as the link, even if the rest of the list item is
not a link - this option will only be respected if you also provide some extraHTML

eg 1.
<body onload="compactMenu('someID',true,'&plusmn; ');">

eg 2.
<script type="text/javascript" language="javascript1.2"><!--
window.onload = function () { compactMenu('someID',false,'&plusmn; '); }
//--></script>

eg 3.
<script type="text/javascript" language="javascript1.2"><!--
compactMenu('someID',true,'&plusmn; ');
//--></script>
</body>

stateToFromStr(theRootID);
oID = string: ID of root nest element, must be a UL or OL; returns a string representing all expanding
branches - can be used with my cookie script to save state when unloading the page
stateToFromStr(theRootID,stringRepresentation);
oID = string: ID of root nest element, must be a UL or OL;
stringRepresentation = string: string representation of expanded branches, as created above
must be called _after_ collapsing the list - values can be recovered from cookies using my cookie script
note: this facility will not be able to take changes in the list structure into account - use session cookies
or short-term cookies to avoid longer term structure change problems

selfLink(theRootID,newClass,shouldExpandBranch[,linkHref]);
theRootID = string: ID of root nest element, must be a UL or OL;
newClass = string: new class name to add to any existing class names
shouldExpandBranch = bool: expand branches to show the first matching link
linkHref = string: by default, it will try to locate links to the current page address - you can
override that here by giving a specific address that it should look for.
Allows you to highlight links to the current page that appear in the list
must be called _after_ collapsing the list
address hash and port are not included in the comparison - links containing href="#" are always ignored

expandCollapseAll(theRootID,shouldExpand);
theRootID = string: ID of root nest element, must be a UL or OL that has been collapsed using compactMenu
shouldExpand = bool: says if it should expand all branches (true) or collapse all branches (false)
Expands/collapses all branches in a collapsed list. Must not be used with auto-collapsing lists.

My cookie script is available on http://www.howtocreate.co.uk/jslibs/
<body onload="compactMenu('someID',true,'&plusmn; ');stateToFromStr(theRootID,retrieveCookie('menuState'));"
onunload="setCookie('menuState',stateToFromStr(theRootID),31536000);">
____________________________________________________________________________________________________*/
var openLists = [], oIcount = 0;
function compactMenu(oID, oAutoCol, oPlMn, oMinimalLink) {
    if (!document.getElementsByTagName || !document.childNodes || !document.createElement) { return; }
    var baseElement = document.getElementById(oID); if (!baseElement) { return; }
    compactChildren(baseElement, 0, oID, oAutoCol, oPlMn, baseElement.tagName.toUpperCase(), oMinimalLink && oPlMn);
}
function compactChildren(oOb, oLev, oBsID, oCol, oPM, oT, oML) {
    if (!oLev) { oBsID = escape(oBsID); if (oCol) { openLists[oBsID] = []; } }
    for (var x = 0, y = oOb.childNodes; x < y.length; x++) {
        if (y[x].tagName) {
            //for each immediate LI child
            var theNextUL = y[x].getElementsByTagName(oT)[0];
            if (theNextUL) {
                //collapse the first UL/OL child
                theNextUL.style.display = 'none';
                //create a link for expanding/collapsing
                var newLink = document.createElement('A');
                newLink.setAttribute('href', '#');
                newLink.onclick = new Function('clickSmack(this,' + oLev + ',\'' + oBsID + '\',' + oCol + ',\'' + escape(oT) + '\');return false;');
                //wrap everything upto the child U/OL in the link
                if (oML) { var theHTML = ''; } else {
                    var theT = y[x].innerHTML.toUpperCase().indexOf('<' + oT);
                    var theA = y[x].innerHTML.toUpperCase().indexOf('<A');
                    var theHTML = y[x].innerHTML.substr(0, (theA + 1 && theA < theT) ? theA : theT);
                    while (!y[x].childNodes[0].tagName || (y[x].childNodes[0].tagName.toUpperCase() != oT && y[x].childNodes[0].tagName.toUpperCase() != 'A')) {
                        y[x].removeChild(y[x].childNodes[0]);
                    }
                }
                y[x].insertBefore(newLink, y[x].childNodes[0]);
                y[x].childNodes[0].innerHTML = oPM + theHTML.replace(/^\s*|\s*$/g, '');
                theNextUL.MWJuniqueID = oIcount++;
                compactChildren(theNextUL, oLev + 1, oBsID, oCol, oPM, oT, oML);
            } 
        } 
    } 
}
function clickSmack(oThisOb, oLevel, oBsID, oCol, oT) {
    if (oThisOb.blur) { oThisOb.blur(); }
    oThisOb = oThisOb.parentNode.getElementsByTagName(unescape(oT))[0];
    if (oCol) {
        for (var x = openLists[oBsID].length - 1; x >= oLevel; x -= 1) {
            if (openLists[oBsID][x]) {
                openLists[oBsID][x].style.display = 'none'; if (oLevel != x) { openLists[oBsID][x] = null; }
            } 
        }
        if (oThisOb == openLists[oBsID][oLevel]) { openLists[oBsID][oLevel] = null; }
        else { oThisOb.style.display = 'block'; openLists[oBsID][oLevel] = oThisOb; }
    } else { oThisOb.style.display = (oThisOb.style.display == 'block') ? 'none' : 'block'; }
}
function stateToFromStr(oID, oFStr) {
    if (!document.getElementsByTagName || !document.childNodes || !document.createElement) { return ''; }
    var baseElement = document.getElementById(oID); if (!baseElement) { return ''; }
    if (!oFStr && typeof (oFStr) != 'undefined') { return ''; } if (oFStr) { oFStr = oFStr.split(':'); }
    for (var oStr = '', l = baseElement.getElementsByTagName(baseElement.tagName), x = 0; l[x]; x++) {
        if (oFStr && MWJisInTheArray(l[x].MWJuniqueID, oFStr) && l[x].style.display == 'none') { l[x].parentNode.getElementsByTagName('a')[0].onclick(); }
        else if (l[x].style.display != 'none') { oStr += (oStr ? ':' : '') + l[x].MWJuniqueID; }
    }
    return oStr;
}
function MWJisInTheArray(oNeed, oHay) { for (var i = 0; i < oHay.length; i++) { if (oNeed == oHay[i]) { return true; } } return false; }
function selfLink(oRootElement, oClass, oExpand, oLink) {
    var tmpLink;
    if (!document.getElementsByTagName || !document.childNodes) { return; }
    oRootElement = document.getElementById(oRootElement);
    if (oLink) {
        tmpLink = document.createElement('a');
        tmpLink.setAttribute('href', oLink);
    }
    for (var x = 0, y = oRootElement.getElementsByTagName('a'); y[x]; x++) {
        if (y[x].getAttribute('href') && !y[x].href.match(/#$/) && getRealAddress(y[x]) == getRealAddress(oLink ? tmpLink : location)) {
            y[x].className = (y[x].className ? (y[x].className + ' ') : '') + oClass;
            if (oExpand) {
                oExpand = false;
                for (var oEl = y[x].parentNode, ulStr = ''; oEl != oRootElement && oEl != document.body; oEl = oEl.parentNode) {
                    if (oEl.tagName && oEl.tagName == oRootElement.tagName) { ulStr = oEl.MWJuniqueID + (ulStr ? (':' + ulStr) : ''); } 
                }
                stateToFromStr(oRootElement.id, ulStr);
            } 
        } 
    } 
}
function getRealAddress(oOb) { return oOb.protocol + ((oOb.protocol.indexOf(':') + 1) ? '' : ':') + oOb.hostname + ((typeof (oOb.pathname) == typeof (' ') && oOb.pathname.indexOf('/') != 0) ? '/' : '') + oOb.pathname + oOb.search; }
function expandCollapseAll(oElID, oState) {
    if (!document.getElementsByTagName || !document.childNodes) { return; }
    var oEl = document.getElementById(oElID);
    var oT = oEl.tagName;
    var oULs = oEl.getElementsByTagName(oT);
    for (var i = 0, oLnk; i < oULs.length; i++) {
        if (typeof (oULs[i].MWJuniqueID) != 'undefined') {
            oLnk = oULs[i].parentNode.getElementsByTagName('a')[0];
            if (oLnk && ((oState && oULs[i].style.display == 'none') || (!oState && oULs[i].style.display != 'none'))) {
                oLnk.onclick();
            } 
        } 
    } 
}
