Compare commits
35 commits
indienotes
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
710416015c | ||
![]() |
3e4be0fe29 | ||
![]() |
d1c7059751 | ||
![]() |
2f9452376d | ||
![]() |
de93b8059a | ||
![]() |
7c63205d36 | ||
![]() |
8b0cc42075 | ||
![]() |
2a2d35dfde | ||
![]() |
2bf7e97c55 | ||
![]() |
2d16a03192 | ||
![]() |
d6a2ca9fd6 | ||
![]() |
2895e2749e | ||
![]() |
cc88b341b2 | ||
![]() |
568ca8005d | ||
![]() |
b2f319af71 | ||
![]() |
3f75c49e3e | ||
![]() |
dfcc5ba27a | ||
![]() |
7161642224 | ||
![]() |
a33fbe3110 | ||
![]() |
54900c3346 | ||
![]() |
aec6cd079b | ||
![]() |
22d6761ba3 | ||
![]() |
fd9b3d9580 | ||
![]() |
f5f9e9761f | ||
![]() |
89142c54c0 | ||
![]() |
54c87e58d8 | ||
![]() |
1f404a846c | ||
![]() |
b5931a8538 | ||
![]() |
208fa70c52 | ||
![]() |
e120f99e4e | ||
![]() |
28ce177e17 | ||
![]() |
d99928908e | ||
![]() |
e9a8af5429 | ||
![]() |
cfad4d4f4d | ||
![]() |
bc1c99354f |
12 changed files with 2882 additions and 732 deletions
|
@ -11,4 +11,4 @@ A RSS mobile client with which you can read your RSS feeds and mark them as read
|
||||||
- Works also offline.
|
- Works also offline.
|
||||||
- You can chose between 4 fresh color schemes.
|
- You can chose between 4 fresh color schemes.
|
||||||
|
|
||||||
To use this RSS client to read your feeds you need a backend server. As a backend you can use [TinyTinyRSS](http://tt-rss.org) or [ownCloud News](http://apps.owncloud.com/content/show.php/News?content=158434). This is not a stand alone application.
|
To use this RSS client to read your feeds you need a backend server. As a backend you can use [TinyTinyRSS](http://tt-rss.org), [ownCloud News](http://apps.owncloud.com/content/show.php/News?content=158434) or [Pond](https://github.com/ArturoVM/pond#pond). This is not a stand alone application.
|
||||||
|
|
|
@ -83,7 +83,7 @@ label {
|
||||||
}
|
}
|
||||||
|
|
||||||
.hidden {
|
.hidden {
|
||||||
display: none;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
section {
|
section {
|
||||||
|
@ -120,11 +120,13 @@ section > header h1 {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reload, .all-read, #setstarred, #setunread {
|
.reload, .all-read, #setstarred, #setunread, #setpublished {
|
||||||
float: right;
|
float: right;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inactive { opacity: 0.4; }
|
||||||
|
|
||||||
.settings, .list {
|
.settings, .list {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
@ -138,22 +140,29 @@ section > footer {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
max-width: 100% !important;
|
max-width: 100% !important;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#full > article pre {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (width: 320px) {
|
@media screen and (width: 320px) {
|
||||||
#full > article * , #full > article pre {
|
#full > article * {
|
||||||
max-width: 300px !important;
|
max-width: 300px !important;
|
||||||
overflow: auto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (width: 480px) {
|
@media screen and (width: 480px) {
|
||||||
#full > article * {
|
#full > article * {
|
||||||
max-width: 460px !important;
|
max-width: 460px !important;
|
||||||
overflow: auto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +174,6 @@ canvas {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#list p {
|
#list p {
|
||||||
|
@ -189,7 +197,6 @@ canvas {
|
||||||
|
|
||||||
#list li {
|
#list li {
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 3em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.red #list li { border-bottom: 1px solid #c0392b; }
|
.red #list li { border-bottom: 1px solid #c0392b; }
|
||||||
|
@ -201,7 +208,7 @@ canvas {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 7px;
|
right: 7px;
|
||||||
top: 0;
|
top: 0.1em;
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
font-size: 3em;
|
font-size: 3em;
|
||||||
font-family: "Entypo";
|
font-family: "Entypo";
|
||||||
|
@ -250,7 +257,6 @@ canvas {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#full .wrapper {
|
#full .wrapper {
|
||||||
|
@ -281,28 +287,9 @@ canvas {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#full article header p:nth-child(1) {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
#full article header p:nth-child(2) {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
#full article header p:nth-child(3) {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
#full .article {
|
|
||||||
clear: both;
|
|
||||||
padding-top: 1em;
|
|
||||||
padding-bottom: 3em;
|
|
||||||
font-size: 1.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#full footer.bar {
|
#full footer.bar {
|
||||||
margin: auto 0 0 0;
|
margin: auto 0 0 0;
|
||||||
position: fixed;
|
position: relative;
|
||||||
height: 3.8em;
|
height: 3.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
19
index.html
19
index.html
|
@ -2,7 +2,7 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
|
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||||
<title>FeedMonkey</title>
|
<title>FeedMonkey</title>
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
<script src="js/Pond.js"></script>
|
<script src="js/Pond.js"></script>
|
||||||
<script src="js/md5.js"></script>
|
<script src="js/md5.js"></script>
|
||||||
<script src="js/Login.js"></script>
|
<script src="js/Login.js"></script>
|
||||||
<script src="js/jester.js"></script>
|
<script src="js/hammer.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
<header class="bar">
|
<header class="bar">
|
||||||
<a class="button icon settings" href="#settings">⚙</a>
|
<a class="button icon settings" href="#settings">⚙</a>
|
||||||
<a class="button icon reload" href="#reload">🔄</a>
|
<a class="button icon reload" href="#reload">🔄</a>
|
||||||
<a class="button icon all-read" href="#all-read" id="all-read">❌</a>
|
<a class="button icon all-read inactive" href="#all-read" id="all-read">✓</a>
|
||||||
<canvas width="40" height="40"></canvas>
|
<canvas width="40" height="40"></canvas>
|
||||||
</header>
|
</header>
|
||||||
<article>
|
<article>
|
||||||
|
@ -71,11 +71,11 @@
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<label for="url">URL:</label>
|
<label for="url">URL:</label>
|
||||||
<input class="button" type="text" name="url" id="url" value="" placeholder="http://example.com/tt-rss/" />
|
<input class="button" type="text" name="url" id="url" value="" placeholder="http://example.com/tt-rss/" x-inputmode="verbatim" autocorrect="off" />
|
||||||
<label for="un">Username:</label>
|
<label for="un">Username:</label>
|
||||||
<input class="button" type="text" name="user" id="un" value="" placeholder="username" />
|
<input class="button" type="text" name="user" id="un" value="" placeholder="username" x-inputmode="verbatim" autocorrect="off" />
|
||||||
<label for="pw">Password:</label>
|
<label for="pw">Password:</label>
|
||||||
<input class="button" type="password" name="pass" id="pw" value="" placeholder="password" />
|
<input class="button" type="password" name="pass" id="pw" value="" placeholder="password" x-inputmode="verbatim" autocorrect="off" />
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<button class="button" type="submit">Sign in</button>
|
<button class="button" type="submit">Sign in</button>
|
||||||
|
@ -87,15 +87,16 @@
|
||||||
<section id="full">
|
<section id="full">
|
||||||
<header class="bar">
|
<header class="bar">
|
||||||
<a class="button icon list" href="#list"></a>
|
<a class="button icon list" href="#list"></a>
|
||||||
<a id="setstarred" class="button icon" href="#starred">☆</a>
|
<a id="setpublished" class="button icon inactive hidden" href="#published"></a>
|
||||||
<a id="setunread" class="button icon" href="#unread">✓</a>
|
<a id="setstarred" class="button icon inactive" href="#starred">★</a>
|
||||||
|
<a id="setunread" class="button icon inactive" href="#unread">✓</a>
|
||||||
<canvas width="40" height="40"></canvas>
|
<canvas width="40" height="40"></canvas>
|
||||||
</header>
|
</header>
|
||||||
<article>
|
<article>
|
||||||
<header>
|
<header>
|
||||||
<p><span class="feed_title"></span> <span class="author"></span></p>
|
<p><span class="feed_title"></span> <span class="author"></span></p>
|
||||||
|
<h1><a class="title" href="" target="_blank"></a></h1>
|
||||||
<p><timedate class="date"></timedate></p>
|
<p><timedate class="date"></timedate></p>
|
||||||
<p><a class="link" href="" target="_blank"></a></p>
|
|
||||||
</header>
|
</header>
|
||||||
<div class="article"></div>
|
<div class="article"></div>
|
||||||
<div class="info swipe">
|
<div class="info swipe">
|
||||||
|
|
198
js/App.js
198
js/App.js
|
@ -8,19 +8,12 @@ function App() {
|
||||||
this.setColor(color);
|
this.setColor(color);
|
||||||
this.fontChange();
|
this.fontChange();
|
||||||
|
|
||||||
var _this = this;
|
var numArticles = localStorage.numArticles;
|
||||||
|
if(!numArticles) numArticles = 50;
|
||||||
window.onkeydown = function(e) {
|
this.numArticles(numArticles);
|
||||||
if(e.keyCode == 39) {
|
var maxDownload = localStorage.maxDownload;
|
||||||
_this.showNext();
|
if(!maxDownload) maxDownload = 500000;
|
||||||
} else if(e.keyCode == 37) {
|
this.maxDownload(maxDownload);
|
||||||
_this.showPrevious();
|
|
||||||
} else if(e.keyCode == 13) {
|
|
||||||
_this.openInBrowser();
|
|
||||||
} else if(e.keyCode == 82) {
|
|
||||||
_this.reload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
App.prototype.authenticate = function() {
|
App.prototype.authenticate = function() {
|
||||||
|
@ -28,11 +21,11 @@ App.prototype.authenticate = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
App.prototype.after_login = function(backend) {
|
App.prototype.after_login = function(backend) {
|
||||||
/*
|
|
||||||
var request = window.navigator.mozApps.getSelf();
|
var request = window.navigator.mozApps.getSelf();
|
||||||
request.onsuccess = function() {
|
request.onsuccess = function() {
|
||||||
$("#version").innerHTML = request.result.manifest.version;
|
$("#version").innerHTML = request.result.manifest.version;
|
||||||
}*/
|
}
|
||||||
|
|
||||||
var _this = this;
|
var _this = this;
|
||||||
|
|
||||||
|
@ -40,7 +33,7 @@ App.prototype.after_login = function(backend) {
|
||||||
|
|
||||||
// do not reload page
|
// do not reload page
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
var url = window.location.hash;
|
var url = window.location.hash;
|
||||||
|
|
||||||
|
@ -61,6 +54,8 @@ App.prototype.after_login = function(backend) {
|
||||||
_this.toggleCurrentUnread();
|
_this.toggleCurrentUnread();
|
||||||
} else if(url == "#starred") {
|
} else if(url == "#starred") {
|
||||||
_this.toggleStarred();
|
_this.toggleStarred();
|
||||||
|
} else if(url == "#published") {
|
||||||
|
_this.togglePublished();
|
||||||
} else if(url == "#logout") {
|
} else if(url == "#logout") {
|
||||||
_this.logout();
|
_this.logout();
|
||||||
} else if(url == "#reset-info") {
|
} else if(url == "#reset-info") {
|
||||||
|
@ -98,11 +93,15 @@ App.prototype.after_login = function(backend) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up swiping
|
// set up swiping
|
||||||
jester($("#full")).flick(function(touches, direction) {
|
var options = {
|
||||||
if(direction == "left") _this.showNext();
|
dragLockToAxis: true,
|
||||||
else _this.showPrevious();
|
dragBlockHorizontal: true
|
||||||
});
|
};
|
||||||
|
var hammertime = new Hammer($("#full"), options);
|
||||||
|
hammertime.on("dragleft", function(ev){ ev.gesture.preventDefault(); });
|
||||||
|
hammertime.on("dragright", function(ev){ ev.gesture.preventDefault(); });
|
||||||
|
hammertime.on("swipeleft", function(ev){ _this.showNext(); ev.gesture.preventDefault(); });
|
||||||
|
hammertime.on("swiperight", function(ev){ _this.showPrevious(); ev.gesture.preventDefault(); });
|
||||||
|
|
||||||
this.changeToPage("#list");
|
this.changeToPage("#list");
|
||||||
|
|
||||||
|
@ -112,7 +111,16 @@ App.prototype.after_login = function(backend) {
|
||||||
this.backend = new Pond(this, localStorage.server_url, localStorage.session_id)
|
this.backend = new Pond(this, localStorage.server_url, localStorage.session_id)
|
||||||
} else {
|
} else {
|
||||||
this.backend = new TinyTinyRSS(this, localStorage.server_url, localStorage.session_id);
|
this.backend = new TinyTinyRSS(this, localStorage.server_url, localStorage.session_id);
|
||||||
|
$("#setpublished").removeClass("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var numArticles = localStorage.numArticles;
|
||||||
|
if(!numArticles) numArticles = 50;
|
||||||
|
this.numArticles(numArticles);
|
||||||
|
var maxDownload = localStorage.maxDownload;
|
||||||
|
if(!maxDownload) maxDownload = 500000;
|
||||||
|
this.maxDownload(maxDownload);
|
||||||
|
|
||||||
this.reload();
|
this.reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -150,30 +158,51 @@ App.prototype.setColor = function(color) {
|
||||||
|
|
||||||
App.prototype.reload = function() {
|
App.prototype.reload = function() {
|
||||||
this.unread_articles = [];
|
this.unread_articles = [];
|
||||||
$("#all-read").innerHTML = "❌";
|
$("#all-read").addClass('inactive');
|
||||||
this.backend.reload(this.gotUnreadFeeds.bind(this));
|
var number=parseInt(localStorage.numArticles);
|
||||||
|
this.backend.reload(this.gotUnreadFeeds.bind(this),number);
|
||||||
};
|
};
|
||||||
|
|
||||||
App.prototype.gotUnreadFeeds = function(new_articles) {
|
App.prototype.gotUnreadFeeds = function(new_articles) {
|
||||||
|
|
||||||
if(new_articles == null || !this.validate(new_articles)) { // on error load the saved unread articles.
|
if(new_articles == null || !this.validate(new_articles)) {
|
||||||
|
|
||||||
var old_articles = localStorage.unread_articles;
|
// Check if we did not get a NOT_LOGGED_IN error, and ask the
|
||||||
if(old_articles) {
|
// user to login again if it is the case.
|
||||||
this.unread_articles = JSON.parse(old_articles);
|
// This can happen with TT-RSS backend
|
||||||
|
if (new_articles.error && new_articles.error === "NOT_LOGGED_IN") {
|
||||||
|
alert("Your TinyTinyRSS session has expired. Please login again");
|
||||||
|
this.login.fillLoginFormFromLocalStorage();
|
||||||
|
this.login.log_in();
|
||||||
|
} else {
|
||||||
|
// On other errors, load the saved unread articles.
|
||||||
|
var old_articles = localStorage.unread_articles;
|
||||||
|
if(old_articles) {
|
||||||
|
this.unread_articles = JSON.parse(old_articles);
|
||||||
|
}
|
||||||
|
this.populateList();
|
||||||
}
|
}
|
||||||
this.populateList();
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
this.unread_articles = this.unread_articles.concat(new_articles);
|
this.unread_articles = this.unread_articles.concat(new_articles);
|
||||||
|
|
||||||
if(new_articles.length > 0) {
|
if(new_articles.length > 0) {
|
||||||
this.backend.getUnreadFeeds(this.gotUnreadFeeds.bind(this), this.unread_articles);
|
try {
|
||||||
} else {
|
//To check if when it fails it is the same
|
||||||
localStorage.unread_articles = JSON.stringify(this.unread_articles);
|
localStorage.unread_articles = JSON.stringify(this.unread_articles);
|
||||||
|
var size = parseInt(localStorage.maxDownload);
|
||||||
|
if(localStorage.unread_articles.length < size) {
|
||||||
|
var num = parseInt(localStorage.numArticles);
|
||||||
|
this.backend.getUnreadFeeds(this.gotUnreadFeeds.bind(this), this.unread_articles,num);
|
||||||
|
} else {
|
||||||
|
alert("Limit size reached: Downloaded: " + this.unread_articles.length + " articles. Reached: " + localStorage.unread_articles.length +" bytes");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert("Reached maximum memory by app " + e.name + " " + e.message + ". We will keep working in anycase with: " + localStorage.unread_articles.length);
|
||||||
|
}
|
||||||
this.populateList();
|
this.populateList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -196,10 +225,8 @@ App.prototype.populateList = function() {
|
||||||
html_str += "<li"+ (article.unread ? " class='unread'" : "") +">";
|
html_str += "<li"+ (article.unread ? " class='unread'" : "") +">";
|
||||||
html_str += "<a href='#full-"+i+"'>";
|
html_str += "<a href='#full-"+i+"'>";
|
||||||
html_str += "<p class='title'>" + article.feed_title + "</p>";
|
html_str += "<p class='title'>" + article.feed_title + "</p>";
|
||||||
var content = article.content.stripHTML();
|
html_str += "<h2>" + article.title + "</h2>";
|
||||||
if(content.replace(/^\s+|\s+$/g,'').length == 0) content = article.title;
|
if(article.excerpt) html_str += "<p class='excerpt'>" + article.excerpt + "</p>";
|
||||||
html_str += "<h2>" + content + "</h2>";
|
|
||||||
//if(article.excerpt) html_str += "<p class='excerpt'>" + article.excerpt + "</p>";
|
|
||||||
html_str += "</a></li>";
|
html_str += "</a></li>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,9 +249,9 @@ App.prototype.updateList = function() {
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
if(unread > 0) {
|
if(unread > 0) {
|
||||||
$("#all-read").innerHTML = "❌";
|
$("#all-read").addClass('inactive');
|
||||||
} else {
|
} else {
|
||||||
$("#all-read").innerHTML = "✓";
|
$("#all-read").removeClass('inactive');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updatePieChart();
|
this.updatePieChart();
|
||||||
|
@ -246,6 +273,7 @@ App.prototype.updatePieChart = function() {
|
||||||
|
|
||||||
var bg = window.getComputedStyle($("body"), null).backgroundColor;
|
var bg = window.getComputedStyle($("body"), null).backgroundColor;
|
||||||
var fg = window.getComputedStyle($(".bar"), null).backgroundColor;
|
var fg = window.getComputedStyle($(".bar"), null).backgroundColor;
|
||||||
|
var tx = window.getComputedStyle($(".bar"), null).color;
|
||||||
|
|
||||||
var myColor = [bg, fg];
|
var myColor = [bg, fg];
|
||||||
|
|
||||||
|
@ -270,7 +298,7 @@ App.prototype.updatePieChart = function() {
|
||||||
|
|
||||||
if(all > 0) {
|
if(all > 0) {
|
||||||
ctx.font = "12px FeuraSans, sans-serif";
|
ctx.font = "12px FeuraSans, sans-serif";
|
||||||
ctx.fillStyle = "#fff";
|
ctx.fillStyle = tx;
|
||||||
ctx.textAlign = "center";
|
ctx.textAlign = "center";
|
||||||
var text = unread + "/" + all;
|
var text = unread + "/" + all;
|
||||||
var x = canvas.width / 2;
|
var x = canvas.width / 2;
|
||||||
|
@ -290,9 +318,9 @@ App.prototype.showFull = function(article, slide_back) {
|
||||||
|
|
||||||
$(page_id + " .date").innerHTML = (new Date(parseInt(article.updated, 10) * 1000)).toLocaleString();
|
$(page_id + " .date").innerHTML = (new Date(parseInt(article.updated, 10) * 1000)).toLocaleString();
|
||||||
|
|
||||||
var link = $(page_id + " .link");
|
var title = $(page_id + " .title");
|
||||||
link.innerHTML = article.link;
|
title.innerHTML = article.title;
|
||||||
link.href = article.link;
|
title.href = article.link;
|
||||||
|
|
||||||
$(page_id + " .feed_title").innerHTML = article.feed_title;
|
$(page_id + " .feed_title").innerHTML = article.feed_title;
|
||||||
|
|
||||||
|
@ -300,23 +328,29 @@ App.prototype.showFull = function(article, slide_back) {
|
||||||
if(article.author && article.author.length > 0)
|
if(article.author && article.author.length > 0)
|
||||||
$(page_id + " .author").innerHTML = "– " + article.author;
|
$(page_id + " .author").innerHTML = "– " + article.author;
|
||||||
|
|
||||||
var content = article.content;
|
$(page_id + " .article").innerHTML = article.content;
|
||||||
if(content.replace(/^\s+|\s+$/g,'').length == 0) content = article.title;
|
|
||||||
$(page_id + " .article").innerHTML = content.urlify();
|
// Open all links in browser
|
||||||
$$(page_id + " .article a").forEach(function(o, i) {
|
$$(page_id + " .article a").forEach(function(o, i) {
|
||||||
o.target = "_blank";
|
o.target = "_blank";
|
||||||
});
|
});
|
||||||
|
|
||||||
if(article.unread) {
|
if(article.unread) {
|
||||||
$("#setunread").innerHTML = "❌";
|
$("#setunread").addClass('inactive');
|
||||||
} else {
|
} else {
|
||||||
$("#setunread").innerHTML = "✓";
|
$("#setunread").removeClass('inactive');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(article.marked) {
|
if(!article.marked) {
|
||||||
$("#setstarred").innerHTML = "★";
|
$("#setstarred").addClass('inactive');
|
||||||
} else {
|
} else {
|
||||||
$("#setstarred").innerHTML = "☆";
|
$("#setstarred").removeClass('inactive');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!article.published) {
|
||||||
|
$("#setpublished").addClass('inactive');
|
||||||
|
} else {
|
||||||
|
$("#setpublished").removeClass('inactive');
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -343,10 +377,6 @@ App.prototype.showPrevious = function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
App.prototype.openInBrowser = function() {
|
|
||||||
$("#full .link").click();
|
|
||||||
};
|
|
||||||
|
|
||||||
App.prototype.setCurrentRead = function() {
|
App.prototype.setCurrentRead = function() {
|
||||||
var article = this.unread_articles[this.currentIndex];
|
var article = this.unread_articles[this.currentIndex];
|
||||||
if(!article) return; // happens if we're not on a full article site
|
if(!article) return; // happens if we're not on a full article site
|
||||||
|
@ -358,7 +388,7 @@ App.prototype.setCurrentRead = function() {
|
||||||
|
|
||||||
article.set_unread = false;
|
article.set_unread = false;
|
||||||
|
|
||||||
$("#setunread").innerHTML = "✓";
|
$("#setunread").removeClass('inactive');
|
||||||
|
|
||||||
this.updatePieChart();
|
this.updatePieChart();
|
||||||
};
|
};
|
||||||
|
@ -368,11 +398,11 @@ App.prototype.toggleCurrentUnread = function() {
|
||||||
if(article.unread) {
|
if(article.unread) {
|
||||||
article.unread = false;
|
article.unread = false;
|
||||||
article.set_unread = false;
|
article.set_unread = false;
|
||||||
$("#setunread").innerHTML = "✓";
|
$("#setunread").removeClass('inactive');
|
||||||
} else {
|
} else {
|
||||||
article.unread = true;
|
article.unread = true;
|
||||||
article.set_unread = true;
|
article.set_unread = true;
|
||||||
$("#setunread").innerHTML = "❌";
|
$("#setunread").addClass('inactive');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateList();
|
this.updateList();
|
||||||
|
@ -381,7 +411,7 @@ App.prototype.toggleCurrentUnread = function() {
|
||||||
|
|
||||||
App.prototype.toggleAllRead = function() {
|
App.prototype.toggleAllRead = function() {
|
||||||
|
|
||||||
if($("#all-read").innerHTML == "❌") { // set all read
|
if($("#all-read").hasClass('inactive')) { // set all read
|
||||||
|
|
||||||
var articles = [];
|
var articles = [];
|
||||||
for (var i = 0; i < this.unread_articles.length; i++) {
|
for (var i = 0; i < this.unread_articles.length; i++) {
|
||||||
|
@ -390,10 +420,9 @@ App.prototype.toggleAllRead = function() {
|
||||||
article.set_unread = false;
|
article.set_unread = false;
|
||||||
articles.push(article);
|
articles.push(article);
|
||||||
}
|
}
|
||||||
$("#all-read").innerHTML = "✓";
|
$("#all-read").removeClass('inactive');
|
||||||
|
|
||||||
this.updateList();
|
this.updateList();
|
||||||
|
|
||||||
this.backend.setArticlesRead(articles);
|
this.backend.setArticlesRead(articles);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -405,11 +434,10 @@ App.prototype.toggleAllRead = function() {
|
||||||
article.set_unread = false;
|
article.set_unread = false;
|
||||||
articles.push(article);
|
articles.push(article);
|
||||||
}
|
}
|
||||||
$("#all-read").innerHTML = "❌";
|
$("#all-read").addClass('inactive');
|
||||||
this.updateList();
|
this.updateList();
|
||||||
|
|
||||||
this.backend.setArticlesUnread(articles);
|
this.backend.setArticlesUnread(articles);
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -421,13 +449,28 @@ App.prototype.toggleStarred = function() {
|
||||||
article.marked = true;
|
article.marked = true;
|
||||||
this.updateList();
|
this.updateList();
|
||||||
this.backend.setArticleStarred(article);
|
this.backend.setArticleStarred(article);
|
||||||
$("#setstarred").innerHTML = "★";
|
$("#setstarred").removeClass('inactive');
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
article.marked = false;
|
article.marked = false;
|
||||||
this.updateList();
|
this.updateList();
|
||||||
this.backend.setArticleUnstarred(article);
|
this.backend.setArticleUnstarred(article);
|
||||||
$("#setstarred").innerHTML = "☆";
|
$("#setstarred").addClass('inactive');
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
App.prototype.togglePublished = function() {
|
||||||
|
var article = this.unread_articles[this.currentIndex];
|
||||||
|
if(!article) return; // happens if we're not on a full article site
|
||||||
|
|
||||||
|
if(!article.published) {
|
||||||
|
article.published = true;
|
||||||
|
this.backend.setArticlePublished(article);
|
||||||
|
$("#setpublished").removeClass('inactive');
|
||||||
|
} else {
|
||||||
|
article.published = false;
|
||||||
|
this.backend.setArticleUnpublished(article);
|
||||||
|
$("#setpublished").addClass('inactive');
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -467,5 +510,20 @@ App.prototype.fontChange = function(size) {
|
||||||
document.body.addClass("f" + i);
|
document.body.addClass("f" + i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
App.prototype.numArticles = function(askfor) {
|
||||||
|
if(askfor < 200 && askfor > 0) {
|
||||||
|
localStorage.numArticles=askfor;
|
||||||
|
} else {
|
||||||
|
localStorage.numArticles=100;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
App.prototype.maxDownload = function(maxdata) {
|
||||||
|
if(maxdata < 5000000 && maxdata > 100000) {
|
||||||
|
localStorage.maxDownload=maxdata;
|
||||||
|
} else {
|
||||||
|
localStorage.maxDownload=500000;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
45
js/Login.js
45
js/Login.js
|
@ -17,6 +17,19 @@ Login.prototype.is_logged_in = function() {
|
||||||
|
|
||||||
Login.prototype.log_in = function() {
|
Login.prototype.log_in = function() {
|
||||||
this.app.changeToPage("#login");
|
this.app.changeToPage("#login");
|
||||||
|
$("#login form").backend.forEach(function(o, i) {
|
||||||
|
o.addEventListener("change", function(e) {
|
||||||
|
if(e.target.checked) {
|
||||||
|
if(e.target.value == "OwnCloud") {
|
||||||
|
$("#url").placeholder = "http://example.com/owncloud/";
|
||||||
|
} else if(e.target.value == "Pond") {
|
||||||
|
$("#url").placeholder = "http://example.com/pond/";
|
||||||
|
} else {
|
||||||
|
$("#url").placeholder = "http://example.com/tt-rss/";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
$("#login form").addEventListener('submit', this.authenticate.bind(this));
|
$("#login form").addEventListener('submit', this.authenticate.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,10 +38,11 @@ Login.prototype.authenticate = function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
var backend = "Pond";
|
var backend = "TinyTinyRSS";
|
||||||
|
if($("#login form").backend[1].checked) backend = "OwnCloud";
|
||||||
|
else if($("#login form").backend[2].checked) backend = "Pond";
|
||||||
|
|
||||||
var server_url = window.location.href.split("#")[0].replace(/\/FeedMonkey\//, '');
|
var server_url = $("#url").value;
|
||||||
console.log(server_url)
|
|
||||||
var user = $("#un").value;
|
var user = $("#un").value;
|
||||||
var password = $("#pw").value;
|
var password = $("#pw").value;
|
||||||
|
|
||||||
|
@ -53,6 +67,7 @@ Login.prototype.authenticate = function(e) {
|
||||||
if(data.version) {
|
if(data.version) {
|
||||||
var auth = btoa(user + ':' + password);
|
var auth = btoa(user + ':' + password);
|
||||||
localStorage.server_url = server_url;
|
localStorage.server_url = server_url;
|
||||||
|
localStorage.username = user;
|
||||||
localStorage.session_id = auth;
|
localStorage.session_id = auth;
|
||||||
localStorage.backend = "OwnCloud";
|
localStorage.backend = "OwnCloud";
|
||||||
_this.app.after_login(localStorage.backend);
|
_this.app.after_login(localStorage.backend);
|
||||||
|
@ -69,6 +84,7 @@ Login.prototype.authenticate = function(e) {
|
||||||
Pond.login(server_url, user, password, function(data) {
|
Pond.login(server_url, user, password, function(data) {
|
||||||
if(data.session_token) {
|
if(data.session_token) {
|
||||||
localStorage.server_url = server_url;
|
localStorage.server_url = server_url;
|
||||||
|
localStorage.username = user;
|
||||||
localStorage.session_id = data.session_token;
|
localStorage.session_id = data.session_token;
|
||||||
localStorage.backend = "Pond";
|
localStorage.backend = "Pond";
|
||||||
_this.app.after_login(localStorage.backend);
|
_this.app.after_login(localStorage.backend);
|
||||||
|
@ -92,6 +108,7 @@ Login.prototype.authenticate = function(e) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
localStorage.server_url = server_url;
|
localStorage.server_url = server_url;
|
||||||
|
localStorage.username = user;
|
||||||
localStorage.session_id = data.session_id;
|
localStorage.session_id = data.session_id;
|
||||||
localStorage.backend = "TinyTinyRSS";
|
localStorage.backend = "TinyTinyRSS";
|
||||||
_this.app.after_login(localStorage.backend);
|
_this.app.after_login(localStorage.backend);
|
||||||
|
@ -106,7 +123,29 @@ Login.prototype.authenticate = function(e) {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Login.prototype.fillLoginFormFromLocalStorage = function() {
|
||||||
|
var serverUrl = localStorage.server_url;
|
||||||
|
if (serverUrl) {
|
||||||
|
$("#url").value = serverUrl;
|
||||||
|
}
|
||||||
|
var userName = localStorage.username;
|
||||||
|
if (userName) {
|
||||||
|
$("#un").value = userName;
|
||||||
|
}
|
||||||
|
var backendName = localStorage.backend;
|
||||||
|
if (backendName === "TinyTinyRSS") {
|
||||||
|
$("#login form").backend[0].checked = true;
|
||||||
|
}
|
||||||
|
else if (backendName === "OwnCloud") {
|
||||||
|
$("#login form").backend[1].checked = true;
|
||||||
|
}
|
||||||
|
else if (backendName === "Pond") {
|
||||||
|
$("#login form").backend[2].checked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Login.prototype.log_out = function() {
|
Login.prototype.log_out = function() {
|
||||||
|
this.fillLoginFormFromLocalStorage();
|
||||||
localStorage.removeItem("server_url");
|
localStorage.removeItem("server_url");
|
||||||
localStorage.removeItem("session_id");
|
localStorage.removeItem("session_id");
|
||||||
localStorage.removeItem("unread_articles");
|
localStorage.removeItem("unread_articles");
|
||||||
|
|
259
js/OwnCloud.js
Normal file
259
js/OwnCloud.js
Normal file
|
@ -0,0 +1,259 @@
|
||||||
|
function OwnCloud(app, server_url, user_pass_btoa) {
|
||||||
|
this.app = app;
|
||||||
|
this.server_url = server_url;
|
||||||
|
this.session_id = user_pass_btoa;
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnCloud.prototype.onoffline = function() {
|
||||||
|
// Do nothing
|
||||||
|
};
|
||||||
|
|
||||||
|
OwnCloud.prototype.ononline = function() {
|
||||||
|
|
||||||
|
["read", "unread", "starred", "unstarred"].forEach(function(type) {
|
||||||
|
var articles = localStorage[type + "_articles"];
|
||||||
|
if(articles) {
|
||||||
|
var callback = function(ok) { if(ok) localStorage[type + "_articles"] = null }
|
||||||
|
this.call("setArticles" + type.capitalize(), [JSON.parse(articles), callback]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
OwnCloud.prototype.doOperation = function(method, operation, new_options, callback) {
|
||||||
|
if(!navigator.onLine) {
|
||||||
|
callback(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = this.server_url + "/index.php/apps/news/api/v1-2/" + operation;
|
||||||
|
var options = {};
|
||||||
|
|
||||||
|
for (var key in new_options) {
|
||||||
|
options[key] = new_options[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(method == "GET" || method == "HEAD") {
|
||||||
|
var a = [];
|
||||||
|
for(var key in options) {
|
||||||
|
a.push(key + "=" + options[key]);
|
||||||
|
}
|
||||||
|
url += "?" + 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.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
||||||
|
xhr.withCredentials = true;
|
||||||
|
xhr.setRequestHeader('Authorization', 'Basic ' + this.session_id);
|
||||||
|
var body = JSON.stringify(options);
|
||||||
|
xhr.send(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnCloud.prototype.reload = function(callback,limit) {
|
||||||
|
var _this = this;
|
||||||
|
this.getFeeds(function() { _this.getUnreadFeeds(callback,0,limit); });
|
||||||
|
};
|
||||||
|
|
||||||
|
OwnCloud.prototype.getUnreadFeeds = function(callback, skip, limit) {
|
||||||
|
if(skip) {
|
||||||
|
skip = skip[skip.length - 1].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
batchSize: limit || 700,
|
||||||
|
offset: skip || 0,
|
||||||
|
type: 3,
|
||||||
|
id: 0,
|
||||||
|
getRead: false
|
||||||
|
};
|
||||||
|
|
||||||
|
var _this = this;
|
||||||
|
this.doOperation("GET", "items", options, function(data) {
|
||||||
|
|
||||||
|
var items = data.items;
|
||||||
|
|
||||||
|
function isFeedAvailable(o) {
|
||||||
|
return !!_this.feeds[o.feedId];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(items.every(isFeedAvailable)) {
|
||||||
|
callback(items.map(_this.normalize_article, _this));
|
||||||
|
} else {
|
||||||
|
_this.getFeeds(function() {
|
||||||
|
callback(items.map(_this.normalize_article, _this));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
OwnCloud.prototype.toString = function() {
|
||||||
|
return "OwnCloud"
|
||||||
|
};
|
||||||
|
|
||||||
|
OwnCloud.prototype.getFeeds = function(callback) {
|
||||||
|
var _this = this;
|
||||||
|
this.doOperation("GET", "feeds", {}, function(data) {
|
||||||
|
|
||||||
|
_this.feeds = {};
|
||||||
|
for (var i = 0; i < data.feeds.length; i++) {
|
||||||
|
var feed = data.feeds[i];
|
||||||
|
_this.feeds[feed.id] = feed;
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.feeds = JSON.stringify(_this.feeds);
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
OwnCloud.prototype.setArticlesRead = function(articles, callback) {
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
items: articles.map(function(o) { return o.id; }),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (navigator.onLine) {
|
||||||
|
this.doOperation("PUT", "items/read/multiple", options, callback);
|
||||||
|
} else {
|
||||||
|
this.append("read_articles", articles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnCloud.prototype.setArticleRead = function(article, callback) {
|
||||||
|
this.setArticlesRead([article], callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnCloud.prototype.setArticlesUnread = function(articles, callback) {
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
items: articles.map(function(o) { return o.id; }),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (navigator.onLine) this.doOperation("PUT", "items/unread/multiple", options, callback);
|
||||||
|
else {
|
||||||
|
this.append("unread_articles", articles);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
OwnCloud.prototype.setArticleUnread = function(article, callback) {
|
||||||
|
this.setArticlesUnread([article], callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnCloud.prototype.setArticlesStarred = function(articles, callback) {
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
items: articles.map(function(o) { return { feedId: o.feed_id, guidHash: o.guid_hash }; })
|
||||||
|
};
|
||||||
|
|
||||||
|
if (navigator.onLine) {
|
||||||
|
this.doOperation("PUT", "items/star/multiple", options, callback);
|
||||||
|
} else {
|
||||||
|
this.append("starred_articles", articles);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
OwnCloud.prototype.setArticleStarred = function(article, callback) {
|
||||||
|
this.setArticlesStarred([article], callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnCloud.prototype.setArticlesUnstarred = function(articles, callback) {
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
items: articles.map(function(o) { return { feedId: o.feed_id, guidHash: o.guid_hash }; })
|
||||||
|
};
|
||||||
|
|
||||||
|
if (navigator.onLine) {
|
||||||
|
this.doOperation("PUT", "items/unstar/multiple", options, callback);
|
||||||
|
} else {
|
||||||
|
this.append("unstarred_articles", articles);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
OwnCloud.prototype.setArticleUnstarred = function(articles, callback) {
|
||||||
|
this.setArticlesUnstarred([articles], callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnCloud.prototype.normalize_article = function(article) {
|
||||||
|
var feed = this.feeds[article.feedId];
|
||||||
|
var feed_title = "";
|
||||||
|
if(feed) {
|
||||||
|
feed_title = feed.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: article.id,
|
||||||
|
guid_hash: article.guidHash,
|
||||||
|
title: article.title,
|
||||||
|
content: article.body,
|
||||||
|
feed_title: feed_title,
|
||||||
|
feed_id: article.feedId,
|
||||||
|
excerpt: article.body.stripHTML().substring(0, 100),
|
||||||
|
updated: article.pubDate,
|
||||||
|
link: article.link,
|
||||||
|
marked: article.starred,
|
||||||
|
unread: article.unread
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
OwnCloud.prototype.logOut = function() {
|
||||||
|
this.doOperation("logout");
|
||||||
|
localStorage.feeds = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
OwnCloud.prototype.getFeedFor = function(o) {
|
||||||
|
return this.feeds[o.feedId];
|
||||||
|
};
|
||||||
|
|
||||||
|
OwnCloud.prototype.append = function(key, array) {
|
||||||
|
|
||||||
|
var tmp = localStorage[key];
|
||||||
|
|
||||||
|
if (typeof tmp !== "undefined") tmp = JSON.parse(tmp);
|
||||||
|
else tmp = [];
|
||||||
|
|
||||||
|
tmp.concat(array);
|
||||||
|
localStorage[key] = JSON.stringify(tmp);
|
||||||
|
};
|
||||||
|
|
||||||
|
OwnCloud.login = function(server_url, user, password, callback) {
|
||||||
|
|
||||||
|
var url = server_url + "/index.php/apps/news/api/v1-2/version";
|
||||||
|
|
||||||
|
var xhr = new XMLHttpRequest({mozSystem: true});
|
||||||
|
xhr.onreadystatechange = function() {
|
||||||
|
if(xhr.readyState == 4) {
|
||||||
|
if(xhr.status == 200) {
|
||||||
|
callback(JSON.parse(xhr.responseText))
|
||||||
|
} else {
|
||||||
|
if(xhr.status == 0) {
|
||||||
|
alert("Something went wrong, please check your credentials and the server address")
|
||||||
|
} else {
|
||||||
|
alert("error: " + xhr.status + " " + xhr.statusText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xhr.open("GET", url, true);
|
||||||
|
xhr.withCredentials = true;
|
||||||
|
var auth = btoa(user + ':' + password);
|
||||||
|
xhr.setRequestHeader('Authorization', 'Basic ' + auth);
|
||||||
|
xhr.send();
|
||||||
|
}
|
46
js/Pond.js
46
js/Pond.js
|
@ -15,7 +15,13 @@ Pond.prototype.onoffline = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
Pond.prototype.ononline = function() {
|
Pond.prototype.ononline = function() {
|
||||||
// Send read
|
["read", "unread"].forEach(function(type) {
|
||||||
|
var articles = localStorage[type + "_articles"];
|
||||||
|
if(articles) {
|
||||||
|
var callback = function(ok) { if(ok) localStorage[type + "_articles"] = null }
|
||||||
|
this.call("setArticles" + type.capitalize(), [JSON.parse(articles), callback]);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Pond.prototype.toString = function() {
|
Pond.prototype.toString = function() {
|
||||||
|
@ -69,15 +75,15 @@ Pond.prototype.doOperation = function(method, operation, new_options, callback)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Pond.prototype.reload = function(callback) {
|
Pond.prototype.reload = function(callback,limit) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
this.getFeeds(function() { _this.getUnreadFeeds(callback); });
|
this.getFeeds(function() { _this.getUnreadFeeds(callback,0,limit); });
|
||||||
};
|
};
|
||||||
|
|
||||||
Pond.prototype.getUnreadFeeds = function(callback, skip) {
|
Pond.prototype.getUnreadFeeds = function(callback, skip, limit) {
|
||||||
var options = {
|
var options = {
|
||||||
status: "unread",
|
status: "unread",
|
||||||
limit: 100
|
limit: limit || 100
|
||||||
};
|
};
|
||||||
|
|
||||||
if(skip && skip.length > 0) {
|
if(skip && skip.length > 0) {
|
||||||
|
@ -106,6 +112,8 @@ Pond.prototype.getUnreadFeeds = function(callback, skip) {
|
||||||
Pond.prototype.getFeeds = function(callback) {
|
Pond.prototype.getFeeds = function(callback) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
this.doOperation("GET", "subscriptions", {}, function(feeds) {
|
this.doOperation("GET", "subscriptions", {}, function(feeds) {
|
||||||
|
|
||||||
|
if(!feeds) return;
|
||||||
|
|
||||||
_this.feeds = {};
|
_this.feeds = {};
|
||||||
for (var i = 0; i < feeds.length; i++) {
|
for (var i = 0; i < feeds.length; i++) {
|
||||||
|
@ -157,11 +165,11 @@ Pond.prototype.setArticleStatus = function(article, callback, status) {
|
||||||
|
|
||||||
var url = "subscriptions/" + article.feed_id + "/articles/" + article.id
|
var url = "subscriptions/" + article.feed_id + "/articles/" + article.id
|
||||||
|
|
||||||
if (navigator.onLine) this.doOperation("PUT", url, options, callback);
|
if (navigator.onLine) {
|
||||||
else {
|
this.doOperation("PUT", url, options, callback);
|
||||||
this.append("unread_articles", articles);
|
} else {
|
||||||
|
this.append(status + "_articles", articles);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Pond.prototype.setArticleRead = function(article, callback) {
|
Pond.prototype.setArticleRead = function(article, callback) {
|
||||||
|
@ -179,7 +187,7 @@ Pond.prototype.setArticlesRead = function(articles, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Pond.prototype.setArticlesUnread = function(articles, callback) {
|
Pond.prototype.setArticlesUnread = function(articles, callback) {
|
||||||
articles.forEach(function(article) {
|
articles.forEach(function(article) {
|
||||||
this.setArticleStatus(article, callback, "unread");
|
this.setArticleStatus(article, callback, "unread");
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -192,6 +200,16 @@ Pond.prototype.setArticleUnstarred = function(articles, callback) {
|
||||||
// not implemented yet in Pond
|
// not implemented yet in Pond
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.append = function(key, array) {
|
||||||
|
var tmp = localStorage[key];
|
||||||
|
|
||||||
|
if (typeof tmp !== "undefined") tmp = JSON.parse(tmp);
|
||||||
|
else tmp = [];
|
||||||
|
|
||||||
|
tmp.concat(array);
|
||||||
|
localStorage[key] = JSON.stringify(tmp);
|
||||||
|
};
|
||||||
|
|
||||||
Pond.prototype.logOut = function() {
|
Pond.prototype.logOut = function() {
|
||||||
this.doOperation("DELETE", "auth/sessions/" + this.session_token );
|
this.doOperation("DELETE", "auth/sessions/" + this.session_token );
|
||||||
localStorage.feeds = null;
|
localStorage.feeds = null;
|
||||||
|
@ -213,7 +231,11 @@ Pond.login = function(server_url, user, password, callback) {
|
||||||
if(xhr.status == 201) {
|
if(xhr.status == 201) {
|
||||||
callback(JSON.parse(xhr.responseText))
|
callback(JSON.parse(xhr.responseText))
|
||||||
} else {
|
} else {
|
||||||
alert("error: " + typeof(xhr.status) + " " + xhr.statusText + "\n\n" + xhr.responseText)
|
if(xhr.status == 0) {
|
||||||
|
alert("Something went wrong, please check your credentials and the server address")
|
||||||
|
} else {
|
||||||
|
alert("error: " + typeof(xhr.status) + " " + xhr.statusText + "\n\n" + xhr.responseText);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,4 +244,4 @@ Pond.login = function(server_url, user, password, callback) {
|
||||||
xhr.setRequestHeader("Content-Length", options.length);
|
xhr.setRequestHeader("Content-Length", options.length);
|
||||||
xhr.setRequestHeader("Connection", "close");
|
xhr.setRequestHeader("Connection", "close");
|
||||||
xhr.send(options);
|
xhr.send(options);
|
||||||
}
|
}
|
||||||
|
|
225
js/TinyTinyRSS.js
Normal file
225
js/TinyTinyRSS.js
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
function TinyTinyRSS(app, server_url, session_id) {
|
||||||
|
this.app = app;
|
||||||
|
this.server_url = server_url;
|
||||||
|
this.session_id = session_id;
|
||||||
|
|
||||||
|
window.addEventListener("offline", this.onoffline.bind(this));
|
||||||
|
window.addEventListener("online", this.ononline.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.onoffline = function() {
|
||||||
|
// Do nothing
|
||||||
|
};
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.ononline = function() {
|
||||||
|
|
||||||
|
["read", "unread", "starred", "unstarred", "published", "unpublished"].forEach(function(type) {
|
||||||
|
var articles = localStorage[type + "_articles"];
|
||||||
|
if(articles) {
|
||||||
|
var callback = function(ok) { if(ok) localStorage[type + "_articles"] = null }
|
||||||
|
this.call("setArticles" + type.capitalize(), [JSON.parse(articles), callback]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.doOperation = function(operation, new_options, callback) {
|
||||||
|
if(!navigator.onLine) {
|
||||||
|
callback(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = this.server_url + "/api/";
|
||||||
|
var options = {
|
||||||
|
sid: this.session_id,
|
||||||
|
op: operation
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var key in new_options) {
|
||||||
|
options[key] = new_options[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
var xhr = new XMLHttpRequest({mozSystem: true});
|
||||||
|
xhr.onreadystatechange = function() {
|
||||||
|
if(xhr.readyState == 4) {
|
||||||
|
if(xhr.status == 200) {
|
||||||
|
if(callback)
|
||||||
|
callback(JSON.parse(xhr.responseText).content);
|
||||||
|
} else {
|
||||||
|
if(xhr.status != 0) alert("error: " + xhr.status + " " + xhr.statusText);
|
||||||
|
if(callback) callback(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xhr.open("POST", url, true);
|
||||||
|
xhr.send(JSON.stringify(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.reload = function(callback,limit) {
|
||||||
|
this.getUnreadFeeds(callback, 0, limit);
|
||||||
|
};
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.getUnreadFeeds = function(callback, skip, limit) {
|
||||||
|
skip = skip.length;
|
||||||
|
var options = {
|
||||||
|
show_excerpt: false,
|
||||||
|
view_mode: "unread",
|
||||||
|
show_content: true,
|
||||||
|
feed_id: -4,
|
||||||
|
limit: limit || 0,
|
||||||
|
skip: skip || 0
|
||||||
|
};
|
||||||
|
|
||||||
|
this.doOperation("getHeadlines", options, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.setArticlesRead = function(articles, callback) {
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
article_ids: articles.map(function(o) { return o.id }).join(","),
|
||||||
|
mode: 0,
|
||||||
|
field: 2
|
||||||
|
};
|
||||||
|
|
||||||
|
if (navigator.onLine) {
|
||||||
|
this.doOperation("updateArticle", options, callback);
|
||||||
|
} else {
|
||||||
|
this.append("read_articles", articles);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.setArticleRead = function(article, callback) {
|
||||||
|
this.setArticlesRead([article], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.setArticlesUnread = function(articles, callback) {
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
article_ids: articles.map(function(o) { return o.id }).join(","),
|
||||||
|
mode: 1,
|
||||||
|
field: 2
|
||||||
|
};
|
||||||
|
|
||||||
|
if (navigator.onLine) {
|
||||||
|
this.doOperation("updateArticle", options, callback);
|
||||||
|
} else {
|
||||||
|
this.append("unread_articles", articles);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.setArticleUnread = function(article, callback) {
|
||||||
|
this.setArticlesUnread([article], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.setArticlesStarred = function(articles, callback) {
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
article_ids: articles.map(function(o) { return o.id }).join(","),
|
||||||
|
mode: 1,
|
||||||
|
field: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
if (navigator.onLine) {
|
||||||
|
this.doOperation("updateArticle", options);
|
||||||
|
} else {
|
||||||
|
this.append("starred_articles", articles);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.setArticleStarred = function(article, callback) {
|
||||||
|
this.setArticlesStarred([article], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.setArticlesUnstarred = function(articles, callback) {
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
article_ids: articles.map(function(o) { return o.id}).join(","),
|
||||||
|
mode: 0,
|
||||||
|
field: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
if (navigator.onLine) {
|
||||||
|
this.doOperation("updateArticle", options, callback);
|
||||||
|
} else {
|
||||||
|
this.append("unstarred_articles", articles);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.setArticleUnstarred = function(article, callback) {
|
||||||
|
this.setArticlesUnstarred([article], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.setArticlesPublished = function(articles, callback) {
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
article_ids: articles.map(function(o) { return o.id }).join(","),
|
||||||
|
mode: 1,
|
||||||
|
field: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
if (navigator.onLine) {
|
||||||
|
this.doOperation("updateArticle", options);
|
||||||
|
} else {
|
||||||
|
this.append("published_articles", articles);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.setArticlePublished = function(article, callback) {
|
||||||
|
this.setArticlesPublished([article], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.setArticlesUnpublished = function(articles, callback) {
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
article_ids: articles.map(function(o) { return o.id}).join(","),
|
||||||
|
mode: 0,
|
||||||
|
field: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
if (navigator.onLine) {
|
||||||
|
this.doOperation("updateArticle", options, callback);
|
||||||
|
} else {
|
||||||
|
this.append("unpublished_articles", articles);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.setArticleUnpublished = function(article, callback) {
|
||||||
|
this.setArticlesUnpublished([article], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.append = function(key, array) {
|
||||||
|
var tmp = localStorage[key];
|
||||||
|
|
||||||
|
if (typeof tmp !== "undefined") tmp = JSON.parse(tmp);
|
||||||
|
else tmp = [];
|
||||||
|
|
||||||
|
tmp.concat(array);
|
||||||
|
localStorage[key] = JSON.stringify(tmp);
|
||||||
|
};
|
||||||
|
|
||||||
|
TinyTinyRSS.prototype.logOut = function() {
|
||||||
|
this.doOperation("logout");
|
||||||
|
};
|
||||||
|
|
||||||
|
TinyTinyRSS.login = function(server_url, user, password, callback) {
|
||||||
|
|
||||||
|
var url = server_url + "/api/";
|
||||||
|
var options = {op: "login", user: user, password: password};
|
||||||
|
|
||||||
|
var xhr = new XMLHttpRequest({mozSystem: true});
|
||||||
|
xhr.onreadystatechange = function() {
|
||||||
|
if(xhr.readyState == 4) {
|
||||||
|
if(xhr.status == 200) {
|
||||||
|
callback(JSON.parse(xhr.responseText).content)
|
||||||
|
} else {
|
||||||
|
if(xhr.status == 0) {
|
||||||
|
alert("Something went wrong, please check your credentials and the server address")
|
||||||
|
} else {
|
||||||
|
alert("error: " + xhr.status + " " + xhr.statusText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xhr.open("POST", url, true);
|
||||||
|
xhr.send(JSON.stringify(options));
|
||||||
|
}
|
|
@ -63,10 +63,5 @@ String.prototype.capitalize = function() {
|
||||||
return this.charAt(0).toUpperCase() + this.slice(1);
|
return this.charAt(0).toUpperCase() + this.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
String.prototype.urlify = function() {
|
|
||||||
var exp = /[^\>](https?:\/\/[^\s\<]*)/ig;
|
|
||||||
return this.replace(exp," <a href='$1'>$1</a>");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!window.app) window.app = new App();
|
if(!window.app) window.app = new App();
|
||||||
|
|
||||||
|
|
2163
js/hammer.js
Normal file
2163
js/hammer.js
Normal file
File diff suppressed because it is too large
Load diff
599
js/jester.js
599
js/jester.js
|
@ -1,599 +0,0 @@
|
||||||
/*
|
|
||||||
* Jester JavaScript Library v0.3
|
|
||||||
* http://github.com/plainview/Jester
|
|
||||||
*
|
|
||||||
* Easy JavaScript gesture recognition.
|
|
||||||
*
|
|
||||||
* Released under MIT License
|
|
||||||
*
|
|
||||||
* Copyright (C) 2011 by Scott Seaward
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
(function(container, undefined) {
|
|
||||||
var Jester = container.Jester = {
|
|
||||||
cache : {},
|
|
||||||
cacheId : "Jester" + (new Date()).getTime(),
|
|
||||||
guid : 0,
|
|
||||||
|
|
||||||
// The Jester constructor
|
|
||||||
Watcher : function(element, options) {
|
|
||||||
|
|
||||||
var that = this,
|
|
||||||
cacheId = Jester.cacheId,
|
|
||||||
cache = Jester.cache,
|
|
||||||
gestures = "swipe flick tap doubletap pinchnarrow pinchwiden pinchend";
|
|
||||||
|
|
||||||
if(!element || !element.nodeType) {
|
|
||||||
throw new TypeError("Jester: no element given.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// if this element hasn't had Jester called on it before,
|
|
||||||
// set it up with a cache entry and give it the expando
|
|
||||||
if(typeof element[cacheId] !== "number") {
|
|
||||||
element[cacheId] = Jester.guid;
|
|
||||||
Jester.guid++;
|
|
||||||
}
|
|
||||||
|
|
||||||
var elementId = element[cacheId];
|
|
||||||
|
|
||||||
if(!(elementId in cache)) {
|
|
||||||
Jester.cache[elementId] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
var elementCache = Jester.cache[elementId];
|
|
||||||
|
|
||||||
if(!("options" in elementCache)) {
|
|
||||||
elementCache.options = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
options = options || elementCache.options || {};
|
|
||||||
|
|
||||||
// cache the option values for reuse or, if options already
|
|
||||||
// exist for this element, replace those that have been
|
|
||||||
// specified
|
|
||||||
if(elementCache.options !== options) {
|
|
||||||
for(var prop in options) {
|
|
||||||
if(elementCache.options[prop]) {
|
|
||||||
if(elementCache.options[prop] !== options[prop]) {
|
|
||||||
elementCache.options[prop] = options[prop];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
elementCache.options[prop] = options[prop];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!("eventSet" in elementCache) || !(elementCache.eventSet instanceof Jester.EventSet)) {
|
|
||||||
elementCache.eventSet = new Jester.EventSet(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!elementCache.touchMonitor) {
|
|
||||||
elementCache.touchMonitor = new Jester.TouchMonitor(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
var events = elementCache.eventSet;
|
|
||||||
var touches = elementCache.touchMonitor;
|
|
||||||
|
|
||||||
this.id = element[cacheId];
|
|
||||||
|
|
||||||
this.bind = function(evt, fn) {
|
|
||||||
if(evt && typeof evt === "string" && typeof fn === "function") {
|
|
||||||
events.register(evt, fn);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
// create shortcut bind methods for all gestures
|
|
||||||
gestures.split(" ").forEach(function(gesture) {
|
|
||||||
this[gesture] = function(fn) {
|
|
||||||
return this.bind(gesture, fn);
|
|
||||||
};
|
|
||||||
}, that);
|
|
||||||
|
|
||||||
this.start = function(fn) {
|
|
||||||
return this.bind("start", fn);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.during = function(fn) {
|
|
||||||
return this.bind("during", fn);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.end = function(fn) {
|
|
||||||
return this.bind("end", fn);
|
|
||||||
};
|
|
||||||
|
|
||||||
// wrapper to cover all three pinch methods
|
|
||||||
this.pinch = function(fns) {
|
|
||||||
if(typeof fns !== "undefined") {
|
|
||||||
// if its just a function it gets assigned to pinchend
|
|
||||||
if(typeof fns === "function") {
|
|
||||||
that.pinchend(fns);
|
|
||||||
}
|
|
||||||
else if(typeof fns === "object") {
|
|
||||||
var method;
|
|
||||||
"narrow widen end".split(" ").forEach(function(eventExt) {
|
|
||||||
method = "pinch" + eventExt;
|
|
||||||
if(typeof fns[eventExt] === "function") {
|
|
||||||
that[method](fns[eventExt]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.halt = function() {
|
|
||||||
touches.stopListening();
|
|
||||||
events.clear();
|
|
||||||
delete elementCache.eventSet;
|
|
||||||
delete elementCache.touchMonitor;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
EventSet : function(element) {
|
|
||||||
// all event names and their associated functions in an array i.e. "swipe" : [fn1, fn2, fn2]
|
|
||||||
var eventsTable = {};
|
|
||||||
this.eventsTable = eventsTable;
|
|
||||||
|
|
||||||
// register a handler with an event
|
|
||||||
this.register = function(eventName, fn) {
|
|
||||||
// if the event exists and has handlers attached to it, add this one to the array of them
|
|
||||||
if(eventsTable[eventName] && eventsTable[eventName].push) {
|
|
||||||
// make sure multiple copies of the same handler aren't inserted
|
|
||||||
if(!~eventsTable[eventName].indexOf(fn)) {
|
|
||||||
eventsTable[eventName].push(fn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// create a new array bound to the event containing only the handler passed in
|
|
||||||
eventsTable[eventName] = [fn];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.release = function(eventName, fn) {
|
|
||||||
if(typeof eventName === "undefined") return;
|
|
||||||
|
|
||||||
// if a handler hasn't been specified, remove all handlers
|
|
||||||
if(typeof fn === "undefined") {
|
|
||||||
for(var handlers in eventsTable.eventName) {
|
|
||||||
delete eventsTable.eventName[handlers];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// pull the given handler from the given event
|
|
||||||
if(eventsTable[eventName] && ~eventsTable[eventName].indexOf(fn))
|
|
||||||
{
|
|
||||||
eventsTable[eventName].splice(eventsTable[eventName].indexOf(fn), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the event has no more handlers registered to it, get rid of the event completely
|
|
||||||
if(eventsTable[eventName] && eventsTable[eventName].length === 0) {
|
|
||||||
delete eventsTable[eventName];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// completely remove all events and their handlers
|
|
||||||
this.clear = function() {
|
|
||||||
var events;
|
|
||||||
for(events in eventsTable) {
|
|
||||||
delete eventsTable[events];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// get all the handlers associated with an event
|
|
||||||
// return an empty array if nothing is registered with the given event name
|
|
||||||
this.getHandlers = function(eventName) {
|
|
||||||
if(eventsTable[eventName] && eventsTable[eventName].length) {
|
|
||||||
return eventsTable[eventName];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// inject an array of handlers into the event table for the given event
|
|
||||||
// this will klobber all current handlers associated with the event
|
|
||||||
this.setHandlers = function(eventName, handlers) {
|
|
||||||
eventsTable[eventName] = handlers;
|
|
||||||
};
|
|
||||||
|
|
||||||
// execute all handlers associated with an event, passing each handler the arguments provided after the event's name.
|
|
||||||
this.execute = function(eventName) {
|
|
||||||
if(typeof eventName === "undefined") return;
|
|
||||||
|
|
||||||
// if the event asked for exists in the events table
|
|
||||||
if(eventsTable[eventName] && eventsTable[eventName].length) {
|
|
||||||
// get the arguments sent to the function
|
|
||||||
var args = Array.prototype.slice.call(arguments, 1);
|
|
||||||
|
|
||||||
// iterate throuh all the handlers
|
|
||||||
for(var i = 0; i < eventsTable[eventName].length; i++) {
|
|
||||||
// check current handler is a function
|
|
||||||
if(typeof eventsTable[eventName][i] === "function") {
|
|
||||||
// execute handler with the provided arguments
|
|
||||||
eventsTable[eventName][i].apply(element, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
TouchMonitor : function(element)
|
|
||||||
{
|
|
||||||
var cacheId = Jester.cacheId,
|
|
||||||
elementId = element[cacheId],
|
|
||||||
cache = Jester.cache,
|
|
||||||
elementCache = cache[elementId],
|
|
||||||
opts = elementCache.options;
|
|
||||||
|
|
||||||
opts.move = opts.move || {};
|
|
||||||
opts.scale = opts.scale || {};
|
|
||||||
|
|
||||||
opts.tapDistance = opts.tapDistance || 0;
|
|
||||||
opts.tapTime = opts.tapTime || 20;
|
|
||||||
|
|
||||||
opts.doubleTapTime = opts.doubleTapTime || 300;
|
|
||||||
|
|
||||||
opts.swipeDistance = opts.swipeDistance || 200;
|
|
||||||
|
|
||||||
opts.flickTime = opts.flickTime || 300;
|
|
||||||
opts.flickDistance = opts.flickDistance || 100;
|
|
||||||
|
|
||||||
opts.deadX = opts.deadX || 0;
|
|
||||||
opts.deadY = opts.deadY || 0;
|
|
||||||
|
|
||||||
if(opts.capture !== false) opts.capture = true;
|
|
||||||
if(typeof opts.preventDefault !== "undefined" && opts.preventDefault !== false) opts.preventDefault = true;
|
|
||||||
if(typeof opts.preventDefault !== "undefined" && opts.stopPropagation !== false) opts.stopPropagation = true;
|
|
||||||
|
|
||||||
var eventSet = elementCache.eventSet;
|
|
||||||
|
|
||||||
var touches;
|
|
||||||
var previousTapTime = 0;
|
|
||||||
|
|
||||||
var touchStart = function(evt) {
|
|
||||||
touches = new Jester.TouchGroup(evt);
|
|
||||||
|
|
||||||
eventSet.execute("start", touches, evt);
|
|
||||||
|
|
||||||
if(opts.preventDefault) evt.preventDefault();
|
|
||||||
if(opts.stopPropagation) evt.stopPropagation();
|
|
||||||
};
|
|
||||||
|
|
||||||
var touchMove = function(evt) {
|
|
||||||
touches.update(evt);
|
|
||||||
|
|
||||||
eventSet.execute("during", touches, evt);
|
|
||||||
|
|
||||||
if(opts.preventDefault) evt.preventDefault();
|
|
||||||
if(opts.stopPropagation) evt.stopPropagation();
|
|
||||||
|
|
||||||
if(touches.numTouches() == 2) {
|
|
||||||
// pinchnarrow
|
|
||||||
if(touches.delta.scale() < 0.0) {
|
|
||||||
eventSet.execute("pinchnarrow", touches);
|
|
||||||
}
|
|
||||||
|
|
||||||
// pinchwiden
|
|
||||||
else if(touches.delta.scale() > 0.0) {
|
|
||||||
eventSet.execute("pinchwiden", touches);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var touchEnd = function(evt) {
|
|
||||||
|
|
||||||
var swipeDirection;
|
|
||||||
|
|
||||||
eventSet.execute("end", touches, evt);
|
|
||||||
|
|
||||||
if(opts.preventDefault) evt.preventDefault();
|
|
||||||
if(opts.stopPropagation) evt.stopPropagation();
|
|
||||||
|
|
||||||
if(touches.numTouches() == 1) {
|
|
||||||
// tap
|
|
||||||
if(touches.touch(0).total.x() <= opts.tapDistance && touches.touch(0).total.y() <= opts.tapDistance && touches.touch(0).total.time() < opts.tapTime) {
|
|
||||||
eventSet.execute("tap", touches);
|
|
||||||
}
|
|
||||||
|
|
||||||
// doubletap
|
|
||||||
if(touches.touch(0).total.time() < opts.tapTime) {
|
|
||||||
var now = (new Date()).getTime();
|
|
||||||
if(now - previousTapTime <= opts.doubleTapTime) {
|
|
||||||
eventSet.execute("doubletap", touches);
|
|
||||||
}
|
|
||||||
previousTapTime = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
// swipe left/right
|
|
||||||
if(Math.abs(touches.touch(0).total.x()) >= opts.swipeDistance) {
|
|
||||||
swipeDirection = touches.touch(0).total.x() < 0 ? "left" : "right";
|
|
||||||
eventSet.execute("swipe", touches, swipeDirection);
|
|
||||||
}
|
|
||||||
|
|
||||||
// swipe up/down
|
|
||||||
if(Math.abs(touches.touch(0).total.y()) >= opts.swipeDistance) {
|
|
||||||
swipeDirection = touches.touch(0).total.y() < 0 ? "up" : "down";
|
|
||||||
eventSet.execute("swipe", touches, swipeDirection);
|
|
||||||
}
|
|
||||||
|
|
||||||
// flick
|
|
||||||
if(Math.abs(touches.touch(0).total.x()) >= opts.flickDistance && touches.touch(0).total.time() <= opts.flickTime) {
|
|
||||||
var flickDirection = touches.touch(0).total.x() < 0 ? "left" : "right";
|
|
||||||
eventSet.execute("flick", touches, flickDirection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(touches.numTouches() == 2) {
|
|
||||||
// pinchend
|
|
||||||
if(touches.current.scale() !== 1.0) {
|
|
||||||
var pinchDirection = touches.current.scale() < 1.0 ? "narrowed" : "widened";
|
|
||||||
eventSet.execute("pinchend", touches, pinchDirection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var stopListening = function() {
|
|
||||||
element.removeEventListener("touchstart", touchStart, opts.capture);
|
|
||||||
element.removeEventListener("touchmove", touchMove, opts.capture);
|
|
||||||
element.removeEventListener("touchend", touchEnd, opts.capture);
|
|
||||||
};
|
|
||||||
|
|
||||||
element.addEventListener("touchstart", touchStart, opts.capture);
|
|
||||||
element.addEventListener("touchmove", touchMove, opts.capture);
|
|
||||||
element.addEventListener("touchend", touchEnd, opts.capture);
|
|
||||||
|
|
||||||
return {
|
|
||||||
stopListening: stopListening
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
TouchGroup : function(event) {
|
|
||||||
var that = this;
|
|
||||||
|
|
||||||
var numTouches = event.touches.length;
|
|
||||||
|
|
||||||
var midpointX = 0;
|
|
||||||
var midpointY = 0;
|
|
||||||
|
|
||||||
var scale = event.scale;
|
|
||||||
var prevScale = scale;
|
|
||||||
var deltaScale = scale;
|
|
||||||
|
|
||||||
for(var i = 0; i < numTouches; i++) {
|
|
||||||
this["touch" + i] = new Jester.Touch(event.touches[i].pageX, event.touches[i].pageY);
|
|
||||||
midpointX = event.touches[i].pageX;
|
|
||||||
midpointY = event.touches[i].pageY;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNumTouches() {
|
|
||||||
return numTouches;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTouch(num) {
|
|
||||||
return that["touch" + num];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMidPointX() {
|
|
||||||
return midpointX;
|
|
||||||
}
|
|
||||||
function getMidPointY() {
|
|
||||||
return midpointY;
|
|
||||||
}
|
|
||||||
function getScale() {
|
|
||||||
return scale;
|
|
||||||
}
|
|
||||||
function getDeltaScale() {
|
|
||||||
return deltaScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateTouches(event) {
|
|
||||||
var mpX = 0;
|
|
||||||
var mpY = 0;
|
|
||||||
|
|
||||||
for(var i = 0; i < event.touches.length; i++) {
|
|
||||||
if(i < numTouches) {
|
|
||||||
that["touch" + i].update(event.touches[i].pageX, event.touches[i].pageY);
|
|
||||||
mpX += event.touches[i].pageX;
|
|
||||||
mpY += event.touches[i].pageY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
midpointX = mpX / numTouches;
|
|
||||||
midpointY = mpY / numTouches;
|
|
||||||
|
|
||||||
prevScale = scale;
|
|
||||||
scale = event.scale;
|
|
||||||
deltaScale = scale - prevScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
numTouches: getNumTouches,
|
|
||||||
touch: getTouch,
|
|
||||||
current: {
|
|
||||||
scale: getScale,
|
|
||||||
midX: getMidPointX,
|
|
||||||
midY: getMidPointY
|
|
||||||
},
|
|
||||||
delta: {
|
|
||||||
scale: getDeltaScale
|
|
||||||
},
|
|
||||||
update: updateTouches
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
Touch : function(_startX, _startY) {
|
|
||||||
var startX = _startX,
|
|
||||||
startY = _startY,
|
|
||||||
startTime = now(),
|
|
||||||
currentX = startX,
|
|
||||||
currentY = startY,
|
|
||||||
currentTime = startTime,
|
|
||||||
currentSpeedX = 0,
|
|
||||||
currentSpeedY = 0,
|
|
||||||
prevX = startX,
|
|
||||||
prevY = startX,
|
|
||||||
prevTime = startTime,
|
|
||||||
prevSpeedX = 0,
|
|
||||||
prevSpeedY = 0,
|
|
||||||
deltaX = 0,
|
|
||||||
deltaY = 0,
|
|
||||||
deltaTime = 0,
|
|
||||||
deltaSpeedX = 0,
|
|
||||||
deltaSpeedY = 0,
|
|
||||||
totalX = 0,
|
|
||||||
totalY = 0,
|
|
||||||
totalTime = 0;
|
|
||||||
|
|
||||||
// position getters
|
|
||||||
function getStartX() {
|
|
||||||
return startX;
|
|
||||||
}
|
|
||||||
function getStartY() {
|
|
||||||
return startY;
|
|
||||||
}
|
|
||||||
function getCurrentX() {
|
|
||||||
return currentX;
|
|
||||||
}
|
|
||||||
function getCurrentY() {
|
|
||||||
return currentY;
|
|
||||||
}
|
|
||||||
function getPrevX() {
|
|
||||||
return prevX;
|
|
||||||
}
|
|
||||||
function getPrevY() {
|
|
||||||
return prevY;
|
|
||||||
}
|
|
||||||
function getDeltaX() {
|
|
||||||
return deltaX;
|
|
||||||
}
|
|
||||||
function getDeltaY() {
|
|
||||||
return deltaY;
|
|
||||||
}
|
|
||||||
function getTotalX() {
|
|
||||||
return totalX;
|
|
||||||
}
|
|
||||||
function getTotalY() {
|
|
||||||
return totalY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// time getters
|
|
||||||
function now() {
|
|
||||||
return (new Date()).getTime();
|
|
||||||
}
|
|
||||||
function getStartTime() {
|
|
||||||
return startTime;
|
|
||||||
}
|
|
||||||
function getCurrentTime() {
|
|
||||||
return currentTime;
|
|
||||||
}
|
|
||||||
function getPrevTime() {
|
|
||||||
return prevTime;
|
|
||||||
}
|
|
||||||
function getDeltaTime() {
|
|
||||||
return deltaTime;
|
|
||||||
}
|
|
||||||
function getTotalTime() {
|
|
||||||
return totalTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
// speed getters
|
|
||||||
function getCurrentSpeedX() {
|
|
||||||
return currentSpeedX;
|
|
||||||
}
|
|
||||||
function getCurrentSpeedY() {
|
|
||||||
return currentSpeedY;
|
|
||||||
}
|
|
||||||
function getPrevSpeedX() {
|
|
||||||
return prevSpeedX;
|
|
||||||
}
|
|
||||||
function getPrevSpeedY() {
|
|
||||||
return prevSpeedY;
|
|
||||||
}
|
|
||||||
function getDeltaSpeedX() {
|
|
||||||
return deltaSpeedX;
|
|
||||||
}
|
|
||||||
function getDeltaSpeedY() {
|
|
||||||
return deltaSpeedY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
start: {
|
|
||||||
x: getStartX,
|
|
||||||
y: getStartY,
|
|
||||||
speedX: 0,
|
|
||||||
speedY: 0,
|
|
||||||
time: getStartTime
|
|
||||||
},
|
|
||||||
current: {
|
|
||||||
x: getCurrentX,
|
|
||||||
y: getCurrentY,
|
|
||||||
time: getCurrentTime,
|
|
||||||
speedX: getCurrentSpeedX,
|
|
||||||
speedY: getCurrentSpeedY
|
|
||||||
},
|
|
||||||
prev: {
|
|
||||||
x: getPrevX,
|
|
||||||
y: getPrevY,
|
|
||||||
time: getPrevTime,
|
|
||||||
speedX: getPrevSpeedX,
|
|
||||||
speedY: getPrevSpeedY
|
|
||||||
},
|
|
||||||
delta: {
|
|
||||||
x: getDeltaX,
|
|
||||||
y: getDeltaY,
|
|
||||||
speedX: getDeltaSpeedX,
|
|
||||||
speedY: getDeltaSpeedY,
|
|
||||||
time: getDeltaTime
|
|
||||||
},
|
|
||||||
total: {
|
|
||||||
x: getTotalX,
|
|
||||||
y: getTotalY,
|
|
||||||
time: getTotalTime
|
|
||||||
},
|
|
||||||
update: function(_x, _y) {
|
|
||||||
prevX = currentX;
|
|
||||||
prevY = currentY;
|
|
||||||
currentX = _x;
|
|
||||||
currentY = _y;
|
|
||||||
deltaX = currentX - prevX;
|
|
||||||
deltaY = currentY - prevY;
|
|
||||||
totalX = currentX - startX;
|
|
||||||
totalY = currentY - startY;
|
|
||||||
|
|
||||||
prevTime = currentTime;
|
|
||||||
currentTime = now();
|
|
||||||
deltaTime = currentTime - prevTime;
|
|
||||||
totalTime = currentTime - startTime;
|
|
||||||
|
|
||||||
prevSpeedX = currentSpeedX;
|
|
||||||
prevSpeedY = currentSpeedY;
|
|
||||||
currentSpeedX = deltaX / (deltaTime/1000);
|
|
||||||
currentSpeedY = deltaY / (deltaTime/1000);
|
|
||||||
deltaSpeedX = currentSpeedX - prevSpeedX;
|
|
||||||
deltaSpeedY = currentSpeedY - prevSpeedY;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
container.jester = function(el, opts) {
|
|
||||||
return new Jester.Watcher(el, opts);
|
|
||||||
};
|
|
||||||
|
|
||||||
}(window));
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "FeedMonkey",
|
"name": "FeedMonkey",
|
||||||
"description": "A TinyTinyRSS mobile client with which you can read your RSS feeds and mark them as read on your server. Works also offline.",
|
"description": "A feed mobile client with which you can read your RSS feeds and mark them as read on your server. Works also offline.",
|
||||||
"launch_path": "/index.html",
|
"launch_path": "/index.html",
|
||||||
"icons": {
|
"icons": {
|
||||||
"58": "/img/icon-58.png",
|
"58": "/img/icon-58.png",
|
||||||
|
@ -15,9 +15,9 @@
|
||||||
"type": "privileged",
|
"type": "privileged",
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"systemXHR": {
|
"systemXHR": {
|
||||||
"description": "Connection with your own TinyTinyRSS server."
|
"description": "Connection with your own server."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"installs_allowed_from": ["*"],
|
"installs_allowed_from": ["*"],
|
||||||
"version": "0.3.0"
|
"version": "0.4.3"
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue