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.
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
);
15 document
.getElementById("restoreSelectedRadioSingleWindowSpanLabel").addEventListener('click', function() {
16 $('#restoreSelectedRadioSingleWindow').prop('checked', true);
19 document
.getElementById("restoreSelectedRadioMultipleWindowsSpanLabel").addEventListener('click', function() {
20 $('#restoreSelectedRadioMultipleWindows').prop('checked', true);
24 initBackupsList (false /*showAll*/);
27 $( "#dialog-confirm" ).dialog({
32 "Delete all items": function() {
33 $( this ).dialog( "close" );
36 $( this ).dialog( "close" );
45 function menu_ShowOptions () {
46 chrome
.runtime
.openOptionsPage();
49 function menu_ShowAdvancedView() {
50 chrome
.tabs
.create({url
: "/advanced.html"});
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;
62 updateRestoreSelectedDiv();
65 function menu_RestoreSelected_Real() {
66 var selectedCheckboxes
= $("input:checked");
68 var restoreToMultipleWindows
= $('#restoreSelectedRadioMultipleWindows').is(':checked');
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) {
83 //checkbox.tbrBackupName
84 //checkbox.tbrWindowIndex
85 console
.log("Restoring " + checkbox
.tbrBackupName
+ " --> " + checkbox
.tbrWindowIndex
);
87 var tabUrl
= checkbox
.tbrTabUrl
;
89 var windowIdx
= checkbox
.tbrWindowIndex
;
90 var bkpName
= checkbox
.tbrBackupName
;
91 var key
= bkpName
+ "_" + windowIdx
;
93 if (!(key
in windows
)) {
95 windowsKeys
.push(key
);
98 windows
[key
].push(tabUrl
);
100 allUrls
.push(checkbox
.tbrTabUrl
);
103 if (restoreToMultipleWindows
) {
104 for (var i
= 0; i
< windowsKeys
.length
; i
++) {
105 var key
= windowsKeys
[i
];
106 var urls
= windows
[key
];
108 var windowProperties
= {
112 // Create a new Window
113 chrome
.windows
.create(windowProperties
, function(createdWindow
) {
118 var windowProperties
= {
122 // Create a new Window
123 chrome
.windows
.create(windowProperties
, function(createdWindow
) {
129 function menu_RestoreSelected() {
130 bootbox
.confirm("Restore selection?", function(confirmed
) {
132 menu_RestoreSelected_Real();
140 function updateRestoreSelectedDiv() {
141 var selectedCheckboxes
= $("input:checked");
143 var numSelectedTabs
= 0;
144 var numSelectedWindows
= 0;
145 for (var i
= 0; i
< selectedCheckboxes
.length
; i
++) {
146 var checkbox
= selectedCheckboxes
[i
];
148 if (checkbox
.tbrIsWindow
!== undefined) {
149 if (checkbox
.tbrIsWindow
) {
150 numSelectedWindows
++;
154 if (checkbox
.tbrBackupName
=== undefined ||
155 checkbox
.tbrWindowIndex
=== undefined ||
156 checkbox
.tbrTabUrl
=== undefined) {
162 // find the first selected tab checkbox
166 $('#restoreSelectedInfoNumTabs').html(numSelectedTabs
);
168 var restoreDiv
= $('#floatingRightDiv');
170 if (numSelectedTabs
> 0) {
173 if (!restoreDiv
.is(':visible')) {
174 restoreDiv
.fadeIn(600);
180 if (restoreDiv
.is(':visible')) {
181 restoreDiv
.fadeOut(600);
186 function menu_ShowOlderBackups () {
187 // save current scrollbar position
188 //var scrollPosition = $(document).scrollTop();
190 // Re-initialize backups list
191 //initBackupsList(true /*showAll*/, function () {
192 // Update the scrollbar position to the saved one
193 // $(document).scrollTop(scrollPosition);
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);
204 var backupsDiv
= document
.getElementById ('backupsDiv');
206 chrome
.storage
.local
.get(null, function(items
) {
207 var backupsList
= [];
208 if(items
.backups_list
) {
209 backupsList
= items
.backups_list
;
212 var shouldInsert
= false;
214 for (var i
= backupsList
.length
-1; i
>= 0; i
--) {
215 var backupName
= backupsList
[i
];
216 var backupObj
= items
[backupName
];
222 if (backupObj
.isAutomatic
=== undefined) {
223 backupObj
.isAutomatic
= true;
226 if (oldestVisibleBackupName
== backupName
) {
227 // found last visible item, start inserting
232 insertBackupItem(backupName
, backupObj
, false /*insertAtBeginning*/, false /*doAnimation*/);
237 // Hide the "show all" link
238 $("#showOlderBackupsDiv").hide();
243 function insertBackupItem (backupName
, backupObj
, insertAtBeginning
, doAnimation
) {
244 var backupsDiv
= document
.getElementById ('backupsDiv');
246 var restoreButtonId
= 'restoreSelectedBackup_' + backupName
;
247 var deleteButtonId
= 'deleteSelectedBackup_' + backupName
;
248 var divId
= 'div_' + backupName
;
250 var elem
= document
.createElement("div");
252 // start with hidden element (only if we are doing the animation later)
253 elem
.style
.cssText
= 'display: none';
256 var backupTitleDivId
= 'backup_title_' + backupName
;
257 var backupItemClickId
= 'backup_click_' + backupName
;
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>' +
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>' +
274 '<div class="backupItemFooter">' +
275 (backupObj
.isAutomatic
? '<span class="backupItemFooterAutoBackup">AUTO BACKUP</span>' :
276 '<span class="backupItemFooterManualBackup">MANUAL BACKUP</span>') +
281 //elem.innerHTML += "# Windows: " +
282 //backupsDiv.appendChild(elem);
284 //var restoreFuncHandler = (function(backupName) {
285 // return function(event) {
286 // bootbox.confirm("Open Windows & Tabs of backup '" + backupName + "'?", function(confirmed) {
288 // chrome.extension.getBackgroundPage().restoreNow(backupName);
292 /*if (!confirm("Open Windows & Tabs of backup '" + backupName + "'?")) {
300 if (insertAtBeginning
&& backupsDiv
.childNodes
.length
> 0) {
301 // some items already exist
302 var firstNode
= backupsDiv
.childNodes
[0];
303 backupsDiv
.insertBefore(elem
, firstNode
);
305 backupsDiv
.appendChild(elem
);
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
);
315 if (restoreDiv
.is(':visible')) {
317 restoreDiv
.slideUp();
318 //$('#restoreSelectedDiv').slideUp();
320 restoreDiv
.slideDown();
321 //$('#restoreSelectedDiv').slideDown();
323 //restoreDiv.remove();
330 //document.getElementById(restoreButtonId).addEventListener('click', restoreFuncHandler);
331 //document.getElementById(deleteButtonId).addEventListener('click', deleteFuncHandler);
334 var divIdJQ
= jq(divId
);
335 $(divIdJQ
).slideDown(1000);
337 /*obj.animate({ height: 1, opacity: 1 }, {
339 complete: function(){obj.css('display', 'block');}
345 //$(divId).display = 'none';
346 //$(divId).slideUp();
347 //$(divId).fadeOut(1000);
348 /*var bkp = $("backupsDiv");
352 /*setTimeout( function() {
353 var obj = $("#" + divId);
360 return '#' + myid
.replace(/(:|\.| )/g,'\\$1');
363 function addClickListenerForWindowTitle (windowTitleDiv
, tabsDivId
) {
364 windowTitleDiv
.addEventListener('click', function(e
) {
365 var clickedElem
= e
.target
;
367 if (clickedElem
.className
.indexOf("parentIgnoreClick") != -1) {
372 $(jq(tabsDivId
)).slideToggle();
376 function addClickListenerForWindowCheckbox(checkboxWindowElem
, windowTabs
, backupName
, i
) {
377 checkboxWindowElem
.addEventListener('click', function(e
) {
378 var isChecked
= checkboxWindowElem
.checked
;
380 for (var j
= 0; j
< windowTabs
.length
; j
++) {
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;
389 updateRestoreSelectedDiv();
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.");
402 var backupTitleDivId
= 'backup_title_' + backupName
;
404 var elem
= document
.createElement("div");
405 var divId
= "restoreDiv_" + backupName
;
408 elem
.className
= 'restoreDiv';
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";
420 expandCollapseDiv
.appendChild(collapseAElem
);
421 expandCollapseDiv
.appendChild(document
.createTextNode(' / '));
422 expandCollapseDiv
.appendChild(expandAElem
);
424 elem
.appendChild(expandCollapseDiv
);
426 var allTabsDivsIds
= [];
428 var fullBackup
= items
[backupName
];
430 for(var i
= 0; i
< fullBackup
.windows
.length
; i
++) {
431 var window
= fullBackup
.windows
[i
];
432 var windowTabs
= window
.tabs
;
434 var windowTitleDiv
= document
.createElement('div');
436 //windowTitleDiv.innerHTML = "<span>Window " + (i+1) + '</span>' +
437 // '<span style="float: right; font-size: 12px;">Nr. Tabs: ' + windowTabs.length + '</span>';
439 windowTitleDiv
.className
= 'windowTitleDiv';
441 var tabsDiv
= document
.createElement('div');
442 tabsDiv
.id
= 'tabsDiv_' + backupName
+ '_' + i
;
443 tabsDiv
.className
= 'tabsDiv';
444 tabsDiv
.hidden
= true;
446 allTabsDivsIds
.push(tabsDiv
.id
);
448 addClickListenerForWindowTitle(windowTitleDiv
, tabsDiv
.id
);
450 var checkboxWindowId
= 'checkbox_window_' + backupName
+ '_' + i
;
452 var checkboxWindowElem
= document
.createElement('input');
453 checkboxWindowElem
.type
= "checkbox";
454 checkboxWindowElem
.id
= checkboxWindowId
;
455 checkboxWindowElem
.className
= "regular-checkbox parentIgnoreClick";
457 checkboxWindowElem
.tbrIsWindow
= true;
459 var checkboxWindowLabelElem
= document
.createElement('label')
460 checkboxWindowLabelElem
.className
= "parentIgnoreClick";
461 checkboxWindowLabelElem
.htmlFor
= checkboxWindowId
;
462 checkboxWindowLabelElem
.style
.cssText
= 'margin-bottom: -4px; margin-right: 8px;';
464 addClickListenerForWindowCheckbox(checkboxWindowElem
, windowTabs
, backupName
, i
);
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>';
474 windowTitleDiv
.appendChild(checkboxWindowElem
);
475 windowTitleDiv
.appendChild(checkboxWindowLabelElem
);
476 windowTitleDiv
.appendChild(windowTitleSpan
);
481 for (var j
= 0; j
< windowTabs
.length
; j
++) {
482 var tab
= windowTabs
[j
];
483 var tabTitle
= tab
.title
;
484 var tabUrl
= tab
.url
;
486 var checkboxId
= 'checkbox_tab_' + backupName
+ '_' + i
+ '_' + j
;
488 var tabElem
= document
.createElement('div');
489 tabElem
.style
.cssText
= "position: relative";
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";
499 checkboxTabElem
.tbrBackupName
= backupName
;
500 checkboxTabElem
.tbrWindowIndex
= i
;
501 checkboxTabElem
.tbrTabUrl
= tabUrl
;
503 var checkboxTabLabelElem
= document
.createElement('label')
504 checkboxTabLabelElem
.htmlFor
= checkboxId
;
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>`;
513 tabElem
.appendChild(checkboxTabElem
);
514 tabElem
.appendChild(checkboxTabLabelElem
);
515 tabElem
.appendChild(tabSpanElem
);
517 tabsDiv
.appendChild(tabElem
);
519 checkboxTabElem
.addEventListener('change', function (e
) {
520 console
.log('cb changed - ' + e
.target
.id
+ ' : ' + e
.target
.checked
);
522 updateRestoreSelectedDiv();
524 //addClickListenerFor(checkboxId
525 //checkboxTabElem.addEventListener('click', function() {
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 />' +
535 elem
.appendChild(windowTitleDiv
);
536 elem
.appendChild(tabsDiv
);
541 collapseAElem
.addEventListener('click', function () {
542 for (var i
= 0; i
< allTabsDivsIds
.length
; i
++) {
543 $(jq(allTabsDivsIds
[i
])).slideUp();
547 expandAElem
.addEventListener('click', function () {
548 for (var i
= 0; i
< allTabsDivsIds
.length
; i
++) {
549 $(jq(allTabsDivsIds
[i
])).slideDown();
553 var backupTitleDiv
= document
.getElementById(backupTitleDivId
);
554 backupTitleDiv
.appendChild(elem
);
556 $(jq(divId
)).slideDown();
557 //$('#restoreSelectedDiv').slideDown();
559 //$('#restoreSelectedDiv').fadeIn(1000);
564 function removeBackupItemDiv (backupName
) {
565 var divId
= 'div_' + backupName
;
566 var divIdClean
= jq(divId
);
567 var obj
= $(divIdClean
);
570 obj
.animate({ height
: 0, opacity
: 0 }, {
572 complete: function(){obj
.remove();}
575 var backupItemDiv
= document
.getElementById (divId
);
576 if (backupItemDiv
.parentNode
) {
577 // backupItemDiv.parentNode.removeChild(backupItemDiv);
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);
591 $("#showOlderBackupsDiv").hide();
594 $("#backupsDiv").hide();
597 chrome
.storage
.local
.get(null, function(items
) {
598 var backupsList
= [];
599 if(items
.backups_list
) {
600 backupsList
= items
.backups_list
;
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
];
613 if (backupObj
.isAutomatic
=== undefined) {
614 backupObj
.isAutomatic
= true;
618 if (numInsertedItems
>= 10) {
619 $("#showOlderBackupsDiv").show();
624 insertBackupItem(backupName
, backupObj
, false /*insertAtBeginning*/, false /*doAnimation*/);
630 $("#backupsDiv").slideDown();
642 var lastTimeBackupNowClicked
= 0;
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) {
653 lastTimeBackupNowClicked
= new Date().getTime();
655 chrome
.runtime
.sendMessage({
656 action
: 'backupNowManual',
657 }, function({success
, backupName
, backupObj
}) {
659 //updateBackupsList();
660 insertBackupItem (backupName
, backupObj
, true /*insertAtBeginning*/, true /*doAnimation*/);
663 //bootbox.alert("Backup successfully created!");
665 alert('An error occured while creating the backup..');
671 function menu_restoreNow() {
672 chrome
.runtime
.sendMessage({
673 action
: 'restoreNow',
674 args
: ['full_backup'],
679 * Callback from other pages (like the background).
681 chrome
.runtime
.onMessage
.addListener(function(request
, sender
, sendResponse
) {
682 console
.log(`Got message from ${sender.id}: action=${request.action}`, request
);
684 let asyncResponse
= false;
685 switch (request
?.action
) {
686 case 'insertBackupItem':
687 insertBackupItem(...request
.args
);
690 case 'removeBackupItemDiv':
691 removeBackupItemDiv(...request
.args
);
694 return asyncResponse
;