Subversion Repositories Sites.tela-botanica.org

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
420 florian 1
/**
2
 * $Id: editor_plugin_src.js 162 2007-01-03 16:16:52Z spocke $
3
 *
4
 * @author Moxiecode
5
 * @copyright Copyright © 2004-2007, Moxiecode Systems AB, All rights reserved.
6
 */
7
 
8
tinyMCE.importPluginLanguagePack('template');
9
 
10
var TinyMCE_TemplatePlugin = {
11
	getInfo : function() {
12
		return {
13
			longname : 'Template plugin',
14
			author : 'Moxiecode Systems AB',
15
			authorurl : 'http://www.moxiecode.com',
16
			infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/template',
17
			version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion
18
		};
19
	},
20
 
21
	initInstance : function(inst) {
22
		var cdate, mdate, content, x = 0, key, value, rvals, ds = inst.getData('template');
23
 
24
		// ensure the required elements and sttributes are added
25
		//inst.cleanup.addRuleStr('*[' + TinyMCE_TemplatePlugin.TMPL_DATE_SRC_ATTR + '],div[title,tsrc]');
26
 
27
		//setup template content functions
28
		// creation date and modified date classes
29
		cdate = tinyMCE.getParam("template_cdate_classes", '').split(/\s+/);
30
		mdate = tinyMCE.getParam("template_mdate_classes", '').split(/\s+/);
31
 
32
		// classes that specify where selected content should go
33
		content = tinyMCE.getParam("template_selected_content_classes", '').split(/\s+/);
34
 
35
		for (x = 0; x < cdate.length; x++)
36
			TinyMCE_TemplatePlugin.functions[cdate[x]] = TinyMCE_TemplatePlugin.functions['cdate'];
37
 
38
		for (x = 0; x < mdate.length; x++)
39
			TinyMCE_TemplatePlugin.functions[mdate[x]] = TinyMCE_TemplatePlugin.functions['mdate'];
40
 
41
		for (x = 0; x < content.length; x++)
42
			TinyMCE_TemplatePlugin.functions[content[x]] = TinyMCE_TemplatePlugin.functions['selectedContent'];
43
 
44
		// special template functions for replacing template content
45
		rvals = tinyMCE.getParam("template_replace_values", false);
46
		for (key in rvals) {
47
			value = rvals[key];
48
 
49
			if (typeof value == "function")
50
				TinyMCE_TemplatePlugin.functions[key] = value;
51
			else
52
				TinyMCE_TemplatePlugin.functions[key] = TinyMCE_TemplatePlugin.functions['generateReplacer'](value);
53
		}
54
 
55
		// Setup replace_items
56
		rvals = tinyMCE.getParam("template_replace_values", false);
57
		ds.replace_items = {};
58
 
59
		for (key in rvals)
60
			ds.replace_items[key] = rvals[key];
61
 
62
		inst.addShortcut('ctrl', 't', 'lang_template_desc', 'mceTemplate');
63
 
64
		// Setup data storage
65
		ds.currentAction = "insert";
66
		ds.currentTmplNode = null;
67
	},
68
 
69
	getControlHTML : function(cn) {
70
		switch (cn) {
71
			case "template":
72
				return tinyMCE.getButtonHTML(cn, 'lang_template_desc', '{$pluginurl}/images/template.gif', 'mceTemplate', true);
73
		}
74
 
75
		return "";
76
	},
77
 
78
	execCommand : function(editor_id, element, command, user_interface, value) {
79
		var nodeArray, current, newTmpl, x, inst = tinyMCE.getInstanceById(editor_id), ds = inst.getData('template'), telm;
80
 
81
		switch (command) {
82
			case "mceTemplate":
83
				if (user_interface) {
84
					// called from toolbar button - show the popup
85
					tinyMCE.openWindow({
86
						file : '../../plugins/template/template.htm', // Relative to theme
87
						width : tinyMCE.getParam('template_popup_width', 750),
88
						height : tinyMCE.getParam('template_popup_height', 600)
89
					}, {editor_id : editor_id, resizable : "yes", scrollbars : "no", pluginObj : TinyMCE_TemplatePlugin});
90
				} else {
91
					// internal command do the template stuff
92
 
93
					// get the returned HTML string from the pop-up and appened it to a DIV element
94
					telm = TinyMCE_TemplatePlugin._convertToNode(value.body);
95
 
96
					// Find template body
97
					nodeArray = tinyMCE.selectElements(telm, 'div', function(n) {
98
						return tinyMCE.hasCSSClass(n, TinyMCE_TemplatePlugin.TMPL);
99
					});
100
 
101
					telm = nodeArray.length > 0 ? nodeArray[0] : null;
102
					nodeArray = [];
103
 
104
					if (ds.currentAction == "insert") {
105
						//insert new template after applying all the template content functions
106
 
107
						// Is it a template or snippet
108
						if (telm) {
109
							tinyMCE.execCommand('mceBeginUndoLevel');
110
							ds.currentAction = "insert-new";
111
							TinyMCE_TemplatePlugin._insertTemplate(editor_id, telm, value.title, value.tsrc, true);
112
							ds.currentAction == "insert";
113
							tinyMCE.execCommand('mceEndUndoLevel');
114
							tinyMCE.execInstanceCommand(editor_id, 'mceCleanup', false);
115
						} else
116
							tinyMCE.execCommand('mceInsertContent', false, this._replaceValues(value.body));
117
					} else {
118
						// First collect the selected template in the editor
119
						nodeArray = TinyMCE_TemplatePlugin._collectTemplateElements(ds.currentTmplNode);
120
						current = [];
121
						newTmpl = [];
122
						tinyMCE.getNodeTree(telm, newTmpl);
123
 
124
						for (x=0; x<nodeArray.length; x++)
125
							tinyMCE.getNodeTree(nodeArray[x], current);
126
 
127
						/**
128
						 * inner function used in the loop below.
129
						 * compares the supplied HTML element to the new template to:
130
						 * - find a match with the new template and copy the element's content over
131
						 * - find no match and indicate content will be lost
132
						 */
133
						var _test = function(elm) {
134
							var replaced = true;
135
 
136
							if (elm.className) {
137
								var names = elm.className.split(/\s+/), c, n;
138
 
139
								for (c = 0; c<names.length; c++) {
140
									if (names[c].match(/^mce/i))
141
										continue; // ignore all internal class names
142
 
143
									for (n=0; n<newTmpl.length; n++){
144
										replaced = false;
145
 
146
										if (newTmpl[n].className && newTmpl[n].className.match(new RegExp(names[c], "gi"))) {
147
											newTmpl[n].innerHTML = elm.innerHTML;
148
											//if(tinyMCE.getAttrib(elm,TinyMCE_TemplatePlugin.TMPL_DATE_SRC_ATTR,"") != "") {
149
											//	tinyMCE.setAttrib(newTmpl[n], TinyMCE_TemplatePlugin.TMPL_DATE_SRC_ATTR, tinyMCE.getAttrib(elm,TinyMCE_TemplatePlugin.TMPL_DATE_SRC_ATTR));
150
											//}
151
											replaced = true;
152
											break;
153
										}
154
 
155
									}
156
								}
157
							}
158
 
159
							return replaced;
160
						};
161
 
162
						// comparison loop - first mis-match alerts user for confirmation.
163
						var cont = true;
164
						var asked = false;
165
 
166
						for (x = 0; x < current.length; x++) {
167
							if(!_test(current[x])) {
168
								cont = (asked || confirm("The new template has less elements than the currently selected content.\nIf you proceed you will loose content.\nAre you sure you want to proceed?", "Proceed?"));
169
								asked = true;
170
 
171
								if (!cont)
172
									break;
173
							}
174
						};
175
 
176
						// apply replacement if allowed to
177
						if (cont) {
178
							tinyMCE.execCommand('mceBeginUndoLevel');
179
							TinyMCE_TemplatePlugin._replaceTemplateContent(current[0], editor_id, telm, value.title, value.tsrc);
180
							tinyMCE.execCommand('mceEndUndoLevel');
181
							tinyMCE.execInstanceCommand(editor_id, 'mceCleanup', false);
182
						}
183
					}
184
 
185
					tinyMCE.triggerNodeChange(true);
186
				}
187
 
188
				return true;
189
		}
190
 
191
		return false;
192
	},
193
 
194
	handleNodeChange : function(editor_id, node, undo_index, undo_levels, visual_aid, any_selection) {
195
		var inst = tinyMCE.getInstanceById(editor_id), ds = inst.getData('template');
196
 
197
		if (tinyMCE.hasCSSClass(node, TinyMCE_TemplatePlugin.TMPL_ELEMENT) || tinyMCE.hasCSSClass(node.parentNode, TinyMCE_TemplatePlugin.TMPL_ELEMENT)) {
198
			tinyMCE.switchClass(editor_id + '_template', 'mceButtonSelected');
199
			ds.currentAction = "update";
200
			ds.currentTmplNode = node;
201
 
202
			return true;
203
		}
204
 
205
		ds.currentAction = "insert";
206
		ds.currentTmplNode = null;
207
		tinyMCE.switchClass(editor_id + '_template', 'mceButtonNormal');
208
 
209
		return false;
210
	},
211
 
212
	cleanup : function(type, content, inst) {
213
		var nodes = [];
214
 
215
		switch (type) {
216
			case "get_from_editor":
217
				// replace the opening wrapper div tag with a HTML comment
218
				content = content.replace(
219
					new RegExp('<div class="' + TinyMCE_TemplatePlugin.TMPL + '">', 'gi'),
220
					'<!-- ' + TinyMCE_TemplatePlugin.TMPL_BEGINS + ' -->'
221
				);
222
 
223
				// delete any empty template wrappers
224
				content = content.replace(
225
					new RegExp('<div class="' + TinyMCE_TemplatePlugin.TMPL + '">(\s|&nbsp;|&#160;)?(<!-- ' + TinyMCE_TemplatePlugin.TMPL_ENDS + ' -->|\s)?</div>', 'gi'),
226
					''
227
				);
228
 
229
				// replace the closing wrapper tag
230
				content = content.replace(
231
					new RegExp('<!-- ' + TinyMCE_TemplatePlugin.TMPL_ENDS + ' --></div>', 'gi'),
232
					'<!-- ' + TinyMCE_TemplatePlugin.TMPL_ENDS + ' -->'
233
				);
234
 
235
				break;
236
 
237
			case "insert_to_editor":
238
				// replace HTML comment with DIV wrapper
239
				content = content.replace(
240
					new RegExp('<!-- ' + TinyMCE_TemplatePlugin.TMPL_BEGINS + ' -->', 'gi'),
241
					'<div class="' + TinyMCE_TemplatePlugin.TMPL + '">'
242
				);
243
 
244
				content = content.replace(
245
					new RegExp('<!-- ' + TinyMCE_TemplatePlugin.TMPL_ENDS + ' -->', 'gi'),
246
					'<!-- ' + TinyMCE_TemplatePlugin.TMPL_ENDS + ' --></div>'
247
				);
248
 
249
				break;
250
 
251
			case "get_from_editor_dom":
252
				// apply template content replacement functions
253
				nodes = tinyMCE.selectNodes(content, function(n) {
254
						return tinyMCE.hasCSSClass(n, TinyMCE_TemplatePlugin.TMPL_ELEMENT);
255
					}
256
				);
257
 
258
				TinyMCE_TemplatePlugin._applyFunctions(nodes, type);
259
 
260
				break;
261
 
262
			case "insert_to_editor_dom":
263
				// apply template content replacement functions
264
				nodes = tinyMCE.selectNodes(content, function(n) {
265
						return tinyMCE.hasCSSClass(n, TinyMCE_TemplatePlugin.TMPL_ELEMENT);
266
					}
267
				);
268
 
269
				TinyMCE_TemplatePlugin._applyFunctions(nodes, type);
270
 
271
				break;
272
		}
273
 
274
		return content;
275
	},
276
 
277
	// Private plugin internal methods
278
 
279
	/**
280
	 * Creates a HTML DIV element and sets the innerHTML to equal the temlate innerHTML so that the template can be manipulated as DOM nodes.
281
	 *
282
	 * @param {string} Template innerHTML
283
	 * @return a HTML Element
284
	 * @type HTMLElement
285
	 */
286
	_convertToNode : function(html) {
287
		var elm = document.createElement('div');
288
 
289
		elm.innerHTML = html;
290
 
291
		return elm;
292
	},
293
 
294
	/**
295
	 * pass an array of template html elements and they will have the template class name added and any template functions applied
296
	 *
297
	 * @param {array} template HTML elements
298
	 * @return array of template HTML elements
299
	 * @type array
300
	 */
301
	_prepareTemplateContent : function(elms) {
302
		var x, n, nodes = [];
303
 
304
		if (!elms)
305
			return {};
306
 
307
		if (!elms.length)
308
			elms = [elms];
309
 
310
		for (x = 0; x<elms.length; x++)
311
			tinyMCE.getNodeTree(elms[x], nodes, 1);
312
 
313
		for (n = 0; n<nodes.length; n++) {
314
			tinyMCE.addCSSClass(nodes[n], TinyMCE_TemplatePlugin.TMPL_ELEMENT);
315
			TinyMCE_TemplatePlugin._applyFunctions(nodes[n], TinyMCE_TemplatePlugin.TMPL_TEMPLATE_EVENT);
316
		}
317
 
318
		return elms;
319
	},
320
 
321
	_replaceValues : function(s) {
322
		var t = this, ds = tinyMCE.selectedInstance.getData('template');
323
 
324
		return s.replace(/\{\$([^\}]+)\}/g, function(a, b) {
325
			var it = ds.replace_items[b];
326
 
327
			if (it) {
328
				// Only supports text for now
329
				if (typeof(it) != 'function')
330
					return it;
331
			}
332
 
333
			return b;
334
		});
335
	},
