/**
 *	selection and range abstraction between IE and W3C compatible browsers (tested only for mozilla & ie)
 *	(createContextualFragment used by pasteHTML seems to be Mozilla-only, and not part of W3C)
 *	(3.2.2011 createContextualFragment replaced by standards-compliant code)
 */
var editor_selection_enableDebug = true;

var wertti_selection = {
	w3c: window.getSelection ? true : false,
	storedSelection: null,
	userSelections: {},
	get: function() {
		var nativeSelection;
		var userSelection = {};
		if (window.getSelection) {
			this.w3c = true;
			nativeSelection = window.getSelection();
			userSelection.execTarget = document;
			userSelection.element = null;
			userSelection.parentNode = null;
			if(nativeSelection.anchorNode || nativeSelection.focusNode) {
				var searchNode = nativeSelection.anchorNode ? nativeSelection.anchorNode : nativeSelection.focusNode;
				userSelection.parentNode = searchNode;
				var parentEditable = this.getParentEditable(userSelection.parentNode);
				if(parentEditable) {
					userSelection.execTarget = document;
				} else {
					userSelection.execTarget = document;
				}
			}
			if(nativeSelection.rangeCount) {
				userSelection.text = nativeSelection.toString();
				userSelection.range = nativeSelection.getRangeAt(0);
				userSelection.parentNode = userSelection.range.commonAncestorContainer;
				var parentEditable = this.getParentEditable(userSelection.parentNode);
				if(!userSelection.execTarget) {
					if(parentEditable) {
						userSelection.execTarget = document;
					} else {
						userSelection.execTarget = document;
					}
				}

				if(userSelection.text && userSelection.range) {
					userSelection.type = "text";
				}
				userSelection.pasteHTML = function(txt) {
					var tempElement = document.createElement('div');
					tempElement.innerHTML = txt;
					if(tempElement.childNodes && tempElement.childNodes.length) {
						userSelection.range.deleteContents();
						for(var tIndex = 0; tIndex < tempElement.childNodes.length; tIndex++) {
							userSelection.range.insertNode(tempElement.childNodes[tIndex]);
						}
					}
				};
				if(nativeSelection.isCollapsed) {
					userSelection.type = "none";
				} else if(nativeSelection.anchorNode == nativeSelection.focusNode && userSelection.range) {
					if(userSelection.range.startContainer == userSelection.range.endContainer) {
						var allElements = document.getElementsByTagName("*");
						for(var i=0; i<allElements.length; i++) {
							if(nativeSelection.containsNode(allElements[i], false)) {
								userSelection.element = allElements[i];
								userSelection.type = "control";
								break;
							}
						}
					}
				}
			} else {
				userSelection.type = "none";
			}

			userSelection.collapse = function() {
				debugPrint("userSelection.collapse", "editor_selection");
				if(this.range) {
					this.range.collapse(true);
				}
			}

			userSelection.insertBefore = function(node) {
				if(this.range) {
					this.range.collapse(true);
					this.range.insertNode(node);
					return node;
				}
			};

			debugPrint("type = "+userSelection.type, "editor_selection");
		} else if (document.selection) {
			this.w3c = false;
			userSelection.type = document.selection.type.toLowerCase();
			nativeSelection = document.selection.createRange();
			userSelection.text = nativeSelection.text;
			userSelection.range = nativeSelection;
			userSelection.parentNode = null;
			if(userSelection.range && userSelection.range.parentElement) userSelection.parentNode = userSelection.range.parentElement();	// containing node
			userSelection.pasteHTML = function(txt) {
				debugPrint("userSelection.pasteHTML", "editor_selection");
				if(this.element) {
					// it's a controlrange collection
					// i don't know any other way to overwrite a controlrange collection than this (in IE)
					if(this.element.outerHTML) this.element.outerHTML = txt;
				} else {
					// it's a textrange
					this.range.pasteHTML(txt);
				}
			};
			userSelection.collapse = function() {
				debugPrint("userSelection.collapse", "editor_selection");
				this.range.collapse(true);
				this.range.select();
			};
			userSelection.insertBefore = function(node) {
				this.range.collapse(true);
				var oldId = node.getAttribute('id');
				var tempId = 'wertti-selection-temp';
				if(oldId) tempId += '-'+oldId;
				node.setAttribute('id', tempId);
				this.range.pasteHTML(node.outerHTML);
				if(node.parentNode) node.parentNode.removeChild(node);
				var temp = document.getElementById(tempId);
				temp.removeAttribute('id');
				if(oldId) temp.setAttribute('id', oldId);
				return temp;
			};

			userSelection.execTarget = null;
			if(userSelection.type == "control" && userSelection.range && userSelection.range.length) {
				userSelection.element = userSelection.range(0);
			}
			var searchNode = userSelection.range.parentElement ? userSelection.range.parentElement() : userSelection.element;
			var parentEditable = this.getParentEditable(searchNode);
			if(parentEditable) {
				userSelection.execTarget = parentEditable.document;
			} else {
				userSelection.execTarget = document;
			}
		}
		return userSelection;
	},

	store: function(selectionName) {
		var sel = this.get();
		if(selectionName) {
			this.userSelections[selectionName] = sel;
		} else {
			this.storedSelection = sel;
		}
		debugPrint("wertti_selection/store "+selectionName+" type="+sel.type+" w3c="+this.w3c,"editor_selection");
		return sel;
	},

	restore: function(selectionName) {
		var tmpSel = null;
		if(selectionName && this.userSelections[selectionName]) {
			tmpSel = this.userSelections[selectionName];
		} else {
			tmpSel = this.storedSelection;
		}
		debugPrint("wertti_selection/restore "+selectionName+" type="+tmpSel.type+" w3c="+this.w3c,"editor_selection");
		if(tmpSel) {
			if(tmpSel.range) {
				debugPrint("tmpSel.range="+tmpSel.range,"editor_selection");
				if(this.w3c) {
					var sel = window.getSelection();
					sel.removeAllRanges();
					var range = document.createRange();
					sel.addRange(tmpSel.range);
				} else if(document.selection) {
					tmpSel.range.select();
				}
			}
			return tmpSel;
		} else {
			return null;
		}
	},

	selectNone: function() {
		debugPrint("wertti_selection/selectNone","editor_selection");
		if (window.getSelection) {
			// seems to me  that it's impossible in mozilla to lose edit focus for eg. an image.
			// so i think this only works for text selections at the moment.
			this.w3c = true;
			nativeSelection = window.getSelection();
			if(nativeSelection.rangeCount) {
				var range = nativeSelection.getRangeAt(0);
				try {
					nativeSelection.collapse(range.startContainer, range.startOffset);
				} catch(e) {
					// ?
				}
			}
			nativeSelection.removeAllRanges();
			// document.execCommand("unselect", false, null);
		} else if (document.selection) {
			this.w3c = false;
			document.selection.empty();
		}
	},

	selectNodeText: function(node) {
		debugPrint("selectNodeText", "editor_selection");
		if (window.getSelection) {
			var sel = window.getSelection();
			sel.removeAllRanges();
			var range = document.createRange();
			range.selectNodeContents(node);
			sel.addRange(range);
			node.focus();
		} else if(document.selection) {
			var range = document.body.createTextRange();
			if(range) {
				range.moveToElementText(node);
				range.select();
			}
		}
	},

	selectNode: function(node) {
		debugPrint("selectNode", "editor_selection");
		if (window.getSelection) {
			var sel = window.getSelection();
			sel.removeAllRanges();
			var range = document.createRange();
			range.selectNode(node);
			sel.addRange(range);
			return wertti_selection.get();
		} else if(document.selection) {
			var rng = document.body.createControlRange();
			rng.add(node);
			rng.select();
			return wertti_selection.get();
		}
	},

	setEditFocus: function(node) {
		debugPrint("setEditFocus "+node, "editor_selection");
		if (window.getSelection) {
			// I don't know how to do this in firefox
		} else if(document.selection) {
			var rng = document.body.createControlRange();
			rng.add(node);
			rng.select();
		}
	},

	getParentEditable: function(searchNode) {
		var editable = null;
		var safe = 100;
		while(safe && searchNode && !editable) {
			if(searchNode.nodeType == Node.ELEMENT_NODE) {
//				debugPrint("searchNode "+searchNode+" "+searchNode.contentEditable, "editor_selection");
				if(searchNode.contentEditable == 'true') {
					editable = searchNode;
					debugPrint("editable "+editable, "editor_selection");
					break;
				}
			}
			safe--;
			searchNode = searchNode.parentNode;
		}
		return editable;
	},

	toSpans: function() {
		var spans = new Array();
		var sel = this.get();
		if(sel && sel.execTarget) {
			// first, we make the selection to a link
			var canLink = sel.execTarget.queryCommandEnabled("createlink", false, "selectionToSpan");
			if(canLink) {
//				debugPrint("canLink: " + canLink,"article_edit_new");
				var linkElements = new Array();
				sel.execTarget.execCommand("createlink", false, "selectionToSpan");
				// then, we get those elements
				var temp = doc.getElementsByTagName("a");
				if(temp && temp.length) for(var i = 0; i < temp.length; i++) {
					if(temp[i].getAttribute('href') == "selectionToSpan") {
						linkElements.push(temp[i]);
						debugPrint("linkElement: " + temp[i] + " cnt="+linkElements.length, "editor_selection");
					}
				}
				// if the link(s) were created
				if(linkElements.length) {
					for(var j=0; j < linkElements.length; j++) {
						var linkElement = linkElements[j];
						debugPrint("iterating link "+linkElement, "editor_selection");

						var span = sel.execTarget.createElement("span");
						debugPrint("created span "+span, "editor_selection");
						// insert the span before the created link
						linkElement.parentNode.insertBefore(span, linkElement);
						// move the link element inside the span
						var removed = linkElement.parentNode.removeChild(linkElement);
						span.appendChild(removed);
						debugPrint("appended removed link to the span "+span, "editor_selection");

						var inner = span.innerHTML;
						var linkContentStart = inner.indexOf(">")+1;
						var linkContentEnd = inner.toLowerCase().indexOf("</a>");
						var linkContentLength = linkContentEnd - linkContentStart;
						var spanContent = inner.substr(linkContentStart, linkContentLength);

						span.innerHTML = spanContent;
						spans.push(span);
					}
				} else {
					// otherwise do nothing
					debugPrint("Failed to create temporary link elements.", "editor_selection");
				}
			}
		}
		if(spans) {
			return spans;
		} else {
			return null;
		}
	}

}

