Login   Register  
PHP Classes
elePHPant
Icontem

File: TreeMenu.js

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Richard Heyes  >  HTML_TreeMenu  >  TreeMenu.js  >  Download  
File: TreeMenu.js
Role: ???
Content type: text/plain
Description: The necessary javascript
Class: HTML_TreeMenu
Easy aPI to produce a dynamic tree menu
Author: By
Last change:
Date: 2002-06-19 11:30
Size: 11,416 bytes
 

Contents

Class file image Download
// +----------------------------------------------------------------------+
// | PHP Version 4                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2002 The PHP Group                                |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license,      |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/2_02.txt.                                 |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | license@php.net so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@phpguru.org>                          |
// |         Harald Radi <harald.radi@nme.at>                             |
// +----------------------------------------------------------------------+
//
// $Id: TreeMenu.js,v 1.2 2002/06/18 23:28:35 richard Exp $

function TreeMenu(layer, iconpath, myname, linkTarget)
{
	// Properties
	this.layer      = layer;
	this.iconpath   = iconpath;
	this.myname     = myname;
	this.linkTarget = linkTarget;
	this.n          = new Array();

	this.branches       = new Array();
	this.branchStatus   = new Array();
	this.layerRelations = new Array();
	this.childParents   = new Array();
	
	// Methods
	//this.preloadImages      = preloadImages;
	this.drawMenu           = drawMenu;
	this.toggleBranch       = toggleBranch;
	this.swapImage          = swapImage;
	this.doesMenu           = doesMenu;
	this.doesPersistence    = doesPersistence;
	this.getLayer           = getLayer;
	this.saveExpandedStatus = saveExpandedStatus;
	this.loadExpandedStatus = loadExpandedStatus;
	this.resetBranches      = resetBranches;
}

function TreeNode(title, icon, link, expanded)
{
	this.title    = title;
	this.icon     = icon;
	this.link     = link;
	this.expanded = expanded;
	this.n        = new Array();
}

/**
* Preload images hack for CrapZilla
*/
function preloadImages()
{
	var plustop    = new Image; plustop.src    = this.iconpath + '/plustop.gif';
	var plusbottom = new Image; plusbottom.src = this.iconpath + '/plusbottom.gif';
	var plus       = new Image; plus.src       = this.iconpath + '/plus.gif';

	var minustop    = new Image; minustop.src    = this.iconpath + '/minustop.gif';
	var minusbottom = new Image; minusbottom.src = this.iconpath + '/minusbottom.gif';
	var minus       = new Image; minus.src       = this.iconpath + '/minus.gif';
}

