Subversion-Projekte lars-tiefland.content-management

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
// Table Operations Plugin for HTMLArea-3.0
2
// Implementation by Mihai Bazon.  Sponsored by http://www.bloki.com
3
//
4
// htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc.
5
// This notice MUST stay intact for use (see license.txt).
6
//
7
// A free WYSIWYG editor replacement for <textarea> fields.
8
// For full source code and docs, visit http://www.interactivetools.com/
9
//
10
// Version 3.0 developed by Mihai Bazon for InteractiveTools.
11
//   http://dynarch.com/mishoo
12
//
13
// $Id: table-operations.js 42 2007-04-18 10:07:34Z tiefland $
14
 
15
// Object that will encapsulate all the table operations provided by
16
// HTMLArea-3.0 (except "insert table" which is included in the main file)
17
function TableOperations(editor) {
18
	this.editor = editor;
19
 
20
	var cfg = editor.config;
21
	var tt = TableOperations.I18N;
22
	var bl = TableOperations.btnList;
23
	var self = this;
24
 
25
	// register the toolbar buttons provided by this plugin
26
	var toolbar = ["linebreak"];
27
	for (var i in bl) {
28
		var btn = bl[i];
29
		if (!btn) {
30
			toolbar.push("separator");
31
		} else {
32
			var id = "TO-" + btn[0];
33
			cfg.registerButton(id, tt[id], editor.imgURL(btn[0] + ".gif", "TableOperations"), false,
34
					   function(editor, id) {
35
						   // dispatch button press event
36
						   self.buttonPress(editor, id);
37
					   }, btn[1]);
38
			toolbar.push(id);
39
		}
40
	}
41
 
42
	// add a new line in the toolbar
43
	cfg.toolbar.push(toolbar);
44
};
45
 
46
TableOperations._pluginInfo = {
47
	name          : "TableOperations",
48
	version       : "1.0",
49
	developer     : "Mihai Bazon",
50
	developer_url : "http://dynarch.com/mishoo/",
51
	c_owner       : "Mihai Bazon",
52
	sponsor       : "Zapatec Inc.",
53
	sponsor_url   : "http://www.bloki.com",
54
	license       : "htmlArea"
55
};
56
 
57
/************************
58
 * UTILITIES
59
 ************************/
60
 
61
// retrieves the closest element having the specified tagName in the list of
62
// ancestors of the current selection/caret.
63
TableOperations.prototype.getClosest = function(tagName) {
64
	var editor = this.editor;
65
	var ancestors = editor.getAllAncestors();
66
	var ret = null;
67
	tagName = ("" + tagName).toLowerCase();
68
	for (var i in ancestors) {
69
		var el = ancestors[i];
70
		if (el.tagName.toLowerCase() == tagName) {
71
			ret = el;
72
			break;
73
		}
74
	}
75
	return ret;
76
};
77
 
