Git'er DoneArboristarg¡eBarg!ehhROGERSii

JavaScript Arborist

tools for manipulating a hierarchy

This laboratory page implements a number of web technologies including JavaScript namespaces,  consuming REST data resources and transforming that data using XSL. Rather than using an Ajax call to update the tree details panel, I use anchor links targeting an iFrame -- also known as NoJax.

Pine Tree Classification


Hierarchy and species details retrieved via Tree of Life Web Project ReST interface.
//JavaScript Arborist - hhrogersii.com
/*global document: false, $: false */
var Tree = ( function ( ) 
{
	'use strict';
	
	var initialNode = 0,
		listTag = 'dl',
		itemTag = 'dd',
		treeClass = 'tree';
	
	function show ( item, list ) 
	{
		if ( typeof list === 'undefined' )
		{
			list = item.getElementsByTagName( listTag )[0];
		}
		if ( typeof list !== 'undefined' )
		{
			list.style.display = 'block';
		}
		item.style.backgroundImage = 'url(minusm.gif)';
		item.style.color = '#036';
	}
	
	function hide ( item, list ) 
	{
		if ( typeof list === 'undefined' )
		{
			list = item.getElementsByTagName( listTag )[0];
		}
		if ( typeof list !== 'undefined' )
		{
			list.style.display = 'none';
		}
		item.style.backgroundImage = 'url(plusm.gif)';
		//item.style.color = '#999';
	}
	
	return {
		
		init: function ( iTag, iClass, iNode )
		{
			listTag = iTag;
			if ( listTag.toUpperCase() !== 'DL' )
			{
				itemTag = 'li';
			}
			
			treeClass = iClass;

			initialNode = iNode;
			if ( typeof iNode !== 'undefined' )
			{
				this.expand( iNode );
			}
		},
		
		select: function ( item )
		{
			var list = item.getElementsByTagName( listTag )[0];
		
			if ( typeof list !== 'undefined' )
			{
				if ( list.style.display !== 'block' )
				{
					show( item, list );
				}
				else
				{
					if ( item.id.indexOf( itemTag + '_0_' + treeClass ) === 0 )
					{
						this.renew( list.id.substring( 5 ) );
					}
					else
					{
						hide( item, list );
					}
				}
			}
		},
		
		expand: function ( id )
		{
			var nodeVal = id.split( '_' )[1],
				item = document.getElementById( id );

			if ( parseInt( nodeVal, 10 ) !== 0 && nodeVal !== '' )
			{
				while ( item && item.tagName !== 'DIV' )
				{
					if ( item.tagName === listTag.toUpperCase() )
					{
						item.style.display = 'block';
					}
					else
					if ( item.tagName === itemTag.toUpperCase() )
					{
						item.style.backgroundImage = 'url(minusm.gif)';
						item.style.color = '#036';
					}
			
					item = item.parentNode;
				}
			}
		},
		
		renew: function ( id )
		{
			var list = document.getElementById( listTag + '_0_' + treeClass ),
				items = document.getElementsByTagName( itemTag.toUpperCase() ),
				i = 0;

			for ( i; i<items.length; i++ )
			{
				hide( items[i] );
			}
			
			if ( typeof id !== 'undefined' )
			{
				initialNode = id;
			}
			
			this.expand( initialNode );
		},
		
		expandAll: function ( )
		{
			var list = document.getElementById( listTag + '_0_' + treeClass ),
				items = document.getElementsByTagName( itemTag.toUpperCase() ),
				i = 0;

			for ( i; i<items.length; i+=1 )
			{
				show( items[i] );
			}
		},
		
		collapseAll: function ( )
		{
			var list = document.getElementById( listTag + '_0_' + treeClass ),
				items = document.getElementsByTagName( itemTag.toUpperCase() ),
				i = 0;

			for ( i; i<items.length; i+=1 )
			{
				hide( items[i] );
			}
		}
	};
}());

// callback from info iframe: parent.$('#info').css('visibility','visible');
function resizeIframe ()
{
	$('#info').height( Math.max( $('#tree').height() - 4, $('#wa').height() ) );
}

