f277c8480ce06c7709e6f246459c3159df949b39
1 /***************************************************************
4 * (c) 2008-2012 Stanislas Rolland <typo3(arobas)sjbr.ca>
7 * This script is part of the TYPO3 project. The TYPO3 project is
8 * free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * The GNU General Public License can be found at
14 * http://www.gnu.org/copyleft/gpl.html.
15 * A copy is found in the textfile GPL.txt and important notices to the license
16 * from the author is found in LICENSE.txt distributed with these scripts.
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * This script is a modified version of a script published under the htmlArea License.
25 * A copy of the htmlArea License may be found in the textfile HTMLAREA_LICENSE.txt.
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
30 * Undo Redo Plugin for TYPO3 htmlArea RTE
32 HTMLArea
.UndoRedo
= Ext
.extend(HTMLArea
.Plugin
, {
34 * This function gets called by the class constructor
36 configurePlugin: function (editor
) {
37 this.pageTSconfiguration
= this.editorConfiguration
.buttons
.undo
;
38 this.customUndo
= true;
39 this.undoQueue
= new Array();
40 this.undoPosition
= -1;
41 // Maximum size of the undo queue
43 // The time interval at which undo samples are taken: 1/2 sec.
44 this.undoTimeout
= 500;
46 * Registering plugin "About" information
48 var pluginInformation
= {
50 developer
: 'Stanislas Rolland',
51 developerUrl
: 'http://www.sjbr.ca',
52 copyrightOwner
: 'Stanislas Rolland',
54 sponsorUrl
: 'http://www.sjbr.ca',
57 this.registerPluginInformation(pluginInformation
);
59 * Registering the buttons
61 var buttonList
= this.buttonList
, buttonId
;
62 for (var i
= 0; i
< buttonList
.length
; ++i
) {
63 var button
= buttonList
[i
];
65 var buttonConfiguration
= {
67 tooltip
: this.localize(buttonId
.toLowerCase()),
68 iconCls
: 'htmlarea-action-' + button
[3],
69 action
: 'onButtonPress',
70 hotKey
: ((this.editorConfiguration
.buttons
[buttonId
.toLowerCase()] && this.editorConfiguration
.buttons
[buttonId
.toLowerCase()].hotKey
) ? this.editorConfiguration
.buttons
[buttonId
.toLowerCase()].hotKey
: button
[2]),
73 this.registerButton(buttonConfiguration
);
78 * The list of buttons added by this plugin
81 ['Undo', null, 'z', 'undo'],
82 ['Redo', null, 'y', 'redo']
85 * This function gets called when the editor is generated
87 onGenerate: function () {
88 // Start undo snapshots
89 if (this.customUndo
) {
91 run
: this.takeSnapshot
,
93 interval
: this.undoTimeout
99 * Start the undo/redo snapshot task
102 if (this.customUndo
) {
103 Ext
.TaskMgr
.start(this.task
);
107 * Start the undo/redo snapshot task
110 if (this.customUndo
) {
111 Ext
.TaskMgr
.stop(this.task
);
115 * Take a snapshot of the current contents for undo
117 takeSnapshot: function () {
118 var currentTime
= (new Date()).getTime();
119 var newSnapshot
= false;
120 if (this.undoPosition
>= this.undoSteps
) {
121 // Remove the first element
122 this.undoQueue
.shift();
125 // New undo slot should be used if this is first takeSnapshot call or if undoTimeout is elapsed
126 if (this.undoPosition
< 0 || this.undoQueue
[this.undoPosition
].time
< currentTime
- this.undoTimeout
) {
131 var text
= this.editor
.getInnerHTML();
134 // If previous slot contains the same text, a new one should not be used
135 if (this.undoPosition
== 0 || this.undoQueue
[this.undoPosition
- 1].text
!= text
) {
136 this.undoQueue
[this.undoPosition
] = this.buildSnapshot();
137 this.undoQueue
[this.undoPosition
].time
= currentTime
;
138 this.undoQueue
.length
= this.undoPosition
+ 1;
139 this.updateButtonsState();
144 if (this.undoQueue
[this.undoPosition
].text
!= text
){
145 var snapshot
= this.buildSnapshot();
146 this.undoQueue
[this.undoPosition
].text
= snapshot
.text
;
147 this.undoQueue
[this.undoPosition
].bookmark
= snapshot
.bookmark
;
148 this.undoQueue
[this.undoPosition
].bookmarkedText
= snapshot
.bookmarkedText
;
149 this.undoQueue
.length
= this.undoPosition
+ 1;
154 * Build the snapshot entry
156 * @return object a snapshot entry with three components:
157 * - text (the content of the RTE without any bookmark),
158 * - bookmark (the bookmark),
159 * - bookmarkedText (the content of the RTE including the bookmark)
161 buildSnapshot: function () {
162 var bookmark
= null, bookmarkedText
= null;
164 if (this.getEditorMode() === 'wysiwyg' && this.editor
.isEditable()) {
165 if ((!Ext
.isIE
&& !(Ext
.isOpera
&& navigator
.userAgent
.toLowerCase().indexOf('presto/2.1') != -1)) || (Ext
.isIE
&& this.editor
.getSelection().getType() !== 'Control')) {
166 // Catch error in FF when the selection contains no usable range
168 bookmark
= this.editor
.getBookMark().get(this.editor
.getSelection().createRange());
173 // Get the bookmarked html text and remove the bookmark
175 bookmarkedText
= this.editor
.getInnerHTML();
176 var range
= this.editor
.getBookMark().moveTo(bookmark
);
177 // Restore Firefox selection
179 this.editor
.getSelection().selectRange(range
);
184 text
: this.editor
.getInnerHTML(),
186 bookmarkedText
: bookmarkedText
190 * Execute the undo request
193 if (this.undoPosition
> 0) {
194 // Make sure we would not loose any changes
196 this.setContent(--this.undoPosition
);
197 this.updateButtonsState();
201 * Execute the redo request
204 if (this.undoPosition
< this.undoQueue
.length
- 1) {
205 // Make sure we would not loose any changes
207 // Previous call could make undo queue shorter
208 if (this.undoPosition
< this.undoQueue
.length
- 1) {
209 this.setContent(++this.undoPosition
);
210 this.updateButtonsState();
215 * Set content using undo queue position
217 setContent: function (undoPosition
) {
218 var bookmark
= this.undoQueue
[undoPosition
].bookmark
;
220 this.editor
.setHTML(this.undoQueue
[undoPosition
].bookmarkedText
);
221 this.editor
.getSelection().selectRange(this.editor
.getBookMark().moveTo(bookmark
));
222 this.editor
.scrollToCaret();
224 this.editor
.setHTML(this.undoQueue
[undoPosition
].text
);
228 * This function gets called when the toolbar is updated
230 onUpdateToolbar: function (button
, mode
, selectionEmpty
, ancestors
) {
231 if (mode
== 'wysiwyg' && this.editor
.isEditable()) {
232 if (this.customUndo
) {
233 switch (button
.itemId
) {
235 button
.setDisabled(this.undoPosition
== 0);
238 button
.setDisabled(this.undoPosition
>= this.undoQueue
.length
-1);
243 button
.setDisabled(!this.editor
.document
.queryCommandEnabled(button
.itemId
));
245 button
.setDisabled(true);
249 button
.setDisabled(!button
.textMode
);
253 * Update the state of the undo/redo buttons
255 updateButtonsState: function () {
256 var mode
= this.getEditorMode(),
257 selectionEmpty
= true,
259 if (mode
=== 'wysiwyg') {
260 selectionEmpty
= this.editor
.getSelection().isEmpty();
261 ancestors
= this.editor
.getSelection().getAllAncestors();
263 var button
= this.getButton('Undo');
265 this.onUpdateToolbar(button
, mode
, selectionEmpty
, ancestors
)
267 var button
= this.getButton('Redo');
269 this.onUpdateToolbar(button
, mode
, selectionEmpty
, ancestors
)
273 * This function gets called when the button was pressed.
275 * @param object editor: the editor instance
276 * @param string id: the button id or the key
278 * @return boolean false if action is completed
280 onButtonPress: function (editor
, id
) {
281 // Could be a button or its hotkey
282 var buttonId
= this.translateHotKey(id
);
283 buttonId
= buttonId
? buttonId
: id
;
284 if (this.getButton(buttonId
) && !this.getButton(buttonId
).disabled
) {
285 if (this.customUndo
) {
286 this[buttonId
.toLowerCase()]();
288 this.editor
.getSelection().execCommand(buttonId
, false, null);