78
// this function requires the file PopupDiv/PopupWin to be loaded from browser
79
TableOperations.prototype.dialogTableProperties = function() {
80
	var i18n = TableOperations.I18N;
81
	// retrieve existing values
82
	var table = this.getClosest("table");
83
	// this.editor.selectNodeContents(table);
84
	// this.editor.updateToolbar();
85
 
86
	var dialog = new PopupWin(this.editor, i18n["Table Properties"], function(dialog, params) {
87
		TableOperations.processStyle(params, table);
88
		for (var i in params) {
89
			var val = params[i];
90
			switch (i) {
91
			    case "f_caption":
92
				if (/\S/.test(val)) {
93
					// contains non white-space characters
94
					var caption = table.getElementsByTagName("caption")[0];
95
					if (!caption) {
96
						caption = dialog.editor._doc.createElement("caption");
97
						table.insertBefore(caption, table.firstChild);
98
					}
99
					caption.innerHTML = val;
100
				} else {
101
					// search for caption and delete it if found
102
					var caption = table.getElementsByTagName("caption")[0];
103
					if (caption) {
104
						caption.parentNode.removeChild(caption);
105
					}
106
				}
107
				break;
108
			    case "f_summary":
109
				table.summary = val;
110
				break;
111
			    case "f_width":
112
				table.style.width = ("" + val) + params.f_unit;
113
				break;
114
			    case "f_align":
115
				table.align = val;
116
				break;
117
			    case "f_spacing":
118
				table.cellSpacing = val;
119
				break;
120
			    case "f_padding":
121
				table.cellPadding = val;
122
				break;
123
			    case "f_borders":
124
				table.border = val;
125
				break;
126
			    case "f_frames":
127
				table.frame = val;
128
				break;
129
			    case "f_rules":
130
				table.rules = val;
131
				break;
132
			}
133
		}
134
		// various workarounds to refresh the table display (Gecko,
135
		// what's going on?! do not disappoint me!)
136
		dialog.editor.forceRedraw();
137
		dialog.editor.focusEditor();
138
		dialog.editor.updateToolbar();
139
		var save_collapse = table.style.borderCollapse;
140
		table.style.borderCollapse = "collapse";
141
		table.style.borderCollapse = "separate";
142
		table.style.borderCollapse = save_collapse;
143
	},
144
 
145
	// this function gets called when the dialog needs to be initialized
146
	function (dialog) {
147
 
148
		var f_caption = "";
149
		var capel = table.getElementsByTagName("caption")[0];
150
		if (capel) {
151
			f_caption = capel.innerHTML;
152
		}
153
		var f_summary = table.summary;
154
		var f_width = parseInt(table.style.width);
155
		isNaN(f_width) && (f_width = "");
156
		var f_unit = /%/.test(table.style.width) ? 'percent' : 'pixels';
157
		var f_align = table.align;
158
		var f_spacing = table.cellSpacing;
159
		var f_padding = table.cellPadding;
160
		var f_borders = table.border;
161
		var f_frames = table.frame;
162
		var f_rules = table.rules;
163
 
164
		function selected(val) {
165
			return val ? " selected" : "";
166
		};
167
 
168
		// dialog contents
169
		dialog.content.style.width = "400px";
170
		dialog.content.innerHTML = " \
171
<div class='title'\
172
 style='background: url(" + dialog.baseURL + dialog.editor.imgURL("table-prop.gif", "TableOperations") + ") #fff 98% 50% no-repeat'>" + i18n["Table Properties"] + "\
173
</div> \
174
<table style='width:100%'> \
175
  <tr> \
176
    <td> \
177
      <fieldset><legend>" + i18n["Description"] + "</legend> \
178
       <table style='width:100%'> \
179
        <tr> \
180
          <td class='label'>" + i18n["Caption"] + ":</td> \
181
          <td class='value'><input type='text' name='f_caption' value='" + f_caption + "'/></td> \
182
        </tr><tr> \
183
          <td class='label'>" + i18n["Summary"] + ":</td> \
184
          <td class='value'><input type='text' name='f_summary' value='" + f_summary + "'/></td> \
185
        </tr> \
186
       </table> \
187
      </fieldset> \
188
    </td> \
189
  </tr> \
190
  <tr><td id='--HA-layout'></td></tr> \
191
  <tr> \
192
    <td> \
193
      <fieldset><legend>" + i18n["Spacing and padding"] + "</legend> \
194
       <table style='width:100%'> \
195
"+//        <tr> \
196
//           <td class='label'>" + i18n["Width"] + ":</td> \
197
//           <td><input type='text' name='f_width' value='" + f_width + "' size='5' /> \
198
//             <select name='f_unit'> \
199
//               <option value='%'" + selected(f_unit == "percent") + ">" + i18n["percent"] + "</option> \
200
//               <option value='px'" + selected(f_unit == "pixels") + ">" + i18n["pixels"] + "</option> \
201
//             </select> &nbsp;&nbsp;" + i18n["Align"] + ": \
202
//             <select name='f_align'> \
203
//               <option value='left'" + selected(f_align == "left") + ">" + i18n["Left"] + "</option> \
204
//               <option value='center'" + selected(f_align == "center") + ">" + i18n["Center"] + "</option> \
205
//               <option value='right'" + selected(f_align == "right") + ">" + i18n["Right"] + "</option> \
206
//             </select> \
207
//           </td> \
208
//         </tr> \
209
"        <tr> \
210
          <td class='label'>" + i18n["Spacing"] + ":</td> \
211
          <td><input type='text' name='f_spacing' size='5' value='" + f_spacing + "' /> &nbsp;" + i18n["Padding"] + ":\
212
            <input type='text' name='f_padding' size='5' value='" + f_padding + "' /> &nbsp;&nbsp;" + i18n["pixels"] + "\
213
          </td> \
214
        </tr> \
215
       </table> \
216
      </fieldset> \
217
    </td> \
218
  </tr> \
219
  <tr> \
220
    <td> \
221
      <fieldset><legend>Frame and borders</legend> \
222
        <table width='100%'> \
223
          <tr> \
224
            <td class='label'>" + i18n["Borders"] + ":</td> \
225
            <td><input name='f_borders' type='text' size='5' value='" + f_borders + "' /> &nbsp;&nbsp;" + i18n["pixels"] + "</td> \
226
          </tr> \
227
          <tr> \
228
            <td class='label'>" + i18n["Frames"] + ":</td> \
229
            <td> \
230
              <select name='f_frames'> \
231
                <option value='void'" + selected(f_frames == "void") + ">" + i18n["No sides"] + "</option> \
232
                <option value='above'" + selected(f_frames == "above") + ">" + i18n["The top side only"] + "</option> \
233
                <option value='below'" + selected(f_frames == "below") + ">" + i18n["The bottom side only"] + "</option> \
234
                <option value='hsides'" + selected(f_frames == "hsides") + ">" + i18n["The top and bottom sides only"] + "</option> \
235
                <option value='vsides'" + selected(f_frames == "vsides") + ">" + i18n["The right and left sides only"] + "</option> \
236
                <option value='lhs'" + selected(f_frames == "lhs") + ">" + i18n["The left-hand side only"] + "</option> \
237
                <option value='rhs'" + selected(f_frames == "rhs") + ">" + i18n["The right-hand side only"] + "</option> \
238
                <option value='box'" + selected(f_frames == "box") + ">" + i18n["All four sides"] + "</option> \
239
              </select> \
240
            </td> \
241
          </tr> \
242
          <tr> \
243
            <td class='label'>" + i18n["Rules"] + ":</td> \
244
            <td> \
245
              <select name='f_rules'> \
246
                <option value='none'" + selected(f_rules == "none") + ">" + i18n["No rules"] + "</option> \
247
                <option value='rows'" + selected(f_rules == "rows") + ">" + i18n["Rules will appear between rows only"] + "</option> \
248
                <option value='cols'" + selected(f_rules == "cols") + ">" + i18n["Rules will appear between columns only"] + "</option> \
249
                <option value='all'" + selected(f_rules == "all") + ">" + i18n["Rules will appear between all rows and columns"] + "</option> \
250
              </select> \
251
            </td> \
252
          </tr> \
253
        </table> \
254
      </fieldset> \
255
    </td> \
256
  </tr> \
257
  <tr> \
258
    <td id='--HA-style'></td> \
259
  </tr> \
260
</table> \
261
";
262
		var st_prop = TableOperations.createStyleFieldset(dialog.doc, dialog.editor, table);
263
		var p = dialog.doc.getElementById("--HA-style");
264
		p.appendChild(st_prop);
265
		var st_layout = TableOperations.createStyleLayoutFieldset(dialog.doc, dialog.editor, table);
266
		p = dialog.doc.getElementById("--HA-layout");
267
		p.appendChild(st_layout);
268
		dialog.modal = true;
269
		dialog.addButtons("ok", "cancel");
270
		dialog.showAtElement(dialog.editor._iframe, "c");
271
	});
272
};
273
 
