/**
 * jQuery Sabramedia Plugin Library - Function for Sabramedia Virtual Office System
 * Click and Discover Branch
 * Copyright (C) 2008 - 2010 Sabramedia, LLC
 * http://www.sabramedia.com
 *
 * @author Nick Johnson {@link http://nickjohnson.com}
 */

/*
 * jQuery JSON Plugin
 * version: 1.0 (2008-04-17)
 *
 * This document is licensed as free software under the terms of the
 * MIT License: http://www.opensource.org/licenses/mit-license.php
 *
 * Brantley Harris technically wrote this plugin, but it is based somewhat
 * on the JSON.org website's http://www.json.org/json2.js, which proclaims:
 * "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
 * I uphold.  I really just cleaned it up.
 *
 * It is also based heavily on MochiKit's serializeJSON, which is 
 * copywrited 2005 by Bob Ippolito.
 */
 

(function($){function toIntegersAtLease(n){return n<10?'0'+n:n;}Date.prototype.toJSON=function(date){return this.getUTCFullYear()+'-'+toIntegersAtLease(this.getUTCMonth())+'-'+toIntegersAtLease(this.getUTCDate());};var escapeable=/["\\\x00-\x1f\x7f-\x9f]/g;var meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};$.quoteString=function(string){if(escapeable.test(string)){return'"'+string.replace(escapeable,function(a){var c=meta[a];if(typeof c==='string'){return c;}c=a.charCodeAt();return'\\u00'+Math.floor(c/16).toString(16)+(c%16).toString(16);})+'"';}return'"'+string+'"';};$.toJSON=function(o,compact){var type=typeof(o);if(type=="undefined")return"undefined";else if(type=="number"||type=="boolean")return o+"";else if(o===null)return"null";if(type=="string"){return $.quoteString(o);}if(type=="object"&&typeof o.toJSON=="function")return o.toJSON(compact);if(type!="function"&&typeof(o.length)=="number"){var ret=[];for(var i=0;i<o.length;i++){ret.push($.toJSON(o[i],compact));}if(compact)return"["+ret.join(",")+"]";else
return"["+ret.join(", ")+"]";}if(type=="function"){throw new TypeError("Unable to convert object of type 'function' to json.");}var ret=[];for(var k in o){var name;type=typeof(k);if(type=="number")name='"'+k+'"';else if(type=="string")name=$.quoteString(k);else
continue;var val=$.toJSON(o[k],compact);if(typeof(val)!="string"){continue;}if(compact)ret.push(name+":"+val);else
ret.push(name+": "+val);}return"{"+ret.join(", ")+"}";};$.compactJSON=function(o){return $.toJSON(o,true);};$.evalJSON=function(src){return eval("("+src+")");};$.secureEvalJSON=function(src){var filtered=src;filtered=filtered.replace(/\\["\\\/bfnrtu]/g,'@');filtered=filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']');filtered=filtered.replace(/(?:^|:|,)(?:\s*\[)+/g,'');if(/^[\],:{}\s]*$/.test(filtered))return eval("("+src+")");else
throw new SyntaxError("Error parsing JSON, source is not valid.");};})(jQuery);


/* Global Variables */
var sabramediaSpinnerSmall = '<span class="pending-spinner"><img src=\'/admin/theme/default/img/spinner-small.gif\' /></span>';
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g,""); };
String.prototype.ltrim = function() { return this.replace(/^\s+/,""); };
String.prototype.rtrim = function() { return this.replace(/\s+$/,""); };

