Provides a common, higher-level environment for solutions that use multiple Editor editors
or plugins that work outside the editor. Use it instead of Editor.createEditor.create()
in advanced application integrations.
Config
ckeditor5-utils下Config是一种类似于lodash get
效果的类,可以通过get(obj, 'a.b.c')
或set(obj, 'a.b.c', 123)
来获取或设置对象obj={a : { b: {c: 1}}}
,用来封装或操作配置。如:
let config = new Config( {
creator: 'inline',
language: 'pl',
resize: {
minHeight: 300,
maxHeight: 800,
icon: {
path: 'xyz'
}
},
toolbar: 'top',
options: {
foo: [
{ bar: 'b' },
{ bar: 'a' },
{ bar: 'z' }
]
}
//
expect( config.get( 'creator' ) ).to.equal( 'inline' );
config.set( {
option1: 1,
option2: {
subOption21: 21
}
} );
expect( config.get( 'option1' ) ).to.equal( 1 );
expect( config.get( 'option2.subOption21' ) ).to.equal( 21 );
Context
Provides a common, higher-level environment for solutions that use multiple editors or plugins that work outside the editor.
All configuration options passed to a context will be used as default options for editor instances initialized in that context.
Context plugins passed to a context instance will be shared among all editor instances initialized in this context. These will be the same plugin instances for all the editors.
- 例1
// Creates and initializes a new context instance.
const commonConfig = { ... }; // Configuration for all the plugins and editors.
const editorPlugins = [ ... ]; // Regular plugins here.
Context
.create( {
// Only context plugins here.
plugins: [ ... ],
// Configure the language for all the editors (it cannot be overwritten).
language: { ... },
// Configuration for context plugins.
comments: { ... },
...
// Default configuration for editor plugins.
toolbar: { ... },
image: { ... },
...
} )
.then( context => {
const promises = [];
promises.push( ClassicEditor.create(
document.getElementById( 'editor1' ),
{
editorPlugins,
context
}
) );
promises.push( ClassicEditor.create(
document.getElementById( 'editor2' ),
{
editorPlugins,
context,
toolbar: { ... } // You can overwrite the configuration of the context.
}
) );
return Promise.all( promises );
} );
- 例2,通过builtinPlugins设置内置插件
// An array of plugins built into the `Context` class.
// It is used in CKEditor 5 builds featuring `Context` to provide a list of context plugins which are later automatically initialized during the context initialization.
// They will be automatically initialized by `Context` unless `config.plugins` is passed.
// Build some context plugins into the Context class first.
Context.builtinPlugins = [ FooPlugin, BarPlugin ];
// Normally, you need to define config.plugins, but since Context.builtinPlugins was
// defined, now you can call create() without any configuration.
Context
.create()
.then( context => {
context.plugins.get( FooPlugin ); // -> An instance of the Foo plugin.
context.plugins.get( BarPlugin ); // -> An instance of the Bar plugin.
} );
- 例3,通过
defaultConfig
设置默认配置
Context.defaultConfig = {
foo: 1,
bar: 2
};
Context
.create()
.then( context => {
context.config.get( 'foo' ); // -> 1
context.config.get( 'bar' ); // -> 2
} );
// The default options can be overridden by the configuration passed to create().
Context
.create( { bar: 3 } )
.then( context => {
context.config.get( 'foo' ); // -> 1
context.config.get( 'bar' ); // -> 3
} );
new Context(config)实例化过程
- 通过传参
config
和defaultConfig
(参见例3)给Config
类生成实例赋给this.config
- 以
plugins
为key,builtinPlugins
(参见例2)为value,更新this.config
-
this.plugins = new PluginCollection( this, availablePlugins );
,PluginCollection实例化过程如下:
- 通过Locale生成对象赋给
this.locale
(详见ckeditor5/locale:国际化方案):
this.locale = new Locale( {
uiLanguage: typeof languageConfig === 'string' ? languageConfig : languageConfig.ui,
contentLanguage: this.config.get( 'language.content' )
} );
this.t = this.locale.t;
-
this.editors = new Collection();
,Collection
用于对editor实例进行诸如新增、删除和查找等管理工作。
基本使用示例
const collection = new Collection( [ { id: 'John' }, { id: 'Mike' } ] );
console.log( collection.get( 0 ) ); // -> { id: 'John' }
console.log( collection.get( 1 ) ); // -> { id: 'Mike' }
console.log( collection.get( 'Mike' ) ); // -> { id: 'Mike' }
// Or
const collection = new Collection();
collection.add( { id: 'John' } );
console.log( collection.get( 0 ) ); // -> { id: 'John' }
// Or you can always pass a configuration object as the last argument of the constructor:
const emptyCollection = new Collection( { idProperty: 'name' } );
emptyCollection.add( { name: 'John' } );
console.log( collection.get( 'John' ) ); // -> { name: 'John' }
const nonEmptyCollection = new Collection( [ { name: 'John' } ], { idProperty: 'name' } );
nonEmptyCollection.add( { name: 'George' } );
console.log( collection.get( 'George' ) ); // -> { name: 'George' }
console.log( collection.get( 'John' ) ); // -> { name: 'John' }
bindTo、as和using
// Binds and synchronizes the collection with another one.
// The binding can be a simple factory:
class FactoryClass {
constructor( data ) {
this.label = data.label;
}
}
const source = new Collection( { idProperty: 'label' } );
const target = new Collection();
target.bindTo( source ).as( FactoryClass );
source.add( { label: 'foo' } );
source.add( { label: 'bar' } );
console.log( target.length ); // 2
console.log( target.get( 1 ).label ); // 'bar'
source.remove( 0 );
console.log( target.length ); // 1
console.log( target.get( 0 ).label ); // 'bar'
// or the factory driven by a custom callback:
class FooClass {
constructor( data ) {
this.label = data.label;
}
}
class BarClass {
constructor( data ) {
this.label = data.label;
}
}
const source = new Collection( { idProperty: 'label' } );
const target = new Collection();
target.bindTo( source ).using( ( item ) => {
if ( item.label == 'foo' ) {
return new FooClass( item );
} else {
return new BarClass( item );
}
} );
source.add( { label: 'foo' } );
source.add( { label: 'bar' } );
console.log( target.length ); // 2
console.log( target.get( 0 ) instanceof FooClass ); // true
console.log( target.get( 1 ) instanceof BarClass ); // true
// or the factory out of property name:
const source = new Collection( { idProperty: 'label' } );
const target = new Collection();
target.bindTo( source ).using( 'label' );
source.add( { label: { value: 'foo' } } );
source.add( { label: { value: 'bar' } } );
console.log( target.length ); // 2
console.log( target.get( 0 ).value ); // 'foo'
console.log( target.get( 1 ).value ); // 'bar'
// It's possible to skip specified items by returning falsy value:
const source = new Collection();
const target = new Collection();
target.bindTo( source ).using( item => {
if ( item.hidden ) {
return null;
}
return item;
} );
source.add( { hidden: true } );
source.add( { hidden: false } );
console.log( source.length ); // 2
console.log( target.length ); // 1
-
this._contextOwner
初始化为null
,在_addEditor
方法中会对其赋值:
/*
Reference to the editor which created the context.
Null when the context was created outside of the editor.
It is used to destroy the context when removing the editor that has created the context.
*/
this._contextOwner = null;
在editor.js
中,会调用Context
方法和_addEditor
class Editor {
constructor(config = {}) {
this._context = config.context || new Context( { language: config.language } );
this._context._addEditor( this, !config.context );
}
}
- _addEditor
// Adds a reference to the editor which is used with this context.
// This method should only be used by the editor.
_addEditor( editor, isContextOwner ) {
if ( this._contextOwner ) {
/**
* Cannot add multiple editors to the context which is created by the editor.
*
* @error context-addeditor-private-context
*/
throw new CKEditorError( 'context-addeditor-private-context' );
}
this.editors.add( editor );
if ( isContextOwner ) {
this._contextOwner = editor;
}
}