1/*
2 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
3 * Copyright (C) 2003-2009 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 * Useful functions used by almost all dialog window pages.
22 * Dialogs should link to this file as the very first script on the page.
23 */
24
25// Automatically detect the correct document.domain (#123).
26(function()
27{
28	var d = document.domain ;
29
30	while ( true )
31	{
32		// Test if we can access a parent property.
33		try
34		{
35			var test = window.parent.document.domain ;
36			break ;
37		}
38		catch( e ) {}
39
40		// Remove a domain part: www.mytest.example.com => mytest.example.com => example.com ...
41		d = d.replace( /.*?(?:\.|$)/, '' ) ;
42
43		if ( d.length == 0 )
44			break ;		// It was not able to detect the domain.
45
46		try
47		{
48			document.domain = d ;
49		}
50		catch (e)
51		{
52			break ;
53		}
54	}
55})() ;
56
57// Attention: FCKConfig must be available in the page.
58function GetCommonDialogCss( prefix )
59{
60	// CSS minified by http://iceyboard.no-ip.org/projects/css_compressor (see _dev/css_compression.txt).
61	return FCKConfig.BasePath + 'dialog/common/' + '|.ImagePreviewArea{border:#000 1px solid;overflow:auto;width:100%;height:170px;background-color:#fff}.FlashPreviewArea{border:#000 1px solid;padding:5px;overflow:auto;width:100%;height:170px;background-color:#fff}.BtnReset{float:left;background-position:center center;background-image:url(images/reset.gif);width:16px;height:16px;background-repeat:no-repeat;border:1px none;font-size:1px}.BtnLocked,.BtnUnlocked{float:left;background-position:center center;background-image:url(images/locked.gif);width:16px;height:16px;background-repeat:no-repeat;border:none 1px;font-size:1px}.BtnUnlocked{background-image:url(images/unlocked.gif)}.BtnOver{border:outset 1px;cursor:pointer;cursor:hand}' ;
62}
63
64// Gets a element by its Id. Used for shorter coding.
65function GetE( elementId )
66{
67	return document.getElementById( elementId )  ;
68}
69
70function ShowE( element, isVisible )
71{
72	if ( typeof( element ) == 'string' )
73		element = GetE( element ) ;
74	element.style.display = isVisible ? '' : 'none' ;
75}
76
77function SetAttribute( element, attName, attValue )
78{
79	if ( attValue == null || attValue.length == 0 )
80		element.removeAttribute( attName, 0 ) ;			// 0 : Case Insensitive
81	else
82		element.setAttribute( attName, attValue, 0 ) ;	// 0 : Case Insensitive
83}
84
85function GetAttribute( element, attName, valueIfNull )
86{
87	var oAtt = element.attributes[attName] ;
88
89	if ( oAtt == null || !oAtt.specified )
90		return valueIfNull ? valueIfNull : '' ;
91
92	var oValue = element.getAttribute( attName, 2 ) ;
93
94	if ( oValue == null )
95		oValue = oAtt.nodeValue ;
96
97	return ( oValue == null ? valueIfNull : oValue ) ;
98}
99
100function SelectField( elementId )
101{
102	var element = GetE( elementId ) ;
103	element.focus() ;
104
105	// element.select may not be available for some fields (like <select>).
106	if ( element.select )
107		element.select() ;
108}
109
110// Functions used by text fields to accept numbers only.
111var IsDigit = ( function()
112	{
113		var KeyIdentifierMap =
114		{
115			End			: 35,
116			Home		: 36,
117			Left		: 37,
118			Right		: 39,
119			'U+00007F'	: 46		// Delete
120		} ;
121
122		return function ( e )
123			{
124				if ( !e )
125					e = event ;
126
127				var iCode = ( e.keyCode || e.charCode ) ;
128
129				if ( !iCode && e.keyIdentifier && ( e.keyIdentifier in KeyIdentifierMap ) )
130						iCode = KeyIdentifierMap[ e.keyIdentifier ] ;
131
132				return (
133						( iCode >= 48 && iCode <= 57 )		// Numbers
134						|| (iCode >= 35 && iCode <= 40)		// Arrows, Home, End
135						|| iCode == 8						// Backspace
136						|| iCode == 46						// Delete
137						|| iCode == 9						// Tab
138				) ;
139			}
140	} )() ;
141
142String.prototype.Trim = function()
143{
144	return this.replace( /(^\s*)|(\s*$)/g, '' ) ;
145}
146
147String.prototype.StartsWith = function( value )
148{
149	return ( this.substr( 0, value.length ) == value ) ;
150}
151
152String.prototype.Remove = function( start, length )
153{
154	var s = '' ;
155
156	if ( start > 0 )
157		s = this.substring( 0, start ) ;
158
159	if ( start + length < this.length )
160		s += this.substring( start + length , this.length ) ;
161
162	return s ;
163}
164
165String.prototype.ReplaceAll = function( searchArray, replaceArray )
166{
167	var replaced = this ;
168
169	for ( var i = 0 ; i < searchArray.length ; i++ )
170	{
171		replaced = replaced.replace( searchArray[i], replaceArray[i] ) ;
172	}
173
174	return replaced ;
175}
176
177function OpenFileBrowser( url, width, height )
178{
179	// oEditor must be defined.
180
181	var iLeft = ( oEditor.FCKConfig.ScreenWidth  - width ) / 2 ;
182	var iTop  = ( oEditor.FCKConfig.ScreenHeight - height ) / 2 ;
183
184	var sOptions = "toolbar=no,status=no,resizable=yes,dependent=yes,scrollbars=yes" ;
185	sOptions += ",width=" + width ;
186	sOptions += ",height=" + height ;
187	sOptions += ",left=" + iLeft ;
188	sOptions += ",top=" + iTop ;
189
190	window.open( url, 'FCKBrowseWindow', sOptions ) ;
191}
192
193/**
194 Utility function to create/update an element with a name attribute in IE, so it behaves properly when moved around
195 It also allows to change the name or other special attributes in an existing node
196	oEditor : instance of FCKeditor where the element will be created
197	oOriginal : current element being edited or null if it has to be created
198	nodeName : string with the name of the element to create
199	oAttributes : Hash object with the attributes that must be set at creation time in IE
200								Those attributes will be set also after the element has been
201								created for any other browser to avoid redudant code
202*/
203function CreateNamedElement( oEditor, oOriginal, nodeName, oAttributes )
204{
205	var oNewNode ;
206
207	// IE doesn't allow easily to change properties of an existing object,
208	// so remove the old and force the creation of a new one.
209	var oldNode = null ;
210	if ( oOriginal && oEditor.FCKBrowserInfo.IsIE )
211	{
212		// Force the creation only if some of the special attributes have changed:
213		var bChanged = false;
214		for( var attName in oAttributes )
215			bChanged |= ( oOriginal.getAttribute( attName, 2) != oAttributes[attName] ) ;
216
217		if ( bChanged )
218		{
219			oldNode = oOriginal ;
220			oOriginal = null ;
221		}
222	}
223
224	// If the node existed (and it's not IE), then we just have to update its attributes
225	if ( oOriginal )
226	{
227		oNewNode = oOriginal ;
228	}
229	else
230	{
231		// #676, IE doesn't play nice with the name or type attribute
232		if ( oEditor.FCKBrowserInfo.IsIE )
233		{
234			var sbHTML = [] ;
235			sbHTML.push( '<' + nodeName ) ;
236			for( var prop in oAttributes )
237			{
238				sbHTML.push( ' ' + prop + '="' + oAttributes[prop] + '"' ) ;
239			}
240			sbHTML.push( '>' ) ;
241			if ( !oEditor.FCKListsLib.EmptyElements[nodeName.toLowerCase()] )
242				sbHTML.push( '</' + nodeName + '>' ) ;
243
244			oNewNode = oEditor.FCK.EditorDocument.createElement( sbHTML.join('') ) ;
245			// Check if we are just changing the properties of an existing node: copy its properties
246			if ( oldNode )
247			{
248				CopyAttributes( oldNode, oNewNode, oAttributes ) ;
249				oEditor.FCKDomTools.MoveChildren( oldNode, oNewNode ) ;
250				oldNode.parentNode.removeChild( oldNode ) ;
251				oldNode = null ;
252
253				if ( oEditor.FCK.Selection.SelectionData )
254				{
255					// Trick to refresh the selection object and avoid error in
256					// fckdialog.html Selection.EnsureSelection
257					var oSel = oEditor.FCK.EditorDocument.selection ;
258					oEditor.FCK.Selection.SelectionData = oSel.createRange() ; // Now oSel.type will be 'None' reflecting the real situation
259				}
260			}
261			oNewNode = oEditor.FCK.InsertElement( oNewNode ) ;
262
263			// FCK.Selection.SelectionData is broken by now since we've
264			// deleted the previously selected element. So we need to reassign it.
265			if ( oEditor.FCK.Selection.SelectionData )
266			{
267				var range = oEditor.FCK.EditorDocument.body.createControlRange() ;
268				range.add( oNewNode ) ;
269				oEditor.FCK.Selection.SelectionData = range ;
270			}
271		}
272		else
273		{
274			oNewNode = oEditor.FCK.InsertElement( nodeName ) ;
275		}
276	}
277
278	// Set the basic attributes
279	for( var attName in oAttributes )
280		oNewNode.setAttribute( attName, oAttributes[attName], 0 ) ;	// 0 : Case Insensitive
281
282	return oNewNode ;
283}
284
285// Copy all the attributes from one node to the other, kinda like a clone
286// But oSkipAttributes is an object with the attributes that must NOT be copied
287function CopyAttributes( oSource, oDest, oSkipAttributes )
288{
289	var aAttributes = oSource.attributes ;
290
291	for ( var n = 0 ; n < aAttributes.length ; n++ )
292	{
293		var oAttribute = aAttributes[n] ;
294
295		if ( oAttribute.specified )
296		{
297			var sAttName = oAttribute.nodeName ;
298			// We can set the type only once, so do it with the proper value, not copying it.
299			if ( sAttName in oSkipAttributes )
300				continue ;
301
302			var sAttValue = oSource.getAttribute( sAttName, 2 ) ;
303			if ( sAttValue == null )
304				sAttValue = oAttribute.nodeValue ;
305
306			oDest.setAttribute( sAttName, sAttValue, 0 ) ;	// 0 : Case Insensitive
307		}
308	}
309	// The style:
310	if ( oSource.style.cssText !== '' )
311		oDest.style.cssText = oSource.style.cssText ;
312}
313
314/**
315* Replaces a tag with another one, keeping its contents:
316* for example TD --> TH, and TH --> TD.
317* input: the original node, and the new tag name
318* http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-renameNode
319*/
320function RenameNode( oNode , newTag )
321{
322	// TODO: if the browser natively supports document.renameNode call it.
323	// does any browser currently support it in order to test?
324
325	// Only rename element nodes.
326	if ( oNode.nodeType != 1 )
327		return null ;
328
329	// If it's already correct exit here.
330	if ( oNode.nodeName == newTag )
331		return oNode ;
332
333	var oDoc = oNode.ownerDocument ;
334	// Create the new node
335	var newNode = oDoc.createElement( newTag ) ;
336
337	// Copy all attributes
338	CopyAttributes( oNode, newNode, {} ) ;
339
340	// Move children to the new node
341	FCKDomTools.MoveChildren( oNode, newNode ) ;
342
343	// Finally replace the node and return the new one
344	oNode.parentNode.replaceChild( newNode, oNode ) ;
345
346	return newNode ;
347}
348