Project

General

Profile

1
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
2
<!--
3
 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
4
 * Copyright (C) 2003-2009 Frederico Caldeira Knabben
5
 *
6
 * == BEGIN LICENSE ==
7
 *
8
 * Licensed under the terms of any of the following licenses at your
9
 * choice:
10
 *
11
 *  - GNU General Public License Version 2 or later (the "GPL")
12
 *    http://www.gnu.org/licenses/gpl.html
13
 *
14
 *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
15
 *    http://www.gnu.org/licenses/lgpl.html
16
 *
17
 *  - Mozilla Public License Version 1.1 or later (the "MPL")
18
 *    http://www.mozilla.org/MPL/MPL-1.1.html
19
 *
20
 * == END LICENSE ==
21
 *
22
 * "Find" and "Replace" dialog box window.
23
-->
24
<html xmlns="http://www.w3.org/1999/xhtml">
25
<head>
26
	<title></title>
27
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
28
	<meta content="noindex, nofollow" name="robots" />
29
	<script src="common/fck_dialog_common.js" type="text/javascript"></script>
30
	<script type="text/javascript">
31

    
32
var dialog	= window.parent ;
33
var oEditor = dialog.InnerDialogLoaded() ;
34
var dialogArguments = dialog.Args() ;
35

    
36
var FCKLang = oEditor.FCKLang ;
37
var FCKDomTools = oEditor.FCKDomTools ;
38
var FCKDomRange = oEditor.FCKDomRange ;
39
var FCKListsLib = oEditor.FCKListsLib ;
40
var FCKTools = oEditor.FCKTools ;
41
var EditorDocument = oEditor.FCK.EditorDocument ;
42
var HighlightStyle = oEditor.FCKStyles.GetStyle( '_FCK_SelectionHighlight' )  ;
43

    
44
dialog.AddTab( 'Find', FCKLang.DlgFindTitle ) ;
45
dialog.AddTab( 'Replace', FCKLang.DlgReplaceTitle ) ;
46
var idMap = {} ;
47

    
48
function OnDialogTabChange( tabCode )
49
{
50
	ShowE( 'divFind', ( tabCode == 'Find' ) ) ;
51
	ShowE( 'divReplace', ( tabCode == 'Replace' ) ) ;
52
	idMap['FindText'] = 'txtFind' + tabCode ;
53
	idMap['CheckCase'] = 'chkCase' + tabCode ;
54
	idMap['CheckWord'] = 'chkWord' + tabCode ;
55

    
56
	if ( tabCode == 'Replace' )
57
		dialog.SetAutoSize( true ) ;
58
}
59

    
60
GetNextNonEmptyTextNode = function( node, stopNode )
61
{
62
	while ( ( node = FCKDomTools.GetNextSourceNode( node, false, 3, stopNode ) ) && node && node.length < 1 )
63
		1 ;
64
	return node ;
65
}
66

    
67
CharacterCursor = function( arg )
68
{
69
	if ( arg.nodeType && arg.nodeType == 9 )
70
	{
71
		this._textNode = GetNextNonEmptyTextNode( arg.body, arg.documentElement ) ;
72
		this._offset = 0 ;
73
		this._doc = arg ;
74
	}
75
	else
76
	{
77
		this._textNode = arguments[0] ;
78
		this._offset = arguments[1] ;
79
		this._doc = FCKTools.GetElementDocument( arguments[0] ) ;
80
	}
81
}
82
CharacterCursor.prototype =
83
{
84
	GetCharacter : function()
85
	{
86
		return ( this._textNode && this._textNode.nodeValue.charAt( this._offset ) ) || null ;
87
	},
88

    
89
	// Non-normalized.
90
	GetTextNode : function()
91
	{
92
		return this._textNode ;
93
	},
94

    
95
	// Non-normalized.
96
	GetIndex : function()
97
	{
98
		return this._offset ;
99
	},
100

    
101
	// Return value means whehther we've crossed a line break or a paragraph boundary.
102
	MoveNext : function()
103
	{
104
		if ( this._offset < this._textNode.length - 1 )
105
		{
106
			this._offset++ ;
107
			return false ;
108
		}
109

    
110
		var crossed = false ;
111
		var curNode = this._textNode ;
112
		while ( ( curNode = FCKDomTools.GetNextSourceNode( curNode ) )
113
				&& curNode && ( curNode.nodeType != 3 || curNode.length < 1 ) )
114
		{
115
			var tag = curNode.nodeName.toLowerCase() ;
116
			if ( FCKListsLib.BlockElements[tag] || tag == 'br' )
117
				crossed = true ;
118
		}
119

    
120
		this._textNode = curNode ;
121
		this._offset = 0 ;
122
		return crossed ;
123
	},
124

    
125
	// Return value means whehther we've crossed a line break or a paragraph boundary.
126
	MoveBack : function()
127
	{
128
		if ( this._offset > 0 && this._textNode.length > 0 )
129
		{
130
			this._offset = Math.min( this._offset - 1, this._textNode.length - 1 ) ;
131
			return false ;
132
		}
133

    
134
		var crossed = false ;
135
		var curNode = this._textNode ;
136
		while ( ( curNode = FCKDomTools.GetPreviousSourceNode( curNode ) )
137
				&& curNode && ( curNode.nodeType != 3 || curNode.length < 1 ) )
138
		{
139
			var tag = curNode.nodeName.toLowerCase() ;
140
			if ( FCKListsLib.BlockElements[tag] || tag == 'br' )
141
				crossed = true ;
142
		}
143

    
144
		this._textNode = curNode ;
145
		this._offset = curNode && curNode.length - 1 ;
146
		return crossed ;
147
	},
148

    
149
	Clone : function()
150
	{
151
		return new CharacterCursor( this._textNode, this._offset ) ;
152
	}
153
} ;
154

    
155
CharacterRange = function( initCursor, maxLength )
156
{
157
	this._cursors = initCursor.push ? initCursor : [initCursor] ;
158
	this._maxLength = maxLength ;
159
	this._highlightRange = null ;
160
}
161
CharacterRange.prototype =
162
{
163
	ToDomRange : function()
164
	{
165
		var firstCursor = this._cursors[0] ;
166
		var lastCursor = this._cursors[ this._cursors.length - 1 ] ;
167
		var domRange = new FCKDomRange( FCKTools.GetElementWindow( firstCursor.GetTextNode() ) ) ;
168
		var w3cRange = domRange._Range = domRange.CreateRange() ;
169
		w3cRange.setStart( firstCursor.GetTextNode(), firstCursor.GetIndex() ) ;
170
		w3cRange.setEnd( lastCursor.GetTextNode(), lastCursor.GetIndex() + 1 ) ;
171
		domRange._UpdateElementInfo() ;
172
		return domRange ;
173
	},
174

    
175
	Highlight : function()
176
	{
177
		if ( this._cursors.length < 1 )
178
			return ;
179

    
180
		var domRange = this.ToDomRange() ;
181
		HighlightStyle.ApplyToRange( domRange, false, true ) ;
182
		this._highlightRange = domRange ;
183

    
184
		var charRange = CharacterRange.CreateFromDomRange( domRange ) ;
185
		var focusNode = domRange.StartNode ;
186
		if ( focusNode.nodeType != 1 )
187
			focusNode = focusNode.parentNode ;
188
		FCKDomTools.ScrollIntoView( focusNode, false ) ;
189
		this._cursors = charRange._cursors ;
190
	},
191

    
192
	RemoveHighlight : function()
193
	{
194
		if ( this._highlightRange )
195
		{
196
			HighlightStyle.RemoveFromRange( this._highlightRange, false, true ) ;
197
			var charRange = CharacterRange.CreateFromDomRange( this._highlightRange ) ;
198
			this._cursors = charRange._cursors ;
199
			this._highlightRange = null ;
200
		}
201
	},
202

    
203
	GetHighlightDomRange : function()
204
	{
205
		return this._highlightRange;
206
	},
207

    
208
	MoveNext : function()
209
	{
210
		var next = this._cursors[ this._cursors.length - 1 ].Clone() ;
211
		var retval = next.MoveNext() ;
212
		if ( retval )
213
			this._cursors = [] ;
214
		this._cursors.push( next ) ;
215
		if ( this._cursors.length > this._maxLength )
216
			this._cursors.shift() ;
217
		return retval ;
218
	},
219

    
220
	MoveBack : function()
221
	{
222
		var prev = this._cursors[0].Clone() ;
223
		var retval = prev.MoveBack() ;
224
		if ( retval )
225
			this._cursors = [] ;
226
		this._cursors.unshift( prev ) ;
227
		if ( this._cursors.length > this._maxLength )
228
			this._cursors.pop() ;
229
		return retval ;
230
	},
231

    
232
	GetEndCharacter : function()
233
	{
234
		if ( this._cursors.length < 1 )
235
			return null ;
236
		var retval = this._cursors[ this._cursors.length - 1 ].GetCharacter() ;
237
		return retval ;
238
	},
239

    
240
	GetNextRange : function( len )
241
	{
242
		if ( this._cursors.length == 0 )
243
			return null ;
244
		var cur = this._cursors[ this._cursors.length - 1 ].Clone() ;
245
		cur.MoveNext() ;
246
		return new CharacterRange( cur, len ) ;
247
	},
248

    
249
	GetCursors : function()
250
	{
251
		return this._cursors ;
252
	}
253
} ;
254

    
255
CharacterRange.CreateFromDomRange = function( domRange )
256
{
257
	var w3cRange = domRange._Range ;
258
	var startContainer = w3cRange.startContainer ;
259
	var endContainer = w3cRange.endContainer ;
260
	var startTextNode, startIndex, endTextNode, endIndex ;
261

    
262
	if ( startContainer.nodeType == 3 )
263
	{
264
		startTextNode = startContainer ;
265
		startIndex = w3cRange.startOffset ;
266
	}
267
	else if ( domRange.StartNode.nodeType == 3 )
268
	{
269
		startTextNode = domRange.StartNode ;
270
		startIndex = 0 ;
271
	}
272
	else
273
	{
274
		startTextNode = GetNextNonEmptyTextNode( domRange.StartNode, domRange.StartNode.parentNode ) ;
275
		if ( !startTextNode )
276
			return null ;
277
		startIndex = 0 ;
278
	}
279

    
280
	if ( endContainer.nodeType == 3 && w3cRange.endOffset > 0 )
281
	{
282
		endTextNode = endContainer ;
283
		endIndex = w3cRange.endOffset - 1 ;
284
	}
285
	else
286
	{
287
		endTextNode = domRange.EndNode ;
288
		while ( endTextNode.nodeType != 3 )
289
			endTextNode = endTextNode.lastChild ;
290
		endIndex = endTextNode.length - 1 ;
291
	}
292

    
293
	var cursors = [] ;
294
	var current = new CharacterCursor( startTextNode, startIndex ) ;
295
	cursors.push( current ) ;
296
	if ( !( current.GetTextNode() == endTextNode && current.GetIndex() == endIndex ) && !domRange.CheckIsEmpty() )
297
	{
298
		do
299
		{
300
			current = current.Clone() ;
301
			current.MoveNext() ;
302
			cursors.push( current ) ;
303
		}
304
		while ( !( current.GetTextNode() == endTextNode && current.GetIndex() == endIndex ) ) ;
305
	}
306

    
307
	return new CharacterRange( cursors, cursors.length ) ;
308
}
309

    
310
// Knuth-Morris-Pratt Algorithm for stream input
311
KMP_NOMATCH = 0 ;
312
KMP_ADVANCED = 1 ;
313
KMP_MATCHED = 2 ;
314
KmpMatch = function( pattern, ignoreCase )
315
{
316
	var overlap = [ -1 ] ;
317
	for ( var i = 0 ; i < pattern.length ; i++ )
318
	{
319
		overlap.push( overlap[i] + 1 ) ;
320
		while ( overlap[ i + 1 ] > 0 && pattern.charAt( i ) != pattern.charAt( overlap[ i + 1 ] - 1 ) )
321
			overlap[ i + 1 ] = overlap[ overlap[ i + 1 ] - 1 ] + 1 ;
322
	}
323
	this._Overlap = overlap ;
324
	this._State = 0 ;
325
	this._IgnoreCase = ( ignoreCase === true ) ;
326
	if ( ignoreCase )
327
		this.Pattern = pattern.toLowerCase();
328
	else
329
		this.Pattern = pattern ;
330
}
331
KmpMatch.prototype = {
332
	FeedCharacter : function( c )
333
	{
334
		if ( this._IgnoreCase )
335
			c = c.toLowerCase();
336

    
337
		while ( true )
338
		{
339
			if ( c == this.Pattern.charAt( this._State ) )
340
			{
341
				this._State++ ;
342
				if ( this._State == this.Pattern.length )
343
				{
344
					// found a match, start over, don't care about partial matches involving the current match
345
					this._State = 0;
346
					return KMP_MATCHED;
347
				}
348
				return KMP_ADVANCED ;
349
			}
350
			else if ( this._State == 0 )
351
				return KMP_NOMATCH;
352
			else
353
				this._State = this._Overlap[ this._State ];
354
		}
355

    
356
		return null ;
357
	},
358

    
359
	Reset : function()
360
	{
361
		this._State = 0 ;
362
	}
363
};
364

    
365
// Place a range at the start of document.
366
function OnLoad()
367
{
368
	// First of all, translate the dialog box texts.
369
	oEditor.FCKLanguageManager.TranslatePage( document ) ;
370

    
371
	// Show the appropriate tab at startup.
372
	if ( dialogArguments.CustomValue == 'Find' )
373
	{
374
		dialog.SetSelectedTab( 'Find' ) ;
375
		dialog.SetAutoSize( true ) ;
376
	}
377
	else
378
		dialog.SetSelectedTab( 'Replace' ) ;
379

    
380
	SelectField( 'txtFind' + dialogArguments.CustomValue ) ;
381
}
382

    
383
function btnStat()
384
{
385
	GetE('btnReplace').disabled =
386
		GetE('btnReplaceAll').disabled =
387
			GetE('btnFind').disabled =
388
				( GetE(idMap["FindText"]).value.length == 0 ) ;
389
}
390

    
391
function btnStatDelayed()
392
{
393
	setTimeout( btnStat, 1 ) ;
394
}
395

    
396
function GetSearchString()
397
{
398
	return GetE(idMap['FindText']).value ;
399
}
400

    
401
function GetReplaceString()
402
{
403
	return GetE("txtReplace").value ;
404
}
405

    
406
function GetCheckCase()
407
{
408
	return !! ( GetE(idMap['CheckCase']).checked ) ;
409
}
410

    
411
function GetMatchWord()
412
{
413
	return !! ( GetE(idMap['CheckWord']).checked ) ;
414
}
415

    
416
/* Is this character a unicode whitespace or a punctuation mark?
417
 * References:
418
 * http://unicode.org/Public/UNIDATA/PropList.txt (whitespaces)
419
 * http://php.chinaunix.net/manual/tw/ref.regex.php (punctuation marks)
420
 */
