@ckeditor/ckeditor5-word-count 35.4.0 → 36.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.md CHANGED
@@ -2,7 +2,7 @@ Software License Agreement
2
2
  ==========================
3
3
 
4
4
  **CKEditor 5 word and character count feature** – https://github.com/ckeditor/ckeditor5-word-count <br>
5
- Copyright (c) 2003-2022, [CKSource Holding sp. z o.o.](https://cksource.com) All rights reserved.
5
+ Copyright (c) 2003-2023, [CKSource Holding sp. z o.o.](https://cksource.com) All rights reserved.
6
6
 
7
7
  Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html).
8
8
 
@@ -1,5 +1,5 @@
1
1
  !function(t){const e=t.en=t.en||{};e.dictionary=Object.assign(e.dictionary||{},{"Characters: %0":"Characters: %0","Words: %0":"Words: %0"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={})),
2
2
  /*!
3
- * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
4
4
  * For licensing, see LICENSE.md.
5
- */(()=>{var t={704:(t,e,r)=>{t.exports=r(79)("./src/core.js")},273:(t,e,r)=>{t.exports=r(79)("./src/ui.js")},209:(t,e,r)=>{t.exports=r(79)("./src/utils.js")},79:t=>{"use strict";t.exports=CKEditor5.dll}},e={};function r(o){var n=e[o];if(void 0!==n)return n.exports;var i=e[o]={exports:{}};return t[o](i,i.exports,r),i.exports}r.d=(t,e)=>{for(var o in e)r.o(e,o)&&!r.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:e[o]})},r.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})};var o={};(()=>{"use strict";r.r(o),r.d(o,{WordCount:()=>z});var t=r(704),e=r(273),n=r(209);const i=function(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)};const s="object"==typeof global&&global&&global.Object===Object&&global;var c="object"==typeof self&&self&&self.Object===Object&&self;const a=s||c||Function("return this")();const u=function(){return a.Date.now()};var d=/\s/;const l=function(t){for(var e=t.length;e--&&d.test(t.charAt(e)););return e};var f=/^\s+/;const h=function(t){return t?t.slice(0,l(t)+1).replace(f,""):t};const p=a.Symbol;var g=Object.prototype,v=g.hasOwnProperty,b=g.toString,w=p?p.toStringTag:void 0;const y=function(t){var e=v.call(t,w),r=t[w];try{t[w]=void 0;var o=!0}catch(t){}var n=b.call(t);return o&&(e?t[w]=r:delete t[w]),n};var _=Object.prototype.toString;const m=function(t){return _.call(t)};var j=p?p.toStringTag:void 0;const O=function(t){return null==t?void 0===t?"[object Undefined]":"[object Null]":j&&j in Object(t)?y(t):m(t)};const x=function(t){return null!=t&&"object"==typeof t};const C=function(t){return"symbol"==typeof t||x(t)&&"[object Symbol]"==O(t)};var S=/^[-+]0x[0-9a-f]+$/i,T=/^0b[01]+$/i,E=/^0o[0-7]+$/i,W=parseInt;const N=function(t){if("number"==typeof t)return t;if(C(t))return NaN;if(i(t)){var e="function"==typeof t.valueOf?t.valueOf():t;t=i(e)?e+"":e}if("string"!=typeof t)return 0===t?t:+t;t=h(t);var r=T.test(t);return r||E.test(t)?W(t.slice(2),r?2:8):S.test(t)?NaN:+t};var P=Math.max,R=Math.min;const V=function(t,e,r){var o,n,s,c,a,d,l=0,f=!1,h=!1,p=!0;if("function"!=typeof t)throw new TypeError("Expected a function");function g(e){var r=o,i=n;return o=n=void 0,l=e,c=t.apply(i,r)}function v(t){return l=t,a=setTimeout(w,e),f?g(t):c}function b(t){var r=t-d;return void 0===d||r>=e||r<0||h&&t-l>=s}function w(){var t=u();if(b(t))return y(t);a=setTimeout(w,function(t){var r=e-(t-d);return h?R(r,s-(t-l)):r}(t))}function y(t){return a=void 0,p&&o?g(t):(o=n=void 0,c)}function _(){var t=u(),r=b(t);if(o=arguments,n=this,d=t,r){if(void 0===a)return v(d);if(h)return clearTimeout(a),a=setTimeout(w,e),g(d)}return void 0===a&&(a=setTimeout(w,e)),c}return e=N(e)||0,i(r)&&(f=!!r.leading,s=(h="maxWait"in r)?P(N(r.maxWait)||0,e):s,p="trailing"in r?!!r.trailing:p),_.cancel=function(){void 0!==a&&clearTimeout(a),l=0,o=d=n=a=void 0},_.flush=function(){return void 0===a?c:y(u())},_};const L=function(t,e,r){var o=!0,n=!0;if("function"!=typeof t)throw new TypeError("Expected a function");return i(r)&&(o="leading"in r?!!r.leading:o,n="trailing"in r?!!r.trailing:n),V(t,e,{leading:o,maxWait:e,trailing:n})};const A=function(t,e){return function(r){return t(e(r))}}(Object.getPrototypeOf,Object);var M=Function.prototype,I=Object.prototype,K=M.toString,$=I.hasOwnProperty,k=K.call(Object);const U=function(t){if(!x(t)||"[object Object]"!=O(t))return!1;var e=A(t);if(null===e)return!0;var r=$.call(e,"constructor")&&e.constructor;return"function"==typeof r&&r instanceof r&&K.call(r)==k};const D=function(t){return x(t)&&1===t.nodeType&&!U(t)};function F(t){if(t.is("$text")||t.is("$textProxy"))return t.data;let e="",r=null;for(const o of t.getChildren()){const t=F(o);r&&r.is("element")&&(e+="\n"),e+=t,r=o}return e}class z extends t.Plugin{constructor(t){super(t),this.set("characters",0),this.set("words",0),Object.defineProperties(this,{characters:{get(){return this.characters=this._getCharacters()}},words:{get(){return this.words=this._getWords()}}}),this.set("_wordsLabel"),this.set("_charactersLabel"),this._config=t.config.get("wordCount")||{},this._outputView=void 0,this._wordsMatchRegExp=n.env.features.isRegExpUnicodePropertySupported?new RegExp("([\\p{L}\\p{N}]+\\S?)+","gu"):/([a-zA-Z0-9À-ž]+\S?)+/gu}static get pluginName(){return"WordCount"}init(){this.editor.model.document.on("change:data",L(this._refreshStats.bind(this),250)),"function"==typeof this._config.onUpdate&&this.on("update",((t,e)=>{this._config.onUpdate(e)})),D(this._config.container)&&this._config.container.appendChild(this.wordCountContainer)}destroy(){this._outputView&&(this._outputView.element.remove(),this._outputView.destroy()),super.destroy()}get wordCountContainer(){const t=this.editor,r=t.t,o=t.config.get("wordCount.displayWords"),n=t.config.get("wordCount.displayCharacters"),i=e.Template.bind(this,this),s=[];return this._outputView||(this._outputView=new e.View,(o||void 0===o)&&(this.bind("_wordsLabel").to(this,"words",(t=>r("Words: %0",t))),s.push({tag:"div",children:[{text:[i.to("_wordsLabel")]}],attributes:{class:"ck-word-count__words"}})),(n||void 0===n)&&(this.bind("_charactersLabel").to(this,"characters",(t=>r("Characters: %0",t))),s.push({tag:"div",children:[{text:[i.to("_charactersLabel")]}],attributes:{class:"ck-word-count__characters"}})),this._outputView.setTemplate({tag:"div",attributes:{class:["ck","ck-word-count"]},children:s}),this._outputView.render()),this._outputView.element}_getCharacters(){return F(this.editor.model.document.getRoot()).replace(/\n/g,"").length}_getWords(){return(F(this.editor.model.document.getRoot()).match(this._wordsMatchRegExp)||[]).length}_refreshStats(){const t=this.words=this._getWords(),e=this.characters=this._getCharacters();this.fire("update",{words:t,characters:e})}}})(),(window.CKEditor5=window.CKEditor5||{}).wordCount=o})();
5
+ */(()=>{var t={704:(t,e,r)=>{t.exports=r(79)("./src/core.js")},273:(t,e,r)=>{t.exports=r(79)("./src/ui.js")},209:(t,e,r)=>{t.exports=r(79)("./src/utils.js")},79:t=>{"use strict";t.exports=CKEditor5.dll}},e={};function r(o){var n=e[o];if(void 0!==n)return n.exports;var i=e[o]={exports:{}};return t[o](i,i.exports,r),i.exports}r.d=(t,e)=>{for(var o in e)r.o(e,o)&&!r.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:e[o]})},r.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})};var o={};(()=>{"use strict";r.r(o),r.d(o,{WordCount:()=>z});var t=r(704),e=r(273),n=r(209);function i(t){if(t.is("$text")||t.is("$textProxy"))return t.data;const e=t;let r="",o=null;for(const t of e.getChildren()){const e=i(t);o&&o.is("element")&&(r+="\n"),r+=e,o=t}return r}const s=function(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)};const c="object"==typeof global&&global&&global.Object===Object&&global;var a="object"==typeof self&&self&&self.Object===Object&&self;const u=c||a||Function("return this")();const d=function(){return u.Date.now()};var l=/\s/;const f=function(t){for(var e=t.length;e--&&l.test(t.charAt(e)););return e};var h=/^\s+/;const p=function(t){return t?t.slice(0,f(t)+1).replace(h,""):t};const g=u.Symbol;var v=Object.prototype,b=v.hasOwnProperty,w=v.toString,y=g?g.toStringTag:void 0;const _=function(t){var e=b.call(t,y),r=t[y];try{t[y]=void 0;var o=!0}catch(t){}var n=w.call(t);return o&&(e?t[y]=r:delete t[y]),n};var m=Object.prototype.toString;const j=function(t){return m.call(t)};var O=g?g.toStringTag:void 0;const x=function(t){return null==t?void 0===t?"[object Undefined]":"[object Null]":O&&O in Object(t)?_(t):j(t)};const C=function(t){return null!=t&&"object"==typeof t};const S=function(t){return"symbol"==typeof t||C(t)&&"[object Symbol]"==x(t)};var T=/^[-+]0x[0-9a-f]+$/i,E=/^0b[01]+$/i,W=/^0o[0-7]+$/i,N=parseInt;const P=function(t){if("number"==typeof t)return t;if(S(t))return NaN;if(s(t)){var e="function"==typeof t.valueOf?t.valueOf():t;t=s(e)?e+"":e}if("string"!=typeof t)return 0===t?t:+t;t=p(t);var r=E.test(t);return r||W.test(t)?N(t.slice(2),r?2:8):T.test(t)?NaN:+t};var R=Math.max,V=Math.min;const L=function(t,e,r){var o,n,i,c,a,u,l=0,f=!1,h=!1,p=!0;if("function"!=typeof t)throw new TypeError("Expected a function");function g(e){var r=o,i=n;return o=n=void 0,l=e,c=t.apply(i,r)}function v(t){return l=t,a=setTimeout(w,e),f?g(t):c}function b(t){var r=t-u;return void 0===u||r>=e||r<0||h&&t-l>=i}function w(){var t=d();if(b(t))return y(t);a=setTimeout(w,function(t){var r=e-(t-u);return h?V(r,i-(t-l)):r}(t))}function y(t){return a=void 0,p&&o?g(t):(o=n=void 0,c)}function _(){var t=d(),r=b(t);if(o=arguments,n=this,u=t,r){if(void 0===a)return v(u);if(h)return clearTimeout(a),a=setTimeout(w,e),g(u)}return void 0===a&&(a=setTimeout(w,e)),c}return e=P(e)||0,s(r)&&(f=!!r.leading,i=(h="maxWait"in r)?R(P(r.maxWait)||0,e):i,p="trailing"in r?!!r.trailing:p),_.cancel=function(){void 0!==a&&clearTimeout(a),l=0,o=u=n=a=void 0},_.flush=function(){return void 0===a?c:y(d())},_};const A=function(t,e,r){var o=!0,n=!0;if("function"!=typeof t)throw new TypeError("Expected a function");return s(r)&&(o="leading"in r?!!r.leading:o,n="trailing"in r?!!r.trailing:n),L(t,e,{leading:o,maxWait:e,trailing:n})};const M=function(t,e){return function(r){return t(e(r))}}(Object.getPrototypeOf,Object);var I=Function.prototype,K=Object.prototype,$=I.toString,k=K.hasOwnProperty,U=$.call(Object);const D=function(t){if(!C(t)||"[object Object]"!=x(t))return!1;var e=M(t);if(null===e)return!0;var r=k.call(e,"constructor")&&e.constructor;return"function"==typeof r&&r instanceof r&&$.call(r)==U};const F=function(t){return C(t)&&1===t.nodeType&&!D(t)};class z extends t.Plugin{constructor(t){super(t),this.set("characters",0),this.set("words",0),Object.defineProperties(this,{characters:{get(){return this.characters=this._getCharacters()}},words:{get(){return this.words=this._getWords()}}}),this.set("_wordsLabel",void 0),this.set("_charactersLabel",void 0),this._config=t.config.get("wordCount")||{},this._outputView=void 0,this._wordsMatchRegExp=n.env.features.isRegExpUnicodePropertySupported?new RegExp("([\\p{L}\\p{N}]+\\S?)+","gu"):/([a-zA-Z0-9À-ž]+\S?)+/gu}static get pluginName(){return"WordCount"}init(){this.editor.model.document.on("change:data",A(this._refreshStats.bind(this),250)),"function"==typeof this._config.onUpdate&&this.on("update",((t,e)=>{this._config.onUpdate(e)})),F(this._config.container)&&this._config.container.appendChild(this.wordCountContainer)}destroy(){this._outputView&&(this._outputView.element.remove(),this._outputView.destroy()),super.destroy()}get wordCountContainer(){const t=this.editor,r=t.t,o=t.config.get("wordCount.displayWords"),n=t.config.get("wordCount.displayCharacters"),i=e.Template.bind(this,this),s=[];return this._outputView||(this._outputView=new e.View,(o||void 0===o)&&(this.bind("_wordsLabel").to(this,"words",(t=>r("Words: %0",t))),s.push({tag:"div",children:[{text:[i.to("_wordsLabel")]}],attributes:{class:"ck-word-count__words"}})),(n||void 0===n)&&(this.bind("_charactersLabel").to(this,"characters",(t=>r("Characters: %0",t))),s.push({tag:"div",children:[{text:[i.to("_charactersLabel")]}],attributes:{class:"ck-word-count__characters"}})),this._outputView.setTemplate({tag:"div",attributes:{class:["ck","ck-word-count"]},children:s}),this._outputView.render()),this._outputView.element}_getCharacters(){return i(this.editor.model.document.getRoot()).replace(/\n/g,"").length}_getWords(){return(i(this.editor.model.document.getRoot()).match(this._wordsMatchRegExp)||[]).length}_refreshStats(){const t=this.words=this._getWords(),e=this.characters=this._getCharacters();this.fire("update",{words:t,characters:e})}}})(),(window.CKEditor5=window.CKEditor5||{}).wordCount=o})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-word-count",
3
- "version": "35.4.0",
3
+ "version": "36.0.0",
4
4
  "description": "Word and character count feature for CKEditor 5.",
