FCKeditor source code analysis with Chinese comments

Source: Internet
Author: User

These days I have been studying FCKeditor's Source code (FCKeditor is a Web Editor widely used in the network.) thanks to the hard translation of nileaderblog.

I searched the internet almost, and it seems that I have explained a lot about the fckconfig. js file. However, the FCKeditor. js file's core class files are almost 0.

Therefore, it took a whole day to annotate the fck Core File FCKeditor. js in the form of toothpaste, so as to serve as a reference for netizens who are also studying fck.

In view of the limited level of the author, the majority of experts are invited to point out what is inappropriate in my comments, so as not to mislead others. Thank you.

We recommend that you copy it to your ide or
Note: This article is based on fckeditor2.6.5
For more authoritative information, see fck official developers GuideCopyCodeThe Code is as follows :/**
*
* *********** Copyright **************
* ------- Annotated by nileader -----
* ----- Version 1.00 -----
* ----- Once copied, marked http://www.nileader.cn
*
* FCKeditor class annotated by nileader
* @ Param {object} the unique name (equivalent to ID) of the InstanceName editor is a non-saving parameter,
* Width, height, toolbarset, and value are optional parameters.
*/
VaR FCKeditor = function (InstanceName, width, height, toolbarset, value)
{
// Note the basic attributes of the Editor: These things take precedence over the configuration in fckconfig. js.

This. InstanceName = InstanceName; // unique name of the Editor (equivalent to ID) (required !)
This. width = width | '000000'; // The default width is 100%.
This. Height = height | '000000'; // The default width is 200.
This. toolbarset = toolbarset | 'default'; // tool set name. The default value is default.
This. value = value | ''; // initialize the HTML code of the editor. The default value is null.
// The default root path of the editor during initialization. The function of the editor is to compile fck. All the paths used are from the FCKeditor. basepath directory. The default value is/FCKeditor/
This. basepath = FCKeditor. basepath;
This. checkbrowser = true; // check whether the browser compatibility is checked before the editor is displayed. The default value is true.
This. displayerrors = true; // whether an error is displayed. The default value is true.
This. Config = new object ();
// Events
This. onerror = NULL; // function (source, errornumber, errordescription) custom error handling function
}
FCKeditor. basepath = '/FCKeditor/'; // default root directory of fck
FCKeditor. minheight = 200; // height and width
FCKeditor. minwidth = 750;
FCKeditor. Prototype. Version = '2. 6.5 '; // version
FCKeditor. Prototype. versionbuild = '201312 ';
/**
* Call createhtml () to generate the HTML code of the editor and output the editor on the page.
*/
FCKeditor. Prototype. Create = function ()
{
// Call the createhtml () method
Document. Write (this. createhtml ());
}
/**
* @ Return shtml is used to generate the HTML code of the editor.
*/
FCKeditor. Prototype. createhtml = function ()
{
// Check whether InstanceName exists. If not, HTML code is not generated.
If (! This. InstanceName | this. InstanceName. Length = 0)
{
This. _ throwerror (701, 'you must specify an Instance name .');
Return '';
}
// Return value of the Function
VaR shtml = '';
/*
* When your browser meets the preset browser types,
* Generate a text box with ID = "This. instancename" name = "This. instancename". The actual content storage
*/
If (! This. checkbrowser | this. _ iscompatiblebrowser ())
{
// Escape the fck initial value and put it into the input
Shtml + = '<input type = "hidden" id = "' + this. instanceName + '"name ="' + this. instanceName + '"value ="' + this. _ htmlencode (this. value) + '"style =" display: none "style =" display: none "/> ';
// Generate a hidden input to place the content in this. config
Shtml + = This. _ getconfightml ();
// Generate the IFRAME code of the Editor
Shtml + = This. _ getiframehtml ();
}
/**
* If your browser is not compatible with several default fck browsers
* Only traditional textarea is available.
*/
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;
}
/**
* Use the editor to replace the corresponding text box.
*/
FCKeditor. Prototype. replacetextarea = function ()
{
// If the tag id = This. InstanceName ___ frame already exists, return directly
If (document. getelementbyid (this. InstanceName + '___ frame '))
Return;
// When your browser meets the preset browser types
If (! This. checkbrowser | this. _ iscompatiblebrowser ())
{
// We must check the elements firstly using the ID and then the name.
// Obtain the HTML Tag of ID = This. InstanceName
VaR otextarea = Document. getelementbyid (this. InstanceName );
// Obtain all tags whose names are this. InstanceName
VaR colelementsbyname = Document. getelementsbyname (this. InstanceName );
VaR I = 0;
/*
* Considering that the user's HTML Tag name is not standardized, the author determines the following calendar: the user uses name = This. InstanceName in the textarea tag.
* Name = This. InstanceName is also used for other labels on the same page.
*/
While (otextarea | I = 0)
{
// Traverse until the textarea tag with name = This. InstanceName is found and assigned to otextarea
If (otextarea & otextarea. tagname. tolowercase () = 'textarea ')
Break;
Otextarea = colelementsbyname [I ++];
}
// If the tag ID or name is this. InstanceName does not exist, the error box is displayed.
If (! Otextarea)
{
Alert ('error: The textarea with ID or name set to "'+ this. InstanceName +'" was not found ');
Return;
}
/*
* After confirming that the textarea tag with name = This. InstanceName exists, assign the editor code to it.
*/
Otextarea. style. Display = 'none ';
// If the order of the tab key is defined for such a textarea label on the page, assign it to this. tabindex to be used.
If (otextarea. tabindex)
This. tabindex = otextarea. tabindex;
This. _ inserthtmlbefore (this. _ getconfightml (), otextarea );
This. _ inserthtmlbefore (this. _ getiframehtml (), otextarea );
}
}

