Ckeditor.js: SRC /core/ckeditor_base.js: SRC /core/ckeditor_base.js: SRC /core/ckeditor_base.js: SRC /core/ckeditor_base

// src/core/ckeditor_base.js
if(!window.CKEDITOR ) {

  window.CKEDITOR = ( function() {
    var basePathSrcPattern = /(^|.*[\\\/])ckeditor\.js(? : \? . * |; . *)? $/i;

    var CKEDITOR = {
      _: {
        pending: [].basePathSrcPattern: basePathSrcPattern
      },
      status: 'unloaded'.basePath: ( function() {}) (),// Find out the editor directory path, based on its <script> tag.
        var path = window.CKEDITOR_BASEPATH || ' ';

        return path;
      } )(),

      domReady: ( function() {
        // Based on the original jQuery code (available under the MIT license, see LICENSE.md).

        var callbacks = [];

        return function( fn ) { callbacks.push( fn ); }; ()}});return CKEDITOR;
  } )();
}
Copy the code

I’ve excerpted the code inside.

.SRC /core/ckeditor_base.js
if ( CKEDITOR.loader )
	CKEDITOR.loader.load( 'ckeditor' );
else {
	// Set the script name to be loaded by the loader.
	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>'); }}Copy the code

There is no CKEDITOR. Loader for the first time, so going to the else section above will start loading core/loader.js, which is the last step in the necessary preparation.

// core/loader.js
CKEDITOR.loader = ( function() {
    // Table of script names and their dependencies.
    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'].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>'); }}}; }) ();Copy the code

As you can see, this defines the name of the JS script to be loaded and its corresponding dependency.

Load CKEDITOR with CKEDITOR. Loader. load(‘ CKEDITOR ‘).

Core /_bootstrap is also loaded inside core/ Ckeditor. As you can see, the ckeditor code is written as a self-executing function.

Let’s take a look at the startup part of the current mainstream framework. Take React as an example

React4-react can be seen in the source code.

// src/useCKEditor.ts
const initEditor = ( CKEDITOR: CKEditorNamespace ) = > {
	const isInline = typeRef.current === 'inline';
	const isReadOnly = configRef.current.readOnly;

	/** * Dispatches `beforeLoad` event. */
	if ( subscribeToRef.current.indexOf( 'beforeLoad')! = = -1) { dispatchEventRef.current? ({.type: CKEditorEventAction.beforeLoad,
	payload: CKEDITOR
	} );
	}

	const editor = CKEDITOR[ isInline ? 'inline' : 'replace' ](
	element,
	configRef.current
	);
}
Copy the code

We usually use the default editor mode is classic, so the above we call CKEDITOR [‘ replace ‘] (element, configRef. Current) to start creating the editor instance

// src/core/creator/themeui.js CKEDITOR.replace = function( element, config ) { return createInstance( element, config, null, CKEDITOR.ELEMENT_MODE_REPLACE ); }; function createInstance( element, config, data, mode ) { element = CKEDITOR.editor._getEditorElement( element ); if ( ! element ) { return null; } // (#4461) if ( CKEDITOR.editor.shouldDelayEditorCreation( element, config ) ) { CKEDITOR.editor.initializeDelayedEditorCreation( element, config, 'replace' ); return null; } // Create the editor instance. var editor = new CKEDITOR.editor( config, element, mode ); if ( mode == CKEDITOR.ELEMENT_MODE_REPLACE ) { // Do not replace the textarea right now, just hide it. The effective // replacement will be done later in the editor creation lifecycle. element.setStyle( 'visibility', 'hidden' ); // https://dev.ckeditor.com/ticket/8031 Remember if textarea was required and remove the attribute. editor._.required = element.hasAttribute( 'required' ); element.removeAttribute( 'required' ); } data && editor.setData( data, null, true ); // Once the editor is loaded, start the UI. editor.on( 'loaded', function() { if ( editor.isDestroyed() || editor.isDetached() ) { return; } loadTheme( editor ); if ( mode == CKEDITOR.ELEMENT_MODE_REPLACE && editor.config.autoUpdateElement && element.$.form ) editor._attachToForm(); editor.setMode( editor.config.startupMode, function() { // Clean on startup. editor.resetDirty(); // Editor is completely loaded for interaction. editor.status = 'ready'; editor.fireOnce( 'instanceReady' ); CKEDITOR.fire( 'instanceReady', null, editor ); }); }); editor.on( 'destroy', destroy ); return editor; }Copy the code

