2 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
\r
3 * Copyright (C) 2003-2010 Frederico Caldeira Knabben
\r
5 * == BEGIN LICENSE ==
\r
7 * Licensed under the terms of any of the following licenses at your
\r
10 * - GNU General Public License Version 2 or later (the "GPL")
\r
11 * http://www.gnu.org/licenses/gpl.html
\r
13 * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
\r
14 * http://www.gnu.org/licenses/lgpl.html
\r
16 * - Mozilla Public License Version 1.1 or later (the "MPL")
\r
17 * http://www.mozilla.org/MPL/MPL-1.1.html
\r
21 * Useful functions used by almost all dialog window pages.
\r
22 * Dialogs should link to this file as the very first script on the page.
\r
25 // Automatically detect the correct document.domain (#123).
\r
28 var d = document.domain ;
\r
32 // Test if we can access a parent property.
\r
35 var test = window.parent.document.domain ;
\r
40 // Remove a domain part: www.mytest.example.com => mytest.example.com => example.com ...
\r
41 d = d.replace( /.*?(?:\.|$)/, '' ) ;
\r
43 if ( d.length == 0 )
\r
44 break ; // It was not able to detect the domain.
\r
48 document.domain = d ;
\r
57 // Attention: FCKConfig must be available in the page.
\r
58 function GetCommonDialogCss( prefix )
\r
60 // CSS minified by http://iceyboard.no-ip.org/projects/css_compressor (see _dev/css_compression.txt).
\r
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}' ;
\r
64 // Gets a element by its Id. Used for shorter coding.
\r
65 function GetE( elementId )
\r
67 return document.getElementById( elementId ) ;
\r
70 function ShowE( element, isVisible )
\r
72 if ( typeof( element ) == 'string' )
\r
73 element = GetE( element ) ;
\r
74 element.style.display = isVisible ? '' : 'none' ;
\r
77 function SetAttribute( element, attName, attValue )
\r
79 if ( attValue == null || attValue.length == 0 )
\r
80 element.removeAttribute( attName, 0 ) ; // 0 : Case Insensitive
\r
82 element.setAttribute( attName, attValue, 0 ) ; // 0 : Case Insensitive
\r
85 function GetAttribute( element, attName, valueIfNull )
\r
87 var oAtt = element.attributes[attName] ;
\r
89 if ( oAtt == null || !oAtt.specified )
\r
90 return valueIfNull ? valueIfNull : '' ;
\r
92 var oValue = element.getAttribute( attName, 2 ) ;
\r
94 if ( oValue == null )
\r
95 oValue = oAtt.nodeValue ;
\r
97 return ( oValue == null ? valueIfNull : oValue ) ;
\r
100 function SelectField( elementId )
\r
102 var element = GetE( elementId ) ;
\r
105 // element.select may not be available for some fields (like <select>).
\r
106 if ( element.select )
\r
110 // Functions used by text fields to accept numbers only.
\r
111 var IsDigit = ( function()
\r
113 var KeyIdentifierMap =
\r
119 'U+00007F' : 46 // Delete
\r
122 return function ( e )
\r
127 var iCode = ( e.keyCode || e.charCode ) ;
\r
129 if ( !iCode && e.keyIdentifier && ( e.keyIdentifier in KeyIdentifierMap ) )
\r
130 iCode = KeyIdentifierMap[ e.keyIdentifier ] ;
\r
133 ( iCode >= 48 && iCode <= 57 ) // Numbers
\r
134 || (iCode >= 35 && iCode <= 40) // Arrows, Home, End
\r
135 || iCode == 8 // Backspace
\r
136 || iCode == 46 // Delete
\r
137 || iCode == 9 // Tab
\r
142 String.prototype.Trim = function()
\r
144 return this.replace( /(^\s*)|(\s*$)/g, '' ) ;
\r
147 String.prototype.StartsWith = function( value )
\r
149 return ( this.substr( 0, value.length ) == value ) ;
\r
152 String.prototype.Remove = function( start, length )
\r
157 s = this.substring( 0, start ) ;
\r
159 if ( start + length < this.length )
\r
160 s += this.substring( start + length , this.length ) ;
\r
165 String.prototype.ReplaceAll = function( searchArray, replaceArray )
\r
167 var replaced = this ;
\r
169 for ( var i = 0 ; i < searchArray.length ; i++ )
\r
171 replaced = replaced.replace( searchArray[i], replaceArray[i] ) ;
\r
177 function OpenFileBrowser( url, width, height )
\r
179 // oEditor must be defined.
\r
181 var iLeft = ( oEditor.FCKConfig.ScreenWidth - width ) / 2 ;
\r
182 var iTop = ( oEditor.FCKConfig.ScreenHeight - height ) / 2 ;
\r
184 var sOptions = "toolbar=no,status=no,resizable=yes,dependent=yes,scrollbars=yes" ;
\r
185 sOptions += ",width=" + width ;
\r
186 sOptions += ",height=" + height ;
\r
187 sOptions += ",left=" + iLeft ;
\r
188 sOptions += ",top=" + iTop ;
\r
190 window.open( url, 'FCKBrowseWindow', sOptions ) ;
\r
194 Utility function to create/update an element with a name attribute in IE, so it behaves properly when moved around
\r
195 It also allows to change the name or other special attributes in an existing node
\r
196 oEditor : instance of FCKeditor where the element will be created
\r
197 oOriginal : current element being edited or null if it has to be created
\r
198 nodeName : string with the name of the element to create
\r
199 oAttributes : Hash object with the attributes that must be set at creation time in IE
\r
200 Those attributes will be set also after the element has been
\r
201 created for any other browser to avoid redudant code
\r
203 function CreateNamedElement( oEditor, oOriginal, nodeName, oAttributes )
\r
207 // IE doesn't allow easily to change properties of an existing object,
\r
208 // so remove the old and force the creation of a new one.
\r
209 var oldNode = null ;
\r
210 if ( oOriginal && oEditor.FCKBrowserInfo.IsIE )
\r
212 // Force the creation only if some of the special attributes have changed:
\r
213 var bChanged = false;
\r
214 for( var attName in oAttributes )
\r
215 bChanged |= ( oOriginal.getAttribute( attName, 2) != oAttributes[attName] ) ;
\r
219 oldNode = oOriginal ;
\r
224 // If the node existed (and it's not IE), then we just have to update its attributes
\r
227 oNewNode = oOriginal ;
\r
231 // #676, IE doesn't play nice with the name or type attribute
\r
232 if ( oEditor.FCKBrowserInfo.IsIE )
\r
235 sbHTML.push( '<' + nodeName ) ;
\r
236 for( var prop in oAttributes )
\r
238 sbHTML.push( ' ' + prop + '="' + oAttributes[prop] + '"' ) ;
\r
240 sbHTML.push( '>' ) ;
\r
241 if ( !oEditor.FCKListsLib.EmptyElements[nodeName.toLowerCase()] )
\r
242 sbHTML.push( '</' + nodeName + '>' ) ;
\r
244 oNewNode = oEditor.FCK.EditorDocument.createElement( sbHTML.join('') ) ;
\r
245 // Check if we are just changing the properties of an existing node: copy its properties
\r
248 CopyAttributes( oldNode, oNewNode, oAttributes ) ;
\r
249 oEditor.FCKDomTools.MoveChildren( oldNode, oNewNode ) ;
\r
250 oldNode.parentNode.removeChild( oldNode ) ;
\r
253 if ( oEditor.FCK.Selection.SelectionData )
\r
255 // Trick to refresh the selection object and avoid error in
\r
256 // fckdialog.html Selection.EnsureSelection
\r
257 var oSel = oEditor.FCK.EditorDocument.selection ;
\r
258 oEditor.FCK.Selection.SelectionData = oSel.createRange() ; // Now oSel.type will be 'None' reflecting the real situation
\r
261 oNewNode = oEditor.FCK.InsertElement( oNewNode ) ;
\r
263 // FCK.Selection.SelectionData is broken by now since we've
\r
264 // deleted the previously selected element. So we need to reassign it.
\r
265 if ( oEditor.FCK.Selection.SelectionData )
\r
267 var range = oEditor.FCK.EditorDocument.body.createControlRange() ;
\r
268 range.add( oNewNode ) ;
\r
269 oEditor.FCK.Selection.SelectionData = range ;
\r
274 oNewNode = oEditor.FCK.InsertElement( nodeName ) ;
\r
278 // Set the basic attributes
\r
279 for( var attName in oAttributes )
\r
280 oNewNode.setAttribute( attName, oAttributes[attName], 0 ) ; // 0 : Case Insensitive
\r
285 // Copy all the attributes from one node to the other, kinda like a clone
\r
286 // But oSkipAttributes is an object with the attributes that must NOT be copied
\r
287 function CopyAttributes( oSource, oDest, oSkipAttributes )
\r
289 var aAttributes = oSource.attributes ;
\r
291 for ( var n = 0 ; n < aAttributes.length ; n++ )
\r
293 var oAttribute = aAttributes[n] ;
\r
295 if ( oAttribute.specified )
\r
297 var sAttName = oAttribute.nodeName ;
\r
298 // We can set the type only once, so do it with the proper value, not copying it.
\r
299 if ( sAttName in oSkipAttributes )
\r
302 var sAttValue = oSource.getAttribute( sAttName, 2 ) ;
\r
303 if ( sAttValue == null )
\r
304 sAttValue = oAttribute.nodeValue ;
\r
306 oDest.setAttribute( sAttName, sAttValue, 0 ) ; // 0 : Case Insensitive
\r
310 if ( oSource.style.cssText !== '' )
\r
311 oDest.style.cssText = oSource.style.cssText ;
\r
315 * Replaces a tag with another one, keeping its contents:
\r
316 * for example TD --> TH, and TH --> TD.
\r
317 * input: the original node, and the new tag name
\r
318 * http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-renameNode
\r
320 function RenameNode( oNode , newTag )
\r
322 // TODO: if the browser natively supports document.renameNode call it.
\r
323 // does any browser currently support it in order to test?
\r
325 // Only rename element nodes.
\r
326 if ( oNode.nodeType != 1 )
\r
329 // If it's already correct exit here.
\r
330 if ( oNode.nodeName == newTag )
\r
333 var oDoc = oNode.ownerDocument ;
\r
334 // Create the new node
\r
335 var newNode = oDoc.createElement( newTag ) ;
\r
337 // Copy all attributes
\r
338 CopyAttributes( oNode, newNode, {} ) ;
\r
340 // Move children to the new node
\r
341 FCKDomTools.MoveChildren( oNode, newNode ) ;
\r
343 // Finally replace the node and return the new one
\r
344 oNode.parentNode.replaceChild( newNode, oNode ) ;
\r