Skip to content

Commit

Permalink
Merge pull request #12978 from ckeditor/ck/12950-full-page
Browse files Browse the repository at this point in the history
Feature (html-support): Added full page mode to preserve content outside of page body. Closes #12950.
  • Loading branch information
arkflpc authored Dec 12, 2022
2 parents 1d3a57a + 6118a33 commit eb79bc9
Show file tree
Hide file tree
Showing 18 changed files with 959 additions and 8 deletions.
34 changes: 34 additions & 0 deletions packages/ckeditor5-engine/src/controller/datacontroller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ export default class DataController extends EmitterMixin() {
ObservableMixin().prototype.decorate.call( this, 'init' as any );
ObservableMixin().prototype.decorate.call( this, 'set' as any );
ObservableMixin().prototype.decorate.call( this, 'get' as any );
ObservableMixin().prototype.decorate.call( this, 'toView' as any );
ObservableMixin().prototype.decorate.call( this, 'toModel' as any );

// Fire the `ready` event when the initialization has completed. Such low-level listener offers the possibility
// to plug into the initialization pipeline without interrupting the initialization flow.
Expand Down Expand Up @@ -259,6 +261,7 @@ export default class DataController extends EmitterMixin() {
* converters attached to {@link #downcastDispatcher} into a
* {@link module:engine/view/documentfragment~DocumentFragment view document fragment}.
*
* @fires toView
* @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} modelElementOrFragment
* Element or document fragment whose content will be converted.
* @param {Object} [options={}] Additional configuration that will be available through the
Expand Down Expand Up @@ -456,6 +459,7 @@ export default class DataController extends EmitterMixin() {
* When marker elements were converted during the conversion process, it will be set as a document fragment's
* {@link module:engine/model/documentfragment~DocumentFragment#markers static markers map}.
*
* @fires toModel
* @param {module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment} viewElementOrFragment
* The element or document fragment whose content will be converted.
* @param {module:engine/model/schema~SchemaContextDefinition} [context='$root'] Base context in which the view will
Expand Down Expand Up @@ -565,6 +569,24 @@ export default class DataController extends EmitterMixin() {
*
* @event get
*/

/**
* Event fired after the {@link #toView toView() method} has been run.
*
* The `toView` event is fired by the decorated {@link #toView} method.
* See {@link module:utils/observablemixin~ObservableMixin#decorate} for more information and samples.
*
* @event toView
*/

/**
* Event fired after the {@link #toModel toModel() method} has been run.
*
* The `toModel` event is fired by the decorated {@link #toModel} method.
* See {@link module:utils/observablemixin~ObservableMixin#decorate} for more information and samples.
*
* @event toModel
*/
}

// Helper function for downcast conversion.
Expand Down Expand Up @@ -659,3 +681,15 @@ export type DataControllerGetEvent = {
args: [ Parameters<DataController[ 'get' ]> ];
return: ReturnType<DataController[ 'get' ]>;
};

export type DataControllerToModelEvent = {
name: 'toModel';
args: [ Parameters<DataController[ 'toModel' ]> ];
return: ReturnType<DataController[ 'toModel' ]>;
};

export type DataControllerToViewEvent = {
name: 'toView';
args: [ Parameters<DataController[ 'toView' ]> ];
return: ReturnType<DataController[ 'toView' ]>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,10 @@ export default class HtmlDataProcessor implements DataProcessor {
* Converts an HTML string to its DOM representation. Returns a document fragment containing nodes parsed from
* the provided data.
*
* @private
* @param {String} data
* @returns {DocumentFragment}
*/
private _toDom( data: string ): DocumentFragment {
protected _toDom( data: string ): DocumentFragment {
// Wrap data with a <body> tag so leading non-layout nodes (like <script>, <style>, HTML comment)
// will be preserved in the body collection.
// Do it only for data that is not a full HTML document.
Expand Down
54 changes: 54 additions & 0 deletions packages/ckeditor5-engine/src/view/documentfragment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import type Node from './node';
export default class DocumentFragment extends EmitterMixin( TypeCheckable ) {
public readonly document: Document;
private readonly _children: Array<Node>;
private readonly _customProperties: Map<string | symbol, unknown>;

/**
* Creates new DocumentFragment instance.
Expand Down Expand Up @@ -59,6 +60,15 @@ export default class DocumentFragment extends EmitterMixin( TypeCheckable ) {
if ( children ) {
this._insertChild( 0, children );
}

/**
* Map of custom properties.
* Custom properties can be added to document fragment instance.
*
* @protected
* @member {Map}
*/
this._customProperties = new Map();
}

/**
Expand Down Expand Up @@ -112,6 +122,26 @@ export default class DocumentFragment extends EmitterMixin( TypeCheckable ) {
return null;
}

/**
* Returns the custom property value for the given key.
*
* @param {String|Symbol} key
* @returns {*}
*/
public getCustomProperty( key: string | symbol ): unknown {
return this._customProperties.get( key );
}

/**
* Returns an iterator which iterates over this document fragment's custom properties.
* Iterator provides `[ key, value ]` pairs for each stored property.
*
* @returns {Iterable.<*>}
*/
public* getCustomProperties(): Iterable<[ string | symbol, unknown ]> {
yield* this._customProperties.entries();
}

/**
* {@link module:engine/view/documentfragment~DocumentFragment#_insertChild Insert} a child node or a list of child nodes at the end
* and sets the parent of these nodes to this fragment.
Expand Down Expand Up @@ -212,6 +242,30 @@ export default class DocumentFragment extends EmitterMixin( TypeCheckable ) {
this.fire( 'change:' + type, node );
}

/**
* Sets a custom property. They can be used to add special data to elements.
*
* @see module:engine/view/downcastwriter~DowncastWriter#setCustomProperty
* @protected
* @param {String|Symbol} key
* @param {*} value
*/
public _setCustomProperty( key: string | symbol, value: unknown ): void {
this._customProperties.set( key, value );
}

/**
* Removes the custom property stored under the given key.
*
* @see module:engine/view/downcastwriter~DowncastWriter#removeCustomProperty
* @protected
* @param {String|Symbol} key
* @returns {Boolean} Returns true if property was removed.
*/
public _removeCustomProperty( key: string | symbol ): boolean {
return this._customProperties.delete( key );
}

// @if CK_DEBUG_ENGINE // printTree() {
// @if CK_DEBUG_ENGINE // let string = 'ViewDocumentFragment: [';

Expand Down
4 changes: 2 additions & 2 deletions packages/ckeditor5-engine/src/view/downcastwriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ export default class DowncastWriter {
* @param {*} value
* @param {module:engine/view/element~Element} element
*/
public setCustomProperty( key: string | symbol, value: unknown, element: Element ): void {
public setCustomProperty( key: string | symbol, value: unknown, element: Element | DocumentFragment ): void {
element._setCustomProperty( key, value );
}

Expand All @@ -567,7 +567,7 @@ export default class DowncastWriter {
* @param {module:engine/view/element~Element} element
* @returns {Boolean} Returns true if property was removed.
*/
public removeCustomProperty( key: string | symbol, element: Element ): boolean {
public removeCustomProperty( key: string | symbol, element: Element | DocumentFragment ): boolean {
return element._removeCustomProperty( key );
}

Expand Down
4 changes: 2 additions & 2 deletions packages/ckeditor5-engine/src/view/upcastwriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ export default class UpcastWriter {
* @param {*} value Custom property value to be stored.
* @param {module:engine/view/element~Element} element Element for which custom property will be set.
*/
public setCustomProperty( key: string | symbol, value: unknown, element: Element ): void {
public setCustomProperty( key: string | symbol, value: unknown, element: Element | DocumentFragment ): void {
element._setCustomProperty( key, value );
}

Expand All @@ -351,7 +351,7 @@ export default class UpcastWriter {
* @param {module:engine/view/element~Element} element Element from which the custom property will be removed.
* @returns {Boolean} Returns true if property was removed.
*/
public removeCustomProperty( key: string | symbol, element: Element ): boolean {
public removeCustomProperty( key: string | symbol, element: Element | DocumentFragment ): boolean {
return element._removeCustomProperty( key );
}

Expand Down
20 changes: 20 additions & 0 deletions packages/ckeditor5-engine/tests/controller/datacontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,16 @@ describe( 'DataController', () => {
upcastHelpers.elementToElement( { view: 'p', model: 'paragraph' } );
} );

it( 'should be decorated', () => {
const viewElement = parseView( '<p>foo</p>' );
const spy = sinon.spy();

data.on( 'toModel', spy );
data.toModel( viewElement );

sinon.assert.calledWithExactly( spy, sinon.match.any, [ viewElement ] );
} );

it( 'should convert content of an element #1', () => {
const viewElement = parseView( '<p>foo</p>' );
const output = data.toModel( viewElement );
Expand Down Expand Up @@ -622,6 +632,16 @@ describe( 'DataController', () => {
downcastHelpers.elementToElement( { model: 'div', view: 'div' } );
} );

it( 'should be decorated', () => {
const modelElement = parseModel( '<div><paragraph>foo</paragraph></div>', schema );
const spy = sinon.spy();

data.on( 'toView', spy );
data.toView( modelElement );

sinon.assert.calledWithExactly( spy, sinon.match.any, [ modelElement ] );
} );

it( 'should use #viewDocument as a parent for returned document fragments', () => {
const modelElement = parseModel( '<div><paragraph>foo</paragraph></div>', schema );
const viewDocumentFragment = data.toView( modelElement );
Expand Down
53 changes: 53 additions & 0 deletions packages/ckeditor5-engine/tests/view/documentfragment.js
Original file line number Diff line number Diff line change
Expand Up @@ -318,4 +318,57 @@ describe( 'DocumentFragment', () => {
expect( fragment.getChild( 0 ) ).to.equal( node2 );
} );
} );

describe( 'custom properties', () => {
it( 'should allow to set and get custom properties', () => {
const fragment = new DocumentFragment( document );

fragment._setCustomProperty( 'foo', 'bar' );

expect( fragment.getCustomProperty( 'foo' ) ).to.equal( 'bar' );
} );

it( 'should allow to add symbol property', () => {
const fragment = new DocumentFragment( document );
const symbol = Symbol( 'custom' );

fragment._setCustomProperty( symbol, 'bar' );

expect( fragment.getCustomProperty( symbol ) ).to.equal( 'bar' );
} );

it( 'should allow to remove custom property', () => {
const fragment = new DocumentFragment( document );
const symbol = Symbol( 'quix' );

fragment._setCustomProperty( 'bar', 'baz' );
fragment._setCustomProperty( symbol, 'test' );

expect( fragment.getCustomProperty( 'bar' ) ).to.equal( 'baz' );
expect( fragment.getCustomProperty( symbol ) ).to.equal( 'test' );

fragment._removeCustomProperty( 'bar' );
fragment._removeCustomProperty( symbol );

expect( fragment.getCustomProperty( 'bar' ) ).to.be.undefined;
expect( fragment.getCustomProperty( symbol ) ).to.be.undefined;
} );

it( 'should allow to iterate over custom properties', () => {
const fragment = new DocumentFragment( document );

fragment._setCustomProperty( 'foo', 1 );
fragment._setCustomProperty( 'bar', 2 );
fragment._setCustomProperty( 'baz', 3 );

const properties = Array.from( fragment.getCustomProperties() );

expect( properties[ 0 ][ 0 ] ).to.equal( 'foo' );
expect( properties[ 0 ][ 1 ] ).to.equal( 1 );
expect( properties[ 1 ][ 0 ] ).to.equal( 'bar' );
expect( properties[ 1 ][ 1 ] ).to.equal( 2 );
expect( properties[ 2 ][ 0 ] ).to.equal( 'baz' );
expect( properties[ 2 ][ 1 ] ).to.equal( 3 );
} );
} );
} );
18 changes: 18 additions & 0 deletions packages/ckeditor5-engine/tests/view/downcastwriter/writer.js
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,14 @@ describe( 'DowncastWriter', () => {

expect( element.getCustomProperty( 'foo' ) ).to.equal( 'bar' );
} );

it( 'should set custom property to given document fragment', () => {
const fragment = writer.createDocumentFragment();

writer.setCustomProperty( 'foo', 'bar', fragment );

expect( fragment.getCustomProperty( 'foo' ) ).to.equal( 'bar' );
} );
} );

describe( 'removeCustomProperty()', () => {
Expand All @@ -438,6 +446,16 @@ describe( 'DowncastWriter', () => {
writer.removeCustomProperty( 'foo', element );
expect( element.getCustomProperty( 'foo' ) ).to.be.undefined;
} );

it( 'should remove custom property from given document fragment', () => {
const fragment = writer.createDocumentFragment();

writer.setCustomProperty( 'foo', 'bar', fragment );
expect( fragment.getCustomProperty( 'foo' ) ).to.equal( 'bar' );

writer.removeCustomProperty( 'foo', fragment );
expect( fragment.getCustomProperty( 'foo' ) ).to.be.undefined;
} );
} );

describe( 'createPositionAt()', () => {
Expand Down
37 changes: 35 additions & 2 deletions packages/ckeditor5-engine/tests/view/upcastwriter.js
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ describe( 'UpcastWriter', () => {
} );

describe( 'setCustomProperty', () => {
it( 'should add or update custom property', () => {
it( 'should add or update custom property on element', () => {
const el = new Element( 'span' );

writer.setCustomProperty( 'prop1', 'foo', el );
Expand All @@ -553,10 +553,29 @@ describe( 'UpcastWriter', () => {
expect( el.getCustomProperty( 'prop2' ) ).to.equal( objectProperty );
expect( Array.from( el.getCustomProperties() ).length ).to.equal( 2 );
} );

it( 'should add or update custom property on document fragment', () => {
const fragment = new DocumentFragment();

writer.setCustomProperty( 'prop1', 'foo', fragment );
writer.setCustomProperty( 'prop2', 'bar', fragment );

expect( fragment.getCustomProperty( 'prop1' ) ).to.equal( 'foo' );
expect( fragment.getCustomProperty( 'prop2' ) ).to.equal( 'bar' );
expect( Array.from( fragment.getCustomProperties() ).length ).to.equal( 2 );

const objectProperty = { foo: 'bar' };

writer.setCustomProperty( 'prop2', objectProperty, fragment );

expect( fragment.getCustomProperty( 'prop1' ) ).to.equal( 'foo' );
expect( fragment.getCustomProperty( 'prop2' ) ).to.equal( objectProperty );
expect( Array.from( fragment.getCustomProperties() ).length ).to.equal( 2 );
} );
} );

describe( 'removeCustomProperty', () => {
it( 'should remove existing custom property', () => {
it( 'should remove existing custom property from element', () => {
const el = new Element( 'p' );

writer.setCustomProperty( 'prop1', 'foo', el );
Expand All @@ -570,6 +589,20 @@ describe( 'UpcastWriter', () => {
expect( Array.from( el.getCustomProperties() ).length ).to.equal( 0 );
} );

it( 'should remove existing custom property from document fragment', () => {
const fragment = new DocumentFragment();

writer.setCustomProperty( 'prop1', 'foo', fragment );

expect( fragment.getCustomProperty( 'prop1' ) ).to.equal( 'foo' );
expect( Array.from( fragment.getCustomProperties() ).length ).to.equal( 1 );

writer.removeCustomProperty( 'prop1', fragment );

expect( fragment.getCustomProperty( 'prop1' ) ).to.undefined;
expect( Array.from( fragment.getCustomProperties() ).length ).to.equal( 0 );
} );

it( 'should have no effect if custom property does not exists', () => {
const el = new Element( 'h1' );

Expand Down
1 change: 1 addition & 0 deletions packages/ckeditor5-html-support/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@ckeditor/ckeditor5-alignment": "^35.3.2",
"@ckeditor/ckeditor5-basic-styles": "^35.3.2",
"@ckeditor/ckeditor5-block-quote": "^35.3.2",
"@ckeditor/ckeditor5-clipboard": "^35.3.2",
"@ckeditor/ckeditor5-cloud-services": "^35.3.2",
"@ckeditor/ckeditor5-code-block": "^35.3.2",
"@ckeditor/ckeditor5-core": "^35.3.2",
Expand Down
Loading

0 comments on commit eb79bc9

Please sign in to comment.