/* Sabramedia jQuery Begins */
(function ($)
{
	/*
	 * 	Easy Slider - jQuery plugin
	 *	written by Alen Grakalic	
	 *	http://cssglobe.com/post/3783/jquery-plugin-easy-image-or-content-slider
	 *
	 *	Copyright (c) 2009 Alen Grakalic (http://cssglobe.com)
	 *	Dual licensed under the MIT (MIT-LICENSE.txt)
	 *	and GPL (GPL-LICENSE.txt) licenses.
	 *
	 *	Built for jQuery library
	 *	http://jquery.com
	 */
	
	$.fn.easySlider = function(options){
		  
		// default configuration properties
		var defaults = {
			prevId: 		'prevBtn',
			prevText: 		'Previous',
			nextId: 		'nextBtn',	
			nextText: 		'Next',
			orientation:	'', //  'vertical' is optional;
			speed: 			800			
		}; 
		
		var options = $.extend(defaults, options);  
		
		return this.each(function() {
			
			var obj = $(this);
			obj.attr('style',''); // clear styles
			var s = $("li", obj).length;
			var w = obj.width(); 
			var h = obj.height(); 
			var ts = s-1;
			var t = 0;
			var vertical = (options.orientation == 'vertical');
			$("ul", obj).css('width',s*w);			
			if(!vertical) $("li", obj).css('float','left');
			obj.find('#'+ options.prevId +',#'+ options.nextId).remove().end()
				.append('<span id="'+ options.prevId +'"><a href=\"javascript:void(0);\">'+ options.prevText +'</a></span> <span id="'+ options.nextId +'"><a href=\"javascript:void(0);\">'+ options.nextText +'</a></span>');		
			$("a","#"+options.prevId).hide();
			$("a","#"+options.nextId).hide();
			$("a","#"+options.nextId).click(function(){		
				animate("next");
				if (t>=ts) $(this).fadeOut();
				$("a","#"+options.prevId).fadeIn();
			});
			$("a","#"+options.prevId).click(function(){		
				animate("prev");
				if (t<=0) $(this).fadeOut();
				$("a","#"+options.nextId).fadeIn();
			});	
			function animate(dir){
				if(dir == "next"){
					t = (t>=ts) ? ts : t+1;	
				} else {
					t = (t<=0) ? 0 : t-1;
				};								
				if(!vertical) {
					p = (t*w*-1);
					$("ul",obj).animate(
						{ marginLeft: p }, 
						options.speed
					);				
				} else {
					p = (t*h*-1);
					$("ul",obj).animate(
						{ marginTop: p }, 
						options.speed
					);					
				}
			};
			if(s>1) $("a","#"+options.nextId).fadeIn();	
		});
	  
	};


/* Ajax Handling Functions */
	$.voAjax = function( settings , thisUpdateField )
	{
		
	// Data Preparation
	
		// if tinyMCE variable set then we need to trigger the new values.
		if(typeof(tinyMCE) != 'undefined'){
			//tinyMCE.triggerSave();
		}
		
		// This doesn't work... I know how to get it to work now, think iframe.
		if(typeof(editAreaLoader) != 'undefined'){
			$('textarea#content-js').append(editAreaLoader.getValue('content-js'));
		}
		
	// Methods
		function getSerializedData()
		{
			var serializedData = '';
			// Set data here. Important to set after this just in case we need to do an preparation.
			if(settings.dataContainer){
				serializedData += $(settings.dataContainer).serializeRowData();
			}
			
			if(settings.dataObject){
				if(settings.dataContainer){ serializedData += '&'; } // Appending character if both are set
				serializedData += serializeJSON(settings.dataObject);
			}
			return serializedData;
		};
		
		function beforeSend( xhr )
		{
			if(settings.spinner){
				$(thisUpdateField).after(sabramediaSpinnerSmall);
			}
			
			var verify = true;
			if(settings.verifyAction){
				verify = confirm(settings.verifyAction);
			}
			
			if( !verify ){
				return false;
			}
			
			// Disable all sibling fields only dataObject is not passed and rowData is used
			// $('button').attr('disabled','disabled');
	
			// Iterate through custom fields
			if(settings.beforeSend){
				$.each(settings.beforeSend,function(i,customFunction){
					// Pass field object of event handler
					customFunction(xhr,thisUpdateField);
				});
			}
		};
		
		function success( data, status )
		{
			$('.pending-spinner').remove();
			if(settings.success){
				$.each(settings.success,function(i,customFunction){
					// Pass field object of event handler
					if($.isFunction( customFunction )){
						customFunction( data, status, thisUpdateField );
					}
				});
			}else{
//				$(thisUpdateField)
//					.focus()
//					.siblings('.temp-message').remove().end()
//					.parent('li').removeAttr('style').end();
			}
			
			if(data.warning){
				$.voMessage.alert( data.warning );
			}
			if(data.message){
				$.voMessage.dock( data.message );
			}
		};
		
		function complete( xhr, status)
		{
			// Re-enable disabled fields upon success
			// $('button').removeAttr('disabled');
			if(settings.complete){
				$.each(settings.complete,function(i,customFunction){
					// Pass field object of event handler
					if($.isFunction( customFunction )){
						customFunction( xhr, status );
					}
				});
			}
		};

	// Ajax intializes here
		
		return $.ajax({
			type:'POST',
			url:settings.actionController,
			dataType:'json',
			data:getSerializedData(),
			beforeSend:beforeSend,
			success:success,
			complete:complete,
			global:settings.global
		});
	};
	
	/**
	* serializeJSON()
	* Turns JSON object into a serialized string in the form of: key=val&key2=val2 
	*
	* @param object literal object
	* @return string serialized for Ajax
	*/
	function serializeJSON(dataObject)
	{
		var dataArray = [];
		var retval;
		
		$.each(dataObject,function(key,data){
			dataArray.push(key + '=' + escape(data));
		});
		
		retval = dataArray.join('&');
		return retval;
	};
	
	function simplifyInputData(dataObject)
	{
		var object = [];
		var retval;
		
		$.each(dataObject,function(key,data){
			
			object[data.name] = data.value;
		});
		
		retval = object;
		return retval;
	};
	
	/** 
	* serializeRowData()
	* Accesses this elements parent element data-json attribute and all sibling element form-type
	* fields, then serializes data for sending via Ajax XHR.<b>
	*
	* @return string serialized data to send via Ajax
	* @dependency metadata
	*/
	$.fn.serializeRowData = function()
	{
		// Gets all json data contained in the parent tag's data-json wrapper 
		var metaData='';
		if($.metadata){
			$.metadata.setType('attr','data-json');
			jsonObject = $('[data-json]',this).andSelf().metadata();
			metaData += serializeJSON(jsonObject)+'&';
		}
		if($(this).data('postdata')){
			metaData += serializeJSON($(this).data('postdata'))+'&';
		}
		// Gets all form type fields based on the current field's containing parent,
		// Then it serializes the data for passing to the ajax controller.
		var inputData = $(this).find(':input');
		
		// Concatentate the jsonData and the inputData
		var serialData = metaData+inputData.serialize();
		//return jsonData;
		// Optional dual-type data object
		
		return serialData;
	};
		
	// Takes JSON returned data and places data in fields or tags with data.key == '.class'
	$.fn.setDataDisplay = function( data )
	{
		if(data){
			object = $(this);
			$.each(data, function( key, val ){
				if(typeof(val) != 'object' && val.length > 0){
					var input_type = $('.'+key, object).attr('type');
					
					if($('.'+key, object).is('select')){
						input_type = 'select';
					}
					
					switch (input_type) {
						case 'text':
							$('.'+key, object).val(val);
						break;
						case 'textarea':
							$('.'+key, object).val(val);
						break;
						case 'radio':
							if(val == true){ val=1; }
							if(val == false){ val=0; }
							$('.'+ key +'[value='+ val +']', object).attr('checked','checked');
						break;
						case 'checkbox':
							if(val == true){ val=1; }
							if(val == false){ val=0; }
							$('.'+ key +'[value='+ val +']', object).attr('checked','checked');
						break;
						case 'select':
							$('.'+key+' option[value='+val+']', object).attr('selected','selected');
						break;
						default:
							$('.'+key, object).text(val);
						break;
					}
					//$('.'+key, object).text(val);
				}
			});
			return this;
		}
	};
		
	$.extend({
	
	/**
	* @name jqPhp
	* @desc Purpose is for php variables to be passed into object to be globally accessed.
	*/
		jqPHP : {
		
			phpUrl : {}, // Url object
			phpVar : {}, // Url object

			// Set the urls from php so custom plugins can access the unique urls created by Sabramedia Virtual Office.
			setUrl : function( name, url )
			{
				this.phpUrl[name] = url;
			},
			// Get the urls in the plugin by name
			getUrl : function( name )
			{
				return this.phpUrl[name];
			},
			
			setVar : function( name, variable )
			{
				this.phpVar[name] = variable;
			},

			getVar : function( name )
			{
				return this.phpVar[name];
			},
			removeVar : function( name )
			{
				delete this.phpVar[name];
			}
		},
	/**
	* @name voMessage
	* @desc Standard method for bringing basic messages to system users.
	*/
		voMessage : {
			// Replaces standard alert with a nicer popup
			alert : function( message )
			{
				var randomId = Math.floor(Math.random()*11);
				var $dialog = $('<div id="vo-dialog'+randomId+'" />');
				$dialog.append('<span class="ui-icon ui-icon-alert" style="float:left; margin:2px 7px 20px 0;"></span>' + message);
				$dialog.appendTo('body'); 
				
				$dialog.dialog({
					title: 'Virtual Office Alert',
					bgiframe: true,
					resizable: false,
					height:200,
					width:300,
					modal: true,
					overlay: {
						backgroundColor: '#000',
						opacity: 0.5
					},
					buttons: {
						'Ok': function() {
							$(this).dialog('close');
							$(this).dialog('destroy');
							$('#vo-dialog'+randomId).remove();
						}
					}
				});
				
			},
			
			// Footer alert
			dock : function( message )
			{
				$("#dock-container").hide();
				$("#dock-container #ajax-message").empty().append( message );
				$("#dock-container").fadeIn('normal');
				setTimeout("$('#dock-container').fadeOut('slow')",4000);
			},
			
			// Safer way to post to Firebug console
			debug : function( message )
			{
				if(window.console){
					console.log(message);
				}else{
					if(typeof message != 'string'){
						message = 'Your browser doesn\'t support advanced debugging. Try Firefox and Firebug!';
					}
					//alert(message);
				}
			}
		},
		
		/**
		* @name log
		* @desc Alias of $.voMessage.debug(); 
		*/
		
		log : function( message )
		{
			$.voMessage.debug( message );
		}
	});
	
		
/* Not Standardized */

	$.fn.addInput = function(name,value,type)
	{
		type = type ? type : 'hidden';
		$('input[type='+type+'][name='+name+']',this).remove();
		$(this).prepend($('<input>').attr({'type':type,'name':name,'value':value}));
	};

	/*	Blog Entry:
		jQuery Comments() Plug-in To Access HTML Comments For DOM Templating
		
		Author:
		Ben Nadel / Kinky Solutions
		
		Link:
		http://www.bennadel.com/index.cfm?dax=blog:1563.view
		
	*/
	
	$.fn.comments = function( blnDeep ){
		var blnDeep = (blnDeep || false);
		var jComments = $( [] );
	 
		// Loop over each node to search its children for
		// comment nodes and element nodes (if deep search).
		this.each(
			function( intI, objNode ){
				var objChildNode = objNode.firstChild;
				var strParentID = $( this ).attr( "id" );
				// Keep looping over the top-level children
				// while we have a node to examine.
				while (objChildNode){
					// Check to see if this node is a comment.
					if (objChildNode.nodeType === 8){
						// We found a comment node. Add it to
						// the nodes collection wrapped in a
						// DIV (as we may have HTML).
						/*jComments = jComments.add(
							"<div rel='" + strParentID + "'>" +
							objChildNode.nodeValue +
							"</div>"
							);*/
						
						jComments = jComments.add(objChildNode.nodeValue); // Nick modifed this
					} else if (
						blnDeep &&
						(objChildNode.nodeType === 1)
						) {
	 
						// Traverse this node deeply.
						jComments = jComments.add(
							$( objChildNode ).comments( true )
							);
					}
					// Move to the next sibling.
					objChildNode = objChildNode.nextSibling;
				}
			}
			);
		// Return the jQuery comments collection.
		return( jComments );
	};
	
	// For comparing two arrays.. thanks David @ stackflow
	$.fn.compare = function(t) {
	    if (this.length != t.length) { return false; }
	    var a = this.sort(),
	        b = t.sort();
	    for (var i = 0; t[i]; i++) {
	        if (a[i] !== b[i]) { 
	                return false;
	        }
	    }
	    return true;
	};

	
	
	/*
	 * The backbone for click and discover interaction
	 */
	
	$.voCnd = {
			'settings':
				{
					'controller':null,
					'panel':'#cnd-navigation',
					'pagination':'.cnd-pagination',
					'contentDom': '',
					'contentDomRow': '',
					'filterPanel' : 0,
					'initContent' : 1
				},
			'hierarchyArray' : {}, // Cached arrays for mult-dimensional catgories
			'cache' : {'panel':{}}, // Added to accommodate panel state caching, deprecate the above eventually.
			'filterArray' : {}, // sent via ajax to controller
			'data':{}, // Set externally to be sent via ajax to controller
			'panelGroups' : {},
			'customContentFunction':{},
			'ajaxCompleteFunction': function( data, status ){},
			'resultTotal' : '', // resultTotal set in setInterface() and filterResults()
			'_currentXhr' : null,
			
			
			'init' :
				function()
				{
					$.voAjax({
						'actionController': $.voCnd.settings.controller,
						'dataObject':$.extend($.voCnd.data,{'vo-action':'init','filter_conditions':$.toJSON($.voCnd.filterArray)}),
						'beforeSend':[$.voCnd.ajaxBeforeSend,$.voCnd.waitingForResults],
						'success':[$.voCnd.ajaxSuccessFunction,$.voCnd.setInterface],
						'complete':[$.voCnd.ajaxCompleteFunction]
					});
				},
				
			'setPaginationMessage' :
				function()
				{
					if($.voCnd.resultTotal){
						var totalPage = Math.ceil( ($.voCnd.resultTotal / $.voCnd.filterArray['limit']));
						var currentPage = ($.voCnd.filterArray['page'] + 1);
						$('span.results .current-page',$.voCnd.settings.pagination).text( currentPage );
						$('span.results .total-page',$.voCnd.settings.pagination).text( totalPage );
						$('span.results .total-item',$.voCnd.settings.pagination).text($.voCnd.resultTotal);
						
						if($.voCnd.filterArray['page'] == 0 ){
							$('button.previous',$.voCnd.settings.pagination).hide();
						}else{
							$('button.previous',$.voCnd.settings.pagination).show();
						}
						
						if( totalPage == 1 || (totalPage == currentPage) ){
							$('button.next',$.voCnd.settings.pagination).hide();
							if(totalPage == 1){ $($.voCnd.settings.pagination).hide(); } 
						}else{
							$('button.next',$.voCnd.settings.pagination).show();
						}
					}
				},
				
			'setPagination' :
				function()
				{
					if(!$.voCnd.filterArray['page']){ $.voCnd.filterArray['page'] = 0; }
					
					

					$.voCnd.setPaginationMessage();
					$('button',$.voCnd.settings.pagination).unbind('click').click(function(e){
						$('html, body').animate({scrollTop:0}, 'slow'); // scroll to to on pagination
						var resultTotal = $.voCnd.resultTotal ? $.voCnd.resultTotal : $($.voCnd.settings.pagination+':first .total-item').text();
						
						if($(e.currentTarget).is('.previous')){
							var previousPage = $.voCnd.filterArray['page'] - 1;
							if( previousPage + 1 > 0 ){
								$.voCnd.filterArray['page'] = previousPage;
							}else{
								$.voCnd.filterArray['page'] = Math.floor( resultTotal / $.voCnd.filterArray['limit'] ); // go to last page
							}
							$.voCnd.getResults();
						}else{
							// page * limit < results
							var nextPage = $.voCnd.filterArray['page'] + 1;
							
							if( nextPage * $.voCnd.filterArray['limit'] < resultTotal ){
								
								$.voCnd.filterArray['page'] = nextPage;
							}else{
								$.voCnd.filterArray['page'] = 0;
							}
							$.voCnd.getResults();
						}
						
						$.voCnd.setPaginationMessage();
					});
				},
				
			'setFilterPanel' : 
				function( boolean )
				{
					$.voCnd.settings.filterPanel = boolean;
				},
				
			'setLimit' : 
				function( limit )
				{
					
					// For true customizabliity in SEO friendly versions I will need to get the limit from the DOM
					if( limit ){
						$.voCnd.filterArray.limit = limit;
					}else if( ! isNaN(parseInt($('#pagination-limit',$.voCnd.settings.pagination).text())) ){
						// Default if the limit is not set
						$.voCnd.filterArray.limit = parseInt($('#pagination-limit',$.voCnd.settings.pagination).text());
					}else{
						$.voCnd.filterArray.limit = 12;
					}
				},
				
			'getResults' :
				function()
				{
					// **Key efficiency logic - abort other ajax requests in favor of current
					// speeds up response
					if($.voCnd._currentXhr){ $.voCnd._currentXhr.abort(); }
					$.voCnd._currentXhr = $.voAjax({
						'actionController': $.voCnd.settings.controller,
						'dataObject':$.extend($.voCnd.data,{'vo-action':null,'filter_conditions':$.toJSON($.voCnd.filterArray)}),
						'beforeSend':[$.voCnd.ajaxBeforeSend,$.voCnd.waitingForResults],
						'success':[$.voCnd.ajaxSuccessFunction,$.voCnd.filterResults],
						'complete':[$.voCnd.ajaxCompleteFunction]
					});
				},
				
			'setController' :
				function( location )
				{
					$.voCnd.settings.controller = location;
				},
			
			'setContentDom' :
				function( contentDom, contentDomRow )
				{
					$.voCnd.settings.contentDom = contentDom;
					$.voCnd.settings.contentDomRow = contentDomRow;
				},
				
			'preload' :
				function()
				{
					
				},
				
			'setPanelGroup' :
				function( callerSettings )
				{
					// groupName, groupDisplayName , multiDimension, defaultValue
					// Build the group object, but don't overwrite it proecedurally.
					// The interface conrtoller gets precedence over the Ajax data settings for group.
					$.voCnd.panelGroups[callerSettings.group] = $.extend({
						'displayName' : (callerSettings.displayName ? callerSettings.displayName : callerSettings.group ),
						'ui' : callerSettings.ui, // if true then treat the group like a categoric hierarchy
						'multiDimension' : (callerSettings.multiDimension ? 1 : 0), // if true then treat the group like a categoric hierarchy
						'dataFormatSingle' : callerSettings.dataFormatSingle,
						'filter' : callerSettings.filter,
						'filterEffect' : callerSettings.filterEffect
					},$.voCnd.panelGroups[callerSettings.group] || {});
				},
			'setGroupDefault' :
				function( groupName, id )
				{
					if(typeof($.voCnd.filterArray[groupName]) != 'object'){
						$.voCnd.filterArray[groupName] = [];
					}
					$.voCnd.filterArray[groupName].push(id);
					$.voCnd.panelGroups[groupName] = $.extend({'defaultValue':id},$.voCnd.panelGroups[groupName] || {});
				},
				
			// Group class
			'group' : 
				function( domObject, groupName, groupSettings )
				{
					var thisGroup= this;
					
					this.menuClose =
						function( e )
						{
							$('h3 span',domObject).removeClass('ui-icon-triangle-1-s');
							$('h3 span',domObject).addClass('ui-icon-triangle-1-e');
							$('.cnd-list',domObject).slideUp('fast');
						};
						
					this.menuOpen = 
						function( e )
						{
							$('h3 span',domObject).removeClass('ui-icon-triangle-1-e');
							$('h3 span',domObject).addClass('ui-icon-triangle-1-s');
							$('.cnd-list',domObject).slideDown('fast');
						};
						
					this.itemClick =
						function( e )
						{
						var $thisObject = $(this).closest('li'); // Makes sure it is an li that gets the effects
						var hasChild = $thisObject.is('.ui-has-child');
						if($thisObject.is('.ui-state-disabled')){
							e.stopPropagation();
						}else{
							// Click on
							if(!$thisObject.is('.ui-state-active')){
								if(groupSettings.dataFormatSingle){
									$.voCnd.filterArray[groupName] = [$thisObject.attr('rel')];
									$(domObject).find('.cnd-button.cancel').show();
								}else{
								// Format interface so group data is sent as an array
									if((clearItemId =$.inArray($('.crumb-current',domObject).attr('rel'), $.voCnd.filterArray[groupName])) != -1){
										$.voCnd.filterArray[groupName].splice(clearItemId,1);
									}
									$.voCnd.filterArray[groupName].push($thisObject.attr('rel'));
									
									// Show filter-cancel button						
									if($.voCnd.filterArray[groupName].length  > 0){
										$(domObject).find('.cnd-button.cancel').show();
									}
								}
								$.voCnd.filterArray['page'] = 0;
								
								// Simulate toggle unbind current event and bind another
								/*$(this)
									.unbind('click')
									.bind('click',thisGroup.itemClickOff);*/
								if(groupSettings.dataFormatSingle){
									$('li',domObject).removeClass('ui-state-active');
									$('.handle',domObject).removeClass('ui-state-active');
								}
								
								if( $thisObject.is('.tier-1') ){
									$('li.ui-has-child ul',domObject).not($thisObject).slideUp('fast');
								}
								
								if( hasChild ){
									$('ul:first',$thisObject).slideDown('fast');
								}
								
								$thisObject.addClass('ui-state-active');
								$('.handle:first',$thisObject).addClass('ui-state-active');
								
								
								$.voCnd.getResults(); // Remember with new parameters we need to set page to 0
							}else{
							// Click off
								$(domObject).find('.cnd-button.cancel').show();
								
								// Remove the clicked item id
								var clickedItem = $thisObject.attr('rel');
								
								/*if(groupSettings.dataFormatSingle){
									$.voCnd.filterArray[groupName] = 0;
								}else{*/
									if((clearCategoryId = $.inArray(clickedItem,$.voCnd.filterArray[groupName])) != -1){
										$.voCnd.filterArray[groupName].splice(clearCategoryId,1);
									}
									if( $thisObject.is('.ui-has-child') ){
										$('ul',$thisObject).slideUp('fast');
									}
								//}
								$.voCnd.filterArray['page'] = 0;
								
								// Remove filter-cancel button
								if(groupSettings.dataFormatSingle || $.voCnd.filterArray[groupName].length  < 1){
									if($('.crumb-current',domObject).attr('rel')){
										/*if(groupSettings.dataFormatSingle){
											$.voCnd.filterArray[groupName] = $('.crumb-current',domObject).attr('rel');
										}else{*/
											$.voCnd.filterArray[groupName] = [$('.crumb-current',domObject).attr('rel')];
										//}
									}
									$(domObject).find('.cnd-button.cancel').hide();
								}
								
								// Set class to off
								$thisObject.removeClass('ui-state-active');
								$('.handle:first',$thisObject).removeClass('ui-state-active');
								$.voCnd.getResults(); // Remember with new parameters we need to set page to 0
							}
						}
					};
						
					this.reset =
						function( e )
						{
							$(this).hide();
							$('li.ui-state-active', domObject)
								.removeClass('ui-state-active');
							
							if($('.crumb-current',domObject).attr('rel')){
								/*if(groupSettings.dataFormatSingle){
									$.voCnd.filterArray[groupName] = $('.crumb-current',domObject).attr('rel');
								}else{*/
									$.voCnd.filterArray[groupName] = [$('.crumb-current',domObject).attr('rel')];
								//}
							}else{
								$.voCnd.filterArray[groupName] = [];
							}
							$.voCnd.getResults();
						};
					/**
					 * filterResults turns group items on or off based on returned panelFilter data.
					 */
					this.filterResults =
						function( groupFilter )
						{
/**/						// Hack: If results are filtered out, e.g. brand, then we need to remove them from query
							// and go get results again. This should be done on php side eventually

							var rerunResults = false;
							if( typeof(groupFilter) == 'object' && groupFilter.length > 0 ){
								var typeCast = typeof(groupFilter[0]);
								$('ul.cnd-list li', domObject).each(function(i, item){
									// For instances where we use string as an ID.
									var itemId = ( typeCast == 'number' ? parseInt( $(item).attr('rel') ) : $(item).attr('rel') );
									if($.inArray(itemId, groupFilter) != -1){
										if(groupSettings.filterEffect == 'hide'){
											$(item).show();
											
											// This is only for the slide down hierarchy display method 
											// Ideally for hierarchy child filter sends back
											// parents, if not, this will make sure the parent structure shows.
											$(item).parents('.'+groupName).show();
										}else{
											$(item).removeClass('ui-state-disabled');
										}
									}else{
										if(groupSettings.filterEffect == 'hide'){
											$(item).hide();
										}else{
											$(item).addClass('ui-state-disabled');
										}
										
										// Remove filter ids
										if((clearCategoryId = $.inArray(itemId, $.voCnd.filterArray[groupName]) ) != -1){
											$.voCnd.filterArray[groupName].splice(clearCategoryId,1);
											rerunResults = true;
										}	
									}
								});
							}else{
								if(groupFilter == 'showall'){
									if(groupSettings.filterEffect == 'hide'){
										$('ul.cnd-list li', domObject).show();
									}else{
										$('ul.cnd-list li', domObject).removeClass('ui-state-disabled');
									}
								}
							}
							if(rerunResults){ $.voCnd.getResults(); }
						};
						
					this.formatHierarchyMenuSlider =
					function( cid, data )
					{
						if(typeof($.voCnd.cache.panel[groupName]) != 'object'){
							
							$.voCnd.cache.panel[groupName] = (data.panel[groupName]['array'] ? data.panel[groupName]['array'] : data.panel[groupName] ); // cache the hierarchy
						}
						
						$('h3',domObject).prepend($('<span>'+groupSettings.displayName+'</span>'));
						
						var panelArray = (data.panel[groupName]['array'] ? data.panel[groupName]['array'] : data.panel[groupName] ); // Backward compat
						var fragment = document.createDocumentFragment();
						//////////////////////
						
						function printCategoryHierarchy( categoryArray, tier )
						{
							// If cid is set then start looking for the id
							if( typeof(retVal) != 'string' ){ var retVal = ''; };
							if( !tier ){ tier = 1; }

							$.each(categoryArray, function(key, category){
								var hasChildren = $(category.children).length > 0 ? true : false;
								retVal += '<li rel="'+key+'" class="'+groupName+' tier-'+tier+(hasChildren ? ' ui-has-child' : '')+'"><div class="handle">'+category.name+'</div>';
								
								if( hasChildren ){
									var i = tier + 1;
									retVal += '<ul'+( i > 1 ? ' style="display:none;"' : '' )+'>';
									retVal += printCategoryHierarchy( category.children, i );
									retVal += '</ul>';
								}
								retVal += '</li>';
								
							});
							
							return retVal;
						};
						
						var $categoryStructure = $(printCategoryHierarchy( panelArray ));
						
						// Activate any li's onload
						$.each($.voCnd.filterArray[groupName], function(key, filterId){
							$('li[rel='+filterId+']', $categoryStructure).addClass('ui-state-active')
								.find('.handle').addClass('ui-state-active')
								.parents('ul').show();
						});
						
						$('.cnd-list',domObject)
							.append( $categoryStructure )
							.find('div.handle').click( thisGroup.itemClick ).end();
					};
						
					this.formatHierarchyMenuPortal =
					function( cid, data )
					{
						// Initialize cache and breadcrumb dom here
						if(typeof($.voCnd.hierarchyArray[groupName]) != 'object'){
							
							$.voCnd.hierarchyArray[groupName] = (data.panel[groupName]['array'] ? data.panel[groupName]['array'] : data.panel[groupName] ); // cache the hierarchy
							
							$('h3',domObject).append('<span class="crumb-parent" />');
							$('h3',domObject).append('<span class="crumb-current" />');
							
							// Events		
							
							// Current Category and Parent
							$('h3 span.crumb-parent',domObject).unbind('click').click(function(){
								/*if(groupSettings.dataFormatSingle){
									if($(this).attr('rel') > 0){
										$.voCnd.filterArray[groupName] = $(this).attr('rel');
									}else{
										$.voCnd.filterArray[groupName] = '';
									}
								}else{*/
									$.voCnd.filterArray[groupName] = [];
									if($(this).attr('rel') > 0){
										$.voCnd.filterArray[groupName].push($(this).attr('rel'));
									}
								//}
								
								$.voCnd.filterArray['page'] = 0;
								$(domObject).find('.cnd-button.cancel').hide();
								$.voCnd.getResults();
								thisGroup.formatHierarchyMenuPortal( $(this).attr('rel') );
								return false;
							});
						}
						
						function getCategoryStructure(categoryArray, cid, parentObject)
						{
						// first level if there will be no parent object, the return category array
							
							if(typeof(cid) == 'undefined' || cid == 0 ){
								return {'parent':null,'categoryArray':{'children':categoryArray}};
							}else{
							// If cid is set then start looking for the id
								if(typeof(retVal) != 'object'){ var retVal = {}; };
								$.each(categoryArray, function(key, category){
									if( category.id == cid ){
										parentObject = typeof(parentObject) == 'object' ? parentObject : null;
										
										// If this category has no children, then step back up the branch
										if($(category.children).length > 0){
											retVal = {'parent':parentObject,'categoryArray':category};
										}else{
											if(parentObject != null){
												retVal = {'parent':parentObject.parentObject,'categoryArray':parentObject};
											}else{
												retVal = {'parent':null,'categoryArray':{'children':categoryArray}};
											}
										}
										return false;
									}else{
										
										if( $(category.children).length > 0 && typeof(retVal.categoryArray) != 'object' ){
											var parent = category;
											parent.id = category.id;
											parent.parentObject = parentObject; // (parent's parent) allows for stepping back up the branch
											retVal = getCategoryStructure(category.children, cid, parent);
										}
									}
								});
								
								return retVal;
							}
						};
						
						var categoryStructure = getCategoryStructure($.voCnd.hierarchyArray[groupName],cid);
						
						var category = categoryStructure.categoryArray;
						var parent = categoryStructure.parent ? categoryStructure.parent : null;
						
							if(cid > 0){
								// $.voMessage.debug(category.name);
								if(category.name){
									$('h3 span.crumb-current',domObject).html('&gt; '+category.name).attr('rel',category.id).css('display','block');
								}
							}else{
								$('h3 span.crumb-current',domObject).empty().removeAttr('rel').hide();
							}
							
							if(parent){
								$('h3 span.crumb-parent',domObject).text(parent.name).attr('rel',parent.id);
							}else{
								
								$('h3 span.crumb-parent',domObject).text(groupSettings.displayName).attr('rel',0);
							}
							
							$('li',domObject).remove();
							if( category.children ){
								var fragment = document.createDocumentFragment();
								
								$.each(category.children, function(i,child){
					
									var newList = $('<li rel=\"'+child.id+'\" class="'+groupName+' ui-state-default">'+child.name+'</li>');
									
									//if(!groupSettings.dataFormatSingle){
										if($.inArray(child.id,$.voCnd.filterArray[groupName]) != -1){
											newList.addClass('ui-state-active');
											if(!groupSettings.dataFormatSingle){
												$(domObject).find('.cnd-button.cancel').show();
											}
										}
									/*}else{
										if(child.id == $.voCnd.filterArray[groupName]){
											newList.addClass('ui-state-active');
											$(domObject).find('.cnd-button.cancel').show();
										}
									}*/
									
									if($(child.children).length > 0 ){
										newList.click(function(){
										// If the single data format is set, then set the ajax data as a string rather than an array
											/*if(groupSettings.dataFormatSingle){
												$.voCnd.filterArray[groupName] = $(this).attr('rel');
											}else{*/
												$.voCnd.filterArray[groupName] = [];
												$.voCnd.filterArray[groupName].push($(this).attr('rel'));
											//}
											$.voCnd.filterArray['page'] = 0;
											//$.voMessage.debug($.voCnd.filterArray[groupName]);
											$.voCnd.getResults();
											thisGroup.formatHierarchyMenuPortal($(this).attr('rel'));
											//
											$('.cnd-list li.ui-state-active',domObject).removeClass('ui-state-active');
											$('.cnd-list li[rel='+$(this).attr('rel')+']',domObject).unbind('hover').removeClass('ui-state-hover').addClass('ui-state-active');
											return false;
										});
									}else{
										// Removing the parent only matters on array formatted ajax data
										if(!groupSettings.dataFormatSingle){
											// !! Important, remove the parent id from the array.
											if((clearParent = $.inArray(category.id,$.voCnd.filterArray[groupName])) != -1){
												$.voCnd.filterArray[groupName].splice(clearParent,1);
											}
										}
										newList.click(thisGroup.itemClick);
									}
									fragment.appendChild( newList.get(0) );
								});
								
								$('.cnd-list', domObject).append( fragment );
							}
					};
						
					this.formatSingleDimension =
						function( data )
						{
							if(typeof($.voCnd.cache.panel[groupName]) != 'object'){
								$.voCnd.cache.panel[groupName] = (data.panel[groupName]['array'] ? data.panel[groupName]['array'] : data.panel[groupName] ); // cache the hierarchy
							}
							$('h3',domObject).prepend($('<span>'+groupSettings.displayName+'</span>'));
							
							var panelArray = (data.panel[groupName]['array'] ? data.panel[groupName]['array'] : data.panel[groupName] ); // Backward compat
							var fragment = document.createDocumentFragment();
							
							$.each(panelArray, function( liKey, liData ){
								var newList = $('<li rel=\"'+liKey+'\" class="'+groupName+' ui-state-default">'+liData.name+'</li>');
								$(newList).attr('rel',liKey);
								
								// For onload, if the id is in the filterArray, the add an active state
								if($.inArray( liKey, $.voCnd.filterArray[groupName] ) != -1){
									newList.addClass('ui-state-active');
								}
								
								// NOT SURE why this code was hid conditionally on the data format being single.
								// Keep it around for a while until I remember whay I did this..
								/*if( ! groupSettings.dataFormatSingle ){
									if($.inArray( liKey, $.voCnd.filterArray[groupName] ) != -1){
										newList.addClass('ui-state-active');
										$(domObject).find('.cnd-button.cancel').show();
									}
								}*/
								$(newList).click( thisGroup.itemClick );
								fragment.appendChild( newList.get(0) );
							});
							
							$('.cnd-list',domObject).append( fragment );
						};
						
					return this;
				},
				
			'setInterface' :
				function(data, status)
				{
					$.voCnd.resultTotal = data.resultTotal;
					$.voCnd.setPagination();
					
				// Set panel dynamically from ajax feed
					$.each(data.panel, function(groupName, groupSettings){
						$.voCnd.setPanelGroup({
							'group':groupName,
							'displayName':groupSettings.display_name,
							'ui':(groupSettings.ui ? groupSettings.ui : 'single_menu'), // single_menu, hierarchy_menu_slider, hierarchy_menu_portal, range_slider
							'multiDimension':(groupSettings.multi_dimension ? groupSettings.multi_dimension : 0),
							'dataFormatSingle':(groupSettings.data_format_single ? 1 : 0),
							'filter':(groupSettings.filter_panel ? 1 : 0),
							'filterEffect':(groupSettings.filter_effect ? groupSettings.filter_effect : 'disable')
						});
					});
					
					// Set Each Panel Group
					var fragment = document.createDocumentFragment();
					$.each(data.panel, function(groupName){
						var groupSettings = $.voCnd.panelGroups[groupName];
					
						if( typeof($.voCnd.filterArray[groupName]) != 'object'){ 
							groupSettings.defaultValue = $.voCnd.filterArray[groupName] ? $.voCnd.filterArray[groupName] : [];
							$.voCnd.filterArray[groupName] = $.voCnd.filterArray[groupName] ? $.voCnd.filterArray[groupName] : []; 
						}
						// else{
							//groupSettings.defaultValue = $.voCnd.filterArray[groupName] ? $.voCnd.filterArray[groupName] : "";
							//$.voCnd.filterArray[groupName] = $.voCnd.filterArray[groupName] ? $.voCnd.filterArray[groupName] : "";
						//}*/
	
						object = $('<div id="cnd-'+ groupName +'" class="cnd-header" />');
						groupObject = new $.voCnd.group( object, groupName, groupSettings );

						$('li',object).remove();
						var collapseButton = $('<div class="ui-state-default ui-corner-all cnd-button collapse"><span class="ui-icon ui-icon-triangle-1-s" /></div>')
												.toggle(groupObject.menuClose, groupObject.menuOpen );
						var cancelButton = $('<div class="ui-state-default ui-corner-all cnd-button cancel"><span class="ui-icon ui-icon-cancel" /></div>')
												.hide()
												.bind('click',groupObject.reset);

						$('<h3 />')
							.addClass('ui-corner-all')
							//.text( groupSettings.displayName ) for now I am doing this in the singleDim and multiDim functions
							.appendTo(object)
							.append(cancelButton)
							.append(collapseButton);
						
						$('<ul class="cnd-list"></ul>').appendTo(object);
						
						// Load group's item list
						switch( groupSettings.ui ){
						case 'single_menu':
							groupObject.formatSingleDimension( data );
							break;
							
						case 'hierarchy_menu_slider':
							groupObject.formatHierarchyMenuSlider((groupSettings.defaultValue ? groupSettings.defaultValue : 0), data);
							break;
							
						case 'hierarchy_menu_portal':
							groupObject.formatHierarchyMenuPortal((groupSettings.defaultValue ? groupSettings.defaultValue : 0), data);
							break;
							
						case 'range_slider':
							//
							break;
						}
						
						if(typeof(data.panelFilter[groupName]) == 'object' || data.panelFilter[groupName] == 'showall'){
							object.show();
							if(groupSettings.filter){
								groupObject.filterResults( data.panelFilter[groupName] );
							}
						}else{
							object.hide();
						}
						fragment.appendChild( object.get(0) );
					});
					
					$($.voCnd.settings.panel).empty().append(fragment);
					
					// Set Content
					if( $.voCnd.settings.initContent ){
						$.voCnd.setContent(data.content);
					}
					$('.waiting-for-results').hide();
					$($.voCnd.settings.contentDom).fadeIn('fast');
				},
			'waitingForResults' :
				function()
				{
					$($.voCnd.settings.contentDom).hide();
					$('.no-results').hide();
					$('.waiting-for-results').show();
				},
				
			'filterResults' :
				function( data )
				{
					$.voCnd.resultTotal = data.resultTotal;
					$.voCnd.setPaginationMessage();
					// Set Each Panel Group
					$.each($.voCnd.panelGroups, function(groupName, groupSettings){
						var groupObject = new $.voCnd.group( $('#cnd-'+ groupName), groupName, groupSettings );
						if(typeof(data.panelFilter[groupName]) == 'object' || data.panelFilter[groupName] == 'showall'){
							$('#cnd-'+ groupName).show();
							if(groupSettings.filter){
								groupObject.filterResults( data.panelFilter[groupName] );
							}
						}else{
							$('#cnd-'+ groupName).hide();
						}
					});
					
					// Set Content
					$('.waiting-for-results').hide();
					// Slower but more flexible for interface developers
					if(data.content_html){
						$($.voCnd.settings.contentDom).replaceWith($(data.content_html).filter('ul'));
					}else{
					// Faster because unformatted Json data is sent and is formatted by javascript.
						$.voCnd.setContent(data.content);
					}
					//$($.voCnd.settings.contentDom).fadeIn('fast');
				},
				
			'setContent':
				function( content )
				{
				var thisObject = this;
					var $rowTemplate = $(thisObject.settings.contentDom).comments();
					$(thisObject.settings.contentDomRow,thisObject.settings.contentDom).remove();
					
					if($(content).length > 0 ){
						$('.no-results').hide();
						var fragment = document.createDocumentFragment();
						$.each(content, function(key, contentItem){
							// Set list display
								var $newRow = $rowTemplate.clone();
								$newRow.setDataDisplay( contentItem );
								$.voCnd.customContentFunction( key, contentItem, $newRow );
								fragment.appendChild($newRow.get(0));
						});
						
						$(thisObject.settings.contentDom).append( fragment );
						
						$($.voCnd.settings.contentDom).fadeIn('fast');
					}else{
						$('.no-results').show();
					}
				}
	};
	
	$.voSlugFormat = function( slug )
	{
		var newSlug = slug.replace(/[^a-zA-Z0-9\s\-]/g,'');
		newSlug = newSlug.trim().replace(/\s+/g,'-');
		return newSlug.toLowerCase();
	};
	
/* Non-Standard */
	$.fn.setRSSItemDisplay = function(jsonObject)
	{
		$('.title',this).text(jsonObject.title[0].Text);
		$('.link',this).attr('href',jsonObject.link[0].Text);
		$('.description',this).html(jsonObject.description[0].Text);
		
		//$(this).show();
		return this;
	};
	
	$.fn.setRSSChannelDisplay = function(jsonObject)
	{
		$('.title',this).text(jsonObject.title[0].Text);
		$('.link',this).attr('href',jsonObject.link[0].Text);
		$('.description',this).text(jsonObject.description[0].Text);
		//$('.image',this).attr('src',jsonObject.image[0].url[0].Text);
		
		//$(this).show();
		return this;
	};
	
	// Depends on jquery.XMLUtils.pack.js
	$.fn.XMLReader = function(callerSettings)
	{
		var settings = $.extend({
			feed: false,
			sortName: 'title',
			sortOrder: 'ASC',
			limit: false
		}, callerSettings || {});
		
		var object = this;
		var feed = settings.feed ? settings.feed : $(this).attr('data-feed');
		
		$.ajax({
			type: 'POST',
			url: 'xml',
			data:{feed:feed},
			dataType: 'xml', //Make sure that you specify the type of file you expecting (XML)
			complete: function(data) {
				// Grab the display template
				var feedChannelTemplate = $('.rss-channel',object).remove();
				var feedItemTemplate = $('.rss-items .template',object).remove();
				var json = $.xmlToJSON(data.responseXML); //Please notice that we use responseXML here which is DOMDocument object
				
				$(object).prepend($(feedChannelTemplate).setRSSChannelDisplay(json.channel[0]));
				
				// Now I want to sort all item nodes by their value
				json.channel[0].item.SortByNode(settings.sortName,settings.sortOrder); //Default order is ASC
				for(var i = 0; i < json.channel[0].item.length; i++) {
					var newList = $(feedItemTemplate).clone();
					$('.rss-items', object).append($(newList).setRSSItemDisplay(json.channel[0].item[i]));
				}
				
				if( settings.limit > 0 ){
					$('.rss-items li:gt('+ (settings.limit - 1) +')', object).hide();
				}
			}
		});
	};
	
/* Deprecated */
	
	// Deprecated because setDataDisplay    
	$.fn.setAddressForm = function(addressObject)
	{
		$(this).attr('data-json','{id:' + addressObject.id + '}');
		$('input[name=full_name]',this).val(addressObject.full_name);
		$('input[name=address1]',this).val(addressObject.address1);
		$('input[name=address2]',this).val(addressObject.address2);
		$('input[name=city]',this).val(addressObject.city);
		$('input[name=state]',this).val(addressObject.state);
		$('input[name=zip]',this).val(addressObject.zip);
		$('input[name=phone]',this).val(addressObject.phone);
		
		addressObject.default_billing ? $('input[name=type_billing]').attr('checked','checked') : $('input[name=type_billing]').attr('checked','');
		addressObject.default_shipping ? $('input[name=type_shipping]').attr('checked','checked') : $('input[name=type_shipping]').attr('checked','');
		
		//addressObject.default_billing ? $('input[name=phone]',this)
		
		return this;
	};
	
	$.fn.loadFileList = function( callerSettings )
	{
		var settings = $.extend({
			actionPath: false,
			data: {},
			sortOrder: 'ASC'
		}, callerSettings || {});
		
		var $object = $(this);
		$object.find('li').empty();
		
		// Grab the display template from the pop-boxes version that way cloned
		// manipulations don't interfere with process
		var $fileTemplate = $object.comments();

		$.getJSON(settings.actionPath, settings.data, function( data ) {
			$.each( data.files, function(key, file ) {
				$newFile = $fileTemplate.clone();
				$newFile.setFileInterface( file );
				$object.append($newFile);
			});
			
			// Delete button to remove one or more images
			// Depends on jquery.json.js - $.toJSON()
			$(document).unbind().keyup(function(e){
				if(e.keyCode == 46){					
					if(confirm('Are you sure you want to delete?')){
						var idArray = new Object();
						$('li.ui-selected', $('#vo2-overlays #photo-list')).each(function(i){
							idArray[i] = $(this).metadata().id;
							$(this).remove();
						});
						
						$.getJSON( $.jqPHP.getUrl('admin-cms-image-remove'),
									{page_id:$('#page').metadata().id,image_id:$.toJSON(idArray)},
									function(){});
					}
				}
			});
		});
	};
	
})(jQuery);
	
