程序员人生 网站导航

FCKeditor源码分析(一):fckeditor.js中文注释

栏目:jscript时间:2014-03-30 18:23:13

  这几天都在研究FCKeditor的源代码( 什么是FCKeditor?)几乎搜遍了Internet,似乎对于fckconfig.js这个文件讲解的很多,但对于fckeditor.js这个FCK的核心类文件的资料几乎为0. 所以,花了整整一天的时间,以挤牙膏的方式,对fckeditor.js这个fck核心类文件作了自己力所能及的注释,供同样学习fck的网友一个参考。 注:本文基于FCKeditor2.6.5

1. /**
2. *
3. * ***********CopyRight**************
4. *-------Annotated by nileader-----
5. *-----Version 1.00 2009-10-18-----
6. *-----Once copied, marked http://www.nileader.cn
7. *
8. * FCKeditor 类 annotated by nileader
9. * @param {Object} instanceName 编辑器的唯一名称(相当于ID) 是不可省参数,
10. * width,height,toolbarset,value 都是 可选参数
11. */
12. var FCKeditor = function( instanceName, width, height, toolbarSet, value )
13. {
14. //编辑器的基本属性 注意:这些东西优先于FCKConfig.js中的配置
15.
16. this.InstanceName = instanceName ; //编辑器的唯一名称(相当于ID)(必须有!)
17. this.Width = width || '100%' ; //宽度 默认是100%
18. this.Height = height || '200' ; //宽度 默认是200
19. this.ToolbarSet = toolbarSet || 'Default' ;//工具集名称,默认值是Default
20. this.Value = value || '' ; //初始化编辑器的HTML代码,默认值为空
21. //编辑器初始化的时候默认的根路径, 其作用是编写fck中,凡是用到的路径,均从FCKeditor.BasePath目录开始 默认为/Fckeditor/
22. this.BasePath = FCKeditor.BasePath ;
23. this.CheckBrowser = true ; //是否在显示编辑器前检查浏览器兼容性,默认为true
24. this.DisplayErrors = true ; //是否显示提示错误,默为true
25. this.Config = new Object() ;
26. // Events
27. this.OnError = null ; // function( source, errorNumber, errorDescription )自定义的错误处理函数
28. }
29. FCKeditor.BasePath = '/fckeditor/' ; // fck默认的根目录
30. FCKeditor.MinHeight = 200 ; //高和宽的限制
31. FCKeditor.MinWidth = 750 ;
32. FCKeditor.prototype.Version = '2.6.5' ; //版本号
33. FCKeditor.prototype.VersionBuild = '23959' ;
34. /**
35. * 调用CreateHtml()来生成编辑器的html代码并在页面上输出编辑器
36. */
37. FCKeditor.prototype.Create = function()
38. {
39. //调用createhtml()方法
40. document.write( this.CreateHtml() ) ;
41. }
42. /**
43. * @return sHtml 用于生成编辑器的html代码
44. */
45. FCKeditor.prototype.CreateHtml = function()
46. {
47. // 检查有无InstanceName 如果没有则不生成html代码
48. if ( !this.InstanceName || this.InstanceName.length == 0 )
49. {
50. this._ThrowError( 701, 'You must specify an instance name.' ) ;
51. return '' ;
52. }
53. //函数的返回值
54. var sHtml = '' ;
55. /*
56. * 当用户的浏览器符合预设的几种浏览器时,
57. * 生成一个id="this.instancename" name="this.instancename"的文本框,事实上的内容储存器
58. */
59. if ( !this.CheckBrowser || this._IsCompatibleBrowser() )
60. {
61. //将此时FCK初始值通过转义之后放入这个input
62. sHtml += '<input type="hidden" id="' + this.InstanceName + '" name="' + this.InstanceName + '" value="' + this._HTMLEncode( this.Value ) + '" style="display:none" mce_style="display:none" />' ;
63. //生成一个隐藏的INPUT来放置this.config中的内容
64. sHtml += this._GetConfigHtml() ;
65. //生成编辑器的iframe的代码
66. sHtml += this._GetIFrameHtml() ;
67. }
68. /**
69. * 如果用户的浏览器不兼容FCK默认的几种浏览器
70. * 只能有传统的textarea了
71. */
72. else
73. {
74. var sWidth = this.Width.toString().indexOf('%') > 0 ? this.Width : this.Width + 'px' ;
75. var sHeight = this.Height.toString().indexOf('%') > 0 ? this.Height : this.Height + 'px' ;
76. sHtml += '<textarea name="' + this.InstanceName +
77. '" rows="4" cols="40" style="width:' + sWidth +
78. ';height:' + sHeight ;
79. if ( this.TabIndex )
80. sHtml += '" tabindex="' + this.TabIndex ;
81. sHtml += '">' +
82. this._HTMLEncode( this.Value ) +
83. '</textarea>' ;
84. }
85. return sHtml ;
86. }
87. /**
88. * 用编辑器来替换对应的文本框
89. */
90. FCKeditor.prototype.ReplaceTextarea = function()
91. {
92. //如果已经有了 id=THIS.INSTANCENAME___Frame 的标签时,直接返回
93. if ( document.getElementById( this.InstanceName + '___Frame' ) )
94. return ;
95. //当用户的浏览器符合预设的几种浏览器时
96. if ( !this.CheckBrowser || this._IsCompatibleBrowser() )
97. {
98. // We must check the elements firstly using the Id and then the name.
99. //获取id=this.InstanceName的html标签
100. var oTextarea = document.getElementById( this.InstanceName ) ;
101. //获取所有name=THIS.instancename的标签
102. var colElementsByName = document.getElementsByName( this.InstanceName ) ;
103. var i = 0;
104. /*
105. * 考虑到用户html标签的命名不规范,所以进行以下编历判断 笔者指的是用户在textarea标签处用了name=this.instancename
106. * 在同个页面的其它标签上也用了name=this.instancename
107. */
108. while ( oTextarea || i == 0 )
109. {
110. //遍历,直到找到name=this.instancename的textarea标签,并赋给oTextarea
111. if ( oTextarea && oTextarea.tagName.toLowerCase() == 'textarea' )
112. break ;
113. oTextarea = colElementsByName[i++] ;
114. }
115. //如果不存在id或者name为this.instancename的标签时,弹出错误框
116. if ( !oTextarea )
117. {
118. alert( 'Error: The TEXTAREA with id or name set to "' + this.InstanceName + '" was not found' ) ;
119. return ;
120. }
121. /*
122. * 确定存在name=this.instancename的textarea标签后,将编辑器的代码赋给它
123. */
124. oTextarea.style.display = 'none' ;
125. //如果页面上对这样的textarea标签定义了tab键的顺序,赋给this.TabIndex待用
126. if ( oTextarea.tabIndex )
127. this.TabIndex = oTextarea.tabIndex ;
128. this._InsertHtmlBefore( this._GetConfigHtml(), oTextarea ) ;
129. this._InsertHtmlBefore( this._GetIFrameHtml(), oTextarea ) ;
130. }
131. }
132.
133.
134.
135. /**
136. * 在指定的页面标签前面插入html代码
137. * @param {Object} 待插入的html代码
138. * @param {Object} 指定的页面标签(对象)
139. */
140. FCKeditor.prototype._InsertHtmlBefore = function( html, element )
141. {
142. if ( element.insertAdjacentHTML ) // IE 私有的 insertAdjacentHTML 方法
143. element.insertAdjacentHTML( 'beforeBegin', html ) ;
144. else // 非ie浏览器
145. {
146.
147. var oRange = document.createRange() ;
148. oRange.setStartBefore( element ) ;
149. var oFragment = oRange.createContextualFragment( html );
150. element.parentNode.insertBefore( oFragment, element ) ;
151. }
152. }
153.
154.
155. /*
156. * 通过编历this.Config[]来生成一个隐藏域,
157. * 例如:
158. * this.Config['nileader']="1104",this.Config['leaderni']="nichao"……
159. * 那么,sConfig=…… &nileader=1104&leaderni=nichao ……
160. * 当然,最终,sConfig会被encodeURIComponent函数转换成百分比编码 放入隐藏的INPUT中去
161. */
162. FCKeditor.prototype._GetConfigHtml = function()
163. {
164. var sConfig = '' ;
165. for ( var o in this.Config )
166. {
167. if ( sConfig.length > 0 ) sConfig += '&' ;
168. //encodeURIComponent函数转换成百分比编码
169. sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.Config[o] ) ;
170. }
171. return '<input type="hidden" id="' + this.InstanceName + '___Config" value="' + sConfig + '" style="display:none" mce_style="display:none" />' ;
172. }
173.
174. /*
175. * 生成iframe的html 这里涉及到src的确定
176. */
177. FCKeditor.prototype._GetIFrameHtml = function()
178. {
179. var sFile = 'fckeditor.html' ;
180. //特殊情况 fckedito所在的窗口没有嵌入在浏览器中
181. try
182. {
183. if ( (/fcksource=true/i).test( window.top.location.search ) )
184. sFile = 'fckeditor.original.html' ;
185. }
186. catch (e) { /* 忽略这个异常. 很多时候,fckedito所在的窗口嵌入在浏览器中. */ }
187. /*
188. * 这里注意的一点:
189. * iframe的工作原理: 当iframe处于可编辑状态时,其实编辑的是src所在的页面
190. * 这里合成一个sLink以放入iframe标签中
191. */
192. //sLink就是这个事实上的页面了,从fck的根目录开始,例如 sLink=/fckeditor/editor/fckeditor.html?InstanceName=nileader&Toolbar=nileadersbar
193. var sLink = this.BasePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.InstanceName ) ;
194. if (this.ToolbarSet)
195. sLink += '&Toolbar=' + this.ToolbarSet ;
196. //生成一个真正的编辑iframer的html代码 当然,放入了src=slink title="liehuo.net"
197. var html = '<iframe id="' + this.InstanceName +
198. '___Frame" src="' + sLink +
199. '" mce_src="' + sLink +
200. '" width="' + this.Width +
201. '" height="' + this.Height ;
202. //如果设定了使用"Tab"键的遍历顺序,则赋给iframe
203. if ( this.TabIndex )
204. html += '" tabindex="' + this.TabIndex ;
205. html += '" frameborder="0" scrolling="no"></iframe>' ;
206. return html ;
207. }
208.
209. /*
210. * 检测用户的bowser是否是fck的默认
211. * 这个方法只是fck公司追求oo,无意义
212. */
213. FCKeditor.prototype._IsCompatibleBrowser = function()
214. {
215. return FCKeditor_IsCompatibleBrowser() ;
216. }
217.
218. /**
219. * 抛出错误
220. * @param {Object} errorNumber 错误编号
221. * @param {Object} errorDescription 错误概述
222. */
223. FCKeditor.prototype._ThrowError = function( errorNumber, errorDescription )
224. {
225. this.ErrorNumber = errorNumber ;
226. this.ErrorDescription = errorDescription ;
227. //是否显示提示错误,默为true
228. if ( this.DisplayErrors )
229. { //将错误编号和错误概述打印出来
230. document.write( '<div style="COLOR: #ff0000" mce_style="COLOR: #ff0000">' ) ;
231. document.write( '[ FCKeditor Error ' + this.ErrorNumber + ': ' + this.ErrorDescription + ' ]' ) ;
232. document.write( '</div>' ) ;
233. }
234. //OnError是否自定义了错误处理函数,若定义了,由其处理
235. if ( typeof( this.OnError ) == 'function' )
236. this.OnError( this, errorNumber, errorDescription ) ;
237. }
238.
239. /**
240. * 转义文本
241. * @param {Object} text 待转义的文本
242. * @return String text 转义完后的文本
243. */
244. FCKeditor.prototype._HTMLEncode = function( text )
245. {
246. if ( typeof( text ) != "string" )
247. text = text.toString() ;
248. //将字符串中的所有 & " < > 用对应的转义字符代换
249. text = text.replace(
250. /&/g, "&").replace(
251. /"/g, """).replace(
252. /</g, "<").replace(
253. />/g, ">") ;
254. return text ;
255. }
256.
257. ;(function()
258. {
259. //把页面上的textarea元素赋给editor变量
260. var textareaToEditor = function( textarea )
261. {
262. var editor = new FCKeditor( textarea.name ) ;
263. editor.Width = Math.max( textarea.offsetWidth, FCKeditor.MinWidth ) ;
264. editor.Height = Math.max( textarea.offsetHeight, FCKeditor.MinHeight ) ;
265. return editor ;
266. }
267. /**
268. * Replace all <textarea> elements available in the document with FCKeditor
269. * instances.
270. *
271. * // Replace all <textarea> elements in the page.
272. * FCKeditor.ReplaceAllTextareas() ;
273. *
274. * // Replace all <textarea class="myClassName"> elements in the page.
275. * FCKeditor.ReplaceAllTextareas( 'myClassName' ) ;
276. *
277. * // Selectively replace <textarea> elements, based on custom assertions.
278. * FCKeditor.ReplaceAllTextareas( function( textarea, editor )
279. * {
280. * // Custom code to evaluate the replace, returning false if it
281. * // must not be done.
282. * // It also passes the "editor" parameter, so the developer can
283. * // customize the instance.
284. * } ) ;
285. */
286. FCKeditor.ReplaceAllTextareas = function()
287. {
288. //获取所有的textarea元素
289. var textareas = document.getElementsByTagName( 'textarea' ) ;
290.
291. for ( var i = 0 ; i < textareas.length ; i++ )
292. {
293. var editor = null ;
294. var textarea = textareas[i] ;
295. var name = textarea.name ;
296. // The "name" attribute must exist.
297. if ( !name || name.length == 0 )
298. continue ;
299. if ( typeof arguments[0] == 'string' )
300. {
301. // The textarea class name could be passed as the function
302. // parameter.
303. var classRegex = new RegExp( '(?:^| )' + arguments[0] + '(?:$| )' ) ;
304. if ( !classRegex.test( textarea.className ) )
305. continue ;
306. }
307. else if ( typeof arguments[0] == 'function' )
308. {
309. // An assertion function could be passed as the function parameter.
310. // It must explicitly return "false" to ignore a specific <textarea>.
311. editor = textareaToEditor( textarea ) ;
312. if ( arguments[0]( textarea, editor ) === false )
313. continue ;
314. }
315. if ( !editor )
316. editor = textareaToEditor( textarea ) ;
317. editor.ReplaceTextarea() ;
318. }
319. }
320. })() ;
321.
322. /**
323. * 检测浏览器的兼容性
324. * 利用了navigator对象返回的一些信息sAgent,判断浏览器 返回包括 浏览器的码名 浏览器名 浏览器版本 语言 等信息 并小写
325. * 例如:
326. * mozilla/4.0 (compatible; msie 6.0; windows nt 5.2; sv1; .net clr 1.1.4322)
327. *
328. * 判断IE浏览器的时候,运用了IE4.0之后支持的增加了对条件编译,
329. * 由于只是IE支持,在W3C标准浏览器中,该属性是不被支持的。因此,适当的利用该特性,判断IE
330. */
331. function FCKeditor_IsCompatibleBrowser()
332. {
333. var sAgent = navigator.userAgent.toLowerCase() ;
334. // 当前浏览器是Internet Explorer 5.5+
335. //利用条件编译判断IE 在IE中,/*@cc_on!@*/false == !false == true,
336. //如果是非IE浏览器,则忽略,/*@cc_on!@*/false == false
337. if ( /*@cc_on!@*/false && sAgent.indexOf("mac") == -1 ) //不是apple mac os
338. {
339. var sBrowserVersion = navigator.appVersion.match(/MSIE (...)/)[1] ;
340. return ( sBrowserVersion >= 5.5 ) ;
341. }
342. // Gecko (Opera 9 tries to behave like Gecko at this point).
343. //检测是否是OPERA 9 浏览器
344. if ( navigator.product == "Gecko" && navigator.productSub >= 20030210 && !( typeof(opera) == 'object' && opera.postError ) )
345. return true ;
346. // Opera 9.50+
347. if ( window.opera && window.opera.version && parseFloat( window.opera.version() ) >= 9.5 )
348. return true ;
349. // Adobe AIR
350. // Checked before Safari because AIR have the WebKit rich text editor
351. // features from Safari 3.0.4, but the version reported is 420.
352. if ( sAgent.indexOf( ' adobeair/' ) != -1 )
353. return ( sAgent.match( / adobeair/(d+)/ )[1] >= 1 ) ; // Build must be at least v1
354. // Safari 3+
355. if ( sAgent.indexOf( ' applewebkit/' ) != -1 )
356. return ( sAgent.match( / applewebkit/(d+)/ )[1] >= 522 ) ; // Build must be at least 522 (v3)
357. return false ;
358. }

   鉴于笔者水平有限,在此,请广大高手指出我的注释中不妥之处,以免误导他人 。谢谢。建议copy到自己的IDE中查看 或者

  更多权威资料,请参见 FCK 官方Developers Guide

  转自:http://www.cnblogs.com/nileaderblog/

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