|
2 | 2 | * Sets up subnav on the sitenav
|
3 | 3 | */
|
4 | 4 |
|
5 |
| -var setupSiteNav = function(){ |
| 5 | +var closeAllDropdownMenus = function() { |
| 6 | + $PBJQ('.Mrphs-sitesNav__menuitem').removeClass('dropdown-is-visible'); |
| 7 | + $PBJQ('.Mrphs-sitesNav__menuitem').find('.is-visible').removeClass('is-visible'); |
| 8 | + $PBJQ('.Mrphs-sitesNav__menuitem').find('.is-clicked').removeClass('is-clicked'); |
| 9 | + |
| 10 | + $PBJQ('.sitenav-dropdown-overlay').remove(); |
| 11 | +}; |
| 12 | + |
| 13 | +var buildDropdownMenu = function(container, siteId, callback) { |
| 14 | + var navsubmenu = "<ul class=\"Mrphs-sitesNav__submenu\" role=\"menu\">"; |
| 15 | + var maxToolsInt = parseInt($PBJQ('#linkNav').attr('data-max-tools-int')); |
| 16 | + var maxToolsText = $PBJQ('#linkNav').attr('data-max-tools-anchor'); |
| 17 | + var goToSite = '<li class=\"Mrphs-sitesNav__submenuitem\"><a role=\"menuitem\" href=\"' + portal.portalPath + '/site/' + siteId + '\" title=\"' + maxToolsText + '\"><span class=\"toolMenuIcon icon-sakai-see-all-tools\"></span>' + maxToolsText + '</a></li>'; |
| 18 | + var siteURL = '/direct/site/' + siteId + '/pages.json'; |
| 19 | + var currentSite = window.location.pathname.split('/').pop(); |
| 20 | + |
| 21 | + $PBJQ.ajax({ |
| 22 | + url: siteURL, |
| 23 | + dataType: "json", |
| 24 | + success: function(data){ |
| 25 | + |
| 26 | + $PBJQ.each(data, function(i, item) { |
| 27 | + |
| 28 | + // Check to see if this is the current tool in the site |
| 29 | + var isCurrent = ""; |
| 30 | + if (currentSite == item.tools[0].id) { |
| 31 | + isCurrent = " is-current"; |
| 32 | + } |
6 | 33 |
|
7 |
| - $PBJQ("ul.Mrphs-sitesNav__menu").each(function(){ |
| 34 | + if (i <= maxToolsInt) { |
| 35 | + var li_template; |
| 36 | + |
| 37 | + if (item.toolpopup) { |
| 38 | + var link_attrs = ' role="menuitem" href="{{tool_url}}?sakai.popup=yes" title="{{item_title}}" onclick="window.open(\'{{item_toolpopupurl}}\');"'; |
| 39 | + li_template = '<li class="Mrphs-sitesNav__submenuitem" >' + |
| 40 | + '<a class="Mrphs-sitesNav__submenuitem-icon"' + link_attrs + '><span class="toolMenuIcon icon-{{icon}}"></span></a>' + |
| 41 | + '<a class="Mrphs-sitesNav__submenuitem-title"' + link_attrs + '>{{item_title}}</a>' + |
| 42 | + '</li>'; |
| 43 | + } else { |
| 44 | + var link_attrs = ' role="menuitem" href="{{tool_url}}" title="{{item_title}}"'; |
| 45 | + |
| 46 | + li_template = '<li class="Mrphs-sitesNav__submenuitem{{is_current}}">' + |
| 47 | + '<a class="Mrphs-sitesNav__submenuitem-icon"' + link_attrs + '><span class="toolMenuIcon icon-{{icon}}"></span></a>' + |
| 48 | + '<a class="Mrphs-sitesNav__submenuitem-title"' + link_attrs + '>{{item_title}}</a>' + |
| 49 | + '</li>'; |
| 50 | + } |
8 | 51 |
|
9 |
| - // Add an escape key handler to slide the page menu up |
10 |
| - $PBJQ(this).keydown(function(e) { |
11 |
| - if (e.keyCode == 27) { |
12 |
| - $PBJQ(this).parent().children('a').focus(); |
13 |
| - $PBJQ(this).toggleClass('is-visible'); |
14 |
| - } |
15 |
| - }); |
| 52 | + navsubmenu += (li_template |
| 53 | + .replace(/{{tool_url}}/g, item.tools[0].url) |
| 54 | + .replace(/{{item_title}}/g, item.title) |
| 55 | + .replace(/{{item_toolpopupurl}}/g, item.toolpopupurl) |
| 56 | + .replace(/{{icon}}/g, item.tools[0].toolId.replace(/\./gi, '-')) |
| 57 | + .replace(/{{is_current}}/g, isCurrent)); |
| 58 | + } |
| 59 | + }); |
16 | 60 |
|
17 |
| - $PBJQ(this).children('li:last').addClass('is-last-item') |
18 |
| - }); |
| 61 | + if((data.length - 1) > maxToolsInt) { |
| 62 | + navsubmenu += goToSite |
| 63 | + } |
19 | 64 |
|
20 |
| - $PBJQ("ul.Mrphs-sitesNav__menu > li").mouseleave(function(){ |
21 |
| - $PBJQ(this).find('ul').toggleClass('is-visible'); |
22 |
| - }); |
| 65 | + navsubmenu += "</ul>" |
23 | 66 |
|
24 |
| - fixTopNav = function(e) { |
| 67 | + navsubmenu = $(navsubmenu); |
25 | 68 |
|
26 |
| - if (e.keyCode == 40) { // downarrow |
27 |
| - |
28 |
| - e.preventDefault(); |
29 |
| - $PBJQ('#selectSite').hide(); |
30 |
| - //$PBJQ('.nav-submenu').hide(); |
31 |
| - // Trigger click on the drop <span>, passing true to set focus on |
32 |
| - // the first tool in the dropdown. |
33 |
| - $PBJQ(this).parent().find(".Mrphs-sitesNav__dropdown").trigger('click',[true]); |
| 69 | + container.append(navsubmenu); |
34 | 70 |
|
35 |
| - } else if (e.keyCode == 27) { // uparrow ? or ESC |
| 71 | + addArrowNavAndDisableTabNav(navsubmenu); |
36 | 72 |
|
37 |
| - $PBJQ(this).parent().children('a').focus(); |
38 |
| - $PBJQ(this).toggleClass('is-visible'); |
| 73 | + callback(navsubmenu); |
| 74 | + }, |
39 | 75 |
|
| 76 | + error: function(XMLHttpRequest, status, error){ |
| 77 | + // Something happened getting the tool list. |
40 | 78 | }
|
41 |
| - } |
42 |
| - |
43 |
| - // SAK-25505 - Switch from live() to on() |
44 |
| - // $PBJQ( "a.offsite" ).live( "click", function() { |
45 |
| - // $PBJQ('.topnav > li.nav-menu > a').live('keydown', function(e){ |
46 |
| - if($PBJQ(document).on) { |
| 79 | + }); |
| 80 | +}; |
47 | 81 |
|
48 |
| - $PBJQ(document).on('keydown', '.Mrphs-sitesNav__menu > li.Mrphs-sitesNav__menuitem > a', fixTopNav); |
49 | 82 |
|
50 |
| - } else { |
| 83 | +var setupSiteNav = function(){ |
51 | 84 |
|
52 |
| - $PBJQ('.Mrphs-sitesNav__menu > .Mrphs-sitesNav__menuitem > a').live('keydown', fixTopNav); |
| 85 | + $PBJQ("ul.Mrphs-sitesNav__menu").each(function(){ |
53 | 86 |
|
54 |
| - } |
55 |
| - |
56 |
| - $PBJQ("ul.Mrphs-sitesNav__menu > li").mouseleave(function(){ |
57 |
| - $PBJQ(this).find('ul').toggleClass('is-visible'); |
| 87 | + // Add an escape key handler to slide the page menu up |
| 88 | + $PBJQ(this).keydown(function(e) { |
| 89 | + if (e.keyCode == 27) { |
| 90 | + closeAllDropdownMenus(); |
| 91 | + } |
| 92 | + }); |
58 | 93 | });
|
| 94 | + |
| 95 | + $PBJQ(document).on('keydown', '.Mrphs-sitesNav__menu > li.Mrphs-sitesNav__menuitem > a', |
| 96 | + function (e) { |
| 97 | + if (e.keyCode == 40) { |
| 98 | + // downarrow |
| 99 | + e.preventDefault(); |
| 100 | + // Trigger click on the drop <span>, passing true to set focus on |
| 101 | + // the first tool in the dropdown. |
| 102 | + var dropdown = $PBJQ(this).parent().find(".Mrphs-sitesNav__dropdown"); |
| 103 | + |
| 104 | + if (dropdown.data('clicked')) { |
| 105 | + // If the user has already triggered a click, give the |
| 106 | + // AJAX a chance to finish. |
| 107 | + } else { |
| 108 | + dropdown.data('clicked', true); |
| 109 | + dropdown.trigger('click', [true]); |
| 110 | + } |
| 111 | + } else if (e.keyCode == 27) { |
| 112 | + // escape |
| 113 | + e.preventDefault(); |
| 114 | + closeAllDropdownMenus(); |
| 115 | + } |
| 116 | + |
| 117 | + }); |
| 118 | + |
59 | 119 | // focusFirstLink is only ever passed from the keydown handler. We
|
60 | 120 | // don't want to focus on click; it looks odd.
|
61 |
| - |
62 | 121 | $PBJQ("ul.Mrphs-sitesNav__menu li span.Mrphs-sitesNav__dropdown").click(function(e, focusFirstLink) {
|
| 122 | + e.preventDefault() |
63 | 123 |
|
64 |
| - /* |
65 |
| - * see if there is a menu sibling |
66 |
| - * if there is a child, display it |
67 |
| - * if no menu sibling |
68 |
| - * retrieve data, construct the menu, append |
69 |
| - */ |
| 124 | + var jqObjDrop = $PBJQ(e.target); |
| 125 | + var container = jqObjDrop.closest('.Mrphs-sitesNav__menuitem'); |
70 | 126 |
|
71 |
| - $PBJQ(this).toggleClass("is-clicked"); //On click toggle class "is-clicked" |
72 |
| - e.preventDefault() |
| 127 | + var dropdownWasShown = container.hasClass('dropdown-is-visible'); |
73 | 128 |
|
74 |
| - var jqObjDrop = $PBJQ(e.target); |
| 129 | + // Hide any currently shown menus so we don't end up with multiple dropdowns shown |
| 130 | + closeAllDropdownMenus(); |
75 | 131 |
|
76 |
| - if(jqObjDrop.next('ul').length) { |
77 |
| - jqObjDrop.next('ul').toggleClass('is-visible'); |
78 |
| - |
79 |
| - if(focusFirstLink) { |
80 |
| - jqObjDrop.next('ul').find("a:first").focus(); |
| 132 | + if (dropdownWasShown) { |
| 133 | + // We've hidden the dropdown now, so all done. |
| 134 | + return; |
81 | 135 | }
|
82 | 136 |
|
83 |
| - } |
| 137 | + var dropdownArrow = $PBJQ(this); |
84 | 138 |
|
85 |
| - else { |
| 139 | + var displayDropdown = function (navsubmenu) { |
| 140 | + // Mark the dropdown arrow and the menu itself as clicked |
| 141 | + dropdownArrow.addClass("is-clicked"); |
| 142 | + container.addClass('dropdown-is-visible'); |
86 | 143 |
|
87 |
| - var navsubmenu = "<ul class=\"Mrphs-sitesNav__submenu is-visible\" role=\"menu\">"; |
88 |
| - var siteId = jqObjDrop.attr('data-site-id'); |
89 |
| - var maxToolsInt = parseInt($PBJQ('#linkNav').attr('data-max-tools-int')); |
90 |
| - var maxToolsText = $PBJQ('#linkNav').attr('data-max-tools-anchor'); |
91 |
| - var goToSite = '<li class=\"Mrphs-sitesNav__submenuitem\"><a role=\"menuitem\" href=\"' + portal.portalPath + '/site/' + siteId + '\" title=\"' + maxToolsText + '\"><span class=\"toolMenuIcon icon-sakai-see-all-tools\"></span>' + maxToolsText + '</a></li>'; |
92 |
| - var siteURL = '/direct/site/' + jqObjDrop.attr('data-site-id') + '/pages.json'; |
93 |
| - var currentSite = window.location.pathname.split('/').pop(); |
| 144 | + // now display the menu |
| 145 | + navsubmenu.addClass('is-visible'); |
94 | 146 |
|
95 |
| - $PBJQ.ajax({ |
96 |
| - url: siteURL, |
97 |
| - dataType: "json", |
98 |
| - success: function(data){ |
| 147 | + if(focusFirstLink) { |
| 148 | + container.find('a.Mrphs-sitesNav__submenuitem-title').first().focus(); |
| 149 | + } |
99 | 150 |
|
100 |
| - $PBJQ.each(data, function(i, item) { |
| 151 | + // Add an invisible overlay to allow clicks to close the dropdown |
| 152 | + var overlay = $('<div class="sitenav-dropdown-overlay" />'); |
101 | 153 |
|
102 |
| - if (i <= maxToolsInt) { |
103 |
| - |
104 |
| - if (item.toolpopup) { |
105 |
| - navsubmenu = navsubmenu + '<li class=\"Mrphs-sitesNav__submenuitem\" ><a role=\"menuitem\" href=\"' + item.tools[0].url + "?sakai.popup=yes\" title=\"" + item.title + "\" onclick=\"window.open('" + item.toolpopupurl + "');\"><span class=\"toolMenuIcon icon-" + item.tools[0].toolId.replace(/\./gi, '-') + "\"></span>" + item.title + "</a></li>"; |
| 154 | + overlay.on('click', function (e) { |
| 155 | + closeAllDropdownMenus(); |
| 156 | + }); |
106 | 157 |
|
107 |
| - } else if (item.tools.length >= 1) { // changed from item.tools.length === 1 |
| 158 | + $('body').prepend(overlay); |
108 | 159 |
|
109 |
| - // Check to see if this is the current tool in the site |
110 |
| - var isCurrent = ""; |
111 |
| - if (currentSite == item.tools[0].id) { |
112 |
| - var isCurrent = " is-current"; |
113 |
| - } |
114 |
| - navsubmenu = navsubmenu + '<li class=\"Mrphs-sitesNav__submenuitem' + isCurrent + '\"><a role=\"menuitem\" href=\"' + item.tools[0].url + "\" title=\"" + item.title + "\"><span class=\"toolMenuIcon icon-" + item.tools[0].toolId.replace(/\./gi, '-') + "\"></span>" + item.title + "</a></li>"; |
115 |
| - } |
116 |
| - } |
117 |
| - }); |
| 160 | + dropdownArrow.removeData('clicked'); |
| 161 | + }; |
118 | 162 |
|
119 |
| - if((data.length - 1) > maxToolsInt) { |
120 |
| - navsubmenu = navsubmenu + goToSite |
121 |
| - } |
122 |
| - |
123 |
| - navsubmenu = navsubmenu + "</ul>" |
124 |
| - |
125 |
| - jqObjDrop.after(navsubmenu); |
126 |
| - |
127 |
| - if(focusFirstLink) { |
128 |
| - jqObjDrop.next('ul').find("a:first").focus(); |
129 |
| - } |
| 163 | + if (!container.find('ul').length) { |
| 164 | + // We haven't yet built the dropdown menu for this item. Do that now. |
| 165 | + buildDropdownMenu(container, jqObjDrop.attr('data-site-id'), displayDropdown); |
| 166 | + } else { |
| 167 | + displayDropdown(container.find('ul')); |
| 168 | + } |
130 | 169 |
|
131 |
| - addArrowNavAndDisableTabNav($PBJQ(".Mrphs-sitesNav__submenu")); |
| 170 | + }).hover(function(){ |
| 171 | + $PBJQ(this).toggleClass("Mrphs-sitesNav__dropdown--hover"); //On hover over, add |
| 172 | + }); |
| 173 | +} |
132 | 174 |
|
133 |
| - }, |
| 175 | +/* Callback is a function and is called after sliding up ul */ |
| 176 | +function addArrowNavAndDisableTabNav(ul) { |
| 177 | + ul.find('li a').attr('tabindex','-1').keydown(function (e) { |
| 178 | + var obj = $PBJQ(e.target); |
| 179 | + if (e.keyCode == 40) { |
| 180 | + // Down arrow. Move to the next item, or loop around if we're at the bottom. |
| 181 | + e.preventDefault(); |
| 182 | + var next = obj.closest('li').next(); |
134 | 183 |
|
135 |
| - error: function(XMLHttpRequest, status, error){ |
136 |
| - // Something happened getting the tool list. |
| 184 | + if (next.length === 0 || next.find('a.Mrphs-sitesNav__submenuitem-title').length == 0) { |
| 185 | + // loop around |
| 186 | + next = ul.find('li').first(); |
137 | 187 | }
|
138 |
| - }); |
139 |
| - } |
140 | 188 |
|
141 |
| - }).hover(function(){ |
142 |
| - $PBJQ(this).toggleClass("Mrphs-sitesNav__dropdown--hover"); //On hover over, add |
143 |
| - }); |
| 189 | + next.find('a.Mrphs-sitesNav__submenuitem-title').focus(); |
144 | 190 |
|
145 |
| -} |
| 191 | + } else if (e.keyCode == 38) { |
| 192 | + // Up arrow. Move to the previous item, or loop around if we're at the top. |
| 193 | + e.preventDefault(); |
| 194 | + var prev = obj.closest('li').prev(); |
146 | 195 |
|
147 |
| -/* Callback is a function and is called after sliding up ul */ |
148 |
| -function addArrowNavAndDisableTabNav(ul,callback) { |
149 |
| - ul.find('li a').attr('tabindex','-1').keydown(function (e) { |
150 |
| - var obj = $PBJQ(e.target); |
151 |
| - if(e.keyCode == 40) { |
152 |
| - e.preventDefault(); |
153 |
| - var next = obj.parent().parent().next(); |
154 |
| - if(next[0] === undefined) { |
155 |
| - ul.slideUp('fast'); |
156 |
| - if(callback !== undefined) { |
157 |
| - callback(); |
158 |
| - } else { |
159 |
| - obj.parent().parent().parent().parent().children('a').focus(); |
160 |
| - } |
161 |
| - } else { |
162 |
| - next.find('a').focus(); |
163 |
| - } |
164 |
| - } else if(e.keyCode == 9) { // Suck up the menu if tab is pressed |
165 |
| - ul.slideUp('fast'); |
166 |
| - } else if(e.keyCode == 38) { |
167 |
| - // Up arrow |
168 |
| - e.preventDefault(); |
169 |
| - var prev = obj.parent().parent().prev(); |
170 |
| - if(prev[0] === undefined) { |
171 |
| - ul.slideUp('fast'); |
172 |
| - if(callback !== undefined) { |
173 |
| - callback(); |
174 |
| - } else { |
175 |
| - obj.parent().parent().parent().parent().children('a').focus(); |
176 |
| - } |
177 |
| - } else { |
178 |
| - prev.find('a').focus(); |
179 |
| - } |
180 |
| - } |
181 |
| - }); |
| 196 | + if (prev.length === 0) { |
| 197 | + // jump to the bottom |
| 198 | + prev = ul.find('a.Mrphs-sitesNav__submenuitem-title').closest('ul') |
| 199 | + } |
| 200 | + |
| 201 | + prev.find('a.Mrphs-sitesNav__submenuitem-title').focus(); |
| 202 | + |
| 203 | + } else if (e.keyCode == 9) { // Suck up the menu if tab is pressed |
| 204 | + closeAllDropdownMenus(); |
| 205 | + } |
| 206 | + }); |
182 | 207 | }
|
0 commit comments