FCKeditor 2.6.6
[freeside.git] / httemplate / elements / fckeditor / fckeditor.js
1 /*\r
2  * FCKeditor - The text editor for Internet - http://www.fckeditor.net\r
3  * Copyright (C) 2003-2010 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  * This is the integration file for JavaScript.\r
22  *\r
23  * It defines the FCKeditor class that can be used to create editor\r
24  * instances in a HTML page in the client side. For server side\r
25  * operations, use the specific integration system.\r
26  */\r
27 \r
28 // FCKeditor Class\r
29 var FCKeditor = function( instanceName, width, height, toolbarSet, value )\r
30 {\r
31         // Properties\r
32         this.InstanceName       = instanceName ;\r
33         this.Width                      = width                 || '100%' ;\r
34         this.Height                     = height                || '200' ;\r
35         this.ToolbarSet         = toolbarSet    || 'Default' ;\r
36         this.Value                      = value                 || '' ;\r
37         this.BasePath           = FCKeditor.BasePath ;\r
38         this.CheckBrowser       = true ;\r
39         this.DisplayErrors      = true ;\r
40 \r
41         this.Config                     = new Object() ;\r
42 \r
43         // Events\r
44         this.OnError            = null ;        // function( source, errorNumber, errorDescription )\r
45 }\r
46 \r
47 /**\r
48  * This is the default BasePath used by all editor instances.\r
49  */\r
50 FCKeditor.BasePath = '/fckeditor/' ;\r
51 \r
52 /**\r
53  * The minimum height used when replacing textareas.\r
54  */\r
55 FCKeditor.MinHeight = 200 ;\r
56 \r
57 /**\r
58  * The minimum width used when replacing textareas.\r
59  */\r
60 FCKeditor.MinWidth = 750 ;\r
61 \r
62 FCKeditor.prototype.Version                     = '2.6.6' ;\r
63 FCKeditor.prototype.VersionBuild        = '25427' ;\r
64 \r
65 FCKeditor.prototype.Create = function()\r
66 {\r
67         document.write( this.CreateHtml() ) ;\r
68 }\r
69 \r
70 FCKeditor.prototype.CreateHtml = function()\r
71 {\r
72         // Check for errors\r
73         if ( !this.InstanceName || this.InstanceName.length == 0 )\r
74         {\r
75                 this._ThrowError( 701, 'You must specify an instance name.' ) ;\r
76                 return '' ;\r
77         }\r
78 \r
79         var sHtml = '' ;\r
80 \r
81         if ( !this.CheckBrowser || this._IsCompatibleBrowser() )\r
82         {\r
83                 sHtml += '<input type="hidden" id="' + this.InstanceName + '" name="' + this.InstanceName + '" value="' + this._HTMLEncode( this.Value ) + '" style="display:none" />' ;\r
84                 sHtml += this._GetConfigHtml() ;\r
85                 sHtml += this._GetIFrameHtml() ;\r
86         }\r
87         else\r
88         {\r
89                 var sWidth  = this.Width.toString().indexOf('%')  > 0 ? this.Width  : this.Width  + 'px' ;\r
90                 var sHeight = this.Height.toString().indexOf('%') > 0 ? this.Height : this.Height + 'px' ;\r
91 \r
92                 sHtml += '<textarea name="' + this.InstanceName +\r
93                         '" rows="4" cols="40" style="width:' + sWidth +\r
94                         ';height:' + sHeight ;\r
95 \r
96                 if ( this.TabIndex )\r
97                         sHtml += '" tabindex="' + this.TabIndex ;\r
98 \r
99                 sHtml += '">' +\r
100                         this._HTMLEncode( this.Value ) +\r
101                         '<\/textarea>' ;\r
102         }\r
103 \r
104         return sHtml ;\r
105 }\r
106 \r
107 FCKeditor.prototype.ReplaceTextarea = function()\r
108 {\r
109         if ( document.getElementById( this.InstanceName + '___Frame' ) )\r
110                 return ;\r
111         if ( !this.CheckBrowser || this._IsCompatibleBrowser() )\r
112         {\r
113                 // We must check the elements firstly using the Id and then the name.\r
114                 var oTextarea = document.getElementById( this.InstanceName ) ;\r
115                 var colElementsByName = document.getElementsByName( this.InstanceName ) ;\r
116                 var i = 0;\r
117                 while ( oTextarea || i == 0 )\r
118                 {\r
119                         if ( oTextarea && oTextarea.tagName.toLowerCase() == 'textarea' )\r
120                                 break ;\r
121                         oTextarea = colElementsByName[i++] ;\r
122                 }\r
123 \r
124                 if ( !oTextarea )\r
125                 {\r
126                         alert( 'Error: The TEXTAREA with id or name set to "' + this.InstanceName + '" was not found' ) ;\r
127                         return ;\r
128                 }\r
129 \r
130                 oTextarea.style.display = 'none' ;\r
131 \r
132                 if ( oTextarea.tabIndex )\r
133                         this.TabIndex = oTextarea.tabIndex ;\r
134 \r
135                 this._InsertHtmlBefore( this._GetConfigHtml(), oTextarea ) ;\r
136                 this._InsertHtmlBefore( this._GetIFrameHtml(), oTextarea ) ;\r
137         }\r
138 }\r
139 \r
140 FCKeditor.prototype._InsertHtmlBefore = function( html, element )\r
141 {\r
142         if ( element.insertAdjacentHTML )       // IE\r
143                 element.insertAdjacentHTML( 'beforeBegin', html ) ;\r
144         else                                                            // Gecko\r
145         {\r
146                 var oRange = document.createRange() ;\r
147                 oRange.setStartBefore( element ) ;\r
148                 var oFragment = oRange.createContextualFragment( html );\r
149                 element.parentNode.insertBefore( oFragment, element ) ;\r
150         }\r
151 }\r
152 \r
153 FCKeditor.prototype._GetConfigHtml = function()\r
154 {\r
155         var sConfig = '' ;\r
156         for ( var o in this.Config )\r
157         {\r
158                 if ( sConfig.length > 0 ) sConfig += '&amp;' ;\r
159                 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.Config[o] ) ;\r
160         }\r
161 \r
162         return '<input type="hidden" id="' + this.InstanceName + '___Config" value="' + sConfig + '" style="display:none" />' ;\r
163 }\r
164 \r
165 FCKeditor.prototype._GetIFrameHtml = function()\r
166 {\r
167         var sFile = 'fckeditor.html' ;\r
168 \r
169         try\r
170         {\r
171                 if ( (/fcksource=true/i).test( window.top.location.search ) )\r
172                         sFile = 'fckeditor.original.html' ;\r
173         }\r
174         catch (e) { /* Ignore it. Much probably we are inside a FRAME where the "top" is in another domain (security error). */ }\r
175 \r
176         var sLink = this.BasePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.InstanceName ) ;\r
177         if (this.ToolbarSet)\r
178                 sLink += '&amp;Toolbar=' + this.ToolbarSet ;\r
179 \r
180         var html = '<iframe id="' + this.InstanceName +\r
181                 '___Frame" src="' + sLink +\r
182                 '" width="' + this.Width +\r
183                 '" height="' + this.Height ;\r
184 \r
185         if ( this.TabIndex )\r
186                 html += '" tabindex="' + this.TabIndex ;\r
187 \r
188         html += '" frameborder="0" scrolling="no"></iframe>' ;\r
189 \r
190         return html ;\r
191 }\r
192 \r
193 FCKeditor.prototype._IsCompatibleBrowser = function()\r
194 {\r
195         return FCKeditor_IsCompatibleBrowser() ;\r
196 }\r
197 \r
198 FCKeditor.prototype._ThrowError = function( errorNumber, errorDescription )\r
199 {\r
200         this.ErrorNumber                = errorNumber ;\r
201         this.ErrorDescription   = errorDescription ;\r
202 \r
203         if ( this.DisplayErrors )\r
204         {\r
205                 document.write( '<div style="COLOR: #ff0000">' ) ;\r
206                 document.write( '[ FCKeditor Error ' + this.ErrorNumber + ': ' + this.ErrorDescription + ' ]' ) ;\r
207                 document.write( '</div>' ) ;\r
208         }\r
209 \r
210         if ( typeof( this.OnError ) == 'function' )\r
211                 this.OnError( this, errorNumber, errorDescription ) ;\r
212 }\r
213 \r
214 FCKeditor.prototype._HTMLEncode = function( text )\r
215 {\r
216         if ( typeof( text ) != "string" )\r
217                 text = text.toString() ;\r
218 \r
219         text = text.replace(\r
220                 /&/g, "&amp;").replace(\r
221                 /"/g, "&quot;").replace(\r
222                 /</g, "&lt;").replace(\r
223                 />/g, "&gt;") ;\r
224 \r
225         return text ;\r
226 }\r
227 \r
228 ;(function()\r
229 {\r
230         var textareaToEditor = function( textarea )\r
231         {\r
232                 var editor = new FCKeditor( textarea.name ) ;\r
233 \r
234                 editor.Width = Math.max( textarea.offsetWidth, FCKeditor.MinWidth ) ;\r
235                 editor.Height = Math.max( textarea.offsetHeight, FCKeditor.MinHeight ) ;\r
236 \r
237                 return editor ;\r
238         }\r
239 \r
240         /**\r
241          * Replace all <textarea> elements available in the document with FCKeditor\r
242          * instances.\r
243          *\r
244          *      // Replace all <textarea> elements in the page.\r
245          *      FCKeditor.ReplaceAllTextareas() ;\r
246          *\r
247          *      // Replace all <textarea class="myClassName"> elements in the page.\r
248          *      FCKeditor.ReplaceAllTextareas( 'myClassName' ) ;\r
249          *\r
250          *      // Selectively replace <textarea> elements, based on custom assertions.\r
251          *      FCKeditor.ReplaceAllTextareas( function( textarea, editor )\r
252          *              {\r
253          *                      // Custom code to evaluate the replace, returning false if it\r
254          *                      // must not be done.\r
255          *                      // It also passes the "editor" parameter, so the developer can\r
256          *                      // customize the instance.\r
257          *              } ) ;\r
258          */\r
259         FCKeditor.ReplaceAllTextareas = function()\r
260         {\r
261                 var textareas = document.getElementsByTagName( 'textarea' ) ;\r
262 \r
263                 for ( var i = 0 ; i < textareas.length ; i++ )\r
264                 {\r
265                         var editor = null ;\r
266                         var textarea = textareas[i] ;\r
267                         var name = textarea.name ;\r
268 \r
269                         // The "name" attribute must exist.\r
270                         if ( !name || name.length == 0 )\r
271                                 continue ;\r
272 \r
273                         if ( typeof arguments[0] == 'string' )\r
274                         {\r
275                                 // The textarea class name could be passed as the function\r
276                                 // parameter.\r
277 \r
278                                 var classRegex = new RegExp( '(?:^| )' + arguments[0] + '(?:$| )' ) ;\r
279 \r
280                                 if ( !classRegex.test( textarea.className ) )\r
281                                         continue ;\r
282                         }\r
283                         else if ( typeof arguments[0] == 'function' )\r
284                         {\r
285                                 // An assertion function could be passed as the function parameter.\r
286                                 // It must explicitly return "false" to ignore a specific <textarea>.\r
287                                 editor = textareaToEditor( textarea ) ;\r
288                                 if ( arguments[0]( textarea, editor ) === false )\r
289                                         continue ;\r
290                         }\r
291 \r
292                         if ( !editor )\r
293                                 editor = textareaToEditor( textarea ) ;\r
294 \r
295                         editor.ReplaceTextarea() ;\r
296                 }\r
297         }\r
298 })() ;\r
299 \r
300 function FCKeditor_IsCompatibleBrowser()\r
301 {\r
302         var sAgent = navigator.userAgent.toLowerCase() ;\r
303 \r
304         // Internet Explorer 5.5+\r
305         if ( /*@cc_on!@*/false && sAgent.indexOf("mac") == -1 )\r
306         {\r
307                 var sBrowserVersion = navigator.appVersion.match(/MSIE (.\..)/)[1] ;\r
308                 return ( sBrowserVersion >= 5.5 ) ;\r
309         }\r
310 \r
311         // Gecko (Opera 9 tries to behave like Gecko at this point).\r
312         if ( navigator.product == "Gecko" && navigator.productSub >= 20030210 && !( typeof(opera) == 'object' && opera.postError ) )\r
313                 return true ;\r
314 \r
315         // Opera 9.50+\r
316         if ( window.opera && window.opera.version && parseFloat( window.opera.version() ) >= 9.5 )\r
317                 return true ;\r
318 \r
319         // Adobe AIR\r
320         // Checked before Safari because AIR have the WebKit rich text editor\r
321         // features from Safari 3.0.4, but the version reported is 420.\r
322         if ( sAgent.indexOf( ' adobeair/' ) != -1 )\r
323                 return ( sAgent.match( / adobeair\/(\d+)/ )[1] >= 1 ) ; // Build must be at least v1\r
324 \r
325         // Safari 3+\r
326         if ( sAgent.indexOf( ' applewebkit/' ) != -1 )\r
327                 return ( sAgent.match( / applewebkit\/(\d+)/ )[1] >= 522 ) ;    // Build must be at least 522 (v3)\r
328 \r
329         return false ;\r
330 }\r