274
// this function requires the file PopupDiv/PopupWin to be loaded from browser
275
TableOperations.prototype.dialogRowCellProperties = function(cell) {
276
	var i18n = TableOperations.I18N;
277
	// retrieve existing values
278
	var element = this.getClosest(cell ? "td" : "tr");
279
	var table = this.getClosest("table");
280
	// this.editor.selectNodeContents(element);
281
	// this.editor.updateToolbar();
282
 
283
	var dialog = new PopupWin(this.editor, i18n[cell ? "Cell Properties" : "Row Properties"], function(dialog, params) {
284
		TableOperations.processStyle(params, element);
285
		for (var i in params) {
286
			var val = params[i];
287
			switch (i) {
288
			    case "f_align":
289
				element.align = val;
290
				break;
291
			    case "f_char":
292
				element.ch = val;
293
				break;
294
			    case "f_valign":
295
				element.vAlign = val;
296
				break;
297
			}
298
		}
299
		// various workarounds to refresh the table display (Gecko,
300
		// what's going on?! do not disappoint me!)
301
		dialog.editor.forceRedraw();
302
		dialog.editor.focusEditor();
303
		dialog.editor.updateToolbar();
304
		var save_collapse = table.style.borderCollapse;
305
		table.style.borderCollapse = "collapse";
306
		table.style.borderCollapse = "separate";
307
		table.style.borderCollapse = save_collapse;
308
	},
309
 
310
	// this function gets called when the dialog needs to be initialized
311
	function (dialog) {
312
 
313
		var f_align = element.align;
314
		var f_valign = element.vAlign;
315
		var f_char = element.ch;
316
 
317
		function selected(val) {
318
			return val ? " selected" : "";
319
		};
320
 
321
		// dialog contents
322
		dialog.content.style.width = "400px";
323
		dialog.content.innerHTML = " \
324
<div class='title'\
325
 style='background: url(" + dialog.baseURL + dialog.editor.imgURL(cell ? "cell-prop.gif" : "row-prop.gif", "TableOperations") + ") #fff 98% 50% no-repeat'>" + i18n[cell ? "Cell Properties" : "Row Properties"] + "</div> \
326
<table style='width:100%'> \
327
  <tr> \
328
    <td id='--HA-layout'> \
329
"+//      <fieldset><legend>" + i18n["Layout"] + "</legend> \
330
//        <table style='width:100%'> \
331
//         <tr> \
332
//           <td class='label'>" + i18n["Align"] + ":</td> \
333
//           <td> \
334
//             <select name='f_align'> \
335
//               <option value='left'" + selected(f_align == "left") + ">" + i18n["Left"] + "</option> \
336
//               <option value='center'" + selected(f_align == "center") + ">" + i18n["Center"] + "</option> \
337
//               <option value='right'" + selected(f_align == "right") + ">" + i18n["Right"] + "</option> \
338
//               <option value='char'" + selected(f_align == "char") + ">" + i18n["Char"] + "</option> \
339
//             </select> \
340
//             &nbsp;&nbsp;" + i18n["Char"] + ": \
341
//             <input type='text' style='font-family: monospace; text-align: center' name='f_char' size='1' value='" + f_char + "' /> \
342
//           </td> \
343
//         </tr><tr> \
344
//           <td class='label'>" + i18n["Vertical align"] + ":</td> \
345
//           <td> \
346
//             <select name='f_valign'> \
347
//               <option value='top'" + selected(f_valign == "top") + ">" + i18n["Top"] + "</option> \
348
//               <option value='middle'" + selected(f_valign == "middle") + ">" + i18n["Middle"] + "</option> \
349
//               <option value='bottom'" + selected(f_valign == "bottom") + ">" + i18n["Bottom"] + "</option> \
350
//               <option value='baseline'" + selected(f_valign == "baseline") + ">" + i18n["Baseline"] + "</option> \
351
//             </select> \
352
//           </td> \
353
//         </tr> \
354
//        </table> \
355
//       </fieldset> \
356
"    </td> \
357
  </tr> \
358
  <tr> \
359
    <td id='--HA-style'></td> \
360
  </tr> \
361
</table> \
362
";
363
		var st_prop = TableOperations.createStyleFieldset(dialog.doc, dialog.editor, element);
364
		var p = dialog.doc.getElementById("--HA-style");
365
		p.appendChild(st_prop);
366
		var st_layout = TableOperations.createStyleLayoutFieldset(dialog.doc, dialog.editor, element);
367
		p = dialog.doc.getElementById("--HA-layout");
368
		p.appendChild(st_layout);
369
		dialog.modal = true;
370
		dialog.addButtons("ok", "cancel");
371
		dialog.showAtElement(dialog.editor._iframe, "c");
372
	});
373
};
374
 
