/* * Copyright(c) 2011 * OJC Technologies * all rights reserved * @author: Jake Smith */ function RealCursor(sideIndicator) { var cr_symbol, nbsp, oof, tab_symbol; this.LEFT = "left"; this.RIGHT = "right"; this.isVirtual = null; this.side = sideIndicator; this.hardret = document.createElement("span"); this.hardret.setAttribute("id", this.side.toString() + "hardreturn"); this.softret = document.createElement("span"); this.softret.setAttribute("id", this.side.toString() + "softreturn"); this.tab = document.createElement("span"); this.tab.setAttribute("id", this.side.toString() + "tab"); /* oof is short for "out of flow"---i.e., won't cause the paragraph to * reflow */ oof = document.createElement("span"); oof.setAttribute("id", this.side.toString() + "oof"); nbsp = document.createTextNode("\u00b7"); oof.appendChild(nbsp); this.softret.appendChild(oof); oof = document.createElement("span"); oof.setAttribute("id", this.side.toString() + "oof"); cr_symbol = document.createTextNode("\u23ce"); oof.appendChild(cr_symbol); this.hardret.appendChild(oof); oof = document.createElement("span"); oof.setAttribute("id", this.side.toString() + "oof"); tab_symbol = document.createTextNode("\u27fc"); oof.appendChild(tab_symbol); this.tab.appendChild(oof); this.cursor = new PhysicalCursor("span", this.side.toString() + "cursor", this.side == "right" ? "cyan" : "orange"); this.atStartOfPage = false; this.atEndOfPage = false; this.parentNode = null; this.startOff = null; this.endOff = null; this.overText = false; this.isTarget = function(clickTargetElement) { // TODO: deal with floating cursor objects return (this.cursor.element == clickTargetElement); } this.placeAt = function(node, startOffset, endOffset) { var before, after, range, string; if (this.isVirtual != null) this.removeVirtual(); string = node.nodeValue; this.parentNode = node.parentNode; range = string.substring(startOffset, endOffset); before = string.substring(0, startOffset); after = string.substring(endOffset); range = document.createTextNode(range); before = document.createTextNode(before); after = document.createTextNode(after); this.startOff = startOffset; this.endOff = endOffset; this.cursor.push(range); this.parentNode.replaceChild(after, node); this.parentNode.insertBefore(this.cursor.element, after); this.parentNode.insertBefore(before, this.cursor.element); this.parentNode.normalize(); if (this.isTab()) { printer.print("is tab"); this.insertTab(); } else if (this.isCollapsed()) this.insertReturn(); this.printWidth(); } this.remove = function() { var textNode; textNode = this.cursor.pop(); if (textNode == null || this.atEndOfPage) { this.parentNode.removeChild(this.cursor.element); } else { this.parentNode.replaceChild(textNode, this.cursor.element); this.parentNode.normalize(); this.parentNode = null; this.atStartOfPage = false; this.atEndOfPage = false; this.startOff = null; this.endOff = null; } } this.insert = function(text) { var textNode; textNode = document.createTextNode(text); this.parentNode.insertBefore(textNode, this.cursor.element); this.parentNode.normalize(); } this.del = function() { this.cursor.pop(); this.rightSideGoesRight(); } this.bksps = function() { if (this.isVirtual != null) this.removeVirtual(); this.cursor.pop(); this.leftSideGoesLeft(); if (this.isTab()) { printer.print("is tab"); this.insertTab(); } else if (this.isCollapsed()) this.insertReturn(); } this.isCollapsed = function() { var functor = new CursorFunctor(); return functor.isNodeCollapsed(this.cursor.element); } this.isToPreserveWhitespace = function() { var functor = new CursorFunctor(); return functor.isToPreserveWhitespace(this.cursor.element); } this.isTab = function() { var first, last; if (!this.isToPreserveWhitespace()) return false; first = this.cursor.element.firstChild; last = this.cursor.element.lastChild; if (first != last) return false; if (first.nodeType != Node.TEXT_NODE) return false; return first.nodeValue == "\t"; } this.isHardReturn = function() { var first, last; if (!this.isToPreserveWhitespace()) return false; first = this.cursor.element.firstChild; last = this.cursor.element.lastChild; if (first != last) return false; if (first.nodeType != Node.TEXT_NODE) return false; return first.nodeValue == "\n" || first.nodeValue == "\r"; } this.moveLeft = function() { var tempNode; if (this.atStartOfPage) { // TODO return; } if (this.isVirtual != null) this.removeVirtual(); tempNode = this.cursor.pop(); if (this.atEndOfPage) { //dumps EOL/EOF indicator this.atEndOfPage = false; } else { if (this.cursor.element.nextSibling == null) { this.parentNode.appendChild(tempNode); } else { this.parentNode.insertBefore(tempNode, this.cursor.element.nextSibling); } } this.leftSideGoesLeft(); if (this.isTab()) { printer.print("is tab"); this.insertTab(); } else if (this.isCollapsed()) this.insertReturn(); this.printWidth(); } this.removeVirtual = function() { var ret; printer.print("enter removeVirtual"); ret = this.isVirtual; if (ret == null) { printer.print("ret is null"); return; } printer.print("ret.parentNode = " + ret.parentNode); ret.parentNode.removeChild(ret); printer.print("exit removeVirtual"); this.isVirtual = null; } this.insertTab = function() { try { var ch = this.cursor.element.firstChild; } catch (err) { printer.print("insertTab: " + err); } this.cursor.element.insertBefore(this.tab, ch); return this.isVirtual = this.tab; return false; } this.insertReturn = function() { var ret; try { var ch = this.cursor.element.firstChild; } catch (err) { printer.print("insertReturn: " + err); } ret = this.isHardReturn() ? this.hardret : this.softret; this.cursor.element.insertBefore(ret, ch); return this.isVirtual = ret; return false; printer.print("enter insertReturn"); functor = new CursorFunctor(); for (p = functor.getNodePreceding(this.cursor.element); p != null; p = functor.getNodePreceding(p)) { printer.print("p = " + p.toString()); if (p.parentNode == null) continue; p.parentNode.replaceChild(this.softret, p); printer.print("1"); this.softret.appendChild(p); printer.print("2"); if (!functor.isNodeCollapsed(this.softret)) break; printer.print("3"); this.softret.parentNode.replaceChild(p, this.softret); printer.print("4"); } if (p == null) { printer.print("exit insertReturn false"); return false; } else { printer.print("exit insertReturn true"); return true; } } this.moveRight = function() { var tempNode; if (this.atEndOfPage) { // TODO return; } if (this.isVirtual != null) this.removeVirtual(); tempNode = this.cursor.pop(); if (this.atStartOfPage) { this.atStartOfPage = false; } else { this.parentNode.insertBefore(tempNode, this.cursor.element); } this.rightSideGoesRight(); if (this.isTab()) { printer.print("is tab"); this.insertTab(); } else if (this.isCollapsed()) this.insertReturn(); this.printWidth(); } this.overBreakingWS = function() { var functor, cursorText, result; result = false; functor = new CursorFunctor(); cursorText = this.cursor.contents.nodeValue; if (functor.isBreakingWhitespace(cursorText)) { result = true; } return result; } this.overChar = function() { var functor, result, cursorText; result = false; functor = new CursorFunctor(); cursorText = this.cursor.contents.nodeValue; if (!functor.isWhitespace(cursorText)) { result = true; } return result; } this.overNonBreakWS = function() { var functor, cursorText, result; result = false; functor = new CursorFunctor(); cursorText = this.cursor.contents.nodeValue; if (functor.isNBSP(cursorText)) { result = true; } return result; } //-----------------------// // "Private" functions // //-----------------------// this.rightSideGoesRight = function() { var nextSib, tempNode, selectorEnd; var tempText, selText, functor, nextNode; functor = new CursorFunctor(); nextSib = this.cursor.element.nextSibling; nextNode = functor.getNodeFollowing(this.cursor.element); if (nextNode == null) { //at end of page tempNode = document.createTextNode("_"); this.cursor.push(tempNode); this.atEndOfPage = true; } else if (nextNode == nextSib) { // within-node shifting right if (nextSib.nodeType == Node.TEXT_NODE) { tempText = nextSib.nodeValue; if (functor.isToPreserveWhitespace(this.parentNode)) { selectorEnd = 1; } else { selectorEnd = functor.getEndOffset(tempText, 0); } selText = tempText.substring(0, selectorEnd); tempText = tempText.substring(selectorEnd); tempNode = document.createTextNode(tempText); this.parentNode.replaceChild(tempNode, nextSib); tempNode = document.createTextNode(selText); this.cursor.push(tempNode); } else { this.placeOverNonTextNode(nextNode); } } else { // jumping elements this.parentNode.removeChild(this.cursor.element); if (nextNode.nodeType != Node.TEXT_NODE) { this.placeOverNonTextNode(nextNode); } else { tempText = nextNode.nodeValue if (functor.isToPreserveWhitespace(this.parentNode)) { selectorEnd = 1; } else { selectorEnd = functor.getEndOffset(tempText, 0); } this.parentNode = nextNode.parentNode; this.placeAt(nextNode, 0, selectorEnd); } } this.parentNode.normalize(); } this.leftSideGoesLeft = function() { var functor, prevSib, prevNode, tempText; var selectorStart, tempNode, selText; functor = new CursorFunctor(); prevSib = this.cursor.element.previousSibling; prevNode = functor.getNodePreceding(this.cursor.element); if (prevNode == null) { if (false) { tempNode = document.createTextNode("$"); this.cursor.push(tempNode); } this.atStartOfPage = true; } else if (prevNode == prevSib) { if (prevNode.nodeType != Node.TEXT_NODE) { this.placeOverNonTextNode(prevNode); } else { tempText = prevSib.nodeValue; if (functor.isToPreserveWhitespace(this.parentNode)) { selectorStart = tempText.length - 1; } else { selectorStart = functor.getStartOffset(tempText, tempText.length - 1); } selText = tempText.substring(selectorStart); tempText = tempText.substring(0, selectorStart); tempNode = document.createTextNode(tempText); this.parentNode.replaceChild(tempNode, prevSib); tempNode = document.createTextNode(selText); this.cursor.push(tempNode); } } else { //jumping nodes this.parentNode.removeChild(this.cursor.element); if (prevNode.nodeType != Node.TEXT_NODE) { this.placeOverNonTextNode(prevNode); } else { tempText = prevNode.nodeValue if (functor.isToPreserveWhitespace(this.parentNode)) { selectorStart = tempText.length - 1; } else { selectorStart = functor.getStartOffset(tempText, tempText.length - 1); } this.placeAt(prevNode, selectorStart, tempText.length); } } this.parentNode.normalize(); } this.placeOverNonTextNode = function(nextNode) { var tempNode; tempNode = nextNode; this.parentNode = nextNode.parentNode; this.parentNode.replaceChild(this.cursor.element, nextNode); this.cursor.push(tempNode); this.startOff = 0; this.endOff = 1; printer.print(this.side + "cursor over non-TextNode"); } this.printWidth = function() { var rect; rect = this.cursor.element.getBoundingClientRect(); printer.print(this.side + " cursor width: " + rect.width); } } // RealCursor()