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([
 44 	'aloha/core',
 45 	'jquery'
 46 ], function (
 47 	Aloha,
 48 	$
 49 ) {
 50 	'use strict';
 51 
 52 	var zeroWidthNode = null;
 53 
 54 	/**
 55 	 * Replaces the text in given text with the given text.
 56 	 *
 57 	 * @param node
 58 	 *        A text node attached to the DOM.
 59 	 * @param text
 60 	 *        A string that is to replace the text of the given text node.
 61 	 */
 62 	function replaceMergeTextNode(node, text) {
 63 		node.deleteData(0, node.length);
 64 		if ('' !== text) {
 65 			if (node.nextSibling && 3 === node.nextSibling.nodeType) {
 66  67 				node.nextSibling.insertData(0, text);
 68 			} else if (node.previousSibling && 3 === node.previousSibling.nodeType) {
 69 				node.previousSibling.insertData(node.previousSibling.length, text);
 70 			} else {
 71 				node.insertData(0, text);
 72 			}
 73 		}
 74 		// We don't remove the node immediately to avoid intefering with a
 75 		// caller's range object that may have a start or end containers
 76 		// equal to this node. Removing it in a timeout may still interfere
 77 		// with the selection, but that was not a problem during testing.
 78 		setTimeout(function () {
 79 			if (0 === node.length) {
 80 				$(node).remove();
 81 			}
 82 		}, 0);
 83 	}
 84 
 85 	/**
 86 	 * Removes a previously inserted zero width text node.
 87 	 * See insertZeroWidthTextNodeFix().
 88 	 */
 89 	function removeZeroWidthTextNodeFix() {
 90 		if (!zeroWidthNode) {
 91 			return;
 92 		}
 93 		// We want to only replace a single zero-width character to avoid
 94 		// interfering with the other zero-width whitespace hack that makes
 95 		// empty lines visible in IE7.
 96 		var text = zeroWidthNode.nodeValue.replace(/\u200b/, '');
 97 		if (text === zeroWidthNode.nodeValue) {
 98 			console.warn('Expected to remove the zero width text node fix, but couldn\'t find it');
 99 		}
100 		replaceMergeTextNode(zeroWidthNode, text);
101 		zeroWidthNode = null;
102 	}
103 
104 	/**
105 	 * Inserts a zero width text node before or after a block.
106 	 *
107 	 * There is a problem where some browsers can't select the boundary
108 	 * between some contenteditable content and non-contenteditable
109 	 * content. For example, if in the example at the top of the file
110 	 * the selection were one step to the right "...</span>s[]ome..."
111 	 * and the left cursor key were pressed, then the selection would
112 	 * just disappear or be stuck between the span and the text node.
113 	 *
114 	 * To work around this problem a zero width text node is inserted
115 	 * before or after a block.
116 	 *
117 	 * The inserted zero width text node will be removed automatically
118 	 * when it isn't necessary any more (on selection change or on
119 	 * editable.getContents()).
120 	 *
121 	 * TODO: In retrospect, a better alternative may be to simply wrap
122 	 *       every inlin-block with an editable span.
123 	 * @param block
124 	 *        The DOM element for a block before or after which the zero
125 	 *        width text node will be inserted.
126 	 * @param isGoingLeft
127 	 *        True if the zero width text node is to be inserted after
128 	 *        the block element, or false if the zero width text node is
129 	 *        to be inserted before the block element.
130 	 * @return
131 	 *        The text node that was inserted.
132 	 */
133 	function insertZeroWidthTextNodeFix(block, isGoingLeft) {
134 		removeZeroWidthTextNodeFix();
135 		zeroWidthNode = document.createTextNode("\u200b");
136 		if (isGoingLeft) {
137 			$(block).after(zeroWidthNode);
138 		} else {
139 			$(block).before(zeroWidthNode);
140 		}
141 		Aloha.bind('aloha-selection-changed', function (event) {
142 			removeZeroWidthTextNodeFix();
143 			Aloha.unbind(event);
144 		});
145 		return zeroWidthNode;
146 	}
147 
148 	return {
149 		removeZeroWidthTextNodeFix: removeZeroWidthTextNodeFix,
150 		insertZeroWidthTextNodeFix: insertZeroWidthTextNodeFix
151 	};
152 });
153