From c39111c534e98874f850eb780c84eb1d6305622a Mon Sep 17 00:00:00 2001 From: Mori Bellamy Date: Sat, 16 May 2020 13:41:52 -0700 Subject: [PATCH] Cleanup types. Properly vendor pure.css. --- @types/graytabby.d.ts | 20 - package-lock.json | 20 +- package.json | 4 +- src/app.html | 13 +- src/app.ts | 4 +- src/archive.ts | 16 +- src/globals.ts | 2 +- src/scss/app.scss | 57 +- src/scss/modal.scss | 24 +- src/scss/pure.css | 1380 ----------------------------------------- src/tabs.ts | 24 +- src/ui.ts | 100 ++- src/utils.ts | 9 + tests/archive.test.ts | 44 +- tests/options.test.ts | 4 +- tests/utils.ts | 9 +- 16 files changed, 232 insertions(+), 1498 deletions(-) delete mode 100644 @types/graytabby.d.ts delete mode 100644 src/scss/pure.css diff --git a/@types/graytabby.d.ts b/@types/graytabby.d.ts deleted file mode 100644 index d7a1330..0000000 --- a/@types/graytabby.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Tabs as WebextTabs } from 'webextension-polyfill-ts/dist/generated/tabs'; -import { Menus } from 'webextension-polyfill-ts/dist/generated/menus'; -import { Options } from '../src/options'; - -export type BrowserTab = WebextTabs.Tab; -export type OnClickData = Menus.OnClickData; - -// export type GrayTab = Pick; -export type GrayTab = Pick & { key: number }; - -export type GrayTabGroup = { - tabs: GrayTab[]; - date: number; -}; - -declare global { - interface Window { - getOptions: () => Promise; - } -} diff --git a/package-lock.json b/package-lock.json index 0db6aaa..1b4a110 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1994,8 +1994,7 @@ "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" }, "bcrypt-pbkdf": { "version": "1.0.2", @@ -2371,7 +2370,6 @@ "version": "5.6.0", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "dev": true, "requires": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" @@ -6376,8 +6374,7 @@ "ieee754": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, "iferr": { "version": "0.1.5", @@ -8594,6 +8591,14 @@ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true }, + "object-sizeof": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-sizeof/-/object-sizeof-1.6.0.tgz", + "integrity": "sha512-Z+suoK94o8WjEMYItJjpPyG6I78STcI1CxM7rjxM8LhbgegAY+jhGSFY0c5Ez20LXgDGtyKzYZNXHpDyr6hj8Q==", + "requires": { + "buffer": "^5.5.0" + } + }, "object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", @@ -9324,6 +9329,11 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "purecss": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/purecss/-/purecss-2.0.3.tgz", + "integrity": "sha512-jK5t8Wdyh1xiskKt9Pk3psruRP7XHcHYfsBkA9tu+CGGxaC3goTyd9EFCFyYuhC7AK/68NqDfuOsP8FgQ9vHRg==" + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", diff --git a/package.json b/package.json index fbef269..50a3029 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,8 @@ "webpack-shell-plugin-next": "^1.1.9" }, "dependencies": { - "google-fonts-plugin": "^5.0.2" + "google-fonts-plugin": "^5.0.2", + "object-sizeof": "^1.6.0", + "purecss": "^2.0.3" } } diff --git a/src/app.html b/src/app.html index 9c03831..1cc833e 100644 --- a/src/app.html +++ b/src/app.html @@ -8,13 +8,9 @@ - - - @@ -36,9 +35,7 @@

Welcome to GrayTabby!

