| [ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
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>«</a></div>'); 775 arrowRight = $('<div class="nav-tabs-arrow nav-tabs-arrow-right"><a>»</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);
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Aug 22 03:56:18 2012 | Hosted by follow the white rabbit. |