1 /**
  2  * This file is part of the smilText parser implemented in JavaScript,
  3  *
  4  * Copyright (C) 2003-2009 Stichting CWI, 
  5  * Science Park 123, 1098 XG Amsterdam, The Netherlands.
  6  *
  7  * smilText parser in JavaScript is free software; you can redistribute it and/or modify
  8  * it under the terms of the GNU Lesser General Public License as published by
  9  * the Free Software Foundation; either version 2.1 of the License, or
 10  * (at your option) any later version.
 11  *
 12  * smilText parser in JavaScript is distributed in the hope that it will be useful,
 13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 15  * GNU Lesser General Public License for more details.
 16  *
 17  * You should have received a copy of the GNU Lesser General Public License
 18  * along with smilText parser in JavaScript; if not, write to the Free Software
 19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 20  */
 21 
 22 /**
 23  @name cwi.smilText
 24  @namespace Hold all smilText functionalities.
 25  @version 1.0
 26  @author <a href="mailto:rlaiola@cwi.nl">Rodrigo Laiola Guimaraes</a>
 27 */
 28 Namespace("cwi.smilText");
 29 
 30 
 31 
 32 
 33 
 34 /* Solve library dependencies */
 35 Import("cwi.adt.Hashtable");
 36 Import("cwi.adt.PriorityQueue");
 37 Import("cwi.adt.DoubleLinkedList");
 38 Import("cwi.util");
 39 Import("cwi.smilText.Time.Playable");
 40 
 41 /**
 42  * Implementation of a SmilText Document.
 43  * @constructor
 44  * @augments cwi.smilText.Time.Playable
 45  * @param {string} region the id of the region where the smilText document will be rendered.
 46  * @author <a href="mailto:rlaiola@cwi.nl">Rodrigo Laiola Guimaraes</a>
 47  */
 48 cwi.smilText.STDocument = function(region)
 49 {
 50 	var self = JSINER.extend(this, "Playable");
 51 	
 52 	/** 
 53 	* Variables
 54 	* @private
 55 	*/
 56 	this.layout = region;					// Hold the rendering region
 57 	this.stylingHash = new Hashtable();		// Hold styling attributes
 58 	
 59 	this.translateFunc = null;				// Keep translation function.
 60 	this.dictionary = null;					// Keep translated sentences.
 61 	
 62 	this.hasFillSemantics = true;			// Keep the Fill semantics associated to the display of text when duration ends.
 63 	
 64 	this.timingQueue = new PriorityQueue();	// Keep a priority queue of timing occurrencies.
 65 	this.doneQueue = new PriorityQueue();	// Keep a priority queue of occurred statements.
 66 	
 67 	return self;
 68 }
 69 
 70 /**
 71  * Return the value of a stored style attribute.
 72  * @param {string} layout the region id
 73  * @param {string} attr the attribute name
 74  * @return {string} val the attribute value
 75  */
 76 cwi.smilText.STDocument.prototype.getStoredAttribute = function(layout, attr)
 77 {
 78 	return this.stylingHash.get(layout + attr);
 79 }
 80 
 81 /**
 82  * Save a style attribute of a given layout element.
 83  * @param {string} layout the region id
 84  * @param {string} attr the attribute name
 85  * @param {string} val the attribute value
 86  */
 87 cwi.smilText.STDocument.prototype.updateAttribute = function(layout, attr, val)
 88 {
 89 	this.stylingHash.put(layout + attr, val);
 90 }
 91 
 92 /**
 93  * Transfer the properties from a given style to a layout.
 94  * @private
 95  */
 96 cwi.smilText.STDocument.prototype.applyStyle = function(typeLayout, id, style)
 97 {
 98 	switch(typeLayout)
 99 	{
100 		case 'smiltext':
101 			this.updateAttribute(id, 'dur', this.getStoredAttribute(style, 'dur'));
102 			this.updateAttribute(id, 'width', this.getStoredAttribute(style, 'width'));
103 			this.updateAttribute(id, 'height', this.getStoredAttribute(style, 'height'));
104 		case 'textstyle':
105 			this.updateAttribute(id, 'textmode', this.getStoredAttribute(style, 'textmode'));
106 			this.updateAttribute(id, 'textplace', this.getStoredAttribute(style, 'textplace'));
107 			this.updateAttribute(id, 'textconceal', this.getStoredAttribute(style, 'textconceal'));
108 			this.updateAttribute(id, 'textrate', this.getStoredAttribute(style, 'textrate'));
109 		case 'div':
110 			this.updateAttribute(id, 'textalign', this.getStoredAttribute(style, 'textalign'));
111 		case 'p':
112 			this.updateAttribute(id, 'xml:space', this.getStoredAttribute(style, 'xml:space'));
113 			this.updateAttribute(id, 'textwrapoption', this.getStoredAttribute(style, 'textwrapoption'));
114 			this.updateAttribute(id, 'textwritingmode', this.getStoredAttribute(style, 'textwritingmode'));
115 		case 'span':
116 			this.updateAttribute(id, 'textbackgroundcolor', this.getStoredAttribute(style, 'textbackgroundcolor'));
117 			this.updateAttribute(id, 'textcolor', this.getStoredAttribute(style, 'textcolor'));
118 			this.updateAttribute(id, 'textfontfamily', this.getStoredAttribute(style, 'textfontfamily'));
119 			this.updateAttribute(id, 'textfontsize', this.getStoredAttribute(style, 'textfontsize'));
120 			this.updateAttribute(id, 'textfontstyle', this.getStoredAttribute(style, 'textfontstyle'));
121 			this.updateAttribute(id, 'textfontweight', this.getStoredAttribute(style, 'textfontweight'));
122 			this.updateAttribute(id, 'textstyle', this.getStoredAttribute(style, 'textstyle'));
123 
124 			if (typeLayout != 'span' && typeLayout != 'textstyle')
125 				this.updateAttribute(id, 'textdirection', this.getStoredAttribute(style, 'textdirection'));
126 	}
127 }
128 
129 /**
130  * Return the style properties of a given layout (xml).
131  * @private
132  */
133 cwi.smilText.STDocument.prototype.getStyleXML = function(typeLayout, id)
134 {
135 	var str = "";
136 	
137 	switch(typeLayout)
138 	{
139 		case 'smiltext':
140 			if (this.getStoredAttribute(id, 'dur'))
141 				str += " dur=\'" + this.getStoredAttribute(id, 'dur') + "\'";
142 			if (this.getStoredAttribute(id, 'width'))
143 				str += " width=\'" + this.getStoredAttribute(id, 'width') + "px\'";
144 			if (this.getStoredAttribute(id, 'height'))
145 				str += " height=\'" + this.getStoredAttribute(id, 'height') + "px\'";
146 		case 'textstyle':
147 			if (this.getStoredAttribute(id, 'textmode'))
148 				str += " textMode=\'" + this.getStoredAttribute(id, 'textmode') + "\'";
149 			if (this.getStoredAttribute(id, 'textplace'))
150 				str += " textPlace=\'" + this.getStoredAttribute(id, 'textplace') + "\'";
151 			if (this.getStoredAttribute(id, 'textconceal'))
152 				str += " textConceal=\'" + this.getStoredAttribute(id, 'textconceal') + "\'";
153 			if (this.getStoredAttribute(id, 'textrate'))
154 				str += " textRate=\'" + this.getStoredAttribute(id, 'textrate') + "\'";
155 		case 'div':
156 			if (this.getStoredAttribute(id, 'textalign'))
157 				str += " textAlign=\'" + this.getStoredAttribute(id, 'textalign') + "\'";
158 		case 'p':
159 			if (this.getStoredAttribute(id, 'xml:space'))
160 				str += " xml:space=\'" + this.getStoredAttribute(id, 'xml:space') + "\'";
161 			if (this.getStoredAttribute(id, 'textwrapoption'))
162 				str += " textWrapOption=\'" + this.getStoredAttribute(id, 'textwrapoption') + "\'";
163 			if (this.getStoredAttribute(id, 'textwritingmode'))
164 				str += " textWritingMode=\'" + this.getStoredAttribute(id, 'textwritingmode') + "\'";
165 		case 'span':
166 			if (this.getStoredAttribute(id, 'textbackgroundcolor'))
167 				str += " textBackgroundColor=\'" + this.getStoredAttribute(id, 'textbackgroundcolor') + "\'";
168 			if (this.getStoredAttribute(id, 'textcolor'))
169 				str += " textColor=\'" + this.getStoredAttribute(id, 'textcolor') + "\'";
170 			if (this.getStoredAttribute(id, 'textfontfamily'))
171 				str += " textFontFamily=\'" + this.getStoredAttribute(id, 'textfontfamily') + "\'";
172 			if (this.getStoredAttribute(id, 'textfontsize'))
173 				str += " textFontSize=\'" + this.getStoredAttribute(id, 'textfontsize') + "\'";
174 			if (this.getStoredAttribute(id, 'textfontstyle'))
175 				str += " textFontStyle=\'" + this.getStoredAttribute(id, 'textfontstyle') + "\'";
176 			if (this.getStoredAttribute(id, 'textfontweight'))
177 				str += " textFontWeight=\'" + this.getStoredAttribute(id, 'textfontweight') + "\'";
178 			if (this.getStoredAttribute(id, 'textstyle'))
179 				str += " textStyle=\'" + this.getStoredAttribute(id, 'textstyle') + "\'";
180 
181 			if (typeLayout != 'span' && typeLayout != 'textstyle') {
182 				if (this.getStoredAttribute(id, 'textdirection'))
183 					str += " textDirection=\'" + this.getStoredAttribute(id, 'textdirection') + "\'";
184 			}
185 	}
186 	
187 	return str;
188 }
189 
190 /**
191 * Return the string representation of the smilText document.
192 * @return {string}
193 */
194 cwi.smilText.STDocument.prototype.getXML = function()
195 {
196 	var header = "<smilText>";
197 	var str = "";
198 	var endTag = "\n</smilText>";
199 	var smilTextFound = false;
200 	var lastType = -1;	// 0: tag; 1: text
201 	var currentTime = 0;
202 	var restorePlay = false;
203 	var dur = null; // explicit duration of the smiltext container
204 	
205 	if (this.isPlaying()) {
206 		this.pause();
207 		restorePlay = true;
208 	}
209 	
210 	var tempQ = new PriorityQueue();
211 	while (this.doneQueue && !this.doneQueue.isEmpty()) 
212 	{
213 		var t = this.doneQueue.lookFirst();
214 		var st = this.doneQueue.pop();
215 
216 		switch(st[0]) 
217 		{
218 			case cwi.smilText.Render.appendContainer:
219 				var id = st[1][1].charAt(0);
220 			 	if (st[1][1].length > 8 && st[1][1].substring(0,8) == 'smiltext') {
221 					id = 'smiltext';
222 					smilTextFound = true;
223 					if (st[1][1] != '')
224 						header = "<smilText id=\'" + st[1][1] + "\' " + this.getStyleXML("smiltext", st[1][1]) + " >";
225 			 	}
226 								
227 				//if (smilTextFound) {	
228 				 	switch(id)
229 				 	{
230 				 		case 'smiltext':  // smiltext tag
231 				 			//str += "<smilText ";
232 				 			//endTag = "\n</smilText>" + endTag;
233 				 			break;
234 				 		case 'd': // div 
235 				 			str += "\n<div ";
236 				 			str += "id=\'" + st[1][1] + "\' " + this.getStyleXML("div", st[1][1]) + " >";
237 				 			endTag = "\n</div>" + endTag;
238 				 			break;
239 				 		case 'p': // p
240 				 			str += "\n<p ";
241 				 			str += "id=\'" + st[1][1] + "\' " + this.getStyleXML("p", st[1][1]) + " >";
242 				 			endTag = "\n</p>" + endTag;
243 				 			break;
244 				 		case 's': // span
245 				 			str += "\n<span ";
246 				 			str += "id=\'" + st[1][1] + "\' " + this.getStyleXML("span", st[1][1]) + " >";
247 				 			endTag = "\n</span>" + endTag;
248 				 			break;
249 				 //	}
250 				 	
251 				 	lastType = 0;
252 				}
253 				
254 				break;
255 			case cwi.smilText.Render.appendLineBreak:
256 				//if (smilTextFound) {
257 					str += "\n<br/>";
258 					lastType = 0;
259 				//}
260 				break;
261 			case cwi.smilText.Render.appendText:
262 				if (t != currentTime) {
263 				//if (smilTextFound && t != currentTime) {
264 					currentTime = t;
265 					str += "\n<tev begin=\'" + t/1000.0 + "s\' />";
266 					lastType = 0;
267 				}
268 				
269 				//if (smilTextFound) {
270 					if (lastType == 1)
271 		 				str += " ";	// Add white space
272 					else str += "\n";
273 					str += st[1][1];
274 					lastType = 1;
275 				//}
276 				break;
277 			case cwi.smilText.Render.clearLayout:
278 				//if (smilTextFound) {
279 					str += "\n<clear begin=\'" + t/1000.0 + "s\' />";
280 					lastType = 0;
281 					currentTime = t;
282 				//}
283 				break;
284 			case cwi.smilText.Render.closeContainer:
285 				// skipped: got at getStyleXML
286 				//dur = t;
287 				break;
288 		}
289 		
290 		tempQ.push(t, st);
291 	}
292 	this.doneQueue = tempQ;
293 	tempQ = new PriorityQueue();
294 	
295 	while (this.timingQueue && !this.timingQueue.isEmpty()) 
296 	{
297 		var t = this.timingQueue.lookFirst();
298 		var st = this.timingQueue.pop();
299 
300 		switch(st[0]) 
301 		{
302 			case cwi.smilText.Render.appendContainer:
303 				var id = st[1][1].charAt(0);
304 			 	if (!smilTextFound && st[1][1].length > 8 && st[1][1].substring(0,8) == 'smiltext') {
305 					id = 'smiltext';
306 					smilTextFound = true;
307 					if (st[1][1] != '')
308 						header = "<smilText id=\'" + st[1][1] + "\' " + this.getStyleXML("smiltext", st[1][1]) + " >";
309 			 	}
310 								
311 				//if (smilTextFound) {	
312 				 	switch(id)
313 				 	{
314 				 		case 'smiltext':  // smiltext tag
315 				 			//str += "<smilText ";
316 				 			//endTag = "\n</smilText>" + endTag;
317 				 			break;
318 				 		case 'd': // div 
319 				 			str += "\n<div ";
320 				 			str += "id=\'" + st[1][1] + "\' " + this.getStyleXML("div", st[1][1]) + " >";
321 				 			endTag = "\n</div>" + endTag;
322 				 			break;
323 				 		case 'p': // p
324 				 			str += "\n<p ";
325 				 			str += "id=\'" + st[1][1] + "\' " + this.getStyleXML("p", st[1][1]) + " >";
326 				 			endTag = "\n</p>" + endTag;
327 				 			break;
328 				 		case 's': // span
329 				 			str += "\n<span ";
330 				 			str += "id=\'" + st[1][1] + "\' " + this.getStyleXML("span", st[1][1]) + " >";
331 				 			endTag = "\n</span>" + endTag;
332 				 			break;
333 				 	}
334 				 	
335 				 	lastType = 0;
336 				//}
337 				
338 				break;
339 			case cwi.smilText.Render.appendLineBreak:
340 				//if (smilTextFound) {
341 					str += "\n<br/>";
342 					lastType = 0;
343 				//}
344 				break;
345 			case cwi.smilText.Render.appendText:
346 				if (t != currentTime) {
347 				//if (smilTextFound && t != currentTime) {
348 					currentTime = t;
349 					str += "\n<tev begin=\'" + t/1000.0 + "s\' />";
350 					lastType = 0;
351 				}
352 		
353 				//if (smilTextFound) {
354 				  if (lastType == 1)
355 	 			    str += " ";	// Add white space
356 				  else str += "\n";
357 				  str += st[1][1];
358 				  lastType = 1;
359 				//}
360 				break;
361 			case cwi.smilText.Render.clearLayout:
362 				//if (smilTextFound) {
363 					str += "\n<clear begin=\'" + t/1000.0 + "s\' />";
364 					lastType = 0;
365 					currentTime = t;
366 				//}
367 				break;
368 			case cwi.smilText.Render.closeContainer:
369 				// skipped: got at getStyleXML
370 				//dur = t;
371 				break;
372 		}
373 		// st[0] : function	| st[1] : array of arguments
374 		//str = st[0] + st[1];
375 		
376 		tempQ.push(t, st);
377 	}
378 	this.timingQueue = tempQ;
379 	
380 	if (dur) {
381 		// it has an explicit duration
382 		header = header.substring(0, header.length - 2) + " dur=\'" + (dur/1000.0) + "s\' >";
383 	} 
384 	str = header + str;
385 	
386 	// Issue end tag
387 	if (endTag) {
388 		str += endTag;
389 	}
390 
391 	if (restorePlay) {
392 		this.play();
393 	}
394 	
395 	return str;
396 }
397 
398 /******************************************************************
399  * Time Engine 
400  ******************************************************************/
401 
402 /**
403  * Add a rendering primitive to the scheduler.
404  * @param {string} entry The smilText rendering primitive.
405  * @param {Array[]} args An array of arguments to the rendering primitive.
406  * @param {integer} t The occurring time (in milliseconds) relative to the smilText container.
407  * @see cwi.smilText.Render.appendContainer
408  * @see cwi.smilText.Render.appendLineBreak
409  * @see cwi.smilText.Render.appendText
410  * @see cwi.smilText.Render.clearLayout
411  * @see cwi.smilText.Render.closeContainer
412  */
413 cwi.smilText.STDocument.prototype.addTimingEntry = function(func, args, t)
414 {
415 	if (func == cwi.smilText.Render.scrollticker)
416 		this.motionEntry = new Array(0, func, args);
417 	else this.timingQueue.push(t, new Array(func, args));
418 }
419 
420 /**
421  * Remove all rendering primitives from the scheduler.
422  * @see cwi.smilText.Render.appendContainer
423  * @see cwi.smilText.Render.appendLineBreak
424  * @see cwi.smilText.Render.appendText
425  * @see cwi.smilText.Render.clearLayout
426  * @see cwi.smilText.Render.closeContainer
427  */
428 cwi.smilText.STDocument.prototype.clearTimingEntries = function()
429 {
430 	this.doneQueue.clear();
431  	this.timingQueue.clear();
432  	this.motionEntry = null;
433 }
434 
435 /**
436  * Return an array containing the timing entries. Each element of this array is an array containing: 
437  * [the occurency time, the rendering function, array of parameters to the rendering function].
438  * @return {Array[ [integer; string; args] ]}
439  * @see cwi.smilText.Render.appendContainer
440  * @see cwi.smilText.Render.appendLineBreak
441  * @see cwi.smilText.Render.appendText
442  * @see cwi.smilText.Render.clearLayout
443  * @see cwi.smilText.Render.closeContainer
444  */
445 cwi.smilText.STDocument.prototype.getTimingEntries = function()
446 {
447 	var l = new DoubleLinkedList();
448 	
449 	this.pause();
450 	
451 	var tempQ = new PriorityQueue();
452 	while (this.doneQueue && !this.doneQueue.isEmpty()) 
453 	{
454 		var t = this.doneQueue.lookFirst();
455 		var st = this.doneQueue.pop();
456 
457 		l.insert(new Array(t, st));
458 
459 		tempQ.push(t, st);
460 	}
461 	this.doneQueue = tempQ;
462 	tempQ = new PriorityQueue();
463 	
464 	while (this.timingQueue && !this.timingQueue.isEmpty()) 
465 	{
466 		var t = this.timingQueue.lookFirst();
467 		var st = this.timingQueue.pop();
468 
469 		l.insert(new Array(t, st));
470 		
471 		tempQ.push(t, st);
472 	}
473 	this.timingQueue = tempQ;
474 	
475 	return l;
476 }
477 
478 /**
479 * Update the current time of the smilText document.
480 * @param {integer} t new time moment (in milliseconds).
481 */
482 cwi.smilText.STDocument.prototype.setTime = function(t)
483 {
484 	cwi.smilText.STDocument.superClass.setTime.call(this, t);
485 	
486 	this.processTimingQueue();
487 }
488 
489 /**
490  * Process all entries at once.
491  * @private
492  */
493 cwi.smilText.STDocument.prototype.batchAllEntries = function()
494 {
495 	// Return to the beginnig.
496 	this.mergeTimingQueues();
497 	
498 	while(true)
499 	{
500 		// Process command.
501 		if (!this.timingQueue.isEmpty()) {
502 			
503 			var t = this.timingQueue.lookFirst();
504 			var st = this.timingQueue.pop();
505 				
506 			// st[0] : function	| st[1] : array of arguments
507 			st[0].apply(this, st[1]);
508 				
509 			this.doneQueue.push(t, st);
510 		}
511 		else break;
512 	}
513 	
514 	// Return to the beginnig again.
515 	this.mergeTimingQueues();
516 }
517 
518 /**
519  * Process the timing queue based on a given time moment.
520  * @private
521  */
522 cwi.smilText.STDocument.prototype.processTimingQueue = function()
523 {
524 	while(true)
525 	{
526 		if (this.motionEntry && this.motionEntry[0] <= this.timeNow && this.isPlaying()) {
527 			// this.motionEntry[1] : function	| this.motionEntry[2] : array of arguments
528 			this.motionEntry[1].apply(this, this.motionEntry[2]);
529 			this.motionEntry[0] =+ 50;
530 		}
531 				
532 		// Process command.
533 		if (this.timingQueue && !this.timingQueue.isEmpty()) {
534 			var t = this.timingQueue.lookFirst();
535 			
536 			if (t <= this.timeNow) {
537 
538 				var st = this.timingQueue.pop();
539 
540 				// st[0] : function	| st[1] : array of arguments
541 				st[0].apply(this, st[1]);
542 				
543 				this.doneQueue.push(t, st);
544 				
545 				// If it is animated the document never ends.
546 				if (this.timingQueue.isEmpty() && !this.motionEntry) {
547 					// notify event listeners: this was the last entry
548 					cwi.smilText.STDocument.superClass.fireEvent.call(this, cwi.smilText.Time.EVENT_END);
549 				}		
550 			} else break;
551 		}
552 		else break;
553 	}
554 }
555 
556 /** 
557 * @private
558 */
559 cwi.smilText.STDocument.prototype.mergeTimingQueues = function()
560 {
561 	// Quick merging
562 	this.doneQueue.merge(this.timingQueue);
563 	this.timingQueue = this.doneQueue;
564 	this.doneQueue = new PriorityQueue();
565 }
566 
567 /**
568  * Perform the seek operation to a given time moment.
569  * @param {integer} t The desired time instant (in milliseconds).
570  */
571 cwi.smilText.STDocument.prototype.seekTo = function(t)
572 {
573 	cwi.smilText.STDocument.superClass.seekTo.call(this, t);
574 	
575 	// merge queues
576 	this.mergeTimingQueues();
577 }
578 
579 /**
580  * Stop the smilText object.
581  */
582 cwi.smilText.STDocument.prototype.stop = function()
583 {
584 	cwi.smilText.STDocument.superClass.stop.call(this);
585 	
586 	cwi.smilText.Render.clearLayout(this, this.layout, true);
587 	if (this.motionEntry)
588 		this.motionEntry[0] = 0;	// reset time
589 }
590 
591 /******************************************************************
592  * Language Functions
593  ******************************************************************/
594 
595 /**
596  * Return the translation of a sentence.
597  * @param {string} s The original sentence.
598  * @return {string} the translated equivalent. 
599  */
600 cwi.smilText.STDocument.prototype.getTranslatedSentence = function(s)
601 {
602 	if (this.dictionary) {
603 		var t = this.dictionary.get(s);
604 		if (!t || t == "") {
605 			this.updateDictionary(s, s);
606 			if (this.translateFunc) this.translateFunc(s);
607 			return s;
608 		}
609 		return t;
610 	}
611 	return s;
612 }
613 
614 /**
615  * Save a sentence and its translation.
616  * @param {string} k The original sentence.
617  * @param {string} val The translation in some language.
618  */
619 cwi.smilText.STDocument.prototype.updateDictionary = function(k, val)
620 {
621 	if (k && val)
622 		this.dictionary.put(k, val);
623 }
624 
625 /**
626  * Set the translation function.
627  * @param {string} func A translation function.
628  */
629 cwi.smilText.STDocument.prototype.setTranslateFunction = function(func)
630 {
631 	this.translateFunc = func;
632 	this.dictionary = new Hashtable();
633 	if (func) this.batchAllEntries();
634 }
635 
636 
637 
638 
639 
640 /**
641  @name cwi.smilText.Parser
642  @namespace Hold all smilText parsing functionalities.
643  @version 1.0
644  @author <a href="mailto:rlaiola@cwi.nl">Rodrigo Laiola Guimaraes</a>
645 */
646 Namespace("cwi.smilText.Parser");
647 
648 /* Solve library dependency */
649 Import("cwi.smilText.STDocument");
650 
651 /**
652  * Parse the html source in order to find smilText tags, and render them 
653  * into the respective enclosing regions.
654  * @return {Array[cwi.smilText.STDocument]}
655  * @see cwi.smilText.STDocument
656  */
657 cwi.smilText.Parser.parseHTML = function()
658 {
659 	var docs = new Array();
660 	var doc = new STDocument(null); // created here just to hold styles' hash
661 	
662 	// Read textStyle elements.
663 	var style = document.getElementsByTagName("textstyle");
664 	
665 	for (var k=0; k<style.length; k++)
666 	{
667 		var id;
668 		
669 		for( var j = 0; j < style[k].attributes.length; j++ ) {
670 			if ( style[k].attributes[j].nodeName.toLowerCase() == 'id' ) {
671 				id = style[k].attributes[j].nodeValue;
672 				break;
673 			}
674 		}
675 		
676 		// check whether the attribute exists
677 		if (id == undefined) {
678 			alert("Error: a textStyle element must have an ID!");
679 		} else {
680 			cwi.smilText.Parser.parseAttributes(doc, id, style[k]);
681 		}
682 	}
683 	
684 	// Process smilText tags
685 	var ti;
686 	var t=document.getElementsByTagName("smiltext");
687 	for(ti=0; ti<t.length; ti++)
688 	{
689 		var dad = t[ti].parentNode;
690 		var layoutId;
691 		// check whether the attribute exists 
692 		for( var i = 0; i < dad.attributes.length; i++ ) {
693 			if ( dad.attributes[i].nodeName.toLowerCase() == 'id' ) {
694 				layoutId = dad.attributes[i].nodeValue;
695 				break;
696 			}
697 		}
698 		 
699 		if (layoutId == undefined) {
700 			alert("Error: All smilText containers must have an ID!");
701 		}
702 		
703 		// Trick code: process the smilText first, and then dont display specification in the html file.
704 		var stdoc = new STDocument(layoutId);
705 		stdoc.stylingHash = doc.stylingHash;		// Share styles
706 		cwi.smilText.Parser.parseSmilTextElem(stdoc, t[ti], layoutId, 0, undefined);
707 		docs[ti] = stdoc;
708 		
709 		// Keep smilText tag inside a hidden DIV
710 		var wrapper = document.createElement('div'); 
711 		wrapper.appendChild(t[ti].cloneNode(false));
712 		t[ti].parentNode.replaceChild(wrapper, t[ti]); 
713 		wrapper.style.visibility='hidden';
714 	}
715 	
716 	return docs;
717 }
718 
719 /**
720 * Parse a smilText file and return a smilText object.
721 * @param {string} xmlFile The path of the file containing the smilText specification.
722 * @param {string} region The id of the rendering region.
723 * @return {cwi.smilText.STDocument}
724 * @see cwi.smilText.STDocument
725 */
726 cwi.smilText.Parser.parseFile = function(xmlFile, region)
727 {
728 	var xmlDoc;
729 
730 	if (document.getElementById(region) == null) 
731 		alert("Error: smilText content must be encapsulated in a container. Region \'" + region + "\' not found!");
732 	
733 	// FF, Opera, Safari, others
734 	if (document.implementation && document.implementation.createDocument)
735 	{
736 		xmlDoc = document.implementation.createDocument("", "", null);
737 		xmlDoc.async = false;
738 		var loaded = xmlDoc.load(xmlFile);
739 		if (loaded) {
740 			return cwi.smilText.Parser.parseDoc(xmlDoc, region);
741 		} else return null;
742 	}
743 	// IE
744 	else if (window.ActiveXObject)
745 	{
746 		xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
747 		xmlDoc.async = false;
748 		xmlDoc.preserveWhiteSpace = true;
749 		//xmlDoc.onreadystatechange = function () {if (xmlDoc.readyState == 4) cwi.smilText.Parser.parseDoc(xmlDoc, region); else return null;};
750 		xmlDoc.load(xmlFile);
751 		// Laiola: Added to make IE return a document.
752 		//return cwi.smilText.Parser.parseDoc(xmlDoc, region);
753 		while(xmlDoc.readyState != 4);
754 		return cwi.smilText.Parser.parseDoc(xmlDoc, region);
755  	}
756 	else
757 	{
758 		alert('Your browser can\'t handle this script');
759 		return null;
760 	}
761 }
762 
763 /**
764  * Parse an XML string and return a smilText object.
765  * @param {string} xmlStr The smilText string.
766  * @param {string} region The id of the rendering region.
767  * @return {cwi.smilText.STDocument}
768  * @see cwi.smilText.STDocument
769  */
770 cwi.smilText.Parser.parseString = function(xmlStr, region)
771 {
772 	var xmlDoc;
773 	
774 	if (document.getElementById(region) == null) 
775 		alert("Error: smilText content must be encapsulated in a container. Region \'" + region + "\' not found!");
776 	
777 	try //Internet Explorer
778 	{
779 		xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
780 		xmlDoc.async = "false";
781 		xmlDoc.loadXML(xmlStr);
782 	}
783 	catch(e)
784 	{
785 		try //Firefox, Mozilla, Opera, etc.
786 		{
787 			var parser = new DOMParser();
788 			xmlDoc = parser.parseFromString(xmlStr,"text/xml");
789 		}
790 		catch(e) {
791 			alert(e.message);
792 			return null;
793 		}
794 	}
795 	
796 	return cwi.smilText.Parser.parseDoc(xmlDoc, region);
797 }
798 
799 /**
800 * Parse a HMTL element and return a smilText object.
801 * @param {string} element The HTML element containing the smilText specification.
802 * @param {string} region The id of the rendering region.
803 * @return {cwi.smilText.STDocument}
804 * @see cwi.smilText.STDocument
805 */
806 cwi.smilText.Parser.parseHTMLElement = function(element, region)
807 {
808 	var stdoc = new STDocument(region);
809 
810 	if (element) {
811 		cwi.smilText.Parser.parseSmilTextElem(stdoc, element, region, 0, undefined);
812 	}
813 	
814 	return stdoc;
815 }
816 
817 /**
818 * Parse a given xml document. So far, the tags are case sensitive.
819 * @private
820 */
821 cwi.smilText.Parser.parseDoc = function(xmlDoc, region)
822 {
823 	var stdoc = new STDocument(region);
824 	
825 	// Read textStyle elements
826 	var style = xmlDoc.getElementsByTagName('textStyle');
827 	
828 	for (var i=0;i<style.length;i++)
829 	{
830 		var id;
831 		
832 		for( var j = 0; j < style[i].attributes.length; j++ ) {
833 			if ( style[i].attributes[j].nodeName.toLowerCase() == 'id' ) {
834 				id = style[i].attributes[j].nodeValue;
835 				break;
836 			}
837 		}
838 		
839 		// It should be inside a textStyling tag
840 		if (style[i].parentNode.nodeName.toLowerCase() != 'textstyling') {
841 			alert("Warning: a textStyle element should be inside a textStyling tag");
842 		}
843 		
844 		// check whether the attribute exists
845 		if (id == undefined) {
846 			alert("Error: a textStyle element must have an ID!");
847 		} else {
848 			cwi.smilText.Parser.parseAttributes(stdoc, id, style[i]);
849 		}
850 	}
851 	
852 	// Process smilText elements
853 	var x = xmlDoc.getElementsByTagName('smilText');
854 	
855 	for (var i=0;i<x.length;i++)
856 	{
857 		cwi.smilText.Parser.parseSmilTextElem(stdoc, x[i], region, 0, undefined);
858 	}
859 	
860 	return stdoc;
861 }
862 
863 /**
864 * Parse a smilText elem.
865 * @private
866 */
867 cwi.smilText.Parser.parseSmilTextElem = function(doc, elem, layout, currentTime, dur)
868 {
869 	var setEnd = false;
870 	
871 	// Make sure that a tag will be inserted.
872 	var force;
873 	if (dur == undefined || currentTime <= dur)
874 		force = true;
875 	else force = false;
876 	
877 	// Put smilText in a DIV
878 	if (elem.nodeName.toLowerCase() == 'smiltext' || elem.nodeName.toLowerCase() == 'smil:smiltext') {
879 		var smiltextID = "smiltext" + Math.random()*10000;
880 
881 		// Clear parent container before hand.
882 		doc.addTimingEntry(cwi.smilText.Render.clearLayout, new Array(doc, layout, force), currentTime * 1000);
883 				  
884 		cwi.smilText.Parser.parseAttributes(doc, smiltextID, elem);
885 		doc.addTimingEntry(cwi.smilText.Render.appendContainer, new Array(doc, smiltextID, layout, force), currentTime * 1000);
886 		layout = smiltextID;
887 		
888 		if (doc.getStoredAttribute(smiltextID, 'dur') != undefined && doc.getStoredAttribute(smiltextID, 'dur') != null)
889 		{
890 			dur = parseFloat(doc.getStoredAttribute(smiltextID, 'dur'));
891 			doc.addTimingEntry(cwi.smilText.Render.closeContainer, new Array(doc), dur * 1000);
892 		}
893 	}
894 			
895 	for (var j=0;j<elem.childNodes.length;j++)
896 	{
897 		
898 		/* string */
899 		if (elem.childNodes[j].nodeType != 1) {
900 			
901 			var str;
902 			
903 			// follow xml:space
904 			if (doc.getStoredAttribute(layout, 'xml:space') == 'preserve')
905 			{
906 				str = elem.childNodes[j].nodeValue;
907 				
908 				var lines = str.split(/\n/g); // line breaks become <br/>
909 				for (var z = 0; z < lines.length; z++) {
910 					//lines[z] = lines[z].replace(new RegExp( "\\'", "g" ), "\\'");  // replace single quotation mark
911 					doc.addTimingEntry(cwi.smilText.Render.appendText, new Array(doc, lines[z], layout, force), currentTime * 1000);
912 					
913 					if (doc.getStoredAttribute(layout, 'textmode') != "crawl") {
914 						if (z != lines.length-1) doc.addTimingEntry(cwi.smilText.Render.appendLineBreak, new Array(doc, layout, force), currentTime * 1000);
915 					}
916 				}
917 			}
918 			else {
919 				str = (cwi.util.trim(elem.childNodes[j].nodeValue));
920 				str = str.replace(new RegExp( "\\n", "g" ), "");     // line breaks discarded.
921 				str = str.replace(new RegExp( "\\t", "g" ), "");     // tabs discarded.
922 				str = str.replace(new RegExp( "\ +", "g" ), " ");    // extra white-spaces discarded.
923 				//str = str.replace(new RegExp( "\\'", "g" ), "\\'");  // replace single quotation mark
924 				
925 				/* test if non-blank */
926 				if (str.length != 0) {
927 					doc.addTimingEntry(cwi.smilText.Render.appendText, new Array(doc, str, layout, force), currentTime * 1000);
928 				}
929 				else{	
930 					// Blank string
931 				}
932 
933 			}
934 		}
935 		
936 		/* smilText tags */
937 		else {
938 			var tag = elem.childNodes[j].nodeName.toLowerCase();
939 					
940 			switch(tag)
941 			{
942 				
943 				/********************/
944 				/* Parse BasicText Module
945 				/********************/
946 				
947 				case 'smil:tev':
948 				case 'tev':
949 				  var b = null;
950 				  var n = null;
951 				  
952 				  // All timing markup to be ignored according to text motion specification.
953 				  if ( (doc.getStoredAttribute(layout, 'textmode') == "crawl" ||
954 						doc.getStoredAttribute(layout, 'textmode') == "scroll" ||
955 						doc.getStoredAttribute(layout, 'textmode') == "jump") &&
956 					   (doc.getStoredAttribute(layout, 'textrate') == null ||
957 					    doc.getStoredAttribute(layout, 'textrate') == undefined ||
958 					    doc.getStoredAttribute(layout, 'textrate') == "auto") ) {
959 					break;
960 				  }
961 				
962 				  // check whether the attribute exists before setting a value 
963 				  for( var i = 0; i < elem.childNodes[j].attributes.length; i++ ) {
964 			  	  	  if ( elem.childNodes[j].attributes[i].nodeName.toLowerCase() == 'begin' ) {
965 			      		  b = parseFloat(elem.childNodes[j].attributes[i].nodeValue);
966 			      		  break;
967 			  		  }
968 			  		
969 			  		  else if ( elem.childNodes[j].attributes[i].nodeName.toLowerCase() == 'next' ) {
970 			      		  n = parseFloat(elem.childNodes[j].attributes[i].nodeValue);
971 			      		  break;
972 			  		  } 
973 				  }
974 				  
975 				  // test begin and next attributes.
976 				  if (n != null){
977 				  	  currentTime += n;
978 				  } 
979 				  else if (b != null){
980 				  	  // test whether the begin time has elapsed.
981 				  	  if (b > currentTime)
982 				  	  	  currentTime = b;
983 				  }
984 				  
985 				  // Update force variable
986 				  if (dur == undefined || currentTime <= dur)
987 					  force = true;
988 				  else force = false;
989 				  
990 				  // Apply append attribute.
991 				  if (doc.getStoredAttribute(layout, 'textmode') == "replace") {
992 				  	  doc.addTimingEntry(cwi.smilText.Render.clearLayout, new Array(doc, layout, force), currentTime * 1000);
993 				  }
994 				  
995 				  break;   
996 				case 'smil:clear':
997 				case 'clear':
998 				  var b = null;
999 				  var n = null;
1000 				
1001 				  // All timing markup to be ignored according to text motion specification.
1002 				  if ( (doc.getStoredAttribute(layout, 'textmode') == "crawl" ||
1003 						doc.getStoredAttribute(layout, 'textmode') == "scroll" ||
1004 						doc.getStoredAttribute(layout, 'textmode') == "jump") &&
1005 					   (doc.getStoredAttribute(layout, 'textrate') == null ||
1006 					    doc.getStoredAttribute(layout, 'textrate') == undefined ||
1007 					    doc.getStoredAttribute(layout, 'textrate') == "auto") ) {
1008 					break;
1009 				  }
1010 				  
1011 				  // check whether the attribute exists before setting a value 
1012 				  for( var i = 0; i < elem.childNodes[j].attributes.length; i++ ) {
1013 			  	  	  if ( elem.childNodes[j].attributes[i].nodeName.toLowerCase() == 'begin' ) {
1014 			      		  b = parseFloat(elem.childNodes[j].attributes[i].nodeValue);
1015 			      		  break;
1016 			  		  }
1017 			  		
1018 			  		  else if ( elem.childNodes[j].attributes[i].nodeName.toLowerCase() == 'next' ) {
1019 			      		  n = parseFloat(elem.childNodes[j].attributes[i].nodeValue);
1020 			      		  break;
1021 			  		  } 
1022 				  }
1023 				  
1024 				  // test begin and next attributes.
1025 				  if (n != null){
1026 				  	  currentTime += n;
1027 				  } 
1028 				  else if (b != null){
1029 				  	  // test whether the begin time has elapsed.
1030 				  	  if (b > currentTime)
1031 				  	  	  currentTime = b;
1032 				  }
1033 				  
1034 				  // Update force variable
1035 				  if (dur == undefined || currentTime <= dur)
1036 					  force = true;
1037 				  else force = false;
1038 				  
1039 				  // Clear container.
1040 				  doc.addTimingEntry(cwi.smilText.Render.clearLayout, new Array(doc, layout, force), currentTime * 1000);
1041 				  
1042 				  break;
1043 				case 'smil:br':
1044 				case 'br':
1045 				  if (doc.getStoredAttribute(layout, 'textmode') != "crawl") {
1046 					doc.addTimingEntry(cwi.smilText.Render.appendLineBreak, new Array(doc, layout, force), currentTime * 1000);
1047 				  }
1048 				  break;
1049 				  
1050 				/********************/
1051 				/* Parse TextStyling Module
1052 				/********************/
1053 				
1054 				case 'smil:div':
1055 				case 'div':
1056 				  // check whether attributes exist
1057 				  
1058 				  // a new div will be created to render the content
1059 				  var divID = "div" + Math.random()*10000;
1060 				  cwi.smilText.Parser.parseAttributes(doc, divID, elem.childNodes[j]);
1061 				  
1062 				  // Setup xml:space
1063 			 	  if (doc.getStoredAttribute(divID, 'xml:space') == undefined || doc.getStoredAttribute(divID, 'xml:space') == null ||
1064 			 	      (doc.getStoredAttribute(divID, 'xml:space') != 'default' && doc.getStoredAttribute(divID, 'xml:space') != 'preserve')) {
1065 					  if (doc.getStoredAttribute(layout, 'xml:space') != undefined && doc.getStoredAttribute(layout, 'xml:space') != null && 
1066 					     (doc.getStoredAttribute(layout, 'xml:space') == 'default' || doc.getStoredAttribute(layout, 'xml:space') == 'preserve'))
1067 					  {
1068 					  	  doc.updateAttribute(divID, 'xml:space', doc.getStoredAttribute(layout, 'xml:space'));
1069 					  } else doc.updateAttribute(divID, 'xml:space', 'default');		
1070 				  }
1071 				  
1072 				  doc.updateAttribute(divID, 'textmode', doc.getStoredAttribute(layout, 'textmode'));
1073 				  doc.updateAttribute(divID, 'textrate', doc.getStoredAttribute(layout, 'textrate'));
1074 				  doc.addTimingEntry(cwi.smilText.Render.appendContainer, new Array(doc, divID, layout, force), currentTime * 1000);
1075 				  
1076 				  currentTime = cwi.smilText.Parser.parseSmilTextElem(doc, elem.childNodes[j], divID, currentTime, dur);
1077 				  break;
1078 				case 'smil:p':
1079 				case 'p':
1080 				  // check whether attributes exist
1081 				  
1082 				  // a new p will be created to render the content
1083 				  var pID = "p" + Math.random()*10000;
1084 				  cwi.smilText.Parser.parseAttributes(doc, pID, elem.childNodes[j]);
1085 				  
1086 				  // Setup xml:space
1087 			 	  if (doc.getStoredAttribute(pID, 'xml:space') == undefined || doc.getStoredAttribute(pID, 'xml:space') == null ||
1088 			 	      (doc.getStoredAttribute(pID, 'xml:space') != 'default' && doc.getStoredAttribute(pID, 'xml:space') != 'preserve')) {
1089 					  if (doc.getStoredAttribute(layout, 'xml:space') != undefined && doc.getStoredAttribute(layout, 'xml:space') != null && 
1090 					     (doc.getStoredAttribute(layout, 'xml:space') == 'default' || doc.getStoredAttribute(layout, 'xml:space') == 'preserve'))
1091 					  {
1092 					  	  doc.updateAttribute(pID, 'xml:space', doc.getStoredAttribute(layout, 'xml:space'));
1093 					  } else doc.updateAttribute(pID, 'xml:space', 'default');		
1094 				  }
1095 				  
1096 				  doc.updateAttribute(pID, 'textmode', doc.getStoredAttribute(layout, 'textmode'));
1097 				  doc.updateAttribute(pID, 'textrate', doc.getStoredAttribute(layout, 'textrate'));
1098 				  doc.addTimingEntry(cwi.smilText.Render.appendContainer, new Array(doc, pID, layout, force), currentTime * 1000);
1099 				  
1100 				  currentTime = cwi.smilText.Parser.parseSmilTextElem(doc, elem.childNodes[j], pID, currentTime, dur);
1101 				  break;
1102 				case 'smil:span':
1103 				case 'span':
1104 				  // check whether attributes exist
1105 				  
1106 				  // a new span will be created to render the content
1107 				  var spanID = "span" + Math.random()*10000;
1108 				  cwi.smilText.Parser.parseAttributes(doc, spanID, elem.childNodes[j]);
1109 				  				  
1110 				  // Setup xml:space
1111 			 	  if (doc.getStoredAttribute(spanID, 'xml:space') == undefined || doc.getStoredAttribute(spanID, 'xml:space') == null ||
1112 			 	      (doc.getStoredAttribute(spanID, 'xml:space') != 'default' && doc.getStoredAttribute(spanID, 'xml:space') != 'preserve')) {
1113 					  if (doc.getStoredAttribute(layout, 'xml:space') != undefined && doc.getStoredAttribute(layout, 'xml:space') != null && 
1114 					     (doc.getStoredAttribute(layout, 'xml:space') == 'default' || doc.getStoredAttribute(layout, 'xml:space') == 'preserve'))
1115 					  {
1116 					  	  doc.updateAttribute(spanID, 'xml:space', doc.getStoredAttribute(layout, 'xml:space'));
1117 					  } else doc.updateAttribute(spanID, 'xml:space', 'default');		
1118 				  }
1119 				  
1120 				  doc.updateAttribute(spanID, 'textmode', doc.getStoredAttribute(layout, 'textmode'));
1121 				  doc.updateAttribute(spanID, 'textrate', doc.getStoredAttribute(layout, 'textrate'));
1122 				  doc.addTimingEntry(cwi.smilText.Render.appendContainer, new Array(doc, spanID, layout, force), currentTime * 1000);
1123 				  
1124 				  currentTime = cwi.smilText.Parser.parseSmilTextElem(doc, elem.childNodes[j], spanID, currentTime, dur);
1125 				  break;
1126 				default:
1127 				  alert("Unknown smilText tag: " + tag);
1128 			}
1129 		}
1130 	}
1131 	
1132 	return currentTime;
1133 }
1134 
1135 /**
1136  * Check the attributes of an element.
1137  * @private
1138  */
1139 cwi.smilText.Parser.parseAttributes = function(doc, id, elem)
1140 {
1141 	var elemName = elem.nodeName.toLowerCase();
1142 
1143 	// Make sure we can recover it on applyAttributes method.	
1144 	if (elemName == 'textstyle') {
1145 		id = 'textstyle' + id;
1146 	}
1147 	else {
1148 		// check whether the textStyle attribute was defined.
1149 		for( var i = 0; i < elem.attributes.length; i++ ) {
1150 			var attr = elem.attributes[i].nodeName.toLowerCase();
1151 			var val = elem.attributes[i].nodeValue;
1152 			
1153 			if (attr == 'textstyle') {
1154 				doc.applyStyle(elemName, id, 'textstyle' + val);
1155 				break;
1156 			}
1157 		}
1158 	}
1159 	
1160 	// check whether the attribute exists before setting a value
1161 	for( var i = 0; i < elem.attributes.length; i++ ) {
1162 		
1163 		var found = false;
1164 		var attr = elem.attributes[i].nodeName.toLowerCase();
1165 		var val = elem.attributes[i].nodeValue;
1166 		
1167 		switch(elemName)
1168 		{
1169 		    case 'smil:smiltext':
1170 			case 'smiltext':
1171 				switch(attr)
1172 				{
1173 					case 'dur':
1174 						found = true;
1175 						break;
1176 					case 'width':
1177 						found = true;
1178 						break;
1179 					case 'height':
1180 						found = true;
1181 						break;
1182 					default:
1183 				}
1184 				if (found){
1185 					doc.updateAttribute(id, attr, val);
1186 					break;
1187 				}
1188 			case 'textstyle':
1189 				switch(attr)
1190 				{
1191 					case 'textmode':
1192 						found = true;
1193 						break;
1194 					case 'textplace':
1195 						found = true;
1196 						break;
1197 					case 'textconceal':
1198 						found = true;
1199 						break;
1200 					case 'textrate':
1201 						found = true;
1202 						break;
1203 					default:
1204 				}
1205 				if (found){
1206 					doc.updateAttribute(id, attr, val);
1207 					break;
1208 				}
1209 			case 'smil:div':
1210 			case 'div':
1211 				switch(attr)
1212 				{
1213 					case 'textalign':
1214 						found = true;
1215 						break;
1216 					default:
1217 				}
1218 				if (found){
1219 					doc.updateAttribute(id, attr, val);
1220 					break;
1221 				}
1222 			case 'smil:p':
1223 			case 'p':
1224 				switch(attr)
1225 				{
1226 					case 'xml:space':
1227 					case 'textwrapoption':
1228 					case 'textwritingmode':
1229 					//case 'textstyle':
1230 						found = true;
1231 						break;
1232 					default:
1233 				}
1234 				if (found){
1235 					doc.updateAttribute(id, attr, val);
1236 					break;
1237 				}
1238 			case 'smil:span':
1239 			case 'span':
1240 				switch(attr)
1241 				{
1242 					case 'textbackgroundcolor':
1243 					case 'textcolor':
1244 					case 'textfontfamily':
1245 					case 'textfontsize':
1246 					case 'textfontstyle':
1247 					case 'textfontweight':
1248 					case 'textstyle':
1249 						found = true;
1250 						break;
1251 					case 'textdirection':
1252 						// Confirm it is a span element. Otherwise jump out.
1253 						if (elemName != 'span' && elemName != 'textstyle') break;
1254 						found = true;
1255 						break;
1256 					default:
1257 				}
1258 				if (found){
1259 					doc.updateAttribute(id, attr, val);
1260 				}
1261 				break;
1262 			default:
1263 				return;
1264 		}
1265 	}
1266 	
1267 }
1268 
1269 
1270 
1271 
1272 
1273 /**
1274  @name cwi.smilText.Render
1275  @namespace Hold all smilText rendering functionalities.
1276  @version 1.0
1277  @author <a href="mailto:rlaiola@cwi.nl">Rodrigo Laiola Guimaraes</a>
1278 */
1279 Namespace("cwi.smilText.Render");
1280 
1281 /* Solve library dependency */
1282 Import("cwi.smilText.STDocument");
1283 
1284 /**
1285  * Append text to a given region.
1286  * @param {cwi.smilText.STDocument} doc the smilText document.
1287  * @param {string} str the string to be appended.
1288  * @param {string} layout the id of the rendering region.
1289  * @param {string} force true to guaratee that the primitive will be issued.
1290  * @see cwi.smilText.STDocument
1291  */
1292 cwi.smilText.Render.appendText = function(doc, str, layout, force)
1293 {
1294 	if (doc == null || doc == undefined)
1295 		return;
1296 	
1297 	// Test whether smilText duration has elapsed.
1298 	if (!force)
1299 		return;
1300 			
1301 	str = doc.getTranslatedSentence(str);
1302 		
1303 	// Apply textWrapOption
1304 	switch(doc.getStoredAttribute(layout, 'textwrapoption'))
1305 	{
1306 		case 'noWrap':
1307 			break;
1308 		case 'inherit':
1309 		    break;
1310 		case 'wrap':
1311 		default:
1312 			str = str.replace(/(.*?)/g, "<wbr/>");
1313 	}
1314 	
1315 	var wrapper = document.createElement('span');
1316 	if (document.getElementById(layout) != null)
1317 		document.getElementById(layout).appendChild(wrapper);
1318 	wrapper.innerHTML = str;
1319 	//document.getElementById(layout).innerHTML += str;
1320 }
1321 
1322 /**
1323  * Append a linebreak to a given region.
1324  * @param {cwi.smilText.STDocument} doc the smilText document.
1325  * @param {string} layout the id of the rendering region.
1326  * @param {string} force true to guaratee that the primitive will be issued.
1327  * @see cwi.smilText.STDocument
1328  */
1329 cwi.smilText.Render.appendLineBreak = function(doc, layout, force)
1330 {
1331 	if (doc == null || doc == undefined)
1332 		return;
1333 		
1334 	// Test whether smilText duration has elapsed.
1335 	if (!force)
1336 		return;
1337 				
1338 	var theData = document.createElement('BR');
1339 	if (document.getElementById(layout) != null) 
1340 		document.getElementById(layout).appendChild(theData);
1341 }
1342 
1343 /**
1344  * Clear a given region.
1345  * @param {cwi.smilText.STDocument} doc the smilText document.
1346  * @param {string} layout the id of the rendering region.
1347  * @param {string} force true to guaratee that the primitive will be issued.
1348  * @see cwi.smilText.STDocument 
1349  */
1350 cwi.smilText.Render.clearLayout = function(doc, layout, force)
1351 {
1352 	if (doc == null || doc == undefined)
1353 		return;
1354 		
1355 	// Test whether smilText duration has elapsed.
1356 	if (!force)
1357 		return;
1358 			
1359 	var layoutElem = document.getElementById(layout);
1360 	if (layoutElem) {
1361 		while (child = layoutElem.firstChild)
1362 			layoutElem.removeChild(child);
1363 	}
1364 }
1365 
1366 /**
1367  * Append a container to a given region.
1368  * @param {cwi.smilText.STDocument} doc the smilText document.
1369  * @param {string} containerName the name (id) of the new container. It must be unique 
1370  * and start with smiltext, div, p or span.
1371  * @param {string} layout the id of the rendering region.
1372  * @param {string} force true to guaratee that the primitive will be issued.
1373  * @see cwi.smilText.STDocument
1374  */
1375 cwi.smilText.Render.appendContainer = function(doc, containerName, layout, force)
1376 {
1377 	if (doc == null || doc == undefined)
1378 		return;
1379 		
1380 	// Test whether smilText duration has elapsed.
1381 	if (!force)
1382 		return;
1383 
1384 	// create a new div to render the content
1385  	var wrapper;
1386  	var id = containerName.charAt(0);
1387  	if (containerName.length > 8 && containerName.substring(0,8) == 'smiltext') {
1388 		id = 'smiltext';
1389  	}
1390 		
1391  	switch(id)
1392  	{
1393  		case 'smiltext':  // smiltext tag
1394  		case 'd': // div 
1395  			wrapper = document.createElement('div');
1396  			try {
1397  				wrapper.style.display='block';
1398  			} catch (e) {}
1399  			break;
1400  		case 'p': // p
1401  			wrapper = document.createElement('div');
1402  			try {
1403  				wrapper.style.display='block';
1404  			} catch (e) {}
1405  			break;
1406  		case 's': // span
1407  			wrapper = document.createElement('div');
1408  			try {
1409  				wrapper.style.display='inline';
1410  			} catch (e) {}
1411  			break;
1412  	}
1413  	
1414  	// Setup text wrap option
1415  	if (doc.getStoredAttribute(containerName, 'textwrapoption') == undefined || 
1416  		doc.getStoredAttribute(containerName, 'textwrapoption') == null) 
1417  	{
1418 		doc.updateAttribute(containerName, 'textwrapoption', 'wrap');
1419 	}
1420 	else if (doc.getStoredAttribute(containerName, 'textwrapoption') == 'inherit') 
1421 	{
1422 		if (doc.getStoredAttribute(layout, 'textwrapoption') != undefined && 
1423 			doc.getStoredAttribute(layout, 'textwrapoption') != null)
1424 		{
1425 			doc.updateAttribute(containerName, 'textwrapoption', doc.getStoredAttribute(layout, 'textwrapoption'));
1426 		} else doc.updateAttribute(containerName, 'textwrapoption', 'wrap');
1427 	}
1428 	
1429 	// Crawl mode: disable line breaks
1430 	if (doc.getStoredAttribute(containerName, 'textmode') == "crawl") {
1431 		doc.updateAttribute(containerName, 'textwrapoption', 'noWrap');
1432 	}
1433 	
1434 	// Apply textWrapOption
1435 	if (doc.getStoredAttribute(containerName, 'xml:space') == 'preserve')
1436 	{
1437 		switch(doc.getStoredAttribute(containerName, 'textwrapoption'))
1438 		{
1439 			case 'noWrap':
1440 				try {
1441 					wrapper.style.whiteSpace='pre';
1442 				} catch (e) {}
1443 		  		break;
1444 			case 'wrap':
1445 			default:
1446 				try {
1447 					wrapper.style.whiteSpace='pre-wrap';
1448 				} catch (e) {}
1449 		}
1450 	} else {
1451 		switch(doc.getStoredAttribute(containerName, 'textwrapoption'))
1452 		{
1453 			case 'noWrap':
1454 				try {
1455 					wrapper.style.whiteSpace='nowrap';
1456 				} catch (e) {}
1457 		  		break;
1458 			case 'wrap':
1459 			default:
1460 				try {
1461 					wrapper.style.whiteSpace='normal';
1462 				} catch (e) {}
1463 		}	
1464 	}
1465 	
1466 	// Discard implicit line breaks on crawl mode
1467  	if (doc.getStoredAttribute(containerName, 'textmode') == "crawl" && id != 'smiltext') {
1468  		try {
1469  			wrapper.style.display='inline';
1470  		} catch (e) {}
1471  	}
1472  	
1473  	if (document.getElementById(layout) != null)
1474  		document.getElementById(layout).appendChild(wrapper);
1475 	id = document.createAttribute("id");
1476  	id.nodeValue=containerName;
1477 	wrapper.setAttributeNode(id);
1478 	
1479 	cwi.smilText.Render.applyAttributes(doc, containerName, wrapper);
1480 }
1481 
1482 /**
1483  * Set the end of the life cycle of a smilText tag.
1484  * @param {cwi.smilText.STDocument} doc the smilText document.
1485  * @see cwi.smilText.STDocument
1486  */
1487 cwi.smilText.Render.closeContainer = function(doc)
1488 {
1489 	if (!doc.hasFillSemantics) cwi.smilText.Render.clearLayout(doc, doc.layout, true);
1490 }
1491 
1492 /**
1493  * Apply the stored attributes in a given layout.
1494  * @private
1495  */
1496 cwi.smilText.Render.applyAttributes = function(doc, id, layout)
1497 {
1498 	
1499 	var elemName;
1500 	if (id.length > 8 && id.substring(0,8) == 'smiltext'){
1501 		elemName = 'smiltext';
1502 	}
1503 	else if (id.length > 9 && id.substring(0,9) == 'textstyle'){
1504 		elemName = 'textstyle';
1505 		
1506 		// get the layout id
1507 		for( var i = 0; i < layout.attributes.length; i++ ) {
1508 			
1509 	 		if ( layout.attributes[i].nodeName.toLowerCase() == 'id' ) {
1510 	 			var container = layout.attributes[i].nodeValue;
1511 
1512 	 			if (container.length > 8 && container.substring(0,8) == 'smiltext');
1513 				else if (container.length > 3 && container.substring(0,3) == 'div')
1514 					elemName += '1';
1515 				else if (container.length > 1 && container.substring(0,1) == 'p')
1516 					elemName += '2';
1517 				else if (container.length > 4 && container.substring(0,4) == 'span')
1518 					elemName += '3';	
1519 					
1520 	 			break;
1521 	 		}
1522 		 	
1523 		}
1524 		
1525 	}
1526 	else elemName = id.charAt(0);
1527 
1528 	// Apply textStyle
1529 	if (elemName != 'textstyle' && doc.getStoredAttribute(id, 'textstyle') != undefined &&
1530 		doc.getStoredAttribute(id, 'textstyle') != null) {
1531 		cwi.smilText.Render.applyAttributes(doc, 'textstyle'+ doc.getStoredAttribute(id, 'textstyle'), layout);
1532 	}
1533 	
1534 	switch(elemName)
1535 	{
1536 		case 'smiltext':
1537 			if (doc.getStoredAttribute(id, 'width') != undefined && doc.getStoredAttribute(id, 'width') != null)
1538 			{
1539 				try {
1540 					layout.style.width = parseFloat(doc.getStoredAttribute(id, 'width'));
1541 					layout.style.overflow='hidden';
1542 				} catch (e) {}
1543 			}
1544 			if (doc.getStoredAttribute(id, 'height') != undefined && doc.getStoredAttribute(id, 'height') != null)
1545 			{
1546 				try {
1547 					layout.style.height = parseFloat(doc.getStoredAttribute(id, 'height'));
1548 					layout.style.overflow='hidden';
1549 				} catch (e) {}
1550 			}
1551 		case 'textstyle':
1552 			if (doc.getStoredAttribute(id, 'textplace') != undefined && doc.getStoredAttribute(id, 'textplace') != null)
1553 			{
1554 				// TODO	
1555 			}
1556 			if (doc.getStoredAttribute(id, 'textmode') != undefined && doc.getStoredAttribute(id, 'textmode') != null)
1557 			{
1558 				switch(doc.getStoredAttribute(id, 'textmode'))
1559 				{
1560 					case 'replace':
1561 						break;
1562 					case 'crawl':
1563 						doc.addTimingEntry(cwi.smilText.Render.scrollticker, new Array(doc, id, '1'), 0);
1564 						break;
1565 					case 'scroll':
1566 						doc.addTimingEntry(cwi.smilText.Render.scrollticker, new Array(doc, id, '2'), 0);
1567 						break;
1568 					case 'jump':
1569 						// TODO
1570 						break;
1571 					case 'append':
1572 					default:
1573 				}
1574 			}
1575 			if (doc.getStoredAttribute(id, 'textconceal') != undefined && doc.getStoredAttribute(id, 'textconceal') != null)
1576 			{
1577 				switch(doc.getStoredAttribute(id, 'textconceal'))
1578 				{
1579 					case 'none':
1580 						// TODO
1581 						break;
1582 					case 'initial':
1583 						// TODO
1584 						break;
1585 					case 'final':
1586 						// TODO
1587 						break;
1588 					case 'both':
1589 						// TODO
1590 						break;
1591 					case 'inherit':
1592 					default:
1593 				}
1594 			}
1595 			if (doc.getStoredAttribute(id, 'textrate') != undefined && doc.getStoredAttribute(id, 'textrate') != null)
1596 			{
1597 				// TODO
1598 			}
1599 		case 'textstyle1':
1600 		case 'd': // div
1601 			if (doc.getStoredAttribute(id, 'textalign') != undefined && doc.getStoredAttribute(id, 'textalign') != null)
1602 			{
1603 				switch(doc.getStoredAttribute(id, 'textalign'))
1604 				{
1605 					case 'inherit':
1606 					case 'start':
1607 					case 'end':
1608 					case 'left':
1609 					case 'right':
1610 					case 'center':
1611 						try {
1612 							layout.style.textAlign = doc.getStoredAttribute(id, 'textalign');
1613 						} catch (e) {}
1614 						break;
1615 					default:
1616 				}
1617 			}
1618 		case 'textstyle2':
1619 		case 'p': // p
1620 			if (doc.getStoredAttribute(id, 'textwritingmode') != undefined && doc.getStoredAttribute(id, 'textwritingmode') != null)
1621 			{
1622 				switch(doc.getStoredAttribute(id, 'textwritingmode'))
1623 				{
1624 					case 'lr-tb':
1625 					case 'rl-tb':
1626 					case 'tb-rl':
1627 					case 'tb-lr':
1628 					case 'rl':
1629 					case 'lr':
1630 					case 'inherit':
1631 						try {
1632 							layout.style.writingMode = doc.getStoredAttribute(id, 'textwritingmode');
1633 						} catch (e) {}
1634 						break;
1635 					default:
1636 				}
1637 			}
1638 		case 'textstyle3':
1639 		case 's': // span
1640 			if (doc.getStoredAttribute(id, 'textbackgroundcolor') != undefined && doc.getStoredAttribute(id, 'textbackgroundcolor') != null)
1641 			{
1642 				try {
1643 					layout.style.backgroundColor = doc.getStoredAttribute(id, 'textbackgroundcolor');
1644 				} catch (e) {}
1645 			}
1646 			if (doc.getStoredAttribute(id, 'textcolor') != undefined && doc.getStoredAttribute(id, 'textcolor') != null)
1647 			{
1648 				try {
1649 					layout.style.color = doc.getStoredAttribute(id, 'textcolor');
1650 				} catch (e) {}
1651 			}
1652 			if (doc.getStoredAttribute(id, 'textfontfamily') != undefined && doc.getStoredAttribute(id, 'textfontfamily') != null)
1653 			{
1654 				try {
1655 					layout.style.fontFamily = doc.getStoredAttribute(id, 'textfontfamily');
1656 				} catch (e) {}
1657 			}
1658 			if (doc.getStoredAttribute(id, 'textfontsize') != undefined && doc.getStoredAttribute(id, 'textfontsize') != null)
1659 			{
1660 				try {
1661 					layout.style.fontSize = doc.getStoredAttribute(id, 'textfontsize');
1662 				} catch (e) {}
1663 			}
1664 			if (doc.getStoredAttribute(id, 'textfontstyle') != undefined && doc.getStoredAttribute(id, 'textfontstyle') != null)
1665 			{
1666 				try {
1667 					layout.style.fontStyle = doc.getStoredAttribute(id, 'textfontstyle');
1668 				} catch (e) {}
1669 			}
1670 			if (doc.getStoredAttribute(id, 'textfontweight') != undefined && doc.getStoredAttribute(id, 'textfontweight') != null)
1671 			{
1672 				try {
1673 					layout.style.fontWeight = doc.getStoredAttribute(id, 'textfontweight');
1674 				} catch (e) {}
1675 			}
1676 			if (doc.getStoredAttribute(id, 'textdirection') != undefined && doc.getStoredAttribute(id, 'textdirection') != null)
1677 			{
1678 				// Confirm it is a span element. Otherwise jump out.
1679 				if (elemName == 's')
1680 				{
1681 					if (doc.getStoredAttribute(id, 'textdirection') == 'rtlo' || 
1682 						doc.getStoredAttribute(id, 'textdirection') == 'rtl') {
1683 						try {
1684 							layout.style.direction = 'rtl';
1685 							layout.style.unicodeBidi = 'bidi-override';
1686 						} catch (e) {}
1687 					} else if (doc.getStoredAttribute(id, 'textdirection') == 'ltro' ||
1688 						doc.getStoredAttribute(id, 'textdirection') == 'ltro') {
1689 						try {
1690 							layout.style.direction = 'ltr';
1691 							layout.style.unicodeBidi = 'bidi-override';
1692 						} catch (e) {}
1693 					}
1694 				}
1695 			}
1696 			break;
1697 		default:
1698 	}
1699 	
1700 }
1701 
1702 /******************************************************************
1703  * Text Motion
1704  ******************************************************************/
1705 
1706 /**
1707 * @private
1708 */
1709 cwi.smilText.Render.cps = 1; // 20px/s
1710 
1711 /**
1712  * Move layout content in a given direction.
1713  * @private
1714  */
1715 cwi.smilText.Render.scrollticker = function(doc, layout, dir)
1716 {
1717 	// there is no layout .... yet
1718 	if (!document.getElementById(layout))
1719 		return;
1720 	
1721 	var mq = document.getElementById(layout).parentNode;
1722 	mq.style.overflow='hidden';
1723 	var mqPosition = mq.style.position;
1724 	mq.style.position='relative';
1725 	
1726 	for (var j = 0; j < mq.childNodes.length; j++)
1727 	{
1728 		var moveElem = mq.childNodes[j];
1729 		
1730 		var direction;
1731 		
1732 		if (dir == '1') { // move against the first writing direction
1733 			if (doc.getStoredAttribute(layout, 'textwritingmode') != undefined && doc.getStoredAttribute(layout, 'textwritingmode') != null)
1734 			{
1735 				switch(doc.getStoredAttribute(layout, 'textwritingmode'))
1736 				{
1737 					case 'lr-tb':
1738 					case 'lr':
1739 						direction = 'left';
1740 						break;
1741 					case 'rl-tb':
1742 					case 'rl':
1743 						direction = 'right';
1744 						break;
1745 					case 'tb-rl':
1746 					case 'tb-lr':
1747 						direction = 'up';
1748 					case 'inherit':
1749 						if (cwi.util.getStyleByElem(mq, 'writingMode') != undefined && 
1750 							cwi.util.getStyleByElem(mq, 'writingMode') != null &&
1751 							cwi.util.getStyleByElem(mq, 'writingMode').length > 0) {
1752 							if (cwi.util.getStyleByElem(mq, 'writingMode').charAt(0) == 'r')
1753 								direction = 'right';
1754 							else if (cwi.util.getStyleByElem(mq, 'writingMode').charAt(0) == 't')
1755 								direction = 'up';
1756 							else direction = 'left';
1757 						}
1758 						else direction = 'left';
1759 						break;
1760 				}
1761 			}
1762 			else{
1763 				if (cwi.util.getStyleByElem(mq, 'writingMode') != undefined && 
1764 					cwi.util.getStyleByElem(mq, 'writingMode') != null && 
1765 					cwi.util.getStyleByElem(mq, 'writingMode').length > 0) {
1766 					if (cwi.util.getStyleByElem(mq, 'writingMode').charAt(0) == 'r')
1767 						direction = 'right';
1768 					else if (cwi.util.getStyleByElem(mq, 'writingMode').charAt(0) == 't')
1769 						direction = 'up';
1770 					else direction = 'left';
1771 				}
1772 				else direction = 'left';
1773 			}
1774 		}
1775 		else if (dir == '2') { // move against the 2nd writing direction
1776 			if (doc.getStoredAttribute(layout, 'textwritingmode') != undefined && doc.getStoredAttribute(layout, 'textwritingmode') != null)
1777 			{
1778 				switch(doc.getStoredAttribute(layout, 'textwritingmode'))
1779 				{
1780 					case 'lr-tb':
1781 					case 'lr':
1782 					case 'rl-tb':
1783 					case 'rl':
1784 						direction = 'up';
1785 						break;
1786 					case 'tb-rl':
1787 						direction = 'right';
1788 						break;
1789 					case 'tb-lr':
1790 						direction = 'left';
1791 						break;
1792 					case 'inherit':
1793 						if (cwi.util.getStyleByElem(mq, 'writingMode') != undefined && 
1794 							cwi.util.getStyleByElem(mq, 'writingMode') != null &&
1795 							cwi.util.getStyleByElem(mq, 'writingMode').length > 0) {
1796 							if (cwi.util.getStyleByElem(mq, 'writingMode').charAt(0) == 'r' ||
1797 								cwi.util.getStyleByElem(mq, 'writingMode').charAt(0) == 'l')
1798 								direction = 'up';
1799 							else {
1800 								if (cwi.util.getStyleByElem(mq, 'writingMode') == 'tb-lr')
1801 									direction = 'left';
1802 								else direction = 'right';
1803 							}
1804 						}	
1805 						else direction = 'left';
1806 						break;
1807 				}
1808 			}
1809 			else{
1810 				if (cwi.util.getStyleByElem(mq, 'writingMode') != undefined && 
1811 					cwi.util.getStyleByElem(mq, 'writingMode') != null && 
1812 					cwi.util.getStyleByElem(mq, 'writingMode').length > 0) {
1813 					if (cwi.util.getStyleByElem(mq, 'writingMode').charAt(0) == 'r' ||
1814 						cwi.util.getStyleByElem(mq, 'writingMode').charAt(0) == 'l')
1815 						direction = 'up';
1816 					else {
1817 						if (cwi.util.getStyleByElem(mq, 'writingMode') == 'tb-lr')
1818 							direction = 'left';
1819 						else direction = 'right';
1820 					}
1821 				}
1822 				else direction = 'up';
1823 			}	
1824 		}
1825 			
1826 		// has textRate?
1827 		var currentCPS = cwi.smilText.Render.cps;
1828 		if (doc.getStoredAttribute(layout, 'textrate') != undefined && 
1829 			doc.getStoredAttribute(layout, 'textrate') != null &&
1830 			doc.getStoredAttribute(layout, 'textrate') != 'auto')
1831 		{
1832 			currentCPS = 0.05 * parseInt(doc.getStoredAttribute(layout, 'textrate'));
1833 		}
1834 		
1835 		if (moveElem.nodeType != 1);
1836 		else
1837 		{
1838 			var w = parseInt(cwi.util.getStyleByElem(mq, 'width'));
1839 			var h = parseInt(cwi.util.getStyleByElem(mq, 'height'));
1840 			moveElem.style.position = 'absolute';
1841 			var aw = moveElem.offsetWidth > w ? moveElem.offsetWidth : w;
1842 			var ah = moveElem.offsetHeight;
1843 			moveElem.style.position = 'relative';
1844 
1845 			if (!aw)
1846 			{
1847 				aw = moveElem.offsetWidth;
1848 			}
1849 			
1850 			if (!ah)
1851 			{
1852 				ah = moveElem.offsetHeight;
1853 			}
1854 			
1855 			if (!w) {
1856 				w = aw;
1857 			}
1858 			if (!h) {
1859 				h = ah;
1860 			}
1861 			
1862 			switch(direction)
1863 			{
1864 				case 'left':
1865 					if (!moveElem.style.left)
1866 					{
1867 						moveElem.style.left = 0;
1868 					}
1869 					
1870 					moveElem.style.left = parseInt(moveElem.style.left) > parseInt(-aw) ? 
1871 						parseFloat(moveElem.style.left) - currentCPS : w;
1872 					break;
1873 				case 'up':
1874 					if (!moveElem.style.top)
1875 					{
1876 						moveElem.style.top = 0;
1877 					}
1878 					
1879 					moveElem.style.top = parseInt(moveElem.style.top) > parseInt(-ah) ? 
1880 						parseFloat(moveElem.style.top) - currentCPS : h;
1881 					break;
1882 				case 'right':
1883 					if (!moveElem.style.left)
1884 					{
1885 						//moveElem.style.left = parseInt(getStyleByElem(moveElem, 'left')) + w;
1886 						moveElem.style.left = 0;
1887 					}
1888 					
1889 					moveElem.style.left = parseInt(moveElem.style.left) < parseInt(w) ? 
1890 						parseFloat(moveElem.style.left) + currentCPS : -aw;
1891 					break;
1892 				case 'down': // not used
1893 					break;
1894 			}
1895 		}
1896 
1897 	}
1898 	
1899 	mq.style.position=mqPosition;
1900 
1901 }
1902 
1903 
1904 
1905 
1906 
1907 /**
1908  * Hold the SmilText Document Style
1909  * @private
1910  * @constructor
1911  * @augments cwi.smilText.Style
1912  * @author <a href="mailto:rlaiola@cwi.nl">Rodrigo Laiola Guimaraes</a>
1913  */
1914 cwi.smilText.Style = function()
1915 {
1916 	var self = JSINER.extend(this);
1917 	
1918 	/** 
1919 	* Variables
1920 	* @private
1921 	*/
1922 	this.stylingHash = new Hashtable();		// Hold styling attributes
1923 	
1924 	return self;
1925 }
1926 
1927 /**
1928  * Return the value of a stored style attribute.
1929  * @private
1930  * @param {string} layout the region id
1931  * @param {string} attr the attribute name
1932  * @return {string} val the attribute value
1933  */
1934 cwi.smilText.Style.prototype.getAttribute = function(layout, attr)
1935 {
1936 	return this.stylingHash.get(layout + attr);
1937 }
1938 
1939 /**
1940  * Save a style attribute of a given layout element.
1941  * @private
1942  * @param {string} layout the region id
1943  * @param {string} attr the attribute name
1944  * @param {string} val the attribute value
1945  */
1946 cwi.smilText.Style.prototype.updateAttribute = function(layout, attr, val)
1947 {
1948 	this.stylingHash.put(layout + attr, val);
1949 }
1950 
1951 /**
1952  * Transfer the properties from a given style to a layout.
1953  * @private
1954  */
1955 cwi.smilText.Style.prototype.toXML = function(typeLayout, id, style)
1956 {
1957 	var str = "";
1958 	
1959 	switch(typeLayout)
1960 	{
1961 		case 'smiltext':
1962 			this.updateAttribute(id, 'dur', this.getStoredAttribute(style, 'dur'));
1963 			this.updateAttribute(id, 'width', this.getStoredAttribute(style, 'width'));
1964 			this.updateAttribute(id, 'height', this.getStoredAttribute(style, 'height'));
1965 		case 'textstyle':
1966 			this.updateAttribute(id, 'textmode', this.getStoredAttribute(style, 'textmode'));
1967 			this.updateAttribute(id, 'textplace', this.getStoredAttribute(style, 'textplace'));
1968 			this.updateAttribute(id, 'textconceal', this.getStoredAttribute(style, 'textconceal'));
1969 			this.updateAttribute(id, 'textrate', this.getStoredAttribute(style, 'textrate'));
1970 		case 'div':
1971 			this.updateAttribute(id, 'textalign', this.getStoredAttribute(style, 'textalign'));
1972 		case 'p':
1973 			this.updateAttribute(id, 'xml:space', this.getStoredAttribute(style, 'xml:space'));
1974 			this.updateAttribute(id, 'textwrapoption', this.getStoredAttribute(style, 'textwrapoption'));
1975 			this.updateAttribute(id, 'textwritingmode', this.getStoredAttribute(style, 'textwritingmode'));
1976 		case 'span':
1977 			this.updateAttribute(id, 'textbackgroundcolor', this.getStoredAttribute(style, 'textbackgroundcolor'));
1978 			this.updateAttribute(id, 'textcolor', this.getStoredAttribute(style, 'textcolor'));
1979 			this.updateAttribute(id, 'textfontfamily', this.getStoredAttribute(style, 'textfontfamily'));
1980 			this.updateAttribute(id, 'textfontsize', this.getStoredAttribute(style, 'textfontsize'));
1981 			this.updateAttribute(id, 'textfontstyle', this.getStoredAttribute(style, 'textfontstyle'));
1982 			this.updateAttribute(id, 'textfontweight', this.getStoredAttribute(style, 'textfontweight'));
1983 			this.updateAttribute(id, 'textstyle', this.getStoredAttribute(style, 'textstyle'));
1984 
1985 			if (typeLayout != 'span' && typeLayout != 'textstyle')
1986 				this.updateAttribute(id, 'textdirection', this.getStoredAttribute(style, 'textdirection'));
1987 	}
1988 	
1989 	return str;
1990 }
1991