michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: this.EXPORTED_SYMBOLS = ["PageMenu"]; michael@0: michael@0: this.PageMenu = function PageMenu() { michael@0: } michael@0: michael@0: PageMenu.prototype = { michael@0: PAGEMENU_ATTR: "pagemenu", michael@0: GENERATEDITEMID_ATTR: "generateditemid", michael@0: michael@0: popup: null, michael@0: builder: null, michael@0: michael@0: maybeBuildAndAttachMenu: function(aTarget, aPopup) { michael@0: var pageMenu = null; michael@0: var target = aTarget; michael@0: while (target) { michael@0: var contextMenu = target.contextMenu; michael@0: if (contextMenu) { michael@0: pageMenu = contextMenu; michael@0: break; michael@0: } michael@0: target = target.parentNode; michael@0: } michael@0: michael@0: if (!pageMenu) { michael@0: return false; michael@0: } michael@0: michael@0: var insertionPoint = this.getInsertionPoint(aPopup); michael@0: if (!insertionPoint) { michael@0: return false; michael@0: } michael@0: michael@0: pageMenu.QueryInterface(Components.interfaces.nsIHTMLMenu); michael@0: pageMenu.sendShowEvent(); michael@0: // the show event is not cancelable, so no need to check a result here michael@0: michael@0: var fragment = aPopup.ownerDocument.createDocumentFragment(); michael@0: michael@0: var builder = pageMenu.createBuilder(); michael@0: if (!builder) { michael@0: return false; michael@0: } michael@0: builder.QueryInterface(Components.interfaces.nsIXULContextMenuBuilder); michael@0: builder.init(fragment, this.GENERATEDITEMID_ATTR); michael@0: michael@0: pageMenu.build(builder); michael@0: michael@0: var pos = insertionPoint.getAttribute(this.PAGEMENU_ATTR); michael@0: if (pos == "start") { michael@0: insertionPoint.insertBefore(fragment, michael@0: insertionPoint.firstChild); michael@0: } else { michael@0: insertionPoint.appendChild(fragment); michael@0: } michael@0: michael@0: this.builder = builder; michael@0: this.popup = aPopup; michael@0: michael@0: this.popup.addEventListener("command", this); michael@0: this.popup.addEventListener("popuphidden", this); michael@0: michael@0: return true; michael@0: }, michael@0: michael@0: handleEvent: function(event) { michael@0: var type = event.type; michael@0: var target = event.target; michael@0: if (type == "command" && target.hasAttribute(this.GENERATEDITEMID_ATTR)) { michael@0: this.builder.click(target.getAttribute(this.GENERATEDITEMID_ATTR)); michael@0: } else if (type == "popuphidden" && this.popup == target) { michael@0: this.removeGeneratedContent(this.popup); michael@0: michael@0: this.popup.removeEventListener("popuphidden", this); michael@0: this.popup.removeEventListener("command", this); michael@0: michael@0: this.popup = null; michael@0: this.builder = null; michael@0: } michael@0: }, michael@0: michael@0: getImmediateChild: function(element, tag) { michael@0: var child = element.firstChild; michael@0: while (child) { michael@0: if (child.localName == tag) { michael@0: return child; michael@0: } michael@0: child = child.nextSibling; michael@0: } michael@0: return null; michael@0: }, michael@0: michael@0: getInsertionPoint: function(aPopup) { michael@0: if (aPopup.hasAttribute(this.PAGEMENU_ATTR)) michael@0: return aPopup; michael@0: michael@0: var element = aPopup.firstChild; michael@0: while (element) { michael@0: if (element.localName == "menu") { michael@0: var popup = this.getImmediateChild(element, "menupopup"); michael@0: if (popup) { michael@0: var result = this.getInsertionPoint(popup); michael@0: if (result) { michael@0: return result; michael@0: } michael@0: } michael@0: } michael@0: element = element.nextSibling; michael@0: } michael@0: michael@0: return null; michael@0: }, michael@0: michael@0: removeGeneratedContent: function(aPopup) { michael@0: var ungenerated = []; michael@0: ungenerated.push(aPopup); michael@0: michael@0: var count; michael@0: while (0 != (count = ungenerated.length)) { michael@0: var last = count - 1; michael@0: var element = ungenerated[last]; michael@0: ungenerated.splice(last, 1); michael@0: michael@0: var i = element.childNodes.length; michael@0: while (i-- > 0) { michael@0: var child = element.childNodes[i]; michael@0: if (!child.hasAttribute(this.GENERATEDITEMID_ATTR)) { michael@0: ungenerated.push(child); michael@0: continue; michael@0: } michael@0: element.removeChild(child); michael@0: } michael@0: } michael@0: } michael@0: }