Jelajahi Sumber

Merge pull request #1 from ckeditor/i/6536

Feature: Implemented the select all feature. Closes ckeditor/ckeditor5#6536.
Piotrek Koszuliński 5 tahun lalu
induk
melakukan
4bae781773
30 mengubah file dengan 805 tambahan dan 3 penghapusan
  1. 16 0
      packages/ckeditor5-select-all/.editorconfig
  2. 12 0
      packages/ckeditor5-select-all/.eslintrc.js
  3. 18 0
      packages/ckeditor5-select-all/.gitattributes
  4. 9 0
      packages/ckeditor5-select-all/.github/PULL_REQUEST_TEMPLATE.md
  5. 1 0
      packages/ckeditor5-select-all/.gitignore
  6. 3 0
      packages/ckeditor5-select-all/.stylelintrc
  7. 2 0
      packages/ckeditor5-select-all/CHANGELOG.md
  8. 4 0
      packages/ckeditor5-select-all/CONTRIBUTING.md
  9. 17 0
      packages/ckeditor5-select-all/LICENSE.md
  10. 10 1
      packages/ckeditor5-select-all/README.md
  11. 0 0
      packages/ckeditor5-select-all/docs/_snippets/features/build-select-all-source.html
  12. 16 0
      packages/ckeditor5-select-all/docs/_snippets/features/build-select-all-source.js
  13. 13 0
      packages/ckeditor5-select-all/docs/_snippets/features/select-all.html
  14. 33 0
      packages/ckeditor5-select-all/docs/_snippets/features/select-all.js
  15. 32 0
      packages/ckeditor5-select-all/docs/api/select-all.md
  16. 62 0
      packages/ckeditor5-select-all/docs/features/select-all.md
  17. 17 2
      packages/ckeditor5-select-all/package.json
  18. 38 0
      packages/ckeditor5-select-all/src/selectall.js
  19. 39 0
      packages/ckeditor5-select-all/src/selectallcommand.js
  20. 49 0
      packages/ckeditor5-select-all/src/selectallediting.js
  21. 61 0
      packages/ckeditor5-select-all/src/selectallui.js
  22. TEMPAT SAMPAH
      packages/ckeditor5-select-all/tests/manual/sample.jpg
  23. 21 0
      packages/ckeditor5-select-all/tests/manual/selectall.html
  24. 28 0
      packages/ckeditor5-select-all/tests/manual/selectall.js
  25. 18 0
      packages/ckeditor5-select-all/tests/manual/selectall.md
  26. 18 0
      packages/ckeditor5-select-all/tests/selectall.js
  27. 107 0
      packages/ckeditor5-select-all/tests/selectallcommand.js
  28. 80 0
      packages/ckeditor5-select-all/tests/selectallediting.js
  29. 80 0
      packages/ckeditor5-select-all/tests/selectallui.js
  30. 1 0
      packages/ckeditor5-select-all/theme/icons/select-all.svg

+ 16 - 0
packages/ckeditor5-select-all/.editorconfig

@@ -0,0 +1,16 @@
+# Configurations to normalize the IDE behavior.
+# http://editorconfig.org/
+
+root = true
+
+[*]
+indent_style = tab
+tab_width = 4
+charset = utf-8
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[package.json]
+indent_style = space
+tab_width = 2

+ 12 - 0
packages/ckeditor5-select-all/.eslintrc.js

@@ -0,0 +1,12 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* eslint-env node */
+
+'use strict';
+
+module.exports = {
+	extends: 'ckeditor5'
+};

+ 18 - 0
packages/ckeditor5-select-all/.gitattributes

@@ -0,0 +1,18 @@
+*			text=auto
+
+*.htaccess	eol=lf
+*.cgi		eol=lf
+*.sh		eol=lf
+
+*.css		text
+*.htm		text
+*.html		text
+*.js		text
+*.json		text
+*.php		text
+*.txt		text
+*.md		text
+
+*.png		-text
+*.gif		-text
+*.jpg		-text

+ 9 - 0
packages/ckeditor5-select-all/.github/PULL_REQUEST_TEMPLATE.md

@@ -0,0 +1,9 @@
+### Suggested merge commit message ([convention](https://github.com/ckeditor/ckeditor5-design/wiki/Git-commit-message-convention))
+
+Type: Message. Closes ckeditor/ckeditor5#000.
+
+---
+
+### Additional information
+
+*For example – encountered issues, assumptions you had to make, other affected tickets, etc.*

