/* Digg Sidebar - Shows Digg stories in the Firefox sidebar in real time. Copyright (C) 2008 Abhinav Sarkar This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ var DiggSidebar = { prefs: Application.extensions.get("diggsidebar@abhinavsarkar.net").prefs, storyListRefreshEventTopic: "extension-diggsidebar-abhinavsarkar-net-storylist-refresh", endpoint: { container: null, topic: null, category: "all" }, updateInterval: 1000, updateIntervalDecay: 0, lastUpdateAt: new Date().getTime(), shownStoriesCount: 30, categories: ['All', 'Popular', 'Upcoming', 'Hot', 'Top'], timerId: null, storyIds: [], refreshing: false, stories: [], expandedStory: null, fetchData: function(url, handler) { DiggSidebar.indicateActivity(); var request = new XMLHttpRequest(); request.onerror = DiggSidebar.errorHandler; request.onload = handler; request.open("GET", url, true); request.send(null); }, errorHandler: function(e) { /*if (DiggSidebar.UI.notificationBox.allNotifications.length > 0) DiggSidebar.UI.notificationBox.removeAllNotifications(true);*/ DiggSidebar.UI.notificationBox.appendNotification( "Unable to access Digg", "dsAccessFailNotification", null, "PRIORITY_CRITICAL_HIGH", [{callback:DiggSidebar.getStories, label: "Retry", accessKey: "R"}] ); DiggSidebar.refreshing = false; DiggSidebar.indicateInactivity(); }, populateMenu: function(e) { var XHR = e.target; var data = DiggSidebar.Utils.decodeJson(XHR.responseText); var topics = data.topics; var containers = new Array(); DiggSidebar.Utils.removeAllChildren(DiggSidebar.UI.topicPopup); topics.forEach(function (topic) { var menu = document.createElement('menu'); menu.setAttribute('id', topic.short_name + 'Menu'); menu.setAttribute('label', topic.name); menu.setAttribute('accesskey', topic.name.charAt(0)); var menupopup = document.createElement('menupopup'); menupopup.setAttribute('id', topic.short_name + 'Popup'); DiggSidebar.categories.forEach( function (label) { var menuitem = document.createElement('menuitem'); menuitem.setAttribute('label', label); menuitem.setAttribute('value', '/topic/' + topic.short_name + '/' + label.toLowerCase()); menuitem.setAttribute('accesskey', label.charAt(0)); menuitem.setAttribute('oncommand', "DiggSidebar.setEndPoint(this.value)"); menupopup.appendChild(menuitem); } ); menu.appendChild(menupopup); DiggSidebar.UI.topicPopup.appendChild(menu); containers.push(topic.container); }); DiggSidebar.Utils.removeAllChildren(DiggSidebar.UI.containerPopup); var filteredContainers = {name: [], short_name: []}; containers.forEach(function (container) { var name = container.name; var short_name = container.short_name; if (filteredContainers.name.indexOf(name) == -1) { filteredContainers.name.push(name); filteredContainers.short_name.push(short_name); } }); for (var i=0; i 0) DiggSidebar.Utils.removeAllChildren(DiggSidebar.UI.storyListBox); filteredStories.forEach(function (story, index) { if (index < DiggSidebar.shownStoriesCount) { var now = new Date(); if (story.promote_date != null) var then = new Date(story.promote_date*1000); else var then = new Date(story.submit_date*1000); var diff = Math.max(now - then, 0) var hr = Math.floor(diff/(1000*3600)); var min = Math.floor(diff/(1000*60)) - 60*hr; var relativeTime = ((hr > 0) && (min > 0)) ? (hr + " hr " + min + " mins ago") : ((hr == 0) && (min > 0)) ? (min + " mins ago") : ((hr == 0) && (min == 0)) ? "just now" : ""; var li = DiggSidebar.UI.storyListBox.appendChild(document.createElement('richlistitem')); li.id = "story_" + story.id; var attributes = { title: story.title, date: relativeTime, status: story.status, container: story.container.name, topic: story.topic.name, username: story.user.name, userlink: "http://digg.com/users/" + story.user.name, diggs: story.diggs, comments: story.comments, desc: story.description, link: story.link, href: story.href } for (var attr in attributes) li.setAttribute(attr, attributes[attr]); li.read = story.read; if (story.id == DiggSidebar.expandedStory) li.showDescription(); } if (DiggSidebar.storyIds.indexOf(story.id) == -1) newStoryIds.push(story.id); }); DiggSidebar.storyIds = DiggSidebar.storyIds.concat(newStoryIds); }, showDescription: function(storyId) { var jp = new JPath(DiggSidebar.stories); var story = jp.query('//[id == ' + storyId + ']')[0]; story.read = true; DiggSidebar.expandedStory = parseInt(storyId); var listitems = DiggSidebar.UI.storyListBox.children; for (var i=0; i b[date]) return -1; else if (a[date] < b[date]) return 1; return 0; }); for (var i=0; i 0) { var previousUpdateInterval = DiggSidebar.updateInterval; DiggSidebar.updateInterval = newUpdateInterval; if (previousUpdateInterval == newUpdateInterval) DiggSidebar.updateIntervalDecay += 1; else DiggSidebar.updateIntervalDecay = 0; } //Adaptive update interval code END }, calculateTimeout: function() { return Math.round(DiggSidebar.updateInterval*(Math.pow(1.5, DiggSidebar.updateIntervalDecay))) }, setEndPoint: function(ep) { DiggSidebar.prefs.get("endpoint").value = ep; DiggSidebar.endpoint = DiggSidebar.getEndpointParts(ep); window.clearTimeout(DiggSidebar.timerId); DiggSidebar.getStories(); }, getEndpointParts: function(ep) { var tmp = { topic: null, container: null, category: "all" }; var parts = ep.split("/"); if (parts.length == 2) tmp.category = parts[1]; else if (parts.length == 4) { tmp[parts[1]] = parts[2]; tmp.category = parts[3]; } return tmp; }, indicateActivity: function() { DiggSidebar.UI.indicator.src = "chrome://global/skin/icons/loading_16.png"; with (DiggSidebar.UI.indicator.parentNode) { disabled = true; setAttribute('tooltiptext', "Refreshing"); onmouseover = null; } }, indicateInactivity: function() { DiggSidebar.UI.indicator.src = "chrome://global/skin/icons/notloading_16.png"; with (DiggSidebar.UI.indicator.parentNode) { disabled = false; onmouseover = function() { var autoRefreshTime = Math.round( (DiggSidebar.lastUpdateAt + DiggSidebar.calculateTimeout() - new Date().getTime())/1000 ); autoRefreshTime = (autoRefreshTime > 3600) ? Math.round(autoRefreshTime/3600) + " hours": (autoRefreshTime > 60) ? Math.round(autoRefreshTime/60) + " minutes": autoRefreshTime + " seconds"; setAttribute('tooltiptext', "Click to refresh now.\n" + "Autorefreshing in " + autoRefreshTime); } } DiggSidebar.lastUpdateAt = new Date().getTime(); }, initialize: function(){ DiggSidebar.endpoint = DiggSidebar.getEndpointParts(DiggSidebar.prefs.get("endpoint").value); var $ = function(id) {return document.getElementById(id)}; DiggSidebar.UI = {}; DiggSidebar.UI.notificationBox = $('dsNotificationBox'); DiggSidebar.UI.indicator = $('dsBusyIndicator'); DiggSidebar.UI.topicPopup = $('dsTopicPopup'); DiggSidebar.UI.containerPopup = $('dsContainerPopup'); DiggSidebar.UI.storyListBox = $('dsStoryListBox'); DiggSidebar.UI.endPointDesc = $('dsEndPointDesc'); DiggSidebar.storyListObserver = new DiggSidebar.StoryListObserver(); DiggSidebar.createMenu(); DiggSidebar.getStories(); }, destroy: function(){ DiggSidebar.storyListObserver.unregister(); } }; DiggSidebar.Utils = { url: function(spec) { var ios = Components.classes["@mozilla.org/network/io-service;1"] .getService(Components.interfaces.nsIIOService); return ios.newURI(spec, null, null); }, openUrlInTab: function(url) { Application.activeWindow.open(DiggSidebar.Utils.url(url)); }, decodeJson: function(string) { var json = Components.classes["@mozilla.org/dom/json;1"] .createInstance(Components.interfaces.nsIJSON); return json.decode(string); }, removeAllChildren: function(node) { while (node.firstChild) node.removeChild(node.firstChild); } } DiggSidebar.StoryListObserver = function() { this.registered = false; this.register(); } DiggSidebar.StoryListObserver.prototype = { observe: function(subject, topic, data) { if (topic == window.DiggSidebar.storyListRefreshEventTopic) { window.DiggSidebar.populateStoryList(); } }, register: function() { if (this.registered == false) { var observerService = Components.classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); observerService.addObserver(this, DiggSidebar.storyListRefreshEventTopic, false); this.registered = true; } }, unregister: function() { if (this.registered == true) { var observerService = Components.classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); observerService.removeObserver(this, DiggSidebar.storyListRefreshEventTopic); this.registered = false; } } } /*TODO preferences toolbar button refresh button */ /*DONE v0.2 added kdb navigation Added progressmeter added error notification added accesskeys added new story notification v0.2.1 adaptive polling interval promote_date pause/play v0.5 new ui */