1 | /*! |
---|
2 | * jQuery UI Menu 1.9.2 |
---|
3 | * http://jqueryui.com |
---|
4 | * |
---|
5 | * Copyright 2012 jQuery Foundation and other contributors |
---|
6 | * Released under the MIT license. |
---|
7 | * http://jquery.org/license |
---|
8 | * |
---|
9 | * http://api.jqueryui.com/menu/ |
---|
10 | * |
---|
11 | * Depends: |
---|
12 | * jquery.ui.core.js |
---|
13 | * jquery.ui.widget.js |
---|
14 | * jquery.ui.position.js |
---|
15 | */ |
---|
16 | (function( $, undefined ) { |
---|
17 | |
---|
18 | var mouseHandled = false; |
---|
19 | |
---|
20 | $.widget( "ui.menu", { |
---|
21 | version: "1.9.2", |
---|
22 | defaultElement: "<ul>", |
---|
23 | delay: 300, |
---|
24 | options: { |
---|
25 | icons: { |
---|
26 | submenu: "ui-icon-carat-1-e" |
---|
27 | }, |
---|
28 | menus: "ul", |
---|
29 | position: { |
---|
30 | my: "left top", |
---|
31 | at: "right top" |
---|
32 | }, |
---|
33 | role: "menu", |
---|
34 | |
---|
35 | // callbacks |
---|
36 | blur: null, |
---|
37 | focus: null, |
---|
38 | select: null |
---|
39 | }, |
---|
40 | |
---|
41 | _create: function() { |
---|
42 | this.activeMenu = this.element; |
---|
43 | this.element |
---|
44 | .uniqueId() |
---|
45 | .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" ) |
---|
46 | .toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length ) |
---|
47 | .attr({ |
---|
48 | role: this.options.role, |
---|
49 | tabIndex: 0 |
---|
50 | }) |
---|
51 | // need to catch all clicks on disabled menu |
---|
52 | // not possible through _on |
---|
53 | .bind( "click" + this.eventNamespace, $.proxy(function( event ) { |
---|
54 | if ( this.options.disabled ) { |
---|
55 | event.preventDefault(); |
---|
56 | } |
---|
57 | }, this )); |
---|
58 | |
---|
59 | if ( this.options.disabled ) { |
---|
60 | this.element |
---|
61 | .addClass( "ui-state-disabled" ) |
---|
62 | .attr( "aria-disabled", "true" ); |
---|
63 | } |
---|
64 | |
---|
65 | this._on({ |
---|
66 | // Prevent focus from sticking to links inside menu after clicking |
---|
67 | // them (focus should always stay on UL during navigation). |
---|
68 | "mousedown .ui-menu-item > a": function( event ) { |
---|
69 | event.preventDefault(); |
---|
70 | }, |
---|
71 | "click .ui-state-disabled > a": function( event ) { |
---|
72 | event.preventDefault(); |
---|
73 | }, |
---|
74 | "click .ui-menu-item:has(a)": function( event ) { |
---|
75 | var target = $( event.target ).closest( ".ui-menu-item" ); |
---|
76 | if ( !mouseHandled && target.not( ".ui-state-disabled" ).length ) { |
---|
77 | mouseHandled = true; |
---|
78 | |
---|
79 | this.select( event ); |
---|
80 | // Open submenu on click |
---|
81 | if ( target.has( ".ui-menu" ).length ) { |
---|
82 | this.expand( event ); |
---|
83 | } else if ( !this.element.is( ":focus" ) ) { |
---|
84 | // Redirect focus to the menu |
---|
85 | this.element.trigger( "focus", [ true ] ); |
---|
86 | |
---|
87 | // If the active item is on the top level, let it stay active. |
---|
88 | // Otherwise, blur the active item since it is no longer visible. |
---|
89 | if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) { |
---|
90 | clearTimeout( this.timer ); |
---|
91 | } |
---|
92 | } |
---|
93 | } |
---|
94 | }, |
---|
95 | "mouseenter .ui-menu-item": function( event ) { |
---|
96 | var target = $( event.currentTarget ); |
---|
97 | // Remove ui-state-active class from siblings of the newly focused menu item |
---|
98 | // to avoid a jump caused by adjacent elements both having a class with a border |
---|
99 | target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" ); |
---|
100 | this.focus( event, target ); |
---|
101 | }, |
---|
102 | mouseleave: "collapseAll", |
---|
103 | "mouseleave .ui-menu": "collapseAll", |
---|
104 | focus: function( event, keepActiveItem ) { |
---|
105 | // If there's already an active item, keep it active |
---|
106 | // If not, activate the first item |
---|
107 | var item = this.active || this.element.children( ".ui-menu-item" ).eq( 0 ); |
---|
108 | |
---|
109 | if ( !keepActiveItem ) { |
---|
110 | this.focus( event, item ); |
---|
111 | } |
---|
112 | }, |
---|
113 | blur: function( event ) { |
---|
114 | this._delay(function() { |
---|
115 | if ( !$.contains( this.element[0], this.document[0].activeElement ) ) { |
---|
116 | this.collapseAll( event ); |
---|
117 | } |
---|
118 | }); |
---|
119 | }, |
---|
120 | keydown: "_keydown" |
---|
121 | }); |
---|
122 | |
---|
123 | this.refresh(); |
---|
124 | |
---|
125 | // Clicks outside of a menu collapse any open menus |
---|
126 | this._on( this.document, { |
---|
127 | click: function( event ) { |
---|
128 | if ( !$( event.target ).closest( ".ui-menu" ).length ) { |
---|
129 | this.collapseAll( event ); |
---|
130 | } |
---|
131 | |
---|
132 | // Reset the mouseHandled flag |
---|
133 | mouseHandled = false; |
---|
134 | } |
---|
135 | }); |
---|
136 | }, |
---|
137 | |
---|
138 | _destroy: function() { |
---|
139 | // Destroy (sub)menus |
---|
140 | this.element |
---|
141 | .removeAttr( "aria-activedescendant" ) |
---|
142 | .find( ".ui-menu" ).andSelf() |
---|
143 | .removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons" ) |
---|
144 | .removeAttr( "role" ) |
---|
145 | .removeAttr( "tabIndex" ) |
---|
146 | .removeAttr( "aria-labelledby" ) |
---|
147 | .removeAttr( "aria-expanded" ) |
---|
148 | .removeAttr( "aria-hidden" ) |
---|
149 | .removeAttr( "aria-disabled" ) |
---|
150 | .removeUniqueId() |
---|
151 | .show(); |
---|
152 | |
---|
153 | // Destroy menu items |
---|
154 | this.element.find( ".ui-menu-item" ) |
---|
155 | .removeClass( "ui-menu-item" ) |
---|
156 | .removeAttr( "role" ) |
---|
157 | .removeAttr( "aria-disabled" ) |
---|
158 | .children( "a" ) |
---|
159 | .removeUniqueId() |
---|
160 | .removeClass( "ui-corner-all ui-state-hover" ) |
---|
161 | .removeAttr( "tabIndex" ) |
---|
162 | .removeAttr( "role" ) |
---|
163 | .removeAttr( "aria-haspopup" ) |
---|
164 | .children().each( function() { |
---|
165 | var elem = $( this ); |
---|
166 | if ( elem.data( "ui-menu-submenu-carat" ) ) { |
---|
167 | elem.remove(); |
---|
168 | } |
---|
169 | }); |
---|
170 | |
---|
171 | // Destroy menu dividers |
---|
172 | this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" ); |
---|
173 | }, |
---|
174 | |
---|
175 | _keydown: function( event ) { |
---|
176 | var match, prev, character, skip, regex, |
---|
177 | preventDefault = true; |
---|
178 | |
---|
179 | function escape( value ) { |
---|
180 | return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ); |
---|
181 | } |
---|
182 | |
---|
183 | switch ( event.keyCode ) { |
---|
184 | case $.ui.keyCode.PAGE_UP: |
---|
185 | this.previousPage( event ); |
---|
186 | break; |
---|
187 | case $.ui.keyCode.PAGE_DOWN: |
---|
188 | this.nextPage( event ); |
---|
189 | break; |
---|
190 | case $.ui.keyCode.HOME: |
---|
191 | this._move( "first", "first", event ); |
---|
192 | break; |
---|
193 | case $.ui.keyCode.END: |
---|
194 | this._move( "last", "last", event ); |
---|
195 | break; |
---|
196 | case $.ui.keyCode.UP: |
---|
197 | this.previous( event ); |
---|
198 | break; |
---|
199 | case $.ui.keyCode.DOWN: |
---|
200 | this.next( event ); |
---|
201 | break; |
---|
202 | case $.ui.keyCode.LEFT: |
---|
203 | this.collapse( event ); |
---|
204 | break; |
---|
205 | case $.ui.keyCode.RIGHT: |
---|
206 | if ( this.active && !this.active.is( ".ui-state-disabled" ) ) { |
---|
207 | this.expand( event ); |
---|
208 | } |
---|
209 | break; |
---|
210 | case $.ui.keyCode.ENTER: |
---|
211 | case $.ui.keyCode.SPACE: |
---|
212 | this._activate( event ); |
---|
213 | break; |
---|
214 | case $.ui.keyCode.ESCAPE: |
---|
215 | this.collapse( event ); |
---|
216 | break; |
---|
217 | default: |
---|
218 | preventDefault = false; |
---|
219 | prev = this.previousFilter || ""; |
---|
220 | character = String.fromCharCode( event.keyCode ); |
---|
221 | skip = false; |
---|
222 | |
---|
223 | clearTimeout( this.filterTimer ); |
---|
224 | |
---|
225 | if ( character === prev ) { |
---|
226 | skip = true; |
---|
227 | } else { |
---|
228 | character = prev + character; |
---|
229 | } |
---|
230 | |
---|
231 | regex = new RegExp( "^" + escape( character ), "i" ); |
---|
232 | match = this.activeMenu.children( ".ui-menu-item" ).filter(function() { |
---|
233 | return regex.test( $( this ).children( "a" ).text() ); |
---|
234 | }); |
---|
235 | match = skip && match.index( this.active.next() ) !== -1 ? |
---|
236 | this.active.nextAll( ".ui-menu-item" ) : |
---|
237 | match; |
---|
238 | |
---|
239 | // If no matches on the current filter, reset to the last character pressed |
---|
240 | // to move down the menu to the first item that starts with that character |
---|
241 | if ( !match.length ) { |
---|
242 | character = String.fromCharCode( event.keyCode ); |
---|
243 | regex = new RegExp( "^" + escape( character ), "i" ); |
---|
244 | match = this.activeMenu.children( ".ui-menu-item" ).filter(function() { |
---|
245 | return regex.test( $( this ).children( "a" ).text() ); |
---|
246 | }); |
---|
247 | } |
---|
248 | |
---|
249 | if ( match.length ) { |
---|
250 | this.focus( event, match ); |
---|
251 | if ( match.length > 1 ) { |
---|
252 | this.previousFilter = character; |
---|
253 | this.filterTimer = this._delay(function() { |
---|
254 | delete this.previousFilter; |
---|
255 | }, 1000 ); |
---|
256 | } else { |
---|
257 | delete this.previousFilter; |
---|
258 | } |
---|
259 | } else { |
---|
260 | delete this.previousFilter; |
---|
261 | } |
---|
262 | } |
---|
263 | |
---|
264 | if ( preventDefault ) { |
---|
265 | event.preventDefault(); |
---|
266 | } |
---|
267 | }, |
---|
268 | |
---|
269 | _activate: function( event ) { |
---|
270 | if ( !this.active.is( ".ui-state-disabled" ) ) { |
---|
271 | if ( this.active.children( "a[aria-haspopup='true']" ).length ) { |
---|
272 | this.expand( event ); |
---|
273 | } else { |
---|
274 | this.select( event ); |
---|
275 | } |
---|
276 | } |
---|
277 | }, |
---|
278 | |
---|
279 | refresh: function() { |
---|
280 | var menus, |
---|
281 | icon = this.options.icons.submenu, |
---|
282 | submenus = this.element.find( this.options.menus ); |
---|
283 | |
---|
284 | // Initialize nested menus |
---|
285 | submenus.filter( ":not(.ui-menu)" ) |
---|
286 | .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" ) |
---|
287 | .hide() |
---|
288 | .attr({ |
---|
289 | role: this.options.role, |
---|
290 | "aria-hidden": "true", |
---|
291 | "aria-expanded": "false" |
---|
292 | }) |
---|
293 | .each(function() { |
---|
294 | var menu = $( this ), |
---|
295 | item = menu.prev( "a" ), |
---|
296 | submenuCarat = $( "<span>" ) |
---|
297 | .addClass( "ui-menu-icon ui-icon " + icon ) |
---|
298 | .data( "ui-menu-submenu-carat", true ); |
---|
299 | |
---|
300 | item |
---|
301 | .attr( "aria-haspopup", "true" ) |
---|
302 | .prepend( submenuCarat ); |
---|
303 | menu.attr( "aria-labelledby", item.attr( "id" ) ); |
---|
304 | }); |
---|
305 | |
---|
306 | menus = submenus.add( this.element ); |
---|
307 | |
---|
308 | // Don't refresh list items that are already adapted |
---|
309 | menus.children( ":not(.ui-menu-item):has(a)" ) |
---|
310 | .addClass( "ui-menu-item" ) |
---|
311 | .attr( "role", "presentation" ) |
---|
312 | .children( "a" ) |
---|
313 | .uniqueId() |
---|
314 | .addClass( "ui-corner-all" ) |
---|
315 | .attr({ |
---|
316 | tabIndex: -1, |
---|
317 | role: this._itemRole() |
---|
318 | }); |
---|
319 | |
---|
320 | // Initialize unlinked menu-items containing spaces and/or dashes only as dividers |
---|
321 | menus.children( ":not(.ui-menu-item)" ).each(function() { |
---|
322 | var item = $( this ); |
---|
323 | // hyphen, em dash, en dash |
---|
324 | if ( !/[^\-ââ\s]/.test( item.text() ) ) { |
---|
325 | item.addClass( "ui-widget-content ui-menu-divider" ); |
---|
326 | } |
---|
327 | }); |
---|
328 | |
---|
329 | // Add aria-disabled attribute to any disabled menu item |
---|
330 | menus.children( ".ui-state-disabled" ).attr( "aria-disabled", "true" ); |
---|
331 | |
---|
332 | // If the active item has been removed, blur the menu |
---|
333 | if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) { |
---|
334 | this.blur(); |
---|
335 | } |
---|
336 | }, |
---|
337 | |
---|
338 | _itemRole: function() { |
---|
339 | return { |
---|
340 | menu: "menuitem", |
---|
341 | listbox: "option" |
---|
342 | }[ this.options.role ]; |
---|
343 | }, |
---|
344 | |
---|
345 | focus: function( event, item ) { |
---|
346 | var nested, focused; |
---|
347 | this.blur( event, event && event.type === "focus" ); |
---|
348 | |
---|
349 | this._scrollIntoView( item ); |
---|
350 | |
---|
351 | this.active = item.first(); |
---|
352 | focused = this.active.children( "a" ).addClass( "ui-state-focus" ); |
---|
353 | // Only update aria-activedescendant if there's a role |
---|
354 | // otherwise we assume focus is managed elsewhere |
---|
355 | if ( this.options.role ) { |
---|
356 | this.element.attr( "aria-activedescendant", focused.attr( "id" ) ); |
---|
357 | } |
---|
358 | |
---|
359 | // Highlight active parent menu item, if any |
---|
360 | this.active |
---|
361 | .parent() |
---|
362 | .closest( ".ui-menu-item" ) |
---|
363 | .children( "a:first" ) |
---|
364 | .addClass( "ui-state-active" ); |
---|
365 | |
---|
366 | if ( event && event.type === "keydown" ) { |
---|
367 | this._close(); |
---|
368 | } else { |
---|
369 | this.timer = this._delay(function() { |
---|
370 | this._close(); |
---|
371 | }, this.delay ); |
---|
372 | } |
---|
373 | |
---|
374 | nested = item.children( ".ui-menu" ); |
---|
375 | if ( nested.length && ( /^mouse/.test( event.type ) ) ) { |
---|
376 | this._startOpening(nested); |
---|
377 | } |
---|
378 | this.activeMenu = item.parent(); |
---|
379 | |
---|
380 | this._trigger( "focus", event, { item: item } ); |
---|
381 | }, |
---|
382 | |
---|
383 | _scrollIntoView: function( item ) { |
---|
384 | var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight; |
---|
385 | if ( this._hasScroll() ) { |
---|
386 | borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0; |
---|
387 | paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0; |
---|
388 | offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop; |
---|
389 | scroll = this.activeMenu.scrollTop(); |
---|
390 | elementHeight = this.activeMenu.height(); |
---|
391 | itemHeight = item.height(); |
---|
392 | |
---|
393 | if ( offset < 0 ) { |
---|
394 | this.activeMenu.scrollTop( scroll + offset ); |
---|
395 | } else if ( offset + itemHeight > elementHeight ) { |
---|
396 | this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight ); |
---|
397 | } |
---|
398 | } |
---|
399 | }, |
---|
400 | |
---|
401 | blur: function( event, fromFocus ) { |
---|
402 | if ( !fromFocus ) { |
---|
403 | clearTimeout( this.timer ); |
---|
404 | } |
---|
405 | |
---|
406 | if ( !this.active ) { |
---|
407 | return; |
---|
408 | } |
---|
409 | |
---|
410 | this.active.children( "a" ).removeClass( "ui-state-focus" ); |
---|
411 | this.active = null; |
---|
412 | |
---|
413 | this._trigger( "blur", event, { item: this.active } ); |
---|
414 | }, |
---|
415 | |
---|
416 | _startOpening: function( submenu ) { |
---|
417 | clearTimeout( this.timer ); |
---|
418 | |
---|
419 | // Don't open if already open fixes a Firefox bug that caused a .5 pixel |
---|
420 | // shift in the submenu position when mousing over the carat icon |
---|
421 | if ( submenu.attr( "aria-hidden" ) !== "true" ) { |
---|
422 | return; |
---|
423 | } |
---|
424 | |
---|
425 | this.timer = this._delay(function() { |
---|
426 | this._close(); |
---|
427 | this._open( submenu ); |
---|
428 | }, this.delay ); |
---|
429 | }, |
---|
430 | |
---|
431 | _open: function( submenu ) { |
---|
432 | var position = $.extend({ |
---|
433 | of: this.active |
---|
434 | }, this.options.position ); |
---|
435 | |
---|
436 | clearTimeout( this.timer ); |
---|
437 | this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) ) |
---|
438 | .hide() |
---|
439 | .attr( "aria-hidden", "true" ); |
---|
440 | |
---|
441 | submenu |
---|
442 | .show() |
---|
443 | .removeAttr( "aria-hidden" ) |
---|
444 | .attr( "aria-expanded", "true" ) |
---|
445 | .position( position ); |
---|
446 | }, |
---|
447 | |
---|
448 | collapseAll: function( event, all ) { |
---|
449 | clearTimeout( this.timer ); |
---|
450 | this.timer = this._delay(function() { |
---|
451 | // If we were passed an event, look for the submenu that contains the event |
---|
452 | var currentMenu = all ? this.element : |
---|
453 | $( event && event.target ).closest( this.element.find( ".ui-menu" ) ); |
---|
454 | |
---|
455 | // If we found no valid submenu ancestor, use the main menu to close all sub menus anyway |
---|
456 | if ( !currentMenu.length ) { |
---|
457 | currentMenu = this.element; |
---|
458 | } |
---|
459 | |
---|
460 | this._close( currentMenu ); |
---|
461 | |
---|
462 | this.blur( event ); |
---|
463 | this.activeMenu = currentMenu; |
---|
464 | }, this.delay ); |
---|
465 | }, |
---|
466 | |
---|
467 | // With no arguments, closes the currently active menu - if nothing is active |
---|
468 | // it closes all menus. If passed an argument, it will search for menus BELOW |
---|
469 | _close: function( startMenu ) { |
---|
470 | if ( !startMenu ) { |
---|
471 | startMenu = this.active ? this.active.parent() : this.element; |
---|
472 | } |
---|
473 | |
---|
474 | startMenu |
---|
475 | .find( ".ui-menu" ) |
---|
476 | .hide() |
---|
477 | .attr( "aria-hidden", "true" ) |
---|
478 | .attr( "aria-expanded", "false" ) |
---|
479 | .end() |
---|
480 | .find( "a.ui-state-active" ) |
---|
481 | .removeClass( "ui-state-active" ); |
---|
482 | }, |
---|
483 | |
---|
484 | collapse: function( event ) { |
---|
485 | var newItem = this.active && |
---|
486 | this.active.parent().closest( ".ui-menu-item", this.element ); |
---|
487 | if ( newItem && newItem.length ) { |
---|
488 | this._close(); |
---|
489 | this.focus( event, newItem ); |
---|
490 | } |
---|
491 | }, |
---|
492 | |
---|
493 | expand: function( event ) { |
---|
494 | var newItem = this.active && |
---|
495 | this.active |
---|
496 | .children( ".ui-menu " ) |
---|
497 | .children( ".ui-menu-item" ) |
---|
498 | .first(); |
---|
499 | |
---|
500 | if ( newItem && newItem.length ) { |
---|
501 | this._open( newItem.parent() ); |
---|
502 | |
---|
503 | // Delay so Firefox will not hide activedescendant change in expanding submenu from AT |
---|
504 | this._delay(function() { |
---|
505 | this.focus( event, newItem ); |
---|
506 | }); |
---|
507 | } |
---|
508 | }, |
---|
509 | |
---|
510 | next: function( event ) { |
---|
511 | this._move( "next", "first", event ); |
---|
512 | }, |
---|
513 | |
---|
514 | previous: function( event ) { |
---|
515 | this._move( "prev", "last", event ); |
---|
516 | }, |
---|
517 | |
---|
518 | isFirstItem: function() { |
---|
519 | return this.active && !this.active.prevAll( ".ui-menu-item" ).length; |
---|
520 | }, |
---|
521 | |
---|
522 | isLastItem: function() { |
---|
523 | return this.active && !this.active.nextAll( ".ui-menu-item" ).length; |
---|
524 | }, |
---|
525 | |
---|
526 | _move: function( direction, filter, event ) { |
---|
527 | var next; |
---|
528 | if ( this.active ) { |
---|
529 | if ( direction === "first" || direction === "last" ) { |
---|
530 | next = this.active |
---|
531 | [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" ) |
---|
532 | .eq( -1 ); |
---|
533 | } else { |
---|
534 | next = this.active |
---|
535 | [ direction + "All" ]( ".ui-menu-item" ) |
---|
536 | .eq( 0 ); |
---|
537 | } |
---|
538 | } |
---|
539 | if ( !next || !next.length || !this.active ) { |
---|
540 | next = this.activeMenu.children( ".ui-menu-item" )[ filter ](); |
---|
541 | } |
---|
542 | |
---|
543 | this.focus( event, next ); |
---|
544 | }, |
---|
545 | |
---|
546 | nextPage: function( event ) { |
---|
547 | var item, base, height; |
---|
548 | |
---|
549 | if ( !this.active ) { |
---|
550 | this.next( event ); |
---|
551 | return; |
---|
552 | } |
---|
553 | if ( this.isLastItem() ) { |
---|
554 | return; |
---|
555 | } |
---|
556 | if ( this._hasScroll() ) { |
---|
557 | base = this.active.offset().top; |
---|
558 | height = this.element.height(); |
---|
559 | this.active.nextAll( ".ui-menu-item" ).each(function() { |
---|
560 | item = $( this ); |
---|
561 | return item.offset().top - base - height < 0; |
---|
562 | }); |
---|
563 | |
---|
564 | this.focus( event, item ); |
---|
565 | } else { |
---|
566 | this.focus( event, this.activeMenu.children( ".ui-menu-item" ) |
---|
567 | [ !this.active ? "first" : "last" ]() ); |
---|
568 | } |
---|
569 | }, |
---|
570 | |
---|
571 | previousPage: function( event ) { |
---|
572 | var item, base, height; |
---|
573 | if ( !this.active ) { |
---|
574 | this.next( event ); |
---|
575 | return; |
---|
576 | } |
---|
577 | if ( this.isFirstItem() ) { |
---|
578 | return; |
---|
579 | } |
---|
580 | if ( this._hasScroll() ) { |
---|
581 | base = this.active.offset().top; |
---|
582 | height = this.element.height(); |
---|
583 | this.active.prevAll( ".ui-menu-item" ).each(function() { |
---|
584 | item = $( this ); |
---|
585 | return item.offset().top - base + height > 0; |
---|
586 | }); |
---|
587 | |
---|
588 | this.focus( event, item ); |
---|
589 | } else { |
---|
590 | this.focus( event, this.activeMenu.children( ".ui-menu-item" ).first() ); |
---|
591 | } |
---|
592 | }, |
---|
593 | |
---|
594 | _hasScroll: function() { |
---|
595 | return this.element.outerHeight() < this.element.prop( "scrollHeight" ); |
---|
596 | }, |
---|
597 | |
---|
598 | select: function( event ) { |
---|
599 | // TODO: It should never be possible to not have an active item at this |
---|
600 | // point, but the tests don't trigger mouseenter before click. |
---|
601 | this.active = this.active || $( event.target ).closest( ".ui-menu-item" ); |
---|
602 | var ui = { item: this.active }; |
---|
603 | if ( !this.active.has( ".ui-menu" ).length ) { |
---|
604 | this.collapseAll( event, true ); |
---|
605 | } |
---|
606 | this._trigger( "select", event, ui ); |
---|
607 | } |
---|
608 | }); |
---|
609 | |
---|
610 | }( jQuery )); |
---|