$('.dropdown-toggle').dropdown();
if ($('#messages').attr('data-show')) {
$('#messages').modal();
}
var goReadAppModule = angular.module('goReadApp', ['ui.sortable'])
.config(function($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhitelist(['.*']);
})
.filter('encodeURI', function() {
return encodeURIComponent;
});
goReadAppModule.controller('GoreadCtrl', function($scope, $http, $timeout, $window, $sce) {
$scope.loading = 0;
$scope.feeds = {};
$scope.stories = {};
$scope.opts = {
folderClose: {},
nav: true,
expanded: false,
mode: 'unread',
sort: 'newest',
hideEmpty: false,
scrollRead: false
};
$scope.sortableOptions = {
stop: function() {
$scope.uploadOpml();
}
};
$scope.importOpml = function() {
$scope.shown = 'feeds';
$scope.loading++;
$scope.http('GET', $('#import-opml-form').attr('data-upload-url'))
.then(processImport);
};
function processImport(data) {
var f = $('#import-opml-form');
f.prop('action', data.data);
f.ajaxSubmit({
clearForm: true,
error: function(jqXHR, textStatus, errorThrown) {
$scope.showMessage(jqXHR.responseText);
$scope.$apply();
},
success: function() {
$scope.loaded();
$scope.showMessage("OPML import is happening." +
" It can take a minute." +
" Don't reorganize your feeds" +
" until it's completed importing." +
" Refresh to see its progress.");
$scope.$apply();
}
});
}
$scope.loaded = function() {
$scope.loading--;
};
$scope.http = function(method, url, data) {
return $http({
method: method,
url: url,
data: $.param(data || ''),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
});
};
$scope.addSubscription = function(e) {
if (!$scope.addFeedUrl) {
return false;
}
var btn = $(e.target);
btn.button('loading');
$scope.loading++;
var f = $('#add-subscription-form');
$scope.http('POST', f.attr('data-url'), {
url: $scope.addFeedUrl
}).then(function() {
$scope.addFeedUrl = '';
btn.button('reset');
// I think this is needed due to the datastore's eventual consistency.
// Without the delay we only get the feed data with no story data.
$timeout(function() {
$scope.refresh()
.finally(function() {
$scope.loaded();
$scope.setActive('feed', _.last($scope.feeds).XmlUrl);
});
}, 250);
}, function(data) {
if (data.data) {
alert(data.data);
}
$scope.loaded();
btn.button('reset');
});
};
$scope.procStory = function(xmlurl, story, read) {
story.guid = xmlurl + '|' + story.Id;
if ($scope.stories[story.guid]) return;
story.read = !!read;
story.feed = $scope.feeds[xmlurl];
if (!story.Title) {
story.Title = '(title unknown)';
}
var today = new Date().toDateString();
var d = new Date(story.Date * 1000);
story.dispdate = moment(d).format(d.toDateString() == today ? "h:mm a" : "MMM D, YYYY");
story.canUnread = story.Created >= $scope.unreadDate;
$scope.stories[story.guid] = story;
};
$scope.update = function() {
$scope.updateFolders();
$scope.updateCounts();
$scope.updateStories();
};
$scope.updateCounts = function() {
$scope.updateUnread();
$scope.updateTitle();
};
$scope.updateFolders = function() {
_.each($scope.opml, function(f) {
if (f.Outline) {
_.each(f.Outline, function(s) {
var feed = $scope.feeds[s.XmlUrl];
feed.folder = f.Title;
});
} else {
var feed = $scope.feeds[f.XmlUrl];
delete feed.folder;
}
});
};
$scope.refresh = function() {
$scope.loading++;
$scope.numfeeds = 0;
$scope.shown = 'feeds';
$scope.resetScroll();
delete $scope.currentStory;
var promise = $http.post($('#refresh').attr('data-url-feeds'))
.success(function(data) {
if (data.ErrorSubscription) {
$timeout(function() {
alert('Free trial ended. Please subscribe.');
});
$scope.shown = 'account';
return;
}
$scope.unreadDate = data.UnreadDate;
$scope.untilDays = data.UntilDate ? moment.unix(data.UntilDate).diff(moment(), 'days') : 0;
$scope.opml = data.Opml || $scope.opml || [];
_.each(data.Feeds, function(e) {
e.Checked = moment(e.Checked).fromNow();
e.NextUpdate = moment(e.NextUpdate).fromNow();
$scope.feeds[e.Url] = e;
});
var mapFeed = function(o) {
var f = $scope.feeds[o.XmlUrl];
f.opml = o;
_.each(o, function(value, key) {
f[key] = value;
});
};
for (var i = 0; i < $scope.opml.length; i++) {
var o = $scope.opml[i];
if (o.Outline) {
_.each(o.Outline, mapFeed);
} else if (o.XmlUrl) {
mapFeed(o);
} else {
$scope.opml.splice(i, 1);
i--;
}
}
$scope.opts = data.Options ? JSON.parse(data.Options) : $scope.opts;
$scope.trialRemaining = data.TrialRemaining;
_.each(data.Stories, function(stories, feed) {
$scope.numfeeds = 1;
_.each(stories, function(story) {
$scope.procStory(feed, story, false);
});
});
_.each(data.Stars, function(s) {
if ($scope.stories[s])
$scope.stories[s].star = Date.now();
});
})
.error(function() {
alert('Error during refresh: try again.');
})
.finally(function() {
$scope.loaded();
$scope.update();
$scope.resetLimit();
setTimeout($scope.applyGetFeed);
});
return promise;
};
$scope.updateTitle = function() {
var ur = $scope.unread.all || 0;
document.title = 'go read' + (ur !== 0 ? ' (' + ur + ')' : '');
};
/* options dictionary:
* - collapse: can be true, false, or 'toggle'
* - noMarkRead: don't mark as read
* - noOpen: don't open or jump
* - noScroll: don't scrollIntoView
* - preventDefault: on middle/ctrl click, preventDefault
* ways to call this function:
* - j/k
* - n/p
* - expanded mode click on item
* - click on title: if open and list mode: collapse
* - middle/ctrl click title: open new tab, mark read, don't open
* - click right arrow: open new tab, mark read, don't open
* - mark read on scroll: expanded mode only, don't jump
* - NOT: o/enter: these have their own logic
*/
$scope.setCurrent = function(i, opts, $event) {
opts = opts || {};
var middleClick = $event && ($event.which == 2 || $event.ctrlKey);
if (opts.preventDefault && $event && !middleClick) {
$event.preventDefault();
}
var setCurrent = !opts.noOpen && !middleClick;
var jumpStory = setCurrent && $scope.currentStory != i && !opts.noScroll;
var collapse = opts.collapse; // undefined is falsy, so no collapse
if ($scope.opts.expanded) {
$scope.storyCollapse = false;
} else if (collapse === 'toggle') {
if ($scope.currentStory == i) {
$scope.storyCollapse = !$scope.storyCollapse;
} else {
$scope.storyCollapse = false;
}
} else {
$scope.storyCollapse = collapse ? true : false;
}
var markRead = !opts.noMarkRead && !$scope.storyCollapse;
var story = $scope.dispStories[i];
$scope.getContents(story);
if (i > 0) {
$scope.getContents($scope.dispStories[i - 1]);
}
if (i < $scope.dispStories.length - 2) {
$scope.getContents($scope.dispStories[i + 1]);
}
if (i == $scope.dispLimit - 1) {
$scope.loadNextPage();
}
if (jumpStory) {
$timeout(function() {
var se = $('#storydiv' + i);
setTimeout(function() { se[0].scrollIntoView(); });
});
}
if (setCurrent) {
$scope.currentStory = i;
}
if (markRead) {
$scope.markAllRead(story);
}
};
$scope.prev = function(opts) {
if ($scope.currentStory > 0) {
$scope.setCurrent($scope.currentStory - 1, opts);
}
};
$scope.toggleHideEmpty = function() {
$scope.opts.hideEmpty = !$scope.opts.hideEmpty;
$scope.saveOpts();
};
$scope.toggleScrollRead = function() {
$scope.opts.scrollRead = !$scope.opts.scrollRead;
$scope.saveOpts();
};
$scope.shouldHideEmpty = function(f) {
if (!$scope.opts.hideEmpty) return false;
var cnt = f.Outline ? $scope.unread.folders[f.Title] : $scope.unread.feeds[f.XmlUrl];
return cnt === 0;
};
$scope.next = function(page, opts) {
if ($scope.dispStories && typeof $scope.currentStory === 'undefined') {
$scope.setCurrent(0, opts);
return;
}
if (page) {
var sl = $('#story-list');
var sd = $('#storydiv' + $scope.currentStory);
var sdh = sd.height();
var sdt = sd.position().top;
var slh = sl.height();
if (sdt + sdh > slh) {
var slt = sl.scrollTop();
sl.scrollTop(slt + slh - 20);
return;
}
}
if ($scope.dispStories && $scope.currentStory < $scope.dispStories.length - 1) {
$scope.setCurrent($scope.currentStory + 1, opts);
}
};
$scope.updateUnread = function() {
$scope.unread = {
'all': 0,
'feeds': {},
'folders': {}
};
_.each($scope.opml, function(f) {
if (f.Outline) {
$scope.unread.folders[f.Title] = 0;
_.each(f.Outline, function(subf) {
$scope.unread.feeds[subf.XmlUrl] = 0;
});
} else {
$scope.unread.feeds[f.XmlUrl] = 0;
}
});
_.each($scope.stories, function(s) {
if (!s.read) {
$scope.unread.all++;
$scope.unread.feeds[s.feed.XmlUrl]++;
var folder = s.feed.folder;
if (folder) {
$scope.unread.folders[folder]++;
}
}
});
$scope.updateUnreadCurrent();
};
$scope.updateUnreadCurrent = function() {
if ($scope.activeFeed) $scope.unread.current = $scope.unread.feeds[$scope.activeFeed];
else if ($scope.activeFolder) $scope.unread.current = $scope.unread.folders[$scope.activeFolder];
else $scope.unread.current = $scope.unread.all;
};
$scope.markReadStories = [];
$scope.markAllRead = function(story) {
if (!$scope.dispStories.length) return;
var checkStories = story ? [story] : $scope.dispStories;
_.each(checkStories, function(s) {
if (!s.read) {
s.read = true;
$scope.markReadStories.push({
Feed: s.feed.XmlUrl,
Story: s.Id
});
}
});
$scope.sendReadStories();
$scope.updateCounts();
};
$scope.markUnread = function(s) {
s.read = !s.read;
var attr = s.read ? '' : 'un';
$scope.http('POST', $('#mark-all-read').attr('data-url-' + attr + 'read'), {
feed: s.feed.XmlUrl,
story: s.Id
});
$scope.updateCounts();
};
$scope.sendReadStories = _.debounce(function() {
var ss = $scope.markReadStories;
$scope.markReadStories = [];
if (ss.length > 0) {
$http.post($('#mark-all-read').attr('data-url-read'), ss);
$scope.$apply();
}
}, 500);
$scope.active = function() {
if ($scope.activeFolder) return $scope.activeFolder;
if ($scope.activeFeed) return $scope.feeds[$scope.activeFeed].Title;
if ($scope.activeStar) return 'starred items';
return 'all items';
};
$scope.nothing = function() {
return $scope.loading === 0 && $scope.stories && !$scope.numfeeds && $scope.shown != 'about' && $scope.shown != 'account' && $scope.shown != 'feed-history';
};
$scope.toggleNav = function() {
$scope.opts.nav = !$scope.opts.nav;
$scope.saveOpts();
};
$scope.toggleExpanded = function() {
$scope.opts.expanded = !$scope.opts.expanded;
$scope.saveOpts();
$scope.applyGetFeed();
};
$scope.setExpanded = function(v) {
$scope.opts.expanded = v;
$scope.saveOpts();
$scope.applyGetFeed();
};
$scope.navspan = function() {
return $scope.opts.nav ? '' : 'no-nav';
};
$scope.navmargin = function() {
return $scope.opts.nav ? {} : {'margin-left': '0'};
};
var prevOpts;
$scope.saveOpts = _.debounce(function() {
var opts = JSON.stringify($scope.opts);
if (opts == prevOpts) return;
prevOpts = opts;
$scope.http('POST', $('#story-list').attr('data-url-options'), {
options: opts
});
$scope.$apply();
}, 1000);
$scope.overContents = function(s) {
if (typeof s.contents !== 'undefined') {
return;
}
s.getTimeout = $timeout(function() {
$scope.getContents(s);
}, 250);
};
$scope.leaveContents = function(s) {
if (s.getTimeout) {
$timeout.cancel(s.getTimeout);
}
};
$scope.toFetch = [];
$scope.getContents = function(s) {
if (typeof s.contents !== 'undefined') return;
$scope.toFetch.push(s);
if (!$scope.fetchPromise) {
$scope.fetchPromise = $timeout($scope.fetchContents);
}
};
$scope.fetchContents = function() {
delete $scope.fetchPromise;
if ($scope.toFetch.length === 0) {
return;
}
var tofetch = $scope.toFetch;
$scope.toFetch = [];
var data = [];
_.each(tofetch, function(s) {
s.contents = '';
data.push({
Feed: s.feed.XmlUrl,
Story: s.Id
});
});
$http.post($('#mark-all-read').attr('data-url-contents'), data)
.success(function(data) {
_.each(data, function(d, i) {
var div = $('
' + d + '
');
$('a', div).attr('target', '_blank');
$('iframe', div).each(function() {
if (this.src.indexOf("http://") !== 0) {
return;
}
this.src = this.src.substr(5);
});
tofetch[i].contents = $sce.trustAsHtml(div.html());
});
});
};
var limitInc = 20;
$scope.dispLimit = limitInc;
$scope.resetLimit = function() {
$scope.dispLimit = limitInc;
$scope.checkLoadNextPage();
};
$scope.setActive = function(type, value) {
delete $scope.activeFeed;
delete $scope.activeFolder;
delete $scope.activeStar;
delete $scope.activeAll;
if (type == 'feed') $scope.activeFeed = value;
if (type == 'folder') $scope.activeFolder = value;
if (type == 'star') $scope.activeStar = true;
if (!type) $scope.activeAll = true;
delete $scope.currentStory;
$scope.updateStories();
$scope.applyGetFeed();
$scope.updateUnreadCurrent();
$scope.resetScroll();
$scope.resetLimit();
};
$scope.resetScroll = function() {
$('#story-list').scrollTop(0);
};
$scope.setMode = function(mode) {
$scope.opts.mode = mode;
$scope.updateStories();
$scope.applyGetFeed();
$scope.saveOpts();
};
$scope.setSort = function(order) {
$scope.opts.sort = order;
$scope.updateStories();
$scope.saveOpts();
$scope.resetScroll();
$scope.resetLimit();
};
$scope.updateStories = function() {
$scope.dispStories = [];
_.each($scope.stories, function(s) {
if ($scope.opts.mode == 'unread' && s.read) {
return;
} else if ($scope.activeFolder) {
if (s.feed.folder != $scope.activeFolder) {
return;
}
} else if ($scope.activeFeed) {
if (s.feed.XmlUrl != $scope.activeFeed) {
return;
}
} else if ($scope.activeStar) {
if (!s.star) {
return;
}
}
$scope.dispStories.push(s);
});
var swap = $scope.opts.sort == 'oldest';
if (swap) {
// turn off swap for all items mode on a feed
if ($scope.activeFeed && $scope.opts.mode == 'all')
swap = false;
}
$scope.dispStories.sort(function(_a, _b) {
var a, b;
if (!swap) {
a = _a;
b = _b;
} else {
a = _b;
b = _a;
}
var d = b.Date - a.Date;
if (!d)
return a.guid.localeCompare(b.guid);
return d;
});
};
$scope.rename = function(feed) {
var name = prompt('Rename to', $scope.feeds[feed].Title);
if (!name) return;
$scope.feeds[feed].Title = name;
$scope.feeds[feed].opml.Title = name;
$scope.uploadOpml();
};
$scope.renameFolder = function(folder) {
var name = prompt('Rename to', folder);
if (!name) return;
var src, dst;
for (var i = 0; i < $scope.opml.length; i++) {
var f = $scope.opml[i];
if (f.Outline) {
if (f.Title == folder) src = f;
else if (f.Title == name) dst = f;
}
}
if (!dst) {
src.Title = name;
} else {
dst.Outline.push.apply(dst.Outline, src.Outline);
var idx = $scope.feeds.indexOf(src);
$scope.feeds.splice(idx, 1);
}
$scope.activeFolder = name;
$scope.uploadOpml();
$scope.update();
};
$scope.deleteFolder = function(folder) {
if (!confirm('Delete ' + folder + ' and unsubscribe from all feeds in it?')) return;
for (var i = 0; i < $scope.opml.length; i++) {
var f = $scope.opml[i];
if (f.Outline && f.Title == folder) {
$scope.opml.splice(i, 1);
break;
}
}
$scope.setActive();
$scope.uploadOpml();
$scope.update();
};
$scope.unsubscribe = function(feed) {
if (!confirm('Unsubscribe from ' + $scope.feeds[feed].Title + ' (' + feed + ')?')) return;
for (var i = 0; i < $scope.opml.length; i++) {
var f = $scope.opml[i];
if (f.Outline) {
for (var j = 0; j < f.Outline.length; j++) {
if (f.Outline[j].XmlUrl == feed) {
f.Outline.splice(j, 1);
break;
}
}
if (!f.Outline.length) {
$scope.opml.splice(i, 1);
break;
}
}
if (f.XmlUrl == feed) {
$scope.opml.splice(i, 1);
break;
}
}
angular.forEach($scope.stories, function(v, k) {
if (v.feed.XmlUrl == feed) {
delete $scope.stories[k];
}
});
$scope.setActive();
$scope.uploadOpml();
$scope.update();
};
$scope.moveFeed = function(url, folder) {
var feed;
var found = false;
for (var i = $scope.opml.length - 1; i >= 0; i--) {
var f = $scope.opml[i];
if (f.Outline) {
for (var j = 0; j < f.Outline.length; j++) {
var o = f.Outline[j];
if (o.XmlUrl == url) {
if (f.Title == folder)
return;
feed = f.Outline[j];
f.Outline.splice(j, 1);
if (!f.Outline.length)
$scope.opml.splice(i, 1);
break;
}
}
if (f.Title == folder)
found = true;
} else if (f.XmlUrl == url) {
if (!folder)
return;
feed = f;
$scope.opml.splice(i, 1);
}
}
if (!feed) return;
if (!folder) {
$scope.opml.push(feed);
} else {
if (!found) {
$scope.opml.push({
Outline: [],
Title: folder
});
}
for (var k = 0; k < $scope.opml.length; k++) {
var fk = $scope.opml[k];
if (fk.Outline && fk.Title == folder) {
$scope.opml[k].Outline.push(feed);
}
}
}
$scope.uploadOpml();
$scope.update();
};
$scope.moveFeedNew = function(url) {
var folder = prompt('New folder name');
if (!folder) return;
$scope.moveFeed(url, folder);
};
var prevOpml;
$scope.uploadOpml = _.debounce(function() {
var opml = JSON.stringify($scope.opml);
if (opml == prevOpml) return;
prevOpml = opml;
$scope.http('POST', $('#story-list').attr('data-url-upload'), {
opml: opml
});
$scope.$apply();
}, 1000);
var sl = $('#story-list');
$scope.cursors = {};
$scope.fetching = {};
$scope.getFeed = function() {
var success = null;
var url = null;
if ($scope.activeFeed) {
var f = $scope.activeFeed;
if ($scope.fetching[f]) return;
$scope.fetching[f] = true;
url = sl.attr('data-url-get-feed') + '?' + $.param({
f: f,
c: $scope.cursors[f] || ''
});
success = function (data) {
if (!data.Stories) return;
delete $scope.fetching[f];
$scope.cursors[f] = data.Cursor;
_.each(data.Stories, function(s) {
$scope.procStory(f, s, true);
});
_.each(data.Stars, function(s) {
$scope.stories[s].star = Date.now();
});
};
} else if ($scope.activeStar) {
if ($scope.fetching.stars) return;
$scope.fetching.stars = true;
url = sl.attr('data-url-get-stars') + '?' + $.param({
c: $scope.cursors.stars || ''
});
success = function(data) {
if (!data.Stories) return;
delete $scope.fetching.stars;
$scope.cursors.stars = data.Cursor;
_.each(data.Feeds, function(f) {
if (!$scope.feeds[f.Url]) {
$scope.feeds[f.Url] = f;
}
});
_.each(data.Stories, function(stories, f) {
_.each(stories, function(s) {
$scope.procStory(f, s, true);
});
});
_.each(data.Stars, function(v, k) {
$scope.stories[k].star = v * 1000;
});
};
} else {
return;
}
if ($scope.dispStories.length !== 0) {
var sh = sl[0].scrollHeight;
var h = sl.height();
var st = sl.scrollTop();
if (sh - (st + h) > 200) {
return;
}
}
$scope.http('GET', url).success(function(data) {
success(data);
$scope.updateStories();
$scope.checkLoadNextPage();
$scope.applyGetFeed();
});
};
$scope.applyGetFeed = function() {
if ($scope.opts.mode == 'all' || $scope.activeStar) {
$scope.getFeed();
}
if ($scope.opts.expanded) {
$scope.getVisibleContents();
}
};
$scope.onScroll = _.debounce(function() {
$scope.$apply(function() {
$scope.applyGetFeed();
$scope.collapsed = $(window).width() <= 768;
$scope.scrollRead();
$scope.checkLoadNextPage();
});
}, 100);
$scope.checkLoadNextPage = function() {
if(!sl.length) return;
var sh = sl[0].scrollHeight;
var h = Math.min(sl.height(), $(window).height());
var st = Math.max(sl.scrollTop(), $(window).scrollTop());
if (sh - (st + h) > 200) {
return;
}
$scope.loadNextPage();
};
$scope.loadNextPage = function() {
var max = $scope.dispStories.length;
var len = $scope.dispLimit + limitInc;
if (len > max)
len = max;
else if (len < max)
$timeout($scope.checkLoadNextPage);
$scope.dispLimit = len;
};
sl.on('scroll', $scope.onScroll);
$window.onscroll = $scope.onScroll;
$window.onresize = $scope.onScroll;
$scope.scrollRead = function() {
if (!$scope.opts.scrollRead || !$scope.opts.expanded || $scope.collapsed)
return;
var sl = $('#story-list');
var slh = sl.height();
var sle = sl[0];
if (sle.scrollHeight == sle.scrollTop + slh) {
$scope.markAllRead();
$scope.setCurrent($scope.dispStories.length - 1);
return;
}
// find the first visible item
for (var i = 0; i < $scope.dispStories.length; i++) {
var sd = $('#storydiv' + i + ' .story-content');
var sdt = sd.position().top;
if (sdt >= 0) {
// first item is long, second item is below window scroll
if (sdt > slh) {
i--;
}
for (var j = $scope.currentStory + 1; j < i; j++) {
$scope.markAllRead($scope.dispStories[j]);
}
$scope.setCurrent(i, {noScroll: true});
break;
}
}
};
$scope.getVisibleContents = function() {
var h = sl.height();
var st = sl.scrollTop();
var b = st + h + 200;
var fetched = 0;
for (var i = 0; i < $scope.dispStories.length && fetched < 10; i++) {
var s = $scope.dispStories[i];
if ($scope.stories[s.guid].contents) continue;
var sd = $('#storydiv' + i);
if (!sd.length) continue;
var sdt = sd.position().top;
var sdb = sdt + sd.height();
if (sdt < b && sdb > 0) {
fetched += 1;
$scope.getContents(s);
}
}
};
$scope.setAddSubscription = function() {
$scope.shown = 'add-subscription';
// need to wait for the keypress to finish before focusing
setTimeout(function() {
$('#add-subscription-form input[type="text"]').focus();
});
};
$scope.mobileSite = function() {
document.cookie = 'goread-desktop=mobile; max-age=31536000';
location.reload();
};
$scope.clearFeeds = function() {
if (!confirm('Remove all folders and subscriptions?')) return;
$scope.feeds = {};
$scope.stories = {};
$scope.opml = [];
$scope.setActive();
$scope.uploadOpml();
$scope.update();
};
$scope.deleteAccount = function() {
if (!confirm('Delete your account?')) return;
window.location.href = $('#delete-account').attr('data-url');
};
var checkoutLoaded = false;
$scope.getAccount = function() {
$scope.loadCheckout();
$scope.shown = 'account';
if ($scope.account) return;
$http.post($('#account').attr('data-url-account'))
.success(function(data) {
$scope.account = data;
});
};
$scope.loadCheckout = function(cb) {
if (!checkoutLoaded) {
$.getScript("https://checkout.stripe.com/v2/checkout.js", function() {
checkoutLoaded = true;
if (cb) cb();
});
} else {
if (cb) cb();
}
};
$scope.date = function(d) {
var m = moment(d);
if (!m.isValid()) return d;
return m.format('D MMMM YYYY');
};
$scope.checkout = function(plan, desc, amount) {
$scope.loadCheckout(function() {
var token = function(res){
var button = $('#button' + plan);
button.button('loading');
$scope.http('POST', $('#account').attr('data-url-charge'), {
token: res.id,
plan: plan
})
.success(function(data) {
button.button('reset');
$scope.accountType = 2;
$scope.account = data;
})
.error(function(data) {
button.button('reset');
alert(data);
});
$scope.$apply();
};
StripeCheckout.open({
key: $('#account').attr('data-stripe-key'),
amount: amount,
currency: 'usd',
name: 'Go Read',
description: desc,
panelLabel: 'Subscribe for',
token: token
});
});
};
$scope.unCheckout = function() {
if (!confirm('Sure you want to unsubscribe?')) return;
var button = $('#uncheckoutButton');
button.button('loading');
$http.post($('#account').attr('data-url-uncheckout'))
.success(function() {
delete $scope.account;
$scope.accountType = 0;
button.button('reset');
alert('Unsubscribed');
})
.error(function(data) {
button.button('reset');
alert('Error');
});
};
$scope.getFeedHistory = function() {
$http.post($('#feed-history').attr('data-url'))
.success(function(data) {
$scope.shown = 'feed-history';
$scope.feedHistory = [];
data.reverse();
for(var i = 0; i < data.length; i++) {
var m = data[i];
m = parseInt(m.substr(0, m.length - 6), 10);
$scope.feedHistory.push({
value: data[i],
time: moment(m).format('MMMM Do YYYY, h:mm a')
});
}
});
};
$scope.toggleStar = function(story) {
if ($scope.stories[story.guid].star) {
delete $scope.stories[story.guid].star;
} else {
$scope.stories[story.guid].star = Date.now();
}
$scope.http('POST', $('#mark-all-read').attr('data-url-star'), {
feed: story.feed.XmlUrl,
story: story.Id,
del: $scope.stories[story.guid].star ? '' : '1'
});
};
$scope.encode = encodeURIComponent;
$scope.shortcuts = $('#shortcuts');
Mousetrap.bind('?', function() {
$scope.shortcuts.modal('toggle');
return false;
});
Mousetrap.bind('esc', function() {
$scope.shortcuts.modal('hide');
$('#messages').modal('hide');
return false;
});
Mousetrap.bind('r', function() {
if ($scope.nouser) {
return;
}
$scope.refresh();
$scope.$apply();
return false;
});
Mousetrap.bind('n', function() {
$scope.$apply(function() {
$scope.next(null, {collapse: true});
});
return false;
});
Mousetrap.bind('j', function() {
$scope.$apply(function() {
$scope.next(null, {collapse: false});
});
return false;
});
Mousetrap.bind('space', function() {
$scope.$apply('next(true)');
return false;
});
Mousetrap.bind('p', function() {
$scope.$apply(function() {
$scope.prev({collapse: true});
});
return false;
});
Mousetrap.bind(['k', 'shift+space'], function() {
$scope.$apply(function() {
$scope.prev({collapse: false});
});
return false;
});
Mousetrap.bind('v', function() {
if ($scope.dispStories[$scope.currentStory]) {
window.open($scope.dispStories[$scope.currentStory].Link);
return false;
}
});
Mousetrap.bind('b', function() {
var s = $scope.dispStories[$scope.currentStory];
if (s) {
var $link = document.createElement("a");
$link.href = s.Link;
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, true, false, false, true, 0, null);
$link.dispatchEvent(evt);
return false;
}
});
Mousetrap.bind('shift+a', function() {
if ($scope.nouser) {
return;
}
$scope.$apply($scope.markAllRead());
return false;
});
Mousetrap.bind('a', function() {
if ($scope.nouser) {
return;
}
$scope.$apply("setAddSubscription()");
return false;
});
Mousetrap.bind('g a', function() {
if ($scope.nouser) {
return;
}
$scope.$apply("shown = 'feeds'; setActive();");
return false;
});
Mousetrap.bind('u', function() {
$scope.$apply("toggleNav()");
return false;
});
Mousetrap.bind('1', function() {
$scope.$apply("setExpanded(true)");
return false;
});
Mousetrap.bind('2', function() {
$scope.$apply("setExpanded(false)");
return false;
});
Mousetrap.bind('m', function() {
var s = $scope.dispStories[$scope.currentStory];
if (s && s.canUnread) {
$scope.$apply(function() {
s.Unread = !s.Unread;
$scope.markUnread(s);
});
}
return false;
});
Mousetrap.bind(['o', 'enter'], function() {
$scope.$apply(function() {
$scope.storyCollapse = !$scope.storyCollapse;
if (!$scope.storyCollapse) {
$scope.markAllRead($scope.dispStories[$scope.currentStory]);
}
});
return false;
});
$scope.registerHandler = function() {
if (navigator && navigator.registerContentHandler) {
navigator.registerContentHandler("application/vnd.mozilla.maybe.feed", "http://" + window.location.host + "/user/add-subscription?url=%s", "Go Read");
}
};
$scope.showMessage = function(m) {
$('#message-list').text(m);
$('#messages').modal('show');
};
});