// This assists file uploads. We can clean this up later
	var newStageImageTemplate; // For adding dom outside of ready function
	var voFile = function( callerSettings ){
		
		this.settings = $.extend({
			'action':'/action/admin/cms/file-upload',
			'ulDom':'',
			'relateId':'',
			'relateType':''
		}, callerSettings);
		
		this.ulDom = typeof(this.settings.ulDom) == 'object' ?  this.settings.ulDom : $(this.settings.ulDom);
		var obj = this;
		
		this.addFileToDom = function( id, fileName )
		{
			var $newRow = typeof(newStageImageTemplate) == 'object' ? newStageImageTemplate.clone() : this.ulDom.comments();
			$newRow.attr('id','loading'+id);
			$newRow.attr('class','loading');
			var $progressBar = $('<div class="progress-bar" />').progressbar({value:0});
			$newRow.append('<div class="loading-title">Loading <span class="loading-file-name">'+ fileName +'</span></div>');
			$newRow.append($progressBar);
			$('img', $newRow).hide();
			
			$newRow.appendTo(this.ulDom);
		};
		
		this.setProgressBar = function setProgressBar( id, percent )
		{
			$('#loading'+id+' .progress-bar').progressbar('option', 'value', percent);
		};
		
		this.loadThumbnail = function loadThumbnail( id, xhr )
		{
			var $loadingBox = $('#loading'+id);
			$('.progress-bar', $loadingBox).progressbar('destroy');
			
			// Handle HTTP status response
			switch( xhr.status ){
			case 200: // Successful response
				// Load object here
				var data = typeof(xhr.responseText) == 'string' ? $.evalJSON(xhr.responseText) : xhr.responseText;
				
				if( data.error_message ){
					var $removeButton = $('<div><a href="#">Remove</a></div>').click(function(){ $loadingBox.remove(); return false; });
					$('.loading-title, .image-controls',$loadingBox).remove();
					$('.image',$loadingBox).text(data.error_message).append($removeButton);
					$loadingBox.addClass('bad');
				}else{
					$('.progress-bar, .loading-title',$loadingBox).remove();
					this.setInterface( '#loading'+id, data );
					$($loadingBox).hover(function(e){
						$('.image-controls',this).fadeIn('fast');
					},
					function(){
						$('.image-controls',this).fadeOut('fast');
					});
				}
				$loadingBox.attr('id','').removeClass('loading');
			break;
			
			case 500: // Server error, usually a program error
				var $removeButton = $('<div><a href="#">Remove</a></div>').click(function(){ $loadingBox.remove(); return false; });
				var fileName = $('.loading-file-name', $loadingBox).text();
				$('.image-controls',$loadingBox).remove();
				$('.loading-title',$loadingBox).html('Error loading <strong>'+ fileName + '</strong>. If problems persist send file to support.').append($removeButton);
				$loadingBox.addClass('bad');
				$loadingBox.attr('id','').removeClass('loading');
			break;
			
			}
			
		};
		
		this.setInterface = function( loadingObject, data )
		{
			// Set externally 
		};
		
		this.uploadFiles = function ( files ) {
			if( (typeof(FileReader) != 'function') ){
				//alert($(this).val());
				var $inputFile = $(this);
				var $thisParent = $(this).parent();
				var $inputFileClone = $(this).clone(true);
				$inputFile.attr('name','Filedata');
				var max = 10000;
				var id = obj.ulDom.children('li').length;
				obj.setInterface = function( loadingObject, data )
				{
					$(loadingObject).setFileInterface( data.file );
				};
				obj.addFileToDom( id, $(this).val().replace(/.*fakepath\\/,'') );
				
				var $iframe = $( '<iframe name="postframe" id="post-frame" src="about:none" style="visibility:hidden; width:0; height:0;" />' );
				$('body').append( $iframe );
				var $uploadForm = $('<form />').attr({
					'action':obj.settings.action,
					'method':'POST',
					'fileData':$inputFile.val(),
					'MAX_FILE_SIZE':max,
					'enctype':'multipart/form-data',
					'encoding':'multipart/form-data',
					'target':'postframe'
				}).css({'visibility':'hidden'}).append($inputFile).appendTo('body').submit();
				
				//need to get contents of the iframe
				$iframe.load(
					function(){
						iframeContents = $iframe[0].contentDocument.body.innerHTML;
						// This error handling could be spoofed, but it is all I have right now
						// I know all good response should be a proper JSON string, so I check
						// for the first left curly bracket {.
						if( iframeContents[0] == '{' ){
							var data = $.evalJSON(iframeContents);
							// Fake an xhr 200 status for now
							var fakeXhr = {'status':200,'responseText':data};
							obj.loadThumbnail( id, fakeXhr );
						}else{
							obj.loadThumbnail( id, {'status':500} );
						}
						$uploadForm.remove();
						$iframe.remove();
						// For IE Only
						$thisParent.prepend($inputFileClone);
					}
				);
				
			}else{
				var fileList = this.files ? this.files : files;
				var uploadNext = 0;
				
				$.each( fileList, function(i,file){
					obj.addFileToDom( i, file.name );
				});
				
				function upload(id, file){
					
				    var reader = new FileReader();
				    
				  // var imageType = /image.*/;
				  /* if (file.type.match(imageType)) {
				    	var img = new Image();
					    reader.onload = function(e)
					    {
					    	img.src = e.target.result;
					    	img.onload = function()
					    	{
					    		$.log(img.width);
					    	};
					    };
				    }*/
	
				    var fileName = file.name;
				    var fileSize = file.size;
				    var fileType = file.type;
					reader.onloadend = function()
					{
						var source = reader.result;
						var jsonObject = {
							'id':obj.settings.relateId,
							'name':fileName,
							'size':fileSize,
							'type':fileType,
							'source':source,
							'relate':obj.settings.relateType
						};
						var xhr = new XMLHttpRequest();
						
						xhr.upload.addEventListener("progress", function(e) {
					    	var percentage = Math.round((e.loaded * 100) / e.total);
					    	obj.setProgressBar( id, percentage );
					    }, false);
	
						xhr.upload.addEventListener("load", function(e) {
							obj.setProgressBar( id, 100 );
					    }, false);
						
						var jsonString = $.toJSON(jsonObject);
						xhr.open("POST", obj.settings.action, true);
						xhr.onload = function(event)
						{
							obj.loadThumbnail( id, xhr );
							
							if(fileList.length > (uploadNext + 1)){
								uploadNext++;
								upload(uploadNext,fileList[uploadNext]);
							}
						};
						
						xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
						
						xhr.sendAsBinary(jsonString);
					};
	
					reader.readAsDataURL(file);
				};
				
				upload(uploadNext,fileList[uploadNext]);
			}
		};
	};

