| [ Root ] [ Search ] [ Index ] |
PHP Cross Reference of WordPress 3.0.1Provided by Yoast |
[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 }, 431 432 /** 433 * An interface for managing default values for input elements 434 * that is both JS and accessibility-friendly. 435 * 436 * Input elements that add the class 'input-with-default-title' 437 * will have their values set to the provided HTML title when empty. 438 */ 439 setupInputWithDefaultTitle : function() { 440 var name = 'input-with-default-title'; 441 442 $('.' + name).each( function(){ 443 var $t = $(this), title = $t.attr('title'), val = $t.val(); 444 $t.data( name, title ); 445 446 if( '' == val ) $t.val( title ); 447 else if ( title == val ) return; 448 else $t.removeClass( name ); 449 }).focus( function(){ 450 var $t = $(this); 451 if( $t.val() == $t.data(name) ) 452 $t.val('').removeClass( name ); 453 }).blur( function(){ 454 var $t = $(this); 455 if( '' == $t.val() ) 456 $t.addClass( name ).val( $t.data(name) ); 457 }); 458 }, 459 460 attachThemeLocationsListeners : function() { 461 var loc = $('#nav-menu-theme-locations'), params = {}; 462 params['action'] = 'menu-locations-save'; 463 params['menu-settings-column-nonce'] = $('#menu-settings-column-nonce').val(); 464 loc.find('input[type=submit]').click(function() { 465 loc.find('select').each(function() { 466 params[this.name] = $(this).val(); 467 }); 468 loc.find('.waiting').show(); 469 $.post( ajaxurl, params, function(r) { 470 loc.find('.waiting').hide(); 471 }); 472 return false; 473 }); 474 }, 475 476 attachQuickSearchListeners : function() { 477 var searchTimer; 478 479 $('.quick-search').keypress(function(e){ 480 var t = $(this); 481 482 if( 13 == e.which ) { 483 api.updateQuickSearchResults( t ); 484 return false; 485 } 486 487 if( searchTimer ) clearTimeout(searchTimer); 488 489 searchTimer = setTimeout(function(){ 490 api.updateQuickSearchResults( t ); 491 }, 400); 492 }).attr('autocomplete','off'); 493 }, 494 495 updateQuickSearchResults : function(input) { 496 var panel, params, 497 minSearchLength = 2, 498 q = input.val(); 499 500 if( q.length < minSearchLength ) return; 501 502 panel = input.parents('.tabs-panel'); 503 params = { 504 'action': 'menu-quick-search', 505 'response-format': 'markup', 506 'menu': $('#menu').val(), 507 'menu-settings-column-nonce': $('#menu-settings-column-nonce').val(), 508 'q': q, 509 'type': input.attr('name') 510 }; 511 512 $('img.waiting', panel).show(); 513 514 $.post( ajaxurl, params, function(menuMarkup) { 515 api.processQuickSearchQueryResponse(menuMarkup, params, panel); 516 }); 517 }, 518 519 addCustomLink : function( processMethod ) { 520 var url = $('#custom-menu-item-url').val(), 521 label = $('#custom-menu-item-name').val(); 522 523 processMethod = processMethod || api.addMenuItemToBottom; 524 525 if ( '' == url || 'http://' == url ) 526 return false; 527 528 // Show the ajax spinner 529 $('.customlinkdiv img.waiting').show(); 530 this.addLinkToMenu( url, label, processMethod, function() { 531 // Remove the ajax spinner 532 $('.customlinkdiv img.waiting').hide(); 533 // Set custom link form back to defaults 534 $('#custom-menu-item-name').val('').blur(); 535 $('#custom-menu-item-url').val('http://'); 536 }); 537 }, 538 539 addLinkToMenu : function(url, label, processMethod, callback) { 540 processMethod = processMethod || api.addMenuItemToBottom; 541 callback = callback || function(){}; 542 543 api.addItemToMenu({ 544 '-1': { 545 'menu-item-type': 'custom', 546 'menu-item-url': url, 547 'menu-item-title': label 548 } 549 }, processMethod, callback); 550 }, 551 552 addItemToMenu : function(menuItem, processMethod, callback) { 553 var menu = $('#menu').val(), 554 nonce = $('#menu-settings-column-nonce').val(); 555 556 processMethod = processMethod || function(){}; 557 callback = callback || function(){}; 558 559 params = { 560 'action': 'add-menu-item', 561 'menu': menu, 562 'menu-settings-column-nonce': nonce, 563 'menu-item': menuItem 564 }; 565 566 $.post( ajaxurl, params, function(menuMarkup) { 567 var ins = $('#menu-instructions'); 568 processMethod(menuMarkup, params); 569 if( ! ins.hasClass('menu-instructions-inactive') && ins.siblings().length ) 570 ins.addClass('menu-instructions-inactive'); 571 callback(); 572 }); 573 }, 574 575 /** 576 * Process the add menu item request response into menu list item. 577 * 578 * @param string menuMarkup The text server response of menu item markup. 579 * @param object req The request arguments. 580 */ 581 addMenuItemToBottom : function( menuMarkup, req ) { 582 $(menuMarkup).hideAdvancedMenuItemFields().appendTo( api.targetList ); 583 }, 584 585 addMenuItemToTop : function( menuMarkup, req ) { 586 $(menuMarkup).hideAdvancedMenuItemFields().prependTo( api.targetList ); 587 }, 588 589 attachUnsavedChangesListener : function() { 590 $('#menu-management input, #menu-management select, #menu-management, #menu-management textarea').change(function(){ 591 api.registerChange(); 592 }); 593 594 if ( 0 != $('#menu-to-edit').length ) { 595 window.onbeforeunload = function(){ 596 if ( api.menusChanged ) 597 return navMenuL10n.saveAlert; 598 }; 599 } else { 600 // Make the post boxes read-only, as they can't be used yet 601 $('#menu-settings-column').find('input,select').attr('disabled', 'disabled').end().find('a').attr('href', '#').unbind('click'); 602 } 603 }, 604 605 registerChange : function() { 606 api.menusChanged = true; 607 }, 608 609 attachTabsPanelListeners : function() { 610 $('#menu-settings-column').bind('click', function(e) { 611 var selectAreaMatch, panelId, wrapper, items, 612 target = $(e.target); 613 614 if ( target.hasClass('nav-tab-link') ) { 615 panelId = /#(.*)$/.exec(e.target.href); 616 if ( panelId && panelId[1] ) 617 panelId = panelId[1] 618 else 619 return false; 620 621 wrapper = target.parents('.inside').first(); 622 623 // upon changing tabs, we want to uncheck all checkboxes 624 $('input', wrapper).removeAttr('checked'); 625 626 $('.tabs-panel-active', wrapper).removeClass('tabs-panel-active').addClass('tabs-panel-inactive'); 627 $('#' + panelId, wrapper).removeClass('tabs-panel-inactive').addClass('tabs-panel-active'); 628 629 $('.tabs', wrapper).removeClass('tabs'); 630 target.parent().addClass('tabs'); 631 632 // select the search bar 633 $('.quick-search', wrapper).focus(); 634 635 return false; 636 } else if ( target.hasClass('select-all') ) { 637 selectAreaMatch = /#(.*)$/.exec(e.target.href); 638 if ( selectAreaMatch && selectAreaMatch[1] ) { 639 items = $('#' + selectAreaMatch[1] + ' .tabs-panel-active .menu-item-title input'); 640 if( items.length === items.filter(':checked').length ) 641 items.removeAttr('checked'); 642 else 643 items.attr('checked', 'checked'); 644 return false; 645 } 646 } else if ( target.hasClass('submit-add-to-menu') ) { 647 api.registerChange(); 648 649 if ( e.target.id && 'submit-customlinkdiv' == e.target.id ) 650 api.addCustomLink( api.addMenuItemToBottom ); 651 else if ( e.target.id && -1 != e.target.id.indexOf('submit-') ) 652 $('#' + e.target.id.replace(/submit-/, '')).addSelectedToMenu( api.addMenuItemToBottom ); 653 return false; 654 } else if ( target.hasClass('page-numbers') ) { 655 $.post( ajaxurl, e.target.href.replace(/.*\?/, '').replace(/action=([^&]*)/, '') + '&action=menu-get-metabox', 656 function( resp ) { 657 if ( -1 == resp.indexOf('replace-id') ) 658 return; 659 660 var metaBoxData = $.parseJSON(resp), 661 toReplace = document.getElementById(metaBoxData['replace-id']), 662 placeholder = document.createElement('div'), 663 wrap = document.createElement('div'); 664 665 if ( ! metaBoxData['markup'] || ! toReplace ) 666 return; 667 668 wrap.innerHTML = metaBoxData['markup'] ? metaBoxData['markup'] : ''; 669 670 toReplace.parentNode.insertBefore( placeholder, toReplace ); 671 placeholder.parentNode.removeChild( toReplace ); 672 673 placeholder.parentNode.insertBefore( wrap, placeholder ); 674 675 placeholder.parentNode.removeChild( placeholder ); 676 677 } 678 ); 679 680 return false; 681 } 682 }); 683 }, 684 685 initTabManager : function() { 686 var fixed = $('.nav-tabs-wrapper'), 687 fluid = fixed.children('.nav-tabs'), 688 active = fluid.children('.nav-tab-active'), 689 tabs = fluid.children('.nav-tab'), 690 tabsWidth = 0, 691 fixedRight, fixedLeft, 692 arrowLeft, arrowRight, resizeTimer, css = {}, 693 marginFluid = api.isRTL ? 'margin-right' : 'margin-left', 694 marginFixed = api.isRTL ? 'margin-left' : 'margin-right', 695 msPerPx = 2; 696 697 /** 698 * Refreshes the menu tabs. 699 * Will show and hide arrows where necessary. 700 * Scrolls to the active tab by default. 701 * 702 * @param savePosition {boolean} Optional. Prevents scrolling so 703 * that the current position is maintained. Default false. 704 **/ 705 api.refreshMenuTabs = function( savePosition ) { 706 var fixedWidth = fixed.width(), 707 margin = 0, css = {}; 708 fixedLeft = fixed.offset().left; 709 fixedRight = fixedLeft + fixedWidth; 710 711 if( !savePosition ) 712 active.makeTabVisible(); 713 714 // Prevent space from building up next to the last tab if there's more to show 715 if( tabs.last().isTabVisible() ) { 716 margin = fixed.width() - tabsWidth; 717 margin = margin > 0 ? 0 : margin; 718 css[marginFluid] = margin + 'px'; 719 fluid.animate( css, 100, "linear" ); 720 } 721 722 // Show the arrows only when necessary 723 if( fixedWidth > tabsWidth ) 724 arrowLeft.add( arrowRight ).hide(); 725 else 726 arrowLeft.add( arrowRight ).show(); 727 } 728 729 $.fn.extend({ 730 makeTabVisible : function() { 731 var t = this.eq(0), left, right, css = {}, shift = 0; 732 733 if( ! t.length ) return this; 734 735 left = t.offset().left; 736 right = left + t.outerWidth(); 737 738 if( right > fixedRight ) 739 shift = fixedRight - right; 740 else if ( left < fixedLeft ) 741 shift = fixedLeft - left; 742 743 if( ! shift ) return this; 744 745 css[marginFluid] = "+=" + api.negateIfRTL * shift + 'px'; 746 fluid.animate( css, Math.abs( shift ) * msPerPx, "linear" ); 747 return this; 748 }, 749 isTabVisible : function() { 750 var t = this.eq(0), 751 left = t.offset().left, 752 right = left + t.outerWidth(); 753 return ( right <= fixedRight && left >= fixedLeft ) ? true : false; 754 } 755 }); 756 757 // Find the width of all tabs 758 tabs.each(function(){ 759 tabsWidth += $(this).outerWidth(true); 760 }); 761 762 // Set up fixed margin for overflow, unset padding 763 css['padding'] = 0; 764 css[marginFixed] = (-1 * tabsWidth) + 'px'; 765 fluid.css( css ); 766 767 // Build tab navigation 768 arrowLeft = $('<div class="nav-tabs-arrow nav-tabs-arrow-left"><a>«</a></div>'); 769 arrowRight = $('<div class="nav-tabs-arrow nav-tabs-arrow-right"><a>»</a></div>'); 770 // Attach to the document 771 fixed.wrap('<div class="nav-tabs-nav"/>').parent().prepend( arrowLeft ).append( arrowRight ); 772 773 // Set the menu tabs 774 api.refreshMenuTabs(); 775 // Make sure the tabs reset on resize 776 $(window).resize(function() { 777 if( resizeTimer ) clearTimeout(resizeTimer); 778 resizeTimer = setTimeout( api.refreshMenuTabs, 200); 779 }); 780 781 // Build arrow functions 782 $.each([{ 783 arrow : arrowLeft, 784 next : "next", 785 last : "first", 786 operator : "+=" 787 },{ 788 arrow : arrowRight, 789 next : "prev", 790 last : "last", 791 operator : "-=" 792 }], function(){ 793 var that = this; 794 this.arrow.mousedown(function(){ 795 var marginFluidVal = Math.abs( parseInt( fluid.css(marginFluid) ) ), 796 shift = marginFluidVal, 797 css = {}; 798 799 if( "-=" == that.operator ) 800 shift = Math.abs( tabsWidth - fixed.width() ) - marginFluidVal; 801 802 if( ! shift ) return; 803 804 css[marginFluid] = that.operator + shift + 'px'; 805 fluid.animate( css, shift * msPerPx, "linear" ); 806 }).mouseup(function(){ 807 var tab, next; 808 fluid.stop(true); 809 tab = tabs[that.last](); 810 while( (next = tab[that.next]()) && next.length && ! next.isTabVisible() ) { 811 tab = next; 812 } 813 tab.makeTabVisible(); 814 }); 815 }); 816 }, 817 818 eventOnClickEditLink : function(clickedEl) { 819 var settings, item, 820 matchedSection = /#(.*)$/.exec(clickedEl.href); 821 if ( matchedSection && matchedSection[1] ) { 822 settings = $('#'+matchedSection[1]); 823 item = settings.parent(); 824 if( 0 != item.length ) { 825 if( item.hasClass('menu-item-edit-inactive') ) { 826 if( ! settings.data('menu-item-data') ) { 827 settings.data( 'menu-item-data', settings.getItemData() ); 828 } 829 settings.slideDown('fast'); 830 item.removeClass('menu-item-edit-inactive') 831 .addClass('menu-item-edit-active'); 832 } else { 833 settings.slideUp('fast'); 834 item.removeClass('menu-item-edit-active') 835 .addClass('menu-item-edit-inactive'); 836 } 837 return false; 838 } 839 } 840 }, 841 842 eventOnClickCancelLink : function(clickedEl) { 843 var settings = $(clickedEl).closest('.menu-item-settings'); 844 settings.setItemData( settings.data('menu-item-data') ); 845 return false; 846 }, 847 848 eventOnClickMenuSave : function(clickedEl) { 849 var locs = '', 850 menuName = $('#menu-name'), 851 menuNameVal = menuName.val(); 852 // Cancel and warn if invalid menu name 853 if( !menuNameVal || menuNameVal == menuName.attr('title') || !menuNameVal.replace(/\s+/, '') ) { 854 menuName.parent().addClass('form-invalid'); 855 return false; 856 } 857 // Copy menu theme locations 858 $('#nav-menu-theme-locations select').each(function() { 859 locs += '<input type="hidden" name="' + this.name + '" value="' + $(this).val() + '" />'; 860 }); 861 $('#update-nav-menu').append( locs ); 862 // Update menu item position data 863 api.menuList.find('.menu-item-data-position').val( function(index) { return index + 1; } ); 864 window.onbeforeunload = null; 865 866 return true; 867 }, 868 869 eventOnClickMenuDelete : function(clickedEl) { 870 // Delete warning AYS 871 if ( confirm( navMenuL10n.warnDeleteMenu ) ) { 872 window.onbeforeunload = null; 873 return true; 874 } 875 return false; 876 }, 877 878 eventOnClickMenuItemDelete : function(clickedEl) { 879 var itemID = parseInt(clickedEl.id.replace('delete-', ''), 10); 880 api.removeMenuItem( $('#menu-item-' + itemID) ); 881 api.registerChange(); 882 return false; 883 }, 884 885 /** 886 * Process the quick search response into a search result 887 * 888 * @param string resp The server response to the query. 889 * @param object req The request arguments. 890 * @param jQuery panel The tabs panel we're searching in. 891 */ 892 processQuickSearchQueryResponse : function(resp, req, panel) { 893 var i, matched, newID, 894 takenIDs = {}, 895 form = document.getElementById('nav-menu-meta'), 896 pattern = new RegExp('menu-item\\[(\[^\\]\]*)', 'g'), 897 items = resp.match(/<li>.*<\/li>/g); 898 899 if( ! items ) { 900 $('.categorychecklist', panel).html( '<li><p>' + navMenuL10n.noResultsFound + '</p></li>' ); 901 $('img.waiting', panel).hide(); 902 return; 903 } 904 905 i = items.length; 906 while( i-- ) { 907 // make a unique DB ID number 908 matched = pattern.exec(items[i]); 909 if ( matched && matched[1] ) { 910 newID = matched[1]; 911 while( form.elements['menu-item[' + newID + '][menu-item-type]'] || takenIDs[ newID ] ) { 912 newID--; 913 } 914 915 takenIDs[newID] = true; 916 if ( newID != matched[1] ) { 917 items[i] = items[i].replace(new RegExp('menu-item\\[' + matched[1] + '\\]', 'g'), 'menu-item[' + newID + ']'); 918 } 919 } 920 } 921 922 $('.categorychecklist', panel).html( items.join('') ); 923 $('img.waiting', panel).hide(); 924 }, 925 926 removeMenuItem : function(el) { 927 var children = el.childMenuItems(); 928 929 el.addClass('deleting').animate({ 930 opacity : 0, 931 height: 0 932 }, 350, function() { 933 var ins = $('#menu-instructions'); 934 el.remove(); 935 children.shiftDepthClass(-1).updateParentMenuItemDBId(); 936 if( ! ins.siblings().length ) 937 ins.removeClass('menu-instructions-inactive'); 938 }); 939 }, 940 941 depthToPx : function(depth) { 942 return depth * api.options.menuItemDepthPerLevel; 943 }, 944 945 pxToDepth : function(px) { 946 return Math.floor(px / api.options.menuItemDepthPerLevel); 947 } 948 949 }; 950 951 $(document).ready(function(){ wpNavMenu.init(); }); 952 953 })(jQuery);
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Oct 14 05:12:05 2010 | Cross-referenced by PHPXref 0.7 |