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