375
// this function gets called when some button from the TableOperations toolbar
376
// was pressed.
377
TableOperations.prototype.buttonPress = function(editor, button_id) {
378
	this.editor = editor;
379
	var mozbr = HTMLArea.is_gecko ? "<br />" : "";
380
	var i18n = TableOperations.I18N;
381
 
382
	// helper function that clears the content in a table row
383
	function clearRow(tr) {
384
		var tds = tr.getElementsByTagName("td");
385
		for (var i = tds.length; --i >= 0;) {
386
			var td = tds[i];
387
			td.rowSpan = 1;
388
			td.innerHTML = mozbr;
389
		}
390
	};
391
 
392
	function splitRow(td) {
393
		var n = parseInt("" + td.rowSpan);
394
		var nc = parseInt("" + td.colSpan);
395
		td.rowSpan = 1;
396
		tr = td.parentNode;
397
		var itr = tr.rowIndex;
398
		var trs = tr.parentNode.rows;
399
		var index = td.cellIndex;
400
		while (--n > 0) {
401
			tr = trs[++itr];
402
			var otd = editor._doc.createElement("td");
403
			otd.colSpan = td.colSpan;
404
			otd.innerHTML = mozbr;
405
			tr.insertBefore(otd, tr.cells[index]);
406
		}
407
		editor.forceRedraw();
408
		editor.updateToolbar();
409
	};
410
 
411
	function splitCol(td) {
412
		var nc = parseInt("" + td.colSpan);
413
		td.colSpan = 1;
414
		tr = td.parentNode;
415
		var ref = td.nextSibling;
416
		while (--nc > 0) {
417
			var otd = editor._doc.createElement("td");
418
			otd.rowSpan = td.rowSpan;
419
			otd.innerHTML = mozbr;
420
			tr.insertBefore(otd, ref);
421
		}
422
		editor.forceRedraw();
423
		editor.updateToolbar();
424
	};
425
 
426
	function splitCell(td) {
427
		var nc = parseInt("" + td.colSpan);
428
		splitCol(td);
429
		var items = td.parentNode.cells;
430
		var index = td.cellIndex;
431
		while (nc-- > 0) {
432
			splitRow(items[index++]);
433
		}
434
	};
435
 
436
	function selectNextNode(el) {
437
		var node = el.nextSibling;
438
		while (node && node.nodeType != 1) {
439
			node = node.nextSibling;
440
		}
441
		if (!node) {
442
			node = el.previousSibling;
443
			while (node && node.nodeType != 1) {
444
				node = node.previousSibling;
445
			}
446
		}
447
		if (!node) {
448
			node = el.parentNode;
449
		}
450
		editor.selectNodeContents(node);
451
	};
452
 
453
	switch (button_id) {
454
		// ROWS
455
 
456
	    case "TO-row-insert-above":
457
	    case "TO-row-insert-under":
458
		var tr = this.getClosest("tr");
459
		if (!tr) {
460
			break;
461
		}
462
		var otr = tr.cloneNode(true);
463
		clearRow(otr);
464
		tr.parentNode.insertBefore(otr, /under/.test(button_id) ? tr.nextSibling : tr);
465
		editor.forceRedraw();
466
		editor.focusEditor();
467
		break;
468
	    case "TO-row-delete":
469
		var tr = this.getClosest("tr");
470
		if (!tr) {
471
			break;
472
		}
473
		var par = tr.parentNode;
474
		if (par.rows.length == 1) {
475
			alert(i18n["not-del-last-row"]);
476
			break;
477
		}
478
		// set the caret first to a position that doesn't
479
		// disappear.
480
		selectNextNode(tr);
481
		par.removeChild(tr);
482
		editor.forceRedraw();
483
		editor.focusEditor();
484
		editor.updateToolbar();
485
		break;
486
	    case "TO-row-split":
487
		var td = this.getClosest("td");
488
		if (!td) {
489
			break;
490
		}
491
		splitRow(td);
492
		break;
493
 
494
		// COLUMNS
495
 
496
	    case "TO-col-insert-before":
497
	    case "TO-col-insert-after":
498
		var td = this.getClosest("td");
499
		if (!td) {
500
			break;
501
		}
502
		var rows = td.parentNode.parentNode.rows;
503
		var index = td.cellIndex;
504
		for (var i = rows.length; --i >= 0;) {
505
			var tr = rows[i];
506
			var ref = tr.cells[index + (/after/.test(button_id) ? 1 : 0)];
507
			var otd = editor._doc.createElement("td");
508
			otd.innerHTML = mozbr;
509
			tr.insertBefore(otd, ref);
510
		}
511
		editor.focusEditor();
512
		break;
513
	    case "TO-col-split":
514
		var td = this.getClosest("td");
515
		if (!td) {
516
			break;
517
		}
518
		splitCol(td);
519
		break;
520
	    case "TO-col-delete":
521
		var td = this.getClosest("td");
522
		if (!td) {
523
			break;
524
		}
525
		var index = td.cellIndex;
526
		if (td.parentNode.cells.length == 1) {
527
			alert(i18n["not-del-last-col"]);
528
			break;
529
		}
530
		// set the caret first to a position that doesn't disappear
531
		selectNextNode(td);
532
		var rows = td.parentNode.parentNode.rows;
533
		for (var i = rows.length; --i >= 0;) {
534
			var tr = rows[i];
535
			tr.removeChild(tr.cells[index]);
536
		}
537
		editor.forceRedraw();
538
		editor.focusEditor();
539
		editor.updateToolbar();
540
		break;
541
 
542
		// CELLS
543
 
544
	    case "TO-cell-split":
545
		var td = this.getClosest("td");
546
		if (!td) {
547
			break;
548
		}
549
		splitCell(td);
550
		break;
551
	    case "TO-cell-insert-before":
552
	    case "TO-cell-insert-after":
553
		var td = this.getClosest("td");
554
		if (!td) {
555
			break;
556
		}
557
		var tr = td.parentNode;
558
		var otd = editor._doc.createElement("td");
559
		otd.innerHTML = mozbr;
560
		tr.insertBefore(otd, /after/.test(button_id) ? td.nextSibling : td);
561
		editor.forceRedraw();
562
		editor.focusEditor();
563
		break;
564
	    case "TO-cell-delete":
565
		var td = this.getClosest("td");
566
		if (!td) {
567
			break;
568
		}
569
		if (td.parentNode.cells.length == 1) {
570
			alert(i18n["not-del-last-cell"]);
571
			break;
572
		}
573
		// set the caret first to a position that doesn't disappear
574
		selectNextNode(td);
575
		td.parentNode.removeChild(td);
576
		editor.forceRedraw();
577
		editor.updateToolbar();
578
		break;
579
	    case "TO-cell-merge":
580
		// !! FIXME: Mozilla specific !!
581
		var sel = editor._getSelection();
582
		var range, i = 0;
583
		var rows = [];
584
		var row = null;
585
		var cells = null;
586
		if (!HTMLArea.is_ie) {
587
			try {
588
				while (range = sel.getRangeAt(i++)) {
589
					var td = range.startContainer.childNodes[range.startOffset];
590
					if (td.parentNode != row) {
591
						row = td.parentNode;
592
						(cells) && rows.push(cells);
593
						cells = [];
594
					}
595
					cells.push(td);
596
				}
597
			} catch(e) {/* finished walking through selection */}
598
			rows.push(cells);
599
		} else {
600
			// Internet Explorer "browser"
601
			var td = this.getClosest("td");
602
			if (!td) {
603
				alert(i18n["Please click into some cell"]);
604
				break;
605
			}
606
			var tr = td.parentElement;
607
			var no_cols = prompt(i18n["How many columns would you like to merge?"], 2);
608
			if (!no_cols) {
609
				// cancelled
610
				break;
611
			}
612
			var no_rows = prompt(i18n["How many rows would you like to merge?"], 2);
613
			if (!no_rows) {
614
				// cancelled
615
				break;
616
			}
617
			var cell_index = td.cellIndex;
618
			while (no_rows-- > 0) {
619
				td = tr.cells[cell_index];
620
				cells = [td];
621
				for (var i = 1; i < no_cols; ++i) {
622
					td = td.nextSibling;
623
					if (!td) {
624
						break;
625
					}
626
					cells.push(td);
627
				}
628
				rows.push(cells);
629
				tr = tr.nextSibling;
630
				if (!tr) {
631
					break;
632
				}
633
			}
634
		}
635
		var HTML = "";
636
		for (i = 0; i < rows.length; ++i) {
637
			// i && (HTML += "<br />");
638
			var cells = rows[i];
639
			for (var j = 0; j < cells.length; ++j) {
640
				// j && (HTML += "&nbsp;");
641
				var cell = cells[j];
642
				HTML += cell.innerHTML;
643
				(i || j) && (cell.parentNode.removeChild(cell));
644
			}
645
		}
646
		var td = rows[0][0];
647
		td.innerHTML = HTML;
648
		td.rowSpan = rows.length;
649
		td.colSpan = rows[0].length;
650
		editor.selectNodeContents(td);
651
		editor.forceRedraw();
652
		editor.focusEditor();
653
		break;
654
 
655
		// PROPERTIES
656
 
657
	    case "TO-table-prop":
658
		this.dialogTableProperties();
659
		break;
660
 
661
	    case "TO-row-prop":
662
		this.dialogRowCellProperties(false);
663
		break;
664
 
665
	    case "TO-cell-prop":
666
		this.dialogRowCellProperties(true);
667
		break;
668
 
669
	    default:
670
		alert("Button [" + button_id + "] not yet implemented");
671
	}
672
};
673
 
