// Copyright 2011, Mats Mattsson http://howaboutanorange.com

function RowStylingGroup (styles) {
	this.isRunning = false
	this.paragraphs = []
	this.paragraphs_sorted = true
	this.taskQueue = []
	this.styles = styles
}
RowStylingGroup.prototype.addTask = function(fun) {
	if ( 0 == this.taskQueue.length && !this.isRunning && typeof this.onRunning == 'function') {
		this.onRunning(true)
	}
	this.taskQueue.push(fun)
	this.runTasks()
}
RowStylingGroup.prototype.runTasks = function() {
	if (!this.isRunning) {
		this.isRunning = true
		var that = this
		if (0 < this.taskQueue.length) {
			setTimeout(function() {
				var task_function = that.taskQueue.pop()
				if (typeof task_function == 'function') {
					task_function()
				}
				if ( 0 == that.taskQueue.length && typeof that.onRunning == 'function') {
					that.onRunning(false)
				}
				that.isRunning = false
				that.runTasks()
			}, 27)
		} else {
			setTimeout(function() {
				that.isRunning = false
				that.checkLayout()
				that.runTasks()
			}, 3000)
		}
	}
}
RowStylingGroup.prototype.checkLayout = function() {
	for (var i = 0; i < this.paragraphs.length; ++i) {
		this.paragraphs[i].checkLayout()
	}
}


var RowStyling_taskQueue = new Array
var RowStyling_taskIsRunning = false
var RowStyling_styledElements = new Array

function RowStylingParagraph(group, element, styles) {
	this.group = group
	this.element = element
	this.styles = styles
	this.origHTML = element.innerHTML
	this.sp='&#8203;'
}

RowStylingParagraph.prototype.t = function (oA, i) {
	if (null !== oA[i]) {
		return
	}
	
	this.element.innerHTML = this.origHTML.substring(0,i) + "<span id=\"tt_a\">"+this.sp+"</span>" + this.origHTML.substring(i, this.i_end) + "<span id=\"tt_b\">"+this.sp+"</span>" + this.origHTML.substr(this.i_end)
	var ea = document.getElementById("tt_a")
	var eb = document.getElementById("tt_b")
	if (ea && eb && eb.offsetLeft == this.x_end && eb.offsetTop == this.y_end && ea.offsetTop > 0) {
		oA[i] = [ea.offsetTop, ea.offsetLeft]
	} else {
		oA[i] = undefined;
	}
}


RowStylingParagraph.prototype.findLayout = function () {
	var startTime = new Date().getTime()
	var oA = []
	oA.length=this.i+1
	oA[this.i]=null
	var cN = this.element.childNodes
	var cO = 0
	for (var i=0;i<cN.length;++i){
		if('#text'==cN[i].nodeName){
			var t=cN[i].nodeValue
			var s=this.origHTML.indexOf(t)
			for(var j=s;j<s+t.length;++j){oA[j]=null}
		}
	}
	
	while (this.i >= 0 && (new Date().getTime() - startTime) < 19.) {
		var n = this.i
		this.t(oA,n)
		this.t(oA,0)
		if (!oA[n] || !oA[0] || oA[n][0] == oA[0][0]) {
			this.i = -1
			this.rowsStack.push(this.origHTML.substring(0,n))
			var that = this
			this.group.addTask(function(){that.stylizeRows()})
		} else {
			var s = 0
			var t = n
			var j_old = -1
			var i_old = -1
			while (true) {
				var i=Math.floor((t+s)/2)
				var j=i+1
				this.t(oA,i)
				this.t(oA,j)
				while(!oA[i] &&i>0) {
					--i
					this.t(oA,i)
				}
				while(!oA[j] &&j<n) {
					++j
					this.t(oA,j)
				}
				if (!oA[j]||!oA[i]||i==i_old||j==j_old){
					this.i = 0
					this.rowsStack=[this.origHTML]
					break;
				} else {
					if (oA[j][0]!=oA[i][0]&&oA[n][0]==oA[j][0]) {
						var row = this.origHTML.substring(i,this.i_end)
						this.x_end = oA[i][1]
						this.y_end = oA[i][0]
						this.i_end = i
						this.i = i
						this.rowsStack.push(row)
						break
					} else if (oA[n][0]==oA[i][0]) {
						t = i
					} else {
						s = oA[n][0]!=oA[j][0] ? j : i + 1
					}
				}
				i_old = i
				j_old = j
			}
		}
	}
	if (this.i >= 0) {
		var that = this
		this.group.addTask(function(){that.findLayout()})
	}
}

