2010-10-20 20:11:15 +02:00
|
|
|
/*
|
|
|
|
|
|
|
|
Copyright 2010, Google Inc.
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions are
|
|
|
|
met:
|
|
|
|
|
2011-08-04 22:37:14 +02:00
|
|
|
* Redistributions of source code must retain the above copyright
|
2010-10-20 20:11:15 +02:00
|
|
|
notice, this list of conditions and the following disclaimer.
|
2011-08-04 22:37:14 +02:00
|
|
|
* Redistributions in binary form must reproduce the above
|
2010-10-20 20:11:15 +02:00
|
|
|
copyright notice, this list of conditions and the following disclaimer
|
|
|
|
in the documentation and/or other materials provided with the
|
|
|
|
distribution.
|
2011-08-04 22:37:14 +02:00
|
|
|
* Neither the name of Google Inc. nor the names of its
|
2010-10-20 20:11:15 +02:00
|
|
|
contributors may be used to endorse or promote products derived from
|
|
|
|
this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
2011-08-04 22:37:14 +02:00
|
|
|
*/
|
2010-10-20 20:11:15 +02:00
|
|
|
|
2010-01-26 08:57:11 +01:00
|
|
|
MenuSystem = {
|
2011-08-04 22:37:14 +02:00
|
|
|
_layers: [],
|
|
|
|
_overlay: null
|
2010-01-26 08:57:11 +01:00
|
|
|
};
|
|
|
|
|
2010-02-06 00:24:52 +01:00
|
|
|
MenuSystem.showMenu = function(elmt, onDismiss) {
|
2011-08-04 22:37:14 +02:00
|
|
|
if (!MenuSystem._overlay) {
|
|
|
|
MenuSystem._overlay = $('<div> </div>')
|
|
|
|
.addClass("menu-overlay")
|
|
|
|
.appendTo(document.body)
|
|
|
|
.click(MenuSystem.dismissAll);
|
|
|
|
}
|
|
|
|
|
|
|
|
elmt.css("z-index", 1010 + MenuSystem._layers.length).appendTo(document.body);
|
|
|
|
|
|
|
|
var layer = {
|
|
|
|
elmt: elmt,
|
|
|
|
onDismiss: onDismiss
|
|
|
|
};
|
|
|
|
|
|
|
|
MenuSystem._layers.push(layer);
|
|
|
|
|
|
|
|
var level = MenuSystem._layers.length;
|
|
|
|
|
|
|
|
layer.keyHandler = function(evt) {
|
|
|
|
if (evt.keyCode == 27 && !evt.shiftKey && !evt.metaKey && !evt.altKey && !evt.ctrlKey &&
|
|
|
|
evt.target.tagName.toLowerCase() != "input" &&
|
|
|
|
evt.target.tagName.toLowerCase() != "textarea") {
|
|
|
|
MenuSystem.dismissUntil(level - 1);
|
|
|
|
evt.stopImmediatePropagation();
|
|
|
|
evt.stopPropagation();
|
|
|
|
evt.preventDefault();
|
|
|
|
return false;
|
2010-01-26 08:57:11 +01:00
|
|
|
}
|
2011-08-04 22:37:14 +02:00
|
|
|
};
|
|
|
|
$(document).stack("keydown", layer.keyHandler);
|
|
|
|
|
|
|
|
return level;
|
2010-01-26 08:57:11 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
MenuSystem.dismissAll = function() {
|
2011-08-04 22:37:14 +02:00
|
|
|
MenuSystem.dismissUntil(0);
|
|
|
|
if (MenuSystem._overlay !== null) {
|
|
|
|
MenuSystem._overlay.remove();
|
|
|
|
MenuSystem._overlay = null;
|
|
|
|
}
|
2010-01-26 08:57:11 +01:00
|
|
|
};
|
|
|
|
|
2010-01-26 20:54:14 +01:00
|
|
|
MenuSystem.dismissUntil = function(level) {
|
2011-08-04 22:37:14 +02:00
|
|
|
for (var i = MenuSystem._layers.length - 1; i >= level; i--) {
|
|
|
|
var layer = MenuSystem._layers[i];
|
|
|
|
|
|
|
|
$(document).unbind("keydown", layer.keyHandler);
|
|
|
|
|
|
|
|
layer.elmt.remove();
|
|
|
|
layer.elmt.unbind();
|
|
|
|
layer.onDismiss();
|
|
|
|
}
|
|
|
|
MenuSystem._layers = MenuSystem._layers.slice(0, level);
|
2010-01-26 08:57:11 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
MenuSystem.createMenu = function() {
|
2011-08-04 22:37:14 +02:00
|
|
|
return $('<div></div>').addClass("menu-container");
|
2010-01-26 08:57:11 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
MenuSystem.createMenuItem = function() {
|
2011-08-04 22:37:14 +02:00
|
|
|
return $('<a href="javascript:{}"></a>').addClass("menu-item");
|
2010-01-26 08:57:11 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
MenuSystem.positionMenuAboveBelow = function(menu, elmt) {
|
2011-08-04 22:37:14 +02:00
|
|
|
var offset = elmt.offset();
|
|
|
|
var windowWidth = $(window).width();
|
|
|
|
var windowHeight = $(window).height();
|
|
|
|
|
|
|
|
if (offset.top + elmt.outerHeight() - document.body.scrollTop + menu.outerHeight() > windowHeight - 10) {
|
|
|
|
menu.css("top", (offset.top - menu.outerHeight()) + "px");
|
|
|
|
} else {
|
|
|
|
menu.css("top", (offset.top + elmt.outerHeight()) + "px");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset.left - document.body.scrollLeft + menu.outerWidth() > windowWidth - 10) {
|
|
|
|
menu.css("left", (offset.left + elmt.outerWidth() - menu.outerWidth()) + "px");
|
|
|
|
} else {
|
|
|
|
menu.css("left", offset.left + "px");
|
|
|
|
}
|
2010-01-26 08:57:11 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
MenuSystem.positionMenuLeftRight = function(menu, elmt) {
|
2011-08-04 22:37:14 +02:00
|
|
|
var offset = elmt.offset();
|
|
|
|
var windowWidth = $(window).width();
|
|
|
|
var windowHeight = $(window).height();
|
|
|
|
|
|
|
|
if (offset.top - document.body.scrollTop + menu.outerHeight() > windowHeight - 10) {
|
|
|
|
menu.css("top", (offset.top + elmt.outerHeight() - menu.outerHeight()) + "px");
|
|
|
|
} else {
|
|
|
|
menu.css("top", offset.top + "px");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset.left + elmt.outerWidth() - document.body.scrollLeft + menu.outerWidth() > windowWidth - 10) {
|
|
|
|
menu.css("left", Math.max(10, offset.left - menu.outerWidth()) + "px");
|
|
|
|
} else {
|
|
|
|
menu.css("left", (offset.left + elmt.outerWidth()) + "px");
|
|
|
|
}
|
2010-01-26 08:57:11 +01:00
|
|
|
};
|
2010-01-26 20:54:14 +01:00
|
|
|
|
|
|
|
MenuSystem.createAndShowStandardMenu = function(items, elmt, options) {
|
2011-08-04 22:37:14 +02:00
|
|
|
options = options || {
|
|
|
|
horizontal: false
|
|
|
|
};
|
|
|
|
|
|
|
|
var menu = MenuSystem.createMenu();
|
|
|
|
if ("width" in options) {
|
|
|
|
menu.width(options.width);
|
|
|
|
}
|
|
|
|
|
|
|
|
var createMenuItem = function(item) {
|
|
|
|
if ("label" in item) {
|
|
|
|
var menuItem = MenuSystem.createMenuItem().appendTo(menu);
|
|
|
|
if ("submenu" in item) {
|
|
|
|
menuItem.html(
|
|
|
|
'<table width="100%" cellspacing="0" cellpadding="0" class="menu-item-layout"><tr>' +
|
|
|
|
'<td>' + item.label + '</td>' +
|
2012-10-13 19:47:08 +02:00
|
|
|
'<td width="1%"><img src="images/right-arrow.png" /></td>' +
|
2011-08-04 22:37:14 +02:00
|
|
|
'</tr></table>'
|
|
|
|
);
|
|
|
|
menuItem.mouseenter(function() {
|
|
|
|
MenuSystem.dismissUntil(level);
|
|
|
|
|
|
|
|
menuItem.addClass("menu-expanded");
|
|
|
|
|
|
|
|
var options = {
|
|
|
|
horizontal: true,
|
|
|
|
onDismiss: function() {
|
|
|
|
menuItem.removeClass("menu-expanded");
|
2010-01-26 20:54:14 +01:00
|
|
|
}
|
2011-08-04 22:37:14 +02:00
|
|
|
};
|
|
|
|
if ("width" in item) {
|
|
|
|
options.width = item.width;
|
|
|
|
}
|
|
|
|
|
|
|
|
MenuSystem.createAndShowStandardMenu(item.submenu, this, options);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
menuItem.html(item.label).click(function(evt) {
|
|
|
|
item.click.call(this, evt);
|
|
|
|
MenuSystem.dismissAll();
|
|
|
|
});
|
|
|
|
if ("tooltip" in item) {
|
|
|
|
menuItem.attr("title", item.tooltip);
|
2010-01-26 20:54:14 +01:00
|
|
|
}
|
2011-08-04 22:37:14 +02:00
|
|
|
menuItem.mouseenter(function() {
|
|
|
|
MenuSystem.dismissUntil(level);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else if ("heading" in item) {
|
|
|
|
$('<div></div>').addClass("menu-section").text(item.heading).appendTo(menu);
|
2010-01-26 20:54:14 +01:00
|
|
|
} else {
|
2011-08-04 22:37:14 +02:00
|
|
|
$('<hr />').appendTo(menu);
|
2010-01-26 20:54:14 +01:00
|
|
|
}
|
2011-08-04 22:37:14 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
for (var i = 0; i < items.length; i++) {
|
|
|
|
createMenuItem(items[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
var level = MenuSystem.showMenu(menu, "onDismiss" in options ? options.onDismiss : function(){});
|
|
|
|
if (options.horizontal) {
|
|
|
|
MenuSystem.positionMenuLeftRight(menu, $(elmt));
|
|
|
|
} else {
|
|
|
|
MenuSystem.positionMenuAboveBelow(menu, $(elmt));
|
|
|
|
}
|
|
|
|
|
|
|
|
return level;
|
2010-01-26 20:54:14 +01:00
|
|
|
};
|
2010-08-09 03:13:57 +02:00
|
|
|
|
|
|
|
MenuSystem.find = function(rootItems, path, levels) {
|
2011-08-04 22:37:14 +02:00
|
|
|
var menuItems = rootItems;
|
|
|
|
for (var p = 0; p < path.length && p < levels; p++) {
|
|
|
|
var segment = path[p];
|
|
|
|
var subMenuItems;
|
|
|
|
|
|
|
|
for (var i = 0; i < menuItems.length; i++) {
|
|
|
|
var menuItem = menuItems[i];
|
|
|
|
if (menuItem.id == segment) {
|
|
|
|
if ("submenu" in menuItem) {
|
|
|
|
subMenuItems = menuItem.submenu;
|
2010-08-09 03:13:57 +02:00
|
|
|
} else {
|
2011-08-04 22:37:14 +02:00
|
|
|
return undefined;
|
2010-08-09 03:13:57 +02:00
|
|
|
}
|
2011-08-04 22:37:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2010-08-09 03:13:57 +02:00
|
|
|
}
|
2011-08-04 22:37:14 +02:00
|
|
|
|
|
|
|
if (subMenuItems) {
|
|
|
|
menuItems = subMenuItems;
|
|
|
|
} else {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return menuItems;
|
2010-08-09 03:13:57 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
MenuSystem.appendTo = function(rootItems, path, what) {
|
2011-08-04 22:37:14 +02:00
|
|
|
var menuItems = MenuSystem.find(rootItems, path, path.length);
|
|
|
|
if (menuItems) {
|
|
|
|
if (what instanceof Array) {
|
|
|
|
$.merge(menuItems, what);
|
|
|
|
} else {
|
|
|
|
menuItems.push(what);
|
2010-08-09 03:13:57 +02:00
|
|
|
}
|
2011-08-04 22:37:14 +02:00
|
|
|
}
|
2010-08-09 03:13:57 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
MenuSystem.insertBefore = function(rootItems, path, what) {
|
2011-08-04 22:37:14 +02:00
|
|
|
var menuItems = MenuSystem.find(rootItems, path, path.length - 1);
|
|
|
|
if ((menuItems) && path.length > 0) {
|
|
|
|
var spliceArgs = [ 0, 0 ];
|
|
|
|
if (what instanceof Array) {
|
|
|
|
$.merge(spliceArgs, what);
|
|
|
|
} else {
|
|
|
|
spliceArgs.push(what);
|
2010-08-09 03:13:57 +02:00
|
|
|
}
|
2011-08-04 22:37:14 +02:00
|
|
|
|
|
|
|
var segment = path[path.length - 1];
|
|
|
|
for (var i = 0; i < menuItems.length; i++) {
|
|
|
|
var menuItem = menuItems[i];
|
|
|
|
if (menuItem.id == segment) {
|
|
|
|
spliceArgs[0] = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Array.prototype.splice.apply(menuItems, spliceArgs);
|
|
|
|
}
|
2010-08-09 03:13:57 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
MenuSystem.insertAfter = function(rootItems, path, what) {
|
2011-08-04 22:37:14 +02:00
|
|
|
var menuItems = MenuSystem.find(rootItems, path, path.length - 1);
|
|
|
|
if ((menuItems) && path.length > 0) {
|
|
|
|
var spliceArgs = [ menuItems.length, 0 ];
|
|
|
|
if (what instanceof Array) {
|
|
|
|
$.merge(spliceArgs, what);
|
|
|
|
} else {
|
|
|
|
spliceArgs.push(what);
|
2010-08-09 03:13:57 +02:00
|
|
|
}
|
2011-08-04 22:37:14 +02:00
|
|
|
|
|
|
|
var segment = path[path.length - 1];
|
|
|
|
for (var i = 0; i < menuItems.length; i++) {
|
|
|
|
var menuItem = menuItems[i];
|
|
|
|
if (menuItem.id == segment) {
|
|
|
|
spliceArgs[0] = i + 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Array.prototype.splice.apply(menuItems, spliceArgs);
|
|
|
|
}
|
2012-10-13 19:47:08 +02:00
|
|
|
};
|