674
// the list of buttons added by this plugin
675
TableOperations.btnList = [
676
	// table properties button
677
	["table-prop",       "table"],
678
	null,			// separator
679
 
680
	// ROWS
681
	["row-prop",         "tr"],
682
	["row-insert-above", "tr"],
683
	["row-insert-under", "tr"],
684
	["row-delete",       "tr"],
685
	["row-split",        "td[rowSpan!=1]"],
686
	null,
687
 
688
	// COLS
689
	["col-insert-before", "td"],
690
	["col-insert-after",  "td"],
691
	["col-delete",        "td"],
692
	["col-split",         "td[colSpan!=1]"],
693
	null,
694
 
695
	// CELLS
696
	["cell-prop",          "td"],
697
	["cell-insert-before", "td"],
698
	["cell-insert-after",  "td"],
699
	["cell-delete",        "td"],
700
	["cell-merge",         "tr"],
701
	["cell-split",         "td[colSpan!=1,rowSpan!=1]"]
702
	];
703
 
704
 
705
 
706
//// GENERIC CODE [style of any element; this should be moved into a separate
707
//// file as it'll be very useful]
708
//// BEGIN GENERIC CODE -----------------------------------------------------
709
 
710
TableOperations.getLength = function(value) {
711
	var len = parseInt(value);
712
	if (isNaN(len)) {
713
		len = "";
714
	}
715
	return len;
716
};
717
 
