Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

executable file 1051 lines (959 sloc) 31.422 kb
<html>
<head></head>
<body>
<script type="text/javascript" src="lib/3rdparty/jquery.js"></script>
<script type="text/javascript" src="lib/3rdparty/oauth.js"></script>
<script type="text/javascript" src="lib/3rdparty/sha1.js"></script>
<script type="text/javascript" src="lib/twitter_lib.js"></script>
<script type="text/javascript" src="lib/icon_creator.js"></script>
<script>
OptionsBackend = {
defaultOptions: {
home_refresh_interval: 90 * 1000,
mentions_refresh_interval: 150 * 1000,
dms_refresh_interval: 150 * 1000,
lists_refresh_interval: 150 * 1000,
favorites_refresh_interval: 600 * 1000,
tweets_per_page: 10,
max_cached_tweets: 30,
url_shortener: 'bitly',
shortener_acct: false,
shortener_login: '',
shortener_key: '',
name_attribute: 'name',
request_timeout: 6000,
compose_position: 'top',
hover_timeout: 1000,
base_url: 'http://twitter.com/',
base_signing_url: 'http://twitter.com/',
base_authorize_url: 'https://twitter.com/oauth/authorize?oauth_token=',
use_desktop_notifications: false,
home_on_page: false,
home_icon: true,
mentions_on_page: true,
mentions_icon: true,
dms_on_page: true,
dms_icon: true,
favorites_on_page: false,
favorites_icon: false,
lists_on_page: true,
lists_icon: true,
home_visible: true,
mentions_visible: true,
dms_visible: true,
favorites_visible: true,
lists_visible: true,
reply_all: false,
lists_count: 1,
idle_color: '#4880a6',
home_color: '#9c2e2e',
mentions_color: '#7f870b',
dms_color: '#7f870b',
favorites_color: '#7f870b',
lists_color: '#7f870b',
notification_fade_timeout: 6000,
theme: 'css/chromified.css,css/chromified-theme/jquery-ui-1.7.2.custom.css'
},
cachedOptions: null,
save: function(optionsMap) {
localStorage['options'] = JSON.stringify(optionsMap);
this.cachedOptions = this.load();
},
load: function(forceDefault) {
var map = $.extend(true, {}, this.defaultOptions);
if(forceDefault) {
return map;
}
try {
var parsedMap = JSON.parse(localStorage['options']);
if(parsedMap)
$.extend(true, map, parsedMap);
} catch(e) { /* ignored */ }
return map;
},
get: function(option) {
if(this.cachedOptions == null) {
this.cachedOptions = this.load();
}
return this.cachedOptions[option];
}
};
function TweetsTimeline(timelineId, manager, recheckTime) {
this.timelineId = timelineId;
this.manager = manager;
this.recheckTime = recheckTime;
this.tweetsCache = [];
this.newTweetsCache = [];
this.unreadNotified = [];
this.timerId = null;
this.currentError = null;
this.currentCallback = null;
this.currentScroll = 0;
this.firstRun = true;
this.timelinePath = timelineId;
this.usingPages = false;
this.timelineId = timelineId;
this.listId = null;
this.timelineStopped = false;
switch(timelineId) {
case 'home':
this.timelinePath = 'statuses/home_timeline';
break;
case 'mentions':
this.timelinePath = 'statuses/mentions';
break;
case 'dms':
this.timelinePath = 'direct_messages';
break;
case 'favorites':
this.timelinePath = 'favorites';
this.usingPages = true;
break;
}
}
TweetsTimeline.prototype = {
setError: function(status) {
this.currentError = status;
},
setTimelinePath: function(path) {
this.timelinePath = path;
},
killTimeline: function() {
this.timelineStopped = true;
this.stopTimer();
},
stopTimer: function() {
if(this.timerId) {
clearTimeout(this.timerId);
this.timerId = null;
}
},
onFetchNew: function(success, tweets, status, context) {
if(this.timelineStopped) {
return;
}
var _this = this;
if(!success) {
this.setError(status);
if(context.onFinish)
context.onFinish(0);
this.timerId = setTimeout(function() { _this.fetchNewTweets.call(_this); }, this.recheckTime);
return;
}
this.setError(null);
var unreadLength = 0;
if(this.usingPages) {
for(var i = 0; i < tweets.length; ++i) {
var j = 0;
for(; j < this.tweetsCache.length; ++j) {
if(tweets[i].id == this.tweetsCache[j].id) {
break;
}
}
if(j != this.tweetsCache.length) {
break;
}
this.newTweetsCache.push(tweets[i]);
}
} else {
for(var i = tweets.length - 1; i >= 0; --i) {
this.newTweetsCache.unshift(tweets[i]);
var tid = tweets[i].id;
if(context.onFinish) {
this.manager.readTweet(tid);
} else if(!this.manager.readTweets[tid]) {
++unreadLength;
this.manager.unreadTweets[tid] = true;
}
}
}
if(tweets.length > 0) {
this.manager.notifyNewTweets(this.timelineId, this.newTweetsCache.length, unreadLength);
}
if(context.onFinish)
context.onFinish(tweets.length);
this.timerId = setTimeout(function() { _this.fetchNewTweets.call(_this); }, this.recheckTime);
},
fetchNewTweets: function(onFinishCallback) {
this.stopTimer();
var lastId = null;
if(this.newTweetsCache.length > 0) {
lastId = this.newTweetsCache[0].id;
} else if(this.tweetsCache.length > 0) {
lastId = this.tweetsCache[0].id;
}
var _this = this;
var context = { onFinish: onFinishCallback };
var params = {};
if(this.usingPages) {
//No params
} else {
if(lastId) {
params.since_id = lastId;
}
}
this.manager.twitterBackend.timeline(this.timelinePath, function(success, tweets, status, context) {
_this.onFetchNew.call(_this, success, tweets, status, context);
}, context, params);
},
onFetch: function(success, tweets, status, context) {
if(this.timelineStopped) {
return;
}
if(!success) {
this.setError(status);
this.currentCallback(null);
this.setError(null);
this.currentCallback = null;
return;
}
this.setError(null);
if(this.usingPages) {
for(var i = 0; i < tweets.length; ++i) {
this.pushTweet(tweets[i]);
}
} else {
var i = 0;
if(context.usingMaxId) {
// If we're fetching using maxId, ignore the first one (we already have it)
i = 1;
}
for(; i < tweets.length; ++i) {
this.tweetsCache.push(tweets[i]);
}
}
// Let's clean this.currentCallback before calling it as
// there might be unexpected errors and this should not block
// the extension.
var callback = this.currentCallback;
this.currentCallback = null;
try {
callback(this.tweetsCache, this.timelineId);
} catch(e) {
/* ignoring exception, popup might be closed */
}
if(this.firstRun) {
this.firstRun = false;
var _this = this;
this.timerId = setTimeout(function() { _this.fetchNewTweets.call(_this); }, this.recheckTime);
}
},
giveMeTweets: function(callback, syncNew, cacheOnly) {
if(this.currentCallback) {
//Handling multiple calls to giveMeTweets, just update the registered
//callback and let the first request finish.
this.currentCallback = callback;
return;
}
if(syncNew) {
//We should fetch new tweets, update the cache and then return the
//cached results.
var oldNewTweetsCallback = this.manager.newTweetsCallback;
var _this = this;
this.currentCallback = callback;
this.manager.newTweetsCallback = null;
var onFinishCallback = function() {
var tweetsCallback = _this.currentCallback;
_this.currentCallback = null;
_this.updateNewTweets();
_this.manager.updateAlert();
_this.giveMeTweets(tweetsCallback, false, true);
_this.manager.newTweetsCallback = oldNewTweetsCallback;
}
this.fetchNewTweets(onFinishCallback);
return;
}
if(cacheOnly && !this.firstRun) {
//Only return cached results if this is not the first run.
if(this.currentScroll == 0) {
this.cleanUpCache();
}
callback(this.tweetsCache, this.timelineId);
return;
}
//If we didn't return yet it's because we want to fetch old tweets
//from twitter's API.
this.currentCallback = callback;
var _this = this;
var params, context;
if(this.usingPages) {
params = {page: (this.tweetsCache.length / 20) + 1};
} else {
var maxId = null;
if(this.tweetsCache.length > 0) {
maxId = this.tweetsCache[this.tweetsCache.length - 1].id;
}
context = {
usingMaxId: !!maxId
}
params = {count: OptionsBackend.get('tweets_per_page'), per_page: OptionsBackend.get('tweets_per_page')};
if(maxId) {
params.max_id = maxId;
}
}
this.manager.twitterBackend.timeline(this.timelinePath, function(success, tweets, status, context) {
_this.onFetch.call(_this, success, tweets, status, context);
}, context, params);
},
cleanUpCache: function() {
var len = this.tweetsCache.length;
if(len <= OptionsBackend.get('max_cached_tweets'))
return;
this.tweetsCache = this.tweetsCache.slice(0, OptionsBackend.get('max_cached_tweets') + 1);
},
updateNewTweets: function() {
this.tweetsCache = this.newTweetsCache.concat(this.tweetsCache);
this.newTweetsCache = [];
this.unreadNotified = [];
},
newTweetsCount: function() {
var unreadCount = 0;
for(var i = this.newTweetsCache.length - 1; i >= 0; --i) {
if(!this.manager.isTweetRead(this.newTweetsCache[i].id)) {
++unreadCount;
}
}
return [this.newTweetsCache.length, unreadCount];
},
getNewUnreadTweets: function() {
var unreadNewList = [];
for(var i = 0; i < this.newTweetsCache.length; ++i) {
if(!this.manager.isTweetRead(this.newTweetsCache[i].id) && !this.unreadNotified[this.newTweetsCache[i].id]) {
unreadNewList.push(this.newTweetsCache[i]);
this.unreadNotified[this.newTweetsCache[i].id] = true;
}
}
return unreadNewList;
},
getNewUnreadIds: function() {
var unreadNewIds = [];
for(var i = 0; i < this.newTweetsCache.length; ++i) {
if(!this.manager.isTweetRead(this.newTweetsCache[i].id)) {
unreadNewIds.push(this.newTweetsCache[i].id);
}
}
return unreadNewIds;
},
removeFromCache: function(id) {
var i = 0;
for(; i < this.tweetsCache.length; ++i) {
if(this.tweetsCache[i].id == id)
break;
}
if(i != this.tweetsCache.length) {
this.tweetsCache.splice(i, 1);
}
},
findTweet: function(id) {
var i = 0;
for(; i < this.tweetsCache.length; ++i) {
if(this.tweetsCache[i].id == id)
return this.tweetsCache[i];
}
return null;
},
pushTweet: function(tweet) {
var baseTime = Date.parse(tweet.created_at);
var i = 0;
for(; i < this.tweetsCache.length; ++i) {
var tweetTime = Date.parse(this.tweetsCache[i].created_at);
if(baseTime >= tweetTime) {
break;
}
}
if(i == this.tweetsCache.length || this.tweetsCache[i].id != tweet.id)
this.tweetsCache.splice(i, 0, tweet);
}
}
function ComposerData() {
this.saveMessage = '';
this.urlShortener = '';
this.isComposing = false;
this.replyId = null;
this.replyUser = null;
}
function TweetManager() {
this.unreadTweets = {};
this.readTweets = {};
this.retweets = {};
this.injectTweets = null;
this.newTweetsCallback = null;
this.composerData = new ComposerData();
this.timelines = {};
this.iconImg = null;
this.listsCache = null;
this.listsTabCount = OptionsBackend.get('lists_count');
var timelineNames = ["home", "mentions", "dms", "favorites", "lists"];
for(var i = 0; i < timelineNames.length; ++i) {
var refreshInterval = OptionsBackend.get(timelineNames[i] + '_refresh_interval');
var visible = OptionsBackend.get(timelineNames[i] + '_visible');
if(!visible) {
continue;
}
if(timelineNames[i] == "lists") {
for(var j = 0; j < this.listsTabCount; ++j) {
this.timelines["lists_" + j] = new TweetsTimeline("lists_" + j, this, refreshInterval);
}
} else {
this.timelines[timelineNames[i]] = new TweetsTimeline(timelineNames[i], this, refreshInterval);
}
if(!this.currentTimeline) {
this.currentTimeline = (timelineNames[i] == "lists") ? "lists_0" : timelineNames[i];
}
}
this.lastAvailableHits = null;
this.lastAvailableHitsTime = null;
this.warningsCallback = null;
this.warningMessage = null;
this.autoClearWarning = false;
this.tooManyHitsCount = 0;
this.initIconAndAlerts();
var _this = this;
this.twitterBackend = new TwitterLib(OptionsBackend.get('request_timeout'),
function onAuthenticated() {
for(var i = 0; i < timelineNames.length; ++i) {
var visible = OptionsBackend.get(timelineNames[i] + '_visible');
if(!visible) {
continue;
}
if(timelineNames[i] == "lists") {
_this.lists();
var selectedLists = _this.getSelectedLists();
for(var timelineId in selectedLists) {
var timeline = _this.changeList(timelineId, selectedLists[timelineId]);
if(timeline) {
timeline.giveMeTweets(function() {});
}
}
} else {
_this.giveMeTweets.call(_this, timelineNames[i], function() {});
}
}
},
function(remainingHits, nextHitsReset, hourlyLimit) {
_this.onHitsUpdated.call(_this, remainingHits, nextHitsReset, hourlyLimit);
},
OptionsBackend.get('base_url'),
OptionsBackend.get('base_authorize_url'),
OptionsBackend.get('base_signing_url')
);
}
TweetManager.prototype = {
setWarning: function(msg) {
this.warningMessage = msg;
try {
if(this.warningCallback) {
this.warningCallback(msg);
}
} catch(e) {
/* ignoring, the popup window might be closed. */
}
},
clearWarning: function() {
this.warningMessage = null;
},
registerWarningsCallback: function(callback) {
this.warningsCallback = callback;
if(this.warningMessage && this.warningsCallback) {
this.warningsCallback(this.warningMessage);
}
},
getSelectedLists: function() {
var selectedListsStr = localStorage['selected_lists']
if(selectedListsStr) {
return JSON.parse(selectedListsStr);
}
return {};
},
registerSelectedList: function(timelineId, listId) {
var selectedLists = this.getSelectedLists();
selectedLists[timelineId] = listId;
localStorage['selected_lists'] = JSON.stringify(selectedLists);
},
eachTimeline: function(callback) {
for(var tId in this.timelines) {
callback.call(tId, this.timelines[tId]);
}
},
orderedEachTimeline: function(callback) {
var retList = [];
for(var tId in this.timelines) {
var orderedPos = this.getTimelinePosition(tId);
if(orderedPos == -1) {
orderedPos = retList.length;
}
if(retList[orderedPos]) {
retList.splice(orderedPos, 0, tId);
} else {
retList[orderedPos] = tId;
}
}
for(var i = 0; i < retList.length; ++i) {
var tId = retList[i];
if(tId) {
callback.call(tId, this.timelines[tId]);
}
}
},
getTimelinePosition: function(timelineId) {
if(!this.timelineOrderCache) {
var storedOrder = localStorage['timeline_order'];
if(storedOrder) {
this.timelineOrderCache = JSON.parse(storedOrder);
} else {
this.timelineOrderCache = [];
}
}
for(var i = 0; i < this.timelineOrderCache.length; ++i) {
if(timelineId == this.timelineOrderCache[i]) {
return i;
}
}
return -1;
},
setTimelineOrder: function(sortedTimelinesArray) {
this.timelineOrderCache = sortedTimelinesArray;
localStorage['timeline_order'] = JSON.stringify(sortedTimelinesArray);
},
initIconAndAlerts: function() {
var $icon = $('<img>').attr('src', 'img/icon19.png');
this.iconImg = $icon[0];
var _this = this;
$icon.load(function() {
_this.updateAlert();
});
},
unique: function(srcList) {
var newList = [];
for(var i = 0; i < srcList.length; ++i) {
if($.inArray(srcList[i], newList) == -1) {
newList[newList.length] = srcList[i];
}
}
return newList;
},
updateAlert: function() {
var colors = [];
var unreadNewTweets = [];
var totalUnreadNewIds = [];
this.eachTimeline(function(timeline) {
var timelineId = this;
var unreadNewIds = timeline.getNewUnreadIds();
totalUnreadNewIds = totalUnreadNewIds.concat(unreadNewIds);
if(unreadNewIds.length > 0) {
if(timelineId.match(/^lists/)) {
timelineId = 'lists';
}
if(OptionsBackend.get(timelineId + '_icon')) {
colors.push(OptionsBackend.get(timelineId + '_color'));
}
if(OptionsBackend.get(timelineId + '_on_page')) {
unreadNewTweets = unreadNewTweets.concat(timeline.getNewUnreadTweets());
}
}
});
var totalUnreadNewCount = this.unique(totalUnreadNewIds).length;
if(colors.length == 0) {
chrome.browserAction.setTitle({title: "Chromed Bird"});
chrome.browserAction.setIcon({imageData: IconCreator.paintIcon(this.iconImg, OptionsBackend.get('idle_color'))});
chrome.browserAction.setBadgeText({text: ''});
} else {
var title = totalUnreadNewCount + " new tweet";
if(totalUnreadNewCount > 1)
title += "s";
chrome.browserAction.setTitle({title: title});
chrome.browserAction.setIcon({imageData: IconCreator.paintIcon(this.iconImg, colors)});
chrome.browserAction.setBadgeBackgroundColor({color: [255, 0, 0, 0]});
chrome.browserAction.setBadgeText({text: '' + totalUnreadNewCount});
}
if(unreadNewTweets.length > 0) {
this.showTweetsNotifications(unreadNewTweets);
}
},
showTweetsNotifications: function(tweetsToNotify, forceOnPage) {
this.injectTweets = tweetsToNotify;
if(!forceOnPage && OptionsBackend.get('use_desktop_notifications')) {
try {
var notificationCenter = window.notifications || window.webkitNotifications;
var notification = notificationCenter.createHTMLNotification(
chrome.extension.getURL('tweets_notifier.html')
);
notification.show();
} catch(e) {
// Fallback to 'on page' notifications
this.showTweetsNotifications(tweetsToNotify, true);
}
} else {
var injectHelper = function(action, file, allFrames, callback) {
var method;
if(action == 'script') {
method = chrome.tabs.executeScript;
} else if(action == 'css') {
method = chrome.tabs.insertCSS;
} else {
return;
}
var params = {file: file};
if(allFrames) {
params.allFrames = true;
}
try {
method.call(chrome.tabs, null, params, callback);
} catch(e) {
// Maybe this exception is due to allFrames = true, let's try without it
if(allFrames) {
try {
method.call(chrome.tabs, null, {file: file}, callback);
} catch(e) {
// This time something really bad happened, logging and ignoring
console.log(e);
}
} else {
// We don't know the motive, logging and ignoring
console.log(e);
}
}
};
injectHelper('script', 'lib/3rdparty/jquery.js', true, function() {
injectHelper('css', 'css/injectedTweets.css', true, function() {
injectHelper('script', 'lib/tweets_assembler.js', true);
});
});
}
},
registerNewTweetsCallback: function(callback) {
this.newTweetsCallback = callback;
},
readTweet: function(id) {
this.readTweets[id] = true;
delete this.unreadTweets[id];
var _this = this;
this.eachTimeline(function(timeline) {
var newTweets = timeline.newTweetsCount();
_this.notifyNewTweets(this, newTweets[0], newTweets[1]);
});
},
isTweetRead: function(id) {
return !this.unreadTweets[id];
},
isRetweet: function(id) {
return this.retweets[id];
},
notifyNewTweets: function(timelineId, size, unreadSize) {
try {
// The callback might be invalid (popup not active), so let's ignore errors for now.
if(this.newTweetsCallback) {
this.newTweetsCallback(size, unreadSize, timelineId);
}
} catch(e) { /* ignoring */ }
this.updateAlert();
},
postTweet: function(callback, msg, replyId) {
return this.twitterBackend.tweet(callback, msg, replyId);
},
postRetweet: function(callback, id) {
var _this = this;
return this.twitterBackend.retweet(function(success, data, status) {
if(success) {
_this.retweets[id] = true;
}
callback(success, data, status);
}, id);
},
destroy: function(callback, id) {
var _this = this;
var firstCallback = function(success, data, status) {
if(success) {
_this.eachTimeline(function(timeline) {
timeline.removeFromCache(id);
});
}
callback(success, data, status);
};
if(this.currentTimeline == "dms") {
return this.twitterBackend.destroyDM(firstCallback, id);
} else {
return this.twitterBackend.destroy(firstCallback, id);
}
},
favorite: function(callback, id) {
var _this = this;
var firstCallback = function(success, data, status) {
if(success) {
var favTimeline = _this.timelines["favorites"];
favTimeline.pushTweet(data);
_this.eachTimeline(function(timeline) {
var tweet = timeline.findTweet(id);
if(tweet)
tweet.favorited = true;
});
}
callback(success, data, status);
};
return this.twitterBackend.favorite(firstCallback, id);
},
unFavorite: function(callback, id) {
var _this = this;
var firstCallback = function(success, data, status) {
if(success) {
var favTimeline = _this.timelines["favorites"];
favTimeline.removeFromCache(id);
_this.eachTimeline(function(timeline) {
var tweet = timeline.findTweet(id);
if(tweet)
tweet.favorited = false;
});
}
callback(success, data, status);
};
return this.twitterBackend.unFavorite(firstCallback, id);
},
lists: function(force, callback) {
if(force) {
this.listsCache = null;
}
if(!this.listsCache) {
var _this = this;
this.twitterBackend.lists(function(success, data, status) {
if(success) {
_this.listsCache = data.lists;
}
if(callback) {
callback(success, _this.listsCache, status);
}
});
return;
}
if(callback) {
callback(true, this.listsCache, null);
}
},
changeList: function(timelineName, listId) {
var timeline = this.timelines[timelineName];
if(!timeline) {
return null;
}
if(timeline.listId != listId) {
this.registerSelectedList(timelineName, listId);
timeline.stopTimer();
delete this.timelines[timelineName];
timeline = this.timelines[timelineName] = new TweetsTimeline(timelineName, this, OptionsBackend.get('lists_refresh_interval'));
}
timeline.listId = listId;
timeline.setTimelinePath(this.twitterBackend.username() + '/lists/' + listId + '/statuses');
return timeline;
},
getListId: function(timelineName) {
if(!timelineName) {
timelineName = this.currentTimeline;
}
var timeline = this.timelines[timelineName];
if(!timeline) {
return null;
}
var listId = timeline.listId;
if(listId && this.listsCache) {
for(var i = 0; i < this.listsCache.length; ++i) {
if(this.listsCache[i].slug == listId) {
return listId;
}
}
}
return null;
},
/* Delegates */
giveMeTweets: function(timelineId, callback, syncNew, cacheOnly) {
var timeline = this.timelines[timelineId];
if(!timeline) {
callback([], timelineId);
return;
}
return timeline.giveMeTweets(callback, syncNew, cacheOnly);
},
newTweetsCount: function(timelineId) {
return this.timelines[timelineId].newTweetsCount();
},
updateNewTweets: function() {
if(this.currentTimeline == 'favorites') {
for(var i = 0, len = this.timelines[this.currentTimeline].newTweetsCache.length; i < len; ++i) {
var id = this.timelines[this.currentTimeline].newTweetsCache[i].id;
this.eachTimeline(function(timeline) {
var tweet = timeline.findTweet(id);
if(tweet)
tweet.favorited = true;
});
}
}
this.timelines[this.currentTimeline].updateNewTweets();
this.updateAlert();
},
getCurrentTimeline: function() {
return this.timelines[this.currentTimeline];
},
currentError: function() {
return this.timelines[this.currentTimeline].currentError;
},
stopAll: function() {
this.eachTimeline(function(timeline) {
timeline.killTimeline();
delete timeline;
});
},
signout: function() {
localStorage.removeItem('oauth_token_data');
localStorage.removeItem('selected_lists');
this.stopAll();
TweetManager.instance = new TweetManager();
},
markTimelineAsRead: function() {
var tweetsToRead = this.getCurrentTimeline().tweetsCache;
for(var i = 0; i < tweetsToRead.length; ++i) {
this.readTweet(tweetsToRead[i].id);
}
},
onHitsUpdated: function(remainingHits, nextHitsReset, hourlyLimit) {
var nextResetDate = new Date();
nextResetDate.setTime(parseInt(nextHitsReset) * 1000);
if(remainingHits == 0) {
this.autoClearWarning = true;
this.setWarning("You've exceeded API hits limit. " +
"Please review your refresh interval settings in the <a href='javascript:chrome.tabs.create({url:\"" + chrome.extension.getURL('options.html') + "\"})'>options page</a>. " +
"Chromed Bird won't update until " + nextResetDate.toLocaleDateString() + " " + nextResetDate.toLocaleTimeString() + ".");
return;
}
if(this.autoClearWarning) {
this.autoClearWarning = false;
this.clearWarning();
}
if(this.lastAvailableHitsTime == null) {
this.lastAvailableHitsTime = new Date();
this.lastAvailableHits = remainingHits;
}
var timeDiff = new Date() - this.lastAvailableHitsTime;
if(timeDiff >= 5 * 60 * 1000) {
var realHitsPerHour = ((this.lastAvailableHits - remainingHits) / timeDiff) * 60 * 60 * 1000;
if(realHitsPerHour > hourlyLimit) {
// If we keep this same speed we're gonna exhaust our hits limit.
this.tooManyHitsCount += 1;
if(this.tooManyHitsCount >= 2) {
// We've exceeded the limit twice in a row, let's warn users.
this.setWarning("Hey! Your remaining API hits are ending too fast.<br>" +
"Please review your refresh interval settings in the <a href='javascript:chrome.tabs.create({url:\"" + chrome.extension.getURL('options.html') + "\"})'>options page</a>. " +
"Take special care if you're running multiple Twitter clients, as they all share the same limit.<br>" +
"Available hits left: " + remainingHits + " - Next hits reset: " + nextResetDate.toLocaleDateString() + " " + nextResetDate.toLocaleTimeString());
this.tooManyHitsCount = 0;
}
} else {
// We only wanna warn users if we exceed hits limit more than once
// in a row, so let's just reset the counter.
this.tooManyHitsCount = 0;
}
this.lastAvailableHitsTime = new Date();
this.lastAvailableHits = remainingHits;
}
}
}
/* Clean up old versions mess */
localStorage.removeItem('password');
localStorage.removeItem('logged');
localStorage.removeItem('username');
localStorage.removeItem('remember');
localStorage.removeItem('currentTheme');
function compareVersions(v1, v2) {
if(!v1) return -1;
if(!v2) return 1;
var maxLen = Math.max(v1.length, v2.length);
for(var i = 0; i < maxLen; ++i) {
if(!v1[i]) return -1;
if(!v2[i]) return 1;
if(v1[i] > v2[i]) return 1;
if(v1[i] < v2[i]) return -1;
}
return 0;
}
function checkVersionChanges(manifest) {
try {
var currentVersion = manifest.version.split('.');
var storageVersion = localStorage['version'];
if(storageVersion) {
storageVersion = JSON.parse(storageVersion);
}
if(compareVersions(currentVersion, storageVersion) != 0) {
// if last version is < 1.3.0
if(compareVersions(storageVersion, [1, 3, 0]) < 0) {
//Old default refresh interval, let's update it for the new version
if(OptionsBackend.get('home_refresh_interval') == 60000) {
var options = OptionsBackend.load();
options['home_refresh_interval'] = OptionsBackend.defaultOptions['home_refresh_interval'];
OptionsBackend.save(options);
}
}
//TODO: Maybe restart tweetManager
localStorage['version'] = JSON.stringify(currentVersion);
}
} catch(e) {
/* experimental code, something can go wrong */
console.log(e);
}
}
initializeExtension();
$.ajax({
type: 'GET',
url: 'manifest.json',
data: {},
dataType: "json",
timeout: 5000,
success: function(data, status) {
checkVersionChanges(data);
},
error: function (request, status, error) {
try {
// Ugly hack, but currently Chrome is returning an error
checkVersionChanges(JSON.parse(request.responseText));
} catch(e) {
/* Something can go wrong, ignoring... */
}
}
});
function initializeExtension() {
TweetManager.instance = new TweetManager();
var waitingFirstRequest = true;
var selectedResponse = null;
var biggestArea = -1;
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if(request.cb_requesting_tweets_immediate) {
sendResponse({
tweets: TweetManager.instance.injectTweets,
nameAttribute: OptionsBackend.get('name_attribute'),
extensionId: location.host,
fadeTimeout: OptionsBackend.get('notification_fade_timeout')
});
TweetManager.instance.injectTweets = null;
} else if(request.cb_requesting_tweets) {
if(waitingFirstRequest) {
waitingFirstRequest = false;
setTimeout(function() {
if(selectedResponse) {
selectedResponse({
tweets: TweetManager.instance.injectTweets,
nameAttribute: OptionsBackend.get('name_attribute'),
extensionId: location.host,
fadeTimeout: OptionsBackend.get('notification_fade_timeout')
});
}
waitingFirstRequest = true;
TweetManager.instance.injectTweets = null;
biggestArea = -1;
}, 200);
}
var area = request.frame_area;
if(area >= biggestArea) {
biggestArea = area;
selectedResponse = sendResponse;
}
}
});
}
</script>
</body>
</html>
Jump to Line
Something went wrong with that request. Please try again.