1 /* block-jump.js is part of the Aloha Editor project http://aloha-editor.org
  2  *
  3  * Aloha Editor is a WYSIWYG HTML5 inline editing library and editor. 
  4  * Copyright (c) 2010-2012 Gentics Software GmbH, Vienna, Austria.
  5  * Contributors http://aloha-editor.org/contribution.php 
  6  * 
  7  * Aloha Editor is free software; you can redistribute it and/or
  8  * modify it under the terms of the GNU General Public License
  9  * as published by the Free Software Foundation; either version 2
 10  * of the License, or any later version.
 11  *
 12  * Aloha Editor is distributed in the hope that it will be useful,
 13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15  * GNU General Public License for more details.
 16  *
 17  * You should have received a copy of the GNU General Public License
 18  * along with this program; if not, write to the Free Software
 19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 20  * 
 21  * As an additional permission to the GNU GPL version 2, you may distribute
 22  * non-source (e.g., minimized or compacted) forms of the Aloha-Editor
 23  * source code without the copy of the GNU GPL normally required,
 24  * provided you include this license notice and a URL through which
 25  * recipients can access the Corresponding Source.
 26  */
 27 /**
 28  * Implements some logic related to moving the cursor keys across blocks.
 29  * 
 30  * In the following example
 31  *
 32  * "some text<span class="aloha-block ..." contenteditable="false" ...>...</span>[]some text"
 33  *
 34  * when one moves the cursor indicated by "[]" to the left, the entire
 35  * non-contenteditable block is skipped. The same for moving the cursor
 36  * right across the block.
 37  *
 38  * TODO: actually, the block shouldn't be skipped, it should be
 39  *       selected/highlighted first.
 40  * TODO: this file currently doesn't contain all the code to implement
 41  *       block jumping. Some of it is currently implemented in markup.js.
 42  */
 43 define(['aloha/core', 'jquery'], function(Aloha, $){
 44 
 45 	var zeroWidthNode = null;
 46 
 47 	/**
 48 	 * Removes a previously inserted zero width text node.
 49 	 * See insertZeroWidthTextNodeFix().
 50 	 */
 51 	function removeZeroWidthTextNodeFix() {
 52 		if (!zeroWidthNode) {
 53 			return;
 54 		}
 55 		// We want to only replace a single zero-width character to avoid
 56 		// interfering with the other zero-width whitespace hack that makes
 57 		// empty lines visible in IE7.
 58 		var text = zeroWidthNode.nodeValue.replace(/\u200b/, '');
 59 		if (text === zeroWidthNode.nodeValue) {
 60 			console.warn('Expected to remove the zero width text node fix, but couldn\'t find it');
 61 		}
 62 		replaceMergeTextNode(zeroWidthNode, text);
 63 		zeroWidthNode = null;
 64 	}
 65 
 66 	/**
 67 	 * Replaces the text in given text with the given text.
 68 	 *
 69 	 * @param node
 70 	 *        A text node attached to the DOM.
 71 	 * @param text
 72 	 *        A string that is to replace the text of the given text node.
 73 	 */
 74 	function replaceMergeTextNode(node, text) {
 75 		node.deleteData(0, node.length);
 76 		if ("" === text) {
 77 			// already deleted above
 78 		} else if (node.nextSibling && 3 === node.nextSibling.nodeType) {
 79 			node.nextSibling.insertData(0, text);
 80 		} else if (node.previousSibling && 3 === node.previousSibling.nodeType) {
 81 			node.previousSibling.insertData(node.previousSibling.length, text);
 82 		} else {
 83 			node.insertData(0, text);
 84 		}
 85 		// We don't remove the node immediately to avoid intefering with a
 86 		// caller's range object that may have a start or end containers
 87 		// equal to this node. Removing it in a timeout may still interfere
 88 		// with the selection, but that was not a problem during testing.
 89 		setTimeout(function () {
 90 			if (0 === node.length) {
 91 				$(node).remove();
 92 			}
 93 		}, 0);
 94 	}
 95 
 96 	/**
 97 	 * Inserts a zero width text node before or after a block.
 98 	 *
 99 	 * There is a problem where some browsers can't select the boundary
100 	 * between some contenteditable content and non-contenteditable
101 	 * content. For example, if in the example at the top of the file
102 	 * the selection were one step to the right "...</span>s[]ome..."
103 	 * and the left cursor key were pressed, then the selection would
104 	 * just disappear or be stuck between the span and the text node.
105 	 *
106 	 * To work around this problem a zero width text node is inserted
107 	 * before or after a block.
108 	 *
109 	 * The inserted zero width text node will be removed automatically
110 	 * when it isn't necessary any more (on selection change or on
111 	 * editable.getContents()).
112 	 *
113 	 * TODO: In retrospect, a better alternative may be to simply wrap
114 	 *       every inlin-block with an editable span.
115 	 * @param block
116 	 *        The DOM element for a block before or after which the zero
117 	 *        width text node will be inserted.
118 	 * @param isGoingLeft
119 	 *        True if the zero width text node is to be inserted after
120 	 *        the block element, or false if the zero width text node is
121 	 *        to be inserted before the block element.
122 	 * @return
123 	 *        The text node that was inserted.
124 	 */
125 	function insertZeroWidthTextNodeFix(block, isGoingLeft) {
126 		removeZeroWidthTextNodeFix();
127 		zeroWidthNode = document.createTextNode("\u200b");
128 		if (isGoingLeft) {
129 			$(block).after(zeroWidthNode);
130 		} else {
131 			$(block).before(zeroWidthNode);
132 		}
133 134 		Aloha.bind('aloha-selection-changed', function(event){
135 			removeZeroWidthTextNodeFix();
136 			Aloha.unbind(event);
137 		});
138 		return zeroWidthNode;
139 	}
140 
141 	return {
142 		removeZeroWidthTextNodeFix: removeZeroWidthTextNodeFix,
143 		insertZeroWidthTextNodeFix: insertZeroWidthTextNodeFix
144 	};
145 });
146