718
// Applies the style found in "params" to the given element.
719
TableOperations.processStyle = function(params, element) {
720
	var style = element.style;
721
	for (var i in params) {
722
		var val = params[i];
723
		switch (i) {
724
		    case "f_st_backgroundColor":
725
			style.backgroundColor = val;
726
			break;
727
		    case "f_st_color":
728
			style.color = val;
729
			break;
730
		    case "f_st_backgroundImage":
731
			if (/\S/.test(val)) {
732
				style.backgroundImage = "url(" + val + ")";
733
			} else {
734
				style.backgroundImage = "none";
735
			}
736
			break;
737
		    case "f_st_borderWidth":
738
			style.borderWidth = val;
739
			break;
740
		    case "f_st_borderStyle":
741
			style.borderStyle = val;
742
			break;
743
		    case "f_st_borderColor":
744
			style.borderColor = val;
745
			break;
746
		    case "f_st_borderCollapse":
747
			style.borderCollapse = val ? "collapse" : "";
748
			break;
749
		    case "f_st_width":
750
			if (/\S/.test(val)) {
751
				style.width = val + params["f_st_widthUnit"];
752
			} else {
753
				style.width = "";
754
			}
755
			break;
756
		    case "f_st_height":
757
			if (/\S/.test(val)) {
758
				style.height = val + params["f_st_heightUnit"];
759
			} else {
760
				style.height = "";
761
			}
762
			break;
763
		    case "f_st_textAlign":
764
			if (val == "char") {
765
				var ch = params["f_st_textAlignChar"];
766
				if (ch == '"') {
767
					ch = '\\"';
768
				}
769
				style.textAlign = '"' + ch + '"';
770
			} else {
771
				style.textAlign = val;
772
			}
773
			break;
774
		    case "f_st_verticalAlign":
775
			style.verticalAlign = val;
776
			break;
777
		    case "f_st_float":
778
			style.cssFloat = val;
779
			break;
780
// 		    case "f_st_margin":
781
// 			style.margin = val + "px";
782
// 			break;
783
// 		    case "f_st_padding":
784
// 			style.padding = val + "px";
785
// 			break;
786
		}
787
	}
788
};
789
 
790
// Returns an HTML element for a widget that allows color selection.  That is,
791
// a button that contains the given color, if any, and when pressed will popup
792
// the sooner-or-later-to-be-rewritten select_color.html dialog allowing user
793
// to select some color.  If a color is selected, an input field with the name
794
// "f_st_"+name will be updated with the color value in #123456 format.
795
TableOperations.createColorButton = function(doc, editor, color, name) {
796
	if (!color) {
797
		color = "";
798
	} else if (!/#/.test(color)) {
799
		color = HTMLArea._colorToRgb(color);
800
	}
801
 
802
	var df = doc.createElement("span");
803
 	var field = doc.createElement("input");
804
	field.type = "hidden";
805
	df.appendChild(field);
806
 	field.name = "f_st_" + name;
807
	field.value = color;
808
	var button = doc.createElement("span");
809
	button.className = "buttonColor";
810
	df.appendChild(button);
811
	var span = doc.createElement("span");
812
	span.className = "chooser";
813
	// span.innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
814
	span.style.backgroundColor = color;
815
	button.appendChild(span);
816
	button.onmouseover = function() { if (!this.disabled) { this.className += " buttonColor-hilite"; }};
817
	button.onmouseout = function() { if (!this.disabled) { this.className = "buttonColor"; }};
818
	span.onclick = function() {
819
		if (this.parentNode.disabled) {
820
			return false;
821
		}
822
		editor._popupDialog("select_color.html", function(color) {
823
			if (color) {
824
				span.style.backgroundColor = "#" + color;
825
				field.value = "#" + color;
826
			}
827
		}, color);
828
	};
829
	var span2 = doc.createElement("span");
830
	span2.innerHTML = "&#x00d7;";
831
	span2.className = "nocolor";
832
	span2.title = TableOperations.I18N["Unset color"];
833
	button.appendChild(span2);
834
	span2.onmouseover = function() { if (!this.parentNode.disabled) { this.className += " nocolor-hilite"; }};
835
	span2.onmouseout = function() { if (!this.parentNode.disabled) { this.className = "nocolor"; }};
836
	span2.onclick = function() {
837
		span.style.backgroundColor = "";
838
		field.value = "";
839
	};
840
	return df;
841
};
842
 
