enju_leaf 1.1.0.rc3 → 1.1.0.rc4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/enju_leaf.js +0 -2
  3. data/app/controllers/page_controller.rb +2 -2
  4. data/app/controllers/users_controller.rb +7 -7
  5. data/app/views/layouts/patrons.html.erb +2 -2
  6. data/app/views/my_accounts/show.html.erb +9 -1
  7. data/app/views/page/_menu.html.erb +1 -1
  8. data/app/views/page/advanced_search.html.erb +2 -2
  9. data/app/views/page/configuration.html.erb +3 -3
  10. data/app/views/page/export.html.erb +2 -2
  11. data/app/views/page/import.html.erb +1 -1
  12. data/app/views/page/statistics.html.erb +2 -2
  13. data/app/views/users/index.html.erb +0 -11
  14. data/app/views/users/new.html.erb +4 -4
  15. data/app/views/users/show.html.erb +9 -1
  16. data/config/locales/translation_en.yml +13 -13
  17. data/config/locales/translation_ja.yml +13 -13
  18. data/lib/enju_leaf/user.rb +12 -12
  19. data/lib/enju_leaf/version.rb +1 -1
  20. data/lib/enju_leaf.rb +2 -2
  21. data/lib/generators/enju_leaf/setup/setup_generator.rb +10 -3
  22. data/spec/dummy/db/migrate/001_create_patrons.rb +7 -7
  23. data/spec/dummy/db/migrate/012_create_owns.rb +2 -2
  24. data/spec/dummy/db/migrate/015_create_creates.rb +2 -2
  25. data/spec/dummy/db/migrate/047_create_produces.rb +2 -2
  26. data/spec/dummy/db/migrate/125_create_donates.rb +2 -2
  27. data/spec/dummy/db/migrate/20080830154109_create_realizes.rb +2 -2
  28. data/spec/dummy/db/migrate/20080905191442_create_patron_types.rb +1 -1
  29. data/spec/dummy/db/migrate/20081028083142_create_patron_import_files.rb +8 -8
  30. data/spec/dummy/db/migrate/20090812151902_create_patron_relationship_types.rb +1 -1
  31. data/spec/dummy/db/migrate/20091012101112_add_dcndl_schema.rb +2 -2
  32. data/spec/dummy/db/migrate/20100606073747_create_patron_relationships.rb +4 -4
  33. data/spec/dummy/db/migrate/20100814091104_add_position_to_patron_relationship.rb +2 -2
  34. data/spec/dummy/db/migrate/20100925074559_create_patron_import_results.rb +3 -3
  35. data/spec/dummy/db/migrate/20110301121550_add_birth_date_and_death_date_to_patron.rb +4 -4
  36. data/spec/dummy/db/migrate/20120413161403_add_fingerprint_to_patron_import_file.rb +1 -1
  37. data/spec/dummy/db/migrate/20120413170720_add_error_message_to_patron_import_file.rb +1 -1
  38. data/spec/dummy/db/migrate/20120415060323_rename_patron_import_file_imported_at_to_executed_at.rb +2 -2
  39. data/spec/dummy/db/migrate/20120511072422_add_patron_identifier_to_patron.rb +2 -2
  40. data/spec/dummy/db/migrate/20120602141129_add_edit_mode_to_patron_import_file.rb +1 -1
  41. data/spec/dummy/db/schema.rb +129 -129
  42. data/spec/dummy/db/test.sqlite3 +0 -0
  43. data/spec/dummy/solr/data/test/index/{_6z.fdt → _7z.fdt} +0 -0
  44. data/spec/dummy/solr/data/test/index/{_6z.fdx → _7z.fdx} +0 -0
  45. data/spec/dummy/solr/data/test/index/{_6z.fnm → _7z.fnm} +0 -0
  46. data/spec/dummy/solr/data/test/index/{_6z.frq → _7z.frq} +0 -0
  47. data/spec/dummy/solr/data/test/index/{_6z.nrm → _7z.nrm} +0 -0
  48. data/spec/dummy/solr/data/test/index/{_6z.prx → _7z.prx} +0 -0
  49. data/spec/dummy/solr/data/test/index/{_6z.tii → _7z.tii} +0 -0
  50. data/spec/dummy/solr/data/test/index/{_6z.tis → _7z.tis} +0 -0
  51. data/spec/dummy/solr/data/test/index/segments.gen +0 -0
  52. data/spec/dummy/solr/data/test/index/segments_g1 +0 -0
  53. data/spec/factories/{patron.rb → agent.rb} +3 -3
  54. data/spec/factories/agent_relationship.rb +6 -0
  55. data/spec/factories/agent_relationship_type.rb +5 -0
  56. data/spec/factories/agent_type.rb +5 -0
  57. data/spec/factories/create.rb +1 -1
  58. data/spec/factories/donate.rb +1 -1
  59. data/spec/factories/own.rb +1 -1
  60. data/spec/factories/produce.rb +1 -1
  61. data/spec/factories/realize.rb +1 -1
  62. data/spec/fixtures/{patron_import_files.yml → agent_import_files.yml} +18 -18
  63. data/spec/fixtures/{patron_import_results.yml → agent_import_results.yml} +7 -7
  64. data/spec/fixtures/{patron_relationship_types.yml → agent_relationship_types.yml} +4 -4
  65. data/spec/fixtures/{patron_relationships.yml → agent_relationships.yml} +4 -4
  66. data/spec/fixtures/{patron_types.yml → agent_types.yml} +4 -4
  67. data/spec/fixtures/{patrons.yml → agents.yml} +47 -47
  68. data/spec/fixtures/creates.yml +8 -8
  69. data/spec/fixtures/donates.yml +4 -4
  70. data/spec/fixtures/owns.yml +3 -3
  71. data/spec/fixtures/produces.yml +20 -20
  72. data/spec/fixtures/realizes.yml +11 -11
  73. data/spec/spec_helper.rb +0 -2
  74. data/vendor/assets/javascripts/jquery.colorbox.js +70 -33
  75. data/vendor/assets/javascripts/jquery.ui.menubar.js +342 -187
  76. data/vendor/assets/stylesheets/jquery.ui.menubar.css +25 -5
  77. metadata +61 -76
  78. data/app/assets/stylesheets/images/ajax-loader.gif +0 -0
  79. data/app/assets/stylesheets/images/border.png +0 -0
  80. data/app/assets/stylesheets/images/controls.png +0 -0
  81. data/app/assets/stylesheets/images/icons-18-black.png +0 -0
  82. data/app/assets/stylesheets/images/icons-18-white.png +0 -0
  83. data/app/assets/stylesheets/images/icons-36-black.png +0 -0
  84. data/app/assets/stylesheets/images/icons-36-white.png +0 -0
  85. data/app/assets/stylesheets/images/ie6/borderBottomCenter.png +0 -0
  86. data/app/assets/stylesheets/images/ie6/borderBottomLeft.png +0 -0
  87. data/app/assets/stylesheets/images/ie6/borderBottomRight.png +0 -0
  88. data/app/assets/stylesheets/images/ie6/borderMiddleLeft.png +0 -0
  89. data/app/assets/stylesheets/images/ie6/borderMiddleRight.png +0 -0
  90. data/app/assets/stylesheets/images/ie6/borderTopCenter.png +0 -0
  91. data/app/assets/stylesheets/images/ie6/borderTopLeft.png +0 -0
  92. data/app/assets/stylesheets/images/ie6/borderTopRight.png +0 -0
  93. data/app/assets/stylesheets/images/loading.gif +0 -0
  94. data/app/assets/stylesheets/images/loading_background.png +0 -0
  95. data/app/assets/stylesheets/images/overlay.png +0 -0
  96. data/app/assets/stylesheets/images/spinner_bar.gif +0 -0
  97. data/spec/dummy/db/development.sqlite3 +0 -0
  98. data/spec/dummy/solr/data/test/index/segments_e1 +0 -0
  99. data/spec/factories/patron_relationship.rb +0 -6
  100. data/spec/factories/patron_relationship_type.rb +0 -5
  101. data/spec/factories/patron_type.rb +0 -5
  102. data/vendor/assets/stylesheets/images/ie6/borderBottomCenter.png +0 -0
  103. data/vendor/assets/stylesheets/images/ie6/borderBottomLeft.png +0 -0
  104. data/vendor/assets/stylesheets/images/ie6/borderBottomRight.png +0 -0
  105. data/vendor/assets/stylesheets/images/ie6/borderMiddleLeft.png +0 -0
  106. data/vendor/assets/stylesheets/images/ie6/borderMiddleRight.png +0 -0
  107. data/vendor/assets/stylesheets/images/ie6/borderTopCenter.png +0 -0
  108. data/vendor/assets/stylesheets/images/ie6/borderTopLeft.png +0 -0
  109. data/vendor/assets/stylesheets/images/ie6/borderTopRight.png +0 -0
