@ckeditor/ckeditor5-word-count 35.4.0 → 36.0.1

Sign up to get free protection for your applications and to get access to all the features.
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:()=>Q});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="[object Null]",x="[object Undefined]",C=g?g.toStringTag:void 0;const S=function(t){return null==t?void 0===t?x:O:C&&C in Object(t)?_(t):j(t)};const T=function(t){return null!=t&&"object"==typeof t};var E="[object Symbol]";const W=function(t){return"symbol"==typeof t||T(t)&&S(t)==E};var P=NaN,R=/^[-+]0x[0-9a-f]+$/i,V=/^0b[01]+$/i,L=/^0o[0-7]+$/i,N=parseInt;const A=function(t){if("number"==typeof t)return t;if(W(t))return P;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=V.test(t);return r||L.test(t)?N(t.slice(2),r?2:8):R.test(t)?P:+t};var M="Expected a function",I=Math.max,K=Math.min;const $=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(M);function g(e){var r=o,i=n;return o=n=void 0,l=e,c=t.apply(i,r)}function v(t){var r=t-u;return void 0===u||r>=e||r<0||h&&t-l>=i}function b(){var t=d();if(v(t))return w(t);a=setTimeout(b,function(t){var r=e-(t-u);return h?K(r,i-(t-l)):r}(t))}function w(t){return a=void 0,p&&o?g(t):(o=n=void 0,c)}function y(){var t=d(),r=v(t);if(o=arguments,n=this,u=t,r){if(void 0===a)return function(t){return l=t,a=setTimeout(b,e),f?g(t):c}(u);if(h)return clearTimeout(a),a=setTimeout(b,e),g(u)}return void 0===a&&(a=setTimeout(b,e)),c}return e=A(e)||0,s(r)&&(f=!!r.leading,i=(h="maxWait"in r)?I(A(r.maxWait)||0,e):i,p="trailing"in r?!!r.trailing:p),y.cancel=function(){void 0!==a&&clearTimeout(a),l=0,o=u=n=a=void 0},y.flush=function(){return void 0===a?c:w(d())},y};var k="Expected a function";const U=function(t,e,r){var o=!0,n=!0;if("function"!=typeof t)throw new TypeError(k);return s(r)&&(o="leading"in r?!!r.leading:o,n="trailing"in r?!!r.trailing:n),$(t,e,{leading:o,maxWait:e,trailing:n})};const D=function(t,e){return function(r){return t(e(r))}}(Object.getPrototypeOf,Object);var F="[object Object]",z=Function.prototype,Z=Object.prototype,q=z.toString,B=Z.hasOwnProperty,G=q.call(Object);const H=function(t){if(!T(t)||S(t)!=F)return!1;var e=D(t);if(null===e)return!0;var r=B.call(e,"constructor")&&e.constructor;return"function"==typeof r&&r instanceof r&&q.call(r)==G};const J=function(t){return T(t)&&1===t.nodeType&&!H(t)};class Q 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",U(this._refreshStats.bind(this),250)),"function"==typeof this._config.onUpdate&&this.on("update",((t,e)=>{this._config.onUpdate(e)})),J(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.1",
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.1"
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.1",
20
+ "@ckeditor/ckeditor5-block-quote": "^36.0.1",
21
+ "@ckeditor/ckeditor5-core": "^36.0.1",
22
+ "@ckeditor/ckeditor5-dev-utils": "^32.0.0",
23
+ "@ckeditor/ckeditor5-editor-classic": "^36.0.1",
24
+ "@ckeditor/ckeditor5-engine": "^36.0.1",
25
+ "@ckeditor/ckeditor5-enter": "^36.0.1",
26
+ "@ckeditor/ckeditor5-image": "^36.0.1",
27
+ "@ckeditor/ckeditor5-link": "^36.0.1",
28
+ "@ckeditor/ckeditor5-list": "^36.0.1",
29
+ "@ckeditor/ckeditor5-paragraph": "^36.0.1",
30
+ "@ckeditor/ckeditor5-table": "^36.0.1",
31
+ "@ckeditor/ckeditor5-theme-lark": "^36.0.1",
32
+ "@ckeditor/ckeditor5-utils": "^36.0.1",
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
- */