// END OF PLUGINS

// Body stuff

/*
Example DOM:
<ul id="n1">
	<li>Level 1
		<ul>
			<li>Level 2</li>
			<li>Level 2</li>
		</ul>
	</li>
	<li>Level 1</li>
</ul>

*/

$(document).ready(function(){
	$("#menu-main-menu").navigationMenu();
// Forms

	$.fn.Placeholder = function()
	{
		$(this).each(function( ){

			var forField	= $(this).attr("for");
			var input		= $('input#'+forField);
			var replaceMethod = ( $(this).is('.overlay') ) ? 'overlay' : 'value';

				switch( replaceMethod ){
				case 'overlay':
					var $label = $(this);
					var labelText	= $label.text();
					$(input).focus(function(){
						if($(input).val() == labelText){
							$(input).val('');
						}
						$label.css('text-indent','-10000px');

					}).blur(function(){
						if($(input).val() == ''){
							$label.css('text-indent','');
						}
					});
					if( input.val() ){
						$label.css('text-indent','-10000px');
					}
					$label.click(function(){ $(input).trigger('focus'); });
				break;

				case 'value':
					var labelText	= $(this).remove().text();
					if( $(input).val() == '' ){ $(input).addClass('placeholder').val(labelText); }

					$(input).focus(function(){
						if( this.value == labelText ){
							$(this).removeClass('placeholder').val('');
						}
					}).blur(function(){
						if( this.value == '' ){
							$(this).addClass('placeholder').val(labelText);
						}
					});
				break;
				}

		});
	};

	$('label.hint').Placeholder();
	$('label[for="input_27_1_3"]').text('First Name');
	$('label[for="input_27_1_6"]').text('Last Name');
	$('#gform_fields_27 label').Placeholder();

	// Custom video/audio toggle for single recording page
	$('#media-toggle li').click(function(e){
		switch( $(this).attr('class') ){
		case 'video selected':
			$('#player .media-space-container').fadeOut(400,function(){
				$('#vimeospace').fadeIn(300);
			});
			break;
			
		case 'audio':
			$('#vimeospace').fadeOut(400,function(){
				$('#player .media-space-container').fadeIn(300);
			});
			
			break;
		}
	});
	
	$cartPopup = $('#cart-popup');
	$cartBlock = $('div.cart-block');
	
	function getCartTotal()
	{
		var total = 0;
		$('.cart-block table.list tbody tr').each(function(){
			total += $(this).data('attributes')['line_price'];
		});
		return total / 100;
	};
	
	function getCartCount()
	{
		var total = 0;
		$('.cart-block table.list tbody tr').each(function(){
			total += $(this).data('attributes')['quantity'];
		});
		return total;
	};
	
	function updateQuantityTotal( count )
	{
		$('#shopping-cart .cart .quantity').text( count );
		$('.quantity',$cartPopup).text( count + ' Item' + (count > 1 ? 's' :'') );
	};
	
	function updateCart( cart )
	{
		if( cart.items.length > 0 ){
			$('table,.totals', $cartBlock ).show();
			$('.no-cart-results',$cartBlock).hide();
			var $rowTemplate = $('.cart-block table.list tbody').comments();
			$('.cart-block table.list tbody tr').remove();
			$('#shopping-cart .quantity-link span').show().text(cart.item_count);
			updateQuantityTotal( cart.item_count );
			$('.cart-total').text('$'+parseInt(cart.total_price/100));
			
			var fragment = document.createDocumentFragment();
			$.each( cart.items, function(key, item){
				var $newRow = $rowTemplate.clone();
				$newRow.data('attributes',item);
				var $quantityField = $('.quantity input',$newRow);
				var $deleteField = $('.delete',$newRow);
				var $priceField = $('.price',$newRow);
				var $linePriceField = $('.total',$newRow);
				$('.thumbnail',$newRow).attr('src',item.photo);
				
				$('.title .name',$newRow).text(item.title);		
				$quantityField.val(item.quantity);
				$deleteField.text('(remove)');
				$priceField.text('$'+parseInt((item.price/100)));
				$linePriceField.text('$'+parseInt((item.line_price/100)));
				
				var initialQty = item.quantity;
				
				$deleteField.click(function(){
					$.voAjax({
						actionController:'/shopify_sessions/shopify-cart.php',
						verifyAction:'Are you sure?',
						dataObject:{'action':'remove_item','item_id':item.id},
						success:[function( data ){
							updateCart( data.cart );
						}]
					})
				});
				
				var _currentXhr = false;
				$quantityField.keyup(function(){
					var qty = $(this).val();
					if(qty !== '' && qty !== '0'){
						
						if( _currentXhr ){ _currentXhr.abort(); }
						_currentXhr = $.voAjax({
							actionController:'/shopify_sessions/shopify-cart.php',
							dataObject:{'action':'update_item','quantity':qty,'item_id':item.id}
						});
						
						qty = parseInt(qty);
						$(this).val(qty);
						$(this).unbind('blur');
						initialQty = qty;
						var $parentTr = $(this).parents('tr');
						$parentTr.data('attributes')['quantity'] = qty;
						$parentTr.data('attributes')['line_price'] = qty * item.price;
						$linePriceField.text('$'+parseInt((qty * item.price / 100)));
						$('.cart-total').text('$'+getCartTotal());
						updateQuantityTotal( cart.item_count );
						$('.quantity-link span').text(getCartCount());
					}else{
						// don't leave quantity field blank
						$(this).unbind('blur').blur(function(){
							$(this).val(initialQty);
							$(this).trigger('keyup');
						});
					}
				});
	
				fragment.appendChild($newRow.get(0));
			});
			$('.cart-block table.list tbody').append( fragment );
		}else{
			$('.cart-total').text('$0.00');
			$('.quantity',$cartPopup).text('0 Items');
			$('table,.totals', $cartBlock ).hide();
			$('.no-cart-results',$cartBlock).show();
			$('#shopping-cart .quantity-link span').hide();
		}
	};

	$cartBlock.dialog({
		autoOpen: false,
		modal: true,
		resizable: false,
		width: 760,
		height: 400
	});
	
	$('a.button.close',$cartBlock).click(function(){
		$cartBlock.dialog('close');
	});
	
	$('a.button.cart',$cartPopup).click(function(){
		$('div.cart-block').dialog('open');
		$cartPopup.hide();
		return false;
	});
	
	// Added by Jonathan on 11.8.11
	
	$videoBlock = $('div.video-block');

	$videoBlock.dialog({
		autoOpen: false,
		modal: true,
		resizable: false,
		width: 760,
		height: 400
	});

	$('a.button.close').click(function(){
		$videoBlock.dialog('close');
	});
	
	$('a.publishing-popup').click(function(){
		$videoBlock.dialog('open');
	});
	
	// End added by Jonathan
	
	$(window).scroll(function(){
		var scrollTop = $(this).scrollTop();
		if( scrollTop > 57 ){
			$cartPopup.css('top',scrollTop - 32);
		}else{
			$cartPopup.css('top','');
		}
	});
	
	$('#cart-popup a.button.close').click(function(){
		$('#cart-popup').hide();
		return false;
	});
	
	$('#shopping-cart a.quantity-link, #shopping-cart a.checkout').click(function(){
		$('#cart-popup').show();
		return false;
	});
	
	$.ajax({
		url:'/shopify_sessions/shopify-cart.php',
		type:'POST',
		dataType:'json',
		data:'action=get_cart',
		success:function(data,status){
			if( data.cart ){
				// Update the cart at the top.
				updateCart( data.cart );
				
				$('.cart-block a.button.checkout,#cart-popup .checkout').data('checkoutUrl', data.checkout_url).click(function(){
					window.location.href = $(this).data('checkoutUrl');
				});
				//$('.cart-block a.button.checkout,#cart-popup .checkout').attr('href',data.checkout_url);
			}
		}
	});
	
	// Shopify context handler
	$('form.add-to-cart').attr('target','').submit(function(){
		$thisAddForm = $(this);
		$.ajax({
			url:'/shopify_sessions/shopify-cart.php',
			type:'POST',
			dataType:'json',
			data:'action=add_item&'+$(this).find(':input').serialize(),
			beforeSend: function(){ $('.item-added-to-cart-loading',$thisAddForm).show(); },
			success:function(data,status){
				if( data.cart ){
					// Show the item was added in context.
					$('.cart-block a.button.checkout,#cart-popup .checkout').data('checkoutUrl', data.checkout_url);
					$cartPopup.fadeIn('fast');
					$('.item-added-to-cart-loading',$thisAddForm).hide();
					$('.item-added-to-cart',$thisAddForm).fadeIn(function(){
						setTimeout(function(){ $('.item-added-to-cart',$thisAddForm).fadeOut(); },7000);
					});
					// Update the cart at the top.
					updateCart( data.cart );
				}
			}
		});
		
		return false;
	});
	

});
//Handle money formatting
function isThousands( position )
{
	if (Math.floor(position/3)*3==position) return true;
	return false;
};

