]> git.wh0rd.org - chrome-ext/tabs-backup.git/blob - advanced.js
use padStart API
[chrome-ext/tabs-backup.git] / advanced.js
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Add event listeners once the DOM has fully loaded by listening for the
6 // `DOMContentLoaded` event on the document, and adding your listeners to
7 // specific elements when it triggers.
8 document.addEventListener('DOMContentLoaded', function () {
9 //document.querySelector('button').addEventListener('click', testf);
10 //document.getElementById("menuItem_restoreNow").addEventListener('click', menu_restoreNow);
11 document.getElementById("menuItem_showOlderBackups").addEventListener('click', menu_ShowOlderBackups);
12 document.getElementById("restoreSelectedDiv").addEventListener('click', menu_RestoreSelected);
13 document.getElementById("restoreSelectedClearSelectionLink").addEventListener('click', menu_ClearSelection);
14
15 document.getElementById("restoreSelectedRadioSingleWindowSpanLabel").addEventListener('click', function() {
16 $('#restoreSelectedRadioSingleWindow').prop('checked', true);
17 });
18
19 document.getElementById("restoreSelectedRadioMultipleWindowsSpanLabel").addEventListener('click', function() {
20 $('#restoreSelectedRadioMultipleWindows').prop('checked', true);
21 });
22
23
24 initBackupsList (false /*showAll*/);
25
26 /*$(function() {
27 $( "#dialog-confirm" ).dialog({
28 resizable: false,
29 height:140,
30 modal: true,
31 buttons: {
32 "Delete all items": function() {
33 $( this ).dialog( "close" );
34 },
35 Cancel: function() {
36 $( this ).dialog( "close" );
37 }
38 }
39 });
40 });*/
41
42
43 });
44
45 function menu_ShowOptions () {
46 chrome.runtime.openOptionsPage();
47 }
48
49 function menu_ShowAdvancedView() {
50 chrome.tabs.create({url: "/advanced.html"});
51 }
52
53 function menu_ClearSelection () {
54 var selectedCheckboxes = $("input:checked");
55 for (var i = 0; i < selectedCheckboxes.length; i++) {
56 var checkbox = selectedCheckboxes[i];
57 if (checkbox.type == 'checkbox') {
58 checkbox.checked = false;
59 }
60 }
61
62 updateRestoreSelectedDiv();
63 }
64
65 function menu_RestoreSelected_Real() {
66 var selectedCheckboxes = $("input:checked");
67
68 var restoreToMultipleWindows = $('#restoreSelectedRadioMultipleWindows').is(':checked');
69
70 var allUrls = [];
71
72 var windows = {};
73 var windowsKeys = [];
74
75 for (var i = 0; i < selectedCheckboxes.length; i++) {
76 var checkbox = selectedCheckboxes[i];
77 //if (!checkbox.hasAttribute('tbrBackupName') || !checkbox.hasAttribute('tbrWindowIndex')) {
78 if (checkbox.tbrBackupName === undefined ||
79 checkbox.tbrWindowIndex === undefined ||
80 checkbox.tbrTabUrl === undefined) {
81 continue;
82 }
83 //checkbox.tbrBackupName
84 //checkbox.tbrWindowIndex
85 console.log("Restoring " + checkbox.tbrBackupName + " --> " + checkbox.tbrWindowIndex);
86
87 var tabUrl = checkbox.tbrTabUrl;
88
89 var windowIdx = checkbox.tbrWindowIndex;
90 var bkpName = checkbox.tbrBackupName;
91 var key = bkpName + "_" + windowIdx;
92
93 if (!(key in windows)) {
94 windows[key] = [];
95 windowsKeys.push(key);
96 }
97
98 windows[key].push(tabUrl);
99
100 allUrls.push(checkbox.tbrTabUrl);
101 }
102
103 if (restoreToMultipleWindows) {
104 for (var i = 0; i < windowsKeys.length; i++) {
105 var key = windowsKeys[i];
106 var urls = windows[key];
107
108 var windowProperties = {
109 url: urls
110 };
111
112 // Create a new Window
113 chrome.windows.create(windowProperties, function(createdWindow) {
114
115 });
116 }
117 } else {
118 var windowProperties = {
119 url: allUrls
120 };
121
122 // Create a new Window
123 chrome.windows.create(windowProperties, function(createdWindow) {
124
125 });
126 }
127 }
128
129 function menu_RestoreSelected() {
130 bootbox.confirm("Restore selection?", function(confirmed) {
131 if (confirmed) {
132 menu_RestoreSelected_Real();
133 }
134 });
135
136
137
138 }
139
140 function updateRestoreSelectedDiv() {
141 var selectedCheckboxes = $("input:checked");
142
143 var numSelectedTabs = 0;
144 var numSelectedWindows = 0;
145 for (var i = 0; i < selectedCheckboxes.length; i++) {
146 var checkbox = selectedCheckboxes[i];
147
148 if (checkbox.tbrIsWindow !== undefined) {
149 if (checkbox.tbrIsWindow) {
150 numSelectedWindows++;
151 }
152 }
153
154 if (checkbox.tbrBackupName === undefined ||
155 checkbox.tbrWindowIndex === undefined ||
156 checkbox.tbrTabUrl === undefined) {
157 continue;
158 }
159
160
161
162 // find the first selected tab checkbox
163 numSelectedTabs++;
164 }
165
166 $('#restoreSelectedInfoNumTabs').html(numSelectedTabs);
167
168 var restoreDiv = $('#floatingRightDiv');
169
170 if (numSelectedTabs > 0) {
171
172
173 if (!restoreDiv.is(':visible')) {
174 restoreDiv.fadeIn(600);
175 }
176
177
178
179 } else {
180 if (restoreDiv.is(':visible')) {
181 restoreDiv.fadeOut(600);
182 }
183 }
184 }
185
186 function menu_ShowOlderBackups () {
187 // save current scrollbar position
188 //var scrollPosition = $(document).scrollTop();
189
190 // Re-initialize backups list
191 //initBackupsList(true /*showAll*/, function () {
192 // Update the scrollbar position to the saved one
193 // $(document).scrollTop(scrollPosition);
194 //});
195
196
197
198 var oldestVisibleBackupItem = $(".backupItem:last");
199 var oldestVisibleBackupItemId = oldestVisibleBackupItem.attr('id');
200 // the id is in the form 'div_' + backupName
201 var oldestVisibleBackupName = oldestVisibleBackupItemId.substring(4);
202
203
204 var backupsDiv = document.getElementById ('backupsDiv');
205
206 chrome.storage.local.get(null, function(items) {
207 var backupsList = [];
208 if(items.backups_list) {
209 backupsList = items.backups_list;
210 }
211
212 var shouldInsert = false;
213
214 for (var i = backupsList.length-1; i >= 0; i--) {
215 var backupName = backupsList[i];
216 var backupObj = items[backupName];
217
218 if (!backupObj) {
219 continue;
220 }
221
222 if (backupObj.isAutomatic === undefined) {
223 backupObj.isAutomatic = true;
224 }
225
226 if (oldestVisibleBackupName == backupName) {
227 // found last visible item, start inserting
228 shouldInsert = true;
229 } else {
230
231 if (shouldInsert) {
232 insertBackupItem(backupName, backupObj, false /*insertAtBeginning*/, false /*doAnimation*/);
233 }
234 }
235 }
236
237 // Hide the "show all" link
238 $("#showOlderBackupsDiv").hide();
239 });
240
241 }
242
243 function insertBackupItem (backupName, backupObj, insertAtBeginning, doAnimation) {
244 var backupsDiv = document.getElementById ('backupsDiv');
245
246 var restoreButtonId = 'restoreSelectedBackup_' + backupName;
247 var deleteButtonId = 'deleteSelectedBackup_' + backupName;
248 var divId = 'div_' + backupName;
249
250 var elem = document.createElement("div");
251 if (doAnimation) {
252 // start with hidden element (only if we are doing the animation later)
253 elem.style.cssText = 'display: none';
254 }
255
256 var backupTitleDivId = 'backup_title_' + backupName;
257 var backupItemClickId = 'backup_click_' + backupName;
258
259 elem.id = divId;
260 elem.className = 'backupItem';
261 elem.innerHTML = '<div class="backupItemWrapper" id="' + backupTitleDivId + '">' +
262 '<div class="backupItemContent" id="' + backupItemClickId + '">' +
263 '<div class="backupItemTitle">' + backupName + "</div>" +
264 '<div class="backupItemDetails">' +
265 'Nr. Windows:<span class="backupItemDetailsNr">' + backupObj.windows.length + '</span><br />' +
266 'Nr. Tabs:<span class="backupItemDetailsNr">' + backupObj.totNumTabs + '</span>' +
267 '</div>' +
268
269 // '<div class="backupItemToolbar">' +
270 // '<a id="' + restoreButtonId + '"><img src="icon_48.png" title="Open Windows & Tabs" style="border: 0; width: 24px; height: 24px" /></a>' +
271 // '<a id="' + deleteButtonId + '"><img src="trash_48.png" title="Delete Backup" style="border: 0; width: 22px; height: 22px" /></a>' +
272 // '</div>' +
273
274 '<div class="backupItemFooter">' +
275 (backupObj.isAutomatic ? '<span class="backupItemFooterAutoBackup">AUTO BACKUP</span>' :
276 '<span class="backupItemFooterManualBackup">MANUAL BACKUP</span>') +
277 '</div>' +
278 '</div>' +
279 '</div>';
280
281 //elem.innerHTML += "# Windows: " +
282 //backupsDiv.appendChild(elem);
283
284 //var restoreFuncHandler = (function(backupName) {
285 // return function(event) {
286 // bootbox.confirm("Open Windows & Tabs of backup '" + backupName + "'?", function(confirmed) {
287 // if (confirmed) {
288 // chrome.extension.getBackgroundPage().restoreNow(backupName);
289 // }
290 // });
291
292 /*if (!confirm("Open Windows & Tabs of backup '" + backupName + "'?")) {
293 return;
294 }*/
295
296
297 // };
298 //})(backupName);
299
300 if (insertAtBeginning && backupsDiv.childNodes.length > 0) {
301 // some items already exist
302 var firstNode = backupsDiv.childNodes[0];
303 backupsDiv.insertBefore(elem, firstNode);
304 } else {
305 backupsDiv.appendChild(elem);
306 }
307
308 $(jq(backupItemClickId)).click(function() {
309 var restoreDivId = "restoreDiv_" + backupName;
310 var restoreDiv = $(jq(restoreDivId));
311 if (restoreDiv.length == 0) {
312 // element never created, create it
313 showAdvancedRestoreFor(backupName);
314 } else {
315 if (restoreDiv.is(':visible')) {
316 //restoreDiv.hide();
317 restoreDiv.slideUp();
318 //$('#restoreSelectedDiv').slideUp();
319 } else {
320 restoreDiv.slideDown();
321 //$('#restoreSelectedDiv').slideDown();
322 }
323 //restoreDiv.remove();
324
325 }
326
327
328 });
329
330 //document.getElementById(restoreButtonId).addEventListener('click', restoreFuncHandler);
331 //document.getElementById(deleteButtonId).addEventListener('click', deleteFuncHandler);
332
333 if (doAnimation) {
334 var divIdJQ = jq(divId);
335 $(divIdJQ).slideDown(1000);
336 }
337 /*obj.animate({ height: 1, opacity: 1 }, {
338 duration: 1000,
339 complete: function(){obj.css('display', 'block');}
340 });*/
341
342 //obj.fadeIn(2000);
343 //obj.slideDown();
344
345 //$(divId).display = 'none';
346 //$(divId).slideUp();
347 //$(divId).fadeOut(1000);
348 /*var bkp = $("backupsDiv");
349 bkp.remove();
350 var div = $(divId);
351 var a = 0;*/
352 /*setTimeout( function() {
353 var obj = $("#" + divId);
354 obj.fadeIn();
355
356 }, 1000 );*/
357 }
358
359 function jq(myid) {
360 return '#' + myid.replace(/(:|\.| )/g,'\\$1');
361 }
362
363 function addClickListenerForWindowTitle (windowTitleDiv, tabsDivId) {
364 windowTitleDiv.addEventListener('click', function(e) {
365 var clickedElem = e.target;
366 if (clickedElem) {
367 if (clickedElem.className.indexOf("parentIgnoreClick") != -1) {
368 // ignore click
369 return;
370 }
371 }
372 $(jq(tabsDivId)).slideToggle();
373 });
374 }
375
376 function addClickListenerForWindowCheckbox(checkboxWindowElem, windowTabs, backupName, i) {
377 checkboxWindowElem.addEventListener('click', function(e) {
378 var isChecked = checkboxWindowElem.checked;
379
380 for (var j = 0; j < windowTabs.length; j++) {
381
382 var checkboxId = 'checkbox_tab_' + backupName + '_' + i + '_' + j;
383 $(jq(checkboxId)).prop('checked', isChecked);
384 //var el = document.getElementById(checkboxId);
385 //document.getElementById(checkboxId).change()
386 //document.getElementById(checkboxId).checked = isChecked;
387 }
388
389 updateRestoreSelectedDiv();
390 });
391 }
392
393
394
395 function showAdvancedRestoreFor (backupName) {
396 chrome.storage.local.get(backupName, function(items) {
397 if(!items[backupName]) {
398 alert("An error occured. Please reload the page.");
399 return;
400 }
401
402 var backupTitleDivId = 'backup_title_' + backupName;
403
404 var elem = document.createElement("div");
405 var divId = "restoreDiv_" + backupName;
406
407 elem.id = divId;
408 elem.className = 'restoreDiv';
409 elem.innerHTML = '';
410
411 var expandCollapseDiv = document.createElement('div');
412 expandCollapseDiv.className = "restoreDivCollapseExpand";
413 //expandCollapseDiv.innerHTML = 'Collapse all';
414 var collapseAElem = document.createElement('a');
415 collapseAElem.innerHTML = "Collapse all";
416 var expandAElem = document.createElement('a');
417 expandAElem.innerHTML = "Expand all";
418
419
420 expandCollapseDiv.appendChild(collapseAElem);
421 expandCollapseDiv.appendChild(document.createTextNode(' / '));
422 expandCollapseDiv.appendChild(expandAElem);
423
424 elem.appendChild(expandCollapseDiv);
425
426 var allTabsDivsIds = [];
427
428 var fullBackup = items[backupName];
429
430 for(var i = 0; i < fullBackup.windows.length; i++) {
431 var window = fullBackup.windows[i];
432 var windowTabs = window.tabs;
433
434 var windowTitleDiv = document.createElement('div');
435
436 //windowTitleDiv.innerHTML = "<span>Window " + (i+1) + '</span>' +
437 // '<span style="float: right; font-size: 12px;">Nr. Tabs: ' + windowTabs.length + '</span>';
438
439 windowTitleDiv.className = 'windowTitleDiv';
440
441 var tabsDiv = document.createElement('div');
442 tabsDiv.id = 'tabsDiv_' + backupName + '_' + i;
443 tabsDiv.className = 'tabsDiv';
444 tabsDiv.hidden = true;
445
446 allTabsDivsIds.push(tabsDiv.id);
447
448 addClickListenerForWindowTitle(windowTitleDiv, tabsDiv.id);
449
450 var checkboxWindowId = 'checkbox_window_' + backupName + '_' + i;
451
452 var checkboxWindowElem = document.createElement('input');
453 checkboxWindowElem.type = "checkbox";
454 checkboxWindowElem.id = checkboxWindowId;
455 checkboxWindowElem.className = "regular-checkbox parentIgnoreClick";
456
457 checkboxWindowElem.tbrIsWindow = true;
458
459 var checkboxWindowLabelElem = document.createElement('label')
460 checkboxWindowLabelElem.className = "parentIgnoreClick";
461 checkboxWindowLabelElem.htmlFor = checkboxWindowId;
462 checkboxWindowLabelElem.style.cssText = 'margin-bottom: -4px; margin-right: 8px;';
463
464 addClickListenerForWindowCheckbox(checkboxWindowElem, windowTabs, backupName, i);
465
466
467 var windowTitleSpan = document.createElement('span');
468 windowTitleSpan.innerHTML =
469 `<span style="font-weight: bold">Window ${i + 1} (${window.state}) ${window.width}×${window.height} @ ${window.top}×${window.left}</span>` +
470 `<span style="float: right; font-size: 11px;">Tabs: ${windowTabs.length}</span>`;
471 //windowTitleSpan.innerHTML = '<span>Window ' + (i+1) + '</span>' +
472 // '<br /><span style="font-size: 10px;">Nr. Tabs: ' + windowTabs.length + '</span>';
473
474 windowTitleDiv.appendChild(checkboxWindowElem);
475 windowTitleDiv.appendChild(checkboxWindowLabelElem);
476 windowTitleDiv.appendChild(windowTitleSpan);
477
478
479
480
481 for (var j = 0; j < windowTabs.length; j++) {
482 var tab = windowTabs[j];
483 var tabTitle = tab.title;
484 var tabUrl = tab.url;
485
486 var checkboxId = 'checkbox_tab_' + backupName + '_' + i + '_' + j;
487
488 var tabElem = document.createElement('div');
489 tabElem.style.cssText = "position: relative";
490
491 var checkboxTabElem = document.createElement('input');
492 checkboxTabElem.type = "checkbox";
493 //checkboxTabElem.name = "name";
494 //checkboxTabElem.value = "value";
495 checkboxTabElem.id = checkboxId;
496 checkboxTabElem.className = "regular-checkbox";
497
498 // custom attributes
499 checkboxTabElem.tbrBackupName = backupName;
500 checkboxTabElem.tbrWindowIndex = i;
501 checkboxTabElem.tbrTabUrl = tabUrl;
502
503 var checkboxTabLabelElem = document.createElement('label')
504 checkboxTabLabelElem.htmlFor = checkboxId;
505
506 var tabSpanElem = document.createElement('span');
507 tabSpanElem.className = "restoreTabSpan";
508 var title = tabTitle === '' ? tabUrl : tabTitle;
509 tabSpanElem.innerHTML =
510 (tab.pinned ? '📌 ' : '') +
511 `<a href="${tabUrl}" target="_blank">${title}</a>`;
512
513 tabElem.appendChild(checkboxTabElem);
514 tabElem.appendChild(checkboxTabLabelElem);
515 tabElem.appendChild(tabSpanElem);
516
517 tabsDiv.appendChild(tabElem);
518
519 checkboxTabElem.addEventListener('change', function (e) {
520 console.log('cb changed - ' + e.target.id + ' : ' + e.target.checked);
521
522 updateRestoreSelectedDiv();
523 });
524 //addClickListenerFor(checkboxId
525 //checkboxTabElem.addEventListener('click', function() {
526 //});
527
528 //var checkboxId = 'checkbox_tab_' + backupName + '_' + i + '_' + j;
529 //var checkboxHtml = '<input type="checkbox" id="' + checkboxId + '" class="regular-checkbox" /><label for="' + checkboxId + '" class="labelCheckbox"></label>'
530 //tabsDiv.innerHTML += '<div style="position: relative">' + checkboxHtml + '<span class="restoreTabSpan"><a href="' + tabUrl + '" target="_blank">' + tabTitle + '</a></span>' + '<br />' +
531 // '</div>';
532 }
533
534
535 elem.appendChild(windowTitleDiv);
536 elem.appendChild(tabsDiv);
537
538 }
539
540
541 collapseAElem.addEventListener('click', function () {
542 for (var i = 0; i < allTabsDivsIds.length; i++) {
543 $(jq(allTabsDivsIds[i])).slideUp();
544 }
545 });
546
547 expandAElem.addEventListener('click', function () {
548 for (var i = 0; i < allTabsDivsIds.length; i++) {
549 $(jq(allTabsDivsIds[i])).slideDown();
550 }
551 });
552
553 var backupTitleDiv = document.getElementById(backupTitleDivId);
554 backupTitleDiv.appendChild(elem);
555 $(jq(divId)).hide();
556 $(jq(divId)).slideDown();
557 //$('#restoreSelectedDiv').slideDown();
558
559 //$('#restoreSelectedDiv').fadeIn(1000);
560
561 });
562 }
563
564 function removeBackupItemDiv (backupName) {
565 var divId = 'div_' + backupName;
566 var divIdClean = jq(divId);
567 var obj = $(divIdClean);
568 //obj.fadeOut();
569 //obj.slideUp();
570 obj.animate({ height: 0, opacity: 0 }, {
571 duration: 1000,
572 complete: function(){obj.remove();}
573 });
574
575 var backupItemDiv = document.getElementById (divId);
576 if (backupItemDiv.parentNode) {
577 // backupItemDiv.parentNode.removeChild(backupItemDiv);
578 }
579 }
580
581 function initBackupsList(showAll, callback) {
582 var backupsDiv = document.getElementById ('backupsDiv');
583 //var node = backupsDiv.childNodes[0];
584 backupsDiv.innerHTML = '';
585 //backupsDiv.style = 'display: none';
586 //$("#backupsDiv").html("");
587 /*while (backupsDiv.hasChildNodes()) {
588 backupsDiv.removeChild(backupsDiv.lastChild);
589 }*/
590
591 $("#showOlderBackupsDiv").hide();
592
593 if (!showAll) {
594 $("#backupsDiv").hide();
595 }
596
597 chrome.storage.local.get(null, function(items) {
598 var backupsList = [];
599 if(items.backups_list) {
600 backupsList = items.backups_list;
601 }
602
603 var numInsertedItems = 0;
604 for (var i = backupsList.length-1; i >= 0; i--) {
605 //for (var i = 0; i < backupsList.length; i++) {
606 var backupName = backupsList[i];
607 var backupObj = items[backupName];
608
609 if (!backupObj) {
610 continue;
611 }
612
613 if (backupObj.isAutomatic === undefined) {
614 backupObj.isAutomatic = true;
615 }
616
617 if (!showAll) {
618 if (numInsertedItems >= 10) {
619 $("#showOlderBackupsDiv").show();
620 break;
621 }
622 }
623
624 insertBackupItem(backupName, backupObj, false /*insertAtBeginning*/, false /*doAnimation*/);
625
626 numInsertedItems++;
627 }
628
629 if (!showAll) {
630 $("#backupsDiv").slideDown();
631 }
632
633 if (callback) {
634 callback();
635 }
636
637 });
638
639
640 }
641
642 var lastTimeBackupNowClicked = 0;
643
644 function menu_backupNow() {
645 // Ignore clicks if less than 1 second has passed since last click (avoids rapid useless backups)
646 if (lastTimeBackupNowClicked != 0) {
647 var diffTime = Math.abs(new Date().getTime() - lastTimeBackupNowClicked);
648 if (diffTime < 1000) {
649 return;
650 }
651 }
652
653 lastTimeBackupNowClicked = new Date().getTime();
654
655 chrome.runtime.sendMessage({
656 action: 'backupNowManual',
657 }, function({success, backupName, backupObj}) {
658 if (success) {
659 //updateBackupsList();
660 insertBackupItem (backupName, backupObj, true /*insertAtBeginning*/, true /*doAnimation*/);
661
662
663 //bootbox.alert("Backup successfully created!");
664 } else {
665 alert('An error occured while creating the backup..');
666 }
667 });
668
669 }
670
671 function menu_restoreNow() {
672 chrome.runtime.sendMessage({
673 action: 'restoreNow',
674 args: ['full_backup'],
675 });
676 }
677
678 /**
679 * Callback from other pages (like the background).
680 */
681 chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
682 console.log(`Got message from ${sender.id}: action=${request.action}`, request);
683
684 let asyncResponse = false;
685 switch (request?.action) {
686 case 'insertBackupItem':
687 insertBackupItem(...request.args);
688 break;
689
690 case 'removeBackupItemDiv':
691 removeBackupItemDiv(...request.args);
692 break;
693 }
694 return asyncResponse;
695 });