'CKEditor - Limit characters on more editors in one page

I'm developing a web based ERP and I need some help.

As text editor, I choose CKEditor, works great and do everything I need. Well...not exactly everything...

I've added a plugin name "wordcount", that count words or characters and sets the limit.

The problem is that I've more CKeditors on one page, and I need to set different limits for each one. As you see, the plugin is setting the same limit for both editors:

enter image description here

Parameters are passed through config.js:

config.wordcount = {

// Whether or not you want to show the Paragraphs Count
showParagraphs: false,

// Whether or not you want to show the Word Count
showWordCount: false,

// Whether or not you want to show the Char Count
showCharCount: true,

// Whether or not you want to count Spaces as Chars
countSpacesAsChars: true,

// Whether or not to include Html chars in the Char Count
countHTML: false,

// Maximum allowed Word Count, -1 is default for unlimited
maxWordCount: 400,

// Maximum allowed Char Count, -1 is default for unlimited
maxCharCount: 400};

Do you know some way to do this? Also with another plugin or "manually".

Thanks in advance!



Solution 1:[1]

I realized this as follows: need to add attrs data to textArea tags with maxWord and maxChar, initialize CKeditor

window.InitializeCkeditor = {
  init: function() {
    var element, elements, i, results;
    elements = CKEDITOR.document.find('.js-ckeditor'); // your textArea
    i = 0;
    results = [];
     while (element = elements.getItem(i++)) {
       CKEDITOR.replace(element, {
         toolbar: 'mini', // your toolbar 
         height: 200
       });
       results.push(CKEDITOR.on('instanceCreated', function(event) {
         var editor, element;
         editor = event.editor;
         element = editor.element;
         return editor.on('configLoaded', function() {
           return editor.config.wordcount = {
             showWordCount: true,
             maxWordCount: element.data('word-max')
           };
        });
      }));
    }
    return results;
  }
};

Solution 2:[2]

You can specify specific configurations, when call CKEDITOR.replace() on your view page , the configurations you specified will overlap corresponding configurations in CKEDITOR config.js

var wordCountConf1 = {
    showParagraphs: false,
    showWordCount: true,
    showCharCount: true,
    countSpacesAsChars: false,
    countHTML: false,
    maxWordCount: -1,
    maxCharCount: 2000}
}

var wordCountConf2 = {
    showParagraphs: false,
    showWordCount: true,
    showCharCount: true,
    countSpacesAsChars: false,
    countHTML: false,
    maxWordCount: -1,
    maxCharCount: 5000}
}

CKEDITOR.replace('editor1', {wordcount: wordCountConf1});
CKEDITOR.replace('editor2', {wordcount: wordCountConf2});

Solution 3:[3]

CKEDITOR.replace('comments',
{toolbar:[
    { name: 'document', groups: [ 'mode', 'document', 'doctools' ], items: [ 'Source' ] },
    { name: 'clipboard', items: [ 'Undo', 'Redo', '-', 'Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord' ] },
    { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ], items: [ 'Bold', 'Italic', 'Strike', '-', 'TextColor' ] },
    { name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align' ], items: [ 'NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', 'Blocks' ] },
    { name: 'links', items: [ 'Link', 'Unlink' ] },
    { name: 'insert', items: [ 'Image', 'Table', 'SpecialChar' ] },
    { name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ], items: [ 'Scayt' ] }
],
height:400,
resize_enabled:true,
wordcount: {
    showParagraphs: false,
    showWordCount: true,
    showCharCount: true,
    countSpacesAsChars: false,
    countHTML: false,
    maxWordCount: -1,
    maxCharCount: 4000}
});

Solution 4:[4]

I use Ckeditor version 4.

After so long trying i solved the problem changing the wordcount plugin as follow. After that, all you need is set the attribute maxCharCount in the html element

