-
Notifications
You must be signed in to change notification settings - Fork 5
Changes from 11 commits
9f4fd8d
ad4684e
76d7485
8e59ff9
1f0a05b
faff505
a92751d
fe53c68
a7b485f
5b5ab05
88858fd
244f4ad
5470728
0a9fd82
fb86541
06a2ca4
334afa9
0942361
f49ac89
0a7318d
fbdd2a8
4053902
22050ba
da6b718
2677157
c42606d
dae254f
57bfe06
8c63c46
6510e03
efb7224
f90e5ef
cfcff7d
55c2ca4
2aa90c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/** | ||
* @license Copyright (c) 2003-2019, 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 WordCount from '@ckeditor/ckeditor5-word-count/src/wordcount'; | ||
|
||
ClassicEditor.builtinPlugins.push( WordCount ); | ||
|
||
window.ClassicEditor = ClassicEditor; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
|
||
<style> | ||
.customized-counter__color-box { | ||
--hue: 180; | ||
width: 20px; | ||
height: 20px; | ||
background-color: hsl( var( --hue ), 100%, 50% ); | ||
display: inline-block; | ||
} | ||
|
||
.customized-counter { | ||
border: 3px solid #333; | ||
padding-left: 5px; | ||
margin-bottom: 15px; | ||
} | ||
|
||
.customized-counter > div { | ||
display: inline-block; | ||
width: 50%; | ||
margin-left: 0; | ||
margin-right: 0; | ||
} | ||
</style> | ||
|
||
<div id="demo-editor-update"> | ||
<p>A <strong>black hole</strong> is a region of <a href="https://en.wikipedia.org/wiki/Spacetime">spacetime</a> exhibiting <a href="https://en.wikipedia.org/wiki/Gravitation">gravitational</a> acceleration so strong that nothing—no <a href="https://en.wikipedia.org/wiki/Particle">particles</a> or even <a href="https://en.wikipedia.org/wiki/Electromagnetic_radiation">electromagnetic radiation</a> such as <a href="https://en.wikipedia.org/wiki/Light">light</a>—can escape from it.<a href="https://en.wikipedia.org/wiki/Black_hole#cite_note-6">[6]</a> The theory of <a href="https://en.wikipedia.org/wiki/General_relativity">general relativity</a> predicts that a sufficiently compact <a href="https://en.wikipedia.org/wiki/Mass">mass</a> can deform <a href="https://en.wikipedia.org/wiki/Spacetime">spacetime</a> to form a black hole.</p> | ||
</div> | ||
<div class="customized-counter"> | ||
<div class="customized-counter__words"> | ||
<label>Words: | ||
<progress value="48" max='100'></progress> | ||
</label> | ||
</div> | ||
<div class="customized-counter__characters"> | ||
Characters: | ||
<div class="customized-counter__color-box"></div> | ||
</div> | ||
</div> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/** | ||
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. | ||
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license | ||
*/ | ||
|
||
/* global window, document, console, ClassicEditor */ | ||
|
||
ClassicEditor | ||
.create( document.querySelector( '#demo-editor-update' ), { | ||
toolbar: { | ||
items: [ | ||
'heading', | ||
'bold', | ||
'italic', | ||
'bulletedList', | ||
'numberedList', | ||
'blockQuote', | ||
'link', | ||
'|', | ||
'mediaEmbed', | ||
'insertTable', | ||
'|', | ||
'undo', | ||
'redo' | ||
], | ||
viewportTopOffset: window.getViewportTopOffsetConfig() | ||
}, | ||
table: { | ||
contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ] | ||
} | ||
} ) | ||
.then( editor => { | ||
const wordCountPlugin = editor.plugins.get( 'WordCount' ); | ||
|
||
const progressBar = document.querySelector( '.customized-counter progress' ); | ||
const colorBox = document.querySelector( '.customized-counter__color-box' ); | ||
|
||
wordCountPlugin.on( 'update', updateHandler ); | ||
|
||
function updateHandler( evt, payload ) { | ||
progressBar.value = payload.words; | ||
colorBox.style.setProperty( '--hue', payload.characters * 3 ); | ||
} | ||
} ) | ||
.catch( err => { | ||
console.error( err.stack ); | ||
} ); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<style> | ||
.word-counter { | ||
border: 3px solid #333; | ||
padding-left: 5px; | ||
margin-bottom: 15px; | ||
} | ||
</style> | ||
<div id="demo-editor"> | ||
<p>The <strong>Battle of Westerplatte</strong> was one of the first battles in Germany's <a href="https://en.wikipedia.org/wiki/Invasion_of_Poland">invasion of Poland</a>, marking the start of <a href="https://en.wikipedia.org/wiki/World_War_II">World War II</a> in <a href="https://en.wikipedia.org/wiki/Europe">Europe</a>. Beginning on 1 September 1939, <a href="https://en.wikipedia.org/wiki/Nazi_Germany">German</a> <a href="https://en.wikipedia.org/wiki/German_Army_(Wehrmacht)">army</a>, <a href="https://en.wikipedia.org/wiki/Kriegsmarine">naval</a> and <a href="https://en.wikipedia.org/wiki/Luftwaffe">air forces</a> and <a href="https://en.wikipedia.org/wiki/Free_City_of_Danzig_Police">Danzig police</a> assaulted <a href="https://en.wikipedia.org/wiki/Poland">Poland</a>'s Military Transit Depot (<i>Wojskowa Składnica Tranzytowa</i>, or <i>WST</i>) on the <a href="https://en.wikipedia.org/wiki/Westerplatte">Westerplatte</a> peninsula in the harbor of the <a href="https://en.wikipedia.org/wiki/Free_City_of_Danzig">Free City of Danzig</a>. The Poles held out for seven days and repelled 13 assaults that included <a href="https://en.wikipedia.org/wiki/Dive_bomber">dive-bomber</a> attacks and naval shelling.</p> | ||
<p>Westerplatte's defense served as an inspiration for the <a href="https://en.wikipedia.org/wiki/Polish_Army">Polish Army</a> and people in the face of German advances elsewhere, and is still regarded as a symbol of resistance in modern Poland.</p> | ||
</div> | ||
<div id="demo-word-counter" class="word-counter"> | ||
</div> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/** | ||
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. | ||
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license | ||
*/ | ||
|
||
/* global document, window, console, ClassicEditor */ | ||
|
||
ClassicEditor | ||
.create( document.querySelector( '#demo-editor' ), { | ||
toolbar: { | ||
items: [ | ||
'heading', | ||
'bold', | ||
'italic', | ||
'bulletedList', | ||
'numberedList', | ||
'blockQuote', | ||
'link', | ||
'|', | ||
'mediaEmbed', | ||
'insertTable', | ||
'|', | ||
'undo', | ||
'redo' | ||
], | ||
viewportTopOffset: window.getViewportTopOffsetConfig() | ||
}, | ||
table: { | ||
contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ] | ||
} | ||
} ) | ||
.then( editor => { | ||
window.editor = editor; | ||
|
||
document.getElementById( 'demo-word-counter' ).appendChild( editor.plugins.get( 'WordCount' ).getWordCountContainer() ); | ||
} ) | ||
.catch( err => { | ||
console.error( err.stack ); | ||
} ); |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,108 @@ | ||||||
--- | ||||||
category: features | ||||||
--- | ||||||
|
||||||
{@snippet features/build-word-count-source} | ||||||
|
||||||
# Word count | ||||||
|
||||||
The {@link module:wordcount/wordcount~WordCount} features provide a possibility to track the number of words and characters written in the editor. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
## Demo | ||||||
|
||||||
{@snippet features/word-count} | ||||||
|
||||||
```html | ||||||
<div id="editor"> | ||||||
<p>Hello world.</p> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You may left typical There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OTOH maybe just one line that you need some HTML element to place the word counter somewhere, ie in a div. (so an example without the Also, I'd use an |
||||||
</div> | ||||||
<div class="word-count"> | ||||||
</div> | ||||||
``` | ||||||
|
||||||
```js | ||||||
ClassicEditor | ||||||
.create( document.querySelector( '#editor' ), { | ||||||
// configuration details | ||||||
msamsel marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
} ) | ||||||
.then( editor => { | ||||||
const wordCountPlugin = editor.plugins.get( 'WordCount' ); | ||||||
const wordCountWrapper = document.querySelector( '.word-count' ); | ||||||
|
||||||
wordCountWrapper.appendChild( wordCounterPlugin.getWordCountContainer() ); | ||||||
} ) | ||||||
.catch( err => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be |
||||||
console.error( err.stack ); | ||||||
} ); | ||||||
``` | ||||||
|
||||||
## Configuring options | ||||||
msamsel marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
There are two options which change the output container. If there is set {@link module:wordcount/wordcount~WordCountConfig#displayWords} to `false`, then the section with word counter is removed from self-updating output container. In a similar way works second option {@link module:wordcount/wordcount~WordCountConfig#displayCharacters} with character container. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
## Update event | ||||||
|
||||||
Word count feature emits an {@link module:wordcount/wordcount~WordCount#event:update update event} whenever there is a change in a model. This allows on having own callback with customized behavior reacting on this change. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
Below you can find an example, where the background color of a square is changed according to the number of characters in the editor. There is also a progress bar which indicates how many words is in it (the maximal value of the progress bar is set to 100, however, you can write further and progress bar remain in the maximal state). | ||||||
|
||||||
{@snippet features/word-count-update} | ||||||
|
||||||
```js | ||||||
ClassicEditor | ||||||
.create( document.querySelector( '#editor' ), { | ||||||
// configuration details | ||||||
} ) | ||||||
.then( editor => { | ||||||
const wordCountPlugin = editor.plugins.get( 'WordCount' ); | ||||||
|
||||||
wordCountPlugin.on( 'update', ( evt, payload ) => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that we use |
||||||
// payload is an object with "words" and "characters" field | ||||||
doSthWithNewWordsNumber( payload.words ); | ||||||
doSthWithNewCharactersNumber( payload.characters ); | ||||||
} ); | ||||||
|
||||||
} ) | ||||||
.catch( err => { | ||||||
console.error( err.stack ); | ||||||
} ); | ||||||
``` | ||||||
|
||||||
## Installation | ||||||
|
||||||
To add this feature to your rich-text editor, install the [`@ckeditor/ckeditor5-word-count`](https://www.npmjs.com/package/@ckeditor/ckeditor5-word-count) package: | ||||||
|
||||||
```bash | ||||||
npm install --save @ckeditor/ckeditor5-word-count | ||||||
``` | ||||||
|
||||||
And add it to your plugin list configuration: | ||||||
|
||||||
```js | ||||||
import WordCount from '@ckeditor/ckeditor5-word-count/src/wordcount'; | ||||||
|
||||||
ClassicEditor | ||||||
.create( document.querySelector( '#editor' ), { | ||||||
plugins: [ WordCount, ... ], | ||||||
} ) | ||||||
.then( ... ) | ||||||
.catch( ... ); | ||||||
``` | ||||||
|
||||||
<info-box info> | ||||||
Read more about {@link builds/guides/integration/installing-plugins installing plugins}. | ||||||
</info-box> | ||||||
|
||||||
## Common API | ||||||
|
||||||
The {@link module:wordcount/wordcount~WordCount} plugin provides: | ||||||
* {@link module:wordcount/wordcount~WordCount#getWordCountContainer} method. It returns a self-updating HTML Element which might be used to track the current amount of words and characters in the editor. There is a possibility to remove "Words" or "Characters" counter with proper configuration of {@link module:wordcount/wordcount~WordCountConfig#displayWords} and {@link module:wordcount/wordcount~WordCountConfig#displayCharacters}, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
* {@link module:wordcount/wordcount~WordCount#event:update update event} which provides more versatile option to handle changes of words' and characters' number. There is a possibility to run own callback function with updated values. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd go with more general description: "which is fired whenever the plugins update the number of counted words and characters. Note this event is throttled... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I rephrase it a little bite in a more generic manner. |
||||||
|
||||||
<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-word-count. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/** | ||
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. | ||
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license | ||
*/ | ||
|
||
/** | ||
* @module wordcount/utils | ||
*/ | ||
|
||
/** | ||
* Function walks through all the model's nodes. It obtains a plain text from each {@link module:engine/model/text~Text} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Description of implementation ;) Something like: "Returns plain text representation of an element and it's children. The blocks are separated by a newline ( |
||
* and {@link module:engine/model/textproxy~TextProxy}. All sections, which are not a text, are separated with a new line (`\n`). | ||
* | ||
Reinmar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* **Note:** Function walks through the entire model. There should be considered throttling during usage. | ||
* | ||
* @param {module:engine/model/node~Node} node | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wrong param: should be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It actually needs to be But, at the same time, you're completely right that neither of them have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks so - I wrote that ticket after this review actually. I worry that we can have more such mistype declaration in the docs for |
||
* @returns {String} Plain text representing model's data | ||
*/ | ||
export function modelElementToPlainText( node ) { | ||
let text = ''; | ||
|
||
if ( node.is( 'text' ) || node.is( 'textProxy' ) ) { | ||
text += node.data; | ||
} else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd remove the indentation here: return on The other question is do you consider There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've made simple code with treewalker and performance dropped significantly.
|
||
let prev = null; | ||
|
||
for ( const child of node.getChildren() ) { | ||
const childText = modelElementToPlainText( child ); | ||
|
||
// If last block was finish, start from new line. | ||
if ( prev && prev.is( 'element' ) ) { | ||
text += '\n'; | ||
} | ||
|
||
text += childText; | ||
|
||
prev = child; | ||
} | ||
} | ||
|
||
return text; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/'/"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/48/42