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