diff --git a/license.txt b/LICENSE.markdown similarity index 100% rename from license.txt rename to LICENSE.markdown diff --git a/stufftodo.txt b/TODO.markdown similarity index 100% rename from stufftodo.txt rename to TODO.markdown diff --git a/background.html b/background.html deleted file mode 100644 index 7ffa114..0000000 --- a/background.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Background Processes - - - - -

Background Processes

-

Unless you are developing and inspecting the code, you probably should not be seeing this page.

- - \ No newline at end of file diff --git a/background.js b/background.js deleted file mode 100644 index 6877426..0000000 --- a/background.js +++ /dev/null @@ -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) - }); -} \ No newline at end of file diff --git a/popup.css b/css/popup.css similarity index 90% rename from popup.css rename to css/popup.css index 301fd08..f5fc4d9 100644 --- a/popup.css +++ b/css/popup.css @@ -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; } \ No newline at end of file diff --git a/fancy-settings/index.html b/fancy-settings/index.html index ec72db6..72559be 100755 --- a/fancy-settings/index.html +++ b/fancy-settings/index.html @@ -11,29 +11,29 @@ - - - - + + + + - - - - - - - - - - + + + + + + + + + + diff --git a/fancy-settings/manifest.js b/fancy-settings/manifest.js index 0acd249..832f360 100755 --- a/fancy-settings/manifest.js +++ b/fancy-settings/manifest.js @@ -1,7 +1,7 @@ // SAMPLE this.manifest = { "name": "Mostly Harmless", - "icon": "../alien.png", + "icon": "/pix/alien.png", "settings": [ { "tab": "Performance", diff --git a/html/background.html b/html/background.html new file mode 100644 index 0000000..b95db07 --- /dev/null +++ b/html/background.html @@ -0,0 +1,24 @@ + + + + + Mostly Harmless Background Processes + + + + + + + \ No newline at end of file diff --git a/html/popup.html b/html/popup.html new file mode 100644 index 0000000..4f656a9 --- /dev/null +++ b/html/popup.html @@ -0,0 +1,30 @@ + + + + + Mostly Harmless Popup + + + + + + + Loading... + + \ No newline at end of file diff --git a/js/common.js b/js/common.js new file mode 100644 index 0000000..52a64b1 --- /dev/null +++ b/js/common.js @@ -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 = '
    '; + + 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 += '
  1. '; + listHTML += '
    '; + listHTML += ''; + listHTML += '' + data.score + ''; + listHTML += ''; + listHTML += '
    '; + listHTML += ''; + listHTML += '' + data.title + ''; + listHTML += ''; + listHTML += '
    '; + listHTML += '' + data.title + ' '; + listHTML += '(' + data.domain + ')'; + listHTML += '
    '; + listHTML += 'submitted ' + utils.prettyDate(utils.ISODateString(new Date(data.created_utc * 1000))) + ' by '; + listHTML += '' + data.author + ' to '; + listHTML += '' + data.subreddit + ''; + listHTML += '
    '; + listHTML += '
    '; + listHTML += '' + data.num_comments + ' comments'; + listHTML += ''; + listHTML += '' + saveText + ''; + listHTML += '' + hiddenText + ''; + listHTML += 'report'; + listHTML += '
    '; + listHTML += '
    ' + listHTML += '
  2. '; + } + + listHTML += '
' + return listHTML; + +}; diff --git a/manifest.json b/manifest.json index 8451348..24e64e6 100644 --- a/manifest.json +++ b/manifest.json @@ -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" } \ No newline at end of file diff --git a/pix/ajaxload.info.gif b/pix/ajaxload.info.gif new file mode 100644 index 0000000..c9f14f7 Binary files /dev/null and b/pix/ajaxload.info.gif differ diff --git a/alien.png b/pix/alien.png similarity index 100% rename from alien.png rename to pix/alien.png diff --git a/gradient-button-hover.png b/pix/gradient-button-hover.png similarity index 100% rename from gradient-button-hover.png rename to pix/gradient-button-hover.png diff --git a/gradient-button.png b/pix/gradient-button.png similarity index 100% rename from gradient-button.png rename to pix/gradient-button.png diff --git a/sprite.png b/pix/sprite.png similarity index 100% rename from sprite.png rename to pix/sprite.png diff --git a/popup.html b/popup.html deleted file mode 100644 index 27500ca..0000000 --- a/popup.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Chrome Popup - - - - - -
    - -
- - - \ No newline at end of file diff --git a/popup.js b/popup.js deleted file mode 100644 index 45ddcde..0000000 --- a/popup.js +++ /dev/null @@ -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 += '
  • '; - entry += '
    '; - entry += ''; - entry += '' + data.score + ''; - entry += ''; - entry += '
    '; - entry += ''; - var thumbSrc = data.thumbnail.indexOf('/') === 0 ? 'http://www.reddit.com' + data.thumbnail : data.thumbnail; - entry += '' + data.title + ''; - entry += ''; - entry += '
    '; - entry += '' + data.title + ' '; - entry += '(' + data.domain + ')'; - entry += '
    '; - entry += 'submitted ' + prettyDate(ISODateString(new Date(data.created_utc * 1000))) + ' by '; - entry += '' + data.author + ' to '; - entry += '' + data.subreddit + ''; - entry += '
    '; - entry += '
    '; - entry += '' + data.num_comments + ' comments'; - entry += ''; - entry += '' + saveText + ''; - entry += '' + hiddenText + ''; - entry += 'report'; - entry += '
    '; - entry += '
    ' - entry += '
  • '; - 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'} \ No newline at end of file