3 * Marek Habersack <grendel@twistedcode.net>
5 * (C) 2010 Novell, Inc (http: *novell.com)
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 * This code serves only the List rendering mode of the Menu control in Mono
29 if (!window.Sys) { window.Sys = {}; }
30 if (!Sys.WebForms) { Sys.WebForms = {}; }
32 Sys.WebForms.Menu = function (options)
35 throw "Sys.WebForms.Menu constructor requires options to be not null.";
37 if (options.element == null)
38 throw "options.element is required.";
40 if (options.orientation == null)
41 throw "options.orientation is required.";
43 if (typeof (options.element) == "string")
44 this.menuID = options.element;
46 this.mainElement = options.element;
48 this.disappearAfter = options.disappearAfter || 500;
49 this.orientation = options.orientation;
50 this.tabIndex = options.tabIndex || 0;
51 this.disabled = options.disabled || false;
52 this.level = options.level || 0;
53 this.menuItemIndex = 0;
55 if (this.level != 0) {
56 if (options.parentMenu == null)
57 throw "options.parentMenu is required for all submenus.";
59 this.parentMenu = options.parentMenu;
62 if (this.mainElement == null) {
63 this.mainElement = document.getElementById (this.menuID);
64 if (this.mainElement == null)
65 throw "Unable to find menu element with id '" + this.menuID + "'.";
67 if (this.mainElement.tagName != "DIV")
68 throw "This script must be used only when the menu containing element is DIV.";
71 /* Due to the way we generate the menu in the list mode, every submenu other than the root one is dynamic */
73 this.menuType = "dynamic";
74 if (options.parentMenu == null)
75 throw "options.parent is required for all submenus.";
77 var subMenuId = Sys.WebForms.Menu.Helpers.getNextSubMenuId ();
78 this.parentMenu = options.parentMenu;
79 this.orientation = this.parentMenu.orientation;
80 this.path = this.parentMenu.path + subMenuId;
81 this.menuID = this.parentMenu.menuID;
82 if (this.mainElement.id == null || this.mainElement.id == "")
83 this.mainElement.id = this.menuID + ":submenu:" + subMenuId;
85 this.menuType = "static";
86 if (this.level == 1) {
87 this.menuID = this.parentMenu.menuID;
88 this.orientation = this.parentMenu.orientation;
90 this.parentMenu = null;
92 this.mainElement.setAttribute ("tabindex", this.tabIndex);
93 this.mainElement.setAttribute ("role", this.orientation == "vertical" ? "menu" : "menubar");
94 with (this.mainElement.style) {
96 position = "relative";
100 if (this.level > 0) {
101 Sys.WebForms.Menu.Helpers.appendCssClass (this.mainElement, this.menuType);
105 Sys.WebForms.Menu.Helpers.setFloat (this.mainElement, "left");
110 Sys.WebForms.Menu.Helpers = {
113 __popupToClose: null,
115 setPopupToClose: function (element) {
116 this.__popupToClose = element;
119 getPopupToClose: function () {
120 return this.__popupToClose;
123 setFloat: function (element, side) {
124 /* For standards-compliant browsers */
125 element.style.cssFloat = "left";
128 element.style.styleFloat = "left";
131 appendCssClass: function (element, className) {
132 if (element == null || className == null)
135 var cname = element.className;
136 if (cname == null || cname == "")
139 cname += " " + className;
141 element.className = cname;
144 highlight: function (element) {
147 if (element.classList.contains ("highlighted"))
149 element.classList.add ("highlighted");
152 unhighlight: function (element) {
155 if (!element.classList.contains ("highlighted"))
157 element.classList.remove ("highlighted");
160 getNextSubMenuId: function () {
161 return ++this.__subMenuCounter;
164 addMenuItem: function (item) {
168 if (!(item instanceof Sys.WebForms.MenuItem))
169 throw "item must be an instance of Sys.WebForms.MenuItem";
171 if (this.__menuItems [item.path] != null)
172 throw "item already exists (path " + item.path + ")";
174 this.__menuItems [item.path] = item;
177 getMenuItem: function (element) {
181 var itemPath = element ["__MonoMenuItemPath"];
182 if (itemPath == null)
185 return this.__menuItems [itemPath];
188 addEventHandler: function (element, eventType, handler, capture) {
189 /* There's also element.attachEvent, but it changes handler semantics on IE, so we don't
190 * even take it into consideration.
192 if (element.addEventListener)
193 element.addEventListener(eventType, handler, !!capture);
195 element ["on" + eventType] = handler;
199 Sys.WebForms.Menu.prototype.loadItems = function ()
201 var children = this.mainElement.childNodes;
202 var count = children.length;
205 for (var i = 0; i < count; i++) {
206 child = children [i];
207 if (child.nodeType != 1)
210 if (child.tagName == "UL") {
211 var submenu = new Sys.WebForms.Menu ({ element: child, disappearAfter: this.disappearAfter, orientation: this.orientation,
212 disabled: this.disabled, level: this.level + 1, tabIndex: this.tabIndex, parentMenu: this});
213 } else if (child.tagName == "LI") {
214 var menuItem = new Sys.WebForms.MenuItem ({ element: child, menuType: this.menuType, disappearAfter: this.disappearAfter, orientation: this.orientation,
215 disabled: this.disabled, level: this.level + 1, tabIndex: this.tabIndex, parentMenu: this});
220 Sys.WebForms.Menu.prototype.getNextMenuItemId = function ()
222 return ++this.menuItemIndex;
225 Sys.WebForms.MenuItem = function (options)
228 throw "Sys.WebForms.MenuItem constructor requires options to be not null.";
230 if (options.element == null)
231 throw "options.element must be set.";
233 if (options.menuType == null)
234 throw "options.menuType must be set.";
236 if (options.parentMenu == null)
237 throw "options.parentMenu is required.";
239 this.element = options.element;
240 this.menuType = options.menuType;
241 this.parentMenu = options.parentMenu;
242 this.path = this.parentMenu.path + this.parentMenu.getNextMenuItemId ();
244 var children = this.element.childNodes;
248 for (var i = 0; i < children.length; i++) {
249 child = children [i];
250 if (child.nodeType != 1)
253 switch (child.tagName) {
255 Sys.WebForms.Menu.Helpers.appendCssClass (child, this.menuType);
256 Sys.WebForms.Menu.Helpers.addEventHandler (child, "mouseover", this.mouseOverHandler);
257 Sys.WebForms.Menu.Helpers.addEventHandler (child, "mouseout", this.mouseOutHandler);
258 child.setAttribute ("tabindex", "-1");
262 this.subMenu = new Sys.WebForms.Menu ({ element: child, disappearAfter: options.disappearAfter, orientation: options.orientation,
263 disabled: options.disabled, level: options.level, tabIndex: options.tabIndex, parentMenu: options.parentMenu});
264 if (this.subMenu.menuType == "dynamic") {
268 if (this.subMenu.orientation == "horizontal" && this.subMenu.parentMenu != null && this.subMenu.parentMenu.menuType == "static") {
276 with (this.subMenu.mainElement.style) {
278 position = "absolute";
284 Sys.WebForms.Menu.Helpers.appendCssClass (this.element, "has-popup");
285 this.element.setAttribute ("aria-haspopup", this.subMenu.mainElement.id);
286 Sys.WebForms.Menu.Helpers.addEventHandler (this.element, "mouseover", this.mouseOverHandler);
287 Sys.WebForms.Menu.Helpers.addEventHandler (this.element, "mouseout", this.mouseOutHandler);
292 Sys.WebForms.Menu.Helpers.appendCssClass (this.element, this.menuType);
293 this.element.style.position = "relative";
294 this.element.setAttribute ("role", "menuitem");
295 this.element ["__MonoMenuItemPath"] = this.path;
297 if (this.parentMenu.orientation == "horizontal" && this.parentMenu.menuType == "static")
298 Sys.WebForms.Menu.Helpers.setFloat (this.element, "left");
300 Sys.WebForms.Menu.Helpers.addMenuItem (this);
303 Sys.WebForms.MenuItem.prototype.log = function (msg)
305 if (console && console.log)
309 Sys.WebForms.MenuItem.prototype.hide = function (popup, leaveParentOpen)
311 if (popup == null || popup.mainElement == null || popup.menuType == "static")
315 while (current != null) {
316 if (current.menuType == "static" || (leaveParentOpen && current == this.parentMenu))
319 if (current.mainElement != null) {
320 current.mainElement.style.display = "none";
321 Sys.WebForms.Menu.Helpers.unhighlight (current.mainElement.parentNode.children [0]);
324 if (current.hideTimerId != null) {
325 window.clearTimeout (current.hideTimerId);
326 current.hideTimerId = null;
329 current = current.parentMenu;
333 Sys.WebForms.MenuItem.prototype.isPopupChildOf = function (popup, element)
335 if (popup == null || element == null)
338 var cur = popup.parentNode;
339 while (cur != null) {
342 cur = cur.parentNode;
348 Sys.WebForms.MenuItem.prototype.onMouseOver = function (popupId)
351 var noPopup = popupId == null || popupId == "";
355 popup = document.getElementById (popupId);
357 var cur = Sys.WebForms.Menu.Helpers.getPopupToClose ();
359 if (cur.hideTimerId != null) {
360 window.clearTimeout (cur.hideTimerId);
361 cur.hideTimerId = null;
364 if (popup == null || (popup != cur.mainElement && !this.isPopupChildOf (popup, cur.mainElement)))
365 this.hide (cur, true);
366 Sys.WebForms.Menu.Helpers.setPopupToClose (null);
373 throw "Popup with id '" + popupId + "' could not be found.";
375 if (cur != null && popup != cur.mainElement)
376 this.hide (cur, true);
377 if (popup.style.display != "block")
378 popup.style.display = "block";
379 Sys.WebForms.Menu.Helpers.highlight (popup.parentNode.children [0]);
382 Sys.WebForms.MenuItem.prototype.onMouseOut = function (popupId)
384 if (popupId == null || popupId == "")
387 var popup = document.getElementById (popupId);
389 throw "Popup with id '" + popupId + "' could not be found.";
391 var cur = this.subMenu;
394 cur.hideTimerId = window.setTimeout (function () {
395 myself.hide (cur, false);
397 this.subMenu.disappearAfter);
400 Sys.WebForms.Menu.Helpers.setPopupToClose (cur);
403 Sys.WebForms.MenuItem.prototype.mouseOverHandler = function (e)
406 if (this.nodeName == "A") {
407 if (this.parentNode.getAttribute ("role") != "menuitem" || this.parentNode.getAttribute ("aria-haspopup") != null)
409 Sys.WebForms.Menu.Helpers.highlight (this);
413 var menuItem = Sys.WebForms.Menu.Helpers.getMenuItem (this);
414 if (menuItem == null || !(menuItem instanceof Sys.WebForms.MenuItem)) {
415 e.returnResult = false;
416 e.cancelBuble = false;
417 throw "MenuItem could not be found in mouseover handler.";
420 menuItem.onMouseOver (this.getAttribute ("aria-haspopup"));
421 menuItem.finalizeEvent (e);
424 Sys.WebForms.MenuItem.prototype.mouseOutHandler = function (e)
426 if (this.nodeName == "A") {
427 if (this.parentNode.getAttribute ("role") != "menuitem" || this.parentNode.getAttribute ("aria-haspopup") != null)
429 Sys.WebForms.Menu.Helpers.unhighlight (this);
433 var menuItem = Sys.WebForms.Menu.Helpers.getMenuItem (this);
435 if (menuItem == null || !(menuItem instanceof Sys.WebForms.MenuItem)) {
436 e.returnResult = false;
437 e.cancelBuble = false;
438 throw "MenuItem could not be found in mouseout handler.";
440 menuItem.onMouseOut (this.getAttribute ("aria-haspopup"));
441 menuItem.finalizeEvent (e);
444 Sys.WebForms.MenuItem.prototype.finalizeEvent = function (e)
446 /* For standards-compliant browsers */
448 if (e.preventDefault)
451 e.returnResult = false;
453 if (e.stopPropagation)
456 e.cancelBubble = true;
460 if (window.event != null)
461 window.event.cancelBubble = true;