﻿/// <reference path="jquery.intellisense.js"/>  
/**
 * Cobalt Elements for jQuery
 * tree - this manages the expading/collapsing tree.
 *
 * These methods are build on the jQuery and interface foundation to provider
 * client functionality to the Cobalt CMS system.
 *
 * Copyright (c) 2007 Scorpion Design, Inc.
 *
 */
(function($) {

	// Return a new instance of the cobalt editor.
	$.fn.Tree = function(o)
	{
		return this.each(function()
			{
				// If the tree hasn't already been built, do so now.
				if (!this.itree) { new $.cobalt.tree(this,o); }
			});
	};
	
	// Create the cobalt namespace if it has not already been done.
	$.cobalt = $.cobalt || {};

	// Tree contructor.
	$.cobalt.tree = function(el,o)
	{
		// Override any default properties/methods.
		$.extend(this,o||{});

		// Initialize the tree.
		this.init(el);
	};

	// Extend the tree prototype with these shared properties/methods.
	$.extend($.cobalt.tree.prototype, {

		// Events
		oninit : null,
		onload : null,
		ontoggle : null,

		// Ajax
		getChildrenUrl : 'CobaltAjax.ashx?M=GetPageTree',

		// Show/hide the child elements of the one just clicked.
		handleClick : function(e)
			{
				// If we clicked on a link fire that event.
				var el = $(e.target);
				if (el.is('span'))
				{
					var link = el.parent();
					if (link.attr('href'))
					{
						if (this.itree.selectLink)
							return this.itree.selectLink(link,e);
					}
					else
						return false;
				}

				// Are we opening or closing this branch?
				var on;
				var end;
				if (el.is('li'))
				{
					switch (el.attr('class'))
					{
						case "guideopened":
							el.attr('class','guideclosed');
							on = false;
							end = false;
							break;
						case "guideclosed":
							el.attr('class','guideopened');
							on = true;
							end = false;
							break;
						case "guideendopened":
							el.attr('class','guideendclosed');
							on = false;
							end = true;
							break;
						case "guideendclosed":
							el.attr('class','guideendopened');
							on = true;
							end = true;
							break;
					}
				}
				else
					return;

				// Show or hide the branch.
				var ul = el.children('ul');
				var ajax = false;
				if (on)
				{
					if (ul.length)
						ul.css({display:'block'});
					else
					{
						// Make an ajax call.
						el.attr('class',end?'guideendpending':'guidepending');
						var pageid = el.attr('_pageid');
						if (pageid=='-1')
							pageid = el.getRow().Path||el.getRow().BasePath;
						this.itree.loadChildren(el,pageid,null,e.path);
						ajax = true;
					}
				}
				else
					ul.css({display:'none'});

				// If the ontoggle event has been defined, fire it.
				if (!ajax && $.isFunction(this.itree.ontoggle))
					this.itree.ontoggle.call(this.itree,el);

				// Cancel any bubble.
				return false;
			},

		// Load the children of the clicked element.
		loadChildren : function(el,pageid,href,path)
			{
				var it = this;
				var levels = it.levels||(it.expandall?-1:1);
				var url = $.getAjaxUrl(it.getChildrenUrl,{X:this.fields,S:1,P:pageid,H:$.encode(href),L:levels});
				if (url)
				{
					$.ajax({url:url,success:function(data){it.renderChildren.call(it,data,el,path);}});
					return false;
				}
			},

		// Render the children.
		renderChildren : function(results,el,path)
			{
				// Ensure we have results.
				if (!results)
				{
					// If the list hasn't yet been initialized, do it now.
					if (!this.initialized)
						this.initSetup();
					
					// Then exit.
					return;
				}

				// Turn off any pending messages.
				switch (el.attr('class'))
				{
					case 'guidepending':
						el.attr('class','guideopened');
						break;
					case 'guideendpending':
						el.attr('class','guideendopened');
						break;
				}
			
				// Get the UL of this element.
				var ul = el.is('ul') ? el : el.children('ul');
				if (!ul.length)
					ul = $('<ul class="itree"></ul>').appendTo(el);

				// Assign the new HTML.
				ul.html(results).show();

				if (!this.initialized)
					// If the list hasn't yet been initialized, do it now.
					this.initSetup();
				else if ($.isFunction(this.ontoggle))
					// Fire any toggle event.
					this.ontoggle.call(this,el);

				// Fire any onload event.
				if ($.isFunction(this.onload))
					this.onload.call(this,el);

				// If a specific path was defined, look for it and click it.
				if (path)
				{
					var link = this.element.find('a[_path='+path.toLowerCase()+']').children('span');
					if (link.length)
						this.handleClick.call(this.element[0],{target:link[0]});
				}
			},

		// Once the first part of the tree is rendered, handle any branch states.
		initSetup : function()
			{
				// If we are set to expand all elements, do it now.
				if (this.expandall)
				{
					this.element.find('li.guideclosed').attr('class','guideopened').children('ul').css({display:'block'});
					this.element.find('li.guideendclosed').attr('class','guideendopened').children('ul').css({display:'block'});
				}

				// If we have an href supplied, select the element and expand the parents.
				var match = this.href && this.expand(this.href);

				// Fire any oninit event, saying whether or not we found a matching link.
				if ($.isFunction(this.oninit))
					this.oninit.call(this,match);

				// Note we have initialized the tree.
				this.initialized = true;
			},

		// Select a specific item and expand the tree to show it.
		expand : function(href)
			{
				if (href)
				{
					// Remove any apostrophes in the href, which crash jQuery (and the browser) during the find..
					href = href.toLowerCase().replace(/'/g,'');
					
					// Look for a matching link.
					var link = this.element.find("a[_path='"+href+"']");

					if (link.length)
					{
						// Ensure all of the closed branches are shown.
						link.parent().parents('li.guideclosed,li.guideendclosed').each(function(i)
							{
								var li = $(this);
								var end = li.is('.guideendclosed');
								li.attr('class',end?'guideendopened':'guideopened').children('ul').css({display:'block'});
							});

						// Then fire the select link event.
						this.selectLink(link,{});
						
						// If the link is not visible in the viewport, scroll to make it visible.
						var list = this.element.parent();
						if (list[0].scrollHeight>list[0].offsetHeight)
						{
							// Get the position of the link and the scrollbar.
							var pos = link[0].offsetTop;
							var zone = {h:list[0].offsetHeight/2};
							zone.t = (zone.h/2) + list[0].scrollTop;
							
							// If the link is before or after our "visible" zone, scroll the list to position.
							if (pos<zone.t || pos>zone.t+zone.h)
								list[0].scrollTop = pos-zone.h;
						}

						// Indicate we found a link.
						return true;
					}
				}
				
				// No matching link found.
				return false;
			},

		// Initialize the tree.
		init : function(el)
			{
				// Assign the element.
				this.element = $(el);

				if (this.build)
				{
					// Ensure we have a UL as our top-level element.
					if (!this.element.is('ul'))
						this.element = $('<ul class="itree"></ul>').appendTo(el);

					// Load the top level pages for this new list.
					this.loadChildren(this.element,this.pageid||0,this.href||'');
				}
				else
				{
					// Initialize the setup.
					this.initSetup();
				}
				
				if ($.browser.msie6)
					// Add an IE6 css hack.
					this.element.addClass('itree6');
				else if ($.browser.msie && $.browser.version=='7.0')
					// On an IE7 css hack.
					this.element.addClass('itree7');

				this.element.bind('click',this.handleClick)[0].itree = this;
			}
	});

	// Set a UL's tree state accordingly.
	$.fn.setTreeState = function() {

		return this.each(function(i){

			// Make sure we have a tree-based UL.
			var ul = $(this).filter('ul.itree');
			if (ul.length)
			{
				// Ensure the end element is properly set.
				ul.children(':not(:last)').filter('[class*=end]').setTreeEnd(false);
				var last = ul.children(':last').setTreeEnd(true);

				// See if this UL has a parent LI that needs it's branch state set.
				var li = ul.parent('li');
				if (li.length)
					li.setTreeBranch(last.length>0);
			}

		});

	};

	// Set a LI tree item's end guide.
	$.fn.setTreeEnd = function(end) {

		return this.each(function(i){

			// Make sure we have an LI.
			var li = $(this);
			if (!li.is('li'))
				return;

			if (end)
			{
				// Unmark the previous end guide.
				li.parent('ul').children('li[class*=end]').setTreeEnd(false);

				// Get the current class.
				var cls = li.attr('class');

				if (!cls)
					li.attr('class','guideend');
				else if (cls.indexOf('end')<0)
					li.attr('class',cls.replace('guide','guideend'));
			}
			else
			{
				// Get the current class.
				var cls = li.attr('class');

				if (cls=='guideend')
					li.removeAttr('class');
				else if (cls)
					li.attr('class',cls.replace('guideend','guide'));
			}

		});

	};

	// Set a LI tree item's branch guide.
	$.fn.setTreeBranch = function(branch) {

		return this.each(function(i){

			// Make sure we have an LI.
			var li = $(this);
			if (!li.is('li'))
				return;

			// Get the current class.
			var cls = li.attr('class');

			if (branch)
			{
				var open = li.children('ul:visible').length>0;

				if (!cls)
					li.attr('class',open?'guideopened':'guideclosed');
				else if (cls=='guideend')
					li.attr('class',open?'guideendopened':'guideendclosed');
			}
			else
			{
				if (cls.indexOf('end')>0)
					li.attr('class','guideend');
				else if (cls)
					li.removeAttr('class');
			}

		});

	};

	// Preload these images.
	$.preload('Shared/images/tree/GuideClosed.gif');
	$.preload('Shared/images/tree/GuideOpened.gif');
	$.preload('Shared/images/tree/GuideEndClosed.gif');
	$.preload('Shared/images/tree/GuideEndOpened.gif');
	$.preload('Shared/images/tree/GuidePending.gif');
	$.preload('Shared/images/tree/GuideEndPending.gif');
	
})(jQuery);
