221 self.$search.val( '' ).trigger( 'focus' ).trigger( 'input' ); |
221 self.$search.val( '' ).trigger( 'focus' ).trigger( 'input' ); |
222 } ); |
222 } ); |
223 |
223 |
224 this.$el.on( 'input', '#custom-menu-item-name.invalid, #custom-menu-item-url.invalid', function() { |
224 this.$el.on( 'input', '#custom-menu-item-name.invalid, #custom-menu-item-url.invalid', function() { |
225 $( this ).removeClass( 'invalid' ); |
225 $( this ).removeClass( 'invalid' ); |
|
226 var errorMessageId = $( this ).attr( 'aria-describedby' ); |
|
227 $( '#' + errorMessageId ).hide(); |
|
228 $( this ).removeAttr( 'aria-invalid' ).removeAttr( 'aria-describedby' ); |
226 }); |
229 }); |
227 |
230 |
228 // Load available items if it looks like we'll need them. |
231 // Load available items if it looks like we'll need them. |
229 api.panel( 'nav_menus' ).container.on( 'expanded', function() { |
232 api.panel( 'nav_menus' ).container.on( 'expanded', function() { |
230 if ( ! self.rendered ) { |
233 if ( ! self.rendered ) { |
544 // Adds the custom menu item to the menu. |
547 // Adds the custom menu item to the menu. |
545 submitLink: function() { |
548 submitLink: function() { |
546 var menuItem, |
549 var menuItem, |
547 itemName = $( '#custom-menu-item-name' ), |
550 itemName = $( '#custom-menu-item-name' ), |
548 itemUrl = $( '#custom-menu-item-url' ), |
551 itemUrl = $( '#custom-menu-item-url' ), |
|
552 urlErrorMessage = $( '#custom-url-error' ), |
|
553 nameErrorMessage = $( '#custom-name-error' ), |
549 url = itemUrl.val().trim(), |
554 url = itemUrl.val().trim(), |
550 urlRegex; |
555 urlRegex, |
|
556 errorText; |
551 |
557 |
552 if ( ! this.currentMenuControl ) { |
558 if ( ! this.currentMenuControl ) { |
553 return; |
559 return; |
554 } |
560 } |
555 |
561 |
564 * |
570 * |
565 * Any further validation will be handled on the server when the setting is attempted to be saved, |
571 * Any further validation will be handled on the server when the setting is attempted to be saved, |
566 * so this pattern does not need to be complete. |
572 * so this pattern does not need to be complete. |
567 */ |
573 */ |
568 urlRegex = /^((\w+:)?\/\/\w.*|\w+:(?!\/\/$)|\/|\?|#)/; |
574 urlRegex = /^((\w+:)?\/\/\w.*|\w+:(?!\/\/$)|\/|\?|#)/; |
569 |
575 if ( ! urlRegex.test( url ) || '' === itemName.val() ) { |
570 if ( '' === itemName.val() ) { |
576 if ( ! urlRegex.test( url ) ) { |
571 itemName.addClass( 'invalid' ); |
577 itemUrl.addClass( 'invalid' ) |
|
578 .attr( 'aria-invalid', 'true' ) |
|
579 .attr( 'aria-describedby', 'custom-url-error' ); |
|
580 urlErrorMessage.show(); |
|
581 errorText = urlErrorMessage.text(); |
|
582 // Announce error message via screen reader |
|
583 wp.a11y.speak( errorText, 'assertive' ); |
|
584 } |
|
585 if ( '' === itemName.val() ) { |
|
586 itemName.addClass( 'invalid' ) |
|
587 .attr( 'aria-invalid', 'true' ) |
|
588 .attr( 'aria-describedby', 'custom-name-error' ); |
|
589 nameErrorMessage.show(); |
|
590 errorText = ( '' === errorText ) ? nameErrorMessage.text() : errorText + nameErrorMessage.text(); |
|
591 // Announce error message via screen reader |
|
592 wp.a11y.speak( errorText, 'assertive' ); |
|
593 } |
572 return; |
594 return; |
573 } else if ( ! urlRegex.test( url ) ) { |
595 } |
574 itemUrl.addClass( 'invalid' ); |
596 |
575 return; |
597 urlErrorMessage.hide(); |
576 } |
598 nameErrorMessage.hide(); |
|
599 itemName.removeClass( 'invalid' ) |
|
600 .removeAttr( 'aria-invalid', 'true' ) |
|
601 .removeAttr( 'aria-describedby', 'custom-name-error' ); |
|
602 itemUrl.removeClass( 'invalid' ) |
|
603 .removeAttr( 'aria-invalid', 'true' ) |
|
604 .removeAttr( 'aria-describedby', 'custom-name-error' ); |
577 |
605 |
578 menuItem = { |
606 menuItem = { |
579 'title': itemName.val(), |
607 'title': itemName.val(), |
580 'url': url, |
608 'url': url, |
581 'type': 'custom', |
609 'type': 'custom', |
1105 */ |
1133 */ |
1106 updateAssignedLocationsInSectionTitle: function( themeLocationSlugs ) { |
1134 updateAssignedLocationsInSectionTitle: function( themeLocationSlugs ) { |
1107 var section = this, |
1135 var section = this, |
1108 $title; |
1136 $title; |
1109 |
1137 |
1110 $title = section.container.find( '.accordion-section-title:first' ); |
1138 $title = section.container.find( '.accordion-section-title button:first' ); |
1111 $title.find( '.menu-in-location' ).remove(); |
1139 $title.find( '.menu-in-location' ).remove(); |
1112 _.each( themeLocationSlugs, function( themeLocationSlug ) { |
1140 _.each( themeLocationSlugs, function( themeLocationSlug ) { |
1113 var $label, locationName; |
1141 var $label, locationName; |
1114 $label = $( '<span class="menu-in-location"></span>' ); |
1142 $label = $( '<span class="menu-in-location"></span>' ); |
1115 locationName = api.Menus.data.locationSlugMappedToName[ themeLocationSlug ]; |
1143 locationName = api.Menus.data.locationSlugMappedToName[ themeLocationSlug ]; |