FCKeditor 源代码分析附中文注释

这几天都在研究FCKeditor的源代码 (FCKeditor就是网络中应用比较广泛的网页编辑器)  这里需要感谢nileaderblog的辛苦翻译。

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

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

鉴于笔者水平有限,在此,请广大高手指出我的注释中不妥之处,以免误导他人 。谢谢。

建议copy到自己的IDE中查看 或者
注:本文基于FCKeditor2.6.5
更多权威资料,请参见 FCK 官方Developers Guide


代码如下:

/**
*
* ***********CopyRight**************
*-------Annotated by nileader-----
*-----Version 1.00 2009-10-18-----
*-----Once copied, marked http://www.nileader.cn
*
* FCKeditor 类 annotated by nileader
* @param {Object} instanceName 编辑器的唯一名称(相当于ID) 是不可省参数,
* width,height,toolbarset,value 都是 可选参数
*/
var FCKeditor = function( instanceName, width, height, toolbarSet, value )
{
//编辑器的基本属性 注意:这些东西优先于FCKConfig.js中的配置

this.InstanceName = instanceName ; //编辑器的唯一名称(相当于ID)(必须有!)
this.Width = width || '100%' ; //宽度 默认是100%
this.Height = height || '200' ; //宽度 默认是200
this.ToolbarSet = toolbarSet || 'Default' ;//工具集名称,默认值是Default
this.Value = value || '' ; //初始化编辑器的HTML代码,默认值为空
//编辑器初始化的时候默认的根路径, 其作用是编写fck中,凡是用到的路径,均从FCKeditor.BasePath目录开始 默认为/Fckeditor/
this.BasePath = FCKeditor.BasePath ;
this.CheckBrowser = true ; //是否在显示编辑器前检查浏览器兼容性,默认为true
this.DisplayErrors = true ; //是否显示提示错误,默为true
this.Config = new Object() ;
// Events
this.OnError = null ; // function( source, errorNumber, errorDescription )自定义的错误处理函数
}
FCKeditor.BasePath = '/fckeditor/' ; // fck默认的根目录
FCKeditor.MinHeight = 200 ; //高和宽的限制
FCKeditor.MinWidth = 750 ;
FCKeditor.prototype.Version = '2.6.5' ; //版本号
FCKeditor.prototype.VersionBuild = '23959' ;
/**
* 调用CreateHtml()来生成编辑器的html代码并在页面上输出编辑器
*/
FCKeditor.prototype.Create = function()
{
//调用createhtml()方法
document.write( this.CreateHtml() ) ;
}
/**
* @return sHtml 用于生成编辑器的html代码
*/
FCKeditor.prototype.CreateHtml = function()
{
// 检查有无InstanceName 如果没有则不生成html代码
if ( !this.InstanceName || this.InstanceName.length == 0 )
{
this._ThrowError( 701, 'You must specify an instance name.' ) ;
return '' ;
}
//函数的返回值
var sHtml = '' ;
/*
* 当用户的浏览器符合预设的几种浏览器时,
* 生成一个id="this.instancename" name="this.instancename"的文本框,事实上的内容储存器
*/
if ( !this.CheckBrowser || this._IsCompatibleBrowser() )
{
//将此时FCK初始值通过转义之后放入这个input
sHtml += '<input type="hidden" id="' + this.InstanceName + '" name="' + this.InstanceName + '" value="' + this._HTMLEncode( this.Value ) + '" style="display:none" style="display:none" />' ;
//生成一个隐藏的INPUT来放置this.config中的内容
sHtml += this._GetConfigHtml() ;
//生成编辑器的iframe的代码
sHtml += this._GetIFrameHtml() ;
}
/**
* 如果用户的浏览器不兼容FCK默认的几种浏览器
* 只能有传统的textarea了
*/
else
{
var sWidth = this.Width.toString().indexOf('%') > 0 ? this.Width : this.Width + 'px' ;
var sHeight = this.Height.toString().indexOf('%') > 0 ? this.Height : this.Height + 'px' ;
sHtml += '<textarea name="' + this.InstanceName +
'" rows="4" cols="40" style="width:' + sWidth +
';height:' + sHeight ;
if ( this.TabIndex )
sHtml += '" tabindex="' + this.TabIndex ;
sHtml += '">' +
this._HTMLEncode( this.Value ) +
'<\/textarea>' ;
}
return sHtml ;
}
/**
* 用编辑器来替换对应的文本框
*/
FCKeditor.prototype.ReplaceTextarea = function()
{
//如果已经有了 id=THIS.INSTANCENAME___Frame 的标签时,直接返回
if ( document.getElementById( this.InstanceName + '___Frame' ) )
return ;
//当用户的浏览器符合预设的几种浏览器时
if ( !this.CheckBrowser || this._IsCompatibleBrowser() )
{
// We must check the elements firstly using the Id and then the name.
//获取id=this.InstanceName的html标签
var oTextarea = document.getElementById( this.InstanceName ) ;
//获取所有name=THIS.instancename的标签
var colElementsByName = document.getElementsByName( this.InstanceName ) ;
var i = 0;
/*
* 考虑到用户html标签的命名不规范,所以进行以下编历判断 笔者指的是用户在textarea标签处用了name=this.instancename
* 在同个页面的其它标签上也用了name=this.instancename
*/
while ( oTextarea || i == 0 )
{
//遍历,直到找到name=this.instancename的textarea标签,并赋给oTextarea
if ( oTextarea && oTextarea.tagName.toLowerCase() == 'textarea' )
break ;
oTextarea = colElementsByName[i++] ;
}
//如果不存在id或者name为this.instancename的标签时,弹出错误框
if ( !oTextarea )
{
alert( 'Error: The TEXTAREA with id or name set to "' + this.InstanceName + '" was not found' ) ;
return ;
}
/*
* 确定存在name=this.instancename的textarea标签后,将编辑器的代码赋给它
*/
oTextarea.style.display = 'none' ;
//如果页面上对这样的textarea标签定义了tab键的顺序,赋给this.TabIndex待用
if ( oTextarea.tabIndex )
this.TabIndex = oTextarea.tabIndex ;
this._InsertHtmlBefore( this._GetConfigHtml(), oTextarea ) ;
this._InsertHtmlBefore( this._GetIFrameHtml(), oTextarea ) ;
}
}