- - - + diff --git a/src/app.ts b/src/app.ts index eb3adb6..199474e 100644 --- a/src/app.ts +++ b/src/app.ts @@ -3,10 +3,8 @@ * non-trivial logic here. */ -// Webpack uses MiniCssExtractPlugin when it sees these. +// Webpack uses MiniCssExtractPlugin when it sees this. import './scss/app.scss'; -import './scss/pure.css'; -import './scss/montserrat.css'; import { grayTabby } from './ui'; import { PAGE_LOAD } from './globals'; diff --git a/src/archive.ts b/src/archive.ts index a799f2c..f9c4d91 100644 --- a/src/archive.ts +++ b/src/archive.ts @@ -1,6 +1,13 @@ -import { BrowserTab, OnClickData } from '../@types/graytabby'; -import { PAGE_LOAD, BROWSER, ARCHIVAL } from './globals'; +/** + * Any logic having to do with archival. + */ + +import { Menus } from 'webextension-polyfill-ts/dist/generated/menus'; +import { ARCHIVAL, BROWSER, PAGE_LOAD } from './globals'; import { getOptions } from './options'; +import { BrowserTab } from './tabs'; + +export type OnClickData = Menus.OnClickData; /** * @returns where the user should browse to for the main GrayTabby page. @@ -32,7 +39,7 @@ function shouldJustClose(url: string): boolean { * archived. Second element is tabs that will just be closed and not archived. * None of the GrayTabby tabs are counted in either member. */ -function archivePlan( +export function archivePlan( browserTabs: BrowserTab[], homeURL: string, archiveDupes: boolean, @@ -42,6 +49,7 @@ function archivePlan( const seen: Set = new Set(); for (const tab of browserTabs) { + console.log('aaa', tab.url, tab.pinned); if (tab.url === homeURL || tab.pinned) continue; else if (seen.has(tab.url) && !archiveDupes) tabsToClose.push(tab); else if (shouldJustClose(tab.url)) tabsToClose.push(tab); @@ -50,7 +58,7 @@ function archivePlan( seen.add(tab.url); } } - + console.log(tabsToArchive, tabsToClose); return [tabsToArchive, tabsToClose]; } diff --git a/src/globals.ts b/src/globals.ts index 537caa5..45e63dd 100644 --- a/src/globals.ts +++ b/src/globals.ts @@ -1,6 +1,6 @@ import { browser } from 'webextension-polyfill-ts'; -import { BrowserTab } from '../@types/graytabby'; import { Broker } from './brokers'; +import { BrowserTab } from './tabs'; class Wrapper { wrapped: T; diff --git a/src/scss/app.scss b/src/scss/app.scss index db92955..ea0f10b 100644 --- a/src/scss/app.scss +++ b/src/scss/app.scss @@ -4,34 +4,49 @@ //$font-family-base: "Montserrat", "sans-serif"; // @import "~bootstrap/scss/bootstrap"; -@import 'modal.scss'; +@import "purecss"; +@import "modal.scss"; +@import "montserrat.css"; body { - font-family: 'Montserrat', 'sans-serif'; + font-family: "Montserrat", "sans-serif"; } -ul { - list-style-type: none; - padding-left: 20px; -} - -#app a { - padding-left: 5px; -} +$padding: 10px; -.form-group { - label { - font-weight: bold; +#app { + padding: $padding; + h1 { + margin: 0; + margin-bottom: 0.67em; + } + ul { + list-style-type: none; + padding-left: 20px; } - span { - font-style: italic; + a { + padding-left: 5px; + } + #optionsButton { + @extend .pure-button; + @extend .pure-button-primary; + float: right; + } + #logo { + position: fixed; + right: $padding; + bottom: $padding; + width: 250px; + height: 208px; } } -#logo { - position: fixed; - right: 0px; - bottom: 0px; - width: 250px; - height: 208px; +#optionsModal { + form { + label { + input[type='checkbox'] { + margin-right: 5px; + } + } + } } diff --git a/src/scss/modal.scss b/src/scss/modal.scss index 96b80f4..e16741c 100644 --- a/src/scss/modal.scss +++ b/src/scss/modal.scss @@ -1,4 +1,4 @@ -/* The Modal (background) */ +/* The Modal (background). Cribbed from W3Schools. */ .modal { display: none; /* Hidden by default */ position: fixed; /* Stay in place */ @@ -19,16 +19,16 @@ width: 70%; /* Could be more or less, depending on screen size */ } - .close { - color: #aaa; - float: right; - font-size: 28px; - font-weight: bold; + // .close { + // color: #aaa; + // float: right; + // font-size: 28px; + // font-weight: bold; - :hover :focus { - color: black; - text-decoration: none; - cursor: pointer; - } - } + // :hover :focus { + // color: black; + // text-decoration: none; + // cursor: pointer; + // } + // } } diff --git a/src/scss/pure.css b/src/scss/pure.css deleted file mode 100644 index 6ec9f43..0000000 --- a/src/scss/pure.css +++ /dev/null @@ -1,1380 +0,0 @@ -/*! -Pure v2.0.3 -Copyright 2013 Yahoo! -Licensed under the BSD License. -https://github.com/pure-css/pure/blob/master/LICENSE.md -*/ -/*! -normalize.css v | MIT License | git.io/normalize -Copyright (c) Nicolas Gallagher and Jonathan Neal -*/ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ - -/* Document - ========================================================================== */ - -/** - * 1. Correct the line height in all browsers. - * 2. Prevent adjustments of font size after orientation changes in iOS. - */ - -html { - line-height: 1.15; /* 1 */ - -webkit-text-size-adjust: 100%; /* 2 */ -} - -/* Sections - ========================================================================== */ - -/** - * Remove the margin in all browsers. - */ - -body { - margin: 0; -} - -/** - * Render the `main` element consistently in IE. - */ - -main { - display: block; -} - -/** - * Correct the font size and margin on `h1` elements within `section` and - * `article` contexts in Chrome, Firefox, and Safari. - */ - -h1 { - font-size: 2em; - margin: 0.67em 0; -} - -/* Grouping content - ========================================================================== */ - -/** - * 1. Add the correct box sizing in Firefox. - * 2. Show the overflow in Edge and IE. - */ - -hr { - -webkit-box-sizing: content-box; - box-sizing: content-box; /* 1 */ - height: 0; /* 1 */ - overflow: visible; /* 2 */ -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -pre { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/* Text-level semantics - ========================================================================== */ - -/** - * Remove the gray background on active links in IE 10. - */ - -a { - background-color: transparent; -} - -/** - * 1. Remove the bottom border in Chrome 57- - * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. - */ - -abbr[title] { - border-bottom: none; /* 1 */ - text-decoration: underline; /* 2 */ - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; /* 2 */ -} - -/** - * Add the correct font weight in Chrome, Edge, and Safari. - */ - -b, -strong { - font-weight: bolder; -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -code, -kbd, -samp { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/** - * Add the correct font size in all browsers. - */ - -small { - font-size: 80%; -} - -/** - * Prevent `sub` and `sup` elements from affecting the line height in - * all browsers. - */ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* Embedded content - ========================================================================== */ - -/** - * Remove the border on images inside links in IE 10. - */ - -img { - border-style: none; -} - -/* Forms - ========================================================================== */ - -/** - * 1. Change the font styles in all browsers. - * 2. Remove the margin in Firefox and Safari. - */ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; /* 1 */ - font-size: 100%; /* 1 */ - line-height: 1.15; /* 1 */ - margin: 0; /* 2 */ -} - -/** - * Show the overflow in IE. - * 1. Show the overflow in Edge. - */ - -button, -input { /* 1 */ - overflow: visible; -} - -/** - * Remove the inheritance of text transform in Edge, Firefox, and IE. - * 1. Remove the inheritance of text transform in Firefox. - */ - -button, -select { /* 1 */ - text-transform: none; -} - -/** - * Correct the inability to style clickable types in iOS and Safari. - */ - -button, -[type="button"], -[type="reset"], -[type="submit"] { - -webkit-appearance: button; -} - -/** - * Remove the inner border and padding in Firefox. - */ - -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - border-style: none; - padding: 0; -} - -/** - * Restore the focus styles unset by the previous rule. - */ - -button:-moz-focusring, -[type="button"]:-moz-focusring, -[type="reset"]:-moz-focusring, -[type="submit"]:-moz-focusring { - outline: 1px dotted ButtonText; -} - -/** - * Correct the padding in Firefox. - */ - -fieldset { - padding: 0.35em 0.75em 0.625em; -} - -/** - * 1. Correct the text wrapping in Edge and IE. - * 2. Correct the color inheritance from `fieldset` elements in IE. - * 3. Remove the padding so developers are not caught out when they zero out - * `fieldset` elements in all browsers. - */ - -legend { - -webkit-box-sizing: border-box; - box-sizing: border-box; /* 1 */ - color: inherit; /* 2 */ - display: table; /* 1 */ - max-width: 100%; /* 1 */ - padding: 0; /* 3 */ - white-space: normal; /* 1 */ -} - -/** - * Add the correct vertical alignment in Chrome, Firefox, and Opera. - */ - -progress { - vertical-align: baseline; -} - -/** - * Remove the default vertical scrollbar in IE 10+. - */ - -textarea { - overflow: auto; -} - -/** - * 1. Add the correct box sizing in IE 10. - * 2. Remove the padding in IE 10. - */ - -[type="checkbox"], -[type="radio"] { - -webkit-box-sizing: border-box; - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} - -/** - * Correct the cursor style of increment and decrement buttons in Chrome. - */ - -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -/** - * 1. Correct the odd appearance in Chrome and Safari. - * 2. Correct the outline style in Safari. - */ - -[type="search"] { - -webkit-appearance: textfield; /* 1 */ - outline-offset: -2px; /* 2 */ -} - -/** - * Remove the inner padding in Chrome and Safari on macOS. - */ - -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -/** - * 1. Correct the inability to style clickable types in iOS and Safari. - * 2. Change font properties to `inherit` in Safari. - */ - -::-webkit-file-upload-button { - -webkit-appearance: button; /* 1 */ - font: inherit; /* 2 */ -} - -/* Interactive - ========================================================================== */ - -/* - * Add the correct display in Edge, IE 10+, and Firefox. - */ - -details { - display: block; -} - -/* - * Add the correct display in all browsers. - */ - -summary { - display: list-item; -} - -/* Misc - ========================================================================== */ - -/** - * Add the correct display in IE 10+. - */ - -template { - display: none; -} - -/** - * Add the correct display in IE 10. - */ - -[hidden] { - display: none; -} - -/*csslint important:false*/ - -/* ========================================================================== - Pure Base Extras - ========================================================================== */ - -/** - * Extra rules that Pure adds on top of Normalize.css - */ - -html { - font-family: sans-serif; -} - -/** - * Always hide an element when it has the `hidden` HTML attribute. - */ - -.hidden, -[hidden] { - display: none !important; -} - -/** - * Add this class to an image to make it fit within it's fluid parent wrapper while maintaining - * aspect ratio. - */ -.pure-img { - max-width: 100%; - height: auto; - display: block; -} - -/*csslint regex-selectors:false, known-properties:false, duplicate-properties:false*/ - -.pure-g { - letter-spacing: -0.31em; /* Webkit: collapse white-space between units */ - text-rendering: optimizespeed; /* Webkit: fixes text-rendering: optimizeLegibility */ - - /* - Sets the font stack to fonts known to work properly with the above letter - and word spacings. See: https://github.com/pure-css/pure/issues/41/ - - The following font stack makes Pure Grids work on all known environments. - - * FreeSans: Ships with many Linux distros, including Ubuntu - - * Arimo: Ships with Chrome OS. Arimo has to be defined before Helvetica and - Arial to get picked up by the browser, even though neither is available - in Chrome OS. - - * Droid Sans: Ships with all versions of Android. - - * Helvetica, Arial, sans-serif: Common font stack on OS X and Windows. - */ - font-family: FreeSans, Arimo, "Droid Sans", Helvetica, Arial, sans-serif; - - /* Use flexbox when possible to avoid `letter-spacing` side-effects. */ - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row wrap; - flex-flow: row wrap; - - /* Prevents distributing space between rows */ - -ms-flex-line-pack: start; - align-content: flex-start; -} - -/* IE10 display: -ms-flexbox (and display: flex in IE 11) does not work inside a table; fall back to block and rely on font hack */ -@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { - table .pure-g { - display: block; - } -} - -/* Opera as of 12 on Windows needs word-spacing. - The ".opera-only" selector is used to prevent actual prefocus styling - and is not required in markup. -*/ -.opera-only :-o-prefocus, -.pure-g { - word-spacing: -0.43em; -} - -.pure-u { - display: inline-block; - letter-spacing: normal; - word-spacing: normal; - vertical-align: top; - text-rendering: auto; -} - -/* -Resets the font family back to the OS/browser's default sans-serif font, -this the same font stack that Normalize.css sets for the `body`. -*/ -.pure-g [class *= "pure-u"] { - font-family: sans-serif; -} - -.pure-u-1, -.pure-u-1-1, -.pure-u-1-2, -.pure-u-1-3, -.pure-u-2-3, -.pure-u-1-4, -.pure-u-3-4, -.pure-u-1-5, -.pure-u-2-5, -.pure-u-3-5, -.pure-u-4-5, -.pure-u-5-5, -.pure-u-1-6, -.pure-u-5-6, -.pure-u-1-8, -.pure-u-3-8, -.pure-u-5-8, -.pure-u-7-8, -.pure-u-1-12, -.pure-u-5-12, -.pure-u-7-12, -.pure-u-11-12, -.pure-u-1-24, -.pure-u-2-24, -.pure-u-3-24, -.pure-u-4-24, -.pure-u-5-24, -.pure-u-6-24, -.pure-u-7-24, -.pure-u-8-24, -.pure-u-9-24, -.pure-u-10-24, -.pure-u-11-24, -.pure-u-12-24, -.pure-u-13-24, -.pure-u-14-24, -.pure-u-15-24, -.pure-u-16-24, -.pure-u-17-24, -.pure-u-18-24, -.pure-u-19-24, -.pure-u-20-24, -.pure-u-21-24, -.pure-u-22-24, -.pure-u-23-24, -.pure-u-24-24 { - display: inline-block; - letter-spacing: normal; - word-spacing: normal; - vertical-align: top; - text-rendering: auto; -} - -.pure-u-1-24 { - width: 4.1667%; -} - -.pure-u-1-12, -.pure-u-2-24 { - width: 8.3333%; -} - -.pure-u-1-8, -.pure-u-3-24 { - width: 12.5000%; -} - -.pure-u-1-6, -.pure-u-4-24 { - width: 16.6667%; -} - -.pure-u-1-5 { - width: 20%; -} - -.pure-u-5-24 { - width: 20.8333%; -} - -.pure-u-1-4, -.pure-u-6-24 { - width: 25%; -} - -.pure-u-7-24 { - width: 29.1667%; -} - -.pure-u-1-3, -.pure-u-8-24 { - width: 33.3333%; -} - -.pure-u-3-8, -.pure-u-9-24 { - width: 37.5000%; -} - -.pure-u-2-5 { - width: 40%; -} - -.pure-u-5-12, -.pure-u-10-24 { - width: 41.6667%; -} - -.pure-u-11-24 { - width: 45.8333%; -} - -.pure-u-1-2, -.pure-u-12-24 { - width: 50%; -} - -.pure-u-13-24 { - width: 54.1667%; -} - -.pure-u-7-12, -.pure-u-14-24 { - width: 58.3333%; -} - -.pure-u-3-5 { - width: 60%; -} - -.pure-u-5-8, -.pure-u-15-24 { - width: 62.5000%; -} - -.pure-u-2-3, -.pure-u-16-24 { - width: 66.6667%; -} - -.pure-u-17-24 { - width: 70.8333%; -} - -.pure-u-3-4, -.pure-u-18-24 { - width: 75%; -} - -.pure-u-19-24 { - width: 79.1667%; -} - -.pure-u-4-5 { - width: 80%; -} - -.pure-u-5-6, -.pure-u-20-24 { - width: 83.3333%; -} - -.pure-u-7-8, -.pure-u-21-24 { - width: 87.5000%; -} - -.pure-u-11-12, -.pure-u-22-24 { - width: 91.6667%; -} - -.pure-u-23-24 { - width: 95.8333%; -} - -.pure-u-1, -.pure-u-1-1, -.pure-u-5-5, -.pure-u-24-24 { - width: 100%; -} -.pure-button { - /* Structure */ - display: inline-block; - line-height: normal; - white-space: nowrap; - vertical-align: middle; - text-align: center; - cursor: pointer; - -webkit-user-drag: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - -webkit-box-sizing: border-box; - box-sizing: border-box; -} - -/* Firefox: Get rid of the inner focus border */ -.pure-button::-moz-focus-inner { - padding: 0; - border: 0; -} - -/* Inherit .pure-g styles */ -.pure-button-group { - letter-spacing: -0.31em; /* Webkit: collapse white-space between units */ - text-rendering: optimizespeed; /* Webkit: fixes text-rendering: optimizeLegibility */ -} - -.opera-only :-o-prefocus, -.pure-button-group { - word-spacing: -0.43em; -} - -.pure-button-group .pure-button { - letter-spacing: normal; - word-spacing: normal; - vertical-align: top; - text-rendering: auto; -} - -/*csslint outline-none:false*/ - -.pure-button { - font-family: inherit; - font-size: 100%; - padding: 0.5em 1em; - color: rgba(0, 0, 0, 0.80); - border: none rgba(0, 0, 0, 0); - background-color: #E6E6E6; - text-decoration: none; - border-radius: 2px; -} - -.pure-button-hover, -.pure-button:hover, -.pure-button:focus { - background-image: -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(40%, rgba(0,0,0, 0.05)), to(rgba(0,0,0, 0.10))); - background-image: linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10)); -} -.pure-button:focus { - outline: 0; -} -.pure-button-active, -.pure-button:active { - -webkit-box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset, 0 0 6px rgba(0,0,0, 0.20) inset; - box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset, 0 0 6px rgba(0,0,0, 0.20) inset; - border-color: #000; -} - -.pure-button[disabled], -.pure-button-disabled, -.pure-button-disabled:hover, -.pure-button-disabled:focus, -.pure-button-disabled:active { - border: none; - background-image: none; - opacity: 0.40; - cursor: not-allowed; - -webkit-box-shadow: none; - box-shadow: none; - pointer-events: none; -} - -.pure-button-hidden { - display: none; -} - -.pure-button-primary, -.pure-button-selected, -a.pure-button-primary, -a.pure-button-selected { - background-color: rgb(0, 120, 231); - color: #fff; -} - -/* Button Groups */ -.pure-button-group .pure-button { - margin: 0; - border-radius: 0; - border-right: 1px solid rgba(0, 0, 0, 0.2); - -} - -.pure-button-group .pure-button:first-child { - border-top-left-radius: 2px; - border-bottom-left-radius: 2px; -} -.pure-button-group .pure-button:last-child { - border-top-right-radius: 2px; - border-bottom-right-radius: 2px; - border-right: none; -} - -/*csslint box-model:false*/ -/* -Box-model set to false because we're setting a height on select elements, which -also have border and padding. This is done because some browsers don't render -the padding. We explicitly set the box-model for select elements to border-box, -so we can ignore the csslint warning. -*/ - -.pure-form input[type="text"], -.pure-form input[type="password"], -.pure-form input[type="email"], -.pure-form input[type="url"], -.pure-form input[type="date"], -.pure-form input[type="month"], -.pure-form input[type="time"], -.pure-form input[type="datetime"], -.pure-form input[type="datetime-local"], -.pure-form input[type="week"], -.pure-form input[type="number"], -.pure-form input[type="search"], -.pure-form input[type="tel"], -.pure-form input[type="color"], -.pure-form select, -.pure-form textarea { - padding: 0.5em 0.6em; - display: inline-block; - border: 1px solid #ccc; - -webkit-box-shadow: inset 0 1px 3px #ddd; - box-shadow: inset 0 1px 3px #ddd; - border-radius: 4px; - vertical-align: middle; - -webkit-box-sizing: border-box; - box-sizing: border-box; -} - -/* -Need to separate out the :not() selector from the rest of the CSS 2.1 selectors -since IE8 won't execute CSS that contains a CSS3 selector. -*/ -.pure-form input:not([type]) { - padding: 0.5em 0.6em; - display: inline-block; - border: 1px solid #ccc; - -webkit-box-shadow: inset 0 1px 3px #ddd; - box-shadow: inset 0 1px 3px #ddd; - border-radius: 4px; - -webkit-box-sizing: border-box; - box-sizing: border-box; -} - - -/* Chrome (as of v.32/34 on OS X) needs additional room for color to display. */ -/* May be able to remove this tweak as color inputs become more standardized across browsers. */ -.pure-form input[type="color"] { - padding: 0.2em 0.5em; -} - - -.pure-form input[type="text"]:focus, -.pure-form input[type="password"]:focus, -.pure-form input[type="email"]:focus, -.pure-form input[type="url"]:focus, -.pure-form input[type="date"]:focus, -.pure-form input[type="month"]:focus, -.pure-form input[type="time"]:focus, -.pure-form input[type="datetime"]:focus, -.pure-form input[type="datetime-local"]:focus, -.pure-form input[type="week"]:focus, -.pure-form input[type="number"]:focus, -.pure-form input[type="search"]:focus, -.pure-form input[type="tel"]:focus, -.pure-form input[type="color"]:focus, -.pure-form select:focus, -.pure-form textarea:focus { - outline: 0; - border-color: #129FEA; -} - -/* -Need to separate out the :not() selector from the rest of the CSS 2.1 selectors -since IE8 won't execute CSS that contains a CSS3 selector. -*/ -.pure-form input:not([type]):focus { - outline: 0; - border-color: #129FEA; -} - -.pure-form input[type="file"]:focus, -.pure-form input[type="radio"]:focus, -.pure-form input[type="checkbox"]:focus { - outline: thin solid #129FEA; - outline: 1px auto #129FEA; -} -.pure-form .pure-checkbox, -.pure-form .pure-radio { - margin: 0.5em 0; - display: block; -} - -.pure-form input[type="text"][disabled], -.pure-form input[type="password"][disabled], -.pure-form input[type="email"][disabled], -.pure-form input[type="url"][disabled], -.pure-form input[type="date"][disabled], -.pure-form input[type="month"][disabled], -.pure-form input[type="time"][disabled], -.pure-form input[type="datetime"][disabled], -.pure-form input[type="datetime-local"][disabled], -.pure-form input[type="week"][disabled], -.pure-form input[type="number"][disabled], -.pure-form input[type="search"][disabled], -.pure-form input[type="tel"][disabled], -.pure-form input[type="color"][disabled], -.pure-form select[disabled], -.pure-form textarea[disabled] { - cursor: not-allowed; - background-color: #eaeded; - color: #cad2d3; -} - -/* -Need to separate out the :not() selector from the rest of the CSS 2.1 selectors -since IE8 won't execute CSS that contains a CSS3 selector. -*/ -.pure-form input:not([type])[disabled] { - cursor: not-allowed; - background-color: #eaeded; - color: #cad2d3; -} -.pure-form input[readonly], -.pure-form select[readonly], -.pure-form textarea[readonly] { - background-color: #eee; /* menu hover bg color */ - color: #777; /* menu text color */ - border-color: #ccc; -} - -.pure-form input:focus:invalid, -.pure-form textarea:focus:invalid, -.pure-form select:focus:invalid { - color: #b94a48; - border-color: #e9322d; -} -.pure-form input[type="file"]:focus:invalid:focus, -.pure-form input[type="radio"]:focus:invalid:focus, -.pure-form input[type="checkbox"]:focus:invalid:focus { - outline-color: #e9322d; -} -.pure-form select { - /* Normalizes the height; padding is not sufficient. */ - height: 2.25em; - border: 1px solid #ccc; - background-color: white; -} -.pure-form select[multiple] { - height: auto; -} -.pure-form label { - margin: 0.5em 0 0.2em; -} -.pure-form fieldset { - margin: 0; - padding: 0.35em 0 0.75em; - border: 0; -} -.pure-form legend { - display: block; - width: 100%; - padding: 0.3em 0; - margin-bottom: 0.3em; - color: #333; - border-bottom: 1px solid #e5e5e5; -} - -.pure-form-stacked input[type="text"], -.pure-form-stacked input[type="password"], -.pure-form-stacked input[type="email"], -.pure-form-stacked input[type="url"], -.pure-form-stacked input[type="date"], -.pure-form-stacked input[type="month"], -.pure-form-stacked input[type="time"], -.pure-form-stacked input[type="datetime"], -.pure-form-stacked input[type="datetime-local"], -.pure-form-stacked input[type="week"], -.pure-form-stacked input[type="number"], -.pure-form-stacked input[type="search"], -.pure-form-stacked input[type="tel"], -.pure-form-stacked input[type="color"], -.pure-form-stacked input[type="file"], -.pure-form-stacked select, -.pure-form-stacked label, -.pure-form-stacked textarea { - display: block; - margin: 0.25em 0; -} - -/* -Need to separate out the :not() selector from the rest of the CSS 2.1 selectors -since IE8 won't execute CSS that contains a CSS3 selector. -*/ -.pure-form-stacked input:not([type]) { - display: block; - margin: 0.25em 0; -} -.pure-form-aligned input, -.pure-form-aligned textarea, -.pure-form-aligned select, -.pure-form-message-inline { - display: inline-block; - vertical-align: middle; -} -.pure-form-aligned textarea { - vertical-align: top; -} - -/* Aligned Forms */ -.pure-form-aligned .pure-control-group { - margin-bottom: 0.5em; -} -.pure-form-aligned .pure-control-group label { - text-align: right; - display: inline-block; - vertical-align: middle; - width: 10em; - margin: 0 1em 0 0; -} -.pure-form-aligned .pure-controls { - margin: 1.5em 0 0 11em; -} - -/* Rounded Inputs */ -.pure-form input.pure-input-rounded, -.pure-form .pure-input-rounded { - border-radius: 2em; - padding: 0.5em 1em; -} - -/* Grouped Inputs */ -.pure-form .pure-group fieldset { - margin-bottom: 10px; -} -.pure-form .pure-group input, -.pure-form .pure-group textarea { - display: block; - padding: 10px; - margin: 0 0 -1px; - border-radius: 0; - position: relative; - top: -1px; -} -.pure-form .pure-group input:focus, -.pure-form .pure-group textarea:focus { - z-index: 3; -} -.pure-form .pure-group input:first-child, -.pure-form .pure-group textarea:first-child { - top: 1px; - border-radius: 4px 4px 0 0; - margin: 0; -} -.pure-form .pure-group input:first-child:last-child, -.pure-form .pure-group textarea:first-child:last-child { - top: 1px; - border-radius: 4px; - margin: 0; -} -.pure-form .pure-group input:last-child, -.pure-form .pure-group textarea:last-child { - top: -2px; - border-radius: 0 0 4px 4px; - margin: 0; -} -.pure-form .pure-group button { - margin: 0.35em 0; -} - -.pure-form .pure-input-1 { - width: 100%; -} -.pure-form .pure-input-3-4 { - width: 75%; -} -.pure-form .pure-input-2-3 { - width: 66%; -} -.pure-form .pure-input-1-2 { - width: 50%; -} -.pure-form .pure-input-1-3 { - width: 33%; -} -.pure-form .pure-input-1-4 { - width: 25%; -} - -/* Inline help for forms */ -.pure-form-message-inline { - display: inline-block; - padding-left: 0.3em; - color: #666; - vertical-align: middle; - font-size: 0.875em; -} - -/* Block help for forms */ -.pure-form-message { - display: block; - color: #666; - font-size: 0.875em; -} - -@media only screen and (max-width : 480px) { - .pure-form button[type="submit"] { - margin: 0.7em 0 0; - } - - .pure-form input:not([type]), - .pure-form input[type="text"], - .pure-form input[type="password"], - .pure-form input[type="email"], - .pure-form input[type="url"], - .pure-form input[type="date"], - .pure-form input[type="month"], - .pure-form input[type="time"], - .pure-form input[type="datetime"], - .pure-form input[type="datetime-local"], - .pure-form input[type="week"], - .pure-form input[type="number"], - .pure-form input[type="search"], - .pure-form input[type="tel"], - .pure-form input[type="color"], - .pure-form label { - margin-bottom: 0.3em; - display: block; - } - - .pure-group input:not([type]), - .pure-group input[type="text"], - .pure-group input[type="password"], - .pure-group input[type="email"], - .pure-group input[type="url"], - .pure-group input[type="date"], - .pure-group input[type="month"], - .pure-group input[type="time"], - .pure-group input[type="datetime"], - .pure-group input[type="datetime-local"], - .pure-group input[type="week"], - .pure-group input[type="number"], - .pure-group input[type="search"], - .pure-group input[type="tel"], - .pure-group input[type="color"] { - margin-bottom: 0; - } - - .pure-form-aligned .pure-control-group label { - margin-bottom: 0.3em; - text-align: left; - display: block; - width: 100%; - } - - .pure-form-aligned .pure-controls { - margin: 1.5em 0 0 0; - } - - .pure-form-message-inline, - .pure-form-message { - display: block; - font-size: 0.75em; - /* Increased bottom padding to make it group with its related input element. */ - padding: 0.2em 0 0.8em; - } -} - -/*csslint adjoining-classes: false, box-model:false*/ -.pure-menu { - -webkit-box-sizing: border-box; - box-sizing: border-box; -} - -.pure-menu-fixed { - position: fixed; - left: 0; - top: 0; - z-index: 3; -} - -.pure-menu-list, -.pure-menu-item { - position: relative; -} - -.pure-menu-list { - list-style: none; - margin: 0; - padding: 0; -} - -.pure-menu-item { - padding: 0; - margin: 0; - height: 100%; -} - -.pure-menu-link, -.pure-menu-heading { - display: block; - text-decoration: none; - white-space: nowrap; -} - -/* HORIZONTAL MENU */ -.pure-menu-horizontal { - width: 100%; - white-space: nowrap; -} - -.pure-menu-horizontal .pure-menu-list { - display: inline-block; -} - -/* Initial menus should be inline-block so that they are horizontal */ -.pure-menu-horizontal .pure-menu-item, -.pure-menu-horizontal .pure-menu-heading, -.pure-menu-horizontal .pure-menu-separator { - display: inline-block; - vertical-align: middle; -} - -/* Submenus should still be display: block; */ -.pure-menu-item .pure-menu-item { - display: block; -} - -.pure-menu-children { - display: none; - position: absolute; - left: 100%; - top: 0; - margin: 0; - padding: 0; - z-index: 3; -} - -.pure-menu-horizontal .pure-menu-children { - left: 0; - top: auto; - width: inherit; -} - -.pure-menu-allow-hover:hover > .pure-menu-children, -.pure-menu-active > .pure-menu-children { - display: block; - position: absolute; -} - -/* Vertical Menus - show the dropdown arrow */ -.pure-menu-has-children > .pure-menu-link:after { - padding-left: 0.5em; - content: "\25B8"; - font-size: small; -} - -/* Horizontal Menus - show the dropdown arrow */ -.pure-menu-horizontal .pure-menu-has-children > .pure-menu-link:after { - content: "\25BE"; -} - -/* scrollable menus */ -.pure-menu-scrollable { - overflow-y: scroll; - overflow-x: hidden; -} - -.pure-menu-scrollable .pure-menu-list { - display: block; -} - -.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list { - display: inline-block; -} - -.pure-menu-horizontal.pure-menu-scrollable { - white-space: nowrap; - overflow-y: hidden; - overflow-x: auto; - /* a little extra padding for this style to allow for scrollbars */ - padding: .5em 0; -} - -/* misc default styling */ - -.pure-menu-separator, -.pure-menu-horizontal .pure-menu-children .pure-menu-separator { - background-color: #ccc; - height: 1px; - margin: .3em 0; -} - -.pure-menu-horizontal .pure-menu-separator { - width: 1px; - height: 1.3em; - margin: 0 .3em ; -} - -/* Need to reset the separator since submenu is vertical */ -.pure-menu-horizontal .pure-menu-children .pure-menu-separator { - display: block; - width: auto; -} - -.pure-menu-heading { - text-transform: uppercase; - color: #565d64; -} - -.pure-menu-link { - color: #777; -} - -.pure-menu-children { - background-color: #fff; -} - -.pure-menu-link, -.pure-menu-disabled, -.pure-menu-heading { - padding: .5em 1em; -} - -.pure-menu-disabled { - opacity: .5; -} - -.pure-menu-disabled .pure-menu-link:hover { - background-color: transparent; -} - -.pure-menu-active > .pure-menu-link, -.pure-menu-link:hover, -.pure-menu-link:focus { - background-color: #eee; -} - -.pure-menu-selected > .pure-menu-link, -.pure-menu-selected > .pure-menu-link:visited { - color: #000; -} - -.pure-table { - /* Remove spacing between table cells (from Normalize.css) */ - border-collapse: collapse; - border-spacing: 0; - empty-cells: show; - border: 1px solid #cbcbcb; -} - -.pure-table caption { - color: #000; - font: italic 85%/1 arial, sans-serif; - padding: 1em 0; - text-align: center; -} - -.pure-table td, -.pure-table th { - border-left: 1px solid #cbcbcb;/* inner column border */ - border-width: 0 0 0 1px; - font-size: inherit; - margin: 0; - overflow: visible; /*to make ths where the title is really long work*/ - padding: 0.5em 1em; /* cell padding */ -} - -.pure-table thead { - background-color: #e0e0e0; - color: #000; - text-align: left; - vertical-align: bottom; -} - -/* -striping: - even - #fff (white) - odd - #f2f2f2 (light gray) -*/ -.pure-table td { - background-color: transparent; -} -.pure-table-odd td { - background-color: #f2f2f2; -} - -/* nth-child selector for modern browsers */ -.pure-table-striped tr:nth-child(2n-1) td { - background-color: #f2f2f2; -} - -/* BORDERED TABLES */ -.pure-table-bordered td { - border-bottom: 1px solid #cbcbcb; -} -.pure-table-bordered tbody > tr:last-child > td { - border-bottom-width: 0; -} - - -/* HORIZONTAL BORDERED TABLES */ - -.pure-table-horizontal td, -.pure-table-horizontal th { - border-width: 0 0 1px 0; - border-bottom: 1px solid #cbcbcb; -} -.pure-table-horizontal tbody > tr:last-child > td { - border-bottom-width: 0; -} diff --git a/src/tabs.ts b/src/tabs.ts index e70505c..400530f 100644 --- a/src/tabs.ts +++ b/src/tabs.ts @@ -1,5 +1,17 @@ -import { GrayTabGroup } from '../@types/graytabby'; -import { erase, load, save, fieldKeeper } from './utils'; +/** + * Tab types and logic. + */ + +import { Tabs as WebextTabs } from 'webextension-polyfill-ts/dist/generated/tabs'; +import { erase, load, save, fieldKeeper, loadBatch } from './utils'; + +export type BrowserTab = WebextTabs.Tab; +export type GrayTab = Pick & { key: number }; + +export type GrayTabGroup = { + tabs: GrayTab[]; + date: number; +}; export const INDEX_V1_KEY = 'tabGroups'; export const INDEX_V2_KEY = 'g'; @@ -21,7 +33,7 @@ export function keyFromDate(date: number): string { } export function dateFromKey(key: string): number { - key = key.substr(1); + key = key.substr(INDEX_V2_KEY.length); return Number(key); } @@ -68,11 +80,7 @@ export async function loadAllTabGroups(): Promise { await save(INDEX_V2_KEY, result); } const groupIds: string[] = result; - const promises: Promise[] = []; - for (const id of groupIds) { - promises.push(load(id)); - } - return Promise.all(promises); + return loadBatch(groupIds); } export async function saveTabGroup(group: GrayTabGroup): Promise { diff --git a/src/ui.ts b/src/ui.ts index 16ff59b..79d39f4 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -1,14 +1,23 @@ -import { BrowserTab, GrayTabGroup, GrayTab } from '../@types/graytabby'; +/** + * User interface code for graytabby. + * + * Anything responsible for responding to user input or for displaying data to the user. + */ + +import * as sizeof from 'object-sizeof'; +import { ARCHIVAL, BROWSER, DOCUMENT } from './globals'; +import { getOptions, setOptions } from './options'; import { - loadAllTabGroups, - eraseTabGroup, - saveTabGroup, - keyFromGroup, dateFromKey, + eraseTabGroup, keyFromDate, + keyFromGroup, + loadAllTabGroups, + saveTabGroup, + GrayTabGroup, + GrayTab, + BrowserTab, } from './tabs'; -import { getOptions, setOptions } from './options'; -import { DOCUMENT, BROWSER, ARCHIVAL } from './globals'; function getDomain(url: string): string { return new URL(url).hostname; @@ -43,10 +52,21 @@ function makeElement( async function bindOptions(): Promise { const document = DOCUMENT.get(); const modal = document.querySelector('#optionsModal'); - const button = document.querySelector('#optionsButton'); - const close = document.querySelector('#optionsModal .close'); - button.onclick = () => (modal.style.display = 'block'); - close.onclick = () => (modal.style.display = 'none'); + const logo = document.querySelector('#logo'); + const content = document.querySelector('#optionsModal .content'); + logo.onclick = () => (modal.style.display = 'block'); + modal.onclick = event => { + if (!content.contains(event.target)) modal.style.display = 'none'; + }; + + const checkboxes: HTMLInputElement[] = Array.from( + content.querySelectorAll('label input[type="checkbox"]'), + ); + for (const checkbox of checkboxes) { + const label = checkbox.parentElement; + const span = label.querySelector('span'); + span.onclick = () => (checkbox.checked = !checkbox.checked); + } const optionsLimitNode = document.querySelector('#optionsLimit'); const optionsDupesNode = document.querySelector('#optionsDupes'); @@ -86,10 +106,6 @@ async function bindOptions(): Promise { }; } -// function patchWindow(): void { -// getDocument().defaultView.getOptions = getOptions; -// } - function renderFavicon(url: string): HTMLImageElement { const loc = faviconLocation(url); return makeElement('img', { @@ -99,16 +115,12 @@ function renderFavicon(url: string): HTMLImageElement { }); } -/** - * Updates the backend to match the DOM for the tab group with the given date. - */ -async function syncGroupFromDOM(target: number | HTMLDivElement): Promise { +export function groupFromDiv(target: number | HTMLDivElement): GrayTabGroup { let div: HTMLDivElement; let date: number; if (typeof target === 'number') { date = target; div = DOCUMENT.get().querySelector(`#${keyFromDate(date)}`); - if (div == null) return eraseTabGroup(date); } else { div = target; date = dateFromKey(div.id); @@ -117,6 +129,7 @@ async function syncGroupFromDOM(target: number | HTMLDivElement): Promise date: date, tabs: [], }; + if (div == null) return group; const lis = div.querySelectorAll('li'); lis.forEach(li => { const a: HTMLAnchorElement = li.querySelector('a'); @@ -127,8 +140,16 @@ async function syncGroupFromDOM(target: number | HTMLDivElement): Promise }; group.tabs.push(tab); }); + return group; +} + +/** + * Updates the backend to match the DOM for the tab group with the given date. + */ +export async function syncGroupFromDOM(target: number | HTMLDivElement): Promise { + const group = groupFromDiv(target); if (group.tabs.length == 0) { - return eraseTabGroup(date); + return eraseTabGroup(group.date); } return saveTabGroup(group); } @@ -186,14 +207,18 @@ function countGroups(): number { return DOCUMENT.get().querySelectorAll('#groups > div').length; } -async function ingestTabs(tabSummaries: BrowserTab[], groupsNode: HTMLDivElement): Promise { +async function ingestTabs( + tabSummaries: BrowserTab[], + groupsNode: HTMLDivElement, + now = () => new Date().getTime(), +): Promise { if (tabSummaries.length == 0) return; let counter = 0; const group: GrayTabGroup = { tabs: tabSummaries.map(ts => { return { ...ts, key: counter++ }; }), - date: Math.round(new Date().getTime()), + date: now(), }; prependInsideContainer(groupsNode, renderGroup(group)); await syncGroupFromDOM(group.date); @@ -209,6 +234,30 @@ async function ingestTabs(tabSummaries: BrowserTab[], groupsNode: HTMLDivElement } } +class Debugger { + async double(): Promise { + const groups = await loadAllTabGroups(); + let earliest = Math.min(...groups.map(g => g.date)); + const groupDiv = DOCUMENT.get().querySelector('#groups'); + const promises: Promise[] = []; + for (const group of groups) { + earliest -= 1000; + group.date = earliest; + groupDiv.appendChild(renderGroup(group)); + await syncGroupFromDOM(group.date); + updateInfo(group.tabs.length); + } + await Promise.all(promises); + console.log(sizeof.default(await loadAllTabGroups())); + } +} + +declare global { + interface Window { + gt: Debugger; + } +} + /** * The main entry point for GrayTabby. */ @@ -225,6 +274,7 @@ export async function grayTabby(): Promise { } ARCHIVAL.get().sub(summaries => ingestTabs(summaries, groupsNode)); updateInfo(counter); - // updateInfo(); - // patchWindow(); + + const window = DOCUMENT.get().defaultView; + window.gt = new Debugger(); } diff --git a/src/utils.ts b/src/utils.ts index 55aefe7..a8af40f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -18,6 +18,15 @@ export async function load(key: string): Promise { return results[key]; } +export async function loadBatch(keys: string[]): Promise { + const retval = []; + const results = await BROWSER.get().storage.local.get(keys); + for (const key in results) { + retval.push(results[key]); + } + return retval; +} + export async function erase(key: string): Promise { return BROWSER.get().storage.local.remove(key); } diff --git a/tests/archive.test.ts b/tests/archive.test.ts index f143f8c..5235360 100644 --- a/tests/archive.test.ts +++ b/tests/archive.test.ts @@ -1,12 +1,14 @@ +import { expect } from 'chai'; import * as mockBrowser from 'sinon-chrome'; -import { BrowserTab } from '../@types/graytabby'; +import { archivePlan } from '../src/archive'; import { Broker, BrokerConsumer } from '../src/brokers'; import { ARCHIVAL, DOCUMENT } from '../src/globals'; -import { dateFromKey, INDEX_V2_KEY } from '../src/tabs'; +import { dateFromKey, INDEX_V2_KEY, BrowserTab } from '../src/tabs'; import { assertElement, initGrayTabby, testTab } from './utils'; -describe('archive', function() { +describe('archive operation', function() { it('should work', async function() { + //TODO const archival = new Broker('moreTabs'); // Capture callback for tab archival so we can inject tabs in test. let consumer: BrokerConsumer; @@ -27,3 +29,39 @@ describe('archive', function() { a.click(); }); }); + +describe('archivePlan', () => { + it('should be noop when no tabs are open', () => { + const [tabsToArchive, tabsToClose] = archivePlan([], '', true); + expect(tabsToArchive).to.be.empty; + expect(tabsToClose).to.be.empty; + }); + + it('should archive non-home tabs and skip home tab', () => { + const [tabsToArchive, tabsToClose] = archivePlan( + [ + testTab({ url: 'foo', pinned: false, windowId: 1, title: '', id: 1 }), + testTab({ url: 'home', pinned: false, windowId: 1, title: '', id: 2 }), + testTab({ url: 'bar', pinned: false, windowId: 1, title: '', id: 3 }), + ], + 'home', + true, + ); + expect(tabsToArchive.map(x => x.id)).to.have.members([1, 3]); + expect(tabsToClose).to.be.empty; + }); + + it('should keep pinned tabs', () => { + const [tabsToArchive, tabsToClose] = archivePlan( + [ + testTab({ url: 'home', pinned: false, windowId: 1, title: '', id: 1 }), + testTab({ url: 'foo', pinned: false, windowId: 1, title: '', id: 2 }), + testTab({ url: 'bar', pinned: true, windowId: 2, title: '', id: 2 }), + ], + 'home', + true, + ); + expect(tabsToArchive.map(x => [x.windowId, x.id])).deep.equal([[1, 2]]); + expect(tabsToClose).to.be.empty; + }); +}); diff --git a/tests/options.test.ts b/tests/options.test.ts index de019d3..f1633d0 100644 --- a/tests/options.test.ts +++ b/tests/options.test.ts @@ -1,6 +1,6 @@ -import * as assert from 'assert'; import * as mockBrowser from 'sinon-chrome'; import { getOptions, Options, OPTIONS_KEY } from '../src/options'; +import { expect } from 'chai'; describe('options', function() { it('should load old string format', async function() { @@ -11,6 +11,6 @@ describe('options', function() { }; mockBrowser.storage.local.get.withArgs(OPTIONS_KEY).returns(JSON.stringify(oldOptions)); const options = await getOptions(); - assert.deepStrictEqual(oldOptions, options); + expect(oldOptions).to.deep.equal(options); }); }); diff --git a/tests/utils.ts b/tests/utils.ts index 269632c..1c915bb 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -1,11 +1,10 @@ -import { BrowserTab } from '../@types/graytabby'; -import * as assert from 'assert'; import { BROWSER, DOCUMENT } from '../src/globals'; import * as mockBrowser from 'sinon-chrome'; import { JSDOM } from 'jsdom'; -import { INDEX_V1_KEY, INDEX_V2_KEY } from '../src/tabs'; +import { INDEX_V1_KEY, INDEX_V2_KEY, BrowserTab } from '../src/tabs'; import { OPTIONS_KEY } from '../src/options'; import { grayTabby } from '../src/ui'; +import { expect } from 'chai'; export async function initGrayTabby(): Promise { BROWSER.set(mockBrowser); @@ -18,7 +17,7 @@ export async function initGrayTabby(): Promise { try { return await grayTabby(); } catch (err) { - assert.fail('Uncaught error: ' + err); + expect.fail('Uncaught error: ' + err); } } @@ -45,7 +44,7 @@ export function assertElement( idx = 1, ): Element { const nodes = getElements(query, parent); - assert.equal(nodes.length, total); + expect(nodes.length).to.equal(total); if (total != 0) return nodes[idx - 1]; return null; }