2 ** Copyright 2011 - Tommi Laukkanen (www.substanceofcode.com)
3 ** Copyright (C) 2012 Christophe CHAPUIS <chris.chapuis _at_ gmail _dot_ com>
5 ** This package is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
10 ** This package is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
15 ** You should have received a copy of the GNU General Public License
16 ** along with this package; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
33 var continuation = "";
35 var actionFeedUrl = "";
42 function doWebRequest(method, url, params, callback) {
43 var doc = new XMLHttpRequest();
44 //console.log(method + " " + url);
46 doc.onreadystatechange = function() {
47 if (doc.readyState == XMLHttpRequest.HEADERS_RECEIVED) {
48 var status = doc.status;
50 showError("Google API returned " + status + " " + doc.statusText);
52 } else if (doc.readyState == XMLHttpRequest.DONE) {
54 var contentType = doc.getResponseHeader("Content-Type");
55 data = doc.responseText;
60 doc.open(method, url);
62 //console.log("Authorization GoogleLogin auth=" + sid);
63 doc.setRequestHeader("Authorization", "GoogleLogin auth=" + sid);
64 doc.setRequestHeader("Cookie", "SID=" + sidToken);
67 //console.log("Sending: " + params);
68 doc.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
69 doc.setRequestHeader("Content-Length", String(params.length));
76 /** Parse parameter from given URL */
77 function parseAuth(data, parameterName) {
78 var parameterIndex = data.indexOf(parameterName + "=");
79 if(parameterIndex<0) {
80 // We didn't find parameter
81 console.log("Didn't find Auth");
82 addError("Didn't find Auth");
85 var equalIndex = data.indexOf("=", parameterIndex);
90 var lineBreakIndex = data.indexOf("\n", equalIndex+1)
93 value = data.substring(equalIndex+1, lineBreakIndex);
97 function addError(msg) {
110 function login(email, password) {
112 // waiting.state = "shown";
113 var url = "https://www.google.com/accounts/ClientLogin?Email=" + encodeURIComponent(email) + "&Passwd=" + encodeURIComponent(password) + "&service=reader";
114 doWebRequest("GET", url, "", parseToken);
116 showError("Error while logging in.");
120 function showError(msg) {
121 console.log("ERROR: " + msg)
122 // waiting.state = "hidden";
123 // error.reason = msg;
124 // error.state = "shown";
127 function removeLinks(original) {
129 txt = txt.replace(/<a /g, "<span ");
130 txt = txt.replace(/<\/a>/g, "</span>");
134 function parseToken(data) {
135 sid = parseAuth(data, "Auth");
136 //console.log("Auth=" + sid);
137 sidToken = parseAuth(data, "SID");
138 //console.log("SID=" + sidToken);
139 // logo.state = "hidden"; //.visible = false;
141 //navigation.state = "menu";
142 //waiting.state = "hidden";
144 WorkerScript.sendMessage({"sid": sid, "sidToken": sidToken});
148 addError("Couldn't parse SID");
149 //waiting.state = "hidden";
153 function loadSubscriptions() {
154 //waiting.state = "shown";
155 var url = "http://www.google.com/reader/api/0/subscription/list?output=json";
156 doWebRequest("GET", url, "", parseSubscriptions);
159 function parseSubscriptions(data) {
160 //console.log("Subscriptions: " + data);
162 var tags = eval("[" + data + "]")[0];
163 for(var i in tags.subscriptions) {
164 var tag = tags.subscriptions[i];
166 var title = tag.title;
168 WorkerScript.sendMessage({"title": title, "published": '',"tag": tag, "id":id});
170 //navigation.state = "tags";
171 //waiting.state = "hidden";
174 function loadTags() {
175 //waiting.state = "shown";
176 var url = "http://www.google.com/reader/api/0/tag/list?output=json";
177 doWebRequest("GET", url, "", parseTags);
180 function parseTags(data) {
181 var tags = eval("[" + data + "]")[0];
182 for(var i in tags.tags) {
183 var tag = tags.tags[i];
186 while(title.indexOf("/")>0) {
187 var index = title.indexOf("/");
188 title = title.substring(index+1);
191 WorkerScript.sendMessage({"title": title, "published": '', "tag": tag, "id":id});
193 //navigation.state = "tags";
194 //waiting.state = "hidden";
197 function loadAllNews() {
198 itemsURL = "http://www.google.com/reader/api/0/stream/contents/user/-/state/com.google/reading-list";
202 function loadUnreadNews() {
203 itemsURL = "http://www.google.com/reader/api/0/stream/contents/user/-/state/com.google/reading-list?xt=user/-/state/com.google/read";
207 function loadStarred() {
208 itemsURL = "http://www.google.com/reader/api/0/stream/contents/user/-/state/com.google/starred";
212 function loadTaggedItems(tag) {
213 itemsURL = "http://www.google.com/reader/api/0/stream/contents/" + tag;
217 function loadSubscriptionItems(subscription) {
218 itemsURL = "http://www.google.com/reader/api/0/stream/contents/" + subscription;
222 function loadNews() {
224 //waiting.state = "shown";
225 doWebRequest("GET", itemsURL, "", parseNews);
227 showError("Error while loading news: " + err);
231 function getNodeValue(node, name) {
233 for(var i=0; i<node.childNodes.length; i++) {
234 var nodeName = node.childNodes[i].nodeName;
236 nodeValue = node.childNodes[i].firstChild.nodeValue;
242 function parseEntry(item) {
244 if(typeof(item.content)!=undefined && item.content!=null) {
245 content = item.content.content;
246 } else if(typeof(item.summary)!=undefined && item.summary!=null) {
247 content = item.summary.content;
251 content = removeLinks(content);
252 var milliseconds = parseInt(parseInt(item.published)*1000);
253 var published = new Date(parseInt(milliseconds));
254 var link = item.alternate[0].href;
255 //console.log("Link: " + link);
257 for(var i in item.categories) {
258 var category = item.categories[i];
259 if(category.indexOf("/reading-list")>0) {
265 WorkerScript.sendMessage({
268 "description": content,
269 "author": item.origin.title,
270 "published": prettyDate(published),
272 "source": item.origin.title,
274 "feedUrl": item.origin.streamId,
279 function parseNews(data) {
281 //console.log("DATA: " + data);
282 var doc = eval("[" + data + "]")[0];
283 if(doc==null || typeof(doc)==undefined) {
284 WorkerScript.sendMessage({
292 //waiting.state = "hidden";
296 //var moreIndex = model.count - 1;
298 continuation = doc.continuation;
299 for(var i in doc.items) {
300 var item = doc.items[i];
305 if(model.get(moreIndex).title.indexOf("Loading...")>-1) {
306 model.remove(moreIndex);
311 "title": "Load more...<br/><br/>",
320 // addError("Error: " + err);
322 // navigation.state = "items";
323 // waiting.state = "hidden";
326 function loadMore() {
327 //waiting.state = "shown";
329 if(itemsURL.indexOf("?")>0) {
334 url += "c=" + continuation;
335 doWebRequest("GET", url, "", parseNews);
338 function getToken() {
339 var url = "http://www.google.com/reader/api/0/token";
340 doWebRequest("GET", url, "", parseAccessToken, null);
343 function parseAccessToken(data) {
346 var url = "http://www.google.com/reader/api/0/edit-tag";
347 var dd = "ac=edit-tags&a=user/-/state/com.google/read&i=" + encodeURIComponent(actionID) + "&s=" + encodeURIComponent(actionFeedUrl) + "&T=" + accessToken;
348 doWebRequest("POST", url, dd, showDone, null);
349 } else if(action=="unread") {
350 var url = "http://www.google.com/reader/api/0/edit-tag";
351 var dd = "ac=edit-tags&a=user/-/state/com.google/kept-unread&r=user/-/state/com.google/read&i=" + encodeURIComponent(actionID) + "&s=" + encodeURIComponent(actionFeedUrl) + "&T=" + accessToken;
352 doWebRequest("POST", url, dd, showDone, null);
353 } else if(action=="fav") {
354 var url = "http://www.google.com/reader/api/0/edit-tag?client=-";
355 var dd = "a=user/-/state/com.google/starred&i=" + encodeURIComponent(actionID) + "&s=" + encodeURIComponent(actionFeedUrl) + "&T=" + accessToken;
356 doWebRequest("POST", url, dd, showDone, null);
357 } else if(action=="tags") {
358 var url = "http://www.google.com/reader/api/0/edit-tag?client=-";
360 "a=user/-/label/" + encodeURIComponent(tags) +
361 "&i=" + encodeURIComponent(actionID) +
362 "&s=" + encodeURIComponent(actionFeedUrl) +
364 doWebRequest("POST", url, dd, showDone, null);
368 function markAsRead(id, feedUrl, showLoadingIndicator) {
370 actionFeedUrl = feedUrl;
371 if(showLoadingIndicator) {
372 waiting.state = "shown";
378 function markAsUnread(id, feedUrl) {
380 actionFeedUrl = feedUrl;
381 //waiting.state = "shown";
383 //done.status = "Marked as unread";
387 function addTags(id, feedUrl, newTags) {
389 actionFeedUrl = feedUrl;
390 //waiting.state = "shown";
392 //done.status = "Added tags";
397 function markAsFavourite(id, feedUrl) {
399 actionFeedUrl = feedUrl;
400 //waiting.state = "shown";
402 //done.status = "Marked as favourite";
406 function showDone(data) {
407 console.log("DONE: " + data);
408 //if(waiting.state!="shown") {
411 //waiting.state = "hidden";
413 if(typeof(data)!=undefined && data!=null) {
415 //done.status = "Marked as read " + data;
416 } else if(action=="unread") {
417 //done.status = "Marked as unread " + data;
419 //done.status = "" + data;
422 //done.state = "shown";
425 function doNothing(data) {
429 function prettyDate(date){
431 var diff = (((new Date()).getTime() - date.getTime()) / 1000);
432 var day_diff = Math.floor(diff / 86400);
434 if ( isNaN(day_diff) || day_diff >= 31 ) {
435 //console.log("Days: " + day_diff);
436 return "some time ago";
437 } else if (day_diff < 0) {
438 //console.log("day_diff: " + day_diff);
442 return day_diff == 0 && (
443 diff < 60 && "just now" ||
444 diff < 120 && "1 minute ago" ||
445 diff < 3600 && Math.floor( diff / 60 ) + " min ago" ||
446 diff < 7200 && "1 hour ago" ||
447 diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") ||
448 day_diff == 1 && "Yesterday" ||
449 day_diff < 7 && day_diff + " days ago" ||
450 day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago";
451 day_diff >= 31 && Math.ceil( day_diff / 30 ) + " months ago";
453 console.log("Error: " + err);
454 return "some time ago";
458 // 2011-01-24T18:48:00Z
459 function parseDate(stamp)
462 //console.log("stamp: " + stamp);
463 var parts = stamp.split("T");
472 var dates = parts[0].split("-");
473 year = parseInt(dates[0]);
474 month = parseInt(dates[1])-1;
475 day = parseInt(dates[2]);
477 var times = parts[1].split(":");
478 hours = parseInt(times[0]);
479 minutes = parseInt(times[1]);
484 dt.setUTCMonth(month);
485 dt.setUTCHours(hours);
486 dt.setUTCMinutes(minutes);
487 dt.setUTCSeconds(seconds);
489 //console.log("day: " + day + " year: " + year + " month " + month + " hour " + hours);
493 console.log("Error while parsing date: " + err);
498 /// ======== WorkerScript related functions ========
499 WorkerScript.onMessage = function(message) {
501 // the structure of "message" object is:
502 // - attribute 'action' --> what to do
503 // - other attributes: will be passed on to the right function
505 if(message.action === 'login') {
506 login(message.email, message.password)
509 sid = message.sid; sidToken = message.sidToken;
511 if(message.action === 'getCategoryContent') {
512 // read the feeds in the right category
513 switch(message.category) {
514 case 0: loadAllNews(); break;
515 case 1: loadUnreadNews(); break;
516 case 2: loadStarred(); break;
517 case 3: loadSubscriptions(); break;
518 case 4: loadTags(); break;
521 else if(message.action === 'getSubscriptionItems') {
522 // read the feeds of that subscription
523 loadSubscriptionItems(message.subscription)
525 else if(message.action === 'getTaggedItems') {
526 // read the feeds of that subscription
527 loadTaggedItems(message.tag)