import rt 3.8.7
[freeside.git] / rt / share / html / NoAuth / RichText / FCKeditor / editor / _source / internals / fcktools.js
1 /*\r
2  * FCKeditor - The text editor for Internet - http://www.fckeditor.net\r
3  * Copyright (C) 2003-2009 Frederico Caldeira Knabben\r
4  *\r
5  * == BEGIN LICENSE ==\r
6  *\r
7  * Licensed under the terms of any of the following licenses at your\r
8  * choice:\r
9  *\r
10  *  - GNU General Public License Version 2 or later (the "GPL")\r
11  *    http://www.gnu.org/licenses/gpl.html\r
12  *\r
13  *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")\r
14  *    http://www.gnu.org/licenses/lgpl.html\r
15  *\r
16  *  - Mozilla Public License Version 1.1 or later (the "MPL")\r
17  *    http://www.mozilla.org/MPL/MPL-1.1.html\r
18  *\r
19  * == END LICENSE ==\r
20  *\r
21  * Utility functions.\r
22  */\r
23 \r
24 var FCKTools = new Object() ;\r
25 \r
26 FCKTools.CreateBogusBR = function( targetDocument )\r
27 {\r
28         var eBR = targetDocument.createElement( 'br' ) ;\r
29 //      eBR.setAttribute( '_moz_editor_bogus_node', 'TRUE' ) ;\r
30         eBR.setAttribute( 'type', '_moz' ) ;\r
31         return eBR ;\r
32 }\r
33 \r
34 /**\r
35  * Fixes relative URL entries defined inside CSS styles by appending a prefix\r
36  * to them.\r
37  * @param (String) cssStyles The CSS styles definition possibly containing url()\r
38  *              paths.\r
39  * @param (String) urlFixPrefix The prefix to append to relative URLs.\r
40  */\r
41 FCKTools.FixCssUrls = function( urlFixPrefix, cssStyles )\r
42 {\r
43         if ( !urlFixPrefix || urlFixPrefix.length == 0 )\r
44                 return cssStyles ;\r
45 \r
46         return cssStyles.replace( /url\s*\(([\s'"]*)(.*?)([\s"']*)\)/g, function( match, opener, path, closer )\r
47                 {\r
48                         if ( /^\/|^\w?:/.test( path ) )\r
49                                 return match ;\r
50                         else\r
51                                 return 'url(' + opener + urlFixPrefix + path + closer + ')' ;\r
52                 } ) ;\r
53 }\r
54 \r
55 FCKTools._GetUrlFixedCss = function( cssStyles, urlFixPrefix )\r
56 {\r
57         var match = cssStyles.match( /^([^|]+)\|([\s\S]*)/ ) ;\r
58 \r
59         if ( match )\r
60                 return FCKTools.FixCssUrls( match[1], match[2] ) ;\r
61         else\r
62                 return cssStyles ;\r
63 }\r
64 \r
65 /**\r
66  * Appends a <link css> or <style> element to the document.\r
67  * @param (Object) documentElement The DOM document object to which append the\r
68  *              stylesheet.\r
69  * @param (Variant) cssFileOrDef A String pointing to the CSS file URL or an\r
70  *              Array with many CSS file URLs or the CSS definitions for the <style>\r
71  *              element.\r
72  * @return {Array} An array containing all elements created in the target\r
73  *              document. It may include <link> or <style> elements, depending on the\r
74  *              value passed with cssFileOrDef.\r
75  */\r
76 FCKTools.AppendStyleSheet = function( domDocument, cssFileOrArrayOrDef )\r
77 {\r
78         if ( !cssFileOrArrayOrDef )\r
79                 return [] ;\r
80 \r
81         if ( typeof( cssFileOrArrayOrDef ) == 'string' )\r
82         {\r
83                 // Test if the passed argument is an URL.\r
84                 if ( /[\\\/\.][^{}]*$/.test( cssFileOrArrayOrDef ) )\r
85                 {\r
86                         // The string may have several URLs separated by comma.\r
87                         return this.AppendStyleSheet( domDocument, cssFileOrArrayOrDef.split(',') ) ;\r
88                 }\r
89                 else\r
90                         return [ this.AppendStyleString( domDocument, FCKTools._GetUrlFixedCss( cssFileOrArrayOrDef ) ) ] ;\r
91         }\r
92         else\r
93         {\r
94                 var styles = [] ;\r
95                 for ( var i = 0 ; i < cssFileOrArrayOrDef.length ; i++ )\r
96                         styles.push( this._AppendStyleSheet( domDocument, cssFileOrArrayOrDef[i] ) ) ;\r
97                 return styles ;\r
98         }\r
99 }\r
100 \r
101 FCKTools.GetStyleHtml = (function()\r
102 {\r
103         var getStyle = function( styleDef, markTemp )\r
104         {\r
105                 if ( styleDef.length == 0 )\r
106                         return '' ;\r
107 \r
108                 var temp = markTemp ? ' _fcktemp="true"' : '' ;\r
109                 return '<' + 'style type="text/css"' + temp + '>' + styleDef + '<' + '/style>' ;\r
110         }\r
111 \r
112         var getLink = function( cssFileUrl, markTemp )\r
113         {\r
114                 if ( cssFileUrl.length == 0 )\r
115                         return '' ;\r
116 \r
117                 var temp = markTemp ? ' _fcktemp="true"' : '' ;\r
118                 return '<' + 'link href="' + cssFileUrl + '" type="text/css" rel="stylesheet" ' + temp + '/>' ;\r
119         }\r
120 \r
121         return function( cssFileOrArrayOrDef, markTemp )\r
122         {\r
123                 if ( !cssFileOrArrayOrDef )\r
124                         return '' ;\r
125 \r
126                 if ( typeof( cssFileOrArrayOrDef ) == 'string' )\r
127                 {\r
128                         // Test if the passed argument is an URL.\r
129                         if ( /[\\\/\.][^{}]*$/.test( cssFileOrArrayOrDef ) )\r
130                         {\r
131                                 // The string may have several URLs separated by comma.\r
132                                 return this.GetStyleHtml( cssFileOrArrayOrDef.split(','), markTemp ) ;\r
133                         }\r
134                         else\r
135                                 return getStyle( this._GetUrlFixedCss( cssFileOrArrayOrDef ), markTemp ) ;\r
136                 }\r
137                 else\r
138                 {\r
139                         var html = '' ;\r
140 \r
141                         for ( var i = 0 ; i < cssFileOrArrayOrDef.length ; i++ )\r
142                                 html += getLink( cssFileOrArrayOrDef[i], markTemp ) ;\r
143 \r
144                         return html ;\r
145                 }\r
146         }\r
147 })() ;\r
148 \r
149 FCKTools.GetElementDocument = function ( element )\r
150 {\r
151         return element.ownerDocument || element.document ;\r
152 }\r
153 \r
154 // Get the window object where the element is placed in.\r
155 FCKTools.GetElementWindow = function( element )\r
156 {\r
157         return this.GetDocumentWindow( this.GetElementDocument( element ) ) ;\r
158 }\r
159 \r
160 FCKTools.GetDocumentWindow = function( document )\r
161 {\r
162         // With Safari, there is not way to retrieve the window from the document, so we must fix it.\r
163         if ( FCKBrowserInfo.IsSafari && !document.parentWindow )\r
164                 this.FixDocumentParentWindow( window.top ) ;\r
165 \r
166         return document.parentWindow || document.defaultView ;\r
167 }\r
168 \r
169 /*\r
170         This is a Safari specific function that fix the reference to the parent\r
171         window from the document object.\r
172 */\r
173 FCKTools.FixDocumentParentWindow = function( targetWindow )\r
174 {\r
175         if ( targetWindow.document )\r
176                 targetWindow.document.parentWindow = targetWindow ;\r
177 \r
178         for ( var i = 0 ; i < targetWindow.frames.length ; i++ )\r
179                 FCKTools.FixDocumentParentWindow( targetWindow.frames[i] ) ;\r
180 }\r
181 \r
182 FCKTools.HTMLEncode = function( text )\r
183 {\r
184         if ( !text )\r
185                 return '' ;\r
186 \r
187         text = text.replace( /&/g, '&amp;' ) ;\r
188         text = text.replace( /</g, '&lt;' ) ;\r
189         text = text.replace( />/g, '&gt;' ) ;\r
190 \r
191         return text ;\r
192 }\r
193 \r
194 FCKTools.HTMLDecode = function( text )\r
195 {\r
196         if ( !text )\r
197                 return '' ;\r
198 \r
199         text = text.replace( /&gt;/g, '>' ) ;\r
200         text = text.replace( /&lt;/g, '<' ) ;\r
201         text = text.replace( /&amp;/g, '&' ) ;\r
202 \r
203         return text ;\r
204 }\r
205 \r
206 FCKTools._ProcessLineBreaksForPMode = function( oEditor, text, liState, node, strArray )\r
207 {\r
208         var closeState = 0 ;\r
209         var blockStartTag = "<p>" ;\r
210         var blockEndTag = "</p>" ;\r
211         var lineBreakTag = "<br />" ;\r
212         if ( liState )\r
213         {\r
214                 blockStartTag = "<li>" ;\r
215                 blockEndTag = "</li>" ;\r
216                 closeState = 1 ;\r
217         }\r
218 \r
219         // Are we currently inside a <p> tag now?\r
220         // If yes, close it at the next double line break.\r
221         while ( node && node != oEditor.FCK.EditorDocument.body )\r
222         {\r
223                 if ( node.tagName.toLowerCase() == 'p' )\r
224                 {\r
225                         closeState = 1 ;\r
226                         break;\r
227                 }\r
228                 node = node.parentNode ;\r
229         }\r
230 \r
231         for ( var i = 0 ; i < text.length ; i++ )\r
232         {\r
233                 var c = text.charAt( i ) ;\r
234                 if ( c == '\r' )\r
235                         continue ;\r
236 \r
237                 if ( c != '\n' )\r
238                 {\r
239                         strArray.push( c ) ;\r
240                         continue ;\r
241                 }\r
242 \r
243                 // Now we have encountered a line break.\r
244                 // Check if the next character is also a line break.\r
245                 var n = text.charAt( i + 1 ) ;\r
246                 if ( n == '\r' )\r
247                 {\r
248                         i++ ;\r
249                         n = text.charAt( i + 1 ) ;\r
250                 }\r
251                 if ( n == '\n' )\r
252                 {\r
253                         i++ ;   // ignore next character - we have already processed it.\r
254                         if ( closeState )\r
255                                 strArray.push( blockEndTag ) ;\r
256                         strArray.push( blockStartTag ) ;\r
257                         closeState = 1 ;\r
258                 }\r
259                 else\r
260                         strArray.push( lineBreakTag ) ;\r
261         }\r
262 }\r
263 \r
264 FCKTools._ProcessLineBreaksForDivMode = function( oEditor, text, liState, node, strArray )\r
265 {\r
266         var closeState = 0 ;\r
267         var blockStartTag = "<div>" ;\r
268         var blockEndTag = "</div>" ;\r
269         if ( liState )\r
270         {\r
271                 blockStartTag = "<li>" ;\r
272                 blockEndTag = "</li>" ;\r
273                 closeState = 1 ;\r
274         }\r
275 \r
276         // Are we currently inside a <div> tag now?\r
277         // If yes, close it at the next double line break.\r
278         while ( node && node != oEditor.FCK.EditorDocument.body )\r
279         {\r
280                 if ( node.tagName.toLowerCase() == 'div' )\r
281                 {\r
282                         closeState = 1 ;\r
283                         break ;\r
284                 }\r
285                 node = node.parentNode ;\r
286         }\r
287 \r
288         for ( var i = 0 ; i < text.length ; i++ )\r
289         {\r
290                 var c = text.charAt( i ) ;\r
291                 if ( c == '\r' )\r
292                         continue ;\r
293 \r
294                 if ( c != '\n' )\r
295                 {\r
296                         strArray.push( c ) ;\r
297                         continue ;\r
298                 }\r
299 \r
300                 if ( closeState )\r
301                 {\r
302                         if ( strArray[ strArray.length - 1 ] == blockStartTag )\r
303                         {\r
304                                 // A div tag must have some contents inside for it to be visible.\r
305                                 strArray.push( "&nbsp;" ) ;\r
306                         }\r
307                         strArray.push( blockEndTag ) ;\r
308                 }\r
309                 strArray.push( blockStartTag ) ;\r
310                 closeState = 1 ;\r
311         }\r
312         if ( closeState )\r
313                 strArray.push( blockEndTag ) ;\r
314 }\r
315 \r
316 FCKTools._ProcessLineBreaksForBrMode = function( oEditor, text, liState, node, strArray )\r
317 {\r
318         var closeState = 0 ;\r
319         var blockStartTag = "<br />" ;\r
320         var blockEndTag = "" ;\r
321         if ( liState )\r
322         {\r
323                 blockStartTag = "<li>" ;\r
324                 blockEndTag = "</li>" ;\r
325                 closeState = 1 ;\r
326         }\r
327 \r
328         for ( var i = 0 ; i < text.length ; i++ )\r
329         {\r
330                 var c = text.charAt( i ) ;\r
331                 if ( c == '\r' )\r
332                         continue ;\r
333 \r
334                 if ( c != '\n' )\r
335                 {\r
336                         strArray.push( c ) ;\r
337                         continue ;\r
338                 }\r
339 \r
340                 if ( closeState && blockEndTag.length )\r
341                         strArray.push ( blockEndTag ) ;\r
342                 strArray.push( blockStartTag ) ;\r
343                 closeState = 1 ;\r
344         }\r
345 }\r
346 \r
347 FCKTools.ProcessLineBreaks = function( oEditor, oConfig, text )\r
348 {\r
349         var enterMode = oConfig.EnterMode.toLowerCase() ;\r
350         var strArray = [] ;\r
351 \r
352         // Is the caret or selection inside an <li> tag now?\r
353         var liState = 0 ;\r
354         var range = new oEditor.FCKDomRange( oEditor.FCK.EditorWindow ) ;\r
355         range.MoveToSelection() ;\r
356         var node = range._Range.startContainer ;\r
357         while ( node && node.nodeType != 1 )\r
358                 node = node.parentNode ;\r
359         if ( node && node.tagName.toLowerCase() == 'li' )\r
360                 liState = 1 ;\r
361 \r
362         if ( enterMode == 'p' )\r
363                 this._ProcessLineBreaksForPMode( oEditor, text, liState, node, strArray ) ;\r
364         else if ( enterMode == 'div' )\r
365                 this._ProcessLineBreaksForDivMode( oEditor, text, liState, node, strArray ) ;\r
366         else if ( enterMode == 'br' )\r
367                 this._ProcessLineBreaksForBrMode( oEditor, text, liState, node, strArray ) ;\r
368         return strArray.join( "" ) ;\r
369 }\r
370 \r
371 /**\r
372  * Adds an option to a SELECT element.\r
373  */\r
374 FCKTools.AddSelectOption = function( selectElement, optionText, optionValue )\r
375 {\r
376         var oOption = FCKTools.GetElementDocument( selectElement ).createElement( "OPTION" ) ;\r
377 \r
378         oOption.text    = optionText ;\r
379         oOption.value   = optionValue ;\r
380 \r
381         selectElement.options.add(oOption) ;\r
382 \r
383         return oOption ;\r
384 }\r
385 \r
386 FCKTools.RunFunction = function( func, thisObject, paramsArray, timerWindow )\r
387 {\r
388         if ( func )\r
389                 this.SetTimeout( func, 0, thisObject, paramsArray, timerWindow ) ;\r
390 }\r
391 \r
392 FCKTools.SetTimeout = function( func, milliseconds, thisObject, paramsArray, timerWindow )\r
393 {\r
394         return ( timerWindow || window ).setTimeout(\r
395                 function()\r
396                 {\r
397                         if ( paramsArray )\r
398                                 func.apply( thisObject, [].concat( paramsArray ) ) ;\r
399                         else\r
400                                 func.apply( thisObject ) ;\r
401                 },\r
402                 milliseconds ) ;\r
403 }\r
404 \r
405 FCKTools.SetInterval = function( func, milliseconds, thisObject, paramsArray, timerWindow )\r
406 {\r
407         return ( timerWindow || window ).setInterval(\r
408                 function()\r
409                 {\r
410                         func.apply( thisObject, paramsArray || [] ) ;\r
411                 },\r
412                 milliseconds ) ;\r
413 }\r
414 \r
415 FCKTools.ConvertStyleSizeToHtml = function( size )\r
416 {\r
417         return size.EndsWith( '%' ) ? size : parseInt( size, 10 ) ;\r
418 }\r
419 \r
420 FCKTools.ConvertHtmlSizeToStyle = function( size )\r
421 {\r
422         return size.EndsWith( '%' ) ? size : ( size + 'px' ) ;\r
423 }\r
424 \r
425 // START iCM MODIFICATIONS\r
426 // Amended to accept a list of one or more ascensor tag names\r
427 // Amended to check the element itself before working back up through the parent hierarchy\r
428 FCKTools.GetElementAscensor = function( element, ascensorTagNames )\r
429 {\r
430 //      var e = element.parentNode ;\r
431         var e = element ;\r
432         var lstTags = "," + ascensorTagNames.toUpperCase() + "," ;\r
433 \r
434         while ( e )\r
435         {\r
436                 if ( lstTags.indexOf( "," + e.nodeName.toUpperCase() + "," ) != -1 )\r
437                         return e ;\r
438 \r
439                 e = e.parentNode ;\r
440         }\r
441         return null ;\r
442 }\r
443 // END iCM MODIFICATIONS\r
444 \r
445 FCKTools.CreateEventListener = function( func, params )\r
446 {\r
447         var f = function()\r
448         {\r
449                 var aAllParams = [] ;\r
450 \r
451                 for ( var i = 0 ; i < arguments.length ; i++ )\r
452                         aAllParams.push( arguments[i] ) ;\r
453 \r
454                 func.apply( this, aAllParams.concat( params ) ) ;\r
455         }\r
456 \r
457         return f ;\r
458 }\r
459 \r
460 FCKTools.IsStrictMode = function( document )\r
461 {\r
462         // There is no compatMode in Safari, but it seams that it always behave as\r
463         // CSS1Compat, so let's assume it as the default for that browser.\r
464         return ( 'CSS1Compat' == ( document.compatMode || ( FCKBrowserInfo.IsSafari ? 'CSS1Compat' : null ) ) ) ;\r
465 }\r
466 \r
467 // Transforms a "arguments" object to an array.\r
468 FCKTools.ArgumentsToArray = function( args, startIndex, maxLength )\r
469 {\r
470         startIndex = startIndex || 0 ;\r
471         maxLength = maxLength || args.length ;\r
472 \r
473         var argsArray = new Array() ;\r
474 \r
475         for ( var i = startIndex ; i < startIndex + maxLength && i < args.length ; i++ )\r
476                 argsArray.push( args[i] ) ;\r
477 \r
478         return argsArray ;\r
479 }\r
480 \r
481 FCKTools.CloneObject = function( sourceObject )\r
482 {\r
483         var fCloneCreator = function() {} ;\r
484         fCloneCreator.prototype = sourceObject ;\r
485         return new fCloneCreator ;\r
486 }\r
487 \r
488 // Appends a bogus <br> at the end of the element, if not yet available.\r
489 FCKTools.AppendBogusBr = function( element )\r
490 {\r
491         if ( !element )\r
492                 return ;\r
493 \r
494         var eLastChild = this.GetLastItem( element.getElementsByTagName('br') ) ;\r
495 \r
496         if ( !eLastChild || ( eLastChild.getAttribute( 'type', 2 ) != '_moz' && eLastChild.getAttribute( '_moz_dirty' ) == null ) )\r
497         {\r
498                 var doc = this.GetElementDocument( element ) ;\r
499 \r
500                 if ( FCKBrowserInfo.IsOpera )\r
501                         element.appendChild( doc.createTextNode('') ) ;\r
502                 else\r
503                         element.appendChild( this.CreateBogusBR( doc ) ) ;\r
504         }\r
505 }\r
506 \r
507 FCKTools.GetLastItem = function( list )\r
508 {\r
509         if ( list.length > 0 )\r
510                 return list[ list.length - 1 ] ;\r
511 \r
512         return null ;\r
513 }\r
514 \r
515 FCKTools.GetDocumentPosition = function( w, node )\r
516 {\r
517         var x = 0 ;\r
518         var y = 0 ;\r
519         var curNode = node ;\r
520         var prevNode = null ;\r
521         var curWindow = FCKTools.GetElementWindow( curNode ) ;\r
522         while ( curNode && !( curWindow == w && ( curNode == w.document.body || curNode == w.document.documentElement ) ) )\r
523         {\r
524                 x += curNode.offsetLeft - curNode.scrollLeft ;\r
525                 y += curNode.offsetTop - curNode.scrollTop ;\r
526 \r
527                 if ( ! FCKBrowserInfo.IsOpera )\r
528                 {\r
529                         var scrollNode = prevNode ;\r
530                         while ( scrollNode && scrollNode != curNode )\r
531                         {\r
532                                 x -= scrollNode.scrollLeft ;\r
533                                 y -= scrollNode.scrollTop ;\r
534                                 scrollNode = scrollNode.parentNode ;\r
535                         }\r
536                 }\r
537 \r
538                 prevNode = curNode ;\r
539                 if ( curNode.offsetParent )\r
540                         curNode = curNode.offsetParent ;\r
541                 else\r
542                 {\r
543                         if ( curWindow != w )\r
544                         {\r
545                                 curNode = curWindow.frameElement ;\r
546                                 prevNode = null ;\r
547                                 if ( curNode )\r
548                                         curWindow = curNode.contentWindow.parent ;\r
549                         }\r
550                         else\r
551                                 curNode = null ;\r
552                 }\r
553         }\r
554 \r
555         // document.body is a special case when it comes to offsetTop and offsetLeft values.\r
556         // 1. It matters if document.body itself is a positioned element;\r
557         // 2. It matters is when we're in IE and the element has no positioned ancestor.\r
558         // Otherwise the values should be ignored.\r
559         if ( FCKDomTools.GetCurrentElementStyle( w.document.body, 'position') != 'static'\r
560                         || ( FCKBrowserInfo.IsIE && FCKDomTools.GetPositionedAncestor( node ) == null ) )\r
561         {\r
562                 x += w.document.body.offsetLeft ;\r
563                 y += w.document.body.offsetTop ;\r
564         }\r
565 \r
566         return { "x" : x, "y" : y } ;\r
567 }\r
568 \r
569 FCKTools.GetWindowPosition = function( w, node )\r
570 {\r
571         var pos = this.GetDocumentPosition( w, node ) ;\r
572         var scroll = FCKTools.GetScrollPosition( w ) ;\r
573         pos.x -= scroll.X ;\r
574         pos.y -= scroll.Y ;\r
575         return pos ;\r
576 }\r
577 \r
578 FCKTools.ProtectFormStyles = function( formNode )\r
579 {\r
580         if ( !formNode || formNode.nodeType != 1 || formNode.tagName.toLowerCase() != 'form' )\r
581                 return [] ;\r
582         var hijackRecord = [] ;\r
583         var hijackNames = [ 'style', 'className' ] ;\r
584         for ( var i = 0 ; i < hijackNames.length ; i++ )\r
585         {\r
586                 var name = hijackNames[i] ;\r
587                 if ( formNode.elements.namedItem( name ) )\r
588                 {\r
589                         var hijackNode = formNode.elements.namedItem( name ) ;\r
590                         hijackRecord.push( [ hijackNode, hijackNode.nextSibling ] ) ;\r
591                         formNode.removeChild( hijackNode ) ;\r
592                 }\r
593         }\r
594         return hijackRecord ;\r
595 }\r
596 \r
597 FCKTools.RestoreFormStyles = function( formNode, hijackRecord )\r
598 {\r
599         if ( !formNode || formNode.nodeType != 1 || formNode.tagName.toLowerCase() != 'form' )\r
600                 return ;\r
601         if ( hijackRecord.length > 0 )\r
602         {\r
603                 for ( var i = hijackRecord.length - 1 ; i >= 0 ; i-- )\r
604                 {\r
605                         var node = hijackRecord[i][0] ;\r
606                         var sibling = hijackRecord[i][1] ;\r
607                         if ( sibling )\r
608                                 formNode.insertBefore( node, sibling ) ;\r
609                         else\r
610                                 formNode.appendChild( node ) ;\r
611                 }\r
612         }\r
613 }\r
614 \r
615 // Perform a one-step DFS walk.\r
616 FCKTools.GetNextNode = function( node, limitNode )\r
617 {\r
618         if ( node.firstChild )\r
619                 return node.firstChild ;\r
620         else if ( node.nextSibling )\r
621                 return node.nextSibling ;\r
622         else\r
623         {\r
624                 var ancestor = node.parentNode ;\r
625                 while ( ancestor )\r
626                 {\r
627                         if ( ancestor == limitNode )\r
628                                 return null ;\r
629                         if ( ancestor.nextSibling )\r
630                                 return ancestor.nextSibling ;\r
631                         else\r
632                                 ancestor = ancestor.parentNode ;\r
633                 }\r
634         }\r
635         return null ;\r
636 }\r
637 \r
638 FCKTools.GetNextTextNode = function( textnode, limitNode, checkStop )\r
639 {\r
640         node = this.GetNextNode( textnode, limitNode ) ;\r
641         if ( checkStop && node && checkStop( node ) )\r
642                 return null ;\r
643         while ( node && node.nodeType != 3 )\r
644         {\r
645                 node = this.GetNextNode( node, limitNode ) ;\r
646                 if ( checkStop && node && checkStop( node ) )\r
647                         return null ;\r
648         }\r
649         return node ;\r
650 }\r
651 \r
652 /**\r
653  * Merge all objects passed by argument into a single object.\r
654  */\r
655 FCKTools.Merge = function()\r
656 {\r
657         var args = arguments ;\r
658         var o = args[0] ;\r
659 \r
660         for ( var i = 1 ; i < args.length ; i++ )\r
661         {\r
662                 var arg = args[i] ;\r
663                 for ( var p in arg )\r
664                         o[p] = arg[p] ;\r
665         }\r
666 \r
667         return o ;\r
668 }\r
669 \r
670 /**\r
671  * Check if the passed argument is a real Array. It may not working when\r
672  * calling it cross windows.\r
673  */\r
674 FCKTools.IsArray = function( it )\r
675 {\r
676         return ( it instanceof Array ) ;\r
677 }\r
678 \r
679 /**\r
680  * Appends a "length" property to an object, containing the number of\r
681  * properties available on it, excluded the append property itself.\r
682  */\r
683 FCKTools.AppendLengthProperty = function( targetObject, propertyName )\r
684 {\r
685         var counter = 0 ;\r
686 \r
687         for ( var n in targetObject )\r
688                 counter++ ;\r
689 \r
690         return targetObject[ propertyName || 'length' ] = counter ;\r
691 }\r
692 \r
693 /**\r
694  * Gets the browser parsed version of a css text (style attribute value). On\r
695  * some cases, the browser makes changes to the css text, returning a different\r
696  * value. For example, hexadecimal colors get transformed to rgb().\r
697  */\r
698 FCKTools.NormalizeCssText = function( unparsedCssText )\r
699 {\r
700         // Injects the style in a temporary span object, so the browser parses it,\r
701         // retrieving its final format.\r
702         var tempSpan = document.createElement( 'span' ) ;\r
703         tempSpan.style.cssText = unparsedCssText ;\r
704         return tempSpan.style.cssText ;\r
705 }\r
706 \r
707 /**\r
708  * Binding the "this" reference to an object for a function.\r
709  */\r
710 FCKTools.Bind = function( subject, func )\r
711 {\r
712   return function(){ return func.apply( subject, arguments ) ; } ;\r
713 }\r
714 \r
715 /**\r
716  * Retrieve the correct "empty iframe" URL for the current browser, which\r
717  * causes the minimum fuzz (e.g. security warnings in HTTPS, DNS error in\r
718  * IE5.5, etc.) for that browser, making the iframe ready to DOM use whithout\r
719  * having to loading an external file.\r
720  */\r
721 FCKTools.GetVoidUrl = function()\r
722 {\r
723         if ( FCK_IS_CUSTOM_DOMAIN )\r
724                 return "javascript: void( function(){" +\r
725                         "document.open();" +\r
726                         "document.write('<html><head><title></title></head><body></body></html>');" +\r
727                         "document.domain = '" + FCK_RUNTIME_DOMAIN + "';" +\r
728                         "document.close();" +\r
729                         "}() ) ;";\r
730 \r
731         if ( FCKBrowserInfo.IsIE )\r
732         {\r
733                 if ( FCKBrowserInfo.IsIE7 || !FCKBrowserInfo.IsIE6 )\r
734                         return "" ;                                     // IE7+ / IE5.5\r
735                 else\r
736                         return "javascript: '';" ;      // IE6+\r
737         }\r
738 \r
739         return "javascript: void(0);" ;         // All other browsers.\r
740 }\r
741 \r
742 FCKTools.ResetStyles = function( element )\r
743 {\r
744         element.style.cssText = 'margin:0;' +\r
745                 'padding:0;' +\r
746                 'border:0;' +\r
747                 'background-color:transparent;' +\r
748                 'background-image:none;' ;\r
749 }\r