学校实验室的一个老师说,要和一个日本公司合作做一个web在线编辑器,所以这几天都在研究FCKeditor的源代码 什么是FCKeditor? 但是几乎搜遍了Internet,似乎对于fckconfig.js这个文件讲解的很多,但对于fckeditor.js这个FCK的核心类文件的资料几乎为0.
所以,花了整整一天的时间,以挤牙膏的方式,对fckeditor.js这个fck核心类文件作了自己力所能及的注释,供同样学习fck的网友一个参考。
鉴于自己水平有限,在此,请广大午饭高手指出我的注释中不妥之处,以免误导他人 。
另外,源代码建议copy到自己的IDE中查看。注:本文基于FCKeditor2.6.5

更多权威资料,请参见FCK 官方Developers Guide附件中有这个文件。

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

本文转自 nileader 51CTO博客,原文链接:http://blog.51cto.com/nileader/301614,如需转载请自行联系原作者

FCKeditor源代码分析(一)-----fckeditor.js的中文注释分析(原创)相关推荐

  1. 每日源码分析 - Lodash(remove.js)

    本系列使用 lodash 4.17.4版本 源码分析不包括引用文件分析 一.源码 import basePullAt from './.internal/basePullAt.js'/*** Remo ...

  2. python中文情感分析分类和英文情感分析的库和方法汇总

    情感分析是自然语言处理中的一个常见任务.以下是用 Python 写的一个简单的情感分析分类函数的代码示例: import jieba import numpy as npdef sentiment_a ...

  3. 南京邮电大学C语言中文文本分析处理

    南京邮电大学C语言中文文本分析处理 程序设计题2:中文文本分析处理 1问题描述 编写一个程序,对一篇中文文章进行分析和处理. 2功能要求 要能提供以下几个基本功能: (1)从硬盘读入事先录入的中文文档 ...

  4. NDoc修改版,支持中文注释及中文界面。

    这几天正在做一个项目的开发文档,以前试用NDoc做开发文档时不支持中文,真是不爽.这几天看了源代码,修改了其中的一段源代码及配置文件后,支持中文注释及中文界面(目前只做了Msdn2003一种). 以下 ...

  5. 基因功能注释分析的意义

    基因功能注释分析的意义 基因功能注释分析简介 基因功能的注释依赖于基因结构或者序列,将基因序列或蛋白序列和主流数据库进行比对获取该基因的功能信息,最终对预测的编码基因进行功能注释.常见的功能注释数据库 ...

  6. fckeditor组件使用---配置FCKeditor,以及使用方法2

    6.总结: 配置FCKeditor的方法:   a.直接修改主配置文件,fckconfig.js   b.定义单独的配置文件(只需要写修改的配置项,然后在fckconfig.js中引入)   c.在页 ...

  7. 基于大连理工大学的情感词汇表的中文情感分析

    前言 为什么要写这篇文章? 前段时间帮人写了一个这样的小项目,在网上查找资料的过程中,有不少关于该项目的资料,由于各个博主写的代码不尽相同,且没有一个详尽的分析方法,所以我在完成该项目后,想到可以把该 ...

  8. (附源码)node.js中小学生中文写作网 毕业设计 250623

    基于node.js中小学生中文写作网 摘 要 信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以 ...

  9. Turf.js——用于地理空间分析的js库,处理各种地图算法

    Turf.js--用于地理空间分析的js库,处理各种地图算法 一.官网 中文--https://turfjs.fenxianglu.cn/ 英文--https://turfjs.org/ npm地址- ...

最新文章

  1. 很简单很简单的DBHelper类
  2. MRP的数据处理-华北水利水电大学(作业)
  3. Spring Cloud微服务系列-Eureka Client源码解析(一)
  4. 安卓安装包不能覆盖原安装包_LOL手游全网最简单注册,安装教程,安卓 IOS皆可登录!攻略群有游戏安装包!...
  5. C#获取当前应用程序所在路径及环境变量
  6. win10 android 手机驱动下载,小米手机驱动win10驱动
  7. 轻量级纯CSS框架,11款最轻量级的CSS框架
  8. 中国第一,全球领先的「浪潮」:用智慧计算征服的计算力天下
  9. c语言 输出等腰梯形,梯形是什么样子
  10. 财帮子(caibangzi.com)网站架构
  11. C语言基础编程(switch语句)
  12. 会说话的TOM猫的原理是什么
  13. java GUI实现用户管理系统
  14. 误提交了target目下的文件怎么删除?
  15. 【边缘计算】芯片企业都在扎堆布局这一新领域:边缘智能;物联网“网”的本质:5G、LPWAN、SDN、NFV、TSN...
  16. Python3.x+迅雷x 自动下载高分电影,嘿嘿!你懂的
  17. 《求医不如求己》1,2,3全集下载(附人体穴位图)
  18. Houdini制作pyside2插件崩溃原因
  19. Swift - 二维码QRCode的读取
  20. 冒泡排序(超详细图解加代码解析,5分钟看懂)

热门文章

  1. SpringBoot集成阿里云支付
  2. Prism 模板包,快捷创建Prism程序
  3. 电脑快捷键大全(2)
  4. 哈姆雷特(hamlet)现代英语版
  5. Lucene中倒排索引原理
  6. 【Linux】Linux文件与文件的存储
  7. TOGAF ADM 教程
  8. nested exception is java.lang.NoClassDefFoundError: com/fasterxml/jackson/databind/exc/Invalid.....
  9. 2006-07-16蜡肉的吃法(不完全)
  10. 光流文件(.flo)转成图片(.png)