Subversion Repositories Applications.papyrus

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2150 mathias 1
if(!dojo._hasResource["dojox.dtl.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2
dojo._hasResource["dojox.dtl.html"] = true;
3
dojo.provide("dojox.dtl.html");
4
 
5
dojo.require("dojox.dtl._base");
6
 
7
dojox.dtl.ObjectMap = function(){
8
	this.contents = [];
9
}
10
dojo.extend(dojox.dtl.ObjectMap, {
11
	get: function(key){
12
		var contents = this.contents;
13
		for(var i = 0, content; content = contents[i]; i++){
14
			if(content[0] === key){
15
				return content[1];
16
			}
17
		}
18
	},
19
	put: function(key, value){
20
		var contents = this.contents;
21
		for(var i = 0, content; content = contents[i]; i++){
22
			if(content[0] === key){
23
				if(arguments.length == 1){
24
					contents.splice(i, 1);
25
					return;
26
				}
27
				content[1] = value;
28
				return;
29
			}
30
		}
31
		contents.push([key, value]);
32
	},
33
	toString: function(){ return "dojox.dtl.ObjectMap"; }
34
});
35
 
36
dojox.dtl.html = {
37
	types: dojo.mixin({change: -11, attr: -12, elem: 1, text: 3}, dojox.dtl.text.types),
38
	_attributes: {},
39
	_re: /(^\s+|\s+$)/g,
40
	_re2: /\b([a-zA-Z]+)="/g,
41
	_re3: /<!--({({|%).*?(%|})})-->/g,
42
	_re4: /^function anonymous\(\)\s*{\s*(.*)\s*}$/,
43
	_trim: function(/*String*/ str){
44
		return str.replace(this._re, "");
45
	},
46
	getTemplate: function(text){
47
		if(typeof this._commentable == "undefined"){
48
			// Check to see if the browser can handle comments
49
			this._commentable = false;
50
			var div = document.createElement("div");
51
			div.innerHTML = "<!--Test comment handling, and long comments, using comments whenever possible.-->";
52
			if(div.childNodes.length && div.childNodes[0].nodeType == 8 && div.childNodes[0].data == "comment"){
53
				this._commentable = true;
54
			}
55
		}
56
 
57
		if(!this._commentable){
58
			// Strip comments
59
			text = text.replace(this._re3, "$1");
60
		}
61
 
62
		var match;
63
		while(match = this._re2.exec(text)){
64
			this._attributes[match[1]] = true;
65
		}
66
		var div = document.createElement("div");
67
		div.innerHTML = text;
68
		var output = { pres: [], posts: []}
69
		while(div.childNodes.length){
70
			if(!output.node && div.childNodes[0].nodeType == 1){
71
				output.node = div.removeChild(div.childNodes[0]);
72
			}else if(!output.node){
73
				output.pres.push(div.removeChild(div.childNodes[0]));
74
			}else{
75
				output.posts.push(div.removeChild(div.childNodes[0]));
76
			}
77
		}
78
 
79
		if(!output.node){
80
			throw new Error("Template did not provide any content");
81
		}
82
 
83
		return output;
84
	},
85
	tokenize: function(/*Node*/ node, /*Array?*/ tokens, /*Array?*/ preNodes, /*Array?*/ postNodes){
86
		tokens = tokens || [];
87
		var first = !tokens.length;
88
		var types = this.types;
89
 
90
		var children = [];
91
		for(var i = 0, child; child = node.childNodes[i]; i++){
92
			children.push(child);
93
		}
94
 
95
		if(preNodes){
96
			for(var i = 0, child; child = preNodes[i]; i++){
97
				this._tokenize(node, child, tokens);
98
			}
99
		}
100
 
101
		tokens.push([types.elem, node]);
102
		tokens.push([types.change, node]);
103
 
104
		for(var key in this._attributes){
105
			var value = "";
106
			if(key == "class"){
107
				value = node.className || value;
108
			}else if(key == "for"){
109
				value = node.htmlFor || value;
110
			}else if(node.getAttribute){
111
				value = node.getAttribute(key, 2) || value;
112
				if(key == "href" || key == "src"){
113
					if(dojo.isIE){
114
						var hash = location.href.lastIndexOf(location.hash);
115
						var href = location.href.substring(0, hash).split("/");
116
						href.pop();
117
						href = href.join("/") + "/";
118
						if(value.indexOf(href) == 0){
119
							value = value.replace(href, "");
120
						}
121
						value = value.replace(/%20/g, " ").replace(/%7B/g, "{").replace(/%7D/g, "}").replace(/%25/g, "%");
122
					}
123
					if(value.indexOf("{%") != -1 || value.indexOf("{{") != -1){
124
						node.setAttribute(key, "");
125
					}
126
				}
127
			}
128
			if(typeof value == "function"){
129
				value = value.toString().replace(this._re4, "$1");
130
			}
131
			if(typeof value == "string" && (value.indexOf("{%") != -1 || value.indexOf("{{") != -1 || (value && dojox.dtl.text.getTag("attr:" + key, true)))){
132
				tokens.push([types.attr, node, key, value]);
133
			}
134
		}
135
 
136
		if(!children.length){
137
			tokens.push([types.change, node.parentNode, true]);
138
			if(postNodes){
139
				for(var i = 0, child; child = postNodes[i]; i++){
140
					this._tokenize(node, child, tokens);
141
				}
142
			}
143
			return tokens;
144
		}
145
 
146
		for(var i = 0, child; child = children[i]; i++){
147
			this._tokenize(node, child, tokens);
148
		}
149
 
150
		if(node.parentNode && node.parentNode.tagName){
151
			tokens.push([types.change, node.parentNode, true]);
152
			node.parentNode.removeChild(node);
153
		}
154
 
155
		if(postNodes){
156
			for(var i = 0, child; child = postNodes[i]; i++){
157
				this._tokenize(node, child, tokens);
158
			}
159
		}
160
 
161
		if(first){
162
			tokens.push([types.change, node, true]);
163
		}
164
 
165
		return tokens;
166
	},
167
	_tokenize: function(parent, child, tokens){
168
		var types = this.types;
169
		var data = child.data;
170
		switch(child.nodeType){
171
			case 1:
172
				this.tokenize(child, tokens);
173
				break;
174
			case 3:
175
				if(data.match(/[^\s\n]/)){
176
					if(data.indexOf("{{") != -1 || data.indexOf("{%") != -1){
177
						var texts = dojox.dtl.text.tokenize(data);
178
						for(var j = 0, text; text = texts[j]; j++){
179
							if(typeof text == "string"){
180
								tokens.push([types.text, text]);
181
							}else{
182
								tokens.push(text);
183
							}
184
						}
185
					}else{
186
						tokens.push([child.nodeType, child]);
187
					}
188
				}
189
				if(child.parentNode) child.parentNode.removeChild(child);
190
				break;
191
			case 8:
192
				if(data.indexOf("{%") == 0){
193
					tokens.push([types.tag, this._trim(data.substring(2, data.length - 3))]);
194
				}
195
				if(data.indexOf("{{") == 0){
196
					tokens.push([types.varr, this._trim(data.substring(2, data.length - 3))]);
197
				}
198
				if(child.parentNode) child.parentNode.removeChild(child);
199
				break;
200
		}
201
	}
202
}
203
 
204
dojox.dtl.HtmlTemplate = function(/*String|dojo._Url*/ obj){
205
	// summary: Use this object for HTML templating
206
	var dd = dojox.dtl;
207
	var ddh = dd.html;
208
 
209
	if(!obj.node){
210
		if(typeof obj == "object"){
211
			obj = dojox.dtl.text.getTemplateString(obj);
212
		}
213
		obj = ddh.getTemplate(obj);
214
	}
215
 
216
	var tokens = ddh.tokenize(obj.node, [], obj.pres, obj.posts);
217
	var parser = new dd.HtmlParser(tokens);
218
	this.nodelist = parser.parse();
219
}
220
dojo.extend(dojox.dtl.HtmlTemplate, {
221
	_count: 0,
222
	_re: /\bdojo:([a-zA-Z0-9_]+)\b/g,
223
	setClass: function(str){
224
		this.getRootNode().className = str;
225
	},
226
	getRootNode: function(){
227
		return this.rootNode;
228
	},
229
	getBuffer: function(){
230
		return new dojox.dtl.HtmlBuffer();
231
	},
232
	render: function(context, buffer){
233
		buffer = buffer || this.getBuffer();
234
		this.rootNode = null;
235
		var onSetParent = dojo.connect(buffer, "onSetParent", this, function(node){
236
			if(!this.rootNode){
237
				this.rootNode = node || true;
238
			}
239
		});
240
		var output = this.nodelist.render(context || new dojox.dtl.Context({}), buffer);
241
		dojo.disconnect(onSetParent);
242
		buffer._flushCache();
243
		return output;
244
	},
245
	unrender: function(context, buffer){
246
		return this.nodelist.unrender(context, buffer);
247
	},
248
	toString: function(){ return "dojox.dtl.HtmlTemplate"; }
249
});
250
 
251
dojox.dtl.HtmlBuffer = function(/*Node*/ parent){
252
	// summary: Allows the manipulation of DOM
253
	// description:
254
	//		Use this to append a child, change the parent, or
255
	//		change the attribute of the current node.
256
	this._parent = parent;
257
	this._cache = [];
258
}
259
dojo.extend(dojox.dtl.HtmlBuffer, {
260
	concat: function(/*DOMNode*/ node){
261
		if(!this._parent) return this;
262
		if(node.nodeType){
263
			var caches = this._getCache(this._parent);
264
			if(node.parentNode === this._parent){
265
				// If we reach a node that already existed, fill in the cache for this same parent
266
				var i = 0;
267
				for(var i = 0, cache; cache = caches[i]; i++){
268
					this.onAddNode(node);
269
					this._parent.insertBefore(cache, node);
270
				}
271
				caches.length = 0;
272
			}
273
			if(!node.parentNode || !node.parentNode.tagName){
274
				if(!this._parent.childNodes.length){
275
					this.onAddNode(node);
276
					this._parent.appendChild(node);
277
				}else{
278
					caches.push(node);
279
				}
280
			}
281
		}
282
		return this;
283
	},
284
	remove: function(obj){
285
		if(typeof obj == "string"){
286
			this._parent.removeAttribute(obj);
287
		}else{
288
			if(obj.parentNode === this._parent){
289
				this.onRemoveNode();
290
				this._parent.removeChild(obj);
291
			}
292
		}
293
		return this;
294
	},
295
	setAttribute: function(key, value){
296
		if(key == "class"){
297
			this._parent.className = value;
298
		}else if(key == "for"){
299
			this._parent.htmlFor = value;
300
		}else if(this._parent.setAttribute){
301
			this._parent.setAttribute(key, value);
302
		}
303
		return this;
304
	},
305
	setParent: function(node, /*Boolean?*/ up){
306
		if(!this._parent) this._parent = node;
307
		var caches = this._getCache(this._parent);
308
		if(caches && caches.length && up){
309
			for(var i = 0, cache; cache = caches[i]; i++){
310
				if(cache !== this._parent && (!cache.parentNode || !cache.parentNode.tagName)){
311
					this.onAddNode(cache);
312
					this._parent.appendChild(cache);
313
				}
314
			}
315
			caches.length = 0;
316
		}
317
 
318
		this.onSetParent(node, up);
319
		this._parent = node;
320
		return this;
321
	},
322
	getParent: function(){
323
		return this._parent;
324
	},
325
	onSetParent: function(){
326
		// summary: Stub called when setParent is used.
327
	},
328
	onAddNode: function(){
329
		// summary: Stub called when new nodes are added
330
	},
331
	onRemoveNode: function(){
332
		// summary: Stub called when nodes are removed
333
	},
334
	_getCache: function(node){
335
		for(var i = 0, cache; cache = this._cache[i]; i++){
336
			if(cache[0] === node){
337
				return cache[1];
338
			}
339
		}
340
		var arr = [];
341
		this._cache.push([node, arr]);
342
		return arr;
343
	},
344
	_flushCache: function(node){
345
		for(var i = 0, cache; cache = this._cache[i]; i++){
346
			if(!cache[1].length){
347
				this._cache.splice(i--, 1);
348
			}
349
		}
350
	},
351
	toString: function(){ return "dojox.dtl.HtmlBuffer"; }
352
});
353
 
354
dojox.dtl.HtmlNode = function(node){
355
	// summary: Places a node into DOM
356
	this.contents = node;
357
}
358
dojo.extend(dojox.dtl.HtmlNode, {
359
	render: function(context, buffer){
360
		return buffer.concat(this.contents);
361
	},
362
	unrender: function(context, buffer){
363
		return buffer.remove(this.contents);
364
	},
365
	clone: function(buffer){
366
		return new dojox.dtl.HtmlNode(this.contents);
367
	},
368
	toString: function(){ return "dojox.dtl.HtmlNode"; }
369
});
370
 
371
dojox.dtl.HtmlNodeList = function(/*Node[]*/ nodes){
372
	// summary: A list of any HTML-specific node object
373
	// description:
374
	//		Any object that's used in the constructor or added
375
	//		through the push function much implement the
376
	//		render, unrender, and clone functions.
377
	this.contents = nodes || [];
378
}
379
dojo.extend(dojox.dtl.HtmlNodeList, {
380
	parents: new dojox.dtl.ObjectMap(),
381
	push: function(node){
382
		this.contents.push(node);
383
	},
384
	unshift: function(node){
385
		this.contents.unshift(node);
386
	},
387
	render: function(context, buffer, /*Node*/ instance){
388
		if(instance){
389
			var parent = buffer.getParent();
390
		}
391
		for(var i = 0; i < this.contents.length; i++){
392
			buffer = this.contents[i].render(context, buffer);
393
			if(!buffer) throw new Error("Template node render functions must return their buffer");
394
		}
395
		if(parent){
396
			buffer.setParent(parent, true);
397
		}
398
		return buffer;
399
	},
400
	unrender: function(context, buffer){
401
		for(var i = 0; i < this.contents.length; i++){
402
			buffer = this.contents[i].unrender(context, buffer);
403
			if(!buffer) throw new Error("Template node render functions must return their buffer");
404
		}
405
		return buffer;
406
	},
407
	clone: function(buffer){
408
		// summary:
409
		//		Used to create an identical copy of a NodeList, useful for things like the for tag.
410
		var dd = dojox.dtl;
411
		var ddh = dd.html;
412
		var parent = buffer.getParent();
413
		var contents = this.contents;
414
		var nodelist = new dd.HtmlNodeList();
415
		var cloned = [];
416
		for(var i = 0; i < contents.length; i++){
417
			var clone = contents[i].clone(buffer);
418
			if(clone instanceof dd.ChangeNode || clone instanceof dd.HtmlNode){
419
				var item = this.parents.get(clone.contents);
420
				if(item){
421
					clone.contents = item;
422
				}else if(parent !== clone.contents && clone instanceof dd.HtmlNode){
423
					var node = clone.contents;
424
					clone.contents = clone.contents.cloneNode(false);
425
					cloned.push(node);
426
					this.parents.put(node, clone.contents);
427
				}
428
			}
429
			nodelist.push(clone);
430
		}
431
 
432
		for(var i = 0, clone; clone = cloned[i]; i++){
433
			this.parents.put(clone);
434
		}
435
 
436
		return nodelist;
437
	},
438
	toString: function(){ return "dojox.dtl.HtmlNodeList"; }
439
});
440
 
441
dojox.dtl.HtmlVarNode = function(str){
442
	// summary: A node to be processed as a variable
443
	// description:
444
	//		Will render an object that supports the render function
445
	// 		and the getRootNode function
446
	this.contents = new dojox.dtl.Filter(str);
447
	this._lists = {};
448
}
449
dojo.extend(dojox.dtl.HtmlVarNode, {
450
	render: function(context, buffer){
451
		this._rendered = true;
452
		var dd = dojox.dtl;
453
		var ddh = dd.html;
454
		var str = this.contents.resolve(context);
455
		if(str && str.render && str.getRootNode){
456
			var root = this._curr = str.getRootNode();
457
			var lists = this._lists;
458
			var list = lists[root];
459
			if(!list){
460
				list = lists[root] = new dd.HtmlNodeList();
461
				list.push(new dd.ChangeNode(buffer.getParent()));
462
				list.push(new dd.HtmlNode(root));
463
				list.push(str);
464
				list.push(new dd.ChangeNode(buffer.getParent(), true));
465
			}
466
			return list.render(context, buffer);
467
		}else{
468
			if(!this._txt) this._txt = document.createTextNode(str);
469
			if(this._txt.data != str) this._txt.data = str;
470
			return buffer.concat(this._txt);
471
		}
472
		return buffer;
473
	},
474
	unrender: function(context, buffer){
475
		if(this._rendered){
476
			this._rendered = false;
477
			if(this._curr){
478
				return this._lists[this._curr].unrender(context, buffer);
479
			}else if(this._txt){
480
				return buffer.remove(this._txt);
481
			}
482
		}
483
		return buffer;
484
	},
485
	clone: function(){
486
		return new dojox.dtl.HtmlVarNode(this.contents.contents);
487
	},
488
	toString: function(){ return "dojox.dtl.HtmlVarNode"; }
489
});
490
 
491
dojox.dtl.ChangeNode = function(node, /*Boolean?*/ up){
492
	// summary: Changes the parent during render/unrender
493
	this.contents = node;
494
	this._up = up;
495
}
496
dojo.extend(dojox.dtl.ChangeNode, {
497
	render: function(context, buffer){
498
		return buffer.setParent(this.contents, this._up);
499
	},
500
	unrender: function(context, buffer){
501
		return buffer.setParent(this.contents);
502
	},
503
	clone: function(buffer){
504
		return new dojox.dtl.ChangeNode(this.contents, this._up);
505
	},
506
	toString: function(){ return "dojox.dtl.ChangeNode"; }
507
});
508
 
509
dojox.dtl.AttributeNode = function(key, value){
510
	// summary: Works on attributes
511
	this._key = key;
512
	this._value = value;
513
	this._tpl = new dojox.dtl.Template(value);
514
	this.contents = "";
515
}
516
dojo.extend(dojox.dtl.AttributeNode, {
517
	render: function(context, buffer){
518
		var key = this._key;
519
		var value = this._tpl.render(context);
520
		if(this._rendered){
521
			if(value != this.contents){
522
				this.contents = value;
523
				return buffer.setAttribute(key, value);
524
			}
525
		}else{
526
			this._rendered = true;
527
			this.contents = value;
528
			return buffer.setAttribute(key, value);
529
		}
530
		return buffer;
531
	},
532
	unrender: function(context, buffer){
533
		if(this._rendered){
534
			this._rendered = false;
535
			this.contents = "";
536
			return buffer.remove(this.contents);
537
		}
538
		return buffer;
539
	},
540
	clone: function(){
541
		return new dojox.dtl.AttributeNode(this._key, this._value);
542
	},
543
	toString: function(){ return "dojox.dtl.AttributeNode"; }
544
});
545
 
546
dojox.dtl.HtmlTextNode = function(str){
547
	// summary: Adds a straight text node without any processing
548
	this.contents = document.createTextNode(str);
549
}
550
dojo.extend(dojox.dtl.HtmlTextNode, {
551
	render: function(context, buffer){
552
		return buffer.concat(this.contents);
553
	},
554
	unrender: function(context, buffer){
555
		return buffer.remove(this.contents);
556
	},
557
	clone: function(){
558
		return new dojox.dtl.HtmlTextNode(this.contents.data);
559
	},
560
	toString: function(){ return "dojox.dtl.HtmlTextNode"; }
561
});
562
 
563
dojox.dtl.HtmlParser = function(tokens){
564
	// summary: Turn a simple array into a set of objects
565
	// description:
566
	//	This is also used by all tags to move through
567
	//	the list of nodes.
568
	this.contents = tokens;
569
}
570
dojo.extend(dojox.dtl.HtmlParser, {
571
	parse: function(/*Array?*/ stop_at){
572
		var dd = dojox.dtl;
573
		var ddh = dd.html;
574
		var types = ddh.types;
575
		var terminators = {};
576
		var tokens = this.contents;
577
		if(!stop_at){
578
			stop_at = [];
579
		}
580
		for(var i = 0; i < stop_at.length; i++){
581
			terminators[stop_at[i]] = true;
582
		}
583
		var nodelist = new dd.HtmlNodeList();
584
		while(tokens.length){
585
			var token = tokens.shift();
586
			var type = token[0];
587
			var value = token[1];
588
			if(type == types.change){
589
				nodelist.push(new dd.ChangeNode(value, token[2]));
590
			}else if(type == types.attr){
591
				var fn = dojox.dtl.text.getTag("attr:" + token[2], true);
592
				if(fn){
593
					nodelist.push(fn(null, token[2] + " " + token[3]));
594
				}else{
595
					nodelist.push(new dd.AttributeNode(token[2], token[3]));
596
				}
597
			}else if(type == types.elem){
598
				var fn = dojox.dtl.text.getTag("node:" + value.tagName.toLowerCase(), true);
599
				if(fn){
600
					// TODO: We need to move this to tokenization so that it's before the
601
					// 				node and the parser can be passed here instead of null
602
					nodelist.push(fn(null, value, value.tagName.toLowerCase()));
603
				}
604
				nodelist.push(new dd.HtmlNode(value));
605
			}else if(type == types.varr){
606
				nodelist.push(new dd.HtmlVarNode(value));
607
			}else if(type == types.text){
608
				nodelist.push(new dd.HtmlTextNode(value.data || value));
609
			}else if(type == types.tag){
610
				if(terminators[value]){
611
					tokens.unshift(token);
612
					return nodelist;
613
				}
614
				var cmd = value.split(/\s+/g);
615
				if(cmd.length){
616
					cmd = cmd[0];
617
					var fn = dojox.dtl.text.getTag(cmd);
618
					if(typeof fn != "function"){
619
						throw new Error("Function not found for ", cmd);
620
					}
621
					var tpl = fn(this, value);
622
					if(tpl){
623
						nodelist.push(tpl);
624
					}
625
				}
626
			}
627
		}
628
 
629
		if(stop_at.length){
630
			throw new Error("Could not find closing tag(s): " + stop_at.toString());
631
		}
632
 
633
		return nodelist;
634
	},
635
	next: function(){
636
		// summary: Used by tags to discover what token was found
637
		var token = this.contents.shift();
638
		return {type: token[0], text: token[1]};
639
	},
640
	skipPast: function(endtag){
641
		return dojox.dtl.Parser.prototype.skipPast.call(this, endtag);
642
	},
643
	getVarNode: function(){
644
		return dojox.dtl.HtmlVarNode;
645
	},
646
	getTextNode: function(){
647
		return dojox.dtl.HtmlTextNode;
648
	},
649
	getTemplate: function(/*String*/ loc){
650
		return new dojox.dtl.HtmlTemplate(dojox.dtl.html.getTemplate(loc));
651
	},
652
	toString: function(){ return "dojox.dtl.HtmlParser"; }
653
});
654
 
655
dojox.dtl.register.tag("dojox.dtl.tag.event", "dojox.dtl.tag.event", [[/(attr:)?on(click|key(up))/i, "on"]]);
656
dojox.dtl.register.tag("dojox.dtl.tag.html", "dojox.dtl.tag.html", ["html", "attr:attach", "attr:tstyle"]);
657
 
658
}