/**
* 在指定的页面标签前面插入html代码
* @param {Object} 待插入的html代码
* @param {Object} 指定的页面标签(对象)
*/
FCKeditor.prototype._InsertHtmlBefore = function( html, element )
{
if ( element.insertAdjacentHTML ) // IE 私有的 insertAdjacentHTML 方法
element.insertAdjacentHTML( 'beforeBegin', html ) ;
else // 非ie浏览器
{

var oRange = document.createRange() ;
oRange.setStartBefore( element ) ;
var oFragment = oRange.createContextualFragment( html );
element.parentNode.insertBefore( oFragment, element ) ;
}
}

/*
* 通过编历this.Config[]来生成一个隐藏域,
* 例如:
* this.Config['nileader']="1104",this.Config['leaderni']="nichao"……
* 那么,sConfig=…… &nileader=1104&leaderni=nichao ……
* 当然,最终,sConfig会被encodeURIComponent函数转换成百分比编码 放入隐藏的INPUT中去
*/
FCKeditor.prototype._GetConfigHtml = function()
{
var sConfig = '' ;
for ( var o in this.Config )
{
if ( sConfig.length > 0 ) sConfig += '&' ;
//encodeURIComponent函数转换成百分比编码
sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.Config[o] ) ;
}
return '<input type="hidden" id="' + this.InstanceName + '___Config" value="' + sConfig + '" style="display:none" style="display:none" />' ;
}