843
TableOperations.createStyleLayoutFieldset = function(doc, editor, el) {
844
	var i18n = TableOperations.I18N;
845
	var fieldset = doc.createElement("fieldset");
846
	var legend = doc.createElement("legend");
847
	fieldset.appendChild(legend);
848
	legend.innerHTML = i18n["Layout"];
849
	var table = doc.createElement("table");
850
	fieldset.appendChild(table);
851
	table.style.width = "100%";
852
	var tbody = doc.createElement("tbody");
853
	table.appendChild(tbody);
854
 
855
	var tagname = el.tagName.toLowerCase();
856
	var tr, td, input, select, option, options, i;
857
 
858
	if (tagname != "td" && tagname != "tr" && tagname != "th") {
859
		tr = doc.createElement("tr");
860
		tbody.appendChild(tr);
861
		td = doc.createElement("td");
862
		td.className = "label";
863
		tr.appendChild(td);
864
		td.innerHTML = i18n["Float"] + ":";
865
		td = doc.createElement("td");
866
		tr.appendChild(td);
867
		select = doc.createElement("select");
868
		td.appendChild(select);
869
		select.name = "f_st_float";
870
		options = ["None", "Left", "Right"];
871
		for (i in options) {
872
			var Val = options[i];
873
			var val = options[i].toLowerCase();
874
			option = doc.createElement("option");
875
			option.innerHTML = i18n[Val];
876
			option.value = val;
877
			option.selected = (("" + el.style.cssFloat).toLowerCase() == val);
878
			select.appendChild(option);
879
		}
880
	}
881
 
882
	tr = doc.createElement("tr");
883
	tbody.appendChild(tr);
884
	td = doc.createElement("td");
885
	td.className = "label";
886
	tr.appendChild(td);
887
	td.innerHTML = i18n["Width"] + ":";
888
	td = doc.createElement("td");
889
	tr.appendChild(td);
890
	input = doc.createElement("input");
891
	input.type = "text";
892
	input.value = TableOperations.getLength(el.style.width);
893
	input.size = "5";
894
	input.name = "f_st_width";
895
	input.style.marginRight = "0.5em";
896
	td.appendChild(input);
897
	select = doc.createElement("select");
898
	select.name = "f_st_widthUnit";
899
	option = doc.createElement("option");
900
	option.innerHTML = i18n["percent"];
901
	option.value = "%";
902
	option.selected = /%/.test(el.style.width);
903
	select.appendChild(option);
904
	option = doc.createElement("option");
905
	option.innerHTML = i18n["pixels"];
906
	option.value = "px";
907
	option.selected = /px/.test(el.style.width);
908
	select.appendChild(option);
909
	td.appendChild(select);
910
 
911
	select.style.marginRight = "0.5em";
912
	td.appendChild(doc.createTextNode(i18n["Text align"] + ":"));
913
	select = doc.createElement("select");
914
	select.style.marginLeft = select.style.marginRight = "0.5em";
915
	td.appendChild(select);
916
	select.name = "f_st_textAlign";
917
	options = ["Left", "Center", "Right", "Justify"];
918
	if (tagname == "td") {
919
		options.push("Char");
920
	}
921
	input = doc.createElement("input");
922
	input.name = "f_st_textAlignChar";
923
	input.size = "1";
924
	input.style.fontFamily = "monospace";
925
	td.appendChild(input);
926
	for (i in options) {
927
		var Val = options[i];
928
		var val = Val.toLowerCase();
929
		option = doc.createElement("option");
930
		option.value = val;
931
		option.innerHTML = i18n[Val];
932
		option.selected = (el.style.textAlign.toLowerCase() == val);
933
		select.appendChild(option);
934
	}
935
	function setCharVisibility(value) {
936
		input.style.visibility = value ? "visible" : "hidden";
937
		if (value) {
938
			input.focus();
939
			input.select();
940
		}
941
	};
942
	select.onchange = function() { setCharVisibility(this.value == "char"); };
943
	setCharVisibility(select.value == "char");
944
 
945
	tr = doc.createElement("tr");
946
	tbody.appendChild(tr);
947
	td = doc.createElement("td");
948
	td.className = "label";
949
	tr.appendChild(td);
950
	td.innerHTML = i18n["Height"] + ":";
951
	td = doc.createElement("td");
952
	tr.appendChild(td);
953
	input = doc.createElement("input");
954
	input.type = "text";
955
	input.value = TableOperations.getLength(el.style.height);
956
	input.size = "5";
957
	input.name = "f_st_height";
958
	input.style.marginRight = "0.5em";
959
	td.appendChild(input);
960
	select = doc.createElement("select");
961
	select.name = "f_st_heightUnit";
962
	option = doc.createElement("option");
963
	option.innerHTML = i18n["percent"];
964
	option.value = "%";
965
	option.selected = /%/.test(el.style.height);
966
	select.appendChild(option);
967
	option = doc.createElement("option");
968
	option.innerHTML = i18n["pixels"];
969
	option.value = "px";
970
	option.selected = /px/.test(el.style.height);
971
	select.appendChild(option);
972
	td.appendChild(select);
973
 
974
	select.style.marginRight = "0.5em";
975
	td.appendChild(doc.createTextNode(i18n["Vertical align"] + ":"));
976
	select = doc.createElement("select");
977
	select.name = "f_st_verticalAlign";
978
	select.style.marginLeft = "0.5em";
979
	td.appendChild(select);
980
	options = ["Top", "Middle", "Bottom", "Baseline"];
981
	for (i in options) {
982
		var Val = options[i];
983
		var val = Val.toLowerCase();
984
		option = doc.createElement("option");
985
		option.value = val;
986
		option.innerHTML = i18n[Val];
987
		option.selected = (el.style.verticalAlign.toLowerCase() == val);
988
		select.appendChild(option);
989
	}
990
 
991
	return fieldset;
992
};
993
 
