summaryrefslogtreecommitdiff
path: root/rt/share/html/NoAuth/RichText/FCKeditor/editor/_source/internals/fckundo.js
blob: 404a5d910e82a496769effacb2633ddaaf5bc7f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/*
 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
 * Copyright (C) 2003-2009 Frederico Caldeira Knabben
 *
 * == BEGIN LICENSE ==
 *
 * Licensed under the terms of any of the following licenses at your
 * choice:
 *
 *  - GNU General Public License Version 2 or later (the "GPL")
 *    http://www.gnu.org/licenses/gpl.html
 *
 *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
 *    http://www.gnu.org/licenses/lgpl.html
 *
 *  - Mozilla Public License Version 1.1 or later (the "MPL")
 *    http://www.mozilla.org/MPL/MPL-1.1.html
 *
 * == END LICENSE ==
 */

var FCKUndo = new Object() ;

FCKUndo.SavedData = new Array() ;
FCKUndo.CurrentIndex = -1 ;
FCKUndo.TypesCount = 0 ;
FCKUndo.Changed = false ;	// Is the document changed in respect to its initial image?
FCKUndo.MaxTypes = 25 ;
FCKUndo.Typing = false ;
FCKUndo.SaveLocked = false ;

FCKUndo._GetBookmark = function()
{
	FCKSelection.Restore() ;

	var range = new FCKDomRange( FCK.EditorWindow ) ;
	try
	{
		// There are some tricky cases where this might fail (e.g. having a lone empty table in IE)
		range.MoveToSelection() ;
	}
	catch ( e )
	{
		return null ;
	}
	if ( FCKBrowserInfo.IsIE )
	{
		var bookmark = range.CreateBookmark() ;
		var dirtyHtml = FCK.EditorDocument.body.innerHTML ;
		range.MoveToBookmark( bookmark ) ;
		return [ bookmark, dirtyHtml ] ;
	}
	return range.CreateBookmark2() ;
}

FCKUndo._SelectBookmark = function( bookmark )
{
	if ( ! bookmark )
		return ;

	var range = new FCKDomRange( FCK.EditorWindow ) ;
	if ( bookmark instanceof Object )
	{
		if ( FCKBrowserInfo.IsIE )
			range.MoveToBookmark( bookmark[0] ) ;
		else
			range.MoveToBookmark2( bookmark ) ;
		try
		{
			// this does not always succeed, there are still some tricky cases where it fails
			// e.g. add a special character at end of document, undo, redo -> error
			range.Select() ;
		}
		catch ( e )
		{
			// if select restore fails, put the caret at the end of the document
			range.MoveToPosition( FCK.EditorDocument.body, 4 ) ;
			range.Select() ;
		}
	}
}

FCKUndo._CompareCursors = function( cursor1, cursor2 )
{
	for ( var i = 0 ; i < Math.min( cursor1.length, cursor2.length ) ; i++ )
	{
		if ( cursor1[i] < cursor2[i] )
			return -1;
		else if (cursor1[i] > cursor2[i] )
			return 1;
	}
	if ( cursor1.length < cursor2.length )
		return -1;
	else if (cursor1.length > cursor2.length )
		return 1;
	return 0;
}

FCKUndo._CheckIsBookmarksEqual = function( bookmark1, bookmark2 )
{
	if ( ! ( bookmark1 && bookmark2 ) )
		return false ;
	if ( FCKBrowserInfo.IsIE )
	{
		var startOffset1 = bookmark1[1].search( bookmark1[0].StartId ) ;
		var startOffset2 = bookmark2[1].search( bookmark2[0].StartId ) ;
		var endOffset1 = bookmark1[1].search( bookmark1[0].EndId ) ;
		var endOffset2 = bookmark2[1].search( bookmark2[0].EndId ) ;
		return startOffset1 == startOffset2 && endOffset1 == endOffset2 ;
	}
	else
	{
		return this._CompareCursors( bookmark1.Start, bookmark2.Start ) == 0
			&& this._CompareCursors( bookmark1.End, bookmark2.End ) == 0 ;
	}
}

FCKUndo.SaveUndoStep = function()
{
	if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG || this.SaveLocked )
		return ;

	// Assume the editor content is changed when SaveUndoStep() is called after the first time.
	// This also enables the undo button in toolbar.
	if ( this.SavedData.length )
		this.Changed = true ;

	// Get the HTML content.
	var sHtml = FCK.EditorDocument.body.innerHTML ;
	var bookmark = this._GetBookmark() ;

	// Shrink the array to the current level.
	this.SavedData = this.SavedData.slice( 0, this.CurrentIndex + 1 ) ;

	// Cancel operation if the new step is identical to the previous one.
	if ( this.CurrentIndex > 0
			&& sHtml == this.SavedData[ this.CurrentIndex ][0]
			&& this._CheckIsBookmarksEqual( bookmark, this.SavedData[ this.CurrentIndex ][1] ) )
		return ;
	// Save the selection and caret position in the first undo level for the first change.
	else if ( this.CurrentIndex == 0 && this.SavedData.length && sHtml == this.SavedData[0][0] )
	{
		this.SavedData[0][1] = bookmark ;
		return ;
	}

	// If we reach the Maximum number of undo levels, we must remove the first
	// entry of the list shifting all elements.
	if ( this.CurrentIndex + 1 >= FCKConfig.MaxUndoLevels )
		this.SavedData.shift() ;
	else
		this.CurrentIndex++ ;

	// Save the new level in front of the actual position.
	this.SavedData[ this.CurrentIndex ] = [ sHtml, bookmark ] ;

	FCK.Events.FireEvent( "OnSelectionChange" ) ;
}

FCKUndo.CheckUndoState = function()
{
	return ( this.Changed || this.CurrentIndex > 0 ) ;
}

FCKUndo.CheckRedoState = function()
{
	return ( this.CurrentIndex < ( this.SavedData.length - 1 ) ) ;
}

FCKUndo.Undo = function()
{
	if ( this.CheckUndoState() )
	{
		// If it is the first step.
		if ( this.CurrentIndex == ( this.SavedData.length - 1 ) )
		{
			// Save the actual state for a possible "Redo" call.
			this.SaveUndoStep() ;
		}

		// Go a step back.
		this._ApplyUndoLevel( --this.CurrentIndex ) ;

		FCK.Events.FireEvent( "OnSelectionChange" ) ;
	}
}

FCKUndo.Redo = function()
{
	if ( this.CheckRedoState() )
	{
		// Go a step forward.
		this._ApplyUndoLevel( ++this.CurrentIndex ) ;

		FCK.Events.FireEvent( "OnSelectionChange" ) ;
	}
}

FCKUndo._ApplyUndoLevel = function( level )
{
	var oData = this.SavedData[ level ] ;

	if ( !oData )
		return ;

	// Update the editor contents with that step data.
	if ( FCKBrowserInfo.IsIE )
	{
		if ( oData[1] && oData[1][1] )
			FCK.SetInnerHtml( oData[1][1] ) ;
		else
			FCK.SetInnerHtml( oData[0] ) ;
	}
	else
		FCK.EditorDocument.body.innerHTML = oData[0] ;

	// Restore the selection
	this._SelectBookmark( oData[1] ) ;

	this.TypesCount = 0 ;
	this.Changed = false ;
	this.Typing = false ;
}