Răsfoiți Sursa

WIP: Introduced link command and link feature.

Oskar Wrobel 9 ani în urmă
părinte
comite
89f6387c8c

+ 53 - 0
packages/ckeditor5-link/src/link.js

@@ -0,0 +1,53 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import Feature from '../core/feature.js';
+import LinkEngine from './linkengine.js';
+import ButtonController from '../ui/button/button.js';
+import ButtonView from '../ui/button/buttonview.js';
+import Model from '../ui/model.js';
+
+/**
+ * The bold feature. It introduces the Bold button and the <kbd>Ctrl+B</kbd> keystroke.
+ *
+ * It uses the {@link basic-styles.BoldEngine bold engine feature}.
+ *
+ * @memberOf basic-styles
+ * @extends core.Feature
+ */
+export default class Link extends Feature {
+	/**
+	 * @inheritDoc
+	 */
+	static get requires() {
+		return [ LinkEngine ];
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		const t = editor.t;
+		const command = editor.commands.get( 'link' );
+
+		// Create button model.
+		const buttonModel = new Model( {
+			isEnabled: true,
+			isOn: false,
+			label: t( 'Link' ),
+			icon: 'link'
+		} );
+
+		// Bind button model to command.
+		buttonModel.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
+
+		// Execute command.
+		this.listenTo( buttonModel, 'execute', () => editor.execute( 'link', 'http://www.cksource.com' ) );
+
+		// Add bold button to feature components.
+		editor.ui.featureComponents.add( 'link', ButtonController, ButtonView, buttonModel );
+	}
+}

+ 73 - 0
packages/ckeditor5-link/src/linkcommand.js

@@ -0,0 +1,73 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import Command from '../core/command/command.js';
+import getSchemaValidRanges from '../core/command/helpers/getschemavalidranges.js';
+import isAttributeAllowedInSelection from '../core/command/helpers/isattributeallowedinselection.js';
+
+/**
+ * An extension of basic {@link core.command.Command} class, which provides utilities for a command that toggle a single
+ * attribute on a text or element with value `true`. ToggleAttributeCommand uses {@link engine.model.Document#selection}
+ * to decide which nodes (if any) should be changed, and applies or removes attributes from them.
+ * See {@link engine.view.Converter#execute} for more.
+ *
+ * The command checks {@link engine.model.Document#schema} to decide if it should be enabled.
+ * See {@link engine.view.Converter#checkSchema} for more.
+ *
+ * @memberOf core.command
+ */
+export default class ToggleAttributeCommand extends Command {
+	/**
+	 * Checks if {@link engine.model.Document#schema} allows to create attribute in {@link engine.model.Document#selection}
+	 *
+	 * @private
+	 * @returns {Boolean}
+	 */
+	_checkEnabled() {
+		const document = this.editor.document;
+
+		return isAttributeAllowedInSelection( this.attributeKey, document.selection, document.schema );
+	}
+
+	/**
+	 * Executes the command: adds or removes attributes to nodes or selection.
+	 *
+	 * If the command is active (`value == true`), it will remove attributes. Otherwise, it will set attributes.
+	 *
+	 * The execution result differs, depending on the {@link engine.model.Document#selection}:
+	 * * if selection is on a range, the command applies the attribute on all nodes in that ranges
+	 * (if they are allowed to have this attribute by the {@link engine.model.Schema schema}),
+	 * * if selection is collapsed in non-empty node, the command applies attribute to the {@link engine.model.Document#selection}
+	 * itself (note that typed characters copy attributes from selection),
+	 * * if selection is collapsed in empty node, the command applies attribute to the parent node of selection (note
+	 * that selection inherits all attributes from a node if it is in empty node).
+	 *
+	 * If the command is disabled (`isEnabled == false`) when it is executed, nothing will happen.
+	 *
+	 * @private
+	 * @param {Boolean} [forceValue] If set it will force command behavior. If `true`, command will apply attribute,
+	 * otherwise command will remove attribute. If not set, command will look for it's current value to decide what it should do.
+	 */
+	_doExecute( href ) {
+		const document = this.editor.document;
+		const selection = document.selection;
+
+		if ( selection.isCollapsed ) {
+			selection.setAttribute( 'link', href );
+		} else {
+			// If selection has non-collapsed ranges, we change attribute on nodes inside those ranges.
+			document.enqueueChanges( () => {
+				const ranges = getSchemaValidRanges( 'link', selection.getRanges(), document.schema );
+
+				// Keep it as one undo step.
+				const batch = document.batch();
+
+				for ( let range of ranges ) {
+					batch.setAttribute( range, 'link', href );
+				}
+			} );
+		}
+	}
+}

+ 5 - 1
packages/ckeditor5-link/src/engine.js → packages/ckeditor5-link/src/linkengine.js

@@ -7,6 +7,7 @@ import Feature from '../core/feature.js';
 import buildModelConverter from '../engine/conversion/buildmodelconverter.js';
 import buildViewConverter from '../engine/conversion/buildviewconverter.js';
 import AttributeElement from '../engine/view/attributeelement.js';
+import LinkCommand from './linkcommand.js';
 
 /**
  * The link engine feature.
@@ -16,7 +17,7 @@ import AttributeElement from '../engine/view/attributeelement.js';
  * @memberOf link
  * @extends core.Feature
  */
-export default class BoldEngine extends Feature {
+export default class LinkEngine extends Feature {
 	/**
 	 * @inheritDoc
 	 */
@@ -40,5 +41,8 @@ export default class BoldEngine extends Feature {
 				key: 'link',
 				value: viewElement.getAttribute( 'href' )
 			} ) );
+
+		// Create link command.
+		editor.commands.set( 'link', new LinkCommand( editor ) );
 	}
 }

+ 13 - 1
packages/ckeditor5-link/tests/engine.js → packages/ckeditor5-link/tests/linkengine.js

@@ -3,7 +3,8 @@
  * For licensing, see LICENSE.md.
  */
 
-import LinkEngine from '/ckeditor5/link/engine.js';
+import LinkEngine from '/ckeditor5/link/linkengine.js';
+import LinkCommand from '/ckeditor5/link/linkcommand.js';
 import VirtualTestEditor from '/tests/core/_utils/virtualtesteditor.js';
 import { getData as getModelData, setData as setModelData } from '/tests/engine/_utils/model.js';
 import { getData as getViewData } from '/tests/engine/_utils/view.js';
@@ -32,6 +33,17 @@ describe( 'LinkEngine', () => {
 		expect( doc.schema.check( { name: '$inline', attributes: [ 'link' ] } ) ).to.be.true;
 	} );
 
+	describe( 'command', () => {
+		it( 'should register link command', () => {
+			expect( editor.commands.has( 'link' ) ).to.be.true;
+
+			const command = editor.commands.get( 'link' );
+
+			expect( command ).to.be.instanceOf( LinkCommand );
+			expect( command ).to.have.property( 'attributeKey', 'link' );
+		} );
+	} );
+
 	describe( 'data pipeline conversions', () => {
 		it( 'should convert `<a href="url">` to `bold="url"` attribute', () => {
 			editor.setData( '<a href="url">foo</a>bar' );