diff --git a/backend.php b/backend.php
index a71b649e4..393246732 100644
--- a/backend.php
+++ b/backend.php
@@ -43,7 +43,7 @@
$print_exec_time = false;
- if ((!$op || $op == "rpc" || $op == "rss" || $op == "digestSend" ||
+ if ((!$op || $op == "rpc" || $op == "rss" || $op == "view" || $op == "digestSend" ||
$op == "globalUpdateFeeds") && !$_REQUEST["noxml"]) {
header("Content-Type: application/xml; charset=utf-8");
} else {
@@ -141,10 +141,9 @@
}
- if ($op == "view") {
+ function outputArticleXML($link, $id, $feed_id, $mark_as_read = true) {
- $id = db_escape_string($_GET["id"]);
- $feed_id = db_escape_string($_GET["feed"]);
+ print "";
}
+
+ print "]]>";
+
+ }
+
+ if ($op == "view") {
+
+ $id = db_escape_string($_GET["id"]);
+ $feed_id = db_escape_string($_GET["feed"]);
+ $cids = split(",", db_escape_string($_GET["cids"]));
+ $mode = db_escape_string($_GET["mode"]);
+
+ print "";
+
+ // in prefetch mode we only output requested cids, main article
+ // just gets marked as read (it already exists in client cache)
+
+ if ($mode != "prefetch") {
+ outputArticleXML($link, $id, $feed_id);
+ } else {
+ catchupArticleById($link, $id, 0);
+ }
+
+ foreach ($cids as $cid) {
+ if ($cid) {
+ outputArticleXML($link, $cid, $feed_id, false);
+ }
+ }
+
+ print "";
}
if ($op == "viewfeed") {
diff --git a/feedlist.js b/feedlist.js
index 77a31302f..f5a675772 100644
--- a/feedlist.js
+++ b/feedlist.js
@@ -46,10 +46,10 @@ function viewfeed(feed, subop, is_cat, subop_param, skip_history, offset) {
enableHotkeys();
- if (!skip_history) {
+/* if (!skip_history) {
history_push('FEED:' + feed + ':' + subop + ':' + is_cat +
':' + subop_param);
- }
+ } */
var toolbar_query = Form.serialize("main_toolbar_form");
var toolbar_form = document.forms["main_toolbar_form"];
diff --git a/functions.js b/functions.js
index 3a67b1837..09f430096 100644
--- a/functions.js
+++ b/functions.js
@@ -1667,3 +1667,34 @@ function logoutUser() {
exception_error("logoutUser", e);
}
}
+
+// this only searches loaded headlines list, not in CDM
+function getRelativePostIds(id) {
+
+ debug("getRelativePostIds: " + id);
+
+ var ids = new Array();
+ var container = document.getElementById("headlinesList");
+
+ if (container) {
+ var rows = container.rows;
+
+ for (var i = 0; i < rows.length; i++) {
+ var r_id = rows[i].id.replace("RROW-", "");
+
+ if (r_id == id) {
+ if (i > 0) ids.push(rows[i-1].id.replace("RROW-", ""));
+ if (i > 1) ids.push(rows[i-2].id.replace("RROW-", ""));
+ if (i > 2) ids.push(rows[i-3].id.replace("RROW-", ""));
+
+ if (i < rows.length) ids.push(rows[i+1].id.replace("RROW-", ""));
+ if (i < rows.length-1) ids.push(rows[i+2].id.replace("RROW-", ""));
+ if (i < rows.length-2) ids.push(rows[i+3].id.replace("RROW-", ""));
+
+ return ids;
+ }
+ }
+ }
+
+ return false;
+}
diff --git a/functions.php b/functions.php
index cdf592f70..6188e38d9 100644
--- a/functions.php
+++ b/functions.php
@@ -2811,6 +2811,23 @@
}
}
+ function catchupArticleById($link, $id, $cmode) {
+
+ if ($cmode == 0) {
+ db_query($link, "UPDATE ttrss_user_entries SET
+ unread = false,last_read = NOW()
+ WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
+ } else if ($cmode == 1) {
+ db_query($link, "UPDATE ttrss_user_entries SET
+ unread = true
+ WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
+ } else {
+ db_query($link, "UPDATE ttrss_user_entries SET
+ unread = NOT unread,last_read = NOW()
+ WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
+ }
+ }
+
function escape_for_form($s) {
return htmlspecialchars(db_unescape_string($s));
}
diff --git a/tt-rss.js b/tt-rss.js
index b46c859ac..d22aa1fba 100644
--- a/tt-rss.js
+++ b/tt-rss.js
@@ -21,7 +21,7 @@ var xmlhttp_ctr = Ajax.getTransport();
var init_params = new Object();
-var op_history = new Array();
+//var op_history = new Array();
function tagsAreDisplayed() {
return display_tags;
@@ -696,7 +696,7 @@ function feedEditSave() {
exception_error("feedEditSave (main)", e);
}
}
-
+/*
function localHotkeyHandler(e) {
var keycode;
@@ -756,7 +756,7 @@ function localHotkeyHandler(e) {
}
debug("LKP=" + keycode);
-}
+}
function history_push(op) {
debug("history_push: " + op);
@@ -776,4 +776,4 @@ function history_pop() {
function history_clear() {
debug("history_clear");
op_history.clear();
-}
+} */
diff --git a/viewfeed.js b/viewfeed.js
index 8a3518053..f5791eaf7 100644
--- a/viewfeed.js
+++ b/viewfeed.js
@@ -16,6 +16,8 @@ var _reload_feedlist_after_view = false;
var _cdm_wd_timeout = false;
var _cdm_wd_vishist = new Array();
+var article_cache = new Array();
+
function catchup_callback() {
if (xmlhttp_rpc.readyState == 4) {
try {
@@ -66,14 +68,49 @@ function headlines_callback() {
}
}
-function article_callback() {
- if (xmlhttp.readyState == 4) {
- debug("article_callback");
+function render_article(article) {
+ try {
var f = document.getElementById("content-frame");
try {
f.scrollTop = 0;
} catch (e) { };
- f.innerHTML = xmlhttp.responseText;
+
+ f.innerHTML = article;
+
+ } catch (e) {
+ exception_error("render_article", e);
+ }
+}
+
+function article_callback() {
+ if (xmlhttp.readyState == 4) {
+ debug("article_callback");
+
+ try {
+ if (xmlhttp.responseXML) {
+ var reply = xmlhttp.responseXML.firstChild.firstChild;
+
+ var articles = xmlhttp.responseXML.getElementsByTagName("article");
+
+ for (var i = 0; i < articles.length; i++) {
+ var a_id = articles[i].getAttribute("id");
+
+ debug("found id: " + a_id);
+
+ if (a_id == active_post_id) {
+ debug("active article, rendering...");
+ render_article(articles[i].firstChild.nodeValue);
+ }
+
+ cache_inject(a_id, articles[i].firstChild.nodeValue);
+ }
+
+ } else {
+ debug("article_callback: returned no XML object");
+ }
+ } catch (e) {
+ exception_error("article_callback", e);
+ }
var date = new Date();
last_article_view = date.getTime() / 1000;
@@ -100,9 +137,13 @@ function view(id, feed_id, skip_history) {
active_real_feed_id = feed_id;
- if (!skip_history) {
+ var cached_article = cache_find(id);
+
+ debug("cache check result: " + (cached_article != false));
+
+/* if (!skip_history) {
history_push("ARTICLE:" + id + ":" + feed_id);
- }
+ } */
enableHotkeys();
@@ -119,11 +160,15 @@ function view(id, feed_id, skip_history) {
xmlhttp.abort();
}
- if (xmlhttp_ready(xmlhttp)) {
+ if (cached_article || xmlhttp_ready(xmlhttp)) {
cleanSelected("headlinesList");
var crow = document.getElementById("RROW-" + active_post_id);
+
+ var article_is_unread = crow.className.match("Unread");
+ debug("article is unread: " + article_is_unread);
+
crow.className = crow.className.replace("Unread", "");
var upd_img_pic = document.getElementById("FUPDPIC-" + active_post_id);
@@ -135,15 +180,55 @@ function view(id, feed_id, skip_history) {
selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', false);
markHeadline(active_post_id);
+ var neighbor_ids = getRelativePostIds(active_post_id);
+
+ /* only request uncached articles */
+
+ var cids_to_request = Array();
+
+ for (var i = 0; i < neighbor_ids.length; i++) {
+ if (!cache_check(neighbor_ids[i])) {
+ cids_to_request.push(neighbor_ids[i]);
+ }
+ }
+
+ debug("additional ids: " + cids_to_request.toString());
+
var date = new Date();
var timestamp = Math.round(date.getTime() / 1000);
query = query + "&ts=" + timestamp;
- notify_progress("Loading, please wait...");
+ query = query + "&cids=" + cids_to_request.toString();
+
+ if (!cached_article) {
+
+ notify_progress("Loading, please wait...");
+
+ debug(query);
+
+ xmlhttp.open("GET", query, true);
+ xmlhttp.onreadystatechange=article_callback;
+ xmlhttp.send(null);
+ } else if (cached_article && article_is_unread) {
+
+ query = query + "&mode=prefetch";
+
+ debug(query);
+
+ xmlhttp.open("GET", query, true);
+ xmlhttp.onreadystatechange=article_callback;
+ xmlhttp.send(null);
+
+ render_article(cached_article);
+
+ } else if (cached_article) {
+
+ render_article(cached_article);
+
+ }
+
+ cache_expire();
- xmlhttp.open("GET", query, true);
- xmlhttp.onreadystatechange=article_callback;
- xmlhttp.send(null);
} else {
debug("xmlhttp busy (@view)");
printLockingError();
@@ -650,3 +735,37 @@ function cdmWatchdog() {
}
}
+
+
+function cache_inject(id, article) {
+ if (!article_cache[id]) {
+ debug("cache_article: miss: " + id);
+
+ var cache_obj = new Array();
+
+ var d = new Date();
+ cache_obj["entered"] = d.getTime() / 1000;
+ cache_obj["data"] = article;
+
+ article_cache[id] = cache_obj;
+
+ } else {
+ debug("cache_article: hit: " + id);
+ }
+}
+
+function cache_find(id) {
+ if (typeof article_cache[id] != 'undefined') {
+ return article_cache[id]["data"];
+ } else {
+ return false;
+ }
+}
+
+function cache_check(id) {
+ return typeof article_cache[id] != 'undefined';
+}
+
+function cache_expire() {
+ /* TODO */
+}