336
 
337
	/**
338
	 * Applies any special functions to the template elements
339
	 *
340
	 * @param {array} template HTML elements
341
	 * @return array of template HTML elements
342
	 * @type array
343
	 */
344
	_applyFunctions : function(elms, editor_event) {
345
		var x, elm, names, c, f;
346
 
347
		if (!elms)
348
			return {};
349
 
350
		if (!elms.length)
351
			elms = [elms];
352
 
353
		for(x = 0; x < elms.length; x++) {
354
			elm = elms[x];
355
 
356
			if (elm.className){
357
				names = elm.className.split(/\s+/);
358
 
359
				for (c = 0; c < names.length; c++){
360
					if (names[c] == TinyMCE_TemplatePlugin.TMPL_ELEMENT)
361
						continue;
362
 
363
					f = (TinyMCE_TemplatePlugin.functions[names[c]] ? TinyMCE_TemplatePlugin.functions[names[c]] : TinyMCE_TemplatePlugin.functions['blank']);
364
					f(elm, editor_event);
365
				}
366
			}
367
		}
368
 
369
		return elms;
370
	},
371
 
372
	/**
373
	 * Given one node reference this function will collect all the nodes of the template to which it belongs.
374
	 * It does this by finding the parent template wrapper DIV and returning all child nodes.
375
	 *
376
	 * @param {HTMLElement} a HTMLElement which is part of a template
377
	 * @return array of template HTML elements
378
	 * @type array
379
	 */
