|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 this.EXPORTED_SYMBOLS = ["PageMenu"]; |
|
6 |
|
7 this.PageMenu = function PageMenu() { |
|
8 } |
|
9 |
|
10 PageMenu.prototype = { |
|
11 PAGEMENU_ATTR: "pagemenu", |
|
12 GENERATEDITEMID_ATTR: "generateditemid", |
|
13 |
|
14 popup: null, |
|
15 builder: null, |
|
16 |
|
17 maybeBuildAndAttachMenu: function(aTarget, aPopup) { |
|
18 var pageMenu = null; |
|
19 var target = aTarget; |
|
20 while (target) { |
|
21 var contextMenu = target.contextMenu; |
|
22 if (contextMenu) { |
|
23 pageMenu = contextMenu; |
|
24 break; |
|
25 } |
|
26 target = target.parentNode; |
|
27 } |
|
28 |
|
29 if (!pageMenu) { |
|
30 return false; |
|
31 } |
|
32 |
|
33 var insertionPoint = this.getInsertionPoint(aPopup); |
|
34 if (!insertionPoint) { |
|
35 return false; |
|
36 } |
|
37 |
|
38 pageMenu.QueryInterface(Components.interfaces.nsIHTMLMenu); |
|
39 pageMenu.sendShowEvent(); |
|
40 // the show event is not cancelable, so no need to check a result here |
|
41 |
|
42 var fragment = aPopup.ownerDocument.createDocumentFragment(); |
|
43 |
|
44 var builder = pageMenu.createBuilder(); |
|
45 if (!builder) { |
|
46 return false; |
|
47 } |
|
48 builder.QueryInterface(Components.interfaces.nsIXULContextMenuBuilder); |
|
49 builder.init(fragment, this.GENERATEDITEMID_ATTR); |
|
50 |
|
51 pageMenu.build(builder); |
|
52 |
|
53 var pos = insertionPoint.getAttribute(this.PAGEMENU_ATTR); |
|
54 if (pos == "start") { |
|
55 insertionPoint.insertBefore(fragment, |
|
56 insertionPoint.firstChild); |
|
57 } else { |
|
58 insertionPoint.appendChild(fragment); |
|
59 } |
|
60 |
|
61 this.builder = builder; |
|
62 this.popup = aPopup; |
|
63 |
|
64 this.popup.addEventListener("command", this); |
|
65 this.popup.addEventListener("popuphidden", this); |
|
66 |
|
67 return true; |
|
68 }, |
|
69 |
|
70 handleEvent: function(event) { |
|
71 var type = event.type; |
|
72 var target = event.target; |
|
73 if (type == "command" && target.hasAttribute(this.GENERATEDITEMID_ATTR)) { |
|
74 this.builder.click(target.getAttribute(this.GENERATEDITEMID_ATTR)); |
|
75 } else if (type == "popuphidden" && this.popup == target) { |
|
76 this.removeGeneratedContent(this.popup); |
|
77 |
|
78 this.popup.removeEventListener("popuphidden", this); |
|
79 this.popup.removeEventListener("command", this); |
|
80 |
|
81 this.popup = null; |
|
82 this.builder = null; |
|
83 } |
|
84 }, |
|
85 |
|
86 getImmediateChild: function(element, tag) { |
|
87 var child = element.firstChild; |
|
88 while (child) { |
|
89 if (child.localName == tag) { |
|
90 return child; |
|
91 } |
|
92 child = child.nextSibling; |
|
93 } |
|
94 return null; |
|
95 }, |
|
96 |
|
97 getInsertionPoint: function(aPopup) { |
|
98 if (aPopup.hasAttribute(this.PAGEMENU_ATTR)) |
|
99 return aPopup; |
|
100 |
|
101 var element = aPopup.firstChild; |
|
102 while (element) { |
|
103 if (element.localName == "menu") { |
|
104 var popup = this.getImmediateChild(element, "menupopup"); |
|
105 if (popup) { |
|
106 var result = this.getInsertionPoint(popup); |
|
107 if (result) { |
|
108 return result; |
|
109 } |
|
110 } |
|
111 } |
|
112 element = element.nextSibling; |
|
113 } |
|
114 |
|
115 return null; |
|
116 }, |
|
117 |
|
118 removeGeneratedContent: function(aPopup) { |
|
119 var ungenerated = []; |
|
120 ungenerated.push(aPopup); |
|
121 |
|
122 var count; |
|
123 while (0 != (count = ungenerated.length)) { |
|
124 var last = count - 1; |
|
125 var element = ungenerated[last]; |
|
126 ungenerated.splice(last, 1); |
|
127 |
|
128 var i = element.childNodes.length; |
|
129 while (i-- > 0) { |
|
130 var child = element.childNodes[i]; |
|
131 if (!child.hasAttribute(this.GENERATEDITEMID_ATTR)) { |
|
132 ungenerated.push(child); |
|
133 continue; |
|
134 } |
|
135 element.removeChild(child); |
|
136 } |
|
137 } |
|
138 } |
|
139 } |