Finally, you can look at the Editor constructor

// core/editor.js
function Editor( instanceConfig, element, mode ) {
    // Call the CKEDITOR.event constructor to initialize this instance.
    CKEDITOR.event.call( this );

    // Make a clone of the config object, to avoid having it touched by our code. (https://dev.ckeditor.com/ticket/9636)
    instanceConfig = instanceConfig && CKEDITOR.tools.clone( instanceConfig );
    // Declare the private namespace.
    this. _ = {};this.commands = {};

    this.templates = {};

    this.name = this.name || genEditorName();

    /**
     * A unique random string assigned to each editor instance on the page.
     *
     * @readonly
     * @property {String}* /
    this.id = CKEDITOR.tools.getNextId();

    
    this.status = 'unloaded';

    this.config = CKEDITOR.tools.prototypedCopy( CKEDITOR.config );

    /**
     * The namespace containing UI features related to this editor instance.
     *
     * @readonly
     * @property {CKEDITOR.ui}* /
    this.ui = new CKEDITOR.ui( this );

    this.focusManager = new CKEDITOR.focusManager( this );

    /**
     * Controls keystroke typing in this editor instance.
     *
     * @readonly
     * @property {CKEDITOR.keystrokeHandler}* /
    this.keystrokeHandler = new CKEDITOR.keystrokeHandler( this );

    // Make the editor update its command states on mode change.
    this.on( 'readOnly', updateCommands );
    this.on( 'selectionChange'.function( evt ) {
      updateCommandsContext( this, evt.data.path ); });this.on( 'activeFilterChange'.function() {
      updateCommandsContext( this.this.elementPath(), true); });this.on( 'mode', updateCommands );

    // Optimize selection starting/ending on element boundaries (#3175).
    CKEDITOR.dom.selection.setupEditorOptimization( this );

    // Handle startup focus.
    this.on( 'instanceReady'.function() {
      if ( this.config.startupFocus ) {
        if ( this.config.startupFocus === 'end' ) {
          var range = this.createRange();
          range.selectNodeContents( this.editable() );
          range.shrink( CKEDITOR.SHRINK_ELEMENT, true );
          range.collapse();
          this.getSelection().selectRanges( [ range ] );
        }
        this.focus(); }}); CKEDITOR.fire('instanceCreated'.null.this );

    // Add this new editor to the CKEDITOR.instances collections.
    CKEDITOR.add( this );

    // Return the editor instance immediately to enable early stage event registrations.
    CKEDITOR.tools.setTimeout( function() {
      if(!this.isDestroyed() && !this.isDetached() ) {
        initConfig( this, instanceConfig ); }},0.this );
  }
Copy the code

We can see initConfig at the back and start generating our editor based on the configuration. The following path is roughly as follows, with some notification messages that the phase is complete:

  • initConfig
  • onConfigLoaded
  • initComponents
  • loadSkin
  • loadLang
  • preloadStylesSet
  • loadPlugins

Finally, it’s time for our plug-in loading section… Once the plug-in is loaded, you can announce that the instance is loaded

// core/editor.js
editor.status = 'loaded';
editor.fireOnce( 'loaded' );
Copy the code

Many plugins already listen for loaded events, for example

// core/creator/themeui.js
		editor.on( 'loaded', function() {
			if ( editor.isDestroyed() || editor.isDetached() ) {
				return;
			}

			loadTheme( editor );
		}
Copy the code

The uiSpace message is triggered in the loadTheme method, and our toolbar (Core/tooltool.js) starts generating toolbar action buttons after listening for this message.

// plugins/toolbar/plugin.js editor.on( 'uiSpace', function( event ) { for ( var r = 0; r < toolbarLength; r++ ) { // Create all items defined for this toolbar. for ( var i = 0; i < items.length; i++ ) { function addItem( item ) { // jshint ignore:line var itemObj = item.render( editor, output ); index = toolbarObj.items.push( itemObj ) - 1; } addItem( item ); }}}Copy the code

Remember from the above item.render, our toolbar is generated by calling the render of each plugin.

Okay, I’ll write the rest next time.