380
	_collectTemplateElements : function(node) {
381
		var nodeArray = [], p;
382
 
383
		p = tinyMCE.getParentElement(node, 'DIV', function(n) {
384
			return tinyMCE.hasCSSClass(n, TinyMCE_TemplatePlugin.TMPL);
385
		});
386
 
387
		if (p)
388
			tinyMCE.getNodeTree(p, nodeArray);
389
 
390
		return nodeArray;
391
	},
392
 
393
	/**
394
	 * Simply calls TinyMCE_TemplatePlugin._deleteTemplateContent and then TinyMCE_TemplatePlugin._insertTemplate
395
	 *
396
	 * @param {HTMLElement} currently selected template node in editor
397
	 * @param {string} id of editor instance
398
	 * @param {HTMLElement} template contents as a HTMLElement (the parent DIV wrapper)
399
	 * @param {string} title of template (unused as yet)
400
	 * @param {string} source URI of the template file (unused as yet)
401
	 * @return array of template HTML elements
402
	 * @type array
403
	 */
404
	_replaceTemplateContent : function(currentNode, editor_id, newTemplate, title, tsrc) {
405
		TinyMCE_TemplatePlugin._deleteTemplateContent(currentNode);
406
		TinyMCE_TemplatePlugin._insertTemplate(editor_id, newTemplate, title, tsrc, false);
407
	},