RowStylingParagraph.prototype.stylizeRows = function() {
	var rowCount = this.rowCount = this.rowsStack.length
	var result = ""
	var safeTags = ["b", "i", "sub", "sup"]
	var tagStack = []
	for (var i=0;i<rowCount;++i) {
		var row = this.rowsStack[rowCount-1-i]
		var begintag = "<span class=\""+ this.styles[i%this.styles.length]+"\">"
		var endtag = "</span>"

		do {
			var t = row.indexOf('<')
			if (t>=0) {
				var s = row.substring(t).indexOf('>')+t
				var r = row.substring(t).indexOf(' ')+t
				if (s > t) {
					var tagname = row.substring(t+1,s<r?s:r);
					var tagStackIsSafe = true
					for(var j=0;tagStackIsSafe&&j<tagStack.length;++j) {
						var foundMatch = false;
						for(var k=0;!foundMatch&&k<safeTags.length;++k) {
							foundMatch = (tagStack[j]===safeTags[k])
						}
						tagStackIsSafe = foundMatch
					}
					result += (tagStackIsSafe ? begintag + row.substring(0, t) + endtag : row.substring(0, t)  ) + row.substring(t, s + 1)
					if (row.charAt(t+1)=='/') {
						tagStack.pop()
					} else {
						tagStack.push(tagname)
					}
					row = row.substring(s+1)
				}
			}
		} while (t >= 0 && row.length > 0)
		if (tagStack.length>0) {
			result += row
		} else {
			result += begintag + row + endtag
		}
	}
	var endElementId = "a_" + this.element.id + "" + Math.round(1e10*Math.random()) + "_end"
	this.element.innerHTML = result + "<span id=\"" + endElementId + "\">"+this.sp+"</span>"

	this.endElement = document.getElementById(endElementId)
	this.x_endElement = this.offsetLeft(this.endElement) - this.offsetLeft(this.element)
	this.y_endElement = this.offsetTop(this.endElement) - this.offsetTop(this.element)
	if (this.onStylingComplete) {
		var that = this
		this.group.addTask(function() {that.onStylingComplete()})
	}
}
RowStylingParagraph.prototype.offsetLeft = function(e) {var o=0;while(e){o+=e.offsetLeft;e=e.offsetParent}return o;}
RowStylingParagraph.prototype.offsetTop = function(e) {var o=0;while(e){o+=e.offsetTop;e=e.offsetParent}return o;}

RowStylingParagraph.prototype.hasLayout = function() {
	return this.endElement && this.x_endElement == this.offsetLeft(this.endElement) - this.offsetLeft(this.element) && this.y_endElement == this.offsetTop(this.endElement) - this.offsetTop(this.element)
}

RowStylingParagraph.prototype.checkLayout = function() {
	if (!this.hasLayout()) {
		// this.resetStyling()
		this.element.innerHTML = this.origHTML
	}
}

RowStylingParagraph.prototype.resetStyling = function() {
	this.rowsStack = new Array
	this.i = this.origHTML.length
	this.element.innerHTML = this.origHTML + "<span id=\"tt\">&#8203;</span>"
	var et = document.getElementById("tt")
	if (et.offsetWidth && et.offsetWidth > 0) this.sp = ''
	this.x_end = et.offsetLeft
	this.y_end = et.offsetTop
	this.i_end = this.origHTML.length
	this.element.innerHTML = this.origHTML
	var that = this
	this.group.addTask(function(){that.findLayout()})
}

RowStylingParagraph.prototype.setStyles = function(styles) {
	this.styles = styles
	var that = this
	this.group.addTask(function(){that.stylizeRows()})
}

RowStylingParagraph.prototype.getStyles = function() {
	return this.styles
}


RowStylingGroup.prototype.addParagraph = function(e) {
	var paragraph = new RowStylingParagraph(this, e, this.styles)
	this.paragraphs.push(paragraph)
	this.paragraphs_sorted = false
	if (!this.onStylingComplete) {
		var that = this
		this.onStylingComplete = function() {
			var isComplete = true
			for (var i = 0; isComplete && i < that.paragraphs.length; ++i) {
				isComplete = that.paragraphs[i].hasLayout()
			}
			if (isComplete) {
				if (!that.paragraphs_sorted) {
					that.paragraphs.sort(function(a, b) {
						a.offsetTop(a.element) - b.offsetTop(b.element)
					})
					that.paragraphs_sorted = true
				}
				var rowCount = 0
				var s = that.styles
				for (var i = 0; isComplete && i < that.paragraphs.length; ++i) {
					var p = that.paragraphs[i]
					var r = rowCount % s.length
					var ns = s.slice(s.length - r).concat(s.slice(0, s.length - r))
					var c = false
					for (var j = 0; !c && j < s.length; ++j) {
						c = (p.styles[j] != ns[j])
					}
					if (c) {
						p.setStyles(ns)
					}
					
					rowCount += p.rowCount
				}
			}
		}
	}
	paragraph.onStylingComplete = this.onStylingComplete
	paragraph.resetStyling()
}

