// Context Menu Plugin for HTMLArea-3.0 // Sponsored by www.americanbible.org // Implementation by Mihai Bazon, http://dynarch.com/mishoo/ // // (c) dynarch.com 2003. // Distributed under the same terms as HTMLArea itself. // This notice MUST stay intact for use (see license.txt). // HTMLArea.loadStyle("menu.css", "ContextMenu"); function ContextMenu(editor) { this.editor = editor; }; ContextMenu._pluginInfo = { name : "ContextMenu", version : "1.0", developer : "Mihai Bazon", developer_url : "http://dynarch.com/mishoo/", c_owner : "dynarch.com", sponsor : "American Bible Society", sponsor_url : "http://www.americanbible.org", license : "htmlArea" }; ContextMenu.prototype.onGenerate = function() { var self = this; var doc = this.editordoc = this.editor._iframe.contentWindow.document; HTMLArea._addEvents(doc, ["contextmenu"], function (event) { return self.popupMenu(HTMLArea.is_ie ? self.editor._iframe.contentWindow.event : event); }); this.currentMenu = null; }; ContextMenu.prototype.getContextMenu = function(target) { var self = this; var editor = this.editor; var config = editor.config; var menu = []; var tbo = this.editor.plugins.TableOperations; if (tbo) tbo = tbo.instance; var i18n = ContextMenu.I18N; var selection = editor.hasSelectedText(); if (selection) menu.push([ i18n["Cut"], function() { editor.execCommand("cut"); }, null, config.btnList["cut"][1] ], [ i18n["Copy"], function() { editor.execCommand("copy"); }, null, config.btnList["copy"][1] ]); menu.push([ i18n["Paste"], function() { editor.execCommand("paste"); }, null, config.btnList["paste"][1] ]); var currentTarget = target; var elmenus = []; var link = null; var table = null; var tr = null; var td = null; var img = null; function tableOperation(opcode) { tbo.buttonPress(editor, opcode); }; for (; target; target = target.parentNode) { var tag = target.tagName; if (!tag) continue; tag = tag.toLowerCase(); switch (tag) { case "img": img = target; elmenus.push(null, [ i18n["Image Properties"], function() { editor._insertImage(img); }, i18n["Show the image properties dialog"], config.btnList["insertimage"][1] ] ); break; case "a": link = target; elmenus.push(null, [ i18n["Modify Link"], function() { editor.execCommand("createlink", true); }, i18n["Current URL is"] + ': ' + link.href, config.btnList["createlink"][1] ], [ i18n["Check Link"], function() { window.open(link.href); }, i18n["Opens this link in a new window"] ], [ i18n["Remove Link"], function() { if (confirm(i18n["Please confirm that you want to unlink this element."] + "\n" + i18n["Link points to:"] + " " + link.href)) { while (link.firstChild) link.parentNode.insertBefore(link.firstChild, link); link.parentNode.removeChild(link); } }, i18n["Unlink the current element"] ] ); break; case "td": td = target; if (!tbo) break; elmenus.push(null, [ i18n["Cell Properties"], function() { tableOperation("TO-cell-prop"); }, i18n["Show the Table Cell Properties dialog"], config.btnList["TO-cell-prop"][1] ] ); break; case "tr": tr = target; if (!tbo) break; elmenus.push(null, [ i18n["Row Properties"], function() { tableOperation("TO-row-prop"); }, i18n["Show the Table Row Properties dialog"], config.btnList["TO-row-prop"][1] ], [ i18n["Insert Row Before"], function() { tableOperation("TO-row-insert-above"); }, i18n["Insert a new row before the current one"], config.btnList["TO-row-insert-above"][1] ], [ i18n["Insert Row After"], function() { tableOperation("TO-row-insert-under"); }, i18n["Insert a new row after the current one"], config.btnList["TO-row-insert-under"][1] ], [ i18n["Delete Row"], function() { tableOperation("TO-row-delete"); }, i18n["Delete the current row"], config.btnList["TO-row-delete"][1] ] ); break; case "table": table = target; if (!tbo) break; elmenus.push(null, [ i18n["Table Properties"], function() { tableOperation("TO-table-prop"); }, i18n["Show the Table Properties dialog"], config.btnList["TO-table-prop"][1] ], [ i18n["Insert Column Before"], function() { tableOperation("TO-col-insert-before"); }, i18n["Insert a new column before the current one"], config.btnList["TO-col-insert-before"][1] ], [ i18n["Insert Column After"], function() { tableOperation("TO-col-insert-after"); }, i18n["Insert a new column after the current one"], config.btnList["TO-col-insert-after"][1] ], [ i18n["Delete Column"], function() { tableOperation("TO-col-delete"); }, i18n["Delete the current column"], config.btnList["TO-col-delete"][1] ] ); break; case "body": elmenus.push(null, [ i18n["Justify Left"], function() { editor.execCommand("justifyleft"); }, null, config.btnList["justifyleft"][1] ], [ i18n["Justify Center"], function() { editor.execCommand("justifycenter"); }, null, config.btnList["justifycenter"][1] ], [ i18n["Justify Right"], function() { editor.execCommand("justifyright"); }, null, config.btnList["justifyright"][1] ], [ i18n["Justify Full"], function() { editor.execCommand("justifyfull"); }, null, config.btnList["justifyfull"][1] ] ); break; } } if (selection && !link) menu.push(null, [ i18n["Make link"], function() { editor.execCommand("createlink", true); }, i18n["Create a link"], config.btnList["createlink"][1] ]); for (var i in elmenus) menu.push(elmenus[i]); menu.push(null, [ i18n["Remove the"] + " <" + currentTarget.tagName + "> " + i18n["Element"], function() { if (confirm(i18n["Please confirm that you want to remove this element:"] + " " + currentTarget.tagName)) { var el = currentTarget; var p = el.parentNode; p.removeChild(el); if (HTMLArea.is_gecko) { if (p.tagName.toLowerCase() == "td" && !p.hasChildNodes()) p.appendChild(editor._doc.createElement("br")); editor.forceRedraw(); editor.focusEditor(); editor.updateToolbar(); if (table) { var save_collapse = table.style.borderCollapse; table.style.borderCollapse = "collapse"; table.style.borderCollapse = "separate"; table.style.borderCollapse = save_collapse; } } } }, i18n["Remove this node from the document"] ]); return menu; }; ContextMenu.prototype.popupMenu = function(ev) { var self = this; var i18n = ContextMenu.I18N; if (this.currentMenu) this.currentMenu.parentNode.removeChild(this.currentMenu); function getPos(el) { var r = { x: el.offsetLeft, y: el.offsetTop }; if (el.offsetParent) { var tmp = getPos(el.offsetParent); r.x += tmp.x; r.y += tmp.y; } return r; }; function documentClick(ev) { ev || (ev = window.event); if (!self.currentMenu) { alert(i18n["How did you get here? (Please report!)"]); return false; } var el = HTMLArea.is_ie ? ev.srcElement : ev.target; for (; el != null && el != self.currentMenu; el = el.parentNode); if (el == null) self.closeMenu(); //HTMLArea._stopEvent(ev); //return false; }; var keys = []; function keyPress(ev) { ev || (ev = window.event); HTMLArea._stopEvent(ev); if (ev.keyCode == 27) { self.closeMenu(); return false; } var key = String.fromCharCode(HTMLArea.is_ie ? ev.keyCode : ev.charCode).toLowerCase(); for (var i = keys.length; --i >= 0;) { var k = keys[i]; if (k[0].toLowerCase() == key) k[1].__msh.activate(); } }; self.closeMenu = function() { self.currentMenu.parentNode.removeChild(self.currentMenu); self.currentMenu = null; HTMLArea._removeEvent(document, "mousedown", documentClick); HTMLArea._removeEvent(self.editordoc, "mousedown", documentClick); if (keys.length > 0) HTMLArea._removeEvent(self.editordoc, "keypress", keyPress); if (HTMLArea.is_ie) self.iePopup.hide(); }; var target = HTMLArea.is_ie ? ev.srcElement : ev.target; var ifpos = getPos(self.editor._iframe); var x = ev.clientX + ifpos.x; var y = ev.clientY + ifpos.y; var div; var doc; if (!HTMLArea.is_ie) { doc = document; } else { // IE stinks var popup = this.iePopup = window.createPopup(); doc = popup.document; doc.open(); doc.write(""); doc.close(); } div = doc.createElement("div"); if (HTMLArea.is_ie) div.unselectable = "on"; div.oncontextmenu = function() { return false; }; div.className = "htmlarea-context-menu"; if (!HTMLArea.is_ie) div.style.left = div.style.top = "0px"; doc.body.appendChild(div); var table = doc.createElement("table"); div.appendChild(table); table.cellSpacing = 0; table.cellPadding = 0; var parent = doc.createElement("tbody"); table.appendChild(parent); var options = this.getContextMenu(target); for (var i = 0; i < options.length; ++i) { var option = options[i]; var item = doc.createElement("tr"); parent.appendChild(item); if (HTMLArea.is_ie) item.unselectable = "on"; else item.onmousedown = function(ev) { HTMLArea._stopEvent(ev); return false; }; if (!option) { item.className = "separator"; var td = doc.createElement("td"); td.className = "icon"; var IE_IS_A_FUCKING_SHIT = '>'; if (HTMLArea.is_ie) { td.unselectable = "on"; IE_IS_A_FUCKING_SHIT = " unselectable='on' style='height=1px'> "; } td.innerHTML = ""; var td1 = td.cloneNode(true); td1.className = "label"; item.appendChild(td); item.appendChild(td1); } else { var label = option[0]; item.className = "item"; item.__msh = { item: item, label: label, action: option[1], tooltip: option[2] || null, icon: option[3] || null, activate: function() { self.closeMenu(); self.editor.focusEditor(); this.action(); } }; label = label.replace(/_([a-zA-Z0-9])/, "$1"); if (label != option[0]) keys.push([ RegExp.$1, item ]); label = label.replace(/__/, "_"); var td1 = doc.createElement("td"); if (HTMLArea.is_ie) td1.unselectable = "on"; item.appendChild(td1); td1.className = "icon"; if (item.__msh.icon) td1.innerHTML = ""; var td2 = doc.createElement("td"); if (HTMLArea.is_ie) td2.unselectable = "on"; item.appendChild(td2); td2.className = "label"; td2.innerHTML = label; item.onmouseover = function() { this.className += " hover"; self.editor._statusBarTree.innerHTML = this.__msh.tooltip || ' '; }; item.onmouseout = function() { this.className = "item"; }; item.oncontextmenu = function(ev) { this.__msh.activate(); if (!HTMLArea.is_ie) HTMLArea._stopEvent(ev); return false; }; item.onmouseup = function(ev) { var timeStamp = (new Date()).getTime(); if (timeStamp - self.timeStamp > 500) this.__msh.activate(); if (!HTMLArea.is_ie) HTMLArea._stopEvent(ev); return false; }; //if (typeof option[2] == "string") //item.title = option[2]; } } if (!HTMLArea.is_ie) { var dx = x + div.offsetWidth - window.innerWidth + 4; var dy = y + div.offsetHeight - window.innerHeight + 4; if (dx > 0) x -= dx; if (dy > 0) y -= dy; div.style.left = x + "px"; div.style.top = y + "px"; } else { // determine the size (did I mention that IE stinks?) var foobar = document.createElement("div"); foobar.className = "htmlarea-context-menu"; foobar.innerHTML = div.innerHTML; document.body.appendChild(foobar); var w = foobar.offsetWidth; var h = foobar.offsetHeight; document.body.removeChild(foobar); this.iePopup.show(ev.screenX, ev.screenY, w, h); } this.currentMenu = div; this.timeStamp = (new Date()).getTime(); HTMLArea._addEvent(document, "mousedown", documentClick); HTMLArea._addEvent(this.editordoc, "mousedown", documentClick); if (keys.length > 0) HTMLArea._addEvent(this.editordoc, "keypress", keyPress); HTMLArea._stopEvent(ev); return false; };