home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-admin / js / nav-menu.js < prev    next >
Encoding:
JavaScript  |  2017-08-03  |  41.3 KB  |  1,295 lines

  1. /**
  2.  * WordPress Administration Navigation Menu
  3.  * Interface JS functions
  4.  *
  5.  * @version 2.0.0
  6.  *
  7.  * @package WordPress
  8.  * @subpackage Administration
  9.  */
  10.  
  11. /* global menus, postboxes, columns, isRtl, navMenuL10n, ajaxurl */
  12.  
  13. var wpNavMenu;
  14.  
  15. (function($) {
  16.  
  17.     var api;
  18.  
  19.     api = wpNavMenu = {
  20.  
  21.         options : {
  22.             menuItemDepthPerLevel : 30, // Do not use directly. Use depthToPx and pxToDepth instead.
  23.             globalMaxDepth:  11,
  24.             sortableItems:   '> *',
  25.             targetTolerance: 0
  26.         },
  27.  
  28.         menuList : undefined,    // Set in init.
  29.         targetList : undefined, // Set in init.
  30.         menusChanged : false,
  31.         isRTL: !! ( 'undefined' != typeof isRtl && isRtl ),
  32.         negateIfRTL: ( 'undefined' != typeof isRtl && isRtl ) ? -1 : 1,
  33.         lastSearch: '',
  34.  
  35.         // Functions that run on init.
  36.         init : function() {
  37.             api.menuList = $('#menu-to-edit');
  38.             api.targetList = api.menuList;
  39.  
  40.             this.jQueryExtensions();
  41.  
  42.             this.attachMenuEditListeners();
  43.  
  44.             this.attachQuickSearchListeners();
  45.             this.attachThemeLocationsListeners();
  46.             this.attachMenuSaveSubmitListeners();
  47.  
  48.             this.attachTabsPanelListeners();
  49.  
  50.             this.attachUnsavedChangesListener();
  51.  
  52.             if ( api.menuList.length )
  53.                 this.initSortables();
  54.  
  55.             if ( menus.oneThemeLocationNoMenus )
  56.                 $( '#posttype-page' ).addSelectedToMenu( api.addMenuItemToBottom );
  57.  
  58.             this.initManageLocations();
  59.  
  60.             this.initAccessibility();
  61.  
  62.             this.initToggles();
  63.  
  64.             this.initPreviewing();
  65.         },
  66.  
  67.         jQueryExtensions : function() {
  68.             // jQuery extensions
  69.             $.fn.extend({
  70.                 menuItemDepth : function() {
  71.                     var margin = api.isRTL ? this.eq(0).css('margin-right') : this.eq(0).css('margin-left');
  72.                     return api.pxToDepth( margin && -1 != margin.indexOf('px') ? margin.slice(0, -2) : 0 );
  73.                 },
  74.                 updateDepthClass : function(current, prev) {
  75.                     return this.each(function(){
  76.                         var t = $(this);
  77.                         prev = prev || t.menuItemDepth();
  78.                         $(this).removeClass('menu-item-depth-'+ prev )
  79.                             .addClass('menu-item-depth-'+ current );
  80.                     });
  81.                 },
  82.                 shiftDepthClass : function(change) {
  83.                     return this.each(function(){
  84.                         var t = $(this),
  85.                             depth = t.menuItemDepth(),
  86.                             newDepth = depth + change;
  87.  
  88.                         t.removeClass( 'menu-item-depth-'+ depth )
  89.                             .addClass( 'menu-item-depth-'+ ( newDepth ) );
  90.  
  91.                         if ( 0 === newDepth ) {
  92.                             t.find( '.is-submenu' ).hide();
  93.                         }
  94.                     });
  95.                 },
  96.                 childMenuItems : function() {
  97.                     var result = $();
  98.                     this.each(function(){
  99.                         var t = $(this), depth = t.menuItemDepth(), next = t.next( '.menu-item' );
  100.                         while( next.length && next.menuItemDepth() > depth ) {
  101.                             result = result.add( next );
  102.                             next = next.next( '.menu-item' );
  103.                         }
  104.                     });
  105.                     return result;
  106.                 },
  107.                 shiftHorizontally : function( dir ) {
  108.                     return this.each(function(){
  109.                         var t = $(this),
  110.                             depth = t.menuItemDepth(),
  111.                             newDepth = depth + dir;
  112.  
  113.                         // Change .menu-item-depth-n class
  114.                         t.moveHorizontally( newDepth, depth );
  115.                     });
  116.                 },
  117.                 moveHorizontally : function( newDepth, depth ) {
  118.                     return this.each(function(){
  119.                         var t = $(this),
  120.                             children = t.childMenuItems(),
  121.                             diff = newDepth - depth,
  122.                             subItemText = t.find('.is-submenu');
  123.  
  124.                         // Change .menu-item-depth-n class
  125.                         t.updateDepthClass( newDepth, depth ).updateParentMenuItemDBId();
  126.  
  127.                         // If it has children, move those too
  128.                         if ( children ) {
  129.                             children.each(function() {
  130.                                 var t = $(this),
  131.                                     thisDepth = t.menuItemDepth(),
  132.                                     newDepth = thisDepth + diff;
  133.                                 t.updateDepthClass(newDepth, thisDepth).updateParentMenuItemDBId();
  134.                             });
  135.                         }
  136.  
  137.                         // Show "Sub item" helper text
  138.                         if (0 === newDepth)
  139.                             subItemText.hide();
  140.                         else
  141.                             subItemText.show();
  142.                     });
  143.                 },
  144.                 updateParentMenuItemDBId : function() {
  145.                     return this.each(function(){
  146.                         var item = $(this),
  147.                             input = item.find( '.menu-item-data-parent-id' ),
  148.                             depth = parseInt( item.menuItemDepth(), 10 ),
  149.                             parentDepth = depth - 1,
  150.                             parent = item.prevAll( '.menu-item-depth-' + parentDepth ).first();
  151.  
  152.                         if ( 0 === depth ) { // Item is on the top level, has no parent
  153.                             input.val(0);
  154.                         } else { // Find the parent item, and retrieve its object id.
  155.                             input.val( parent.find( '.menu-item-data-db-id' ).val() );
  156.                         }
  157.                     });
  158.                 },
  159.                 hideAdvancedMenuItemFields : function() {
  160.                     return this.each(function(){
  161.                         var that = $(this);
  162.                         $('.hide-column-tog').not(':checked').each(function(){
  163.                             that.find('.field-' + $(this).val() ).addClass('hidden-field');
  164.                         });
  165.                     });
  166.                 },
  167.                 /**
  168.                  * Adds selected menu items to the menu.
  169.                  *
  170.                  * @param jQuery metabox The metabox jQuery object.
  171.                  */
  172.                 addSelectedToMenu : function(processMethod) {
  173.                     if ( 0 === $('#menu-to-edit').length ) {
  174.                         return false;
  175.                     }
  176.  
  177.                     return this.each(function() {
  178.                         var t = $(this), menuItems = {},
  179.                             checkboxes = ( menus.oneThemeLocationNoMenus && 0 === t.find( '.tabs-panel-active .categorychecklist li input:checked' ).length ) ? t.find( '#page-all li input[type="checkbox"]' ) : t.find( '.tabs-panel-active .categorychecklist li input:checked' ),
  180.                             re = /menu-item\[([^\]]*)/;
  181.  
  182.                         processMethod = processMethod || api.addMenuItemToBottom;
  183.  
  184.                         // If no items are checked, bail.
  185.                         if ( !checkboxes.length )
  186.                             return false;
  187.  
  188.                         // Show the ajax spinner
  189.                         t.find( '.button-controls .spinner' ).addClass( 'is-active' );
  190.  
  191.                         // Retrieve menu item data
  192.                         $(checkboxes).each(function(){
  193.                             var t = $(this),
  194.                                 listItemDBIDMatch = re.exec( t.attr('name') ),
  195.                                 listItemDBID = 'undefined' == typeof listItemDBIDMatch[1] ? 0 : parseInt(listItemDBIDMatch[1], 10);
  196.  
  197.                             if ( this.className && -1 != this.className.indexOf('add-to-top') )
  198.                                 processMethod = api.addMenuItemToTop;
  199.                             menuItems[listItemDBID] = t.closest('li').getItemData( 'add-menu-item', listItemDBID );
  200.                         });
  201.  
  202.                         // Add the items
  203.                         api.addItemToMenu(menuItems, processMethod, function(){
  204.                             // Deselect the items and hide the ajax spinner
  205.                             checkboxes.removeAttr('checked');
  206.                             t.find( '.button-controls .spinner' ).removeClass( 'is-active' );
  207.                         });
  208.                     });
  209.                 },
  210.                 getItemData : function( itemType, id ) {
  211.                     itemType = itemType || 'menu-item';
  212.  
  213.                     var itemData = {}, i,
  214.                     fields = [
  215.                         'menu-item-db-id',
  216.                         'menu-item-object-id',
  217.                         'menu-item-object',
  218.                         'menu-item-parent-id',
  219.                         'menu-item-position',
  220.                         'menu-item-type',
  221.                         'menu-item-title',
  222.                         'menu-item-url',
  223.                         'menu-item-description',
  224.                         'menu-item-attr-title',
  225.                         'menu-item-target',
  226.                         'menu-item-classes',
  227.                         'menu-item-xfn'
  228.                     ];
  229.  
  230.                     if( !id && itemType == 'menu-item' ) {
  231.                         id = this.find('.menu-item-data-db-id').val();
  232.                     }
  233.  
  234.                     if( !id ) return itemData;
  235.  
  236.                     this.find('input').each(function() {
  237.                         var field;
  238.                         i = fields.length;
  239.                         while ( i-- ) {
  240.                             if( itemType == 'menu-item' )
  241.                                 field = fields[i] + '[' + id + ']';
  242.                             else if( itemType == 'add-menu-item' )
  243.                                 field = 'menu-item[' + id + '][' + fields[i] + ']';
  244.  
  245.                             if (
  246.                                 this.name &&
  247.                                 field == this.name
  248.                             ) {
  249.                                 itemData[fields[i]] = this.value;
  250.                             }
  251.                         }
  252.                     });
  253.  
  254.                     return itemData;
  255.                 },
  256.                 setItemData : function( itemData, itemType, id ) { // Can take a type, such as 'menu-item', or an id.
  257.                     itemType = itemType || 'menu-item';
  258.  
  259.                     if( !id && itemType == 'menu-item' ) {
  260.                         id = $('.menu-item-data-db-id', this).val();
  261.                     }
  262.  
  263.                     if( !id ) return this;
  264.  
  265.                     this.find('input').each(function() {
  266.                         var t = $(this), field;
  267.                         $.each( itemData, function( attr, val ) {
  268.                             if( itemType == 'menu-item' )
  269.                                 field = attr + '[' + id + ']';
  270.                             else if( itemType == 'add-menu-item' )
  271.                                 field = 'menu-item[' + id + '][' + attr + ']';
  272.  
  273.                             if ( field == t.attr('name') ) {
  274.                                 t.val( val );
  275.                             }
  276.                         });
  277.                     });
  278.                     return this;
  279.                 }
  280.             });
  281.         },
  282.  
  283.         countMenuItems : function( depth ) {
  284.             return $( '.menu-item-depth-' + depth ).length;
  285.         },
  286.  
  287.         moveMenuItem : function( $this, dir ) {
  288.  
  289.             var items, newItemPosition, newDepth,
  290.                 menuItems = $( '#menu-to-edit li' ),
  291.                 menuItemsCount = menuItems.length,
  292.                 thisItem = $this.parents( 'li.menu-item' ),
  293.                 thisItemChildren = thisItem.childMenuItems(),
  294.                 thisItemData = thisItem.getItemData(),
  295.                 thisItemDepth = parseInt( thisItem.menuItemDepth(), 10 ),
  296.                 thisItemPosition = parseInt( thisItem.index(), 10 ),
  297.                 nextItem = thisItem.next(),
  298.                 nextItemChildren = nextItem.childMenuItems(),
  299.                 nextItemDepth = parseInt( nextItem.menuItemDepth(), 10 ) + 1,
  300.                 prevItem = thisItem.prev(),
  301.                 prevItemDepth = parseInt( prevItem.menuItemDepth(), 10 ),
  302.                 prevItemId = prevItem.getItemData()['menu-item-db-id'];
  303.  
  304.             switch ( dir ) {
  305.             case 'up':
  306.                 newItemPosition = thisItemPosition - 1;
  307.  
  308.                 // Already at top
  309.                 if ( 0 === thisItemPosition )
  310.                     break;
  311.  
  312.                 // If a sub item is moved to top, shift it to 0 depth
  313.                 if ( 0 === newItemPosition && 0 !== thisItemDepth )
  314.                     thisItem.moveHorizontally( 0, thisItemDepth );
  315.  
  316.                 // If prev item is sub item, shift to match depth
  317.                 if ( 0 !== prevItemDepth )
  318.                     thisItem.moveHorizontally( prevItemDepth, thisItemDepth );
  319.  
  320.                 // Does this item have sub items?
  321.                 if ( thisItemChildren ) {
  322.                     items = thisItem.add( thisItemChildren );
  323.                     // Move the entire block
  324.                     items.detach().insertBefore( menuItems.eq( newItemPosition ) ).updateParentMenuItemDBId();
  325.                 } else {
  326.                     thisItem.detach().insertBefore( menuItems.eq( newItemPosition ) ).updateParentMenuItemDBId();
  327.                 }
  328.                 break;
  329.             case 'down':
  330.                 // Does this item have sub items?
  331.                 if ( thisItemChildren ) {
  332.                     items = thisItem.add( thisItemChildren ),
  333.                         nextItem = menuItems.eq( items.length + thisItemPosition ),
  334.                         nextItemChildren = 0 !== nextItem.childMenuItems().length;
  335.  
  336.                     if ( nextItemChildren ) {
  337.                         newDepth = parseInt( nextItem.menuItemDepth(), 10 ) + 1;
  338.                         thisItem.moveHorizontally( newDepth, thisItemDepth );
  339.                     }
  340.  
  341.                     // Have we reached the bottom?
  342.                     if ( menuItemsCount === thisItemPosition + items.length )
  343.                         break;
  344.  
  345.                     items.detach().insertAfter( menuItems.eq( thisItemPosition + items.length ) ).updateParentMenuItemDBId();
  346.                 } else {
  347.                     // If next item has sub items, shift depth
  348.                     if ( 0 !== nextItemChildren.length )
  349.                         thisItem.moveHorizontally( nextItemDepth, thisItemDepth );
  350.  
  351.                     // Have we reached the bottom
  352.                     if ( menuItemsCount === thisItemPosition + 1 )
  353.                         break;
  354.                     thisItem.detach().insertAfter( menuItems.eq( thisItemPosition + 1 ) ).updateParentMenuItemDBId();
  355.                 }
  356.                 break;
  357.             case 'top':
  358.                 // Already at top
  359.                 if ( 0 === thisItemPosition )
  360.                     break;
  361.                 // Does this item have sub items?
  362.                 if ( thisItemChildren ) {
  363.                     items = thisItem.add( thisItemChildren );
  364.                     // Move the entire block
  365.                     items.detach().insertBefore( menuItems.eq( 0 ) ).updateParentMenuItemDBId();
  366.                 } else {
  367.                     thisItem.detach().insertBefore( menuItems.eq( 0 ) ).updateParentMenuItemDBId();
  368.                 }
  369.                 break;
  370.             case 'left':
  371.                 // As far left as possible
  372.                 if ( 0 === thisItemDepth )
  373.                     break;
  374.                 thisItem.shiftHorizontally( -1 );
  375.                 break;
  376.             case 'right':
  377.                 // Can't be sub item at top
  378.                 if ( 0 === thisItemPosition )
  379.                     break;
  380.                 // Already sub item of prevItem
  381.                 if ( thisItemData['menu-item-parent-id'] === prevItemId )
  382.                     break;
  383.                 thisItem.shiftHorizontally( 1 );
  384.                 break;
  385.             }
  386.             $this.focus();
  387.             api.registerChange();
  388.             api.refreshKeyboardAccessibility();
  389.             api.refreshAdvancedAccessibility();
  390.         },
  391.  
  392.         initAccessibility : function() {
  393.             var menu = $( '#menu-to-edit' );
  394.  
  395.             api.refreshKeyboardAccessibility();
  396.             api.refreshAdvancedAccessibility();
  397.  
  398.             // Refresh the accessibility when the user comes close to the item in any way
  399.             menu.on( 'mouseenter.refreshAccessibility focus.refreshAccessibility touchstart.refreshAccessibility' , '.menu-item' , function(){
  400.                 api.refreshAdvancedAccessibilityOfItem( $( this ).find( 'a.item-edit' ) );
  401.             } );
  402.  
  403.             // We have to update on click as well because we might hover first, change the item, and then click.
  404.             menu.on( 'click', 'a.item-edit', function() {
  405.                 api.refreshAdvancedAccessibilityOfItem( $( this ) );
  406.             } );
  407.  
  408.             // Links for moving items
  409.             menu.on( 'click', '.menus-move', function () {
  410.                 var $this = $( this ),
  411.                     dir = $this.data( 'dir' );
  412.  
  413.                 if ( 'undefined' !== typeof dir ) {
  414.                     api.moveMenuItem( $( this ).parents( 'li.menu-item' ).find( 'a.item-edit' ), dir );
  415.                 }
  416.             });
  417.         },
  418.  
  419.         /**
  420.          * refreshAdvancedAccessibilityOfItem( [itemToRefresh] )
  421.          *
  422.          * Refreshes advanced accessibility buttons for one menu item.
  423.          * Shows or hides buttons based on the location of the menu item.
  424.          *
  425.          * @param  {object} itemToRefresh The menu item that might need its advanced accessibility buttons refreshed
  426.          */
  427.         refreshAdvancedAccessibilityOfItem : function( itemToRefresh ) {
  428.  
  429.             // Only refresh accessibility when necessary
  430.             if ( true !== $( itemToRefresh ).data( 'needs_accessibility_refresh' ) ) {
  431.                 return;
  432.             }
  433.  
  434.             var thisLink, thisLinkText, primaryItems, itemPosition, title,
  435.                 parentItem, parentItemId, parentItemName, subItems,
  436.                 $this = $( itemToRefresh ),
  437.                 menuItem = $this.closest( 'li.menu-item' ).first(),
  438.                 depth = menuItem.menuItemDepth(),
  439.                 isPrimaryMenuItem = ( 0 === depth ),
  440.                 itemName = $this.closest( '.menu-item-handle' ).find( '.menu-item-title' ).text(),
  441.                 position = parseInt( menuItem.index(), 10 ),
  442.                 prevItemDepth = ( isPrimaryMenuItem ) ? depth : parseInt( depth - 1, 10 ),
  443.                 prevItemNameLeft = menuItem.prevAll('.menu-item-depth-' + prevItemDepth).first().find( '.menu-item-title' ).text(),
  444.                 prevItemNameRight = menuItem.prevAll('.menu-item-depth-' + depth).first().find( '.menu-item-title' ).text(),
  445.                 totalMenuItems = $('#menu-to-edit li').length,
  446.                 hasSameDepthSibling = menuItem.nextAll( '.menu-item-depth-' + depth ).length;
  447.  
  448.                 menuItem.find( '.field-move' ).toggle( totalMenuItems > 1 );
  449.  
  450.             // Where can they move this menu item?
  451.             if ( 0 !== position ) {
  452.                 thisLink = menuItem.find( '.menus-move-up' );
  453.                 thisLink.attr( 'aria-label', menus.moveUp ).css( 'display', 'inline' );
  454.             }
  455.  
  456.             if ( 0 !== position && isPrimaryMenuItem ) {
  457.                 thisLink = menuItem.find( '.menus-move-top' );
  458.                 thisLink.attr( 'aria-label', menus.moveToTop ).css( 'display', 'inline' );
  459.             }
  460.  
  461.             if ( position + 1 !== totalMenuItems && 0 !== position ) {
  462.                 thisLink = menuItem.find( '.menus-move-down' );
  463.                 thisLink.attr( 'aria-label', menus.moveDown ).css( 'display', 'inline' );
  464.             }
  465.  
  466.             if ( 0 === position && 0 !== hasSameDepthSibling ) {
  467.                 thisLink = menuItem.find( '.menus-move-down' );
  468.                 thisLink.attr( 'aria-label', menus.moveDown ).css( 'display', 'inline' );
  469.             }
  470.  
  471.             if ( ! isPrimaryMenuItem ) {
  472.                 thisLink = menuItem.find( '.menus-move-left' ),
  473.                 thisLinkText = menus.outFrom.replace( '%s', prevItemNameLeft );
  474.                 thisLink.attr( 'aria-label', menus.moveOutFrom.replace( '%s', prevItemNameLeft ) ).text( thisLinkText ).css( 'display', 'inline' );
  475.             }
  476.  
  477.             if ( 0 !== position ) {
  478.                 if ( menuItem.find( '.menu-item-data-parent-id' ).val() !== menuItem.prev().find( '.menu-item-data-db-id' ).val() ) {
  479.                     thisLink = menuItem.find( '.menus-move-right' ),
  480.                     thisLinkText = menus.under.replace( '%s', prevItemNameRight );
  481.                     thisLink.attr( 'aria-label', menus.moveUnder.replace( '%s', prevItemNameRight ) ).text( thisLinkText ).css( 'display', 'inline' );
  482.                 }
  483.             }
  484.  
  485.             if ( isPrimaryMenuItem ) {
  486.                 primaryItems = $( '.menu-item-depth-0' ),
  487.                 itemPosition = primaryItems.index( menuItem ) + 1,
  488.                 totalMenuItems = primaryItems.length,
  489.  
  490.                 // String together help text for primary menu items
  491.                 title = menus.menuFocus.replace( '%1$s', itemName ).replace( '%2$d', itemPosition ).replace( '%3$d', totalMenuItems );
  492.             } else {
  493.                 parentItem = menuItem.prevAll( '.menu-item-depth-' + parseInt( depth - 1, 10 ) ).first(),
  494.                 parentItemId = parentItem.find( '.menu-item-data-db-id' ).val(),
  495.                 parentItemName = parentItem.find( '.menu-item-title' ).text(),
  496.                 subItems = $( '.menu-item .menu-item-data-parent-id[value="' + parentItemId + '"]' ),
  497.                 itemPosition = $( subItems.parents('.menu-item').get().reverse() ).index( menuItem ) + 1;
  498.  
  499.                 // String together help text for sub menu items
  500.                 title = menus.subMenuFocus.replace( '%1$s', itemName ).replace( '%2$d', itemPosition ).replace( '%3$s', parentItemName );
  501.             }
  502.  
  503.             $this.attr( 'aria-label', title );
  504.  
  505.             // Mark this item's accessibility as refreshed
  506.             $this.data( 'needs_accessibility_refresh', false );
  507.         },
  508.  
  509.         /**
  510.          * refreshAdvancedAccessibility
  511.          *
  512.          * Hides all advanced accessibility buttons and marks them for refreshing.
  513.          */
  514.         refreshAdvancedAccessibility : function() {
  515.  
  516.             // Hide all the move buttons by default.
  517.             $( '.menu-item-settings .field-move .menus-move' ).hide();
  518.  
  519.             // Mark all menu items as unprocessed
  520.             $( 'a.item-edit' ).data( 'needs_accessibility_refresh', true );
  521.  
  522.             // All open items have to be refreshed or they will show no links
  523.             $( '.menu-item-edit-active a.item-edit' ).each( function() {
  524.                 api.refreshAdvancedAccessibilityOfItem( this );
  525.             } );
  526.         },
  527.  
  528.         refreshKeyboardAccessibility : function() {
  529.             $( 'a.item-edit' ).off( 'focus' ).on( 'focus', function(){
  530.                 $(this).off( 'keydown' ).on( 'keydown', function(e){
  531.  
  532.                     var arrows,
  533.                         $this = $( this ),
  534.                         thisItem = $this.parents( 'li.menu-item' ),
  535.                         thisItemData = thisItem.getItemData();
  536.  
  537.                     // Bail if it's not an arrow key
  538.                     if ( 37 != e.which && 38 != e.which && 39 != e.which && 40 != e.which )
  539.                         return;
  540.  
  541.                     // Avoid multiple keydown events
  542.                     $this.off('keydown');
  543.  
  544.                     // Bail if there is only one menu item
  545.                     if ( 1 === $('#menu-to-edit li').length )
  546.                         return;
  547.  
  548.                     // If RTL, swap left/right arrows
  549.                     arrows = { '38': 'up', '40': 'down', '37': 'left', '39': 'right' };
  550.                     if ( $('body').hasClass('rtl') )
  551.                         arrows = { '38' : 'up', '40' : 'down', '39' : 'left', '37' : 'right' };
  552.  
  553.                     switch ( arrows[e.which] ) {
  554.                     case 'up':
  555.                         api.moveMenuItem( $this, 'up' );
  556.                         break;
  557.                     case 'down':
  558.                         api.moveMenuItem( $this, 'down' );
  559.                         break;
  560.                     case 'left':
  561.                         api.moveMenuItem( $this, 'left' );
  562.                         break;
  563.                     case 'right':
  564.                         api.moveMenuItem( $this, 'right' );
  565.                         break;
  566.                     }
  567.                     // Put focus back on same menu item
  568.                     $( '#edit-' + thisItemData['menu-item-db-id'] ).focus();
  569.                     return false;
  570.                 });
  571.             });
  572.         },
  573.  
  574.         initPreviewing : function() {
  575.             // Update the item handle title when the navigation label is changed.
  576.             $( '#menu-to-edit' ).on( 'change input', '.edit-menu-item-title', function(e) {
  577.                 var input = $( e.currentTarget ), title, titleEl;
  578.                 title = input.val();
  579.                 titleEl = input.closest( '.menu-item' ).find( '.menu-item-title' );
  580.                 // Don't update to empty title.
  581.                 if ( title ) {
  582.                     titleEl.text( title ).removeClass( 'no-title' );
  583.                 } else {
  584.                     titleEl.text( navMenuL10n.untitled ).addClass( 'no-title' );
  585.                 }
  586.             } );
  587.         },
  588.  
  589.         initToggles : function() {
  590.             // init postboxes
  591.             postboxes.add_postbox_toggles('nav-menus');
  592.  
  593.             // adjust columns functions for menus UI
  594.             columns.useCheckboxesForHidden();
  595.             columns.checked = function(field) {
  596.                 $('.field-' + field).removeClass('hidden-field');
  597.             };
  598.             columns.unchecked = function(field) {
  599.                 $('.field-' + field).addClass('hidden-field');
  600.             };
  601.             // hide fields
  602.             api.menuList.hideAdvancedMenuItemFields();
  603.  
  604.             $('.hide-postbox-tog').click(function () {
  605.                 var hidden = $( '.accordion-container li.accordion-section' ).filter(':hidden').map(function() { return this.id; }).get().join(',');
  606.                 $.post(ajaxurl, {
  607.                     action: 'closed-postboxes',
  608.                     hidden: hidden,
  609.                     closedpostboxesnonce: jQuery('#closedpostboxesnonce').val(),
  610.                     page: 'nav-menus'
  611.                 });
  612.             });
  613.         },
  614.  
  615.         initSortables : function() {
  616.             var currentDepth = 0, originalDepth, minDepth, maxDepth,
  617.                 prev, next, prevBottom, nextThreshold, helperHeight, transport,
  618.                 menuEdge = api.menuList.offset().left,
  619.                 body = $('body'), maxChildDepth,
  620.                 menuMaxDepth = initialMenuMaxDepth();
  621.  
  622.             if( 0 !== $( '#menu-to-edit li' ).length )
  623.                 $( '.drag-instructions' ).show();
  624.  
  625.             // Use the right edge if RTL.
  626.             menuEdge += api.isRTL ? api.menuList.width() : 0;
  627.  
  628.             api.menuList.sortable({
  629.                 handle: '.menu-item-handle',
  630.                 placeholder: 'sortable-placeholder',
  631.                 items: api.options.sortableItems,
  632.                 start: function(e, ui) {
  633.                     var height, width, parent, children, tempHolder;
  634.  
  635.                     // handle placement for rtl orientation
  636.                     if ( api.isRTL )
  637.                         ui.item[0].style.right = 'auto';
  638.  
  639.                     transport = ui.item.children('.menu-item-transport');
  640.  
  641.                     // Set depths. currentDepth must be set before children are located.
  642.                     originalDepth = ui.item.menuItemDepth();
  643.                     updateCurrentDepth(ui, originalDepth);
  644.  
  645.                     // Attach child elements to parent
  646.                     // Skip the placeholder
  647.                     parent = ( ui.item.next()[0] == ui.placeholder[0] ) ? ui.item.next() : ui.item;
  648.                     children = parent.childMenuItems();
  649.                     transport.append( children );
  650.  
  651.                     // Update the height of the placeholder to match the moving item.
  652.                     height = transport.outerHeight();
  653.                     // If there are children, account for distance between top of children and parent
  654.                     height += ( height > 0 ) ? (ui.placeholder.css('margin-top').slice(0, -2) * 1) : 0;
  655.                     height += ui.helper.outerHeight();
  656.                     helperHeight = height;
  657.                     height -= 2; // Subtract 2 for borders
  658.                     ui.placeholder.height(height);
  659.  
  660.                     // Update the width of the placeholder to match the moving item.
  661.                     maxChildDepth = originalDepth;
  662.                     children.each(function(){
  663.                         var depth = $(this).menuItemDepth();
  664.                         maxChildDepth = (depth > maxChildDepth) ? depth : maxChildDepth;
  665.                     });
  666.                     width = ui.helper.find('.menu-item-handle').outerWidth(); // Get original width
  667.                     width += api.depthToPx(maxChildDepth - originalDepth); // Account for children
  668.                     width -= 2; // Subtract 2 for borders
  669.                     ui.placeholder.width(width);
  670.  
  671.                     // Update the list of menu items.
  672.                     tempHolder = ui.placeholder.next( '.menu-item' );
  673.                     tempHolder.css( 'margin-top', helperHeight + 'px' ); // Set the margin to absorb the placeholder
  674.                     ui.placeholder.detach(); // detach or jQuery UI will think the placeholder is a menu item
  675.                     $(this).sortable( 'refresh' ); // The children aren't sortable. We should let jQ UI know.
  676.                     ui.item.after( ui.placeholder ); // reattach the placeholder.
  677.                     tempHolder.css('margin-top', 0); // reset the margin
  678.  
  679.                     // Now that the element is complete, we can update...
  680.                     updateSharedVars(ui);
  681.                 },
  682.                 stop: function(e, ui) {
  683.                     var children, subMenuTitle,
  684.                         depthChange = currentDepth - originalDepth;
  685.  
  686.                     // Return child elements to the list
  687.                     children = transport.children().insertAfter(ui.item);
  688.  
  689.                     // Add "sub menu" description
  690.                     subMenuTitle = ui.item.find( '.item-title .is-submenu' );
  691.                     if ( 0 < currentDepth )
  692.                         subMenuTitle.show();
  693.                     else
  694.                         subMenuTitle.hide();
  695.  
  696.                     // Update depth classes
  697.                     if ( 0 !== depthChange ) {
  698.                         ui.item.updateDepthClass( currentDepth );
  699.                         children.shiftDepthClass( depthChange );
  700.                         updateMenuMaxDepth( depthChange );
  701.                     }
  702.                     // Register a change
  703.                     api.registerChange();
  704.                     // Update the item data.
  705.                     ui.item.updateParentMenuItemDBId();
  706.  
  707.                     // address sortable's incorrectly-calculated top in opera
  708.                     ui.item[0].style.top = 0;
  709.  
  710.                     // handle drop placement for rtl orientation
  711.                     if ( api.isRTL ) {
  712.                         ui.item[0].style.left = 'auto';
  713.                         ui.item[0].style.right = 0;
  714.                     }
  715.  
  716.                     api.refreshKeyboardAccessibility();
  717.                     api.refreshAdvancedAccessibility();
  718.                 },
  719.                 change: function(e, ui) {
  720.                     // Make sure the placeholder is inside the menu.
  721.                     // Otherwise fix it, or we're in trouble.
  722.                     if( ! ui.placeholder.parent().hasClass('menu') )
  723.                         (prev.length) ? prev.after( ui.placeholder ) : api.menuList.prepend( ui.placeholder );
  724.  
  725.                     updateSharedVars(ui);
  726.                 },
  727.                 sort: function(e, ui) {
  728.                     var offset = ui.helper.offset(),
  729.                         edge = api.isRTL ? offset.left + ui.helper.width() : offset.left,
  730.                         depth = api.negateIfRTL * api.pxToDepth( edge - menuEdge );
  731.  
  732.                     // Check and correct if depth is not within range.
  733.                     // Also, if the dragged element is dragged upwards over
  734.                     // an item, shift the placeholder to a child position.
  735.                     if ( depth > maxDepth || offset.top < ( prevBottom - api.options.targetTolerance ) ) {
  736.                         depth = maxDepth;
  737.                     } else if ( depth < minDepth ) {
  738.                         depth = minDepth;
  739.                     }
  740.  
  741.                     if( depth != currentDepth )
  742.                         updateCurrentDepth(ui, depth);
  743.  
  744.                     // If we overlap the next element, manually shift downwards
  745.                     if( nextThreshold && offset.top + helperHeight > nextThreshold ) {
  746.                         next.after( ui.placeholder );
  747.                         updateSharedVars( ui );
  748.                         $( this ).sortable( 'refreshPositions' );
  749.                     }
  750.                 }
  751.             });
  752.  
  753.             function updateSharedVars(ui) {
  754.                 var depth;
  755.  
  756.                 prev = ui.placeholder.prev( '.menu-item' );
  757.                 next = ui.placeholder.next( '.menu-item' );
  758.  
  759.                 // Make sure we don't select the moving item.
  760.                 if( prev[0] == ui.item[0] ) prev = prev.prev( '.menu-item' );
  761.                 if( next[0] == ui.item[0] ) next = next.next( '.menu-item' );
  762.  
  763.                 prevBottom = (prev.length) ? prev.offset().top + prev.height() : 0;
  764.                 nextThreshold = (next.length) ? next.offset().top + next.height() / 3 : 0;
  765.                 minDepth = (next.length) ? next.menuItemDepth() : 0;
  766.  
  767.                 if( prev.length )
  768.                     maxDepth = ( (depth = prev.menuItemDepth() + 1) > api.options.globalMaxDepth ) ? api.options.globalMaxDepth : depth;
  769.                 else
  770.                     maxDepth = 0;
  771.             }
  772.  
  773.             function updateCurrentDepth(ui, depth) {
  774.                 ui.placeholder.updateDepthClass( depth, currentDepth );
  775.                 currentDepth = depth;
  776.             }
  777.  
  778.             function initialMenuMaxDepth() {
  779.                 if( ! body[0].className ) return 0;
  780.                 var match = body[0].className.match(/menu-max-depth-(\d+)/);
  781.                 return match && match[1] ? parseInt( match[1], 10 ) : 0;
  782.             }
  783.  
  784.             function updateMenuMaxDepth( depthChange ) {
  785.                 var depth, newDepth = menuMaxDepth;
  786.                 if ( depthChange === 0 ) {
  787.                     return;
  788.                 } else if ( depthChange > 0 ) {
  789.                     depth = maxChildDepth + depthChange;
  790.                     if( depth > menuMaxDepth )
  791.                         newDepth = depth;
  792.                 } else if ( depthChange < 0 && maxChildDepth == menuMaxDepth ) {
  793.                     while( ! $('.menu-item-depth-' + newDepth, api.menuList).length && newDepth > 0 )
  794.                         newDepth--;
  795.                 }
  796.                 // Update the depth class.
  797.                 body.removeClass( 'menu-max-depth-' + menuMaxDepth ).addClass( 'menu-max-depth-' + newDepth );
  798.                 menuMaxDepth = newDepth;
  799.             }
  800.         },
  801.  
  802.         initManageLocations : function () {
  803.             $('#menu-locations-wrap form').submit(function(){
  804.                 window.onbeforeunload = null;
  805.             });
  806.             $('.menu-location-menus select').on('change', function () {
  807.                 var editLink = $(this).closest('tr').find('.locations-edit-menu-link');
  808.                 if ($(this).find('option:selected').data('orig'))
  809.                     editLink.show();
  810.                 else
  811.                     editLink.hide();
  812.             });
  813.         },
  814.  
  815.         attachMenuEditListeners : function() {
  816.             var that = this;
  817.             $('#update-nav-menu').bind('click', function(e) {
  818.                 if ( e.target && e.target.className ) {
  819.                     if ( -1 != e.target.className.indexOf('item-edit') ) {
  820.                         return that.eventOnClickEditLink(e.target);
  821.                     } else if ( -1 != e.target.className.indexOf('menu-save') ) {
  822.                         return that.eventOnClickMenuSave(e.target);
  823.                     } else if ( -1 != e.target.className.indexOf('menu-delete') ) {
  824.                         return that.eventOnClickMenuDelete(e.target);
  825.                     } else if ( -1 != e.target.className.indexOf('item-delete') ) {
  826.                         return that.eventOnClickMenuItemDelete(e.target);
  827.                     } else if ( -1 != e.target.className.indexOf('item-cancel') ) {
  828.                         return that.eventOnClickCancelLink(e.target);
  829.                     }
  830.                 }
  831.             });
  832.             $('#add-custom-links input[type="text"]').keypress(function(e){
  833.                 $('#customlinkdiv').removeClass('form-invalid');
  834.  
  835.                 if ( e.keyCode === 13 ) {
  836.                     e.preventDefault();
  837.                     $( '#submit-customlinkdiv' ).click();
  838.                 }
  839.             });
  840.         },
  841.  
  842.         attachMenuSaveSubmitListeners : function() {
  843.             /*
  844.              * When a navigation menu is saved, store a JSON representation of all form data
  845.              * in a single input to avoid PHP `max_input_vars` limitations. See #14134.
  846.              */
  847.             $( '#update-nav-menu' ).submit( function() {
  848.                 var navMenuData = $( '#update-nav-menu' ).serializeArray();
  849.                 $( '[name="nav-menu-data"]' ).val( JSON.stringify( navMenuData ) );
  850.             });
  851.         },
  852.  
  853.         attachThemeLocationsListeners : function() {
  854.             var loc = $('#nav-menu-theme-locations'), params = {};
  855.             params.action = 'menu-locations-save';
  856.             params['menu-settings-column-nonce'] = $('#menu-settings-column-nonce').val();
  857.             loc.find('input[type="submit"]').click(function() {
  858.                 loc.find('select').each(function() {
  859.                     params[this.name] = $(this).val();
  860.                 });
  861.                 loc.find( '.spinner' ).addClass( 'is-active' );
  862.                 $.post( ajaxurl, params, function() {
  863.                     loc.find( '.spinner' ).removeClass( 'is-active' );
  864.                 });
  865.                 return false;
  866.             });
  867.         },
  868.  
  869.         attachQuickSearchListeners : function() {
  870.             var searchTimer,
  871.                 inputEvent;
  872.  
  873.             // Prevent form submission.
  874.             $( '#nav-menu-meta' ).on( 'submit', function( event ) {
  875.                 event.preventDefault();
  876.             });
  877.  
  878.             /*
  879.              * Use feature detection to determine whether inputs should use
  880.              * the `keyup` or `input` event. Input is preferred but lacks support
  881.              * in legacy browsers. See changeset 34078, see also ticket #26600#comment:59
  882.              */
  883.             if ( 'oninput' in document.createElement( 'input' ) ) {
  884.                 inputEvent = 'input';
  885.             } else {
  886.                 inputEvent = 'keyup';
  887.             }
  888.  
  889.             $( '#nav-menu-meta' ).on( inputEvent, '.quick-search', function() {
  890.                 var $this = $( this );
  891.  
  892.                 $this.attr( 'autocomplete', 'off' );
  893.  
  894.                 if ( searchTimer ) {
  895.                     clearTimeout( searchTimer );
  896.                 }
  897.  
  898.                 searchTimer = setTimeout( function() {
  899.                     api.updateQuickSearchResults( $this );
  900.                  }, 500 );
  901.             }).on( 'blur', '.quick-search', function() {
  902.                 api.lastSearch = '';
  903.             });
  904.         },
  905.  
  906.         updateQuickSearchResults : function(input) {
  907.             var panel, params,
  908.                 minSearchLength = 2,
  909.                 q = input.val();
  910.  
  911.             /*
  912.              * Minimum characters for a search. Also avoid a new AJAX search when
  913.              * the pressed key (e.g. arrows) doesn't change the searched term.
  914.              */
  915.             if ( q.length < minSearchLength || api.lastSearch == q ) {
  916.                 return;
  917.             }
  918.  
  919.             api.lastSearch = q;
  920.  
  921.             panel = input.parents('.tabs-panel');
  922.             params = {
  923.                 'action': 'menu-quick-search',
  924.                 'response-format': 'markup',
  925.                 'menu': $('#menu').val(),
  926.                 'menu-settings-column-nonce': $('#menu-settings-column-nonce').val(),
  927.                 'q': q,
  928.                 'type': input.attr('name')
  929.             };
  930.  
  931.             $( '.spinner', panel ).addClass( 'is-active' );
  932.  
  933.             $.post( ajaxurl, params, function(menuMarkup) {
  934.                 api.processQuickSearchQueryResponse(menuMarkup, params, panel);
  935.             });
  936.         },
  937.  
  938.         addCustomLink : function( processMethod ) {
  939.             var url = $('#custom-menu-item-url').val(),
  940.                 label = $('#custom-menu-item-name').val();
  941.  
  942.             processMethod = processMethod || api.addMenuItemToBottom;
  943.  
  944.             if ( '' === url || 'http://' == url ) {
  945.                 $('#customlinkdiv').addClass('form-invalid');
  946.                 return false;
  947.             }
  948.  
  949.             // Show the ajax spinner
  950.             $( '.customlinkdiv .spinner' ).addClass( 'is-active' );
  951.             this.addLinkToMenu( url, label, processMethod, function() {
  952.                 // Remove the ajax spinner
  953.                 $( '.customlinkdiv .spinner' ).removeClass( 'is-active' );
  954.                 // Set custom link form back to defaults
  955.                 $('#custom-menu-item-name').val('').blur();
  956.                 $('#custom-menu-item-url').val('http://');
  957.             });
  958.         },
  959.  
  960.         addLinkToMenu : function(url, label, processMethod, callback) {
  961.             processMethod = processMethod || api.addMenuItemToBottom;
  962.             callback = callback || function(){};
  963.  
  964.             api.addItemToMenu({
  965.                 '-1': {
  966.                     'menu-item-type': 'custom',
  967.                     'menu-item-url': url,
  968.                     'menu-item-title': label
  969.                 }
  970.             }, processMethod, callback);
  971.         },
  972.  
  973.         addItemToMenu : function(menuItem, processMethod, callback) {
  974.             var menu = $('#menu').val(),
  975.                 nonce = $('#menu-settings-column-nonce').val(),
  976.                 params;
  977.  
  978.             processMethod = processMethod || function(){};
  979.             callback = callback || function(){};
  980.  
  981.             params = {
  982.                 'action': 'add-menu-item',
  983.                 'menu': menu,
  984.                 'menu-settings-column-nonce': nonce,
  985.                 'menu-item': menuItem
  986.             };
  987.  
  988.             $.post( ajaxurl, params, function(menuMarkup) {
  989.                 var ins = $('#menu-instructions');
  990.  
  991.                 menuMarkup = $.trim( menuMarkup ); // Trim leading whitespaces
  992.                 processMethod(menuMarkup, params);
  993.  
  994.                 // Make it stand out a bit more visually, by adding a fadeIn
  995.                 $( 'li.pending' ).hide().fadeIn('slow');
  996.                 $( '.drag-instructions' ).show();
  997.                 if( ! ins.hasClass( 'menu-instructions-inactive' ) && ins.siblings().length )
  998.                     ins.addClass( 'menu-instructions-inactive' );
  999.  
  1000.                 callback();
  1001.             });
  1002.         },
  1003.  
  1004.         /**
  1005.          * Process the add menu item request response into menu list item. Appends to menu.
  1006.          *
  1007.          * @param {string} menuMarkup The text server response of menu item markup.
  1008.          *
  1009.          * @fires document#menu-item-added Passes menuMarkup as a jQuery object.
  1010.          */
  1011.         addMenuItemToBottom : function( menuMarkup ) {
  1012.             var $menuMarkup = $( menuMarkup );
  1013.             $menuMarkup.hideAdvancedMenuItemFields().appendTo( api.targetList );
  1014.             api.refreshKeyboardAccessibility();
  1015.             api.refreshAdvancedAccessibility();
  1016.             $( document ).trigger( 'menu-item-added', [ $menuMarkup ] );
  1017.         },
  1018.  
  1019.         /**
  1020.          * Process the add menu item request response into menu list item. Prepends to menu.
  1021.          *
  1022.          * @param {string} menuMarkup The text server response of menu item markup.
  1023.          *
  1024.          * @fires document#menu-item-added Passes menuMarkup as a jQuery object.
  1025.          */
  1026.         addMenuItemToTop : function( menuMarkup ) {
  1027.             var $menuMarkup = $( menuMarkup );
  1028.             $menuMarkup.hideAdvancedMenuItemFields().prependTo( api.targetList );
  1029.             api.refreshKeyboardAccessibility();
  1030.             api.refreshAdvancedAccessibility();
  1031.             $( document ).trigger( 'menu-item-added', [ $menuMarkup ] );
  1032.         },
  1033.  
  1034.         attachUnsavedChangesListener : function() {
  1035.             $('#menu-management input, #menu-management select, #menu-management, #menu-management textarea, .menu-location-menus select').change(function(){
  1036.                 api.registerChange();
  1037.             });
  1038.  
  1039.             if ( 0 !== $('#menu-to-edit').length || 0 !== $('.menu-location-menus select').length ) {
  1040.                 window.onbeforeunload = function(){
  1041.                     if ( api.menusChanged )
  1042.                         return navMenuL10n.saveAlert;
  1043.                 };
  1044.             } else {
  1045.                 // Make the post boxes read-only, as they can't be used yet
  1046.                 $( '#menu-settings-column' ).find( 'input,select' ).end().find( 'a' ).attr( 'href', '#' ).unbind( 'click' );
  1047.             }
  1048.         },
  1049.  
  1050.         registerChange : function() {
  1051.             api.menusChanged = true;
  1052.         },
  1053.  
  1054.         attachTabsPanelListeners : function() {
  1055.             $('#menu-settings-column').bind('click', function(e) {
  1056.                 var selectAreaMatch, panelId, wrapper, items,
  1057.                     target = $(e.target);
  1058.  
  1059.                 if ( target.hasClass('nav-tab-link') ) {
  1060.  
  1061.                     panelId = target.data( 'type' );
  1062.  
  1063.                     wrapper = target.parents('.accordion-section-content').first();
  1064.  
  1065.                     // upon changing tabs, we want to uncheck all checkboxes
  1066.                     $('input', wrapper).removeAttr('checked');
  1067.  
  1068.                     $('.tabs-panel-active', wrapper).removeClass('tabs-panel-active').addClass('tabs-panel-inactive');
  1069.                     $('#' + panelId, wrapper).removeClass('tabs-panel-inactive').addClass('tabs-panel-active');
  1070.  
  1071.                     $('.tabs', wrapper).removeClass('tabs');
  1072.                     target.parent().addClass('tabs');
  1073.  
  1074.                     // select the search bar
  1075.                     $('.quick-search', wrapper).focus();
  1076.  
  1077.                     // Hide controls in the search tab if no items found.
  1078.                     if ( ! wrapper.find( '.tabs-panel-active .menu-item-title' ).length ) {
  1079.                         wrapper.addClass( 'has-no-menu-item' );
  1080.                     } else {
  1081.                         wrapper.removeClass( 'has-no-menu-item' );
  1082.                     }
  1083.  
  1084.                     e.preventDefault();
  1085.                 } else if ( target.hasClass('select-all') ) {
  1086.                     selectAreaMatch = /#(.*)$/.exec(e.target.href);
  1087.                     if ( selectAreaMatch && selectAreaMatch[1] ) {
  1088.                         items = $('#' + selectAreaMatch[1] + ' .tabs-panel-active .menu-item-title input');
  1089.                         if( items.length === items.filter(':checked').length )
  1090.                             items.removeAttr('checked');
  1091.                         else
  1092.                             items.prop('checked', true);
  1093.                         return false;
  1094.                     }
  1095.                 } else if ( target.hasClass('submit-add-to-menu') ) {
  1096.                     api.registerChange();
  1097.  
  1098.                     if ( e.target.id && 'submit-customlinkdiv' == e.target.id )
  1099.                         api.addCustomLink( api.addMenuItemToBottom );
  1100.                     else if ( e.target.id && -1 != e.target.id.indexOf('submit-') )
  1101.                         $('#' + e.target.id.replace(/submit-/, '')).addSelectedToMenu( api.addMenuItemToBottom );
  1102.                     return false;
  1103.                 }
  1104.             });
  1105.  
  1106.             /*
  1107.              * Delegate the `click` event and attach it just to the pagination
  1108.              * links thus excluding the current page `<span>`. See ticket #35577.
  1109.              */
  1110.             $( '#nav-menu-meta' ).on( 'click', 'a.page-numbers', function() {
  1111.                 var $container = $( this ).closest( '.inside' );
  1112.  
  1113.                 $.post( ajaxurl, this.href.replace( /.*\?/, '' ).replace( /action=([^&]*)/, '' ) + '&action=menu-get-metabox',
  1114.                     function( resp ) {
  1115.                         var metaBoxData = $.parseJSON( resp ),
  1116.                             toReplace;
  1117.  
  1118.                         if ( -1 === resp.indexOf( 'replace-id' ) ) {
  1119.                             return;
  1120.                         }
  1121.  
  1122.                         // Get the post type menu meta box to update.
  1123.                         toReplace = document.getElementById( metaBoxData['replace-id'] );
  1124.  
  1125.                         if ( ! metaBoxData.markup || ! toReplace ) {
  1126.                             return;
  1127.                         }
  1128.  
  1129.                         // Update the post type menu meta box with new content from the response.
  1130.                         $container.html( metaBoxData.markup );
  1131.                     }
  1132.                 );
  1133.  
  1134.                 return false;
  1135.             });
  1136.         },
  1137.  
  1138.         eventOnClickEditLink : function(clickedEl) {
  1139.             var settings, item,
  1140.             matchedSection = /#(.*)$/.exec(clickedEl.href);
  1141.             if ( matchedSection && matchedSection[1] ) {
  1142.                 settings = $('#'+matchedSection[1]);
  1143.                 item = settings.parent();
  1144.                 if( 0 !== item.length ) {
  1145.                     if( item.hasClass('menu-item-edit-inactive') ) {
  1146.                         if( ! settings.data('menu-item-data') ) {
  1147.                             settings.data( 'menu-item-data', settings.getItemData() );
  1148.                         }
  1149.                         settings.slideDown('fast');
  1150.                         item.removeClass('menu-item-edit-inactive')
  1151.                             .addClass('menu-item-edit-active');
  1152.                     } else {
  1153.                         settings.slideUp('fast');
  1154.                         item.removeClass('menu-item-edit-active')
  1155.                             .addClass('menu-item-edit-inactive');
  1156.                     }
  1157.                     return false;
  1158.                 }
  1159.             }
  1160.         },
  1161.  
  1162.         eventOnClickCancelLink : function(clickedEl) {
  1163.             var settings = $( clickedEl ).closest( '.menu-item-settings' ),
  1164.                 thisMenuItem = $( clickedEl ).closest( '.menu-item' );
  1165.             thisMenuItem.removeClass('menu-item-edit-active').addClass('menu-item-edit-inactive');
  1166.             settings.setItemData( settings.data('menu-item-data') ).hide();
  1167.             return false;
  1168.         },
  1169.  
  1170.         eventOnClickMenuSave : function() {
  1171.             var locs = '',
  1172.             menuName = $('#menu-name'),
  1173.             menuNameVal = menuName.val();
  1174.             // Cancel and warn if invalid menu name
  1175.             if( !menuNameVal || menuNameVal == menuName.attr('title') || !menuNameVal.replace(/\s+/, '') ) {
  1176.                 menuName.parent().addClass('form-invalid');
  1177.                 return false;
  1178.             }
  1179.             // Copy menu theme locations
  1180.             $('#nav-menu-theme-locations select').each(function() {
  1181.                 locs += '<input type="hidden" name="' + this.name + '" value="' + $(this).val() + '" />';
  1182.             });
  1183.             $('#update-nav-menu').append( locs );
  1184.             // Update menu item position data
  1185.             api.menuList.find('.menu-item-data-position').val( function(index) { return index + 1; } );
  1186.             window.onbeforeunload = null;
  1187.  
  1188.             return true;
  1189.         },
  1190.  
  1191.         eventOnClickMenuDelete : function() {
  1192.             // Delete warning AYS
  1193.             if ( window.confirm( navMenuL10n.warnDeleteMenu ) ) {
  1194.                 window.onbeforeunload = null;
  1195.                 return true;
  1196.             }
  1197.             return false;
  1198.         },
  1199.  
  1200.         eventOnClickMenuItemDelete : function(clickedEl) {
  1201.             var itemID = parseInt(clickedEl.id.replace('delete-', ''), 10);
  1202.             api.removeMenuItem( $('#menu-item-' + itemID) );
  1203.             api.registerChange();
  1204.             return false;
  1205.         },
  1206.  
  1207.         /**
  1208.          * Process the quick search response into a search result
  1209.          *
  1210.          * @param string resp The server response to the query.
  1211.          * @param object req The request arguments.
  1212.          * @param jQuery panel The tabs panel we're searching in.
  1213.          */
  1214.         processQuickSearchQueryResponse : function(resp, req, panel) {
  1215.             var matched, newID,
  1216.             takenIDs = {},
  1217.             form = document.getElementById('nav-menu-meta'),
  1218.             pattern = /menu-item[(\[^]\]*/,
  1219.             $items = $('<div>').html(resp).find('li'),
  1220.             wrapper = panel.closest( '.accordion-section-content' ),
  1221.             $item;
  1222.  
  1223.             if( ! $items.length ) {
  1224.                 $('.categorychecklist', panel).html( '<li><p>' + navMenuL10n.noResultsFound + '</p></li>' );
  1225.                 $( '.spinner', panel ).removeClass( 'is-active' );
  1226.                 wrapper.addClass( 'has-no-menu-item' );
  1227.                 return;
  1228.             }
  1229.  
  1230.             $items.each(function(){
  1231.                 $item = $(this);
  1232.  
  1233.                 // make a unique DB ID number
  1234.                 matched = pattern.exec($item.html());
  1235.  
  1236.                 if ( matched && matched[1] ) {
  1237.                     newID = matched[1];
  1238.                     while( form.elements['menu-item[' + newID + '][menu-item-type]'] || takenIDs[ newID ] ) {
  1239.                         newID--;
  1240.                     }
  1241.  
  1242.                     takenIDs[newID] = true;
  1243.                     if ( newID != matched[1] ) {
  1244.                         $item.html( $item.html().replace(new RegExp(
  1245.                             'menu-item\\[' + matched[1] + '\\]', 'g'),
  1246.                             'menu-item[' + newID + ']'
  1247.                         ) );
  1248.                     }
  1249.                 }
  1250.             });
  1251.  
  1252.             $('.categorychecklist', panel).html( $items );
  1253.             $( '.spinner', panel ).removeClass( 'is-active' );
  1254.             wrapper.removeClass( 'has-no-menu-item' );
  1255.         },
  1256.  
  1257.         /**
  1258.          * Remove a menu item.
  1259.          * @param  {object} el The element to be removed as a jQuery object.
  1260.          *
  1261.          * @fires document#menu-removing-item Passes the element to be removed.
  1262.          */
  1263.         removeMenuItem : function(el) {
  1264.             var children = el.childMenuItems();
  1265.  
  1266.             $( document ).trigger( 'menu-removing-item', [ el ] );
  1267.             el.addClass('deleting').animate({
  1268.                     opacity : 0,
  1269.                     height: 0
  1270.                 }, 350, function() {
  1271.                     var ins = $('#menu-instructions');
  1272.                     el.remove();
  1273.                     children.shiftDepthClass( -1 ).updateParentMenuItemDBId();
  1274.                     if ( 0 === $( '#menu-to-edit li' ).length ) {
  1275.                         $( '.drag-instructions' ).hide();
  1276.                         ins.removeClass( 'menu-instructions-inactive' );
  1277.                     }
  1278.                     api.refreshAdvancedAccessibility();
  1279.                 });
  1280.         },
  1281.  
  1282.         depthToPx : function(depth) {
  1283.             return depth * api.options.menuItemDepthPerLevel;
  1284.         },
  1285.  
  1286.         pxToDepth : function(px) {
  1287.             return Math.floor(px / api.options.menuItemDepthPerLevel);
  1288.         }
  1289.  
  1290.     };
  1291.  
  1292.     $(document).ready(function(){ wpNavMenu.init(); });
  1293.  
  1294. })(jQuery);
  1295.