605 lines
17 KiB
JavaScript
605 lines
17 KiB
JavaScript
(function() {
|
|
|
|
// List of subscriptions
|
|
var feeds = [];
|
|
|
|
// List of feeds currently updating
|
|
var queue = [];
|
|
|
|
// Number of concurrent requests when updating all feeds
|
|
var queue_length = 5;
|
|
|
|
// Keyboard shortcuts queue
|
|
var keyqueue = [];
|
|
|
|
// Download full content from the original website
|
|
function download_item()
|
|
{
|
|
var container = document.getElementById("download-item");
|
|
if (! container) return;
|
|
|
|
var item_id = container.getAttribute("data-item-id");
|
|
var message = container.getAttribute("data-before-message");
|
|
|
|
var img = document.createElement("img");
|
|
img.src = "assets/img/refresh.gif";
|
|
|
|
container.innerHTML = "";
|
|
container.className = "downloading";
|
|
container.appendChild(img);
|
|
container.appendChild(document.createTextNode(" " + message));
|
|
|
|
var request = new XMLHttpRequest();
|
|
|
|
request.onload = function() {
|
|
|
|
var response = JSON.parse(request.responseText);
|
|
|
|
if (response.result) {
|
|
|
|
var content = document.getElementById("item-content");
|
|
if (content) content.innerHTML = response.content;
|
|
|
|
if (container) {
|
|
|
|
var message = container.getAttribute("data-after-message");
|
|
|
|
container.innerHTML = "";
|
|
container.appendChild(document.createTextNode(" " + message));
|
|
}
|
|
}
|
|
else {
|
|
|
|
if (container) {
|
|
|
|
var message = container.getAttribute("data-failure-message");
|
|
|
|
container.innerHTML = "";
|
|
container.appendChild(document.createTextNode(" " + message));
|
|
}
|
|
}
|
|
};
|
|
|
|
request.open("POST", "?action=download-item&id=" + item_id, true);
|
|
request.send();
|
|
}
|
|
|
|
// Flip item status between unread and read
|
|
function switch_status(item_id, hide)
|
|
{
|
|
var request = new XMLHttpRequest();
|
|
|
|
request.onload = function() {
|
|
|
|
if (is_listing()) {
|
|
|
|
var response = JSON.parse(request.responseText);
|
|
|
|
if (response.status == "read" || response.status == "unread") {
|
|
|
|
find_next_item();
|
|
if (hide) remove_item(response.item_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
request.open("POST", "?action=change-item-status&id=" + item_id, true);
|
|
request.send();
|
|
}
|
|
|
|
// Set all items of the current page to the status read and redirect to the main page
|
|
function mark_items_as_read(redirect)
|
|
{
|
|
var articles = document.getElementsByTagName("article");
|
|
var idlist = [];
|
|
|
|
for (var i = 0, ilen = articles.length; i < ilen; i++) {
|
|
idlist.push(articles[i].getAttribute("data-item-id"));
|
|
}
|
|
|
|
var request = new XMLHttpRequest();
|
|
|
|
request.onload = function() {
|
|
window.location.href = redirect;
|
|
};
|
|
|
|
request.open("POST", "?action=mark-items-as-read", true);
|
|
request.send(JSON.stringify(idlist));
|
|
}
|
|
|
|
// Mark the current item read and hide this item
|
|
function mark_as_read(item_id)
|
|
{
|
|
var request = new XMLHttpRequest();
|
|
|
|
request.onload = function() {
|
|
remove_item(item_id);
|
|
};
|
|
|
|
request.open("POST", "?action=mark-item-read&id=" + item_id, true);
|
|
request.send();
|
|
}
|
|
|
|
// Set the current item unread and hide this item
|
|
function mark_as_unread(item_id)
|
|
{
|
|
var request = new XMLHttpRequest();
|
|
|
|
request.onload = function() {
|
|
remove_item(item_id);
|
|
};
|
|
|
|
request.open("POST", "?action=mark-item-unread&id=" + item_id, true);
|
|
request.send();
|
|
}
|
|
|
|
// Bookmark the selected item
|
|
function bookmark_item()
|
|
{
|
|
var item = document.getElementById("current-item");
|
|
|
|
if (item) {
|
|
|
|
var item_id = item.getAttribute("data-item-id");
|
|
var link = document.getElementById("bookmark-" + item_id);
|
|
|
|
if (link) link.click();
|
|
}
|
|
}
|
|
|
|
// Show the refresh icon when updating a feed
|
|
function show_refresh_icon(feed_id)
|
|
{
|
|
var container = document.getElementById("loading-feed-" + feed_id);
|
|
|
|
if (container) {
|
|
|
|
var img = document.createElement("img");
|
|
img.src = "assets/img/refresh.gif";
|
|
|
|
container.appendChild(img);
|
|
}
|
|
}
|
|
|
|
// Hide the refresh icon after update
|
|
function hide_refresh_icon(feed_id)
|
|
{
|
|
var container = document.getElementById("loading-feed-" + feed_id);
|
|
if (container) container.innerHTML = "";
|
|
|
|
var container = document.getElementById("last-checked-feed-" + feed_id);
|
|
if (container) container.innerHTML = container.getAttribute("data-after-update");
|
|
}
|
|
|
|
// Update one feed in the background and execute a callback after that
|
|
function refresh_feed(feed_id, callback)
|
|
{
|
|
if (! feed_id) return false;
|
|
|
|
show_refresh_icon(feed_id);
|
|
|
|
var request = new XMLHttpRequest();
|
|
|
|
request.onload = function() {
|
|
|
|
hide_refresh_icon(feed_id);
|
|
|
|
try {
|
|
if (callback) {
|
|
callback(JSON.parse(this.responseText));
|
|
}
|
|
}
|
|
catch (e) {}
|
|
};
|
|
|
|
request.open("POST", "?action=refresh-feed&feed_id=" + feed_id, true);
|
|
request.send();
|
|
|
|
return true;
|
|
}
|
|
|
|
// Get all subscriptions from the feeds page
|
|
function get_feeds()
|
|
{
|
|
var links = document.getElementsByTagName("a");
|
|
|
|
for (var i = 0, ilen = links.length; i < ilen; i++) {
|
|
var feed_id = links[i].getAttribute('data-feed-id');
|
|
if (feed_id) feeds.push(parseInt(feed_id));
|
|
}
|
|
}
|
|
|
|
// Refresh all feeds (use a queue to allow 5 concurrent feed updates)
|
|
function refresh_all()
|
|
{
|
|
get_feeds();
|
|
|
|
var interval = setInterval(function() {
|
|
|
|
while (feeds.length > 0 && queue.length < queue_length) {
|
|
|
|
var feed_id = feeds.shift();
|
|
queue.push(feed_id);
|
|
|
|
refresh_feed(feed_id, function(response) {
|
|
|
|
var index = queue.indexOf(response.feed_id);
|
|
if (index >= 0) queue.splice(index, 1);
|
|
|
|
if (feeds.length == 0 && queue.length == 0) {
|
|
clearInterval(interval);
|
|
window.location.href = "?action=unread";
|
|
}
|
|
});
|
|
}
|
|
|
|
}, 100);
|
|
}
|
|
|
|
// Go the next page
|
|
function open_next_page()
|
|
{
|
|
var link = document.getElementById("next-page");
|
|
if (link) link.click();
|
|
}
|
|
|
|
// Go to the previous page
|
|
function open_previous_page()
|
|
{
|
|
var link = document.getElementById("previous-page");
|
|
if (link) link.click();
|
|
}
|
|
|
|
// Hide one item and update the item counter on the top
|
|
function remove_item(item_id)
|
|
{
|
|
var item = document.getElementById("item-" + item_id);
|
|
|
|
if (! item) {
|
|
item = document.getElementById("current-item");
|
|
if (item.getAttribute("data-item-id") != item_id) item = false;
|
|
}
|
|
|
|
if (item) {
|
|
|
|
item.parentNode.removeChild(item);
|
|
var container = document.getElementById("page-counter");
|
|
|
|
if (container) {
|
|
|
|
counter = parseInt(container.textContent.trim(), 10) - 1;
|
|
|
|
if (counter == 0) {
|
|
|
|
window.location = "?action=feeds¬hing_to_read=1";
|
|
}
|
|
else {
|
|
|
|
container.textContent = counter + " ";
|
|
document.title = "miniflux (" + counter + ")";
|
|
document.getElementById("nav-counter").textContent = "(" + counter + ")";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Open the original url inside a new tab
|
|
function open_original_item()
|
|
{
|
|
var link = document.getElementById("original-item");
|
|
|
|
if (link) {
|
|
|
|
if (is_listing() && link.getAttribute("data-hide")) {
|
|
mark_as_read(link.getAttribute("data-item-id"));
|
|
}
|
|
|
|
link.removeAttribute("data-action");
|
|
link.click();
|
|
}
|
|
}
|
|
|
|
// Show item content
|
|
function open_item()
|
|
{
|
|
var link = document.getElementById("open-item");
|
|
if (link) link.click();
|
|
}
|
|
|
|
// Show the next item
|
|
function open_next_item()
|
|
{
|
|
var link = document.getElementById("next-item");
|
|
|
|
if (link) {
|
|
|
|
link.click();
|
|
}
|
|
else if (is_listing()) {
|
|
|
|
find_next_item();
|
|
}
|
|
}
|
|
|
|
// Show the previous item
|
|
function open_previous_item()
|
|
{
|
|
var link = document.getElementById("previous-item");
|
|
|
|
if (link) {
|
|
|
|
link.click();
|
|
}
|
|
else if (is_listing()) {
|
|
|
|
find_previous_item();
|
|
}
|
|
}
|
|
|
|
// Change item status and select the next item in the list
|
|
function change_item_status()
|
|
{
|
|
if (is_listing() && ! document.getElementById("current-item")) {
|
|
find_next_item();
|
|
}
|
|
|
|
var item = document.getElementById("current-item");
|
|
|
|
if (item) {
|
|
switch_status(item.getAttribute("data-item-id"), item.getAttribute("data-hide"));
|
|
}
|
|
}
|
|
|
|
// Scroll automatically the page when using keyboard shortcuts
|
|
function scroll_page_to(item)
|
|
{
|
|
var clientHeight = pageYOffset + document.documentElement.clientHeight;
|
|
var itemPosition = item.offsetTop + item.offsetHeight;
|
|
|
|
if (clientHeight - itemPosition < 0 || clientHeight - item.offsetTop > document.documentElement.clientHeight) {
|
|
window.scrollTo(0, item.offsetTop - 10);
|
|
}
|
|
}
|
|
|
|
// Prepare the DOM for the selected item
|
|
function set_links_item(item_id)
|
|
{
|
|
var link = document.getElementById("current-item");
|
|
if (link) scroll_page_to(link);
|
|
|
|
var link = document.getElementById("original-item");
|
|
if (link) link.id = "original-" + link.getAttribute("data-item-id");
|
|
|
|
var link = document.getElementById("open-item");
|
|
if (link) link.id = "open-" + link.getAttribute("data-item-id");
|
|
|
|
var link = document.getElementById("original-" + item_id);
|
|
if (link) link.id = "original-item";
|
|
|
|
var link = document.getElementById("open-" + item_id);
|
|
if (link) link.id = "open-item";
|
|
}
|
|
|
|
// Find the next item in the listing page
|
|
function find_next_item()
|
|
{
|
|
var items = document.getElementsByTagName("article");
|
|
|
|
if (! document.getElementById("current-item")) {
|
|
|
|
items[0].id = "current-item";
|
|
set_links_item(items[0].getAttribute("data-item-id"));
|
|
}
|
|
else {
|
|
|
|
for (var i = 0, ilen = items.length; i < ilen; i++) {
|
|
|
|
if (items[i].id == "current-item") {
|
|
|
|
items[i].id = "item-" + items[i].getAttribute("data-item-id");
|
|
|
|
if (i + 1 < ilen) {
|
|
|
|
items[i + 1].id = "current-item";
|
|
set_links_item(items[i + 1].getAttribute("data-item-id"));
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find the previous item in the listing page
|
|
function find_previous_item()
|
|
{
|
|
var items = document.getElementsByTagName("article");
|
|
|
|
if (! document.getElementById("current-item")) {
|
|
|
|
items[items.length - 1].id = "current-item";
|
|
set_links_item(items[items.length - 1].getAttribute("data-item-id"));
|
|
}
|
|
else {
|
|
|
|
for (var i = items.length - 1; i >= 0; i--) {
|
|
|
|
if (items[i].id == "current-item") {
|
|
|
|
items[i].id = "item-" + items[i].getAttribute("data-item-id");
|
|
|
|
if (i - 1 >= 0) {
|
|
|
|
items[i - 1].id = "current-item";
|
|
set_links_item(items[i - 1].getAttribute("data-item-id"));
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if we are on a listing page
|
|
function is_listing()
|
|
{
|
|
if (document.getElementById("listing")) return true;
|
|
return false;
|
|
}
|
|
|
|
// Authentication with Mozilla Persona
|
|
function mozilla_auth(action)
|
|
{
|
|
navigator.id.watch({
|
|
onlogin: function(assertion) {
|
|
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.open("POST", "?action=" + action, true);
|
|
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
|
xhr.setRequestHeader("Connection", "close");
|
|
|
|
xhr.onload = function () {
|
|
window.location.href = this.responseText;
|
|
};
|
|
|
|
xhr.send("token=" + assertion);
|
|
},
|
|
onlogout: function() {}
|
|
});
|
|
|
|
navigator.id.request();
|
|
}
|
|
|
|
// Click event handler, if there is a "data-action" attribute execute the corresponding callback
|
|
document.onclick = function(e) {
|
|
|
|
var action = e.target.getAttribute("data-action");
|
|
|
|
if (action) {
|
|
|
|
switch (action) {
|
|
case 'refresh-all':
|
|
e.preventDefault();
|
|
refresh_all();
|
|
break;
|
|
case 'refresh-feed':
|
|
e.preventDefault();
|
|
var feed_id = e.target.getAttribute("data-feed-id");
|
|
refresh_feed(feed_id);
|
|
break;
|
|
case 'mark-read':
|
|
e.preventDefault();
|
|
var item_id = e.target.getAttribute("data-item-id");
|
|
mark_as_read(item_id);
|
|
break;
|
|
case 'mark-unread':
|
|
e.preventDefault();
|
|
var item_id = e.target.getAttribute("data-item-id");
|
|
mark_as_unread(item_id);
|
|
break;
|
|
case 'mark-all-read':
|
|
e.preventDefault();
|
|
mark_items_as_read("?action=unread");
|
|
break;
|
|
case 'mark-feed-read':
|
|
e.preventDefault();
|
|
mark_items_as_read("?action=feed-items&feed_id=" + e.target.getAttribute("data-feed-id"));
|
|
break;
|
|
case 'original-link':
|
|
var item_id = e.target.getAttribute("data-item-id");
|
|
mark_as_read(item_id);
|
|
break;
|
|
case 'download-item':
|
|
e.preventDefault();
|
|
download_item();
|
|
break;
|
|
case 'mozilla-login':
|
|
e.preventDefault();
|
|
mozilla_auth("mozilla-auth");
|
|
break;
|
|
case 'mozilla-link':
|
|
e.preventDefault();
|
|
mozilla_auth("mozilla-link");
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Keyboard handler, handle keyboard shortcuts
|
|
document.onkeypress = function(e) {
|
|
|
|
keyqueue.push(e.keyCode || e.which);
|
|
|
|
if (keyqueue[0] == 103) { // g
|
|
|
|
switch (keyqueue[1]) {
|
|
case undefined:
|
|
break;
|
|
case 117: // u
|
|
window.location.href = "?action=unread";
|
|
keyqueue = [];
|
|
break;
|
|
case 98: // b
|
|
window.location.href = "?action=bookmarks";
|
|
keyqueue = [];
|
|
break;
|
|
case 104: // h
|
|
window.location.href = "?action=history";
|
|
keyqueue = [];
|
|
break;
|
|
case 115: // s
|
|
window.location.href = "?action=feeds";
|
|
keyqueue = [];
|
|
break;
|
|
case 112: // p
|
|
window.location.href = "?action=config";
|
|
keyqueue = [];
|
|
break;
|
|
default:
|
|
keyqueue = [];
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
|
|
keyqueue = [];
|
|
|
|
switch (e.keyCode || e.which) {
|
|
case 100: // d
|
|
download_item();
|
|
break;
|
|
case 112: // p
|
|
case 107: // k
|
|
open_previous_item();
|
|
break;
|
|
case 110: // n
|
|
case 106: // j
|
|
open_next_item();
|
|
break;
|
|
case 118: // v
|
|
open_original_item();
|
|
break;
|
|
case 111: // o
|
|
open_item();
|
|
break;
|
|
case 109: // m
|
|
change_item_status();
|
|
break;
|
|
case 102: // f
|
|
bookmark_item();
|
|
break;
|
|
case 104: // h
|
|
open_previous_page();
|
|
break
|
|
case 108: // l
|
|
open_next_page();
|
|
break;
|
|
case 63: // ?
|
|
open("?action=show-help", "Help", "width=320,height=450,location=no,scrollbars=no,status=no,toolbar=no");
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
})();
|