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