5
5
  "keywords": [
6
6
  "ckeditor",
@@ -13,23 +13,24 @@
13
13
  "main": "src/index.js",
14
14
  "dependencies": {
15
15
  "lodash-es": "^4.17.15",
16
- "ckeditor5": "^35.4.0"
16
+ "ckeditor5": "^36.0.0"
17
17
  },
18
18
  "devDependencies": {
19
- "@ckeditor/ckeditor5-basic-styles": "^35.4.0",
20
- "@ckeditor/ckeditor5-block-quote": "^35.4.0",
21
- "@ckeditor/ckeditor5-core": "^35.4.0",
22
- "@ckeditor/ckeditor5-dev-utils": "^31.0.0",
23
- "@ckeditor/ckeditor5-editor-classic": "^35.4.0",
24
- "@ckeditor/ckeditor5-engine": "^35.4.0",
25
- "@ckeditor/ckeditor5-enter": "^35.4.0",
26
- "@ckeditor/ckeditor5-image": "^35.4.0",
27
- "@ckeditor/ckeditor5-link": "^35.4.0",
28
- "@ckeditor/ckeditor5-list": "^35.4.0",
29
- "@ckeditor/ckeditor5-paragraph": "^35.4.0",
30
- "@ckeditor/ckeditor5-table": "^35.4.0",
31
- "@ckeditor/ckeditor5-theme-lark": "^35.4.0",
32
- "@ckeditor/ckeditor5-utils": "^35.4.0",
19
+ "@ckeditor/ckeditor5-basic-styles": "^36.0.0",
20
+ "@ckeditor/ckeditor5-block-quote": "^36.0.0",
21
+ "@ckeditor/ckeditor5-core": "^36.0.0",
22
+ "@ckeditor/ckeditor5-dev-utils": "^32.0.0",
23
+ "@ckeditor/ckeditor5-editor-classic": "^36.0.0",
24
+ "@ckeditor/ckeditor5-engine": "^36.0.0",
25
+ "@ckeditor/ckeditor5-enter": "^36.0.0",
26
+ "@ckeditor/ckeditor5-image": "^36.0.0",
27
+ "@ckeditor/ckeditor5-link": "^36.0.0",
28
+ "@ckeditor/ckeditor5-list": "^36.0.0",
29
+ "@ckeditor/ckeditor5-paragraph": "^36.0.0",
30
+ "@ckeditor/ckeditor5-table": "^36.0.0",
31
+ "@ckeditor/ckeditor5-theme-lark": "^36.0.0",
32
+ "@ckeditor/ckeditor5-utils": "^36.0.0",
33
+ "typescript": "^4.8.4",
33
34
  "webpack": "^5.58.1",
34
35
  "webpack-cli": "^4.9.0"
35
36
  },
@@ -48,13 +49,16 @@
48
49
  },