@@ -1,11 +1,12 @@
1
- /*
1
+ /*!
2
2
  * jQuery UI Menubar @VERSION
3
+ * http://jqueryui.com
3
4
  *
4
- * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
5
- * Dual licensed under the MIT or GPL Version 2 licenses.
5
+ * Copyright 2013 jQuery Foundation and other contributors
6
+ * Released under the MIT license.
6
7
  * http://jquery.org/license
7
8
  *
8
- * http://docs.jquery.com/UI/Menubar
9
+ * http://api.jqueryui.com/menubar/
9
10
  *
10
11
  * Depends:
11
12
  * jquery.ui.core.js
@@ -15,182 +16,328 @@
15
16
  */
16
17
  (function( $ ) {
17
18
 
18
- // TODO when mixing clicking menus and keyboard navigation, focus handling is broken
19
- // there has to be just one item that has tabindex
20
19
  $.widget( "ui.menubar", {
21
20
  version: "@VERSION",
22
21
  options: {
23
- autoExpand: false,
24
- buttons: false,
25
22
  items: "li",
26
- menuElement: "ul",
27
- menuIcon: false,
23
+ menus: "ul",
24
+ icons: {
25
+ dropdown: "ui-icon-triangle-1-s"
26
+ },
28
27
  position: {
29
28
  my: "left top",
30
29
  at: "left bottom"
31
- }
30
+ },
31
+
32
+ // callbacks
33
+ select: null
32
34
  },
35
+
33
36
  _create: function() {
34
- var that = this;
37
+ // Top-level elements containing the submenu-triggering elem
35
38
  this.menuItems = this.element.children( this.options.items );
39
+
40
+ // Links or buttons in menuItems, triggers of the submenus
36
41
  this.items = this.menuItems.children( "button, a" );
37
42
 
38
- this.menuItems
39
- .addClass( "ui-menubar-item" )
40
- .attr( "role", "presentation" );
41
- // let only the first item receive focus
42
- this.items.slice(1).attr( "tabIndex", -1 );
43
+ // Keep track of open submenus
44
+ this.openSubmenus = 0;
43
45
 
46
+ this._initializeWidget();
47
+ this._initializeMenuItems();
48
+ this._initializeItems();
49
+ },
50
+
51
+ _initializeWidget: function() {
44
52
  this.element
45
53
  .addClass( "ui-menubar ui-widget-header ui-helper-clearfix" )
46
54
  .attr( "role", "menubar" );
47
- this._focusable( this.items );
48
- this._hoverable( this.items );
49
- this.items.siblings( this.options.menuElement )
50
- .menu({
51
- position: {
52
- within: this.options.position.within
53
- },
54
- select: function( event, ui ) {
55
- ui.item.parents( "ul.ui-menu:last" ).hide();
56
- that._close();
57
- // TODO what is this targetting? there's probably a better way to access it
58
- $(event.target).prev().focus();
59
- that._trigger( "select", event, ui );
60
- },
61
- menus: that.options.menuElement
62
- })
55
+ this._on({
56
+ keydown: function( event ) {
57
+ var active;
58
+
59
+ // If we are in a nested sub-sub-menu and we see an ESCAPE
60
+ // we must close recursively.
61
+ if ( event.keyCode === $.ui.keyCode.ESCAPE &&
62
+ this.active &&
63
+ this.active.menu( "collapse", event ) !== true ) {
64
+ active = this.active;
65
+ this.active.blur();
66
+ this._close( event );
67
+ $( event.target ).blur().mouseleave();
68
+ active.prev().focus();
69
+ }
70
+ },
71
+ focusin: function() {
72
+ clearTimeout( this.closeTimer );
73
+ },
74
+ focusout: function( event ) {
75
+ this.closeTimer = this._delay( function() {
76
+ this._close( event );
77
+ this.items.attr( "tabIndex", -1 );
78
+ this.lastFocused.attr( "tabIndex", 0 );
79
+ }, 150 );
80
+ },
81
+ "mouseenter .ui-menubar-item": function() {
82
+ clearTimeout( this.closeTimer );
83
+ }
84
+ } );
85
+ },
86
+
87
+ _initializeMenuItems: function() {
88
+ var subMenus,
89
+ menubar = this;
90
+
91
+ this.menuItems
92
+ .addClass( "ui-menubar-item" )
93
+ .attr( "role", "presentation" )
94
+ // TODO why do these not work when moved to CSS?
95
+ .css({
96
+ "border-width": "1px",
97
+ "border-style": "hidden"
98
+ });
99
+
100
+ subMenus = this.menuItems.children( menubar.options.menus ).menu({
101
+ position: {
102
+ within: this.options.position.within
103
+ },
104
+ select: function( event, ui ) {
105
+ // TODO don't hardcode markup selectors
106
+ ui.item.parents( "ul.ui-menu:last" ).hide();
107
+ menubar._close();
108
+ ui.item.parents( ".ui-menubar-item" ).children().first().focus();
109
+ menubar._trigger( "select", event, ui );
110
+ },
111
+ menus: this.options.menus
112
+ })
63
113
  .hide()
64
114
  .attr({
65
115
  "aria-hidden": "true",
66
116
  "aria-expanded": "false"
67
- })
68
- // TODO use _on
69
- .bind( "keydown.menubar", function( event ) {
70
- var menu = $( this );
117
+ });
118
+
119
+ this._on( subMenus, {
120
+ keydown: function( event ) {
121
+ // TODO why is this needed?
122
+ $( event.target ).attr( "tabIndex", 0 );
123
+ var parentButton,
124
+ menu = $( this );
125
+ // TODO why are there keydown events on a hidden menu?
71
126
  if ( menu.is( ":hidden" ) ) {
72
127
  return;
73
128
  }
74
129
  switch ( event.keyCode ) {
75
130
  case $.ui.keyCode.LEFT:
76
- that.previous( event );
131
+ // TODO why can't this call menubar.previous()?
132
+ parentButton = menubar.active.prev( ".ui-button" );
133
+
134
+ if ( this.openSubmenus ) {
135
+ this.openSubmenus--;
136
+ } else if ( this._hasSubMenu( parentButton.parent().prev() ) ) {
137
+ menubar.active.blur();
138
+ menubar._open( event, parentButton.parent().prev().find( ".ui-menu" ) );
139
+ } else {
140
+ parentButton.parent().prev().find( ".ui-button" ).focus();
141
+ menubar._close( event );
142
+ this.open = true;
143
+ }
144
+
77
145
  event.preventDefault();
146
+ // TODO same as above where it's set to 0
147
+ $( event.target ).attr( "tabIndex", -1 );
78
148
  break;
79
149
  case $.ui.keyCode.RIGHT:
80
- that.next( event );
150
+ this.next( event );
81
151
  event.preventDefault();
82
152
  break;
83
153
  }
154
+ },
155
+ focusout: function( event ) {
156
+ // TODO why does this have to use event.target? Is that different from currentTarget?
157
+ $( event.target ).removeClass( "ui-state-focus" );
158
+ }
159
+ });
160
+
161
+ this.menuItems.each(function( index, menuItem ) {
162
+ menubar._identifyMenuItemsNeighbors( $( menuItem ), menubar, index );
163
+ });
164
+
165
+ },
166
+
167
+ _hasSubMenu: function( menuItem ) {
168
+ return $( menuItem ).children( this.options.menus ).length > 0;
169
+ },
170
+
171
+ // TODO get rid of these - currently still in use in _move
172
+ _identifyMenuItemsNeighbors: function( menuItem, menubar, index ) {
173
+ var collectionLength = this.menuItems.length,
174
+ isFirstElement = ( index === 0 ),
175
+ isLastElement = ( index === ( collectionLength - 1 ) );
176
+
177
+ if ( isFirstElement ) {
178
+ menuItem.data( "prevMenuItem", $( this.menuItems[collectionLength - 1]) );
179
+ menuItem.data( "nextMenuItem", $( this.menuItems[index+1]) );
180
+ } else if ( isLastElement ) {
181
+ menuItem.data( "nextMenuItem", $( this.menuItems[0]) );
182
+ menuItem.data( "prevMenuItem", $( this.menuItems[index-1]) );
183
+ } else {
184
+ menuItem.data( "nextMenuItem", $( this.menuItems[index+1]) );
185
+ menuItem.data( "prevMenuItem", $( this.menuItems[index-1]) );
186
+ }
187
+ },
188
+
189
+ _initializeItems: function() {
190
+ var menubar = this;
191
+
192
+ this._focusable( this.items );
193
+ this._hoverable( this.items );
194
+
195
+ // let only the first item receive focus
196
+ this.items.slice(1).attr( "tabIndex", -1 );
197
+
198
+ this.items.each(function( index, item ) {
199
+ menubar._initializeItem( $( item ), menubar );
200
+ });
201
+ },
202
+
203
+ _initializeItem: function( anItem ) {
204
+ var menuItemHasSubMenu = this._hasSubMenu( anItem.parent() );
205
+
206
+ anItem
207
+ .addClass( "ui-button ui-widget ui-button-text-only ui-menubar-link" )
208
+ .attr( "role", "menuitem" )
209
+ .wrapInner( "<span class='ui-button-text'></span>" );
210
+
211
+ this._on( anItem, {
212
+ focus: function(){
213
+ anItem.attr( "tabIndex", 0 );
214
+ anItem.addClass( "ui-state-focus" );
215
+ event.preventDefault();
216
+ },
217
+ focusout: function(){
218
+ anItem.attr( "tabIndex", -1 );
219
+ this.lastFocused = anItem;
220
+ anItem.removeClass( "ui-state-focus" );
221
+ event.preventDefault();
222
+ }
223
+ } );
224
+
225
+ if ( menuItemHasSubMenu ) {
226
+ this._on( anItem, {
227
+ click: this._mouseBehaviorForMenuItemWithSubmenu,
228
+ focus: this._mouseBehaviorForMenuItemWithSubmenu,
229
+ mouseenter: this._mouseBehaviorForMenuItemWithSubmenu
84
230
  });
85
- this.items.each(function() {
86
- var input = $(this),
87
- // TODO menu var is only used on two places, doesn't quite justify the .each
88
- menu = input.next( that.options.menuElement );
89
-
90
- // might be a non-menu button
91
- if ( menu.length ) {
92
- // TODO use _on
93
- input.bind( "click.menubar focus.menubar mouseenter.menubar", function( event ) {
94
- // ignore triggered focus event
95
- if ( event.type === "focus" && !event.originalEvent ) {
96
- return;
97
- }
98
- event.preventDefault();
99
- // TODO can we simplify or extractthis check? especially the last two expressions
100
- // there's a similar active[0] == menu[0] check in _open
101
- if ( event.type === "click" && menu.is( ":visible" ) && that.active && that.active[0] === menu[0] ) {
102
- that._close();
103
- return;
104
- }
105
- if ( ( that.open && event.type === "mouseenter" ) || event.type === "click" || that.options.autoExpand ) {
106
- if( that.options.autoExpand ) {
107
- clearTimeout( that.closeTimer );
108
- }
109
231
 
110
- that._open( event, menu );
111
- }
112
- })
113
- // TODO use _on
114
- .bind( "keydown", function( event ) {
232
+ this._on( anItem, {
233
+ keydown: function( event ) {
115
234
  switch ( event.keyCode ) {
116
235
  case $.ui.keyCode.SPACE:
117
236
  case $.ui.keyCode.UP:
118
237
  case $.ui.keyCode.DOWN:
119
- that._open( event, $( this ).next() );
238
+ this._open( event, $( event.target ).next() );
120
239
  event.preventDefault();
121
240
  break;
122
241
  case $.ui.keyCode.LEFT:
123
- that.previous( event );
242
+ this.previous( event );
124
243
  event.preventDefault();
125
244
  break;
126
245
  case $.ui.keyCode.RIGHT:
127
- that.next( event );
246
+ this.next( event );
128
247
  event.preventDefault();
129
248
  break;
249
+ case $.ui.keyCode.TAB:
250
+ break;
130
251
  }
131
- })
132
- .attr( "aria-haspopup", "true" );
133
-
134
- // TODO review if these options (menuIcon and buttons) are a good choice, maybe they can be merged
135
- if ( that.options.menuIcon ) {
136
- input.addClass( "ui-state-default" ).append( "<span class='ui-button-icon-secondary ui-icon ui-icon-triangle-1-s'></span>" );
137
- input.removeClass( "ui-button-text-only" ).addClass( "ui-button-text-icon-secondary" );
138
252
  }
139
- } else {
140
- // TODO use _on
141
- input.bind( "click.menubar mouseenter.menubar", function( event ) {
142
- if ( ( that.open && event.type === "mouseenter" ) || event.type === "click" ) {
143
- that._close();
144
- }
145
- });
146
- }
147
-
148
- input
149
- .addClass( "ui-button ui-widget ui-button-text-only ui-menubar-link" )
150
- .attr( "role", "menuitem" )
151
- .wrapInner( "<span class='ui-button-text'></span>" );
253
+ });
152
254
 
153
- if ( that.options.buttons ) {
154
- input.removeClass( "ui-menubar-link" ).addClass( "ui-state-default" );
255
+ anItem.attr( "aria-haspopup", "true" );
256
+ if ( this.options.icons ) {
257
+ anItem.append( "<span class='ui-button-icon-secondary ui-icon " + this.options.icons.dropdown + "'></span>" );
258
+ anItem.removeClass( "ui-button-text-only" ).addClass( "ui-button-text-icon-secondary" );
155
259
  }
156
- });
157
- that._on( {
158
- keydown: function( event ) {
159
- if ( event.keyCode === $.ui.keyCode.ESCAPE && that.active && that.active.menu( "collapse", event ) !== true ) {
160
- var active = that.active;
161
- that.active.blur();
162
- that._close( event );
163
- active.prev().focus();
164
- }
165
- },
166
- focusin: function( event ) {
167
- clearTimeout( that.closeTimer );
168
- },
169
- focusout: function( event ) {
170
- that.closeTimer = setTimeout( function() {
171
- that._close( event );
172
- }, 150);
173
- },
174
- "mouseleave .ui-menubar-item": function( event ) {
175
- if ( that.options.autoExpand ) {
176
- that.closeTimer = setTimeout( function() {
177
- that._close( event );
178
- }, 150);
260
+ } else {
261
+ this._on( anItem, {
262
+ click: function() {
263
+ if ( this.active ) {
264
+ this._close();
265
+ } else {
266
+ this.open = true;
267
+ this.active = $( anItem ).parent();
268
+ }
269
+ },
270
+ mouseenter: function() {
271
+ if ( this.open ) {
272
+ this.stashedOpenMenu = this.active;
273
+ this._close();
274
+ }
275
+ },
276
+ keydown: function( event ) {
277
+ if ( event.keyCode === $.ui.keyCode.LEFT ) {
278
+ this.previous( event );
279
+ event.preventDefault();
280
+ } else if ( event.keyCode === $.ui.keyCode.RIGHT ) {
281
+ this.next( event );
282
+ event.preventDefault();
283
+ }
179
284
  }
180
- },
181
- "mouseenter .ui-menubar-item": function( event ) {
182
- clearTimeout( that.closeTimer );
183
- }
184
- });
285
+ });
286
+ }
287
+ },
185
288
 
186
- // Keep track of open submenus
187
- this.openSubmenus = 0;
289
+ // TODO silly name, too much complexity
290
+ // TODO why is this used for three types of events?
291
+ _mouseBehaviorForMenuItemWithSubmenu: function( event ) {
292
+ var isClickingToCloseOpenMenu, menu;
293
+
294
+ // ignore triggered focus event
295
+ if ( event.type === "focus" && !event.originalEvent ) {
296
+ return;
297
+ }
298
+ event.preventDefault();
299
+
300
+ menu = $(event.target).parents( ".ui-menubar-item" ).children( this.options.menus );
301
+
302
+ // If we have an open menu and we see a click on the menuItem
303
+ // and the menu thereunder is the same as the active menu, close it.
304
+ // Succinctly: toggle menu open / closed on the menuItem
305
+ isClickingToCloseOpenMenu = event.type === "click" &&
306
+ menu.is( ":visible" ) &&
307
+ this.active &&
308
+ this.active[0] === menu[0];
309
+
310
+ if ( isClickingToCloseOpenMenu ) {
311
+ this._close();
312
+ return;
313
+ }
314
+ if ( event.type === "mouseenter" ) {
315
+ this.element.find( ":focus" ).focusout();
316
+ if ( this.stashedOpenMenu ) {
317
+ this._open( event, menu);
318
+ }
319
+ this.stashedOpenMenu = undefined;
320
+ }
321
+ // If we already opened a menu and then changed to be "over" another MenuItem ||
322
+ // we clicked on a new menuItem (whether open or not) or if we auto expand (i.e.
323
+ // we expand regardless of click if there is a submenu
324
+ if ( ( this.open && event.type === "mouseenter" ) || event.type === "click" ) {
325
+ clearTimeout( this.closeTimer );
326
+ this._open( event, menu );
327
+ // Stop propagation so that menuItem mouseenter doesn't fire. If it does it
328
+ // takes the "selected" status off off of the first element of the submenu.
329
+ event.stopPropagation();
330
+ }
188
331
  },
189
332
 
190
333
  _destroy : function() {
191
334
  this.menuItems
192
335
  .removeClass( "ui-menubar-item" )
193
- .removeAttr( "role" );
336
+ .removeAttr( "role" )
337
+ .css({
338
+ "border-width": "",
339
+ "border-style": ""
340
+ });
194
341
 
195
342
  this.element
196
343
  .removeClass( "ui-menubar ui-widget-header ui-helper-clearfix" )
@@ -202,13 +349,19 @@ $.widget( "ui.menubar", {
202
349
  .removeClass( "ui-button ui-widget ui-button-text-only ui-menubar-link ui-state-default" )
203
350
  .removeAttr( "role" )
204
351
  .removeAttr( "aria-haspopup" )
205
- // TODO unwrap?
206
- .children( "span.ui-button-text" ).each(function( i, e ) {
352
+ .children( ".ui-icon" ).remove();
353
+
354
+ // TODO fix this
355
+ if ( false ) {
356
+ // Does not unwrap
357
+ this.items.children( "span.ui-button-text" ).unwrap();
358
+ } else {
359
+ // Does "unwrap"
360
+ this.items.children( "span.ui-button-text" ).each( function(){
207
361
  var item = $( this );
208
362
  item.parent().html( item.html() );
209
- })
210
- .end()
211
- .children( ".ui-icon" ).remove();
363
+ });
364
+ }
212
365
 
213
366
  this.element.find( ":ui-menu" )
214
367
  .menu( "destroy" )
@@ -219,8 +372,8 @@ $.widget( "ui.menubar", {
219
372
  .unbind( ".menubar" );
220
373
  },
221
374
 
222
- _close: function() {
223
- if ( !this.active || !this.active.length ) {
375
+ _collapseActiveMenu: function() {
376
+ if ( !this.active.is( ":ui-menu" ) ) {
224
377
  return;
225
378
  }
226
379
  this.active
@@ -229,58 +382,70 @@ $.widget( "ui.menubar", {
229
382
  .attr({
230
383
  "aria-hidden": "true",
231
384
  "aria-expanded": "false"
232
- });
233
- this.active
234
- .prev()
235
- .removeClass( "ui-state-active" )
236
- .removeAttr( "tabIndex" );
385
+ })
386
+ .closest( this.options.items ).removeClass( "ui-state-active" );
387
+ },
388
+
389
+ _close: function() {
390
+ if ( !this.active ) {
391
+ return;
392
+ }
393
+
394
+ this._collapseActiveMenu();
395
+
237
396
  this.active = null;
238
397
  this.open = false;
239
398
  this.openSubmenus = 0;
240
399
  },
241
400
 
242
401
  _open: function( event, menu ) {
243
- // on a single-button menubar, ignore reopening the same menu
244
- if ( this.active && this.active[0] === menu[0] ) {
245
- return;
402
+ var menuItem = menu.closest( ".ui-menubar-item" );
403
+
404
+ if ( this.active && this.active.length &&
405
+ this._hasSubMenu( this.active.closest( this.options.items ) ) ) {
406
+ this._collapseActiveMenu();
246
407
  }
247
- // TODO refactor, almost the same as _close above, but don't remove tabIndex
248
- if ( this.active ) {
249
- this.active
250
- .menu( "collapseAll" )
251
- .hide()
252
- .attr({
253
- "aria-hidden": "true",
254
- "aria-expanded": "false"
255
- });
256
- this.active
257
- .prev()
258
- .removeClass( "ui-state-active" );
408
+
409
+ menuItem.addClass( "ui-state-active" );
410
+ // workaround when clicking a non-menu item, then hovering a menu, then going back
411
+ // this way afterwards its still possible to tab back to a menubar, even if its
412
+ // the wrong item
413
+ // see also "click menu-less item, hover in and out of item with menu" test in menubar_core
414
+ if ( !this.lastFocused ) {
415
+ this.lastFocused = menu.prev();
259
416
  }
260
- // set tabIndex -1 to have the button skipped on shift-tab when menu is open (it gets focus)
261
- var button = menu.prev().addClass( "ui-state-active" ).attr( "tabIndex", -1 );
417
+
262
418
  this.active = menu
263
419
  .show()
264
420
  .position( $.extend({
265
- of: button
421
+ of: menuItem
266
422
  }, this.options.position ) )
267
423
  .removeAttr( "aria-hidden" )
268
424
  .attr( "aria-expanded", "true" )
269
- .menu("focus", event, menu.children( ".ui-menu-item" ).first() )
270
- // TODO need a comment here why both events are triggered
271
- .focus()
272
- .focusin();
425
+ .menu( "focus", event, menu.children( ".ui-menu-item" ).first() )
426
+ .focus();
427
+
273
428
  this.open = true;
274
429
  },
275
430
 
276
431
  next: function( event ) {
277
- if ( this.open && this.active.data( "menu" ).active.has( ".ui-menu" ).length ) {
278
- // Track number of open submenus and prevent moving to next menubar item
279
- this.openSubmenus++;
280
- return;
432
+ function shouldOpenNestedSubMenu() {
433
+ return this.active &&
434
+ this._hasSubMenu( this.active.closest( this.options.items ) ) &&
435
+ this.active.data( "uiMenu" ) &&
436
+ this.active.data( "uiMenu" ).active &&
437
+ this.active.data( "uiMenu" ).active.has( ".ui-menu" ).length;
438
+ }
439
+
440
+ if ( this.open ) {
441
+ if ( shouldOpenNestedSubMenu.call( this ) ) {
442
+ // Track number of open submenus and prevent moving to next menubar item
443
+ this.openSubmenus++;
444
+ return;
445
+ }
281
446
  }
282
447
  this.openSubmenus = 0;
283
- this._move( "next", "first", event );
448
+ this._move( "next", event );
284
449
  },
285
450
 
286
451
  previous: function( event ) {
@@ -290,38 +455,28 @@ $.widget( "ui.menubar", {
290
455
  return;
291
456
  }
292
457
  this.openSubmenus = 0;
293
- this._move( "prev", "last", event );
458
+ this._move( "prev", event );
294
459
  },
295
460
 
296
- _move: function( direction, filter, event ) {
297
- var next,
298
- wrapItem;
299
- if ( this.open ) {
300
- next = this.active.closest( ".ui-menubar-item" )[ direction + "All" ]( this.options.items ).first().children( ".ui-menu" ).eq( 0 );
301
- wrapItem = this.menuItems[ filter ]().children( ".ui-menu" ).eq( 0 );
302
- } else {
303
- if ( event ) {
304
- next = $( event.target ).closest( ".ui-menubar-item" )[ direction + "All" ]( this.options.items ).children( ".ui-menubar-link" ).eq( 0 );
305
- wrapItem = this.menuItems[ filter ]().children( ".ui-menubar-link" ).eq( 0 );
306
- } else {
307
- next = wrapItem = this.menuItems.children( "a" ).eq( 0 );
308
- }
309
- }
461
+ _move: function( direction, event ) {
462
+ var closestMenuItem = $( event.target ).closest( ".ui-menubar-item" ),
463
+ nextMenuItem = closestMenuItem.data( direction + "MenuItem" ),
464
+ focusableTarget = nextMenuItem.find( ".ui-button" );
310
465
 
311
- if ( next.length ) {
312
- if ( this.open ) {
313
- this._open( event, next );
466
+ if ( this.open ) {
467
+ if ( this._hasSubMenu( nextMenuItem ) ) {
468
+ this._open( event, nextMenuItem.children( ".ui-menu" ) );
314
469
  } else {
315
- next.removeAttr( "tabIndex")[0].focus();
470
+ this._collapseActiveMenu();
471
+ nextMenuItem.find( ".ui-button" ).focus();
472
+ this.open = true;
316
473
  }
317
474
  } else {
318
- if ( this.open ) {
319
- this._open( event, wrapItem );
320
- } else {
321
- wrapItem.removeAttr( "tabIndex")[0].focus();
322
- }
475
+ closestMenuItem.find( ".ui-button" );
476
+ focusableTarget.focus();
323
477
  }
324
478
  }
479
+
325
480
  });
326
481
 
327
482
  }( jQuery ));