Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

1113 lines (1017 sloc) 36.101 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/math_utils.js"></script>
<script type="text/javascript" src="lib/persistence.js"></script>
<script type="text/javascript" src="lib/twitter_lib.js"></script>
<script type="text/javascript" src="lib/icon_creator.js"></script>
<script type="text/javascript" src="lib/expander_lib.js"></script>
<script type="text/javascript" src="lib/options_backend.js"></script>
<script type="text/javascript" src="lib/timeline_factory.js"></script>
<script type="text/javascript" src="lib/timeline_template.js"></script>
<script type="text/javascript" src="lib/timelines/timeline.js"></script>
<script type="text/javascript" src="lib/timelines/favorites_timeline.js"></script>
<script type="text/javascript" src="lib/timelines/unified_timeline.js"></script>
<script type="text/javascript" src="lib/timelines/unified_dms_timeline.js"></script>
<script type="text/javascript" src="lib/timelines/multiple_timeline.js"></script>
<script type="text/javascript" src="lib/timelines/lists_timeline.js"></script>
<script type="text/javascript" src="lib/timelines/search_timeline.js"></script>
<script type="text/javascript" src="lib/send_queue.js"></script>
<script type="text/javascript" src="lib/image_services.js"></script>
<script type="text/javascript" src="lib/locales.js"></script>
<script>
function initializeJQueryOptions() {
$.ajaxSetup({
timeout: OptionsBackend.get('request_timeout')
});
}
initializeJQueryOptions();
urlExpander = new Expander();
function ComposerData() {
this.saveMessage = '';
this.urlShortener = '';
this.isComposing = false;
this.replyId = null;
this.replyUser = null;
}
function TweetManager() {
this.unreadTweets = {};
this.readTweets = {};
this.shouldNotReadMap = {};
this.retweets = {};
this.injectTweets = null;
// Using an object instead of an array to take advantage of hash look-ups
this.blockedUserIds = {};
this.newTweetsCallback = null;
this.composerData = new ComposerData();
this.timelines = {};
this.iconImg = null;
this.listsCache = null;
this.listsTabCount = OptionsBackend.get('lists_count');
this.suspend = false;
this.timelineOrderData = Persistence.timelineOrder();
this.oauthTokenData = Persistence.oauthTokenData();
this.oauthTokenService = Persistence.oauthTokenService();
var currentService = OptionsBackend.get('microblogging_service');
if(!this.oauthTokenService.val()) {
this.oauthTokenService.save(currentService);
} else if(currentService != this.oauthTokenService.val()) {
this.oauthTokenService.save(currentService);
this.oauthTokenData.remove();
}
var _this = this;
TimelineTemplate.initTemplates(this);
TimelineTemplate.eachTimelineTemplate(function(template) {
var createdTimelines = template.createTimelines();
for(var i in createdTimelines) {
_this.timelines[createdTimelines[i].timelineId] = createdTimelines[i];
}
});
this.orderedEachTimeline(function(timeline) {
_this.currentTimelineId = timeline.timelineId;
return false;
}, true);
this.warningsCallback = null;
this.warningMessage = null;
this.autoClearWarning = false;
this.maxApiHitsStates = 30;
this.apiHitsStates = [];
this.firstPossibleWarnTime = null;
this.authenticated = false;
this.initIconAndAlerts();
this.twitterBackend = new TwitterLib(
function onAuthenticated() {
TimelineTemplate.initAfterAuthentication();
_this.authenticated = true;
_this.retrieveBlockedUserIds();
_this.lists();
_this.eachTimeline(function(timeline) {
timeline.init();
}, true);
_this.retrieveFollowingUsers();
},
function(remainingHits, nextHitsReset, hourlyLimit) {
_this.onHitsUpdated.call(_this, remainingHits, nextHitsReset, hourlyLimit);
},
OptionsBackend.get('microblogging_service'),
OptionsBackend.get('base_url'),
OptionsBackend.get('base_oauth_url'),
OptionsBackend.get('base_signing_url'),
OptionsBackend.get('base_oauth_signing_url'),
OptionsBackend.get('base_search_url'),
this.oauthTokenData);
this.sendQueue = new SendQueue(this.twitterBackend);
}
TweetManager.prototype = {
showTimelineTemplate: function(timelineTemplateId, showOnly) {
var template = TimelineTemplate.getTemplate(timelineTemplateId);
template.setVisible(true);
return this.createTimelineTemplate(template, showOnly);
},
hideTimelineTemplate: function(timelineTemplateId) {
var template = TimelineTemplate.getTemplate(timelineTemplateId);
template.setVisible(false);
var toDelete = [];
this.eachTimeline(function(timeline) {
if(timeline.template.id == timelineTemplateId) {
timeline.killTimeline();
toDelete.push(timeline);
}
}, true);
for(var i = 0, len = toDelete.length; i < len; ++i) {
var timeline = toDelete[i];
delete this.timelines[timeline.timelineId];
}
},
createTimelineTemplate: function(template, showOnly) {
var createdTimelines = [];
var shownTimelines = [];
if(template.multipleTimelines && !showOnly) {
createdTimelines = [template.addTimeline()];
} else {
var createTimelines = true;
this.eachTimeline(function(timeline) {
if(timeline.template.id == template.id) {
createTimelines = false;
shownTimelines.push(timeline);
}
});
if(createTimelines) {
createdTimelines = template.createTimelines();
}
}
for(var i = 0, len = createdTimelines.length; i < len; ++i) {
var timeline = createdTimelines[i];
this.timelines[timeline.timelineId] = timeline;
if(this.authenticated) {
timeline.init();
}
shownTimelines.push(timeline);
}
return shownTimelines;
},
hideTimeline: function(timelineId){
var timeline = this.timelines[timelineId];
var shouldDelete = timeline.remove();
if(shouldDelete) {
delete this.timelines[timelineId];
}
},
toggleUnified: function(templateId, forcedState) {
var template = TimelineTemplate.getTemplate(templateId);
var newState = !template.includeInUnified;
if(forcedState !== undefined) {
newState = forcedState;
}
template.setIncludeInUnified(newState);
if(template.multipleTimelines || template.visible) {
return;
}
if(template.includeInUnified) {
this.createTimelineTemplate(template);
} else {
this.hideTimeline(template.id);
}
},
toggleNotify: function(templateId) {
var template = TimelineTemplate.getTemplate(templateId);
var newState = !template.showOnPageNotification;
template.setShowOnPageNotification(newState);
},
toggleChangeIcon: function(templateId) {
var template = TimelineTemplate.getTemplate(templateId);
var newState = !template.showIconNotification;
template.setShowIconNotification(newState);
},
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);
}
},
/**
* Update this.blockedUserIds and schedule another update after
* "blockedusers_refresh_interval" time has elapsed.
*/
retrieveBlockedUserIds: function() {
var _this = this;
this.twitterBackend.blockedUserIds(function(success, data, status, context) {
if(success) {
_this.blockedUserIds = {};
for(var i in data) {
_this.blockedUserIds[data[i]] = data[i];
}
}
setTimeout(function() { _this.retrieveBlockedUserIds(); }, OptionsBackend.get('blockedusers_refresh_interval'));
});
},
eachTimeline: function(callback, includeHidden) {
for(var tId in this.timelines) {
var timeline = this.timelines[tId];
if(!includeHidden && (!timeline.template.visible || timeline.template.hiddenTemplate)) {
continue;
}
if(callback.call(tId, timeline) === false) {
break;
}
}
},
/* Lists timelines won't be included as they'll only be shown later */
orderedEachTimeline: function(callback, includeLists) {
var retList = [], tId, timeline;
for(tId in this.timelines) {
timeline = this.timelines[tId];
if(!includeLists && timeline.template.id == TimelineTemplate.LISTS) {
continue;
}
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) {
tId = retList[i];
if(tId) {
timeline = this.timelines[tId];
if(timeline.template.visible && !timeline.template.hiddenTemplate) {
var ret = callback.call(tId, timeline);
if(ret === false) {
break;
}
}
}
}
},
getTimelinePosition: function(timelineId) {
if(!this.timelineOrderCache) {
var storedOrder = this.timelineOrderData.val();
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;
this.timelineOrderData.save(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();
var timelineTemplate = timeline.template;
if(timelineTemplate.showIconNotification) {
totalUnreadNewIds = totalUnreadNewIds.concat(unreadNewIds);
if(unreadNewIds.length > 0) {
colors.push(timelineTemplate.iconNotificationColor);
}
}
if(timelineTemplate.showOnPageNotification) {
unreadNewTweets = unreadNewTweets.concat(timeline.getNewUnreadTweets());
}
}, true);
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 tweet_string = totalUnreadNewCount > 1 ? 'newtweets_plural' : 'newtweets_singular';
var title = chrome.i18n.getMessage("newTweets", [totalUnreadNewCount, chrome.i18n.getMessage(tweet_string)]);
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);
}
},
safeTweetsNotifications: function(tweetsToNotify, shouldChangeOption) {
if(shouldChangeOption) {
OptionsBackend.saveOption('tweets_notification_style', 'on_page');
}
this.showTweetsNotifications(tweetsToNotify, true);
},
showTweetsNotifications: function(tweetsToNotify, forceOnPage) {
if(!tweetsToNotify || tweetsToNotify.length === 0) {
return;
}
var maxTweetsNotifications = OptionsBackend.get('notification_max_popups');
if(maxTweetsNotifications != -1 && tweetsToNotify.length > maxTweetsNotifications) {
tweetsToNotify.splice(maxTweetsNotifications, tweetsToNotify.length - maxTweetsNotifications);
}
this.injectTweets = tweetsToNotify;
var notificationStyle = OptionsBackend.get('tweets_notification_style');
if(!forceOnPage && notificationStyle == 'desktop') {
try {
var notificationCenter = window.notifications || window.webkitNotifications;
if(!notificationCenter) {
throw 'NotificationCenter not available';
}
var authStatus = notificationCenter.checkPermission();
if(authStatus == 1 || authStatus == 2) { //Not Allowed or Denied
throw 'Desktop notifications not allowed';
}
// FIXME: This check is here because stable channel releases
// currently silently fail in showing html notifications
var notificationDisplayed = false;
var onDisplayFunc = function() {
notificationDisplayed = true;
};
var _this = this;
setTimeout(function() {
if(!notificationDisplayed) {
for(var i = 0; i < tweetsToNotify.length; ++i) {
_this.shouldNotReadMap[tweetsToNotify[i].id] = true;
}
_this.safeTweetsNotifications(tweetsToNotify, false);
}
}, 1000);
for(var i = 0; i < tweetsToNotify.length; ++i) {
var notification = notificationCenter.createHTMLNotification(
chrome.extension.getURL('tweets_notifier.html'));
(function inClosure(tweet) {
notification.onclose = function(e) {
setTimeout(function() {
_this.readTweet(tweet.id);
}, 200);
};
})(tweetsToNotify[i]);
notification.ondisplay = onDisplayFunc;
notification.show();
}
} catch(e) {
console.warn(e);
// Fallback to 'on page' notifications
this.safeTweetsNotifications(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/any_click.js', true, function() {
injectHelper('script', 'lib/tweet_transforms.js', true, function() {
injectHelper('script', 'lib/timeline_template.js', true, function() {
injectHelper('script', 'lib/tweets_assembler.js', true);
});
});
});
});
});
}
},
registerNewTweetsCallback: function(callback) {
this.newTweetsCallback = callback;
},
readTweet: function(id) {
if(this.shouldNotReadMap[id]) {
delete this.shouldNotReadMap[id];
return;
}
this.readTweets[id] = true;
delete this.unreadTweets[id];
this.notifyNewTweets();
},
isTweetRead: function(id) {
return !this.unreadTweets[id];
},
isRetweet: function(id) {
return this.retweets[id];
},
notifyNewTweets: function() {
if(this.newTweetsCallback) {
var _this = this;
this.eachTimeline(function(timeline) {
var newTweets = timeline.newTweetsCount();
try {
// The callback might be invalid (popup not active), so let's ignore errors for now.
_this.newTweetsCallback(newTweets[0], newTweets[1], timeline.timelineId);
} catch(e) { /* ignoring */ }
});
}
this.updateAlert();
},
enqueueTweet: function(msg, replyId) {
this.sendQueue.enqueueTweet(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);
},
getInReplyToTweet: function(callback, tweet) {
if(tweet.inReplyToTweet) {
callback(true, tweet.inReplyToTweet);
return;
}
var _this = this;
this.twitterBackend.showTweet(function(success, data, status) {
if(success) {
tweet.inReplyToTweet = data;
}
callback(success, data, status);
}, tweet.in_reply_to_status_id);
},
destroy: function(callback, tweetTimelineId, id) {
var _this = this;
var firstCallback = function(success, data, status) {
if(success) {
_this.eachTimeline(function(timeline) {
timeline.removeFromCache(id);
}, true);
}
callback(success, data, status);
};
if(tweetTimelineId == TimelineTemplate.RECEIVED_DMS || tweetTimelineId == TimelineTemplate.SENT_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[TimelineTemplate.FAVORITES];
if(favTimeline) {
favTimeline.pushTweet(data);
}
_this.eachTimeline(function(timeline) {
var tweet = timeline.findTweet(id);
if(tweet)
tweet.favorited = true;
}, 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[TimelineTemplate.FAVORITES];
if(favTimeline) {
favTimeline.removeFromCache(id);
}
_this.eachTimeline(function(timeline) {
var tweet = timeline.findTweet(id);
if(tweet)
tweet.favorited = false;
}, true);
}
callback(success, data, status);
};
return this.twitterBackend.unFavorite(firstCallback, id);
},
lists: function(force, callback) {
if(!TimelineTemplate.getTemplate(TimelineTemplate.LISTS).visible) {
/* No lists tab should be shown, let's just ignore the callback */
return;
}
if(force) {
this.listsCache = null;
}
if(!this.listsCache) {
var _this = this;
this.twitterBackend.lists(function(success, data, status) {
if(success) {
if(!data.lists) {
data.lists = [];
}
_this.listsCache = data.lists;
_this.twitterBackend.subs(function(success, data, status) {
if(success) {
if(!data.lists) {
data.lists = [];
}
_this.listsCache = _this.listsCache.concat(data.lists);
}
if(callback) {
callback(success, _this.listsCache, status);
}
});
} else {
if(callback) {
callback(success, _this.listsCache, status);
}
}
});
return;
}
if(callback) {
callback(true, this.listsCache, null);
}
},
changeSearch: function(timelineId, searchQuery) {
var timeline = this.timelines[timelineId];
if(!timeline) {
return false;
}
return timeline.changeSearchQuery(searchQuery);
},
getSearchQuery: function(timelineId) {
var timeline = this.timelines[timelineId];
if(!timeline) {
return null;
}
return timeline.getSearchQuery();
},
changeList: function(timelineId, listId) {
var timeline = this.timelines[timelineId];
if(!timeline) {
return null;
}
timeline.changeList(listId);
return undefined;
},
getListId: function(timelineId) {
if(!timelineId) {
timelineId = this.currentTimelineId;
}
var timeline = this.timelines[timelineId];
if(!timeline) {
return null;
}
var listId = timeline.getListId();
if(listId && this.listsCache) {
// Check if the listId really exists
for(var i = 0; i < this.listsCache.length; ++i) {
if(this.listsCache[i].uri == listId) {
return listId;
}
}
}
return null;
},
getList: function(timelineId) {
if(!timelineId) {
timelineId = this.currentTimelineId;
}
var timeline = this.timelines[timelineId];
if(!timeline) {
return null;
}
var listId = timeline.getListId();
if(listId && this.listsCache) {
for(var i = 0; i < this.listsCache.length; ++i) {
if(this.listsCache[i].uri == listId) {
return this.listsCache[i];
}
}
}
return null;
},
giveMeTweets: function(timelineId, callback, syncNew, cacheOnly) {
var timeline = this.timelines[timelineId];
if(!timeline) {
callback([], timelineId);
return undefined;
}
if(syncNew && timeline.template.includeInUnified) {
var originalCallback = callback;
var _this = this;
callback = function(tweets, timelineId) {
originalCallback(tweets, timelineId);
_this.timelines[TimelineTemplate.UNIFIED].giveMeTweets(originalCallback, false, true);
};
}
return timeline.giveMeTweets(callback, syncNew, cacheOnly);
},
newTweetsCount: function(timelineId) {
return this.timelines[timelineId].newTweetsCount();
},
updateNewTweets: function() {
var currentTimeline = this.timelines[this.currentTimelineId];
if(currentTimeline.template.id == TimelineTemplate.FAVORITES) {
var newTweets = this.timelines[this.currentTimelineId].getNewTweetsCache();
for(var i = 0, len = newTweets.length; i < len; ++i) {
var id = newTweets[i].id;
this.eachTimeline(function(timeline) {
var tweet = timeline.findTweet(id);
if(tweet)
tweet.favorited = true;
}, true);
}
}
currentTimeline.updateNewTweets();
this.updateAlert();
},
getCurrentTimeline: function() {
return this.timelines[this.currentTimelineId];
},
getTimeline: function(timelineId) {
return this.timelines[timelineId];
},
currentError: function() {
return this.timelines[this.currentTimelineId].getError();
},
stopAll: function() {
this.eachTimeline(function(timeline) {
timeline.killTimeline();
delete timeline;
}, true);
},
signout: function() {
this.oauthTokenData.remove();
this.stopAll();
TweetManager.instance = new TweetManager();
},
restart: function() {
this.stopAll();
initializeJQueryOptions();
TweetManager.instance = new TweetManager();
},
markTimelineAsRead: function() {
var tweetsToRead = this.getCurrentTimeline().getTweetsCache();
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, 10) * 1000);
if(remainingHits === 0) {
this.autoClearWarning = true;
this.setWarning(chrome.i18n.getMessage("exceededAPIHits", [chrome.extension.getURL('options.html'), nextResetDate.toLocaleDateString(), nextResetDate.toLocaleTimeString()]));
return;
}
if(this.autoClearWarning) {
this.autoClearWarning = false;
this.clearWarning();
}
if(this.apiHitsStates.length > 0) {
if(remainingHits > this.apiHitsStates[this.apiHitsStates.length - 1][1]) {
// Rate limit was reset
this.apiHitsStates = [];
}
}
this.apiHitsStates.push([(new Date()).getTime(), parseInt(remainingHits, 10)]);
if(this.apiHitsStates.length > this.maxApiHitsStates) {
this.apiHitsStates.shift();
}
if(this.apiHitsStates.length > 20) {
var offset = this.apiHitsStates[0][0];
var tendencyFunction = Math.linearLeastSquares(this.apiHitsStates, offset);
var projectedRemainingHits = tendencyFunction(nextResetDate.getTime() - offset);
if(projectedRemainingHits < 0) {
if(this.firstPossibleWarnTime) {
var timeDiff = new Date() - this.firstPossibleWarnTime;
if(timeDiff > 3 * 60 * 1000) {
// We've been keeping this tendency for 5 minutes, so we'll probably
// exceed the limit if we keep this same rate, let's warn users.
this.setWarning(chrome.i18n.getMessage("warmingAPIHits", [chrome.extension.getURL('options.html'), remainingHits, nextResetDate.toLocaleDateString(), nextResetDate.toLocaleTimeString()]));
this.firstPossibleWarnTime = null;
}
} else {
this.firstPossibleWarnTime = new Date();
}
} else {
this.firstPossibleWarnTime = null;
}
}
},
retrieveUsersData: function(usersIdList) {
var _this = this;
this.twitterBackend.lookupUsers(function(success, users) {
if(!success) {
// Try again in a while...
setTimeout(function() {
_this.retrieveUsersData(usersIdList);
}, 120000);
return;
}
for(var i = 0, len = users.length; i < len; ++i) {
var user = users[i];
_this.followingUsersMap[user.screen_name] = user;
_this.followingUsersNames.push(user.screen_name);
}
_this.followingUsersNames.sort(function(a, b) {
return a.toUpperCase().localeCompare(b.toUpperCase());
});
}, usersIdList);
},
retrieveFollowingUsers: function() {
this.followingUsersNames = [];
this.followingUsersMap = {};
var _this = this;
this.twitterBackend.friendsIds(function(success, usersData) {
if(!success) {
// Try again in a while...
setTimeout(function() {
_this.retrieveFollowingUsers();
}, 120000);
return;
}
var idsList = usersData.ids;
if(!idsList) {
idsList = [];
}
for(var i = 0, len = Math.ceil(idsList.length / 100.0); i < len; ++i) {
var firstIndex = i * 100;
var idsListSlice = idsList.slice(firstIndex, firstIndex + 100);
_this.retrieveUsersData(idsListSlice);
}
});
},
getFollowingUsers: function() {
return this.followingUsersNames;
},
getFollowingUsersMap: function() {
return this.followingUsersMap;
},
resetTimeline: function(timelineId) {
var timeline = this.getTimeline(timelineId);
if(timeline) {
timeline.reset();
}
},
followUser: function(callback, username) {
var _this = this;
this.twitterBackend.follow(function(success, userData) {
if(success) {
_this.followingUsersMap[userData.screen_name] = userData;
_this.followingUsersNames.push(userData.screen_name);
_this.followingUsersNames.sort(function(a, b) {
return a.toUpperCase().localeCompare(b.toUpperCase());
});
_this.resetTimeline(TimelineTemplate.HOME);
_this.resetTimeline(TimelineTemplate.UNIFIED);
}
callback(success, userData);
}, username);
},
unfollowUser: function(callback, username) {
var _this = this;
this.twitterBackend.unfollow(function(success, userData) {
if(success) {
delete _this.followingUsersMap[userData.screen_name];
var position = $.inArray(userData.screen_name);
if(position > -1) {
_this.followingUsersNames.splice(position, 1);
}
_this.resetTimeline(TimelineTemplate.HOME);
_this.resetTimeline(TimelineTemplate.UNIFIED);
}
callback(success, userData);
}, username);
},
};
/* Clean up old versions mess */
Persistence.cleanupOldData();
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] === undefined) return -1;
if(v2[i] === undefined) return 1;
if(parseInt(v1[i], 10) > parseInt(v2[i], 10)) return 1;
if(parseInt(v1[i], 10) < parseInt(v2[i], 10)) return -1;
}
return 0;
}
function checkVersionChanges(manifest) {
try {
var currentVersion = manifest.version.split('.');
var storageData = Persistence.version();
var storageVersion = storageData.val();
var options, baseUrl;
if(storageVersion) {
storageVersion = JSON.parse(storageVersion);
} else {
// No previous version data let's just assume we're running the latest version
storageData.save(JSON.stringify(currentVersion));
return;
}
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) {
options = OptionsBackend.load();
options['home_refresh_interval'] = OptionsBackend.defaultOptions['home_refresh_interval'];
OptionsBackend.save(options);
}
}
if(compareVersions(storageVersion, [1, 5, 1]) <= 0) {
var timelineOrderData = Persistence.timelineOrder();
var storedOrder = timelineOrderData.val();
if(storedOrder) {
storedOrder = JSON.parse(storedOrder);
} else {
storedOrder = [];
}
storedOrder.unshift('unified');
timelineOrderData.save(JSON.stringify(storedOrder));
}
if(compareVersions(storageVersion, [1, 6, 1]) <= 0) {
options = OptionsBackend.load();
baseUrl = options['base_url'];
if(baseUrl.match(/http:\/\/twitter\.com/)) {
options['base_url'] = 'http://api.twitter.com/1/';
options['base_signing_url'] = 'http://api.twitter.com/1/';
} else if(baseUrl.match(/https:\/\/twitter\.com/)) {
options['base_url'] = 'https://api.twitter.com/1/';
options['base_signing_url'] = 'https://api.twitter.com/1/';
}
OptionsBackend.save(options);
}
if(compareVersions(storageVersion, [1, 7, 0]) <= 0) {
var selectedListsData = Persistence.selectedLists();
var selectedListsInfo = selectedListsData.val();
if(selectedListsInfo) {
var selectedObj = JSON.parse(selectedListsInfo);
if(selectedObj) {
var listsIdList = [];
for(var key in selectedObj) {
var listId = selectedObj[key];
listsIdList.push(listId);
}
TweetManager.instance.hideTimelineTemplate(TimelineTemplate.LISTS);
var listsTemplate = TimelineTemplate.getTemplate(TimelineTemplate.LISTS);
listsTemplate.setUserData(listsIdList);
TweetManager.instance.showTimelineTemplate(TimelineTemplate.LISTS, true);
}
}
selectedListsData.remove();
}
if(compareVersions(storageVersion, [1, 8, 0]) <= 0) {
var requestTimeout = OptionsBackend.get('request_timeout');
if(requestTimeout == 6000) {
// Twitter itself recomends a bigger timeout value, so
// let's update the default value
requestTimeout = OptionsBackend.defaultOptions['request_timeout'];
OptionsBackend.saveOption('request_timeout', requestTimeout);
initializeJQueryOptions();
}
baseUrl = OptionsBackend.get('base_url');
var baseSigningUrl = OptionsBackend.get('base_signing_url');
if(baseUrl == baseSigningUrl && baseUrl == 'http://api.twitter.com/1/') {
// This days the SSL endpoint seems more reliable, let's make it the
// default one unless users have manually changed it.
baseUrl = OptionsBackend.defaultOptions['base_url'];
baseSigningUrl = OptionsBackend.defaultOptions['base_signing_url'];
OptionsBackend.saveOption('base_url', baseUrl);
OptionsBackend.saveOption('base_signing_url', baseSigningUrl);
TwitterLib.URLS.BASE = baseUrl;
TwitterLib.URLS.BASE_SIGNING = baseSigningUrl;
}
}
if(compareVersions(storageVersion, [1, 8, 5]) <= 0) {
// Enabling desktop notifications as the default option,
// users may switch back to onpage notifications latter if they wish.
OptionsBackend.saveOption('tweets_notification_style', 'desktop');
}
if(compareVersions(storageVersion, [1, 9, 5, 1]) <= 0) {
var maxNotifications = OptionsBackend.get('notification_max_popups');
if(maxNotifications == -1) {
// Set new default value if we're using unlimited notifications,
// otherwise there might have been thousands of notifications
// to be shown, mainly after we fixed the way we fetch new
// tweets.
OptionsBackend.saveOption('notification_max_popups', OptionsBackend.defaultOptions['notification_max_popups']);
}
}
storageData.save(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) {
if(waitingFirstRequest) {
waitingFirstRequest = false;
setTimeout(function() {
if(selectedResponse) {
selectedResponse({
tweets: TweetManager.instance.injectTweets,
nameAttribute: OptionsBackend.get('name_attribute'),
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;
}
return;
}
});
}
</script>
</body>
</html>
Jump to Line
Something went wrong with that request. Please try again.