Browse Source

Added prototype of fake selection manual test.

Szymon Kupś 9 years ago
parent
commit
61263199e5

+ 15 - 0
packages/ckeditor5-engine/tests/view/manual/fakeselection.html

@@ -0,0 +1,15 @@
+<head>
+	<link rel="stylesheet" href="%APPS_DIR%ckeditor/build/modules/amd/theme/ckeditor.css">
+</head>
+
+<div id="editor">
+	<p> this is an <span>editor</span> instance <span>foo bar</span></p>
+	<img src="http://a.cksource.com/e/1/img/logo-ckeditor-h100.png" />
+	<p>some text after image and <span>two widgets</span><span>one after another</span> the end</p>
+	<p><span>only widget in block</span></p>
+	<p>another line</p>
+</div>
+
+<div style="height: 1500px; width: 1500px; background: #eee; outline: 2px dashed #ddd; margin: 1em 0; padding: 1em;">
+	* I'm a dummy div to enable scrolling in this test. *
+</div>

+ 196 - 0
packages/ckeditor5-engine/tests/view/manual/fakeselection.js

@@ -0,0 +1,196 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals console:false, document, window */
+
+import ClassicEditor from '/ckeditor5/editor-classic/classic.js';
+import testUtils from '/tests/utils/_utils/utils.js';
+import Feature from '/ckeditor5/core/feature.js';
+import buildModelConverter from '/ckeditor5/engine/conversion/buildmodelconverter.js';
+import buildViewConverter from '/ckeditor5/engine/conversion/buildviewconverter.js';
+import ViewContainerElement from '/ckeditor5/engine/view/containerelement.js';
+import ViewRange from '/ckeditor5/engine/view/range.js';
+import ModelElement from '/ckeditor5/engine/model/element.js';
+import ModelRange from '/ckeditor5/engine/model/range.js';
+import ModelLiveRange from '/ckeditor5/engine/model/liverange.js';
+import ClickObserver from '/ckeditor5/engine/view/observer/clickobserver.js';
+
+let editor, editable, observer;
+
+class Test extends Feature {
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		const data = editor.data;
+		const editing = editor.editing;
+		const view  = editing.view;
+		const document = editor.document;
+		const selected = new Set();
+
+		// Handle clicks on view elements.
+		view.addObserver( ClickObserver );
+		editor.editing.view.on( 'click', ( eventInfo, domEventData ) => {
+			const target = domEventData.target;
+
+			if ( target.name == 'span' ) {
+				const viewRange = ViewRange.createOn( target );
+				const modelRange = ModelLiveRange.createFromRange( editing.mapper.toModelRange( viewRange ) );
+
+				// Put selection around span element.
+				// TODO: Do we need this inside enq changes? Batch needed?
+				document.enqueueChanges( ( ) => {
+					document.selection.setRanges( [ modelRange ] );
+				} );
+			}
+		} );
+
+		// Handle selection conversion.
+		editing.modelToView.on( 'selection', ( evt, data, consumable, conversionApi ) => {
+			const viewSelection = conversionApi.viewSelection;
+			const range = data.selection.getFirstRange();
+			const nodeAfterStart = range.start.nodeAfter;
+
+			// remove selection from all selected widgets
+			for ( let viewElement of selected ) {
+				viewElement.setStyle( 'border', '2px solid yellow' );
+			}
+
+			// This could be just one element instead of set.
+			selected.clear();
+
+			if ( !data.selection.isCollapsed && nodeAfterStart && nodeAfterStart.name == 'span' && ModelRange.createOn( nodeAfterStart ).isEqual( range ) ) {
+				viewSelection.setFake( true, { label: 'span fake selection' } );
+				const viewElement = conversionApi.mapper.toViewElement( nodeAfterStart );
+				viewElement.setStyle( 'border', '2px solid red' );
+				selected.add( viewElement );
+			}
+		}, { priority: 'low' } );
+
+		// Allow bold attribute on all inline nodes.
+		// document.schema.allow( { name: '$inline', attributes: [ 'span' ] } );
+		document.schema.registerItem( 'span', '$inline' );
+		document.schema.allow( { name: 'span', inside: '$block' } );
+
+		// Build converter from model to view for data and editing pipelines.
+		buildModelConverter().for( data.modelToView, editing.modelToView )
+			.fromElement( 'span' )
+			// .toElement( new ViewContainerElement( 'span' ) );
+			.toElement( () => {
+				const element = new ViewContainerElement( 'span', {
+					contenteditable: 'false',
+					style: 'background-color: yellow; color: black; border: 2px solid yellow;'
+				} );
+
+				return widgetize( element );
+			} );
+
+		// Build converter from view to model for data pipeline.
+		buildViewConverter().for( data.viewToModel )
+			.fromElement( 'span' )
+			.toElement( 'span' );
+	}
+}
+
+class BasicImage extends Feature {
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		const data = editor.data;
+		const editing = editor.editing;
+		const view  = editing.view;
+		const document = editor.document;
+		const selected = new Set();
+
+		// Handle clicks on view elements.
+		view.addObserver( ClickObserver );
+		editor.editing.view.on( 'click', ( eventInfo, domEventData ) => {
+			const target = domEventData.target;
+
+			if ( target.name == 'img' ) {
+				const viewRange = ViewRange.createOn( target );
+				const modelRange = ModelLiveRange.createFromRange( editing.mapper.toModelRange( viewRange ) );
+
+				document.enqueueChanges( ( ) => {
+					document.selection.setRanges( [ modelRange ] );
+				} );
+			}
+		} );
+
+		// Handle selection conversion.
+		editing.modelToView.on( 'selection', ( evt, data, consumable, conversionApi ) => {
+			const viewSelection = conversionApi.viewSelection;
+			const range = data.selection.getFirstRange();
+			const nodeAfterStart = range.start.nodeAfter;
+
+			// remove selection from all selected widgets
+			for ( let viewElement of selected ) {
+				viewElement.setStyle( 'border', '2px solid yellow' );
+			}
+
+			// This could be just one element instead of set.
+			selected.clear();
+
+			if ( !data.selection.isCollapsed && nodeAfterStart && nodeAfterStart.name == 'image' && ModelRange.createOn( nodeAfterStart ).isEqual( range ) ) {
+				viewSelection.setFake( true, { label: 'image fake selection' } );
+				const viewElement = conversionApi.mapper.toViewElement( nodeAfterStart );
+				viewElement.setStyle( 'border', '2px solid red' );
+				selected.add( viewElement );
+			}
+		}, { priority: 'low' } );
+
+		// Allow bold attribute on all inline nodes.
+		// document.schema.allow( { name: '$inline', attributes: [ 'span' ] } );
+		document.schema.registerItem( 'image', '$block' );
+		document.schema.allow( { name: 'image', attributes: [ 'src', 'alt' ] } );
+
+		// Build converter from model to view for data and editing pipelines.
+		buildModelConverter().for( data.modelToView, editing.modelToView )
+			.fromElement( 'image' )
+			.toElement( ( data ) => {
+				return widgetize( new ViewContainerElement( 'img', {
+					src: data.item.getAttribute( 'src' ),
+					alt: data.item.getAttribute( 'alt' ),
+					style: 'border: 2px solid yellow'
+				} ) );
+			} );
+
+		// Build converter from view to model for data pipeline.
+		buildViewConverter().for( data.viewToModel )
+			.fromElement( 'img' ).consuming( { name: true, attributes: [ 'src', 'alt' ] } )
+			.toElement( ( viewElement ) => new ModelElement( 'image', {
+				src: viewElement.getAttribute( 'src' ),
+				alt: viewElement.getAttribute( 'alt' )
+			} ) );
+	}
+}
+
+function initEditor() {
+	ClassicEditor.create( document.querySelector( '#editor' ), {
+		features: [ 'enter', 'typing', 'paragraph', 'undo', 'heading', Test, BasicImage ],
+		toolbar: [ 'headings' ]
+	} )
+		.then( newEditor => {
+			window.editor = editor = newEditor;
+			window.editable = editable = editor.editing.view.getRoot();
+
+			observer = testUtils.createObserver();
+			observer.observe( 'Editable', editable, [ 'isFocused' ] );
+		} )
+		.catch( err => {
+			console.error( err.stack );
+		} );
+}
+
+initEditor();
+
+function widgetize( element ) {
+	element.isWidget = true;
+
+	return element;
+}

+ 4 - 0
packages/ckeditor5-engine/tests/view/manual/fakeselection.md

@@ -0,0 +1,4 @@
+@bender-ui: collapsed
+@bender-tags: view
+
+## Fake selection