+ 1 - 0
packages/ckeditor5-select-all/.gitignore

@@ -0,0 +1 @@
+node_modules/

+ 3 - 0
packages/ckeditor5-select-all/.stylelintrc

@@ -0,0 +1,3 @@
+{
+	"extends": "stylelint-config-ckeditor5"
+}

+ 2 - 0
packages/ckeditor5-select-all/CHANGELOG.md

@@ -0,0 +1,2 @@
+Changelog
+=========

+ 4 - 0
packages/ckeditor5-select-all/CONTRIBUTING.md

@@ -0,0 +1,4 @@
+Contributing
+========================================
+
+See the [official contributors' guide to CKEditor 5](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html) to learn more.

+ 17 - 0
packages/ckeditor5-select-all/LICENSE.md

@@ -0,0 +1,17 @@
+Software License Agreement
+==========================
+
+**CKEditor 5 Select All Feature** – https://github.com/ckeditor/ckeditor5-select-all <br>
+Copyright (c) 2003-2020, [CKSource](http://cksource.com) Frederico Knabben. All rights reserved.
+
+Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html).
+
+Sources of Intellectual Property Included in CKEditor
+-----------------------------------------------------
+
+Where not otherwise indicated, all CKEditor content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, CKEditor will incorporate work done by developers outside of CKSource with their express permission.
+
+Trademarks
+----------
+
+**CKEditor** is a trademark of [CKSource](http://cksource.com) Frederico Knabben. All other brand and product names are trademarks, registered trademarks or service marks of their respective holders.

+ 10 - 1
packages/ckeditor5-select-all/README.md

@@ -8,4 +8,13 @@ CKEditor 5 select all feature
 [![Dependency Status](https://david-dm.org/ckeditor/ckeditor5-select-all/status.svg)](https://david-dm.org/ckeditor/ckeditor5-select-all)
 [![devDependency Status](https://david-dm.org/ckeditor/ckeditor5-select-all/dev-status.svg)](https://david-dm.org/ckeditor/ckeditor5-select-all?type=dev)
 
-This is an initial package for development purposes. It does not contain code yet.
+This package implements the select all feature for CKEditor 5.
+
+## Documentation
+
+See the [`@ckeditor/ckeditor5-select-all` package](https://ckeditor.com/docs/ckeditor5/latest/api/select-all.html) in [CKEditor 5 documentation](https://ckeditor.com/docs/ckeditor5/latest/).
+
+## License
+
+Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html). For full details about the license, please check the `LICENSE.md` file or [https://ckeditor.com/legal/ckeditor-oss-license](https://ckeditor.com/legal/ckeditor-oss-license).
+

+ 0 - 0
packages/ckeditor5-select-all/docs/_snippets/features/build-select-all-source.html


+ 16 - 0
packages/ckeditor5-select-all/docs/_snippets/features/build-select-all-source.js

@@ -0,0 +1,16 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* globals window */
+
+import ClassicEditor from '@ckeditor/ckeditor5-build-classic/src/ckeditor';
+
+import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
+import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
+import Underline from '@ckeditor/ckeditor5-basic-styles/src/underline';
+
+ClassicEditor.builtinPlugins.push( Bold, Italic, Underline );
+
+window.ClassicEditor = ClassicEditor;

+ 13 - 0
packages/ckeditor5-select-all/docs/_snippets/features/select-all.html

@@ -0,0 +1,13 @@
+<div id="editor">
+	<h2>The three greatest things you learn from traveling</h2>
+
+	<p>Like all the great things on earth traveling teaches us by example. Here are some of the most precious lessons I’ve learned over the years of traveling.</p>
+
+	<figure class="image image-style-side"><img src="%BASE_PATH%/assets/img/volcano.jpg" alt="A lone wanderer looking at Mount Bromo volcano in Indonesia.">
+		<figcaption>Leaving your comfort zone might lead you to such beautiful sceneries like this one.</figcaption>
+	</figure>
+
+	<h3>Appreciation of diversity</h3>
+
+	<p>Getting used to an entirely different culture can be challenging. While it’s also nice to learn about cultures online or from books, nothing comes close to experiencing cultural diversity in person. You learn to appreciate each and every single one of the differences while you become more culturally fluid.</p>
+</div>

+ 33 - 0
packages/ckeditor5-select-all/docs/_snippets/features/select-all.js

@@ -0,0 +1,33 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* globals ClassicEditor, console, window, document */
+
+ClassicEditor
+	.create( document.querySelector( '#editor' ), {
+		toolbar: {
+			items: [
+				'heading',
+				'|',
+				'bold',
+				'italic',
+				'underline',
+				'link',
+				'insertTable',
+				'|',
+				'undo',
+				'redo',
+				'|',
+				'selectAll'
+			],
+			viewportTopOffset: window.getViewportTopOffsetConfig()
+		}
+	} )
+	.then( editor => {
+		window.editor = editor;
+	} )
+	.catch( err => {
+		console.error( err.stack );
+	} );

+ 32 - 0
packages/ckeditor5-select-all/docs/api/select-all.md

@@ -0,0 +1,32 @@
+---
+category: api-reference
+---
+
+# CKEditor 5 Select all feature
+
+[![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-select-all.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-select-all)
+
+This package implements Select all feature for CKEditor 5.
+
+## Documentation
+
+See the {@link module:select-all/selectall~SelectAll} plugin documentation.
+
+This plugin is usually enabled by the {@link module:essentials/essentials~Essentials} plugin from the {@link api/essentials `@ckeditor/ckeditor5-essentials`} package (which also enables other fundamental editing features).
+
+## Installation
+
+```bash
+npm install --save @ckeditor/ckeditor5-select-all
+```
+
+## Contribute
+
+The source code of this package is available on GitHub in https://github.com/ckeditor/ckeditor5-select-all.
+
+## External links
+
+* [`@ckeditor/ckeditor5-select-all` on npm](https://www.npmjs.com/package/@ckeditor/ckeditor5-select-all)
+* [`ckeditor/ckeditor5-select-all` on GitHub](https://github.com/ckeditor/ckeditor5-select-all)
+* [Issue tracker](https://github.com/ckeditor/ckeditor5/issues)
+* [Changelog](https://github.com/ckeditor/ckeditor5-select-all/blob/master/CHANGELOG.md)

+ 62 - 0
packages/ckeditor5-select-all/docs/features/select-all.md

@@ -0,0 +1,62 @@
+---
+title: Select all
+category: features
+---
+
+{@snippet features/build-select-all-source}
+
+The {@link module:select-all/selectall~SelectAll} feature allows selecting the entire content of the editor using the <kbd>Ctrl/⌘</kbd>+<kbd>A</kbd> keystroke or a toolbar button.
+
+## Demo
+
+Press <kbd>Ctrl/⌘</kbd>+<kbd>A</kbd> or use the toolbar button to select the entire content of the editor. Note that when editing an image caption, the selection will only expand to the boundaries of the caption.
+
+{@snippet features/select-all}
+
+## Installation
+
+<info-box info>
+	This feature is enabled by default in all builds (loaded by the {@link module:essentials/essentials~Essentials} plugin). The installation instructions are for developers interested in building their own, custom rich text editor.
+</info-box>
+
+To add this feature to your editor, install the [`@ckeditor/ckeditor5-select-all`](https://www.npmjs.com/package/@ckeditor/ckeditor5-select-all) package:
+
+```bash
+npm install --save @ckeditor/ckeditor5-select-all
+```
+
+Then add the `SelectAll` plugin to your plugin list:
+
+```js
+import SelectAll from '@ckeditor/ckeditor5-select-all/src/selectall';
+
+ClassicEditor
+	.create( document.querySelector( '#editor' ), {
+		// Load the plugin.
+		plugins: [ SelectAll, ... ],
+
+		// Display the "Select all" button in the toolbar.
+		toolbar: [ 'selectAll', ... ],
+	} )
+	.then( ... )
+	.catch( ... );
+```
+
+## Common API
+
+The {@link module:select-all/selectall~SelectAll} plugin registers the `'selectAll'` UI button component and the `'selectAll'` command implemented by {@link module:select-all/selectallcommand~SelectAllCommand}.
+
+The command can be executed using the {@link module:core/editor/editor~Editor#execute `editor.execute()`} method:
+
+```js
+// Select the entire content of the editor.
+editor.execute( 'selectAll' );
+```
+
+<info-box>
+	We recommend using the official {@link framework/guides/development-tools#ckeditor-5-inspector CKEditor 5 inspector} for development and debugging. It will give you tons of useful information about the state of the editor such as internal data structures, selection, commands, and many more.
+</info-box>
+
+## Contribute
+
+The source code of the feature is available on GitHub in https://github.com/ckeditor/ckeditor5-select-all.

+ 17 - 2
packages/ckeditor5-select-all/package.json

@@ -8,11 +8,22 @@
     "ckeditor 5",
     "ckeditor5-lib"
   ],
+  "dependencies": {
+    "@ckeditor/ckeditor5-core": "^18.0.0",
+    "@ckeditor/ckeditor5-ui": "^18.0.0",
+    "@ckeditor/ckeditor5-utils": "^18.0.0"
+  },
   "devDependencies": {
+    "@ckeditor/ckeditor5-engine": "^18.0.0",
+    "@ckeditor/ckeditor5-image": "^18.0.0",
+    "@ckeditor/ckeditor5-paragraph": "^18.0.0",
+    "@ckeditor/ckeditor5-table": "^18.0.0",
     "eslint": "^5.5.0",
     "eslint-config-ckeditor5": "^2.0.0",
     "husky": "^2.4.1",
-    "lint-staged": "^8.2.1"
+    "lint-staged": "^8.2.1",
+    "stylelint": "^11.1.1",
+    "stylelint-config-ckeditor5": "^1.0.0"
   },
   "engines": {
     "node": ">=8.0.0",
@@ -32,11 +43,15 @@
     "theme"
   ],
   "scripts": {
-    "lint": "eslint --quiet '**/*.js'"
+    "lint": "eslint --quiet '**/*.js'",
+    "stylelint": "stylelint --quiet --allow-empty-input 'theme/**/*.css' 'docs/**/*.css'"
   },
   "lint-staged": {
     "**/*.js": [
       "eslint --quiet"
+    ],
+    "**/*.css": [
+      "stylelint --quiet --allow-empty-input"
     ]
   },
   "eslintIgnore": [

+ 38 - 0
packages/ckeditor5-select-all/src/selectall.js

@@ -0,0 +1,38 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module select-all/selectall
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import SelectAllEditing from './selectallediting';
+import SelectAllUI from './selectallui';
+
+/**
+ * The select all feature.
+ *
+ * This is a "glue" plugin which loads the {@link module:select-all/selectallediting~SelectAllEditing select all editing feature}
+ * and the {@link module:select-all/selectallui~SelectAllUI select all UI feature}.
+ *
+ * Please refer to the documentation of individual features to learn more.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class SelectAll extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get requires() {
+		return [ SelectAllEditing, SelectAllUI ];
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'SelectAll';
+	}
+}

+ 39 - 0
packages/ckeditor5-select-all/src/selectallcommand.js

@@ -0,0 +1,39 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module select-all/selectallcommand
+ */
+
+import Command from '@ckeditor/ckeditor5-core/src/command';
+
+/**
+ * The select all command.
+ *
+ * It is used by the {@link module:select-all/selectallediting~SelectAllEditing select all editing feature} to handle
+ * the <kbd>Ctrl/⌘</kbd>+<kbd>A</kbd> keystroke.
+ *
+ * Executing this command changes the {@glink framework/guides/architecture/editing-engine#model model}
+ * selection so it contains the entire content of the editable root of the editor the selection is
+ * {@link module:engine/model/selection~Selection#anchor anchored} in.
+ *
+ * If the selection was anchored in a {@glink framework/guides/tutorials/implementing-a-block-widget nested editable}
+ * (e.g. a caption of an image), the new selection will contain its entire content.
+ *
+ * @extends module:core/command~Command
+ */
+export default class SelectAllCommand extends Command {
+	/**
+	 * @inheritDoc
+	 */
+	execute() {
+		const model = this.editor.model;
+		const limitElement = model.schema.getLimitElement( model.document.selection );
+
+		model.change( writer => {
+			writer.setSelection( limitElement, 'in' );
+		} );
+	}
+}

+ 49 - 0
packages/ckeditor5-select-all/src/selectallediting.js

@@ -0,0 +1,49 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module select-all/selectallediting
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import { getCode, parseKeystroke } from '@ckeditor/ckeditor5-utils/src/keyboard';
+import SelectAllCommand from './selectallcommand';
+
+const SELECT_ALL_KEYSTROKE = parseKeystroke( 'Ctrl+A' );
+
+/**
+ * The select all editing feature.
+ *
+ * It registers the `'selectAll'` {@link module:select-all/selectallcommand~SelectAllCommand command}
+ * and the <kbd>Ctrl/⌘</kbd>+<kbd>A</kbd> keystroke listener which executes it.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class SelectAllEditing extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'SelectAllEditing';
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		const view = editor.editing.view;
+		const viewDocument = view.document;
+
+		editor.commands.add( 'selectAll', new SelectAllCommand( editor ) );
+
+		this.listenTo( viewDocument, 'keydown', ( eventInfo, domEventData ) => {
+			if ( getCode( domEventData ) === SELECT_ALL_KEYSTROKE ) {
+				editor.execute( 'selectAll' );
+				domEventData.preventDefault();
+			}
+		} );
+	}
+}

+ 61 - 0
packages/ckeditor5-select-all/src/selectallui.js

@@ -0,0 +1,61 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module select-all/selectallui
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+
+import selectAllIcon from '../theme/icons/select-all.svg';
+
+/**
+ * The select all UI feature.
+ *
+ * It registers the `'selectAll'` UI button in the editor's
+ * {@link module:ui/componentfactory~ComponentFactory component factory}. When clicked, the button
+ * executes the {@link module:select-all/selectallcommand~SelectAllCommand select all command}.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class SelectAllUI extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'SelectAllUI';
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+
+		editor.ui.componentFactory.add( 'selectAll', locale => {
+			const command = editor.commands.get( 'selectAll' );
+			const view = new ButtonView( locale );
+			const t = locale.t;
+
+			view.set( {
+				label: t( 'Select all' ),
+				icon: selectAllIcon,
+				keystroke: 'Ctrl+A',
+				tooltip: true
+			} );
+
+			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
+
+			// Execute command.
+			this.listenTo( view, 'execute', () => {
+				editor.execute( 'selectAll' );
+				editor.editing.view.focus();
+			} );
+
+			return view;
+		} );
+	}
+}

TEMPAT SAMPAH
packages/ckeditor5-select-all/tests/manual/sample.jpg


+ 21 - 0
packages/ckeditor5-select-all/tests/manual/selectall.html

@@ -0,0 +1,21 @@
+<div id="editor">
+	<p>This is an <a href="https://ckeditor.com">editor</a> instance.</p>
+	<figure class="table">
+		<table>
+			<tbody>
+				<tr>
+					<td>
+						<p>Apple pie marshmallow brownie. Tootsie roll dragée macaroon donut dessert caramels chocolate bar biscuit. Topping chocolate candy powder cake lollipop brownie sugar plum. Marzipan sweet roll lemon drops.</p>
+						<p>Dessert oat cake candy canes gummi bears cupcake marzipan chocolate liquorice liquorice. Jelly-o candy canes biscuit soufflé cake cookie. Chocolate cake pudding gummi bears chocolate bar cookie dragée cotton candy. Wafer pastry donut chocolate bar chocolate bar sugar plum macaroon donut.</p>
+					</td>
+					<td>
+						<p>Apple pie marshmallow brownie. Tootsie roll dragée macaroon donut dessert caramels chocolate bar biscuit. Topping chocolate candy powder cake lollipop brownie sugar plum. Marzipan sweet roll lemon drops.</p>
+						<figure class="image"><img src="./sample.jpg"><figcaption>Caption</figcaption></figure>
+					</td>
+				</tr>
+			</tbody>
+		</table>
+	</figure>
+	<p>An image at the end of the content.</p>
+	<figure class="image"><img src="./sample.jpg"><figcaption>Caption</figcaption></figure>
+</div>

+ 28 - 0
packages/ckeditor5-select-all/tests/manual/selectall.js

@@ -0,0 +1,28 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* globals console:false, window, document */
+
+import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
+import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
+import Heading from '@ckeditor/ckeditor5-heading/src/heading';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
+import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
+import Image from '@ckeditor/ckeditor5-image/src/image';
+import ImageCaption from '@ckeditor/ckeditor5-image/src/imagecaption';
+import Table from '@ckeditor/ckeditor5-table/src/table';
+
+ClassicEditor
+	.create( document.querySelector( '#editor' ), {
+		plugins: [ Essentials, Paragraph, Bold, Italic, Heading, Image, ImageCaption, Table ],
+		toolbar: [ 'heading', '|', 'bold', 'italic', '|', 'insertTable', '|', 'selectAll' ]
+	} )
+	.then( editor => {
+		window.editor = editor;
+	} )
+	.catch( err => {
+		console.error( err.stack );
+	} );

+ 18 - 0
packages/ckeditor5-select-all/tests/manual/selectall.md

@@ -0,0 +1,18 @@
+## The select all feature
+
+### Selecting the entire editor content
+
+1. Put the selection anywhere but inside the image caption or table cell.
+2. Press <kbd>Ctrl/⌘</kbd>+<kbd>A</kbd>.
+3. The entire content should be selected. You should be able to remove it using the <kbd>Backspace</kbd> key.
+4. Repeat the scenario but instead of the keystroke use the toolbar button.
+
+### Selecting the nested editable content
+
+1. Put the selection inside:
+    a. an image caption
+    b. the caption of the image in the table
+    c. the table cell.
+2. Press <kbd>Ctrl/⌘</kbd>+<kbd>A</kbd>.
+3. The caption content should be selected. **The selection should not go beyond the nested editable.**
+4. Repeat the scenario but instead of the keystroke use the toolbar button.

+ 18 - 0
packages/ckeditor5-select-all/tests/selectall.js

@@ -0,0 +1,18 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+import SelectAll from '../src/selectall';
+import SelectAllEditing from '../src/selectallediting';
+import SelectAllUI from '../src/selectallui';
+
+describe( 'SelectAll', () => {
+	it( 'should require SelectAllEditing and SelectAllUI', () => {
+		expect( SelectAll.requires ).to.deep.equal( [ SelectAllEditing, SelectAllUI ] );
+	} );
+
+	it( 'should be named', () => {
+		expect( SelectAll.pluginName ).to.equal( 'SelectAll' );
+	} );
+} );

+ 107 - 0
packages/ckeditor5-select-all/tests/selectallcommand.js

@@ -0,0 +1,107 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
+import SelectAllEditing from '../src/selectallediting';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import ImageEditing from '@ckeditor/ckeditor5-image/src/image/imageediting';
+import ImageCaptionEditing from '@ckeditor/ckeditor5-image/src/imagecaption/imagecaptionediting';
+import TableEditing from '@ckeditor/ckeditor5-table/src/tableediting';
+import { setData, getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+
+describe( 'SelectAllCommand', () => {
+	let editor, model, command;
+
+	beforeEach( () => {
+		return ModelTestEditor
+			.create( {
+				plugins: [ SelectAllEditing, Paragraph, ImageEditing, ImageCaptionEditing, TableEditing ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+				model = editor.model;
+				command = editor.commands.get( 'selectAll' );
+			} );
+	} );
+
+	afterEach( () => {
+		return editor.destroy();
+	} );
+
+	describe( 'isEnabled', () => {
+		it( 'should always be "true" because the command is stateless', () => {
+			expect( command.isEnabled ).to.be.true;
+		} );
+	} );
+
+	describe( 'execute()', () => {
+		it( 'should select all (collapsed selection in a block with text)', () => {
+			setData( model, '<paragraph>f[]oo</paragraph>' );
+
+			editor.execute( 'selectAll' );
+
+			expect( getData( model ) ).to.equal( '<paragraph>[foo]</paragraph>' );
+		} );
+
+		it( 'should select all (collapsed selection in a content with an object)', () => {
+			setData( model, '<paragraph>fo[]o</paragraph><image src="foo.png"><caption></caption></image>' );
+
+			editor.execute( 'selectAll' );
+
+			expect( getData( model ) ).to.equal( '<paragraph>[foo</paragraph><image src="foo.png"><caption></caption></image>]' );
+		} );
+
+		it( 'should select all (selection on an object)', () => {
+			setData( model, '<paragraph>foo</paragraph>[<image src="foo.png"><caption></caption></image>]' );
+
+			editor.execute( 'selectAll' );
+
+			expect( getData( model ) ).to.equal( '<paragraph>[foo</paragraph><image src="foo.png"><caption></caption></image>]' );
+		} );
+
+		it( 'should select all (collapsed selection in a nested editable)', () => {
+			setData( model, '<paragraph>foo</paragraph><image src="foo.png"><caption>b[]ar</caption></image>' );
+
+			editor.execute( 'selectAll' );
+
+			expect( getData( model ) ).to.equal( '<paragraph>foo</paragraph><image src="foo.png"><caption>[bar]</caption></image>' );
+		} );
+
+		it( 'should select all (selection in a nested editable)', () => {
+			setData( model, '<paragraph>foo</paragraph><image src="foo.png"><caption>[bar]</caption></image>' );
+
+			editor.execute( 'selectAll' );
+
+			expect( getData( model ) ).to.equal( '<paragraph>foo</paragraph><image src="foo.png"><caption>[bar]</caption></image>' );
+		} );
+
+		it( 'should select all in the closest nested editable (nested editable inside another nested editable)', () => {
+			setData( model,
+				'<paragraph>foo</paragraph>' +
+				'<table>' +
+					'<tableRow>' +
+						'<tableCell>' +
+							'<paragraph>foo</paragraph>' +
+							'<image src="foo.png"><caption>b[]ar</caption></image>' +
+						'</tableCell>' +
+					'</tableRow>' +
+				'</table>'
+			);
+
+			editor.execute( 'selectAll' );
+
+			expect( getData( model ) ).to.equal( '<paragraph>foo</paragraph>' +
+				'<table>' +
+					'<tableRow>' +
+						'<tableCell>' +
+							'<paragraph>foo</paragraph>' +
+							'<image src="foo.png"><caption>[bar]</caption></image>' +
+						'</tableCell>' +
+					'</tableRow>' +
+				'</table>'
+			);
+		} );
+	} );
+} );

+ 80 - 0
packages/ckeditor5-select-all/tests/selectallediting.js

@@ -0,0 +1,80 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
+import SelectAllEditing from '../src/selectallediting';
+import SelectAllCommand from '../src/selectallcommand';
+import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
+
+describe( 'SelectAllEditing', () => {
+	let editor, viewDocument;
+
+	beforeEach( () => {
+		return ModelTestEditor
+			.create( {
+				plugins: [ SelectAllEditing ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+				viewDocument = editor.editing.view.document;
+
+				sinon.spy( editor, 'execute' );
+			} );
+	} );
+
+	afterEach( () => {
+		return editor.destroy();
+	} );
+
+	it( 'should have a name', () => {
+		expect( SelectAllEditing.pluginName ).to.equal( 'SelectAllEditing' );
+	} );
+
+	it( 'should register the "selectAll" command', () => {
+		const command = editor.commands.get( 'selectAll' );
+
+		expect( command ).to.be.instanceOf( SelectAllCommand );
+	} );
+
+	describe( 'Ctrl+A keystroke listener', () => {
+		it( 'should execute the "selectAll" command', () => {
+			const domEventDataMock = {
+				keyCode: keyCodes.a,
+				ctrlKey: true,
+				preventDefault: sinon.spy()
+			};
+
+			viewDocument.fire( 'keydown', domEventDataMock );
+
+			sinon.assert.calledOnce( editor.execute );
+			sinon.assert.calledWithExactly( editor.execute, 'selectAll' );
+		} );
+
+		it( 'should prevent the default action', () => {
+			const domEventDataMock = {
+				keyCode: keyCodes.a,
+				ctrlKey: true,
+				preventDefault: sinon.spy()
+			};
+
+			viewDocument.fire( 'keydown', domEventDataMock );
+
+			sinon.assert.calledOnce( domEventDataMock.preventDefault );
+		} );
+
+		it( 'should not react to other keystrokes', () => {
+			const domEventDataMock = {
+				keyCode: keyCodes.x,
+				ctrlKey: true,
+				preventDefault: sinon.spy()
+			};
+
+			viewDocument.fire( 'keydown', domEventDataMock );
+
+			sinon.assert.notCalled( editor.execute );
+			sinon.assert.notCalled( domEventDataMock.preventDefault );
+		} );
+	} );
+} );

+ 80 - 0
packages/ckeditor5-select-all/tests/selectallui.js

@@ -0,0 +1,80 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* global document */
+
+import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+import SelectAllEditing from '../src/selectallediting';
+import SelectAllUI from '../src/selectallui';
+
+describe( 'SelectAllUI', () => {
+	let editor, editorElement, button;
+
+	beforeEach( () => {
+		editorElement = document.createElement( 'div' );
+		document.body.appendChild( editorElement );
+
+		return ClassicTestEditor
+			.create( editorElement, {
+				plugins: [ SelectAllEditing, SelectAllUI ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+				button = editor.ui.componentFactory.create( 'selectAll' );
+			} );
+	} );
+
+	afterEach( () => {
+		editorElement.remove();
+
+		return editor.destroy();
+	} );
+
+	it( 'should have a name', () => {
+		expect( SelectAllUI.pluginName ).to.equal( 'SelectAllUI' );
+	} );
+
+	describe( 'the "selectAll" button', () => {
+		it( 'should be an instance of ButtonView', () => {
+			expect( button ).to.be.instanceOf( ButtonView );
+		} );
+
+		it( 'should have a label', () => {
+			expect( button.label ).to.equal( 'Select all' );
+		} );
+
+		it( 'should have an icon', () => {
+			expect( button.icon ).to.match( /^<svg/ );
+		} );
+
+		it( 'should have a keystroke', () => {
+			expect( button.keystroke ).to.equal( 'CTRL+A' );
+		} );
+
+		it( 'should have a tooltip', () => {
+			expect( button.tooltip ).to.be.true;
+		} );
+
+		it( 'should have #isEnabled bound to the command state', () => {
+			expect( button.isEnabled ).to.be.true;
+
+			editor.commands.get( 'selectAll' ).isEnabled = false;
+
+			expect( button.isEnabled ).to.be.false;
+		} );
+
+		it( 'should execute the "selectAll" command and focus the editing view', () => {
+			sinon.spy( editor, 'execute' );
+			sinon.spy( editor.editing.view, 'focus' );
+
+			button.fire( 'execute' );
+
+			sinon.assert.calledOnce( editor.execute );
+			sinon.assert.calledWithExactly( editor.execute, 'selectAll' );
+			sinon.assert.calledOnce( editor.editing.view.focus );
+		} );
+	} );
+} );

+ 1 - 0
packages/ckeditor5-select-all/theme/icons/select-all.svg

@@ -0,0 +1 @@
+<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M.75 15.5a.75.75 0 0 1 .75.75V18l.008.09A.5.5 0 0 0 2 18.5h1.75a.75.75 0 1 1 0 1.5H1.5l-.144-.007a1.5 1.5 0 0 1-1.35-1.349L0 18.5v-2.25a.75.75 0 0 1 .75-.75zm18.5 0a.75.75 0 0 1 .75.75v2.25l-.007.144a1.5 1.5 0 0 1-1.349 1.35L18.5 20h-2.25a.75.75 0 1 1 0-1.5H18a.5.5 0 0 0 .492-.41L18.5 18v-1.75a.75.75 0 0 1 .75-.75zm-10.45 3c.11 0 .2.09.2.2v1.1a.2.2 0 0 1-.2.2H7.2a.2.2 0 0 1-.2-.2v-1.1c0-.11.09-.2.2-.2h1.6zm4 0c.11 0 .2.09.2.2v1.1a.2.2 0 0 1-.2.2h-1.6a.2.2 0 0 1-.2-.2v-1.1c0-.11.09-.2.2-.2h1.6zm.45-5.5a.75.75 0 1 1 0 1.5h-8.5a.75.75 0 1 1 0-1.5h8.5zM1.3 11c.11 0 .2.09.2.2v1.6a.2.2 0 0 1-.2.2H.2a.2.2 0 0 1-.2-.2v-1.6c0-.11.09-.2.2-.2h1.1zm18.5 0c.11 0 .2.09.2.2v1.6a.2.2 0 0 1-.2.2h-1.1a.2.2 0 0 1-.2-.2v-1.6c0-.11.09-.2.2-.2h1.1zm-4.55-2a.75.75 0 1 1 0 1.5H4.75a.75.75 0 1 1 0-1.5h10.5zM1.3 7c.11 0 .2.09.2.2v1.6a.2.2 0 0 1-.2.2H.2a.2.2 0 0 1-.2-.2V7.2c0-.11.09-.2.2-.2h1.1zm18.5 0c.11 0 .2.09.2.2v1.6a.2.2 0 0 1-.2.2h-1.1a.2.2 0 0 1-.2-.2V7.2c0-.11.09-.2.2-.2h1.1zm-4.55-2a.75.75 0 1 1 0 1.5h-2.5a.75.75 0 1 1 0-1.5h2.5zm-5 0a.75.75 0 1 1 0 1.5h-5.5a.75.75 0 0 1 0-1.5h5.5zm-6.5-5a.75.75 0 0 1 0 1.5H2a.5.5 0 0 0-.492.41L1.5 2v1.75a.75.75 0 0 1-1.5 0V1.5l.007-.144A1.5 1.5 0 0 1 1.356.006L1.5 0h2.25zM18.5 0l.144.007a1.5 1.5 0 0 1 1.35 1.349L20 1.5v2.25a.75.75 0 1 1-1.5 0V2l-.008-.09A.5.5 0 0 0 18 1.5h-1.75a.75.75 0 1 1 0-1.5h2.25zM8.8 0c.11 0 .2.09.2.2v1.1a.2.2 0 0 1-.2.2H7.2a.2.2 0 0 1-.2-.2V.2c0-.11.09-.2.2-.2h1.6zm4 0c.11 0 .2.09.2.2v1.1a.2.2 0 0 1-.2.2h-1.6a.2.2 0 0 1-.2-.2V.2c0-.11.09-.2.2-.2h1.6z"/></svg>