49
50
  "files": [
50
51
  "lang",
51
- "src",
52
+ "src/**/*.js",
53
+ "src/**/*.d.ts",
52
54
  "theme",
53
55
  "build",
54
56
  "ckeditor5-metadata.json",
55
57
  "CHANGELOG.md"
56
58
  ],
57
59
  "scripts": {
58
- "dll:build": "webpack"
60
+ "dll:build": "webpack",
61
+ "build": "tsc -p ./tsconfig.release.json",
62
+ "postversion": "npm run build"
59
63
  }
60
64
  }
package/src/index.js CHANGED
@@ -1,10 +1,8 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
-
6
5
  /**
7
6
  * @module word-count
8
7
  */
9
-
10
8
  export { default as WordCount } from './wordcount';
package/src/utils.js CHANGED
@@ -1,38 +1,27 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
-
6
- /**
7
- * @module word-count/utils
8
- */
9
-
10
5
  /**
11
6
  * Returns a plain text representation of an element and its children.
12
7
  *
13
- * @param {module:engine/model/element~Element} element
14
- * @returns {String} Plain text representing the model's data.
8
+ * @returns Plain text representing the model's data.
15
9
  */
16
- export function modelElementToPlainText( element ) {
17
- if ( element.is( '$text' ) || element.is( '$textProxy' ) ) {
18
- return element.data;
19
- }
20
-
21
- let text = '';
22
- let prev = null;
23
-
24
- for ( const child of element.getChildren() ) {
25
- const childText = modelElementToPlainText( child );
26
-
27
- // If last block was finish, start from new line.
28
- if ( prev && prev.is( 'element' ) ) {
29
- text += '\n';
30
- }
31
-
32
- text += childText;
33
-
34
- prev = child;
35
- }
36
-
37
- return text;
10
+ export function modelElementToPlainText(item) {
11
+ if (item.is('$text') || item.is('$textProxy')) {
12
+ return item.data;
13
+ }
14
+ const element = item;
15
+ let text = '';
16
+ let prev = null;
17
+ for (const child of element.getChildren()) {
18
+ const childText = modelElementToPlainText(child);
19
+ // If last block was finish, start from new line.
20
+ if (prev && prev.is('element')) {
21
+ text += '\n';
22
+ }
23
+ text += childText;
24
+ prev = child;
25
+ }
26
+ return text;
38
27
  }
package/src/wordcount.js CHANGED
@@ -1,18 +1,12 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
-
6
- /**
7
- * @module word-count/wordcount
8
- */
9
-
10
5
  import { Plugin } from 'ckeditor5/src/core';
11
- import { View, Template } from 'ckeditor5/src/ui';
6
+ import { Template, View } from 'ckeditor5/src/ui';
12
7
  import { env } from 'ckeditor5/src/utils';
13
- import { throttle, isElement } from 'lodash-es';
14
8
  import { modelElementToPlainText } from './utils';
15
-
9
+ import { throttle, isElement } from 'lodash-es';
16
10
  /**
17
11
  * The word count plugin.
18
12
  *
@@ -25,365 +19,183 @@ import { modelElementToPlainText } from './utils';
25
19
  *
26
20
  * Here are some examples of how the word and character calculations are made:
27
21
  *
28
- * <paragraph>foo</paragraph>
29
- * <paragraph>bar</paragraph>
30
- * // Words: 2, Characters: 7
31
- *
32
- * <paragraph><$text bold="true">foo</$text>bar</paragraph>
33
- * // Words: 1, Characters: 6
22
+ * ```html
23
+ * <paragraph>foo</paragraph>
24
+ * <paragraph>bar</paragraph>
25
+ * // Words: 2, Characters: 7
34
26
  *
35
- * <paragraph>*&^%)</paragraph>
36
- * // Words: 0, Characters: 5
27
+ * <paragraph><$text bold="true">foo</$text>bar</paragraph>
28
+ * // Words: 1, Characters: 6
37
29
  *
38
- * <paragraph>foo(bar)</paragraph>
39
- * //Words: 1, Characters: 8
30
+ * <paragraph>*&^%)</paragraph>
31
+ * // Words: 0, Characters: 5
40
32
  *
41
- * <paragraph>12345</paragraph>
42
- * // Words: 1, Characters: 5
33
+ * <paragraph>foo(bar)</paragraph>
34
+ * //Words: 1, Characters: 8
43
35
  *
44
- * @extends module:core/plugin~Plugin
36
+ * <paragraph>12345</paragraph>
37
+ * // Words: 1, Characters: 5
38
+ * ```
45
39
  */
46
40
  export default class WordCount extends Plugin {
47
- /**
48
- * @inheritDoc
49
- */
50
- constructor( editor ) {
51
- super( editor );
52
-
53
- /**
54
- * The number of characters in the editor.
55
- *
56
- * @observable
57
- * @readonly
58
- * @member {Number} module:word-count/wordcount~WordCount#characters
59
- */
60
- this.set( 'characters', 0 );
61
-
62
- /**
63
- * The number of words in the editor.
64
- *
65
- * @observable
66
- * @readonly
67
- * @member {Number} module:word-count/wordcount~WordCount#words
68
- */
69
- this.set( 'words', 0 );
70
-
71
- // Don't wait for the #update event to set the value of the properties but obtain it right away.
72
- // This way, accessing the properties directly returns precise numbers, e.g. for validation, etc.
73
- // If not accessed directly, the properties will be refreshed upon #update anyway.
74
- Object.defineProperties( this, {
75
- characters: {
76
- get() {
77
- return ( this.characters = this._getCharacters() );
78
- }
79
- },
80
- words: {
81
- get() {
82
- return ( this.words = this._getWords() );
83
- }
84
- }
85
- } );
86
-
87
- /**
88
- * The label used to display the words value in the {@link #wordCountContainer output container}.
89
- *
90
- * @observable
91
- * @private
92
- * @readonly
93
- * @member {String} module:word-count/wordcount~WordCount#_wordsLabel
94
- */
95
- this.set( '_wordsLabel' );
96
-
97
- /**
98
- * The label used to display the characters value in the {@link #wordCountContainer output container}.
99
- *
100
- * @observable
101
- * @private
102
- * @readonly
103
- * @member {String} module:word-count/wordcount~WordCount#_charactersLabel
104
- */
105
- this.set( '_charactersLabel' );
106
-
107
- /**
108
- * The configuration of this plugin.
109
- *
110
- * @private
111
- * @type {Object}
112
- */
113
- this._config = editor.config.get( 'wordCount' ) || {};
114
-
115
- /**
116
- * The reference to a {@link module:ui/view~View view object} that contains the self-updating HTML container.
117
- *
118
- * @private
119
- * @readonly
120
- * @type {module:ui/view~View}
121
- */
122
- this._outputView = undefined;
123
-
124
- /**
125
- * A regular expression used to recognize words in the editor's content.
126
- *
127
- * @readonly
128
- * @private
129
- * @type {RegExp}
130
- */
131
- this._wordsMatchRegExp = env.features.isRegExpUnicodePropertySupported ?
132
- // Usage of regular expression literal cause error during build (ckeditor/ckeditor5-dev#534).
133
- // Groups:
134
- // {L} - Any kind of letter from any language.
135
- // {N} - Any kind of numeric character in any script.
136
- new RegExp( '([\\p{L}\\p{N}]+\\S?)+', 'gu' ) :
137
- /([a-zA-Z0-9À-ž]+\S?)+/gu;
138
- }
139
-
140
- /**
141
- * @inheritDoc
142
- */
143
- static get pluginName() {
144
- return 'WordCount';
145
- }
146
-
147
- /**
148
- * @inheritDoc
149
- */
150
- init() {
151
- const editor = this.editor;
152
-
153
- editor.model.document.on( 'change:data', throttle( this._refreshStats.bind( this ), 250 ) );
154
-
155
- if ( typeof this._config.onUpdate == 'function' ) {
156
- this.on( 'update', ( evt, data ) => {
157
- this._config.onUpdate( data );
158
- } );
159
- }
160
-
161
- if ( isElement( this._config.container ) ) {
162
- this._config.container.appendChild( this.wordCountContainer );
163
- }
164
- }
165
-
166
- /**
167
- * @inheritDoc
168
- */
169
- destroy() {
170
- if ( this._outputView ) {
171
- this._outputView.element.remove();
172
- this._outputView.destroy();
173
- }
174
-
175
- super.destroy();
176
- }
177
-
178
- /**
179
- * Creates a self-updating HTML element. Repeated executions return the same element.
180
- * The returned element has the following HTML structure:
181
- *
182
- * <div class="ck ck-word-count">
183
- * <div class="ck-word-count__words">Words: 4</div>
184
- * <div class="ck-word-count__characters">Characters: 28</div>
185
- * </div>
186
- *
187
- * @type {HTMLElement}
188
- */
189
- get wordCountContainer() {
190
- const editor = this.editor;
191
- const t = editor.t;
192
- const displayWords = editor.config.get( 'wordCount.displayWords' );
193
- const displayCharacters = editor.config.get( 'wordCount.displayCharacters' );
194
- const bind = Template.bind( this, this );
195
- const children = [];
196
-
197
- if ( !this._outputView ) {
198
- this._outputView = new View();
199
-
200
- if ( displayWords || displayWords === undefined ) {
201
- this.bind( '_wordsLabel' ).to( this, 'words', words => {
202
- return t( 'Words: %0', words );
203
- } );
204
-
205
- children.push( {
206
- tag: 'div',
207
- children: [
208
- {
209
- text: [ bind.to( '_wordsLabel' ) ]
210
- }
211
- ],
212
- attributes: {
213
- class: 'ck-word-count__words'
214
- }
215
- } );
216
- }
217
-
218
- if ( displayCharacters || displayCharacters === undefined ) {
219
- this.bind( '_charactersLabel' ).to( this, 'characters', words => {
220
- return t( 'Characters: %0', words );
221
- } );
222
-
223
- children.push( {
224
- tag: 'div',
225
- children: [
226
- {
227
- text: [ bind.to( '_charactersLabel' ) ]
228
- }
229
- ],
230
- attributes: {
231
- class: 'ck-word-count__characters'
232
- }
233
- } );
234
- }
235
-
236
- this._outputView.setTemplate( {
237
- tag: 'div',
238
- attributes: {
239
- class: [
240
- 'ck',
241
- 'ck-word-count'
242
- ]
243
- },
244
- children
245
- } );
246
-
247
- this._outputView.render();
248
- }
249
-
250
- return this._outputView.element;
251
- }
252
-
253
- /**
254
- * Determines the number of characters in the current editor's model.
255
- *
256
- * @private
257
- * @returns {Number}
258
- */
259
- _getCharacters() {
260
- const txt = modelElementToPlainText( this.editor.model.document.getRoot() );
261
-
262
- return txt.replace( /\n/g, '' ).length;
263
- }
264
-
265
- /**
266
- * Determines the number of words in the current editor's model.
267
- *
268
- * @private
269
- * @returns {Number}
270
- */
271
- _getWords() {
272
- const txt = modelElementToPlainText( this.editor.model.document.getRoot() );
273
- const detectedWords = txt.match( this._wordsMatchRegExp ) || [];
274
-
275
- return detectedWords.length;
276
- }
277
-
278
- /**
279
- * Determines the number of words and characters in the current editor's model and assigns it to {@link #characters} and {@link #words}.
280
- * It also fires the {@link #event:update}.
281
- *
282
- * @private
283
- * @fires update
284
- */
285
- _refreshStats() {
286
- const words = this.words = this._getWords();
287
- const characters = this.characters = this._getCharacters();
288
-
289
- this.fire( 'update', {
290
- words,
291
- characters
292
- } );
293
- }
41
+ /**
42
+ * @inheritDoc
43
+ */
44
+ constructor(editor) {
45
+ super(editor);
46
+ this.set('characters', 0);
47
+ this.set('words', 0);
48
+ // Don't wait for the #update event to set the value of the properties but obtain it right away.
49
+ // This way, accessing the properties directly returns precise numbers, e.g. for validation, etc.
50
+ // If not accessed directly, the properties will be refreshed upon #update anyway.
51
+ Object.defineProperties(this, {
52
+ characters: {
53
+ get() {
54
+ return (this.characters = this._getCharacters());
55
+ }
56
+ },
57
+ words: {
58
+ get() {
59
+ return (this.words = this._getWords());
60
+ }
61
+ }
62
+ });
63
+ this.set('_wordsLabel', undefined);
64
+ this.set('_charactersLabel', undefined);
65
+ this._config = editor.config.get('wordCount') || {};
66
+ this._outputView = undefined;
67
+ this._wordsMatchRegExp = env.features.isRegExpUnicodePropertySupported ?
68
+ // Usage of regular expression literal cause error during build (ckeditor/ckeditor5-dev#534).
69
+ // Groups:
70
+ // {L} - Any kind of letter from any language.
71
+ // {N} - Any kind of numeric character in any script.
72
+ new RegExp('([\\p{L}\\p{N}]+\\S?)+', 'gu') :
73
+ /([a-zA-Z0-9À-ž]+\S?)+/gu;
74
+ }
75
+ /**
76
+ * @inheritDoc
77
+ */
78
+ static get pluginName() {
79
+ return 'WordCount';
80
+ }
81
+ /**
82
+ * @inheritDoc
83
+ */
84
+ init() {
85
+ const editor = this.editor;
86
+ editor.model.document.on('change:data', throttle(this._refreshStats.bind(this), 250));
87
+ if (typeof this._config.onUpdate == 'function') {
88
+ this.on('update', (evt, data) => {
89
+ this._config.onUpdate(data);
90
+ });
91
+ }
92
+ if (isElement(this._config.container)) {
93
+ this._config.container.appendChild(this.wordCountContainer);
94
+ }
95
+ }
96
+ /**
97
+ * @inheritDoc
98
+ */
99
+ destroy() {
100
+ if (this._outputView) {
101
+ this._outputView.element.remove();
102
+ this._outputView.destroy();
103
+ }
104
+ super.destroy();
105
+ }
106
+ /**
107
+ * Creates a self-updating HTML element. Repeated executions return the same element.
108
+ * The returned element has the following HTML structure:
109
+ *
110
+ * ```html
111
+ * <div class="ck ck-word-count">
112
+ * <div class="ck-word-count__words">Words: 4</div>
113
+ * <div class="ck-word-count__characters">Characters: 28</div>
114
+ * </div>
115
+ * ```
116
+ */
117
+ get wordCountContainer() {
118
+ const editor = this.editor;
119
+ const t = editor.t;
120
+ const displayWords = editor.config.get('wordCount.displayWords');
121
+ const displayCharacters = editor.config.get('wordCount.displayCharacters');
122
+ const bind = Template.bind(this, this);
123
+ const children = [];
124
+ if (!this._outputView) {
125
+ this._outputView = new View();
126
+ if (displayWords || displayWords === undefined) {
127
+ this.bind('_wordsLabel').to(this, 'words', words => {
128
+ return t('Words: %0', words);
129
+ });
130
+ children.push({
131
+ tag: 'div',
132
+ children: [
133
+ {
134
+ text: [bind.to('_wordsLabel')]
135
+ }
136
+ ],
137
+ attributes: {
138
+ class: 'ck-word-count__words'
139
+ }
140
+ });
141
+ }
142
+ if (displayCharacters || displayCharacters === undefined) {
143
+ this.bind('_charactersLabel').to(this, 'characters', words => {
144
+ return t('Characters: %0', words);
145
+ });
146
+ children.push({
147
+ tag: 'div',
148
+ children: [
149
+ {
150
+ text: [bind.to('_charactersLabel')]
151
+ }
152
+ ],
153
+ attributes: {
154
+ class: 'ck-word-count__characters'
155
+ }
156
+ });
157
+ }
158
+ this._outputView.setTemplate({
159
+ tag: 'div',
160
+ attributes: {
161
+ class: [
162
+ 'ck',
163
+ 'ck-word-count'
164
+ ]
165
+ },
166
+ children
167
+ });
168
+ this._outputView.render();
169
+ }
170
+ return this._outputView.element;
171
+ }
172
+ /**
173
+ * Determines the number of characters in the current editor's model.
174
+ */
175
+ _getCharacters() {
176
+ const txt = modelElementToPlainText(this.editor.model.document.getRoot());
177
+ return txt.replace(/\n/g, '').length;
178
+ }
179
+ /**
180
+ * Determines the number of words in the current editor's model.
181
+ */
182
+ _getWords() {
183
+ const txt = modelElementToPlainText(this.editor.model.document.getRoot());
184
+ const detectedWords = txt.match(this._wordsMatchRegExp) || [];
185
+ return detectedWords.length;
186
+ }
187
+ /**
188
+ * Determines the number of words and characters in the current editor's model and assigns it to {@link #characters} and {@link #words}.
189
+ * It also fires the {@link #event:update}.
190
+ *
191
+ * @fires update
192
+ */
193
+ _refreshStats() {
194
+ const words = this.words = this._getWords();
195
+ const characters = this.characters = this._getCharacters();
196
+ this.fire('update', {
197
+ words,
198
+ characters
199
+ });
200
+ }
294
201
  }
295
-
296
- /**
297
- * An event fired after {@link #words} and {@link #characters} are updated.
298
- *
299
- * @event update
300
- * @param {Object} data
301
- * @param {Number} data.words The number of words in the current model.
302
- * @param {Number} data.characters The number of characters in the current model.
303
- */
304
-
305
- /**
306
- * The configuration of the word count feature.
307
- *
308
- * ClassicEditor
309
- * .create( {
310
- * wordCount: ... // Word count feature configuration.
311
- * } )
312
- * .then( ... )
313
- * .catch( ... );
314
- *
315
- * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
316
- *
317
- * @interface module:word-count/wordcount~WordCountConfig
318
- */
319
-
320
- /**
321
- * The configuration of the word count feature.
322
- * It is introduced by the {@link module:word-count/wordcount~WordCount} feature.
323
- *
324
- * Read more in {@link module:word-count/wordcount~WordCountConfig}.
325
- *
326
- * @member {module:word-count/wordcount~WordCountConfig} module:core/editor/editorconfig~EditorConfig#wordCount
327
- */
328
-
329
- /**
330
- * This option allows for hiding the word counter. The element obtained through
331
- * {@link module:word-count/wordcount~WordCount#wordCountContainer} will only preserve
332
- * the characters part. Word counter is displayed by default when this configuration option is not defined.
333
- *
334
- * const wordCountConfig = {
335
- * displayWords: false
336
- * };
337
- *
338
- * The configuration above will result in the following container:
339
- *
340
- * <div class="ck ck-word-count">
341
- * <div class="ck-word-count__characters">Characters: 28</div>
342
- * </div>
343
- *
344
- * @member {Boolean} module:word-count/wordcount~WordCountConfig#displayWords
345
- */
346
-
347
- /**
348
- * This option allows for hiding the character counter. The element obtained through
349
- * {@link module:word-count/wordcount~WordCount#wordCountContainer} will only preserve
350
- * the words part. Character counter is displayed by default when this configuration option is not defined.
351
- *
352
- * const wordCountConfig = {
353
- * displayCharacters: false
354
- * };
355
- *
356
- * The configuration above will result in the following container:
357
- *
358
- * <div class="ck ck-word-count">
359
- * <div class="ck-word-count__words">Words: 4</div>
360
- * </div>
361
- *
362
- * @member {Boolean} module:word-count/wordcount~WordCountConfig#displayCharacters
363
- */
364
-
365
- /**
366
- * This configuration takes a function that is executed whenever the word count plugin updates its values.
367
- * This function is called with one argument, which is an object with the `words` and `characters` keys containing
368
- * the number of detected words and characters in the document.
369
- *
370
- * const wordCountConfig = {
371
- * onUpdate: function( stats ) {
372
- * doSthWithWordNumber( stats.words );
373
- * doSthWithCharacterNumber( stats.characters );
374
- * }
375
- * };
376
- *
377
- * @member {Function} module:word-count/wordcount~WordCountConfig#onUpdate
378
- */
379
-
380
- /**
381
- * Allows for providing the HTML element that the
382
- * {@link module:word-count/wordcount~WordCount#wordCountContainer word count container} will be appended to automatically.
383
- *
384
- * const wordCountConfig = {
385
- * container: document.getElementById( 'container-for-word-count' );
386
- * };
387
- *
388
- * @member {HTMLElement} module:word-count/wordcount~WordCountConfig#container
389
- */