/* zz.js
 * 
 * Zig-Zag Quotes for Matt Buchanan by Peter Asquith, wasabicube
 *
 * Relies on:
 * 	 jquery.js by John Resig et al (http://jquery.com/)
 *
 *
 * Operation:
 *
 *  Call, for example, WASABICUBE.process(".wczz", "2em", "1em");
 *  from within the $(window).load() function.
 *
 *  The arguments are, respectively, the class name for the 
 *  <blockquote>s being processed, the indent amount, and the padding
 *  required on the ends of each resulting line to accommodate
 *  a background graphic, for example.
 *
 *
 * Known limitations (all of which may be resolved soon):
 *
 * - Quotes must be supplied in <blockquote>s and it's expected
 *   there will be block-level elements within those <blockquotes>
 *
 * - Doesn't properly handle massive font-size increases within
 *   the <p> tags of the <blockquote>s being processed.  
 *
 * - If the width of the containing <blockquote> is narrower than the
 *   widest word within the quote then it will be expanded to that width
 *
 *
 * Future considerations:
 * 
 * - The WASABICUBE.processBlockQuote and WASABICUBE.processNode functions
 *   should be able to be combined into one processNode function
 *
 */
 
var WASABICUBE = function() 
{
	var WASABICUBE = function() 
	{
		// Identification
		var url = "http://wasabicube.com/";
		var copyright = "This code, zz.js, is published by Peter S Asquith under a Creative Commons Attribution-Share Alike 3.0 New Zealand License (http://creativecommons.org/licenses/by-sa/3.0/nz/)";
		
		//------------------------------------------------------
		// Constants
		var ZZTARGET="wczz";
		var IDZZCURRENT="wczzcurrent";
		var ZZMEASURE = "wczzmeasure";
		var ZZMEASURESPAN = "wczzmeasurespan";
		var ZZLEFT = "wczzleft";
		var ZZRIGHT = "wczzright";
		var ZZDEBUG = "wczzdebug";
		var ZZWIDTHESTIMATE = 1.36;
		
		//------------------------------------------------------
		var indentState = true;
		var availableWidth = 0;
		var currentWidth = 0;
		var currentIndent = 0;
		var aveWidth = 0;
		var indentAmount = "0";
		var linePadding = "0";
		var currentInsertHTML = "";
		var replacementHTML = "";
		var currentExpression = null;
		var blockTag = "p";
		var openingTags = [];
		var closingTags = [];
		
		return {
		  //----------------------------------------------------
		  debug:function(debugText) {
		  	$("#" + ZZDEBUG).text($("#" + ZZDEBUG).text() + debugText);
		  },
			//----------------------------------------------------
			getOpeningTags:function(){
			  var result="";
				jQuery.each(openingTags, function(index, value) {result += value;});
				return result;
			},
			//----------------------------------------------------
			getClosingTags:function(){
			  var result="";
				jQuery.each(closingTags, function(index, value) {result = value + result;});
				return result;
			},
			//----------------------------------------------------
			makeMeasurer:function(expression) {
				$(expression).append("<div id=" + ZZMEASURE + "></div>");
				$("#" + ZZMEASURE).css("visibility", "hidden").css("width", "auto");
			}, // makeMeasurer
			//----------------------------------------------------
			removeMeasurer:function() {
				$("#" + ZZMEASURE).remove();
			}, // removeMeasurer
			//----------------------------------------------------
			measureText:function(text) {
				$("#"+ZZMEASURE).html(WASABICUBE.getOpeningTags() + "<span id='" + ZZMEASURESPAN + "'>" + text + "</span>" + WASABICUBE.getClosingTags());
				$("#"+ZZMEASURESPAN).attr("display", "block");
				var result = ($("#"+ZZMEASURESPAN).outerWidth());
				$("#"+ZZMEASURESPAN).html("");						
				return result;
			},
			//----------------------------------------------------
			measureIndent:function() {
				$("#"+ZZMEASURE).html("<p class='" + ZZRIGHT + "' id='zzmetric'>Indent</p>");
				$("#zzmetric").css("margin-left", indentAmount);
				var result = parseInt($("#zzmetric").css("margin-left"));
				$("#"+ZZMEASURE).html("");
				return result;
			},
			//----------------------------------------------------
			wrapOutput:function() {
				return "\r\n<" + blockTag + " class='" + (indentState ? "wczzright" : "wczzleft") + "'><span>" + currentInsertHTML + "</span></" + blockTag + ">";							
			}, // wrapOutput
			//----------------------------------------------------
			processText:function(text) {
				
				var tokens = text.split(/\s/);
				var textWidth = 0;
				// Establish the average character width for the current tag nest
				aveWidth = WASABICUBE.measureText("M") /ZZWIDTHESTIMATE;
				for(var i=0; i<tokens.length; i++) {
					textWidth = parseInt((aveWidth * tokens[i].length) + aveWidth);
					currentWidth += textWidth;
					// If this token is too wide we widen the parent just enough (rough but fair)
					if(availableWidth < textWidth) {
						$(currentExpression).css("width", textWidth + "px");
						availableWidth = textWidth;
					}	
					// Have we got enough room?	
					if((availableWidth - currentWidth) < 0){
						i--; // back up one token
						currentInsertHTML += WASABICUBE.getClosingTags();
						replacementHTML += WASABICUBE.wrapOutput();
						indentState = !indentState;
						currentInsertHTML = WASABICUBE.getOpeningTags();
						indentState ? currentWidth = currentIndent : currentWidth = 0;
					}
					else {
						currentInsertHTML += tokens[i] + " ";
					}
				}
			}, // processText
			//----------------------------------------------------
			processNode:function(node) {
				if(node != null) {
					node = node.firstChild;
					while(node != null) {
						if(node.nodeType == 1){
							openingTags.push("<" + node.nodeName.toLowerCase() + ">");
							closingTags.push("</" + node.nodeName.toLowerCase() + ">"); 
							currentInsertHTML += openingTags[openingTags.length-1];
							WASABICUBE.processNode(node);
							currentInsertHTML += closingTags[closingTags.length-1];
							openingTags.pop();
							closingTags.pop();
						}
						else if(node.nodeType == 3){
							WASABICUBE.processText(node.nodeValue);
						}
						node = node.nextSibling;	
					} // while
				}
			}, // processNode			
			//----------------------------------------------------
			processBlockQuote:function(expression) {
				replacementHTML = "";
				currentInsertHTML="";
				currentExpression = expression;
				availableWidth = $(expression).innerWidth();
				WASABICUBE.makeMeasurer(expression);		
				aveWidth = WASABICUBE.measureText("M") / ZZWIDTHESTIMATE;
				currentIndent = WASABICUBE.measureIndent();
				availableWidth -= currentIndent;
				indentState ? currentWidth = currentIndent : currentWidth = 0;
				// For each child node of the blockquote
				var node = $(expression).children()[0];
				while(node != null) {
			  		blockTag = node.nodeName.toLowerCase();
			  		WASABICUBE.processNode(node);
			  		node = node.nextSibling;
			  	}
			  	$(expression).html(replacementHTML + WASABICUBE.wrapOutput() + "\r\n");
				WASABICUBE.removeMeasurer();		
				// Special handling for IE
				if($.browser.msie()) {$(expression).css("zoom", "1").css("width", (availableWidth * 2) + "px");}
			},		
			//----------------------------------------------------
			process:function(expression, indent, padding){
				indentAmount = indent;
				linePadding = padding;
				// Preemptively tidy up - make sure the blockquote is up to spec (i.e. contains block-level immediate children)
				$(expression + ":not(:has(p,dl,div,noscript,blockquote,form,hr,table,fieldset,address,ol,ul,h1,h2,h3,h4,h5,h6))").wrapInner("<p></p>");
				// We do this one blockquote at a time 
				$(expression).each(function(){WASABICUBE.processBlockQuote(this);});
				// Clear the floats we're about to introduce
				$(expression).css("overflow", "hidden");
				// Now apply the indent rules
				$("." + ZZLEFT).css("float", "left").css("clear", "left").css("padding-left", linePadding);
				$("." + ZZLEFT + " span").css("display", "block").css("padding-right", linePadding);
				$("." + ZZRIGHT).css("float", "left").css("clear", "left").css("padding-right", linePadding).css("margin-left", indentAmount);
				$("." + ZZRIGHT + " span").css("display", "block").css("padding-left", linePadding);
			}
			//----------------------------------------------------
		}
	}()
	return WASABICUBE;
}()