CKEDITOR.plugins.add("wordcount",
    {
        lang: "ar,bg,ca,cs,da,de,el,en,es,eu,fa,fi,fr,he,hr,hu,it,ka,ko,ja,nl,no,pl,pt,pt-br,ru,sk,sv,tr,uk,zh-cn,zh,ro", // %REMOVE_LINE_CORE%
        version: "1.17.9",
        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,
                wordDelims: '',

                //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 (editor.element.getAttribute('maxCharCount')) {
                editor.config.wordcount.maxCharCount = parseInt(editor.element.getAttribute('maxCharCount'));
            }

            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 += "/" + "%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(/&nbsp;/g, "");
                }

                if (config.countLineBreaks) {
                    normalizedText = normalizedText.replace(/(\r\n|\n|\r)/gm, " ");
                } else {
                    normalizedText = normalizedText.replace(/(\r\n|\n|\r)/gm, "").replace(/&nbsp;/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(/&nbsp;/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("&nbsp;", " ");

                normalizedText = strip(normalizedText);

                var re = config.wordDelims ? new RegExp('[\\s'+config.wordDelims+']+') : /\s+/;
                var words = normalizedText.split(re);
                
                re = config.wordDelims ? new RegExp('^([\\s\\t\\r\\n'+config.wordDelims+']*)$') : /^([\s\t\r\n]*)$/;
                for (var wordIndex = words.length - 1; wordIndex >= 0; wordIndex--) {
                    if (!words[wordIndex] || words[wordIndex].match(re)) {
                        words.splice(wordIndex, 1);
                    }
                }

                return (words.length);
            }

            function limitReached(editorInstance, notify) {
                limitReachedNotified = true;
                limitRestoredNotified = false;

                if (!config.warnOnLimitOnly) {
                    if (config.hardLimit) {
                        if (editor.mode === "source" && editor.plugins.codemirror) {
                            window["codemirror_" + editor.id].undo();
                        } else {
                    //        editorInstance.execCommand("undo");
                            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 (editorInstance.config.wordcount.showCharCount) {
                        charCount = countCharacters(text);
                    }

                    if (editorInstance.config.wordcount.showParagraphs) {
                        paragraphs = countParagraphs(text);
                    }

                    if (editorInstance.config.wordcount.showWordCount) {
                        wordCount = countWords(text);
                    }
                }

                var html = format;
                if (config.showRemaining) {
                    if (editorInstance.config.wordcount.maxCharCount >= 0) {
                        html = html.replace("%charCount%", editorInstance.config.wordcount.maxCharCount - charCount);
                        html = html.replace("%maxCharCount%", editorInstance.config.wordcount.maxCharCount);
                        
                    } else {
                        html = html.replace("%charCount%", charCount);
                    }

                    if (editorInstance.config.wordcount.maxWordCount >= 0) {
                        html = html.replace("%wordCount%", editorInstance.config.wordcount.maxWordCount - wordCount);
                    } else {
                        html = html.replace("%wordCount%", wordCount);
                    }

                    if (editorInstance.config.wordcount.maxParagraphs >= 0) {
                        html = html.replace("%paragraphsCount%", editorInstance.config.wordcount.maxParagraphs - paragraphs);
                    } else {
                        html = html.replace("%paragraphsCount%", paragraphs);
                    }
                } else {
                    html = html.replace("%wordCount%", wordCount).replace("%charCount%", charCount).replace("%paragraphsCount%", paragraphs).replace("%maxCharCount%", editorInstance.config.wordcount.maxCharCount);
                }

                (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 == editorInstance.config.wordcount.maxCharCount || wordCount == editorInstance.config.wordcount.maxWordCount || paragraphs > editorInstance.config.wordcount.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 ((editorInstance.config.wordcount.maxWordCount > -1 && wordCount > editorInstance.config.wordcount.maxWordCount && deltaWord > 0) ||
                    (editorInstance.config.wordcount.maxCharCount > -1 && charCount > editorInstance.config.wordcount.maxCharCount && deltaChar > 0) ||
                    (editorInstance.config.wordcount.maxParagraphs > -1 && paragraphs > editorInstance.config.wordcount.maxParagraphs && deltaParagraphs > 0)) {

                    limitReached(editorInstance, limitReachedNotified);
                } else if ((editorInstance.config.wordcount.maxWordCount == -1 || wordCount <= editorInstance.config.wordcount.maxWordCount) &&
                    (editorInstance.config.wordcount.maxCharCount == -1 || charCount <= editorInstance.config.wordcount.maxCharCount) &&
                    (editorInstance.config.wordcount.maxParagraphs == -1 || paragraphs <= editorInstance.config.wordcount.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 > editorInstance.config.wordcount.maxCharCount && editorInstance.config.wordcount.maxCharCount > -1) {
                        config.charCountGreaterThanMaxLengthEvent(charCount, editorInstance.config.wordcount.maxCharCount);
                    } else {
                        config.charCountLessThanMaxLengthEvent(charCount, editorInstance.config.wordcount.maxCharCount);
                    }
                }

                if (config.wordCountGreaterThanMaxLengthEvent && config.wordCountLessThanMaxLengthEvent) {
                    if (wordCount > editorInstance.config.wordcount.maxWordCount && editorInstance.config.wordcount.maxWordCount > -1) {
                        config.wordCountGreaterThanMaxLengthEvent(wordCount, editorInstance.config.wordcount.maxWordCount);

                    } else {
                        config.wordCountLessThanMaxLengthEvent(wordCount, editorInstance.config.wordcount.maxWordCount);
                    }
                }

                return true;
            }

            function isCloseToLimits(editorInstance) {
                if (editorInstance.config.wordcount.maxWordCount > -1 && editorInstance.config.wordcount.maxWordCount - lastWordCount < 5) {
                    return true;
                }

                if (editorInstance.config.wordcount.maxCharCount > -1 && editorInstance.config.wordcount.maxCharCount - lastCharCount < 20) {
                    return true;
                }

                if (editorInstance.config.wordcount.maxParagraphs > -1 && editorInstance.config.wordcount.maxParagraphs - lastParagraphs < 1) {
                    return true;
                }

                return false;
            }

            editor.on("key",
                function (event) {
                    updateCounter(event.editor);
                    /*var ms = isCloseToLimits() ? 5 : 250;

                    if (editor.mode === "source") {
                        clearTimeout(timeoutId);
                        timeoutId = setTimeout(
                            updateCounter.bind(this, event.editor),
                            ms
                        );
                    }

                    if (editor.mode === "wysiwyg") {
                        clearTimeout(timeoutId);
                        timeoutId = setTimeout(
                            updateCounter.bind(this, event.editor),
                            ms
                        );
                    }

                    if (event.data.keyCode == 13) {
                        clearTimeout(timeoutId);
                        timeoutId = setTimeout(
                            updateCounter.bind(this, event.editor),
                            ms
                        );
                    }
                    */
                },
                editor);

            editor.on("change",
                function(event) {
                    var ms = isCloseToLimits(event.editor) ? 5 : 150;
                    clearTimeout(timeoutId);
                    timeoutId = setTimeout(
                        updateCounter.bind(this, event.editor),
                        ms
                    );
                },
                editor);

            editor.on("uiSpace",
                function (event) {
                    var wordcountClass = "cke_wordcount";

                    if (editor.lang.dir == "rtl") {
                        wordcountClass = wordcountClass + " cke_wordcount_rtl";
                    }

                    if (editor.elementMode === CKEDITOR.ELEMENT_MODE_INLINE) {
                        if (event.data.space == "top") {
                            event.data.html += "<div class=\"" + wordcountClass +"\" style=\"\"" +
                                " title=\"" +
                                editor.lang.wordcount.title +
                                "\"" +
                                "><span id=\"" +
                                counterId(event.editor) +
                                "\" class=\"cke_path_item\">&nbsp;</span></div>";
                        }
                    } else {
                        if (event.data.space == "bottom") {
                            event.data.html += "<div class=\""+wordcountClass+"\" style=\"\"" +
                                " title=\"" +
                                editor.lang.wordcount.title +
                                "\"" +
                                "><span id=\"" +
                                counterId(event.editor) +
                                "\" class=\"cke_path_item\">&nbsp;</span></div>";
                        }
                    }

                },
                editor,
                null,
                100);

            editor.on("dataReady",
                function(event) {
                    updateCounter(event.editor);
                },
                editor,
                null,
                100);

            editor.on("paste",
                function(event) {
                    if (!config.warnOnLimitOnly && (event.editor.config.wordcount.maxWordCount > 0 || event.editor.config.wordcount.maxCharCount > 0 || event.editor.config.wordcount.maxParagraphs > 0)) {

                        // Check if pasted content is above the limits
                        var wordCount = -1,
                            charCount = -1,
                            paragraphs = -1;

                        var mySelection = event.editor.getSelection(),
                            selectedText = mySelection.getNative().toString().trim();


                        // 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);

                        if (selectedText.length > 0) {
                            var plaintext = event.editor.document.getBody().getText();

                            if (plaintext.length === selectedText.length) {
                                text = "";
                            }
                        }


                        text += event.data.dataValue;

                        if (event.editor.config.wordcount.showCharCount) {
                            charCount = countCharacters(text);
                        }

                        if (event.editor.config.wordcount.showWordCount) {
                            wordCount = countWords(text);
                        }

                        if (event.editor.config.wordcount.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 (event.editor.config.wordcount.maxCharCount > 0 && charCount > event.editor.config.wordcount.maxCharCount && config.hardLimit) {
                            if (!notification.isVisible()) {
                                notification.show();
                            }
                            event.cancel();
                        }

                        if (event.editor.config.wordcount.maxWordCount > 0 && wordCount > event.editor.config.wordcount.maxWordCount && config.hardLimit) {
                            if (!notification.isVisible()) {
                                notification.show();
                            }
                            event.cancel();
                        }

                        if (event.editor.config.wordcount.maxParagraphs > 0 && paragraphs > event.editor.config.wordcount.maxParagraphs && config.hardLimit) {
                            if (!notification.isVisible()) {
                                notification.show();
                            }
                            event.cancel();
                        }
                    }
                },
                editor,
                null,
                100);

            editor.on("afterPaste",
                function(event) {
                    updateCounter(event.editor);
                },
                editor,
                null,
                100);

            editor.on("afterPasteFromWord",
                function (event) {
                    updateCounter(event.editor);
                },
                editor,
                null,
                100);
        }
    });

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Vladimir
Solution 2
Solution 3 Ouroborus
Solution 4 Ouroborus