408
 
409
	/**
410
	 * Deletes a template from the editor content
411
	 * Finds the parent DIV wrapper and deletes it and all children
412
	 * @param {HTMLElement} currently selected template node in editor
413
	 */
414
	_deleteTemplateContent : function(node) {
415
		var p = tinyMCE.getParentElement(node, 'DIV', function(n) {
416
			return tinyMCE.hasCSSClass(n, TinyMCE_TemplatePlugin.TMPL);
417
		});
418
 
419
		if (p)
420
			p.parentNode.removeChild(p, true);
421
	},
422
 
423
	/**
424
	 *  Inserts a template into the specified editor
425
	 *
426
	 * @param {string} id of editor instance
427
	 * @param {HTMLElement} template contents as a HTMLElement (the parent DIV wrapper)
428
	 * @param {string} title of template (unused as yet)
429
	 * @param {string} source URI of the template file (unused as yet)
430
	 */
431
	_insertTemplate : function(editor_id, elm, title, tsrc, incComments) {
432
		var html;
433
 
434
		TinyMCE_TemplatePlugin._prepareTemplateContent(elm);
435
 
436
		html = '<div class="' + TinyMCE_TemplatePlugin.TMPL + '">';
437
		html += elm.innerHTML;
438
		html += '<!-- ' + TinyMCE_TemplatePlugin.TMPL_ENDS + ' --></div>';
439
 
440
		tinyMCE.execInstanceCommand(editor_id, 'mceInsertContent', false, html);
441
	},
