/**
 * MooColumns beta [for mootools 1.2]
 * @author Jason J. Jaeger | greengeckodesign.com
 * @version 0.67 
 * @license MIT-style License
 *			Permission is hereby granted, free of charge, to any person obtaining a copy
 *			of this software and associated documentation files (the "Software"), to deal
 *			in the Software without restriction, including without limitation the rights
 *			to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *			copies of the Software, and to permit persons to whom the Software is
 *			furnished to do so, subject to the following conditions:
 *	
 *			The above copyright notice and this permission notice shall be included in
 *			all copies or substantial portions of the Software.
 *	
 *			THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *			IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *			FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *			AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *			LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *			OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *			THE SOFTWARE.
 *	
 *  @changeLog_________________________________________________________________________________
 *  July 20th 2008
 *  JJJ - Incremented version to 0.67
 *  	- Fixed gutter height bug with IE6
 *  
 *  July 5th 2008
 *  JJJ - Incremented version to 0.66
 *  	- Made columns widths, gutters widths, and x positioning % based so they can be used in fluid layouts
 *  	- Made work based on selector to choose which page elements should be affected
 *  	- Tested with multiple mooColumns per page
 *  	- Created example files
 *  
 *  June 29th 2008:
 *  JJJ - Incremented version to 0.65
 *  	- Tested with official release of Mootools 1.2
 *  
 *  May 13th 2008:
 *  JJJ - Incremented version to 0.6
 *  	- Added colBreaksTrump option (true by default) which disables the auto-split of content if column-breaks are present, this is 
 *  	  because it can be difficult to predict where the column break is going to end up when the content is auto-split first
 **/
/*
 * In your screen stylesheet:
 *  .multiColumn{ display:block; }
 *  .multiColumn-screen{ display:block; } 
 *  .multiColumn-print{ display:none; }
 *  
 * In your print stylesheet:
 *  .multiColumn{ display:block; }
 *  .multiColumn-screen{ display:none; } 
 *  .multiColumn-print{ display:block; }
 *  
 *  [ Assuming "multiColumn" is your className ]
 */

var MooColumns = new Class({
	Implements: Options,
	options: {
		selector:'.multiColumn',
		className:'multiColumn',
       	numOfColumns:3,
		defaultNumOfColumns:1,
		gutterWidth:5, //% by default.. can set to a string with px at the end to set width in px
		gutterClassName:'gutter',
		columnClassName:'column',
		tweak:{x:0,y:0,width:0},
		splittableElements:['p','div','span','ul','br'],
		morePrecise: true,//setting to false will make script run faster
		tolerance:10,//how far below the target bottom non-splittable elements can hang before they are moved to the next column
		colBreaksTrump: true,
		debug:false		
    },
	
	sizerElWrapper:null,
	columnParents:[],
	mooColumnsAreasArr:[],
	
	initialize: function(options){
		this.setOptions(options);
		if($type(parseFloat(this.options.numOfColumns)) != 'number'){
			this.options.numOfColumns = this.options.defaultNumOfColumns;
		}		
		
		//create wrapper for sizer elements
		this.sizerElWrapper = new Element('div', {'id': 'sizerElWrapper'	}).inject(document.body,'inside').setStyles({
			'visibility':'hidden','position':'absolute','display':'block','padding':0,'margin':0,'top':0,'left':0,'width':0,'height':0,'overflow':'hidden'
		});
		
		this.columnParents = $(document.body).getElements(this.options.selector);

		this.columnParents.each(function(item,index){
			var id = this.getOrSetId($(item));
			if(!item.hasClass('moocolumned')){
				var aMooColumnsArea = new MooColumnsArea(this.options,item);
				this.mooColumnsAreasArr[id] = aMooColumnsArea;
				item.className +='-screen';
				item.addClass('moocolumned');
			}
		}.bind(this));

    },

	getOrSetId: function(obj){
		if(!obj.id){
			var d = new Date();
			var milli = d.getMilliseconds().toString();
			var randomNumber = Math.floor(Math.random()*1000);
			if(obj.nodeName){
				obj.id = obj.nodeName.toString() + '-' + milli + randomNumber.toString();
			}else{
				obj.id = "element" + milli + randomNumber.toString();	
			}
		}
		return obj.id;
	}	
	
});

