Ticket #2763: 2763_3.patch

File 2763_3.patch, 15.5 KB (added by Garry Yao, 11 years ago)
  • _source/core/commanddefinition.js

     
    3333 *     }
    3434 * });
    3535 */
     36 
     37/**
     38 * Whether the command need to be hooked into the redo/undo system.
     39 * @name  CKEDITOR.commandDefinition.supportUndoRedo
     40 * @type {Boolean} If not defined or 'true' both hook into undo system, set it to 'false' explicitly  keep it out. 
     41 * @field
     42 * @example
     43 *  editorInstance.addCommand( 'sample',
     44 * {
     45 *     CKEDITOR.commandDefinition.supportUndoRedo : false    //declare this command does not support undo/redo 
     46 * });
     47 */
     48 No newline at end of file
  • _source/core/config.js

     
    145145         * @example
    146146         * config.plugins = 'elementspath,toolbar,wysiwygarea';
    147147         */
    148         plugins : 'basicstyles,button,dialog,elementspath,horizontalrule,htmldataprocessor,keystrokes,removeformat,smiley,link,sourcearea,tab,toolbar,wysiwygarea,forms,image,find,table,specialchar,flash,print,pagebreak,newpage',
     148        plugins : 'basicstyles,button,dialog,elementspath,horizontalrule,htmldataprocessor,keystrokes,removeformat,smiley,link,sourcearea,tab,toolbar,wysiwygarea,forms,image,find,table,specialchar,flash,print,pagebreak,undoredo,newpage',
    149149
    150150        /**
    151151         * The theme to be used to build the UI.
  • _source/core/dom/range.js

     
    408408                        return docFrag;
    409409                },
    410410
    411                 // This is an "intrusive" way to create a bookmark. It includes <span> tags
    412                 // in the range boundaries. The advantage of it is that it is possible to
    413                 // handle DOM mutations when moving back to the bookmark.
    414                 // Attention: the inclusion of nodes in the DOM is a design choice and
    415                 // should not be changed as there are other points in the code that may be
    416                 // using those nodes to perform operations. See GetBookmarkNode.
    417                 createBookmark : function()
     411                /**
     412                 * This is an "intrusive" way to create a bookmark. It includes <span>
     413                 * tags in the range boundaries. The advantage of it is that it is
     414                 * possible to handle DOM mutations when moving back to the bookmark.
     415                 * Attention: the inclusion of nodes in the DOM is a design choice.
     416                 *
     417                 * @param serializeable {Boolean} If set to true will record the bookmark with startNode/endNode IDs for serializing purpose.
     418                 */
     419                createBookmark : function(serializeable)
    418420                {
    419                         var startNode, endNode;
     421                        var startNode , endNode;
    420422                        var clone;
    421423
    422424                        startNode = this.document.createElement( 'span' );
    423                         startNode.setAttribute( '_fck_bookmark', 1 );
    424                         startNode.setStyle( 'display', 'none' );
     425                        startNode.setAttribute( '_fck_bookmark' , 1 );
     426                        startNode.setStyle( 'display' , 'none' );
    425427
    426428                        // For IE, it must have something inside, otherwise it may be
    427429                        // removed during DOM operations.
     
    451453                        else
    452454                                this.moveToPosition( startNode, CKEDITOR.POSITION_AFTER_END );
    453455
     456                        if ( serializeable )
     457                        {
     458                                startNode.setAttribute( 'id' , ( new Date() ).valueOf()
     459                                                + Math.floor( Math.random() * 1000 ) + 'S' );
     460                                if ( endNode )
     461                                        endNode.setAttribute( 'id' , ( new Date() ).valueOf()
     462                                                        + Math.floor( Math.random() * 1000 ) + 'E' );
     463                        }
    454464                        return {
    455                                 startNode : startNode,
    456                                 endNode : endNode
     465                                startNode :serializeable ? startNode.getAttribute( 'id' )
     466                                                : startNode,
     467                                endNode :endNode ? ( serializeable ? endNode
     468                                                .getAttribute( 'id' ) : endNode ) : null
    457469                        };
    458470                },
    459471
     472                /**
     473                 * Move the range boundaries to where the bookmarks indicated.
     474                 *
     475                 * @see CKEDITOR.dom.range::createBookmark
     476                 * @param {Object} bookmark
     477                 *
     478                 */
    460479                moveToBookmark : function( bookmark )
    461480                {
    462                         // Set the range start at the bookmark start node position.
    463                         this.setStartBefore( bookmark.startNode );
     481                        var startNode = bookmark.startNode , endNode = bookmark.endNode;
    464482
     483                        // Set the range startNodeart at the bookmark startNodeart node position.
     484                        this.setStartBefore( typeof startNode === 'string' ? ( startNode = this.document
     485                                        .getById( startNode ) ) : startNode );
     486
    465487                        // Remove it, because it may interfere in the setEndBefore call.
    466                         bookmark.startNode.remove();
     488                        startNode.remove();
    467489
    468490                        // Set the range end at the bookmark end node position, or simply
    469491                        // collapse it if it is not available.
    470                         var endNode = bookmark.endNode;
    471492                        if ( endNode )
    472493                        {
    473                                 this.setEndBefore( endNode );
     494                                this.setEndBefore( typeof endNode === 'string' ?
     495                                                ( endNode = this.document.getById( endNode ) )
     496                                                : endNode );
    474497                                endNode.remove();
    475498                        }
    476499                        else
  • _source/core/editor.js

     
    345345                execCommand : function( commandName, data )
    346346                {
    347347                        var command = this.getCommand( commandName );
     348                        var exeResult;
     349                        var commandEvent =
     350
     351                        /**
     352                         * The event data for this command
     353                         * @type FCKEDITOR.command.Event
     354                         */
     355                        {
     356                                name :commandName,
     357                                command :command
     358                        };
    348359                        if ( command && command.state != CKEDITOR.TRISTATE_DISABLED )
    349                                 return command.exec( this, data );
     360                        {
     361                                this.fire( 'beforeCommandExec' , commandEvent );
     362                                exeResult = command.exec( this , data );
     363                                this.fire( 'afterCommandExec' , commandEvent );
     364                                return exeResult;
     365                        }
    350366
    351367                        // throw 'Unknown command name "' + commandName + '"';
    352368                        return false;
  • _source/plugins/editingblock/plugin.js

     
    174174                modeEditor.load( holderElement, data || this.getData() );
    175175
    176176                this.mode = mode;
    177                 this.fire( 'mode' );
     177                this.fire( 'mode' , mode);
    178178        };
    179179
    180180        /**
  • _source/plugins/selection/plugin.js

     
    614614                                                sel.addRange( nativeRange );
    615615                                        }
    616616                                },
    617 
    618                 createBookmarks : function()
     617        /**
     618                 * This method is a delegate to ceate bookmark for every ranges in this selection.
     619                 *
     620                 * @see CKEDITOR.dom.range::createBookmark
     621                 */
     622                createBookmarks : function ( serializable )
    619623                {
    620624                        var retval = [],
    621625                                ranges = this.getRanges();
    622626                        for ( var i = 0 ; i < ranges.length ; i++ )
    623                                 retval.push( ranges[i].createBookmark() );
     627                                retval.push( ranges[ i ].createBookmark( serializable ) );
    624628                        return retval;
    625629                },
    626630
  • _source/plugins/undoredo/plugin.js

     
     1/*
     2 * Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
     3 * For 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 * @see #2763, #2, #915
     10 * @description
     11 *      <p>
     12 *      <span style="border-collapse: separate; color: rgb(0, 0, 0);
     13 *      font-family: Verdana; font-size: 13px; font-style: normal; font-variant:
     14 *      normal; font-weight: normal; letter-spacing: normal; line-height:
     15 *      normal; orphans: 2; text-indent: 0px; text-transform: none; white-space:
     16 *      normal; widows: 2; word-spacing: 0px;" class="Apple-style-span">
     17 *      <p>
     18 *      Undo/Redo system need to be ported from v2, the features could be summed
     19 *      up as below:
     20 *      </p>
     21 *      <ul>
     22 *      <li>Undo system restore/retrieve document status from both selection
     23 *      and content</li>
     24 *      <li>All commands that modify the document could support undo/redo
     25 *      feature,but each command has chance to optionaly declare whether would
     26 *      hooked with undo systemwhen they're registed.</li>
     27 *      </ul>
     28 *      <blockquote><blockquote>
     29 *      <p>
     30 *      <strong>Note</strong>: Since from v3 all keystroke will pre-bind to
     31 *      command, so keystrokes also hooked with undo system through command
     32 *      interface.
     33 *      </p>
     34 *      </blockquote></blockquote>
     35 *      <ul>
     36 *      <li>Undo feature itself performed as a command, A button plugin is
     37 *      required for interfacing this command to end user and keystroke pair of
     38 *      'Ctrl-Z' and 'Ctrl-Y' too.</li>
     39 *      <li>Support empty/reset all the undo snapshots for clean up, and A
     40 *      button plugin is required for interfacing this<span
     41 *      class="Apple-converted-space">&nbsp;</span><i>reset</i></li>
     42 *      <li>snapshot for undo system could be recorded in two forms:
     43 *      <ol>
     44 *      <li>One snapshot for each record action, this apply for almost every
     45 *      functional commands, but a few special keystroke commands that also
     46 *      being recorded in this way including:
     47 *      <ol style="list-style-type: lower-alpha;" class="loweralpha">
     48 *      <li>'Enter'</li>
     49 *      <li>'ShiftEnter'</li>
     50 *      <li>'CtrlBackspace'</li>
     51 *      <li>'Delete'</li>
     52 *      <li>'Tab'</li>
     53 *      <li>'Ctrl-X'</li>
     54 *      <li>'Ctrl-V'</li>
     55 *      </ol>
     56 *      </li>
     57 *      <li>One snapshot for a serials of record actions, this apply for most
     58 *      keystroke commands.</li>
     59 *      </ol>
     60 *      </li>
     61 *      </ul>
     62 *      </span>
     63 *      </p>
     64 */
     65CKEDITOR.plugins.add( 'undoredo' ,
     66/**
     67 * @implements CKEDITOR.pluginDefinition The undo/redo feature for wysiwygarea
     68 *             mode
     69 */
     70{
     71
     72        requires : [ 'selection', 'wysiwygarea' ],
     73        beforeInit : function ( editor )
     74        {
     75
     76                var urm = new UndoRedoManager( editor );
     77                function recordCommand ( evt )
     78                {
     79                        var event /* {FCKEDITOR.command.Event} */= evt.data;
     80                        if ( event.command.supportUndoRedo !== false // ingore mode change
     81                                        // command
     82                                        && event.name !== 'source'
     83                                        && event.name !== 'wysiwyg'
     84                                        && urm.enabled )
     85                        {
     86                                urm.save( evt.name === 'beforeCommandExec' ? true : false );
     87                        }
     88                }
     89
     90                editor.on( 'beforeCommandExec' , recordCommand );
     91                editor.on( 'afterCommandExec' , recordCommand );
     92
     93                // sensitive to mode change, only appliable for 'wysiwyg' mode
     94                editor.on( 'mode' , function ( currentMode )
     95                {
     96                        urm.enabled = currentMode.data === 'wysiwyg';
     97                } );
     98
     99                editor.addCommand( 'undo' ,
     100                /**
     101                 * @implements CKEDITOR.commandDefinition Rollback to last modification
     102                 *             of this document
     103                 */
     104                {
     105
     106                        exec : function() {
     107                                if (urm.undo()) {
     108                                        this.fire('AfterUndo');
     109                                        // TODO: fire 'selectionchange'
     110                                }
     111                        },
     112                        supportUndoRedo : false
     113                } );
     114
     115                editor.addCommand( 'redo' ,
     116                /**
     117                 * @implements CKEDITOR.commandDefinition Retrieve to next modification
     118                 *             of this document
     119                 */
     120                {
     121                        exec : function() {
     122                                if (urm.redo()) {
     123                                        this.fire('AfterRedo');
     124                                        // TODO: fire 'selectionchange'
     125                                }
     126                        },
     127                        supportUndoRedo : false
     128                } );
     129        }
     130} );
     131
     132/**
     133 * @constructor Main logic for Redo/Undo feature
     134 * @related FCKUndo in v2
     135 */
     136function UndoRedoManager ( editor )
     137{
     138
     139        /**
     140         * @field Whether undo system is usable decided by envoriment
     141         */
     142        this.enabled = false;
     143
     144        var UNDO_NUM_LIMIT = 20;
     145
     146        /**
     147         * Stack for all the undo and redo snapshots, they're always created/removed
     148         * in consistency.
     149         *
     150         * @type {Array<DocumentImage>}
     151         *
     152         */
     153        var undoSnaps = [] , redoSnaps = [];
     154
     155        /**
     156         * Current snapshot history index
     157         */
     158        var index = 0; // First capture start from 1
     159
     160        /**
     161         * @private Get the current blockediting mode of this editor
     162         * @param editor
     163         * @param mode
     164         * @return
     165         */
     166        function getMode ( mode )
     167        {
     168                return editor._.modes && editor._.modes[ mode || editor.mode ];
     169        }
     170
     171        /**
     172         * @constructor DocumentImage - A snapshot image which represent the current
     173         *              document status.
     174         */
     175        function DocumentImage ( )
     176        {
     177
     178                var bms , ranges , cWithBm , c;
     179
     180                c = getMode( 'wysiwyg' ).getSnapshotData();
     181                bms = editor.document.getSelection().createBookmarks( true );
     182                ranges = editor.document.getSelection().getRanges(); // Get ranges
     183                // from cache
     184                cWithBm = getMode( 'wysiwyg' ).getSnapshotData() // Get content along
     185                // with bookmark
     186                editor.document.getSelection().selectBookmarks( bms );
     187                // restore
     188                // selection by
     189                // the bookmark
     190                // and we
     191                // also
     192                // destroy the nodes in document
     193                return {
     194                        'content' : /** {String} original document string */
     195                        c,
     196                        'dirtyContent' :
     197                        /**
     198                         * {String} document content string with bookmarks
     199                         */
     200                        cWithBm,
     201                        'bookmark' /** {Array<CKEDITOR.dom.range.Bookmark>} */
     202                        :bms,
     203                        equals :
     204                        /**
     205                         * Compare if two document snapshot are the same, this is only
     206                         * content comparison with bookmarks striped. (
     207                         *
     208                         * @img {DocumentImage} The other image to compare with
     209                         */
     210                        function ( img )
     211                        {
     212                                return this.content == img.content;
     213                        }
     214                }
     215        }
     216
     217        /**
     218         * @method Save a snapshot of document image for later retrieve, content
     219         *         being saved as raw html string, while selection saved as a
     220         *         lightweight bookmark.
     221         */
     222        this.save = function ( isFront )
     223        {
     224                var sns = isFront ? undoSnaps : redoSnaps;
     225
     226                if ( index == UNDO_NUM_LIMIT )
     227                {
     228                        sns.unshift(); // Die out earlier ones.
     229                }
     230
     231                sns.splice( index , sns.length - index ); // Drop older snaps
     232
     233                var img = new DocumentImage();
     234                if ( sns.length && img.equals( sns[ sns.length - 1 ] ) ) // duplicate
     235                        // examination
     236                        return;
     237
     238                sns.push( img );
     239
     240                if ( !isFront )
     241                        index++; // increase when command has been fully recorded.
     242        }
     243
     244        function restoreImage ( index, isUndo )
     245        {
     246                var img;
     247                img = isUndo ? undoSnaps[ index ] : redoSnaps[ index ];
     248                if ( img.content )
     249                {
     250                        getMode( 'wysiwyg' ).loadSnapshotData( img.dirtyContent );
     251                        if ( img.bookmark )
     252                        {
     253                                editor.document.getSelection().selectBookmarks( img.bookmark );
     254                        }
     255                }
     256        }
     257
     258        /**
     259         * Check the current state of redo
     260         *
     261         * @return {Boolean} Whether the document has previous state to retrieve
     262         */
     263        this.redoAble = function ( )
     264        {
     265                return index < redoSnaps.length;
     266        }
     267
     268        /**
     269         * Check the current state of undo
     270         *
     271         * @return {Boolean} Whether the document has future state to restore
     272         */
     273        this.undoAble = function ( )
     274        {
     275                return index > 0
     276        }
     277        /**
     278         * Perform undo on current index.
     279         *
     280         * @method
     281         */
     282        this.undo = function ( )
     283        {
     284                if ( this.undoAble() )
     285                {
     286                        restoreImage( --index , true ); // retrieve previous one from undo
     287                        // history
     288                }
     289                else
     290                        return false;
     291        }
     292
     293        /**
     294         * Perform redo on current index.
     295         *
     296         * @method
     297         */
     298        this.redo = function ( )
     299        {
     300                if ( this.redoAble() )
     301                {
     302                        restoreImage( index++ , false ); // retrieve next one from redo
     303                        // history
     304                }
     305                else
     306                        return false;
     307        }
     308
     309}
© 2003 – 2019 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy