/**
 * Konstruktor für den AjaxFeedReader
 * 
 * Der AjaxFeedReader benötigt Prototype
 * 
 * @param containerElement Element in das der Newsfeed gerendert werden soll
 * @param feedUrl Die URL des RSS- oder Atom-Feeds
 * @param maxItems Die maximale Anzahl der zu rendernden Newsitems
 * @param showDate Soll das Änderungsdatum bei den Items angezeigt werden? true oder false.
 * @param showTime Soll die Änderungszeit bei den Items angezeigt werden? true oder false.
 * @param showDescription Soll die Beschreibung angezeigt werden?
 * 			true: Ja, Klick auf den Titel klappt die Beschreibung auf.
 * 			false: Nein, Titel ist Link.
 * @param errorHtml HTML-Quelltext, der im Fehlerfall angezeigt wird
 * @param loadingText Text (ohne Auszeichnung), der beim Laden angezeigt wird.
 * 			false oder leerer String: Keine Ladeanimation anzeigen
 * 
 * @author (C)2009 Chamaeleon AG
 */
function AjaxFeedReader(containerElement, feedUrl, maxItems, showDate, showTime, showDescription, errorHtml, loadingText) {
	this.containerElement = containerElement;
	this.feedUrl = feedUrl;
	this.maxItems = maxItems;
	this.showDate = showDate;
	this.showTime = showTime;
	this.showDescription = showDescription;
	this.errorHtml = errorHtml;
	
	this.numLoadingPeriods = 0;
	this.periodicalExecuter = 0;
	this.loadingText = loadingText;
	
	this.update();
}

/**
 * Lade den Newsfeed per Ajax.
 */
AjaxFeedReader.prototype.update = function() {
	// Starte die Ladeanimation
	this.updateLoadingText();
	if(this.periodicalExecuter == 0 && this.loadingText) {
		this.periodicalExecuter = new PeriodicalExecuter(this.updateLoadingText.bind(this), 0.2);
	}
	
	// Newsfeed laden
	new Ajax.Request(
		this.feedUrl, {
			method: 'get',
			onSuccess: this.processResponse.bind(this),
			onFailure: this.processError.bind(this)
		}
	);
}

AjaxFeedReader.prototype.updateLoadingText = function() {
	if(this.numLoadingPeriods == 0) {
		this.containerElement.innerHTML = this.loadingText;
	} else {
		this.containerElement.innerHTML += ".";
	}
	this.numLoadingPeriods = (this.numLoadingPeriods+1)%4;
}

AjaxFeedReader.prototype.processError = function(transport) {
	if(this.periodicalExecuter != 0) {
		this.periodicalExecuter.stop();
		this.periodicalExecuter = 0;
	}
	this.containerElement.innerHTML = this.errorHtml;
}

AjaxFeedReader.prototype.processResponse = function(transport) {
	// Stoppe die Ladeanimation
	if(this.periodicalExecuter != 0) {
		this.periodicalExecuter.stop();
		this.periodicalExecuter = 0;
	}
	this.containerElement.innerHTML = '';

	// Parse die Ajax-Antwort als XML (getrennt für IE/FF)
	if (window.ActiveXObject) {
		var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
		xmlDoc.async = "false";
		xmlDoc.loadXML(transport.responseText);
	} else {
		var xmlDoc = (new DOMParser()).parseFromString(transport.responseText, "text/xml");
	}
	
	if(!xmlDoc || !xmlDoc.documentElement) {
		this.containerElement.innerHTML = this.errorHtml;
		return;
	}
	xml = xmlDoc.documentElement;
	
	// Extrahiere die relevanten Informationen aus dem Feed
	var items = this.munchFeed(xml);
	if(!items) {
		this.containerElement.innerHTML = this.errorHtml;
		return;
	}
	
	// Erzeuge die HTML-Elemente aus den Feed-Elementen
	for(var i=0;i<Math.min(items.length,this.maxItems);i++) {
		var item = items[i];
		var pubDate = item["pubDate"];
		var pubDateText = "";
		var title;
		
		// Datum formatieren
		if(this.showDate && pubDate) {
			pubDateText = (pubDate.getDate()).toPaddedString(2) +'.'+ (pubDate.getMonth()+1).toPaddedString(2) +'.'+ pubDate.getFullYear(); 
		}
		if(this.showTime && pubDate) {
			pubDateText += ' '+ (pubDate.getHours()).toPaddedString(2)+':'+ (pubDate.getMinutes()).toPaddedString(2);
		}
		
		var itemContainer = new Element('div', {'class':'feedItem'});
		var dateContainer = new Element('div', {'class':'feedItemDate'}).update(pubDateText);
		itemContainer.appendChild(dateContainer);
		
		var titleContainer = new Element('div', {'class':'feedItemTitle'});
		itemContainer.appendChild(titleContainer);
		
		if (this.showDescription) {
			title = new Element('span');
			var descriptionContainer = new Element('div', {'class':'feedItemDescription', 'style':'display:none'});
			itemContainer.appendChild(descriptionContainer);
			if(item["description"]) {
				// Entferne sämtliche Auszeichnung aus der Itembeschreibung, aber versuche dabei Absätze zu erhalten.
				descriptionContainer.innerHTML=item["description"].replace(/\n/g, "").replace(/<br\s*\/?>/ig,":::CHAM_NEWLINE:::").replace(/<\/?p\s*>/ig,":::CHAM_NEWLINE:::");
				if(descriptionContainer.innerText) {
					descriptionContainer.innerHTML="<p>"+descriptionContainer.innerText
						.replace(/^\s*/,"")
						.replace(/\s*$/,"")
						.replace(/\s*(:::CHAM_NEWLINE:::\s*)+/g, "</p><p>")+"</p>";
				} else if(descriptionContainer.textContent) {
					descriptionContainer.innerHTML="<p>"+descriptionContainer.textContent
						.replace(/^\s*/,"")
						.replace(/\s*$/,"")
						.replace(/\s*(:::CHAM_NEWLINE:::\s*)+/g, "</p><p>")+"</p>";
				}
			}
			
			Event.observe(titleContainer, 'click', function() {var el = this.nextSibling;if(el.visible()){el.hide();} else {el.show();}});
			if (item["link"]) {
				var a = new Element('a', {'href':item["link"], 'target':'_blank'}).update("Weiterführender Link");
				descriptionContainer.appendChild(a);	
			}
		} else if (item["link"]){
			title = new Element('a', {'href':item["link"], 'target':'_blank'});
		} else {
			title = new Element('span');
		}
		titleContainer.appendChild(title);
		title.update(item["title"]);
		
		this.containerElement.appendChild(itemContainer);
	}
}

/**
 * Werte einen Feed aus und gib den Inhalt als Array von Objekten zurück.
 * Die Objekte haben folgende Attribute, je nach Verfügbarkeit der Information:
 * title, description, pubDate, link
 */
AjaxFeedReader.prototype.munchFeed = function(xmlFeed) {
	var items = $A(xmlFeed.getElementsByTagName('item'));
	var resultItems = new Array();
	
	// RSS legt die Einträge in <item>-Tags ab, ATOM in <entry>-Tags.
	if(items.length<=0) {
		var entries = $A(xmlFeed.getElementsByTagName('entry'));
		if(entries.length<=0) {
			return false;
		} else {
			items = entries;
		}
	}
	
	for(var i=0;i<items.length;i++) {
		var resultItem = new Object();
		var item=items[i];
		
		// Wie gut passen die bisher gefundenen Angaben für unseren Feed-Reader?
		var linkScore = 0;
		var dateScore = 0;
		
		for(var e = item.firstChild; e; e = e.nextSibling) {
			// Langbeschreibung oder Inhalt des Eintrags (RSS)
			if(e.nodeName == 'description') {
				resultItem["description"] = this.getContainedText(e);
			}
			
			// Langbeschreibung oder Inhalt des Eintrags (ATOM)
			if(e.nodeName == 'content') {
				resultItem["description"] = this.getContainedText(e);
			}
			
			// Verweis auf weitere Informationen (RSS/ATOM)
			if(e.nodeName == 'link') {
				if(e.getAttribute('href')) {
					var atomScore = 0;
					// ATOM-Link.
					if(!e.getAttribute('rel') || e.getAttribute('rel') == 'alternate') {
						atomScore = 10;
					} else if(e.getAttribute('rel') == 'self') {
						atomScore = 9;
					} if(e.getAttribute('rel') == 'related') {
						atomScore = 6;
					} else if(e.getAttribute('rel') == 'comments') {
						atomScore = 5;
					} else if(e.getAttribute('rel') == 'enclosure') {
						atomScore = 4;
					}
					if(atomScore > linkScore) {
						resultItem["link"] = e.getAttribute('href');
						linkScore = atomScore;
					}
				} else {
					if(linkScore<10) {
						resultItem["link"] = e.firstChild.nodeValue;
						linkScore = 10;
					}
				}
			}
			
			// Datum der Veröffentlichung (RSS)
			if(e.nodeName == 'pubDate') {
				if(dateScore<10) {
					resultItem["pubDate"] = new Date(e.firstChild.nodeValue);
					dateScore = 10;
				}
			}
			
			// Datum der Veröffentlichung (ATOM)
			if(e.nodeName == 'published' || e.nodeName == 'updated') {
				var atomScore = e.nodeName == 'published' ? 5 : 10;
				var dateExp = /(\d{4})(-)?(\d{2})(-)?(\d{2})(T)?(\d{2})(:)?(\d{2})(:)?(\d{2})(\.\d+)?(Z|([+-])(\d{2})(:)?(\d{2}))/;
				if(atomScore > dateScore && e.firstChild.nodeValue.match(new RegExp(dateExp))) {
					var pubDate = new Date();
					var dt = e.firstChild.nodeValue.match(new RegExp(dateExp));
					
					pubDate.setUTCDate(1);
					pubDate.setUTCFullYear(parseInt(dt[1],10));
					pubDate.setUTCMonth(parseInt(dt[3],10) - 1);
					pubDate.setUTCDate(parseInt(dt[5],10));
					pubDate.setUTCHours(parseInt(dt[7],10));
					pubDate.setUTCMinutes(parseInt(dt[9],10));
					pubDate.setUTCSeconds(parseInt(dt[11],10));
					if (dt[12]) {
						pubDate.setUTCMilliseconds(parseFloat(dt[12]) * 1000);
					} else {
						pubDate.setUTCMilliseconds(0);
					}
					if (dt[13] != 'Z') {
						var offset = (dt[15] * 60) + parseInt(dt[17],10);
						offset *= ((dt[14] == '-') ? -1 : 1);
						pubDate.setTime(pubDate.getTime() - offset * 60 * 1000);
					}
					resultItem["pubDate"] = pubDate;
					dateScore = atomScore;
				}
			}
			
			// Überschrift des Eintrags (RSS/ATOM)
			if(e.nodeName == 'title') {
				resultItem["title"] = e.firstChild.nodeValue;
			}
		}
		resultItems.push(resultItem);
	}
	return resultItems;
}

/**
 * Lese den gesamten Textinhalt aus einem XML-Element
 */
AjaxFeedReader.prototype.getContainedText = function(elem) {
	var result = "";
	for(var child = elem.firstChild; child != null; child=child.nextSibling) {
		if(child.nodeType == 3 || child.nodeType==4) {
			result += child.nodeValue;
		} else if(child.nodeType == 1) {
			result += this.getContainedText(child);
		}
	}
	return result;
}
