/*
ngelib.js
==============
This file contains an editor class for editing math elements.

development version

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at
your option) any later version.

This program is distributed in the hope that it will be useful, 
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License (at http://www.gnu.org/copyleft/gpl.html) 
for more details.
*/

var mathmlNS = "http://www.w3.org/1998/Math/MathML";
var keyTimer = 300;

String.prototype.split = function(regex,include_delimiters,include_empty) {
	var str = this.toString();
	var returnArray = new Array();
	while (str.match(regex)) {
		var i=str.indexOf(str.match(regex));
		left = str.substring(0,i);
		mlen = String(str.match(regex)).length;
		if (left!=''||include_empty) returnArray.push(left);
		if (include_delimiters) returnArray.push(str.substring(i,i+mlen));
		str = str.substring(i+mlen,str.length);
	}
	if (str!=''||include_empty) returnArray.push(str);
	return returnArray;
}

Element.prototype.getElementsByNodeType = function(type) { 
   var nodes = []; 
   var node; 
   for(var i = 0; i < this.childNodes.length; i++) { 
      node = this.childNodes[i]; 
      if(node.nodeType == type) nodes.push(node) 
      if(node.hasChildNodes()) { 
         nodes = nodes.concat(node.getElementsByNodeType(type)) 
      } 
   }   
   return nodes; 
}

Element.prototype.getElementsByClassName = function(className) { 
   var nodes = []; 
   var node; 
   for(var i = 0; i < this.childNodes.length; i++) { 
      node = this.childNodes[i]; 
      if(node.className == className) nodes.push(node) 
      if(node.hasChildNodes()) { 
         nodes = nodes.concat(node.getElementsByClassName(className)) 
      } 
   }   
   return nodes; 
}

Node.prototype.serialize = function(depth) {
	if (!depth) var depth=0;
	var str = '';
	if (this.nodeType==3) {
		var val = this.nodeValue;
		val = val.replace(/&/,'&amp;');
		val = val.replace(/>/,'&gt;');
		val = val.replace(/</,'&lt;');
		str+=val
	}
	if (this.nodeType==1) {
		str = '\n';
		for (var i=0; i<depth; i++) str+= '  ';
		str+='<'+this.nodeName;
		var attrs = this.attributes;
		for (var i=0; i<attrs.length; i++) {
			if (!attrs[i].nodeName.match(/-moz|selected/)) {
				str += ' ';
				str += attrs[i].nodeName + '="' + attrs[i].nodeValue + '"';
			}
		}
		str += '>';
		for (var i=0; i<this.childNodes.length; i++) {
			if (this.childNodes[i].serialize) str += this.childNodes[i].serialize(depth+1);
		}
		if (this.firstChild&&this.firstChild.nodeType==1) for (var i=0; i<depth; i++) str+= '  ';
		str += '</'+this.nodeName+'>\n';
	}
	str = str.replace(/\s*[\n\r]+/g,"\n");
	str = str.replace(/\240/g,"&nbsp;");
	return str;
}

//xhtml editor functions

xhtmlEditor = function() {
	this.editorWindow = window;
	this.previewWindow = window;
	window.xhtmleditor = this;
}

xhtmlEditor.prototype.setRealTimePreview = function(bool) {
	this.realTimePreview = bool;
}

xhtmlEditor.prototype.setEditorWindow = function(w) {
	this.editorWindow = w;
}

xhtmlEditor.prototype.setPreviewWindow = function(w) {
	this.previewWindow = w;
	if (window.mmlEditor) mmlEditor.setPreviewWindow(w);
}

xhtmlEditor.prototype.setInputElement = function(id) {
	this.inputElement = this.editorWindow.document.getElementById(id);
	this.inputElement.editor = this;
	this.inputElement.onkeyup = function(e) {
		if (this.editor.realTimePreview) {
			//this.editor.preview();
			clearTimeout(this.inputTimer);
			this.inputTimer = setTimeout(function(){window.xhtmleditor.preview()},keyTimer);
		}
	}
}

xhtmlEditor.prototype.setPreviewElement = function(id) {
	this.previewElement = this.previewWindow.document.getElementById(id);
}



xhtmlEditor.prototype.preview = function() {
	var targetEl = this.previewElement;
	for (var i=0; i<targetEl.childNodes.length; i++) targetEl.removeChild(targetEl.firstChild);
	targetEl.appendChild(this.parseXHTML(this.inputElement.value));
	if (window.mmlEditor) window.mmlEditor.reBuild(this.previewElement);
}

xhtmlEditor.prototype.parseXHTML = function(str) {
	var tmpArr = str.split(/<\/?.*?>/,true);
	var outputNode = document.createElement('div');
	var currNode = outputNode;
	for (var i=0; i<tmpArr.length; i++) {
		var n = tmpArr[i];
		if (n.match(/<[^\/].*?>/)) {
			var tag = n.match(/<[^\/].*?>/)[0];
			var tagName = tag.match(/<([^\/ >]+)/)[1];
			var attributes = tag.match(/\w+=".+?"/g);
				currNode = currNode.appendChild(document.createElement(tagName));
			if (attributes) {
			for (var j=0; j<attributes.length; j++) {
				var name = attributes[j].split('=')[0].replace(/^ *| *$/g,'');
				var value = attributes[j].split('=')[1].replace(/^ *| *$/g,'').replace(/"/g,'');
				currNode.setAttribute(name,value);
			}
			}
		} else 
		if (n.match(/<(\/.+?)>/)) {
			currNode = currNode.parentNode;
		} else {
			window.status=currNode.nodeName;
			if (currNode.nodeName == 'rk:htmlent') {
				n = n.replace(/&/g,'&amp;');
				n = n.replace(/</g,'&lt;');
				n = n.replace(/>/g,'&gt;');
			} else {
				n = n.replace(/&lt;/g,'<');
				n = n.replace(/&gt;/g,'>');
				n = n.replace(/&amp;/g,'&');
			}
			currNode.appendChild(document.createTextNode(n));
			AMprocessNode(currNode,true);
		}
	}
	return outputNode;
}

//mathml editor functions

mathMLEditor = function() {
	this.previewWindow = window;
	window.mmlEditor = this;
}

mathMLEditor.prototype.setPreviewWindow = function(w) {
	this.previewWindow = w;
}

mathMLEditor.prototype.setMenu = function(id) {
	this.menu = this.previewWindow.document.getElementById(id);
	this.menu.editor = this;
}

mathMLEditor.prototype.setStatusBar = function(id) {
	this.statusBar = this.previewWindow.document.getElementById(id);
	if (!this.statusBar.firstChild) this.statusBar.appendChild(document.createTextNode('ready'));
}

mathMLEditor.prototype.reBuild = function(el) {
	var allMath = el.getElementsByTagName('math');
	for (var i=0; i<allMath.length; i++) {
		allMath[i].editor = this;
		allMath[i].setAttribute('selected','selected');
		allMath[i].onclick = function() {
			this.editor.selectMathElement(this);
		}
	}
}

mathMLEditor.prototype.selectMathElement = function(el) {
	el.setAttribute('selected','selected');
	if (this.selectedElement) this.selectedElement.removeAttribute('selected');
	this.selectedElement = el;
	this.buildMathDynamics(el);
}

mathMLEditor.prototype.deSelectMathElement = function(el) {
	el.removeAttribute('selected');
	this.selectedElement = null;
}

mathMLEditor.prototype.selectElement = function(el) {
	if (this.selectedNode) this.selectedNode.removeAttribute('selected');
	el.setAttribute('selected','selected');
	this.selectedNode = el;
	this.setStatus(el.nodeName);
	this.mmlInput.focus();
}

mathMLEditor.prototype.deSelectElement = function(el) {
	el.removeAttribute('selected');
	this.selectedNode = null;
}

mathMLEditor.prototype.buildMathDynamics = function(el) {
	el.setAttribute('display','none');
	this.nodes = el.getElementsByNodeType(1);
	for (var i=0; i<this.nodes.length; i++) {
		this.nodes[i].nodeNr = i;
		this.nodes[i].editor = this;
		this.nodes[i].onclick = function(e) {
			//addEventListener('click', selectMe, false);
			this.editor.selectElement(this);
			e.cancelBubble = true;
		}
	}
	el.removeAttribute('display');
}

mathMLEditor.prototype.init = function(activeElement) {
	its = this.menu.getElementsByClassName('insertTemplate');
	for (var i=0; i<its.length; i++) {
		var itsc = its[i].getElementsByTagName('li');
		for (var j=0; j<itsc.length; j++) {
			itsc[j].editor = this;
			itsc[j].onclick = function(e) {
				this.editor.insertMathML(this.lastChild.firstChild)
				e.cancelBubble = true;
			}
			itsc[j].onmousedown = function(e) {
				e.cancelBubble = true;
			}
			itsc[j].onmouseover = function() {
				this.editor.setStatus(this.firstChild.getAttribute('title'));
			}
			itsc[j].onmouseout = function() {
				this.editor.resetStatus();
			}
		}
	}
	this.menu.onmousedown = function() {
		this.dragging = true;
		this.editor.setStatus('dragging');
	}
	this.menu.onmousemove = function(e) {
		if (this.dragging) {
			this.editor.setStatus(e.x);
			this.style.left = (e.pageX-30) +'px';
			this.style.top = (e.pageY-30) + 'px';
		}
	}
	this.menu.onmouseup = function() {
		this.dragging = false;
	}
	this.mmlInput = this.menu.getElementsByTagName('input')[0];//getElementById('mmlInput');
	this.mmlInput.editor = this;
	this.mmlInput.onkeyup = function(e) {
		this.editor.processType(e);
	}
	this.reBuild(activeElement);
}

mathMLEditor.prototype.insertMathML = function(mathML) {
	var se = this.selectedNode;
	var newEl = mathML.cloneNode(true);
	switch (se.nodeName) {
		case 'mrow': 	if (se.getAttribute('class')=='empty') {
							se.removeAttribute('class');
							se.removeChild(se.firstChild);
						}
						se.appendChild(newEl);
						break;
		default: 		se.parentNode.insertBefore(newEl,se);
	}
	this.buildMathDynamics(this.selectedElement);
	this.selectElement(se);
}

mathMLEditor.prototype.insertType = function(str) {
	if (str.match(/[a-zA-Z\s\240]/)) {var newEl = document.createElementNS(mathmlNS,'mi')}							//identifiers
	if (str.match(/[0-9]/)) var newEl = document.createElementNS(mathmlNS,'mn');								//numbers
	if (str.match(/[\\\|\(\)\[\]\{\}\^\$\*\+\?\.=-]/)) var newEl = document.createElementNS(mathmlNS,'mo');		//operators
	if (newEl) {
		newEl.appendChild(document.createTextNode(str));
		this.insertMathML(newEl);
	}
}

mathMLEditor.prototype.deleteElement = function(el) {
	var par = el.parentNode;
	this.selectElement(par);
	par.removeChild(el);
	if (par.getElementsByNodeType(1).length==0) {
		par.setAttribute('class','empty');
		par.appendChild(document.createTextNode(String.fromCharCode(160)));
	}
	this.buildMathDynamics(this.selectedElement);
}

mathMLEditor.prototype.clearInput = function() {
	with (window.mmlEditor) {
		if (mmlInput.value!='') insertType(mmlInput.value);
		mmlInput.value = '';
	}
}

mathMLEditor.prototype.processType = function(event) {
	var key = event.which;
	this.setStatus(key);
	if (key==37||key==38) this.selectPreviousElement();
	if (key==39||key==40) this.selectNextElement();
	if (key==46) this.deleteElement(this.selectedNode);
	//if (key==32) this.deleteElement(this.selectedNode);
	clearTimeout(this.inputTimer);
	this.inputTimer = setTimeout(this.clearInput,keyTimer);
}

mathMLEditor.prototype.selectPreviousElement = function() {
	if (this.selectedNode.nodeNr!=0) this.selectElement(this.nodes[this.selectedNode.nodeNr-1]);
}

mathMLEditor.prototype.selectNextElement = function() {
	if (this.selectedNode.nodeNr!=this.nodes.length-1) this.selectElement(this.nodes[this.selectedNode.nodeNr+1]);
}

mathMLEditor.prototype.setStatus = function(newStat) {
	this.oldStatus = this.status;
	this.status = newStat;
	this.showStatus();
}

mathMLEditor.prototype.resetStatus = function() {
	this.status = this.oldStatus;
	this.showStatus();
}

mathMLEditor.prototype.showStatus = function() {
	if (this.statusBar) this.statusBar.firstChild.nodeValue = this.status; else window.status = this.status;
}