ginebra2/chrome/bedrockchrome/contextmenu.snippet/contextmenu.js
changeset 5 0f2326c2a325
parent 0 1450b09d0cfd
child 6 1c3b8676e58c
--- a/ginebra2/chrome/bedrockchrome/contextmenu.snippet/contextmenu.js	Fri May 14 15:40:36 2010 +0300
+++ b/ginebra2/chrome/bedrockchrome/contextmenu.snippet/contextmenu.js	Wed Jun 23 17:59:43 2010 +0300
@@ -1,125 +1,443 @@
-function printProp(x) {
-    window.app.debug(x + ":");
-    for (property in x) {
-        window.chrome.alert("  " + property + ": " + x[property]);
-    }
-}
+var cm_TheContextMenu;
+    
+function ContextMenu(snippetId, contentView) {
+    this.snippetId = snippetId;
+    this.mainDiv = undefined;
+    this.tailEl = undefined;
+    this.contentView = contentView;
+    this.showTimeoutId = 0;
+    // Width of a tab with no text, just the icon.  Icons must all be the same width.
+    // Update this if icon size or tab border width etc. changes -- or better yet, determine it dynamically.
+    this.normalTabWidth = 64;
 
-function historyViewContextEvent(e) {
-    window.chrome.alert("history context: " + e + " " + e.itemIndex + 
-                        " x=" + e.pos.x + " y=" + e.pos.y);
-    printProp(e);
-}
+    // ContextMenu is a singleton to avoid problems with scope-chaining in some of the
+    // callbacks that it uses.  See handleTabActivate.
+    if (cm_TheContextMenu != undefined) app.debug("ERROR: cm_TheContextMenu must be a singleton");
+    cm_TheContextMenu = this;
 
-window.chrome.chromeComplete.connect(chromeLoadComplete);
+    // Create tabs and their corresponding menus based on JSON data.
+    this.createTabsElement = function(data) {
+        this.mainDiv = document.createElement("div");
+        this.mainDiv.setAttribute("class", "ContextMenuDiv");
+        this.mainDiv.setAttribute("id", "cm_mainDivId");
 
+        var tabsDiv = document.createElement("div");
+        tabsDiv.setAttribute("class", "TabsDiv");
+        tabsDiv.setAttribute("id", "cm_tabsDivId");
 
-function chromeLoadComplete() {
-       
-    window.snippets.ContextMenuId.externalMouseEvent.connect(
-        function(type, name, description) {
-        	
-            if ((name == "QGraphicsSceneMouseReleaseEvent") || (name == "QGraphicsSceneResizeEvent")){
-            	  window.snippets.ContextMenuId.hide();                                    
-                window.snippets.ContextMenuId.dontShow = true;
-                setTimeout ( 'window.snippets.ContextMenuId.dontShow = false', 500 ); // reset the flag in 0.5 sec.
-            } 
-        }
-    );
- }
+        var menuDiv = document.createElement("div");
+        menuDiv.setAttribute("class", "MenuDiv");
+        menuDiv.setAttribute("id", "cm_menuDivId");
+
+        var tabsUl = document.createElement("ul");
+        tabsUl.setAttribute("class", "TabsUl");
+        tabsUl.setAttribute("id", "cm_tabsUlId");
+        tabsDiv.appendChild(tabsUl);
+
+        var currentTabFound = false;
 
-function ContextMenu()
-{
-	 // attach internal funcs
-    this.write = writeContextMenu;
+        // Iterate through the list of tabs.
+        for (var i=0; i < data.tabs.length; i++) {
+            var tab = data.tabs[i];
+            if (tab.visible != undefined) {
+                if (!tab.visible()) {
+                    continue;
+                }
+            }
 
-    // do setup
-    this.write();
-}
-////
+            // Create the tab.
+            var tabEl = document.createElement("li");
+            tabsUl.appendChild(tabEl);
+            
+            var tabDiv = document.createElement("div");
+            tabEl.appendChild(tabDiv);
 
-function showBookmarkView()
-{
-    if(window.views.WebView.BookmarkTreeView == undefined) {
-        window.views.WebView.createSuperPage("BookmarkTreeView", true);
-    }
-    window.views.WebView.BookmarkTreeView.load(chrome.baseDirectory + "bookmarkview.superpage/BookmarkView.html");
+            var iconEl = undefined;
+            var iconHighlightedEl = undefined;
+            
+            // Create the tab's icons.
+            if (tab.icon != undefined) {
+                iconEl = document.createElement("img");
+                iconEl.setAttribute("id", "icon");
+                iconEl.setAttribute("src", tab.icon);
+                tabDiv.appendChild(iconEl);
+            }
+            if (tab.iconHighlighted != undefined) {
+                iconHighlightedEl = document.createElement("img");
+                iconHighlightedEl.setAttribute("id", "iconHighlighted");
+                iconHighlightedEl.setAttribute("src", tab.iconHighlighted);
+                tabDiv.appendChild(iconHighlightedEl);
+            }
 
-    // Show it.
-    window.views.WebView.zoomFactor = 1.0;
-    window.views.WebView.showSuperPage("BookmarkTreeView");
-}
+            // Create the tab's text.
+            if (tab.text != undefined) {
+                var anchorEl = document.createElement("a");
+                tabDiv.appendChild(anchorEl);
+                var textEl = document.createTextNode(tab.text);
+                anchorEl.appendChild(textEl);
+            }
 
-function showSettingsView()
-{
-	   if(window.views.WebView.SettingsView == undefined) {
-        window.views.WebView.createSuperPage("SettingsView", true);
-    }
-    window.views.WebView.SettingsView.load(chrome.baseDirectory + "settingsview.superpage/SettingsView.html");
+            // Create the menu for this tab.
+            var menuEl = this.createMenuElement(tab);
+            menuDiv.appendChild(menuEl);
 
-    // Show it.
-    window.views.WebView.zoomFactor = 1.0;
-    window.views.WebView.showSuperPage("SettingsView");
-}
+            var tabClassName;
+            if (tab.current == "true") {
+                // This is the current, or selected, tab.
+                tabClassName = "ViewContext_HighlightedTab";
+                tabDiv.className = "ViewContext_HighlightedTabDiv";
+                if(iconEl != undefined) {
+                    iconEl.setAttribute("style", "display: none;");
+                }
+                currentTabFound = true;
+            }
+            else {
+                // Not selected.
+                tabClassName = "ViewContext_NormalTab";
+                tabDiv.className = "ViewContext_NormalTabDiv";
+                if(iconHighlightedEl != undefined) {
+                    iconHighlightedEl.setAttribute("style", "display: none;");
+                }
+
+                // Hide its menu.
+                menuEl.style.display = "none";
+            }
+
+            if (tab.disabled == "true") {
+                // The tab is disabled, add the appropriate CSS class to it.
+                tabClassName += " ViewContext_DisabledTab";
+            }
 
-function showHistoryView()
-{
-    if(window.views.WebView.BookmarkHistoryView == undefined) {
-	        window.views.WebView.createSuperPage("BookmarkHistoryView", true);
-	  }
-	  window.views.WebView.BookmarkHistoryView.load(chrome.baseDirectory + "historyview.superpage/historyView.html");
-     
-    // Show it.
-    window.views.WebView.zoomFactor = 1.0;
-    window.views.WebView.showSuperPage("BookmarkHistoryView");
-}
+            tabEl.className = tabClassName;
+
+            // Set up callback to show the menu that corresponds to this tab.
+            tabEl.onmouseup = this.handleTabActivate;
+            tabEl.cm_menu = menuEl;
+
+            // Remember that this tab is disabled.
+            tabEl.cm_disabled = tab.disabled == "true";
+        }
 
-function goToRecentUrlView () {
-	  showHistoryView();
-	  window.ViewStack.switchView("BookmarkHistoryView", "WebView");
+        // If a "current" tab was not specified, highlight the first tab and display its menu.
+        if (!currentTabFound) {
+            var firstTabEl = tabsUl.firstChild;
+            firstTabEl.className = "ViewContext_HighlightedTab";
+            firstTabEl.firstChild.className = "ViewContext_HighlightedTabDiv";
+            firstTabEl.cm_menu.style.display = "";
+        }
+
+//        this.tailEl = document.createElement("img");
+//        this.mainDiv.appendChild(this.tailEl);
+//        this.tailEl.setAttribute("id", "cm_tailId");
+//        this.tailEl.setAttribute("src", "contextmenu.snippet/icons/menu_tail.png");
+
+        this.mainDiv.appendChild(tabsDiv);
+        this.mainDiv.appendChild(menuDiv);
+        return this.mainDiv;
     }
 
-function goToBookmarkView () {
-    showBookmarkView();
-    window.ViewStack.switchView("BookmarkTreeView", "WebView");
+    // Create a single menu based on the given data structure.
+    this.createMenuElement = function(data) {
+        // Create menu list.
+        var menuUl = document.createElement("ul");
+        menuUl.setAttribute("class", "MenuUl");
+
+        for (var i=0; i < data.menuItems.length; i++) {
+            var menuItem = data.menuItems[i];
+
+            // Create the item.
+            var itemLi = document.createElement("li");
+            itemLi.setAttribute("class", "MenuLi");
+            var itemSpan = document.createElement("div");
+
+            // Is it a row if items? enumerate that as a ul inside of this outer li
+            if(menuItem.menuRow != undefined) {
+                var menuRowUl = document.createElement("ul");
+                menuRowUl.setAttribute("class", "MenuRowUl");
+                itemSpan.appendChild(menuRowUl);
+
+                for(var j=0; j < menuItem.menuRow.length; j++)
+                {
+                    var menuRowItem = menuItem.menuRow[j];
+                    
+                    var rowItemLi = document.createElement("li");
+                    rowItemLi.setAttribute("class", "MenuRowLi");
+                    menuRowUl.appendChild(rowItemLi);
+                    
+                    // bind to mouseup
+                    rowItemLi.onmouseup =  (function(handler) {
+                        return function() {
+                            if (handler != undefined)
+                                handler();
+                            this.hide();
+                        }.bind(this)
+                    }.bind(this))(menuRowItem.onclick);
+
+                    if (menuRowItem.text != undefined) {
+                        var textEl = document.createTextNode(menuRowItem.text);
+                        rowItemLi.appendChild(textEl);
+                    }
+                }
+            }
+            else {
+                itemLi.className += " RegularMenuLi";
+                if (menuItem.disabled == "true" || data.disabled == "true") {
+                    // Disabled item.
+                    itemLi.className += " ViewContext_DisabledMenuItem";
+                }
+                else {
+                    // Enabled item.  Set up the onmouseup handler.
+                    itemLi.onmouseup = (function(handler) {
+                        return function() {
+                            if (handler != undefined)
+                                handler();
+                            this.hide();
+                        }.bind(this)
+                    }.bind(this))(menuItem.onclick);
+                }
+
+                // Create the item's icon.
+                if (menuItem.icon != undefined) {
+                    var iconEl = document.createElement("img");
+                    itemSpan.appendChild(iconEl);
+                    iconEl.setAttribute("src", menuItem.icon);
+                }
+
+                // Create the item's text.
+                if (menuItem.text != undefined) {
+                    var anchorEl = document.createElement("a");
+                    itemSpan.appendChild(anchorEl);
+                    var textEl = document.createTextNode(menuItem.text);
+                    anchorEl.appendChild(textEl);
+                }
+            }
+
+            menuUl.appendChild(itemLi);
+            itemLi.appendChild(itemSpan);
+        }
+        return menuUl;
     }
     
-function goToSettingsView () {
-    showSettingsView();
-    window.ViewStack.switchView("SettingsView", "WebView");
-    //window.snippets.UrlSearchChromeId.hide(false);       
-}
+    // Handle mouse clicks on tab elements.
+    // Note: "this" refers to the element that was clicked on, ie. the tab item.
+    this.handleTabActivate = function() {
+        var tabsDivChildren = document.getElementById("cm_tabsUlId").childNodes;
+        var otherTabsWidth = 0;
+        // Set the class for each tab.
+        for (var i = 0; i < tabsDivChildren.length; i++) {
+            var tabEl = tabsDivChildren[i];
+            var iconHighlighted = getChildById(tabEl, "iconHighlighted");
+            var icon = getChildById(tabEl, "icon");
+            if (tabEl == this) {
+                // Activate the tab.
+                tabEl.className = "ViewContext_HighlightedTab";
+                //tabEl.firstChild.className = "ViewContext_HighlightedTabDiv";
+                
+                // Show the highlighted icon, if one exists.
+                if(iconHighlighted != undefined) {
+                    iconHighlighted.style.display = "";
+                    
+                    // Hide the normal icon.
+                    if (icon != undefined) {
+                        icon.style.display = "none";
+                    }
+                }
+            }
+            else {
+                // Deactivate the tab.
+                tabEl.className = "ViewContext_NormalTab";
+                //tabEl.firstChildclassName = "ViewContext_NormalTabDiv";
+                
+                // If a highlighted icon exists, switch to the normal one, otherwise leave
+                // the normal one alone.
+                if(iconHighlighted != undefined) {
+                    iconHighlighted.style.display = "none";
+                    if (icon != undefined) {
+                        icon.style.display = "";
+                    }
+                }
+            }
+            if (tabEl.cm_disabled) {
+                tabEl.className += " ViewContext_DisabledTab";
+                //tabEl.firstClassName += " ViewContext_DisabledTabDiv";
+            }
+        }
+
+        // Show the menu of the tab that was just clicked.
+        var menuDivChildren = document.getElementById("cm_menuDivId").childNodes;
+        for (var i = 0; i < menuDivChildren.length; i++) {
+            var menuEl = menuDivChildren[i];
+            if (menuEl == this.cm_menu) {
+                menuDivChildren[i].style.display =  "";
+            }
+            else {
+                menuDivChildren[i].style.display =  "none";
+            }
+        }
+        //cm_TheContextMenu.positionTail();
+        //document.getElementById(this.snippetId).clientHeight = this.mainDiv.clientHeight;
+        //document.getElementById("ContextMenuId").setAttribute("style", "height: " + document.getElementById("cm_mainDivId").clientHeight + "px;")
+
+        cm_TheContextMenu.updateTabSizes();
+        snippets[cm_TheContextMenu.snippetId].updateOwnerArea();
+    }
+
+    // Return the width of the "non-content" part of the element box, ie. thickness of
+    // the padding, the border and the margin.
+    this.getNonContentWidth = function(element) {
+        var tabStyle = document.defaultView.getComputedStyle(element, null);
+        return parseInt(tabStyle["margin-left"]) + parseInt(tabStyle["margin-right"]) +
+               parseInt(tabStyle["padding-left"]) + parseInt(tabStyle["padding-right"]) +
+               parseInt(tabStyle["border-left-width"]) + parseInt(tabStyle["border-right-width"]);
+    }
 
-// "Private" methods
-function writeContextMenu() {
-    var html =
-    '<div class="loadImagesTop">'+
-    '</div>'+
-    '<div class="loadImagesCenter">'+
-    '</div>'+
-    '<div class="loadImagesBottom">'+
-    '</div>'+
+    // Update the tab widths.  Expand the highlighted tab to its maximum width and shrink the
+    // normal tabs to their minimum widths.  Note: it would be preferable to have this done
+    // by CSS.
+    this.updateTabSizes = function() {
+        var tabsUl = document.getElementById("cm_tabsUlId");
+        var tabsDivChildren = tabsUl.childNodes;
+        var otherTabsWidth = 0;
+        var highlightedTab;
+        for (var i = 0; i < tabsDivChildren.length; i++) {
+            var tabEl = tabsDivChildren[i];
+            if (tabEl.className.indexOf("ViewContext_HighlightedTab") != -1) {
+                highlightedTab = tabEl;
+            }
+            else {
+                var newTabWidth = cm_TheContextMenu.normalTabWidth - cm_TheContextMenu.getNonContentWidth(tabEl);
+                tabEl.style.width = newTabWidth;
+                otherTabsWidth += tabEl.offsetWidth;
+            }
+        }
+        if (highlightedTab != undefined) {
+            var newWidth = tabsUl.offsetWidth - otherTabsWidth;
+            newWidth -= cm_TheContextMenu.getNonContentWidth(highlightedTab) + 2;
+            highlightedTab.style.width = newWidth;
+        }
+    }
+
+    this.positionTail = function() {
+        // Move the "tail" into position.
+        var tailEl = document.getElementById("cm_tailId");
+        if (tailEl != undefined) {
+            //var mainDiv = document.getElementById("cm_mainDivId");
+            var tailX = (this.mainDiv.clientWidth - tailEl.clientWidth) / 2;
+            var tailY = this.mainDiv.clientHeight;
+            tailEl.setAttribute("style", "position:absolute; top: " + tailY + "px; left: " + tailX);
+        }
+    }
+
+    // Create all the DOM elements of the window.
+    this.create = function(menuData) {
+        var snippetEl = document.getElementById(this.snippetId);
+        var el = this.createTabsElement(menuData);
+        snippetEl.appendChild(el);
+    }
+
+    // Show the content menu.  The menuData must contain an object tree describing the structure of the
+    // tabs and sub-menus.
+    //
+    // Example menu data in JSON format:
+    //        var MenuData = {
+    //            "tabs": [ {
+    //                     "text": "Tab 1",
+    //                     "icon": "tab1.png",
+    //                     "current": "true",
+    //                     "menuItems": [ {
+    //                             "text": Menu item 1,
+    //                             "onclick": handleMenu1,
+    //                             "icon": "menu1.png",
+    //                           },
+    //                           {
+    //                             "text": Menu item 2,
+    //                             "onclick": function() { alert("hello"); },
+    //                             "icon": "menu2.png",
+    //                           },
+    //                         ],
+    //                 },
+    //                 {
+    //                     "text": "Tab 2",
+    //                     "icon": "tab2.png",
+    //                     "menuItems": [ {
+    //                             "text": Menu item 1,
+    //                             "onclick": handleMenu21,
+    //                           },
+    //                           {
+    //                             "text": Menu item 2,
+    //                             "onclick": handleMenu22,
+    //                           },
+    //                         ],
+    //                 },
+    //                ]
+    //        };
+    //
     
-    '<div class="menuItem menuTop">'+
-      '<div class="menuItemContents" onmouseup="goToBookmarkView(); window.snippets.ContextMenuId.hide()">'+
-        '<img STYLE="vertical-align: middle; padding-left: 20px; padding-right: 15px;" src="contextmenu.snippet/icons/menu_icon_bookmarks.png">'+
-        '<span class="menuItemLabel">'+          
-          window.localeDelegate.translateText("content_view_menu_bookmarks")+
-        '</span>'+
-      '</div>'+
-    '</div>'+
+    this.cancel = function() {
+        //app.debug("CM: cancel " + this.showTimeoutId);
+        clearTimeout(this.showTimeoutId);
+        this.showTimeoutId = 0;
+        this.cleanUp();
+    }
+    
+    this.show = function(menuData) {
+        this.cleanUp();
+        this.create(menuData);
+        this.showTimeoutId = setTimeout('cm_TheContextMenu.showIt()', 10);
+    }
+
+    this.cleanUp = function() {
+        // Remove elements from DOM to save memory.
+        var oldEl = document.getElementById("cm_mainDivId");
+        if (oldEl) {
+            var snippetEl = document.getElementById(cm_TheContextMenu.snippetId);
+            snippetEl.removeChild(oldEl);
+        }
+    }
+
+    // Hide this window.
+    this.hide = function() {
+        snippets[cm_TheContextMenu.snippetId].hide();
+    }
+
+    this.onHide = function() {
+        this.cleanUp();
+    }
 
-    '<div class="menuItem menuBot">'+
-      '<div class="menuItemContents" onmouseup="goToRecentUrlView();window.snippets.ContextMenuId.hide()">'+
-        '<img STYLE="vertical-align: middle; padding-left: 20px; padding-right: 15px;" src="contextmenu.snippet/icons/menu_icon_settings.png">'+
-        '<span class="menuItemLabel">'+
-        window.localeDelegate.translateText("content_view_menu_history")+
-        '</span>'+
-      '</div>'+
-    '</div>';
-   
-  document.write(html);
-}
+    this.showIt = function() {
+        cm_TheContextMenu.updateTabSizes();
+        // Use a timer to actually show the window to allow the page re-layout
+        // to finish.  We don't know when this really happens but 50ms seems to
+        // be enough on the N97.  Without this delay the bottom of the window
+        // often gets clipped.
+        setTimeout("cm_TheContextMenu.showIt2()", 50);
+    }
+
+    this.showIt2 = function() {
+        var snippet = snippets[cm_TheContextMenu.snippetId];
+
+        snippet.updateOwnerArea();
+        snippet.setZValue(2);
+
+        centerSnippet(snippet);
 
+//        if (showTail) {
+//            cm_TheContextMenu.positionTail();
+//        }
 
+        snippet.show();
+    }
 
+    chrome.chromeComplete.connect(createDelegate(this,
+        function() {
+            var snippet = snippets[this.snippetId];
+
+            chrome.aspectChanged.connect(createDelegate(this,
+                    function(a) {
+                        centerSnippet(snippets[this.snippetId]);
+                    }));
+
+            snippet.hidden.connect(createDelegate(this, this.onHide));
+
+        }));
+}  // End ContextMenu class