ckeditor wordcount
parent
15e23a2d04
commit
9f903b645a
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) Ingo Herbote
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
@ -0,0 +1,106 @@
|
|||||||
|
CKEditor-WordCount-Plugin
|
||||||
|
=========================
|
||||||
|
|
||||||
|
WordCount Plugin for CKEditor v4 (or above) that counts the words/characters an shows the word count and/or char count in the footer of the editor.
|
||||||
|
|
||||||
|
![Screenshot](http://www.watchersnet.de/Portals/0/screenshots/dnn/CKEditorWordCountPlugin.png)
|
||||||
|
|
||||||
|
#### Demo
|
||||||
|
|
||||||
|
http://w8tcha.github.com/CKEditor-WordCount-Plugin/
|
||||||
|
|
||||||
|
DISCLAIMER: This is a forked Version, i can not find the original Author anymore if anyone knows the original Author please contact me and i can include the Author in the Copyright Notices.
|
||||||
|
|
||||||
|
#### License
|
||||||
|
|
||||||
|
Licensed under the terms of the MIT License.
|
||||||
|
|
||||||
|
#### Installation
|
||||||
|
|
||||||
|
If building a new editor using the CKBuilder from http://ckeditor.com/, there is no need to follow numbers steps below. If adding the Word Count & Char Count plugin to an already established CKEditor, follow the numbered steps below.
|
||||||
|
|
||||||
|
1. Download the Word Count & Char Count plugin from http://ckeditor.com/addon/wordcount or https://github.com/w8tcha/CKEditor-WordCount-Plugin. This will download a folder named **wordcount_*version*.zip** or **CKEditor-WordCount-Plugin-master.zip** to your Downloads folder.
|
||||||
|
2. Download the Notification plugin from http://ckeditor.com/addon/notification. This will download a folder named **notification_*version*.zip** to your Downloads folder.
|
||||||
|
3. Extract the .zip folders for both the Word Count & Char Count and Notification plugin. After extraction, you should have a folder named **wordcount** and a folder named **notification**.
|
||||||
|
4. Move the wordcount folder to /web/server/root/ckeditor/plugins/. Move the notification folder to /web/server/root/ckeditor/plugins/.
|
||||||
|
5. Add the following line of text to the config.js file, which is located at /web/server/root/ckeditor/.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
config.extraPlugins = 'wordcount,notification';
|
||||||
|
```
|
||||||
|
|
||||||
|
Below is an example of what your config.js file might look like after adding config.extraPlugins = 'wordcount,notification';
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
CKEDITOR.editorConfig = function( config ) {
|
||||||
|
config.extraPlugins = 'wordcount,notification';
|
||||||
|
config.toolbar [
|
||||||
|
et cetera . . .
|
||||||
|
];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
There now should be text in the bottom right-hand corner of your CKEditor which counts the number of Paragraphs and number of Words in your CKEditor.
|
||||||
|
|
||||||
|
To modify the behavior of the Word Count & Char Count text at the bottom right-hand corner of your CKEditor, add the following text to your config.js file located at /web/server/root/ckeditor/config.js.
|
||||||
|
|
||||||
|
````js
|
||||||
|
config.wordcount = {
|
||||||
|
|
||||||
|
// Whether or not you want to show the Paragraphs Count
|
||||||
|
showParagraphs: true,
|
||||||
|
|
||||||
|
// Whether or not you want to show the Word Count
|
||||||
|
showWordCount: true,
|
||||||
|
|
||||||
|
// Whether or not you want to show the Char Count
|
||||||
|
showCharCount: false,
|
||||||
|
|
||||||
|
// Whether or not you want to count Spaces as Chars
|
||||||
|
countSpacesAsChars: false,
|
||||||
|
|
||||||
|
// Whether or not to include Html chars in the Char Count
|
||||||
|
countHTML: false,
|
||||||
|
|
||||||
|
// Whether or not to include Line Breaks in the Char Count
|
||||||
|
countLineBreaks: false,
|
||||||
|
|
||||||
|
// Maximum allowed Word Count, -1 is default for unlimited
|
||||||
|
maxWordCount: -1,
|
||||||
|
|
||||||
|
// Maximum allowed Char Count, -1 is default for unlimited
|
||||||
|
maxCharCount: -1,
|
||||||
|
|
||||||
|
// Maximum allowed Paragraphs Count, -1 is default for unlimited
|
||||||
|
maxParagraphs: -1,
|
||||||
|
|
||||||
|
// How long to show the 'paste' warning, 0 is default for not auto-closing the notification
|
||||||
|
pasteWarningDuration: 0,
|
||||||
|
|
||||||
|
|
||||||
|
// Add filter to add or remove element before counting (see CKEDITOR.htmlParser.filter), Default value : null (no filter)
|
||||||
|
filter: new CKEDITOR.htmlParser.filter({
|
||||||
|
elements: {
|
||||||
|
div: function( element ) {
|
||||||
|
if(element.attributes.class == 'mediaembed') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
````
|
||||||
|
|
||||||
|
**Note:** If you plan to change some of the JavaScript, you probably will not want to use the CKBuilder, because this will place the JavaScript of the Word Count & Char Count plugin in the ckeditor.js file located at /web/server/root/ckeditor/ckeditor.js. The JavaScript for the Word Count & Char Count plugin in the ckeditor.js file is different than the JavaScript used when manually adding the Word Count & Char Count plugin. When manually adding the Word Count & Char Count plugin, the JavaScript will be in the plugin.js file located at
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
If you want to query the current wordcount you can do it via
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// get the word count
|
||||||
|
CKEDITOR.instances.editor1.wordCount.wordCount
|
||||||
|
|
||||||
|
// get the char count
|
||||||
|
CKEDITOR.instances.editor1.wordCount.charCount
|
||||||
|
```
|
@ -0,0 +1,3 @@
|
|||||||
|
.cke_wordcount {display:block;float:right;margin-top:-2px;margin-right:3px;color:black;}
|
||||||
|
|
||||||
|
.cke_wordcountLimitReached {color:red! important}
|
@ -0,0 +1,12 @@
|
|||||||
|
// Arabic Translation by Amine BENHAMIDA
|
||||||
|
|
||||||
|
CKEDITOR.plugins.setLang('wordcount', 'ar', {
|
||||||
|
WordCount: 'كلمات:',
|
||||||
|
CharCount: 'حروف:',
|
||||||
|
CharCountWithHTML: 'حروف مع إتش تي إم إل',
|
||||||
|
Paragraphs: 'فقرات',
|
||||||
|
ParagraphsRemaining: 'Paragraphs remaining',
|
||||||
|
pasteWarning: 'لا يمكن اضافة هذا المحتوى لانه تجاوز الحد الاقصى',
|
||||||
|
Selected: 'محدد: ',
|
||||||
|
title: 'احصائيات'
|
||||||
|
});
|
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
|
||||||
|
For licensing, see LICENSE.html or http://ckeditor.com/license
|
||||||
|
*/
|
||||||
|
CKEDITOR.plugins.setLang('wordcount', 'da', {
|
||||||
|
WordCount: 'Ord:',
|
||||||
|
CharCount: 'Karakterer:',
|
||||||
|
CharCountWithHTML: 'Karakterer (med HTML):',
|
||||||
|
Paragraphs: 'Afsnit:',
|
||||||
|
ParagraphsRemaining: 'Paragraphs remaining',
|
||||||
|
pasteWarning: 'Indholdet kan ikke indsættes da det er længere end den tilladte grænse.',
|
||||||
|
Selected: 'Markeret: ',
|
||||||
|
title: 'Statistik'
|
||||||
|
});
|
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Finnish localisation.
|
||||||
|
*
|
||||||
|
* @author Joel Posti / Response200.pro
|
||||||
|
*/
|
||||||
|
CKEDITOR.plugins.setLang('wordcount', 'fi', {
|
||||||
|
WordCount: 'Sanoja:',
|
||||||
|
CharCount: 'Merkkejä:',
|
||||||
|
CharCountWithHTML: 'Merkkejä (ml. HTML):',
|
||||||
|
Paragraphs: 'Kappaleita:',
|
||||||
|
ParagraphsRemaining: 'Paragraphs remaining',
|
||||||
|
pasteWarning: 'Sisältöä ei voida liittää, koska se ylittää sallitun rajan.',
|
||||||
|
Selected: 'Valittuna: ',
|
||||||
|
title: 'Statistiikkaa'
|
||||||
|
});
|
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
|
||||||
|
For licensing, see LICENSE.html or http://ckeditor.com/license
|
||||||
|
@author translation: Davide Montorio
|
||||||
|
*/
|
||||||
|
CKEDITOR.plugins.setLang('wordcount', 'it', {
|
||||||
|
WordCount: 'Parole:',
|
||||||
|
CharCount: 'Caratteri:',
|
||||||
|
CharCountWithHTML: 'Caratteri (HTML incluso):',
|
||||||
|
Paragraphs: 'Paragrafi:',
|
||||||
|
ParagraphsRemaining: 'Paragraphs remaining',
|
||||||
|
pasteWarning: 'Il contenuto non può essere incollato poiché supera il limite massimo di caratteri disponibili',
|
||||||
|
Selected: 'Selezionato: ',
|
||||||
|
title: 'Statistiche'
|
||||||
|
});
|
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
Korean translation by Maxime Houdais
|
||||||
|
*/
|
||||||
|
CKEDITOR.plugins.setLang('wordcount', 'ko', {
|
||||||
|
WordCount: '단어:',
|
||||||
|
WordCountRemaining: '남은 단어',
|
||||||
|
CharCount: '글자:',
|
||||||
|
CharCountRemaining: '남은 글자',
|
||||||
|
CharCountWithHTML: '글자 와 HTML:',
|
||||||
|
CharCountWithHTMLRemaining: '남은 글자 와 HTML',
|
||||||
|
Paragraphs: '단락:',
|
||||||
|
ParagraphsRemaining: 'Paragraphs remaining',
|
||||||
|
pasteWarning: '허용 된 한도를 초과하여 콘텐츠를 붙여 넣을 수 없습니다.',
|
||||||
|
Selected: '선택:',
|
||||||
|
title: '통계'
|
||||||
|
});
|
@ -0,0 +1,10 @@
|
|||||||
|
CKEDITOR.plugins.setLang('wordcount', 'pt', {
|
||||||
|
WordCount: 'Palavras:',
|
||||||
|
CharCount: 'Caracteres:',
|
||||||
|
CharCountWithHTML: 'Carateres (incluindo HTML):',
|
||||||
|
Paragraphs: 'Parágrafos:',
|
||||||
|
ParagraphsRemaining: 'Paragraphs remaining',
|
||||||
|
pasteWarning: 'O conteúdo não pode ser colado porque ultrapassa o limite permitido',
|
||||||
|
Selected: 'Selecionado: ',
|
||||||
|
title: 'Estatísticas'
|
||||||
|
});
|
@ -0,0 +1,596 @@
|
|||||||
|
/**
|
||||||
|
* @license Copyright (c) CKSource - Frederico Knabben. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.html or http://ckeditor.com/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
CKEDITOR.plugins.add("wordcount",
|
||||||
|
{
|
||||||
|
lang: "ar,bg,ca,cs,da,de,el,en,es,eu,fa,fi,fr,he,hr,hu,it,ko,ja,nl,no,pl,pt,pt-br,ru,sk,sv,tr,uk,zh-cn,zh,ro", // %REMOVE_LINE_CORE%
|
||||||
|
version: "1.17.6",
|
||||||
|
requires: 'htmlwriter,notification,undo',
|
||||||
|
bbcodePluginLoaded: false,
|
||||||
|
onLoad: function() {
|
||||||
|
CKEDITOR.document.appendStyleSheet(this.path + "css/wordcount.css");
|
||||||
|
},
|
||||||
|
init: function(editor) {
|
||||||
|
var defaultFormat = "",
|
||||||
|
lastWordCount = -1,
|
||||||
|
lastCharCount = -1,
|
||||||
|
lastParagraphs = -1,
|
||||||
|
limitReachedNotified = false,
|
||||||
|
limitRestoredNotified = false,
|
||||||
|
timeoutId = 0,
|
||||||
|
notification = null;
|
||||||
|
|
||||||
|
|
||||||
|
var dispatchEvent = function(type, currentLength, maxLength) {
|
||||||
|
if (typeof document.dispatchEvent == 'undefined') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = 'ckeditor.wordcount.' + type;
|
||||||
|
|
||||||
|
var cEvent;
|
||||||
|
var eventInitDict = {
|
||||||
|
bubbles: false,
|
||||||
|
cancelable: true,
|
||||||
|
detail: {
|
||||||
|
currentLength: currentLength,
|
||||||
|
maxLength: maxLength
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
cEvent = new CustomEvent(type, eventInitDict);
|
||||||
|
} catch (o_O) {
|
||||||
|
cEvent = document.createEvent('CustomEvent');
|
||||||
|
cEvent.initCustomEvent(
|
||||||
|
type,
|
||||||
|
eventInitDict.bubbles,
|
||||||
|
eventInitDict.cancelable,
|
||||||
|
eventInitDict.detail
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.dispatchEvent(cEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Default Config
|
||||||
|
var defaultConfig = {
|
||||||
|
showRemaining: false,
|
||||||
|
showParagraphs: true,
|
||||||
|
showWordCount: true,
|
||||||
|
showCharCount: false,
|
||||||
|
countBytesAsChars: false,
|
||||||
|
countSpacesAsChars: false,
|
||||||
|
countHTML: false,
|
||||||
|
countLineBreaks: false,
|
||||||
|
hardLimit: true,
|
||||||
|
warnOnLimitOnly: false,
|
||||||
|
|
||||||
|
//MAXLENGTH Properties
|
||||||
|
maxWordCount: -1,
|
||||||
|
maxCharCount: -1,
|
||||||
|
maxParagraphs: -1,
|
||||||
|
|
||||||
|
// Filter
|
||||||
|
filter: null,
|
||||||
|
|
||||||
|
// How long to show the 'paste' warning
|
||||||
|
pasteWarningDuration: 0,
|
||||||
|
|
||||||
|
//DisAllowed functions
|
||||||
|
wordCountGreaterThanMaxLengthEvent: function(currentLength, maxLength) {
|
||||||
|
dispatchEvent('wordCountGreaterThanMaxLengthEvent', currentLength, maxLength);
|
||||||
|
},
|
||||||
|
charCountGreaterThanMaxLengthEvent: function(currentLength, maxLength) {
|
||||||
|
dispatchEvent('charCountGreaterThanMaxLengthEvent', currentLength, maxLength);
|
||||||
|
},
|
||||||
|
|
||||||
|
//Allowed Functions
|
||||||
|
wordCountLessThanMaxLengthEvent: function(currentLength, maxLength) {
|
||||||
|
dispatchEvent('wordCountLessThanMaxLengthEvent', currentLength, maxLength);
|
||||||
|
},
|
||||||
|
charCountLessThanMaxLengthEvent: function(currentLength, maxLength) {
|
||||||
|
dispatchEvent('charCountLessThanMaxLengthEvent', currentLength, maxLength);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get Config & Lang
|
||||||
|
var config = CKEDITOR.tools.extend(defaultConfig, editor.config.wordcount || {}, true);
|
||||||
|
|
||||||
|
if (config.showParagraphs) {
|
||||||
|
if (config.maxParagraphs > -1) {
|
||||||
|
if (config.showRemaining) {
|
||||||
|
defaultFormat += "%paragraphsCount% " + editor.lang.wordcount.ParagraphsRemaining;
|
||||||
|
} else {
|
||||||
|
defaultFormat += editor.lang.wordcount.Paragraphs + " %paragraphsCount%";
|
||||||
|
|
||||||
|
defaultFormat += "/" + config.maxParagraphs;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
defaultFormat += editor.lang.wordcount.Paragraphs + " %paragraphsCount%";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.showParagraphs && (config.showWordCount || config.showCharCount)) {
|
||||||
|
defaultFormat += ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.showWordCount) {
|
||||||
|
if (config.maxWordCount > -1) {
|
||||||
|
if (config.showRemaining) {
|
||||||
|
defaultFormat += "%wordCount% " + editor.lang.wordcount.WordCountRemaining;
|
||||||
|
} else {
|
||||||
|
defaultFormat += editor.lang.wordcount.WordCount + " %wordCount%";
|
||||||
|
|
||||||
|
defaultFormat += "/" + config.maxWordCount;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
defaultFormat += editor.lang.wordcount.WordCount + " %wordCount%";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.showCharCount && config.showWordCount) {
|
||||||
|
defaultFormat += ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.showCharCount) {
|
||||||
|
if (config.maxCharCount > -1) {
|
||||||
|
if (config.showRemaining) {
|
||||||
|
defaultFormat += "%charCount% " +
|
||||||
|
editor.lang.wordcount[config.countHTML
|
||||||
|
? "CharCountWithHTMLRemaining"
|
||||||
|
: "CharCountRemaining"];
|
||||||
|
} else {
|
||||||
|
defaultFormat += editor.lang.wordcount[config.countHTML
|
||||||
|
? "CharCountWithHTML"
|
||||||
|
: "CharCount"] +
|
||||||
|
" %charCount%";
|
||||||
|
|
||||||
|
defaultFormat += "/" + config.maxCharCount;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
defaultFormat += editor.lang.wordcount[config.countHTML ? "CharCountWithHTML" : "CharCount"] +
|
||||||
|
" %charCount%";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var format = defaultFormat;
|
||||||
|
|
||||||
|
bbcodePluginLoaded = typeof editor.plugins.bbcode != 'undefined';
|
||||||
|
|
||||||
|
function counterId(editorInstance) {
|
||||||
|
return "cke_wordcount_" + editorInstance.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function counterElement(editorInstance) {
|
||||||
|
return document.getElementById(counterId(editorInstance));
|
||||||
|
}
|
||||||
|
|
||||||
|
function strip(html) {
|
||||||
|
if (bbcodePluginLoaded) {
|
||||||
|
// stripping out BBCode tags [...][/...]
|
||||||
|
return html.replace(/\[.*?\]/gi, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
var tmp = document.createElement("div");
|
||||||
|
|
||||||
|
// Add filter before strip
|
||||||
|
html = filter(html);
|
||||||
|
|
||||||
|
tmp.innerHTML = html;
|
||||||
|
|
||||||
|
if (tmp.textContent == "" && typeof tmp.innerText == "undefined") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmp.textContent || tmp.innerText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement filter to add or remove before counting
|
||||||
|
* @param html
|
||||||
|
* @returns string
|
||||||
|
*/
|
||||||
|
function filter(html) {
|
||||||
|
if (config.filter instanceof CKEDITOR.htmlParser.filter) {
|
||||||
|
var fragment = CKEDITOR.htmlParser.fragment.fromHtml(html),
|
||||||
|
writer = new CKEDITOR.htmlParser.basicWriter();
|
||||||
|
config.filter.applyTo(fragment);
|
||||||
|
fragment.writeHtml(writer);
|
||||||
|
return writer.getHtml();
|
||||||
|
}
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function countCharacters(text) {
|
||||||
|
if (config.countHTML) {
|
||||||
|
return config.countBytesAsChars ? countBytes(filter(text)) : filter(text).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalizedText;
|
||||||
|
|
||||||
|
// strip body tags
|
||||||
|
if (editor.config.fullPage) {
|
||||||
|
var i = text.search(new RegExp("<body>", "i"));
|
||||||
|
if (i != -1) {
|
||||||
|
var j = text.search(new RegExp("</body>", "i"));
|
||||||
|
text = text.substring(i + 6, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizedText = text;
|
||||||
|
|
||||||
|
if (!config.countSpacesAsChars) {
|
||||||
|
normalizedText = text.replace(/\s/g, "").replace(/ /g, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.countLineBreaks) {
|
||||||
|
normalizedText = normalizedText.replace(/(\r\n|\n|\r)/gm, " ");
|
||||||
|
} else {
|
||||||
|
normalizedText = normalizedText.replace(/(\r\n|\n|\r)/gm, "").replace(/ /gi, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizedText = strip(normalizedText).replace(/^([\t\r\n]*)$/, "");
|
||||||
|
|
||||||
|
return config.countBytesAsChars ? countBytes(normalizedText) : normalizedText.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function countBytes(text) {
|
||||||
|
var count = 0, stringLength = text.length, i;
|
||||||
|
text = String(text || "");
|
||||||
|
for (i = 0; i < stringLength; i++) {
|
||||||
|
var partCount = encodeURI(text[i]).split("%").length;
|
||||||
|
count += partCount == 1 ? 1 : partCount - 1;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
function countParagraphs(text) {
|
||||||
|
return (text.replace(/ /g, " ").replace(/(<([^>]+)>)/ig, "").replace(/^\s*$[\n\r]{1,}/gm, "++")
|
||||||
|
.split("++").length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function countWords(text) {
|
||||||
|
var normalizedText = text.replace(/(\r\n|\n|\r)/gm, " ").replace(/^\s+|\s+$/g, "")
|
||||||
|
.replace(" ", " ");
|
||||||
|
|
||||||
|
normalizedText = strip(normalizedText);
|
||||||
|
|
||||||
|
var words = normalizedText.split(/\s+/);
|
||||||
|
|
||||||
|
for (var wordIndex = words.length - 1; wordIndex >= 0; wordIndex--) {
|
||||||
|
if (words[wordIndex].match(/^([\s\t\r\n]*)$/)) {
|
||||||
|
words.splice(wordIndex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (words.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function limitReached(editorInstance, notify) {
|
||||||
|
limitReachedNotified = true;
|
||||||
|
limitRestoredNotified = false;
|
||||||
|
|
||||||
|
if (!config.warnOnLimitOnly) {
|
||||||
|
if (config.hardLimit) {
|
||||||
|
editorInstance.execCommand('undo');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!notify) {
|
||||||
|
counterElement(editorInstance).className = "cke_path_item cke_wordcountLimitReached";
|
||||||
|
editorInstance.fire("limitReached", { firedBy: "wordCount.limitReached" }, editor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function limitRestored(editorInstance) {
|
||||||
|
limitRestoredNotified = true;
|
||||||
|
limitReachedNotified = false;
|
||||||
|
|
||||||
|
if (!config.warnOnLimitOnly) {
|
||||||
|
editorInstance.fire('saveSnapshot');
|
||||||
|
}
|
||||||
|
|
||||||
|
counterElement(editorInstance).className = "cke_path_item";
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCounter(editorInstance) {
|
||||||
|
if (!counterElement(editorInstance)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var paragraphs = 0,
|
||||||
|
wordCount = 0,
|
||||||
|
charCount = 0,
|
||||||
|
text;
|
||||||
|
|
||||||
|
// BeforeGetData and getData events are fired when calling
|
||||||
|
// getData(). We can prevent this by passing true as an
|
||||||
|
// argument to getData(). This allows us to fire the events
|
||||||
|
// manually with additional event data: firedBy. This additional
|
||||||
|
// data helps differentiate calls to getData() made by
|
||||||
|
// wordCount plugin from calls made by other plugins/code.
|
||||||
|
editorInstance.fire("beforeGetData", { firedBy: "wordCount.updateCounter" }, editor);
|
||||||
|
text = editorInstance.getData(true);
|
||||||
|
editorInstance.fire("getData", { dataValue: text, firedBy: "wordCount.updateCounter" }, editor);
|
||||||
|
|
||||||
|
if (text) {
|
||||||
|
if (config.showCharCount) {
|
||||||
|
charCount = countCharacters(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.showParagraphs) {
|
||||||
|
paragraphs = countParagraphs(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.showWordCount) {
|
||||||
|
wordCount = countWords(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = format;
|
||||||
|
if (config.showRemaining) {
|
||||||
|
if (config.maxCharCount >= 0) {
|
||||||
|
html = html.replace("%charCount%", config.maxCharCount - charCount);
|
||||||
|
} else {
|
||||||
|
html = html.replace("%charCount%", charCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.maxWordCount >= 0) {
|
||||||
|
html = html.replace("%wordCount%", config.maxWordCount - wordCount);
|
||||||
|
} else {
|
||||||
|
html = html.replace("%wordCount%", wordCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.maxParagraphs >= 0) {
|
||||||
|
html = html.replace("%paragraphsCount%", config.maxParagraphs - paragraphs);
|
||||||
|
} else {
|
||||||
|
html = html.replace("%paragraphsCount%", paragraphs);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html = html.replace("%wordCount%", wordCount).replace("%charCount%", charCount).replace("%paragraphsCount%", paragraphs);
|
||||||
|
}
|
||||||
|
|
||||||
|
(editorInstance.config.wordcount || (editorInstance.config.wordcount = {})).wordCount = wordCount;
|
||||||
|
(editorInstance.config.wordcount || (editorInstance.config.wordcount = {})).charCount = charCount;
|
||||||
|
|
||||||
|
if (CKEDITOR.env.gecko) {
|
||||||
|
counterElement(editorInstance).innerHTML = html;
|
||||||
|
} else {
|
||||||
|
counterElement(editorInstance).innerText = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (charCount == lastCharCount && wordCount == lastWordCount && paragraphs == lastParagraphs) {
|
||||||
|
if (charCount == config.maxCharCount || wordCount == config.maxWordCount || paragraphs > config.maxParagraphs) {
|
||||||
|
editorInstance.fire('saveSnapshot');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//If the limit is already over, allow the deletion of characters/words. Otherwise,
|
||||||
|
//the user would have to delete at one go the number of offending characters
|
||||||
|
var deltaWord = wordCount - lastWordCount;
|
||||||
|
var deltaChar = charCount - lastCharCount;
|
||||||
|
var deltaParagraphs = paragraphs - lastParagraphs;
|
||||||
|
|
||||||
|
lastWordCount = wordCount;
|
||||||
|
lastCharCount = charCount;
|
||||||
|
lastParagraphs = paragraphs;
|
||||||
|
|
||||||
|
if (lastWordCount == -1) {
|
||||||
|
lastWordCount = wordCount;
|
||||||
|
}
|
||||||
|
if (lastCharCount == -1) {
|
||||||
|
lastCharCount = charCount;
|
||||||
|
}
|
||||||
|
if (lastParagraphs == -1) {
|
||||||
|
lastParagraphs = paragraphs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for word limit and/or char limit
|
||||||
|
if ((config.maxWordCount > -1 && wordCount > config.maxWordCount && deltaWord > 0) ||
|
||||||
|
(config.maxCharCount > -1 && charCount > config.maxCharCount && deltaChar > 0) ||
|
||||||
|
(config.maxParagraphs > -1 && paragraphs > config.maxParagraphs && deltaParagraphs > 0)) {
|
||||||
|
|
||||||
|
limitReached(editorInstance, limitReachedNotified);
|
||||||
|
} else if ((config.maxWordCount == -1 || wordCount <= config.maxWordCount) &&
|
||||||
|
(config.maxCharCount == -1 || charCount <= config.maxCharCount) &&
|
||||||
|
(config.maxParagraphs == -1 || paragraphs <= config.maxParagraphs)) {
|
||||||
|
|
||||||
|
limitRestored(editorInstance);
|
||||||
|
} else {
|
||||||
|
editorInstance.fire('saveSnapshot');
|
||||||
|
}
|
||||||
|
|
||||||
|
// update instance
|
||||||
|
editorInstance.wordCount =
|
||||||
|
{
|
||||||
|
paragraphs: paragraphs,
|
||||||
|
wordCount: wordCount,
|
||||||
|
charCount: charCount
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Fire Custom Events
|
||||||
|
if (config.charCountGreaterThanMaxLengthEvent && config.charCountLessThanMaxLengthEvent) {
|
||||||
|
if (charCount > config.maxCharCount && config.maxCharCount > -1) {
|
||||||
|
config.charCountGreaterThanMaxLengthEvent(charCount, config.maxCharCount);
|
||||||
|
} else {
|
||||||
|
config.charCountLessThanMaxLengthEvent(charCount, config.maxCharCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.wordCountGreaterThanMaxLengthEvent && config.wordCountLessThanMaxLengthEvent) {
|
||||||
|
if (wordCount > config.maxWordCount && config.maxWordCount > -1) {
|
||||||
|
config.wordCountGreaterThanMaxLengthEvent(wordCount, config.maxWordCount);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
config.wordCountLessThanMaxLengthEvent(wordCount, config.maxWordCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCloseToLimits() {
|
||||||
|
if (config.maxWordCount > -1 && config.maxWordCount - lastWordCount < 5) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.maxCharCount > -1 && config.maxCharCount - lastCharCount < 20) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.maxParagraphs > -1 && config.maxParagraphs - lastParagraphs < 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.on("key",
|
||||||
|
function(event) {
|
||||||
|
if (editor.mode === "source") {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
timeoutId = setTimeout(
|
||||||
|
updateCounter.bind(this, event.editor),
|
||||||
|
250
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
editor,
|
||||||
|
null,
|
||||||
|
100);
|
||||||
|
|
||||||
|
editor.on("change",
|
||||||
|
function(event) {
|
||||||
|
var ms = isCloseToLimits() ? 5 : 250;
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
timeoutId = setTimeout(
|
||||||
|
updateCounter.bind(this, event.editor),
|
||||||
|
ms
|
||||||
|
);
|
||||||
|
},
|
||||||
|
editor,
|
||||||
|
null,
|
||||||
|
100);
|
||||||
|
|
||||||
|
editor.on("uiSpace",
|
||||||
|
function(event) {
|
||||||
|
if (editor.elementMode === CKEDITOR.ELEMENT_MODE_INLINE) {
|
||||||
|
if (event.data.space == "top") {
|
||||||
|
event.data.html += "<div class=\"cke_wordcount\" style=\"\"" +
|
||||||
|
" title=\"" +
|
||||||
|
editor.lang.wordcount.title +
|
||||||
|
"\"" +
|
||||||
|
"><span id=\"" +
|
||||||
|
counterId(event.editor) +
|
||||||
|
"\" class=\"cke_path_item\"> </span></div>";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (event.data.space == "bottom") {
|
||||||
|
event.data.html += "<div class=\"cke_wordcount\" style=\"\"" +
|
||||||
|
" title=\"" +
|
||||||
|
editor.lang.wordcount.title +
|
||||||
|
"\"" +
|
||||||
|
"><span id=\"" +
|
||||||
|
counterId(event.editor) +
|
||||||
|
"\" class=\"cke_path_item\"> </span></div>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
editor,
|
||||||
|
null,
|
||||||
|
100);
|
||||||
|
|
||||||
|
editor.on("dataReady",
|
||||||
|
function(event) {
|
||||||
|
updateCounter(event.editor);
|
||||||
|
},
|
||||||
|
editor,
|
||||||
|
null,
|
||||||
|
100);
|
||||||
|
|
||||||
|
editor.on("paste",
|
||||||
|
function(event) {
|
||||||
|
if (!config.warnOnLimitOnly && (config.maxWordCount > 0 || config.maxCharCount > 0 || config.maxParagraphs > 0)) {
|
||||||
|
|
||||||
|
// Check if pasted content is above the limits
|
||||||
|
var wordCount = -1,
|
||||||
|
charCount = -1,
|
||||||
|
paragraphs = -1;
|
||||||
|
|
||||||
|
// BeforeGetData and getData events are fired when calling
|
||||||
|
// getData(). We can prevent this by passing true as an
|
||||||
|
// argument to getData(). This allows us to fire the events
|
||||||
|
// manually with additional event data: firedBy. This additional
|
||||||
|
// data helps differentiate calls to getData() made by
|
||||||
|
// wordCount plugin from calls made by other plugins/code.
|
||||||
|
event.editor.fire("beforeGetData", { firedBy: "wordCount.onPaste" }, event.editor);
|
||||||
|
var text = event.editor.getData(true);
|
||||||
|
event.editor.fire("getData", { dataValue: text, firedBy: "wordCount.onPaste" }, event.editor);
|
||||||
|
|
||||||
|
text += event.data.dataValue;
|
||||||
|
|
||||||
|
if (config.showCharCount) {
|
||||||
|
charCount = countCharacters(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.showWordCount) {
|
||||||
|
wordCount = countWords(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.showParagraphs) {
|
||||||
|
paragraphs = countParagraphs(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Instantiate the notification when needed and only have one instance
|
||||||
|
if (notification === null) {
|
||||||
|
notification = new CKEDITOR.plugins.notification(event.editor,
|
||||||
|
{
|
||||||
|
message: event.editor.lang.wordcount.pasteWarning,
|
||||||
|
type: 'warning',
|
||||||
|
duration: config.pasteWarningDuration
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.maxCharCount > 0 && charCount > config.maxCharCount && config.hardLimit) {
|
||||||
|
if (!notification.isVisible()) {
|
||||||
|
notification.show();
|
||||||
|
}
|
||||||
|
event.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.maxWordCount > 0 && wordCount > config.maxWordCount && config.hardLimit) {
|
||||||
|
if (!notification.isVisible()) {
|
||||||
|
notification.show();
|
||||||
|
}
|
||||||
|
event.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.maxParagraphs > 0 && paragraphs > config.maxParagraphs && config.hardLimit) {
|
||||||
|
if (!notification.isVisible()) {
|
||||||
|
notification.show();
|
||||||
|
}
|
||||||
|
event.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
editor,
|
||||||
|
null,
|
||||||
|
100);
|
||||||
|
|
||||||
|
editor.on("afterPaste",
|
||||||
|
function(event) {
|
||||||
|
updateCounter(event.editor);
|
||||||
|
},
|
||||||
|
editor,
|
||||||
|
null,
|
||||||
|
100);
|
||||||
|
}
|
||||||
|
});
|
Loading…
Reference in New Issue