]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-admin/js/nav-menu.dev.js
WordPress 3.4
[autoinstalls/wordpress.git] / wp-admin / js / nav-menu.dev.js
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 var wpNavMenu;
12
13 (function($) {
14
15         var api = wpNavMenu = {
16
17                 options : {
18                         menuItemDepthPerLevel : 30, // Do not use directly. Use depthToPx and pxToDepth instead.
19                         globalMaxDepth : 11
20                 },
21
22                 menuList : undefined,   // Set in init.
23                 targetList : undefined, // Set in init.
24                 menusChanged : false,
25                 isRTL: !! ( 'undefined' != typeof isRtl && isRtl ),
26                 negateIfRTL: ( 'undefined' != typeof isRtl && isRtl ) ? -1 : 1,
27
28                 // Functions that run on init.
29                 init : function() {
30                         api.menuList = $('#menu-to-edit');
31                         api.targetList = api.menuList;
32
33                         this.jQueryExtensions();
34
35                         this.attachMenuEditListeners();
36
37                         this.setupInputWithDefaultTitle();
38                         this.attachQuickSearchListeners();
39                         this.attachThemeLocationsListeners();
40
41                         this.attachTabsPanelListeners();
42
43                         this.attachUnsavedChangesListener();
44
45                         if( api.menuList.length ) // If no menu, we're in the + tab.
46                                 this.initSortables();
47
48                         this.initToggles();
49
50                         this.initTabManager();
51                 },
52
53                 jQueryExtensions : function() {
54                         // jQuery extensions
55                         $.fn.extend({
56                                 menuItemDepth : function() {
57                                         var margin = api.isRTL ? this.eq(0).css('margin-right') : this.eq(0).css('margin-left');
58                                         return api.pxToDepth( margin && -1 != margin.indexOf('px') ? margin.slice(0, -2) : 0 );
59                                 },
60                                 updateDepthClass : function(current, prev) {
61                                         return this.each(function(){
62                                                 var t = $(this);
63                                                 prev = prev || t.menuItemDepth();
64                                                 $(this).removeClass('menu-item-depth-'+ prev )
65                                                         .addClass('menu-item-depth-'+ current );
66                                         });
67                                 },
68                                 shiftDepthClass : function(change) {
69                                         return this.each(function(){
70                                                 var t = $(this),
71                                                         depth = t.menuItemDepth();
72                                                 $(this).removeClass('menu-item-depth-'+ depth )
73                                                         .addClass('menu-item-depth-'+ (depth + change) );
74                                         });
75                                 },
76                                 childMenuItems : function() {
77                                         var result = $();
78                                         this.each(function(){
79                                                 var t = $(this), depth = t.menuItemDepth(), next = t.next();
80                                                 while( next.length && next.menuItemDepth() > depth ) {
81                                                         result = result.add( next );
82                                                         next = next.next();
83                                                 }
84                                         });
85                                         return result;
86                                 },
87                                 updateParentMenuItemDBId : function() {
88                                         return this.each(function(){
89                                                 var item = $(this),
90                                                         input = item.find('.menu-item-data-parent-id'),
91                                                         depth = item.menuItemDepth(),
92                                                         parent = item.prev();
93
94                                                 if( depth == 0 ) { // Item is on the top level, has no parent
95                                                         input.val(0);
96                                                 } else { // Find the parent item, and retrieve its object id.
97                                                         while( ! parent[0] || ! parent[0].className || -1 == parent[0].className.indexOf('menu-item') || ( parent.menuItemDepth() != depth - 1 ) )
98                                                                 parent = parent.prev();
99                                                         input.val( parent.find('.menu-item-data-db-id').val() );
100                                                 }
101                                         });
102                                 },
103                                 hideAdvancedMenuItemFields : function() {
104                                         return this.each(function(){
105                                                 var that = $(this);
106                                                 $('.hide-column-tog').not(':checked').each(function(){
107                                                         that.find('.field-' + $(this).val() ).addClass('hidden-field');
108                                                 });
109                                         });
110                                 },
111                                 /**
112                                  * Adds selected menu items to the menu.
113                                  *
114                                  * @param jQuery metabox The metabox jQuery object.
115                                  */
116                                 addSelectedToMenu : function(processMethod) {
117                                         if ( 0 == $('#menu-to-edit').length ) {
118                                                 return false;
119                                         }
120
121                                         return this.each(function() {
122                                                 var t = $(this), menuItems = {},
123                                                         checkboxes = t.find('.tabs-panel-active .categorychecklist li input:checked'),
124                                                         re = new RegExp('menu-item\\[(\[^\\]\]*)');
125
126                                                 processMethod = processMethod || api.addMenuItemToBottom;
127
128                                                 // If no items are checked, bail.
129                                                 if ( !checkboxes.length )
130                                                         return false;
131
132                                                 // Show the ajax spinner
133                                                 t.find('img.waiting').show();
134
135                                                 // Retrieve menu item data
136                                                 $(checkboxes).each(function(){
137                                                         var t = $(this),
138                                                                 listItemDBIDMatch = re.exec( t.attr('name') ),
139                                                                 listItemDBID = 'undefined' == typeof listItemDBIDMatch[1] ? 0 : parseInt(listItemDBIDMatch[1], 10);
140                                                         if ( this.className && -1 != this.className.indexOf('add-to-top') )
141                                                                 processMethod = api.addMenuItemToTop;
142                                                         menuItems[listItemDBID] = t.closest('li').getItemData( 'add-menu-item', listItemDBID );
143                                                 });
144
145                                                 // Add the items
146                                                 api.addItemToMenu(menuItems, processMethod, function(){
147                                                         // Deselect the items and hide the ajax spinner
148                                                         checkboxes.removeAttr('checked');
149                                                         t.find('img.waiting').hide();
150                                                 });
151                                         });
152                                 },
153                                 getItemData : function( itemType, id ) {
154                                         itemType = itemType || 'menu-item';
155
156                                         var itemData = {}, i,
157                                         fields = [
158                                                 'menu-item-db-id',
159                                                 'menu-item-object-id',
160                                                 'menu-item-object',
161                                                 'menu-item-parent-id',
162                                                 'menu-item-position',
163                                                 'menu-item-type',
164                                                 'menu-item-title',
165                                                 'menu-item-url',
166                                                 'menu-item-description',
167                                                 'menu-item-attr-title',
168                                                 'menu-item-target',
169                                                 'menu-item-classes',
170                                                 'menu-item-xfn'
171                                         ];
172
173                                         if( !id && itemType == 'menu-item' ) {
174                                                 id = this.find('.menu-item-data-db-id').val();
175                                         }
176
177                                         if( !id ) return itemData;
178
179                                         this.find('input').each(function() {
180                                                 var field;
181                                                 i = fields.length;
182                                                 while ( i-- ) {
183                                                         if( itemType == 'menu-item' )
184                                                                 field = fields[i] + '[' + id + ']';
185                                                         else if( itemType == 'add-menu-item' )
186                                                                 field = 'menu-item[' + id + '][' + fields[i] + ']';
187
188                                                         if (
189                                                                 this.name &&
190                                                                 field == this.name
191                                                         ) {
192                                                                 itemData[fields[i]] = this.value;
193                                                         }
194                                                 }
195                                         });
196
197                                         return itemData;
198                                 },
199                                 setItemData : function( itemData, itemType, id ) { // Can take a type, such as 'menu-item', or an id.
200                                         itemType = itemType || 'menu-item';
201
202                                         if( !id && itemType == 'menu-item' ) {
203                                                 id = $('.menu-item-data-db-id', this).val();
204                                         }
205
206                                         if( !id ) return this;
207
208                                         this.find('input').each(function() {
209                                                 var t = $(this), field;
210                                                 $.each( itemData, function( attr, val ) {
211                                                         if( itemType == 'menu-item' )
212                                                                 field = attr + '[' + id + ']';
213                                                         else if( itemType == 'add-menu-item' )
214                                                                 field = 'menu-item[' + id + '][' + attr + ']';
215
216                                                         if ( field == t.attr('name') ) {
217                                                                 t.val( val );
218                                                         }
219                                                 });
220                                         });
221                                         return this;
222                                 }
223                         });
224                 },
225
226                 initToggles : function() {
227                         // init postboxes
228                         postboxes.add_postbox_toggles('nav-menus');
229
230                         // adjust columns functions for menus UI
231                         columns.useCheckboxesForHidden();
232                         columns.checked = function(field) {
233                                 $('.field-' + field).removeClass('hidden-field');
234                         }
235                         columns.unchecked = function(field) {
236                                 $('.field-' + field).addClass('hidden-field');
237                         }
238                         // hide fields
239                         api.menuList.hideAdvancedMenuItemFields();
240                 },
241
242                 initSortables : function() {
243                         var currentDepth = 0, originalDepth, minDepth, maxDepth,
244                                 prev, next, prevBottom, nextThreshold, helperHeight, transport,
245                                 menuEdge = api.menuList.offset().left,
246                                 body = $('body'), maxChildDepth,
247                                 menuMaxDepth = initialMenuMaxDepth();
248
249                         // Use the right edge if RTL.
250                         menuEdge += api.isRTL ? api.menuList.width() : 0;
251
252                         api.menuList.sortable({
253                                 handle: '.menu-item-handle',
254                                 placeholder: 'sortable-placeholder',
255                                 start: function(e, ui) {
256                                         var height, width, parent, children, tempHolder;
257
258                                         // handle placement for rtl orientation
259                                         if ( api.isRTL )
260                                                 ui.item[0].style.right = 'auto';
261
262                                         transport = ui.item.children('.menu-item-transport');
263
264                                         // Set depths. currentDepth must be set before children are located.
265                                         originalDepth = ui.item.menuItemDepth();
266                                         updateCurrentDepth(ui, originalDepth);
267
268                                         // Attach child elements to parent
269                                         // Skip the placeholder
270                                         parent = ( ui.item.next()[0] == ui.placeholder[0] ) ? ui.item.next() : ui.item;
271                                         children = parent.childMenuItems();
272                                         transport.append( children );
273
274                                         // Update the height of the placeholder to match the moving item.
275                                         height = transport.outerHeight();
276                                         // If there are children, account for distance between top of children and parent
277                                         height += ( height > 0 ) ? (ui.placeholder.css('margin-top').slice(0, -2) * 1) : 0;
278                                         height += ui.helper.outerHeight();
279                                         helperHeight = height;
280                                         height -= 2; // Subtract 2 for borders
281                                         ui.placeholder.height(height);
282
283                                         // Update the width of the placeholder to match the moving item.
284                                         maxChildDepth = originalDepth;
285                                         children.each(function(){
286                                                 var depth = $(this).menuItemDepth();
287                                                 maxChildDepth = (depth > maxChildDepth) ? depth : maxChildDepth;
288                                         });
289                                         width = ui.helper.find('.menu-item-handle').outerWidth(); // Get original width
290                                         width += api.depthToPx(maxChildDepth - originalDepth); // Account for children
291                                         width -= 2; // Subtract 2 for borders
292                                         ui.placeholder.width(width);
293
294                                         // Update the list of menu items.
295                                         tempHolder = ui.placeholder.next();
296                                         tempHolder.css( 'margin-top', helperHeight + 'px' ); // Set the margin to absorb the placeholder
297                                         ui.placeholder.detach(); // detach or jQuery UI will think the placeholder is a menu item
298                                         $(this).sortable( "refresh" ); // The children aren't sortable. We should let jQ UI know.
299                                         ui.item.after( ui.placeholder ); // reattach the placeholder.
300                                         tempHolder.css('margin-top', 0); // reset the margin
301
302                                         // Now that the element is complete, we can update...
303                                         updateSharedVars(ui);
304                                 },
305                                 stop: function(e, ui) {
306                                         var children, depthChange = currentDepth - originalDepth;
307
308                                         // Return child elements to the list
309                                         children = transport.children().insertAfter(ui.item);
310
311                                         // Update depth classes
312                                         if( depthChange != 0 ) {
313                                                 ui.item.updateDepthClass( currentDepth );
314                                                 children.shiftDepthClass( depthChange );
315                                                 updateMenuMaxDepth( depthChange );
316                                         }
317                                         // Register a change
318                                         api.registerChange();
319                                         // Update the item data.
320                                         ui.item.updateParentMenuItemDBId();
321
322                                         // address sortable's incorrectly-calculated top in opera
323                                         ui.item[0].style.top = 0;
324
325                                         // handle drop placement for rtl orientation
326                                         if ( api.isRTL ) {
327                                                 ui.item[0].style.left = 'auto';
328                                                 ui.item[0].style.right = 0;
329                                         }
330
331                                         // The width of the tab bar might have changed. Just in case.
332                                         api.refreshMenuTabs( true );
333                                 },
334                                 change: function(e, ui) {
335                                         // Make sure the placeholder is inside the menu.
336                                         // Otherwise fix it, or we're in trouble.
337                                         if( ! ui.placeholder.parent().hasClass('menu') )
338                                                 (prev.length) ? prev.after( ui.placeholder ) : api.menuList.prepend( ui.placeholder );
339
340                                         updateSharedVars(ui);
341                                 },
342                                 sort: function(e, ui) {
343                                         var offset = ui.helper.offset(),
344                                                 edge = api.isRTL ? offset.left + ui.helper.width() : offset.left,
345                                                 depth = api.negateIfRTL * api.pxToDepth( edge - menuEdge );
346                                         // Check and correct if depth is not within range.
347                                         // Also, if the dragged element is dragged upwards over
348                                         // an item, shift the placeholder to a child position.
349                                         if ( depth > maxDepth || offset.top < prevBottom ) depth = maxDepth;
350                                         else if ( depth < minDepth ) depth = minDepth;
351
352                                         if( depth != currentDepth )
353                                                 updateCurrentDepth(ui, depth);
354
355                                         // If we overlap the next element, manually shift downwards
356                                         if( nextThreshold && offset.top + helperHeight > nextThreshold ) {
357                                                 next.after( ui.placeholder );
358                                                 updateSharedVars( ui );
359                                                 $(this).sortable( "refreshPositions" );
360                                         }
361                                 }
362                         });
363
364                         function updateSharedVars(ui) {
365                                 var depth;
366
367                                 prev = ui.placeholder.prev();
368                                 next = ui.placeholder.next();
369
370                                 // Make sure we don't select the moving item.
371                                 if( prev[0] == ui.item[0] ) prev = prev.prev();
372                                 if( next[0] == ui.item[0] ) next = next.next();
373
374                                 prevBottom = (prev.length) ? prev.offset().top + prev.height() : 0;
375                                 nextThreshold = (next.length) ? next.offset().top + next.height() / 3 : 0;
376                                 minDepth = (next.length) ? next.menuItemDepth() : 0;
377
378                                 if( prev.length )
379                                         maxDepth = ( (depth = prev.menuItemDepth() + 1) > api.options.globalMaxDepth ) ? api.options.globalMaxDepth : depth;
380                                 else
381                                         maxDepth = 0;
382                         }
383
384                         function updateCurrentDepth(ui, depth) {
385                                 ui.placeholder.updateDepthClass( depth, currentDepth );
386                                 currentDepth = depth;
387                         }
388
389                         function initialMenuMaxDepth() {
390                                 if( ! body[0].className ) return 0;
391                                 var match = body[0].className.match(/menu-max-depth-(\d+)/);
392                                 return match && match[1] ? parseInt(match[1]) : 0;
393                         }
394
395                         function updateMenuMaxDepth( depthChange ) {
396                                 var depth, newDepth = menuMaxDepth;
397                                 if ( depthChange === 0 ) {
398                                         return;
399                                 } else if ( depthChange > 0 ) {
400                                         depth = maxChildDepth + depthChange;
401                                         if( depth > menuMaxDepth )
402                                                 newDepth = depth;
403                                 } else if ( depthChange < 0 && maxChildDepth == menuMaxDepth ) {
404                                         while( ! $('.menu-item-depth-' + newDepth, api.menuList).length && newDepth > 0 )
405                                                 newDepth--;
406                                 }
407                                 // Update the depth class.
408                                 body.removeClass( 'menu-max-depth-' + menuMaxDepth ).addClass( 'menu-max-depth-' + newDepth );
409                                 menuMaxDepth = newDepth;
410                         }
411                 },
412
413                 attachMenuEditListeners : function() {
414                         var that = this;
415                         $('#update-nav-menu').bind('click', function(e) {
416                                 if ( e.target && e.target.className ) {
417                                         if ( -1 != e.target.className.indexOf('item-edit') ) {
418                                                 return that.eventOnClickEditLink(e.target);
419                                         } else if ( -1 != e.target.className.indexOf('menu-save') ) {
420                                                 return that.eventOnClickMenuSave(e.target);
421                                         } else if ( -1 != e.target.className.indexOf('menu-delete') ) {
422                                                 return that.eventOnClickMenuDelete(e.target);
423                                         } else if ( -1 != e.target.className.indexOf('item-delete') ) {
424                                                 return that.eventOnClickMenuItemDelete(e.target);
425                                         } else if ( -1 != e.target.className.indexOf('item-cancel') ) {
426                                                 return that.eventOnClickCancelLink(e.target);
427                                         }
428                                 }
429                         });
430                         $('#add-custom-links input[type="text"]').keypress(function(e){
431                                 if ( e.keyCode === 13 ) {
432                                         e.preventDefault();
433                                         $("#submit-customlinkdiv").click();
434                                 }
435                         });
436                 },
437
438                 /**
439                  * An interface for managing default values for input elements
440                  * that is both JS and accessibility-friendly.
441                  *
442                  * Input elements that add the class 'input-with-default-title'
443                  * will have their values set to the provided HTML title when empty.
444                  */
445                 setupInputWithDefaultTitle : function() {
446                         var name = 'input-with-default-title';
447
448                         $('.' + name).each( function(){
449                                 var $t = $(this), title = $t.attr('title'), val = $t.val();
450                                 $t.data( name, title );
451
452                                 if( '' == val ) $t.val( title );
453                                 else if ( title == val ) return;
454                                 else $t.removeClass( name );
455                         }).focus( function(){
456                                 var $t = $(this);
457                                 if( $t.val() == $t.data(name) )
458                                         $t.val('').removeClass( name );
459                         }).blur( function(){
460                                 var $t = $(this);
461                                 if( '' == $t.val() )
462                                         $t.addClass( name ).val( $t.data(name) );
463                         });
464                 },
465
466                 attachThemeLocationsListeners : function() {
467                         var loc = $('#nav-menu-theme-locations'), params = {};
468                         params['action'] = 'menu-locations-save';
469                         params['menu-settings-column-nonce'] = $('#menu-settings-column-nonce').val();
470                         loc.find('input[type="submit"]').click(function() {
471                                 loc.find('select').each(function() {
472                                         params[this.name] = $(this).val();
473                                 });
474                                 loc.find('.waiting').show();
475                                 $.post( ajaxurl, params, function(r) {
476                                         loc.find('.waiting').hide();
477                                 });
478                                 return false;
479                         });
480                 },
481
482                 attachQuickSearchListeners : function() {
483                         var searchTimer;
484
485                         $('.quick-search').keypress(function(e){
486                                 var t = $(this);
487
488                                 if( 13 == e.which ) {
489                                         api.updateQuickSearchResults( t );
490                                         return false;
491                                 }
492
493                                 if( searchTimer ) clearTimeout(searchTimer);
494
495                                 searchTimer = setTimeout(function(){
496                                         api.updateQuickSearchResults( t );
497                                 }, 400);
498                         }).attr('autocomplete','off');
499                 },
500
501                 updateQuickSearchResults : function(input) {
502                         var panel, params,
503                         minSearchLength = 2,
504                         q = input.val();
505
506                         if( q.length < minSearchLength ) return;
507
508                         panel = input.parents('.tabs-panel');
509                         params = {
510                                 'action': 'menu-quick-search',
511                                 'response-format': 'markup',
512                                 'menu': $('#menu').val(),
513                                 'menu-settings-column-nonce': $('#menu-settings-column-nonce').val(),
514                                 'q': q,
515                                 'type': input.attr('name')
516                         };
517
518                         $('img.waiting', panel).show();
519
520                         $.post( ajaxurl, params, function(menuMarkup) {
521                                 api.processQuickSearchQueryResponse(menuMarkup, params, panel);
522                         });
523                 },
524
525                 addCustomLink : function( processMethod ) {
526                         var url = $('#custom-menu-item-url').val(),
527                                 label = $('#custom-menu-item-name').val();
528
529                         processMethod = processMethod || api.addMenuItemToBottom;
530
531                         if ( '' == url || 'http://' == url )
532                                 return false;
533
534                         // Show the ajax spinner
535                         $('.customlinkdiv img.waiting').show();
536                         this.addLinkToMenu( url, label, processMethod, function() {
537                                 // Remove the ajax spinner
538                                 $('.customlinkdiv img.waiting').hide();
539                                 // Set custom link form back to defaults
540                                 $('#custom-menu-item-name').val('').blur();
541                                 $('#custom-menu-item-url').val('http://');
542                         });
543                 },
544
545                 addLinkToMenu : function(url, label, processMethod, callback) {
546                         processMethod = processMethod || api.addMenuItemToBottom;
547                         callback = callback || function(){};
548
549                         api.addItemToMenu({
550                                 '-1': {
551                                         'menu-item-type': 'custom',
552                                         'menu-item-url': url,
553                                         'menu-item-title': label
554                                 }
555                         }, processMethod, callback);
556                 },
557
558                 addItemToMenu : function(menuItem, processMethod, callback) {
559                         var menu = $('#menu').val(),
560                                 nonce = $('#menu-settings-column-nonce').val();
561
562                         processMethod = processMethod || function(){};
563                         callback = callback || function(){};
564
565                         params = {
566                                 'action': 'add-menu-item',
567                                 'menu': menu,
568                                 'menu-settings-column-nonce': nonce,
569                                 'menu-item': menuItem
570                         };
571
572                         $.post( ajaxurl, params, function(menuMarkup) {
573                                 var ins = $('#menu-instructions');
574                                 processMethod(menuMarkup, params);
575                                 if( ! ins.hasClass('menu-instructions-inactive') && ins.siblings().length )
576                                         ins.addClass('menu-instructions-inactive');
577                                 callback();
578                         });
579                 },
580
581                 /**
582                  * Process the add menu item request response into menu list item.
583                  *
584                  * @param string menuMarkup The text server response of menu item markup.
585                  * @param object req The request arguments.
586                  */
587                 addMenuItemToBottom : function( menuMarkup, req ) {
588                         $(menuMarkup).hideAdvancedMenuItemFields().appendTo( api.targetList );
589                 },
590
591                 addMenuItemToTop : function( menuMarkup, req ) {
592                         $(menuMarkup).hideAdvancedMenuItemFields().prependTo( api.targetList );
593                 },
594
595                 attachUnsavedChangesListener : function() {
596                         $('#menu-management input, #menu-management select, #menu-management, #menu-management textarea').change(function(){
597                                 api.registerChange();
598                         });
599
600                         if ( 0 != $('#menu-to-edit').length ) {
601                                 window.onbeforeunload = function(){
602                                         if ( api.menusChanged )
603                                                 return navMenuL10n.saveAlert;
604                                 };
605                         } else {
606                                 // Make the post boxes read-only, as they can't be used yet
607                                 $('#menu-settings-column').find('input,select').prop('disabled', true).end().find('a').attr('href', '#').unbind('click');
608                         }
609                 },
610
611                 registerChange : function() {
612                         api.menusChanged = true;
613                 },
614
615                 attachTabsPanelListeners : function() {
616                         $('#menu-settings-column').bind('click', function(e) {
617                                 var selectAreaMatch, panelId, wrapper, items,
618                                         target = $(e.target);
619
620                                 if ( target.hasClass('nav-tab-link') ) {
621                                         panelId = /#(.*)$/.exec(e.target.href);
622                                         if ( panelId && panelId[1] )
623                                                 panelId = panelId[1]
624                                         else
625                                                 return false;
626
627                                         wrapper = target.parents('.inside').first();
628
629                                         // upon changing tabs, we want to uncheck all checkboxes
630                                         $('input', wrapper).removeAttr('checked');
631
632                                         $('.tabs-panel-active', wrapper).removeClass('tabs-panel-active').addClass('tabs-panel-inactive');
633                                         $('#' + panelId, wrapper).removeClass('tabs-panel-inactive').addClass('tabs-panel-active');
634
635                                         $('.tabs', wrapper).removeClass('tabs');
636                                         target.parent().addClass('tabs');
637
638                                         // select the search bar
639                                         $('.quick-search', wrapper).focus();
640
641                                         return false;
642                                 } else if ( target.hasClass('select-all') ) {
643                                         selectAreaMatch = /#(.*)$/.exec(e.target.href);
644                                         if ( selectAreaMatch && selectAreaMatch[1] ) {
645                                                 items = $('#' + selectAreaMatch[1] + ' .tabs-panel-active .menu-item-title input');
646                                                 if( items.length === items.filter(':checked').length )
647                                                         items.removeAttr('checked');
648                                                 else
649                                                         items.prop('checked', true);
650                                                 return false;
651                                         }
652                                 } else if ( target.hasClass('submit-add-to-menu') ) {
653                                         api.registerChange();
654
655                                         if ( e.target.id && 'submit-customlinkdiv' == e.target.id )
656                                                 api.addCustomLink( api.addMenuItemToBottom );
657                                         else if ( e.target.id && -1 != e.target.id.indexOf('submit-') )
658                                                 $('#' + e.target.id.replace(/submit-/, '')).addSelectedToMenu( api.addMenuItemToBottom );
659                                         return false;
660                                 } else if ( target.hasClass('page-numbers') ) {
661                                         $.post( ajaxurl, e.target.href.replace(/.*\?/, '').replace(/action=([^&]*)/, '') + '&action=menu-get-metabox',
662                                                 function( resp ) {
663                                                         if ( -1 == resp.indexOf('replace-id') )
664                                                                 return;
665
666                                                         var metaBoxData = $.parseJSON(resp),
667                                                         toReplace = document.getElementById(metaBoxData['replace-id']),
668                                                         placeholder = document.createElement('div'),
669                                                         wrap = document.createElement('div');
670
671                                                         if ( ! metaBoxData['markup'] || ! toReplace )
672                                                                 return;
673
674                                                         wrap.innerHTML = metaBoxData['markup'] ? metaBoxData['markup'] : '';
675
676                                                         toReplace.parentNode.insertBefore( placeholder, toReplace );
677                                                         placeholder.parentNode.removeChild( toReplace );
678
679                                                         placeholder.parentNode.insertBefore( wrap, placeholder );
680
681                                                         placeholder.parentNode.removeChild( placeholder );
682
683                                                 }
684                                         );
685
686                                         return false;
687                                 }
688                         });
689                 },
690
691                 initTabManager : function() {
692                         var fixed = $('.nav-tabs-wrapper'),
693                                 fluid = fixed.children('.nav-tabs'),
694                                 active = fluid.children('.nav-tab-active'),
695                                 tabs = fluid.children('.nav-tab'),
696                                 tabsWidth = 0,
697                                 fixedRight, fixedLeft,
698                                 arrowLeft, arrowRight, resizeTimer, css = {},
699                                 marginFluid = api.isRTL ? 'margin-right' : 'margin-left',
700                                 marginFixed = api.isRTL ? 'margin-left' : 'margin-right',
701                                 msPerPx = 2;
702
703                         /**
704                          * Refreshes the menu tabs.
705                          * Will show and hide arrows where necessary.
706                          * Scrolls to the active tab by default.
707                          *
708                          * @param savePosition {boolean} Optional. Prevents scrolling so
709                          *                that the current position is maintained. Default false.
710                          **/
711                         api.refreshMenuTabs = function( savePosition ) {
712                                 var fixedWidth = fixed.width(),
713                                         margin = 0, css = {};
714                                 fixedLeft = fixed.offset().left;
715                                 fixedRight = fixedLeft + fixedWidth;
716
717                                 if( !savePosition )
718                                         active.makeTabVisible();
719
720                                 // Prevent space from building up next to the last tab if there's more to show
721                                 if( tabs.last().isTabVisible() ) {
722                                         margin = fixed.width() - tabsWidth;
723                                         margin = margin > 0 ? 0 : margin;
724                                         css[marginFluid] = margin + 'px';
725                                         fluid.animate( css, 100, "linear" );
726                                 }
727
728                                 // Show the arrows only when necessary
729                                 if( fixedWidth > tabsWidth )
730                                         arrowLeft.add( arrowRight ).hide();
731                                 else
732                                         arrowLeft.add( arrowRight ).show();
733                         }
734
735                         $.fn.extend({
736                                 makeTabVisible : function() {
737                                         var t = this.eq(0), left, right, css = {}, shift = 0;
738
739                                         if( ! t.length ) return this;
740
741                                         left = t.offset().left;
742                                         right = left + t.outerWidth();
743
744                                         if( right > fixedRight )
745                                                 shift = fixedRight - right;
746                                         else if ( left < fixedLeft )
747                                                 shift = fixedLeft - left;
748
749                                         if( ! shift ) return this;
750
751                                         css[marginFluid] = "+=" + api.negateIfRTL * shift + 'px';
752                                         fluid.animate( css, Math.abs( shift ) * msPerPx, "linear" );
753                                         return this;
754                                 },
755                                 isTabVisible : function() {
756                                         var t = this.eq(0),
757                                                 left = t.offset().left,
758                                                 right = left + t.outerWidth();
759                                         return ( right <= fixedRight && left >= fixedLeft ) ? true : false;
760                                 }
761                         });
762
763                         // Find the width of all tabs
764                         tabs.each(function(){
765                                 tabsWidth += $(this).outerWidth(true);
766                         });
767
768                         // Set up fixed margin for overflow, unset padding
769                         css['padding'] = 0;
770                         css[marginFixed] = (-1 * tabsWidth) + 'px';
771                         fluid.css( css );
772
773                         // Build tab navigation
774                         arrowLeft = $('<div class="nav-tabs-arrow nav-tabs-arrow-left"><a>&laquo;</a></div>');
775                         arrowRight = $('<div class="nav-tabs-arrow nav-tabs-arrow-right"><a>&raquo;</a></div>');
776                         // Attach to the document
777                         fixed.wrap('<div class="nav-tabs-nav"/>').parent().prepend( arrowLeft ).append( arrowRight );
778
779                         // Set the menu tabs
780                         api.refreshMenuTabs();
781                         // Make sure the tabs reset on resize
782                         $(window).resize(function() {
783                                 if( resizeTimer ) clearTimeout(resizeTimer);
784                                 resizeTimer = setTimeout( api.refreshMenuTabs, 200);
785                         });
786
787                         // Build arrow functions
788                         $.each([{
789                                         arrow : arrowLeft,
790                                         next : "next",
791                                         last : "first",
792                                         operator : "+="
793                                 },{
794                                         arrow : arrowRight,
795                                         next : "prev",
796                                         last : "last",
797                                         operator : "-="
798                                 }], function(){
799                                 var that = this;
800                                 this.arrow.mousedown(function(){
801                                         var marginFluidVal = Math.abs( parseInt( fluid.css(marginFluid) ) ),
802                                                 shift = marginFluidVal,
803                                                 css = {};
804
805                                         if( "-=" == that.operator )
806                                                 shift = Math.abs( tabsWidth - fixed.width() ) - marginFluidVal;
807
808                                         if( ! shift ) return;
809
810                                         css[marginFluid] = that.operator + shift + 'px';
811                                         fluid.animate( css, shift * msPerPx, "linear" );
812                                 }).mouseup(function(){
813                                         var tab, next;
814                                         fluid.stop(true);
815                                         tab = tabs[that.last]();
816                                         while( (next = tab[that.next]()) && next.length && ! next.isTabVisible() ) {
817                                                 tab = next;
818                                         }
819                                         tab.makeTabVisible();
820                                 });
821                         });
822                 },
823
824                 eventOnClickEditLink : function(clickedEl) {
825                         var settings, item,
826                         matchedSection = /#(.*)$/.exec(clickedEl.href);
827                         if ( matchedSection && matchedSection[1] ) {
828                                 settings = $('#'+matchedSection[1]);
829                                 item = settings.parent();
830                                 if( 0 != item.length ) {
831                                         if( item.hasClass('menu-item-edit-inactive') ) {
832                                                 if( ! settings.data('menu-item-data') ) {
833                                                         settings.data( 'menu-item-data', settings.getItemData() );
834                                                 }
835                                                 settings.slideDown('fast');
836                                                 item.removeClass('menu-item-edit-inactive')
837                                                         .addClass('menu-item-edit-active');
838                                         } else {
839                                                 settings.slideUp('fast');
840                                                 item.removeClass('menu-item-edit-active')
841                                                         .addClass('menu-item-edit-inactive');
842                                         }
843                                         return false;
844                                 }
845                         }
846                 },
847
848                 eventOnClickCancelLink : function(clickedEl) {
849                         var settings = $(clickedEl).closest('.menu-item-settings');
850                         settings.setItemData( settings.data('menu-item-data') );
851                         return false;
852                 },
853
854                 eventOnClickMenuSave : function(clickedEl) {
855                         var locs = '',
856                         menuName = $('#menu-name'),
857                         menuNameVal = menuName.val();
858                         // Cancel and warn if invalid menu name
859                         if( !menuNameVal || menuNameVal == menuName.attr('title') || !menuNameVal.replace(/\s+/, '') ) {
860                                 menuName.parent().addClass('form-invalid');
861                                 return false;
862                         }
863                         // Copy menu theme locations
864                         $('#nav-menu-theme-locations select').each(function() {
865                                 locs += '<input type="hidden" name="' + this.name + '" value="' + $(this).val() + '" />';
866                         });
867                         $('#update-nav-menu').append( locs );
868                         // Update menu item position data
869                         api.menuList.find('.menu-item-data-position').val( function(index) { return index + 1; } );
870                         window.onbeforeunload = null;
871
872                         return true;
873                 },
874
875                 eventOnClickMenuDelete : function(clickedEl) {
876                         // Delete warning AYS
877                         if ( confirm( navMenuL10n.warnDeleteMenu ) ) {
878                                 window.onbeforeunload = null;
879                                 return true;
880                         }
881                         return false;
882                 },
883
884                 eventOnClickMenuItemDelete : function(clickedEl) {
885                         var itemID = parseInt(clickedEl.id.replace('delete-', ''), 10);
886                         api.removeMenuItem( $('#menu-item-' + itemID) );
887                         api.registerChange();
888                         return false;
889                 },
890
891                 /**
892                  * Process the quick search response into a search result
893                  *
894                  * @param string resp The server response to the query.
895                  * @param object req The request arguments.
896                  * @param jQuery panel The tabs panel we're searching in.
897                  */
898                 processQuickSearchQueryResponse : function(resp, req, panel) {
899                         var matched, newID,
900                         takenIDs = {},
901                         form = document.getElementById('nav-menu-meta'),
902                         pattern = new RegExp('menu-item\\[(\[^\\]\]*)', 'g'),
903                         $items = $('<div>').html(resp).find('li'),
904                         $item;
905
906                         if( ! $items.length ) {
907                                 $('.categorychecklist', panel).html( '<li><p>' + navMenuL10n.noResultsFound + '</p></li>' );
908                                 $('img.waiting', panel).hide();
909                                 return;
910                         }
911
912                         $items.each(function(){
913                                 $item = $(this);
914
915                                 // make a unique DB ID number
916                                 matched = pattern.exec($item.html());
917
918                                 if ( matched && matched[1] ) {
919                                         newID = matched[1];
920                                         while( form.elements['menu-item[' + newID + '][menu-item-type]'] || takenIDs[ newID ] ) {
921                                                 newID--;
922                                         }
923
924                                         takenIDs[newID] = true;
925                                         if ( newID != matched[1] ) {
926                                                 $item.html( $item.html().replace(new RegExp(
927                                                         'menu-item\\[' + matched[1] + '\\]', 'g'),
928                                                         'menu-item[' + newID + ']'
929                                                 ) );
930                                         }
931                                 }
932                         });
933
934                         $('.categorychecklist', panel).html( $items );
935                         $('img.waiting', panel).hide();
936                 },
937
938                 removeMenuItem : function(el) {
939                         var children = el.childMenuItems();
940
941                         el.addClass('deleting').animate({
942                                         opacity : 0,
943                                         height: 0
944                                 }, 350, function() {
945                                         var ins = $('#menu-instructions');
946                                         el.remove();
947                                         children.shiftDepthClass(-1).updateParentMenuItemDBId();
948                                         if( ! ins.siblings().length )
949                                                 ins.removeClass('menu-instructions-inactive');
950                                 });
951                 },
952
953                 depthToPx : function(depth) {
954                         return depth * api.options.menuItemDepthPerLevel;
955                 },
956
957                 pxToDepth : function(px) {
958                         return Math.floor(px / api.options.menuItemDepthPerLevel);
959                 }
960
961         };
962
963         $(document).ready(function(){ wpNavMenu.init(); });
964
965 })(jQuery);