/** * Used for character counters */ window.CMB2 = window.CMB2 || {}; window.CMB2.charcounter = window.CMB2.charcounter || {}; (function (window, document, $, cmb, counter) { "use strict"; if (!wp.utils || !wp.utils.WordCounter) { return cmb.log("Cannot find wp.utils!"); } // Private variables counter.counters = {}; var counters = counter.counters; var wpCounter = new wp.utils.WordCounter(); /** * Update a field's character counter * * @since 2.7.0 * * @param {string} field_id * * @return {int} */ counter.updateCounter = function (field_id) { // No counter? if (!counters.hasOwnProperty(field_id)) { return null; } var instance = counters[field_id]; var wysiwyg = instance.editor && !instance.editor.isHidden(); // Are we dealing with WYSIWYG visual editor, or textarea / WYSIWYG textarea? var text = wysiwyg ? instance.editor.getContent({ format: "raw" }) : cmb.$id(field_id).val().trim(); var count = wpCounter.count(text, instance.type); var exceeded = instance.max && count > instance.max; // Number remaining when max is defined var val = instance.max ? instance.max - count : count; // Over maximum? instance.$el .parents(".cmb2-char-counter-wrap") [exceeded ? "addClass" : "removeClass"]("cmb2-max-exceeded"); // Update counter, and update counter input width. instance.$el.val(val).outerWidth(8 * String(val).length + 15 + "px"); return count; }; counter.instantiate = function ($el) { var data = $el.data(); // Add counter details if not already done if (!(data.fieldId in counters)) { var instance = { $el: $el, max: data.max, type: "words" === data.counterType ? "words" : "characters_including_spaces", editor: false, }; counters[data.fieldId] = instance; // Initialise counter counter.updateCounter(data.fieldId); } }; /** * Initializes all character counters. Hooked to cmb_init. * * @since 2.7.0 * * @param {bool} init First init? * * @return {void} */ counter.initAll = function () { // Gather counters and initialise $(".cmb2-char-counter").each(function () { counter.instantiate($(this)); }); }; /** * Initializes WYSIWYG editors. Hooked to tinymce-editor-init * * @since 2.7.0 * * @param {object} evt * @param {object} editor * * @return {void} */ counter.initWysiwyg = function (evt, editor) { // Check if it's one of our WYSIWYGs // Should have already been registered in counters via hidden textarea if (editor.id in counters) { // Add editor to counter counters[editor.id].editor = editor; // Add nodechange event editor.on("nodechange keyup", counter.countWysiwyg); } }; /** * Initializes after a new repeatable row has been added. Hooked to cmb2_add_row * * @since 2.7.0 * * @param {object} evt A jQuery-normalized event object. * @param {object} $row A jQuery dom element object for the group row. * * @return {void} */ counter.addRow = function (evt, $row) { // Character counters in row? $row.find(".cmb2-char-counter").each(function () { // Update attributes var $this = $(this); var id = $this.attr("id"); var field_id = id.replace(/^char-counter-/, ""); $this.attr("data-field-id", field_id).data("field-id", field_id); counter.instantiate($this); }); }; /** * Clean the counters array. * Removes counters after a repeatable row has been removed. Hooked to cmb2_remove_row. * * @since 2.7.0 * * @return {void} */ counter.cleanCounters = function () { var field_id, remove = []; // Got through counters for (field_id in counters) { // Check for element, gather for removal if (!document.getElementById(field_id)) { remove.push(field_id); } } // Anything to remove? if (remove.length) { _.each(remove, function (field_id) { delete counters[field_id]; }); } }; /** * Counts the value of wysiwyg on the keyup event. * * @since 2.7.0 * * @param {object} evt * * @return {void} */ counter.countWysiwyg = _.throttle(function (evt) { // Init event if (evt.hasOwnProperty("element")) { return counter.updateCounter($(evt.element).data("id")); } // Nodechange event if (evt.hasOwnProperty("currentTarget")) { return counter.updateCounter($(evt.currentTarget).data("id")); } }); /** * Counts the value of textarea on the keyup event. * * @since 2.7.0 * * @param {object} evt * * @return {void} */ counter.countTextarea = _.throttle(function (evt) { counter.updateCounter(evt.currentTarget.id); }, 400); // Hook in our event callbacks. $(document) .on("cmb_init", counter.initAll) .on("tinymce-editor-init", counter.initWysiwyg) .on("cmb2_add_row", counter.addRow) .on("cmb2_remove_row", counter.cleanCounters) .on("input keyup", ".cmb2-count-chars", counter.countTextarea); })(window, document, jQuery, window.CMB2, window.CMB2.charcounter);