Subversion-Projekte lars-tiefland.content-management

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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: context-menu.js 42 2007-04-18 10:07:34Z tiefland $
10
 
11
HTMLArea.loadStyle("menu.css", "ContextMenu");
12
 
13
function ContextMenu(editor) {
14
	this.editor = editor;
15
};
16
 
17
ContextMenu._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
 
28
ContextMenu.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
 
38
ContextMenu.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
 
221
ContextMenu.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
};