/** * Used for WYSIWYG logic */ window.CMB2 = window.CMB2 || {}; window.CMB2.wysiwyg = window.CMB2.wysiwyg || {}; (function (window, document, $, cmb, wysiwyg, undefined) { "use strict"; // Private variables var toBeDestroyed = []; var toBeInitialized = []; var all = (wysiwyg.all = {}); // Private functions /** * Initializes any editors that weren't initialized because they didn't exist yet. * * @since 2.2.3 * * @return {void} */ function delayedInit() { // Don't initialize until they've all been destroyed. if (0 === toBeDestroyed.length) { toBeInitialized.forEach(function (toInit) { toBeInitialized.splice(toBeInitialized.indexOf(toInit), 1); wysiwyg.init.apply(wysiwyg, toInit); }); } else { window.setTimeout(delayedInit, 100); } } /** * Destroys any editors that weren't destroyed because they didn't exist yet. * * @since 2.2.3 * * @return {void} */ function delayedDestroy() { toBeDestroyed.forEach(function (id) { toBeDestroyed.splice(toBeDestroyed.indexOf(id), 1); wysiwyg.destroy(id); }); } /** * Gets the option data for a group (and initializes that data if it doesn't exist). * * @since 2.2.3 * * @param {object} data The group/field data. * * @return {object} Options data object for a group. */ function getGroupData(data) { var groupid = data.groupid; var fieldid = data.fieldid; if (!all[groupid] || !all[groupid][fieldid]) { all[groupid] = all[groupid] || {}; all[groupid][fieldid] = { template: wp.template("cmb2-wysiwyg-" + groupid + "-" + fieldid), defaults: { // Get the data from the template-wysiwyg initiation. mce: $.extend( {}, tinyMCEPreInit.mceInit["cmb2_i_" + groupid + fieldid], ), qt: $.extend( {}, tinyMCEPreInit.qtInit["cmb2_i_" + groupid + fieldid], ), }, }; // This is the template-wysiwyg data, and we do not want that to be initiated. delete tinyMCEPreInit.mceInit["cmb2_i_" + groupid + fieldid]; delete tinyMCEPreInit.qtInit["cmb2_i_" + groupid + fieldid]; } return all[groupid][fieldid]; } /** * Initiates the tinyMCEPreInit options for a wysiwyg editor instance. * * @since 2.2.3 * * @param {object} options Options data object for the wysiwyg editor instance. * * @return {void} */ function initOptions(options) { var nameRegex = new RegExp( "cmb2_n_" + options.groupid + options.fieldid, "g", ); var idRegex = new RegExp( "cmb2_i_" + options.groupid + options.fieldid, "g", ); var prop, newSettings, newQTS; // If no settings for this field. Clone from placeholder. if ("undefined" === typeof tinyMCEPreInit.mceInit[options.id]) { newSettings = $.extend({}, options.defaults.mce); for (prop in newSettings) { if ("string" === typeof newSettings[prop]) { newSettings[prop] = newSettings[prop] .replace(idRegex, options.id) .replace(nameRegex, options.name); } } tinyMCEPreInit.mceInit[options.id] = newSettings; } // If no Quicktag settings for this field. Clone from placeholder. if ("undefined" === typeof tinyMCEPreInit.qtInit[options.id]) { newQTS = $.extend({}, options.defaults.qt); for (prop in newQTS) { if ("string" === typeof newQTS[prop]) { newQTS[prop] = newQTS[prop] .replace(idRegex, options.id) .replace(nameRegex, options.name); } } tinyMCEPreInit.qtInit[options.id] = newQTS; } } /** * Initializes all group wysiwyg editors. Hooked to cmb_init. * * @since 2.2.3 * * @return {void} */ wysiwyg.initAll = function () { var $this, data, initiated; $(".cmb2-wysiwyg-placeholder").each(function () { $this = $(this); data = $this.data(); if (data.groupid) { data.id = $this.attr("id"); data.name = $this.attr("name"); data.value = $this.val(); wysiwyg.init($this, data, false); initiated = true; } }); if (true === initiated) { if ("undefined" !== typeof window.QTags) { window.QTags._buttonsInit(); } // Hook in our event callbacks. $(document) .on("cmb2_add_row", wysiwyg.addRow) .on("cmb2_remove_group_row_start", wysiwyg.destroyRowEditors) .on("cmb2_shift_rows_start", wysiwyg.shiftStart) .on("cmb2_shift_rows_complete", wysiwyg.shiftComplete); } }; /** * Initiates wysiwyg editors in a new group row. Hooked to cmb2_add_row. * * @since 2.2.3 * * @param {object} evt A jQuery-normalized event object. * @param {object} $row A jQuery dom element object for the group row. * * @return {void} */ wysiwyg.addRow = function (evt, $row) { wysiwyg.initRow($row, evt); }; /** * Destroys wysiwyg editors in a group row when that row is removed. Hooked to cmb2_remove_group_row_start. * * @since 2.2.3 * * @param {object} evt A jQuery-normalized event object. * @param {object} $btn A jQuery dom element object for the remove-row button. * * @return {void} */ wysiwyg.destroyRowEditors = function (evt, $btn) { wysiwyg.destroy( $btn .parents(".cmb-repeatable-grouping") .find(".wp-editor-area") .attr("id"), ); }; /** * When a row-shift starts, we need to destroy the wysiwyg editors for the group-rows being shuffled. * * @since 2.2.3 * * @param {object} evt A jQuery-normalized event object. * @param {object} $btn A jQuery dom element object for the remove-row button. * @param {object} $from A jQuery dom element object for the row being shifted from. * @param {object} $to A jQuery dom element object for the row being shifted to. * * @return {void} */ wysiwyg.shiftStart = function (evt, $btn, $from, $to) { $from .add($to) .find(".wp-editor-wrap textarea") .each(function () { wysiwyg.destroy($(this).attr("id")); }); }; /** * When a row-shift completes, we need to re-init the wysiwyg editors for the group-rows being shuffled. * * @since 2.2.3 * * @param {object} evt A jQuery-normalized event object. * @param {object} $btn A jQuery dom element object for the remove-row button. * @param {object} $from A jQuery dom element object for the row being shifted from. * @param {object} $to A jQuery dom element object for the row being shifted to. * * @return {void} */ wysiwyg.shiftComplete = function (evt, $btn, $from, $to) { $from.add($to).each(function () { wysiwyg.initRow($(this), evt); }); }; /** * Initializes editors for a new CMB row. * * @since 2.2.3 * * @param {object} $row A jQuery dom element object for the group row. * @param {object} evt A jQuery-normalized event object. * * @return {void} */ wysiwyg.initRow = function ($row, evt) { var $toReplace, data, defVal; $row.find(".cmb2-wysiwyg-inner-wrap").each(function () { $toReplace = $(this); data = $toReplace.data(); defVal = cmb.getFieldArg(data.hash, "default", ""); defVal = "undefined" !== typeof defVal && false !== defVal ? defVal : ""; data.iterator = $row.data("iterator"); data.fieldid = data.id; data.id = data.groupid + "_" + data.iterator + "_" + data.fieldid; data.name = data.groupid + "[" + data.iterator + "][" + data.fieldid + "]"; data.value = "cmb2_add_row" !== evt.type && $toReplace.find(".wp-editor-area").length ? $toReplace.find(".wp-editor-area").val() : defVal; // The destroys might not have happened yet. Don't init until they have. if (0 === toBeDestroyed.length) { wysiwyg.init($toReplace, data); } else { toBeInitialized.push([$toReplace, data]); window.setTimeout(delayedInit, 100); } }); }; /** * Initiates a wysiwyg editor instance and replaces the passed dom element w/ the editor html. * * @since 2.2.3 * * @param {object} $toReplace A jQuery dom element which will be replaced with the wysiwyg editor. * @param {object} data Data used to initate the editor. * @param {bool} buttonsInit Whether to run QTags._buttonsInit() * * @return {void} */ wysiwyg.init = function ($toReplace, data, buttonsInit) { if (!data.groupid) { return false; } var mceActive = cmb.canTinyMCE(); var qtActive = "function" === typeof window.quicktags; $.extend(data, getGroupData(data)); initOptions(data); $toReplace.replaceWith(data.template(data)); if (mceActive) { window.tinyMCE.init(tinyMCEPreInit.mceInit[data.id]); } if (qtActive) { window.quicktags(tinyMCEPreInit.qtInit[data.id]); } if (mceActive) { $(document.getElementById(data.id)) .parents(".wp-editor-wrap") .removeClass("html-active") .addClass("tmce-active"); } if (false !== buttonsInit && "undefined" !== typeof window.QTags) { window.QTags._buttonsInit(); } }; /** * Destroys a wysiwyg editor instance. * * @since 2.2.3 * * @param {string} id Editor id. * * @return {void} */ wysiwyg.destroy = function (id) { if (!cmb.canTinyMCE()) { // Nothing to see here. return; } // The editor might not be initialized yet. But we need to destroy it once it is. var editor = tinyMCE.get(id); if (editor !== null && typeof editor !== "undefined") { editor.destroy(); if ("undefined" === typeof tinyMCEPreInit.mceInit[id]) { delete tinyMCEPreInit.mceInit[id]; } if ("undefined" === typeof tinyMCEPreInit.qtInit[id]) { delete tinyMCEPreInit.qtInit[id]; } } else if (-1 === toBeDestroyed.indexOf(id)) { toBeDestroyed.push(id); window.setTimeout(delayedDestroy, 100); } }; // Hook in our event callbacks. $(document).on("cmb_init", wysiwyg.initAll); })(window, document, jQuery, window.CMB2, window.CMB2.wysiwyg);