|
1 // //////////////////////////////////////////////////////////////////////////// |
|
2 // Symbian Foundation Example Code |
|
3 // |
|
4 // This software is in the public domain. No copyright is claimed, and you |
|
5 // may use it for any purpose without license from the Symbian Foundation. |
|
6 // No warranty for any purpose is expressed or implied by the authors or |
|
7 // the Symbian Foundation. |
|
8 // //////////////////////////////////////////////////////////////////////////// |
|
9 |
|
10 // Forums |
|
11 |
|
12 // Forums have the following structure: |
|
13 // |
|
14 // Forum group list |
|
15 // Forum list |
|
16 // Thread list |
|
17 // Message list |
|
18 |
|
19 // All four views are based on customised RssReader. We customise two aspects: |
|
20 // - Parsing XML - data is not RSS |
|
21 // - Handling item selection (e.g. creating a new view for a newly selected forum) |
|
22 |
|
23 // ///////////////////////////////////////////////////////////////////////////// |
|
24 // Forum groups |
|
25 |
|
26 // response parser for forum groups |
|
27 |
|
28 var feedItemControl; |
|
29 |
|
30 function forumGroupsResponseParser(broker, responseStatus, xmlDoc) { |
|
31 if (responseStatus == 200 && xmlDoc != null) { |
|
32 // node ref for iterating |
|
33 var node; |
|
34 |
|
35 // for compatibility with rss |
|
36 var lastModified = new Date(); |
|
37 |
|
38 // init result items array |
|
39 var items = []; |
|
40 |
|
41 var elements = xmlDoc.getElementsByTagName("group"); |
|
42 for (var i = 0; i < elements.length; i++) { |
|
43 var groupid = elements[i].getAttribute("id"); |
|
44 var grouptitle = elements[i].getAttribute("title"); |
|
45 items.push({ id: groupid, title: grouptitle}); |
|
46 } |
|
47 |
|
48 // update was completed successfully |
|
49 return { status: "ok", lastModified: lastModified, items: items }; |
|
50 } else { |
|
51 // update failed |
|
52 return { status: "error" }; |
|
53 } |
|
54 } |
|
55 |
|
56 // FeedPresenter implementation for forum groups |
|
57 function ForumGroupsFeedPresenter(rssreader){ |
|
58 if (rssreader) { |
|
59 this.init(rssreader); |
|
60 } |
|
61 } |
|
62 |
|
63 // ForumGroupsFeedPresenter is a subclass of ButtonFeedPresenter |
|
64 ForumGroupsFeedPresenter.prototype = new ButtonFeedPresenter(null); |
|
65 |
|
66 // ForumGroupsFeedPresenter "Constructor" |
|
67 ForumGroupsFeedPresenter.prototype.init = function(rssreader) { |
|
68 ButtonFeedPresenter.prototype.init.call(this, rssreader); |
|
69 } |
|
70 |
|
71 // Handle the click on a specific item |
|
72 ForumGroupsFeedPresenter.prototype.feedClicked = function(event){ |
|
73 var buttonid = event.source.id; |
|
74 |
|
75 if (buttonid == "latestPosts") { |
|
76 // show latest posts |
|
77 var url = forumFeedURL; |
|
78 var latestPostsView = new RssReader("Latest posts", url, new LatestPostsFeedPresenter(null), this.rssreader, null); |
|
79 latestPostsView.show(); |
|
80 } |
|
81 else { |
|
82 // show forum group |
|
83 var groupid = this.items[buttonid].id; |
|
84 var grouptitle = this.items[buttonid].title; |
|
85 |
|
86 var url = forumsListUrl + groupid; |
|
87 var forumListView = new RssReader(grouptitle, url, new ForumsListFeedPresenter(null), this.rssreader, forumListResponseParser); |
|
88 forumListView.show(); |
|
89 } |
|
90 } |
|
91 |
|
92 // Create and add controls to be shown before items list. |
|
93 ForumGroupsFeedPresenter.prototype.addPreambleItems = function(){ |
|
94 var feedItemControl = new NavigationButton("latestPosts", "blueright.gif", "Latest posts"); |
|
95 var self = this; |
|
96 feedItemControl.addEventListener("ActionPerformed", function(event) { self.feedClicked(event); }); |
|
97 this.rssreader.addControl(feedItemControl); |
|
98 } |
|
99 |
|
100 |
|
101 // /////////////////////////////////////////////////////////////////////////// |
|
102 // List of forums in a group |
|
103 |
|
104 // response parser for forum list - in a group |
|
105 function forumListResponseParser(broker, responseStatus, xmlDoc) { |
|
106 if (responseStatus == 200 && xmlDoc != null) { |
|
107 // node ref for iterating |
|
108 var node; |
|
109 |
|
110 // for compatibility with rss |
|
111 var lastModified = new Date(); |
|
112 |
|
113 // init result items array |
|
114 var items = []; |
|
115 |
|
116 // extract items for all group elements |
|
117 var elements = xmlDoc.getElementsByTagName("group"); |
|
118 for (var i = 0; i < elements.length; i++) { |
|
119 var forumid = elements[i].getAttribute("id"); |
|
120 var forumtitle = elements[i].getAttribute("title"); |
|
121 items.push({ id: forumid, title: forumtitle}); |
|
122 } |
|
123 |
|
124 // update was completed successfully |
|
125 return { status: "ok", lastModified: lastModified, items: items }; |
|
126 } else { |
|
127 // update failed |
|
128 return { status: "error" }; |
|
129 } |
|
130 } |
|
131 |
|
132 // FeedPresenter implementation for forum groups |
|
133 function ForumsListFeedPresenter(rssreader){ |
|
134 if (rssreader) { |
|
135 this.init(rssreader); |
|
136 } |
|
137 } |
|
138 |
|
139 // ForumsListFeedPresenter is a subclass of ButtonFeedPresenter |
|
140 ForumsListFeedPresenter.prototype = new ButtonFeedPresenter(null); |
|
141 |
|
142 // ForumsListFeedPresenter constructor |
|
143 ForumsListFeedPresenter.prototype.init = function(rssreader) { |
|
144 ButtonFeedPresenter.prototype.init.call(this, rssreader); |
|
145 } |
|
146 |
|
147 |
|
148 // forum has been selected, create a reader showing threads in the forum |
|
149 ForumsListFeedPresenter.prototype.feedClicked = function(event){ |
|
150 var buttonid = event.source.id; |
|
151 if (buttonid == "latestPosts") { |
|
152 // show latest posts |
|
153 var url = forumFeedURL + "&forumids="; |
|
154 // append requested forum ids |
|
155 for( var i = 0; i < this.items.length; i++) { |
|
156 url += this.items[i].id + ","; |
|
157 } |
|
158 |
|
159 var latestPostsView = new RssReader( |
|
160 "Latest posts in " + this.rssreader.feedName, |
|
161 url, |
|
162 new LatestPostsFeedPresenter(null), |
|
163 this.rssreader, |
|
164 null); |
|
165 latestPostsView.show(); |
|
166 } |
|
167 else { |
|
168 var forumid = this.items[buttonid].id; |
|
169 var forumtitle = this.items[buttonid].title; |
|
170 |
|
171 var url = forumFeedURL + forumsForumSpecQuery + forumid; |
|
172 var forumListView = new RssReader(forumtitle, url, new ThreadListFeedPresenter(null), this.rssreader, null); |
|
173 forumListView.show(); |
|
174 } |
|
175 } |
|
176 |
|
177 // Create and add controls to be shown before items list. |
|
178 ForumsListFeedPresenter.prototype.addPreambleItems = function(){ |
|
179 var feedItemControl = new NavigationButton("latestPosts", "blueright.gif", "Latest posts in " + this.rssreader.feedName); |
|
180 var self = this; |
|
181 feedItemControl.addEventListener("ActionPerformed", function(event) { self.feedClicked(event); }); |
|
182 this.rssreader.addControl(feedItemControl); |
|
183 } |
|
184 |
|
185 //// // FeedPresenter implementation for forum groups |
|
186 function ForumsSettingsFeedPresenter(rssreader){ |
|
187 if (rssreader) { |
|
188 this.init(rssreader); |
|
189 } |
|
190 } |
|
191 |
|
192 // ForumsListFeedPresenter is a subclass of ButtonFeedPresenter |
|
193 ForumsSettingsFeedPresenter.prototype = new ButtonFeedPresenter(null); |
|
194 |
|
195 // ForumsListFeedPresenter constructor |
|
196 ForumsSettingsFeedPresenter.prototype.init = function(rssreader) { |
|
197 ButtonFeedPresenter.prototype.init.call(this, rssreader); |
|
198 } |
|
199 |
|
200 |
|
201 // forum has been selected, create a reader showing threads in the forum |
|
202 ForumsSettingsFeedPresenter.prototype.feedClicked = function(event){ |
|
203 var buttonid = event.source.id; |
|
204 var firstboot =true; |
|
205 var forumid = this.items[buttonid].id; |
|
206 var forumtitle = this.items[buttonid].title; |
|
207 if(myforumid){firstboot=false;} |
|
208 |
|
209 feedItemControl.setText(forumtitle + ": " + forumid); |
|
210 ForumControl.setText("Forum: " + forumtitle); |
|
211 myforumid=forumid; |
|
212 myforumtitle=forumtitle; |
|
213 if (firstboot){ |
|
214 forum_reader = new RssReader(myforumtitle, forumFeedURL + forumsForumSpecQuery + myforumid, new ThreadListFeedPresenter(null), bugzilla, null); |
|
215 forum_reader.show(); |
|
216 savePreferences(); |
|
217 } else { |
|
218 settings.show(); |
|
219 |
|
220 } |
|
221 } |
|
222 |
|
223 // Create and add controls to be shown before items list. |
|
224 ForumsSettingsFeedPresenter.prototype.addPreambleItems = function(){ |
|
225 feedItemControl = new Label(null, "Choosen forum:"); |
|
226 var self = this; |
|
227 this.rssreader.addControl(feedItemControl); |
|
228 } |
|
229 |
|
230 |
|
231 |
|
232 // /////////////////////////////////////////////////////////////////////////// |
|
233 // List of threads in a forum |
|
234 |
|
235 // response parser for thread list is the usual rss parser |
|
236 |
|
237 // FeedPresenter implementation for forum groups |
|
238 function ThreadListFeedPresenter(rssreader){ |
|
239 if (rssreader) { |
|
240 this.init(rssreader); |
|
241 } |
|
242 } |
|
243 |
|
244 // ThreadListFeedPresenter is a subclass of ButtonFeedPresenter |
|
245 ThreadListFeedPresenter.prototype = new ButtonFeedPresenter(null); |
|
246 |
|
247 // ThreadListFeedPresenter constructor |
|
248 ThreadListFeedPresenter.prototype.init = function(rssreader) { |
|
249 ButtonFeedPresenter.prototype.init.call(this, rssreader); |
|
250 } |
|
251 |
|
252 |
|
253 // Handle the click on a specific item |
|
254 ThreadListFeedPresenter.prototype.feedClicked = function(event){ |
|
255 var buttonid = event.source.id; |
|
256 |
|
257 if (buttonid == "newThread") { |
|
258 // extract forum id from rssreader.feedURL |
|
259 var ind = this.rssreader.feedURL.indexOf(forumsForumSpecQuery); |
|
260 var forumid = this.rssreader.feedURL.substring( ind + forumsForumSpecQuery.length); |
|
261 var postForm = new ForumPostForm(this.rssreader, forumid); |
|
262 postForm.show(); |
|
263 } |
|
264 else { |
|
265 var weburl = this.items[buttonid].url; |
|
266 |
|
267 // extract thread id from url. looking for t=xxx |
|
268 var ind1 = weburl.indexOf("?t="); |
|
269 if (ind1 == -1) { |
|
270 ind1 = weburl.indexOf("&t="); |
|
271 } |
|
272 if (ind1 != -1) { |
|
273 var threadid = ""; |
|
274 var ind2 = weburl.indexOf("&", ind1); |
|
275 if (ind2 == -1) { |
|
276 threadid = weburl.substring(ind1 + 3); // ?t= |
|
277 } |
|
278 else { |
|
279 threadid = weburl.substring(ind1 + 3, ind2); // ?t= |
|
280 } |
|
281 var url = forumThreadUrl + threadid; |
|
282 var title = this.items[buttonid].title; |
|
283 if (title.length > 30) { |
|
284 title = title.substring(0, 30) + "..."; |
|
285 } |
|
286 var threadView = new RssReader(title, url, new ThreadFeedPresenter(null), this.rssreader, threadResponseParser); |
|
287 threadView.show(); |
|
288 } |
|
289 } |
|
290 } |
|
291 |
|
292 // Create and add controls to be shown before items list. |
|
293 ThreadListFeedPresenter.prototype.addPreambleItems = function(){ |
|
294 var feedItemControl = new NavigationButton("newThread", "blueright.gif", "Post a new thread"); |
|
295 var self = this; |
|
296 feedItemControl.addEventListener("ActionPerformed", function(event) { self.feedClicked(event); }); |
|
297 this.rssreader.addControl(feedItemControl); |
|
298 } |
|
299 |
|
300 // /////////////////////////////////////////////////////////////////////////// |
|
301 // List of messages in a thread |
|
302 |
|
303 // response parser for thread list |
|
304 function threadResponseParser(broker, responseStatus, xmlDoc) { |
|
305 if (responseStatus == 200 && xmlDoc != null) { |
|
306 // node ref for iterating |
|
307 var node; |
|
308 |
|
309 // for compatibility with rss |
|
310 var lastModified = new Date(); |
|
311 |
|
312 // init result items array |
|
313 var items = []; |
|
314 |
|
315 // iterate over message elements |
|
316 var elements = xmlDoc.getElementsByTagName("message"); |
|
317 for (var i = 0; i < elements.length; i++) { |
|
318 var postid; |
|
319 var threadid; |
|
320 var username; |
|
321 var title; |
|
322 var dateline; |
|
323 var pagetext; |
|
324 var isdeleted; |
|
325 |
|
326 // extract info about the post |
|
327 node = elements[i].firstChild; |
|
328 while (node != null) { |
|
329 if ( node.nodeName == "postid" ) postid=getTextOfNode(node); |
|
330 else if ( node.nodeName == "threadid" ) threadid=getTextOfNode(node); |
|
331 else if ( node.nodeName == "username" ) username=getTextOfNode(node); |
|
332 else if ( node.nodeName == "title" ) title=getTextOfNode(node); |
|
333 else if ( node.nodeName == "dateline" ) dateline=getTextOfNode(node); |
|
334 else if ( node.nodeName == "pagetext" ) pagetext=getTextOfNode(node); |
|
335 else if ( node.nodeName == "isdeleted" ) isdeleted=getTextOfNode(node); |
|
336 node = node.nextSibling; |
|
337 } |
|
338 if ( isdeleted == 1 ) continue; |
|
339 |
|
340 items.push({ |
|
341 postid: postid, |
|
342 threadid: threadid, |
|
343 username: username, |
|
344 title: title, |
|
345 dateline: dateline, |
|
346 pagetext: pagetext |
|
347 }); |
|
348 } |
|
349 |
|
350 // update was completed successfully |
|
351 return { status: "ok", lastModified: lastModified, items: items }; |
|
352 } else { |
|
353 // update failed |
|
354 return { status: "error" }; |
|
355 } |
|
356 } |
|
357 |
|
358 // FeedPresenter implementation for forum groups |
|
359 function ThreadFeedPresenter(rssreader){ |
|
360 if (rssreader) { |
|
361 this.init(rssreader); |
|
362 } |
|
363 } |
|
364 |
|
365 // ThreadFeedPresenter is a subclass of HtmlFeedPresenter |
|
366 ThreadFeedPresenter.prototype = new HtmlFeedPresenter(null); |
|
367 |
|
368 // ThreadFeedPresenter constructor |
|
369 ThreadFeedPresenter.prototype.init = function(rssreader) { |
|
370 HtmlFeedPresenter.prototype.init.call(this, rssreader); |
|
371 } |
|
372 |
|
373 |
|
374 // Handle the click on a specific item |
|
375 ThreadFeedPresenter.prototype.feedClicked = function(event){ |
|
376 // do nothing |
|
377 } |
|
378 |
|
379 // Create a control that represents this item and add it to |
|
380 // parent rss reader |
|
381 ThreadFeedPresenter.prototype.show = function(item) { |
|
382 // get a feed item control from the pool or create one and |
|
383 // place it in the pool if there aren't enough feed item controls |
|
384 var feedItemControl = new ContentPanel(null, null, null, true); |
|
385 |
|
386 // initialize feed item control |
|
387 var title = item.title; |
|
388 if ( !title || title.length == 0 ) { |
|
389 title = "Re:"; |
|
390 item.title = title; |
|
391 } |
|
392 feedItemControl.setCaption(bbcode2html(title)); |
|
393 feedItemControl.setContent(this.getContentHTMLForFeedItem(item)); |
|
394 feedItemControl.setExpanded(true); |
|
395 |
|
396 // add the feed item control to the main view |
|
397 this.rssreader.feedItemControls.push(feedItemControl); |
|
398 this.rssreader.addControl(feedItemControl); |
|
399 } |
|
400 |
|
401 // Generate HTML content from the feed item |
|
402 ThreadFeedPresenter.prototype.getContentHTMLForFeedItem = function (item){ |
|
403 var buf = ""; |
|
404 |
|
405 // item date |
|
406 if (item.dateline != null) { |
|
407 var date = new Date(); |
|
408 date.setTime(item.dateline*1000); |
|
409 buf += "<div class=\"FeedItemDate\">" ; |
|
410 if ( item.username != null ) { |
|
411 buf += item.username + ", "; |
|
412 } |
|
413 buf += date + "</div>"; |
|
414 } |
|
415 |
|
416 // item description |
|
417 if (item.pagetext != null) { |
|
418 var text = bbcode2html(item.pagetext); |
|
419 text = text.replace(/\r\n/g, "<br>"); |
|
420 buf += "<div class=\"FeedItemDescription\">" + text + "</div>"; |
|
421 buf += "<div class=\"FeedItemLink\">"; |
|
422 buf += "<a href=\"JavaScript:void(0)\" onclick=\"showReplyForm(" |
|
423 + item.threadid+ "," + item.postid + ", '" + item.title |
|
424 + "'); return false;\">"; |
|
425 buf += "<strong>Reply to this post<strong></a>" |
|
426 buf += "</div>"; |
|
427 } |
|
428 |
|
429 return buf; |
|
430 } |
|
431 |
|
432 // Show the reply-to-post form |
|
433 function showReplyForm(threadid, postid, title) { |
|
434 var replyForm = new ForumReplyForm(uiManager.currentView, threadid, postid, title); |
|
435 replyForm.show(); |
|
436 } |
|
437 |
|
438 |
|
439 // /////////////////////////////////////////////////////////////////////////// |
|
440 // Latest posts - same as ThreadListFeedPresenter, only has no preamble items |
|
441 // because it doesn't show one thread (so we can't post to latest items)... |
|
442 |
|
443 // FeedPresenter implementation for latest posts |
|
444 function LatestPostsFeedPresenter(rssreader){ |
|
445 if (rssreader) { |
|
446 this.init(rssreader); |
|
447 } |
|
448 } |
|
449 |
|
450 LatestPostsFeedPresenter.prototype = new ThreadListFeedPresenter(null); |
|
451 |
|
452 // ForumGroupsFeedPresenter "Constructor" |
|
453 LatestPostsFeedPresenter.prototype.init = function(rssreader) { |
|
454 ButtonFeedPresenter.prototype.init.call(this, rssreader); |
|
455 } |
|
456 |
|
457 // LatestPostsFeedPresenter has no preamble items |
|
458 LatestPostsFeedPresenter.prototype.addPreambleItems = function(){ |
|
459 } |
|
460 |
|
461 |
|
462 // /////////////////////////////////////////////////////////////////////////// |
|
463 // Utilities |
|
464 |
|
465 |
|
466 // Forum posts can be be quite messy and include bbcodes, smilies etc. |
|
467 // This function does the minimum by stripping bbcodes and such |
|
468 function sanitize(text) { |
|
469 var prevind = 0; |
|
470 var ind = text.indexOf("["); |
|
471 if ( ind == -1 ) return text; |
|
472 var buf = ""; |
|
473 while ( ind != -1 ) { |
|
474 buf += text.substring(prevind, ind); |
|
475 var ind2 = text.indexOf("]", ind); |
|
476 if ( ind2 != -1 ) { |
|
477 prevind = ind2+1; |
|
478 } else { |
|
479 break; |
|
480 } |
|
481 ind = text.indexOf("[", prevind); |
|
482 } |
|
483 if ( prevind > 0 && prevind < text.length) { |
|
484 buf += text.substring(prevind); |
|
485 } |
|
486 return buf; |
|
487 } |
|
488 |
|
489 |
|
490 |
|
491 // feeds contain bbcodes - this function should turn the bbcode markup |
|
492 // to HTML |
|
493 function bbcode2html(s) { |
|
494 var prevind = 0; |
|
495 var buf = ""; |
|
496 var ind = s.indexOf("["); |
|
497 if ( ind == -1 ) return s; |
|
498 while ( ind != -1 ) { |
|
499 buf += s.substring(prevind, ind); |
|
500 var ind2 = s.indexOf("]", ind); // end of tag |
|
501 var fulltag = s.substring(ind+1,ind2); |
|
502 var tag = fulltag; |
|
503 var ind3 = s.indexOf("=", ind); // end of tag name, eg. [URL=http...] |
|
504 if ( ind3 != -1 && ind3 < ind2) { |
|
505 tag = s.substring(ind+1,ind3); |
|
506 } |
|
507 var ind4 = s.indexOf("[/"+tag+"]", ind2); |
|
508 var tagContent = s.substring(ind2+1, ind4); |
|
509 buf += convertTag(tag, fulltag, tagContent); |
|
510 if ( ind4 != -1 ) { |
|
511 prevind = s.indexOf(']',ind4) + 1; |
|
512 } else { |
|
513 break; |
|
514 } |
|
515 ind = s.indexOf("[", prevind); |
|
516 } |
|
517 buf += s.substring(prevind); |
|
518 return buf; |
|
519 } |
|
520 |
|
521 function convertTag(tag, fulltag, tagContent) { |
|
522 tag = tag.toLowerCase(); |
|
523 var param = null; |
|
524 var eqsign = fulltag.indexOf("="); // onclick=\"openURL('" + item.url + "'); |
|
525 if (eqsign > -1) { |
|
526 param = fulltag.substring(eqsign+1); |
|
527 } |
|
528 switch(tag) { |
|
529 case '*': return bbcode2html(tagContent); |
|
530 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': |
|
531 { |
|
532 return '<' + tag + '>' + bbcode2html(tagContent) + "</" + tag + ">"; |
|
533 } |
|
534 case 'font': return '<font face="'+param+'">' + bbcode2html(tagContent) + '</font>'; |
|
535 case 'size': return '<font size="'+param+'">' + bbcode2html(tagContent) + '</font>'; |
|
536 case 'color': return '<font color="'+param+'">' + bbcode2html(tagContent) + '</font>'; |
|
537 case 'left': return '<div align="left">' + bbcode2html(tagContent) + '</div>'; |
|
538 case 'right': return '<div align="right">' + bbcode2html(tagContent) + '</div>'; |
|
539 case 'center': return '<div align="center">' + bbcode2html(tagContent) + '</div>'; |
|
540 case 'list':{ |
|
541 tagContent = tagContent.replace(/\[\*\]/g, "<br>• "); |
|
542 return bbcode2html(tagContent); // todo |
|
543 } |
|
544 case 'php': |
|
545 case 'code': |
|
546 case 'html':{ |
|
547 var escaped = tagContent.replace(/</g, "<").replace(/>/g, ">"); |
|
548 return '<div class=codebox><pre>' + escaped + '</pre></div>'; |
|
549 } |
|
550 case 'quote': return '<div class=codebox><b>Quote:</b><br><i>' + tagContent + '</i></div>'; |
|
551 case 'url': { |
|
552 if ( eqsign > -1 ) { |
|
553 return "<div class=\"FeedItemLink\"><a href=\"JavaScript:void(0)\" onclick=\"openURL( '" |
|
554 + param |
|
555 + "')\" ><i>" |
|
556 + tagContent |
|
557 + '</i></a></div>'; |
|
558 } else { |
|
559 return "<div class=\"FeedItemLink\"><a href=\"JavaScript:void(0)\" onclick=\"openURL( '" |
|
560 + tagContent |
|
561 + "')\" ><i>" |
|
562 + tagContent |
|
563 + '</i></a></div>'; |
|
564 } |
|
565 } |
|
566 } |
|
567 } |