442
 
443
	/**
444
	 * template functions - functions for modifying template content
445
	 */
446
	functions : {
447
		blank : function(elm, editor_event) {},
448
 
449
		cdate : function(elm, editor_event) {
450
			var d, dsrc;
451
 
452
			if (editor_event != TinyMCE_TemplatePlugin.TMPL_TEMPLATE_EVENT)
453
				return;
454
 
455
			d = new Date();
456
			// find out if the creation date was previously stored
457
			dsrc = elm.innerHTML.match(new RegExp("<!-- " + TinyMCE_TemplatePlugin.TMPL_DATE_SRC_ATTR + ":(.*)  -->", "gi"));
458
 
459
			if (dsrc)
460
				d = new Date(RegExp.$1);
461
 
462
			elm.innerHTML = TinyMCE_TemplatePlugin._getDateTime(d, tinyMCE.getParam("template_cdate_format", tinyMCE.getLang("lang_template_def_date_format")));
463
			//now we have to store the date value in a format easily read again, in case a future template change changes the date format...
464
			elm.innerHTML += "<!-- " + TinyMCE_TemplatePlugin.TMPL_DATE_SRC_ATTR + ":" + d.toUTCString() + "  -->";
465
		},
466
 
467
		mdate : function(elm, editor_event) {
468
			var d = new Date();
469
			elm.innerHTML = TinyMCE_TemplatePlugin._getDateTime(d, tinyMCE.getParam("template_mdate_format", tinyMCE.getLang("lang_template_def_date_format")));
470
		},
471
 
472
		/**
473
		 * This will insert the currently selected editor content into the template element.
474
		 * It only does this if the template inserted is a new one and if the element does not have the special class.
475
		 * The special class name prevents this from happening more than once.
476
		 */
477
		selectedContent : function(elm, editor_event) {
478
			var ds = tinyMCE.selectedInstance.getData('template');
479
 
480
			if (editor_event != TinyMCE_TemplatePlugin.TMPL_TEMPLATE_EVENT)
481
				return;
482
 
483
			if (ds.currentAction == "insert-new" && !tinyMCE.hasCSSClass(elm, TinyMCE_TemplatePlugin.TMPL_SEL_HTML_DONE)) {
484
				elm.innerHTML = tinyMCE.selectedInstance.selection.getSelectedHTML();
485
				tinyMCE.addCSSClass(elm, TinyMCE_TemplatePlugin.TMPL_SEL_HTML_DONE);
486
			}
487
		},
488
 
489
		/**
490
		 * When the plugin is initialised this generates the functions that insert configured strings into template elements.
491
		 */
492
		generateReplacer : function(s) {
493
			return function(elm, editor_event) {elm.innerHTML = "" + s;};
494
		}
495
	},