994
// Returns an HTML element containing the style attributes for the given
995
// element.  This can be easily embedded into any dialog; the functionality is
996
// also provided.
997
TableOperations.createStyleFieldset = function(doc, editor, el) {
998
	var i18n = TableOperations.I18N;
999
	var fieldset = doc.createElement("fieldset");
1000
	var legend = doc.createElement("legend");
1001
	fieldset.appendChild(legend);
1002
	legend.innerHTML = i18n["CSS Style"];
1003
	var table = doc.createElement("table");
1004
	fieldset.appendChild(table);
1005
	table.style.width = "100%";
1006
	var tbody = doc.createElement("tbody");
1007
	table.appendChild(tbody);
1008
 
1009
	var tr, td, input, select, option, options, i;
1010
 
1011
	tr = doc.createElement("tr");
1012
	tbody.appendChild(tr);
1013
	td = doc.createElement("td");
1014
	tr.appendChild(td);
1015
	td.className = "label";
1016
	td.innerHTML = i18n["Background"] + ":";
1017
	td = doc.createElement("td");
1018
	tr.appendChild(td);
1019
	var df = TableOperations.createColorButton(doc, editor, el.style.backgroundColor, "backgroundColor");
1020
	df.firstChild.nextSibling.style.marginRight = "0.5em";
1021
	td.appendChild(df);
1022
	td.appendChild(doc.createTextNode(i18n["Image URL"] + ": "));
1023
	input = doc.createElement("input");
1024
	input.type = "text";
1025
	input.name = "f_st_backgroundImage";
1026
	if (el.style.backgroundImage.match(/url\(\s*(.*?)\s*\)/)) {
1027
		input.value = RegExp.$1;
1028
	}
1029
	// input.style.width = "100%";
1030
	td.appendChild(input);
1031
 
1032
	tr = doc.createElement("tr");
1033
	tbody.appendChild(tr);
1034
	td = doc.createElement("td");
1035
	tr.appendChild(td);
1036
	td.className = "label";
1037
	td.innerHTML = i18n["FG Color"] + ":";
1038
	td = doc.createElement("td");
1039
	tr.appendChild(td);
1040
	td.appendChild(TableOperations.createColorButton(doc, editor, el.style.color, "color"));
1041
 
1042
	// for better alignment we include an invisible field.
1043
	input = doc.createElement("input");
1044
	input.style.visibility = "hidden";
1045
	input.type = "text";
1046
	td.appendChild(input);
1047
 
1048
	tr = doc.createElement("tr");
1049
	tbody.appendChild(tr);
1050
	td = doc.createElement("td");
1051
	tr.appendChild(td);
1052
	td.className = "label";
1053
	td.innerHTML = i18n["Border"] + ":";
1054
	td = doc.createElement("td");
1055
	tr.appendChild(td);
1056
 
1057
	var colorButton = TableOperations.createColorButton(doc, editor, el.style.borderColor, "borderColor");
1058
	var btn = colorButton.firstChild.nextSibling;
1059
	td.appendChild(colorButton);
1060
	// borderFields.push(btn);
1061
	btn.style.marginRight = "0.5em";
1062
 
1063
	select = doc.createElement("select");
1064
	var borderFields = [];
1065
	td.appendChild(select);
1066
	select.name = "f_st_borderStyle";
1067
	options = ["none", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset"];
1068
	var currentBorderStyle = el.style.borderStyle;
1069
	// Gecko reports "solid solid solid solid" for "border-style: solid".
1070
	// That is, "top right bottom left" -- we only consider the first
1071
	// value.
1072
	(currentBorderStyle.match(/([^\s]*)\s/)) && (currentBorderStyle = RegExp.$1);
1073
	for (i in options) {
1074
		var val = options[i];
1075
		option = doc.createElement("option");
1076
		option.value = val;
1077
		option.innerHTML = val;
1078
		(val == currentBorderStyle) && (option.selected = true);
1079
		select.appendChild(option);
1080
	}
1081
	select.style.marginRight = "0.5em";
1082
	function setBorderFieldsStatus(value) {
1083
		for (i in borderFields) {
1084
			var el = borderFields[i];
1085
			el.style.visibility = value ? "hidden" : "visible";
1086
			if (!value && (el.tagName.toLowerCase() == "input")) {
1087
				el.focus();
1088
				el.select();
1089
			}
1090
		}
1091
	};
1092
	select.onchange = function() { setBorderFieldsStatus(this.value == "none"); };
1093
 
1094
	input = doc.createElement("input");
1095
	borderFields.push(input);
1096
	input.type = "text";
1097
	input.name = "f_st_borderWidth";
1098
	input.value = TableOperations.getLength(el.style.borderWidth);
1099
	input.size = "5";
1100
	td.appendChild(input);
1101
	input.style.marginRight = "0.5em";
1102
	var span = doc.createElement("span");
1103
	span.innerHTML = i18n["pixels"];
1104
	td.appendChild(span);
1105
	borderFields.push(span);
1106
 
1107
	setBorderFieldsStatus(select.value == "none");
1108
 
1109
	if (el.tagName.toLowerCase() == "table") {
1110
		// the border-collapse style is only for tables
1111
		tr = doc.createElement("tr");
1112
		tbody.appendChild(tr);
1113
		td = doc.createElement("td");
1114
		td.className = "label";
1115
		tr.appendChild(td);
1116
		input = doc.createElement("input");
1117
		input.type = "checkbox";
1118
		input.name = "f_st_borderCollapse";
1119
		input.id = "f_st_borderCollapse";
1120
		var val = (/collapse/i.test(el.style.borderCollapse));
1121
		input.checked = val ? 1 : 0;
1122
		td.appendChild(input);
1123
 
1124
		td = doc.createElement("td");
1125
		tr.appendChild(td);
1126
		var label = doc.createElement("label");
1127
		label.htmlFor = "f_st_borderCollapse";
1128
		label.innerHTML = i18n["Collapsed borders"];
1129
		td.appendChild(label);
1130
	}
1131
 
1132
// 	tr = doc.createElement("tr");
1133
// 	tbody.appendChild(tr);
1134
// 	td = doc.createElement("td");
1135
// 	td.className = "label";
1136
// 	tr.appendChild(td);
1137
// 	td.innerHTML = i18n["Margin"] + ":";
1138
// 	td = doc.createElement("td");
1139
// 	tr.appendChild(td);
1140
// 	input = doc.createElement("input");
1141
// 	input.type = "text";
1142
// 	input.size = "5";
1143
// 	input.name = "f_st_margin";
1144
// 	td.appendChild(input);
1145
// 	input.style.marginRight = "0.5em";
1146
// 	td.appendChild(doc.createTextNode(i18n["Padding"] + ":"));
1147
 
1148
// 	input = doc.createElement("input");
1149
// 	input.type = "text";
1150
// 	input.size = "5";
1151
// 	input.name = "f_st_padding";
1152
// 	td.appendChild(input);
1153
// 	input.style.marginLeft = "0.5em";
1154
// 	input.style.marginRight = "0.5em";
1155
// 	td.appendChild(doc.createTextNode(i18n["pixels"]));
1156
 
1157
	return fieldset;
1158
};
1159
 
1160
//// END GENERIC CODE -------------------------------------------------------