1507 lines
43 KiB
JavaScript
1507 lines
43 KiB
JavaScript
/**
|
|
* Controls the behaviours of custom metabox fields.
|
|
*
|
|
* @author CMB2 team
|
|
* @see https://github.com/CMB2/CMB2
|
|
*/
|
|
|
|
/**
|
|
* Custom jQuery for Custom Metaboxes and Fields
|
|
*/
|
|
window.CMB2 = window.CMB2 || {};
|
|
(function (window, document, $, cmb, undefined) {
|
|
"use strict";
|
|
|
|
// localization strings
|
|
var l10n = window.cmb2_l10;
|
|
var setTimeout = window.setTimeout;
|
|
var $document;
|
|
var $id = function (selector) {
|
|
return $(document.getElementById(selector));
|
|
};
|
|
cmb.$id = $id;
|
|
var defaults = {
|
|
idNumber: false,
|
|
repeatEls:
|
|
'input:not([type="button"],[id^=filelist]),select,textarea,.cmb2-media-status',
|
|
noEmpty:
|
|
'input:not([type="button"]):not([type="radio"]):not([type="checkbox"]),textarea',
|
|
repeatUpdate: 'input:not([type="button"]),select,textarea,label',
|
|
styleBreakPoint: 450,
|
|
mediaHandlers: {},
|
|
defaults: {
|
|
time_picker: l10n.defaults.time_picker,
|
|
date_picker: l10n.defaults.date_picker,
|
|
color_picker: l10n.defaults.color_picker || {},
|
|
code_editor: l10n.defaults.code_editor,
|
|
},
|
|
media: {
|
|
frames: {},
|
|
},
|
|
};
|
|
|
|
cmb.init = function () {
|
|
$document = $(document);
|
|
|
|
// Setup the CMB2 object defaults.
|
|
$.extend(cmb, defaults);
|
|
|
|
cmb.trigger("cmb_pre_init");
|
|
|
|
var $metabox = cmb.metabox();
|
|
var $repeatGroup = $metabox.find(".cmb-repeatable-group");
|
|
|
|
// Init time/date/color pickers
|
|
cmb.initPickers(
|
|
$metabox.find('input[type="text"].cmb2-timepicker'),
|
|
$metabox.find('input[type="text"].cmb2-datepicker'),
|
|
$metabox.find('input[type="text"].cmb2-colorpicker'),
|
|
);
|
|
|
|
// Init code editors.
|
|
cmb.initCodeEditors(
|
|
$metabox.find(".cmb2-textarea-code:not(.disable-codemirror)"),
|
|
);
|
|
|
|
// Insert toggle button into DOM wherever there is multicheck. credit: Genesis Framework
|
|
$(
|
|
'<p><span class="button-secondary cmb-multicheck-toggle">' +
|
|
l10n.strings.check_toggle +
|
|
"</span></p>",
|
|
).insertBefore(".cmb2-checkbox-list:not(.no-select-all)");
|
|
|
|
// Make File List drag/drop sortable:
|
|
cmb.makeListSortable();
|
|
// Make Repeatable fields drag/drop sortable:
|
|
cmb.makeRepeatableSortable();
|
|
|
|
$metabox
|
|
.on("change", ".cmb2_upload_file", function () {
|
|
cmb.media.field = $(this).attr("id");
|
|
$id(cmb.media.field + "_id").val("");
|
|
})
|
|
// Media/file management
|
|
.on("click", ".cmb-multicheck-toggle", cmb.toggleCheckBoxes)
|
|
.on("click", ".cmb2-upload-button", cmb.handleMedia)
|
|
.on(
|
|
"click",
|
|
".cmb-attach-list li, .cmb2-media-status .img-status img, .cmb2-media-status .file-status > span",
|
|
cmb.handleFileClick,
|
|
)
|
|
.on("click", ".cmb2-remove-file-button", cmb.handleRemoveMedia)
|
|
// Repeatable content
|
|
.on("click", ".cmb-add-group-row", cmb.addGroupRow)
|
|
.on("click", ".cmb-add-row-button", cmb.addAjaxRow)
|
|
.on("click", ".cmb-remove-group-row", cmb.removeGroupRow)
|
|
.on("click", ".cmb-remove-row-button", cmb.removeAjaxRow)
|
|
// Ajax oEmbed display
|
|
.on("keyup paste focusout", ".cmb2-oembed", cmb.maybeOembed)
|
|
// Reset titles when removing a row
|
|
.on(
|
|
"cmb2_remove_row",
|
|
".cmb-repeatable-group",
|
|
cmb.resetTitlesAndIterator,
|
|
)
|
|
.on(
|
|
"click",
|
|
".cmbhandle, .cmbhandle + .cmbhandle-title",
|
|
cmb.toggleHandle,
|
|
);
|
|
|
|
if ($repeatGroup.length) {
|
|
$repeatGroup
|
|
.on("cmb2_add_row", cmb.emptyValue)
|
|
.on("cmb2_add_row", cmb.setDefaults)
|
|
.filter(".sortable")
|
|
.each(function () {
|
|
// Add sorting arrows
|
|
$(this)
|
|
.find(".cmb-remove-group-row-button")
|
|
.before(
|
|
'<a class="button-secondary cmb-shift-rows move-up alignleft" href="#"><span class="' +
|
|
l10n.up_arrow_class +
|
|
'"></span></a> <a class="button-secondary cmb-shift-rows move-down alignleft" href="#"><span class="' +
|
|
l10n.down_arrow_class +
|
|
'"></span></a>',
|
|
);
|
|
})
|
|
.on("click", ".cmb-shift-rows", cmb.shiftRows);
|
|
}
|
|
|
|
// on pageload
|
|
setTimeout(cmb.resizeoEmbeds, 500);
|
|
// and on window resize
|
|
$(window).on("resize", cmb.resizeoEmbeds);
|
|
|
|
if ($id("addtag").length) {
|
|
cmb.listenTagAdd();
|
|
}
|
|
|
|
$(document).on("cmb_init", cmb.mceEnsureSave);
|
|
|
|
cmb.trigger("cmb_init");
|
|
};
|
|
|
|
// Handles updating tiny mce instances when saving a gutenberg post.
|
|
// https://github.com/CMB2/CMB2/issues/1156
|
|
cmb.mceEnsureSave = function () {
|
|
// If no wp.data, do not proceed (no gutenberg)
|
|
if (!wp.data || !wp.data.hasOwnProperty("subscribe")) {
|
|
return;
|
|
}
|
|
|
|
// If the current user cannot richedit, or MCE is not available, bail.
|
|
if (!cmb.canTinyMCE()) {
|
|
return;
|
|
}
|
|
|
|
wp.data.subscribe(function () {
|
|
var editor = wp.data.hasOwnProperty("select")
|
|
? wp.data.select("core/editor")
|
|
: null;
|
|
|
|
// the post is currently being saved && we have tinymce editors
|
|
if (
|
|
editor &&
|
|
editor.isSavingPost &&
|
|
editor.isSavingPost() &&
|
|
window.tinyMCE.editors.length
|
|
) {
|
|
for (var i = 0; i < window.tinyMCE.editors.length; i++) {
|
|
if (window.tinyMCE.activeEditor !== window.tinyMCE.editors[i]) {
|
|
window.tinyMCE.editors[i].save();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
cmb.canTinyMCE = function () {
|
|
return l10n.user_can_richedit && window.tinyMCE;
|
|
};
|
|
|
|
cmb.listenTagAdd = function () {
|
|
$document.ajaxSuccess(function (evt, xhr, settings) {
|
|
if (
|
|
settings.data &&
|
|
settings.data.length &&
|
|
-1 !== settings.data.indexOf("action=add-tag")
|
|
) {
|
|
cmb.resetBoxes($id("addtag").find(".cmb2-wrap > .cmb2-metabox"));
|
|
}
|
|
});
|
|
};
|
|
|
|
cmb.resetBoxes = function ($boxes) {
|
|
$.each($boxes, function () {
|
|
cmb.resetBox($(this));
|
|
});
|
|
};
|
|
|
|
cmb.resetBox = function ($box) {
|
|
$box.find(".wp-picker-clear").trigger("click");
|
|
$box.find(".cmb2-remove-file-button").trigger("click");
|
|
$box
|
|
.find(
|
|
".cmb-row.cmb-repeatable-grouping:not(:first-of-type) .cmb-remove-group-row",
|
|
)
|
|
.click();
|
|
$box.find(".cmb-repeat-row:not(:first-child)").remove();
|
|
|
|
$box.find('input:not([type="button"]),select,textarea').each(function () {
|
|
var $element = $(this);
|
|
var tagName = $element.prop("tagName");
|
|
|
|
if ("INPUT" === tagName) {
|
|
var elType = $element.attr("type");
|
|
if ("checkbox" === elType || "radio" === elType) {
|
|
$element.prop("checked", false);
|
|
} else {
|
|
$element.val("");
|
|
}
|
|
}
|
|
if ("SELECT" === tagName) {
|
|
$("option:selected", this).prop("selected", false);
|
|
}
|
|
if ("TEXTAREA" === tagName) {
|
|
$element.html("");
|
|
}
|
|
});
|
|
};
|
|
|
|
cmb.resetTitlesAndIterator = function (evt) {
|
|
if (!evt.group) {
|
|
return;
|
|
}
|
|
|
|
// Loop repeatable group tables
|
|
$(".cmb-repeatable-group.repeatable").each(function () {
|
|
var $table = $(this);
|
|
var groupTitle = $table.find(".cmb-add-group-row").data("grouptitle");
|
|
|
|
// Loop repeatable group table rows
|
|
$table.find(".cmb-repeatable-grouping").each(function (rowindex) {
|
|
var $row = $(this);
|
|
var $rowTitle = $row.find("h3.cmb-group-title");
|
|
// Reset rows iterator
|
|
$row.data("iterator", rowindex);
|
|
// Reset rows title
|
|
if ($rowTitle.length) {
|
|
$rowTitle.text(groupTitle.replace("{#}", rowindex + 1));
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
cmb.toggleHandle = function (evt) {
|
|
evt.preventDefault();
|
|
cmb.trigger(
|
|
"postbox-toggled",
|
|
$(this).parent(".postbox").toggleClass("closed"),
|
|
);
|
|
};
|
|
|
|
cmb.toggleCheckBoxes = function (evt) {
|
|
evt.preventDefault();
|
|
var $this = $(this);
|
|
var $multicheck = $this
|
|
.closest(".cmb-td")
|
|
.find("input[type=checkbox]:not([disabled])");
|
|
|
|
// If the button has already been clicked once...
|
|
if ($this.data("checked")) {
|
|
// clear the checkboxes and remove the flag
|
|
$multicheck.prop("checked", false);
|
|
$this.data("checked", false);
|
|
}
|
|
// Otherwise mark the checkboxes and add a flag
|
|
else {
|
|
$multicheck.prop("checked", true);
|
|
$this.data("checked", true);
|
|
}
|
|
};
|
|
|
|
cmb.handleMedia = function (evt) {
|
|
evt.preventDefault();
|
|
|
|
var $el = $(this);
|
|
cmb.attach_id = !$el.hasClass("cmb2-upload-list")
|
|
? $el.closest(".cmb-td").find(".cmb2-upload-file-id").val()
|
|
: false;
|
|
// Clean up default 0 value
|
|
cmb.attach_id = "0" !== cmb.attach_id ? cmb.attach_id : false;
|
|
|
|
cmb._handleMedia(
|
|
$el.prev("input.cmb2-upload-file").attr("id"),
|
|
$el.hasClass("cmb2-upload-list"),
|
|
);
|
|
};
|
|
|
|
cmb.handleFileClick = function (evt) {
|
|
if ($(evt.target).is("a")) {
|
|
return;
|
|
}
|
|
|
|
evt.preventDefault();
|
|
|
|
var $el = $(this);
|
|
var $td = $el.closest(".cmb-td");
|
|
var isList = $td.find(".cmb2-upload-button").hasClass("cmb2-upload-list");
|
|
cmb.attach_id = isList
|
|
? $el.find('input[type="hidden"]').data("id")
|
|
: $td.find(".cmb2-upload-file-id").val();
|
|
|
|
if (cmb.attach_id) {
|
|
cmb._handleMedia(
|
|
$td.find("input.cmb2-upload-file").attr("id"),
|
|
isList,
|
|
cmb.attach_id,
|
|
);
|
|
}
|
|
};
|
|
|
|
cmb._handleMedia = function (id, isList) {
|
|
if (!wp) {
|
|
return;
|
|
}
|
|
|
|
var media, handlers;
|
|
|
|
handlers = cmb.mediaHandlers;
|
|
media = cmb.media;
|
|
media.field = id;
|
|
media.$field = $id(media.field);
|
|
media.fieldData = media.$field.data();
|
|
media.previewSize = media.fieldData.previewsize;
|
|
media.sizeName = media.fieldData.sizename;
|
|
media.fieldName = media.$field.attr("name");
|
|
media.isList = isList;
|
|
|
|
// If this field's media frame already exists, reopen it.
|
|
if (id in media.frames) {
|
|
return media.frames[id].open();
|
|
}
|
|
|
|
// Create the media frame.
|
|
media.frames[id] = wp.media({
|
|
title: cmb
|
|
.metabox()
|
|
.find('label[for="' + id + '"]')
|
|
.text(),
|
|
library: media.fieldData.queryargs || {},
|
|
button: {
|
|
text: l10n.strings[isList ? "upload_files" : "upload_file"],
|
|
},
|
|
multiple: isList ? "add" : false,
|
|
});
|
|
|
|
// Enable the additional media filters: https://github.com/CMB2/CMB2/issues/873
|
|
media.frames[id].states.first().set("filterable", "all");
|
|
|
|
cmb.trigger("cmb_media_modal_init", media);
|
|
|
|
handlers.list = function (selection, returnIt) {
|
|
// Setup our fileGroup array
|
|
var fileGroup = [];
|
|
var attachmentHtml;
|
|
|
|
if (!handlers.list.templates) {
|
|
handlers.list.templates = {
|
|
image: wp.template("cmb2-list-image"),
|
|
file: wp.template("cmb2-list-file"),
|
|
};
|
|
}
|
|
|
|
// Loop through each attachment
|
|
selection.each(function (attachment) {
|
|
// Image preview or standard generic output if it's not an image.
|
|
attachmentHtml = handlers.getAttachmentHtml(attachment, "list");
|
|
|
|
// Add our file to our fileGroup array
|
|
fileGroup.push(attachmentHtml);
|
|
});
|
|
|
|
if (!returnIt) {
|
|
// Append each item from our fileGroup array to .cmb2-media-status
|
|
media.$field.siblings(".cmb2-media-status").append(fileGroup);
|
|
} else {
|
|
return fileGroup;
|
|
}
|
|
};
|
|
|
|
handlers.single = function (selection) {
|
|
if (!handlers.single.templates) {
|
|
handlers.single.templates = {
|
|
image: wp.template("cmb2-single-image"),
|
|
file: wp.template("cmb2-single-file"),
|
|
};
|
|
}
|
|
|
|
// Only get one file from the uploader
|
|
var attachment = selection.first();
|
|
|
|
media.$field.val(attachment.get("url"));
|
|
$id(media.field + "_id").val(attachment.get("id"));
|
|
|
|
// Image preview or standard generic output if it's not an image.
|
|
var attachmentHtml = handlers.getAttachmentHtml(attachment, "single");
|
|
|
|
// add/display our output
|
|
media.$field
|
|
.siblings(".cmb2-media-status")
|
|
.slideDown()
|
|
.html(attachmentHtml);
|
|
};
|
|
|
|
handlers.getAttachmentHtml = function (attachment, templatesId) {
|
|
var isImage = "image" === attachment.get("type");
|
|
var data = handlers.prepareData(attachment, isImage);
|
|
|
|
// Image preview or standard generic output if it's not an image.
|
|
return handlers[templatesId].templates[isImage ? "image" : "file"](data);
|
|
};
|
|
|
|
handlers.prepareData = function (data, image) {
|
|
if (image) {
|
|
// Set the correct image size data
|
|
handlers.getImageData.call(data, 50);
|
|
}
|
|
|
|
data = data.toJSON();
|
|
data.mediaField = media.field;
|
|
data.mediaFieldName = media.fieldName;
|
|
data.stringRemoveImage = l10n.strings.remove_image;
|
|
data.stringFile = l10n.strings.file;
|
|
data.stringDownload = l10n.strings.download;
|
|
data.stringRemoveFile = l10n.strings.remove_file;
|
|
|
|
return data;
|
|
};
|
|
|
|
handlers.getImageData = function (fallbackSize) {
|
|
// Preview size dimensions
|
|
var previewW = media.previewSize[0] || fallbackSize;
|
|
var previewH = media.previewSize[1] || fallbackSize;
|
|
|
|
// Image dimensions and url
|
|
var url = this.get("url");
|
|
var width = this.get("width");
|
|
var height = this.get("height");
|
|
var sizes = this.get("sizes");
|
|
|
|
// Get the correct dimensions and url if a named size is set and exists
|
|
// fallback to the 'large' size
|
|
if (sizes) {
|
|
if (sizes[media.sizeName]) {
|
|
url = sizes[media.sizeName].url;
|
|
width = sizes[media.sizeName].width;
|
|
height = sizes[media.sizeName].height;
|
|
} else if (sizes.large) {
|
|
url = sizes.large.url;
|
|
width = sizes.large.width;
|
|
height = sizes.large.height;
|
|
}
|
|
}
|
|
|
|
// Fit the image in to the preview size, keeping the correct aspect ratio
|
|
if (width > previewW) {
|
|
height = Math.floor((previewW * height) / width);
|
|
width = previewW;
|
|
}
|
|
|
|
if (height > previewH) {
|
|
width = Math.floor((previewH * width) / height);
|
|
height = previewH;
|
|
}
|
|
|
|
if (!width) {
|
|
width = previewW;
|
|
}
|
|
|
|
if (!height) {
|
|
height =
|
|
"svg" === this.get("filename").split(".").pop() ? "100%" : previewH;
|
|
}
|
|
|
|
this.set("sizeUrl", url);
|
|
this.set("sizeWidth", width);
|
|
this.set("sizeHeight", height);
|
|
|
|
return this;
|
|
};
|
|
|
|
handlers.selectFile = function () {
|
|
var selection = media.frames[id].state().get("selection");
|
|
var type = isList ? "list" : "single";
|
|
|
|
if (cmb.attach_id && isList) {
|
|
$('[data-id="' + cmb.attach_id + '"]')
|
|
.parents("li")
|
|
.replaceWith(handlers.list(selection, true));
|
|
} else {
|
|
handlers[type](selection);
|
|
}
|
|
|
|
cmb.trigger("cmb_media_modal_select", selection, media);
|
|
};
|
|
|
|
handlers.openModal = function () {
|
|
var selection = media.frames[id].state().get("selection");
|
|
var attach;
|
|
|
|
if (!cmb.attach_id) {
|
|
selection.reset();
|
|
} else {
|
|
attach = wp.media.attachment(cmb.attach_id);
|
|
attach.fetch();
|
|
selection.set(attach ? [attach] : []);
|
|
}
|
|
|
|
cmb.trigger("cmb_media_modal_open", selection, media);
|
|
};
|
|
|
|
// When a file is selected, run a callback.
|
|
media.frames[id]
|
|
.on("select", handlers.selectFile)
|
|
.on("open", handlers.openModal);
|
|
|
|
// Finally, open the modal
|
|
media.frames[id].open();
|
|
};
|
|
|
|
cmb.handleRemoveMedia = function (evt) {
|
|
evt.preventDefault();
|
|
var $this = $(this);
|
|
if ($this.is(".cmb-attach-list .cmb2-remove-file-button")) {
|
|
$this.parents(".cmb2-media-item").remove();
|
|
return false;
|
|
}
|
|
|
|
cmb.media.field = $this.attr("rel");
|
|
|
|
cmb.metabox().find(document.getElementById(cmb.media.field)).val("");
|
|
cmb
|
|
.metabox()
|
|
.find(document.getElementById(cmb.media.field + "_id"))
|
|
.val("");
|
|
$this.parents(".cmb2-media-status").html("");
|
|
|
|
return false;
|
|
};
|
|
|
|
cmb.cleanRow = function ($row, prevNum, group) {
|
|
var $elements = $row.find(cmb.repeatUpdate);
|
|
if (group) {
|
|
var $other = $row.find("[id]").not(cmb.repeatUpdate);
|
|
|
|
// Remove extra ajaxed rows
|
|
$row.find(".cmb-repeat-table .cmb-repeat-row:not(:first-child)").remove();
|
|
|
|
// Update all elements w/ an ID
|
|
if ($other.length) {
|
|
$other.each(function () {
|
|
var $_this = $(this);
|
|
var oldID = $_this.attr("id");
|
|
var newID = oldID.replace("_" + prevNum, "_" + cmb.idNumber);
|
|
var $buttons = $row.find('[data-selector="' + oldID + '"]');
|
|
$_this.attr("id", newID);
|
|
|
|
// Replace data-selector vars
|
|
if ($buttons.length) {
|
|
$buttons.attr("data-selector", newID).data("selector", newID);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
$elements.filter(":checked").removeAttr("checked");
|
|
$elements.find(":checked").removeAttr("checked");
|
|
$elements.filter(":selected").removeAttr("selected");
|
|
$elements.find(":selected").removeAttr("selected", false);
|
|
|
|
if ($row.find("h3.cmb-group-title").length) {
|
|
$row
|
|
.find("h3.cmb-group-title")
|
|
.text($row.data("title").replace("{#}", cmb.idNumber + 1));
|
|
}
|
|
|
|
$elements.each(function () {
|
|
cmb.elReplacements($(this), prevNum, group);
|
|
});
|
|
|
|
return cmb;
|
|
};
|
|
|
|
cmb.elReplacements = function ($newInput, prevNum, group) {
|
|
var oldFor = $newInput.attr("for");
|
|
var oldVal = $newInput.val();
|
|
var type = $newInput.prop("type");
|
|
var defVal = cmb.getFieldArg($newInput, "default");
|
|
var newVal =
|
|
"undefined" !== typeof defVal && false !== defVal ? defVal : "";
|
|
var tagName = $newInput.prop("tagName");
|
|
var checkable = "radio" === type || "checkbox" === type ? oldVal : false;
|
|
var attrs = {};
|
|
var newID, oldID;
|
|
if (oldFor) {
|
|
attrs = { for: oldFor.replace("_" + prevNum, "_" + cmb.idNumber) };
|
|
} else {
|
|
var oldName = $newInput.attr("name");
|
|
var newName;
|
|
oldID = $newInput.attr("id");
|
|
|
|
// Handle adding groups vs rows.
|
|
if (group) {
|
|
// Expect another bracket after group's index closing bracket.
|
|
newName = oldName
|
|
? oldName.replace("[" + prevNum + "][", "[" + cmb.idNumber + "][")
|
|
: "";
|
|
// Expect another underscore after group's index trailing underscore.
|
|
newID = oldID
|
|
? oldID.replace("_" + prevNum + "_", "_" + cmb.idNumber + "_")
|
|
: "";
|
|
} else {
|
|
// Row indexes are at the very end of the string.
|
|
newName = oldName
|
|
? cmb.replaceLast(
|
|
oldName,
|
|
"[" + prevNum + "]",
|
|
"[" + cmb.idNumber + "]",
|
|
)
|
|
: "";
|
|
newID = oldID
|
|
? cmb.replaceLast(oldID, "_" + prevNum, "_" + cmb.idNumber)
|
|
: "";
|
|
}
|
|
|
|
attrs = {
|
|
id: newID,
|
|
name: newName,
|
|
};
|
|
}
|
|
|
|
// Clear out textarea values
|
|
if ("TEXTAREA" === tagName) {
|
|
$newInput.html(newVal);
|
|
}
|
|
|
|
if ("SELECT" === tagName && "undefined" !== typeof defVal) {
|
|
var $toSelect = $newInput.find('[value="' + defVal + '"]');
|
|
if ($toSelect.length) {
|
|
$toSelect.attr("selected", "selected").prop("selected", "selected");
|
|
}
|
|
}
|
|
|
|
if (checkable) {
|
|
$newInput.removeAttr("checked");
|
|
if ("undefined" !== typeof defVal && oldVal === defVal) {
|
|
$newInput.attr("checked", "checked").prop("checked", "checked");
|
|
}
|
|
}
|
|
|
|
if (!group && $newInput[0].hasAttribute("data-iterator")) {
|
|
attrs["data-iterator"] = cmb.idNumber;
|
|
}
|
|
|
|
$newInput
|
|
.removeClass("hasDatepicker")
|
|
.val(checkable ? checkable : newVal)
|
|
.attr(attrs);
|
|
|
|
return $newInput;
|
|
};
|
|
|
|
cmb.newRowHousekeeping = function ($row) {
|
|
var $colorPicker = $row.find(".wp-picker-container");
|
|
var $list = $row.find(".cmb2-media-status");
|
|
|
|
if ($colorPicker.length) {
|
|
// Need to clean-up colorpicker before appending
|
|
$colorPicker.each(function () {
|
|
var $td = $(this).parent();
|
|
$td.html(
|
|
$td.find('input[type="text"].cmb2-colorpicker').attr("style", ""),
|
|
);
|
|
});
|
|
}
|
|
|
|
// Need to clean-up colorpicker before appending
|
|
if ($list.length) {
|
|
$list.empty();
|
|
}
|
|
|
|
return cmb;
|
|
};
|
|
|
|
cmb.afterRowInsert = function ($row) {
|
|
// Init pickers from new row
|
|
cmb.initPickers(
|
|
$row.find('input[type="text"].cmb2-timepicker'),
|
|
$row.find('input[type="text"].cmb2-datepicker'),
|
|
$row.find('input[type="text"].cmb2-colorpicker'),
|
|
);
|
|
};
|
|
|
|
cmb.updateNameAttr = function () {
|
|
var $this = $(this);
|
|
var name = $this.attr("name"); // get current name
|
|
|
|
// If name is defined
|
|
if ("undefined" !== typeof name) {
|
|
var prevNum = parseInt(
|
|
$this.parents(".cmb-repeatable-grouping").data("iterator"),
|
|
10,
|
|
);
|
|
var newNum = prevNum - 1; // Subtract 1 to get new iterator number
|
|
|
|
// Update field name attributes so data is not orphaned when a row is removed and post is saved
|
|
var $newName = name.replace("[" + prevNum + "]", "[" + newNum + "]");
|
|
|
|
// New name with replaced iterator
|
|
$this.attr("name", $newName);
|
|
}
|
|
};
|
|
|
|
cmb.emptyValue = function (evt, row) {
|
|
$(cmb.noEmpty, row).val("");
|
|
};
|
|
|
|
cmb.setDefaults = function (evt, row) {
|
|
$(cmb.noEmpty, row).each(function () {
|
|
var $el = $(this);
|
|
var defVal = cmb.getFieldArg($el, "default");
|
|
if ("undefined" !== typeof defVal && false !== defVal) {
|
|
$el.val(defVal);
|
|
}
|
|
});
|
|
};
|
|
|
|
cmb.addGroupRow = function (evt) {
|
|
evt.preventDefault();
|
|
|
|
var $this = $(this);
|
|
|
|
// before anything significant happens
|
|
cmb.triggerElement($this, "cmb2_add_group_row_start", $this);
|
|
|
|
var $table = $id($this.data("selector"));
|
|
var $oldRow = $table.find(".cmb-repeatable-grouping").last();
|
|
var prevNum = parseInt($oldRow.data("iterator"), 10);
|
|
cmb.idNumber = parseInt(prevNum, 10) + 1;
|
|
var $row = $oldRow.clone();
|
|
var nodeName = $row.prop("nodeName") || "div";
|
|
var getRowId = function (id) {
|
|
id = id.split("-");
|
|
id.splice(id.length - 1, 1);
|
|
id.push(cmb.idNumber);
|
|
return id.join("-");
|
|
};
|
|
|
|
// Make sure the next number doesn't exist.
|
|
while (
|
|
$table.find(
|
|
'.cmb-repeatable-grouping[data-iterator="' + cmb.idNumber + '"]',
|
|
).length > 0
|
|
) {
|
|
cmb.idNumber++;
|
|
}
|
|
|
|
cmb
|
|
.newRowHousekeeping($row.data("title", $this.data("grouptitle")))
|
|
.cleanRow($row, prevNum, true);
|
|
$row.find(".cmb-add-row-button").prop("disabled", false);
|
|
|
|
var $newRow = $(
|
|
"<" +
|
|
nodeName +
|
|
' id="' +
|
|
getRowId($oldRow.attr("id")) +
|
|
'" class="postbox cmb-row cmb-repeatable-grouping" data-iterator="' +
|
|
cmb.idNumber +
|
|
'">' +
|
|
$row.html() +
|
|
"</" +
|
|
nodeName +
|
|
">",
|
|
);
|
|
$oldRow.after($newRow);
|
|
|
|
cmb.afterRowInsert($newRow);
|
|
|
|
cmb.triggerElement($table, { type: "cmb2_add_row", group: true }, $newRow);
|
|
};
|
|
|
|
cmb.addAjaxRow = function (evt) {
|
|
evt.preventDefault();
|
|
|
|
var $this = $(this);
|
|
var $table = $id($this.data("selector"));
|
|
var $row = $table.find(".empty-row");
|
|
var prevNum = parseInt($row.find("[data-iterator]").data("iterator"), 10);
|
|
cmb.idNumber = parseInt(prevNum, 10) + 1;
|
|
var $emptyrow = $row.clone();
|
|
|
|
cmb.newRowHousekeeping($emptyrow).cleanRow($emptyrow, prevNum);
|
|
|
|
$row.removeClass("empty-row hidden").addClass("cmb-repeat-row");
|
|
$row.after($emptyrow);
|
|
|
|
cmb.afterRowInsert($emptyrow);
|
|
|
|
cmb.triggerElement(
|
|
$table,
|
|
{ type: "cmb2_add_row", group: false },
|
|
$emptyrow,
|
|
$row,
|
|
);
|
|
};
|
|
|
|
cmb.removeGroupRow = function (evt) {
|
|
evt.preventDefault();
|
|
|
|
var $this = $(this);
|
|
var confirmation = $this.data("confirm");
|
|
|
|
// Process further only if deletion confirmation enabled and user agreed.
|
|
if (confirmation && !window.confirm(confirmation)) {
|
|
return;
|
|
}
|
|
|
|
var $table = $id($this.data("selector"));
|
|
var $parent = $this.parents(".cmb-repeatable-grouping");
|
|
var number = $table.find(".cmb-repeatable-grouping").length;
|
|
|
|
if (number < 2) {
|
|
return cmb.resetRow(
|
|
$parent.parents(".cmb-repeatable-group").find(".cmb-add-group-row"),
|
|
$this,
|
|
);
|
|
}
|
|
|
|
cmb.triggerElement($table, "cmb2_remove_group_row_start", $this);
|
|
|
|
// When a group is removed, loop through all next groups and update fields names.
|
|
$parent
|
|
.nextAll(".cmb-repeatable-grouping")
|
|
.find(cmb.repeatEls)
|
|
.each(cmb.updateNameAttr);
|
|
|
|
$parent.remove();
|
|
|
|
cmb.triggerElement($table, { type: "cmb2_remove_row", group: true });
|
|
};
|
|
|
|
cmb.removeAjaxRow = function (evt) {
|
|
evt.preventDefault();
|
|
|
|
var $this = $(this);
|
|
|
|
// Check if disabled
|
|
if ($this.hasClass("button-disabled")) {
|
|
return;
|
|
}
|
|
|
|
var $parent = $this.parents(".cmb-row");
|
|
var $table = $this.parents(".cmb-repeat-table");
|
|
var number = $table.find(".cmb-row").length;
|
|
|
|
if (number <= 2) {
|
|
return cmb.resetRow($parent.find(".cmb-add-row-button"), $this);
|
|
}
|
|
|
|
if ($parent.hasClass("empty-row")) {
|
|
$parent.prev().addClass("empty-row").removeClass("cmb-repeat-row");
|
|
}
|
|
|
|
$this.parents(".cmb-repeat-table .cmb-row").remove();
|
|
|
|
cmb.triggerElement($table, { type: "cmb2_remove_row", group: false });
|
|
};
|
|
|
|
cmb.resetRow = function ($addNewBtn, $removeBtn) {
|
|
// Click the "add new" button followed by the "remove this" button
|
|
// in order to reset the repeat row to empty values.
|
|
$addNewBtn.trigger("click");
|
|
$removeBtn.trigger("click");
|
|
};
|
|
|
|
cmb.shiftRows = function (evt) {
|
|
evt.preventDefault();
|
|
|
|
var $this = $(this);
|
|
var $from = $this.parents(".cmb-repeatable-grouping");
|
|
var $goto = $this.hasClass("move-up")
|
|
? $from.prev(".cmb-repeatable-grouping")
|
|
: $from.next(".cmb-repeatable-grouping");
|
|
|
|
// Before shift occurs.
|
|
cmb.triggerElement($this, "cmb2_shift_rows_enter", $this, $from, $goto);
|
|
|
|
if (!$goto.length) {
|
|
return;
|
|
}
|
|
|
|
// About to shift
|
|
cmb.triggerElement($this, "cmb2_shift_rows_start", $this, $from, $goto);
|
|
|
|
var inputVals = [];
|
|
// Loop this item's fields
|
|
$from.find(cmb.repeatEls).each(function () {
|
|
var $element = $(this);
|
|
var elType = $element.attr("type");
|
|
var val;
|
|
|
|
if ($element.hasClass("cmb2-media-status")) {
|
|
// special case for image previews
|
|
val = $element.html();
|
|
} else if ("checkbox" === elType || "radio" === elType) {
|
|
val = $element.is(":checked");
|
|
} else if ("select" === $element.prop("tagName")) {
|
|
val = $element.is(":selected");
|
|
} else {
|
|
val = $element.val();
|
|
}
|
|
|
|
// Get all the current values per element
|
|
inputVals.push({ val: val, $: $element });
|
|
});
|
|
// And swap them all
|
|
$goto.find(cmb.repeatEls).each(function (index) {
|
|
var $element = $(this);
|
|
var elType = $element.attr("type");
|
|
var val;
|
|
|
|
if ($element.hasClass("cmb2-media-status")) {
|
|
var toRowId = $element
|
|
.closest(".cmb-repeatable-grouping")
|
|
.attr("data-iterator");
|
|
var fromRowId = inputVals[index].$.closest(
|
|
".cmb-repeatable-grouping",
|
|
).attr("data-iterator");
|
|
|
|
// special case for image previews
|
|
val = $element.html();
|
|
$element.html(inputVals[index].val);
|
|
inputVals[index].$.html(val);
|
|
|
|
inputVals[index].$.find("input").each(function () {
|
|
var name = $(this).attr("name");
|
|
name = name.replace("[" + toRowId + "]", "[" + fromRowId + "]");
|
|
$(this).attr("name", name);
|
|
});
|
|
$element.find("input").each(function () {
|
|
var name = $(this).attr("name");
|
|
name = name.replace("[" + fromRowId + "]", "[" + toRowId + "]");
|
|
$(this).attr("name", name);
|
|
});
|
|
}
|
|
// handle checkbox swapping
|
|
else if ("checkbox" === elType) {
|
|
inputVals[index].$.prop("checked", $element.is(":checked"));
|
|
$element.prop("checked", inputVals[index].val);
|
|
}
|
|
// handle radio swapping
|
|
else if ("radio" === elType) {
|
|
if ($element.is(":checked")) {
|
|
inputVals[index].$.attr("data-checked", "true");
|
|
}
|
|
if (inputVals[index].$.is(":checked")) {
|
|
$element.attr("data-checked", "true");
|
|
}
|
|
}
|
|
// handle select swapping
|
|
else if ("select" === $element.prop("tagName")) {
|
|
inputVals[index].$.prop("selected", $element.is(":selected"));
|
|
$element.prop("selected", inputVals[index].val);
|
|
}
|
|
// handle normal input swapping
|
|
else {
|
|
inputVals[index].$.val($element.val());
|
|
$element.val(inputVals[index].val);
|
|
}
|
|
});
|
|
|
|
$from
|
|
.find("input[data-checked=true]")
|
|
.prop("checked", true)
|
|
.removeAttr("data-checked");
|
|
$goto
|
|
.find("input[data-checked=true]")
|
|
.prop("checked", true)
|
|
.removeAttr("data-checked");
|
|
|
|
// trigger color picker change event
|
|
$from.find('input[type="text"].cmb2-colorpicker').trigger("change");
|
|
$goto.find('input[type="text"].cmb2-colorpicker').trigger("change");
|
|
|
|
// shift done
|
|
cmb.triggerElement($this, "cmb2_shift_rows_complete", $this, $from, $goto);
|
|
};
|
|
|
|
cmb.initPickers = function ($timePickers, $datePickers, $colorPickers) {
|
|
cmb.trigger("cmb_init_pickers", {
|
|
time: $timePickers,
|
|
date: $datePickers,
|
|
color: $colorPickers,
|
|
});
|
|
|
|
// Initialize jQuery UI timepickers
|
|
cmb.initDateTimePickers($timePickers, "timepicker", "time_picker");
|
|
// Initialize jQuery UI datepickers
|
|
cmb.initDateTimePickers($datePickers, "datepicker", "date_picker");
|
|
// Initialize color picker
|
|
cmb.initColorPickers($colorPickers);
|
|
};
|
|
|
|
cmb.initDateTimePickers = function ($selector, method, defaultKey) {
|
|
if ($selector.length) {
|
|
$selector[method]("destroy").each(function () {
|
|
var $this = $(this);
|
|
var fieldOpts = $this.data(method) || {};
|
|
var options = $.extend({}, cmb.defaults[defaultKey], fieldOpts);
|
|
$this[method](cmb.datePickerSetupOpts(fieldOpts, options, method));
|
|
});
|
|
}
|
|
};
|
|
|
|
cmb.datePickerSetupOpts = function (fieldOpts, options, method) {
|
|
var existing = $.extend({}, options);
|
|
|
|
options.beforeShow = function (input, inst) {
|
|
if ("timepicker" === method) {
|
|
cmb.addTimePickerClasses(inst.dpDiv);
|
|
}
|
|
|
|
// Wrap datepicker w/ class to narrow the scope of jQuery UI CSS and prevent conflicts
|
|
$id("ui-datepicker-div").addClass("cmb2-element");
|
|
|
|
// Let's be sure to call beforeShow if it was added
|
|
if ("function" === typeof existing.beforeShow) {
|
|
existing.beforeShow(input, inst);
|
|
}
|
|
};
|
|
|
|
if ("timepicker" === method) {
|
|
options.onChangeMonthYear = function (year, month, inst, picker) {
|
|
cmb.addTimePickerClasses(inst.dpDiv);
|
|
|
|
// Let's be sure to call onChangeMonthYear if it was added
|
|
if ("function" === typeof existing.onChangeMonthYear) {
|
|
existing.onChangeMonthYear(year, month, inst, picker);
|
|
}
|
|
};
|
|
}
|
|
|
|
options.onClose = function (dateText, inst) {
|
|
// Remove the class when we're done with it (and hide to remove FOUC).
|
|
var $picker = $id("ui-datepicker-div").removeClass("cmb2-element").hide();
|
|
if ("timepicker" === method && !$(inst.input).val()) {
|
|
// Set the timepicker field value if it's empty.
|
|
inst.input.val($picker.find(".ui_tpicker_time").text());
|
|
}
|
|
|
|
// Let's be sure to call onClose if it was added
|
|
if ("function" === typeof existing.onClose) {
|
|
existing.onClose(dateText, inst);
|
|
}
|
|
};
|
|
|
|
return options;
|
|
};
|
|
|
|
// Adds classes to timepicker buttons.
|
|
cmb.addTimePickerClasses = function ($picker) {
|
|
var func = cmb.addTimePickerClasses;
|
|
func.count = func.count || 0;
|
|
|
|
// Wait a bit to let the timepicker render, since these are pre-render events.
|
|
setTimeout(function () {
|
|
if ($picker.find(".ui-priority-secondary").length) {
|
|
$picker.find(".ui-priority-secondary").addClass("button-secondary");
|
|
$picker.find(".ui-priority-primary").addClass("button-primary");
|
|
func.count = 0;
|
|
} else if (func.count < 5) {
|
|
func.count++;
|
|
func($picker);
|
|
}
|
|
}, 10);
|
|
};
|
|
|
|
cmb.initColorPickers = function ($selector) {
|
|
if (!$selector.length) {
|
|
return;
|
|
}
|
|
if (
|
|
"object" === typeof jQuery.wp &&
|
|
"function" === typeof jQuery.wp.wpColorPicker
|
|
) {
|
|
$selector.each(function () {
|
|
var $this = $(this);
|
|
var fieldOpts = $this.data("colorpicker") || {};
|
|
$this.wpColorPicker($.extend({}, cmb.defaults.color_picker, fieldOpts));
|
|
});
|
|
} else {
|
|
$selector
|
|
.each(function (i) {
|
|
$(this).after(
|
|
'<div id="picker-' +
|
|
i +
|
|
'" style="z-index: 1000; background: #EEE; border: 1px solid #CCC; position: absolute; display: block;"></div>',
|
|
);
|
|
$id("picker-" + i)
|
|
.hide()
|
|
.farbtastic($(this));
|
|
})
|
|
.focus(function () {
|
|
$(this).next().show();
|
|
})
|
|
.blur(function () {
|
|
$(this).next().hide();
|
|
});
|
|
}
|
|
};
|
|
|
|
cmb.initCodeEditors = function ($selector) {
|
|
cmb.trigger("cmb_init_code_editors", $selector);
|
|
|
|
if (
|
|
!cmb.defaults.code_editor ||
|
|
!wp ||
|
|
!wp.codeEditor ||
|
|
!$selector.length
|
|
) {
|
|
return;
|
|
}
|
|
|
|
$selector.each(function () {
|
|
wp.codeEditor.initialize(
|
|
this.id,
|
|
cmb.codeEditorArgs($(this).data("codeeditor")),
|
|
);
|
|
});
|
|
};
|
|
|
|
cmb.codeEditorArgs = function (overrides) {
|
|
var props = ["codemirror", "csslint", "jshint", "htmlhint"];
|
|
var args = $.extend({}, cmb.defaults.code_editor);
|
|
overrides = overrides || {};
|
|
|
|
for (var i = props.length - 1; i >= 0; i--) {
|
|
if (overrides.hasOwnProperty(props[i])) {
|
|
args[props[i]] = $.extend(
|
|
{},
|
|
args[props[i]] || {},
|
|
overrides[props[i]],
|
|
);
|
|
}
|
|
}
|
|
|
|
return args;
|
|
};
|
|
|
|
cmb.makeListSortable = function () {
|
|
var $filelist = cmb.metabox().find(".cmb2-media-status.cmb-attach-list");
|
|
if ($filelist.length) {
|
|
$filelist.sortable({ cursor: "move" }).disableSelection();
|
|
}
|
|
};
|
|
|
|
cmb.makeRepeatableSortable = function () {
|
|
var $repeatables = cmb.metabox().find(".cmb-repeat-table .cmb-field-list");
|
|
|
|
if ($repeatables.length) {
|
|
$repeatables.sortable({
|
|
items: ".cmb-repeat-row",
|
|
cursor: "move",
|
|
// The default "cancel" attributes are: "input,textarea,button,select,option".
|
|
// We are appending .CodeMirror.
|
|
// See https://api.jqueryui.com/sortable/#option-cancel
|
|
cancel: "input,textarea,button,select,option,.CodeMirror",
|
|
});
|
|
}
|
|
};
|
|
|
|
cmb.maybeOembed = function (evt) {
|
|
var $this = $(this);
|
|
|
|
var m = {
|
|
focusout: function () {
|
|
setTimeout(function () {
|
|
// if it's been 2 seconds, hide our spinner
|
|
cmb.spinner(".cmb2-metabox", true);
|
|
}, 2000);
|
|
},
|
|
keyup: function () {
|
|
var betw = function (min, max) {
|
|
return evt.which <= max && evt.which >= min;
|
|
};
|
|
// Only Ajax on normal keystrokes
|
|
if (
|
|
betw(48, 90) ||
|
|
betw(96, 111) ||
|
|
betw(8, 9) ||
|
|
evt.which === 187 ||
|
|
evt.which === 190
|
|
) {
|
|
// fire our ajax function
|
|
cmb.doAjax($this, evt);
|
|
}
|
|
},
|
|
paste: function () {
|
|
// paste event is fired before the value is filled, so wait a bit
|
|
setTimeout(function () {
|
|
cmb.doAjax($this);
|
|
}, 100);
|
|
},
|
|
};
|
|
|
|
m[evt.type]();
|
|
};
|
|
|
|
/**
|
|
* Resize oEmbed videos to fit in their respective metaboxes
|
|
*
|
|
* @since 0.9.4
|
|
*
|
|
* @return {return}
|
|
*/
|
|
cmb.resizeoEmbeds = function () {
|
|
cmb.metabox().each(function () {
|
|
var $this = $(this);
|
|
var $tableWrap = $this.parents(".inside");
|
|
var isSide =
|
|
$this.parents(".inner-sidebar").length ||
|
|
$this.parents("#side-sortables").length;
|
|
var isSmall = isSide;
|
|
var isSmallest = false;
|
|
if (!$tableWrap.length) {
|
|
return true; // continue
|
|
}
|
|
|
|
// Calculate new width
|
|
var tableW = $tableWrap.width();
|
|
|
|
if (cmb.styleBreakPoint > tableW) {
|
|
isSmall = true;
|
|
isSmallest = cmb.styleBreakPoint - 62 > tableW;
|
|
}
|
|
|
|
tableW = isSmall ? tableW : Math.round($tableWrap.width() * 0.82 * 0.97);
|
|
var newWidth = tableW - 30;
|
|
if (isSmall && !isSide && !isSmallest) {
|
|
newWidth = newWidth - 75;
|
|
}
|
|
if (newWidth > 639) {
|
|
return true; // continue
|
|
}
|
|
|
|
var $embeds = $this.find(".cmb-type-oembed .embed-status");
|
|
var $children = $embeds.children().not(".cmb2-remove-wrapper");
|
|
if (!$children.length) {
|
|
return true; // continue
|
|
}
|
|
|
|
$children.each(function () {
|
|
var $this = $(this);
|
|
var iwidth = $this.width();
|
|
var iheight = $this.height();
|
|
var _newWidth = newWidth;
|
|
if ($this.parents(".cmb-repeat-row").length && !isSmall) {
|
|
// Make room for our repeatable "remove" button column
|
|
_newWidth = newWidth - 91;
|
|
_newWidth = 785 > tableW ? _newWidth - 15 : _newWidth;
|
|
}
|
|
// Calc new height
|
|
var newHeight = Math.round((_newWidth * iheight) / iwidth);
|
|
$this.width(_newWidth).height(newHeight);
|
|
});
|
|
});
|
|
};
|
|
|
|
// function for running our ajax
|
|
cmb.doAjax = function ($obj) {
|
|
// get typed value
|
|
var oembed_url = $obj.val();
|
|
// only proceed if the field contains more than 6 characters
|
|
if (oembed_url.length < 6) {
|
|
return;
|
|
}
|
|
|
|
// get field id
|
|
var field_id = $obj.attr("id");
|
|
var $context = $obj.closest(".cmb-td");
|
|
var $embed_container = $context.find(".embed-status");
|
|
var $embed_wrap = $context.find(".embed_wrap");
|
|
var $child_el = $embed_container.find(":first-child");
|
|
var oembed_width =
|
|
$embed_container.length && $child_el.length
|
|
? $child_el.width()
|
|
: $obj.width();
|
|
|
|
cmb.log("oembed_url", oembed_url, field_id);
|
|
|
|
// show our spinner
|
|
cmb.spinner($context);
|
|
// clear out previous results
|
|
$embed_wrap.html("");
|
|
// and run our ajax function
|
|
setTimeout(function () {
|
|
// if they haven't typed in 500 ms
|
|
if ($(".cmb2-oembed:focus").val() !== oembed_url) {
|
|
return;
|
|
}
|
|
$.ajax({
|
|
type: "post",
|
|
dataType: "json",
|
|
url: l10n.ajaxurl,
|
|
data: {
|
|
action: "cmb2_oembed_handler",
|
|
oembed_url: oembed_url,
|
|
oembed_width: oembed_width > 300 ? oembed_width : 300,
|
|
field_id: field_id,
|
|
object_id: $obj.data("objectid"),
|
|
object_type: $obj.data("objecttype"),
|
|
cmb2_ajax_nonce: l10n.ajax_nonce,
|
|
},
|
|
success: function (response) {
|
|
cmb.log(response);
|
|
// hide our spinner
|
|
cmb.spinner($context, true);
|
|
// and populate our results from ajax response
|
|
$embed_wrap.html(response.data);
|
|
},
|
|
});
|
|
}, 500);
|
|
};
|
|
|
|
/**
|
|
* Gets jQuery object containing all CMB metaboxes. Caches the result.
|
|
*
|
|
* @since 1.0.2
|
|
*
|
|
* @return {Object} jQuery object containing all CMB metaboxes.
|
|
*/
|
|
cmb.metabox = function () {
|
|
if (cmb.$metabox) {
|
|
return cmb.$metabox;
|
|
}
|
|
cmb.$metabox = $(".cmb2-wrap > .cmb2-metabox");
|
|
return cmb.$metabox;
|
|
};
|
|
|
|
/**
|
|
* Starts/stops contextual spinner.
|
|
*
|
|
* @since 1.0.1
|
|
*
|
|
* @param {object} $context The jQuery parent/context object.
|
|
* @param {bool} hide Whether to hide the spinner (will show by default).
|
|
*
|
|
* @return {void}
|
|
*/
|
|
cmb.spinner = function ($context, hide) {
|
|
var m = hide ? "removeClass" : "addClass";
|
|
$(".cmb-spinner", $context)[m]("is-active");
|
|
};
|
|
|
|
/**
|
|
* Triggers a jQuery event on the document object.
|
|
*
|
|
* @since 2.2.3
|
|
*
|
|
* @param {string} evtName The name of the event to trigger.
|
|
*
|
|
* @return {void}
|
|
*/
|
|
cmb.trigger = function (evtName) {
|
|
var args = Array.prototype.slice.call(arguments, 1);
|
|
args.push(cmb);
|
|
$document.trigger(evtName, args);
|
|
};
|
|
|
|
/**
|
|
* Triggers a jQuery event on the given jQuery object.
|
|
*
|
|
* @since 2.2.3
|
|
*
|
|
* @param {object} $el The jQuery element object.
|
|
* @param {string} evtName The name of the event to trigger.
|
|
*
|
|
* @return {void}
|
|
*/
|
|
cmb.triggerElement = function ($el, evtName) {
|
|
var args = Array.prototype.slice.call(arguments, 2);
|
|
args.push(cmb);
|
|
$el.trigger(evtName, args);
|
|
};
|
|
|
|
/**
|
|
* Get an argument for a given field.
|
|
*
|
|
* @since 2.5.0
|
|
*
|
|
* @param {string|object} hash The field hash, id, or a jQuery object for a field.
|
|
* @param {string} arg The argument to get on the field.
|
|
*
|
|
* @return {mixed} The argument value.
|
|
*/
|
|
cmb.getFieldArg = function (hash, arg) {
|
|
return cmb.getField(hash)[arg];
|
|
};
|
|
|
|
/**
|
|
* Get a field object instances. Can be filtered by passing in a filter callback function.
|
|
* e.g. `const fileFields = CMB2.getFields(f => 'file' === f.type);`
|
|
*
|
|
* @since 2.5.0
|
|
*
|
|
* @param {mixed} filterCb An optional filter callback function.
|
|
*
|
|
* @return array An array of field object instances.
|
|
*/
|
|
cmb.getFields = function (filterCb) {
|
|
if ("function" === typeof filterCb) {
|
|
var fields = [];
|
|
$.each(l10n.fields, function (hash, field) {
|
|
if (filterCb(field, hash)) {
|
|
fields.push(field);
|
|
}
|
|
});
|
|
return fields;
|
|
}
|
|
|
|
return l10n.fields;
|
|
};
|
|
|
|
/**
|
|
* Get a field object instance by hash or id.
|
|
*
|
|
* @since 2.5.0
|
|
*
|
|
* @param {string|object} hash The field hash, id, or a jQuery object for a field.
|
|
*
|
|
* @return {object} The field object or an empty object.
|
|
*/
|
|
cmb.getField = function (hash) {
|
|
var field = {};
|
|
hash = hash instanceof jQuery ? hash.data("hash") : hash;
|
|
if (hash) {
|
|
try {
|
|
if (l10n.fields[hash]) {
|
|
throw new Error(hash);
|
|
}
|
|
|
|
cmb.getFields(function (field) {
|
|
if ("function" === typeof hash) {
|
|
if (hash(field)) {
|
|
throw new Error(field.hash);
|
|
}
|
|
} else if (field.id && field.id === hash) {
|
|
throw new Error(field.hash);
|
|
}
|
|
});
|
|
} catch (e) {
|
|
field = l10n.fields[e.message];
|
|
}
|
|
}
|
|
|
|
return field;
|
|
};
|
|
|
|
/**
|
|
* Safely log things if query var is set. Accepts same parameters as console.log.
|
|
*
|
|
* @since 1.0.0
|
|
*
|
|
* @return {void}
|
|
*/
|
|
cmb.log = function () {
|
|
if (l10n.script_debug && console && "function" === typeof console.log) {
|
|
console.log.apply(console, arguments);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Replace the last occurrence of a string.
|
|
*
|
|
* @since 2.2.6
|
|
*
|
|
* @param {string} string String to search/replace.
|
|
* @param {string} search String to search.
|
|
* @param {string} replace String to replace search with.
|
|
*
|
|
* @return {string} Possibly modified string.
|
|
*/
|
|
cmb.replaceLast = function (string, search, replace) {
|
|
// find the index of last time word was used
|
|
var n = string.lastIndexOf(search);
|
|
|
|
// slice the string in 2, one from the start to the lastIndexOf
|
|
// and then replace the word in the rest
|
|
return string.slice(0, n) + string.slice(n).replace(search, replace);
|
|
};
|
|
|
|
// Kick it off!
|
|
$(cmb.init);
|
|
})(window, document, jQuery, window.CMB2);
|