var settings, cache, utils, button, reddit, background; settings = new Store('settings', { 'cacheTime': 3, 'timeoutLength': 15, 'freshCutoff': 90, 'popupWidth': 640, 'shamelessPlug': false, 'waitForClick': false, 'checkMail': true, 'mailInterval': 5, 'mailDisplayTime': 30, 'mailSound': false, 'excludedDomains': 'secure.ingdirect.com\nchaseonline.chase.com\nonline.wellsfargo.com\nmail.google.com\ndocs.google.com', 'excludedRegex': 'chrome://.*\nchrome-extension://.*\nview-source://.*\nftp://.*\nhttps?://www\\.google\\.com/search.*\nhttps?://search\\.yahoo\\.com/search.*\nhttps?://www\\.bing\\.com/search.*\nhttps?://www\\.reddit\\.com/(?:r/(?:\\w|\\+)+/?)?(?:$|\\?count)' }); 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; } /** * Convert an array to an object with properties of the array each having empty values, for use in "in" statements. * @alias MHUtils.objConvert(arr) * @param {Array} arr The array to be converted * @return {Object} Returns the object * @method */ MHUtils.prototype.objConvert = function (arr) { var obj = {}; for (var i = 0; i < arr.length; i++) { obj[arr[i]]=''; } return obj; }; /** * 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); }; /** * Iterate over an object. * @alias MHUtils.forEachIn(object, action) * @return {Boolean} Returns true. * @method */ MHUtils.prototype.forEachIn = function (object, action) { for (var property in object) { if (object.hasOwnProperty(property)) { action(property, object[property]); } } return true; }; /** * Iterates over an array. * @alias MHUtils.forEach(array, action) * @return {Boolean} Returns true. * @method */ MHUtils.prototype.forEach = function (array, action) { for (var i = 0; i < array.length; i++) { action(array[i]); } return true; }; /** * Parses a URL and returns a useful object. http://james.padolsey.com/javascript/parsing-urls-with-the-dom/ * @alias MHUtils.parseURL(url) * @param {String} url A string with a full URL to be parsed. * @return {Object} Returns a URL object. * @method */ MHUtils.prototype.parseURL = function (url) { var a = document.createElement('a'); a.href = url; return { source: url, protocol: a.protocol.replace(':',''), host: a.hostname, port: a.port, query: a.search, params: (function (){ var ret = {}, seg = a.search.replace(/^\?/,'').split('&'), len = seg.length, i = 0, s; for (;iclick to view your post.'; submitButton.innerHTML = chrome.i18n.getMessage('submit_page'); submitButton.setAttribute('disabled'); title.setAttribute('readonly'); subreddit.setAttribute('readonly'); cache.remove(url); button.setBadgeDefaults(parseInt(tabId)); } } submitButton = e.srcElement; status = submitButton.parentNode.getElementsByClassName('status')[0]; title = document.getElementById('submit_title'); subreddit = document.getElementById('submit_reddit'); url = document.getElementById('submit_url'); if (title.value === '' || subreddit.value === '') { status.innerHTML = chrome.i18n.getMessage('error_empty'); } else { formData = new FormData(); formData.append('title', title.value); formData.append('url', url.value); formData.append('sr', subreddit.value); formData.append('kind', 'link'); formData.append('uh', cache.get('modhash')); status.innerHTML = 'submitting...'; try { reddit.apiTransmit('POST', 'http://www.reddit.com/api/submit?app=mh', formData, afterSubmission); } catch (error) { status.innerHTML = error; } } }; /** * Grabs the logged in user's reddits. * @alias RedditAPI.getReddits() * @return {Boolean} Returns true. * @method */ RedditAPI.prototype.getReddits = function () { reddit.apiTransmit('GET', 'http://' + this.domain + '/reddits/mine.json?app=mh', null, function (response) { cache.set('reddits', response.data.children); 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') { try { var excludedRegex, match; excludedRegex = settings.get('excludedRegex').split('\n'); excludedDomains = settings.get('excludedDomains').split('\n'); match = false; for (var i = 0; i < excludedRegex.length; i++) { if (tab.url.match(new RegExp(excludedRegex[i], "i")) !== null) { match = true; } } if (utils.parseURL(tab.url).host in utils.objConvert(excludedDomains)) { match = true; } if (settings.get('waitForClick') === false && match === false) { if (cache.get(tab.url) === undefined || cache.get(tab.url).cacheDate - utils.epoch() < -60 * settings.get('cacheTime')) { console.log(chrome.i18n.getMessage('loading_api')); reddit.getInfo(tab.url, tabId); } else { console.log(chrome.i18n.getMessage('loading_cache')); button.setBadgeFor(tab.url, tabId); } } else { button.setBadgeIgnore(tabId); } } catch (error) { button.setBadgeError(tabId, error.message); } } return true; }; /** * Sets a timeout for the next time to run checkMail. * @alias Background.watchMail() * @return {Boolean} Returns true. * @method */ Background.prototype.watchMail = function () { var mailProcess, pop; function showNotification (hasMail) { var notification, notificationTimeout, mailInterval; mailInterval = settings.get('mailInterval') * 1000 * 60; if (hasMail === true) { if (settings.get('mailSound') === true) { pop.play(); } notification = webkitNotifications.createNotification( '/pix/mail.png', // icon url - can be relative chrome.i18n.getMessage('orangered_received'), // notification title chrome.i18n.getMessage('orangered_action') // notification body text ); notificationTimeout = window.setTimeout(function () { notification.cancel(); }, settings.get('mailDisplayTime') * 1000); notification.onclick = function () { chrome.tabs.create({'url': 'http://' + reddit.domain + '/message/unread/', 'selected': true}); notification.cancel(); window.clearTimeout(notificationTimeout); }; notification.show(); } window.setTimeout(checkPrefs, mailInterval); } function getMail () { reddit.apiTransmit('GET', 'http://' + reddit.domain + '/api/me.json?app=mh', null, function (response) { showNotification(response.data.has_mail); }); } function checkPrefs () { if (settings.get('checkMail') === true) { getMail(); } } pop = document.createElement('audio'); pop.src = '/pix/pop.ogg'; checkPrefs(); }; /** * 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, staleCounter; if (cache.get(url) === undefined) { throw chrome.i18n.getMessage('error_not_cached'); } listHTML = '
    '; staleCounter = 0; utils.forEachIn(cache.get(url).posts, function (name, value) { var data, voteDir, hiddenText, hideStatus, hideAction, saveText, saveStatus, saveAction, isFreshEnough, freshText, thumbSrc, commentText; data = value.data; if (data.likes === true) voteDir = 1; if (data.likes === null) voteDir = 0; if (data.likes === false) voteDir = -1; hideStatus = data.hidden; hiddenText = hideStatus === true ? 'unhide' : 'hide'; hideAction = hideStatus === true ? 'reddit.unhidePost(event)' : 'reddit.hidePost(event)'; saveStatus = data.saved; saveText = saveStatus === true ? 'unsave' : 'save'; saveAction = saveStatus === true ? 'reddit.unsavePost(event)' : 'reddit.savePost(event)'; isFreshEnough = settings.get('freshCutoff') === 91 ? 'true' : data.created_utc >= utils.epoch() - settings.get('freshCutoff') * 24 * 60 * 60; commentText = value.savedCommentText === undefined ? '' : value.savedCommentText; if (!isFreshEnough) staleCounter++; freshText = isFreshEnough ? 'fresh' : 'stale'; 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 += '' + chrome.i18n.getMessage('submitted_when', utils.prettyDate(utils.ISODateString(new Date(data.created_utc * 1000)))) + ' ' + chrome.i18n.getMessage('by') + ' '; listHTML += '' + data.author + ' ' + chrome.i18n.getMessage('to') + ' '; listHTML += '' + data.subreddit + ''; listHTML += '
    '; listHTML += ''; listHTML += '
    '; listHTML += '
    '; listHTML += '
    '; listHTML += '' + chrome.i18n.getMessage('leave_comment') + ''; listHTML += ''; listHTML += ''; listHTML += ''; listHTML += ''; listHTML += '
    '; listHTML += '
    '; listHTML += '
  2. '; }); listHTML += '
' if (staleCounter > 0) { listHTML += '
' + chrome.i18n.getMessage('stale_posts_hiding', staleCounter.toString()) + '
'; } return listHTML; }; /** * Create the HTML for a submission form. * @alias Popup.createSubmitForm(tab) * @param {String} tab The Google Chrome Tab object. * @return {String} Returns the generated Form. * @method */ Popup.prototype.createSubmitForm = function (tab) { var submitHTML, redditCache; redditCache = cache.get('reddits'); submitHTML = '
'; submitHTML += '

' + chrome.i18n.getMessage('submit_page') + '

'; submitHTML += '
'; submitHTML += ''; submitHTML += ''; submitHTML += ''; submitHTML += '
'; submitHTML += '
'; submitHTML += ''; submitHTML += ''; submitHTML += '
'; submitHTML += '
'; submitHTML += ''; submitHTML += ''; if (redditCache !== undefined || reddit.getReddits() === true) { submitHTML += '' + chrome.i18n.getMessage('popular_choices') + ''; submitHTML += ''; } submitHTML += '
'; submitHTML += ''; submitHTML += '' submitHTML += '
'; return submitHTML; } /** * Show stale posts. * @alias Popup.showStalePosts() * @return {Boolean} Returns true. * @method */ Popup.prototype.showStalePosts = function () { var stalePosts; stalePosts = document.getElementsByClassName('stale'); document.getElementById('information').innerHTML = chrome.i18n.getMessage('stale_posts_showing'); while (stalePosts.length > 0) { stalePosts[0].className = 'stale-shown'; } }; /** * Show the comment form for a given post. * @alias Popup.showCommentForm(postId) * @param {String} postId The ID of the thing to show the comment form for. * @return {Boolean} Returns true. * @method */ Popup.prototype.showCommentForm = function (postId) { document.getElementById(postId).getElementsByClassName('comment')[0].style.display = 'block'; return true; }; /** * Cache a comment for a given post. * @alias Popup.cacheComment(event) * @param {Object} event The event object of the keyup. * @return {Boolean} Returns true. * @method */ Popup.prototype.cacheComment = function (e) { var value, postId, url, oldCache; value = e.srcElement.value; postId = e.srcElement.parentNode.parentNode.parentNode.id url = e.srcElement.parentNode.parentNode.parentNode.parentNode.getAttribute('data-url'); oldCache = cache.get(url); oldCache.posts[postId].savedCommentText = value; cache.set(url, oldCache); return true; };