$MenuBar.DIVIDER = "DIVIDER";
function $MenuBar(menus){
var $ = jQuery;
var $G = $(self);
var $menus = $(E("div")).addClass("menus");
$menus.attr("touch-action", "none");
var selecting_menus = false;
var _html = function(menus_key){
return menus_key.replace(/&(.)/, function(m){
return "";
});
};
var _hotkey = function(menus_key){
return menus_key[menus_key.indexOf("&")+1].toUpperCase();
};
var close_menus = function(){
$menus.find(".menu-button").trigger("release");
// Close any rogue floating submenus
$(".menu-popup").hide();
};
var is_disabled = function(item){
if(typeof item.enabled === "function"){
return !item.enabled();
}else if(typeof item.enabled === "boolean"){
return !item.enabled;
}else{
return false;
}
};
// TODO: API for context menus (i.e. floating menu popups)
function $MenuPopup(menu_items){
var $menu_popup = $(E("div")).addClass("menu-popup");
var $menu_popup_table = $(E("table")).addClass("menu-popup-table").appendTo($menu_popup);
$.map(menu_items, function(item){
var $row = $(E("tr")).addClass("menu-row").appendTo($menu_popup_table)
if(item === $MenuBar.DIVIDER){
var $td = $(E("td")).attr({colspan: 4}).appendTo($row);
var $hr = $(E("hr")).addClass("menu-hr").appendTo($td);
}else{
var $item = $row.addClass("menu-item");
var $checkbox_area = $(E("td")).addClass("menu-item-checkbox-area");
var $label = $(E("td")).addClass("menu-item-label");
var $shortcut = $(E("td")).addClass("menu-item-shortcut");
var $submenu_area = $(E("td")).addClass("menu-item-submenu-area");
$item.append($checkbox_area, $label, $shortcut, $submenu_area);
$item.attr("tabIndex", -1);
$label.html(_html(item.item));
$shortcut.text(item.shortcut);
$menu_popup.on("update", function(){
$item.attr("disabled", is_disabled(item));
if(item.checkbox && item.checkbox.check){
$checkbox_area.text(item.checkbox.check() ? "✓" : "");
}
});
$item.on("pointerover", function(){
$menu_popup.triggerHandler("update");
$item.focus();
});
if(item.checkbox){
$checkbox_area.text("✓");
}
if(item.submenu){
$submenu_area.html('');
var $submenu_popup = $MenuPopup(item.submenu).appendTo("body");
$submenu_popup.hide();
var open_submenu = function(){
$submenu_popup.show();
$submenu_popup.triggerHandler("update");
var rect = $submenu_area[0].getBoundingClientRect();
$submenu_popup.css({
position: "absolute",
left: rect.right,
top: rect.top,
});
};
var open_tid, close_tid;
$item.add($submenu_popup).on("pointerover", function(e){
if(open_tid){clearTimeout(open_tid);}
if(close_tid){clearTimeout(close_tid);}
});
$item.on("pointerover", function(e){
if(open_tid){clearTimeout(open_tid);}
if(close_tid){clearTimeout(close_tid);}
open_tid = setTimeout(open_submenu, 200);
});
$item.add($submenu_popup).on("pointerout", function(){
$menu_popup.closest(".menu-container").find(".menu-button").focus();
if(open_tid){clearTimeout(open_tid);}
if(close_tid){clearTimeout(close_tid);}
close_tid = setTimeout(function(){
$submenu_popup.hide();
}, 200);
});
$item.on("click pointerdown", open_submenu);
}
var item_action = function(){
if(item.checkbox){
if(item.checkbox.toggle){
item.checkbox.toggle();
}
$menu_popup.triggerHandler("update");
}else if(item.action){
close_menus();
item.action();
}
};
$item.on("pointerup", function(e){
if(e.pointerType === "mouse" && e.button !== 0){
return;
}
item_action();
});
$item.on("pointerover", function(){
if(item.submenu){
$menus.triggerHandler("info", "");
}else{
$menus.triggerHandler("info", item.description || "");
}
});
$item.on("pointerout", function(){
if($item.is(":visible")){
$menus.triggerHandler("info", "");
$menu_popup.closest(".menu-container").find(".menu-button").focus();
}
});
$item.on("keydown", function(e){
if(e.ctrlKey || e.shiftKey || e.altKey){
return;
}
if(e.keyCode === 13){ // Enter
e.preventDefault();
item_action();
}
});
$menu_popup.on("keydown", function(e){
if(e.ctrlKey || e.shiftKey || e.altKey){
return;
}
if(String.fromCharCode(e.keyCode) === _hotkey(item.item)){
e.preventDefault();
$item.click();
}
});
}
});
return $menu_popup;
}
var this_click_opened_the_menu = false;
$.each(menus, function(menus_key, menu_items){
var $menu_container = $(E("div")).addClass("menu-container").appendTo($menus);
var $menu_button = $(E("div")).addClass("menu-button").appendTo($menu_container);
var $menu_popup = $MenuPopup(menu_items).appendTo($menu_container);
var menu_id = menus_key.replace("&", "").replace(/ /g, "-").toLowerCase();
$menu_button.addClass("" + menu_id + "-menu-button");
if(menu_id == "extras"){
// TODO: refactor shared key string, move to function
try{
if(localStorage["jspaint extras menu visible"] != "true"){
$menu_button.hide();
}
}catch(e){}
}
$menu_popup.hide();
$menu_button.html(_html(menus_key));
$menu_button.attr("tabIndex", -1)
$menu_container.on("keydown", function(e){
var $focused_item = $menu_popup.find(".menu-item:focus");
switch(e.keyCode){
case 37: // Left
$menu_container.prev().find(".menu-button").trigger("pointerdown");
break;
case 39: // Right
if($focused_item.find(".menu-item-submenu-area:not(:empty)").length){
$focused_item.click();
$(".menu-popup .menu-item").first().focus();
e.preventDefault();
}else{
$menu_container.next().find(".menu-button").trigger("pointerdown");
}
break;
case 40: // Down
if($menu_popup.is(":visible") && $focused_item.length){
var $next = $focused_item.next();
while($next.length && !$next.is(".menu-item")){
$next = $next.next();
}
$next.focus();
}else{
$menu_button.trigger("pointerdown");
$menu_popup.find(".menu-item").first().focus();
}
break;
case 38: // Up
if($menu_popup.is(":visible") && $focused_item.length){
var $prev = $focused_item.prev();
while($prev.length && !$prev.is(".menu-item")){
$prev = $prev.prev();
}
$prev.focus();
}else{
$menu_button.trigger("pointerdown"); // or maybe do nothing?
$menu_popup.find(".menu-item").last().focus();
}
break;
}
});
$G.on("keydown", function(e){
if(e.ctrlKey){ // Ctrl+...
if(e.keyCode !== 17){ // anything but Ctrl
close_menus();
}
return;
}
if(e.altKey){
if(String.fromCharCode(e.keyCode) === _hotkey(menus_key)){
e.preventDefault();
$menu_button.trigger("pointerdown");
}
}
});
$menu_button.on("pointerdown pointerover", function(e){
if(e.type === "pointerover" && !selecting_menus){
return;
}
if(e.type !== "pointerover"){
if(!$menu_button.hasClass("active")){
this_click_opened_the_menu = true;
}
}
close_menus();
$menu_button.focus();
$menu_button.addClass("active");
$menu_popup.show();
$menu_popup.triggerHandler("update");
selecting_menus = true;
$menus.triggerHandler("info", "");
});
$menu_button.on("pointerup", function(e){
if(this_click_opened_the_menu){
this_click_opened_the_menu = false;
return;
}
if($menu_button.hasClass("active")){
close_menus();
}
});
$menu_button.on("release", function(e){
selecting_menus = false;
$menu_button.removeClass("active");
$menu_popup.hide();
$menus.triggerHandler("default-info");
});
});
$G.on("keypress", function(e){
if(e.keyCode === 27){ // Esc
close_menus();
}
});
$G.on("blur", function(e){
// console.log("blur", e.target, document.activeElement);
close_menus();
});
$G.on("pointerdown pointerup", function(e){
if($(e.target).closest(".menus, .menu-popup").length === 0){
// console.log(e.type, "occurred outside of menus (on ", e.target, ") so...");
close_menus();
}
});
return $menus;
}