// $(window).load(function(){if(typeof main!=='undefined'){main();}});;
function main ()
{
	'use strict';
	
	Tree.init( 'dl', 'tree', 'dl_21645_tree' );
	$('.tree').click( 
		function(event){ 
			Tree.select( event.target ); 
			resizeIframe(); 
		} 
	);
	
	resizeIframe();
	
	$('.tree a').click( 
		function(){
			$('#wa').show();
			$('#info').css('visibility','hidden');
		} 
	);
	
	$('#expandAll').click( function(){ Tree.expandAll(); } );
	$('#collapseAll').click( function(){ Tree.collapseAll(); $('#wa').hide(); } );
	$('#resetAll').click( function(){ Tree.renew(); } );
}
<cfset var Tree = CreateObject( "Component", "Tree" )>

<!doctype html>
<html>

<head>
	<meta charset="utf-8" />
	<title>&gt;arg&iexcl;eBarg!e: JavaScript Arborist</title>
	<meta name="description" content="&hellip;methods for managing tree structures." />
	<meta name="author" content="Henry Rogers" />
	<meta name="viewport" content="width=device-width, initial-scale=1" />
	<!--[if lt IE 9]>
		<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
	<![endif]-->
	<!-- Adding "maximum-scale=1" fixes the Mobile Safari auto-zoom bug: http://filamentgroup.com/examples/iosScaleBug/ -->
	<link type="text/css" rel="stylesheet" href="Tree.css" />
</head>

<body lang="en">

	<div id="header-container" class="wrapper lab">
			<div style="float:right">
				<a href="https://github.com/hhrogersii/argleBargle/tree/master/arborist/"><img id="octodex" src="/img/octodex.png" width="24" height="24" alt="Git'er Done" /></a>  &larr;
	 			<a href="http://hhrogersii.com/lab/arborist/">Arborist</a> &larr; 
				<a href="http://hhrogersii.com/lab/">arg&iexcl;eBarg!e</a> &larr; 
				<a href="http://hhrogersii.com/">hhROGERSii</a>
			</div>
    	<header>
			<h1>&gt;arg&iexcl;eBarg!e:</h1>
			<span>&hellip;such as an argument, often a worthless, but energetic, conversation or comment.</span>
		</header>
	</div>

	<div id="main" class="wrapper lab" role="main">
    <article>

        <header>
        	<h2>JavaScript Arborist</h2>
        	<p>&hellip;methods for managing tree structures.</p>
        </header>

		<section>
			<div class="section" style="position:relative">

				<h3>Pine Tree Classification</h3>

				<div id="tree">
					<div id="menu">
						<span id="expandAll" class="menu">Expand All</span> 
						<span id="collapseAll" class="menu">Collapse All</span> 
						<span id="resetAll" class="menu">Reset</span>
					</div>
					<dl id="dl_0_tree" class="tree">
<cfoutput>#Tree.OutputNode( Tree.GetPineTrees(), 0 )#</cfoutput>
					</dl>
				</div>

				<div id="wa">
<iframe name="wa-details" id="info" height="200" width="500" src="#" style="float:right;height:100%"></iframe>
				</div>

				<hr style="clear:left">
				<span>Hierarchy and species details retrieved via <a href="http://tolweb.org/">Tree of Life Web Project</a> ReST interface.</span>

			</div>
		</section>

   		<footer>
			<p>
				<a href="https://github.com/hhrogersii/argleBargle/tree/master/arborist/"><img id="octodex" src="/img/octodex.png" width="24" height="24" alt="Git'er Done" /></a>  &larr;
	 			<a href="http://hhrogersii.com/lab/arborist/">Arborist</a> &larr; 
				<a href="http://hhrogersii.com/lab/">arg&iexcl;eBarg!e</a> &larr; 
				<a href="http://hhrogersii.com/">hhROGERSii</a>
			</p>
		</footer>

    </article>

	</div>

	<div id="footer-container" class="wrapper lab">
		<footer>
			<p>All code authored by hhrogersii is available for use with or without attribution under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>.
			<br />See disclaimers provided by the authors of code included in this site for the terms and conditions of the use of their work.</p>
		</footer>
	</div>

	<!--Thank you jQuery -->
	<script src="http://code.jquery.com/jquery-latest.js"></script>
	<script>!window.jQuery && document.write(unescape('%3Cscript src="/js/libs/jquery-1.5.1.min.js"%3E%3C/script%3E'))</script>
	<script type="text/javascript" src="Tree.js"></script>
	<script>$(window).load(function(){if(typeof main!=='undefined'){main();}});</script>

