-
Notifications
You must be signed in to change notification settings - Fork 21
/
background.js
232 lines (185 loc) · 6.58 KB
/
background.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/*
The background page is responsible for the following:
* keeping track of the saved state, as well as what's open.
* retrieving, storing and updating this state in localStorage.
* listening for window/tab events to keep our state up to date.
* saving, opening and deleting windows actions from the popups.
FEATURE: omnibox support
*/
var DEFAULT_NAME = "Window";
/* BASIC STATE */
// an array of the names of all saved windows
var savedWindowNames = restoreFromLocalStorage("savedWindowNames", new Array());
// saved windows, keyed by name
// If the savedWindow has an id, it is currently open.
// Each savedWindow can only correspond to one open window at any given time.
var savedWindows = new Object();
// map the ids of open windows to saved window names
// used to respond to events
var windowIdToName = new Object();
/* EDGE CASES */
// saved windows that aren't currently open, keyed by name
// used to match new windows to saved windows that are still closed
var closedWindows = new Object();
// Unfortunately, removing a tab doesn't give us a windowId
// so we need to keep track of that mapping.
var tabIdToSavedWindowId = new Object();
// object that stores per-window flags as to whether API indicated
// window-closing intention on tab removal
var isWindowClosing = new Object();
/* INIT */
// Google Analytics
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-18459718-1']);
_gaq.push(['_setCustomVar', 1, 'windowCount', savedWindowNames.length, 1]);
(function() {
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = 'https://www.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
// populate savedWindows from local storage
// as we go, try matching them to open windows
chrome.windows.getAll({populate:true}, function(browserWindows) {
for (var i in savedWindowNames) {
var name = savedWindowNames[i];
var savedWindow = restoreFromLocalStorage(name);
if (!savedWindow) {
console.error("Window " + name + " was not found in localStorage.");
savedWindowNames.splice(savedWindowNames.indexOf(name), 1);
localStorage.savedWindowNames = JSON.stringify(savedWindowNames);
continue;
}
savedWindows[name] = savedWindow;
// by default, we assume the window is closed (id is undefined)
delete savedWindow.id;
// now, let's check if it's one of the open windows
for (var j in browserWindows) {
var browserWindow = browserWindows[j];
if (windowsAreEqual(browserWindow, savedWindow)) {
storeWindow(browserWindow, name, savedWindow.displayName);
markWindowAsOpen(browserWindow);
break;
}
}
if (!savedWindows[name].id) {
closedWindows[name] = savedWindows[name];
}
}
});
// compares a current window to a saved window
// we are optimistic here: as long as the tabs of the new window
// match those of the saved window, we consider them equal
// even if the new window has more tabs
// TODO: try disregarding query strings (might be better?)
function windowsAreEqual(browserWindow, savedWindow) {
if (browserWindow.incognito) {
return false;
}
if (!browserWindow.tabs || !savedWindow.tabs) {
return false;
}
if (browserWindow.tabs.length < savedWindow.tabs.length) {
return false;
}
for (var i in savedWindow.tabs) {
if (browserWindow.tabs[i].url != savedWindow.tabs[i].url) {
return false;
}
}
return true;
}
// save a window
// returns the saved window object
function saveWindow(browserWindow, displayName) {
var displayName = (displayName == "") ? DEFAULT_NAME : displayName;
// handle duplicate names
var name = displayName;
var n = 0;
while(savedWindows[name]) {
name = displayName + n;
n++;
}
// add window to indexes
savedWindowNames.push(name);
localStorage.savedWindowNames = JSON.stringify(savedWindowNames);
storeWindow(browserWindow, name, displayName);
if (browserWindow.id) {
markWindowAsOpen(browserWindow);
}
return browserWindow;
}
// store a window object
// returns the stored window
function storeWindow(browserWindow, name, displayName) {
browserWindow.name = name;
browserWindow.displayName = displayName;
savedWindows[name] = browserWindow;
localStorage[name] = JSON.stringify(browserWindow);
return browserWindow;
}
function markWindowAsOpen(savedWindow) {
delete closedWindows[savedWindow.name];
windowIdToName[savedWindow.id] = savedWindow.name;
for (var i in savedWindow.tabs) {
tabIdToSavedWindowId[savedWindow.tabs[i].id] = savedWindow.id;
}
updateBadgeForWindow(savedWindow.id);
}
function markWindowAsClosed(savedWindow) {
delete windowIdToName[savedWindow.id];
closedWindows[savedWindow.name] = savedWindow;
delete savedWindow.id;
}
// restore a previously saved window
function openWindow(name) {
chrome.tabs.getSelected(null, function(tab){
// if the window was opened from a new tab, close the new tab
if (tab.url == "chrome://newtab/") {
chrome.tabs.remove(tab.id);
}
// compile the raw list of urls
var savedWindow = savedWindows[name];
var urls = [];
for (i in savedWindow.tabs) {
urls[i] = savedWindow.tabs[i].url;
}
// create a window and open the tabs in it.
var createData = {url: urls};
var callback = function (browserWindow) { onWindowOpened(savedWindow, browserWindow); };
chrome.windows.create(createData, callback);
});
}
// mark a window as opened and pin tabs if necessary
function onWindowOpened(savedWindow, browserWindow) {
savedWindow.id = browserWindow.id;
markWindowAsOpen(savedWindow);
// pinned tabs
for (var i in savedWindow.tabs) {
if (savedWindow.tabs[i].pinned) {
chrome.tabs.update(browserWindow.tabs[i].id, {pinned: true});
}
}
// move the window to the end of the list (so it appears at the top of the popup)
savedWindowNames.splice(savedWindowNames.indexOf(savedWindow.name), 1);
savedWindowNames[savedWindowNames.length] = savedWindow.name;
localStorage.savedWindowNames = JSON.stringify(savedWindowNames);
}
// removed a saved window
function deleteSavedWindow(name) {
var savedWindow = savedWindows[name];
var id = savedWindow.id;
if (id) {
markWindowAsClosed(savedWindow);
updateBadgeForWindow(id);
for (var i in savedWindow.tabs) {
delete tabIdToSavedWindowId[savedWindow.tabs[i].id];
}
}
delete closedWindows[savedWindow.name];
delete localStorage[name];
delete savedWindows[name];
savedWindowNames.splice(savedWindowNames.indexOf(name), 1);
localStorage.savedWindowNames = JSON.stringify(savedWindowNames);
}