496
 
497
	/**
498
	 * formats a date according to the format string - straight from the 'insert date/time' plugin
499
	 *
500
	 * @param {Date} date object
501
	 * @param {string} format string
502
	 * @return formatted date
503
	 * @type string
504
	 */
505
	_getDateTime : function(d,fmt) {
506
			if (!fmt)
507
				return "";
508
 
509
			function addZeros(value, len) {
510
				var i;
511
 
512
				value = "" + value;
513
 
514
				if (value.length < len) {
515
					for (i=0; i<(len-value.length); i++)
516
						value = "0" + value;
517
				}
518
 
519
				return value;
520
			}
521
 
522
			fmt = fmt.replace("%D", "%m/%d/%y");
523
			fmt = fmt.replace("%r", "%I:%M:%S %p");
524
			fmt = fmt.replace("%Y", "" + d.getFullYear());
525
			fmt = fmt.replace("%y", "" + d.getYear());
526
			fmt = fmt.replace("%m", addZeros(d.getMonth()+1, 2));
527
			fmt = fmt.replace("%d", addZeros(d.getDate(), 2));
528
			fmt = fmt.replace("%H", "" + addZeros(d.getHours(), 2));
529
			fmt = fmt.replace("%M", "" + addZeros(d.getMinutes(), 2));
530
			fmt = fmt.replace("%S", "" + addZeros(d.getSeconds(), 2));
531
			fmt = fmt.replace("%I", "" + ((d.getHours() + 11) % 12 + 1));
532
			fmt = fmt.replace("%p", "" + (d.getHours() < 12 ? "AM" : "PM"));
533
			fmt = fmt.replace("%B", "" + tinyMCE.getLang("lang_template_months_long")[d.getMonth()]);
534
			fmt = fmt.replace("%b", "" + tinyMCE.getLang("lang_template_months_short")[d.getMonth()]);
535
			fmt = fmt.replace("%A", "" + tinyMCE.getLang("lang_template_day_long")[d.getDay()]);
536
			fmt = fmt.replace("%a", "" + tinyMCE.getLang("lang_template_day_short")[d.getDay()]);
537
			fmt = fmt.replace("%%", "%");
538
 
539
			return fmt;
540
	},
541
 
542
	TMPL_ELEMENT : 'mceTmplElm',
543
	TMPL : 'mceTmpl',
544
	TMPL_BEGINS : 'mceTmplBegins',
545
	TMPL_SEL_HTML_DONE : 'mceSelHTMLDone',
546
	TMPL_ENDS : 'mceTmplEnds',
547
	TMPL_DATE_SRC_ATTR : 'mcetmpldtesrc',
548
	TMPL_TEMPLATE_EVENT : 'prepare_template'
549
};
550
 
551
tinyMCE.addPlugin("template", TinyMCE_TemplatePlugin);