mypackage_wrt/Forums.js
changeset 42 20be4dd42b12
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mypackage_wrt/Forums.js	Fri Apr 30 15:01:03 2010 +0100
@@ -0,0 +1,567 @@
+// ////////////////////////////////////////////////////////////////////////////
+// Symbian Foundation Example Code
+//
+// This software is in the public domain. No copyright is claimed, and you 
+// may use it for any purpose without license from the Symbian Foundation.
+// No warranty for any purpose is expressed or implied by the authors or
+// the Symbian Foundation. 
+// ////////////////////////////////////////////////////////////////////////////
+
+// Forums
+
+// Forums have the following structure:
+//
+// Forum group list
+//   Forum list
+//     Thread list
+//       Message list
+
+// All four views are based on customised RssReader. We customise two aspects:
+// - Parsing XML - data is not RSS
+// - Handling item selection (e.g. creating a new view for a newly selected forum)
+
+// /////////////////////////////////////////////////////////////////////////////
+// Forum groups
+
+// response parser for forum groups
+
+var feedItemControl;
+
+function forumGroupsResponseParser(broker, responseStatus, xmlDoc) {
+    if (responseStatus == 200 && xmlDoc != null) {
+        // node ref for iterating
+        var node;
+
+		// for compatibility with rss
+		var lastModified = new Date();
+		
+        // init result items array
+        var items = [];
+
+		var elements = xmlDoc.getElementsByTagName("group");
+		for (var i = 0; i < elements.length; i++) {
+			var groupid = elements[i].getAttribute("id");
+			var grouptitle = elements[i].getAttribute("title");
+            items.push({ id: groupid, title: grouptitle});
+		}
+
+        // update was completed successfully
+        return { status: "ok", lastModified: lastModified, items: items };
+    } else {
+        // update failed
+        return { status: "error" };
+    }
+}
+
+// FeedPresenter implementation for forum groups
+function ForumGroupsFeedPresenter(rssreader){
+	if (rssreader) {
+		this.init(rssreader);
+	}
+}
+
+// ForumGroupsFeedPresenter is a subclass of ButtonFeedPresenter
+ForumGroupsFeedPresenter.prototype = new ButtonFeedPresenter(null);
+
+// ForumGroupsFeedPresenter "Constructor"
+ForumGroupsFeedPresenter.prototype.init = function(rssreader) {
+	ButtonFeedPresenter.prototype.init.call(this, rssreader);
+}
+
+// Handle the click on a specific item
+ForumGroupsFeedPresenter.prototype.feedClicked = function(event){
+	var buttonid = event.source.id;
+	
+	if (buttonid == "latestPosts") {
+		// show latest posts
+		var url = forumFeedURL;
+		var latestPostsView = new RssReader("Latest posts", url, new LatestPostsFeedPresenter(null), this.rssreader, null);
+		latestPostsView.show();
+	}
+	else {
+		// show forum group
+		var groupid = this.items[buttonid].id;
+		var grouptitle = this.items[buttonid].title;
+		
+		var url = forumsListUrl + groupid;
+		var forumListView = new RssReader(grouptitle, url, new ForumsListFeedPresenter(null), this.rssreader, forumListResponseParser);
+		forumListView.show();
+	}
+}
+
+// Create and add controls to be shown before items list.
+ForumGroupsFeedPresenter.prototype.addPreambleItems = function(){
+	var feedItemControl = new NavigationButton("latestPosts", "blueright.gif", "Latest posts");
+    var self = this;
+	feedItemControl.addEventListener("ActionPerformed", function(event) { self.feedClicked(event); });
+	this.rssreader.addControl(feedItemControl);
+}
+
+
+// ///////////////////////////////////////////////////////////////////////////
+// List of forums in a group
+
+// response parser for forum list - in a group
+function forumListResponseParser(broker, responseStatus, xmlDoc) {
+    if (responseStatus == 200 && xmlDoc != null) {
+        // node ref for iterating
+        var node;
+
+		// for compatibility with rss
+		var lastModified = new Date();
+		
+        // init result items array
+        var items = [];
+
+		// extract items for all group elements
+		var elements = xmlDoc.getElementsByTagName("group");
+		for (var i = 0; i < elements.length; i++) {
+			var forumid = elements[i].getAttribute("id");
+			var forumtitle = elements[i].getAttribute("title");
+            items.push({ id: forumid, title: forumtitle});
+		}
+
+        // update was completed successfully
+        return { status: "ok", lastModified: lastModified, items: items };
+    } else {
+        // update failed
+        return { status: "error" };
+    }
+}
+
+// FeedPresenter implementation for forum groups
+function ForumsListFeedPresenter(rssreader){
+	if (rssreader) {
+		this.init(rssreader);
+	}
+}
+
+// ForumsListFeedPresenter is a subclass of ButtonFeedPresenter
+ForumsListFeedPresenter.prototype = new ButtonFeedPresenter(null);
+
+// ForumsListFeedPresenter constructor
+ForumsListFeedPresenter.prototype.init = function(rssreader) {
+	ButtonFeedPresenter.prototype.init.call(this, rssreader);
+}
+
+
+// forum has been selected, create a reader showing threads in the forum
+ForumsListFeedPresenter.prototype.feedClicked = function(event){
+	var buttonid = event.source.id;
+	if (buttonid == "latestPosts") {
+		// show latest posts
+		var url = forumFeedURL + "&forumids=";
+		// append requested forum ids
+		for( var i = 0; i < this.items.length; i++) {
+			url += this.items[i].id + ",";
+		}
+		
+		var latestPostsView = new RssReader(
+			"Latest posts in " + this.rssreader.feedName, 
+			url, 
+			new LatestPostsFeedPresenter(null), 
+			this.rssreader, 
+			null);
+		latestPostsView.show();
+	}
+	else {
+		var forumid = this.items[buttonid].id;
+		var forumtitle = this.items[buttonid].title;
+		
+		var url = forumFeedURL + forumsForumSpecQuery + forumid;
+		var forumListView = new RssReader(forumtitle, url, new ThreadListFeedPresenter(null), this.rssreader, null);
+		forumListView.show();
+	}
+}
+
+// Create and add controls to be shown before items list.
+ForumsListFeedPresenter.prototype.addPreambleItems = function(){
+	var feedItemControl = new NavigationButton("latestPosts", "blueright.gif", "Latest posts in " + this.rssreader.feedName);
+    var self = this;
+	feedItemControl.addEventListener("ActionPerformed", function(event) { self.feedClicked(event); });
+	this.rssreader.addControl(feedItemControl);
+}
+
+//// // FeedPresenter implementation for forum groups
+function ForumsSettingsFeedPresenter(rssreader){
+	if (rssreader) {
+		this.init(rssreader);
+	}
+}
+
+// ForumsListFeedPresenter is a subclass of ButtonFeedPresenter
+ForumsSettingsFeedPresenter.prototype = new ButtonFeedPresenter(null);
+
+// ForumsListFeedPresenter constructor
+ForumsSettingsFeedPresenter.prototype.init = function(rssreader) {
+	ButtonFeedPresenter.prototype.init.call(this, rssreader);
+}
+
+
+// forum has been selected, create a reader showing threads in the forum
+ForumsSettingsFeedPresenter.prototype.feedClicked = function(event){
+	var buttonid = event.source.id;
+	    var firstboot =true;
+		var forumid = this.items[buttonid].id;
+		var forumtitle = this.items[buttonid].title;
+		if(myforumid){firstboot=false;}
+		
+		feedItemControl.setText(forumtitle + ": " + forumid);
+		ForumControl.setText("Forum: " + forumtitle);
+		myforumid=forumid;
+		myforumtitle=forumtitle;
+		if (firstboot){
+			forum_reader = new RssReader(myforumtitle, forumFeedURL + forumsForumSpecQuery + myforumid, new ThreadListFeedPresenter(null), bugzilla, null);
+			forum_reader.show();
+			savePreferences();
+		} else {
+			settings.show();
+			
+		}
+}
+
+// Create and add controls to be shown before items list.
+ForumsSettingsFeedPresenter.prototype.addPreambleItems = function(){
+	feedItemControl = new Label(null, "Choosen forum:");
+    var self = this;
+	this.rssreader.addControl(feedItemControl);
+}
+
+
+
+// ///////////////////////////////////////////////////////////////////////////
+// List of threads in a forum
+
+// response parser for thread list is the usual rss parser
+
+// FeedPresenter implementation for forum groups
+function ThreadListFeedPresenter(rssreader){
+	if (rssreader) {
+		this.init(rssreader);
+	}
+}
+
+// ThreadListFeedPresenter is a subclass of ButtonFeedPresenter
+ThreadListFeedPresenter.prototype = new ButtonFeedPresenter(null);
+
+// ThreadListFeedPresenter constructor
+ThreadListFeedPresenter.prototype.init = function(rssreader) {
+	ButtonFeedPresenter.prototype.init.call(this, rssreader);
+}
+
+
+// Handle the click on a specific item
+ThreadListFeedPresenter.prototype.feedClicked = function(event){
+	var buttonid = event.source.id;
+	
+	if (buttonid == "newThread") {
+		// extract forum id from rssreader.feedURL
+		var ind = this.rssreader.feedURL.indexOf(forumsForumSpecQuery);
+		var forumid = this.rssreader.feedURL.substring( ind + forumsForumSpecQuery.length);
+		var postForm = new ForumPostForm(this.rssreader, forumid);
+		postForm.show();
+	}
+	else {
+		var weburl = this.items[buttonid].url;
+		
+		// extract thread id from url. looking for t=xxx
+		var ind1 = weburl.indexOf("?t=");
+		if (ind1 == -1) {
+			ind1 = weburl.indexOf("&t=");
+		}
+		if (ind1 != -1) {
+			var threadid = "";
+			var ind2 = weburl.indexOf("&", ind1);
+			if (ind2 == -1) {
+				threadid = weburl.substring(ind1 + 3); // ?t=
+			}
+			else {
+				threadid = weburl.substring(ind1 + 3, ind2); // ?t=
+			}
+			var url = forumThreadUrl + threadid;
+			var title = this.items[buttonid].title;
+			if (title.length > 30) {
+				title = title.substring(0, 30) + "...";
+			}
+			var threadView = new RssReader(title, url, new ThreadFeedPresenter(null), this.rssreader, threadResponseParser);
+			threadView.show();
+		}
+	}
+}
+
+// Create and add controls to be shown before items list.
+ThreadListFeedPresenter.prototype.addPreambleItems = function(){
+	var feedItemControl = new NavigationButton("newThread", "blueright.gif", "Post a new thread");
+    var self = this;
+	feedItemControl.addEventListener("ActionPerformed", function(event) { self.feedClicked(event); });
+	this.rssreader.addControl(feedItemControl);
+}
+
+// ///////////////////////////////////////////////////////////////////////////
+// List of messages in a thread
+
+// response parser for thread list
+function threadResponseParser(broker, responseStatus, xmlDoc) {
+    if (responseStatus == 200 && xmlDoc != null) {
+        // node ref for iterating
+        var node;
+
+		// for compatibility with rss
+		var lastModified = new Date();
+		
+        // init result items array
+        var items = [];
+
+		// iterate over message elements
+		var elements = xmlDoc.getElementsByTagName("message");
+		for (var i = 0; i < elements.length; i++) {
+			var postid;
+			var threadid;
+			var username;
+			var title;
+			var dateline;
+			var pagetext;
+			var isdeleted;
+			
+			// extract info about the post
+			node = elements[i].firstChild;
+			while (node != null) {
+				if ( node.nodeName == "postid" ) postid=getTextOfNode(node);
+				else if ( node.nodeName == "threadid" ) threadid=getTextOfNode(node);
+				else if ( node.nodeName == "username" ) username=getTextOfNode(node);
+				else if ( node.nodeName == "title" ) title=getTextOfNode(node);
+				else if ( node.nodeName == "dateline" ) dateline=getTextOfNode(node);
+				else if ( node.nodeName == "pagetext" ) pagetext=getTextOfNode(node);
+				else if ( node.nodeName == "isdeleted" ) isdeleted=getTextOfNode(node);
+				node = node.nextSibling;
+			}
+			if ( isdeleted == 1 ) continue;
+			
+			items.push({
+				postid: postid,
+				threadid: threadid,
+				username: username,
+				title: title,
+				dateline: dateline,
+				pagetext: pagetext
+			});
+		}
+
+        // update was completed successfully
+        return { status: "ok", lastModified: lastModified, items: items };
+    } else {
+        // update failed
+        return { status: "error" };
+    }
+}
+
+// FeedPresenter implementation for forum groups
+function ThreadFeedPresenter(rssreader){
+	if (rssreader) {
+		this.init(rssreader);
+	}
+}
+
+// ThreadFeedPresenter is a subclass of HtmlFeedPresenter
+ThreadFeedPresenter.prototype = new HtmlFeedPresenter(null);
+
+// ThreadFeedPresenter constructor
+ThreadFeedPresenter.prototype.init = function(rssreader) {
+	HtmlFeedPresenter.prototype.init.call(this, rssreader);
+}
+
+
+// Handle the click on a specific item
+ThreadFeedPresenter.prototype.feedClicked = function(event){
+	// do nothing
+}
+
+// Create a control that represents this item and add it to
+// parent rss reader
+ThreadFeedPresenter.prototype.show = function(item) {
+	// get a feed item control from the pool or create one and
+	// place it in the pool if there aren't enough feed item controls
+	var feedItemControl = new ContentPanel(null, null, null, true);
+
+	// initialize feed item control
+	var title = item.title;
+	if ( !title || title.length == 0 ) { 
+		title = "Re:";
+		item.title = title;
+	}
+	feedItemControl.setCaption(bbcode2html(title));
+	feedItemControl.setContent(this.getContentHTMLForFeedItem(item));
+	feedItemControl.setExpanded(true);
+	
+	// add the feed item control to the main view
+	this.rssreader.feedItemControls.push(feedItemControl);
+	this.rssreader.addControl(feedItemControl);
+}
+
+// Generate HTML content from the feed item
+ThreadFeedPresenter.prototype.getContentHTMLForFeedItem = function (item){
+	var buf = "";
+	
+	// item date
+	if (item.dateline != null) {
+		var date = new Date();
+		date.setTime(item.dateline*1000);
+		buf += "<div class=\"FeedItemDate\">" ;
+		if ( item.username != null ) {
+			buf += item.username + ", ";
+		}
+		buf += date + "</div>";
+	}
+	
+	// item description
+	if (item.pagetext != null) {
+		var text = bbcode2html(item.pagetext);
+		text = text.replace(/\r\n/g, "<br>");
+		buf += "<div class=\"FeedItemDescription\">" + text + "</div>";
+        buf += "<div class=\"FeedItemLink\">";
+		buf += "<a href=\"JavaScript:void(0)\" onclick=\"showReplyForm(" 
+				+ item.threadid+ "," + item.postid + ", '" + item.title
+				+ "'); return false;\">";
+		buf += "<strong>Reply to this post<strong></a>"
+		buf += "</div>";
+	}
+	
+	return buf;
+}
+
+// Show the reply-to-post form
+function showReplyForm(threadid, postid, title) {
+	var replyForm = new ForumReplyForm(uiManager.currentView, threadid, postid, title);
+	replyForm.show();
+}
+
+
+// ///////////////////////////////////////////////////////////////////////////
+// Latest posts - same as ThreadListFeedPresenter, only has no preamble items
+// because it doesn't show one thread (so we can't post to latest items)...
+
+// FeedPresenter implementation for latest posts
+function LatestPostsFeedPresenter(rssreader){
+	if (rssreader) {
+		this.init(rssreader);
+	}
+}
+
+LatestPostsFeedPresenter.prototype = new ThreadListFeedPresenter(null);
+
+// ForumGroupsFeedPresenter "Constructor"
+LatestPostsFeedPresenter.prototype.init = function(rssreader) {
+	ButtonFeedPresenter.prototype.init.call(this, rssreader);
+}
+
+// LatestPostsFeedPresenter has no preamble items
+LatestPostsFeedPresenter.prototype.addPreambleItems = function(){
+}
+
+
+// ///////////////////////////////////////////////////////////////////////////
+// Utilities
+
+
+// Forum posts can be be quite messy and include bbcodes, smilies etc.
+// This function does the minimum by stripping bbcodes and such
+function sanitize(text) {
+	var prevind = 0;
+	var ind = text.indexOf("[");
+	if ( ind == -1 ) return text;
+	var buf = "";
+	while ( ind != -1 ) {
+		buf += text.substring(prevind, ind);
+		var ind2 = text.indexOf("]", ind);
+		if ( ind2 != -1 ) {
+			prevind = ind2+1;
+		} else {
+			break;
+		}
+		ind = text.indexOf("[", prevind);
+	}
+	if ( prevind > 0 && prevind < text.length) {
+		buf += text.substring(prevind);
+	}
+	return buf;
+}
+
+
+
+// feeds contain bbcodes - this function should turn the bbcode markup
+// to HTML
+function bbcode2html(s) {
+	var prevind = 0;
+	var buf = "";
+	var ind = s.indexOf("[");
+	if ( ind == -1 ) return s;
+	while ( ind != -1 ) {
+		buf += s.substring(prevind, ind);
+		var ind2 = s.indexOf("]", ind); // end of tag
+		var fulltag = s.substring(ind+1,ind2);
+		var tag = fulltag;
+		var ind3 = s.indexOf("=", ind); // end of tag name, eg. [URL=http...]
+		if ( ind3 != -1 && ind3 < ind2) {
+			tag = s.substring(ind+1,ind3);
+		} 
+		var ind4 = s.indexOf("[/"+tag+"]", ind2);
+		var tagContent = s.substring(ind2+1, ind4);
+		buf += convertTag(tag, fulltag, tagContent);
+		if ( ind4 != -1 ) {
+			prevind = s.indexOf(']',ind4) + 1;
+		} else {
+			break;
+		}
+		ind = s.indexOf("[", prevind);
+	}
+	buf += s.substring(prevind);
+	return buf;
+}
+
+function convertTag(tag, fulltag, tagContent) {
+	tag = tag.toLowerCase();
+	var param = null;
+	var eqsign = fulltag.indexOf("=");  // onclick=\"openURL('" + item.url + "');
+	if (eqsign > -1) {
+		param = fulltag.substring(eqsign+1);
+	}
+	switch(tag) {
+		case '*': return bbcode2html(tagContent);
+		case 'b':case 'i':case 'u':case 's':case 'sup':case 'sub':case 'h1':case 'h2':case 'h3':case 'h4':case 'h5':case 'h6':case 'table':case 'tr':case 'th':case 'td':
+		{
+			return '<' + tag + '>' + bbcode2html(tagContent) + "</" + tag + ">";
+		}
+		case 'font': return '<font face="'+param+'">' + bbcode2html(tagContent) + '</font>';
+		case 'size': return '<font size="'+param+'">' + bbcode2html(tagContent) + '</font>';
+		case 'color': return '<font color="'+param+'">' + bbcode2html(tagContent) + '</font>';
+		case 'left': return '<div align="left">' + bbcode2html(tagContent) + '</div>';
+		case 'right': return '<div align="right">' + bbcode2html(tagContent) + '</div>';
+		case 'center': return '<div align="center">' + bbcode2html(tagContent) + '</div>';
+		case 'list':{
+			tagContent = tagContent.replace(/\[\*\]/g, "<br>&bull; ");
+			return bbcode2html(tagContent); // todo
+		}
+		case 'php': 
+		case 'code': 
+		case 'html':{
+			var escaped = tagContent.replace(/</g, "&lt;").replace(/>/g, "&gt;");
+			return '<div class=codebox><pre>' + escaped + '</pre></div>';
+		}
+		case 'quote': return '<div class=codebox><b>Quote:</b><br><i>' + tagContent + '</i></div>';
+		case 'url': {
+			if ( eqsign > -1 ) {
+				return "<div class=\"FeedItemLink\"><a href=\"JavaScript:void(0)\" onclick=\"openURL( '"
+				+ param
+				+ "')\" ><i>"
+				+ tagContent
+				+ '</i></a></div>'; 
+			} else {
+				return "<div class=\"FeedItemLink\"><a href=\"JavaScript:void(0)\" onclick=\"openURL( '"
+				+ tagContent
+				+ "')\" ><i>"
+				+ tagContent
+				+ '</i></a></div>'; 
+			}
+		}
+	}
+}