421
function CheckIsWordSeparator( c )
422
{
423
	if ( !c )
424
		return true;
425
	var code = c.charCodeAt( 0 );
426
	if ( code >= 9 && code <= 0xd )
427
		return true;
428
	if ( code >= 0x2000 && code <= 0x200a )
429
		return true;
430
	switch ( code )
431
	{
432
		case 0x20:
433
		case 0x85:
434
		case 0xa0:
435
		case 0x1680:
436
		case 0x180e:
437
		case 0x2028:
438
		case 0x2029:
439
		case 0x202f:
440
		case 0x205f:
441
		case 0x3000:
442
			return true;
443
		default:
444
	}
445
	return /[.,"'?!;:]/.test( c ) ;
446
}
447

    
448
FindRange = null ;
449
function _Find()
450
{
451
	var searchString = GetSearchString() ;
452
	if ( !FindRange )
453
		FindRange = new CharacterRange( new CharacterCursor( EditorDocument ), searchString.length ) ;
454
	else
455
	{
456
		FindRange.RemoveHighlight() ;
457
		FindRange = FindRange.GetNextRange( searchString.length ) ;
458
	}
459
	var matcher = new KmpMatch( searchString, ! GetCheckCase() ) ;
460
	var matchState = KMP_NOMATCH ;
461
	var character = '%' ;
462

    
463
	while ( character != null )
464
	{
465
		while ( ( character = FindRange.GetEndCharacter() ) )
466
		{
467
			matchState = matcher.FeedCharacter( character ) ;
468
			if ( matchState == KMP_MATCHED )
469
				break ;
470
			if ( FindRange.MoveNext() )
471
				matcher.Reset() ;
472
		}
473

    
474
		if ( matchState == KMP_MATCHED )
475
		{
476
			if ( GetMatchWord() )
477
			{
478
				var cursors = FindRange.GetCursors() ;
479
				var head = cursors[ cursors.length - 1 ].Clone() ;
480
				var tail = cursors[0].Clone() ;
481
				if ( !head.MoveNext() && !CheckIsWordSeparator( head.GetCharacter() ) )
482
					continue ;
483
				if ( !tail.MoveBack() && !CheckIsWordSeparator( tail.GetCharacter() ) )
484
					continue ;
485
			}
486

    
487
			FindRange.Highlight() ;
488
			return true ;
489
		}
490
	}
491

    
492
	FindRange = null ;
493
	return false ;
494
}
495

    
496
function Find()
497
{
498
	if ( ! _Find() )
499
		alert( FCKLang.DlgFindNotFoundMsg ) ;
500
}
501

    
502
function Replace()
503
{
504
	var saveUndoStep = function( selectRange )
505
	{
506
		var ieRange ;
507
		if ( oEditor.FCKBrowserInfo.IsIE )
508
			ieRange = document.selection.createRange() ;
509

    
510
		selectRange.Select() ;
511
		oEditor.FCKUndo.SaveUndoStep() ;
512
		var cloneRange = selectRange.Clone() ;
513
		cloneRange.Collapse( false ) ;
514
		cloneRange.Select() ;
515

    
516
		if ( ieRange )
517
			setTimeout( function(){ ieRange.select() ; }, 1 ) ;
518
	}
519

    
520
	if ( FindRange && FindRange.GetHighlightDomRange() )
521
	{
522
		var range = FindRange.GetHighlightDomRange() ;
523
		var bookmark = range.CreateBookmark() ;
524
		FindRange.RemoveHighlight() ;
525
		range.MoveToBookmark( bookmark ) ;
526

    
527
		saveUndoStep( range ) ;
528
		range.DeleteContents() ;
529
		range.InsertNode( EditorDocument.createTextNode( GetReplaceString() ) ) ;
530
		range._UpdateElementInfo() ;
531

    
532
		FindRange = CharacterRange.CreateFromDomRange( range ) ;
533
	}
534
	else
535
	{
536
		if ( ! _Find() )
537
		{
538
			FindRange && FindRange.RemoveHighlight() ;
539
			alert( FCKLang.DlgFindNotFoundMsg ) ;
540
		}
541
	}
542
}
543

    
544
function ReplaceAll()
545
{
546
	oEditor.FCKUndo.SaveUndoStep() ;
547
	var replaceCount = 0 ;
548

    
549
	while ( _Find() )
550
	{
551
		var range = FindRange.GetHighlightDomRange() ;
552
		var bookmark = range.CreateBookmark() ;
553
		FindRange.RemoveHighlight() ;
554
		range.MoveToBookmark( bookmark) ;
555

    
556
		range.DeleteContents() ;
557
		range.InsertNode( EditorDocument.createTextNode( GetReplaceString() ) ) ;
558
		range._UpdateElementInfo() ;
559

    
560
		FindRange = CharacterRange.CreateFromDomRange( range ) ;
561
		replaceCount++ ;
562
	}
563
	if ( replaceCount == 0 )
564
	{
565
		FindRange && FindRange.RemoveHighlight() ;
566
		alert( FCKLang.DlgFindNotFoundMsg ) ;
567
	}
568
	dialog.Cancel() ;
569
}
570

    
571
window.onunload = function()
572
{
573
	if ( FindRange )
574
	{
575
		FindRange.RemoveHighlight() ;
576
		FindRange.ToDomRange().Select() ;
577
	}
578
}
579
	</script>
580
</head>
581
<body onload="OnLoad()" style="overflow: hidden">
582
	<div id="divFind" style="display: none">
583
		<table cellspacing="3" cellpadding="2" width="100%" border="0">
584
			<tr>
585
				<td nowrap="nowrap">
586
					<label for="txtFindFind" fcklang="DlgReplaceFindLbl">
587
						Find what:</label>
588
				</td>
589
				<td width="100%">
590
					<input id="txtFindFind" onkeyup="btnStat()" oninput="btnStat()" onpaste="btnStatDelayed()" style="width: 100%" tabindex="1"
591
						type="text" />
592
				</td>
593
				<td>
594
					<input id="btnFind" style="width: 80px" disabled="disabled" onclick="Find();"
595
						type="button" value="Find" fcklang="DlgFindFindBtn" />
596
				</td>
597
			</tr>
598
			<tr>
599
				<td valign="bottom" colspan="3">
600
					&nbsp;<input id="chkCaseFind" tabindex="3" type="checkbox" /><label for="chkCaseFind" fcklang="DlgReplaceCaseChk">Match
601
						case</label>
602
					<br />
603
					&nbsp;<input id="chkWordFind" tabindex="4" type="checkbox" /><label for="chkWordFind" fcklang="DlgReplaceWordChk">Match
604
						whole word</label>
605
				</td>
606
			</tr>
607
		</table>
608
	</div>
609
	<div id="divReplace" style="display:none">
610
		<table cellspacing="3" cellpadding="2" width="100%" border="0">
611
			<tr>
612
				<td nowrap="nowrap">
613
					<label for="txtFindReplace" fcklang="DlgReplaceFindLbl">
614
						Find what:</label>
615
				</td>
616
				<td width="100%">
617
					<input id="txtFindReplace" onkeyup="btnStat()" oninput="btnStat()" onpaste="btnStatDelayed()" style="width: 100%" tabindex="1"
618
						type="text" />
619
				</td>
620
				<td>
621
					<input id="btnReplace" style="width: 80px" disabled="disabled" onclick="Replace();"
622
						type="button" value="Replace" fcklang="DlgReplaceReplaceBtn" />
623
				</td>
624
			</tr>
625
			<tr>
626
				<td valign="top" nowrap="nowrap">
627
					<label for="txtReplace" fcklang="DlgReplaceReplaceLbl">
628
						Replace with:</label>
629
				</td>
630
				<td valign="top">
631
					<input id="txtReplace" style="width: 100%" tabindex="2" type="text" />
632
				</td>
633
				<td>
634
					<input id="btnReplaceAll" style="width: 80px" disabled="disabled" onclick="ReplaceAll()" type="button"
635
						value="Replace All" fcklang="DlgReplaceReplAllBtn" />
636
				</td>
637
			</tr>
638
			<tr>
639
				<td valign="bottom" colspan="3">
640
					&nbsp;<input id="chkCaseReplace" tabindex="3" type="checkbox" /><label for="chkCaseReplace" fcklang="DlgReplaceCaseChk">Match
641
						case</label>
642
					<br />
643
					&nbsp;<input id="chkWordReplace" tabindex="4" type="checkbox" /><label for="chkWordReplace" fcklang="DlgReplaceWordChk">Match
644
						whole word</label>
645
				</td>
646
			</tr>
647
		</table>
648
	</div>
649
</body>
650
</html>
(16-16/27)