1 /* state-override.js is part of 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 define([ 28 'aloha/core', 29 'jquery', 30 'aloha/command', 31 'util/dom2', 32 'util/maps', 33 'util/range', 34 'PubSub' 35 ], function ( 36 Aloha, 37 jQuery, 38 Command, 39 Dom, 40 Maps, 41 RangeObject, 42 PubSub 43 ) { 44 'use strict'; 45 46 // Because we want to provide an easy way to disable the state-override feature. 47 var enabled = Aloha.settings.stateOverride !== false; 48 var overrides = null; 49 var overrideRange = null; 50 51 function rangeObjectFromRange(range) { 52 return new RangeObject(range); 53 } 54 55 function clear() { 56 overrideRange = null; 57 overrides = null; 58 } 59 60 function keyPressHandler(event) { 61 if (!overrides) { 62 return; 63 } 64 if (event.altKey || event.ctrlKey || !event.which) { 65 return; 66 } 67 var selection = Aloha.getSelection(); 68 if (!selection.getRangeCount()) { 69 return; 70 } 71 var text = String.fromCharCode(event.which); 72 var range = selection.getRangeAt(0); 73 Dom.insertSelectText(text, range); 74 Maps.forEach(overrides, function (formatFn, command) { 75 formatFn(command, range); 76 }); 77 Dom.collapseToEnd(range); 78 selection.removeAllRanges(); 79 selection.addRange(range); 80 // Because we handled the character insert ourselves via 81 // insertText we must not let the browser's default action 82 // insert the character a second time. 83 event.preventDefault(); 84 } 85 86 function set(command, range, formatFn) { 87 if (!enabled) { 88 return; 89 } 90 overrideRange = range; 91 overrides = overrides || {}; 92 overrides[command] = formatFn; 93 } 94 95 function setWithRangeObject(command, rangeObject, formatFn) { 96 if (!enabled) { 97 return; 98 } 99 set(command, Dom.rangeFromRangeObject(rangeObject), function (command, range) { 100 var rangeObject = rangeObjectFromRange(range); 101 formatFn(command, rangeObject); 102 Dom.setRangeFromRef(range, rangeObject); 103 }); 104 // Because without doing rangeObject.select(), the 105 // next insertText command (see editable.js) will 106 // not be reached and instead the browsers default 107 // insert behaviour will be applied (which doesn't 108 // know anything about state overrides). I don't 109 // know the exact reasons why; probably some 110 // stopPropagation somewhere by some plugin. 111 rangeObject.select(); 112 } 113 114 function enabledAccessor(trueFalse) { 115 if (null != trueFalse) { 116 enabled = trueFalse; 117 } 118 return enabled; 119 } 120 121 // https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html#state-override 122 // "Whenever the number of ranges in the selection changes to 123 // something different, and whenever a boundary point of the range 124 // at a given index in the selection changes to something different, 125 // the state override and value override must be unset for every 126 // command." 127 Aloha.bind('aloha-selection-changed', function (event, range) { 128 if (overrideRange && !Dom.areRangesEq(overrideRange, range)) { 129 clear(); 130 // Because the UI may reflect the any potentially state 131 // overrides that are now no longer in effect, we must 132 // redraw the UI according to the current selection. 133 PubSub.pub('aloha.selection.context-change', { 134 range: range, 135 event: event 136 }); 137 } 138 }); 139 140 return { 141 enabled: enabledAccessor, 142 keyPressHandler: keyPressHandler, 143 setWithRangeObject: setWithRangeObject, 144 set: set, 145 clear: clear 146 }; 147 }); 148