</body>

</html>
<cfcomponent displayname="Arborist Methods" output="false" hint="Methods for displaying a nested tree structure based on an XML document.">

<cffunction name="GetPineTrees">
	<cfif NOT IsDefined("SESSION.treeCache.pines") OR SESSION.treeCache.ttl LTE Now()>
		<cfhttp url="http://tolweb.org/onlinecontributors/app?service=external&page=xml/TreeStructureService&node_id=21626"  />
		<cflock name="_pinetrees" timeout="30" type="exclusive">
			<cfset SESSION.treeCache = StructNew()>
			<cfset SESSION.treeCache.ttl = DateAdd("h", 1, Now())>
			<cfset SESSION.treeCache.pines = cfhttp.filecontent>
		</cflock>
	</cfif>
	<cfreturn ConvertXmlToStruct(SESSION.treeCache.pines, StructNew(), 'name')>
</cffunction>

<cfscript>
function OutputNode( data, id )
{
	var i = 0;
	
	if ( StructKeyExists(data,'NODES') )
		WriteOutput( '<dd id="dd_' & data['ID'] & '_tree" class="node">' );
	else
		WriteOutput( '<dd>' );

	if ( StructKeyExists( data, 'NAME' ) )
	{
		if ( Compare(data.Name,"") )
		{ 
			WriteOutput( '<div><a href="wa.cfm?input=' & URLEncodedFormat(data.Name) & '" target="wa-details">' & data.Name & '</a></div>' );
		}
		else
		{
			WriteOutput( '&hellip;' );
		}
	}
	
	if ( StructKeyExists(data,'NODES') AND IsArray(data.Nodes) AND ArrayLen(data.Nodes) )
	{
		WriteOutput( '<dl id="dl_' & data['ID'] & '_tree">' );
		for ( i=1; i LTE ArrayLen(data.Nodes); i++ )
		{
			if ( IsStruct( data.Nodes[i] ) )
			{
				OutputNode( data.Nodes[i], data['ID'] );
			}
		}
		//while ( i++ LT ArrayLen(data.Nodes) );
		WriteOutput( '</dl>' );
	}

	WriteOutput( '</dd>' );
}
</cfscript>

<cffunction name="ConvertXmlToStruct" access="public" returntype="any" output="true"
			hint="Parse raw XML response body into ColdFusion structs and arrays and return it.">
	<cfargument name="xmlNode" type="string" required="true" />
	<cfargument name="str" type="struct" required="true" />
	<!---Setup local variables for recurse: --->
	<cfset var i = 0 />
	<cfset var axml = arguments.xmlNode />
	<cfset var astr = arguments.str />
	<cfset var n = "" />
	<cfset var tmpContainer = "" />

	<cfset axml = XmlSearch(XmlParse(arguments.xmlNode),"/node()")>
	<cfset axml = axml[1] />

	<!--- For each children of context node: --->
	<cfloop from="1" to="#arrayLen(axml.XmlChildren)#" index="i">
		<!--- Read XML node name without namespace: --->
		<cfset n = replace(axml.XmlChildren[i].XmlName, axml.XmlChildren[i].XmlNsPrefix&":", "") />
		<!--- If key with that name exists within output struct ... --->
		<cfif  ListFind("NAME,NODE,NODES",n)>
			<cfif arrayLen(axml.XmlChildren[i].XmlChildren) gt 0>
				<!--- recurse call: get complex item: --->
				<cfset stuff = ConvertXmlToStruct(axml.XmlChildren[i], structNew()) />
				<!--- <cfdump var="#axml.XmlChildren[i].XmlAttributes.Id#"><cfdump var="#stuff#"><cfabort><cfabort> --->
				<cfif IsStruct(stuff)>
					<cfset stuff.ID = axml.XmlChildren[i].XmlAttributes.id>
				</cfif>
			<cfelse>
				<!--- else: assign node value as last element of array: --->
				<cfset stuff = axml.XmlChildren[i].XmlText />
			</cfif>
			<cfif structKeyExists(astr, n)>
				<!--- ... and is not an array... --->
				<cfif not isArray(astr[n])>
					<!--- ... get this item into temp variable, ... --->
					<cfset tmpContainer = astr[n] />
					<!--- ... setup array for this item beacuse we have multiple items with same name, ... --->
					<cfset astr[n] = arrayNew(1) />
					<!--- ... and reassing temp item as a first element of new array: --->
					<cfset astr[n][1] = tmpContainer />
				</cfif>
				<cfset astr[n][arrayLen(astr[n])+1] = stuff />
			<cfelse>
				<cfset astr[n] = stuff />
			</cfif>
		</cfif>
	</cfloop>
	<cfif StructKeyExists(astr,"node")>
		<cfreturn astr.node />
	<cfelse>
		<cfreturn astr />
	</cfif>
