PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
Dir : /home/trave494/seoshop.live/wp-content/plugins/admin-menu-editor/js/ |
Server: Linux ngx353.inmotionhosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 IP: 209.182.202.254 |
Dir : /home/trave494/seoshop.live/wp-content/plugins/admin-menu-editor/js/menu-editor.js |
//(c) W-Shadow /** @namespace wsEditorData */ var wsIdCounter = 0; (function ($){ /* * Utility function for generating pseudo-random alphanumeric menu IDs. * Rationale: Simpler than atomically auto-incrementing or globally unique IDs. */ function randomMenuId(size){ if ( typeof size == 'undefined' ){ size = 5; } var text = ""; var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; for( var i=0; i < size; i++ ) text += possible.charAt(Math.floor(Math.random() * possible.length)); return text; } function outputWpMenu(menu){ //Remove the current menu data $('#ws_menu_box').empty(); $('#ws_submenu_box').empty(); //Kill autocomplete boxes $('.ac_results').remove(); //Display the new menu var i = 0; for (var filename in menu){ outputTopMenu(menu[filename]); i++; } //Automatically select the first top-level menu $('#ws_menu_box .ws_menu:first').click(); } /* * 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 a container for menu items, even if there are none var submenu = buildSubmenu(menu.items); //Create the menu widget var menu_obj = buildTopMenu(menu); menu_obj.data('submenu_id', submenu.attr('id')); //Display submenu.appendTo('#ws_submenu_box'); if ( typeof afterNode != 'undefined' ){ $(afterNode).after(menu_obj); } else { menu_obj.appendTo('#ws_menu_box'); } return { 'menu' : menu_obj, 'submenu' : submenu }; } /* * Create an edit widget for a top-level menu. */ function buildTopMenu(menu){ var subclass = ''; if ( menu.separator ) { subclass = subclass + ' ws_menu_separator'; } //Create the menu HTML var menu_obj = $('<div></div>') .attr('class', "ws_container ws_menu "+subclass) .attr('id', 'ws-topmenu-'+(wsIdCounter++)) .data('defaults', menu.defaults) .data('initial_values', getCurrentFieldValues(menu)) .data('field_editors_created', false); //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 = []; contents.push( '<div class="ws_item_head">', menu.separator ? '' : '<a class="ws_edit_link"> </a><div class="ws_flag_container"> </div>', '<span class="ws_item_title">', ((menu.menu_title!=null) ? menu.menu_title : menu.defaults.menu_title), ' </span>', '</div>', '<div class="ws_editbox" style="display: none;"></div>' ); menu_obj.append(contents.join('')); //Apply flags based on the item's state if (menu.missing && !getFieldValue(menu, 'custom', false)) { addMenuFlag(menu_obj, 'missing'); } if (menu.hidden) { addMenuFlag(menu_obj, 'hidden'); } if (menu.unused) { addMenuFlag(menu_obj, 'unused'); } if (getFieldValue(menu, 'custom', false)) { addMenuFlag(menu_obj, 'custom_item'); } if ( !menu.separator ){ //Allow the user to drag menu items to top-level menus menu_obj.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); var submenu = $('#' + menu_obj.data('submenu_id')); submenu.append(new_item); ui.draggable.remove(); }) }); } return menu_obj; } /* * Create and populate a submenu container. */ function buildSubmenu(items){ //Create a container for menu items, even if there are none var submenu = $('<div class="ws_submenu"style="display:none;"></div>'); submenu.attr('id', 'ws-submenu-'+(wsIdCounter++)); //Only show menus that have items. //Skip arrays (with a length) because filled menus are encoded as custom objects. var entry = null; if (items && (typeof items != 'Array')){ for (var item_file in items){ entry = buildMenuItem(items[item_file]); if ( entry ){ submenu.append(entry); } } } //Make the submenu sortable makeBoxSortable(submenu); return submenu; } /* * Create an edit widget for a menu entry and return it. */ function buildMenuItem(entry){ if (!entry.defaults) { return null }; var item = $('<div class="ws_container ws_item">') .data('defaults', entry.defaults) .data('initial_values', getCurrentFieldValues(entry)) .data('field_editors_created', false); //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 = []; contents.push( '<div class="ws_item_head">', '<a class="ws_edit_link"> </a><div class="ws_flag_container"> </div>', '<span class="ws_item_title">', ((entry.menu_title!=null)?entry.menu_title:entry.defaults.menu_title), ' </span>', '</div>', '<div class="ws_editbox" style="display: none;"></div>' ); item.append(contents.join('')); //Apply flags based on the item's state if (entry.missing && !getFieldValue(entry, 'custom', false)) { addMenuFlag(item, 'missing'); } if (entry.hidden) { addMenuFlag(item, 'hidden'); } if (entry.unused) { addMenuFlag(item, 'unused'); } if (getFieldValue(entry, 'custom', false)) { addMenuFlag(item, 'custom_item'); } return item; } /* * List of all menu fields that have an associated editor */ var knownMenuFields = { 'menu_title' : { caption : 'Menu title', standardCaption : true, advanced : false, type : 'text', defaultValue: '', visible: true }, 'access_level' : { caption: 'Required capability', standardCaption : true, advanced : false, type : 'text', defaultValue: 'read', addDropdown : true, visible: true }, 'file' : { caption: 'URL', advanced : false, standardCaption : true, type : 'text', defaultValue: '', addDropdown : 'ws_page_selector', visible: true }, 'page_title' : { caption: "Window title", standardCaption : true, advanced : true, type : 'text', defaultValue: '', visible: true }, 'open_in' : { caption: 'Open in', standardCaption : true, advanced : true, type : 'select', options : { 'Same window or tab' : 'same_window', 'New window' : 'new_window', 'Frame' : 'iframe' }, defaultValue: 'same_window', visible: false }, 'css_class' : { caption: 'CSS classes', standardCaption : true, advanced : true, type : 'text', defaultValue: '', visible: true }, 'icon_url' : { caption: 'Icon URL', standardCaption : true, advanced : true, type : 'icon_selector', defaultValue: 'div', visible: true }, 'hookname' : { caption: 'Hook name', standardCaption : true, advanced : true, type : 'text', defaultValue: '', visible: true }, 'custom' : { caption : 'Custom', standardCaption : false, advanced: true, type: 'checkbox', defaultValue: false, visible: false } }; /* * Create editors for the visible fields of a menu entry and append them to the specified node. */ function buildEditboxFields(containerNode, entry){ var fields = knownMenuFields; var basicFields = $('<div class="ws_edit_panel ws_basic"></div>').appendTo(containerNode); var advancedFields = $('<div class="ws_edit_panel ws_advanced"></div>').appendTo(containerNode); if ( hideAdvancedSettings ){ advancedFields.css('display', 'none'); } for (var field_name in fields){ if (!fields.hasOwnProperty(field_name)) { continue; } var field = buildEditboxField(entry, field_name, fields[field_name]); if (field){ if (fields[field_name].advanced){ advancedFields.append(field); } else { basicFields.append(field); } if (field_name == 'icon_url') { updateIconField(field.find('.ws_field_value')); } } } //Add a link that shows/hides advanced fields containerNode.append( '<div class="ws_toggle_container"><a href="#" class="ws_toggle_advanced_fields"'+ (hideAdvancedSettings?'':' style="display:none;"')+'>'+ (hideAdvancedSettings?captionShowAdvanced:captionHideAdvanced) +'</a></div>' ); } /* * Create an editor for a specified field. */ function buildEditboxField(entry, field_name, field_settings){ if (typeof entry[field_name] === 'undefined') { return null; //skip fields this entry doesn't have } var default_value = (typeof entry.defaults[field_name] != 'undefined')?entry.defaults[field_name]:field_settings.defaultValue; var value = (entry[field_name]!=null)?entry[field_name]:default_value; //Build a form field of the appropriate type var inputBox = null; var basicTextField = '<input type="text" class="ws_field_value">'; switch(field_settings.type){ case 'select': inputBox = $('<select class="ws_field_value">'); var option = null; for( var optionTitle in field_settings.options ){ option = $('<option>') .val(field_settings.options[optionTitle]) .text(optionTitle); if ( field_settings.options[optionTitle] == value ){ option.prop('selected', 'selected'); } option.appendTo(inputBox); } break; case 'checkbox': inputBox = $('<label><input type="checkbox"'+(value?' checked="checked"':'')+ ' class="ws_field_value"> '+ field_settings.caption+'</label>' ); break; case 'icon_selector': inputBox = $(basicTextField).val(value) .add('<button class="button ws_select_icon" title="Select icon"><div class="icon16 icon-settings"></div><img src="" style="display:none;"></button>'); break; case 'text': default: inputBox = $(basicTextField).val(value); } var className = "ws_edit_field ws_edit_field-"+field_name; if(entry[field_name]==null){ className += ' ws_input_default'; } var hasDropdown = (typeof(field_settings['addDropdown']) != 'undefined') && field_settings.addDropdown; if ( hasDropdown ){ className += ' ws_has_dropdown'; } var editField = $('<div>' + (field_settings.standardCaption?(field_settings.caption+'<br>'):'') + '</div>') .attr('class', className) .append(inputBox); if ( hasDropdown ){ //Add a dropdown button var dropdownId = 'ws_cap_selector'; if ( typeof(field_settings.addDropdown) == 'string' ){ dropdownId = field_settings.addDropdown; } editField.append( $('<input type="button" value="▼">') .addClass('button ws_dropdown_button') .attr('tabindex', '-1') .data('dropdownId', dropdownId) ); } editField .append('<img src="'+imagesUrl+'/transparent16.png" class="ws_reset_button" title="Reset to default value"> </img>') .data('field_name', field_name) .data('default_value', default_value); if ( !field_settings.visible ){ editField.css('display', 'none'); } return editField; } function updateIconField(input) { //Display the current icon in the selector. var container = input.closest('.ws_container'); var cssClass = container.find('.ws_edit_field-css_class .ws_field_value').val(); var iconUrl = input.val(); 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(/\bmenu-icon-([^\s]+)\b/); //Icon URL take precedence over icon class. if ( iconUrl && iconUrl !== 'none' && iconUrl !== 'div' ) { cssIcon.hide(); imageIcon.prop('src', iconUrl).show(); } else if ( matches ) { imageIcon.hide(); cssIcon.removeClass().addClass('icon16 icon-' + matches[1]).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(); } } /* * Get the current values of all menu fields (ignores defaults). * Returns an object containing each field as a separate property. */ function getCurrentFieldValues(entry){ var values = {}; for (var field_name in knownMenuFields){ if (typeof entry[field_name] === 'undefined') { continue; //skip fields this entry doesn't have } values[field_name] = entry[field_name]; } values.defaults = entry.defaults; return values; } /* * Get the current value of a single menu field. * * If the specified field is not set, this function will attempt to retrieve it * from the "defaults" property of the menu object. If *that* fails, it will return * the value of the optional third argument defaultValue. */ function getFieldValue(entry, fieldName, defaultValue){ if ( (typeof entry[fieldName] === 'undefined') || (entry[fieldName] === null) ) { if ( (typeof entry['defaults'] === 'undefined') || (typeof entry['defaults'][fieldName] === 'undefined') ){ return defaultValue; } else { return entry.defaults[fieldName]; } } else { return entry[fieldName]; } } /* * Make a menu container sortable */ function makeBoxSortable(menuBox){ //Make the submenu sortable menuBox.sortable({ items: '> .ws_container', cursor: 'move', dropOnEmpty: true, cancel : '.ws_editbox, .ws_edit_link' }); } /*************************************************************************** Parsing & encoding menu inputs ***************************************************************************/ /* * Encode the current menu structure as JSON * * Returns : * A JSON-encoded string representing the current menu tree loaded in the editor. */ function encodeMenuAsJSON(){ var tree = readMenuTreeState(); return $.toJSON(tree); } function readMenuTreeState(){ var tree = {}; var menu_position = 0; //Gather all menus and their items $('#ws_menu_box .ws_menu').each(function(i) { var menu = readMenuState(this, menu_position++); //Attach the current menu to the main struct var filename = (menu.file !== null)?menu.file:menu.defaults.file; tree[filename] = menu; }); return tree; } /* * Extract the settings of a top-level menu from its editor widget(s). * * Inputs : * menu_div - DOM node, typically one with the .ws_menu class. * position - The current menu position (int). * * Output : * A menu object in the tree format. * */ function readMenuState(menu_div, position){ menu_div = $(menu_div); var menu = readAllFields(menu_div); menu.defaults = menu_div.data('defaults'); menu.position = position; menu.defaults.position = position; //the real default value will later overwrite this menu.separator = menu_div.hasClass('ws_menu_separator'); menu.hidden = menu_div.hasClass('ws_hidden'); //Gather the menu's items, if any menu.items = {}; var item_position = 0; $('#'+menu_div.data('submenu_id')).find('.ws_item').each(function (i) { var item = readItemState(this, item_position++); menu.items[ (item.file?item.file:item.defaults.file) ] = item; }); return menu; } /* * Extract the current menu item settings from its editor widget. * * Inputs : * item_div - DOM node containing the editor widget, usually with the .ws_item class. * position - Menu item position among its sibling menu items. * */ function readItemState(item_div, position){ var item_div = $(item_div); var item = readAllFields(item_div); item.defaults = item_div.data('defaults'); //Save the position data if ( typeof position == 'undefined' ){ position = 0; } item.position = position; item.defaults.position = position; //Check if the item is hidden item.hidden = item_div.hasClass('ws_hidden'); return item; } /* * Extract the values of all menu/item fields present in a container node * * Inputs: * container - a jQuery collection representing the node to read. */ function readAllFields(container){ if ( !container.hasClass('ws_container') ){ container = container.parents('ws_container').first(); } if ( !container.data('field_editors_created') ){ return container.data('initial_values'); } var state = {}; //Iterate over all fields of the item container.find('.ws_edit_field').each(function() { var field = $(this); //Get the name of this field field_name = field.data('field_name'); //Skip if unnamed if (!field_name) return true; //Find the field (usually an input or select element). input_box = field.find('.ws_field_value'); //Save null if default used, custom value otherwise if (field.hasClass('ws_input_default')){ state[field_name] = null; } else { if ( input_box.attr('type') == 'checkbox' ){ state[field_name] = input_box.is(':checked'); } else { state[field_name] = input_box.val(); } } }); return state; } /*************************************************************************** Flag manipulation ***************************************************************************/ var item_flags = { 'custom_item' : 'This is a custom menu item', 'unused' : 'This item was automatically (re)inserted into your custom menu because it is present in the default WordPress menu', 'missing' : 'This item is not present in the default WordPress menu.', 'hidden' : 'This item is hidden' } function addMenuFlag(item, flag){ item = $(item); var item_class = 'ws_' + flag; var img_class = 'ws_' + flag + '_flag'; item.addClass(item_class); //Add the flag image var flag_container = item.find('.ws_flag_container'); if ( flag_container.find('.' + img_class).length == 0 ){ flag_container.append('<div class="ws_flag '+img_class+'" title="'+item_flags[flag]+'"></div>'); } } function removeMenuFlag(item, flag){ item = $(item); var item_class = 'ws_' + flag; var img_class = 'ws_' + flag + '_flag'; item.removeClass('ws_' + flag); item.find('.' + img_class).remove(); } function toggleMenuFlag(item, flag){ if (menuHasFlag(item, flag)){ removeMenuFlag(item, flag); } else { addMenuFlag(item, flag); } } function menuHasFlag(item, flag){ return $(item).hasClass('ws_'+flag); } function clearMenuFlags(item){ item = $(item); item.find('.ws_flag').remove(); for(var flag in item_flags){ item.removeClass('ws_'+flag); } } //Cut & paste stuff var menu_in_clipboard = null; var submenu_in_clipboard = null; var item_in_clipboard = null; var ws_paste_count = 0; $(document).ready(function(){ if (window.wsMenuEditorPro) { knownMenuFields['open_in'].visible = true; } //Make the top menu box sortable (we only need to do this once) var mainMenuBox = $('#ws_menu_box'); makeBoxSortable(mainMenuBox); /*************************************************************************** Event handlers for editor widgets ***************************************************************************/ var menuEditorNode = $('#ws_menu_editor'); //Highlight the clicked menu item and show it's submenu var currentVisibleSubmenu = null; $('#ws_menu_editor .ws_container').live('click', (function () { var container = $(this); if ( container.hasClass('ws_active') ){ return; } //Highlight the active item and un-highlight the previous one container.addClass('ws_active') container.siblings('.ws_active').removeClass('ws_active'); if ( container.hasClass('ws_menu') ){ //Show/hide the appropriate submenu if ( currentVisibleSubmenu ){ currentVisibleSubmenu.hide(); } currentVisibleSubmenu = $('#'+container.data('submenu_id')).show(); } })); //Show/hide a menu's properties $('#ws_menu_editor .ws_edit_link').live('click', (function () { var container = $(this).parents('.ws_container').first(); var box = container.find('.ws_editbox'); //For performance, the property editors for each menu are only created //when the user tries to access access them for the first time. if ( !container.data('field_editors_created') ){ buildEditboxFields(box, container.data('initial_values')); container.data('field_editors_created', true); } $(this).toggleClass('ws_edit_link_expanded'); //show/hide the editbox if ($(this).hasClass('ws_edit_link_expanded')){ box.show(); } else { //Make sure changes are applied before the menu is collapsed box.find('input').change(); box.hide(); } })); //The "Default" button : Reset to default value when clicked $('#ws_menu_editor .ws_reset_button').live('click', (function () { //Find the field div (it holds the default value) var field = $(this).parent(); //Find the related input field var input = field.find('.ws_field_value'); if ( (input.length > 0) && (field.length > 0) ) { //Set the value to the default if (input.attr('type') == 'checkbox'){ if ( field.data('default_value') ){ input.attr('checked', 'checked'); } else { input.removeAttr('checked'); } } else { input.val(field.data('default_value')); } field.addClass('ws_input_default'); //Trigger the change event to ensure consistency input.change(); } })); //When a field is edited, change it's appearance if it's contents don't match the default value. function fieldValueChange(){ var input = $(this); var field = input.parents('.ws_edit_field').first(); if ( input.attr('type') == 'checkbox' ){ var value = input.is(':checked'); } else { var value = input.val(); } if ( field.data('default_value') != value ) { field.removeClass('ws_input_default'); } var fieldName = field.data('field_name'); if ( fieldName == 'menu_title' ){ //If the changed field is the menu title, update the header field.parents('.ws_container').first().find('.ws_item_title').html(input.val()+' '); } else if ( fieldName == 'custom' ){ //Show/hide the custom flag var myContainer = field.parents('.ws_container').first(); if ( value ){ addMenuFlag(myContainer, 'custom_item'); } else { removeMenuFlag(myContainer, 'custom_item'); } } else if (fieldName == 'file' ){ //A menu must always have a non-empty URL. If the user deletes the current value, //reset back to the default. if ( value == '' ){ field.find('.ws_reset_button').click(); } } else if (fieldName == 'icon_url') { updateIconField(input); } else if (fieldName == 'css_class') { updateIconField(input.closest('.ws_container').find('.ws_edit_field-icon_url .ws_field_value')); } } menuEditorNode.on('click change', '.ws_field_value', fieldValueChange); //Show/hide advanced fields $('#ws_menu_editor .ws_toggle_advanced_fields').live('click', function(){ var self = $(this); var advancedFields = self.parents('.ws_container').first().find('.ws_advanced'); if ( advancedFields.is(':visible') ){ advancedFields.hide(); self.text(captionShowAdvanced); } else { advancedFields.show(); self.text(captionHideAdvanced); } return false; }); /*************************************************************************** Dropdown list for combobox fields ***************************************************************************/ var availableDropdowns = { 'ws_cap_selector' : { list : $('#ws_cap_selector'), currentOwner : null, timeoutForgetOwner : 0 }, 'ws_page_selector' : { list : $('#ws_page_selector'), currentOwner : null, timeoutForgetOwner : 0 } }; //Show/hide the capability dropdown list when the button is clicked $('#ws_menu_editor input.ws_dropdown_button').live('click',function(event){ var button = $(this); var inputBox = button.parent().find('input.ws_field_value'); var dropdown = availableDropdowns[button.data('dropdownId')]; clearTimeout(dropdown.timeoutForgetOwner); dropdown.timeoutForgetOwner = 0; //If we already own the list, hide it and rescind ownership. if ( dropdown.currentOwner == this ){ dropdown.list.hide(); dropdown.currentOwner = null; inputBox.focus(); return; } dropdown.currentOwner = this; //Got ye now! //Pre-select the current capability (will clear selection if there's no match) dropdown.list.val(inputBox.val()); //Show the list before moving it into place. This ensures the position will be calc. properly. dropdown.list.show(); //Move the dropdown near to the button var inputPos = inputBox.offset(); dropdown.list .css({ position: 'absolute' }) .offset({ left: inputPos.left, top: inputPos.top + inputBox.outerHeight() }); dropdown.list.focus(); }); //Also show it when the user presses the down arrow in the input field $('#ws_menu_editor .ws_has_dropdown input.ws_field_value').live('keyup', function(event){ if ( event.which == 40 ){ $(this).parent().find('input.ws_dropdown_button').click(); } }); //Event handlers for the dropdowns themselves var dropdownNodes = $('.ws_dropdown'); //Hide capability dropdown when it loses focus dropdownNodes.blur(function(event){ var dropdown = availableDropdowns[$(this).attr('id')]; dropdown.list.hide(); /* * Hackiness : make sure the list doesn't disappear & immediately reappear * when the event that caused it to lose focus was the user clicking on the * dropdown button. */ dropdown.timeoutForgetOwner = setTimeout( (function(){ dropdown.currentOwner = null; }), 200 ); }); dropdownNodes.keydown(function(event){ var dropdown = availableDropdowns[$(this).attr('id')]; //Also hide it when the user presses Esc if ( event.which == 27 ){ var inputBox = $(dropdown.currentOwner).parent().find('input.ws_field_value'); dropdown.list.hide(); if ( dropdown.currentOwner ){ $(dropdown.currentOwner).parent().find('input.ws_field_value').focus(); } dropdown.currentOwner = null; //Select an item & hide the list when the user presses Enter or Tab } else if ( (event.which == 13) || (event.which == 9) ){ dropdown.list.hide(); var inputBox = $(dropdown.currentOwner).parent().find('input.ws_field_value'); if ( dropdown.list.val() ){ inputBox.val(dropdown.list.val()); inputBox.change(); } inputBox.focus(); dropdown.currentOwner = null; event.preventDefault(); } }); //Eat Tab keys to prevent focus theft. Required to make the "select item on Tab" thing work. dropdownNodes.keyup(function(event){ if ( event.which == 9 ){ event.preventDefault(); } }) //Update the input & hide the list when an option is clicked dropdownNodes.click(function(){ var dropdown = availableDropdowns[$(this).attr('id')]; if ( !dropdown.currentOwner || !dropdown.list.val() ){ return; } dropdown.list.hide(); var inputBox = $(dropdown.currentOwner).parent().find('input.ws_field_value'); inputBox.val(dropdown.list.val()).change().focus(); dropdown.currentOwner = null; }); //Highlight an option when the user mouses over it (doesn't work in IE) dropdownNodes.mousemove(function(event){ if ( !event.target ){ return; } var option = $(event.target); if ( !option.attr('selected') && option.attr('value')){ option.attr('selected', 'selected'); } }); /************************************************************************* Icon selector *************************************************************************/ var iconSelector = $('#ws_icon_selector'); var currentIconButton = null; //Keep track of the last clicked icon button. //When the user clicks one of the available icons, update the menu item. iconSelector.on('click', '.ws_icon_option', function() { var selectedIcon = $(this).addClass('ws_selected_icon'); iconSelector.hide(); //Assign the selected icon to the menu. if (currentIconButton) { var container = currentIconButton.closest('.ws_container'); var cssClassField = container.find('.ws_edit_field-css_class .ws_field_value'); var iconUrlField = container.find('.ws_edit_field-icon_url .ws_field_value'); //Remove the existing icon class, if any. var cssClass = cssClassField.val(); cssClass = jsTrim( cssClass.replace(/\bmenu-icon-[^\s]+\b/, '') ); if (selectedIcon.data('icon-class')) { //Add the new class. cssClass = selectedIcon.data('icon-class') + ' ' + cssClass; //Can't have both a class and an image or we'll get two overlapping icons. iconUrlField.val(''); } else if (selectedIcon.data('icon-url')) { iconUrlField.val(selectedIcon.data('icon-url')); } cssClassField.val(cssClass).change(); iconUrlField.change(); } currentIconButton = null; }); //Show/hide the icon selector when the user clicks the icon button. menuEditorNode.on('click', '.ws_select_icon', function() { var button = $(this); //Clicking the same button a second time hides the icon list. if ( currentIconButton && button.is(currentIconButton) ) { iconSelector.hide(); //noinspection JSUnusedAssignment currentIconButton = null; return; } currentIconButton = button; var container = currentIconButton.closest('.ws_container'); var cssClassField = container.find('.ws_edit_field-css_class .ws_field_value'); var iconUrlField = container.find('.ws_edit_field-icon_url .ws_field_value'); var cssClass = cssClassField.val(); var iconUrl = iconUrlField.val(); var customImageOption = iconSelector.find('.ws_custom_image_icon').hide(); //Highlight the currently selected icon. iconSelector.find('.ws_selected_icon').removeClass('ws_selected_icon'); var matches = cssClass.match(/\bmenu-icon-([^\s]+)\b/); if ( iconUrl && iconUrl !== 'none' && iconUrl !== 'div' ) { var currentIcon = iconSelector.find('.ws_icon_option img[src="' + iconUrl + '"]').first().closest('.ws_icon_option'); if ( currentIcon.length > 0 ) { currentIcon.addClass('ws_selected_icon').show(); } else { //Display and highlight the custom image. customImageOption.find('img').prop('src', iconUrl); customImageOption.addClass('ws_selected_icon').show().data('icon-url', iconUrl); } } else if ( matches ) { //Highlight the icon that corresponds to the current CSS class. iconSelector.find('.icon-' + matches[1]).closest('.ws_icon_option').addClass('ws_selected_icon'); } iconSelector.show(); iconSelector.position({ //Requires jQuery UI. my: 'right top', at: 'right bottom', of: button }); }); //Alternatively, use the WordPress media uploader to select a custom icon. //This code is based on the header selection script in /wp-admin/js/custom-header.js. $('#ws_choose_icon_from_media').click(function(event) { event.preventDefault(); var frame = null; //This option is not usable on the demo site since the filesystem is usually read-only. if (wsEditorData.isDemoMode) { alert('Sorry, image upload is disabled in demo mode!'); return; } //If the media frame already exists, reopen it. if ( frame ) { frame.open(); return; } //Create a custom media frame. frame = wp.media.frames.customAdminMenuIcon = wp.media({ //Set the title of the modal. title: 'Choose a Custom Icon (16x16)', //Tell it to show only images. library: { type: 'image' }, //Customize the submit button. button: { text: 'Set as icon', //Button text. close: true //Clicking the button closes the frame. } }); //When an image is selected, set it as the menu icon. frame.on( 'select', function() { //Grab the selected attachment. var attachment = frame.state().get('selection').first(); //TODO: Warn the user if the image exceeds 16x16 pixels. //Set the menu icon to the attachment URL. if (currentIconButton) { var container = currentIconButton.closest('.ws_container'); var cssClassField = container.find('.ws_edit_field-css_class .ws_field_value'); var iconUrlField = container.find('.ws_edit_field-icon_url .ws_field_value'); var cssClass = cssClassField.val(); var iconUrl = iconUrlField.val(); //Remove the existing icon class, if any. cssClass = jsTrim( cssClass.replace(/\bmenu-icon-[^\s]+\b/, '') ); cssClassField.val(cssClass); //Set the new icon URL. iconUrlField.val(attachment.attributes.url); cssClassField.change(); iconUrlField.change(); } currentIconButton = null; }); //If the user closes the frame by via Esc or the "X" button, clear up state. frame.on('escape', function(){ currentIconButton = null; }); frame.open(); iconSelector.hide(); }); //Hide the icon selector if the user clicks outside of it. //Exception: Clicks on "Select icon" buttons are handled above. $(document).on('mouseup', function(event) { if ( !iconSelector.is(':visible') ) { return; } if ( !iconSelector.is(event.target) && iconSelector.has(event.target).length === 0 && $(event.target).closest('.ws_select_icon').length == 0 ) { iconSelector.hide(); currentIconButton = null; } }); /************************************************************************* Menu toolbar buttons *************************************************************************/ //Show/Hide menu $('#ws_hide_menu').click(function () { //Get the selected menu var selection = $('#ws_menu_box .ws_active'); if (!selection.length) return; //Mark the menu as hidden/visible //selection.toggleClass('ws_hidden'); toggleMenuFlag(selection, 'hidden'); //Also mark all of it's submenus as hidden/visible if ( menuHasFlag(selection,'hidden') ){ $('#' + selection.data('submenu_id') + ' .ws_item').each(function(){ addMenuFlag(this, 'hidden'); }); } else { $('#' + selection.data('submenu_id') + ' .ws_item').each(function(){ removeMenuFlag(this, 'hidden'); }); } }); //Delete menu $('#ws_delete_menu').click(function () { //Get the selected menu var selection = $('#ws_menu_box .ws_active'); if (!selection.length) return; if (confirm('Delete this menu?')){ //Delete the submenu first $('#' + selection.data('submenu_id')).remove(); //Delete the menu selection.remove(); } }); //Copy menu $('#ws_copy_menu').click(function () { //Get the selected menu var selection = $('#ws_menu_box .ws_active'); if (!selection.length) return; //Store a copy of the current menu state in clipboard menu_in_clipboard = readMenuState(selection); }); //Cut menu $('#ws_cut_menu').click(function () { //Get the selected menu var selection = $('#ws_menu_box .ws_active'); if (!selection.length) return; //Store a copy of the current menu state in clipboard menu_in_clipboard = readMenuState(selection); //Remove the original menu and submenu selection.remove(); $('#'+selection.data('submenu_id')).remove(); }); //Paste menu $('#ws_paste_menu').click(function () { //Check if anything has been copied/cut if (!menu_in_clipboard) return; var menu = $.extend(true, {}, menu_in_clipboard); //The user shouldn't need to worry about giving separators a unique filename. if (menu.separator) { menu.defaults.file = 'separator_'+randomMenuId(); } //Get the selected menu var selection = $('#ws_menu_box .ws_active'); if (selection.length > 0) { //If a menu is selected add the pasted item after it outputTopMenu(menu, selection); } else { //Otherwise add the pasted item at the end outputTopMenu(menu); } }); //New menu $('#ws_new_menu').click(function () { ws_paste_count++; //The new menu starts out rather bare var randomId = 'custom_menu_'+randomMenuId(); var menu = { menu_title : null, page_title : null, access_level : null, file : null, css_class : null, icon_url : null, hookname : null, position : null, custom : null, open_in: null, items : {}, //No items defaults : { menu_title : 'Custom Menu '+ws_paste_count, page_title : '', access_level : 'read', file : randomId, css_class : 'menu-top', icon_url : 'images/generic.png', hookname : randomId, open_in : 'same_window', custom: true, //Important : flag the new menu as custom, or it won't show up after saving. position : 0, separator: false } }; //Insert the new menu result = outputTopMenu(menu); //The menus's editbox is always open result.menu.find('.ws_edit_link').click(); }); //New separator $('#ws_new_separator').click(function () { ws_paste_count++; //The new menu starts out rather bare var randomId = 'separator_'+randomMenuId(); var menu = { menu_title : null, page_title : null, access_level : null, file : null, css_class : null, icon_url : null, hookname : null, position : null, separator : true, //Flag as a separator custom : null, open_in : null, items : {}, //No items defaults : { menu_title : '', page_title : '', access_level : 'read', file : randomId, css_class : 'wp-menu-separator', icon_url : '', hookname : '', position : 0, custom: false, //Separators don't need to flagged as custom to be retained. open_in: 'same_window', separator: true } }; //Insert the new menu result = outputTopMenu(menu); }); /************************************************************************* Item toolbar buttons *************************************************************************/ //Show/Hide item $('#ws_hide_item').click(function () { //Get the selected item var selection = $('#ws_submenu_box .ws_submenu:visible .ws_active'); if (!selection.length) return; //Mark the item as hidden/visible toggleMenuFlag(selection, 'hidden'); }); //Delete menu $('#ws_delete_item').click(function () { //Get the selected menu var selection = $('#ws_submenu_box .ws_submenu:visible .ws_active'); if (!selection.length) return; if (confirm('Delete this menu item?')){ //Delete the item selection.remove(); } }); //Copy item $('#ws_copy_item').click(function () { //Get the selected item var selection = $('#ws_submenu_box .ws_submenu:visible .ws_active'); if (!selection.length) return; //Store a copy of item state in the clipboard item_in_clipboard = readItemState(selection); }); //Cut item $('#ws_cut_item').click(function () { //Get the selected item var selection = $('#ws_submenu_box .ws_submenu:visible .ws_active'); if (!selection.length) return; //Store a copy of item state in the clipboard item_in_clipboard = readItemState(selection); //Remove the original item selection.remove(); }); //Paste item $('#ws_paste_item').click(function () { //Check if anything has been copied/cut if (!item_in_clipboard) return; //Create a new editor widget for the copied item var item = $.extend(true, {}, item_in_clipboard); var new_item = buildMenuItem(item); //Get the selected menu var selection = $('#ws_submenu_box .ws_submenu:visible .ws_active'); if (selection.length > 0) { //If an item is selected add the pasted item after it selection.after(new_item); } else { //Otherwise add the pasted item at the end $('#ws_submenu_box .ws_submenu:visible').append(new_item); } new_item.show(); }); //New item $('#ws_new_item').click(function () { if ($('.ws_submenu:visible').length < 1) { return; //Abort if no submenu visible } ws_paste_count++; var entry = { menu_title : null, page_title : null, access_level : null, file : null, custom : null, open_in : null, defaults : { menu_title : 'Custom Item ' + ws_paste_count, page_title : '', access_level : 'read', file : 'custom_item_'+randomMenuId(), is_plugin_page : false, custom: true, open_in : 'same_window' } }; var menu = buildMenuItem(entry); //Insert the item into the box $('#ws_submenu_box .ws_submenu:visible').append(menu); //The items's editbox is always open menu.find('.ws_edit_link').click(); }); function jsTrim(str){ return str.replace(/^\s+|\s+$/g, ""); } function compareMenus(a, b){ function jsTrim(str){ return str.replace(/^\s+|\s+$/g, ""); } var aTitle = jsTrim( $(a).find('.ws_item_title').text() ); var bTitle = jsTrim( $(b).find('.ws_item_title').text() ); aTitle = aTitle.toLowerCase(); bTitle = bTitle.toLowerCase(); return aTitle > bTitle ? 1 : -1; } //Sort items in ascending order $('#ws_sort_ascending').click(function () { var submenu = $('#ws_submenu_box .ws_submenu:visible'); if (submenu.length < 1) { return; //Abort if no submenu visible } submenu.find('.ws_container').sort(compareMenus); }); //Sort items in descending order $('#ws_sort_descending').click(function () { var submenu = $('#ws_submenu_box .ws_submenu:visible'); if (submenu.length < 1) { return; //Abort if no submenu visible } submenu.find('.ws_container').sort((function(a, b){ return -compareMenus(a, b); })); }); //============================================== // Main buttons //============================================== //Save Changes - encode the current menu as JSON and save $('#ws_save_menu').click(function () { var data = encodeMenuAsJSON(); $('#ws_data').val(data); $('#ws_main_form').submit(); }); //Load default menu - load the default WordPress menu $('#ws_load_menu').click(function () { if (confirm('Are you sure you want to load the default WordPress menu?')){ outputWpMenu(defaultMenu); } }); //Reset menu - re-load the custom menu. Discards any changes made by user. $('#ws_reset_menu').click(function () { if (confirm('Undo all changes made in the current editing session?')){ outputWpMenu(customMenu); } }); //Export menu - download the current menu as a file $('#export_dialog').dialog({ autoOpen: false, closeText: ' ', modal: true, minHeight: 100 }); $('#ws_export_menu').click(function(){ var button = $(this); button.attr('disabled', 'disabled'); button.val('Exporting...'); $('#export_complete_notice, #download_menu_button').hide(); $('#export_progress_notice').show(); $('#export_dialog').dialog('open'); //Encode and store the menu for download var menu = readMenuTreeState(); var exportData = { 'format' : exportFormatString, 'menu' : menu }; exportData = $.toJSON(exportData); $.post( adminAjaxUrl, { 'data' : exportData, 'action' : 'export_custom_menu', '_ajax_nonce' : exportMenuNonce }, function(data, textStatus){ button.val('Export'); button.removeAttr('disabled'); if ( typeof data['error'] != 'undefined' ){ $('#export_dialog').dialog('close'); alert(data.error); } if ( (typeof data['download_url'] != 'undefined') && data.download_url ){ //window.location = data.download_url; $('#download_menu_button').attr('href', data.download_url); $('#export_progress_notice').hide(); $('#export_complete_notice, #download_menu_button').show(); } }, 'json' ); }); $('#ws_cancel_export').click(function(){ $('#export_dialog').dialog('close'); }); $('#download_menu_button').click(function(){ $('#export_dialog').dialog('close'); }); //Import menu - upload an exported menu and show it in the editor $('#import_dialog').dialog({ autoOpen: false, closeText: ' ', modal: true }); $('#ws_cancel_import').click(function(){ $('#import_dialog').dialog('close'); }); $('#ws_import_menu').click(function(){ $('#import_progress_notice, #import_progress_notice2, #import_complete_notice').hide(); $('#import_menu_form').resetForm(); //The "Upload" button is disabled until the user selects a file $('#ws_start_import').attr('disabled', 'disabled'); $('#import_dialog .hide-when-uploading').show(); $('#import_dialog').dialog('open'); }) $('#import_file_selector').change(function(){ if ( $(this).val() ){ $('#ws_start_import').removeAttr('disabled'); } else { $('#ws_start_import').attr('disabled', 'disabled'); }; }); //AJAXify the upload form $('#import_menu_form').ajaxForm({ dataType : 'json', beforeSubmit: function(formData, $form, options) { //Check if the user has selected a file for(var i = 0; i < formData.length; i++){ if ( formData[i].name == 'menu' ){ if ( (typeof formData[i]['value'] == 'undefined') || !formData[i]['value']){ alert('Select a file first!'); return false; } } } $('#import_dialog .hide-when-uploading').hide(); $('#import_progress_notice').show(); $('#ws_start_import').attr('disabled', 'disabled'); }, success: function(data){ if ( !$('#import_dialog').dialog('isOpen') ){ //Whoops, the user closed the dialog while the upload was in progress. //Discard the response silently. return; }; if ( typeof data['error'] != 'undefined' ){ alert(data.error); //Let the user try again $('#import_menu_form').resetForm(); $('#import_dialog .hide-when-uploading').show(); } $('#import_progress_notice').hide(); if ( (typeof data['menu'] != 'undefined') && data.menu ){ //Whee, we got back a (seemingly) valid menu. A veritable miracle! //Lets load it into the editor. $('#import_progress_notice2').show(); outputWpMenu(data.menu); $('#import_progress_notice2').hide(); //Display a success notice, then automatically close the window after a few moments $('#import_complete_notice').show(); setTimeout((function(){ //Close the import dialog $('#import_dialog').dialog('close'); }), 500); } } }); //Flag closed hints as hidden by sending the appropriate AJAX request to the backend. $('.ws_hint_close').click(function() { var hint = $(this).parents('.ws_hint').first(); hint.hide(); wsEditorData.showHints[hint.attr('id')] = false; $.post( wsEditorData.adminAjaxUrl, { 'action' : 'ws_ame_hide_hint', 'hint' : hint.attr('id') } ); }); //Finally, show the menu outputWpMenu(customMenu); }); })(jQuery); //============================================== // Screen options //============================================== jQuery(function($){ var screenOptions = $('#ws-ame-screen-meta-contents'); var checkbox = screenOptions.find('#ws-hide-advanced-settings'); if ( hideAdvancedSettings ){ checkbox.attr('checked', 'checked'); } else { checkbox.removeAttr('checked'); } //Update editor state when settings change checkbox.click(function(){ hideAdvancedSettings = $(this).attr('checked'); //Using '$(this)' instead of 'checkbox' due to jQuery bugs if ( hideAdvancedSettings ){ $('#ws_menu_editor div.ws_advanced').hide(); $('#ws_menu_editor a.ws_toggle_advanced_fields').text(captionShowAdvanced).show(); } else { $('#ws_menu_editor div.ws_advanced').show(); $('#ws_menu_editor a.ws_toggle_advanced_fields').text(captionHideAdvanced).hide(); } $.post( adminAjaxUrl, { 'action' : 'ws_ame_save_screen_options', 'hide_advanced_settings' : hideAdvancedSettings?1:0, '_ajax_nonce' : hideAdvancedSettingsNonce } ); }); //Move our options into the screen meta panel $('#adv-settings').empty().append(screenOptions.show()); });