source: branches/2.2/filemanager/tp/ckeditor/_source/plugins/undo/plugin.js @ 3019

Revision 3019, 11.7 KB checked in by amuller, 14 years ago (diff)

Ticket #1135 - Corrigindo CSS e adicionando filemanager

Line 
1/*
2Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
3For licensing, see LICENSE.html or http://ckeditor.com/license
4*/
5
6/**
7 * @fileOverview Undo/Redo system for saving shapshot for document modification
8 *              and other recordable changes.
9 */
10
11(function()
12{
13        CKEDITOR.plugins.add( 'undo',
14        {
15                requires : [ 'selection', 'wysiwygarea' ],
16
17                init : function( editor )
18                {
19                        var undoManager = new UndoManager( editor );
20
21                        var undoCommand = editor.addCommand( 'undo',
22                                {
23                                        exec : function()
24                                        {
25                                                if ( undoManager.undo() )
26                                                {
27                                                        editor.selectionChange();
28                                                        this.fire( 'afterUndo' );
29                                                }
30                                        },
31                                        state : CKEDITOR.TRISTATE_DISABLED,
32                                        canUndo : false
33                                });
34
35                        var redoCommand = editor.addCommand( 'redo',
36                                {
37                                        exec : function()
38                                        {
39                                                if ( undoManager.redo() )
40                                                {
41                                                        editor.selectionChange();
42                                                        this.fire( 'afterRedo' );
43                                                }
44                                        },
45                                        state : CKEDITOR.TRISTATE_DISABLED,
46                                        canUndo : false
47                                });
48
49                        undoManager.onChange = function()
50                        {
51                                undoCommand.setState( undoManager.undoable() ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
52                                redoCommand.setState( undoManager.redoable() ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
53                        };
54
55                        function recordCommand( event )
56                        {
57                                // If the command hasn't been marked to not support undo.
58                                if ( undoManager.enabled && event.data.command.canUndo !== false )
59                                        undoManager.save();
60                        }
61
62                        // We'll save snapshots before and after executing a command.
63                        editor.on( 'beforeCommandExec', recordCommand );
64                        editor.on( 'afterCommandExec', recordCommand );
65
66                        // Save snapshots before doing custom changes.
67                        editor.on( 'saveSnapshot', function()
68                                {
69                                        undoManager.save();
70                                });
71
72                        // Registering keydown on every document recreation.(#3844)
73                        editor.on( 'contentDom', function()
74                                {
75                                        editor.document.on( 'keydown', function( event )
76                                                {
77                                                        // Do not capture CTRL hotkeys.
78                                                        if ( !event.data.$.ctrlKey && !event.data.$.metaKey )
79                                                                undoManager.type( event );
80                                                });
81                                });
82
83                        // Always save an undo snapshot - the previous mode might have
84                        // changed editor contents.
85                        editor.on( 'beforeModeUnload', function()
86                                {
87                                        editor.mode == 'wysiwyg' && undoManager.save( true );
88                                });
89
90                        // Make the undo manager available only in wysiwyg mode.
91                        editor.on( 'mode', function()
92                                {
93                                        undoManager.enabled = editor.mode == 'wysiwyg';
94                                        undoManager.onChange();
95                                });
96
97                        editor.ui.addButton( 'Undo',
98                                {
99                                        label : editor.lang.undo,
100                                        command : 'undo'
101                                });
102
103                        editor.ui.addButton( 'Redo',
104                                {
105                                        label : editor.lang.redo,
106                                        command : 'redo'
107                                });
108
109                        editor.resetUndo = function()
110                        {
111                                // Reset the undo stack.
112                                undoManager.reset();
113
114                                // Create the first image.
115                                editor.fire( 'saveSnapshot' );
116                        };
117                }
118        });
119
120        // Gets a snapshot image which represent the current document status.
121        function Image( editor )
122        {
123                var selection = editor.getSelection();
124
125                this.contents   = editor.getSnapshot();
126                this.bookmarks  = selection && selection.createBookmarks2( true );
127
128                // In IE, we need to remove the expando attributes.
129                if ( CKEDITOR.env.ie )
130                        this.contents = this.contents.replace( /\s+_cke_expando=".*?"/g, '' );
131        }
132
133        Image.prototype =
134        {
135                equals : function( otherImage, contentOnly )
136                {
137                        if ( this.contents != otherImage.contents )
138                                return false;
139
140                        if ( contentOnly )
141                                return true;
142
143                        var bookmarksA = this.bookmarks,
144                                bookmarksB = otherImage.bookmarks;
145
146                        if ( bookmarksA || bookmarksB )
147                        {
148                                if ( !bookmarksA || !bookmarksB || bookmarksA.length != bookmarksB.length )
149                                        return false;
150
151                                for ( var i = 0 ; i < bookmarksA.length ; i++ )
152                                {
153                                        var bookmarkA = bookmarksA[ i ],
154                                                bookmarkB = bookmarksB[ i ];
155
156                                        if (
157                                                bookmarkA.startOffset != bookmarkB.startOffset ||
158                                                bookmarkA.endOffset != bookmarkB.endOffset ||
159                                                !CKEDITOR.tools.arrayCompare( bookmarkA.start, bookmarkB.start ) ||
160                                                !CKEDITOR.tools.arrayCompare( bookmarkA.end, bookmarkB.end ) )
161                                        {
162                                                return false;
163                                        }
164                                }
165                        }
166
167                        return true;
168                }
169        };
170
171        /**
172         * @constructor Main logic for Redo/Undo feature.
173         */
174        function UndoManager( editor )
175        {
176                this.editor = editor;
177
178                // Reset the undo stack.
179                this.reset();
180        }
181
182        UndoManager.prototype =
183        {
184                /**
185                 * Process undo system regard keystrikes.
186                 * @param {CKEDITOR.dom.event} event
187                 */
188                type : function( event )
189                {
190                        var keystroke = event && event.data.getKeystroke(),
191
192                                // Backspace, Delete
193                                modifierCodes = { 8:1, 46:1 },
194                                // Keystrokes which will modify the contents.
195                                isModifier = keystroke in modifierCodes,
196                                wasModifier = this.lastKeystroke in modifierCodes,
197                                lastWasSameModifier = isModifier && keystroke == this.lastKeystroke,
198
199                                // Arrows: L, T, R, B
200                                resetTypingCodes = { 37:1, 38:1, 39:1, 40:1 },
201                                // Keystrokes which navigation through contents.
202                                isReset = keystroke in resetTypingCodes,
203                                wasReset = this.lastKeystroke in resetTypingCodes,
204
205                                // Keystrokes which just introduce new contents.
206                                isContent = ( !isModifier && !isReset ),
207
208                                // Create undo snap for every different modifier key.
209                                modifierSnapshot = ( isModifier && !lastWasSameModifier ),
210                                // Create undo snap on the following cases:
211                                // 1. Just start to type.
212                                // 2. Typing some content after a modifier.
213                                // 3. Typing some content after make a visible selection.
214                                startedTyping = !this.typing
215                                        || ( isContent && ( wasModifier || wasReset ) );
216
217                        if ( startedTyping || modifierSnapshot )
218                        {
219                                var beforeTypeImage = new Image( this.editor );
220
221                                // Use setTimeout, so we give the necessary time to the
222                                // browser to insert the character into the DOM.
223                                CKEDITOR.tools.setTimeout( function()
224                                        {
225                                                var currentSnapshot = this.editor.getSnapshot();
226
227                                                // In IE, we need to remove the expando attributes.
228                                                if ( CKEDITOR.env.ie )
229                                                        currentSnapshot = currentSnapshot.replace( /\s+_cke_expando=".*?"/g, '' );
230
231                                                if ( beforeTypeImage.contents != currentSnapshot )
232                                                {
233                                                        // This's a special save, with specified snapshot
234                                                        // and without auto 'fireChange'.
235                                                        if ( !this.save( false, beforeTypeImage, false ) )
236                                                                // Drop future snapshots.
237                                                                this.snapshots.splice( this.index + 1, this.snapshots.length - this.index - 1 );
238
239                                                        this.hasUndo = true;
240                                                        this.hasRedo = false;
241
242                                                        this.typesCount = 1;
243                                                        this.modifiersCount = 1;
244
245                                                        this.onChange();
246                                                }
247                                        },
248                                        0, this
249                                );
250                        }
251
252                        this.lastKeystroke = keystroke;
253                        // Create undo snap after typed too much (over 25 times).
254                        if ( isModifier )
255                        {
256                                this.typesCount = 0;
257                                this.modifiersCount++;
258
259                                if ( this.modifiersCount > 25 )
260                                {
261                                        this.save();
262                                        this.modifiersCount = 1;
263                                }
264                        }
265                        else if ( !isReset )
266                        {
267                                this.modifiersCount = 0;
268                                this.typesCount++;
269
270                                if ( this.typesCount > 25 )
271                                {
272                                        this.save();
273                                        this.typesCount = 1;
274                                }
275                        }
276
277                        this.typing = true;
278                },
279
280                reset : function()      // Reset the undo stack.
281                {
282                        /**
283                         * Remember last pressed key.
284                         */
285                        this.lastKeystroke = 0;
286
287                        /**
288                         * Stack for all the undo and redo snapshots, they're always created/removed
289                         * in consistency.
290                         */
291                        this.snapshots = [];
292
293                        /**
294                         * Current snapshot history index.
295                         */
296                        this.index = -1;
297
298                        this.limit = this.editor.config.undoStackSize;
299
300                        this.currentImage = null;
301
302                        this.hasUndo = false;
303                        this.hasRedo = false;
304
305                        this.resetType();
306                },
307
308                /**
309                 * Reset all states about typing.
310                 * @see  UndoManager.type
311                 */
312                resetType : function()
313                {
314                        this.typing = false;
315                        delete this.lastKeystroke;
316                        this.typesCount = 0;
317                        this.modifiersCount = 0;
318                },
319                fireChange : function()
320                {
321                        this.hasUndo = !!this.getNextImage( true );
322                        this.hasRedo = !!this.getNextImage( false );
323                        // Reset typing
324                        this.resetType();
325                        this.onChange();
326                },
327
328                /**
329                 * Save a snapshot of document image for later retrieve.
330                 */
331                save : function( onContentOnly, image, autoFireChange )
332                {
333                        var snapshots = this.snapshots;
334
335                        // Get a content image.
336                        if ( !image )
337                                image = new Image( this.editor );
338
339                        // Check if this is a duplicate. In such case, do nothing.
340                        if ( this.currentImage && image.equals( this.currentImage, onContentOnly ) )
341                                return false;
342
343                        // Drop future snapshots.
344                        snapshots.splice( this.index + 1, snapshots.length - this.index - 1 );
345
346                        // If we have reached the limit, remove the oldest one.
347                        if ( snapshots.length == this.limit )
348                                snapshots.shift();
349
350                        // Add the new image, updating the current index.
351                        this.index = snapshots.push( image ) - 1;
352
353                        this.currentImage = image;
354
355                        if ( autoFireChange !== false )
356                                this.fireChange();
357                        return true;
358                },
359
360                restoreImage : function( image )
361                {
362                        this.editor.loadSnapshot( image.contents );
363
364                        if ( image.bookmarks )
365                                this.editor.getSelection().selectBookmarks( image.bookmarks );
366                        else if ( CKEDITOR.env.ie )
367                        {
368                                // IE BUG: If I don't set the selection to *somewhere* after setting
369                                // document contents, then IE would create an empty paragraph at the bottom
370                                // the next time the document is modified.
371                                var $range = this.editor.document.getBody().$.createTextRange();
372                                $range.collapse( true );
373                                $range.select();
374                        }
375
376                        this.index = image.index;
377
378                        this.currentImage = image;
379
380                        this.fireChange();
381                },
382
383                // Get the closest available image.
384                getNextImage : function( isUndo )
385                {
386                        var snapshots = this.snapshots,
387                                currentImage = this.currentImage,
388                                image, i;
389
390                        if ( currentImage )
391                        {
392                                if ( isUndo )
393                                {
394                                        for ( i = this.index - 1 ; i >= 0 ; i-- )
395                                        {
396                                                image = snapshots[ i ];
397                                                if ( !currentImage.equals( image, true ) )
398                                                {
399                                                        image.index = i;
400                                                        return image;
401                                                }
402                                        }
403                                }
404                                else
405                                {
406                                        for ( i = this.index + 1 ; i < snapshots.length ; i++ )
407                                        {
408                                                image = snapshots[ i ];
409                                                if ( !currentImage.equals( image, true ) )
410                                                {
411                                                        image.index = i;
412                                                        return image;
413                                                }
414                                        }
415                                }
416                        }
417
418                        return null;
419                },
420
421                /**
422                 * Check the current redo state.
423                 * @return {Boolean} Whether the document has previous state to
424                 *              retrieve.
425                 */
426                redoable : function()
427                {
428                        return this.enabled && this.hasRedo;
429                },
430
431                /**
432                 * Check the current undo state.
433                 * @return {Boolean} Whether the document has future state to restore.
434                 */
435                undoable : function()
436                {
437                        return this.enabled && this.hasUndo;
438                },
439
440                /**
441                 * Perform undo on current index.
442                 */
443                undo : function()
444                {
445                        if ( this.undoable() )
446                        {
447                                this.save( true );
448
449                                var image = this.getNextImage( true );
450                                if ( image )
451                                        return this.restoreImage( image ), true;
452                        }
453
454                        return false;
455                },
456
457                /**
458                 * Perform redo on current index.
459                 */
460                redo : function()
461                {
462                        if ( this.redoable() )
463                        {
464                                // Try to save. If no changes have been made, the redo stack
465                                // will not change, so it will still be redoable.
466                                this.save( true );
467
468                                // If instead we had changes, we can't redo anymore.
469                                if ( this.redoable() )
470                                {
471                                        var image = this.getNextImage( false );
472                                        if ( image )
473                                                return this.restoreImage( image ), true;
474                                }
475                        }
476
477                        return false;
478                }
479        };
480})();
481
482/**
483 * The number of undo steps to be saved. The higher this setting value the more
484 * memory is used for it.
485 * @type Number
486 * @default 20
487 * @example
488 * config.undoStackSize = 50;
489 */
490CKEDITOR.config.undoStackSize = 20;
Note: See TracBrowser for help on using the repository browser.