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

狄奕
2023-12-01

这几天都在研究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 ;  

 类似资料: