/** * Copyright (c) 2011 * OJC Technologies * all rights reserved * @author: Jake Smith * * * REQUIRES JQuery, cursorinserter.js, and, for debug, printer.js */ function processClick(e) { var event, clickX, clickY, target, textNodes, cursorInfo; event = e || window.event; event.stopPropagation(); clickX = event.pageX; clickY = event.pageY; target = event.target || event.srcElement; // revisit below: still want a cursor when we click -near- (but not on) text if(target.nodeName == 'BODY' || target.nodeName == 'HTML' || target == document.getElementById(cursorId)) return; removeStandingCursor(); textNodes = getTerminalNodesIn(target); cursorInfo = getCursorPosition(textNodes, clickX, clickY); dropCursorAt(cursorInfo[0], cursorInfo[1]); } function getTerminalNodesIn(target) { if(target.childElementCount == 0) { return target.childNodes; } return severalTerminalNodes(target); } function severalTerminalNodes(target) { var i, termNodes, termCount; termNodes = new Array(); termCount = 0; for (i = 0; i < target.childNodes.length; i++) { var childNode = target.childNodes[i]; if (childNode.nodeType == Node.TEXT_NODE) { termNodes[termCount] = childNode; termCount++; } } return termNodes; } /** * |==========================| * | from helperbranch | * |==========================| */ function pointInRect(rect, x, y) { var bool, l, r, t, b; l = rect.left + window.scrollX; r = rect.right + window.scrollX; t = Math.round(rect.top) + window.scrollY; b = Math.round(rect.bottom) + window.scrollY; // possibily rounding issues with Firefox (rect.top & bottom) if (x < l) { bool = false; } else if (x > r) { bool = false; } else if (y < t) { bool = false; } else if (y > b) { bool = false; } else { bool = true; } return bool; } function clickInBounds(element, x, y) { var i, boundingRects; boundingRects = element.getClientRects(); // netbeans lies; supported accd. to quirksmode for (i = 0; i < boundingRects.length; i += 1) { if (pointInRect(boundingRects[i], x, y)) { return true; } } return false; } function getSubrange(elem, startI, endI) { var subR; subR = document.createRange(); subR.setStart(elem, startI); subR.setEnd(elem, endI); return subR } function specifyIndex(node, x, y) { var len, index, fence, parent, tempSpan; len = node.nodeValue.length; if(len < 1) { index = -2; // definitely not here } else if (len === 1) { // length of 1, search the one element parent = node.parentNode; tempSpan = makeTempSpan(node); parent.replaceChild(tempSpan, node); index = (clickInBounds(tempSpan, x, y)) ? 0 : -2; parent.replaceChild(node, tempSpan); parent.normalize(); } else { // length > 1; will have to split and search parent = node.parentNode; fence = Math.ceil(len / 2); var newNode = node.splitText(fence); // printDebug("subdividing into \'" + node.nodeValue + "\' and \'" // + newNode.nodeValue + "\' (fence = " + fence + ")"); var tempSpanL = makeTempSpan(node); var tempSpanR = makeTempSpan(newNode); parent.replaceChild(tempSpanL, node); parent.replaceChild(tempSpanR, newNode); if (clickInBounds(tempSpanL, x, y)) { // in left side index = specifyIndex(tempSpanL.childNodes[0], x, y, 0, -1); } else if (clickInBounds(tempSpanR, x, y)) { // in right side index = specifyIndex(tempSpanR.childNodes[0], x, y, 0, -1) + fence; } else { throw new Error("helperbranch.js, Line _117_"); } parent.replaceChild(node, tempSpanL); parent.replaceChild(newNode, tempSpanR); parent.normalize(); } return index; } function replaceWith(newNode, oldNode) { var nextSib, parent; nextSib = oldNode.nextSibling; parent = oldNode.parentNode; parent.removeChild(oldNode); if (nextSib) { parent.insertBefore(newNode, nextSib); } else { parent.appendChild(newNode); } } function makeTempSpan(textNode) { var copyTextNode, elem; copyTextNode = document.createTextNode(textNode.nodeValue); elem = getEmptyTempSpan(); elem.appendChild(copyTextNode); return elem; } function getEmptyTempSpan() { var elem; elem = document.createElement('span'); elem.setAttribute('class', 'temporarysearchspan'); return elem; } function getOneTextNodeFromSeveral(arr, x, y) { var textNode, nodeSpan, contains, i; for (i = 0; i < arr.length; i += 1) { textNode = arr[i]; nodeSpan = makeTempSpan(textNode); // span across the entire textnode replaceWith(nodeSpan, textNode); // insert span in place of textnode contains = clickInBounds(nodeSpan, x, y) ? true : false; replaceWith(textNode, nodeSpan); // return temp span to textnode if (contains) { return textNode; } } return null; } function getCursorPosition(textNodeArr, clickX, clickY) { var cursorIndex, oneNode, positionData; if (textNodeArr == null) { throw new Error( "banana phone ... clickchooser.js > getCursorPosition(array,x,y)"); } else if (textNodeArr.length < 1) { throw new Error("looking in empty array of textnodes"); } else if (textNodeArr.length > 1) { oneNode = getOneTextNodeFromSeveral(textNodeArr, clickX, clickY); } else { var aNode = textNodeArr[0]; var tempSpan = makeTempSpan(aNode); var contains; replaceWith(tempSpan, aNode); contains = clickInBounds(tempSpan, clickX, clickY); replaceWith(aNode, tempSpan); oneNode = (contains) ? aNode : null; } if(oneNode === null) { if (isAbove(textNodeArr[0], clickY)) { positionData = new Array(textNodeArr[0], 0); } else if (isBelow(textNodeArr[textNodeArr.length - 1], clickY)) { var n = textNodeArr[textNodeArr.length - 1]; positionData = new Array(n, n.nodeValue.length); //throw new Error("Clicked BELOW the text in this element"); } else { throw new Error("clicked TO THE SIDE of the text"); } } else { cursorIndex = specifyIndex(oneNode, clickX, clickY); positionData = new Array(oneNode, cursorIndex); } return positionData; } function isAbove(node, clickY) { var span, bounds, top; span = makeTempSpan(node); replaceWith(span, node); bounds = span.getBoundingClientRect(); replaceWith(node, span); top = bounds.top; return (clickY < top); } function isBelow(node, y) { var span, bounds, bottom; span = makeTempSpan(node); replaceWith(span, node); bounds = span.getBoundingClientRect(); replaceWith(node, span); bottom = bounds.bottom; return (y > bottom); }