comparison sidebar/js/tab-collection-manager.js @ 0:d13d59494613

Initial revision
author Guido Berhoerster <guido+set-aside@berhoerster.name>
date Sat, 17 Nov 2018 10:44:16 +0100
parents
children b0827360b8e4
comparison
equal deleted inserted replaced
-1:000000000000 0:d13d59494613
1 /*
2 * Copyright (C) 2018 Guido Berhoerster <guido+set-aside@berhoerster.name>
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 */
8
9 'use strict';
10
11 var tabManager;
12
13 class TabManager {
14 constructor() {
15 this.tabCollectionTemplate =
16 document.querySelector('#tab-collection-template');
17 this.tabItemTemplate = document.querySelector('#tab-item-template');
18 this.tabCollectionsElement = document.querySelector('#tab-collections');
19 this.port = browser.runtime.connect({name: 'tab-collection-manager'});
20 this.port.onMessage.addListener(this.onMessage.bind(this));
21 this.port.onDisconnect.addListener(this.onMessage.bind(this));
22 this.port.postMessage({type: 'getTabCollections'});
23 this.isInitialized = false;
24 }
25
26 initTabCollections(tabCollections) {
27 if (this.isInitialized) {
28 return;
29 }
30
31 for (let tabCollection of tabCollections.values()) {
32 this.prependTabCollection(tabCollection);
33 }
34 this.sortTabCollections();
35
36 document.querySelector('#message').textContent =
37 browser.i18n.getMessage('emptySidebarMessage');
38
39 document.body.addEventListener('click', this);
40
41 this.isInitialized = true;
42 }
43
44 createTabCollectionNode(tabCollection) {
45 let tabCollectionNode =
46 document.importNode(this.tabCollectionTemplate.content, true);
47
48 tabCollectionNode.querySelector('.tab-collection')
49 .dataset.tabCollectionUuid = tabCollection.uuid;
50
51 tabCollectionNode.querySelector('.tab-collection-title').textContent =
52 browser.i18n.getMessage('collectionTitle',
53 tabCollection.tabs.size);
54
55 let tabCollectionCtimeElement =
56 tabCollectionNode.querySelector('.tab-collection-ctime');
57 tabCollectionCtimeElement.dateTime = tabCollection.date.toISOString();
58 tabCollectionCtimeElement.textContent =
59 tabCollection.date.toLocaleString();
60
61 let tabCollectionRestoreElement =
62 tabCollectionNode.querySelector('.restore-tab-collection');
63 tabCollectionRestoreElement.title =
64 tabCollectionRestoreElement.textContent =
65 browser.i18n.getMessage('restoreTabsButtonTitle');
66 tabCollectionNode.querySelector('.remove-tab-collection').title =
67 browser.i18n.getMessage('removeTabsButtonTitle');
68
69 let tabListElement =
70 tabCollectionNode.querySelector('.tab-collection-tabs');
71 for (let tab of tabCollection.tabs.values()) {
72 let tabItemNode = document.importNode(this.tabItemTemplate.content,
73 true);
74
75 tabItemNode.querySelector('.tab-item').dataset.tabUuid = tab.uuid;
76
77 let tabLinkElement = tabItemNode.querySelector('.tab-link');
78 tabLinkElement.href = tab.url;
79 tabLinkElement.title = tab.title;
80
81 if (tab.thumbnailUrl !== null) {
82 tabItemNode.querySelector('.tab-thumbnail').src =
83 tab.thumbnailUrl;
84 }
85
86 if (tab.favIconUrl !== null) {
87 tabItemNode.querySelector('.tab-favicon').src = tab.favIconUrl;
88 }
89
90 tabItemNode.querySelector('.tab-title').textContent = tab.title;
91
92 tabItemNode.querySelector('.remove-tab').title =
93 browser.i18n.getMessage('removeTabTitle');
94
95 tabListElement.append(tabItemNode);
96 }
97
98 return tabCollectionNode;
99 }
100
101 prependTabCollection(tabCollection) {
102 console.log('prepending tab collection', tabCollection,
103 'to tab collections');
104 this.tabCollectionsElement
105 .prepend(this.createTabCollectionNode(tabCollection));
106 }
107
108 replaceTabCollection(tabCollection) {
109 console.log('replacing tab collection', tabCollection);
110 this.tabCollectionsElement.querySelector(`[data-tab-collection-uuid=` +
111 `"${tabCollection.uuid}"]`)
112 .replaceWith(this.createTabCollectionNode(tabCollection));
113 }
114
115 removeTabCollection(tabCollectionUuid) {
116 console.log('removing tab collection %s', tabCollectionUuid);
117 this.tabCollectionsElement
118 .querySelector(`[data-tab-collection-uuid=` +
119 `"${tabCollectionUuid}"]`)
120 .remove();
121
122 if (this.tabCollectionsElement.childElementCount === 0) {
123 // remove any text nodes so that the :empty CSS selectora applies
124 while (this.tabCollectionsElement.firstChild !== null) {
125 this.tabCollectionsElement
126 .removeChild(this.tabCollectionsElement.firstChild);
127 }
128 }
129 }
130
131 sortTabCollections() {
132 Array.from(this.tabCollectionsElement.children)
133 .map(element =>
134 [element.querySelector('.tab-collection-ctime').dateTime,
135 element])
136 .sort((a, b) => a[0] < b[0] ? 1 : a[0] > b[0] ? -1 : 0)
137 .forEach(([, element]) =>
138 this.tabCollectionsElement.append(element));
139 }
140
141 onMessage(message, port) {
142 console.log('received message', message, 'on port', port);
143 switch (message.type) {
144 case 'tabCollections':
145 this.initTabCollections(message.tabCollections);
146 break;
147 case 'tabCollectionCreated':
148 this.prependTabCollection(message.tabCollection);
149 break;
150 case 'tabCollectionRemoved':
151 this.removeTabCollection(message.tabCollectionUuid);
152 break;
153 case 'tabCollectionChanged':
154 this.replaceTabCollection(message.tabCollection);
155 break;
156 }
157 this.sortTabCollections();
158 }
159
160 handleEvent(ev) {
161 console.log('DOM event', ev);
162 if (ev.type === 'click') {
163 ev.preventDefault();
164 if (ev.target.classList.contains('restore-tab-collection')) {
165 // restore tab collection
166 let tabCollectionUuid = ev.target.closest('.tab-collection')
167 .dataset.tabCollectionUuid;
168 this.port.postMessage({
169 type: 'restoreTabCollection',
170 tabCollectionUuid,
171 windowId: browser.windows.WINDOW_ID_CURRENT
172 });
173 } else if (ev.target.classList.contains('remove-tab-collection')) {
174 // remove tab collection
175 let tabCollectionUuid = ev.target.closest('.tab-collection')
176 .dataset.tabCollectionUuid;
177 this.port.postMessage({
178 type: 'removeTabCollection',
179 tabCollectionUuid
180 });
181 } else if (ev.target.classList.contains('remove-tab')) {
182 // remove tab from collection
183 let tabItemElement = ev.target.closest('.tab-item');
184 let tabCollectionUuid =
185 tabItemElement.closest('.tab-collection')
186 .dataset.tabCollectionUuid;
187 let tabUuid = tabItemElement.dataset.tabUuid;
188 this.port.postMessage({
189 type: 'removeTab',
190 tabCollectionUuid,
191 tabUuid
192 });
193 } else {
194 let tabItemElement = ev.target.closest('.tab-item');
195 if (tabItemElement !== null) {
196 // restore tab from collection
197 let tabCollectionUuid =
198 tabItemElement.closest('.tab-collection')
199 .dataset.tabCollectionUuid;
200 let tabUuid = tabItemElement.dataset.tabUuid;
201 this.port.postMessage({
202 type: 'restoreTab',
203 tabCollectionUuid,
204 tabUuid,
205 windowId: browser.windows.WINDOW_ID_CURRENT
206 });
207 }
208 }
209 }
210 }
211 }
212
213 browser.windows.getCurrent().then(currentWindow => {
214 // disable the sidebar for incognito windows
215 if (currentWindow.incognito) {
216 document.querySelector('#message').textContent =
217 browser.i18n.getMessage('incognitoModeMessage');
218 return;
219 }
220
221 tabManager = new TabManager();
222 });