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