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

来源:互联网 发布:西安程序员 编辑:程序博客网 时间:2024/05/22 17:28

这几天都在研究FCKeditor的源代码     什么是FCKeditor?

几乎搜遍了Internet,似乎对于fckconfig.js这个文件讲解的很多,但对于fckeditor.js这个FCK的核心类文件的资料几乎为0.

      所以,花了整整一天的时间,以挤牙膏的方式,对fckeditor.js这个fck核心类文件作了自己力所能及的注释,供同样学习fck的网友一个参考。

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

原创粉丝点击