//(c) W-Shadow
/*global wsEditorData, defaultMenu, customMenu, _:false */
/**
* @property wsEditorData
* @property {boolean} wsEditorData.wsMenuEditorPro
*
* @property {object} wsEditorData.blankMenuItem
* @property {object} wsEditorData.itemTemplates
* @property {object} wsEditorData.customItemTemplate
*
* @property {string} wsEditorData.adminAjaxUrl
* @property {string} wsEditorData.imagesUrl
*
* @property {string} wsEditorData.menuFormatName
* @property {string} wsEditorData.menuFormatVersion
*
* @property {boolean} wsEditorData.hideAdvancedSettings
* @property {boolean} wsEditorData.showExtraIcons
* @property {boolean} wsEditorData.dashiconsAvailable
* @property {string} wsEditorData.submenuIconsEnabled
* @property {Object} wsEditorData.showHints
*
* @property {string} wsEditorData.hideAdvancedSettingsNonce
* @property {string} wsEditorData.getPagesNonce
* @property {string} wsEditorData.getPageDetailsNonce
* @property {string} wsEditorData.disableDashboardConfirmationNonce
*
* @property {string} wsEditorData.captionShowAdvanced
* @property {string} wsEditorData.captionHideAdvanced
*
* @property {string} wsEditorData.unclickableTemplateId
* @property {string} wsEditorData.unclickableTemplateClass
* @property {string} wsEditorData.embeddedPageTemplateId
*
* @property {string} wsEditorData.currentUserLogin
* @property {string|null} wsEditorData.selectedActor
*
* @property {object} wsEditorData.actors
* @property {string[]} wsEditorData.visibleUsers
*
* @property {object} wsEditorData.postTypes
* @property {object} wsEditorData.taxonomies
*
* @property {string|null} wsEditorData.selectedMenu
* @property {string|null} wsEditorData.selectedSubmenu
*
* @property {string} wsEditorData.setTestConfigurationNonce
* @property {string} wsEditorData.testAccessNonce
*
* @property {boolean} wsEditorData.isDemoMode
* @property {boolean} wsEditorData.isMasterMode
*/
wsEditorData.wsMenuEditorPro = !!wsEditorData.wsMenuEditorPro; //Cast to boolean.
var wsIdCounter = 0;
//A bit of black magic/hack to convince my IDE that wsAmeLodash is an alias for lodash.
window.wsAmeLodash = (function() {
'use strict';
if (typeof wsAmeLodash !== 'undefined') {
return wsAmeLodash;
}
return _.noConflict();
})();
//These two properties must be objects, not arrays.
jQuery.each(['grant_access', 'hidden_from_actor'], function(unused, key) {
'use strict';
if (wsEditorData.blankMenuItem.hasOwnProperty(key) && !jQuery.isPlainObject(wsEditorData.blankMenuItem[key])) {
wsEditorData.blankMenuItem[key] = {};
}
});
AmeCapabilityManager = AmeActors;
/**
* A utility for retrieving post and page titles.
*/
var AmePageTitles = (function($) {
'use strict';
var me = {}, cache = {};
function getCacheKey(pageId, blogId) {
return blogId + '_' + pageId;
}
/**
* Add a page title to the cache.
*
* @param {Number} pageId Post or page ID.
* @param {Number} blogId Blog ID.
* @param {String} title The title of the post or page.
*/
me.add = function(pageId, blogId, title) {
cache[getCacheKey(pageId, blogId)] = title;
};
/**
* Get page title.
*
* Note: This method does not return the title. Instead, it calls the provided callback with the title
* as the first argument. The callback will be executed asynchronously if the title hasn't been cached yet.
*
* @param {Number} pageId
* @param {Number} blogId
* @param {Function} callback
*/
me.get = function(pageId, blogId, callback) {
var key = getCacheKey(pageId, blogId);
if (typeof cache[key] !== 'undefined') {
callback(cache[key], pageId, blogId);
return;
}
$.getJSON(
wsEditorData.adminAjaxUrl,
{
'action' : 'ws_ame_get_page_details',
'_ajax_nonce' : wsEditorData.getPageDetailsNonce,
'post_id' : pageId,
'blog_id' : blogId
},
function(details) {
var title;
if (typeof details.error !== 'undefined'){
title = details.error;
} else if ((typeof details !== 'object') || (typeof details.post_title === 'undefined')) {
title = '< Server error >';
} else {
title = details.post_title;
}
cache[key] = title;
callback(cache[key], pageId, blogId);
}
);
};
return me;
})(jQuery);
var AmeEditorApi = {};
window.AmeEditorApi = AmeEditorApi;
(function ($, _){
'use strict';
var actorSelectorWidget = new AmeActorSelector(AmeActors, wsEditorData.wsMenuEditorPro);
var itemTemplates = {
templates: wsEditorData.itemTemplates,
getTemplateById: function(templateId) {
if (wsEditorData.itemTemplates.hasOwnProperty(templateId)) {
return wsEditorData.itemTemplates[templateId];
} else if ((templateId === '') || (templateId === 'custom')) {
return wsEditorData.customItemTemplate;
}
return null;
},
getDefaults: function (templateId) {
var template = this.getTemplateById(templateId);
if (template) {
return template.defaults;
} else {
return null;
}
},
getDefaultValue: function (templateId, fieldName) {
if (fieldName === 'template_id') {
return null;
}
var defaults = this.getDefaults(templateId);
if (defaults && (typeof defaults[fieldName] !== 'undefined')) {
return defaults[fieldName];
}
return null;
},
hasDefaultValue: function(templateId, fieldName) {
return (this.getDefaultValue(templateId, fieldName) !== null);
}
};
/**
* Set an input field to a value. The only difference from jQuery.val() is that
* setting a checkbox to true/false will check/clear it.
*
* @param input
* @param value
*/
function setInputValue(input, value) {
if (input.attr('type') === 'checkbox'){
input.prop('checked', value);
} else {
input.val(value);
}
}
/**
* Get the value of an input field. The only difference from jQuery.val() is that
* checked/unchecked checkboxes will return true/false.
*
* @param input
* @return {*}
*/
function getInputValue(input) {
if (input.attr('type') === 'checkbox'){
return input.is(':checked');
}
return input.val();
}
/*
* Utility function for generating pseudo-random alphanumeric menu IDs.
* Rationale: Simpler than atomically auto-incrementing or globally unique IDs.
*/
function randomMenuId(prefix, size){
prefix = (typeof prefix === 'undefined') ? 'custom_item_' : prefix;
size = (typeof size === 'undefined') ? 5 : size;
var suffix = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < size; i++ ) {
suffix += possible.charAt(Math.floor(Math.random() * possible.length));
}
return prefix + suffix;
}
function outputWpMenu(menu){
var menuCopy = $.extend(true, {}, menu);
var menuBox = $('#ws_menu_box');
//Remove the current menu data
menuBox.empty();
$('#ws_submenu_box').empty();
//Display the new menu
var i = 0;
for (var filename in menuCopy){
if (!menuCopy.hasOwnProperty(filename)){
continue;
}
outputTopMenu(menuCopy[filename]);
i++;
}
//Automatically select the first top-level menu
menuBox.find('.ws_menu:first').click();
}
/**
* Load a menu configuration in the editor.
* Note: All previous settings will be discarded without warning. Unsaved changes will be lost.
*
* @param {Object} adminMenu The menu structure to load.
*/
function loadMenuConfiguration(adminMenu) {
//There are some menu properties that need to be objects, but PHP JSON-encodes empty associative
//arrays as numeric arrays. We want them to be empty objects instead.
if (adminMenu.hasOwnProperty('color_presets') && !$.isPlainObject(adminMenu.color_presets)) {
adminMenu.color_presets = {};
}
var objectProperties = ['grant_access', 'hidden_from_actor'];
//noinspection JSUnusedLocalSymbols
function fixEmptyObjects(unused, menuItem) {
for (var i = 0; i < objectProperties.length; i++) {
var key = objectProperties[i];
if (menuItem.hasOwnProperty(key) && !$.isPlainObject(menuItem[key])) {
menuItem[key] = {};
}
}
if (menuItem.hasOwnProperty('items')) {
$.each(menuItem.items, fixEmptyObjects);
}
}
$.each(adminMenu.tree, fixEmptyObjects);
//Load color presets from the new configuration.
if (typeof adminMenu.color_presets === 'object') {
colorPresets = $.extend(true, {}, adminMenu.color_presets);
} else {
colorPresets = {};
}
wasPresetDropdownPopulated = false;
//Load capabilities.
AmeCapabilityManager.setGrantedCapabilities(_.get(adminMenu, 'granted_capabilities', {}));
//Load general menu visibility.
generalComponentVisibility = _.get(adminMenu, 'component_visibility', {});
AmeEditorApi.refreshComponentVisibility();
//Display the new admin menu.
outputWpMenu(adminMenu.tree);
}
/*
* Create edit widgets for a top-level menu and its submenus and append them all to the DOM.
*
* Inputs :
* menu - an object containing menu data
* afterNode - if specified, the new menu widget will be inserted after this node. Otherwise,
* it will be added to the end of the list.
* Outputs :
* Object with two fields - 'menu' and 'submenu' - containing the DOM nodes of the created widgets.
*/
function outputTopMenu(menu, afterNode){
//Create the menu widget
var menu_obj = buildMenuItem(menu, true);
if ( (typeof afterNode !== 'undefined') && (afterNode !== null) ){
$(afterNode).after(menu_obj);
} else {
menu_obj.appendTo('#ws_menu_box');
}
//Create a container for menu items, even if there are none
var submenu = buildSubmenu(menu.items, menu_obj.attr('id'));
submenu.appendTo('#ws_submenu_box');
menu_obj.data('submenu_id', submenu.attr('id'));
//Note: Update the menu only after its children are ready. It needs the submenu items to decide whether to display
//the access checkbox as checked or indeterminate.
updateItemEditor(menu_obj);
return {
'menu' : menu_obj,
'submenu' : submenu
};
}
/*
* Create and populate a submenu container.
*/
function buildSubmenu(items, parentMenuId){
//Create a container for menu items, even if there are none
var submenu = $('
');
submenu.attr('id', 'ws-submenu-'+(wsIdCounter++));
if (parentMenuId) {
submenu.data('parent_menu_id', parentMenuId);
}
//Only show menus that have items.
//Skip arrays (with a length) because filled menus are encoded as custom objects.
var entry = null;
if (items) {
$.each(items, function(index, item) {
entry = buildMenuItem(item, false);
if ( entry ){
submenu.append(entry);
updateItemEditor(entry);
}
});
}
//Make the submenu sortable
makeBoxSortable(submenu);
return submenu;
}
/**
* Create an edit widget for a menu item.
*
* @param {Object} itemData
* @param {Boolean} [isTopLevel] Specify if this is a top-level menu or a sub-menu item. Defaults to false (= sub-item).
* @return {*} The created widget as a jQuery object.
*/
function buildMenuItem(itemData, isTopLevel) {
isTopLevel = (typeof isTopLevel === 'undefined') ? false : isTopLevel;
//Create the menu HTML
var item = $('')
.attr('class', "ws_container")
.attr('id', 'ws-menu-item-' + (wsIdCounter++))
.data('menu_item', itemData)
.data('field_editors_created', false);
item.addClass(isTopLevel ? 'ws_menu' : 'ws_item');
if ( itemData.separator ) {
item.addClass('ws_menu_separator');
}
//Add a header and a container for property editors (to improve performance
//the editors themselves are created later, when the user tries to access them
//for the first time).
var contents = [];
var menuTitle = getFieldValue(itemData, 'menu_title', '');
if (menuTitle === '') {
menuTitle = ' ';
}
contents.push(
'
',
itemData.separator ? '' : '
',
'',
'',
formatMenuTitle(menuTitle),
' ',
'
',
''
);
item.append(contents.join(''));
//Apply flags based on the item's state
var flags = ['hidden', 'unused', 'custom'];
for (var i = 0; i < flags.length; i++) {
setMenuFlag(item, flags[i], getFieldValue(itemData, flags[i], false));
}
if ( isTopLevel && !itemData.separator ){
//Allow the user to drag menu items to top-level menus
item.droppable({
'hoverClass' : 'ws_menu_drop_hover',
'accept' : (function(thing){
return thing.hasClass('ws_item');
}),
'drop' : (function(event, ui){
var droppedItemData = readItemState(ui.draggable);
var new_item = buildMenuItem(droppedItemData, false);
var sourceSubmenu = ui.draggable.parent();
var submenu = $('#' + item.data('submenu_id'));
submenu.append(new_item);
if ( !event.ctrlKey ) {
ui.draggable.remove();
}
updateItemEditor(new_item);
//Moving an item can change aggregate menu permissions. Update the UI accordingly.
updateParentAccessUi(submenu);
updateParentAccessUi(sourceSubmenu);
})
});
}
return item;
}
function jsTrim(str){
return str.replace(/^\s+|\s+$/g, "");
}
//Expose this handy tool to our other scripts.
AmeEditorApi.jsTrim = jsTrim;
function stripAllTags(input) {
//Based on: http://phpjs.org/functions/strip_tags/
var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
commentsAndPhpTags = /|<\?(?:php)?[\s\S]*?\?>/gi;
return input.replace(commentsAndPhpTags, '').replace(tags, '');
}
function truncateString(input, maxLength, padding) {
if (typeof padding === 'undefined') {
padding = '';
}
if (input.length > maxLength) {
input = input.substring(0, maxLength - 1) + padding;
}
return input;
}
/**
* Format menu title for display in HTML.
* Strips tags and truncates long titles.
*
* @param {String} title
* @returns {String}
*/
function formatMenuTitle(title) {
title = stripAllTags(title);
//Compact whitespace.
title = title.replace(/[\s\t\r\n]+/g, ' ');
title = jsTrim(title);
//The max. length was chosen empirically.
title = truncateString(title, 34, '\u2026');
return title;
}
//Editor field spec template.
var baseField = {
caption : '[No caption]',
standardCaption : true,
advanced : false,
type : 'text',
defaultValue: '',
onlyForTopMenus: false,
addDropdown : false,
visible: true,
write: null,
display: null,
tooltip: null
};
/*
* List of all menu fields that have an associated editor
*/
var knownMenuFields = {
'menu_title' : $.extend({}, baseField, {
caption : 'Menu title',
display: function(menuItem, displayValue, input, containerNode) {
//Update the header as well.
containerNode.find('.ws_item_title').html(formatMenuTitle(displayValue) + ' ');
return displayValue;
},
write: function(menuItem, value, input, containerNode) {
menuItem.menu_title = value;
containerNode.find('.ws_item_title').html(stripAllTags(input.val()) + ' ');
}
}),
'template_id' : $.extend({}, baseField, {
caption : 'Target page',
type : 'select',
options : (function(){
//Generate name => id mappings for all item templates + the special "Custom" template.
var itemTemplateIds = [];
itemTemplateIds.push([wsEditorData.customItemTemplate.name, '']);
for (var template_id in wsEditorData.itemTemplates) {
if (wsEditorData.itemTemplates.hasOwnProperty(template_id)) {
itemTemplateIds.push([wsEditorData.itemTemplates[template_id].name, template_id]);
}
}
itemTemplateIds.sort(function(a, b) {
if (a[1] === b[1]) {
return 0;
}
//The "Custom" item is always first.
if (a[1] === '') {
return -1;
} else if (b[1] === '') {
return 1;
}
//Top-level items go before submenus.
var aIsTop = (a[1].charAt(0) === '>') ? 1 : 0;
var bIsTop = (b[1].charAt(0) === '>') ? 1 : 0;
if (aIsTop !== bIsTop) {
return bIsTop - aIsTop;
}
//Everything else is sorted by name, in alphabetical order.
if (a[0] > b[0]) {
return 1;
} else if (a[0] < b[0]) {
return -1;
}
return 0;
});
return itemTemplateIds;
})(),
write: function(menuItem, value, input, containerNode) {
var oldTemplateId = menuItem.template_id;
menuItem.template_id = value;
menuItem.defaults = itemTemplates.getDefaults(menuItem.template_id);
menuItem.custom = (menuItem.template_id === '');
// The file/URL of non-custom items is read-only and equal to the default
// value. Rationale: simplifies menu generation, prevents some user mistakes.
if (menuItem.template_id !== '') {
menuItem.file = null;
}
// The new template might not have default values for some of the fields
// currently set to null (= "default"). In those cases, we need to make
// the current values explicit.
containerNode.find('.ws_edit_field').each(function(index, field){
field = $(field);
var fieldName = field.data('field_name');
var isSetToDefault = (menuItem[fieldName] === null);
var hasDefaultValue = itemTemplates.hasDefaultValue(menuItem.template_id, fieldName);
if (isSetToDefault && !hasDefaultValue) {
var oldDefaultValue = itemTemplates.getDefaultValue(oldTemplateId, fieldName);
if (oldDefaultValue !== null) {
menuItem[fieldName] = oldDefaultValue;
}
}
});
}
}),
'embedded_page_id' : $.extend({}, baseField, {
caption: 'Embedded page ID',
defaultValue: 'Select page to display',
type: 'text',
visible: false, //Displayed on-demand.
addDropdown: 'ws_embedded_page_selector',
display: function(menuItem, displayValue, input) {
//Only show this field if the "Embed WP page" template is selected.
input.closest('.ws_edit_field').toggle(menuItem.template_id === wsEditorData.embeddedPageTemplateId);
input.prop('readonly', true);
var pageId = parseInt(getFieldValue(menuItem, 'embedded_page_id', 0), 10),
blogId = parseInt(getFieldValue(menuItem, 'embedded_page_blog_id', 1), 10),
formattedId = 'ID: ' + pageId;
if (pageId <= 0) {
return 'Select page =>';
}
if (blogId !== 1) {
formattedId = formattedId + ', blog ID: ' + blogId;
}
displayValue = formattedId;
AmePageTitles.get(pageId, blogId, function(title) {
//If we retrieved the title via AJAX, the user might have selected a different page in the meantime.
//Make sure it's still the same page before displaying the title.
var currentPageId = parseInt(getFieldValue(menuItem, 'embedded_page_id', 0), 10),
currentBlogId = parseInt(getFieldValue(menuItem, 'embedded_page_blog_id', 1), 10);
if ((currentPageId !== pageId) || (currentBlogId !== blogId)) {
return;
}
displayValue = title + ' (' + formattedId + ')';
input.val(displayValue);
});
return displayValue;
},
write: function() {
//The user cannot directly edit this field. We deliberately ignore writes.
}
}),
'file' : $.extend({}, baseField, {
caption: 'URL',
display: function(menuItem, displayValue, input) {
// The URL/file field is read-only for default menus. Also, since the "file"
// field is usually set to a page slug or plugin filename for plugin/hook pages,
// we display the dynamically generated "url" field here (i.e. the actual URL) instead.
if (menuItem.template_id !== '') {
input.attr('readonly', 'readonly');
displayValue = itemTemplates.getDefaultValue(menuItem.template_id, 'url');
} else {
input.removeAttr('readonly');
}
return displayValue;
},
write: function(menuItem, value) {
// A menu must always have a non-empty URL. If the user deletes the current value,
// reset it to the old value.
if (value === '') {
value = menuItem.file;
}
// Default menus always point to the default file/URL.
if (menuItem.template_id !== '') {
value = null;
}
menuItem.file = value;
}
}),
'access_level' : $.extend({}, baseField, {
caption: 'Permissions',
defaultValue: 'read',
type: 'access_editor',
visible: false, //Will be set to visible only in Pro version.
display: function(menuItem) {
//Permissions display is a little complicated and could use improvement.
var requiredCap = getFieldValue(menuItem, 'access_level', '');
var extraCap = getFieldValue(menuItem, 'extra_capability', '');
var displayValue = (menuItem.template_id === '') ? '< Custom >' : requiredCap;
if (extraCap !== '') {
if (menuItem.template_id === '') {
displayValue = extraCap;
} else {
displayValue = displayValue + '+' + extraCap;
}
}
return displayValue;
},
write: function(menuItem) {
//The required capability can't be directly edited and always equals the default.
menuItem.access_level = null;
}
}),
'required_capability_read_only' : $.extend({}, baseField, {
caption: 'Required capability',
defaultValue: 'none',
type: 'text',
tooltip: "Only users who have this capability can see the menu. "+
"The capability can't be changed because it's usually hard-coded in WordPress or the plugin that created the menu."+
"
Use the \"Extra capability\" field to restrict access to this menu.",
visible: function(menuItem) {
//Show only in the free version, on non-custom menus.
return !wsEditorData.wsMenuEditorPro && (menuItem.template_id !== '');
},
display: function(menuItem, displayValue, input) {
input.prop('readonly', true);
return getFieldValue(menuItem, 'access_level', '');
},
write: function(menuItem, value) {
//The required capability is read-only. Ignore writes.
}
}),
'extra_capability' : $.extend({}, baseField, {
caption: 'Extra capability',
defaultValue: 'read',
type: 'text',
addDropdown: 'ws_cap_selector',
tooltip: function(menuItem) {
if (menuItem.template_id === '') {
return 'Only users who have this capability can see the menu.';
}
return 'An additional capability check that is applied on top of the required capability.';
},
display: function(menuItem) {
var requiredCap = getFieldValue(menuItem, 'access_level', '');
var extraCap = getFieldValue(menuItem, 'extra_capability', '');
//On custom menus, show the default required cap when no extra cap is selected.
//Otherwise there would be no visible capability requirements at all.
var displayValue = extraCap;
if ((menuItem.template_id === '') && (extraCap === '')) {
displayValue = requiredCap;
}
return displayValue;
},
write: function(menuItem, value) {
value = jsTrim(value);
//Reset to default if the user clears the input.
if (value === '') {
menuItem.extra_capability = null;
return;
}
menuItem.extra_capability = value;
}
}),
'appearance_heading' : $.extend({}, baseField, {
caption: 'Appearance',
advanced : true,
onlyForTopMenus: false,
type: 'heading',
standardCaption: false,
visible: false //Only visible in the Pro version.
}),
'icon_url' : $.extend({}, baseField, {
caption: 'Icon URL',
type : 'icon_selector',
advanced : true,
defaultValue: 'div',
onlyForTopMenus: true,
display: function(menuItem, displayValue, input, containerNode) {
//Display the current icon in the selector.
var cssClass = getFieldValue(menuItem, 'css_class', '');
var iconUrl = getFieldValue(menuItem, 'icon_url', '', containerNode);
displayValue = iconUrl;
//When submenu icon visibility is set to "only if manually selected",
//don't show the default submenu icons.
var isDefault = (typeof menuItem.icon_url === 'undefined') || (menuItem.icon_url === null);
if (isDefault && (wsEditorData.submenuIconsEnabled === 'if_custom') && containerNode.hasClass('ws_item')) {
iconUrl = 'none';
cssClass = '';
}
var selectButton = input.closest('.ws_edit_field').find('.ws_select_icon');
var cssIcon = selectButton.find('.icon16');
var imageIcon = selectButton.find('img');
var matches = cssClass.match(/\b(ame-)?menu-icon-([^\s]+)\b/);
var iconFontMatches = iconUrl && iconUrl.match(/^\s*((dashicons|ame-fa)-[a-z0-9\-]+)/);
//Icon URL takes precedence over icon class.
if ( iconUrl && iconUrl !== 'none' && iconUrl !== 'div' && !iconFontMatches ) {
//Regular image icon.
cssIcon.hide();
imageIcon.prop('src', iconUrl).show();
} else if ( iconFontMatches ) {
cssIcon.removeClass().addClass('icon16');
if ( iconFontMatches[2] === 'dashicons' ) {
//Dashicon.
cssIcon.addClass('dashicons ' + iconFontMatches[1]);
} else if ( iconFontMatches[2] === 'ame-fa' ) {
//FontAwesome icon.
cssIcon.addClass('ame-fa ' + iconFontMatches[1]);
}
imageIcon.hide();
cssIcon.show();
} else if ( matches ) {
//Other CSS-based icon.
imageIcon.hide();
var iconClass = (matches[1] ? matches[1] : '') + 'icon-' + matches[2];
cssIcon.removeClass().addClass('icon16 ' + iconClass).show();
} else {
//This menu has no icon at all. This is actually a valid state
//and WordPress will display a menu like that correctly.
imageIcon.hide();
cssIcon.removeClass().addClass('icon16').show();
}
return displayValue;
}
}),
'colors' : $.extend({}, baseField, {
caption: 'Color scheme',
defaultValue: 'Default',
type: 'color_scheme_editor',
onlyForTopMenus: true,
visible: false,
advanced : true,
display: function(menuItem, displayValue, input, containerNode) {
var colors = getFieldValue(menuItem, 'colors', {}) || {};
var colorList = containerNode.find('.ws_color_scheme_display');
colorList.empty();
var count = 0, maxColorsToShow = 7;
$.each(colors, function(name, value) {
if ( !value || (count >= maxColorsToShow) ) {
return;
}
colorList.append(
$('').addClass('ws_color_display_item').css('background-color', value)
);
count++;
});
if (count === 0) {
colorList.append('Default');
}
return 'Placeholder. You should never see this.';
},
write: function(menuItem) {
//Menu colors can't be directly edited.
}
}),
'html_heading' : $.extend({}, baseField, {
caption: 'HTML',
advanced : true,
onlyForTopMenus: true,
type: 'heading',
standardCaption: false
}),
'open_in' : $.extend({}, baseField, {
caption: 'Open in',
advanced : true,
type : 'select',
options : [
['Same window or tab', 'same_window'],
['New window', 'new_window'],
['Frame', 'iframe']
],
defaultValue: 'same_window',
visible: false
}),
'iframe_height' : $.extend({}, baseField, {
caption: 'Frame height (pixels)',
advanced : true,
visible: function(menuItem) {
return wsEditorData.wsMenuEditorPro && (getFieldValue(menuItem, 'open_in') === 'iframe');
},
display: function(menuItem, displayValue, input) {
input.prop('placeholder', 'Auto');
if (displayValue === 0 || displayValue === '0') {
displayValue = '';
}
return displayValue;
},
write: function(menuItem, value) {
value = parseInt(value, 10);
if (isNaN(value) || (value < 0)) {
value = 0;
}
value = Math.round(value);
if (value > 10000) {
value = 10000;
}
if (value === 0) {
menuItem.iframe_height = null;
} else {
menuItem.iframe_height = value;
}
}
}),
'css_class' : $.extend({}, baseField, {
caption: 'CSS classes',
advanced : true,
onlyForTopMenus: true
}),
'hookname' : $.extend({}, baseField, {
caption: 'ID attribute',
advanced : true,
onlyForTopMenus: true
}),
'page_properties_heading' : $.extend({}, baseField, {
caption: 'Page',
advanced : true,
onlyForTopMenus: true,
type: 'heading',
standardCaption: false
}),
'page_heading' : $.extend({}, baseField, {
caption: 'Page heading',
advanced : true,
onlyForTopMenus: false,
visible: false
}),
'page_title' : $.extend({}, baseField, {
caption: "Window title",
standardCaption : true,
advanced : true
}),
'is_always_open' : $.extend({}, baseField, {
caption: 'Keep this menu expanded',
advanced : true,
onlyForTopMenus: true,
type: 'checkbox',
standardCaption: false
})
};
AmeEditorApi.getItemDisplayUrl = function(menuItem) {
var url = getFieldValue(menuItem, 'file', '');
if (menuItem.template_id !== '') {
//Use the template URL. It's a preset that can't be overridden.
var defaultUrl = itemTemplates.getDefaultValue(menuItem.template_id, 'url');
if (defaultUrl) {
url = defaultUrl;
}
}
return url;
};
/*
* Create editors for the visible fields of a menu entry and append them to the specified node.
*/
function buildEditboxFields(fieldContainer, entry, isTopLevel){
isTopLevel = (typeof isTopLevel === 'undefined') ? false : isTopLevel;
var basicFields = $('').appendTo(fieldContainer);
var advancedFields = $('').appendTo(fieldContainer);
if ( wsEditorData.hideAdvancedSettings ){
advancedFields.css('display', 'none');
}
for (var field_name in knownMenuFields){
if (!knownMenuFields.hasOwnProperty(field_name)) {
continue;
}
var fieldSpec = knownMenuFields[field_name];
if (fieldSpec.onlyForTopMenus && !isTopLevel) {
continue;
}
var field = buildEditboxField(entry, field_name, fieldSpec);
if (field){
if (fieldSpec.advanced){
advancedFields.append(field);
} else {
basicFields.append(field);
}
}
}
//Add a link that shows/hides advanced fields
fieldContainer.append(
'
'
);
}
/*
* Create an editor for a specified field.
*/
//noinspection JSUnusedLocalSymbols
function buildEditboxField(entry, field_name, field_settings){
//Build a form field of the appropriate type
var inputBox = null;
var basicTextField = '';
//noinspection FallthroughInSwitchStatementJS
switch(field_settings.type){
case 'select':
inputBox = $('