diff --git a/js/App.js b/js/App.js index 303040f..c874b84 100644 --- a/js/App.js +++ b/js/App.js @@ -94,6 +94,8 @@ App.prototype.after_login = function(backend) { if(backend == "OwnCloud") { this.backend = new OwnCloud(this, localStorage.server_url, localStorage.session_id); + } else if (backend == "Pond") { + this.backend = new Pond(this, localStorage.server_url, localStorage.session_id) } else { this.backend = new TinyTinyRSS(this, localStorage.server_url, localStorage.session_id); } @@ -283,6 +285,9 @@ App.prototype.showFull = function(article, slide_back) { $(page_id + " .author").innerHTML = "– " + article.author; $(page_id + " .article").innerHTML = article.content; + $$(page_id + " .article a").forEach(function(o, i) { + o.target = "_blank"; + }); if(article.unread) { $("#setunread").innerHTML = "❌"; diff --git a/js/Login.js b/js/Login.js index 597f043..72a9490 100644 --- a/js/Login.js +++ b/js/Login.js @@ -83,7 +83,7 @@ Login.prototype.authenticate = function(e) { Pond.login(server_url, user, password, function(data) { if(data.session_token) { localStorage.server_url = server_url; - localStorage.session_token = data.session_token; + localStorage.session_id = data.session_token; localStorage.backend = "Pond"; _this.app.after_login(localStorage.backend); diff --git a/js/OwnCloud.js b/js/OwnCloud.js index ecfeb09..15776f1 100644 --- a/js/OwnCloud.js +++ b/js/OwnCloud.js @@ -46,7 +46,7 @@ OwnCloud.prototype.doOperation = function(method, operation, new_options, callba url += "?" + a.join("&"); } - var xhr = new XMLHttpRequest(); + var xhr = new XMLHttpRequest({mozSystem: true}); xhr.onreadystatechange = function() { if(xhr.readyState == 4) { if(xhr.status == 200) { @@ -213,6 +213,7 @@ OwnCloud.prototype.normalize_article = function(article) { OwnCloud.prototype.logOut = function() { this.doOperation("logout"); + localStorage.feeds = null; }; OwnCloud.prototype.getFeedFor = function(o) { @@ -234,7 +235,7 @@ OwnCloud.login = function(server_url, user, password, callback) { var url = server_url + "/index.php/apps/news/api/v1-2/version"; - var xhr = new XMLHttpRequest(); + var xhr = new XMLHttpRequest({mozSystem: true}); xhr.onreadystatechange = function() { if(xhr.readyState == 4) { if(xhr.status == 200) { diff --git a/js/Pond.js b/js/Pond.js index 5808301..7798e12 100644 --- a/js/Pond.js +++ b/js/Pond.js @@ -2,6 +2,9 @@ function Pond(app, server_url, session_token) { this.app = app; this.server_url = server_url; this.session_token = session_token; + this.feeds = {}; + var feeds = localStorage.feeds; + if(feeds) this.feeds = JSON.parse(feeds); window.addEventListener("offline", this.onoffline.bind(this)); window.addEventListener("online", this.ononline.bind(this)); @@ -19,6 +22,183 @@ Pond.prototype.toString = function() { return "Pond" }; +Pond.prototype.doOperation = function(method, operation, new_options, callback) { + if(!navigator.onLine) { + callback(null); + return; + } + + var url = this.server_url + "/api/" + operation; + var options = {}; + + for (var key in new_options) { + options[key] = new_options[key]; + } + + var a = []; + for(var key in options) { + a.push(key + "=" + options[key]); + } + var body = ""; + if(method == "GET" || method == "HEAD" || method == "PUT") { // FIXME: in future remove put from here + url += "?" + a.join("&"); + } else { + body = a.join("&"); + } + + var xhr = new XMLHttpRequest({mozSystem: true}); + xhr.onreadystatechange = function() { + if(xhr.readyState == 4) { + if(xhr.status == 200) { + if(callback) + callback(JSON.parse(xhr.responseText)); + } else { + if(xhr.status != 0) alert("error: " + xhr.status + " " + xhr.statusText); + if(callback) callback(null); + } + } + } + + xhr.open(method, url, true); + xhr.withCredentials = true; + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xhr.setRequestHeader("Content-Length", body.length); + xhr.setRequestHeader("Connection", "close"); + xhr.setRequestHeader('X-Session-Token', this.session_token); + xhr.send(body); +}; + + +Pond.prototype.reload = function(callback) { + var _this = this; + this.getFeeds(function() { _this.getUnreadFeeds(callback); }); + }; + +Pond.prototype.getUnreadFeeds = function(callback, skip) { + var options = { + status: "unread", + limit: 100 + }; + + if(skip && skip.length > 0) { + options.before_id = skip[skip.length - 1].id + } + + var _this = this; + this.doOperation("GET", "subscriptions/articles", options, function(items) { + + if(!items) items = []; // So it can run the last callback + + function isFeedAvailable(o) { + return !!_this.feeds[o.feedId]; + } + + if(items.every(isFeedAvailable)) { + callback(items.map(_this.normalizeArticle, _this)); + } else { + _this.getFeeds(function() { + callback(items.map(_this.normalizeArticle, _this)); + }); + } + }); +} + +Pond.prototype.getFeeds = function(callback) { + var _this = this; + this.doOperation("GET", "subscriptions", {}, function(feeds) { + + _this.feeds = {}; + for (var i = 0; i < feeds.length; i++) { + var feed = feeds[i]; + _this.feeds[feed.id] = feed; + } + + localStorage.feeds = JSON.stringify(_this.feeds); + callback(); + }); +}; + +Pond.prototype.normalizeArticle = function(article) { + var feed = this.feeds[article.subscription_id]; + var feed_title = ""; + if(feed) { + feed_title = feed.name; + } + + var content; + if(article.body.type == "text") content = article.body.content.replace(/\n/, "
"); + else content = article.body.content.htmlDecode(); + + var excerpt = article.summary.content.htmlDecode(); + if(!excerpt || excerpt.length < 1) excerpt = content; + + return { + id: article.id, + guid_hash: article.url + article.id, + title: article.title, + content: content, + feed_title: feed_title, + feed_id: article.subscription_id, + excerpt: excerpt.stripHTML().substring(0, 100), + updated: article.published_at, + link: article.url, + marked: false, // not implemented in Pond server yet + unread: !article.read + } +}; + + +Pond.prototype.setArticleStatus = function(article, callback, status) { + var options = { + status: status + }; + + var url = "subscriptions/" + article.feed_id + "/articles/" + article.id + + if (navigator.onLine) this.doOperation("PUT", url, options, callback); + else { + this.append("unread_articles", articles); + } + +} + +Pond.prototype.setArticleRead = function(article, callback) { + this.setArticleStatus(article, callback, "read"); +} + +Pond.prototype.setArticleUnread = function(article, callback) { + this.setArticleStatus(article, callback, "unread"); +} + +Pond.prototype.setArticlesRead = function(articles, callback) { + articles.forEach(function(article) { + this.setArticleStatus(article, callback, "read"); + }) +} + +Pond.prototype.setArticlesUnread = function(articles, callback) { + articles.forEach(function(article) { + this.setArticleStatus(article, callback, "unread"); + }) +} + +Pond.prototype.setArticleStarred = function(article, callback) { + // not implemented yet in Pond +} + +Pond.prototype.setArticleUnstarred = function(articles, callback) { + // not implemented yet in Pond +} + +Pond.prototype.logOut = function() { + this.doOperation("auth/sessions/" + this.session_token ); + localStorage.feeds = null; +} + +Pond.prototype.getFeedFor = function(o) { + return this.feeds[o.subscription_id]; +}; + Pond.login = function(server_url, user, password, callback) { var url = server_url + "/api/auth/sessions"; diff --git a/js/application.js b/js/application.js index 5071f91..5e38312 100644 --- a/js/application.js +++ b/js/application.js @@ -49,6 +49,12 @@ String.prototype.escapeHTML = function() { }); } +String.prototype.htmlDecode = function() { + var e = document.createElement('div'); + e.innerHTML = this; + return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue; +} + String.prototype.stripHTML = function() { return this.replace(/(<([^>]+)>)/ig, ""); }