source: temp/trunk/html/test/naka/HTMLArea/plugins/ContextMenu/context-menu.js @ 1694

Revision 1694, 12.7 KB checked in by naka, 20 years ago (diff)

* empty log message *

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1// Context Menu Plugin for HTMLArea-3.0
2// Sponsored by www.americanbible.org
3// Implementation by Mihai Bazon, http://dynarch.com/mishoo/
4//
5// (c) dynarch.com 2003.
6// Distributed under the same terms as HTMLArea itself.
7// This notice MUST stay intact for use (see license.txt).
8//
9// $Id$
10
11HTMLArea.loadStyle("menu.css", "ContextMenu");
12
13function ContextMenu(editor) {
14    this.editor = editor;
15};
16
17ContextMenu._pluginInfo = {
18    name          : "ContextMenu",
19    version       : "1.0",
20    developer     : "Mihai Bazon",
21    developer_url : "http://dynarch.com/mishoo/",
22    c_owner       : "dynarch.com",
23    sponsor       : "American Bible Society",
24    sponsor_url   : "http://www.americanbible.org",
25    license       : "htmlArea"
26};
27
28ContextMenu.prototype.onGenerate = function() {
29    var self = this;
30    var doc = this.editordoc = this.editor._iframe.contentWindow.document;
31    HTMLArea._addEvents(doc, ["contextmenu"],
32                function (event) {
33                    return self.popupMenu(HTMLArea.is_ie ? self.editor._iframe.contentWindow.event : event);
34                });
35    this.currentMenu = null;
36};
37
38ContextMenu.prototype.getContextMenu = function(target) {
39    var self = this;
40    var editor = this.editor;
41    var config = editor.config;
42    var menu = [];
43    var tbo = this.editor.plugins.TableOperations;
44    if (tbo) tbo = tbo.instance;
45    var i18n = ContextMenu.I18N;
46
47    var selection = editor.hasSelectedText();
48    if (selection)
49        menu.push([ i18n["Cut"], function() { editor.execCommand("cut"); }, null, config.btnList["cut"][1] ],
50              [ i18n["Copy"], function() { editor.execCommand("copy"); }, null, config.btnList["copy"][1] ]);
51    menu.push([ i18n["Paste"], function() { editor.execCommand("paste"); }, null, config.btnList["paste"][1] ]);
52
53    var currentTarget = target;
54    var elmenus = [];
55
56    var link = null;
57    var table = null;
58    var tr = null;
59    var td = null;
60    var img = null;
61
62    function tableOperation(opcode) {
63        tbo.buttonPress(editor, opcode);
64    };
65
66    for (; target; target = target.parentNode) {
67        var tag = target.tagName;
68        if (!tag)
69            continue;
70        tag = tag.toLowerCase();
71        switch (tag) {
72            case "img":
73            img = target;
74            elmenus.push(null,
75                     [ i18n["Image Properties"],
76                       function() {
77                           editor._insertImage(img);
78                       },
79                       i18n["Show the image properties dialog"],
80                       config.btnList["insertimage"][1] ]
81                );
82            break;
83            case "a":
84            link = target;
85            elmenus.push(null,
86                     [ i18n["Modify Link"],
87                       function() { editor.execCommand("createlink", true); },
88                       i18n["Current URL is"] + ': ' + link.href,
89                       config.btnList["createlink"][1] ],
90
91                     [ i18n["Check Link"],
92                       function() { window.open(link.href); },
93                       i18n["Opens this link in a new window"] ],
94
95                     [ i18n["Remove Link"],
96                       function() {
97                           if (confirm(i18n["Please confirm that you want to unlink this element."] + "\n" +
98                               i18n["Link points to:"] + " " + link.href)) {
99                               while (link.firstChild)
100                                   link.parentNode.insertBefore(link.firstChild, link);
101                               link.parentNode.removeChild(link);
102                           }
103                       },
104                       i18n["Unlink the current element"] ]
105                );
106            break;
107            case "td":
108            td = target;
109            if (!tbo) break;
110            elmenus.push(null,
111                     [ i18n["Cell Properties"],
112                       function() { tableOperation("TO-cell-prop"); },
113                       i18n["Show the Table Cell Properties dialog"],
114                       config.btnList["TO-cell-prop"][1] ]
115                );
116            break;
117            case "tr":
118            tr = target;
119            if (!tbo) break;
120            elmenus.push(null,
121                     [ i18n["Row Properties"],
122                       function() { tableOperation("TO-row-prop"); },
123                       i18n["Show the Table Row Properties dialog"],
124                       config.btnList["TO-row-prop"][1] ],
125
126                     [ i18n["Insert Row Before"],
127                       function() { tableOperation("TO-row-insert-above"); },
128                       i18n["Insert a new row before the current one"],
129                       config.btnList["TO-row-insert-above"][1] ],
130
131                     [ i18n["Insert Row After"],
132                       function() { tableOperation("TO-row-insert-under"); },
133                       i18n["Insert a new row after the current one"],
134                       config.btnList["TO-row-insert-under"][1] ],
135
136                     [ i18n["Delete Row"],
137                       function() { tableOperation("TO-row-delete"); },
138                       i18n["Delete the current row"],
139                       config.btnList["TO-row-delete"][1] ]
140                );
141            break;
142            case "table":
143            table = target;
144            if (!tbo) break;
145            elmenus.push(null,
146                     [ i18n["Table Properties"],
147                       function() { tableOperation("TO-table-prop"); },
148                       i18n["Show the Table Properties dialog"],
149                       config.btnList["TO-table-prop"][1] ],
150
151                     [ i18n["Insert Column Before"],
152                       function() { tableOperation("TO-col-insert-before"); },
153                       i18n["Insert a new column before the current one"],
154                       config.btnList["TO-col-insert-before"][1] ],
155
156                     [ i18n["Insert Column After"],
157                       function() { tableOperation("TO-col-insert-after"); },
158                       i18n["Insert a new column after the current one"],
159                       config.btnList["TO-col-insert-after"][1] ],
160
161                     [ i18n["Delete Column"],
162                       function() { tableOperation("TO-col-delete"); },
163                       i18n["Delete the current column"],
164                       config.btnList["TO-col-delete"][1] ]
165                );
166            break;
167            case "body":
168            elmenus.push(null,
169                     [ i18n["Justify Left"],
170                       function() { editor.execCommand("justifyleft"); }, null,
171                       config.btnList["justifyleft"][1] ],
172                     [ i18n["Justify Center"],
173                       function() { editor.execCommand("justifycenter"); }, null,
174                       config.btnList["justifycenter"][1] ],
175                     [ i18n["Justify Right"],
176                       function() { editor.execCommand("justifyright"); }, null,
177                       config.btnList["justifyright"][1] ],
178                     [ i18n["Justify Full"],
179                       function() { editor.execCommand("justifyfull"); }, null,
180                       config.btnList["justifyfull"][1] ]
181                );
182            break;
183        }
184    }
185
186    if (selection && !link)
187        menu.push(null, [ i18n["Make link"],
188                  function() { editor.execCommand("createlink", true); },
189                  i18n["Create a link"],
190                  config.btnList["createlink"][1] ]);
191
192    for (var i in elmenus)
193        menu.push(elmenus[i]);
194
195    menu.push(null,
196          [ i18n["Remove the"] + " <" + currentTarget.tagName + "> " + i18n["Element"],
197            function() {
198                if (confirm(i18n["Please confirm that you want to remove this element:"] + " " + currentTarget.tagName)) {
199                    var el = currentTarget;
200                    var p = el.parentNode;
201                    p.removeChild(el);
202                    if (HTMLArea.is_gecko) {
203                        if (p.tagName.toLowerCase() == "td" && !p.hasChildNodes())
204                            p.appendChild(editor._doc.createElement("br"));
205                        editor.forceRedraw();
206                        editor.focusEditor();
207                        editor.updateToolbar();
208                        if (table) {
209                            var save_collapse = table.style.borderCollapse;
210                            table.style.borderCollapse = "collapse";
211                            table.style.borderCollapse = "separate";
212                            table.style.borderCollapse = save_collapse;
213                        }
214                    }
215                }
216            },
217            i18n["Remove this node from the document"] ]);
218    return menu;
219};
220
221ContextMenu.prototype.popupMenu = function(ev) {
222    var self = this;
223    var i18n = ContextMenu.I18N;
224    if (this.currentMenu)
225        this.currentMenu.parentNode.removeChild(this.currentMenu);
226    function getPos(el) {
227        var r = { x: el.offsetLeft, y: el.offsetTop };
228        if (el.offsetParent) {
229            var tmp = getPos(el.offsetParent);
230            r.x += tmp.x;
231            r.y += tmp.y;
232        }
233        return r;
234    };
235    function documentClick(ev) {
236        ev || (ev = window.event);
237        if (!self.currentMenu) {
238            alert(i18n["How did you get here? (Please report!)"]);
239            return false;
240        }
241        var el = HTMLArea.is_ie ? ev.srcElement : ev.target;
242        for (; el != null && el != self.currentMenu; el = el.parentNode);
243        if (el == null)
244            self.closeMenu();
245        //HTMLArea._stopEvent(ev);
246        //return false;
247    };
248    var keys = [];
249    function keyPress(ev) {
250        ev || (ev = window.event);
251        HTMLArea._stopEvent(ev);
252        if (ev.keyCode == 27) {
253            self.closeMenu();
254            return false;
255        }
256        var key = String.fromCharCode(HTMLArea.is_ie ? ev.keyCode : ev.charCode).toLowerCase();
257        for (var i = keys.length; --i >= 0;) {
258            var k = keys[i];
259            if (k[0].toLowerCase() == key)
260                k[1].__msh.activate();
261        }
262    };
263    self.closeMenu = function() {
264        self.currentMenu.parentNode.removeChild(self.currentMenu);
265        self.currentMenu = null;
266        HTMLArea._removeEvent(document, "mousedown", documentClick);
267        HTMLArea._removeEvent(self.editordoc, "mousedown", documentClick);
268        if (keys.length > 0)
269            HTMLArea._removeEvent(self.editordoc, "keypress", keyPress);
270        if (HTMLArea.is_ie)
271            self.iePopup.hide();
272    };
273    var target = HTMLArea.is_ie ? ev.srcElement : ev.target;
274    var ifpos = getPos(self.editor._iframe);
275    var x = ev.clientX + ifpos.x;
276    var y = ev.clientY + ifpos.y;
277
278    var div;
279    var doc;
280    if (!HTMLArea.is_ie) {
281        doc = document;
282    } else {
283        // IE stinks
284        var popup = this.iePopup = window.createPopup();
285        doc = popup.document;
286        doc.open();
287        doc.write("<html><head><style type='text/css'>@import url(" + _editor_url + "plugins/ContextMenu/menu.css); html, body { padding: 0px; margin: 0px; overflow: hidden; border: 0px; }</style></head><body unselectable='yes'></body></html>");
288        doc.close();
289    }
290    div = doc.createElement("div");
291    if (HTMLArea.is_ie)
292        div.unselectable = "on";
293    div.oncontextmenu = function() { return false; };
294    div.className = "htmlarea-context-menu";
295    if (!HTMLArea.is_ie)
296        div.style.left = div.style.top = "0px";
297    doc.body.appendChild(div);
298
299    var table = doc.createElement("table");
300    div.appendChild(table);
301    table.cellSpacing = 0;
302    table.cellPadding = 0;
303    var parent = doc.createElement("tbody");
304    table.appendChild(parent);
305
306    var options = this.getContextMenu(target);
307    for (var i = 0; i < options.length; ++i) {
308        var option = options[i];
309        var item = doc.createElement("tr");
310        parent.appendChild(item);
311        if (HTMLArea.is_ie)
312            item.unselectable = "on";
313        else item.onmousedown = function(ev) {
314            HTMLArea._stopEvent(ev);
315            return false;
316        };
317        if (!option) {
318            item.className = "separator";
319            var td = doc.createElement("td");
320            td.className = "icon";
321            var IE_IS_A_FUCKING_SHIT = '>';
322            if (HTMLArea.is_ie) {
323                td.unselectable = "on";
324                IE_IS_A_FUCKING_SHIT = " unselectable='on' style='height=1px'>&nbsp;";
325            }
326            td.innerHTML = "<div" + IE_IS_A_FUCKING_SHIT + "</div>";
327            var td1 = td.cloneNode(true);
328            td1.className = "label";
329            item.appendChild(td);
330            item.appendChild(td1);
331        } else {
332            var label = option[0];
333            item.className = "item";
334            item.__msh = {
335                item: item,
336                label: label,
337                action: option[1],
338                tooltip: option[2] || null,
339                icon: option[3] || null,
340                activate: function() {
341                    self.closeMenu();
342                    self.editor.focusEditor();
343                    this.action();
344                }
345            };
346            label = label.replace(/_([a-zA-Z0-9])/, "<u>$1</u>");
347            if (label != option[0])
348                keys.push([ RegExp.$1, item ]);
349            label = label.replace(/__/, "_");
350            var td1 = doc.createElement("td");
351            if (HTMLArea.is_ie)
352                td1.unselectable = "on";
353            item.appendChild(td1);
354            td1.className = "icon";
355            if (item.__msh.icon)
356                td1.innerHTML = "<img align='middle' src='" + item.__msh.icon + "' />";
357            var td2 = doc.createElement("td");
358            if (HTMLArea.is_ie)
359                td2.unselectable = "on";
360            item.appendChild(td2);
361            td2.className = "label";
362            td2.innerHTML = label;
363            item.onmouseover = function() {
364                this.className += " hover";
365                self.editor._statusBarTree.innerHTML = this.__msh.tooltip || '&nbsp;';
366            };
367            item.onmouseout = function() { this.className = "item"; };
368            item.oncontextmenu = function(ev) {
369                this.__msh.activate();
370                if (!HTMLArea.is_ie)
371                    HTMLArea._stopEvent(ev);
372                return false;
373            };
374            item.onmouseup = function(ev) {
375                var timeStamp = (new Date()).getTime();
376                if (timeStamp - self.timeStamp > 500)
377                    this.__msh.activate();
378                if (!HTMLArea.is_ie)
379                    HTMLArea._stopEvent(ev);
380                return false;
381            };
382            //if (typeof option[2] == "string")
383            //item.title = option[2];
384        }
385    }
386
387    if (!HTMLArea.is_ie) {
388        var dx = x + div.offsetWidth - window.innerWidth + 4;
389        var dy = y + div.offsetHeight - window.innerHeight + 4;
390        if (dx > 0) x -= dx;
391        if (dy > 0) y -= dy;
392        div.style.left = x + "px";
393        div.style.top = y + "px";
394    } else {
395        // determine the size (did I mention that IE stinks?)
396        var foobar = document.createElement("div");
397        foobar.className = "htmlarea-context-menu";
398        foobar.innerHTML = div.innerHTML;
399        document.body.appendChild(foobar);
400        var w = foobar.offsetWidth;
401        var h = foobar.offsetHeight;
402        document.body.removeChild(foobar);
403        this.iePopup.show(ev.screenX, ev.screenY, w, h);
404    }
405
406    this.currentMenu = div;
407    this.timeStamp = (new Date()).getTime();
408
409    HTMLArea._addEvent(document, "mousedown", documentClick);
410    HTMLArea._addEvent(this.editordoc, "mousedown", documentClick);
411    if (keys.length > 0)
412        HTMLArea._addEvent(this.editordoc, "keypress", keyPress);
413
414    HTMLArea._stopEvent(ev);
415    return false;
416};
Note: See TracBrowser for help on using the repository browser.