一、入口文件ckeditor.js
入口文件主要由两部分代码,第一部分是对CKEDITOR对象的定义,并挂载到window上。
if (!window.CKEDITOR) {
window.CKEDITOR = (function () {
var CKEDITOR = {
timestamp: '',
version: '',
status: 'unloaded',
getUrl: function () {
...
}
...
}
return CKEDITOR;
})()
}
第二部分代码加载core/loader.js, 并通过loader加载ckeditor。
这里有个判断如果有CKEDITOR.loader就直接加载ckeditor.js,否则就通过CKEDITOR._autoLoad = 'ckeditor'; 等loader加载完之后自动加载ckeditor。
if ( CKEDITOR.loader )
CKEDITOR.loader.load( 'ckeditor' );
else{
CKEDITOR._autoLoad = 'ckeditor';
// Include the loader script.
if ( document.body && ( !document.readyState || document.readyState == 'complete' ) ) {
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.src = CKEDITOR.getUrl( 'core/loader.js' );
document.body.appendChild( script );
} else {
document.write( '<script type="text/javascript" src="' + CKEDITOR.getUrl( 'core/loader.js' ) + '"></script>' );
}
}
// 设置主题
CKEDITOR.skinName = 'moono-lisa';
二、加载器 core/loader.js
if ( !CKEDITOR.loader ) {
CKEDITOR.loader = (function(){...})();
}
// 自动加载ckeditor
if ( CKEDITOR._autoLoad ) {
CKEDITOR.loader.load( CKEDITOR._autoLoad );
delete CKEDITOR._autoLoad;
}
这里就是加载器的代码了,加载器的主要功能是加载模块的所有依赖模块,然后加载这个模块。所以其内部存放着模块的依赖关系:
var scripts = {
'_bootstrap': [
'config', 'creators/inline', 'creators/themedui', 'editable', 'ckeditor', 'plugins',
'scriptloader', 'style', 'tools', 'promise', 'selection/optimization', 'tools/color',
// The following are entries that we want to force loading at the end to avoid dependence recursion.
'dom/comment', 'dom/elementpath', 'dom/text', 'dom/rangelist', 'skin'
],
'ckeditor': [
'ckeditor_basic', 'log', 'dom', 'dtd', 'dom/document', 'dom/element', 'dom/iterator', 'editor', 'event',
'htmldataprocessor', 'htmlparser', 'htmlparser/element', 'htmlparser/fragment', 'htmlparser/filter',
'htmlparser/basicwriter', 'template', 'tools'
],
'ckeditor_base': [],
'ckeditor_basic': [ 'editor_basic', 'env', 'event' ],
'command': [],
'config': [ 'ckeditor_base' ],
'dom': [],
'dom/comment': [ 'dom/node' ],
'dom/document': [ 'dom/node', 'dom/window' ],
'dom/documentfragment': [ 'dom/element' ],
'dom/element': [ 'dom', 'dom/document', 'dom/domobject', 'dom/node', 'dom/nodelist', 'tools' ],
'dom/elementpath': [ 'dom/element' ],
'dom/event': [],
'dom/iterator': [ 'dom/range' ],
'dom/node': [ 'dom/domobject', 'tools' ],
'dom/nodelist': [ 'dom/node' ],
'dom/domobject': [ 'dom/event' ],
'dom/range': [ 'dom/document', 'dom/documentfragment', 'dom/element', 'dom/walker' ],
'dom/rangelist': [ 'dom/range' ],
'dom/text': [ 'dom/node', 'dom/domobject' ],
'dom/walker': [ 'dom/node' ],
'dom/window': [ 'dom/domobject' ],
'dtd': [ 'tools' ],
'editable': [ 'editor', 'tools' ],
'editor': [
'command', 'config', 'editor_basic', 'filter', 'focusmanager', 'keystrokehandler', 'lang',
'plugins', 'tools', 'ui'
],
'editor_basic': [ 'event' ],
'env': [],
'event': [],
'filter': [ 'dtd', 'tools' ],
'focusmanager': [],
'htmldataprocessor': [ 'htmlparser', 'htmlparser/basicwriter', 'htmlparser/fragment', 'htmlparser/filter' ],
'htmlparser': [],
'htmlparser/comment': [ 'htmlparser', 'htmlparser/node' ],
'htmlparser/element': [ 'htmlparser', 'htmlparser/fragment', 'htmlparser/node' ],
'htmlparser/fragment': [ 'htmlparser', 'htmlparser/comment', 'htmlparser/text', 'htmlparser/cdata' ],
'htmlparser/text': [ 'htmlparser', 'htmlparser/node' ],
'htmlparser/cdata': [ 'htmlparser', 'htmlparser/node' ],
'htmlparser/filter': [ 'htmlparser' ],
'htmlparser/basicwriter': [ 'htmlparser' ],
'htmlparser/node': [ 'htmlparser' ],
'keystrokehandler': [ 'event' ],
'lang': [],
'log': [ 'ckeditor_basic' ],
'plugins': [ 'resourcemanager' ],
'resourcemanager': [ 'scriptloader', 'tools' ],
'scriptloader': [ 'dom/element', 'env' ],
'selection': [ 'dom/range', 'dom/walker' ],
'skin': [],
'style': [ 'selection' ],
'template': [],
'tools': [ 'env' ],
'ui': [],
'creators/themedui': [],
'creators/inline': [],
'promise': [ 'tools' ],
'selection/optimization': [ 'selection' ],
'tools/color': [ 'tools' ]
};
加载器本质上就是一个对象:
{
loadedScripts: [], // 已加载的模块
scripts: scripts, // 模块的依赖关系
loadPending: function(){...}, //
load: function(scriptName, defer){...}, // 加载模块的主函数
}
加载功能具体实现是在load函数中:
load: function( scriptName, defer ) {
// Check if the script has already been loaded.
if ( ( 's:' + scriptName ) in this.loadedScripts )
return;
// Get the script dependencies list.
var dependencies = scripts[ scriptName ];
if ( !dependencies )
throw 'The script name"' + scriptName + '" is not defined.';
// Mark the script as loaded, even before really loading it, to
// avoid cross references recursion.
// Prepend script name with 's:' to avoid conflict with Array's methods.
this.loadedScripts[ 's:' + scriptName ] = true;
// Load all dependencies first.
for ( var i = 0; i < dependencies.length; i++ )
this.load( dependencies[ i ], true );
var scriptSrc = getUrl( 'core/' + scriptName + '.js' );
// Append the <script> element to the DOM.
// If the page is fully loaded, we can't use document.write
// but if the script is run while the body is loading then it's safe to use it
// Unfortunately, Firefox <3.6 doesn't support document.readyState, so it won't get this improvement
if ( document.body && ( !document.readyState || document.readyState == 'complete' ) ) {
pendingLoad.push( scriptName );
if ( !defer )
this.loadPending();
} else {
// Append this script to the list of loaded scripts.
this.loadedScripts.push( scriptName );
document.write( '<script src="' + scriptSrc + '" type="text/javascript"><\/script>' );
}
}
loadPending函数用来加载被延迟加载的模块:
loadPending: function() {
var scriptName = pendingLoad.shift();
if ( !scriptName )
return;
var scriptSrc = getUrl( 'core/' + scriptName + '.js' );
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.src = scriptSrc;
function onScriptLoaded() {
// Append this script to the list of loaded scripts.
CKEDITOR.loader.loadedScripts.push( scriptName );
// Load the next.
CKEDITOR.loader.loadPending();
}
// We must guarantee the execution order of the scripts, so we
// need to load them one by one. (https://dev.ckeditor.com/ticket/4145)
// The following if/else block has been taken from the scriptloader core code.
if ( typeof script.onreadystatechange !== 'undefined' ) {
/** @ignore */
script.onreadystatechange = function() {
if ( script.readyState == 'loaded' || script.readyState == 'complete' ) {
script.onreadystatechange = null;
onScriptLoaded();
}
};
} else {
/** @ignore */
script.onload = function() {
// Some browsers, such as Safari, may call the onLoad function
// immediately. Which will break the loading sequence. (https://dev.ckeditor.com/ticket/3661)
var timer = setTimeout( function() {
// Once the script is loaded, remove the listener as this might lead to memory leaks (#589).
script.onload = null;
onScriptLoaded( scriptName );
clearTimeout(timer);
}, 0 );
};
}
document.body.appendChild( script );
},
load的具体过程:
- 检查模块是否已经加载过,如果加载过就直接返回
- 检查模块是在依赖表中存在,如果不存在就抛出异常
- 获取模块的依赖关系,开始深度优先遍历加载模块的所有依赖模块
- 加载模块本身 最终通过document.write或document.body.appendChild( script )加载模块