function formatMoney(theNumber,theCurrency,theThousands,theDecimal)
{
	theNumber = parseFloat(theNumber);
	
	if( !isNaN(theNumber) ){
	theNumber = parseFloat(theNumber);
	var theOutput = theCurrency;
	if( theNumber >= 0 ){
		var theDecimalDigits = Math.round((theNumber*100)-(Math.floor(theNumber)*100));
		theDecimalDigits= ""+ (theDecimalDigits + "0").substring(0,2);
		theNumber = ""+Math.floor(theNumber);
		
	}else{
		var theDecimalDigits = Math.abs(Math.round((theNumber*100)-(Math.ceil(theNumber)*100)));
		theDecimalDigits= ""+ (theDecimalDigits + "0").substring(0,2);
		theNumber = ""+Math.abs(Math.ceil(theNumber));
		theOutput += "-"; 
	}
	
	for (x=0; x<theNumber.length; x++) {
		theOutput += theNumber.substring(x,x+1);
		if (isThousands(theNumber.length-x-1) && (theNumber.length-x-1!=0)) {
			theOutput += theThousands;
		};
	};
	
	theOutput += theDecimal + theDecimalDigits;
	}else{
		theOutput = +'0.00';
	}
	return theOutput;
};

function formatErrorMessage( data_error_message, data_dom_error )
{
	// Move to top of page
	$('html, body').animate({scrollTop:0}, 'slow');
	
	if(data_error_message){
		var error_message = '';
		if(typeof(data_error_message) == 'object'){
			$.each(data_error_message,function(i,message){
				error_message += '<div>'+message+'</div>';
			});
		}else{
			error_message = data_error_message;
		}
		$('#error-message').html(error_message).fadeIn('fast');
		$(':input.error').removeClass('error');
		$.each(data_dom_error,function(i,class_name){
			$(':input.'+class_name).addClass('error');
			$(':input.'+class_name).keyup(function(e){
				if( $(this).val() == '' ){
					$(this).addClass('error');
				}else{
					$(this).removeClass('error');
				}
			});
		});
	}else{
		$('#error-message').fadeOut('fast');
	}
};