/*
* 生成iframe的html 这里涉及到src的确定
*/
FCKeditor.prototype._GetIFrameHtml = function()
{
var sFile = 'fckeditor.html' ;
//特殊情况 fckedito所在的窗口没有嵌入在浏览器中
try
{
if ( (/fcksource=true/i).test( window.top.location.search ) )
sFile = 'fckeditor.original.html' ;
}
catch (e) { /* 忽略这个异常. 很多时候,fckedito所在的窗口嵌入在浏览器中. */ }
/*
* 这里注意的一点:
* iframe的工作原理: 当iframe处于可编辑状态时,其实编辑的是src所在的页面
* 这里合成一个sLink以放入iframe标签中
*/
//sLink就是这个事实上的页面了,从fck的根目录开始,例如 sLink=/fckeditor/editor/fckeditor.html?InstanceName=nileader&Toolbar=nileadersbar
var sLink = this.BasePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.InstanceName ) ;
if (this.ToolbarSet)
sLink += '&Toolbar=' + this.ToolbarSet ;
//生成一个真正的编辑iframer的html代码 当然,放入了src=slink
var html = '<iframe id="' + this.InstanceName +
'___Frame" src="' + sLink +
'" src="' + sLink +
'" width="' + this.Width +
'" height="' + this.Height ;
//如果设定了使用"Tab"键的遍历顺序,则赋给iframe
if ( this.TabIndex )
html += '" tabindex="' + this.TabIndex ;
html += '" frameborder="0" scrolling="no"></iframe>' ;
return html ;
}

/*
* 检测用户的bowser是否是fck的默认
* 这个方法只是fck公司追求oo,无意义
*/
FCKeditor.prototype._IsCompatibleBrowser = function()
{
return FCKeditor_IsCompatibleBrowser() ;
}

/**
* 抛出错误
* @param {Object} errorNumber 错误编号
* @param {Object} errorDescription 错误概述
*/
FCKeditor.prototype._ThrowError = function( errorNumber, errorDescription )
{
this.ErrorNumber = errorNumber ;
this.ErrorDescription = errorDescription ;
//是否显示提示错误,默为true
if ( this.DisplayErrors )
{ //将错误编号和错误概述打印出来
document.write( '<div style="COLOR: #ff0000" style="COLOR: #ff0000">' ) ;
document.write( '[ FCKeditor Error ' + this.ErrorNumber + ': ' + this.ErrorDescription + ' ]' ) ;
document.write( '</div>' ) ;
}
//OnError是否自定义了错误处理函数,若定义了,由其处理
if ( typeof( this.OnError ) == 'function' )
this.OnError( this, errorNumber, errorDescription ) ;
}

/**
* 转义文本
* @param {Object} text 待转义的文本
* @return String text 转义完后的文本
*/
FCKeditor.prototype._HTMLEncode = function( text )
{
if ( typeof( text ) != "string" )
text = text.toString() ;
//将字符串中的所有 & " < > 用对应的转义字符代换
text = text.replace(
/&/g, "&").replace(
/"/g, """).replace(
/</g, "<").replace(
/>/g, ">") ;
return text ;
}