/**
* Main function that draws the menu and assigns it
* to the layer (or document.write()s it)
*/
function drawMenu()// OPTIONAL ARGS: nodes = [], level = [], prepend = '', expanded = false, visbility = 'inline', parentLayerID = null
{
	/**
    * Necessary variables
    */
	var output        = '';
	var modifier      = '';
	var layerID       = '';
	var parentLayerID = '';

	/**
    * Parse any optional arguments
    */
	var nodes         = arguments[0] ? arguments[0] : this.n
	var level         = arguments[1] ? arguments[1] : [];
	var prepend       = arguments[2] ? arguments[2] : '';
	var expanded      = arguments[3] ? arguments[3] : false;
	var visibility    = arguments[4] ? arguments[4] : 'inline';
	var parentLayerID = arguments[5] ? arguments[5] : null;

	var currentlevel  = level.length;

	for (var i=0; i<nodes.length; i++) {
	
		level[currentlevel] = i+1;
		layerID = this.layer + '_' + 'node_' + implode('_', level);

		/**
        * Store the child/parent relationship
        */
		this.childParents[layerID] = parentLayerID;

		/**
        * Gif modifier
        */
		if (i == 0 && parentLayerID == null) {
			modifier = nodes.length > 1 ? "top" : 'single';
		} else if(i == (nodes.length-1)) {
			modifier = "bottom";
		} else {
			modifier = "";
		}

		/**
        * Single root branch is always expanded
        */
		if (!doesMenu() || (parentLayerID == null && nodes.length == 1)) {
			expanded = true;
		}

		/**
        * Setup branch status and build an indexed array
		* of branch layer ids
        */
		if (nodes[i].n.length > 1) {
			this.branchStatus[layerID] = expanded;
			this.branches[this.branches.length] = layerID;
		}

		/**
        * Setup toggle relationship
        */
		if (!this.layerRelations[parentLayerID]) {
			this.layerRelations[parentLayerID] = new Array();
		}
		this.layerRelations[parentLayerID][this.layerRelations[parentLayerID].length] = layerID;

		/**
        * Branch images
        */
		var gifname = nodes[i].n.length && this.doesMenu() ? (expanded ? 'minus' : 'plus') : 'branch';
		var iconimg = nodes[i].icon ? sprintf('<img src="%s/%s" align="top">', this.iconpath, nodes[i].icon) : '';
		

		/**
        * Build the html to write to the document
        */
		var divTag    = sprintf('<div id="%s" style="display: %s; behavior: url(#default#userdata)">', layerID, visibility);
		var onClick   = doesMenu() && nodes[i].n.length ? sprintf('onclick="%s.toggleBranch(\'%s\', true)" style="cursor: pointer; cursor: hand"', this.myname, layerID) : '';
		var imgTag    = sprintf('<img src="%s/%s%s.gif" align="top" border="0" name="img_%s" %s />', this.iconpath, gifname, modifier, layerID, onClick);
		var linkStart = nodes[i].link ? sprintf('<a href="%s" target="%s">', nodes[i].link, this.linkTarget) : '';
		var linkEnd   = nodes[i].link ? '</a>' : '';

		output = sprintf('%s<nobr>%s%s%s%s%s%s</nobr><br></div>',
		                  divTag,
						  prepend,
		                  parentLayerID == null && nodes.length == 1 ? '' : imgTag,
						  iconimg,
						  linkStart,
						  nodes[i].title,
						  linkEnd);

		/**
        * Write out the HTML
        */
		if (this.doesMenu()) {
			this.getLayer(this.layer).innerHTML += output

		} else {
			document.write(output);
		}

		if (nodes[i].n.length) {
			/**
            * Determine what to prepend. If there is only one root
			* node then the prepend to pass to children is nothing.
			* Otherwise it depends on where we are in the tree.
            */
			if (parentLayerID == null && nodes.length == 1) {
				var newPrepend = '';

			} else if (i < (nodes.length - 1)) {
				var newPrepend = prepend + sprintf('<img src="%s/line.gif" align="top">', this.iconpath);

			} else {
				var newPrepend = prepend + sprintf('<img src="%s/linebottom.gif" align="top">', this.iconpath);
			}

			this.drawMenu(nodes[i].n,
			              explode('_', implode('_', level)), // Seemingly necessary to enforce passing by value
			              newPrepend,
			              nodes[i].expanded,
			              expanded ? 'inline' : 'none',
			              layerID);
		}
	}
}

function toggleBranch(layerID, updateStatus) // OPTIONAL ARGS: noSave = false
{
	var currentDisplay = this.getLayer(layerID).style.display;
	var newDisplay     = (this.branchStatus[layerID] && currentDisplay == 'inline') ? 'none' : 'inline'

	for (var i=0; i<this.layerRelations[layerID].length; i++) {
		if (this.branchStatus[this.layerRelations[layerID][i]]) {
			this.toggleBranch(this.layerRelations[layerID][i], false);
		}

		this.getLayer(this.layerRelations[layerID][i]).style.display = newDisplay;
	}

	if (updateStatus) {
		this.branchStatus[layerID] = !this.branchStatus[layerID];

		/**
        * Persistence
        */
		if (this.doesPersistence() && !arguments[2]) {
			this.saveExpandedStatus(layerID, this.branchStatus[layerID]);
		}

		// Swap image
		this.swapImage(layerID);
	}
}

