Skip to content

Commit 830decb

Browse files
committed
feat: add more scope options
1 parent c176861 commit 830decb

File tree

4 files changed

+127
-80
lines changed

4 files changed

+127
-80
lines changed

auto-close-duplicate-tabs/background.js

Lines changed: 89 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@
66
const manifest = browser.runtime.getManifest();
77
const extname = manifest.name;
88

9-
let delayTimerId = null;
10-
let isActive = true;
11-
let regexList = null;
12-
let includeAllWindows = false;
9+
let settings = {
10+
delayTimerId: null,
11+
isActive: true,
12+
ignoredREMatchers: [],
13+
ignoreWindowScope: false,
14+
ignoreContainerScope: false,
15+
ignoreGroupScope: false,
16+
ignoreURLPath: false,
17+
ignoreURLParams: false,
18+
};
1319

1420
function serializeSearchParams(searchParams) {
1521
// Sort the key/value pairs
@@ -22,64 +28,61 @@
2228
return out;
2329
}
2430

25-
async function buildRegExList() {
31+
async function storageString2REArray(storageId) {
2632
const out = [];
27-
(await storage.get("string", "matchers", ""))
28-
.split("\n")
29-
.forEach((line) => {
30-
line = line.trim();
31-
if (line !== "") {
32-
try {
33-
out.push(new RegExp(line));
34-
} catch (e) {
35-
console.error(e);
36-
}
33+
(await storage.get("string", storageId, "")).split("\n").forEach((line) => {
34+
line = line.trim();
35+
if (line !== "") {
36+
try {
37+
out.push(new RegExp(line));
38+
} catch (e) {
39+
console.error(e);
3740
}
38-
});
41+
}
42+
});
3943
return out;
4044
}
4145

42-
function isOnRegexList(url) {
46+
function containsMatch(regexList, str) {
4347
for (let i = 0; i < regexList.length; i++) {
44-
if (regexList[i].test(url)) {
48+
if (regexList[i].test(str)) {
4549
return true;
4650
}
4751
}
4852
return false;
4953
}
5054

55+
async function syncSetting(type, id, fallback) {
56+
settings[id] = await storage.get(type, id, fallback);
57+
}
58+
5159
async function onStorageChanged() {
52-
regexList = await buildRegExList();
53-
includeAllWindows = await storage.get(
54-
"boolean",
55-
"includeAllWindows",
56-
false,
60+
settings.ignoredREMatchers = await storageString2REArray(
61+
"ignoredREMatchersString",
5762
);
58-
console.debug(includeAllWindows);
63+
await syncSetting("boolean", "ignoreWindowScope", false);
64+
await syncSetting("boolean", "ignoreContainerScope", false);
65+
await syncSetting("boolean", "ignoreGroupScope", false);
66+
await syncSetting("boolean", "ignoreURLPath", false);
67+
await syncSetting("boolean", "ignoreURLParams", false);
68+
delayed_delDups();
5969
}
6070

6171
async function delayed_delDups() {
62-
clearTimeout(delayTimerId);
63-
delayTimerId = setTimeout(delDups, 2500); // 2.5 seconds w/o tab status changes
72+
clearTimeout(settings.delayTimerId);
73+
settings.delayTimerId = setTimeout(delDups, 3500); // 3.5 seconds w/o tab status changes
6474
}
6575

6676
async function delDups() {
67-
if (!isActive) {
77+
if (!settings.isActive) {
6878
return;
6979
}
7080

7181
let qryobj = {
7282
pinned: false,
7383
};
7484

75-
if (!includeAllWindows) {
76-
qryobj["currentWindow"] = true;
77-
}
78-
7985
const allTabs = await browser.tabs.query(qryobj);
80-
const last_focused_window = await browser.windows.getLastFocused({
81-
populate: false,
82-
});
8386

8487
// check if any tab is still loading , if so we wait
8588
if (allTabs.some((t) => t.status !== "complete")) {
@@ -94,14 +97,34 @@
9497
const dup_groups = new Map();
9598

9699
for (const t of allTabs) {
97-
if (!isOnRegexList(t.url)) {
100+
if (!containsMatch(settings.ignoredREMatchers, t.url)) {
98101
const urlobj = new URL(t.url);
99-
tab_url =
100-
urlobj.origin +
101-
urlobj.pathname +
102-
serializeSearchParams(urlobj.searchParams);
102+
//if (!settings.ignoredHostnames.includes(urlobj.hostname)) {
103+
let key = urlobj.origin;
103104

104-
const key = t.cookieStoreId + "_" + tab_url;
105+
// -----
106+
107+
if (!settings.ignoreURLPath) {
108+
key = key + "_" + urlobj.pathname;
109+
}
110+
if (!settings.ignoreURLParams) {
111+
key = key + "_" + serializeSearchParams(urlobj.searchParams);
112+
}
113+
if (!settings.ignoreContainerScope) {
114+
key = t.cookieStoreId + "_" + key;
115+
}
116+
if (!settings.ignoreWindowScope) {
117+
key = t.windowId + "_" + key;
118+
}
119+
if (!settings.ignoreGroupScope) {
120+
if (browser.tabGroups) {
121+
if (t.groupId) {
122+
key = t.groupId + "_" + key;
123+
}
124+
}
125+
}
126+
127+
// -----
105128

106129
if (!dup_groups.has(key)) {
107130
dup_groups.set(key, []);
@@ -137,35 +160,18 @@
137160
} else {
138161
// in this case BOTH could be active/focused in the window
139162
if (focus_groups.includes(k)) {
140-
if (a.active && b.active) {
141-
// closeing neither would be a way to go or close the one in the inactive Window
142-
// But even if the window is inactive the user could have them open for comparision ...
143-
// this is kind of a not so straight forward decision
144-
// i mean the user could still pin the tabs that is a workaround at least
145-
146-
// lets prefer the active one in the last focused window
147-
if (a.windowId === last_focused_window.id) {
163+
if (!(a.active && b.active)) {
164+
// prefer active
165+
if (a.active) {
148166
return -1;
149167
}
150-
if (b.windowId === last_focused_window.id) {
168+
if (b.active) {
151169
return 1;
152170
}
153-
// if we get here, both tabs are in unfocused windows (cant have 2 focused windows AFAIK) ... what todo now?
154-
// lets just fall back to lastAccessed
155-
return b.lastAccessed - a.lastAccessed;
156-
}
157-
158-
// prefer active
159-
if (a.active) {
160-
return -1;
161-
}
162-
if (b.active) {
163-
return 1;
164171
}
165172
}
166-
//return b.lastAccessed - a.lastAccessed;
167173
}
168-
// and last fallback
174+
// final fallback
169175
return b.lastAccessed - a.lastAccessed;
170176
});
171177

@@ -180,14 +186,14 @@
180186
await browser.tabs.remove(tid);
181187
} catch (e) {}
182188
}
183-
delayTimerId = null;
189+
settings.delayTimerId = null;
184190
}
185191

186192
async function onBAClicked() {
187-
clearTimeout(delayTimerId);
188-
isActive = !isActive;
189-
storage.set("isActive", isActive);
190-
if (isActive) {
193+
clearTimeout(settings.delayTimerId);
194+
settings.isActive = !settings.isActive;
195+
storage.set("isActive", settings.isActive);
196+
if (settings.isActive) {
191197
browser.browserAction.setBadgeBackgroundColor({ color: "green" });
192198
browser.browserAction.setBadgeText({ text: "on" });
193199
delayed_delDups();
@@ -199,10 +205,14 @@
199205

200206
// setup
201207
await onStorageChanged();
202-
isActive = await storage.get("boolean", "isActive", isActive);
203-
storage.set("isActive", isActive);
204-
205-
if (isActive) {
208+
settings.isActive = await storage.get(
209+
"boolean",
210+
"isActive",
211+
settings.isActive,
212+
);
213+
storage.set("isActive", settings.isActive);
214+
215+
if (settings.isActive) {
206216
browser.browserAction.setBadgeBackgroundColor({ color: "green" });
207217
browser.browserAction.setBadgeText({ text: "on" });
208218
} else {
@@ -217,4 +227,12 @@
217227
});
218228
browser.tabs.onCreated.addListener(delayed_delDups);
219229
browser.storage.onChanged.addListener(onStorageChanged);
230+
// tab moving might trigger group changes
231+
browser.tabs.onMoved.addListener(() => {
232+
if (!settings.ignoreGroupScope) {
233+
if (browser.tabGroups) {
234+
delayed_delDups();
235+
}
236+
}
237+
});
220238
})();

auto-close-duplicate-tabs/manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"browser_action": {
88
"default_area": "navbar"
99
},
10-
"description": "Auto close tabs with URLs which were already open",
10+
"description": "Auto close tabs with the same URLs (excluding hashes) in the same context and tabgroup which were already open",
1111
"icons": {
1212
"128": "icon.png"
1313
},
@@ -16,6 +16,6 @@
1616
"options_ui": {
1717
"page": "options.html"
1818
},
19-
"permissions": ["cookies", "storage", "tabs"],
19+
"permissions": ["cookies", "storage", "tabs", "tabGroups"],
2020
"version": "1.9.28"
2121
}

auto-close-duplicate-tabs/options.html

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,43 @@
66
<body style="padding: 7px">
77
<fieldset>
88
<span
9-
><input id="includeAllWindows" type="checkbox" />check to detect and close
10-
duplicates across all windows
9+
><input id="ignoreWindowScope" type="checkbox" />ignore window scope
1110
</span>
1211
</fieldset>
1312

1413
<fieldset>
15-
<legend><b>Ignore URLs</b></legend>
14+
<span
15+
><input id="ignoreContainerScope" type="checkbox" />ignore container scope
16+
</span>
17+
</fieldset>
18+
19+
<fieldset>
20+
<span
21+
><input id="ignoreGroupScope" type="checkbox" /> ignore group scope
22+
</span>
23+
</fieldset>
24+
25+
<fieldset>
26+
<span
27+
><input id="ignoreURLParams" type="checkbox" /> ignore URL parameters
28+
</span>
29+
</fieldset>
30+
31+
<fieldset>
32+
<span><input id="ignoreURLPath" type="checkbox" /> ignore URL path </span>
33+
</fieldset>
34+
35+
<fieldset>
36+
<legend><b>Ignored URL Matchers</b></legend>
1637
<textarea
17-
id="matchers"
38+
id="ignoredREMatchersString"
1839
style="width: 100%; min-height: 20em"
19-
placeholder="Enter regular expressions line by line here
40+
spellcheck="false"
41+
placeholder="Enter Regular Expressions line by line here
2042
Examples:
2143
^https:\/\/www\.facebook\.com.*
2244
^https:\/\/example\.net.*
23-
^https:\/\/.*\.wikipedia\.org.*
45+
^https:\/\/en\.wikipedia\.org.*
2446
"
2547
></textarea>
2648
</fieldset>

auto-close-duplicate-tabs/options.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,14 @@ function onChange(evt) {
1414
browser.storage.local.set(obj);
1515
}
1616

17-
["includeAllWindows", "matchers"].map((id) => {
17+
[
18+
"ignoreWindowScope",
19+
"ignoreContainerScope",
20+
"ignoreGroupScope",
21+
"ignoreURLPath",
22+
"ignoreURLParams",
23+
"ignoredREMatchersString",
24+
].map((id) => {
1825
browser.storage.local
1926
.get(id)
2027
.then((obj) => {

0 commit comments

Comments
 (0)