var MooColumnsArea = new Class({
	Implements: Options,
   
 	options: {
		parentEl:null,
		printEl:null,
		debug:null,
		colBreakDepth:5
	},	
	columnElsArr:[],
	gutterElsArr:[],
	colWidth:null,
	targetHeight:null,
	tempContentHolder:null,
	sizerEl:null,
	tallest:0,
	unsplittableTags:['td','tr','table','tbody'],
	hasColBreaks:false,
	
	initialize: function(options,parentEl){
		this.setOptions(options);
		
		this.options.parentEl = parentEl;

		//create sizer element
		this.sizerEl = new Element('div').inject($('sizerElWrapper'),'inside').setStyles({ 'visibility':'hidden','position':'absolute','display':'block','padding':0,'margin':0,'top':0,'left':0 });
		
		//backup original content into a div for printing and refreshing
		this.options.printEl = new Element('div', {'class':this.options.className +'-print'	}).inject(this.options.parentEl,'after').set('html',this.options.parentEl.innerHTML);
		this.options.parentEl.empty();
		var innerWrapper =  new Element('div').setStyles({'display':'block','position':'relative','padding':0,'margin':0}).inject(this.options.parentEl,'top');
		this.options.parentEl = parentEl.getFirst();
		
		//apply width & x tweaks
		var targetWidth = innerWrapper.getStyle('width').toInt()+this.options.tweak.width.toInt();
		var targetWidthPercent = (100 * targetWidth)/innerWrapper.getStyle('width').toInt();
		innerWrapper.setStyles({'width':targetWidthPercent+'%',left:this.options.tweak.x});
		
		this.go();
		 
		window.addEvent('resize', function() {			
			$clear(refreshTimeout);
			var refreshTimeout = (function(){ this.setHeights(); }.bind(this)).delay(100);			
		}.bind(this)); 
		
		window.fireEvent('moocolumnsdone');
		window.removeEvents('moocolumnsdone');

	},
	
	go:function(){
		
		var numOfGutters = this.options.numOfColumns -1;
		if (this.options.gutterWidth.toString().contains('px')) {
			//convert gutterWidth from px to %
			this.options.gutterWidth = Math.round((100*parseFloat(this.options.gutterWidth))/this.options.parentEl.getCoordinates().width);
		}
		
		//how much width% to subtract from each column to make room for the gutters
		var gutterWidthAdjustment = Math.round(numOfGutters*parseFloat(this.options.gutterWidth)/ this.options.numOfColumns);

		//determine column width
		this.colWidth = Math.round(100/this.options.numOfColumns)-gutterWidthAdjustment;
		
		//determine sizerElSize in px
		var sizerWidth = Math.round((this.options.parentEl.getCoordinates().width * this.colWidth)/100);
		
		//determine & set target height
		this.sizerEl.setStyle('width', sizerWidth).set('html', this.options.printEl.innerHTML );
		this.targetHeight = Math.round(this.sizerEl.getCoordinates().height / this.options.numOfColumns );
		
		//create empty columns and gutters
		this.makeWireFrame();
		
		//strip scripts and html comments
		this.options.printEl.set('html',this.options.printEl.get('html').stripScripts().split(/<!--[^(-->)]*-->/).join(""));

		//wraps naked text nodes within a <p> tag
		this.wrapTextNodes(this.options.printEl);
		
		//convert 2 consecutive horizontal rules to a span with a class of 'colBreak'
		this.convertColBreaks(this.options.printEl);
		
		if($(this.options.printEl).getElements('.colBreak').length > 0){
			this.hasColBreaks = true;
		}				
		
		//if the column breaks are inside of something, split it
		while(this.options.colBreakDepth > 0){
			this.splitColBreakParents(this.options.printEl);
			this.options.colBreakDepth--;
		}
		
		if(this.options.colBreaksTrump === true && this.hasColBreaks){ this.options.morePrecise = false;}
				
		//divide the content
		var columnsContentArr;
		if(this.hasColBreaks && this.options.colBreaksTrump){
			//just divide on colBreaks and don't auto split content
			columnsContentArr = this.divideContent2();
		}else{
			columnsContentArr = this.divideContent();
		}		
		
		//fill the columns
		for(i=0;i<columnsContentArr.length;i++){
			this.columnElsArr[i].set('html',columnsContentArr[i].innerHTML);
		}
		
		//check for & fix overlap
		if (this.options.morePrecise) { this.shaveColumns(); 	}
		
		//set the heights
		this.columnsContentArr = columnsContentArr;
		this.setHeights();
	},
	
	setHeights:function(){
		this.options.parentEl.setStyles({'overflow':'hidden','height':'0'});
		this.tallest = 0;
		if (!this.options.debug) {
			for (i = 0; i < this.columnsContentArr.length; i++) {
				if (this.columnElsArr[i].getCoordinates().height > this.tallest) {
					this.tallest = this.columnElsArr[i].getCoordinates().height;
				}
			}
			
			if(this.options.parentEl.getScrollSize().y >0){
				this.tallest = this.options.parentEl.getScrollSize().y;
			}
			
			for (i = 0; i < this.columnElsArr.length; i++) {
				this.columnElsArr[i].setStyles({'top':0,'bottom':0});
				this.options.parentEl.setStyle('height', this.tallest);				
			}
						
			if(Browser.Engine.trident4){
				this.options.parentEl.getParent().setStyle('height',this.tallest);				
				this.gutterElsArr.each(function(item,index){	item.setStyle('height','100%');	});
			}
		}	
	},
	
	makeWireFrame:function(){
		this.options.parentEl.empty();
		for(i=0;i < this.options.numOfColumns;i++){
			//create column
			colLeft = (i*(parseFloat(this.colWidth)+parseFloat(this.options.gutterWidth)));
			this.columnElsArr[i] = new Element('div', {'class':'column'}).inject(this.options.parentEl,'inside').setStyles({'display':'block','position':'absolute','left':colLeft+'%','top':this.options.tweak.y, 'width':this.colWidth+'%'});
			
			//create gutter	
			if(i < this.options.numOfColumns-1){
				this.gutterElsArr[i] = new Element('div', {'class':'gutter'}).inject(this.options.parentEl,'inside').setStyles({'display':'block','position':'absolute','left':(colLeft + parseFloat(this.colWidth)+'%'),'top':this.options.tweak.y,'width':parseFloat(this.options.gutterWidth)+'%','bottom':0});
				if(this.options.debug){	this.gutterElsArr[i].setStyle('background','yellow');}
			}
			if(this.options.debug){	this.columnElsArr[i].setStyle('background','#eee');	}
		}
		if(this.options.debug){this.options.parentEl.setStyle('background','#ccc');}
	},
	
	convertColBreaks:function(sourceEl){
		var horizontalRules = $(sourceEl).getElements('hr');
		for (var i=0; i<horizontalRules.length; i++) {
			var item =$( horizontalRules[i]);
			
			//<hr /> <hr />
			var case1 = item.getNext() && item.getNext().get('tag') === 'hr';
			
			//<hr /> <br /> <hr />
			var case2 = item.getNext() && 
						item.getNext().get('tag') === 'br' && 
						item.getNext().getNext() && 
						item.getNext().getNext().get('tag') === 'hr';
			
			//<p><hr/></p> <p><hr /></p>
			//since having an hr inside of a paragraph tag is invalid the browser will rearrange
			//things to try to fix it. case3 targets the end result html for FireFox and case4 is for IE7
			var case3 = item.getNext() && 
						item.getNext().get('tag') === 'p' &&
						!item.getNext().get('html') &&
						item.getNext().getNext() && 
						item.getNext().getNext().get('tag') === 'hr';
			
			var case4 = item.getNext() && 
						item.getNext().getNext() &&
						item.getNext().getNext().getNext() &&
						item.getNext().get('tag') === 'p' &&
						item.getNext().getNext().get('tag') === 'p' &&
						item.getNext().getNext().getNext().get('tag') === 'hr' &&
						!item.getNext().get('html') &&
						!item.getNext().getNext().get('html');
			
			if (case4 || case3 || case2 || case1) {
				var colBreak = new Element('span', {'class': 'colBreak'	}).inject(item,'before');
				this.hasColBreaks = true;
			}			
			
			if(case4){
				item.getNext().getNext().getNext().dispose();				
			}
			if(case4 || case3 || case2){
				item.getNext().getNext().dispose();	
			}
			
			if( case4 || case3 || case2 || case1){
				item.getNext().dispose();
				item.dispose();
			}
			
		};
	},
	
	splitColBreakParents:function(sourceEl){
		$(sourceEl).getElements('.colBreak').each(function(item,index){
			if(!item.getParent()){return;}
			if(	!item.getParent().hasClass('wrapper-print') && 	!this.unsplittableTags.contains(item.getParent().get('tag'))	){
				var original = item.getParent();
				var firstHalf = original.clone(false);//the false means the clone will be empty
				if(!firstHalf || firstHalf.hasClass('colBreak')){return;}
				var nodeArr1 = $(original).childNodes;
				var currentNode = nodeArr1[0];
				var currentNodeNumber = 1;
				
				//copy everything before the col break into firstHalf
				while ( currentNode && !currentNode.className || currentNode && currentNode.className != 'colBreak') {
					if(currentNode.parentNode.className && currentNode.parentNode.className.contains('multi')){
						return;
					}
					
					if($type(currentNode) === 'textnode' || $type(currentNode) === 'whitespace'){
						if(firstHalf && firstHalf.innerHTML && currentNode.nodeValue){
							//firstHalf.innerHTML +=currentNode.nodeValue;
							firstHalf.set('html',firstHalf.get('html')+currentNode.nodeValue);
							
							currentNode.parentNode.removeChild(currentNode);

						}else{
							break;
						}
						
					}else{
						currentNode.inject(firstHalf,'bottom');
					}
					currentNodeNumber++;
					currentNode = nodeArr1[currentNodeNumber];
				}
				
				//all that is left in original is the second half of the contents,   
				//so duplicate original and replace the first original with firstHalf
				var secondHalf = original.clone(true,true).inject(original,'after');
				$(firstHalf).replaces($(original));
				
				//now move the col break from the inside of secondHalf to inbetween firstHalf & secondHalf
				if(secondHalf.getElements('.colBreak').length >=1){
					$(secondHalf.getElements('.colBreak')[0]).inject(firstHalf,'after');
				}
					
			}
		}.bind(this));
	},
	
	wrapTextNodes:function(sourceEl){
		var nodeArr = $(sourceEl).childNodes;
		for(i=0; i< nodeArr.length;i++){
			if($type(nodeArr[i]) === 'textnode'){
				var clothes = new Element('p').inject(nodeArr[i],'after').set('html', nodeArr[i].nodeValue );
				nodeArr[i].parentNode.removeChild(nodeArr[i]);
			}
		}
	},
	
	divideContent:function(){
		this.tempContentHolder = new Element('div', {'id':'tempContentHolder'}).set('html',this.options.printEl.innerHTML).inject(document.body,'inside').setStyles({'display':'none','position':'absolute'});
		this.sizerEl.empty();
		var columnsContent = [];
		var currentColNum = 0;
		var limit = 1300;

		while(this.sizerEl.getCoordinates().height <= this.targetHeight && $(this.tempContentHolder).getFirst() && limit > 0){
		 	limit--;
			if(!columnsContent[currentColNum]){	columnsContent[currentColNum] = new Element('div');	}
			$(this.tempContentHolder).getFirst().inject(this.sizerEl,'inside');
			if( this.sizerEl.getCoordinates().height >= this.targetHeight || this.sizerEl.getLast().hasClass('colBreak') ){
				$(columnsContent[currentColNum]).set('html',this.sizerEl.innerHTML);
				this.sizerEl.empty();
				
				currentColNum++;
				if(currentColNum >= this.options.numOfColumns){currentColNum = this.options.numOfColumns -1;}
				//catch any leftover text nodes and put it at the top of the next column
				if(!columnsContent[currentColNum]){	
					columnsContent[currentColNum] = new Element('div');	
				}
				
				if(!this.tempContentHolder.getFirst()){
					$(columnsContent[currentColNum]).set('html',this.tempContentHolder.innerHTML + $(columnsContent[currentColNum]).innerHTML);}
				}
		}
		$(columnsContent[currentColNum]).set('html',$(columnsContent[currentColNum]).innerHTML + this.sizerEl.innerHTML);
		this.sizerEl.empty();
		return columnsContent;
	},
	
	divideContent2:function(){
		this.tempContentHolder = new Element('div', {'id':'tempContentHolder'}).set('html',this.options.printEl.innerHTML).inject(document.body,'inside').setStyles({'display':'none','position':'absolute'});
		this.sizerEl.empty();
		var columnsContent = [];
		var currentColNum = 0;
		var limit = 1300;
		
		while(currentColNum < this.options.numOfColumns  && limit > 0){
		 	limit--;
			if(!columnsContent[currentColNum]){	columnsContent[currentColNum] = new Element('div');	}
			if (this.tempContentHolder.getFirst()) {
				$(this.tempContentHolder).getFirst().inject(this.sizerEl,'inside');
			}
			if(  this.sizerEl.getLast().hasClass('colBreak') ){
				$(columnsContent[currentColNum]).set('html',this.sizerEl.innerHTML);
				this.sizerEl.empty();
				
				currentColNum++;
				if(currentColNum >= this.options.numOfColumns){currentColNum = this.options.numOfColumns -1;}
				//catch any leftover text nodes and put it at the top of the next column
				if(!columnsContent[currentColNum]){	
					columnsContent[currentColNum] = new Element('div');	
				}
				
				if(!this.tempContentHolder.getFirst()){
					$(columnsContent[currentColNum]).set('html',this.tempContentHolder.innerHTML + $(columnsContent[currentColNum]).innerHTML);
				}
			}
		}
		$(columnsContent[currentColNum]).set('html',$(columnsContent[currentColNum]).innerHTML + this.sizerEl.innerHTML);
		
		
		this.sizerEl.empty();
		
		return columnsContent;
	},
	
	shaveColumns:function(){
		for (i = 0; i < this.columnElsArr.length-1; i++) {
			
			var overlap = this.columnElsArr[i].getCoordinates().height - this.targetHeight;
			var ElementIsSplittable = this.columnElsArr[i].getLast().get('tag') && this.options.splittableElements.contains(this.columnElsArr[i].getLast().get('tag'));
			var original = this.columnElsArr[i].getLast();
			
			if(overlap > 0 && ElementIsSplittable ){	
				var elementTargetHeight = original.getCoordinates().height - overlap + this.options.tolerance;
				var clone = original.clone().inject($('sizerElWrapper'),'inside');
				clone.empty();
				var limit = 50;
				
				while(limit >0 && original.childNodes.length  && original.getCoordinates().height > elementTargetHeight){
					limit--;
					
					if($type(original.childNodes[original.childNodes.length-1]) === 'textnode' ||$type(original.childNodes[original.childNodes.length-1]) === 'whitespace'){
						//split and move textnodes
						var limit2 = 50;
						var textArr = original.childNodes[original.childNodes.length-1].nodeValue.split(" ");
						
						while ( limit2 > 0 && original.childNodes[original.childNodes.length-1].nodeValue.length >= 0 && original.getCoordinates().height > elementTargetHeight) {
							limit2--;
							//copy last word to clone
							if($defined(textArr.getLast())){	clone.innerHTML = textArr.getLast().toString()+ " " + clone.innerHTML;	}
							
							//remove the last word from the array
							textArr = textArr.filter(function(item, index){	return index < (textArr.length-1) && $defined(item) ;	});
							//set original textnode value to flattened array
							original.childNodes[original.childNodes.length-1].nodeValue = textArr.join(" ");
						}
						if(!$defined(textArr.getLast())){
							//delete last childNode in original
							original.removeChild(original.childNodes[original.childNodes.length-1]);
						}
					}else if($(original.childNodes[original.childNodes.length-1])){
						if($(original.childNodes[original.childNodes.length-1]).hasClass('colBreak') ){
							limit = 0;
						}else{
							$(original.childNodes[original.childNodes.length-1]).inject(clone,'top');
							//delete last childNode in original
							//original.removeChild(original.childNodes[original.childNodes.length-1]);
						}
					}	
				}
				
				clone.inject(this.columnElsArr[i+1],'top');
				
			}else if (overlap > this.options.tolerance && !ElementIsSplittable){
				original.inject(this.columnElsArr[i+1],'top');
			}
		}
	}
	
});