/**
* Swaps the plus/minus branch images
*/
function swapImage(layerID)
{
	imgSrc = document.images['img_' + layerID].src;

	re = /^(.*)(plus|minus)(bottom|top|single)?.gif$/
	if (matches = imgSrc.match(re)) {
		
		document.images['img_' + layerID].src = sprintf('%s%s%s%s',
		                                                matches[1],
														matches[2] == 'plus' ? 'minus' : 'plus',
														matches[3],
														'.gif');
	}
}

/**
* Can the browser handle the dynamic menu?
*/
function doesMenu()
{
	return (is_ie5up || is_nav6up || is_gecko);
}

/**
* Can the browser handle save the branch status
*/
function doesPersistence()
{
	return is_ie5up;
}

/**
* Returns the appropriate layer accessor
*/
function getLayer(layerID)
{
	if (document.getElementById(layerID)) {
		return document.getElementById(layerID);
	} else if (document.all(layerID)) {
		return document.all(layerID);
	}
}

/**
* Save the status of the layer
*/
function saveExpandedStatus(layerID, expanded)
{
	document.all(layerID).setAttribute("expandedStatus", expanded);
	document.all(layerID).save(layerID);
}

/**
* Load the status of the layer
*/
function loadExpandedStatus(layerID)
{
	document.all(layerID).load(layerID);
	if (val = document.all(layerID).getAttribute("expandedStatus")) {
		return val;
	} else {
		return null;
	}
}

/**
* Reset branch status
*/
function resetBranches()
{
	if (!this.doesPersistence()) {
		return false;
	}

	for (var i=0; i<this.branches.length; i++) {
		var status = this.loadExpandedStatus(this.branches[i]);
		// Only update if it's supposed to be expanded and it's not already
		if (status == 'true' && this.branchStatus[this.branches[i]] != true) {
			if (this.childParents[this.branches[i]] == null || (in_array(this.childParents[this.branches[i]], this.branches) && this.branchStatus[this.childParents[this.branches[i]]])) {
				this.toggleBranch(this.branches[i], true, true);
			} else {
				this.branchStatus[this.branches[i]] = true;
				this.swapImage(this.branches[i]);
			}
		}
	}
}

/**
* Javascript mini implementation of sprintf()
*/
function sprintf(strInput)
{
	var strOutput  = '';
	var currentArg = 1;

	for (var i=0; i<strInput.length; i++) {
		if (strInput.charAt(i) == '%' && i != (strInput.length - 1) && typeof(arguments[currentArg]) != 'undefined') {
			switch (strInput.charAt(++i)) {
				case 's':
					strOutput += arguments[currentArg];
					break;
				case '%':
					strOutput += '%';
					break;
			}
			currentArg++;
		} else {
			strOutput += strInput.charAt(i);
		}
	}

	return strOutput;
}

function explode(seperator, input)
{
	var output = [];
	var tmp    = '';
	skipEmpty  = arguments[2] ? true : false;
	
	for (var i=0; i<input.length; i++) {
		if (input.charAt(i) == seperator) {
			if (tmp == '' && skipEmpty) {
				continue;
			} else {
				output[output.length] = tmp;
				tmp = '';
			}
		} else {
			tmp += input.charAt(i);
		}
	}
	if (tmp != '' || !skipEmpty) {
		output[output.length] = tmp;
	}
	
	return output;
}

function implode(seperator, input)
{
	var output = '';

	for (var i=0; i<input.length; i++) {
		if (i == 0) {
			output += input[i];
		} else {
			output += seperator + input[i];
		}
	}
	
	return output;
}

function in_array(item, arr)
{
	for (var i=0; i<arr.length; i++) {
		if (arr[i] == item) {
			return true;
		}
	}

	return false;
}