Reorganized, made object-oriented, cleaned up.
Note that I have not yet gotten voting working again.
@@ -1,13 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Background Processes</title>
|
||||
<script src="fancy-settings/lib/store.js" type="text/javascript"></script>
|
||||
<script src="background.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Background Processes</h1>
|
||||
<p>Unless you are developing and inspecting the code, you probably should not be seeing this page.</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,202 +0,0 @@
|
||||
var db = openDatabase('mhdb', '1.0', 'Mostly Harmless Database', 5 * 1024 * 1024);
|
||||
var settings = new Store("settings", {
|
||||
"cacheTime": 1,
|
||||
"freshCutoff": 91
|
||||
}).toObject();
|
||||
var commentsMatchPattern = /https?:\/\/www\.reddit\.com(\/r\/(.+?))?\/comments\/(.+?)\/.*/;
|
||||
init();
|
||||
|
||||
function init() {
|
||||
setBadgeDefaults();
|
||||
chrome.tabs.onUpdated.addListener(listenToTabs);
|
||||
if(window.localStorage.getItem('installed') !== 'true') {
|
||||
installDefaults();
|
||||
}
|
||||
db.transaction(function(tx){
|
||||
// Clear cache and post data
|
||||
tx.executeSql('DELETE FROM cache');
|
||||
tx.executeSql('DELETE FROM posts');
|
||||
});
|
||||
}
|
||||
|
||||
function installDefaults(tx) {
|
||||
window.localStorage.setItem('installed','true');
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS cache (pageUrl unique, cacheDate, howManyPosts)');
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS posts (id unique, name, likes, domain, subreddit, author, score, over_18, hidden, thumbnail, downs, permalink, created_utc, url, title, num_comments, ups, modhash)')
|
||||
});
|
||||
}
|
||||
|
||||
function setBadgeDefaults(tabId) {
|
||||
chrome.browserAction.setBadgeBackgroundColor({
|
||||
color: [192,192,192,255] //r,g,b,a
|
||||
});
|
||||
chrome.browserAction.onClicked.removeListener(submitToReddit);
|
||||
if(tabId) {
|
||||
chrome.browserAction.setBadgeText({
|
||||
text: '?',
|
||||
tabId: tabId
|
||||
});
|
||||
chrome.browserAction.setTitle({
|
||||
title: 'Refresh the page to load data.',
|
||||
tabId: tabId
|
||||
});
|
||||
} else {
|
||||
chrome.browserAction.setBadgeText({
|
||||
text: '?',
|
||||
});
|
||||
chrome.browserAction.setTitle({
|
||||
title: 'Refresh the page to load data.'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function listenToTabs(tabId,changeInfo,tab){
|
||||
if(changeInfo.status === 'loading') {
|
||||
//If the user is at /comments instead of /r/subreddit/comments, redirect them.
|
||||
if(commentsMatchPattern.test(tab.url) && !(/https?:\/\/www\.reddit\.com\/r\/(.+?)\/comments\/(.+?)\/.*/.test(tab.url))) {
|
||||
chrome.tabs.update(tabId,{url:'http://redd.it/' + tab.url.match(commentsMatchPattern)[3]});
|
||||
} else {
|
||||
grabData(tab.url,tabId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function grabData(url,tabId) {
|
||||
// If the URL hasn't been cached recently, fetch it from the API.
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql('SELECT * FROM cache WHERE pageUrl=?', [url], function(tx, results) {
|
||||
var cache = results.rows;
|
||||
var isCommentsPage = commentsMatchPattern.test(url);
|
||||
if(cache.length === 0 || -(cache.item(0).cacheDate - epoch()) > 60 * settings.cacheTime ) {
|
||||
console.log('Loading from reddit api...');
|
||||
var reqUrl = new String();
|
||||
if(isCommentsPage) {
|
||||
var matches = url.match(commentsMatchPattern);
|
||||
reqUrl = 'http://www.reddit.com/by_id/t3_' + matches[3] + '.json';
|
||||
} else {
|
||||
reqUrl = 'http://www.reddit.com/api/info.json?url=' + encodeURI(url);
|
||||
}
|
||||
var api = new XMLHttpRequest();
|
||||
api.open('GET',reqUrl,false);
|
||||
api.send(null);
|
||||
if(api.status !== 200) {
|
||||
console.error('Error loading API.\nURL: ' + reqUrl + '\nStatus: ' + api.status);
|
||||
console.log(api);
|
||||
setBadgeDefaults(tabId);
|
||||
}
|
||||
api.onload = cacheData(JSON.parse(api.responseText),url,tabId,isCommentsPage);
|
||||
} else {
|
||||
console.log('Loading from cache...');
|
||||
preparePopup(url,tabId);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function epoch() {
|
||||
return Math.floor(new Date().getTime()/1000);
|
||||
}
|
||||
|
||||
function cacheData(response,pageUrl,tabId,isCommentsPage) {
|
||||
var wasCached = false;
|
||||
var freshPosts = [];
|
||||
var insertUrl = isCommentsPage ? 'http://www.reddit.com' + pageUrl.split('#')[0] : pageUrl.split('#')[0];
|
||||
for(var i = 0; i < response.data.children.length; i++) {
|
||||
var data = response.data.children[i].data;
|
||||
var isFreshEnough = settings.freshCutoff === 91 ? true : data.created_utc >= epoch() - settings.freshCutoff * 24 * 60 * 60;
|
||||
if(isFreshEnough) {
|
||||
wasCached = true;
|
||||
freshPosts.push(data);
|
||||
}
|
||||
}
|
||||
if(wasCached === true) {
|
||||
db.transaction(function(tx) {
|
||||
var insertNum = isCommentsPage ? '...' : freshPosts.length;
|
||||
tx.executeSql('INSERT OR REPLACE INTO cache (pageUrl, cacheDate, howManyPosts) VALUES (?, ?, ?)',[insertUrl, epoch(), insertNum]);
|
||||
for(var j = freshPosts.length - 1; j >= 0; j--) {
|
||||
tx.executeSql(
|
||||
'INSERT OR REPLACE INTO posts' +
|
||||
'(id, name, likes, domain, subreddit, author, score, over_18, hidden, thumbnail, downs, permalink, created_utc, url, title, num_comments, ups, modhash)' +
|
||||
'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[freshPosts[j].id, freshPosts[j].name, freshPosts[j].likes, freshPosts[j].domain, freshPosts[j].subreddit, freshPosts[j].author, freshPosts[j].score, freshPosts[j].over_18, freshPosts[j].hidden, freshPosts[j].thumbnail, freshPosts[j].downs, freshPosts[j].permalink, freshPosts[j].created_utc, insertUrl, freshPosts[j].title, freshPosts[j].num_comments, freshPosts[j].ups, response.data.modhash]
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql('INSERT OR REPLACE INTO cache (pageUrl, cacheDate, howManyPosts) VALUES (?, ?, ?)',[insertUrl, epoch(), '0',]);
|
||||
});
|
||||
}
|
||||
console.log(freshPosts);
|
||||
|
||||
preparePopup(insertUrl,tabId);
|
||||
}
|
||||
|
||||
function preparePopup(url,tabId) {
|
||||
// counts submissions and prepare the browserAction button appropriately
|
||||
var numberOfSubmissions;
|
||||
db.transaction(function(tx){
|
||||
tx.executeSql('SELECT * FROM cache WHERE pageUrl=?', [url], function(tx, results) {
|
||||
numberOfSubmissions = results.rows.item(0).howManyPosts;
|
||||
if(numberOfSubmissions > 0) {
|
||||
chrome.browserAction.setTitle({
|
||||
title: 'This page has been submitted to reddit ' + numberOfSubmissions + ' times.',
|
||||
tabId: tabId
|
||||
});
|
||||
chrome.browserAction.setBadgeText({
|
||||
text: numberOfSubmissions.toString(),
|
||||
tabId: tabId
|
||||
});
|
||||
chrome.browserAction.setPopup({
|
||||
popup: 'popup.html',
|
||||
tabId: tabId
|
||||
});
|
||||
chrome.browserAction.setBadgeBackgroundColor({
|
||||
color: [255,69,0,255], //r,g,b,a,
|
||||
tabId: tabId
|
||||
});
|
||||
chrome.browserAction.onClicked.removeListener(submitToReddit);
|
||||
} else if(numberOfSubmissions === '...') {
|
||||
chrome.browserAction.setTitle({
|
||||
title: 'You are currently viewing the comments for this page.',
|
||||
tabId: tabId
|
||||
});
|
||||
chrome.browserAction.setBadgeText({
|
||||
text: numberOfSubmissions,
|
||||
tabId: tabId
|
||||
});
|
||||
chrome.browserAction.setPopup({
|
||||
popup: 'popup.html',
|
||||
tabId: tabId
|
||||
});
|
||||
chrome.browserAction.setBadgeBackgroundColor({
|
||||
color: [95,153,207,255], //r,g,b,a,
|
||||
tabId: tabId
|
||||
});
|
||||
chrome.browserAction.onClicked.removeListener(submitToReddit);
|
||||
} else {
|
||||
chrome.browserAction.setTitle({
|
||||
title: 'Submit this page to reddit',
|
||||
tabId: tabId
|
||||
});
|
||||
chrome.browserAction.setBadgeText({
|
||||
text: '',
|
||||
tabId: tabId
|
||||
});
|
||||
chrome.browserAction.setPopup({
|
||||
popup: '',
|
||||
tabId: tabId
|
||||
});
|
||||
chrome.browserAction.onClicked.removeListener(submitToReddit);
|
||||
chrome.browserAction.onClicked.addListener(submitToReddit);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function submitToReddit(tab){
|
||||
chrome.tabs.create({
|
||||
url: 'http://www.reddit.com/submit?url=' + encodeURI(tab.url)
|
||||
});
|
||||
}
|
||||
@@ -4,11 +4,15 @@
|
||||
*/
|
||||
|
||||
body {
|
||||
min-width: 700px;
|
||||
width: 64;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
font: normal x-small verdana, arial, helvetica, sans-serif;
|
||||
}
|
||||
#loading {
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -64,7 +68,7 @@ li.hidden .votes, li.hidden .thumblink, li.hidden .link, li.hidden .domain, li.h
|
||||
width: 15px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
background-image: url('sprite.png');
|
||||
background-image: url('/pix/sprite.png');
|
||||
}
|
||||
.upmod {
|
||||
background-position: -4px -271px;
|
||||
@@ -125,7 +129,7 @@ li[data-dir='-1'] .downmod {
|
||||
-webkit-border-bottom-left-radius:6px;
|
||||
border:1px solid #c4dbf1;
|
||||
background:white none repeat-x scroll center left;
|
||||
background-image:url('gradient-button.png');
|
||||
background-image:url('/pix/gradient-button.png');
|
||||
font-size:150%;
|
||||
font-weight:bold;
|
||||
letter-spacing:-1px;
|
||||
@@ -134,7 +138,7 @@ li[data-dir='-1'] .downmod {
|
||||
}
|
||||
.morelink:hover, .mlh {
|
||||
border-color:#879eb4;
|
||||
background-image:url('gradient-button-hover.png');
|
||||
background-image:url('/pix/gradient-button-hover.png');
|
||||
}
|
||||
.morelink a {
|
||||
display:block;
|
||||
@@ -151,10 +155,10 @@ li[data-dir='-1'] .downmod {
|
||||
height:31px;
|
||||
width:24px;
|
||||
background:white none no-repeat scroll center left;
|
||||
background-image:url('sprite.png');
|
||||
background-image:url('/pix/sprite.png');
|
||||
background-position:-4px -4px;
|
||||
}
|
||||
.morelink:hover .nub, .mlhn {
|
||||
background-image:url('sprite.png');
|
||||
background-image:url('/pix/sprite.png');
|
||||
background-position:-4px -43px;
|
||||
}
|
||||
@@ -11,29 +11,29 @@
|
||||
|
||||
<!-- Stylesheets -->
|
||||
<link id="favicon" rel="icon" href="">
|
||||
<link rel="stylesheet" href="lib/default.css" media="screen">
|
||||
<link rel="stylesheet" href="css/main.css" media="screen">
|
||||
<link rel="stylesheet" href="css/setting.css" media="screen">
|
||||
<link rel="stylesheet" href="custom.css" media="screen">
|
||||
<link rel="stylesheet" type="text/css" href="lib/default.css" media="screen">
|
||||
<link rel="stylesheet" type="text/css" href="css/main.css" media="screen">
|
||||
<link rel="stylesheet" type="text/css" href="css/setting.css" media="screen">
|
||||
<link rel="stylesheet" type="text/css" href="custom.css" media="screen">
|
||||
|
||||
<!-- JavaScripts -->
|
||||
<script src="lib/mootools-core.js"></script>
|
||||
<script src="lib/store.js"></script>
|
||||
<script src="js/classes/tab.js"></script>
|
||||
<script src="js/classes/setting.js"></script>
|
||||
<script src="js/classes/search.js"></script>
|
||||
<script src="js/classes/fancy-settings.js"></script>
|
||||
<script src="i18n.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="manifest.js"></script>
|
||||
<script src="settings.js"></script>
|
||||
<script type="text/javascript" src="lib/mootools-core.js"></script>
|
||||
<script type="text/javascript" src="lib/store.js"></script>
|
||||
<script type="text/javascript" src="js/classes/tab.js"></script>
|
||||
<script type="text/javascript" src="js/classes/setting.js"></script>
|
||||
<script type="text/javascript" src="js/classes/search.js"></script>
|
||||
<script type="text/javascript" src="js/classes/fancy-settings.js"></script>
|
||||
<script type="text/javascript" src="i18n.js"></script>
|
||||
<script type="text/javascript" src="js/i18n.js"></script>
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
<script type="text/javascript" src="settings.js"></script>
|
||||
</head>
|
||||
<body class="no-select">
|
||||
<div id="sidebar" class="fancy">
|
||||
<img id="icon" src="" alt=""><h1 id="settings-label"></h1>
|
||||
<div id="tab-container">
|
||||
<div id="search-container" class="tab">
|
||||
<input id="search" type="search" placeholder="">
|
||||
<input id="search" type="search">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SAMPLE
|
||||
this.manifest = {
|
||||
"name": "Mostly Harmless",
|
||||
"icon": "../alien.png",
|
||||
"icon": "/pix/alien.png",
|
||||
"settings": [
|
||||
{
|
||||
"tab": "Performance",
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Mostly Harmless Background Processes</title>
|
||||
|
||||
<script type="text/javascript" src="/fancy-settings/lib/store.js"></script>
|
||||
<script type="text/javascript" src="/js/common.js"></script>
|
||||
<script type="text/javascript">
|
||||
var background;
|
||||
|
||||
background = new Background();
|
||||
|
||||
function initBackground() {
|
||||
cache.removeAll();
|
||||
button.setBadgeDefaults();
|
||||
chrome.tabs.onUpdated.addListener(background.prepareBrowserAction);
|
||||
}
|
||||
|
||||
window.onload = initBackground;
|
||||
</script>
|
||||
</head>
|
||||
<body id="body"></body>
|
||||
</html>
|
||||
@@ -0,0 +1,30 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Mostly Harmless Popup</title>
|
||||
<link type="text/css" rel="stylesheet" href="/css/popup.css">
|
||||
<script type="text/javascript" src="/fancy-settings/lib/store.js"></script>
|
||||
<script type="text/javascript" src="/js/common.js"></script>
|
||||
<script type="text/javascript">
|
||||
var popup;
|
||||
|
||||
popup = new Popup();
|
||||
|
||||
function initPopup() {
|
||||
chrome.tabs.getSelected(null, function(currTab) {
|
||||
if (cache.get(currTab.url) !== undefined) {
|
||||
document.getElementById('body').innerHTML = popup.createListHTML(currTab.url);
|
||||
document.getElementById('body').style.width = '700px';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Show the loader for a little while, just to make it look like the extension is working hard. ;)
|
||||
window.setTimeout('initPopup()', 500);
|
||||
</script>
|
||||
</head>
|
||||
<body id="body">
|
||||
<img id="loading" src="/pix/ajaxload.info.gif" width="32" height="32" alt="Loading..." />
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,325 @@
|
||||
var settings, cache, utils, button, reddit, background;
|
||||
|
||||
settings = new Store('settings').toObject();
|
||||
cache = new Store('cache');
|
||||
|
||||
/**
|
||||
* Create a new framework of utility functions.
|
||||
* @classDescription Creates a new framework of utility functions.
|
||||
* @type {Object}
|
||||
* @return {Boolean} Returns true.
|
||||
* @constructor
|
||||
*/
|
||||
function MHUtils() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape special RegExp characters.
|
||||
* @alias MHUtils.regexEscape(str)
|
||||
* @param {String} str The string to be escaped
|
||||
* @return {String} Returns an escaped string
|
||||
* @method
|
||||
*/
|
||||
MHUtils.prototype.regexEscape = function (str) {
|
||||
return str.replace(/([.?*+\^$\[\]\\(){}\-])/g, "\\$1");
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the UNIX epoch time.
|
||||
* @alias MHUtils.epoch()
|
||||
* @return {Number} Returns the current epoch time
|
||||
* @method
|
||||
*/
|
||||
MHUtils.prototype.epoch = function () {
|
||||
return Math.floor(new Date().getTime() / 1000);
|
||||
};
|
||||
|
||||
/*
|
||||
* JavaScript Pretty Date
|
||||
* Thanks to Dean Landolt's comment on
|
||||
* http://ejohn.org/blog/javascript-pretty-date/#postcomment
|
||||
*/
|
||||
// Takes an ISO time and returns a string representing how
|
||||
// long ago the date represents.
|
||||
MHUtils.prototype.prettyDate = function (date_str) {
|
||||
var time_formats = [
|
||||
[60, 'just now', 1], // 60
|
||||
[120, '1 minute ago', '1 minute from now'], // 60*2
|
||||
[3600, 'minutes', 60], // 60*60, 60
|
||||
[7200, '1 hour ago', '1 hour from now'], // 60*60*2
|
||||
[86400, 'hours', 3600], // 60*60*24, 60*60
|
||||
[172800, 'yesterday', 'tomorrow'], // 60*60*24*2
|
||||
[604800, 'days', 86400], // 60*60*24*7, 60*60*24
|
||||
[1209600, 'last week', 'next week'], // 60*60*24*7*4*2
|
||||
[2419200, 'weeks', 604800], // 60*60*24*7*4, 60*60*24*7
|
||||
[4838400, 'last month', 'next month'], // 60*60*24*7*4*2
|
||||
[29030400, 'months', 2419200], // 60*60*24*7*4*12, 60*60*24*7*4
|
||||
[58060800, 'last year', 'next year'], // 60*60*24*7*4*12*2
|
||||
[2903040000, 'years', 29030400], // 60*60*24*7*4*12*100, 60*60*24*7*4*12
|
||||
[5806080000, 'last century', 'next century'], // 60*60*24*7*4*12*100*2
|
||||
[58060800000, 'centuries', 2903040000] // 60*60*24*7*4*12*100*20, 60*60*24*7*4*12*100
|
||||
];
|
||||
var time = ('' + date_str).replace(/-/g,"/").replace(/[TZ]/g," ");
|
||||
then = new Date(time);
|
||||
utcTime = Date.UTC(then.getFullYear(), then.getMonth(), then.getDate(), then.getHours(), then.getMinutes(), then.getSeconds(), then.getMilliseconds())
|
||||
var seconds = (new Date - new Date(utcTime)) / 1000;
|
||||
var token = 'ago', list_choice = 1;
|
||||
if (seconds < 0) {
|
||||
seconds = Math.abs(seconds);
|
||||
token = 'from now';
|
||||
list_choice = 2;
|
||||
}
|
||||
var i = 0, format;
|
||||
while (format = time_formats[i++]) if (seconds < format[0]) {
|
||||
if (typeof format[2] == 'string')
|
||||
return format[list_choice];
|
||||
else
|
||||
return Math.floor(seconds / format[2]) + ' ' + format[1] + ' ' + token;
|
||||
}
|
||||
return time;
|
||||
};
|
||||
|
||||
/*
|
||||
* ISO 8601 Formatted Dates
|
||||
* Gotten from the Mozilla Developer Center
|
||||
*/
|
||||
MHUtils.prototype.ISODateString = function (d) {
|
||||
function pad(n){return n<10 ? '0'+n : n}
|
||||
return d.getUTCFullYear()+'-'
|
||||
+ pad(d.getUTCMonth()+1)+'-'
|
||||
+ pad(d.getUTCDate())+'T'
|
||||
+ pad(d.getUTCHours())+':'
|
||||
+ pad(d.getUTCMinutes())+':'
|
||||
+ pad(d.getUTCSeconds())+'Z'
|
||||
};
|
||||
|
||||
utils = new MHUtils();
|
||||
|
||||
/**
|
||||
* Create a new instance of the browser action button and its popup. ONLY CALL THIS ONCE!
|
||||
* @classDescription Creates a new browser icon.
|
||||
* @type {Object}
|
||||
* @return {Boolean} Returns true.
|
||||
* @constructor
|
||||
*/
|
||||
function BrowserAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the browser icon badge to its defaults.
|
||||
* @alias BrowserAction.setBadgeDefaults()
|
||||
* @param {Number} tabId If given, only sets badge defaults for this tab.
|
||||
* @return {Boolean} Returns true.
|
||||
* @method
|
||||
*/
|
||||
BrowserAction.prototype.setBadgeDefaults = function (tabId) {
|
||||
chrome.browserAction.setBadgeText({'text': '?', 'tabId': tabId});
|
||||
chrome.browserAction.setTitle({'title': 'Refresh the page to load data.', 'tabId': tabId});
|
||||
chrome.browserAction.setBadgeBackgroundColor({'color': [192, 192, 192, 255], 'tabId': tabId});
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the browser icon badge for a page.
|
||||
* @alias BrowserAction.setBadgeFor(url, tabId)
|
||||
* @param {String} url Sets the badge according to this URL.
|
||||
* @param {Number} tabId Sets the badge for this tab, if specified.
|
||||
* @return {Boolean} Returns true.
|
||||
* @method
|
||||
*/
|
||||
BrowserAction.prototype.setBadgeFor = function (url, tabId) {
|
||||
var cachedPosts;
|
||||
|
||||
cachedPosts = cache.get(url);
|
||||
|
||||
if (cachedPosts.isCommentsPage === true) {
|
||||
chrome.browserAction.setBadgeText({'text': '...', 'tabId': tabId});
|
||||
chrome.browserAction.setTitle({'title': 'You are currently viewing the comments for this page.', 'tabId': tabId});
|
||||
chrome.browserAction.setBadgeBackgroundColor({'color': [95, 153, 207, 255], 'tabId': tabId});
|
||||
chrome.browserAction.setPopup({popup: '/html/popup.html', tabId: tabId});
|
||||
} else if (cachedPosts.api.length === 0) {
|
||||
chrome.browserAction.setBadgeText({'text': '+', 'tabId': tabId});
|
||||
chrome.browserAction.setTitle({'title': 'Submit this page.', 'tabId': tabId});
|
||||
chrome.browserAction.setBadgeBackgroundColor({'color': [0, 0, 0, 255], 'tabId': tabId});
|
||||
chrome.browserAction.setPopup({popup: '/html/popup.html', tabId: tabId});
|
||||
} else {
|
||||
chrome.browserAction.setBadgeText({'text': cachedPosts.api.length.toString(), 'tabId': tabId});
|
||||
chrome.browserAction.setTitle({'title': 'This page has been submitted ' + cachedPosts.api.length.toString() + ' times.', 'tabId': tabId});
|
||||
chrome.browserAction.setBadgeBackgroundColor({'color': [255, 69, 0, 255], 'tabId': tabId});
|
||||
chrome.browserAction.setPopup({popup: '/html/popup.html', tabId: tabId});
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
button = new BrowserAction();
|
||||
|
||||
/**
|
||||
* Create a new instance of a reddit-powered website.
|
||||
* @classDescription Creates a new reddit-powered website.
|
||||
* @param {String} domain The base domain of the reddit-powered website. (e.g. 'www.reddit.com')
|
||||
* @type {Object}
|
||||
* @return {Boolean} Returns true.
|
||||
* @constructor
|
||||
*/
|
||||
function RedditAPI(domain) {
|
||||
if (domain) {
|
||||
this.domain = domain;
|
||||
} else {
|
||||
this.domain = 'www.reddit.com';
|
||||
}
|
||||
this.commentsMatchPattern = new RegExp('https?:\/\/' + utils.regexEscape(this.domain) + '(\/r\/(.+?))?\/comments\/(.+?)\/.*');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs info about a URL via the reddit API and caches it.
|
||||
* @alias RedditAPI.getInfo(url)
|
||||
* @param {String} url The URL of the page to grab info about.
|
||||
* @return {Boolean} Returns true.
|
||||
* @method
|
||||
*/
|
||||
RedditAPI.prototype.getInfo = function (url) {
|
||||
var reqUrl, isCommentsPage, req;
|
||||
|
||||
isCommentsPage = this.commentsMatchPattern.test(url);
|
||||
|
||||
if (isCommentsPage) {
|
||||
var matches;
|
||||
|
||||
matches = url.match(this.commentsMatchPattern);
|
||||
reqUrl = 'http://' + this.domain + '/by_id/t3_' + matches[3] + '.json';
|
||||
} else {
|
||||
reqUrl = 'http://' + this.domain + '/api/info.json?url=' + encodeURI(url);
|
||||
}
|
||||
|
||||
req = new XMLHttpRequest();
|
||||
req.open('GET', reqUrl, false);
|
||||
req.send(null);
|
||||
|
||||
if (req.status !== 200) {
|
||||
console.warn(req);
|
||||
throw 'Error loading API.\nURL: ' + reqUrl + '\nStatus: ' + req.status.toString();
|
||||
}
|
||||
|
||||
cache.set(url, {
|
||||
'api': JSON.parse(req.responseText).data.children,
|
||||
'cacheDate': utils.epoch(),
|
||||
'isCommentsPage': isCommentsPage
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
reddit = new RedditAPI('www.reddit.com');
|
||||
|
||||
/**
|
||||
* Creates a new framework of background processes
|
||||
* @classDescription Creates a new framework of background processes.
|
||||
* @type {Object}
|
||||
* @return {Boolean} Returns true.
|
||||
* @constructor
|
||||
*/
|
||||
function Background() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the browser action (badge, popup, etc.) for a given tab.
|
||||
* @alias Background.prepareBrowserAction(tabId, info, tab)
|
||||
* @param {Number} tabId The ID of the tab to get data for.
|
||||
* @param {Object} info The info for the change as sent by Chrome.
|
||||
* @param {Object} tab The info for the tab as sent by Chrome.
|
||||
* @return {Boolean} Returns true.
|
||||
* @method
|
||||
*/
|
||||
Background.prototype.prepareBrowserAction = function (tabId, info, tab) {
|
||||
if (info.status === 'loading') {
|
||||
button.setBadgeDefaults(tabId);
|
||||
|
||||
if (cache.get(tab.url) === undefined || cache.get(tab.url).cacheDate - utils.epoch() < -60 * settings.cacheTime) {
|
||||
console.log('Grabbing data from the API...');
|
||||
reddit.getInfo(tab.url);
|
||||
} else {
|
||||
console.log('Grabbing data from the cache...');
|
||||
}
|
||||
|
||||
button.setBadgeFor(tab.url, tabId);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new framework of popup processes
|
||||
* @classDescription Creates a new framework of background processes.
|
||||
* @type {Object}
|
||||
* @return {Boolean} Returns true.
|
||||
* @constructor
|
||||
*/
|
||||
function Popup() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and store the HTML for a list of posts.
|
||||
* @alias Popup.createListHTML(url)
|
||||
* @param {String} url The URL of the page to create the HTML for.
|
||||
* @return {String} Returns the generated HTML.
|
||||
* @method
|
||||
*/
|
||||
Popup.prototype.createListHTML = function (url) {
|
||||
var listHTML;
|
||||
|
||||
if (cache.get(url) === undefined) {
|
||||
throw 'Cannot create list HTML for a non-cached URL.';
|
||||
}
|
||||
|
||||
listHTML = '<ol id="posts" data-url="' + url + '">';
|
||||
|
||||
for(var i = 0; i < cache.get(url).api.length; i++) {
|
||||
var data, voteDir, entry, hiddenText, saveText, thumbSrc;
|
||||
|
||||
data = cache.get(url).api[i].data;
|
||||
console.log(data);
|
||||
if (data.likes === true) voteDir = 1;
|
||||
if (data.likes === null) voteDir = 0;
|
||||
if (data.likes === false) voteDir = -1;
|
||||
hiddenText = data.hidden === true ? 'hidden' : 'hide';
|
||||
saveText = data.saved === true ? 'saved' : 'save';
|
||||
thumbSrc = data.thumbnail.indexOf('/') === 0 ? 'http://www.reddit.com' + data.thumbnail : data.thumbnail;
|
||||
|
||||
listHTML += '<li id="' + data.name + '" class="' + hiddenText + ' ' + saveText + '" data-dir="' + voteDir.toString() + '">';
|
||||
listHTML += '<div class="votes">';
|
||||
listHTML += '<a class="upmod" onclick="reddit.voteUp(\'' + data.name + '\')"></a>';
|
||||
listHTML += '<span class="count" id="count_' + data.name + '" title="' + data.ups + ' up votes, ' + data.downs + ' down votes">' + data.score + '</span>';
|
||||
listHTML += '<a class="downmod" onclick="reddit.voteDown(\'' + data.name + '\')"></a>';
|
||||
listHTML += '</div>';
|
||||
listHTML += '<a class="thumblink" href="http://www.reddit.com' + data.permalink + '" target="_blank" title="View this post on reddit">';
|
||||
listHTML += '<img class="thumb" src="' + thumbSrc + '" alt="' + data.title + '" width="70"/>';
|
||||
listHTML += '</a>';
|
||||
listHTML += '<div class="post">';
|
||||
listHTML += '<a class="link" href="http://www.reddit.com' + data.permalink + '" target="_blank" title="View this post on reddit">' + data.title + '</a> ';
|
||||
listHTML += '<a class="domain" href="http://www.reddit.com/domain/' + data.domain + '" target="_blank">(' + data.domain + ')</a>';
|
||||
listHTML += '<div class="meta">';
|
||||
listHTML += '<span class="timestamp">submitted ' + utils.prettyDate(utils.ISODateString(new Date(data.created_utc * 1000))) + '</span> by ';
|
||||
listHTML += '<a class="submitter" href="http://www.reddit.com/user/' + data.author + '" target="_blank">' + data.author + '</a> to ';
|
||||
listHTML += '<a class="subreddit" href="http://www.reddit.com/r/' + data.subreddit + '/" target="_blank">' + data.subreddit + '</a>';
|
||||
listHTML += '</div>';
|
||||
listHTML += '<div class="actions">';
|
||||
listHTML += '<a class="comments" href="http://www.reddit.com' + data.permalink + '" target="_blank">' + data.num_comments + ' comments</a>';
|
||||
listHTML += '<a class="share">share</a>';
|
||||
listHTML += '<a class="save">' + saveText + '</a>';
|
||||
listHTML += '<a class="hide">' + hiddenText + '</a>';
|
||||
listHTML += '<a class="report">report</a>';
|
||||
listHTML += '</div>';
|
||||
listHTML += '</div>'
|
||||
listHTML += '</li>';
|
||||
}
|
||||
|
||||
listHTML += '</ol>'
|
||||
return listHTML;
|
||||
|
||||
};
|
||||
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"name": "Mostly Harmless",
|
||||
"version": "0.2",
|
||||
"version": "0.3",
|
||||
"description": "Easily see how many times URL has been posted to reddit, join the discussion, submit or repost it, and more!",
|
||||
"browser_action": {
|
||||
"default_icon": "alien.png"
|
||||
"default_icon": "/pix/alien.png",
|
||||
"popup": "/html/popup.html"
|
||||
},
|
||||
"permissions": [
|
||||
"http://*.reddit.com/",
|
||||
"tabs"
|
||||
],
|
||||
"background_page": "background.html",
|
||||
"options_page": "fancy-settings/index.html"
|
||||
"background_page": "/html/background.html",
|
||||
"options_page": "/fancy-settings/index.html"
|
||||
}
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 448 B After Width: | Height: | Size: 448 B |
|
Before Width: | Height: | Size: 174 B After Width: | Height: | Size: 174 B |
|
Before Width: | Height: | Size: 138 B After Width: | Height: | Size: 138 B |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -1,19 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Chrome Popup</title>
|
||||
<link href="popup.css" type="text/css" rel="stylesheet">
|
||||
<script src="fancy-settings/lib/store.js" type="text/javascript"></script>
|
||||
<script src="popup.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
<ol id="posts">
|
||||
|
||||
</ol>
|
||||
<div class="morelink">
|
||||
<a id="submit" target="_blank">Submit this link again</a>
|
||||
<div class="nub"> </div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,194 +0,0 @@
|
||||
var db = openDatabase('mhdb', '1.0', 'Mostly Harmless Database', 5 * 1024 * 1024);
|
||||
var settings = new Store("settings", {
|
||||
"cacheTime": 1
|
||||
}).toObject();
|
||||
var commentsMatchPattern = /https?:\/\/www\.reddit\.com(\/r\/(.+?))?\/comments\/(.+?)\/.*/;
|
||||
var isCommentsPage = false;
|
||||
|
||||
chrome.tabs.getSelected(undefined, function(currTab) {
|
||||
buildPage(currTab.url);
|
||||
});
|
||||
function buildPage(pageUrl) {
|
||||
isCommentsPage = commentsMatchPattern.test(pageUrl);
|
||||
var sqlQuery = isCommentsPage ? 'SELECT * FROM posts WHERE id=?' : 'SELECT * FROM posts WHERE url=?';
|
||||
var sqlSearch = isCommentsPage ? pageUrl.match(commentsMatchPattern)[3] : pageUrl.split('#')[0];
|
||||
db.transaction(function(tx){
|
||||
tx.executeSql(sqlQuery, [sqlSearch], function(tx, results) {
|
||||
console.log(results.rows);
|
||||
document.getElementById('posts').setAttribute('data-modhash',results.rows.item(0).modhash);
|
||||
document.getElementById('posts').setAttribute('data-url',pageUrl);
|
||||
var children = results.rows;
|
||||
var now = new Date();
|
||||
for(var i = 0; i < children.length; i++) {
|
||||
var data = children.item(i);
|
||||
var voteDir;
|
||||
if (data.likes === 'true') voteDir = 1;
|
||||
if (data.likes === null) voteDir = 0;
|
||||
if (data.likes === 'false') voteDir = -1;
|
||||
var entry = new String();
|
||||
var hiddenText = data.hidden === 'true' ? 'hidden' : 'hide';
|
||||
var saveText = data.saved === 'true' ? 'saved' : 'save';
|
||||
entry += '<li id="' + data.name + '" class="' + hiddenText + ' ' + saveText + '" data-dir="' + voteDir.toString() + '">';
|
||||
entry += '<div class="votes">';
|
||||
entry += '<a class="upmod" onclick="apiCall(\'upmod\', \'' + data.name + '\')"></a>';
|
||||
entry += '<span class="count" id="count_' + data.name + '" title="' + data.ups + ' up votes, ' + data.downs + ' down votes">' + data.score + '</span>';
|
||||
entry += '<a class="downmod" onclick="apiCall(\'downmod\', \'' + data.name + '\')"></a>';
|
||||
entry += '</div>';
|
||||
entry += '<a class="thumblink" href="http://www.reddit.com' + data.permalink + '" target="_blank" title="View this post on reddit">';
|
||||
var thumbSrc = data.thumbnail.indexOf('/') === 0 ? 'http://www.reddit.com' + data.thumbnail : data.thumbnail;
|
||||
entry += '<img class="thumb" src="' + thumbSrc + '" alt="' + data.title + '" width="70"/>';
|
||||
entry += '</a>';
|
||||
entry += '<div class="post">';
|
||||
entry += '<a class="link" href="http://www.reddit.com' + data.permalink + '" target="_blank" title="View this post on reddit">' + data.title + '</a> ';
|
||||
entry += '<a class="domain" href="http://www.reddit.com/domain/' + data.domain + '" target="_blank">(' + data.domain + ')</a>';
|
||||
entry += '<div class="meta">';
|
||||
entry += '<span class="timestamp">submitted ' + prettyDate(ISODateString(new Date(data.created_utc * 1000))) + '</span> by ';
|
||||
entry += '<a class="submitter" href="http://www.reddit.com/user/' + data.author + '" target="_blank">' + data.author + '</a> to ';
|
||||
entry += '<a class="subreddit" href="http://www.reddit.com/r/' + data.subreddit + '/" target="_blank">' + data.subreddit + '</a>';
|
||||
entry += '</div>';
|
||||
entry += '<div class="actions">';
|
||||
entry += '<a class="comments" href="http://www.reddit.com' + data.permalink + '" target="_blank">' + data.num_comments + ' comments</a>';
|
||||
entry += '<a class="share">share</a>';
|
||||
entry += '<a class="save">' + saveText + '</a>';
|
||||
entry += '<a class="hide">' + hiddenText + '</a>';
|
||||
entry += '<a class="report">report</a>';
|
||||
entry += '</div>';
|
||||
entry += '</div>'
|
||||
entry += '</li>';
|
||||
document.getElementById('posts').innerHTML += entry;
|
||||
}
|
||||
if(!isCommentsPage) {
|
||||
document.getElementById('submit').href = 'http://www.reddit.com/submit?resubmit=true&url=' + encodeURI(document.getElementById('posts').getAttribute('data-url'));
|
||||
} else {
|
||||
document.getElementById('submit').parentNode.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function apiCall(call, postId) {
|
||||
var formData = new FormData();
|
||||
var apiUrl = new String();
|
||||
switch(call) {
|
||||
case 'upmod':
|
||||
apiUrl = 'http://www.reddit.com/api/vote';
|
||||
formData.append('id',postId);
|
||||
formData.append('uh',document.getElementById('posts').getAttribute('data-modhash'));
|
||||
var listItem = document.getElementById(postId);
|
||||
var voteWas = listItem.getAttribute('data-dir');
|
||||
var voteCount = document.getElementById('count_' + postId)
|
||||
if(voteWas === '1') formData.append('dir','0');
|
||||
if(voteWas === '0') formData.append('dir','1');
|
||||
if(voteWas === '-1') formData.append('dir','1');
|
||||
db.transaction(function(tx) {
|
||||
if(voteWas === '1') {
|
||||
listItem.setAttribute('data-dir','0');
|
||||
tx.executeSql('UPDATE posts SET likes=? WHERE name=?', [null, postId]);
|
||||
}
|
||||
if(voteWas === '0') {
|
||||
listItem.setAttribute('data-dir','1');
|
||||
tx.executeSql('UPDATE posts SET likes=? WHERE name=?', ['true', postId]);
|
||||
}
|
||||
if(voteWas === '-1') {
|
||||
listItem.setAttribute('data-dir','1');
|
||||
tx.executeSql('UPDATE posts SET likes=? WHERE name=?', ['true', postId]);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'downmod':
|
||||
apiUrl = 'http://www.reddit.com/api/vote';
|
||||
formData.append('id',postId);
|
||||
formData.append('uh',document.getElementById('posts').getAttribute('data-modhash'));
|
||||
var listItem = document.getElementById(postId);
|
||||
var voteWas = listItem.getAttribute('data-dir');
|
||||
var voteCount = document.getElementById('count_' + postId)
|
||||
if(voteWas === '-1') formData.append('dir','0');
|
||||
if(voteWas === '0') formData.append('dir','-1');
|
||||
if(voteWas === '1') formData.append('dir','-1');
|
||||
db.transaction(function(tx) {
|
||||
if(voteWas === '-1') {
|
||||
listItem.setAttribute('data-dir','0');
|
||||
tx.executeSql('UPDATE posts SET likes=? WHERE name=?', [null, postId]);
|
||||
}
|
||||
if(voteWas === '0') {
|
||||
listItem.setAttribute('data-dir','-1');
|
||||
tx.executeSql('UPDATE posts SET likes=? WHERE name=?', ['false', postId]);
|
||||
}
|
||||
if(voteWas === '1') {
|
||||
listItem.setAttribute('data-dir','-1');
|
||||
tx.executeSql('UPDATE posts SET likes=? WHERE name=?', ['false', postId]);
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.warn('apiCall was called without a proper "call" argument.');
|
||||
break;
|
||||
}
|
||||
var api = new XMLHttpRequest();
|
||||
api.open('POST',apiUrl,false);
|
||||
api.send(formData);
|
||||
if (api.statusText !== 'OK') {
|
||||
console.error('Error voting.\n' + api.statusText);
|
||||
console.warn(api);
|
||||
// Todo: roll back changes?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* JavaScript Pretty Date
|
||||
* Thanks to Dean Landolt's comment on
|
||||
* http://ejohn.org/blog/javascript-pretty-date/#postcomment
|
||||
*/
|
||||
// Takes an ISO time and returns a string representing how
|
||||
// long ago the date represents.
|
||||
function prettyDate(date_str){
|
||||
var time = ('' + date_str).replace(/-/g,"/").replace(/[TZ]/g," ");
|
||||
then = new Date(time);
|
||||
utcTime = Date.UTC(then.getFullYear(), then.getMonth(), then.getDate(), then.getHours(), then.getMinutes(), then.getSeconds(), then.getMilliseconds())
|
||||
var seconds = (new Date - new Date(utcTime)) / 1000;
|
||||
var token = 'ago', list_choice = 1;
|
||||
if (seconds < 0) {
|
||||
seconds = Math.abs(seconds);
|
||||
token = 'from now';
|
||||
list_choice = 2;
|
||||
}
|
||||
var i = 0, format;
|
||||
while (format = time_formats[i++]) if (seconds < format[0]) {
|
||||
if (typeof format[2] == 'string')
|
||||
return format[list_choice];
|
||||
else
|
||||
return Math.floor(seconds / format[2]) + ' ' + format[1] + ' ' + token;
|
||||
}
|
||||
return time;
|
||||
};
|
||||
var time_formats = [
|
||||
[60, 'just now', 1], // 60
|
||||
[120, '1 minute ago', '1 minute from now'], // 60*2
|
||||
[3600, 'minutes', 60], // 60*60, 60
|
||||
[7200, '1 hour ago', '1 hour from now'], // 60*60*2
|
||||
[86400, 'hours', 3600], // 60*60*24, 60*60
|
||||
[172800, 'yesterday', 'tomorrow'], // 60*60*24*2
|
||||
[604800, 'days', 86400], // 60*60*24*7, 60*60*24
|
||||
[1209600, 'last week', 'next week'], // 60*60*24*7*4*2
|
||||
[2419200, 'weeks', 604800], // 60*60*24*7*4, 60*60*24*7
|
||||
[4838400, 'last month', 'next month'], // 60*60*24*7*4*2
|
||||
[29030400, 'months', 2419200], // 60*60*24*7*4*12, 60*60*24*7*4
|
||||
[58060800, 'last year', 'next year'], // 60*60*24*7*4*12*2
|
||||
[2903040000, 'years', 29030400], // 60*60*24*7*4*12*100, 60*60*24*7*4*12
|
||||
[5806080000, 'last century', 'next century'], // 60*60*24*7*4*12*100*2
|
||||
[58060800000, 'centuries', 2903040000] // 60*60*24*7*4*12*100*20, 60*60*24*7*4*12*100
|
||||
];
|
||||
|
||||
/*
|
||||
* ISO 8601 Formatted Dates
|
||||
* Gotten from the Mozilla Developer Center
|
||||
*/
|
||||
function ISODateString(d){
|
||||
function pad(n){return n<10 ? '0'+n : n}
|
||||
return d.getUTCFullYear()+'-'
|
||||
+ pad(d.getUTCMonth()+1)+'-'
|
||||
+ pad(d.getUTCDate())+'T'
|
||||
+ pad(d.getUTCHours())+':'
|
||||
+ pad(d.getUTCMinutes())+':'
|
||||
+ pad(d.getUTCSeconds())+'Z'}
|
||||