;(function()
{
//把页面上的textarea元素赋给editor变量
var textareaToEditor = function( textarea )
{
var editor = new FCKeditor( textarea.name ) ;
editor.Width = Math.max( textarea.offsetWidth, FCKeditor.MinWidth ) ;
editor.Height = Math.max( textarea.offsetHeight, FCKeditor.MinHeight ) ;
return editor ;
}
/**
* Replace all <textarea> elements available in the document with FCKeditor
* instances.
*
* // Replace all <textarea> elements in the page.
* FCKeditor.ReplaceAllTextareas() ;
*
* // Replace all <textarea class="myClassName"> elements in the page.
* FCKeditor.ReplaceAllTextareas( 'myClassName' ) ;
*
* // Selectively replace <textarea> elements, based on custom assertions.
* FCKeditor.ReplaceAllTextareas( function( textarea, editor )
* {
* // Custom code to evaluate the replace, returning false if it
* // must not be done.
* // It also passes the "editor" parameter, so the developer can
* // customize the instance.
* } ) ;
*/
FCKeditor.ReplaceAllTextareas = function()
{
//获取所有的textarea元素
var textareas = document.getElementsByTagName( 'textarea' ) ;

for ( var i = 0 ; i < textareas.length ; i++ )
{
var editor = null ;
var textarea = textareas[i] ;
var name = textarea.name ;
// The "name" attribute must exist.
if ( !name || name.length == 0 )
continue ;
if ( typeof arguments[0] == 'string' )
{
// The textarea class name could be passed as the function
// parameter.
var classRegex = new RegExp( '(?:^| )' + arguments[0] + '(?:$| )' ) ;
if ( !classRegex.test( textarea.className ) )
continue ;
}
else if ( typeof arguments[0] == 'function' )
{
// An assertion function could be passed as the function parameter.
// It must explicitly return "false" to ignore a specific <textarea>.
editor = textareaToEditor( textarea ) ;
if ( arguments[0]( textarea, editor ) === false )
continue ;
}
if ( !editor )
editor = textareaToEditor( textarea ) ;
editor.ReplaceTextarea() ;
}
}
})() ;

/**
* 检测浏览器的兼容性
* 利用了navigator对象返回的一些信息sAgent,判断浏览器 返回包括 浏览器的码名 浏览器名 浏览器版本 语言 等信息 并小写
* 例如:
* mozilla/4.0 (compatible; msie 6.0; windows nt 5.2; sv1; .net clr 1.1.4322)
*
* 判断IE浏览器的时候,运用了IE4.0之后支持的增加了对条件编译,
* 由于只是IE支持,在W3C标准浏览器中,该属性是不被支持的。因此,适当的利用该特性,判断IE
*/
function FCKeditor_IsCompatibleBrowser()
{
var sAgent = navigator.userAgent.toLowerCase() ;
// 当前浏览器是Internet Explorer 5.5+
//利用条件编译判断IE 在IE中,/*@cc_on!@*/false == !false == true,
//如果是非IE浏览器,则忽略,/*@cc_on!@*/false == false
if ( /*@cc_on!@*/false && sAgent.indexOf("mac") == -1 ) //不是apple mac os
{
var sBrowserVersion = navigator.appVersion.match(/MSIE (.\..)/)[1] ;
return ( sBrowserVersion >= 5.5 ) ;
}
// Gecko (Opera 9 tries to behave like Gecko at this point).
//检测是否是OPERA 9 浏览器
if ( navigator.product == "Gecko" && navigator.productSub >= 20030210 && !( typeof(opera) == 'object' && opera.postError ) )
return true ;
// Opera 9.50+
if ( window.opera && window.opera.version && parseFloat( window.opera.version() ) >= 9.5 )
return true ;
// Adobe AIR
// Checked before Safari because AIR have the WebKit rich text editor
// features from Safari 3.0.4, but the version reported is 420.
if ( sAgent.indexOf( ' adobeair/' ) != -1 )
return ( sAgent.match( / adobeair\/(\d+)/ )[1] >= 1 ) ; // Build must be at least v1
// Safari 3+
if ( sAgent.indexOf( ' applewebkit/' ) != -1 )
return ( sAgent.match( / applewebkit\/(\d+)/ )[1] >= 522 ) ; // Build must be at least 522 (v3)
return false ;
}

(0)

相关推荐

  • HTML代码中标签的全部属性 中文注释说明

    例:写一段图片HTML代码 <img src="http://localhost/phpwind6/zx/6.jpg" id="imgs" lang="en-us" alt="测试"> 该标签为img的代码中已可以看出已含有了四个属性,包括src id lang alt,加上本身的img标签有5个属性. 那么该img标签在正常的情况下倒底还含有多个属性?(包含对象),我现在为大家一一列出来(以上面那句代码为例,并附

  • 代码中到底应不应当写注释?

    当很多前辈教育后辈应当多写注释的时候,当网络上充满了有关程序员从不写注释的段子的时候,这是一个非常有争议的话题.作为一个标题党,容我先修正一下我的观点:我认为如果代码写得足够好,那么大多数注释是多余的,我们应该通过写出更好的代码来代替更多注释. 注释的确有其用途,但大部分情况下,程序员在滥用注释.我是反对夹杂在代码间的注释的,我认为注释应当从代码中独立出来--通常被称为文档. 请看下面一段代码. 复制代码 代码如下: /* /static/market/checkout.js 2014.7.2

  • 网页中返回顶部代码(多种方法)另附注释说明

    下面就说下简单的返回顶部效果的代码实现,附注释说明. 1. 最简单的静态返回顶部,点击直接跳转页面顶部,常见于固定放置在页面底部返回顶部功能 方法一:用命名锚点击返回到顶部预设的id为top的元素 复制代码 代码如下: <a href="#top" target="_self">返回顶部</a> 方法二:操作scrooll函数用来控制滚动条的位置(第一个参数是水平位置,第二个参数是垂直位置) 复制代码 代码如下: <a href=&qu

  • jQuery 表格隔行变色代码[修正注释版]

    复制代码 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv=&qu

  • asp.net画曲线图(折线图)代码 详细注释

    复制代码 代码如下: using System; using System.Collections; using System.Configuration; using System.Data; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.UI

  • Javascript 倒计时源代码.(时.分.秒) 详细注释版

    随便写写!闲着无聊!代码如有bug之处欢迎阁下强力拍砖! JS CODE 复制代码 代码如下: <script type="text/javascript" language="javascript"> //总时间,已分为单位 var time = 100; //小时 var h = parseInt(time / 60) > 0 ? parseInt(time / 60) : 0; //分 var m = time % 60; //秒 var s

  • PHP压缩html网页代码(清除空格,换行符,制表符,注释标记)

    PHP压缩html网页代码 (清除空格,换行符,制表符,注释标记). 有个不错的方法就是压缩HTML,压缩html 其实就是:清除换行符,清除制表符,去掉注释标记 .它所起到的作用不可小视. 现提供PHP 压缩HTML函数.请大家不妨试试看,感觉还不错吧. 不废话了,直接上代码: 复制代码 代码如下: <?php /** * 压缩html : 清除换行符,清除制表符,去掉注释标记 * @param $string * @return 压缩后的$string * */ function compr

  • JavaScript 事件监听实例代码[兼容IE,firefox] 含注释

    JavaScript事件监听完整实例(含注释) var oEventUtil = new Object(); oEventUtil.AddEventHandler = function(oTarget,sEventType,fnHandler) { //IE和FF的兼容性处理 //如果是FF if(oTarget.addEventListener){ oTarget.addEventListener(sEventType,fnHandler,false); } //如果是IE else if(o

  • FCKeditor 源代码分析附中文注释

    这几天都在研究FCKeditor的源代码 (FCKeditor就是网络中应用比较广泛的网页编辑器)  这里需要感谢nileaderblog的辛苦翻译. 几乎搜遍了Internet,似乎对于fckconfig.js这个文件讲解的很多,但对于fckeditor.js这个FCK的核心类文件的资料几乎为0. 所以,花了整整一天的时间,以挤牙膏的方式,对fckeditor.js这个fck核心类文件作了自己力所能及的注释,供同样学习fck的网友一个参考. 鉴于笔者水平有限,在此,请广大高手指出我的注释中不妥

  • 一个可以找出源代码中所有中文的工具

    一个可以找出源代码中所有中文的工具 填写需要查找的路径$sf即可. 功能 1 找出所有中文 2 忽略注释语句中的中文 3 可添加需要忽略的文件和文件夹 4 生成日志文件 如果您正发愁为自己的软件更新语言包 请使用此工具. 我的例子: http://127.0.0.1/tools/searchcn.php http://127.0.0.1/tools/cnlog.txt 复制代码 代码如下: <?php set_time_limit(0); ob_implicit_flush(); //注释格式

  • Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析

    在上一篇文章中,我们分析了Android系统进程间通信机制Binder中的Server在启动过程使用Service Manager的addService接口把自己添加到Service Manager守护过程中接受管理.在这一篇文章中,我们将深入到Binder驱动程序源代码去分析Client是如何通过Service Manager的getService接口中来获得Server远程接口的.Client只有获得了Server的远程接口之后,才能进一步调用Server提供的服务. 这里,我们仍然是通过A

  • 解决JS外部文件中文注释出现乱码问题

    问题描述 中文乱码在Java Web开发中经常出现,这是由于不同的部分编码不一样造成的,一般在开发中,我们把所有能设编码的地方,全部设置成UTF-8,但是有时候还是会出现乱码的情况.在开发中,一般把js代码从html中抽出来,放到一个js文件中,js文件中包含中文,在浏览器中页面没有出现乱码,一切正常,当我们打开源码点进js文件,我们发现js文件中的中文注释和js代码中的中文全是乱码. 问题原因  我们没有告诉浏览器,当前的文件使用何种编码,浏览器默认使用的的GBK编码,但是我们js文件使用的U

  • Android uses-permission权限列表中文注释版

    android同时也限定了系统资源的使用,像网络设备,SD卡,录音设备等.如果你的应用希望去使用任何系统资源,我们必须去申请Android的权限.这就是<uses-permission>元素的作用. 一个权限通常有以下格式,用一个名字为name 的字符串去指导我们希望使用的权限. 复制代码 代码如下: <uses-permission android:name="string"/> 例如:想要获得networking APIs的使用权限,我们指定如下的元素作为

  • Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析

    在前面几篇文章中,我们详细介绍了Android系统进程间通信机制Binder的原理,并且深入分析了系统提供的Binder运行库和驱动程序的源代码.细心的读者会发现,这几篇文章分析的Binder接口都是基于C/C++语言来实现的,但是我们在编写应用程序都是基于Java语言的,那么,我们如何使用Java语言来使用系统的Binder机制来进行进程间通信呢?这就是本文要介绍的Android系统应用程序框架层的用Java语言来实现的Binder接口了. 熟悉Android系统的读者,应该能想到应用程序框架

  • Python中的单行、多行、中文注释方法

    一.python单行注释符号(#) python中单行注释采用 #开头 示例:#this is a comment 二.批量.多行注释符号 多行注释是用三引号"' "'包含的,例如: 三.python中文注释方法 今天写脚本的时候,运行报错: SyntaxError: Non-ASCII character '\xe4' in file getoptTest.py on line 14, but no encoding declared; see http://www.python.o

  • 通过源代码分析Mybatis的功能流程详解

    SQL解析 Mybatis在初始化的时候,会读取xml中的SQL,解析后会生成SqlSource对象,SqlSource对象分为两种. DynamicSqlSource,动态SQL,获取SQL(getBoundSQL方法中)的时候生成参数化SQL. RawSqlSource,原始SQL,创建对象时直接生成参数化SQL. 因为RawSqlSource不会重复去生成参数化SQL,调用的时候直接传入参数并执行,而DynamicSqlSource则是每次执行的时候参数化SQL,所以RawSqlSourc

  • Pytorch实现图像识别之数字识别(附详细注释)

    使用了两个卷积层加上两个全连接层实现 本来打算从头手撕的,但是调试太耗时间了,改天有时间在从头写一份 详细过程看代码注释,参考了下一个博主的文章,但是链接没注意关了找不到了,博主看到了联系下我,我加上 代码相关的问题可以评论私聊,也可以翻看博客里的文章,部分有详细解释 Python实现代码: import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transf

  • Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析

    在前面一篇文章Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路中,介绍了在Android系统中Binder进程间通信机制中的Server角色是如何获得Service Manager远程接口的,即defaultServiceManager函数的实现.Server获得了Service Manager远程接口之后,就要把自己的Service添加到Service Manager中去,然后把自己启动起来,等待Client的请求.

随机推荐