1/* 2 * FCKeditor - The text editor for Internet - http://www.fckeditor.net 3 * Copyright (C) 2003-2007 Frederico Caldeira Knabben 4 * 5 * == BEGIN LICENSE == 6 * 7 * Licensed under the terms of any of the following licenses at your 8 * choice: 9 * 10 * - GNU General Public License Version 2 or later (the "GPL") 11 * http://www.gnu.org/licenses/gpl.html 12 * 13 * - GNU Lesser General Public License Version 2.1 or later (the "LGPL") 14 * http://www.gnu.org/licenses/lgpl.html 15 * 16 * - Mozilla Public License Version 1.1 or later (the "MPL") 17 * http://www.mozilla.org/MPL/MPL-1.1.html 18 * 19 * == END LICENSE == 20 */ 21 22var FCKUndo = new Object() ; 23 24FCKUndo.SavedData = new Array() ; 25FCKUndo.CurrentIndex = -1 ; 26FCKUndo.TypesCount = 0 ; 27FCKUndo.Changed = false ; // Is the document changed in respect to its initial image? 28FCKUndo.MaxTypes = 25 ; 29FCKUndo.Typing = false ; 30 31FCKUndo._GetBookmark = function() 32{ 33 var range = new FCKDomRange( FCK.EditorWindow ) ; 34 try 35 { 36 // There are some tricky cases where this might fail (e.g. having a lone empty table in IE) 37 range.MoveToSelection() ; 38 } 39 catch ( e ) 40 { 41 return null ; 42 } 43 if ( FCKBrowserInfo.IsIE ) 44 { 45 var bookmark = range.CreateBookmark() ; 46 var dirtyHtml = FCK.EditorDocument.body.innerHTML ; 47 range.MoveToBookmark( bookmark ) ; 48 return [ bookmark, dirtyHtml ] ; 49 } 50 return range.CreateBookmark2() ; 51} 52 53FCKUndo._SelectBookmark = function( bookmark ) 54{ 55 if ( ! bookmark ) 56 return ; 57 58 var range = new FCKDomRange( FCK.EditorWindow ) ; 59 if ( bookmark instanceof Object ) 60 { 61 if ( FCKBrowserInfo.IsIE ) 62 range.MoveToBookmark( bookmark[0] ) ; 63 else 64 range.MoveToBookmark2( bookmark ) ; 65 try 66 { 67 // this does not always succeed, there are still some tricky cases where it fails 68 // e.g. add a special character at end of document, undo, redo -> error 69 range.Select() ; 70 } 71 catch ( e ) 72 { 73 // if select restore fails, put the caret at the end of the document 74 range.MoveToPosition( FCK.EditorDocument.body, 4 ) ; 75 range.Select() ; 76 } 77 } 78} 79 80FCKUndo._CompareCursors = function( cursor1, cursor2 ) 81{ 82 for ( var i = 0 ; i < Math.min( cursor1.length, cursor2.length ) ; i++ ) 83 { 84 if ( cursor1[i] < cursor2[i] ) 85 return -1; 86 else if (cursor1[i] > cursor2[i] ) 87 return 1; 88 } 89 if ( cursor1.length < cursor2.length ) 90 return -1; 91 else if (cursor1.length > cursor2.length ) 92 return 1; 93 return 0; 94} 95 96FCKUndo._CheckIsBookmarksEqual = function( bookmark1, bookmark2 ) 97{ 98 if ( ! ( bookmark1 && bookmark2 ) ) 99 return false ; 100 if ( FCKBrowserInfo.IsIE ) 101 { 102 var startOffset1 = bookmark1[1].search( bookmark1[0].StartId ) ; 103 var startOffset2 = bookmark2[1].search( bookmark2[0].StartId ) ; 104 var endOffset1 = bookmark1[1].search( bookmark1[0].EndId ) ; 105 var endOffset2 = bookmark2[1].search( bookmark2[0].EndId ) ; 106 return startOffset1 == startOffset2 && endOffset1 == endOffset2 ; 107 } 108 else 109 { 110 return this._CompareCursors( bookmark1.Start, bookmark2.Start ) == 0 111 && this._CompareCursors( bookmark1.End, bookmark2.End ) == 0 ; 112 } 113} 114 115FCKUndo.SaveUndoStep = function() 116{ 117 if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG ) 118 return ; 119 120 // Assume the editor content is changed when SaveUndoStep() is called after the first time. 121 // This also enables the undo button in toolbar. 122 if ( this.SavedData.length ) 123 this.Changed = true ; 124 125 // Get the HTML content. 126 var sHtml = FCK.EditorDocument.body.innerHTML ; 127 var bookmark = this._GetBookmark() ; 128 129 // Shrink the array to the current level. 130 this.SavedData = this.SavedData.slice( 0, this.CurrentIndex + 1 ) ; 131 132 // Cancel operation if the new step is identical to the previous one. 133 if ( this.CurrentIndex > 0 134 && sHtml == this.SavedData[ this.CurrentIndex ][0] 135 && this._CheckIsBookmarksEqual( bookmark, this.SavedData[ this.CurrentIndex ][1] ) ) 136 return ; 137 // Save the selection and caret position in the first undo level for the first change. 138 else if ( this.CurrentIndex == 0 && this.SavedData.length && sHtml == this.SavedData[0][0] ) 139 { 140 this.SavedData[0][1] = bookmark ; 141 return ; 142 } 143 144 // If we reach the Maximum number of undo levels, we must remove the first 145 // entry of the list shifting all elements. 146 if ( this.CurrentIndex + 1 >= FCKConfig.MaxUndoLevels ) 147 this.SavedData.shift() ; 148 else 149 this.CurrentIndex++ ; 150 151 // Save the new level in front of the actual position. 152 this.SavedData[ this.CurrentIndex ] = [ sHtml, bookmark ] ; 153 154 FCK.Events.FireEvent( "OnSelectionChange" ) ; 155} 156 157FCKUndo.CheckUndoState = function() 158{ 159 return ( this.Changed || this.CurrentIndex > 0 ) ; 160} 161 162FCKUndo.CheckRedoState = function() 163{ 164 return ( this.CurrentIndex < ( this.SavedData.length - 1 ) ) ; 165} 166 167FCKUndo.Undo = function() 168{ 169 if ( this.CheckUndoState() ) 170 { 171 // If it is the first step. 172 if ( this.CurrentIndex == ( this.SavedData.length - 1 ) ) 173 { 174 // Save the actual state for a possible "Redo" call. 175 this.SaveUndoStep() ; 176 } 177 178 // Go a step back. 179 this._ApplyUndoLevel( --this.CurrentIndex ) ; 180 181 FCK.Events.FireEvent( "OnSelectionChange" ) ; 182 } 183} 184 185FCKUndo.Redo = function() 186{ 187 if ( this.CheckRedoState() ) 188 { 189 // Go a step forward. 190 this._ApplyUndoLevel( ++this.CurrentIndex ) ; 191 192 FCK.Events.FireEvent( "OnSelectionChange" ) ; 193 } 194} 195 196FCKUndo._ApplyUndoLevel = function( level ) 197{ 198 var oData = this.SavedData[ level ] ; 199 200 if ( !oData ) 201 return ; 202 203 // Update the editor contents with that step data. 204 if ( FCKBrowserInfo.IsIE ) 205 { 206 if ( oData[1] && oData[1][1] ) 207 FCK.SetInnerHtml( oData[1][1] ) ; 208 else 209 FCK.SetInnerHtml( oData[0] ) ; 210 } 211 else 212 FCK.EditorDocument.body.innerHTML = oData[0] ; 213 214 // Restore the selection 215 this._SelectBookmark( oData[1] ) ; 216 217 this.TypesCount = 0 ; 218 this.Changed = false ; 219 this.Typing = false ; 220} 221