/**
* Insert HTML code before the specified page tag
* @ Param {object} the HTML code to be inserted
* @ Param {object} the specified page tag (object)
*/
FCKeditor. Prototype. _ inserthtmlbefore = function (HTML, element)
{
If (element. insertadjacenthtml) // The Private insertadjacenthtml method of IE
Element. insertadjacenthtml ('beforebegin', HTML );
Else // non-IE browser
{

VaR Orange = Document. createRange ();
Orange. setstartbefore (element );
VaR ofragment = Orange. createcontextualfragment (HTML );
Element. parentnode. insertbefore (ofragment, element );
}
}

/*
* Compile this. config [] to generate a hidden domain,
* Example:
* This. config ['nileader '] = "1104", this. config ['leaderni'] = "nichao "......
* Then, sconfig = ...... & Nileader = 1104 & leaderni = nichao ......
* Of course, in the end, sconfig will be converted to percentage encoding by the encodeuricomponent function and put in hidden input.
*/
FCKeditor. Prototype. _ getconfightml = function ()
{
VaR sconfig = '';
For (VAR o in this. config)
{
If (sconfig. length> 0) sconfig + = '&';
// The encodeuricomponent function is converted to percent encoding.
Sconfig + = encodeuricomponent (o) + '=' + encodeuricomponent (this. config [O]);
}
Return '<input type = "hidden" id = "' + this. instanceName + '___ config "value ="' + sconfig + '"style =" display: none "style =" display: none "/> ';
}

/*
* Generating iframe html involves the confirmation of SRC.
*/
FCKeditor. Prototype. _ getiframehtml = function ()
{
VaR sfile = 'fckeditor.html ';
// In special cases, the window in which fckedito is located is not embedded in the browser
Try
{
If (/fcksource = true/I). Test (window. Top. Location. Search ))
Sfile = 'fckeditor.original.html ';
}
Catch (e) {/* ignore this exception. In many cases, the window in which fckedito is located is embedded in the browser .*/}
/*
* Note:
* How IFRAME works: When IFRAME is editable, the page where SRC is actually edited
* Here, an slink is merged into the IFRAME tag.
*/
// Slink is the actual page, starting from the root directory of fck, such as 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;
// Generate a real HTML code for editing iframer. Of course, put src = slink
VaR html = '<IFRAME id = "' + this. InstanceName +
'___ Frame "src ="' + slink +
'"Src ="' + slink +
'"Width ="' + this. Width +
'"Height ="' + this. height;
// If you set the traversal order using the "tab" Key, assign it to IFRAME
If (this. tabindex)
HTML + = '"tabindex ="' + this. tabindex;
HTML + = '"frameborder =" 0 "scrolling =" no "> </iframe> ';
Return HTML;
}

/*
* Checks whether the user's Bock is the default value of fck.
* This method is meaningless because fck pursues oo.
*/
FCKeditor. Prototype. _ iscompatiblebrowser = function ()
{
Return fckeditor_iscompatiblebrowser ();
}

/**
* Throw an error.
* @ Param {object} errornumber error number
* @ Param {object} errordescription error Overview
*/
FCKeditor. Prototype. _ throwerror = function (errornumber, errordescription)
{
This. errornumber = errornumber;
This. errordescription = errordescription;
// Whether an error is displayed. The default value is true.
If (this. displayerrors)
{// Print the error number and overview
Document. Write ('<Div style = "color: # ff0000" style = "color: # ff0000"> ');
Document. Write ('[FCKeditor error' + this. errornumber + ':' + this. errordescription + ']');
Document. Write ('</div> ');
}
// Onerror indicates whether the error processing function is customized. If so, it will handle the error.
If (typeof (this. onerror) = 'function ')
This. onerror (this, errornumber, errordescription );
}

/**
* Escape text
* @ Param {object} text the text to be escaped
* @ Return string text the text after escaping
*/
FCKeditor. Prototype. _ htmlencode = function (text)
{
If (typeof (text )! = "String ")
TEXT = text. tostring ();
// Replace all & "<> In the string with the corresponding Escape Character
TEXT = text. Replace (
/&/G, "&"). Replace (
/"/G,"). Replace (
/</G, "<"). Replace (
/>/G, "> ");
Return text;
}

; (Function ()
{
// Assign the textarea element on the page to the editor variable
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 ()
{
// Obtain all textarea Elements
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 cocould 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 cocould 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 ();
}
}
})();

/**
* Browser compatibility check
* Some information returned by the navigator object sagent is used to determine whether the browser returns information including the browser code name, browser name, browser version language, and other information in lower case.
* Example:
* Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; sv1;. Net CLR 1.1.4322)
*
* When judging ie browsers, Conditional compilation is added after ie4.0 is used,
* This attribute is not supported in W3C standard browsers because it is only supported by IE. Therefore, make proper use of this feature to Determine IE
*/
Function fckeditor_iscompatiblebrowser ()
{
VaR sagent = navigator. useragent. tolowercase ();
// The current browser is Internet Explorer 5.5 +
// Use Conditional compilation to determine whether IE is in IE,/* @ cc_on! @ */False =! False = true,
// If it is not an Internet Explorer, ignore it./* @ cc_on! @ */False = false
If (/* @ cc_on! @ */False & sagent. indexof ("Mac") =-1) // not an 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 ).
// Check whether the browser is 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;
}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.