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

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

 


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

相关文章
|
21天前
|
Web App开发 前端开发 JavaScript
技术心得记录:瀑布流的布局原理分析(纯CSS瀑布流与JS瀑布流)
技术心得记录:瀑布流的布局原理分析(纯CSS瀑布流与JS瀑布流)
15 0
|
5天前
|
JavaScript 前端开发 程序员
《JavaScript权威指南第7版》中文PDF+英文PDF+源代码 +JavaScript权威指南(第6版)(附源码)PDF下载阅读分享推荐
JavaScript是Web标准语言,广泛应用于各类浏览器,造就了其最广泛部署的地位。Node.js的兴起扩展了JavaScript的使用场景,使其成为开发者首选语言。无论新手还是经验丰富的程序员,都能受益于学习JavaScript。[《JavaScript权威指南第7版》资源链接](https://zhangfeidezhu.com/?p=224)
23 5
《JavaScript权威指南第7版》中文PDF+英文PDF+源代码 +JavaScript权威指南(第6版)(附源码)PDF下载阅读分享推荐
|
14天前
|
前端开发 JavaScript
文本,wangEditor5展示HTML无样式,wangEditor5如何看源码,Ctrl + U看CSS文件,代码高亮,Prism.js可以实现,解决方法,参考网页源代码的写法
文本,wangEditor5展示HTML无样式,wangEditor5如何看源码,Ctrl + U看CSS文件,代码高亮,Prism.js可以实现,解决方法,参考网页源代码的写法
|
28天前
|
JavaScript 前端开发 安全
安全开发-JS应用&原生开发&JQuery库&Ajax技术&加密编码库&断点调试&逆向分析&元素属性操作
安全开发-JS应用&原生开发&JQuery库&Ajax技术&加密编码库&断点调试&逆向分析&元素属性操作
|
2月前
|
JavaScript Java 测试技术
基于springboot+vue.js的附带文章和医院后台管理系统源代码设计说明文档ppt
基于springboot+vue.js的附带文章和医院后台管理系统源代码设计说明文档ppt
23 1
基于springboot+vue.js的附带文章和医院后台管理系统源代码设计说明文档ppt
|
13天前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的数据分析岗位招聘信息与分析附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的数据分析岗位招聘信息与分析附带文章源码部署视频讲解等
7 0
|
13天前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的高校成绩分析附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的高校成绩分析附带文章源码部署视频讲解等
11 0
|
13天前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的学生成绩分析和弱项辅助系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的学生成绩分析和弱项辅助系统附带文章源码部署视频讲解等
17 0
|
1月前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的高校智能培训管理系统分析与设计附带文章和源代码设计说明文档ppt
基于ssm+vue.js+uniapp小程序的高校智能培训管理系统分析与设计附带文章和源代码设计说明文档ppt
21 1
|
2月前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的法律咨询系统的分析与设计附带文章和源代码设计说明文档ppt
基于ssm+vue.js+uniapp小程序的法律咨询系统的分析与设计附带文章和源代码设计说明文档ppt
22 1