</cffunction>

</cfcomponent>
/*JavaScript Arborist - hhrogersii.com*/
/*tree view definition lists*/
.tree {
	display: block;
	padding: 0px 3px 2px 5px;
}
.tree dl {
	display: none;
	margin-left: 6px;
	padding: 0;
	font: 12px/8px Verdana, Arial, Helvetica, sans-serif;
	color: #036;
}
.tree dd {
	display: block;
	margin: 0;
	padding: 4px 4px 2px 16px;
	white-space: nowrap;
	font: 12px/12px Verdana, Arial, Helvetica, sans-serif;
	color: #036;
	cursor: auto;
	background: transparent url(nullm.gif) no-repeat left 6px;
}
.tree dd.node {
	cursor: pointer;
	background: transparent url(plusm.gif) no-repeat left 6px;
}

#tree{ 
	float: left;
	margin-bottom: 12px;
}
#menu{ 
	margin-bottom: 8px; 
}
.menu{ 
	text-decoration: underline; 
	cursor: pointer; 
	padding: 4px;
}
.menu: hover{ 
	background-color: #fff7d1 }

#wa{ 
	float: right;
	border: 1px solid #ccbbca;
	display: none;
	background: url('/img/63.gif') no-repeat scroll center 100px transparent; 
}
<cfsetting enableCFoutputOnly="Yes">
<cfflush>

<cfparam name="url.input" default="">
<cfparam name="form.input" default="#url.input#">

<cfif Compare(form.input,"")>

	<cfhttp url="http://api.wolframalpha.com/v2/query?input=#URLEncodedFormat(form.input)#&appid=GX7YKA-ULRXHK3VPT&format=html">
	<cfset xmlData = cfhttp.filecontent>
	<cfset xslFile = "wolframalpha.xsl">

	<cfoutput>
#XmlTransform(
	xmlData,
	xslFile
)#
	</cfoutput>

</cfif>
<?xml version="1.0" encoding="UTF-8"?><!-- DWXMLSource="../Untitled-2.xml" --><!DOCTYPE xsl:stylesheet  [
	<!ENTITY nbsp   "&#160;">
	<!ENTITY copy   "&#169;">
	<!ENTITY reg    "&#174;">
	<!ENTITY trade  "&#8482;">
	<!ENTITY mdash  "&#8212;">
	<!ENTITY ldquo  "&#8220;">
	<!ENTITY rdquo  "&#8221;"> 
	<!ENTITY pound  "&#163;">
	<!ENTITY yen    "&#165;">
	<!ENTITY euro   "&#8364;">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
<xsl:template match="/">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>WolframAlpha</title>
<link href="wa-calculate.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h1><xsl:value-of select="queryresult/@datatypes"/></h1>
<xsl:if test="queryresult/warningsmarkup !=''">
<p><span  class="warning"><xsl:value-of select="queryresult/warningsmarkup" disable-output-escaping="yes"/></span></p>
</xsl:if>
<p>	
	<xsl:for-each select="queryresult/pod">
		<xsl:value-of select="markup" disable-output-escaping="yes"/>
	</xsl:for-each>
</p>
<script type="text/javascript">parent.$('#info').css('visibility','visible');</script>
<!--[if lte IE 8]>
<link rel='stylesheet' type='text/css' media='screen' href='http://www4b.wolframalpha.com/Calculate/css/ie.css' />
<![endif]-->
<!--[if lte IE 6]>
<style type="text/css">
body {behavior: url(http://www4b.wolframalpha.com/Calculate/javascript/csshover.htc);}
</style>
<![endif]-->
</body>
</html>

</xsl:template>
</xsl:stylesheet>