4 * Copyright 2009, Moxiecode Systems AB
5 * Released under LGPL License.
7 * License: http://tinymce.moxiecode.com/license
8 * Contributing: http://tinymce.moxiecode.com/contributing
12 var Dispatcher = tinymce.util.Dispatcher;
15 * This class handles the undo/redo history levels for the editor. Since the build in undo/redo has major drawbacks a custom one was needed.
17 * @class tinymce.UndoManager
19 tinymce.UndoManager = function(editor) {
20 var self, index = 0, data = [];
22 function getContent() {
23 return tinymce.trim(editor.getContent({format : 'raw', no_events : 1}));
28 * State if the user is currently typing or not. This will add a typing operation into one undo
29 * level instead of one new level for each keystroke.
31 * @field {Boolean} typing
36 * This event will fire each time a new undo level is added to the undo manager.
39 * @param {tinymce.UndoManager} sender UndoManager instance that got the new level.
40 * @param {Object} level The new level object containing a bookmark and contents.
42 onAdd : new Dispatcher(self),
45 * This event will fire when the user make an undo of a change.
48 * @param {tinymce.UndoManager} sender UndoManager instance that got the new level.
49 * @param {Object} level The old level object containing a bookmark and contents.
51 onUndo : new Dispatcher(self),
54 * This event will fire when the user make an redo of a change.
57 * @param {tinymce.UndoManager} sender UndoManager instance that got the new level.
58 * @param {Object} level The old level object containing a bookmark and contents.
60 onRedo : new Dispatcher(self),
63 * Stores away a bookmark to be used when performing an undo action so that the selection is before
64 * the change has been made.
66 * @method beforeChange
68 beforeChange : function() {
69 // Set before bookmark on previous level
71 data[index].beforeBookmark = editor.selection.getBookmark(2, true);
75 * Adds a new undo level/snapshot to the undo list.
78 * @param {Object} l Optional undo level object to add.
79 * @return {Object} Undo level that got added or null it a level wasn't needed.
81 add : function(level) {
82 var i, settings = editor.settings, lastLevel;
85 level.content = getContent();
87 // Add undo level if needed
88 lastLevel = data[index];
89 if (lastLevel && lastLevel.content == level.content)
93 if (settings.custom_undo_redo_levels) {
94 if (data.length > settings.custom_undo_redo_levels) {
95 for (i = 0; i < data.length - 1; i++)
96 data[i] = data[i + 1];
103 // Get a non intrusive normalized bookmark
104 level.bookmark = editor.selection.getBookmark(2, true);
106 // Crop array if needed
107 if (index < data.length - 1)
108 data.length = index + 1;
111 index = data.length - 1;
113 self.onAdd.dispatch(self, level);
114 editor.isNotDirty = 0;
120 * Undoes the last action.
123 * @return {Object} Undo level or null if no undo was performed.
134 level = data[--index];
136 editor.setContent(level.content, {format : 'raw'});
137 editor.selection.moveToBookmark(level.beforeBookmark);
139 self.onUndo.dispatch(self, level);
146 * Redoes the last action.
149 * @return {Object} Redo level or null if no redo was performed.
154 if (index < data.length - 1) {
155 level = data[++index];
157 editor.setContent(level.content, {format : 'raw'});
158 editor.selection.moveToBookmark(level.bookmark);
160 self.onRedo.dispatch(self, level);
167 * Removes all undo levels.
178 * Returns true/false if the undo manager has any undo levels.
181 * @return {Boolean} true/false if the undo manager has any undo levels.
183 hasUndo : function() {
184 return index > 0 || this.typing;
188 * Returns true/false if the undo manager has any redo levels.
191 * @return {Boolean} true/false if the undo manager has any redo levels.
193 hasRedo : function() {
194 return index < data.length - 1 && !this.typing;