diff --git a/.appveyor.yml b/.appveyor.yml
index ecabd841d..747505faa 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -32,7 +32,7 @@ build_script:
- cmd: node app/build/create-signed-windows-installer.js
before_deploy:
- - cmd: 7z -ttar a dummy %APPVEYOR_BUILD_FOLDER%\app\dist\*.dll %APPVEYOR_BUILD_FOLDER%\app\dist\mailsync.exe -so | 7z -si -tgzip a .\app\dist\mailsync.tar.gz
+ - cmd: 7z -ttar a dummy %APPVEYOR_BUILD_FOLDER%\app\dist\*.dll %APPVEYOR_BUILD_FOLDER%\app\dist\*.pdb %APPVEYOR_BUILD_FOLDER%\app\dist\mailsync.exe -so | 7z -si -tgzip a .\app\dist\mailsync.tar.gz
- ps: Get-ChildItem .\app\dist\*.tar.gz | % { Push-AppveyorArtifact $_.FullName -FileName "win-ia32/$($_.Name)" -DeploymentName s3-deployment }
- ps: Get-ChildItem .\app\dist\MailspringSetup.exe | % { Push-AppveyorArtifact $_.FullName -FileName "win-ia32/$($_.Name)" -DeploymentName s3-deployment }
- ps: Get-ChildItem .\app\dist\*.nupkg | % { Push-AppveyorArtifact $_.FullName -FileName "win-ia32/$($_.Name)" -DeploymentName s3-deployment }
diff --git a/.travis.yml b/.travis.yml
index 90d5e9463..87bfd9fc7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,7 +6,7 @@ git:
language: node_js
node_js:
- - '11'
+ - '12'
addons:
artifacts:
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 3d78108a3..d7323ae77 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -29,5 +29,6 @@
"git.ignoreLimitWarning": true,
"files.exclude": {
"**/*.dll": true
- }
-}
\ No newline at end of file
+ },
+ "typescript.tsdk": "node_modules/typescript/lib"
+}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 000000000..f3a937be1
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,11 @@
+{
+ "version": "2.0.0",
+ "command": "./node_modules/.bin/tsc",
+ "type": "shell",
+ "args": ["-w", "-p", "./app", "--noEmit"],
+ "presentation": {
+ "reveal": "silent"
+ },
+ "isBackground": true,
+ "problemMatcher": "$tsc-watch"
+}
diff --git a/app/internal_packages/account-sidebar/lib/sidebar-item.ts b/app/internal_packages/account-sidebar/lib/sidebar-item.ts
index b1cf591ee..55f673015 100644
--- a/app/internal_packages/account-sidebar/lib/sidebar-item.ts
+++ b/app/internal_packages/account-sidebar/lib/sidebar-item.ts
@@ -52,7 +52,7 @@ const onDeleteItem = function(item) {
return;
}
- const chosen = remote.dialog.showMessageBox({
+ const response = remote.dialog.showMessageBoxSync({
type: 'info',
message: localized('Are you sure?'),
detail: localized(
@@ -62,7 +62,7 @@ const onDeleteItem = function(item) {
defaultId: 0,
});
- if (chosen !== 0) {
+ if (response !== 0) {
return;
}
diff --git a/app/internal_packages/account-sidebar/lib/sidebar-section.ts b/app/internal_packages/account-sidebar/lib/sidebar-section.ts
index 25648c966..26b99a8ff 100644
--- a/app/internal_packages/account-sidebar/lib/sidebar-section.ts
+++ b/app/internal_packages/account-sidebar/lib/sidebar-section.ts
@@ -18,7 +18,7 @@ import {
import SidebarItem from './sidebar-item';
import * as SidebarActions from './sidebar-actions';
-import { ISidebarSection } from './types';
+import { ISidebarSection, ISidebarItem } from './types';
function isSectionCollapsed(title) {
if (AppEnv.savedState.sidebarKeysCollapsed[title] !== undefined) {
@@ -106,8 +106,8 @@ class SidebarSection {
];
const items = [];
- for (let names of standardNames) {
- names = Array.isArray(names) ? names : [names];
+ for (const nameOrNames of standardNames) {
+ const names = Array.isArray(nameOrNames) ? nameOrNames : [nameOrNames];
const categories = CategoryStore.getCategoriesWithRoles(accounts, ...names);
if (categories.length === 0) {
continue;
@@ -192,15 +192,16 @@ class SidebarSection {
// Inbox.FolderA.FolderB
// Inbox.FolderB
//
- const items = [];
- const seenItems = {};
+ const items: ISidebarItem[] = [];
+ const seenItems: { [key: string]: ISidebarItem } = {};
for (const category of CategoryStore.userCategories(account)) {
// https://regex101.com/r/jK8cC2/1
- let item, parentKey;
+ let item: ISidebarItem = null;
const re = RegExpUtils.subcategorySplitRegex();
const itemKey = category.displayName.replace(re, '/');
let parent = null;
+ let parentKey: string = null;
const parentComponents = itemKey.split('/');
for (let i = parentComponents.length; i >= 1; i--) {
parentKey = parentComponents.slice(0, i).join('/');
diff --git a/app/internal_packages/account-sidebar/lib/types.ts b/app/internal_packages/account-sidebar/lib/types.ts
index 8a8a3985d..989ffe17d 100644
--- a/app/internal_packages/account-sidebar/lib/types.ts
+++ b/app/internal_packages/account-sidebar/lib/types.ts
@@ -12,7 +12,7 @@ export interface ISidebarItem {
collapsed: boolean;
counterStyle: string;
onDelete?: () => void;
- onEdited?: () => void;
+ onEdited?: (item, name: string) => void;
onCollapseToggled: () => void;
onDrop: (item, event) => void;
shouldAcceptDrop: (item, event) => void;
diff --git a/app/internal_packages/account-sidebar/specs/sidebar-item-spec.es6.ts b/app/internal_packages/account-sidebar/specs/sidebar-item-spec.es6.ts
index 52e7b75ce..98c7a59af 100644
--- a/app/internal_packages/account-sidebar/specs/sidebar-item-spec.es6.ts
+++ b/app/internal_packages/account-sidebar/specs/sidebar-item-spec.es6.ts
@@ -3,25 +3,25 @@ import SidebarItem from '../lib/sidebar-item';
describe('sidebar-item', function sidebarItemSpec() {
it('preserves nested labels on rename', () => {
- spyOn(Actions, 'queueTask');
- const categories = [new Folder({ path: 'a.b/c', accountId: window.TEST_ACCOUNT_ID })];
+ const queueTask = spyOn(Actions, 'queueTask');
+ const categories = [new Folder({ path: 'a.b/c', accountId: TEST_ACCOUNT_ID })];
AppEnv.savedState.sidebarKeysCollapsed = {};
const item = SidebarItem.forCategories(categories) as any;
item.onEdited(item, 'd');
- const task = Actions.queueTask.calls[0].args[0];
+ const task = queueTask.calls[0].args[0];
const { existingPath, path } = task;
expect(existingPath).toBe('a.b/c');
expect(path).toBe('a.b/d');
});
it('preserves labels on rename', () => {
- spyOn(Actions, 'queueTask');
- const categories = [new Folder({ path: 'a', accountId: window.TEST_ACCOUNT_ID })];
+ const queueTask = spyOn(Actions, 'queueTask');
+ const categories = [new Folder({ path: 'a', accountId: TEST_ACCOUNT_ID })];
AppEnv.savedState.sidebarKeysCollapsed = {};
const item = SidebarItem.forCategories(categories);
item.onEdited(item, 'b') as any;
- const task = Actions.queueTask.calls[0].args[0];
+ const task = queueTask.calls[0].args[0];
const { existingPath, path } = task;
expect(existingPath).toBe('a');
expect(path).toBe('b');
diff --git a/app/internal_packages/activity/lib/dashboard/share-button.tsx b/app/internal_packages/activity/lib/dashboard/share-button.tsx
index 1ce5bb2c0..384ca5b2f 100644
--- a/app/internal_packages/activity/lib/dashboard/share-button.tsx
+++ b/app/internal_packages/activity/lib/dashboard/share-button.tsx
@@ -5,6 +5,7 @@ import { RetinaImg } from 'mailspring-component-kit';
function buildShareHTML(htmlEl, styleEl) {
return `
+
diff --git a/app/internal_packages/composer-signature/lib/preferences-signatures.tsx b/app/internal_packages/composer-signature/lib/preferences-signatures.tsx
index 7382925e3..b21836cc6 100644
--- a/app/internal_packages/composer-signature/lib/preferences-signatures.tsx
+++ b/app/internal_packages/composer-signature/lib/preferences-signatures.tsx
@@ -57,7 +57,7 @@ class SignatureEditor extends React.Component sig.body === RenderSignatureData({ ...sig.data, templateName: t.name })
);
if (!htmlMatchesATemplate) {
- const idx = remote.dialog.showMessageBox({
+ const idx = remote.dialog.showMessageBoxSync({
type: 'warning',
buttons: [localized('Cancel'), localized('Continue')],
message: localized('Revert custom HTML?'),
diff --git a/app/internal_packages/composer-templates/lib/template-store.ts b/app/internal_packages/composer-templates/lib/template-store.ts
index 0326e0acc..afa2ee1ef 100644
--- a/app/internal_packages/composer-templates/lib/template-store.ts
+++ b/app/internal_packages/composer-templates/lib/template-store.ts
@@ -155,7 +155,7 @@ class TemplateStore extends MailspringStore {
_displayDialog(title, message, buttons) {
return (
- remote.dialog.showMessageBox({
+ remote.dialog.showMessageBoxSync({
title: title,
message: title,
detail: message,
diff --git a/app/internal_packages/composer/lib/composer-view.tsx b/app/internal_packages/composer/lib/composer-view.tsx
index 837af0130..3eb0a1389 100644
--- a/app/internal_packages/composer/lib/composer-view.tsx
+++ b/app/internal_packages/composer/lib/composer-view.tsx
@@ -62,7 +62,9 @@ export default class ComposerView extends React.Component();
sendButton = React.createRef();
focusContainer = React.createRef();
- editor = React.createRef();
+ editor:
+ | React.RefObject
+ | React.RefObject = React.createRef();
header = React.createRef();
_keymapHandlers = {
@@ -158,7 +160,7 @@ export default class ComposerView extends React.Component
{draft.plaintext ? (
}
value={draft.body}
propsForPlugins={{ draft, session }}
onFileReceived={this._onFileReceived}
@@ -170,7 +172,7 @@ export default class ComposerView extends React.Component
}
value={draft.bodyEditorState}
className={quotedTextHidden && 'hiding-quoted-text'}
propsForPlugins={{ draft, session }}
@@ -364,7 +366,7 @@ export default class ComposerView extends React.Component 0 && !options.force) {
- const response = dialog.showMessageBox({
+ const response = dialog.showMessageBoxSync({
type: 'warning',
buttons: [localized('Send Anyway'), localized('Cancel')],
message: localized('Are you sure?'),
diff --git a/app/internal_packages/composer/styles/composer.less b/app/internal_packages/composer/styles/composer.less
index 32fb357fb..6c319f401 100644
--- a/app/internal_packages/composer/styles/composer.less
+++ b/app/internal_packages/composer/styles/composer.less
@@ -385,7 +385,11 @@ body.platform-win32 {
.composer-body-wrap {
padding: 0 0 5px 0;
}
-
+ .composer-header {
+ .key-commands-region {
+ height: initial;
+ }
+ }
.composer-header-actions {
position: relative;
float: right;
diff --git a/app/internal_packages/contacts/lib/FoundInMailEnabledBar.tsx b/app/internal_packages/contacts/lib/FoundInMailEnabledBar.tsx
index c2886bb51..55d0d51f2 100644
--- a/app/internal_packages/contacts/lib/FoundInMailEnabledBar.tsx
+++ b/app/internal_packages/contacts/lib/FoundInMailEnabledBar.tsx
@@ -14,7 +14,12 @@ class FoundInMailEnabledBarWithData extends React.Component {
- const accountId = this.props.perspective.accountId;
+ const { perspective } = this.props;
+ if (!perspective || perspective.type !== 'found-in-mail') {
+ return false;
+ }
+
+ const accountId = perspective.accountId;
let disabled = AppEnv.config.get(CONFIG_KEY);
if (disabled.includes(accountId)) {
disabled = disabled.filter(i => i !== accountId);
diff --git a/app/internal_packages/contacts/lib/GoogleSupport.ts b/app/internal_packages/contacts/lib/GoogleSupport.ts
index 621c6829e..68bcd91fb 100644
--- a/app/internal_packages/contacts/lib/GoogleSupport.ts
+++ b/app/internal_packages/contacts/lib/GoogleSupport.ts
@@ -10,7 +10,7 @@ export const showGPeopleReadonlyNotice = (accountId: string) => {
acct.provider === 'gmail' &&
(!acct.authedAt || acct.authedAt < CONTACTS_OAUTH_SCOPE_ADDED)
) {
- remote.dialog.showMessageBox({
+ remote.dialog.showMessageBoxSync({
message: localized(`Please re-authenticate with Google`),
detail: localized(
`To make changes to contacts in this account, you'll need to re-authorize Mailspring to access your data.\n\n` +
diff --git a/app/internal_packages/draft-list/lib/draft-list-store.ts b/app/internal_packages/draft-list/lib/draft-list-store.ts
index f4da5aac8..4cabecbaf 100644
--- a/app/internal_packages/draft-list/lib/draft-list-store.ts
+++ b/app/internal_packages/draft-list/lib/draft-list-store.ts
@@ -61,11 +61,12 @@ class DraftListStore extends MailspringStore {
const subscription = new MutableQuerySubscription(query, { emitResultSet: true });
const $resultSet = Rx.Observable.combineLatest(
- [
- Rx.Observable.fromNamedQuerySubscription('draft-list', subscription),
- Rx.Observable.fromStore(OutboxStore) as any,
- ],
- (resultSet: QueryResultSet, outbox) => {
+ Rx.Observable.fromNamedQuerySubscription('draft-list', subscription),
+ Rx.Observable.fromStore(OutboxStore),
+ (resultSet, outboxStore) => {
+ if (!(resultSet instanceof QueryResultSet)) {
+ throw 'Set emitResultSet=true and did not receive a QueryResultSet';
+ }
// Generate a new result set that includes additional information on
// the draft objects. This is similar to what we do in the thread-list,
// where we set thread.__messages to the message array.
@@ -73,7 +74,7 @@ class DraftListStore extends MailspringStore {
// TODO BG modelWithId: task.headerMessageId does not work
mailboxPerspective.accountIds.forEach(aid => {
- OutboxStore.itemsForAccount(aid).forEach(task => {
+ outboxStore.itemsForAccount(aid).forEach(task => {
let draft = resultSet.modelWithId(task.headerMessageId) as any;
if (draft) {
draft = draft.clone();
diff --git a/app/internal_packages/github-contact-card/lib/github-user-store.ts b/app/internal_packages/github-contact-card/lib/github-user-store.ts
index 86d59be5f..e8ce32cd0 100644
--- a/app/internal_packages/github-contact-card/lib/github-user-store.ts
+++ b/app/internal_packages/github-contact-card/lib/github-user-store.ts
@@ -82,7 +82,7 @@ class GithubUserStore extends MailspringStore {
`https://api.github.com/search/repositories?q=user:${profile.login}&sort=stars&order=desc`
);
// Sort the repositories by their stars (`-` for descending order)
- profile.repos = _.sortBy(repos.items, repo => -repo.stargazers_count);
+ profile.repos = _.sortBy(repos.items, repo => -repo['stargazers_count']);
// Trigger so that our React components refresh their state and display
// the updated data.
this.trigger(this);
diff --git a/app/internal_packages/localizer-help/lib/main.tsx b/app/internal_packages/localizer-help/lib/main.tsx
index 84d6e4134..a3a1c76df 100644
--- a/app/internal_packages/localizer-help/lib/main.tsx
+++ b/app/internal_packages/localizer-help/lib/main.tsx
@@ -33,7 +33,7 @@ class SubmitLocalizationsBar extends React.Component {
json: true,
});
if (status === 'success') {
- remote.dialog.showMessageBox({
+ remote.dialog.showMessageBoxSync({
type: 'info',
buttons: [localized('OK')],
message: localized('Thank you!'),
diff --git a/app/internal_packages/main-calendar/lib/calendar-wrapper.tsx b/app/internal_packages/main-calendar/lib/calendar-wrapper.tsx
deleted file mode 100644
index 30259d763..000000000
--- a/app/internal_packages/main-calendar/lib/calendar-wrapper.tsx
+++ /dev/null
@@ -1,104 +0,0 @@
-import { Actions, DestroyModelTask, Event } from 'mailspring-exports';
-import React from 'react';
-import { remote } from 'electron';
-
-import { KeyCommandsRegion } from 'mailspring-component-kit';
-import CalendarDataSource from './core/calendar-data-source';
-import CalendarEventPopover from './core/calendar-event-popover';
-import MailspringCalendar from './core/mailspring-calendar';
-
-export default class CalendarWrapper extends React.Component<{}, { selectedEvents: Event[] }> {
- static displayName = 'CalendarWrapper';
- static containerRequired = false;
-
- _dataSource = new CalendarDataSource();
-
- constructor(props) {
- super(props);
- this.state = { selectedEvents: [] };
- }
-
- _openEventPopover(eventModel: Event) {
- const eventEl = document.getElementById(eventModel.id);
- if (!eventEl) {
- return;
- }
- const eventRect = eventEl.getBoundingClientRect();
-
- Actions.openPopover(, {
- originRect: eventRect,
- direction: 'right',
- fallbackDirection: 'left',
- });
- }
-
- _onEventClick = (e: React.MouseEvent, event: Event) => {
- let next = [...this.state.selectedEvents];
-
- if (e.shiftKey || e.metaKey) {
- const idx = next.findIndex(({ id }) => event.id === id);
- if (idx === -1) {
- next.push(event);
- } else {
- next.splice(idx, 1);
- }
- } else {
- next = [event];
- }
-
- this.setState({
- selectedEvents: next,
- });
- };
-
- _onEventDoubleClick = (eventModel: Event) => {
- this._openEventPopover(eventModel);
- };
-
- _onEventFocused = (eventModel: Event) => {
- this._openEventPopover(eventModel);
- };
-
- _onDeleteSelectedEvents = () => {
- if (this.state.selectedEvents.length === 0) {
- return;
- }
- const response = remote.dialog.showMessageBox({
- type: 'warning',
- buttons: ['Delete', 'Cancel'],
- message: 'Delete or decline these events?',
- detail: `Are you sure you want to delete or decline invitations for the selected event(s)?`,
- });
- if (response === 0) {
- // response is button array index
- for (const event of this.state.selectedEvents) {
- const task = new DestroyModelTask({
- modelId: event.id,
- modelName: event.constructor.name,
- endpoint: '/events',
- accountId: event.accountId,
- });
- Actions.queueTask(task);
- }
- }
- };
-
- render() {
- return (
-
-
-
- );
- }
-}
diff --git a/app/internal_packages/main-calendar/lib/core/calendar-constants.ts b/app/internal_packages/main-calendar/lib/core/calendar-constants.ts
index d82021ee4..91e66815e 100644
--- a/app/internal_packages/main-calendar/lib/core/calendar-constants.ts
+++ b/app/internal_packages/main-calendar/lib/core/calendar-constants.ts
@@ -1,4 +1,5 @@
-export const DAY_VIEW = 'day';
-export const WEEK_VIEW = 'week';
-export const MONTH_VIEW = 'month';
-export const YEAR_VIEW = 'year';
+export enum CalendarView {
+ DAY = 'day',
+ WEEK = 'week',
+ MONTH = 'month',
+}
diff --git a/app/internal_packages/main-calendar/lib/core/calendar-data-source.ts b/app/internal_packages/main-calendar/lib/core/calendar-data-source.ts
index 9fb97e86b..c4367b8dd 100644
--- a/app/internal_packages/main-calendar/lib/core/calendar-data-source.ts
+++ b/app/internal_packages/main-calendar/lib/core/calendar-data-source.ts
@@ -2,18 +2,32 @@ import Rx from 'rx-lite';
import { Event, Matcher, DatabaseStore } from 'mailspring-exports';
import IcalExpander from 'ical-expander';
-export default class CalendarDataSource {
- observable: Rx.Observable<{ events: Event[] }>;
+export interface EventOccurrence {
+ start: number; // unix
+ end: number; // unix
+ id: string;
+ accountId: string;
+ calendarId: string;
+ title: string;
+ location: string;
+ description: string;
+ isAllDay: boolean;
+ organizer: { email: string } | null;
+ attendees: { email: string; name: string }[];
+}
- buildObservable({ startTime, endTime, disabledCalendars }) {
+export class CalendarDataSource {
+ observable: Rx.Observable<{ events: EventOccurrence[] }>;
+
+ buildObservable({ startUnix, endUnix, disabledCalendars }) {
const end = Event.attributes.recurrenceEnd;
const start = Event.attributes.recurrenceStart;
const matcher = new Matcher.Or([
- new Matcher.And([start.lte(endTime), end.gte(startTime)]),
- new Matcher.And([start.lte(endTime), start.gte(startTime)]),
- new Matcher.And([end.gte(startTime), end.lte(endTime)]),
- new Matcher.And([end.gte(endTime), start.lte(startTime)]),
+ new Matcher.And([start.lte(endUnix), end.gte(startUnix)]),
+ new Matcher.And([start.lte(endUnix), start.gte(startUnix)]),
+ new Matcher.And([end.gte(startUnix), end.lte(endUnix)]),
+ new Matcher.And([end.gte(endUnix), start.lte(startUnix)]),
]);
if (disabledCalendars && disabledCalendars.length) {
@@ -21,35 +35,9 @@ export default class CalendarDataSource {
}
const query = DatabaseStore.findAll(Event).where(matcher);
- this.observable = Rx.Observable.fromQuery(query).flatMapLatest(results => {
- const events = [];
- results.forEach(result => {
- const icalExpander = new IcalExpander({ ics: result.ics, maxIterations: 100 });
- const expanded = icalExpander.between(new Date(startTime * 1000), new Date(endTime * 1000));
-
- [...expanded.events, ...expanded.occurrences].forEach((e, idx) => {
- const start = e.startDate.toJSDate().getTime() / 1000;
- const end = e.endDate.toJSDate().getTime() / 1000;
- const item = e.item || e;
- events.push({
- start,
- end,
- id: `${result.id}-e${idx}`,
- calendarId: result.calendarId,
- title: item.summary,
- displayTitle: item.summary,
- description: item.description,
- isAllDay: end - start >= 86400 - 1,
- organizer: item.organizer ? { email: item.organizer } : null,
- attendees: item.attendees.map(a => ({
- ...a.jCal[1],
- email: a.getFirstValue(),
- })),
- });
- });
- });
- return Rx.Observable.from([{ events }]);
- });
+ this.observable = Rx.Observable.fromQuery(query).flatMapLatest(results =>
+ Rx.Observable.from([{ events: occurrencesForEvents(results, { startUnix, endUnix }) }])
+ );
return this.observable;
}
@@ -57,3 +45,39 @@ export default class CalendarDataSource {
return this.observable.subscribe(callback);
}
}
+
+export function occurrencesForEvents(
+ results: Event[],
+ { startUnix, endUnix }: { startUnix: number; endUnix: number }
+) {
+ const occurences: EventOccurrence[] = [];
+
+ results.forEach(result => {
+ const icalExpander = new IcalExpander({ ics: result.ics, maxIterations: 100 });
+ const expanded = icalExpander.between(new Date(startUnix * 1000), new Date(endUnix * 1000));
+
+ [...expanded.events, ...expanded.occurrences].forEach((e, idx) => {
+ const start = e.startDate.toJSDate().getTime() / 1000;
+ const end = e.endDate.toJSDate().getTime() / 1000;
+ const item = e.item || e;
+ occurences.push({
+ start,
+ end,
+ id: `${result.id}-e${idx}`,
+ accountId: result.accountId,
+ calendarId: result.calendarId,
+ title: item.summary,
+ location: item.location,
+ description: item.description,
+ isAllDay: end - start >= 86400 - 1,
+ organizer: item.organizer ? { email: item.organizer } : null,
+ attendees: item.attendees.map(a => ({
+ ...a.jCal[1],
+ email: a.getFirstValue(),
+ })),
+ });
+ });
+ });
+
+ return occurences;
+}
diff --git a/app/internal_packages/main-calendar/lib/core/calendar-event-container.tsx b/app/internal_packages/main-calendar/lib/core/calendar-event-container.tsx
index fa2c0e490..215604108 100644
--- a/app/internal_packages/main-calendar/lib/core/calendar-event-container.tsx
+++ b/app/internal_packages/main-calendar/lib/core/calendar-event-container.tsx
@@ -1,18 +1,28 @@
import moment from 'moment';
-
import React from 'react';
import ReactDOM from 'react-dom';
-import PropTypes from 'prop-types';
-export default class CalendarEventContainer extends React.Component {
+import { EventOccurrence } from './calendar-data-source';
+
+export interface CalendarEventArgs {
+ event?: EventOccurrence;
+ time: number;
+ x: number;
+ y: number;
+ width: number;
+ height: number;
+ mouseIsDown: boolean;
+}
+
+interface CalendarEventContainerProps {
+ onCalendarMouseDown: (args: CalendarEventArgs) => void;
+ onCalendarMouseMove: (args: CalendarEventArgs) => void;
+ onCalendarMouseUp: (args: CalendarEventArgs) => void;
+}
+
+export class CalendarEventContainer extends React.Component {
static displayName = 'CalendarEventContainer';
- static propTypes = {
- onCalendarMouseUp: PropTypes.func,
- onCalendarMouseDown: PropTypes.func,
- onCalendarMouseMove: PropTypes.func,
- };
-
_DOMCache: {
eventColumn?: any;
gridWrap?: any;
diff --git a/app/internal_packages/main-calendar/lib/core/calendar-event-popover.tsx b/app/internal_packages/main-calendar/lib/core/calendar-event-popover.tsx
index d8769c071..4e00c94cc 100644
--- a/app/internal_packages/main-calendar/lib/core/calendar-event-popover.tsx
+++ b/app/internal_packages/main-calendar/lib/core/calendar-event-popover.tsx
@@ -1,12 +1,12 @@
-import moment from 'moment';
+import moment, { Moment } from 'moment';
import React from 'react';
import {
- PropTypes,
- Event,
Actions,
- DatabaseStore,
DateUtils,
SyncbackEventTask,
+ localized,
+ RegExpUtils,
+ Autolink,
} from 'mailspring-exports';
import {
DatePicker,
@@ -15,10 +15,12 @@ import {
TabGroupRegion,
TimePicker,
} from 'mailspring-component-kit';
-import EventAttendeesInput from './event-attendees-input';
+import { EventAttendeesInput } from './event-attendees-input';
+import { EventOccurrence } from './calendar-data-source';
+import { EventTimerangePicker } from './event-timerange-picker';
interface CalendarEventPopoverProps {
- event: Event;
+ event: EventOccurrence;
}
interface CalendarEventPopoverState {
@@ -31,36 +33,28 @@ interface CalendarEventPopoverState {
title: string;
}
-export default class CalendarEventPopover extends React.Component<
+export class CalendarEventPopover extends React.Component<
CalendarEventPopoverProps,
CalendarEventPopoverState
> {
- static propTypes = {
- event: PropTypes.object.isRequired,
- };
-
constructor(props) {
super(props);
- const { description, start, end, location, attendees } = this.props.event;
+ const { description, start, end, location, attendees, title } = this.props.event;
this.state = {
description,
start,
end,
location,
- title: this.props.event.displayTitle,
+ title,
editing: false,
- attendees: attendees || [],
+ attendees,
};
}
componentWillReceiveProps = nextProps => {
- const { description, start, end, location, attendees } = nextProps.event;
- this.setState({ description, start, end, location });
- this.setState({
- attendees: attendees || [],
- title: nextProps.event.displayTitle,
- });
+ const { description, start, end, location, attendees, title } = nextProps.event;
+ this.setState({ description, start, end, location, attendees, title });
};
onEdit = () => {
@@ -89,34 +83,7 @@ export default class CalendarEventPopover extends React.Component<
Actions.queueTask(task);
};
- extractNotesFromDescription(node) {
- const els = node.querySelectorAll('meta[itemprop=description]');
- let notes: string = null;
- if (els.length) {
- notes = Array.from(els)
- .map((el: any) => el.content)
- .join('\n');
- } else {
- notes = node.innerText;
- }
- while (true) {
- const nextNotes = notes.replace('\n\n', '\n');
- if (nextNotes === notes) {
- break;
- }
- notes = nextNotes;
- }
- return notes;
- }
-
// If on the hour, formats as "3 PM", else formats as "3:15 PM"
- formatTime(momentTime) {
- const min = momentTime.minutes();
- if (min === 0) {
- return momentTime.format('h A');
- }
- return momentTime.format('h:mm A');
- }
updateAttendees = attendees => {
this.setState({ attendees });
@@ -128,86 +95,10 @@ export default class CalendarEventPopover extends React.Component<
this.setState(updates);
};
- _onChangeDay = newTimestamp => {
- const newDay = moment(newTimestamp);
- const start = this.getStartMoment();
- const end = this.getEndMoment();
- start.year(newDay.year());
- end.year(newDay.year());
- start.dayOfYear(newDay.dayOfYear());
- end.dayOfYear(newDay.dayOfYear());
- this.setState({ start: start.unix(), end: end.unix() });
- };
-
- _onChangeStartTime = newTimestamp => {
- const newStart = moment(newTimestamp);
- let newEnd = this.getEndMoment();
- if (newEnd.isSameOrBefore(newStart)) {
- const leftInDay = moment(newStart)
- .endOf('day')
- .diff(newStart);
- const move = Math.min(leftInDay, moment.duration(1, 'hour').asMilliseconds());
- newEnd = moment(newStart).add(move, 'ms');
- }
- this.setState({ start: newStart.unix(), end: newEnd.unix() });
- };
-
- _onChangeEndTime = newTimestamp => {
- const newEnd = moment(newTimestamp);
- let newStart = this.getStartMoment();
- if (newStart.isSameOrAfter(newEnd)) {
- const sinceDay = moment(newEnd).diff(newEnd.startOf('day'));
- const move = Math.min(sinceDay, moment.duration(1, 'hour').asMilliseconds());
- newStart = moment(newEnd).subtract(move, 'ms');
- }
- this.setState({ end: newEnd.unix(), start: newStart.unix() });
- };
-
- renderTime() {
- const startMoment = this.getStartMoment();
- const endMoment = this.getEndMoment();
- const date = startMoment.format('dddd, MMMM D'); // e.g. Tuesday, February 22
- const timeRange = `${this.formatTime(startMoment)} - ${this.formatTime(endMoment)}`;
- return (
-
- {date}
-
- {timeRange}
-
- );
- }
-
- renderEditableTime() {
- const startVal = this.state.start * 1000;
- const endVal = this.state.end * 1000;
- return (
-
-
-
-
- to
-
-
- {moment()
- .tz(DateUtils.timeZone)
- .format('z')}
-
- on
-
-
-
- );
- }
-
renderEditable = () => {
const { title, description, start, end, location, attendees } = this.state;
- const fragment = document.createDocumentFragment();
- const descriptionRoot = document.createElement('root');
- fragment.appendChild(descriptionRoot);
- descriptionRoot.innerHTML = description;
-
- const notes = this.extractNotesFromDescription(descriptionRoot);
+ const notes = extractNotesFromDescription(description);
return (
@@ -230,9 +121,15 @@ export default class CalendarEventPopover extends React.Component<
this.updateField('location', e.target.value);
}}
/>
-
{this.renderEditableTime()}
-
Invitees:
+
this.setState({ start, end })}
+ />
+
+
+
{localized(`Invitees`)}:
-
Notes:
+
{localized(`Notes`)}:
-
Save
-
Actions.closePopover()}>Cancel
+
{localized(`Save`)}
+
Actions.closePopover()}>{localized(`Cancel`)}
);
@@ -262,14 +159,56 @@ export default class CalendarEventPopover extends React.Component<
if (this.state.editing) {
return this.renderEditable();
}
- const { title, description, location, attendees } = this.state;
+ return (
+ this.setState({ editing: true })}
+ />
+ );
+ }
+}
- const fragment = document.createDocumentFragment();
- const descriptionRoot = document.createElement('root');
- fragment.appendChild(descriptionRoot);
- descriptionRoot.innerHTML = description;
+class CalendarEventPopoverUnenditable extends React.Component<{
+ event: EventOccurrence;
+ onEdit: () => void;
+}> {
+ descriptionRef = React.createRef();
- const notes = this.extractNotesFromDescription(descriptionRoot);
+ renderTime() {
+ const startMoment = moment(this.props.event.start * 1000);
+ const endMoment = moment(this.props.event.end * 1000);
+ const date = startMoment.format('dddd, MMMM D'); // e.g. Tuesday, February 22
+ const timeRange = `${formatTime(startMoment)} - ${formatTime(endMoment)}`;
+ return (
+
+ {date}
+
+ {timeRange}
+
+ );
+ }
+
+ componentDidMount() {
+ this.autolink();
+ }
+
+ componentDidUpdate() {
+ this.autolink();
+ }
+
+ autolink() {
+ if (!this.descriptionRef.current) return;
+ Autolink(this.descriptionRef.current, {
+ async: false,
+ telAggressiveMatch: true,
+ });
+ }
+
+ render() {
+ const { event, onEdit } = this.props;
+ const { title, description, location, attendees } = event;
+
+ const notes = extractNotesFromDescription(description);
return (
@@ -280,22 +219,67 @@ export default class CalendarEventPopover extends React.Component<
name="edit-icon.png"
title="Edit Item"
mode={RetinaImg.Mode.ContentIsMask}
- onClick={this.onEdit}
+ onClick={onEdit}
/>
- {location}
+ {location && (
+
+ {location.startsWith('http') || location.startsWith('tel:') ? (
+
{location}
+ ) : (
+ location
+ )}
+
+ )}
{this.renderTime()}
- Invitees:
- {attendees.map((a, idx) =>
{a.cn}
)}
+ {localized(`Invitees`)}:
+
+ {attendees.map((a, idx) => (
+
{a.cn}
+ ))}
+
-
Notes:
-
{notes}
+
{localized(`Notes`)}:
+
{notes}
);
}
}
+
+function extractNotesFromDescription(description: string) {
+ const fragment = document.createDocumentFragment();
+ const descriptionRoot = document.createElement('root');
+ fragment.appendChild(descriptionRoot);
+ descriptionRoot.innerHTML = description;
+
+ const els = descriptionRoot.querySelectorAll('meta[itemprop=description]');
+ let notes: string = null;
+ if (els.length) {
+ notes = Array.from(els)
+ .map((el: any) => el.content)
+ .join('\n');
+ } else {
+ notes = descriptionRoot.innerText;
+ }
+ while (true) {
+ const nextNotes = notes.replace('\n\n', '\n');
+ if (nextNotes === notes) {
+ break;
+ }
+ notes = nextNotes;
+ }
+ return notes;
+}
+
+function formatTime(momentTime: Moment) {
+ const min = momentTime.minutes();
+ if (min === 0) {
+ return momentTime.format('h A');
+ }
+ return momentTime.format('h:mm A');
+}
diff --git a/app/internal_packages/main-calendar/lib/core/calendar-event.tsx b/app/internal_packages/main-calendar/lib/core/calendar-event.tsx
index 8772e3d8e..e835c29cb 100644
--- a/app/internal_packages/main-calendar/lib/core/calendar-event.tsx
+++ b/app/internal_packages/main-calendar/lib/core/calendar-event.tsx
@@ -1,25 +1,26 @@
import React, { CSSProperties } from 'react';
import ReactDOM from 'react-dom';
-import { Event } from 'mailspring-exports';
import { InjectedComponentSet } from 'mailspring-component-kit';
+import { EventOccurrence } from './calendar-data-source';
import { calcColor } from './calendar-helpers';
interface CalendarEventProps {
- event: Event;
+ event: EventOccurrence;
order: number;
- selected?: boolean;
+ selected: boolean;
scopeEnd: number;
scopeStart: number;
direction: 'horizontal' | 'vertical';
fixedSize: number;
- focused?: boolean;
+ focused: boolean;
concurrentEvents: number;
- onClick: (e: React.MouseEvent, event: Event) => void;
- onDoubleClick: (event: Event) => void;
- onFocused: (event: Event) => void;
+
+ onClick: (e: React.MouseEvent, event: EventOccurrence) => void;
+ onDoubleClick: (event: EventOccurrence) => void;
+ onFocused: (event: EventOccurrence) => void;
}
-export default class CalendarEvent extends React.Component {
+export class CalendarEvent extends React.Component {
static displayName = 'CalendarEvent';
static defaultProps = {
@@ -116,7 +117,7 @@ export default class CalendarEvent extends React.Component {
onDoubleClick={() => onDoubleClick(event)}
>
- {event.displayTitle}
+ {event.title}
{
const calendarId = calendar.id;
const onClick = () => {
@@ -43,7 +43,12 @@ function renderCalendarToggles(calendars, disabledCalendars) {
});
}
-export default function CalendarSourceList(props) {
+interface CalendarSourceListProps {
+ accounts: Account[];
+ calendars: Calendar[];
+ disabledCalendars: string[];
+}
+export function CalendarSourceList(props: CalendarSourceListProps) {
const calsByAccountId = _.groupBy(props.calendars, 'accountId');
const accountSections = [];
for (const accountId of Object.keys(calsByAccountId)) {
@@ -61,9 +66,3 @@ export default function CalendarSourceList(props) {
}
return {accountSections}
;
}
-
-CalendarSourceList.propTypes = {
- accounts: PropTypes.array,
- calendars: PropTypes.array,
- disabledCalendars: PropTypes.array,
-};
diff --git a/app/internal_packages/main-calendar/lib/core/current-time-indicator.tsx b/app/internal_packages/main-calendar/lib/core/current-time-indicator.tsx
index 8a957c380..213ea4885 100644
--- a/app/internal_packages/main-calendar/lib/core/current-time-indicator.tsx
+++ b/app/internal_packages/main-calendar/lib/core/current-time-indicator.tsx
@@ -1,16 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom';
-import PropTypes from 'prop-types';
import Moment from 'moment';
import classNames from 'classnames';
-export default class CurrentTimeIndicator extends React.Component<
- {
- gridHeight: number;
- numColumns: number;
- todayColumnIdx: number;
- visible: boolean;
- },
+interface CurrentTimeIndicatorProps {
+ gridHeight: number;
+ numColumns: number;
+ todayColumnIdx: number;
+ visible: boolean;
+}
+
+export class CurrentTimeIndicator extends React.Component<
+ CurrentTimeIndicatorProps,
{ msecIntoDay: number }
> {
_movementTimer = null;
@@ -48,7 +49,7 @@ export default class CurrentTimeIndicator extends React.Component<
const todayMarker =
todayColumnIdx !== -1 ? (
-
+
) : null;
return (
diff --git a/app/internal_packages/main-calendar/lib/core/event-attendees-input.tsx b/app/internal_packages/main-calendar/lib/core/event-attendees-input.tsx
index 2c350b393..8d22e0249 100644
--- a/app/internal_packages/main-calendar/lib/core/event-attendees-input.tsx
+++ b/app/internal_packages/main-calendar/lib/core/event-attendees-input.tsx
@@ -1,10 +1,10 @@
import React from 'react';
import _ from 'underscore';
import { remote, clipboard } from 'electron';
-import { PropTypes, Utils, Contact, ContactStore, RegExpUtils } from 'mailspring-exports';
+import { Utils, Contact, ContactStore, RegExpUtils, localized } from 'mailspring-exports';
import { TokenizingTextField, Menu, InjectedComponentSet } from 'mailspring-component-kit';
-const TokenRenderer = props => {
+const TokenRenderer = (props: { token: Contact }) => {
const { email, cn } = props.token;
let chipText = email;
if (cn && cn.length > 0 && cn !== email) {
@@ -23,10 +23,6 @@ const TokenRenderer = props => {
);
};
-TokenRenderer.propTypes = {
- token: PropTypes.object,
-};
-
interface EventAttendeesInputProps {
attendees: any[];
change: (next: any[]) => void;
@@ -35,7 +31,7 @@ interface EventAttendeesInputProps {
onFocus?: () => void;
}
-export default class EventAttendeesInput extends React.Component {
+export class EventAttendeesInput extends React.Component {
static displayName = 'EventAttendeesInput';
shouldComponentUpdate(nextProps, nextState) {
@@ -123,7 +119,7 @@ export default class EventAttendeesInput extends React.Component clipboard.writeText(participant.email),
})
);
diff --git a/app/internal_packages/main-calendar/lib/core/event-grid-background.tsx b/app/internal_packages/main-calendar/lib/core/event-grid-background.tsx
index f1741acee..ae247e89f 100644
--- a/app/internal_packages/main-calendar/lib/core/event-grid-background.tsx
+++ b/app/internal_packages/main-calendar/lib/core/event-grid-background.tsx
@@ -1,15 +1,15 @@
import React from 'react';
import ReactDOM from 'react-dom';
-import { PropTypes, Utils } from 'mailspring-exports';
+import { Utils } from 'mailspring-exports';
+import { tickGenerator } from './week-view-helpers';
interface EventGridBackgroundProps {
height: number;
numColumns: number;
- tickGenerator: (arg: { type: string }) => Array<{ yPos }>;
intervalHeight: number;
}
-export default class EventGridBackground extends React.Component {
+export class EventGridBackground extends React.Component {
static displayName = 'EventGridBackground';
_lastHoverRect: { x?; y?; width?; height? } = {};
@@ -36,9 +36,9 @@ export default class EventGridBackground extends React.Component {
ctx.strokeStyle = strokeStyle;
ctx.beginPath();
- for (const { yPos } of this.props.tickGenerator({ type })) {
- ctx.moveTo(0, yPos);
- ctx.lineTo(canvas.width, yPos);
+ for (const { y } of tickGenerator(type, this.props.intervalHeight)) {
+ ctx.moveTo(0, y);
+ ctx.lineTo(canvas.width, y);
}
ctx.stroke();
};
@@ -47,7 +47,11 @@ export default class EventGridBackground extends React.Component) {
+ const width = e.currentTarget.clientWidth;
+ const x = e.clientX;
+ const y = e.clientY;
+
if (!width || x == null || y == null) {
return;
}
@@ -77,7 +81,7 @@ export default class EventGridBackground extends React.Component
+
diff --git a/app/internal_packages/main-calendar/lib/core/event-search-bar.tsx b/app/internal_packages/main-calendar/lib/core/event-search-bar.tsx
index 054b60403..332525e1b 100644
--- a/app/internal_packages/main-calendar/lib/core/event-search-bar.tsx
+++ b/app/internal_packages/main-calendar/lib/core/event-search-bar.tsx
@@ -1,22 +1,19 @@
import React, { Component } from 'react';
-import { Event, DatabaseStore } from 'mailspring-exports';
-import { SearchBar } from 'mailspring-component-kit';
-import PropTypes from 'prop-types';
+import { Event, DatabaseStore, localized } from 'mailspring-exports';
+import { EventOccurrence, occurrencesForEvents } from './calendar-data-source';
+import moment from 'moment';
-class EventSearchBar extends Component<
- {
- disabledCalendars: string[];
- onSelectEvent: (event: Event) => void;
- },
- { query: string; suggestions: Event[] }
+interface EventSearchBarProps {
+ disabledCalendars: string[];
+ onSelectEvent: (event: EventOccurrence) => void;
+}
+
+export class EventSearchBar extends Component<
+ EventSearchBarProps,
+ { query: string; suggestions: EventOccurrence[] }
> {
static displayName = 'EventSearchBar';
- static defaultProps = {
- disabledCalendars: [],
- onSelectEvent: (event: Event) => {},
- };
-
constructor(props) {
super(props);
this.state = {
@@ -40,7 +37,16 @@ class EventSearchBar extends Component<
.search(query)
.limit(10)
.then(events => {
- this.setState({ suggestions: events });
+ this.setState({
+ suggestions: occurrencesForEvents(events, {
+ startUnix: moment()
+ .add(-2, 'years')
+ .unix(),
+ endUnix: moment()
+ .add(2, 'years')
+ .unix(),
+ }),
+ });
});
};
@@ -69,20 +75,18 @@ class EventSearchBar extends Component<
// TODO BG
return ;
- return (
- event.id}
- suggestionRenderer={this.renderEvent}
- onSearchQueryChanged={this.onSearchQueryChanged}
- onSelectSuggestion={this.onSelectEvent}
- onClearSearchQuery={this.onClearSearchQuery}
- onClearSearchSuggestions={this.onClearSearchSuggestions}
- />
- );
+ // return (
+ // event.id}
+ // suggestionRenderer={this.renderEvent}
+ // onSearchQueryChanged={this.onSearchQueryChanged}
+ // onSelectSuggestion={this.onSelectEvent}
+ // onClearSearchQuery={this.onClearSearchQuery}
+ // onClearSearchSuggestions={this.onClearSearchSuggestions}
+ // />
+ // );
}
}
-
-export default EventSearchBar;
diff --git a/app/internal_packages/main-calendar/lib/core/event-timerange-picker.tsx b/app/internal_packages/main-calendar/lib/core/event-timerange-picker.tsx
new file mode 100644
index 000000000..2ff7a2e9b
--- /dev/null
+++ b/app/internal_packages/main-calendar/lib/core/event-timerange-picker.tsx
@@ -0,0 +1,66 @@
+import moment from 'moment';
+import React from 'react';
+import { DateUtils } from 'mailspring-exports';
+import { DatePicker, RetinaImg, TimePicker } from 'mailspring-component-kit';
+
+export const EventTimerangePicker: React.FunctionComponent<{
+ start: number;
+ end: number;
+ onChange: ({ start, end }) => void;
+}> = ({ start, end, onChange }) => {
+ const onChangeStartTime = newTimestamp => {
+ const newStart = moment(newTimestamp);
+ let newEnd = moment(end);
+ if (newEnd.isSameOrBefore(newStart)) {
+ const leftInDay = moment(newStart)
+ .endOf('day')
+ .diff(newStart);
+ const move = Math.min(leftInDay, moment.duration(1, 'hour').asMilliseconds());
+ newEnd = moment(newStart).add(move, 'ms');
+ }
+ onChange({ start: newStart.unix(), end: newEnd.unix() });
+ };
+
+ const onChangeEndTime = (newTimestamp: number) => {
+ const newEnd = moment(newTimestamp);
+ let newStart = moment(start);
+ if (newStart.isSameOrAfter(newEnd)) {
+ const sinceDay = moment(newEnd).diff(newEnd.startOf('day'));
+ const move = Math.min(sinceDay, moment.duration(1, 'hour').asMilliseconds());
+ newStart = moment(newEnd).subtract(move, 'ms');
+ }
+ onChange({ end: newEnd.unix(), start: newStart.unix() });
+ };
+
+ const onChangeDay = (newTimestamp: number) => {
+ const newDay = moment(newTimestamp);
+
+ const newStart = moment(start);
+ newStart.year(newDay.year());
+ newStart.dayOfYear(newDay.dayOfYear());
+
+ const newEnd = moment(end);
+ newEnd.year(newDay.year());
+ newEnd.dayOfYear(newDay.dayOfYear());
+
+ this.setState({ start: newStart.unix(), end: newEnd.unix() });
+ };
+
+ return (
+
+
+
+
+ to
+
+
+ {moment()
+ .tz(DateUtils.timeZone)
+ .format('z')}
+
+ on
+
+
+
+ );
+};
diff --git a/app/internal_packages/main-calendar/lib/core/footer-controls.tsx b/app/internal_packages/main-calendar/lib/core/footer-controls.tsx
deleted file mode 100644
index 184298aac..000000000
--- a/app/internal_packages/main-calendar/lib/core/footer-controls.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import React from 'react';
-import { PropTypes, Utils } from 'mailspring-exports';
-
-export default class FooterControls extends React.Component<{
- footerComponents: React.ReactChildren;
-}> {
- static displayName = 'FooterControls';
-
- static defaultProps = {
- footerComponents: false,
- };
-
- shouldComponentUpdate(nextProps, nextState) {
- return !Utils.isEqualReact(nextProps, this.props) || !Utils.isEqualReact(nextState, this.state);
- }
-
- render() {
- if (!this.props.footerComponents) {
- return false;
- }
- return (
-
-
-
-
- {this.props.footerComponents}
-
- );
- }
-}
diff --git a/app/internal_packages/main-calendar/lib/core/header-controls.tsx b/app/internal_packages/main-calendar/lib/core/header-controls.tsx
index d61872871..8e87c20bd 100644
--- a/app/internal_packages/main-calendar/lib/core/header-controls.tsx
+++ b/app/internal_packages/main-calendar/lib/core/header-controls.tsx
@@ -2,18 +2,13 @@ import React from 'react';
import { Utils } from 'mailspring-exports';
import { RetinaImg } from 'mailspring-component-kit';
-export default class HeaderControls extends React.Component<{
+export class HeaderControls extends React.Component<{
title: string;
- headerComponents: React.ReactNode;
nextAction: () => void;
prevAction: () => void;
}> {
static displayName = 'HeaderControls';
- static defaultProps = {
- headerComonents: false,
- };
-
shouldComponentUpdate(nextProps, nextState) {
return !Utils.isEqualReact(nextProps, this.props) || !Utils.isEqualReact(nextState, this.state);
}
@@ -48,7 +43,7 @@ export default class HeaderControls extends React.Component<{
{this.props.title}
{this._renderNextAction()}
- {this.props.headerComponents}
+ {this.props.children}
);
}
diff --git a/app/internal_packages/main-calendar/lib/core/mailspring-calendar.tsx b/app/internal_packages/main-calendar/lib/core/mailspring-calendar.tsx
index e4ec4fa03..32be477fa 100644
--- a/app/internal_packages/main-calendar/lib/core/mailspring-calendar.tsx
+++ b/app/internal_packages/main-calendar/lib/core/mailspring-calendar.tsx
@@ -1,90 +1,74 @@
import moment, { Moment } from 'moment';
import React from 'react';
-import { Rx, DatabaseStore, AccountStore, Calendar, Account, Event } from 'mailspring-exports';
-import { ScrollRegion, ResizableRegion } from 'mailspring-component-kit';
-import WeekView from './week-view';
-import MonthView from './month-view';
-import EventSearchBar from './event-search-bar';
-import CalendarSourceList from './calendar-source-list';
-import CalendarDataSource from './calendar-data-source';
-import { WEEK_VIEW, MONTH_VIEW } from './calendar-constants';
-import MiniMonthView from './mini-month-view';
+import {
+ Rx,
+ DatabaseStore,
+ AccountStore,
+ Calendar,
+ Account,
+ Actions,
+ localized,
+ DestroyModelTask,
+} from 'mailspring-exports';
+import {
+ ScrollRegion,
+ ResizableRegion,
+ KeyCommandsRegion,
+ MiniMonthView,
+} from 'mailspring-component-kit';
+import { WeekView } from './week-view';
+import { MonthView } from './month-view';
+import { EventSearchBar } from './event-search-bar';
+import { CalendarSourceList } from './calendar-source-list';
+import { CalendarDataSource, EventOccurrence } from './calendar-data-source';
+import { CalendarView } from './calendar-constants';
import { Disposable } from 'rx-core';
+import { CalendarEventArgs } from './calendar-event-container';
+import { CalendarEventPopover } from './calendar-event-popover';
+import { remote } from 'electron';
const DISABLED_CALENDARS = 'mailspring.disabledCalendars';
+const VIEWS = {
+ [CalendarView.WEEK]: WeekView,
+ [CalendarView.MONTH]: MonthView,
+};
+
+export interface EventRendererProps {
+ focusedEvent: EventOccurrence;
+ selectedEvents: EventOccurrence[];
+ onEventClick: (e: React.MouseEvent, event: EventOccurrence) => void;
+ onEventDoubleClick: (event: EventOccurrence) => void;
+ onEventFocused: (event: EventOccurrence) => void;
+}
+
+export interface MailspringCalendarViewProps extends EventRendererProps {
+ dataSource: CalendarDataSource;
+ disabledCalendars: string[];
+ focusedMoment: Moment;
+ onChangeView: (view: CalendarView) => void;
+ onChangeFocusedMoment: (moment: Moment) => void;
+ onCalendarMouseUp: (args: CalendarEventArgs) => void;
+ onCalendarMouseDown: (args: CalendarEventArgs) => void;
+ onCalendarMouseMove: (args: CalendarEventArgs) => void;
+}
+
/*
* Mailspring Calendar
*/
-interface MailspringCalendarProps {
- /*
- * The data source that powers all of the views of the MailspringCalendar
- */
- dataSource: CalendarDataSource;
-
- currentMoment?: Moment;
-
- /*
- * Any extra info you want to display on the top banner of calendar
- * components
- */
- bannerComponents?: {
- day: React.ReactChild;
- week: React.ReactChild;
- month: React.ReactChild;
- year: React.ReactChild;
- };
-
- /*
- * Any extra header components for each of the supported View types of
- * the MailspringCalendar
- */
- headerComponents?: {
- day: React.ReactChild;
- week: React.ReactChild;
- month: React.ReactChild;
- year: React.ReactChild;
- };
-
- /*
- * Any extra footer components for each of the supported View types of
- * the MailspringCalendar
- */
- footerComponents?: {
- day: React.ReactChild;
- week: React.ReactChild;
- month: React.ReactChild;
- year: React.ReactChild;
- };
-
- /*
- * The following are a set of supported interaction handlers.
- *
- * These are passed a custom set of arguments in a single object that
- * includes the `currentView` as well as things like the `time` at the
- * click coordinate.
- */
- onCalendarMouseUp?: () => void;
- onCalendarMouseDown?: () => void;
- onCalendarMouseMove?: () => void;
-
- onEventClick: (e: React.MouseEvent, event: Event) => void;
- onEventDoubleClick: (event: Event) => void;
- onEventFocused: (event: Event) => void;
-
- selectedEvents: Event[];
-}
+interface MailspringCalendarProps {}
interface MailspringCalendarState {
- currentView: string;
- focusedEvent: Event | null;
+ view: CalendarView;
+ selectedEvents: EventOccurrence[];
+ focusedEvent: EventOccurrence | null;
accounts?: Account[];
calendars: Calendar[];
- currentMoment: Moment;
+ focusedMoment: Moment;
disabledCalendars: string[];
}
-export default class MailspringCalendar extends React.Component<
+export class MailspringCalendar extends React.Component<
MailspringCalendarProps,
MailspringCalendarState
> {
@@ -92,26 +76,21 @@ export default class MailspringCalendar extends React.Component<
static WeekView = WeekView;
- static defaultProps = {
- bannerComponents: { day: false, week: false, month: false, year: false },
- headerComponents: { day: false, week: false, month: false, year: false },
- footerComponents: { day: false, week: false, month: false, year: false },
- selectedEvents: [],
- };
-
static containerStyles = {
height: '100%',
};
_disposable?: Disposable;
+ _dataSource = new CalendarDataSource();
constructor(props) {
super(props);
this.state = {
calendars: [],
focusedEvent: null,
- currentView: WEEK_VIEW,
- currentMoment: props.currentMoment || this._now(),
+ selectedEvents: [],
+ view: CalendarView.WEEK,
+ focusedMoment: moment(),
disabledCalendars: AppEnv.config.get(DISABLED_CALENDARS) || [],
};
}
@@ -129,49 +108,109 @@ export default class MailspringCalendar extends React.Component<
const calQueryObs = Rx.Observable.fromQuery(calQuery);
const accQueryObs = Rx.Observable.fromStore(AccountStore);
const configObs = Rx.Observable.fromConfig(DISABLED_CALENDARS);
- return Rx.Observable.combineLatest([calQueryObs, accQueryObs, configObs]).subscribe(
- ([calendars, accountStore, disabledCalendars]: [Calendar[], any, string[] | undefined]) => {
+
+ return Rx.Observable.combineLatest(calQueryObs, accQueryObs, configObs).subscribe(
+ ([calendars, accountStore, disabledCalendars]) => {
this.setState({
- accounts: accountStore.accounts() as Account[],
calendars: calendars,
+ accounts: accountStore.accounts(),
disabledCalendars: disabledCalendars || [],
});
}
);
}
- _now() {
- return moment();
+ onChangeView = (view: CalendarView) => {
+ this.setState({ view });
+ };
+
+ onChangeFocusedMoment = (focusedMoment: Moment) => {
+ this.setState({ focusedMoment, focusedEvent: null });
+ };
+
+ _focusEvent = (event: EventOccurrence) => {
+ this.setState({ focusedMoment: moment(event.start * 1000), focusedEvent: event });
+ };
+
+ _openEventPopover(eventModel: EventOccurrence) {
+ const eventEl = document.getElementById(eventModel.id);
+ if (!eventEl) {
+ return;
+ }
+ Actions.openPopover(, {
+ originRect: eventEl.getBoundingClientRect(),
+ direction: 'right',
+ fallbackDirection: 'left',
+ });
}
- _getCurrentViewComponent() {
- const components = {};
- components[WEEK_VIEW] = WeekView;
- components[MONTH_VIEW] = MonthView;
- return components[this.state.currentView];
- }
+ _onEventClick = (e: React.MouseEvent, event: EventOccurrence) => {
+ let next = [...this.state.selectedEvents];
- _changeCurrentView = currentView => {
- this.setState({ currentView });
+ if (e.shiftKey || e.metaKey) {
+ const idx = next.findIndex(({ id }) => event.id === id);
+ if (idx === -1) {
+ next.push(event);
+ } else {
+ next.splice(idx, 1);
+ }
+ } else {
+ next = [event];
+ }
+
+ this.setState({
+ selectedEvents: next,
+ });
};
- _changeCurrentMoment = currentMoment => {
- this.setState({ currentMoment, focusedEvent: null });
+ _onEventDoubleClick = (occurrence: EventOccurrence) => {
+ this._openEventPopover(occurrence);
};
- _changeCurrentMomentFromValue = value => {
- this.setState({ currentMoment: moment(value), focusedEvent: null });
+ _onEventFocused = (occurrence: EventOccurrence) => {
+ this._openEventPopover(occurrence);
};
- _focusEvent = event => {
- const value = event.start * 1000;
- this.setState({ currentMoment: moment(value), focusedEvent: event });
+ _onDeleteSelectedEvents = () => {
+ if (this.state.selectedEvents.length === 0) {
+ return;
+ }
+ const response = remote.dialog.showMessageBoxSync({
+ type: 'warning',
+ buttons: [localized('Delete'), localized('Cancel')],
+ message: localized('Delete or decline these events?'),
+ detail: localized(
+ `Are you sure you want to delete or decline invitations for the selected event(s)?`
+ ),
+ });
+ if (response === 0) {
+ // response is button array index
+ for (const event of this.state.selectedEvents) {
+ const task = new DestroyModelTask({
+ modelId: event.id,
+ modelName: event.constructor.name,
+ endpoint: '/events',
+ accountId: event.accountId,
+ });
+ Actions.queueTask(task);
+ }
+ }
};
+ _onCalendarMouseDown = () => {};
+ _onCalendarMouseMove = () => {};
+ _onCalendarMouseUp = () => {};
+
render() {
- const CurrentView = this._getCurrentViewComponent();
+ const CurrentView = VIEWS[this.state.view];
+
return (
-
+
);
}
}
diff --git a/app/internal_packages/main-calendar/lib/core/mini-month-view.tsx b/app/internal_packages/main-calendar/lib/core/mini-month-view.tsx
deleted file mode 100644
index 2c455c710..000000000
--- a/app/internal_packages/main-calendar/lib/core/mini-month-view.tsx
+++ /dev/null
@@ -1,138 +0,0 @@
-import _ from 'underscore';
-import React from 'react';
-import PropTypes from 'prop-types';
-import moment from 'moment';
-import classnames from 'classnames';
-
-export default class MiniMonthView extends React.Component<
- { value: number; onChange: (val: number) => void },
- { shownYear: number; shownMonth: number }
-> {
- static displayName = 'MiniMonthView';
-
- static propTypes = {
- value: PropTypes.number,
- onChange: PropTypes.func,
- };
-
- static defaultProps = {
- value: moment().valueOf(),
- onChange: () => {},
- };
-
- today = moment();
-
- constructor(props) {
- super(props);
- this.state = this._stateFromProps(props);
- }
-
- componentWillReceiveProps(newProps) {
- this.setState(this._stateFromProps(newProps));
- }
-
- _stateFromProps(props) {
- const m = props.value ? moment(props.value) : moment();
- return {
- shownYear: m.year(),
- shownMonth: m.month(),
- };
- }
-
- _shownMonthMoment() {
- return moment([this.state.shownYear, this.state.shownMonth]);
- }
-
- _changeMonth = by => {
- const newMonth = this.state.shownMonth + by;
- const newMoment = this._shownMonthMoment().month(newMonth);
- this.setState({
- shownYear: newMoment.year(),
- shownMonth: newMoment.month(),
- });
- };
-
- _renderLegend() {
- const weekdayGen = moment([2016]);
- const legendEls = [];
- for (let i = 0; i < 7; i++) {
- const dayStr = weekdayGen.weekday(i).format('dd'); // Locale aware!
- legendEls.push(
-
- {dayStr}
-
- );
- }
- return {legendEls}
;
- }
-
- _onClickDay = event => {
- if (!event.target.dataset.timestamp) {
- return;
- }
- const newVal = moment(parseInt(event.target.dataset.timestamp, 10)).valueOf();
- this.props.onChange(newVal);
- };
-
- _isSameDay(m1, m2) {
- return m1.dayOfYear() === m2.dayOfYear() && m1.year() === m2.year();
- }
-
- _renderDays() {
- const dayIter = this._shownMonthMoment().date(1);
- const startWeek = dayIter.week();
- const curMonth = this.state.shownMonth;
- const endWeek = moment(dayIter)
- .date(dayIter.daysInMonth())
- .week();
- const weekEls = [];
- const valDay = moment(this.props.value);
- for (let week = startWeek; week <= endWeek; week++) {
- dayIter.week(week); // Locale aware!
- const dayEls = [];
- for (let weekday = 0; weekday < 7; weekday++) {
- dayIter.weekday(weekday); // Locale aware!
- const dayStr = dayIter.format('D');
- const className = classnames({
- day: true,
- today: this._isSameDay(dayIter, this.today),
- 'cur-day': this._isSameDay(dayIter, valDay),
- 'cur-month': dayIter.month() === curMonth,
- });
- dayEls.push(
-
- {dayStr}
-
- );
- }
- weekEls.push(
-
- {dayEls}
-
- );
- }
- return (
-
- {weekEls}
-
- );
- }
-
- render() {
- return (
-
-
-
- ‹
-
-
{this._shownMonthMoment().format('MMMM YYYY')}
-
- ›
-
-
- {this._renderLegend()}
- {this._renderDays()}
-
- );
- }
-}
diff --git a/app/internal_packages/main-calendar/lib/core/month-view.tsx b/app/internal_packages/main-calendar/lib/core/month-view.tsx
index 37358283a..d63554e7a 100644
--- a/app/internal_packages/main-calendar/lib/core/month-view.tsx
+++ b/app/internal_packages/main-calendar/lib/core/month-view.tsx
@@ -1,11 +1,12 @@
import React from 'react';
-import PropTypes from 'prop-types';
+import { MailspringCalendarViewProps } from './mailspring-calendar';
+import { CalendarView } from './calendar-constants';
-export default class MonthView extends React.Component<{ changeView: (view: string) => void }> {
+export class MonthView extends React.Component {
static displayName = 'MonthView';
_onClick = () => {
- this.props.changeView('WeekView');
+ this.props.onChangeView(CalendarView.WEEK);
};
render() {
diff --git a/app/internal_packages/main-calendar/lib/core/top-banner.tsx b/app/internal_packages/main-calendar/lib/core/top-banner.tsx
deleted file mode 100644
index 09e96a251..000000000
--- a/app/internal_packages/main-calendar/lib/core/top-banner.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-export default class TopBanner extends React.Component<{ bannerComponents: React.ReactNode }> {
- static displayName = 'TopBanner';
-
- static propTypes = {
- bannerComponents: PropTypes.node,
- };
-
- render() {
- if (!this.props.bannerComponents) {
- return false;
- }
- return {this.props.bannerComponents}
;
- }
-}
diff --git a/app/internal_packages/main-calendar/lib/core/week-view-all-day-events.tsx b/app/internal_packages/main-calendar/lib/core/week-view-all-day-events.tsx
index 376ac72c2..6cd982541 100644
--- a/app/internal_packages/main-calendar/lib/core/week-view-all-day-events.tsx
+++ b/app/internal_packages/main-calendar/lib/core/week-view-all-day-events.tsx
@@ -1,6 +1,9 @@
import React from 'react';
import { Event, Utils } from 'mailspring-exports';
-import CalendarEvent from './calendar-event';
+import { CalendarEvent } from './calendar-event';
+import { EventOccurrence } from './calendar-data-source';
+import { OverlapByEventId } from './week-view-helpers';
+import { EventRendererProps } from './mailspring-calendar';
/*
* Displays the all day events across the top bar of the week event view.
@@ -9,16 +12,16 @@ import CalendarEvent from './calendar-event';
* we can use `shouldComponentUpdate` to selectively re-render these
* events.
*/
-interface WeekViewAllDayEventsProps {
+interface WeekViewAllDayEventsProps extends EventRendererProps {
end: number;
start: number;
height: number;
minorDim: number;
- allDayEvents: Event[];
- allDayOverlap: any;
+ allDayEvents: EventOccurrence[];
+ allDayOverlap: OverlapByEventId;
}
-export default class WeekViewAllDayEvents extends React.Component {
+export class WeekViewAllDayEvents extends React.Component {
static displayName = 'WeekViewAllDayEvents';
shouldComponentUpdate(nextProps, nextState) {
@@ -26,23 +29,27 @@ export default class WeekViewAllDayEvents extends React.Component {
- return (
-
- );
- });
+ const { height, allDayEvents, allDayOverlap, selectedEvents, focusedEvent } = this.props;
+
return (
-
- {eventComponents}
+
+ {allDayEvents.map(e => (
+
+ ))}
);
}
diff --git a/app/internal_packages/main-calendar/lib/core/week-view-event-column.tsx b/app/internal_packages/main-calendar/lib/core/week-view-event-column.tsx
index a0594c49b..3789ff1c6 100644
--- a/app/internal_packages/main-calendar/lib/core/week-view-event-column.tsx
+++ b/app/internal_packages/main-calendar/lib/core/week-view-event-column.tsx
@@ -1,8 +1,10 @@
import React from 'react';
import moment, { Moment } from 'moment';
import classnames from 'classnames';
-import { PropTypes, Utils, Event } from 'mailspring-exports';
-import CalendarEvent from './calendar-event';
+import { Utils, Event } from 'mailspring-exports';
+import { CalendarEvent } from './calendar-event';
+import { EventOccurrence } from './calendar-data-source';
+import { overlapForEvents } from './week-view-helpers';
/*
* This display a single column of events in the Week View.
@@ -11,71 +13,63 @@ import CalendarEvent from './calendar-event';
* column-by-column basis.
*/
interface WeekViewEventColumnProps {
- events: Event[];
+ events: EventOccurrence[];
day: Moment;
dayEnd: number;
- focusedEvent: Event;
- eventOverlap: any;
- onEventClick: () => void;
- onEventDoubleClick: () => void;
- onEventFocused: () => void;
- selectedEvents: Event[];
+ focusedEvent: EventOccurrence;
+ onEventClick: (e: React.MouseEvent
, event: EventOccurrence) => void;
+ onEventDoubleClick: (event: EventOccurrence) => void;
+ onEventFocused: (event: EventOccurrence) => void;
+ selectedEvents: EventOccurrence[];
}
-export default class WeekViewEventColumn extends React.Component {
+export class WeekViewEventColumn extends React.Component {
static displayName = 'WeekViewEventColumn';
shouldComponentUpdate(nextProps, nextState) {
return !Utils.isEqualReact(nextProps, this.props) || !Utils.isEqualReact(nextState, this.state);
}
- renderEvents() {
+ render() {
const {
events,
focusedEvent,
selectedEvents,
- eventOverlap,
dayEnd,
day,
onEventClick,
onEventDoubleClick,
onEventFocused,
} = this.props;
- return events.map(e => (
-
- ));
- }
- render() {
const className = classnames({
'event-column': true,
- weekend: this.props.day.day() === 0 || this.props.day.day() === 6,
+ weekend: day.day() === 0 || day.day() === 6,
});
- const end = moment(this.props.day)
+ const overlap = overlapForEvents(events);
+ const end = moment(day)
.add(1, 'day')
.subtract(1, 'millisecond')
.valueOf();
+
return (
-
- {this.renderEvents()}
+
+ {events.map(e => (
+
+ ))}
);
}
diff --git a/app/internal_packages/main-calendar/lib/core/week-view-helpers.ts b/app/internal_packages/main-calendar/lib/core/week-view-helpers.ts
new file mode 100644
index 000000000..15ed3162d
--- /dev/null
+++ b/app/internal_packages/main-calendar/lib/core/week-view-helpers.ts
@@ -0,0 +1,132 @@
+import { EventOccurrence } from './calendar-data-source';
+import moment, { Moment } from 'moment';
+import { Utils } from 'mailspring-exports';
+
+// This pre-fetches from Utils to prevent constant disc access
+const overlapsBounds = Utils.overlapsBounds;
+
+export interface OverlapByEventId {
+ [id: string]: { concurrentEvents: number; order: null | number };
+}
+
+/*
+ * Computes the overlap between a set of events in not O(n^2).
+ *
+ * Returns a hash keyed by event id whose value is an object:
+ * - concurrentEvents: number of concurrent events
+ * - order: the order in that series of concurrent events
+ */
+export function overlapForEvents(events: EventOccurrence[]) {
+ const eventsByTime: { [unix: number]: EventOccurrence[] } = {};
+
+ for (const event of events) {
+ if (!eventsByTime[event.start]) {
+ eventsByTime[event.start] = [];
+ }
+ if (!eventsByTime[event.end]) {
+ eventsByTime[event.end] = [];
+ }
+ eventsByTime[event.start].push(event);
+ eventsByTime[event.end].push(event);
+ }
+ const sortedTimes = Object.keys(eventsByTime)
+ .map(Number)
+ .sort();
+
+ const overlapById: OverlapByEventId = {};
+ let ongoingEvents: EventOccurrence[] = [];
+
+ for (const t of sortedTimes) {
+ // Process all event start/ends during this time to keep our
+ // "ongoingEvents" set correct.
+ for (const e of eventsByTime[t]) {
+ if (e.start === t) {
+ overlapById[e.id] = { concurrentEvents: 1, order: null };
+ ongoingEvents.push(e);
+ }
+ if (e.end === t) {
+ ongoingEvents = ongoingEvents.filter(o => o.id !== e.id);
+ }
+ }
+
+ // Write concurrency for all the events currently ongoing if they haven't
+ // been assigned values already
+ for (const e of ongoingEvents) {
+ const numEvents = findMaxConcurrent(ongoingEvents, overlapById);
+ overlapById[e.id].concurrentEvents = numEvents;
+ if (overlapById[e.id].order === null) {
+ // Don't re-assign the order.
+ const order = findAvailableOrder(ongoingEvents, overlapById);
+ overlapById[e.id].order = order;
+ }
+ }
+ }
+ return overlapById;
+}
+
+export function findMaxConcurrent(ongoing: EventOccurrence[], overlapById: OverlapByEventId) {
+ return Math.max(1, ongoing.length, ...ongoing.map(e => overlapById[e.id].concurrentEvents));
+}
+
+export function findAvailableOrder(ongoing: EventOccurrence[], overlapById: OverlapByEventId) {
+ const orders = ongoing.map(e => overlapById[e.id].order);
+ let order = 1;
+ while (true) {
+ if (!orders.includes(order)) {
+ return order;
+ }
+ order += 1;
+ }
+}
+
+export function maxConcurrentEvents(eventOverlap: OverlapByEventId) {
+ return Math.max(-1, ...Object.values(eventOverlap).map(o => o.concurrentEvents));
+}
+
+export function eventsGroupedByDay(events: EventOccurrence[], days: Moment[]) {
+ const map: { allDay: EventOccurrence[]; [dayUnix: string]: EventOccurrence[] } = { allDay: [] };
+
+ const unixDays = days.map(d => d.unix());
+ unixDays.forEach(day => {
+ map[`${day}`] = [];
+ });
+
+ events.forEach(event => {
+ if (event.isAllDay) {
+ map.allDay.push(event);
+ } else {
+ for (const day of unixDays) {
+ const bounds = {
+ start: day,
+ end: day + 24 * 60 * 60 - 1,
+ };
+ if (overlapsBounds(bounds, event)) {
+ map[`${day}`].push(event);
+ }
+ }
+ }
+ });
+
+ return map;
+}
+
+export const DAY_DUR = 24 * 60 * 60;
+export const TICK_STEP = 30 * 60;
+export const TICKS_PER_DAY = DAY_DUR / TICK_STEP;
+
+export function* tickGenerator(type: 'major' | 'minor', tickHeight: number) {
+ const step = TICK_STEP * 2;
+ const skip = TICK_STEP * 2;
+ const stepStart = type === 'minor' ? TICK_STEP : 0;
+
+ // We only use a moment object so we can properly localize the "time"
+ // part. The day is irrelevant. We just need to make sure we're
+ // picking a non-DST boundary day.
+ const time = moment([2015, 1, 1]).add(stepStart, 'seconds');
+
+ for (let tsec = stepStart; tsec <= DAY_DUR; tsec += step) {
+ const y = (tsec / TICK_STEP) * tickHeight;
+ yield { time, y };
+ time.add(skip, 'seconds');
+ }
+}
diff --git a/app/internal_packages/main-calendar/lib/core/week-view.tsx b/app/internal_packages/main-calendar/lib/core/week-view.tsx
index bc4d7f6df..fe3efd0ef 100644
--- a/app/internal_packages/main-calendar/lib/core/week-view.tsx
+++ b/app/internal_packages/main-calendar/lib/core/week-view.tsx
@@ -3,65 +3,43 @@ import _ from 'underscore';
import moment, { Moment } from 'moment-timezone';
import classnames from 'classnames';
import React from 'react';
-import ReactDOM from 'react-dom';
-import { PropTypes, Utils, Event } from 'mailspring-exports';
-import { ScrollRegion } from 'mailspring-component-kit';
-import TopBanner from './top-banner';
-import HeaderControls from './header-controls';
-import FooterControls from './footer-controls';
-import CalendarDataSource from './calendar-data-source';
-import EventGridBackground from './event-grid-background';
-import WeekViewEventColumn from './week-view-event-column';
-import WeekViewAllDayEvents from './week-view-all-day-events';
-import CalendarEventContainer from './calendar-event-container';
-import CurrentTimeIndicator from './current-time-indicator';
+import { ScrollRegion, InjectedComponentSet } from 'mailspring-component-kit';
+import { HeaderControls } from './header-controls';
+import { EventOccurrence } from './calendar-data-source';
+import { EventGridBackground } from './event-grid-background';
+import { WeekViewEventColumn } from './week-view-event-column';
+import { WeekViewAllDayEvents } from './week-view-all-day-events';
+import { CalendarEventContainer } from './calendar-event-container';
+import { CurrentTimeIndicator } from './current-time-indicator';
import { Disposable } from 'rx-core';
+import {
+ overlapForEvents,
+ maxConcurrentEvents,
+ eventsGroupedByDay,
+ TICKS_PER_DAY,
+ tickGenerator,
+} from './week-view-helpers';
+import { MailspringCalendarViewProps } from './mailspring-calendar';
const BUFFER_DAYS = 7; // in each direction
const DAYS_IN_VIEW = 7;
const MIN_INTERVAL_HEIGHT = 21;
-const DAY_DUR = moment.duration(1, 'day').as('seconds');
-const INTERVAL_TIME = moment.duration(30, 'minutes').as('seconds');
+const DAY_PORTION_SHOWN_VERTICALLY = 11 / 24;
-// This pre-fetches from Utils to prevent constant disc access
-const overlapsBounds = Utils.overlapsBounds;
-
-interface WeekViewProps {
- dataSource: CalendarDataSource;
- currentMoment: Moment;
- focusedEvent: Event;
- bannerComponents: React.ReactChildren;
- headerComponents: React.ReactChildren;
- footerComponents: React.ReactChildren;
- disabledCalendars: string[];
- changeCurrentView: () => void;
- changeCurrentMoment: (moment: Moment) => void;
- onCalendarMouseUp: () => void;
- onCalendarMouseDown: () => void;
- onCalendarMouseMove: () => void;
- onEventClick: () => void;
- onEventDoubleClick: () => void;
- onEventFocused: () => void;
- selectedEvents: Event[];
-}
-
-export default class WeekView extends React.Component<
- WeekViewProps,
- { intervalHeight: number; events: Event[] }
+export class WeekView extends React.Component<
+ MailspringCalendarViewProps,
+ { intervalHeight: number; events: EventOccurrence[] }
> {
static displayName = 'WeekView';
- static defaultProps = {
- changeCurrentView: () => {},
- bannerComponents: false,
- headerComponents: false,
- footerComponents: false,
- };
-
_waitingForShift = 0;
_mounted = false;
+ _scrollbar = React.createRef
();
_sub?: Disposable;
- _lastWrapHeight: number;
+
+ _legendWrapEl = React.createRef();
+ _calendarWrapEl = React.createRef();
+ _gridScrollRegion = React.createRef();
constructor(props) {
super(props);
@@ -74,20 +52,23 @@ export default class WeekView extends React.Component<
componentDidMount() {
this._mounted = true;
this._centerScrollRegion();
- this._setIntervalHeight();
- window.addEventListener('resize', this._setIntervalHeight, true);
- const wrap = ReactDOM.findDOMNode(this.refs.calendarAreaWrap) as HTMLElement;
+
+ // Shift ourselves right by a week because we preload 7 days on either side
+ const wrap = this._calendarWrapEl.current;
wrap.scrollLeft += wrap.clientWidth;
+
this.updateSubscription();
+ this._setIntervalHeight();
}
componentDidUpdate(prevProps) {
- this._setIntervalHeight();
- const wrap = ReactDOM.findDOMNode(this.refs.calendarAreaWrap) as HTMLElement;
- wrap.scrollLeft += this._waitingForShift;
- this._waitingForShift = 0;
+ if (this._waitingForShift) {
+ const wrap = this._calendarWrapEl.current;
+ wrap.scrollLeft += this._waitingForShift;
+ this._waitingForShift = 0;
+ }
if (
- prevProps.currentMoment !== this.props.currentMoment ||
+ prevProps.focusedMoment !== this.props.focusedMoment ||
prevProps.disabledCalendars !== this.props.disabledCalendars
) {
this.updateSubscription();
@@ -96,8 +77,7 @@ export default class WeekView extends React.Component<
componentWillUnmount() {
this._mounted = false;
- this._sub.dispose();
- window.removeEventListener('resize', this._setIntervalHeight);
+ this._sub && this._sub.dispose();
}
// Indirection for testing purposes
@@ -106,17 +86,14 @@ export default class WeekView extends React.Component<
}
updateSubscription() {
- if (this._sub) {
- this._sub.dispose();
- }
-
- const { start, end } = this._calculateMomentRange();
+ const { bufferedStart, bufferedEnd } = this._calculateMomentRange();
+ this._sub && this._sub.dispose();
this._sub = this.props.dataSource
.buildObservable({
disabledCalendars: this.props.disabledCalendars,
- startTime: start.unix(),
- endTime: end.unix(),
+ startUnix: bufferedStart.unix(),
+ endUnix: bufferedEnd.unix(),
})
.subscribe(state => {
this.setState(state);
@@ -124,37 +101,39 @@ export default class WeekView extends React.Component<
}
_calculateMomentRange() {
- const { currentMoment } = this.props;
- let start;
+ const { focusedMoment } = this.props;
// NOTE: Since we initialize a new time from one of the properties of
- // the props.currentMomet, we need to check for the timezone!
+ // the props.focusedMoment, we need to check for the timezone!
//
// Other relative operations (like adding or subtracting time) are
// independent of a timezone.
- const tz = currentMoment.tz();
- if (tz) {
- start = moment.tz([currentMoment.year()], tz);
- } else {
- start = moment([currentMoment.year()]);
- }
-
- start = start
+ const tz = focusedMoment.tz();
+ const start = (tz ? moment.tz([focusedMoment.year()], tz) : moment([focusedMoment.year()]))
.weekday(0)
- .week(currentMoment.week())
- .subtract(BUFFER_DAYS, 'days');
+ .week(focusedMoment.week());
- const end = moment(start)
- .add(BUFFER_DAYS * 2 + DAYS_IN_VIEW, 'days')
+ const end = start
+ .clone()
+ .add(DAYS_IN_VIEW, 'days')
.subtract(1, 'millisecond');
- return { start, end };
+ return {
+ visibleStart: start,
+ visibleEnd: end,
+
+ bufferedStart: start.clone().subtract(BUFFER_DAYS, 'days'),
+ bufferedEnd: moment(end)
+ .add(BUFFER_DAYS, 'days')
+ .subtract(1, 'millisecond'),
+ };
}
- _renderDateLabel = (day, idx) => {
+ _renderDateLabel = (day: Moment, idx: number) => {
const className = classnames({
'day-label-wrap': true,
'is-today': this._isToday(day),
+ 'is-hard-stop': day.weekday() == 1,
});
return (
@@ -171,246 +150,79 @@ export default class WeekView extends React.Component<
return todayDayOfYear === day.dayOfYear() && todayYear === day.year();
}
- _renderEventColumn = (eventsByDay, day) => {
- const dayUnix = day.unix();
- const events = eventsByDay[dayUnix];
- return (
-
- );
- };
-
- _allDayEventHeight(allDayOverlap) {
- if (_.size(allDayOverlap) === 0) {
- return 0;
- }
- return this._maxConcurrentEvents(allDayOverlap) * MIN_INTERVAL_HEIGHT + 1;
- }
-
- /*
- * Computes the overlap between a set of events in not O(n^2).
- *
- * Returns a hash keyed by event id whose value is an object:
- * - concurrentEvents: number of concurrent events
- * - order: the order in that series of concurrent events
- */
- _eventOverlap(events) {
- const times = {};
- for (const event of events) {
- if (!times[event.start]) {
- times[event.start] = [];
- }
- if (!times[event.end]) {
- times[event.end] = [];
- }
- times[event.start].push(event);
- times[event.end].push(event);
- }
- const sortedTimes = Object.keys(times)
- .map(k => parseInt(k, 10))
- .sort();
- const overlapById = {};
- let startedEvents = [];
- for (const t of sortedTimes) {
- for (const e of times[t]) {
- if (e.start === t) {
- overlapById[e.id] = { concurrentEvents: 1, order: null };
- startedEvents.push(e);
- }
- if (e.end === t) {
- startedEvents = _.reject(startedEvents, o => o.id === e.id);
- }
- }
- for (const e of startedEvents) {
- if (!overlapById[e.id]) {
- overlapById[e.id] = {};
- }
- const numEvents = this._findMaxConcurrent(startedEvents, overlapById);
- overlapById[e.id].concurrentEvents = numEvents;
- if (overlapById[e.id].order === null) {
- // Dont' re-assign the order.
- const order = this._findAvailableOrder(startedEvents, overlapById);
- overlapById[e.id].order = order;
- }
- }
- }
- return overlapById;
- }
-
- _findMaxConcurrent(startedEvents, overlapById) {
- let max = 1;
- for (const e of startedEvents) {
- max = Math.max(overlapById[e.id].concurrentEvents || 1, max);
- }
- return Math.max(max, startedEvents.length);
- }
-
- _findAvailableOrder(startedEvents, overlapById) {
- const orders = startedEvents.map(e => overlapById[e.id].order);
- let order = 1;
- while (true) {
- if (orders.indexOf(order) === -1) {
- return order;
- }
- order += 1;
- }
- }
-
- _maxConcurrentEvents(eventOverlap) {
- let maxConcurrent = -1;
- _.each(eventOverlap, ({ concurrentEvents }) => {
- maxConcurrent = Math.max(concurrentEvents, maxConcurrent);
- });
- return maxConcurrent;
- }
-
_daysInView() {
- const { start } = this._calculateMomentRange();
- const days = [];
+ const { bufferedStart } = this._calculateMomentRange();
+ const days: Moment[] = [];
for (let i = 0; i < DAYS_IN_VIEW + BUFFER_DAYS * 2; i++) {
// moment::weekday is locale aware since some weeks start on diff
// days. See http://momentjs.com/docs/#/get-set/weekday/
- days.push(moment(start).weekday(i));
+ days.push(moment(bufferedStart).weekday(i));
}
return days;
}
- _headerComponents() {
- const left = (
-
- );
- const right = false;
- return [left, right, this.props.headerComponents];
- }
-
_onClickToday = () => {
- this.props.changeCurrentMoment(this._now());
+ this.props.onChangeFocusedMoment(this._now());
};
_onClickNextWeek = () => {
- const newMoment = moment(this.props.currentMoment).add(1, 'week');
- this.props.changeCurrentMoment(newMoment);
+ const newMoment = moment(this.props.focusedMoment).add(1, 'week');
+ this.props.onChangeFocusedMoment(newMoment);
};
_onClickPrevWeek = () => {
- const newMoment = moment(this.props.currentMoment).subtract(1, 'week');
- this.props.changeCurrentMoment(newMoment);
+ const newMoment = moment(this.props.focusedMoment).subtract(1, 'week');
+ this.props.onChangeFocusedMoment(newMoment);
};
- _gridHeight() {
- return DAY_DUR / INTERVAL_TIME * this.state.intervalHeight;
- }
-
_centerScrollRegion() {
- const wrap = ReactDOM.findDOMNode(this.refs.eventGridWrap) as HTMLElement;
- wrap.scrollTop = this._gridHeight() / 2 - wrap.getBoundingClientRect().height / 2;
- }
-
- // This generates the ticks used mark the event grid and the
- // corresponding legend in the week view.
- *_tickGenerator({ type }) {
- const height = this._gridHeight();
-
- let step = INTERVAL_TIME;
- let stepStart = 0;
-
- // We only use a moment object so we can properly localize the "time"
- // part. The day is irrelevant. We just need to make sure we're
- // picking a non-DST boundary day.
- const start = moment([2015, 1, 1]);
-
- let duration = INTERVAL_TIME;
- if (type === 'major') {
- step = INTERVAL_TIME * 2;
- duration += INTERVAL_TIME;
- } else if (type === 'minor') {
- step = INTERVAL_TIME * 2;
- stepStart = INTERVAL_TIME;
- duration += INTERVAL_TIME;
- start.add(INTERVAL_TIME, 'seconds');
- }
-
- const curTime = moment(start);
- for (let tsec = stepStart; tsec <= DAY_DUR; tsec += step) {
- const y = tsec / DAY_DUR * height;
- yield { time: curTime, yPos: y };
- curTime.add(duration, 'seconds');
- }
+ const wrap = this._gridScrollRegion.current.viewportEl;
+ wrap.scrollTop = wrap.scrollHeight / 2 - wrap.clientHeight / 2;
}
_setIntervalHeight = () => {
if (!this._mounted) {
return;
- } // Resize unmounting is delayed in tests
- const wrap = ReactDOM.findDOMNode(this.refs.eventGridWrap) as HTMLElement;
- const wrapHeight = wrap.getBoundingClientRect().height;
- if (this._lastWrapHeight === wrapHeight) {
- return;
}
- this._lastWrapHeight = wrapHeight;
- const numIntervals = Math.floor(DAY_DUR / INTERVAL_TIME);
- (ReactDOM.findDOMNode(
- this.refs.eventGridLegendWrap
- ) as HTMLElement).style.height = `${wrapHeight}px`;
+ const viewportHeight = this._gridScrollRegion.current.viewportEl.clientHeight;
+ this._legendWrapEl.current.style.height = `${viewportHeight}px`;
+
this.setState({
- intervalHeight: Math.max(wrapHeight / numIntervals, MIN_INTERVAL_HEIGHT),
+ intervalHeight: Math.max(
+ viewportHeight / (TICKS_PER_DAY * DAY_PORTION_SHOWN_VERTICALLY),
+ MIN_INTERVAL_HEIGHT
+ ),
});
};
- _onScrollGrid = event => {
- (ReactDOM.findDOMNode(this.refs.eventGridLegendWrap) as HTMLElement).scrollTop =
- event.target.scrollTop;
- };
+ _onScrollCalendarArea = (event: React.UIEvent) => {
+ console.log(event.currentTarget.scrollLeft);
+ // if (!event.currentTarget.scrollLeft || this._waitingForShift) {
+ // return;
+ // }
- _onScrollCalendarArea = event => {
- if (!event.currentTarget.scrollLeft || this._waitingForShift) {
- return;
- }
+ // const edgeWidth = (event.currentTarget.clientWidth / DAYS_IN_VIEW) * 2;
- const edgeWidth = event.currentTarget.clientWidth / DAYS_IN_VIEW * 2;
-
- if (event.currentTarget.scrollLeft < edgeWidth) {
- this._waitingForShift = event.currentTarget.clientWidth;
- this._onClickPrevWeek();
- } else if (
- event.currentTarget.scrollLeft >
- event.currentTarget.scrollWidth - event.currentTarget.clientWidth - edgeWidth
- ) {
- this._waitingForShift = -event.currentTarget.clientWidth;
- this._onClickNextWeek();
- }
+ // if (event.currentTarget.scrollLeft < edgeWidth) {
+ // this._waitingForShift = event.currentTarget.clientWidth;
+ // this._onClickPrevWeek();
+ // } else if (
+ // event.currentTarget.scrollLeft >
+ // event.currentTarget.scrollWidth - event.currentTarget.clientWidth - edgeWidth
+ // ) {
+ // this._waitingForShift = -event.currentTarget.clientWidth;
+ // this._onClickNextWeek();
+ // }
};
_renderEventGridLabels() {
const labels = [];
- let centering = 0;
- for (const { time, yPos } of this._tickGenerator({ type: 'major' })) {
- const hr = time.format('LT'); // Locale time. 2:00 pm or 14:00
- const style = { top: yPos - centering };
+ for (const { time, y } of tickGenerator('major', this.state.intervalHeight)) {
labels.push(
-
- {hr}
+
+ {time.format('LT')}
);
- centering = 8; // center all except the 1st one.
}
return labels.slice(0, labels.length - 1);
}
@@ -419,75 +231,57 @@ export default class WeekView extends React.Component<
return (BUFFER_DAYS * 2 + DAYS_IN_VIEW) / DAYS_IN_VIEW;
}
- // We calculate events by days so we only need to iterate through all
- // events in the span once.
- _eventsByDay(days) {
- const map = { allDay: [] };
- const unixDays = days.map(d => d.unix());
- unixDays.forEach(d => {
- map[d] = [];
- return;
- });
- for (const event of this.state.events) {
- if (event.isAllDay) {
- map.allDay.push(event);
- } else {
- for (const day of unixDays) {
- const bounds = {
- start: day,
- end: day + DAY_DUR - 1,
- };
- if (overlapsBounds(bounds, event)) {
- map[day].push(event);
- }
- }
- }
- }
- return map;
- }
-
render() {
const days = this._daysInView();
+ const eventsByDay = eventsGroupedByDay(this.state.events, days);
const todayColumnIdx = days.findIndex(d => this._isToday(d));
- const eventsByDay = this._eventsByDay(days);
- const allDayOverlap = this._eventOverlap(eventsByDay.allDay);
- const tickGen = this._tickGenerator.bind(this);
- const gridHeight = this._gridHeight();
+ const totalHeight = TICKS_PER_DAY * this.state.intervalHeight;
- const { start: startMoment, end: endMoment } = this._calculateMomentRange();
+ const range = this._calculateMomentRange();
- const start = moment(startMoment).add(BUFFER_DAYS, 'days');
- const end = moment(endMoment).subtract(BUFFER_DAYS, 'days');
- const headerText = `${start.format('MMMM D')} - ${end.format('MMMM D YYYY')}`;
+ const headerText = [
+ range.visibleStart.format('MMMM D'),
+ range.visibleEnd.format('MMMM D YYYY'),
+ ].join(' - ');
+
+ const allDayOverlap = overlapForEvents(eventsByDay.allDay);
+ const allDayBarHeight = eventsByDay.allDay.length
+ ? maxConcurrentEvents(allDayOverlap) * MIN_INTERVAL_HEIGHT + 1
+ : 0;
return (
-
+
+
+
+ >
+
+
-
+
All Day
-
-
+
+
{this._renderEventGridLabels()}
@@ -495,56 +289,69 @@ export default class WeekView extends React.Component<
{days.map(this._renderDateLabel)}
this.refs.scrollbar}
- onScroll={this._onScrollGrid}
+ ref={this._gridScrollRegion}
+ scrollbarRef={this._scrollbar}
+ onScroll={event => (this._legendWrapEl.current.scrollTop = event.target.scrollTop)}
+ onViewportResize={this._setIntervalHeight}
style={{ width: `${this._bufferRatio() * 100}%` }}
>
-
- {days.map(_.partial(this._renderEventColumn, eventsByDay))}
+
+ {days.map(day => (
+
+ ))}
BUFFER_DAYS && todayColumnIdx <= BUFFER_DAYS + DAYS_IN_VIEW
}
- gridHeight={gridHeight}
+ gridHeight={totalHeight}
numColumns={BUFFER_DAYS * 2 + DAYS_IN_VIEW}
todayColumnIdx={todayColumnIdx}
/>
this.refs.eventGridWrap}
+ ref={this._scrollbar}
+ getScrollRegion={() => this._gridScrollRegion.current}
/>
-
-
);
diff --git a/app/internal_packages/main-calendar/lib/event-description-frame.tsx b/app/internal_packages/main-calendar/lib/event-description-frame.tsx
index f074893da..f158390f2 100644
--- a/app/internal_packages/main-calendar/lib/event-description-frame.tsx
+++ b/app/internal_packages/main-calendar/lib/event-description-frame.tsx
@@ -1,15 +1,11 @@
import { EventedIFrame } from 'mailspring-component-kit';
import React from 'react';
import ReactDOM from 'react-dom';
-import { PropTypes, Utils } from 'mailspring-exports';
+import { Utils } from 'mailspring-exports';
-export default class EmailFrame extends React.Component<{ content: string }> {
+export class EmailFrame extends React.Component<{ content: string }> {
static displayName = 'EmailFrame';
- static propTypes = {
- content: PropTypes.string.isRequired,
- };
-
_mounted = false;
_unlisten?: () => void;
_iframeComponent: EventedIFrame;
@@ -54,7 +50,7 @@ export default class EmailFrame extends React.Component<{ content: string }> {
);
doc.close();
- // autolink(doc, {async: true});
+ // autolink(doc.body, {async: true});
// autoscaleImages(doc);
// addInlineDownloadPrompts(doc);
diff --git a/app/internal_packages/main-calendar/lib/main.tsx b/app/internal_packages/main-calendar/lib/main.tsx
index a74c73499..622e3d19b 100644
--- a/app/internal_packages/main-calendar/lib/main.tsx
+++ b/app/internal_packages/main-calendar/lib/main.tsx
@@ -1,18 +1,22 @@
import React from 'react';
import { WorkspaceStore, ComponentRegistry } from 'mailspring-exports';
-import CalendarWrapper from './calendar-wrapper';
-import QuickEventButton from './quick-event-button';
+import { QuickEventButton } from './quick-event-button';
+import { MailspringCalendar } from './core/mailspring-calendar';
+
+const Notice = () =>
+ AppEnv.inDevMode() ? (
+
+ ) : (
+
+ Calendar is launching later this year! This preview is read-only and only supports Google
+ calendar.
+
+ );
-const Notice = () => (
-
- Calendar is launching later this year! This preview is read-only and only supports Google
- calendar.
-
-);
Notice.displayName = 'Notice';
export function activate() {
- ComponentRegistry.register(CalendarWrapper, {
+ ComponentRegistry.register(MailspringCalendar, {
location: WorkspaceStore.Location.Center,
});
ComponentRegistry.register(Notice, {
@@ -24,6 +28,6 @@ export function activate() {
}
export function deactivate() {
- ComponentRegistry.unregister(CalendarWrapper);
+ ComponentRegistry.unregister(MailspringCalendar);
ComponentRegistry.unregister(QuickEventButton);
}
diff --git a/app/internal_packages/main-calendar/lib/quick-event-button.tsx b/app/internal_packages/main-calendar/lib/quick-event-button.tsx
index 571230188..6493304fd 100644
--- a/app/internal_packages/main-calendar/lib/quick-event-button.tsx
+++ b/app/internal_packages/main-calendar/lib/quick-event-button.tsx
@@ -1,9 +1,9 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Actions } from 'mailspring-exports';
-import QuickEventPopover from './quick-event-popover';
+import { QuickEventPopover } from './quick-event-popover';
-export default class QuickEventButton extends React.Component {
+export class QuickEventButton extends React.Component<{}> {
static displayName = 'QuickEventButton';
onClick = event => {
diff --git a/app/internal_packages/main-calendar/lib/quick-event-popover.tsx b/app/internal_packages/main-calendar/lib/quick-event-popover.tsx
index d5ff84430..a5553f62a 100644
--- a/app/internal_packages/main-calendar/lib/quick-event-popover.tsx
+++ b/app/internal_packages/main-calendar/lib/quick-event-popover.tsx
@@ -1,13 +1,14 @@
import React from 'react';
-import { Actions, Calendar, DatabaseStore, DateUtils, Event } from 'mailspring-exports';
+import { Actions, Calendar, DatabaseStore, DateUtils, Event, localized } from 'mailspring-exports';
import { Moment } from 'moment';
-interface QuickEventPopoverSttae {
+interface QuickEventPopoverState {
start: Moment | null;
end: Moment | null;
leftoverText: string | null;
}
-export default class QuickEventPopover extends React.Component<{}, QuickEventPopoverSttae> {
+
+export class QuickEventPopover extends React.Component<{}, QuickEventPopoverState> {
constructor(props) {
super(props);
this.state = {
@@ -18,7 +19,10 @@ export default class QuickEventPopover extends React.Component<{}, QuickEventPop
}
onInputKeyDown = event => {
- const { key, target: { value } } = event;
+ const {
+ key,
+ target: { value },
+ } = event;
if (value.length > 0 && ['Enter', 'Return'].includes(key)) {
// This prevents onInputChange from being fired
event.stopPropagation();
@@ -31,24 +35,29 @@ export default class QuickEventPopover extends React.Component<{}, QuickEventPop
this.setState(DateUtils.parseDateString(event.target.value));
};
- createEvent = async ({ leftoverText, start, end }) => {
+ createEvent = async ({
+ leftoverText,
+ start,
+ end,
+ }: {
+ leftoverText: string;
+ start: Moment;
+ end: Moment;
+ }) => {
const allCalendars = await DatabaseStore.findAll
(Calendar);
- if (allCalendars.length === 0) {
- throw new Error("Can't create an event, you have no calendars");
- }
- const cals = allCalendars.filter(c => !c.readOnly);
- if (cals.length === 0) {
+ const editableCals = allCalendars.filter(c => !c.readOnly);
+ if (editableCals.length === 0) {
AppEnv.showErrorDialog(
- "This account has no editable calendars. We can't " +
- 'create an event for you. Please make sure you have an editable calendar ' +
- 'with your account provider.'
+ localized(
+ "This account has no editable calendars. We can't create an event for you. Please make sure you have an editable calendar with your account provider."
+ )
);
return;
}
const event = new Event({
- calendarId: cals[0].id,
- accountId: cals[0].accountId,
+ calendarId: editableCals[0].id,
+ accountId: editableCals[0].accountId,
start: start.unix(),
end: end.unix(),
when: {
@@ -86,7 +95,7 @@ export default class QuickEventPopover extends React.Component<{}, QuickEventPop
diff --git a/app/internal_packages/main-calendar/styles/main-calendar.less b/app/internal_packages/main-calendar/styles/main-calendar.less
index a16076102..b0559ed8c 100644
--- a/app/internal_packages/main-calendar/styles/main-calendar.less
+++ b/app/internal_packages/main-calendar/styles/main-calendar.less
@@ -2,10 +2,6 @@
@import 'ui-variables';
@import 'ui-mixins';
-.main-calendar {
- height: 100%;
-}
-
.fixed-popover .calendar-event-popover {
color: fadeout(@text-color, 20%);
background-color: @background-primary;
diff --git a/app/internal_packages/main-calendar/styles/nylas-calendar.less b/app/internal_packages/main-calendar/styles/nylas-calendar.less
index 3d4d2da59..87cd572d2 100644
--- a/app/internal_packages/main-calendar/styles/nylas-calendar.less
+++ b/app/internal_packages/main-calendar/styles/nylas-calendar.less
@@ -91,7 +91,7 @@ body.platform-win32 {
.default-header {
overflow: hidden;
text-overflow: ellipsis;
- margin: 4px 5px 5px 6px;
+ margin: 4px 1px 3px 4px;
cursor: default;
opacity: 0.7;
flex: 1;
@@ -141,6 +141,7 @@ body.platform-win32 {
overflow-x: auto;
overflow-y: hidden;
position: relative;
+ scroll-snap-type: x mandatory;
}
.calendar-legend {
@@ -181,14 +182,14 @@ body.platform-win32 {
.legend-text {
font-size: 11px;
position: absolute;
- color: #bfbfbf;
+ color: @text-color-very-subtle;
right: 10px;
}
.date-label-legend {
width: @legend-width;
position: relative;
- border-bottom: 1px solid #dddddd;
+ border-bottom: 1px solid @border-color-divider;
box-shadow: 0 1px 2.5px rgba(0, 0, 0, 0.15);
z-index: 3;
.legend-text {
@@ -201,6 +202,10 @@ body.platform-win32 {
text-align: center;
flex: 1;
box-shadow: inset 1px 0 0 rgba(177, 177, 177, 0.15);
+ scroll-snap-align: start;
+ &.is-hard-stop {
+ scroll-snap-stop: always;
+ }
&.is-today {
.date-label {
color: @accent-primary;
@@ -214,7 +219,7 @@ body.platform-win32 {
display: block;
font-size: 16px;
font-weight: 300;
- color: #808080;
+ color: @text-color-subtle;
}
.weekday-label {
display: block;
@@ -222,7 +227,7 @@ body.platform-win32 {
text-transform: uppercase;
margin-top: 3px;
font-size: 12px;
- color: #ccd4d8;
+ color: @text-color-subtle;
}
.event-grid-wrap {
@@ -277,6 +282,7 @@ body.platform-win32 {
}
.month-view {
+ color: #000;
}
.current-time-indicator {
@@ -304,7 +310,7 @@ body.platform-win32 {
.top-banner {
color: rgba(33, 99, 146, 0.6);
- background: #e0eff6;
+ background: @gray-lighter;
font-size: 12px;
line-height: 25px;
text-align: center;
@@ -314,7 +320,7 @@ body.platform-win32 {
.header-controls {
padding: 10px;
display: flex;
- color: #808080;
+ color: @text-color-subtle;
border-bottom: 1px solid @border-color-divider;
box-shadow: inset 0 -1px 1px rgba(191, 191, 191, 0.12);
flex-shrink: 0;
@@ -338,99 +344,9 @@ body.platform-win32 {
}
}
- .footer-controls {
- padding: 10px;
- min-height: 45px;
- display: flex;
- color: #808080;
- background: @background-primary;
- border-top: @border-color-divider;
- box-shadow: 0 -3px 16px rgba(0, 0, 0, 0.11);
- z-index: 2;
- }
-
.center-controls {
text-align: center;
flex: 1;
order: 0;
}
}
-
-.mini-month-view {
- width: 100%;
- height: 100%;
- min-width: 200px;
- min-height: 200px;
- text-align: center;
- display: flex;
- flex-direction: column;
- background: @background-primary;
- border: 1px solid @border-color-divider;
- border-radius: @border-radius-base;
-
- .header {
- display: flex;
- background: @background-secondary;
- padding: 4px 0 3px 0;
- .month-title {
- padding-top: 3px;
- color: @text-color;
- flex: 1;
- }
- border-bottom: 1px solid @border-color-divider;
- .btn.btn-icon {
- line-height: 27px;
- height: 27px;
- margin-top: -1px;
- margin-right: 0;
- &:active {
- background: transparent;
- }
- }
- }
-
- .legend {
- display: flex;
- .weekday {
- flex: 1;
- }
- padding: 3px 0;
- border-bottom: 1px solid @border-color-divider;
- }
-
- .day-grid {
- display: flex;
- flex-direction: column;
- flex: 1;
- .week {
- flex: 1;
- display: flex;
- min-height: 28px;
- }
- .day {
- display: flex;
- flex-direction: column;
- justify-content: center;
- flex: 1;
- min-height: 28px;
- color: @text-color-very-subtle;
- &.cur-month {
- color: @text-color;
- }
- &:hover {
- background: rgba(0, 0, 0, 0.05);
- cursor: pointer;
- }
- &.today {
- border: 1px solid @accent-primary;
- }
- &.cur-day {
- background: @accent-primary;
- color: @text-color-inverse;
- &:hover {
- background: darken(@accent-primary, 5%);
- }
- }
- }
- }
-}
diff --git a/app/internal_packages/message-list/lib/email-frame.tsx b/app/internal_packages/message-list/lib/email-frame.tsx
index 63471f1b1..3d0ab038f 100644
--- a/app/internal_packages/message-list/lib/email-frame.tsx
+++ b/app/internal_packages/message-list/lib/email-frame.tsx
@@ -1,8 +1,14 @@
import { EventedIFrame } from 'mailspring-component-kit';
import React from 'react';
import ReactDOM from 'react-dom';
-import { PropTypes, Utils, QuotedHTMLTransformer, MessageStore, Message } from 'mailspring-exports';
-import { autolink } from './autolinker';
+import {
+ PropTypes,
+ Utils,
+ QuotedHTMLTransformer,
+ MessageStore,
+ Message,
+ Autolink,
+} from 'mailspring-exports';
import { adjustImages } from './adjust-images';
import EmailFrameStylesStore from './email-frame-styles-store';
@@ -118,7 +124,10 @@ export default class EmailFrame extends React.Component {
this._iframeDocObserver.observe(iframeEl.contentDocument.firstElementChild);
window.requestAnimationFrame(() => {
- autolink(doc, { async: true });
+ Autolink(doc.body, {
+ async: true,
+ telAggressiveMatch: false,
+ });
adjustImages(doc);
for (const extension of MessageStore.extensions()) {
diff --git a/app/internal_packages/message-list/specs/message-item-body-spec.jsx b/app/internal_packages/message-list/specs/message-item-body-spec.jsx
index dee461ee0..5e9f6fe6d 100644
--- a/app/internal_packages/message-list/specs/message-item-body-spec.jsx
+++ b/app/internal_packages/message-list/specs/message-item-body-spec.jsx
@@ -122,7 +122,7 @@ xdescribe('MessageItem', function() {
snippet: 'snippet one...',
subject: 'Subject One',
threadId: 'thread_12345',
- accountId: window.TEST_ACCOUNT_ID,
+ accountId: TEST_ACCOUNT_ID,
});
// Generate the test component. Should be called after @message is configured
diff --git a/app/internal_packages/onboarding/lib/decorators/create-page-for-form.tsx b/app/internal_packages/onboarding/lib/decorators/create-page-for-form.tsx
index c501a2fa5..1653ac5c9 100644
--- a/app/internal_packages/onboarding/lib/decorators/create-page-for-form.tsx
+++ b/app/internal_packages/onboarding/lib/decorators/create-page-for-form.tsx
@@ -148,7 +148,7 @@ const CreatePageForForm = FormComponent => {
account.settings.imap_host.includes('imap.gmail.com')
) {
didWarnAboutGmailIMAP = true;
- const buttonIndex = remote.dialog.showMessageBox({
+ const buttonIndex = remote.dialog.showMessageBoxSync({
type: 'warning',
buttons: [localized('Go Back'), localized('Continue')],
message: localized('Are you sure?'),
diff --git a/app/internal_packages/onboarding/lib/mailcore-provider-settings.json b/app/internal_packages/onboarding/lib/mailcore-provider-settings.json
index af5f7e523..f3c18bb06 100644
--- a/app/internal_packages/onboarding/lib/mailcore-provider-settings.json
+++ b/app/internal_packages/onboarding/lib/mailcore-provider-settings.json
@@ -162,25 +162,6 @@
"mx-match": ["mx\\.zoho\\.com", "mx[0-9]*\\.zoho\\.com"],
"domain-match": ["zoho\\.com"]
},
- "juno": {
- "servers": {
- "pop": [
- {
- "port": 995,
- "hostname": "pop.juno.com",
- "ssl": true
- }
- ],
- "smtp": [
- {
- "port": 465,
- "hostname": "smtp.juno.com",
- "starttls": true
- }
- ]
- },
- "domain-match": ["juno\\.com"]
- },
"mobileme": {
"servers": {
"imap": [
@@ -1109,44 +1090,6 @@
},
"mx-match": ["mx1\\.comcast\\.net", "mx2\\.comcast\\.net"]
},
- "verizon": {
- "servers": {
- "pop": [
- {
- "port": 995,
- "hostname": "pop.verizon.net",
- "ssl": true
- }
- ],
- "smtp": [
- {
- "port": 465,
- "hostname": "smtp.verizon.net",
- "ssl": true
- }
- ]
- },
- "mx-match": ["relay\\.verizon\\.net"]
- },
- "rcn": {
- "servers": {
- "pop": [
- {
- "port": 110,
- "hostname": "pop.rcn.com",
- "ssl": true
- }
- ],
- "smtp": [
- {
- "port": 25,
- "hostname": "smtp.rcn.com",
- "ssl": true
- }
- ]
- },
- "mx-match": ["mx\\.rcn\\.com"]
- },
"ukrnet": {
"servers": {
"imap": [
diff --git a/app/internal_packages/onboarding/lib/oauth-signin-page.tsx b/app/internal_packages/onboarding/lib/oauth-signin-page.tsx
index ca19adbfb..e2ca0d642 100644
--- a/app/internal_packages/onboarding/lib/oauth-signin-page.tsx
+++ b/app/internal_packages/onboarding/lib/oauth-signin-page.tsx
@@ -7,13 +7,14 @@ import url from 'url';
import FormErrorMessage from './form-error-message';
import { LOCAL_SERVER_PORT } from './onboarding-helpers';
+import AccountProviders from './account-providers';
interface OAuthSignInPageProps {
providerAuthPageUrl: string;
buildAccountFromAuthResponse: (rep: any) => Account | Promise;
onSuccess: (account: Account) => void;
onTryAgain: () => void;
- providerConfig: object;
+ providerConfig: (typeof AccountProviders)[0];
serviceName: string;
}
@@ -79,18 +80,16 @@ export default class OAuthSignInPage extends React.Component<
response.end('Unknown Request');
}
});
- this._server.listen(LOCAL_SERVER_PORT, err => {
- if (err) {
- AppEnv.showErrorDialog({
- title: localized('Unable to Start Local Server'),
- message: localized(
- `To listen for the Gmail Oauth response, Mailspring needs to start a webserver on port ${LOCAL_SERVER_PORT}. Please go back and try linking your account again. If this error persists, use the IMAP/SMTP option with a Gmail App Password.\n\n%@`,
- err
- ),
- });
- return;
- }
+ this._server.once('error', err => {
+ AppEnv.showErrorDialog({
+ title: localized('Unable to Start Local Server'),
+ message: localized(
+ `To listen for the Gmail Oauth response, Mailspring needs to start a webserver on port ${LOCAL_SERVER_PORT}. Please go back and try linking your account again. If this error persists, use the IMAP/SMTP option with a Gmail App Password.\n\n%@`,
+ err
+ ),
+ });
});
+ this._server.listen(LOCAL_SERVER_PORT);
}
componentWillUnmount() {
diff --git a/app/internal_packages/onboarding/lib/page-top-bar.tsx b/app/internal_packages/onboarding/lib/page-top-bar.tsx
index a688057b7..c99b014af 100644
--- a/app/internal_packages/onboarding/lib/page-top-bar.tsx
+++ b/app/internal_packages/onboarding/lib/page-top-bar.tsx
@@ -33,19 +33,17 @@ const PageTopBar = props => {
backButton = null;
}
+ const style: any = {
+ top: 0,
+ left: 26,
+ right: 0,
+ height: 27,
+ zIndex: 100,
+ position: 'absolute',
+ WebkitAppRegion: 'drag',
+ };
return (
-
+
{backButton}
);
diff --git a/app/internal_packages/open-tracking/specs/open-tracking-composer-extension-spec.ts b/app/internal_packages/open-tracking/specs/open-tracking-composer-extension-spec.ts
index 83886a6af..c528f255b 100644
--- a/app/internal_packages/open-tracking/specs/open-tracking-composer-extension-spec.ts
+++ b/app/internal_packages/open-tracking/specs/open-tracking-composer-extension-spec.ts
@@ -20,7 +20,6 @@ xdescribe('Open tracking composer extension', function openTrackingComposerExten
beforeEach(() => {
this.draftBodyRootNode = nodeForHTML(beforeBody);
this.draft = new Message({
- clientId: clientId,
accountId: accountId,
body: beforeBody,
});
diff --git a/app/internal_packages/preferences/lib/tabs/preferences-account-list.tsx b/app/internal_packages/preferences/lib/tabs/preferences-account-list.tsx
index 9821c44eb..4d79dd705 100644
--- a/app/internal_packages/preferences/lib/tabs/preferences-account-list.tsx
+++ b/app/internal_packages/preferences/lib/tabs/preferences-account-list.tsx
@@ -23,7 +23,7 @@ class PreferencesAccountList extends Component
{
onRemoveAccount: PropTypes.func.isRequired,
};
- _renderAccountStateIcon(account) {
+ _renderAccountStateIcon(account: Account) {
if (account.syncState !== 'running') {
return (
@@ -38,7 +38,7 @@ class PreferencesAccountList extends Component
{
return null;
}
- _renderAccount = account => {
+ _renderAccount = (account: Account) => {
const label = account.label;
const accountSub = `${account.name || localized('No name provided')} <${account.emailAddress}>`;
const syncError = account.hasSyncStateError();
diff --git a/app/internal_packages/preferences/lib/tabs/preferences-general.tsx b/app/internal_packages/preferences/lib/tabs/preferences-general.tsx
index b8d434fdf..00614197b 100644
--- a/app/internal_packages/preferences/lib/tabs/preferences-general.tsx
+++ b/app/internal_packages/preferences/lib/tabs/preferences-general.tsx
@@ -28,7 +28,7 @@ class PreferencesGeneral extends React.Component<{
};
_onResetAccountsAndSettings = () => {
- const chosen = remote.dialog.showMessageBox({
+ const chosen = remote.dialog.showMessageBoxSync({
type: 'info',
message: localized('Are you sure?'),
buttons: [localized('Cancel'), localized('Reset')],
diff --git a/app/internal_packages/preferences/lib/tabs/preferences-identity.tsx b/app/internal_packages/preferences/lib/tabs/preferences-identity.tsx
index 5c2593949..25387bdab 100644
--- a/app/internal_packages/preferences/lib/tabs/preferences-identity.tsx
+++ b/app/internal_packages/preferences/lib/tabs/preferences-identity.tsx
@@ -5,6 +5,7 @@ import {
localized,
localizedReactFragment,
IIdentity,
+ EMPTY_IDENTITY,
} from 'mailspring-exports';
import { OpenIdentityPageButton, BillingModal, RetinaImg } from 'mailspring-component-kit';
import { shell } from 'electron';
@@ -158,7 +159,7 @@ class PreferencesIdentity extends React.Component<{}, { identity: IIdentity }> {
_getStateFromStores() {
return {
- identity: IdentityStore.identity() || {},
+ identity: IdentityStore.identity() || { ...EMPTY_IDENTITY },
};
}
diff --git a/app/internal_packages/preferences/lib/tabs/preferences-keymaps.tsx b/app/internal_packages/preferences/lib/tabs/preferences-keymaps.tsx
index 05d15f133..dcdea57d4 100644
--- a/app/internal_packages/preferences/lib/tabs/preferences-keymaps.tsx
+++ b/app/internal_packages/preferences/lib/tabs/preferences-keymaps.tsx
@@ -74,7 +74,7 @@ export default class PreferencesKeymaps extends React.Component<
}
_onDeleteUserKeymap() {
- const chosen = remote.dialog.showMessageBox({
+ const chosen = remote.dialog.showMessageBoxSync({
type: 'info',
message: localized('Are you sure?'),
detail: localized('Delete your custom key bindings and reset to the template defaults?'),
diff --git a/app/internal_packages/print/lib/print-window.ts b/app/internal_packages/print/lib/print-window.ts
index 98e886c12..c619bb7db 100644
--- a/app/internal_packages/print/lib/print-window.ts
+++ b/app/internal_packages/print/lib/print-window.ts
@@ -26,6 +26,7 @@ export default class PrintWindow {
.join('');
const content = `
+
@@ -79,7 +80,7 @@ export default class PrintWindow {
contextIsolation: false,
},
});
- this.browserWin.setMenu(null);
+ this.browserWin.removeMenu();
fs.writeFileSync(tmpMessagesPath, `window.printMessages = ${printMessages}`);
fs.writeFileSync(this.tmpFile, content);
}
diff --git a/app/internal_packages/print/static/print-preload.js b/app/internal_packages/print/static/print-preload.js
index 8e8773cb1..35e13abc7 100644
--- a/app/internal_packages/print/static/print-preload.js
+++ b/app/internal_packages/print/static/print-preload.js
@@ -7,31 +7,28 @@ win.addListener('page-title-updated', event => {
event.preventDefault();
});
-global.printToPDF = () => {
- remote.dialog.showSaveDialog(
+global.printToPDF = async () => {
+ const { filePath } = await remote.dialog.showSaveDialog({
+ defaultPath: `${win.getTitle()}.pdf`,
+ });
+
+ if (!filePath) {
+ return;
+ }
+ webcontents.printToPDF(
{
- defaultPath: `${win.getTitle()}.pdf`,
+ marginsType: 0,
+ pageSize: 'Letter',
+ printBackground: true,
+ landscape: false,
},
- filename => {
- if (!filename) {
+ (error, data) => {
+ if (error) {
+ remote.dialog.showErrorBox('An Error Occurred', `${error}`);
return;
}
- webcontents.printToPDF(
- {
- marginsType: 0,
- pageSize: 'Letter',
- printBackground: true,
- landscape: false,
- },
- (error, data) => {
- if (error) {
- remote.dialog.showErrorBox('An Error Occurred', `${error}`);
- return;
- }
- fs.writeFileSync(filename, data);
- }
- );
+ fs.writeFileSync(filename, data);
}
);
};
diff --git a/app/internal_packages/remove-tracking-pixels/specs/tracking-pixels-extension-spec.ts b/app/internal_packages/remove-tracking-pixels/specs/tracking-pixels-extension-spec.ts
index 8a77aa826..2b4bc1707 100644
--- a/app/internal_packages/remove-tracking-pixels/specs/tracking-pixels-extension-spec.ts
+++ b/app/internal_packages/remove-tracking-pixels/specs/tracking-pixels-extension-spec.ts
@@ -1,6 +1,7 @@
/* eslint no-irregular-whitespace: 0 */
import fs from 'fs';
import { removeTrackingPixels } from '../lib/main';
+import { Message } from 'mailspring-exports';
const readFixture = name => {
return fs
@@ -19,7 +20,7 @@ describe('TrackingPixelsExtension', function trackingPixelsExtension() {
accountId: '1234',
isFromMe: () => true,
};
- removeTrackingPixels(message);
+ removeTrackingPixels(message as Message);
expect(message.body).toEqual(expected);
});
@@ -32,7 +33,7 @@ describe('TrackingPixelsExtension', function trackingPixelsExtension() {
accountId: '1234',
isFromMe: () => false,
};
- removeTrackingPixels(message);
+ removeTrackingPixels(message as Message);
expect(message.body).toEqual(expected);
});
});
diff --git a/app/internal_packages/send-later/lib/send-later-status.tsx b/app/internal_packages/send-later/lib/send-later-status.tsx
index 0b295c272..e71981004 100644
--- a/app/internal_packages/send-later/lib/send-later-status.tsx
+++ b/app/internal_packages/send-later/lib/send-later-status.tsx
@@ -68,7 +68,7 @@ export default class SendLaterStatus extends Component ({
+ (store, items) => ({
focusedThread: store.focused('thread'),
items,
})
diff --git a/app/internal_packages/thread-list/lib/thread-list-data-source.ts b/app/internal_packages/thread-list/lib/thread-list-data-source.ts
index 2979982d0..20cd80447 100644
--- a/app/internal_packages/thread-list/lib/thread-list-data-source.ts
+++ b/app/internal_packages/thread-list/lib/thread-list-data-source.ts
@@ -9,7 +9,7 @@ import {
} from 'mailspring-exports';
const _observableForThreadMessages = (id, initialModels) => {
- const subscription = new QuerySubscription(
+ const subscription = new QuerySubscription(
DatabaseStore.findAll(Message, { threadId: id }),
{
initialModels: initialModels,
diff --git a/app/internal_packages/thread-list/lib/thread-list-participants.tsx b/app/internal_packages/thread-list/lib/thread-list-participants.tsx
index 6f8b997ad..07b3b8dad 100644
--- a/app/internal_packages/thread-list/lib/thread-list-participants.tsx
+++ b/app/internal_packages/thread-list/lib/thread-list-participants.tsx
@@ -59,7 +59,7 @@ class ThreadListParticipants extends React.Component<{ thread: ThreadWithMessage
if (spacer) {
accumulate('...');
} else {
- let short = '';
+ let short = contact.email;
if (contact.name && contact.name.length > 0) {
if (items.length > 1) {
short = contact.displayName({
@@ -69,8 +69,6 @@ class ThreadListParticipants extends React.Component<{ thread: ThreadWithMessage
} else {
short = contact.displayName({ includeAccountLabel: false });
}
- } else {
- short = contact.email;
}
if (idx < items.length - 1 && !items[idx + 1].spacer) {
short += ', ';
diff --git a/app/internal_packages/thread-list/lib/thread-list-store.ts b/app/internal_packages/thread-list/lib/thread-list-store.ts
index dd2334a35..956c72ab1 100644
--- a/app/internal_packages/thread-list/lib/thread-list-store.ts
+++ b/app/internal_packages/thread-list/lib/thread-list-store.ts
@@ -49,7 +49,7 @@ class ThreadListStore extends MailspringStore {
};
selectionObservable = () => {
- return Rx.Observable.fromListSelection(this);
+ return Rx.Observable.fromListSelection(this);
};
// Inbound Events
diff --git a/app/internal_packages/thread-list/specs/thread-list-participants-spec.jsx b/app/internal_packages/thread-list/specs/thread-list-participants-spec.jsx
index 59a16d835..30a57bff6 100644
--- a/app/internal_packages/thread-list/specs/thread-list-participants-spec.jsx
+++ b/app/internal_packages/thread-list/specs/thread-list-participants-spec.jsx
@@ -191,7 +191,7 @@ describe('ThreadListParticipants', function() {
},
];
- for (let scenario of scenarios) {
+ for (const scenario of scenarios) {
const thread = new Thread();
thread.__messages = scenario.in;
const participants = ReactTestUtils.renderIntoDocument(
diff --git a/app/internal_packages/thread-search/lib/thread-search-bar.tsx b/app/internal_packages/thread-search/lib/thread-search-bar.tsx
index f35ed4365..5657df197 100644
--- a/app/internal_packages/thread-search/lib/thread-search-bar.tsx
+++ b/app/internal_packages/thread-search/lib/thread-search-bar.tsx
@@ -23,26 +23,27 @@ import {
wrapInQuotes,
} from './search-bar-util';
-class ThreadSearchBar extends Component<
- {
- query: string;
- isSearching: boolean;
- perspective: MailboxPerspective;
- },
- {
- suggestions: {
- token: string;
- term: string;
- description: string;
- termSuggestions: string[] | ((term: string, accountIds: string[]) => Promise);
- }[];
- focused: boolean;
- selected?: {
- description: any;
- };
- selectedIdx: number;
- }
-> {
+interface ThreadSearchBarProps {
+ query: string;
+ isSearching: boolean;
+ perspective: MailboxPerspective;
+}
+
+interface ThreadSearchBarState {
+ suggestions: {
+ token: string;
+ term: string;
+ description: string;
+ termSuggestions: string[] | ((term: string, accountIds: string[]) => Promise);
+ }[];
+ focused: boolean;
+ selected?: {
+ description: any;
+ };
+ selectedIdx: number;
+}
+
+class ThreadSearchBar extends Component {
static displayName = 'ThreadSearchBar';
static propTypes = {
diff --git a/app/internal_packages/translation/lib/message-header.tsx b/app/internal_packages/translation/lib/message-header.tsx
index de5e0dc7c..4dc996438 100644
--- a/app/internal_packages/translation/lib/message-header.tsx
+++ b/app/internal_packages/translation/lib/message-header.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
-import cld from '@paulcbetts/cld';
+import cld from 'cld';
import { remote } from 'electron';
import {
localized,
@@ -237,7 +237,7 @@ export class TranslateMessageHeader extends React.Component<
_onNeverForLanguage = () => {
if (!this.state.detected) return;
- const response = remote.dialog.showMessageBox({
+ const response = remote.dialog.showMessageBoxSync({
type: 'warning',
buttons: [localized('Yes'), localized('Cancel')],
message: localized('Are you sure?'),
diff --git a/app/internal_packages/translation/lib/service.ts b/app/internal_packages/translation/lib/service.ts
index 83e848740..0f7d3a987 100644
--- a/app/internal_packages/translation/lib/service.ts
+++ b/app/internal_packages/translation/lib/service.ts
@@ -5,6 +5,7 @@ import {
Actions,
MailspringAPIRequest,
RegExpUtils,
+ FeatureLexicon,
} from 'mailspring-exports';
export const TranslatePopupOptions = {
@@ -118,7 +119,7 @@ export const AllLanguages = {
ms: 'Malay',
};
-export const TranslationsUsedLexicon = {
+export const TranslationsUsedLexicon: FeatureLexicon = {
headerText: localized('All Translations Used'),
rechargeText: `${localized(
'You can translate up to %1$@ emails each %2$@ with Mailspring Basic.'
diff --git a/app/internal_packages/undo-redo/lib/undo-redo-toast.tsx b/app/internal_packages/undo-redo/lib/undo-redo-toast.tsx
index 3a4ee6f73..bce28220d 100644
--- a/app/internal_packages/undo-redo/lib/undo-redo-toast.tsx
+++ b/app/internal_packages/undo-redo/lib/undo-redo-toast.tsx
@@ -7,7 +7,7 @@ function isUndoSend(block) {
return (
block.tasks.length === 1 &&
block.tasks[0] instanceof SyncbackMetadataTask &&
- block.tasks[0].value.isUndoSend
+ (block.tasks[0].value as any).isUndoSend
);
}
diff --git a/app/package-lock.json b/app/package-lock.json
index 4179a0644..192296b7e 100644
--- a/app/package-lock.json
+++ b/app/package-lock.json
@@ -1,8 +1,4027 @@
{
"name": "mailspring",
"version": "1.8.0",
- "lockfileVersion": 1,
+ "lockfileVersion": 2,
"requires": true,
+ "packages": {
+ "": {
+ "name": "mailspring",
+ "version": "1.8.0",
+ "license": "GPL-3.0",
+ "dependencies": {
+ "@bengotow/slate-edit-list": "github:bengotow/slate-edit-list#b868e108",
+ "better-sqlite3": "^7.1.2",
+ "chromium-net-errors": "1.0.3",
+ "chrono-node": "^1.1.2",
+ "classnames": "1.2.1",
+ "cld": "2.6.0",
+ "collapse-whitespace": "^1.1.6",
+ "debug": "github:emorikawa/debug#nylas",
+ "deep-extend": "0.6.0",
+ "electron-spellchecker": "github:bengotow/electron-spellchecker#267df08",
+ "emoji-data": "^0.2.0",
+ "enzyme": "^3.8.0",
+ "enzyme-adapter-react-16": "^1.9.0",
+ "event-kit": "^1.0.2",
+ "fs-plus": "^2.3.2",
+ "getmac": "^1.2.1",
+ "graceful-fs": "^4.1.11",
+ "ical-expander": "^2.0.0",
+ "ical.js": "^1.3.0",
+ "immutable": "^3.8.2",
+ "ini": "^1.3.5",
+ "jasmine-json": "~0.0",
+ "jasmine-react-helpers": "^0.2",
+ "jasmine-reporters": "1.x.x",
+ "juice": "^5.2.0",
+ "keytar": "5.5.0",
+ "less-cache": "1.1.0",
+ "lru-cache": "^4.0.1",
+ "mammoth": "1.4.7",
+ "mkdirp": "^0.5",
+ "moment": "^2.24.0",
+ "moment-round": "^1.0.1",
+ "moment-timezone": "^0.5.32",
+ "mousetrap": "^1.5.3",
+ "node-emoji": "^1.2.1",
+ "optimist": "0.4.0",
+ "pick-react-known-prop": "0.x.x",
+ "proxyquire": "1.3.1",
+ "raven": "2.1.2",
+ "react": "16.6.0",
+ "react-color": "^2.17.0",
+ "react-dom": "16.6.0",
+ "react-test-renderer": "16.6.0",
+ "react-transition-group": "1.2.1",
+ "reflux": "0.1.13",
+ "rimraf": "2.5.2",
+ "rtlcss": "2.4.0",
+ "rx-lite": "4.0.8",
+ "slate": "github:bengotow/slate#cd6f40e8",
+ "slate-auto-replace": "0.12.1",
+ "slate-base64-serializer": "0.2.100",
+ "slate-html-serializer": "0.7.39",
+ "slate-plain-serializer": "0.6.39",
+ "slate-prop-types": "0.5.30",
+ "slate-react": "github:bengotow/slate#0.45.1-react",
+ "slate-soft-break": "^0.9.0",
+ "slate-when": "^0.2.0",
+ "snarkdown": "1.2.2",
+ "source-map-support": "^0.3.2",
+ "temp": "^0.8",
+ "tld": "^0.0.2",
+ "underscore": "1.8.x",
+ "underscore.string": "^3.3.5",
+ "utf7": "^1.0.2",
+ "uuid": "^3.0.0",
+ "vcf": "^2.0.5",
+ "windows-iana": "^4.2.1",
+ "windows-shortcuts": "emorikawa/windows-shortcuts#b0a0fc7",
+ "xlsx": "0.14.1"
+ },
+ "optionalDependencies": {
+ "macos-notification-state": "^1.1.0",
+ "node-mac-notifier": "1.1.0",
+ "windows-quiet-hours": "^1.2.5"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.4.5",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz",
+ "integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==",
+ "dependencies": {
+ "regenerator-runtime": "0.13.2"
+ }
+ },
+ "node_modules/@bengotow/slate-edit-list": {
+ "resolved": "git+ssh://git@github.com/bengotow/slate-edit-list.git#b868e1088b44ad1adb2cb06bbbb847a2fef7edb5"
+ },
+ "node_modules/@felixrieseberg/spellchecker": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/@felixrieseberg/spellchecker/-/spellchecker-4.0.12.tgz",
+ "integrity": "sha512-jLAPnRALB1I6Un8ldHVJfJid7m2R1qXoafFF/95sdm7R5VPOsZ3xTreZ/wLKO5x9AdsD2t9zpOcjDFTsCf3VzQ==",
+ "dependencies": {
+ "nan": "2.14.0"
+ }
+ },
+ "node_modules/@icons/material": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
+ "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw=="
+ },
+ "node_modules/@types/node": {
+ "version": "12.0.7",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.7.tgz",
+ "integrity": "sha512-1YKeT4JitGgE4SOzyB9eMwO0nGVNkNEsm9qlIt1Lqm/tG2QEiSMTD4kS3aO6L+w5SClLVxALmIBESK6Mk5wX0A=="
+ },
+ "node_modules/adler-32": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.2.0.tgz",
+ "integrity": "sha1-aj5r8KY5ALoVZSgIyxXGgT0aXyU=",
+ "dependencies": {
+ "exit-on-epipe": "1.0.1",
+ "printj": "1.1.2"
+ },
+ "bin": {
+ "adler32": "bin/adler32.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/airbnb-prop-types": {
+ "version": "2.13.2",
+ "resolved": "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.13.2.tgz",
+ "integrity": "sha512-2FN6DlHr6JCSxPPi25EnqGaXC4OC3/B3k1lCd6MMYrZ51/Gf/1qDfaR+JElzWa+Tl7cY2aYOlsYJGFeQyVHIeQ==",
+ "dependencies": {
+ "array.prototype.find": "2.1.0",
+ "function.prototype.name": "1.1.0",
+ "has": "1.0.3",
+ "is-regex": "1.0.4",
+ "object-is": "1.0.1",
+ "object.assign": "4.1.0",
+ "object.entries": "1.1.0",
+ "prop-types": "15.7.2",
+ "prop-types-exact": "1.2.0",
+ "react-is": "16.8.6"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.10.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
+ "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
+ "dependencies": {
+ "fast-deep-equal": "2.0.1",
+ "fast-json-stable-stringify": "2.0.0",
+ "json-schema-traverse": "0.4.1",
+ "uri-js": "4.2.2"
+ }
+ },
+ "node_modules/amdefine": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
+ "engines": {
+ "node": ">=0.4.2"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dependencies": {
+ "color-convert": "1.9.3"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/aproba": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+ },
+ "node_modules/are-we-there-yet": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
+ "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
+ "dependencies": {
+ "delegates": "1.0.0",
+ "readable-stream": "2.3.7"
+ }
+ },
+ "node_modules/are-we-there-yet/node_modules/readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dependencies": {
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "2.0.1",
+ "safe-buffer": "5.1.2",
+ "string_decoder": "1.1.1",
+ "util-deprecate": "1.0.2"
+ }
+ },
+ "node_modules/are-we-there-yet/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dependencies": {
+ "safe-buffer": "5.1.2"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dependencies": {
+ "sprintf-js": "1.0.3"
+ }
+ },
+ "node_modules/array-filter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz",
+ "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM="
+ },
+ "node_modules/array.prototype.find": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.1.0.tgz",
+ "integrity": "sha512-Wn41+K1yuO5p7wRZDl7890c3xvv5UBrfVXTVIe28rSQb6LS0fZMDrQB6PAcxQFRFy6vJTLDc3A2+3CjQdzVKRg==",
+ "dependencies": {
+ "define-properties": "1.1.3",
+ "es-abstract": "1.13.0"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz",
+ "integrity": "sha512-rVqIs330nLJvfC7JqYvEWwqVr5QjYF1ib02i3YJtR/fICO6527Tjpc/e4Mvmxh3GIePPreRXMdaGyC99YphWEw==",
+ "dependencies": {
+ "define-properties": "1.1.3",
+ "es-abstract": "1.13.0",
+ "function-bind": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
+ "optional": true
+ },
+ "node_modules/asn1": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+ "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+ "dependencies": {
+ "safer-buffer": "2.1.2"
+ }
+ },
+ "node_modules/assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/async": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+ },
+ "node_modules/aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/aws4": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
+ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+ },
+ "node_modules/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=="
+ },
+ "node_modules/bcp47": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/bcp47/-/bcp47-1.1.2.tgz",
+ "integrity": "sha1-NUvjMH/9CEM6ePXh4glYRfifx/4=",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+ "dependencies": {
+ "tweetnacl": "0.14.5"
+ }
+ },
+ "node_modules/better-sqlite3": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-7.1.2.tgz",
+ "integrity": "sha512-8FWYnJ6Bx94MBX03J5Ka7sTRlvXXMEm4FW2Op7nM8ErQZeyALYLmSlbMBnfr4cMpS0tj0aYZv0a+26G2YJuIjg==",
+ "dependencies": {
+ "bindings": "1.5.0",
+ "prebuild-install": "5.3.3",
+ "tar": "6.1.0"
+ }
+ },
+ "node_modules/bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "dependencies": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "node_modules/bl": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
+ "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==",
+ "dependencies": {
+ "buffer": "5.6.0",
+ "inherits": "2.0.4",
+ "readable-stream": "3.4.0"
+ }
+ },
+ "node_modules/bl/node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/block-elements": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/block-elements/-/block-elements-1.2.0.tgz",
+ "integrity": "sha1-jgTMq2OMfiWW9QZftsHHUYyQWl0="
+ },
+ "node_modules/bluebird": {
+ "version": "3.4.7",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz",
+ "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM="
+ },
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
+ },
+ "node_modules/boom": {
+ "version": "2.10.1",
+ "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
+ "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
+ "optional": true,
+ "dependencies": {
+ "hoek": "2.16.3"
+ },
+ "engines": {
+ "node": ">=0.10.40"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dependencies": {
+ "balanced-match": "1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
+ "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
+ "dependencies": {
+ "base64-js": "1.3.1",
+ "ieee754": "1.1.13"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
+ },
+ "node_modules/cfb": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.1.1.tgz",
+ "integrity": "sha512-eov9L63s9/uMiiDvWCiTYbK3AcM5x5AYo4NL1q75G0AZBrD2X0+Lm9hubk94qy1MJP7pYFuM6/xn0h3grAvBXQ==",
+ "dependencies": {
+ "adler-32": "1.2.0",
+ "commander": "2.20.0",
+ "crc-32": "1.2.0",
+ "printj": "1.1.2"
+ },
+ "bin": {
+ "cfb": "bin/cfb.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/chain-function": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/chain-function/-/chain-function-1.0.1.tgz",
+ "integrity": "sha512-SxltgMwL9uCko5/ZCLiyG2B7R9fY4pDZUw7hJ4MhirdjBLosoDqkWABi3XMucddHdLiFJMb7PD2MZifZriuMTg=="
+ },
+ "node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dependencies": {
+ "ansi-styles": "3.2.1",
+ "escape-string-regexp": "1.0.5",
+ "supports-color": "5.5.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cheerio": {
+ "version": "1.0.0-rc.3",
+ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz",
+ "integrity": "sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==",
+ "dependencies": {
+ "css-select": "1.2.0",
+ "dom-serializer": "0.1.1",
+ "entities": "1.1.2",
+ "htmlparser2": "3.10.1",
+ "lodash": "4.17.11",
+ "parse5": "3.0.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
+ },
+ "node_modules/chromium-net-errors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/chromium-net-errors/-/chromium-net-errors-1.0.3.tgz",
+ "integrity": "sha1-lcI2Hvv7HrLThZM6V9k1WdnHvwE="
+ },
+ "node_modules/chrono-node": {
+ "version": "1.3.11",
+ "resolved": "https://registry.npmjs.org/chrono-node/-/chrono-node-1.3.11.tgz",
+ "integrity": "sha512-jDWRnY6nYvzfV3HPYBqo+tot7tcsUs9i3arGbMdI0TouPSXP2C2y/Ctp27rxKTQDi6yuTxAB2cw+Q6igGhOhdQ==",
+ "dependencies": {
+ "moment": "2.21.0"
+ }
+ },
+ "node_modules/chrono-node/node_modules/moment": {
+ "version": "2.21.0",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.21.0.tgz",
+ "integrity": "sha512-TCZ36BjURTeFTM/CwRcViQlfkMvL1/vFISuNLO5GkcVm1+QHfbSiNqZuWeMFjj1/3+uAjXswgRk30j1kkLYJBQ==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/classnames": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-1.2.1.tgz",
+ "integrity": "sha1-xwx4j8kS4GhL2XruFn0u/WgtzfM="
+ },
+ "node_modules/cld": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/cld/-/cld-2.6.0.tgz",
+ "integrity": "sha512-2U8Uiv7Bvl1v4fNWFGB3RYtPvhUWXQJ1MoNKJNVuoALfandEt9oVqK64S+3ZLvQPjDiYjsohtTep/wIs0xOXkw==",
+ "dependencies": {
+ "glob": "5.0.15",
+ "node-addon-api": "2.0.0",
+ "rimraf": "2.5.2",
+ "underscore": "1.8.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+ "optional": true,
+ "engines": {
+ "iojs": ">= 1.0.0",
+ "node": ">= 0.12.0"
+ }
+ },
+ "node_modules/code-point-at": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/codepage": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.14.0.tgz",
+ "integrity": "sha1-jL4lSBMjVZ19MHVxsP/5HnodL5k=",
+ "dependencies": {
+ "commander": "2.14.1",
+ "exit-on-epipe": "1.0.1"
+ },
+ "bin": {
+ "codepage": "bin/codepage.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/codepage/node_modules/commander": {
+ "version": "2.14.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz",
+ "integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw=="
+ },
+ "node_modules/collapse-whitespace": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/collapse-whitespace/-/collapse-whitespace-1.1.7.tgz",
+ "integrity": "sha512-24up1hbQSsnaDSGHPOvGQT84vmxvG0QUrI8tguiQpo9I5irrnypCKwddXindXMyXhoTe+9V6LYj3aFIhTQ4UCg==",
+ "dependencies": {
+ "block-elements": "1.2.0",
+ "void-elements": "2.0.1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+ },
+ "node_modules/colors": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
+ "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=",
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": {
+ "delayed-stream": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "2.20.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
+ "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ=="
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+ },
+ "node_modules/console-control-strings": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
+ },
+ "node_modules/cookie": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+ "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+ },
+ "node_modules/crc-32": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz",
+ "integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==",
+ "dependencies": {
+ "exit-on-epipe": "1.0.1",
+ "printj": "1.1.2"
+ },
+ "bin": {
+ "crc32": "bin/crc32.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dependencies": {
+ "nice-try": "1.0.5",
+ "path-key": "2.0.1",
+ "semver": "5.7.0",
+ "shebang-command": "1.2.0",
+ "which": "1.3.1"
+ },
+ "engines": {
+ "node": ">=4.8"
+ }
+ },
+ "node_modules/cryptiles": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
+ "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
+ "optional": true,
+ "dependencies": {
+ "boom": "2.10.1"
+ },
+ "engines": {
+ "node": ">=0.10.40"
+ }
+ },
+ "node_modules/css-select": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
+ "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
+ "dependencies": {
+ "boolbase": "1.0.0",
+ "css-what": "2.1.3",
+ "domutils": "1.5.1",
+ "nth-check": "1.0.2"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
+ "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/d": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz",
+ "integrity": "sha1-2hhMU10Y2O57oqoim5FACfrhEwk=",
+ "dependencies": {
+ "es5-ext": "0.10.50"
+ }
+ },
+ "node_modules/dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+ "dependencies": {
+ "assert-plus": "1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/datauri": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/datauri/-/datauri-2.0.0.tgz",
+ "integrity": "sha512-zS2HSf9pI5XPlNZgIqJg/wCJpecgU/HA6E/uv2EfaWnW1EiTGLfy/EexTIsC9c99yoCOTXlqeeWk4FkCSuO3/g==",
+ "dependencies": {
+ "image-size": "0.7.4",
+ "mimer": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/datauri/node_modules/image-size": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.7.4.tgz",
+ "integrity": "sha512-GqPgxs+VkOr12aWwjSkyRzf5atzObWpFtiRuDgxCl2I/SDpZOKZFRD3iIAeAN6/usmn8SeLWRt7a8JRYK0Whbw==",
+ "bin": {
+ "image-size": "bin/image-size.js"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/debug": {
+ "resolved": "git+ssh://git@github.com/emorikawa/debug.git#29685485665b44c42d33ebd7ed62570b21c6e4bd",
+ "dependencies": {
+ "ms": "0.7.3"
+ }
+ },
+ "node_modules/decompress-response": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
+ "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
+ "dependencies": {
+ "mimic-response": "2.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "dependencies": {
+ "object-keys": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/delegates": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
+ },
+ "node_modules/detect-libc": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+ "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
+ "bin": {
+ "detect-libc": "bin/detect-libc.js"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/direction": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/direction/-/direction-0.1.5.tgz",
+ "integrity": "sha1-zl15f5fib4vnvv9T99xA4cGp7Ew=",
+ "bin": {
+ "direction": "cli.js"
+ }
+ },
+ "node_modules/discontinuous-range": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
+ "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo="
+ },
+ "node_modules/dom-helpers": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
+ "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==",
+ "dependencies": {
+ "@babel/runtime": "7.4.5"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
+ "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
+ "dependencies": {
+ "domelementtype": "1.3.1",
+ "entities": "1.1.2"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w=="
+ },
+ "node_modules/domhandler": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
+ "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
+ "dependencies": {
+ "domelementtype": "1.3.1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
+ "dependencies": {
+ "dom-serializer": "0.1.1",
+ "domelementtype": "1.3.1"
+ }
+ },
+ "node_modules/duck": {
+ "version": "0.1.11",
+ "resolved": "https://registry.npmjs.org/duck/-/duck-0.1.11.tgz",
+ "integrity": "sha1-OtwaPS+91Yef/TvaBc4PaTVekJM=",
+ "dependencies": {
+ "underscore": "1.4.4"
+ }
+ },
+ "node_modules/duck/node_modules/underscore": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz",
+ "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ="
+ },
+ "node_modules/eachr": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/eachr/-/eachr-3.2.0.tgz",
+ "integrity": "sha1-LDXkPqCGUW95l8+At6pk1VpKRIQ=",
+ "dependencies": {
+ "editions": "1.3.4",
+ "typechecker": "4.7.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/eachr/node_modules/editions": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz",
+ "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+ "dependencies": {
+ "jsbn": "0.1.1",
+ "safer-buffer": "2.1.2"
+ }
+ },
+ "node_modules/editions": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/editions/-/editions-2.1.3.tgz",
+ "integrity": "sha512-xDZyVm0A4nLgMNWVVLJvcwMjI80ShiH/27RyLiCnW1L273TcJIA25C4pwJ33AWV01OX6UriP35Xu+lH4S7HWQw==",
+ "dependencies": {
+ "errlop": "1.1.1",
+ "semver": "5.7.0"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/electron-spellchecker": {
+ "resolved": "git+ssh://git@github.com/bengotow/electron-spellchecker.git#267df085fa4c1a5f5631e5f0ebd627890d59d670",
+ "dependencies": {
+ "@felixrieseberg/spellchecker": "4.0.12",
+ "bcp47": "1.1.2",
+ "cld": "2.6.0",
+ "debug": "4.3.1",
+ "keyboard-layout": "2.0.17",
+ "lru-cache": "5.1.1",
+ "mkdirp": "0.5.1",
+ "pify": "4.0.1",
+ "rxjs": "5.5.12",
+ "rxjs-serial-subscription": "0.1.1",
+ "spawn-rx": "2.0.12"
+ }
+ },
+ "node_modules/electron-spellchecker/node_modules/debug": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+ "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/electron-spellchecker/node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dependencies": {
+ "yallist": "3.1.1"
+ }
+ },
+ "node_modules/electron-spellchecker/node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/electron-spellchecker/node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
+ },
+ "node_modules/emissary": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/emissary/-/emissary-1.3.3.tgz",
+ "integrity": "sha1-phjZLWgrIy0xER3DYlpd9mF5lgY=",
+ "dependencies": {
+ "es6-weak-map": "0.1.4",
+ "mixto": "1.0.0",
+ "property-accessors": "1.1.3",
+ "underscore-plus": "1.7.0"
+ }
+ },
+ "node_modules/emoji-data": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/emoji-data/-/emoji-data-0.2.0.tgz",
+ "integrity": "sha1-pGxmC5ExPoZuvJkAmI6ztktGh+g=",
+ "dependencies": {
+ "underscore.string": "2.4.0"
+ },
+ "engines": {
+ "node": ">=0.10.0",
+ "npm": ">=1.2.10"
+ }
+ },
+ "node_modules/emoji-data/node_modules/underscore.string": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz",
+ "integrity": "sha1-jN2PusTi0uoefi6Al8QvRCKA+Fs=",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dependencies": {
+ "once": "1.4.0"
+ }
+ },
+ "node_modules/entities": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
+ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w=="
+ },
+ "node_modules/enzyme": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.10.0.tgz",
+ "integrity": "sha512-p2yy9Y7t/PFbPoTvrWde7JIYB2ZyGC+NgTNbVEGvZ5/EyoYSr9aG/2rSbVvyNvMHEhw9/dmGUJHWtfQIEiX9pg==",
+ "dependencies": {
+ "array.prototype.flat": "1.2.1",
+ "cheerio": "1.0.0-rc.3",
+ "function.prototype.name": "1.1.0",
+ "has": "1.0.3",
+ "html-element-map": "1.0.1",
+ "is-boolean-object": "1.0.0",
+ "is-callable": "1.1.4",
+ "is-number-object": "1.0.3",
+ "is-regex": "1.0.4",
+ "is-string": "1.0.4",
+ "is-subset": "0.1.1",
+ "lodash.escape": "4.0.1",
+ "lodash.isequal": "4.5.0",
+ "object-inspect": "1.6.0",
+ "object-is": "1.0.1",
+ "object.assign": "4.1.0",
+ "object.entries": "1.1.0",
+ "object.values": "1.1.0",
+ "raf": "3.4.1",
+ "rst-selector-parser": "2.2.3",
+ "string.prototype.trim": "1.1.2"
+ }
+ },
+ "node_modules/enzyme-adapter-react-16": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.14.0.tgz",
+ "integrity": "sha512-7PcOF7pb4hJUvjY7oAuPGpq3BmlCig3kxXGi2kFx0YzJHppqX1K8IIV9skT1IirxXlu8W7bneKi+oQ10QRnhcA==",
+ "dependencies": {
+ "enzyme-adapter-utils": "1.12.0",
+ "has": "1.0.3",
+ "object.assign": "4.1.0",
+ "object.values": "1.1.0",
+ "prop-types": "15.7.2",
+ "react-is": "16.8.6",
+ "react-test-renderer": "16.6.0",
+ "semver": "5.7.0"
+ }
+ },
+ "node_modules/enzyme-adapter-utils": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.12.0.tgz",
+ "integrity": "sha512-wkZvE0VxcFx/8ZsBw0iAbk3gR1d9hK447ebnSYBf95+r32ezBq+XDSAvRErkc4LZosgH8J7et7H7/7CtUuQfBA==",
+ "dependencies": {
+ "airbnb-prop-types": "2.13.2",
+ "function.prototype.name": "1.1.0",
+ "object.assign": "4.1.0",
+ "object.fromentries": "2.0.0",
+ "prop-types": "15.7.2",
+ "semver": "5.7.0"
+ }
+ },
+ "node_modules/errlop": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/errlop/-/errlop-1.1.1.tgz",
+ "integrity": "sha512-WX7QjiPHhsny7/PQvrhS5VMizXXKoKCS3udaBp8gjlARdbn+XmK300eKBAAN0hGyRaTCtRpOaxK+xFVPUJ3zkw==",
+ "dependencies": {
+ "editions": "2.1.3"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/errno": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
+ "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
+ "optional": true,
+ "dependencies": {
+ "prr": "1.0.1"
+ },
+ "bin": {
+ "errno": "cli.js"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz",
+ "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==",
+ "dependencies": {
+ "es-to-primitive": "1.2.0",
+ "function-bind": "1.1.1",
+ "has": "1.0.3",
+ "is-callable": "1.1.4",
+ "is-regex": "1.0.4",
+ "object-keys": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
+ "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
+ "dependencies": {
+ "is-callable": "1.1.4",
+ "is-date-object": "1.0.1",
+ "is-symbol": "1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es5-ext": {
+ "version": "0.10.50",
+ "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.50.tgz",
+ "integrity": "sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==",
+ "dependencies": {
+ "es6-iterator": "2.0.3",
+ "es6-symbol": "3.1.1",
+ "next-tick": "1.0.0"
+ }
+ },
+ "node_modules/es5-ext/node_modules/d": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
+ "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
+ "dependencies": {
+ "es5-ext": "0.10.50"
+ }
+ },
+ "node_modules/es5-ext/node_modules/es6-iterator": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+ "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+ "dependencies": {
+ "d": "1.0.0",
+ "es5-ext": "0.10.50",
+ "es6-symbol": "3.1.1"
+ }
+ },
+ "node_modules/es5-ext/node_modules/es6-symbol": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
+ "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
+ "dependencies": {
+ "d": "1.0.0",
+ "es5-ext": "0.10.50"
+ }
+ },
+ "node_modules/es6-iterator": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-0.1.3.tgz",
+ "integrity": "sha1-1vWLjE/EE8JJtLqhl2j45NfIlE4=",
+ "dependencies": {
+ "d": "0.1.1",
+ "es5-ext": "0.10.50",
+ "es6-symbol": "2.0.1"
+ }
+ },
+ "node_modules/es6-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-2.0.1.tgz",
+ "integrity": "sha1-dhtcZ8/U8dGK+yNPaR1nhoLLO/M=",
+ "dependencies": {
+ "d": "0.1.1",
+ "es5-ext": "0.10.50"
+ }
+ },
+ "node_modules/es6-weak-map": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-0.1.4.tgz",
+ "integrity": "sha1-cGzvnpmqI2undmwjnIueKG6n0ig=",
+ "dependencies": {
+ "d": "0.1.1",
+ "es5-ext": "0.10.50",
+ "es6-iterator": "0.1.3",
+ "es6-symbol": "2.0.1"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/esrever": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/esrever/-/esrever-0.2.0.tgz",
+ "integrity": "sha1-lunSj08bGnZ4TNXUkOquAQ50B7g=",
+ "bin": {
+ "esrever": "bin/esrever"
+ }
+ },
+ "node_modules/event-kit": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/event-kit/-/event-kit-1.5.0.tgz",
+ "integrity": "sha1-Ek72qtgyjcsmtxxHWQtbjmPrxIc=",
+ "dependencies": {
+ "grim": "1.5.0"
+ }
+ },
+ "node_modules/event-target-shim": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-1.1.1.tgz",
+ "integrity": "sha1-qG5e5r2qFgVEddp5fM3fDFVphJE=",
+ "optional": true
+ },
+ "node_modules/eventemitter3": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-0.1.6.tgz",
+ "integrity": "sha1-jHrES4e6q1XNUMgo3Dh3jqwFLqU="
+ },
+ "node_modules/exit-on-epipe": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz",
+ "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/expand-template": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
+ "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+ },
+ "node_modules/extract-opts": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/extract-opts/-/extract-opts-3.3.1.tgz",
+ "integrity": "sha1-WrvtyYwNUgLjJ4cn+Rktfghsa+E=",
+ "dependencies": {
+ "eachr": "3.2.0",
+ "editions": "1.3.4",
+ "typechecker": "4.7.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/extract-opts/node_modules/editions": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz",
+ "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+ "engines": [
+ "node >=0.6.0"
+ ]
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
+ },
+ "node_modules/file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
+ },
+ "node_modules/findup": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/findup/-/findup-0.1.5.tgz",
+ "integrity": "sha1-itkpozk7rGJ5V6fl3kYjsGsOLOs=",
+ "dependencies": {
+ "colors": "0.6.2",
+ "commander": "2.1.0"
+ },
+ "bin": {
+ "findup": "bin/findup.js"
+ },
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/findup/node_modules/commander": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz",
+ "integrity": "sha1-0SG7roYNmZKj1Re6lvVliOR8Z4E=",
+ "engines": {
+ "node": ">= 0.6.x"
+ }
+ },
+ "node_modules/foldline": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/foldline/-/foldline-1.1.0.tgz",
+ "integrity": "sha512-9SheyADS50hjvFYjFJ3OB/GlDz2mD1T2CHd7auIk4Uto5YYWPBcw8iYo3F+gENJ+/SOeH9tT0loHZSqlUlumTA=="
+ },
+ "node_modules/forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dependencies": {
+ "asynckit": "0.4.0",
+ "combined-stream": "1.0.8",
+ "mime-types": "2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.12"
+ }
+ },
+ "node_modules/frac": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
+ "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
+ },
+ "node_modules/fs-minipass": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+ "dependencies": {
+ "minipass": "3.1.3"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/fs-plus": {
+ "version": "2.10.1",
+ "resolved": "https://registry.npmjs.org/fs-plus/-/fs-plus-2.10.1.tgz",
+ "integrity": "sha1-MgR4HXhAYR5jZOe2+wWMljJ8WqU=",
+ "dependencies": {
+ "async": "1.5.2",
+ "mkdirp": "0.5.1",
+ "rimraf": "2.5.2",
+ "underscore-plus": "1.7.0"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.0.tgz",
+ "integrity": "sha512-Bs0VRrTz4ghD8pTmbJQD1mZ8A/mN0ur/jGz+A6FBxPDUPkm1tNfF6bhTYPA7i7aF4lZJVr+OXTNNrnnIl58Wfg==",
+ "dependencies": {
+ "define-properties": "1.1.3",
+ "function-bind": "1.1.1",
+ "is-callable": "1.1.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gauge": {
+ "version": "2.7.4",
+ "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+ "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+ "dependencies": {
+ "aproba": "1.2.0",
+ "console-control-strings": "1.1.0",
+ "has-unicode": "2.0.1",
+ "object-assign": "4.1.1",
+ "signal-exit": "3.0.3",
+ "string-width": "1.0.2",
+ "strip-ansi": "3.0.1",
+ "wide-align": "1.1.3"
+ }
+ },
+ "node_modules/get-document": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/get-document/-/get-document-1.0.0.tgz",
+ "integrity": "sha1-SCG85m8cJMsDMWAr5strEsTwHEs="
+ },
+ "node_modules/get-window": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/get-window/-/get-window-1.1.2.tgz",
+ "integrity": "sha512-yjWpFcy9fjhLQHW1dPtg9ga4pmizLY8y4ZSHdGrAQ1NU277MRhnGnnLPxe19X8W5lWVsCZz++5xEuNozWMVmTw==",
+ "dependencies": {
+ "get-document": "1.0.0"
+ }
+ },
+ "node_modules/getmac": {
+ "version": "1.4.6",
+ "resolved": "https://registry.npmjs.org/getmac/-/getmac-1.4.6.tgz",
+ "integrity": "sha512-3JPwiIr4P6Sgr6y6SVXX0+l2mrB6pyf4Cdyua7rvEV7SveWQkAp11vrkNym8wvRxzLrBenKRcwe93asdghuwWg==",
+ "dependencies": {
+ "editions": "2.1.3",
+ "extract-opts": "3.3.1"
+ },
+ "bin": {
+ "getmac-node": "bin.js"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+ "dependencies": {
+ "assert-plus": "1.0.0"
+ }
+ },
+ "node_modules/github-from-package": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
+ "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4="
+ },
+ "node_modules/glob": {
+ "version": "5.0.15",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+ "dependencies": {
+ "inflight": "1.0.6",
+ "inherits": "2.0.3",
+ "minimatch": "3.0.4",
+ "once": "1.4.0",
+ "path-is-absolute": "1.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.1.15",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA=="
+ },
+ "node_modules/grim": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/grim/-/grim-1.5.0.tgz",
+ "integrity": "sha1-sysI71Z88YUvgXWe2caLDXE5ajI=",
+ "dependencies": {
+ "emissary": "1.3.3"
+ }
+ },
+ "node_modules/har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/har-validator": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
+ "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
+ "dependencies": {
+ "ajv": "6.10.0",
+ "har-schema": "2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dependencies": {
+ "function-bind": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "dependencies": {
+ "ansi-regex": "2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
+ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/has-unicode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+ "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
+ },
+ "node_modules/hawk": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
+ "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=",
+ "optional": true,
+ "dependencies": {
+ "boom": "2.10.1",
+ "cryptiles": "2.0.5",
+ "hoek": "2.16.3",
+ "sntp": "1.0.9"
+ },
+ "engines": {
+ "node": ">=0.10.32"
+ }
+ },
+ "node_modules/hoek": {
+ "version": "2.16.3",
+ "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
+ "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.40"
+ }
+ },
+ "node_modules/html-attributes": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/html-attributes/-/html-attributes-1.1.0.tgz",
+ "integrity": "sha1-ggJ6T6x6YHDqbBjMOIauoY1t6gk=",
+ "engines": {
+ "node": ">= 0.10.26",
+ "npm": ">=1.4.3"
+ }
+ },
+ "node_modules/html-element-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/html-element-map/-/html-element-map-1.0.1.tgz",
+ "integrity": "sha512-BZSfdEm6n706/lBfXKWa4frZRZcT5k1cOusw95ijZsHlI+GdgY0v95h6IzO3iIDf2ROwq570YTwqNPqHcNMozw==",
+ "dependencies": {
+ "array-filter": "1.0.0"
+ }
+ },
+ "node_modules/htmlparser2": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
+ "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
+ "dependencies": {
+ "domelementtype": "1.3.1",
+ "domhandler": "2.4.2",
+ "domutils": "1.5.1",
+ "entities": "1.1.2",
+ "inherits": "2.0.3",
+ "readable-stream": "3.4.0"
+ }
+ },
+ "node_modules/http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+ "dependencies": {
+ "assert-plus": "1.0.0",
+ "jsprim": "1.4.1",
+ "sshpk": "1.16.1"
+ },
+ "engines": {
+ "node": ">=0.8",
+ "npm": ">=1.3.7"
+ }
+ },
+ "node_modules/ical-expander": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ical-expander/-/ical-expander-2.0.0.tgz",
+ "integrity": "sha512-DABFhfEl0c58MeiTbG8hO5VEWQfKlFA7jSqdoIbS2caxS4nGyA73nVBGsFtIbmTrDO7Osgw9/up2ZCnvuxGcKA==",
+ "dependencies": {
+ "ical.js": "1.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/ical.js": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/ical.js/-/ical.js-1.3.0.tgz",
+ "integrity": "sha512-wQ0w77MGOe6vNhZMBQuZAtZoTzDEOQ/QoCDofWL7yfkQ/HYWX5NyWPYjN+yj+4JahOvTkhXjTlrZtiQKv+BSOA=="
+ },
+ "node_modules/ieee754": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+ "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
+ },
+ "node_modules/image-size": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
+ "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=",
+ "optional": true,
+ "bin": {
+ "image-size": "bin/image-size.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/immutable": {
+ "version": "3.8.2",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
+ "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dependencies": {
+ "once": "1.4.0",
+ "wrappy": "1.0.2"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ },
+ "node_modules/ini": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dependencies": {
+ "loose-envify": "1.4.0"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.0.tgz",
+ "integrity": "sha1-mPiygDBoQhmpXzdc+9iM40Bd/5M=",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
+ "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
+ "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "dependencies": {
+ "number-is-nan": "1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-hotkey": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.0.3.tgz",
+ "integrity": "sha512-qpMe/7DxFXs65E3f8u5Zu+3qOXmqlKlAF/cpYlRiPzg1UKC9clzQcwcuf/2RC4WMFhh56DQC+FfK63Qp3lM35w=="
+ },
+ "node_modules/is-in-browser": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
+ "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU="
+ },
+ "node_modules/is-number-object": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.3.tgz",
+ "integrity": "sha1-8mWrian0RQNO9q/xWo8AsA9VF5k=",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dependencies": {
+ "isobject": "3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
+ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
+ "dependencies": {
+ "has": "1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.4.tgz",
+ "integrity": "sha1-zDqbaYV9Yh6WNyWiTK7shzuCbmQ=",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-subset": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz",
+ "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY="
+ },
+ "node_modules/is-symbol": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
+ "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
+ "dependencies": {
+ "has-symbols": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+ },
+ "node_modules/is-window": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-window/-/is-window-1.0.2.tgz",
+ "integrity": "sha1-LIlspT25feRdPDMTOmXYyfVjSA0="
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
+ },
+ "node_modules/isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isomorphic-base64": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/isomorphic-base64/-/isomorphic-base64-1.0.2.tgz",
+ "integrity": "sha1-9Caq6CVpuopOxcpzrSGkSrHueAM="
+ },
+ "node_modules/isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+ },
+ "node_modules/jasmine-json": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/jasmine-json/-/jasmine-json-0.0.3.tgz",
+ "integrity": "sha1-Xi6P1QqlhXAOjzWa9pawupZPg4c="
+ },
+ "node_modules/jasmine-react-helpers": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/jasmine-react-helpers/-/jasmine-react-helpers-0.2.2.tgz",
+ "integrity": "sha1-GkFuRw6okFTrCDa7nGGpE6wmhns="
+ },
+ "node_modules/jasmine-reporters": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/jasmine-reporters/-/jasmine-reporters-1.1.0.tgz",
+ "integrity": "sha1-8zUIhYkMntqtEqCHxi8swZ3PZsA=",
+ "dependencies": {
+ "mkdirp": "0.3.5"
+ }
+ },
+ "node_modules/jasmine-reporters/node_modules/mkdirp": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz",
+ "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc="
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "node_modules/jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
+ },
+ "node_modules/json-schema": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+ },
+ "node_modules/json-stable-stringify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
+ "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
+ "optional": true,
+ "dependencies": {
+ "jsonify": "0.0.0"
+ }
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+ },
+ "node_modules/jsonify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
+ "optional": true
+ },
+ "node_modules/jsprim": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+ "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "dependencies": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.2.3",
+ "verror": "1.10.0"
+ }
+ },
+ "node_modules/jszip": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-2.5.0.tgz",
+ "integrity": "sha1-dET9hVHd8+XacZj+oMkbyDCMwnQ=",
+ "dependencies": {
+ "pako": "0.2.9"
+ }
+ },
+ "node_modules/juice": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/juice/-/juice-5.2.0.tgz",
+ "integrity": "sha512-0l6GZmT3efexyaaay3SchKT5kG311N59TEFP5lfvEy0nz9SNqjx311plJ3b4jze7arsmDsiHQLh/xnAuk0HFTQ==",
+ "dependencies": {
+ "cheerio": "0.22.0",
+ "commander": "2.20.0",
+ "cross-spawn": "6.0.5",
+ "deep-extend": "0.6.0",
+ "mensch": "0.3.3",
+ "slick": "1.12.2",
+ "web-resource-inliner": "4.3.2"
+ },
+ "bin": {
+ "juice": "bin/juice"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/juice/node_modules/cheerio": {
+ "version": "0.22.0",
+ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz",
+ "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=",
+ "dependencies": {
+ "css-select": "1.2.0",
+ "dom-serializer": "0.1.1",
+ "entities": "1.1.2",
+ "htmlparser2": "3.10.1",
+ "lodash.assignin": "4.2.0",
+ "lodash.bind": "4.2.1",
+ "lodash.defaults": "4.2.0",
+ "lodash.filter": "4.6.0",
+ "lodash.flatten": "4.4.0",
+ "lodash.foreach": "4.5.0",
+ "lodash.map": "4.6.0",
+ "lodash.merge": "4.6.1",
+ "lodash.pick": "4.4.0",
+ "lodash.reduce": "4.6.0",
+ "lodash.reject": "4.6.0",
+ "lodash.some": "4.6.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/keyboard-layout": {
+ "version": "2.0.17",
+ "resolved": "https://registry.npmjs.org/keyboard-layout/-/keyboard-layout-2.0.17.tgz",
+ "integrity": "sha512-W9LL+1e8CS9fi0s8ZHINDN1HZ6QpYjE4yLi4+faed7ozppNOAxINjv5w16zG9tJv8Jp5LJrCfO5PZ9aV1m5d4g==",
+ "dependencies": {
+ "event-kit": "2.5.3",
+ "nan": "2.14.0"
+ }
+ },
+ "node_modules/keyboard-layout/node_modules/event-kit": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/event-kit/-/event-kit-2.5.3.tgz",
+ "integrity": "sha512-b7Qi1JNzY4BfAYfnIRanLk0DOD1gdkWHT4GISIn8Q2tAf3LpU8SP2CMwWaq40imYoKWbtN4ZhbSRxvsnikooZQ=="
+ },
+ "node_modules/keytar": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/keytar/-/keytar-5.5.0.tgz",
+ "integrity": "sha512-1d/F2qAL/qijpm25wNq8eez4mE+/J4eBvqyLfspIYIeCEHn3nttFwplowIZfbdNCjFGWh68MzGZOd2vS61Ffew==",
+ "dependencies": {
+ "nan": "2.14.0",
+ "prebuild-install": "5.3.3"
+ }
+ },
+ "node_modules/less": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/less/-/less-2.7.3.tgz",
+ "integrity": "sha512-KPdIJKWcEAb02TuJtaLrhue0krtRLoRoo7x6BNJIBelO00t/CCdJQUnHW5V34OnHMWzIktSalJxRO+FvytQlCQ==",
+ "dependencies": {
+ "graceful-fs": "4.1.15",
+ "mkdirp": "0.5.1"
+ },
+ "bin": {
+ "lessc": "bin/lessc"
+ },
+ "engines": {
+ "node": ">=0.12"
+ },
+ "optionalDependencies": {
+ "errno": "0.1.7",
+ "image-size": "0.5.5",
+ "mime": "1.6.0",
+ "promise": "7.3.1",
+ "request": "2.81.0",
+ "source-map": "0.5.7"
+ }
+ },
+ "node_modules/less-cache": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/less-cache/-/less-cache-1.1.0.tgz",
+ "integrity": "sha1-fi9rOV+lx6l0N0kFyFjy0+nRUyA=",
+ "dependencies": {
+ "fs-plus": "3.1.1",
+ "less": "2.7.3",
+ "underscore-plus": "1.7.0",
+ "walkdir": "0.0.11"
+ }
+ },
+ "node_modules/less-cache/node_modules/fs-plus": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/fs-plus/-/fs-plus-3.1.1.tgz",
+ "integrity": "sha512-Se2PJdOWXqos1qVTkvqqjb0CSnfBnwwD+pq+z4ksT+e97mEShod/hrNg0TRCCsXPbJzcIq+NuzQhigunMWMJUA==",
+ "dependencies": {
+ "async": "1.5.2",
+ "mkdirp": "0.5.1",
+ "rimraf": "2.5.2",
+ "underscore-plus": "1.7.0"
+ }
+ },
+ "node_modules/less/node_modules/ajv": {
+ "version": "4.11.8",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
+ "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
+ "optional": true,
+ "dependencies": {
+ "co": "4.6.0",
+ "json-stable-stringify": "1.0.1"
+ }
+ },
+ "node_modules/less/node_modules/assert-plus": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
+ "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=",
+ "optional": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/less/node_modules/aws-sign2": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
+ "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=",
+ "optional": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/less/node_modules/form-data": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz",
+ "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=",
+ "optional": true,
+ "dependencies": {
+ "asynckit": "0.4.0",
+ "combined-stream": "1.0.8",
+ "mime-types": "2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.12"
+ }
+ },
+ "node_modules/less/node_modules/har-schema": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz",
+ "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=",
+ "optional": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/less/node_modules/har-validator": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz",
+ "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=",
+ "optional": true,
+ "dependencies": {
+ "ajv": "4.11.8",
+ "har-schema": "1.0.5"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/less/node_modules/http-signature": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
+ "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
+ "optional": true,
+ "dependencies": {
+ "assert-plus": "0.2.0",
+ "jsprim": "1.4.1",
+ "sshpk": "1.16.1"
+ },
+ "engines": {
+ "node": ">=0.8",
+ "npm": ">=1.3.7"
+ }
+ },
+ "node_modules/less/node_modules/oauth-sign": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
+ "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=",
+ "optional": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/less/node_modules/performance-now": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz",
+ "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=",
+ "optional": true
+ },
+ "node_modules/less/node_modules/punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+ "optional": true
+ },
+ "node_modules/less/node_modules/qs": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
+ "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=",
+ "optional": true,
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/less/node_modules/request": {
+ "version": "2.81.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz",
+ "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=",
+ "optional": true,
+ "dependencies": {
+ "aws-sign2": "0.6.0",
+ "aws4": "1.8.0",
+ "caseless": "0.12.0",
+ "combined-stream": "1.0.8",
+ "extend": "3.0.2",
+ "forever-agent": "0.6.1",
+ "form-data": "2.1.4",
+ "har-validator": "4.2.1",
+ "hawk": "3.1.3",
+ "http-signature": "1.1.1",
+ "is-typedarray": "1.0.0",
+ "isstream": "0.1.2",
+ "json-stringify-safe": "5.0.1",
+ "mime-types": "2.1.24",
+ "oauth-sign": "0.8.2",
+ "performance-now": "0.2.0",
+ "qs": "6.4.0",
+ "safe-buffer": "5.1.2",
+ "stringstream": "0.0.6",
+ "tough-cookie": "2.3.4",
+ "tunnel-agent": "0.6.0",
+ "uuid": "3.3.2"
+ },
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/less/node_modules/tough-cookie": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz",
+ "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==",
+ "optional": true,
+ "dependencies": {
+ "punycode": "1.4.1"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.11",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
+ },
+ "node_modules/lodash.assign": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
+ "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc="
+ },
+ "node_modules/lodash.assignin": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz",
+ "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI="
+ },
+ "node_modules/lodash.bind": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz",
+ "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU="
+ },
+ "node_modules/lodash.defaults": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+ "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
+ },
+ "node_modules/lodash.escape": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz",
+ "integrity": "sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg="
+ },
+ "node_modules/lodash.filter": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz",
+ "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4="
+ },
+ "node_modules/lodash.flatten": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
+ "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
+ },
+ "node_modules/lodash.flattendeep": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
+ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI="
+ },
+ "node_modules/lodash.foreach": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
+ "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM="
+ },
+ "node_modules/lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
+ },
+ "node_modules/lodash.map": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz",
+ "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM="
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.1",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz",
+ "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ=="
+ },
+ "node_modules/lodash.pick": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
+ "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM="
+ },
+ "node_modules/lodash.reduce": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz",
+ "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs="
+ },
+ "node_modules/lodash.reject": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz",
+ "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU="
+ },
+ "node_modules/lodash.some": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz",
+ "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0="
+ },
+ "node_modules/lodash.toarray": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
+ "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE="
+ },
+ "node_modules/lodash.unescape": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
+ "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw="
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dependencies": {
+ "js-tokens": "4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lop": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/lop/-/lop-0.4.0.tgz",
+ "integrity": "sha1-Tw5DhNXE9FXQuG0lT9UqnQVZPCw=",
+ "dependencies": {
+ "duck": "0.1.11",
+ "option": "0.2.4",
+ "underscore": "1.4.4"
+ }
+ },
+ "node_modules/lop/node_modules/underscore": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz",
+ "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ="
+ },
+ "node_modules/lru-cache": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+ "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+ "dependencies": {
+ "pseudomap": "1.0.2",
+ "yallist": "2.1.2"
+ }
+ },
+ "node_modules/lsmod": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz",
+ "integrity": "sha1-mgD3bco26yP6BTUK/htYXUKZ5ks="
+ },
+ "node_modules/macos-notification-state": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/macos-notification-state/-/macos-notification-state-1.3.1.tgz",
+ "integrity": "sha512-Zd4fezXN4WaXw0fJnO3dr8Eg72AKI4C+g0Jnm5KcX8o7WWvIMV9rzsl8mtuOL8FT+QPYv1xiaLpElqhXubf3Yw==",
+ "optional": true,
+ "dependencies": {
+ "bindings": "1.5.0",
+ "nan": "2.14.0"
+ }
+ },
+ "node_modules/mammoth": {
+ "version": "1.4.7",
+ "resolved": "https://registry.npmjs.org/mammoth/-/mammoth-1.4.7.tgz",
+ "integrity": "sha512-vN13//bwnOhYPYArerIHXgDV6hhSzpyQjdtoRaJjMU9z60DBQlb/A2Ug59QBVgjWiJXpVUH0sLmXunbFg5J0EQ==",
+ "dependencies": {
+ "argparse": "1.0.10",
+ "bluebird": "3.4.7",
+ "jszip": "2.5.0",
+ "lop": "0.4.0",
+ "path-is-absolute": "1.0.1",
+ "sax": "1.1.6",
+ "underscore": "1.8.3",
+ "xmlbuilder": "10.1.1"
+ },
+ "bin": {
+ "mammoth": "bin/mammoth"
+ }
+ },
+ "node_modules/material-colors": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
+ "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
+ },
+ "node_modules/memoize-one": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-4.1.0.tgz",
+ "integrity": "sha512-2GApq0yI/b22J2j9rhbrAlsHb0Qcz+7yWxeLG8h+95sl1XPUgeLimQSOdur4Vw7cUhrBHwaUZxWFZueojqNRzA=="
+ },
+ "node_modules/mensch": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/mensch/-/mensch-0.3.3.tgz",
+ "integrity": "sha1-4gD/TdgjcX+OBWOzLj9UgfyiYrI="
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "optional": true,
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.40.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
+ "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.24",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
+ "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
+ "dependencies": {
+ "mime-db": "1.40.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/mimer/-/mimer-1.0.0.tgz",
+ "integrity": "sha512-4ZJvCzfcwsBgPbkKXUzGoVZMWjv8IDIygkGzVc7uUYhgnK0t2LmGxxjdgH1i+pn0/KQfB5F/VKUJlfyTSOFQjg==",
+ "bin": {
+ "mimer": "bin/mimer"
+ },
+ "engines": {
+ "node": ">= 6.0"
+ }
+ },
+ "node_modules/mimic-response": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
+ "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dependencies": {
+ "brace-expansion": "1.1.11"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ },
+ "node_modules/minipass": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
+ "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minizlib": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+ "dependencies": {
+ "minipass": "3.1.3"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/mixto": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/mixto/-/mixto-1.0.0.tgz",
+ "integrity": "sha1-wyDvYbUvKJj1IuF9i7xtUG2EJbY="
+ },
+ "node_modules/mkdirp": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "dependencies": {
+ "minimist": "0.0.8"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
+ "node_modules/mkdirp-classic": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz",
+ "integrity": "sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g=="
+ },
+ "node_modules/moment": {
+ "version": "2.24.0",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
+ "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/moment-round": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/moment-round/-/moment-round-1.0.1.tgz",
+ "integrity": "sha1-W68rdS6F0PaiJ8Chb2++EHwqLTk="
+ },
+ "node_modules/moment-timezone": {
+ "version": "0.5.32",
+ "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.32.tgz",
+ "integrity": "sha512-Z8QNyuQHQAmWucp8Knmgei8YNo28aLjJq6Ma+jy1ZSpSk5nyfRT8xgUbSQvD2+2UajISfenndwvFuH3NGS+nvA==",
+ "dependencies": {
+ "moment": "2.24.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/moo": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/moo/-/moo-0.4.3.tgz",
+ "integrity": "sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw=="
+ },
+ "node_modules/mousetrap": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.3.tgz",
+ "integrity": "sha512-bd+nzwhhs9ifsUrC2tWaSgm24/oo2c83zaRyZQF06hYA6sANfsXHtnZ19AbbbDXCDzeH5nZBSQ4NvCjgD62tJA=="
+ },
+ "node_modules/ms": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz",
+ "integrity": "sha1-cIFVpeROM/X9D8U+gdDUCpG+H/8="
+ },
+ "node_modules/nan": {
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
+ "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
+ },
+ "node_modules/napi-build-utils": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
+ "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
+ },
+ "node_modules/nearley": {
+ "version": "2.16.0",
+ "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.16.0.tgz",
+ "integrity": "sha512-Tr9XD3Vt/EujXbZBv6UAHYoLUSMQAxSsTnm9K3koXzjzNWY195NqALeyrzLZBKzAkL3gl92BcSogqrHjD8QuUg==",
+ "dependencies": {
+ "commander": "2.20.0",
+ "moo": "0.4.3",
+ "railroad-diagrams": "1.0.0",
+ "randexp": "0.4.6",
+ "semver": "5.7.0"
+ },
+ "bin": {
+ "nearley-railroad": "bin/nearley-railroad.js",
+ "nearley-test": "bin/nearley-test.js",
+ "nearley-unparse": "bin/nearley-unparse.js",
+ "nearleyc": "bin/nearleyc.js"
+ }
+ },
+ "node_modules/next-tick": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
+ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
+ },
+ "node_modules/nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
+ },
+ "node_modules/node-abi": {
+ "version": "2.15.0",
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.15.0.tgz",
+ "integrity": "sha512-FeLpTS0F39U7hHZU1srAK4Vx+5AHNVOTP+hxBNQknR/54laTHSFIJkDWDqiquY1LeLUgTfPN7sLPhMubx0PLAg==",
+ "dependencies": {
+ "semver": "5.7.0"
+ }
+ },
+ "node_modules/node-addon-api": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.0.tgz",
+ "integrity": "sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA=="
+ },
+ "node_modules/node-emoji": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz",
+ "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==",
+ "dependencies": {
+ "lodash.toarray": "4.4.0"
+ }
+ },
+ "node_modules/node-mac-notifier": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/node-mac-notifier/-/node-mac-notifier-1.1.0.tgz",
+ "integrity": "sha512-Fwv09eKMGkM4xn+Eby5g7lvmYF+1KYWX4V5QNn27l4cVFVS7MNYOxhbvGGHT5VuTWbWDQIi/Lr7l6so5vvmqVw==",
+ "optional": true,
+ "dependencies": {
+ "bindings": "1.5.0",
+ "event-target-shim": "1.1.1",
+ "uuid": "3.3.2"
+ }
+ },
+ "node_modules/noop-logger": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
+ "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI="
+ },
+ "node_modules/npmlog": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+ "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+ "dependencies": {
+ "are-we-there-yet": "1.1.5",
+ "console-control-strings": "1.1.0",
+ "gauge": "2.7.4",
+ "set-blocking": "2.0.0"
+ }
+ },
+ "node_modules/nth-check": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
+ "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
+ "dependencies": {
+ "boolbase": "1.0.0"
+ }
+ },
+ "node_modules/number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz",
+ "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ=="
+ },
+ "node_modules/object-is": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz",
+ "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+ "dependencies": {
+ "define-properties": "1.1.3",
+ "function-bind": "1.1.1",
+ "has-symbols": "1.0.0",
+ "object-keys": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.entries": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz",
+ "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==",
+ "dependencies": {
+ "define-properties": "1.1.3",
+ "es-abstract": "1.13.0",
+ "function-bind": "1.1.1",
+ "has": "1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz",
+ "integrity": "sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==",
+ "dependencies": {
+ "define-properties": "1.1.3",
+ "es-abstract": "1.13.0",
+ "function-bind": "1.1.1",
+ "has": "1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz",
+ "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==",
+ "dependencies": {
+ "define-properties": "1.1.3",
+ "es-abstract": "1.13.0",
+ "function-bind": "1.1.1",
+ "has": "1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dependencies": {
+ "wrappy": "1.0.2"
+ }
+ },
+ "node_modules/optimist": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.4.0.tgz",
+ "integrity": "sha1-y47Dfy/jqphky2eidSUOfhliCiU=",
+ "dependencies": {
+ "wordwrap": "0.0.2"
+ }
+ },
+ "node_modules/option": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/option/-/option-0.2.4.tgz",
+ "integrity": "sha1-/Udc35jcq7PLOXo7pShP60Xtv+Q="
+ },
+ "node_modules/os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pako": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
+ "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU="
+ },
+ "node_modules/parse5": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz",
+ "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==",
+ "dependencies": {
+ "@types/node": "12.0.7"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+ },
+ "node_modules/pick-react-known-prop": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/pick-react-known-prop/-/pick-react-known-prop-0.1.5.tgz",
+ "integrity": "sha512-SnDf64AVdvqoAFpHeZUKT9kdn40Ellj84CPALRxYWqNJ6r6f44eAAT+Jtkb0Suhiw7yg5BdOFAQ25OJnjG+afw==",
+ "dependencies": {
+ "html-attributes": "1.1.0",
+ "lodash.isplainobject": "4.0.6",
+ "svg-attributes": "1.0.0"
+ }
+ },
+ "node_modules/pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "6.0.23",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
+ "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
+ "dependencies": {
+ "chalk": "2.4.2",
+ "source-map": "0.6.1",
+ "supports-color": "5.5.0"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/postcss/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/prebuild-install": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.3.tgz",
+ "integrity": "sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==",
+ "dependencies": {
+ "detect-libc": "1.0.3",
+ "expand-template": "2.0.3",
+ "github-from-package": "0.0.0",
+ "minimist": "1.2.5",
+ "mkdirp": "0.5.1",
+ "napi-build-utils": "1.0.2",
+ "node-abi": "2.15.0",
+ "noop-logger": "0.1.1",
+ "npmlog": "4.1.2",
+ "pump": "3.0.0",
+ "rc": "1.2.8",
+ "simple-get": "3.1.0",
+ "tar-fs": "2.0.1",
+ "tunnel-agent": "0.6.0",
+ "which-pm-runs": "1.0.0"
+ },
+ "bin": {
+ "prebuild-install": "bin.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/prebuild-install/node_modules/minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+ },
+ "node_modules/printj": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz",
+ "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==",
+ "bin": {
+ "printj": "bin/printj.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+ },
+ "node_modules/promise": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+ "optional": true,
+ "dependencies": {
+ "asap": "2.0.6"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.7.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
+ "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+ "dependencies": {
+ "loose-envify": "1.4.0",
+ "object-assign": "4.1.1",
+ "react-is": "16.8.6"
+ }
+ },
+ "node_modules/prop-types-exact": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/prop-types-exact/-/prop-types-exact-1.2.0.tgz",
+ "integrity": "sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA==",
+ "dependencies": {
+ "has": "1.0.3",
+ "object.assign": "4.1.0",
+ "reflect.ownkeys": "0.2.0"
+ }
+ },
+ "node_modules/property-accessors": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/property-accessors/-/property-accessors-1.1.3.tgz",
+ "integrity": "sha1-Hd6EAkYxhlkJ7zBwM2VoDF+SixU=",
+ "dependencies": {
+ "es6-weak-map": "0.1.4",
+ "mixto": "1.0.0"
+ }
+ },
+ "node_modules/proxyquire": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-1.3.1.tgz",
+ "integrity": "sha1-EQecCx2R+A74z3phxlDNB7tF2To="
+ },
+ "node_modules/prr": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+ "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
+ "optional": true
+ },
+ "node_modules/pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
+ },
+ "node_modules/psl": {
+ "version": "1.1.32",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz",
+ "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g=="
+ },
+ "node_modules/pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dependencies": {
+ "end-of-stream": "1.4.4",
+ "once": "1.4.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/raf": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
+ "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
+ "dependencies": {
+ "performance-now": "2.1.0"
+ }
+ },
+ "node_modules/railroad-diagrams": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
+ "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234="
+ },
+ "node_modules/randexp": {
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
+ "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
+ "dependencies": {
+ "discontinuous-range": "1.0.0",
+ "ret": "0.1.15"
+ },
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/raven": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/raven/-/raven-2.1.2.tgz",
+ "integrity": "sha512-pBopqPVAuzEP5jyyqEPxJvXg788dAJ9p8NXoHVLF598Ih7MApu2AWT/QHNk2Fa4Rx/xV9yTYDEtAujLXX1FvRg==",
+ "dependencies": {
+ "cookie": "0.3.1",
+ "json-stringify-safe": "5.0.1",
+ "lsmod": "1.0.0",
+ "stack-trace": "0.0.9",
+ "timed-out": "4.0.1",
+ "uuid": "3.0.0"
+ },
+ "bin": {
+ "raven": "bin/raven"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/raven/node_modules/uuid": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz",
+ "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg=",
+ "bin": {
+ "uuid": "bin/uuid"
+ }
+ },
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dependencies": {
+ "deep-extend": "0.6.0",
+ "ini": "1.3.5",
+ "minimist": "1.2.5",
+ "strip-json-comments": "2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/rc/node_modules/minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+ },
+ "node_modules/react": {
+ "version": "16.6.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-16.6.0.tgz",
+ "integrity": "sha512-zJPnx/jKtuOEXCbQ9BKaxDMxR0001/hzxXwYxG8septeyYGfsgAei6NgfbVgOhbY1WOP2o3VPs/E9HaN+9hV3Q==",
+ "dependencies": {
+ "loose-envify": "1.4.0",
+ "object-assign": "4.1.1",
+ "prop-types": "15.7.2",
+ "scheduler": "0.10.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-color": {
+ "version": "2.17.3",
+ "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.17.3.tgz",
+ "integrity": "sha512-1dtO8LqAVotPIChlmo6kLtFS1FP89ll8/OiA8EcFRDR+ntcK+0ukJgByuIQHRtzvigf26dV5HklnxDIvhON9VQ==",
+ "dependencies": {
+ "@icons/material": "0.2.4",
+ "lodash": "4.17.11",
+ "material-colors": "1.2.6",
+ "prop-types": "15.7.2",
+ "reactcss": "1.2.3",
+ "tinycolor2": "1.4.1"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "16.6.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.6.0.tgz",
+ "integrity": "sha512-Stm2D9dXEUUAQdvpvhvFj/DEXwC2PAL/RwEMhoN4dvvD2ikTlJegEXf97xryg88VIAU22ZAP7n842l+9BTz6+w==",
+ "dependencies": {
+ "loose-envify": "1.4.0",
+ "object-assign": "4.1.1",
+ "prop-types": "15.7.2",
+ "scheduler": "0.10.0"
+ }
+ },
+ "node_modules/react-immutable-proptypes": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/react-immutable-proptypes/-/react-immutable-proptypes-2.2.0.tgz",
+ "integrity": "sha512-Vf4gBsePlwdGvSZoLSBfd4HAP93HDauMY4fDjXhreg/vg6F3Fj/MXDNyTbltPC/xZKmZc+cjLu3598DdYK6sgQ==",
+ "dependencies": {
+ "invariant": "2.2.4"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "16.8.6",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
+ "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA=="
+ },
+ "node_modules/react-test-renderer": {
+ "version": "16.6.0",
+ "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.6.0.tgz",
+ "integrity": "sha512-w+Y3YT7OX1LP5KO7HCd0YR34Ol1qmISHaooPNMRYa6QzmwtcWhEGuZPr34wO8UCBIokswuhyLQUq7rjPDcEtJA==",
+ "dependencies": {
+ "object-assign": "4.1.1",
+ "prop-types": "15.7.2",
+ "react-is": "16.8.6",
+ "scheduler": "0.10.0"
+ }
+ },
+ "node_modules/react-transition-group": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-1.2.1.tgz",
+ "integrity": "sha512-CWaL3laCmgAFdxdKbhhps+c0HRGF4c+hdM4H23+FI1QBNUyx/AMeIJGWorehPNSaKnQNOAxL7PQmqMu78CDj3Q==",
+ "dependencies": {
+ "chain-function": "1.0.1",
+ "dom-helpers": "3.4.0",
+ "loose-envify": "1.4.0",
+ "prop-types": "15.7.2",
+ "warning": "3.0.0"
+ }
+ },
+ "node_modules/reactcss": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
+ "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==",
+ "dependencies": {
+ "lodash": "4.17.11"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
+ "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
+ "dependencies": {
+ "inherits": "2.0.3",
+ "string_decoder": "1.2.0",
+ "util-deprecate": "1.0.2"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/reflect.ownkeys": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz",
+ "integrity": "sha1-dJrO7H8/34tj+SegSAnpDFwLNGA="
+ },
+ "node_modules/reflux": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/reflux/-/reflux-0.1.13.tgz",
+ "integrity": "sha1-ZLAsuL5IYDOYhViRsQG7uq1Cllo=",
+ "dependencies": {
+ "eventemitter3": "0.1.6"
+ }
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.13.2",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz",
+ "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA=="
+ },
+ "node_modules/request": {
+ "version": "2.88.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
+ "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
+ "dependencies": {
+ "aws-sign2": "0.7.0",
+ "aws4": "1.8.0",
+ "caseless": "0.12.0",
+ "combined-stream": "1.0.8",
+ "extend": "3.0.2",
+ "forever-agent": "0.6.1",
+ "form-data": "2.3.3",
+ "har-validator": "5.1.3",
+ "http-signature": "1.2.0",
+ "is-typedarray": "1.0.0",
+ "isstream": "0.1.2",
+ "json-stringify-safe": "5.0.1",
+ "mime-types": "2.1.24",
+ "oauth-sign": "0.9.0",
+ "performance-now": "2.1.0",
+ "qs": "6.5.2",
+ "safe-buffer": "5.1.2",
+ "tough-cookie": "2.4.3",
+ "tunnel-agent": "0.6.0",
+ "uuid": "3.3.2"
+ },
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz",
+ "integrity": "sha1-YrqUf6TAtDY4Oa7+zU8PutYFlyY=",
+ "dependencies": {
+ "glob": "7.1.4"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/rimraf/node_modules/glob": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
+ "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
+ "dependencies": {
+ "fs.realpath": "1.0.0",
+ "inflight": "1.0.6",
+ "inherits": "2.0.3",
+ "minimatch": "3.0.4",
+ "once": "1.4.0",
+ "path-is-absolute": "1.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/rst-selector-parser": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
+ "integrity": "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=",
+ "dependencies": {
+ "lodash.flattendeep": "4.4.0",
+ "nearley": "2.16.0"
+ }
+ },
+ "node_modules/rtlcss": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-2.4.0.tgz",
+ "integrity": "sha512-hdjFhZ5FCI0ABOfyXOMOhBtwPWtANLCG7rOiOcRf+yi5eDdxmDjqBruWouEnwVdzfh/TWF6NNncIEsigOCFZOA==",
+ "dependencies": {
+ "chalk": "2.4.2",
+ "findup": "0.1.5",
+ "mkdirp": "0.5.1",
+ "postcss": "6.0.23",
+ "strip-json-comments": "2.0.1"
+ },
+ "bin": {
+ "rtlcss": "bin/rtlcss.js"
+ }
+ },
+ "node_modules/rx-lite": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
+ "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ="
+ },
+ "node_modules/rxjs": {
+ "version": "5.5.12",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz",
+ "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==",
+ "dependencies": {
+ "symbol-observable": "1.0.1"
+ },
+ "engines": {
+ "npm": ">=2.0.0"
+ }
+ },
+ "node_modules/rxjs-serial-subscription": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/rxjs-serial-subscription/-/rxjs-serial-subscription-0.1.1.tgz",
+ "integrity": "sha1-pCsdsL8QlLCSMRkeJ3jKP8+e0Uc=",
+ "dependencies": {
+ "rxjs": "5.5.12"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "node_modules/sax": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.6.tgz",
+ "integrity": "sha1-XWFr6KXmB9VOEUr65Vt+ry/MMkA="
+ },
+ "node_modules/scheduler": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.10.0.tgz",
+ "integrity": "sha512-+TSTVTCBAA3h8Anei3haDc1IRwMeDmtI/y/o3iBe3Mjl2vwYF9DtPDt929HyRmV/e7au7CLu8sc4C4W0VOs29w==",
+ "dependencies": {
+ "loose-envify": "1.4.0",
+ "object-assign": "4.1.1"
+ }
+ },
+ "node_modules/selection-is-backward": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/selection-is-backward/-/selection-is-backward-1.0.0.tgz",
+ "integrity": "sha1-l6VGMxiKURq6ZBn8XB+pG0Z+a+E="
+ },
+ "node_modules/semver": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
+ "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+ },
+ "node_modules/shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dependencies": {
+ "shebang-regex": "1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
+ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
+ },
+ "node_modules/simple-concat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
+ "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
+ },
+ "node_modules/simple-get": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
+ "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
+ "dependencies": {
+ "decompress-response": "4.2.1",
+ "once": "1.4.0",
+ "simple-concat": "1.0.0"
+ }
+ },
+ "node_modules/slate": {
+ "resolved": "git+ssh://git@github.com/bengotow/slate.git#cd6f40e8db9058f518c091cffe364608b3c858e6",
+ "dependencies": {
+ "debug": "3.2.7",
+ "direction": "0.1.5",
+ "esrever": "0.2.0",
+ "is-plain-object": "2.0.4",
+ "lodash": "4.17.11",
+ "tiny-invariant": "1.1.0",
+ "tiny-warning": "0.0.3",
+ "type-of": "2.0.1"
+ }
+ },
+ "node_modules/slate-auto-replace": {
+ "version": "0.12.1",
+ "resolved": "https://registry.npmjs.org/slate-auto-replace/-/slate-auto-replace-0.12.1.tgz",
+ "integrity": "sha512-4TBjWk/NKk2ef8FvPM0kYiOY+fClPieqyxAzAa2lpFbUnVX01I9Up4X34d/cmAlyX2jojn5AvBA/GG5xDschBg==",
+ "dependencies": {
+ "is-hotkey": "0.0.3",
+ "type-of": "2.0.1"
+ }
+ },
+ "node_modules/slate-base64-serializer": {
+ "version": "0.2.100",
+ "resolved": "https://registry.npmjs.org/slate-base64-serializer/-/slate-base64-serializer-0.2.100.tgz",
+ "integrity": "sha512-LpESVGnN3twV/8Sen12YgIEN/CPY1rpff+w7jRBwy/ux/42R68/BanyEOQbKLscEMb//19Dwd6TvrG6ECMqLyQ==",
+ "dependencies": {
+ "isomorphic-base64": "1.0.2"
+ }
+ },
+ "node_modules/slate-dev-environment": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/slate-dev-environment/-/slate-dev-environment-0.2.2.tgz",
+ "integrity": "sha512-JZ09llrRQu6JUsLJCUlGC0lB1r1qIAabAkSd454iyYBq6lDuY//Bypi3Jo8yzIfzZ4+mRLdQvl9e8MbeM9l48Q==",
+ "dependencies": {
+ "is-in-browser": "1.1.3"
+ }
+ },
+ "node_modules/slate-hotkeys": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/slate-hotkeys/-/slate-hotkeys-0.2.9.tgz",
+ "integrity": "sha512-y+C/s5vJEmBxo8fIqHmUcdViGwALL/A6Qow3sNG1OHYD5SI11tC2gfYtGbPh+2q0H7O4lufffCmFsP5bMaDHqA==",
+ "dependencies": {
+ "is-hotkey": "0.1.4",
+ "slate-dev-environment": "0.2.2"
+ }
+ },
+ "node_modules/slate-hotkeys/node_modules/is-hotkey": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.1.4.tgz",
+ "integrity": "sha512-Py+aW4r5mBBY18TGzGz286/gKS+fCQ0Hee3qkaiSmEPiD0PqFpe0wuA3l7rTOUKyeXl8Mxf3XzJxIoTlSv+kxA=="
+ },
+ "node_modules/slate-html-serializer": {
+ "version": "0.7.39",
+ "resolved": "https://registry.npmjs.org/slate-html-serializer/-/slate-html-serializer-0.7.39.tgz",
+ "integrity": "sha512-ZNyVjqCasSa0M80+4W1bFXMhW4KJM7EE6dkyBOhQmIcuRX529xOahcz+6ZYK7fQn0Cyrye899OtX26LulAh/Ew==",
+ "dependencies": {
+ "type-of": "2.0.1"
+ }
+ },
+ "node_modules/slate-plain-serializer": {
+ "version": "0.6.39",
+ "resolved": "https://registry.npmjs.org/slate-plain-serializer/-/slate-plain-serializer-0.6.39.tgz",
+ "integrity": "sha512-EGl+Y+9Fw9IULtPg8sttydaeiAoaibJolMXNfqI79+5GWTQwJFIbg24keKvsTw+3f2RieaPu8fcrKyujKtZ7ZQ=="
+ },
+ "node_modules/slate-prop-types": {
+ "version": "0.5.30",
+ "resolved": "https://registry.npmjs.org/slate-prop-types/-/slate-prop-types-0.5.30.tgz",
+ "integrity": "sha512-zm3gVmnGn0a9l/75REkAC0zYQGl3U/j2Z4p4pZ4rLupCptLM8iLWeGdn07V3JVCUA6n/80y9Mtv43Z9b1G/BAA=="
+ },
+ "node_modules/slate-react": {
+ "resolved": "git+ssh://git@github.com/bengotow/slate.git#4eb9d59ec29bca6d7edf8e61283249cf81034d65",
+ "dependencies": {
+ "debug": "3.2.7",
+ "get-window": "1.1.2",
+ "is-window": "1.0.2",
+ "lodash": "4.17.11",
+ "memoize-one": "4.1.0",
+ "prop-types": "15.7.2",
+ "react-immutable-proptypes": "2.2.0",
+ "selection-is-backward": "1.0.0",
+ "slate-base64-serializer": "0.2.100",
+ "slate-dev-environment": "0.2.2",
+ "slate-hotkeys": "0.2.9",
+ "slate-plain-serializer": "0.6.39",
+ "slate-prop-types": "0.5.30",
+ "slate-react-placeholder": "0.1.20",
+ "tiny-invariant": "1.1.0",
+ "tiny-warning": "0.0.3"
+ }
+ },
+ "node_modules/slate-react-placeholder": {
+ "version": "0.1.20",
+ "resolved": "https://registry.npmjs.org/slate-react-placeholder/-/slate-react-placeholder-0.1.20.tgz",
+ "integrity": "sha512-A4xq1kS3V3YetFbLE/1dv+/SDVjx9zsZZepJqjcmkGK+evHU2yNkWjZXCg8MLMRtZJpGtYT/BE3+kHbkgT5Q4A=="
+ },
+ "node_modules/slate-react/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dependencies": {
+ "ms": "2.1.3"
+ }
+ },
+ "node_modules/slate-react/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+ },
+ "node_modules/slate-soft-break": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/slate-soft-break/-/slate-soft-break-0.9.0.tgz",
+ "integrity": "sha512-iTy2bl/G+tphN10YQBOPDRxDqgM46ZH1UUz0ublmL6PLtwJ3jJK1n08w37YxnY/ge4aW31UN386KV6qvFx6Hsw=="
+ },
+ "node_modules/slate-when": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/slate-when/-/slate-when-0.2.0.tgz",
+ "integrity": "sha512-FtM9N1lrgWSlo4YHME6p2EnNlcDFbn0W8INvq0e4ithbvOZgFwqwrVWbgaFeGTntcEi934Lz8nqLwcVvxDt/zQ=="
+ },
+ "node_modules/slate/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dependencies": {
+ "ms": "2.1.3"
+ }
+ },
+ "node_modules/slate/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+ },
+ "node_modules/slick": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz",
+ "integrity": "sha1-vQSN23TefRymkV+qSldXCzVQwtc=",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/snarkdown": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/snarkdown/-/snarkdown-1.2.2.tgz",
+ "integrity": "sha1-DP4vMBK4BN4SD8DJ93kehpxZzHQ="
+ },
+ "node_modules/sntp": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
+ "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
+ "optional": true,
+ "dependencies": {
+ "hoek": "2.16.3"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.3.3.tgz",
+ "integrity": "sha1-NJAJd9W6PwfHdX7nLnO7GptTdU8=",
+ "dependencies": {
+ "source-map": "0.1.32"
+ }
+ },
+ "node_modules/source-map-support/node_modules/source-map": {
+ "version": "0.1.32",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.32.tgz",
+ "integrity": "sha1-yLbBZ3l7pHQKjqMyUhYv8IWRsmY=",
+ "dependencies": {
+ "amdefine": "1.0.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/spawn-rx": {
+ "version": "2.0.12",
+ "resolved": "https://registry.npmjs.org/spawn-rx/-/spawn-rx-2.0.12.tgz",
+ "integrity": "sha512-gOPXiQQFQ9lTOLuys0iMn3jfxxv9c7zzwhbYLOEbQGvEShHVJ5sSR1oD3Daj88os7jKArDYT7rbOKdvNhe7iEg==",
+ "dependencies": {
+ "debug": "github:emorikawa/debug#29685485665b44c42d33ebd7ed62570b21c6e4bd",
+ "lodash.assign": "4.2.0",
+ "rxjs": "5.5.12"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
+ },
+ "node_modules/ssf": {
+ "version": "0.10.2",
+ "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.10.2.tgz",
+ "integrity": "sha512-rDhAPm9WyIsY8eZEKyE8Qsotb3j/wBdvMWBUsOhJdfhKGLfQidRjiBUV0y/MkyCLiXQ38FG6LWW/VYUtqlIDZQ==",
+ "dependencies": {
+ "frac": "1.1.2"
+ },
+ "bin": {
+ "ssf": "bin/ssf.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/sshpk": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
+ "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+ "dependencies": {
+ "asn1": "0.2.4",
+ "assert-plus": "1.0.0",
+ "bcrypt-pbkdf": "1.0.2",
+ "dashdash": "1.14.1",
+ "ecc-jsbn": "0.1.2",
+ "getpass": "0.1.7",
+ "jsbn": "0.1.1",
+ "safer-buffer": "2.1.2",
+ "tweetnacl": "0.14.5"
+ },
+ "bin": {
+ "sshpk-conv": "bin/sshpk-conv",
+ "sshpk-sign": "bin/sshpk-sign",
+ "sshpk-verify": "bin/sshpk-verify"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stack-trace": {
+ "version": "0.0.9",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz",
+ "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz",
+ "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==",
+ "dependencies": {
+ "safe-buffer": "5.1.2"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "dependencies": {
+ "code-point-at": "1.1.0",
+ "is-fullwidth-code-point": "1.0.0",
+ "strip-ansi": "3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz",
+ "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=",
+ "dependencies": {
+ "define-properties": "1.1.3",
+ "es-abstract": "1.13.0",
+ "function-bind": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/stringstream": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz",
+ "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==",
+ "optional": true
+ },
+ "node_modules/strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dependencies": {
+ "ansi-regex": "2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dependencies": {
+ "has-flag": "3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/svg-attributes": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/svg-attributes/-/svg-attributes-1.0.0.tgz",
+ "integrity": "sha1-tcWWjzYke32+OFMgfyqcaK2Aa/w=",
+ "engines": {
+ "node": ">= 0.10.26",
+ "npm": ">=1.4.3"
+ }
+ },
+ "node_modules/symbol-observable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz",
+ "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/tar": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz",
+ "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==",
+ "dependencies": {
+ "fs-minipass": "2.1.0",
+ "minipass": "3.1.3",
+ "minizlib": "2.1.2"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/tar-fs": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
+ "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==",
+ "dependencies": {
+ "chownr": "1.1.4",
+ "mkdirp-classic": "0.5.2",
+ "pump": "3.0.0",
+ "tar-stream": "2.1.2"
+ }
+ },
+ "node_modules/tar-stream": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz",
+ "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==",
+ "dependencies": {
+ "bl": "4.0.2",
+ "end-of-stream": "1.4.4",
+ "fs-constants": "1.0.0",
+ "inherits": "2.0.3",
+ "readable-stream": "3.4.0"
+ }
+ },
+ "node_modules/temp": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz",
+ "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=",
+ "engines": [
+ "node >=0.8.0"
+ ],
+ "dependencies": {
+ "os-tmpdir": "1.0.2",
+ "rimraf": "2.2.8"
+ }
+ },
+ "node_modules/temp/node_modules/rimraf": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
+ "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=",
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/timed-out": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
+ "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/tiny-invariant": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",
+ "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw=="
+ },
+ "node_modules/tiny-warning": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-0.0.3.tgz",
+ "integrity": "sha512-r0SSA5Y5IWERF9Xh++tFPx0jITBgGggOsRLDWWew6YRw/C2dr4uNO1fw1vanrBmHsICmPyMLNBZboTlxUmUuaA=="
+ },
+ "node_modules/tinycolor2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
+ "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/tld": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/tld/-/tld-0.0.2.tgz",
+ "integrity": "sha1-9g442CzaB14NzHlgHOXWgpzASd4=",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/tough-cookie": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
+ "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
+ "dependencies": {
+ "psl": "1.1.32",
+ "punycode": "1.4.1"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/tough-cookie/node_modules/punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
+ },
+ "node_modules/tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "dependencies": {
+ "safe-buffer": "5.1.2"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
+ },
+ "node_modules/type-of": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/type-of/-/type-of-2.0.1.tgz",
+ "integrity": "sha1-5yoXQYllaOn2KDeNgW1pEvfyOXI="
+ },
+ "node_modules/typechecker": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/typechecker/-/typechecker-4.7.0.tgz",
+ "integrity": "sha512-4LHc1KMNJ6NDGO+dSM/yNfZQRtp8NN7psYrPHUblD62Dvkwsp3VShsbM78kOgpcmMkRTgvwdKOTjctS+uMllgQ==",
+ "dependencies": {
+ "editions": "2.1.3"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/underscore": {
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
+ "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI="
+ },
+ "node_modules/underscore-plus": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/underscore-plus/-/underscore-plus-1.7.0.tgz",
+ "integrity": "sha512-A3BEzkeicFLnr+U/Q3EyWwJAQPbA19mtZZ4h+lLq3ttm9kn8WC4R3YpuJZEXmWdLjYP47Zc8aLZm9kwdv+zzvA==",
+ "dependencies": {
+ "underscore": "1.9.1"
+ }
+ },
+ "node_modules/underscore-plus/node_modules/underscore": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz",
+ "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg=="
+ },
+ "node_modules/underscore.string": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz",
+ "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==",
+ "dependencies": {
+ "sprintf-js": "1.0.3",
+ "util-deprecate": "1.0.2"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+ "dependencies": {
+ "punycode": "2.1.1"
+ }
+ },
+ "node_modules/utf7": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/utf7/-/utf7-1.0.2.tgz",
+ "integrity": "sha1-lV9JCq5lO6IguUVqCod2wZk2CZE=",
+ "dependencies": {
+ "semver": "5.3.0"
+ }
+ },
+ "node_modules/utf7/node_modules/semver": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
+ "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+ },
+ "node_modules/uuid": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+ "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
+ "bin": {
+ "uuid": "bin/uuid"
+ }
+ },
+ "node_modules/valid-data-url": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-0.1.6.tgz",
+ "integrity": "sha512-FXg2qXMzfAhZc0y2HzELNfUeiOjPr+52hU1DNBWiJJ2luXD+dD1R9NA48Ug5aj0ibbxroeGDc/RJv6ThiGgkDw=="
+ },
+ "node_modules/vcf": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/vcf/-/vcf-2.0.5.tgz",
+ "integrity": "sha512-HnTtrJ1xBYmjbCyB24/2Vbugu884e0TI5QL/6QevfNuw4cmhXYaSxTVscEgfK35em6Zehmz3Jc3N/a1K4QmZ9A==",
+ "dependencies": {
+ "camelcase": "5.3.1",
+ "foldline": "1.1.0"
+ }
+ },
+ "node_modules/verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "dependencies": {
+ "assert-plus": "1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "1.3.0"
+ }
+ },
+ "node_modules/void-elements": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
+ "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/walkdir": {
+ "version": "0.0.11",
+ "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.11.tgz",
+ "integrity": "sha1-oW0CXrkxvQO1LzCMrtD0D86+lTI=",
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/warning": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
+ "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
+ "dependencies": {
+ "loose-envify": "1.4.0"
+ }
+ },
+ "node_modules/web-resource-inliner": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-4.3.2.tgz",
+ "integrity": "sha512-eVnNqwG20sbAgqv2JONwyr57UNZFJP4oauioeUjpCMY83AM11956eIhxlCGGXfSMi7bRBjR9Vao05bXFzslh7w==",
+ "dependencies": {
+ "async": "2.6.2",
+ "chalk": "1.1.3",
+ "datauri": "2.0.0",
+ "htmlparser2": "3.10.1",
+ "lodash.unescape": "4.0.1",
+ "request": "2.88.0",
+ "safer-buffer": "2.1.2",
+ "valid-data-url": "0.1.6",
+ "xtend": "4.0.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/web-resource-inliner/node_modules/ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/web-resource-inliner/node_modules/async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
+ "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
+ "dependencies": {
+ "lodash": "4.17.11"
+ }
+ },
+ "node_modules/web-resource-inliner/node_modules/chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dependencies": {
+ "ansi-styles": "2.2.1",
+ "escape-string-regexp": "1.0.5",
+ "has-ansi": "2.0.0",
+ "strip-ansi": "3.0.1",
+ "supports-color": "2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/web-resource-inliner/node_modules/supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dependencies": {
+ "isexe": "2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
+ "node_modules/which-pm-runs": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
+ "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
+ },
+ "node_modules/wide-align": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+ "dependencies": {
+ "string-width": "1.0.2"
+ }
+ },
+ "node_modules/windows-iana": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/windows-iana/-/windows-iana-4.2.1.tgz",
+ "integrity": "sha512-ewNtGiWhAh66r5lYeWwiYWgg3ERcD+KiLMOqA8UCAKnKkFieb9M0VprLt7HD37bRFZKP7CLyXksj2L5hbrtnnw=="
+ },
+ "node_modules/windows-quiet-hours": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/windows-quiet-hours/-/windows-quiet-hours-1.2.7.tgz",
+ "integrity": "sha512-PjKM2/RQhZ3ikG4COq0vPpXvmJsxckcg2YN3xmQv8kZl5l1uu00a/epSGDeY6tczDPkzBhoThBz1FKRqxBA7cQ==",
+ "optional": true,
+ "dependencies": {
+ "bindings": "1.5.0",
+ "nan": "2.14.0"
+ }
+ },
+ "node_modules/windows-shortcuts": {
+ "resolved": "git+ssh://git@github.com/emorikawa/windows-shortcuts.git#b0a0fc7fb86fb03e06ddceb9cbd6c9c5c29e571e"
+ },
+ "node_modules/wordwrap": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+ "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ },
+ "node_modules/xlsx": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.14.1.tgz",
+ "integrity": "sha512-7hjB5YuyJo1fuuzXQjwuxD8LSUzE4Rxu5ToC3fB5JSunZxGjLcgKg69bEFG9GYoxeVDx5GL0k1dUodlvaQNRQw==",
+ "dependencies": {
+ "adler-32": "1.2.0",
+ "cfb": "1.1.1",
+ "codepage": "1.14.0",
+ "commander": "2.17.1",
+ "crc-32": "1.2.0",
+ "exit-on-epipe": "1.0.1",
+ "ssf": "0.10.2"
+ },
+ "bin": {
+ "xlsx": "bin/xlsx.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/xlsx/node_modules/commander": {
+ "version": "2.17.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
+ "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
+ },
+ "node_modules/xmlbuilder": {
+ "version": "10.1.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.1.tgz",
+ "integrity": "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/xtend": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
+ }
+ },
"dependencies": {
"@babel/runtime": {
"version": "7.4.5",
@@ -13,32 +4032,22 @@
}
},
"@bengotow/slate-edit-list": {
- "version": "github:bengotow/slate-edit-list#b868e1088b44ad1adb2cb06bbbb847a2fef7edb5"
+ "version": "git+ssh://git@github.com/bengotow/slate-edit-list.git#b868e1088b44ad1adb2cb06bbbb847a2fef7edb5",
+ "from": "@bengotow/slate-edit-list@github:bengotow/slate-edit-list#b868e108"
+ },
+ "@felixrieseberg/spellchecker": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/@felixrieseberg/spellchecker/-/spellchecker-4.0.12.tgz",
+ "integrity": "sha512-jLAPnRALB1I6Un8ldHVJfJid7m2R1qXoafFF/95sdm7R5VPOsZ3xTreZ/wLKO5x9AdsD2t9zpOcjDFTsCf3VzQ==",
+ "requires": {
+ "nan": "2.14.0"
+ }
},
"@icons/material": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
"integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw=="
},
- "@paulcbetts/cld": {
- "version": "2.4.6",
- "resolved": "https://registry.npmjs.org/@paulcbetts/cld/-/cld-2.4.6.tgz",
- "integrity": "sha1-qZL2vEPKshKsLESIpnHPMC+LYuc=",
- "requires": {
- "glob": "5.0.15",
- "nan": "2.14.0",
- "rimraf": "2.5.2",
- "underscore": "1.8.3"
- }
- },
- "@paulcbetts/spellchecker": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/@paulcbetts/spellchecker/-/spellchecker-4.0.6.tgz",
- "integrity": "sha512-9lhLEvWfAB00n2oOM/S08sna9AuFk+b+bPk8ficpSa2X0Ll40PahMwfFS3G54nqQBIFFZgTPrhoHtCLAao0xmg==",
- "requires": {
- "nan": "2.14.0"
- }
- },
"@types/node": {
"version": "12.0.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.7.tgz",
@@ -110,18 +4119,18 @@
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
"requires": {
"delegates": "1.0.0",
- "readable-stream": "2.3.6"
+ "readable-stream": "2.3.7"
},
"dependencies": {
"readable-stream": {
- "version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
- "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
- "process-nextick-args": "2.0.0",
+ "process-nextick-args": "2.0.1",
"safe-buffer": "5.1.2",
"string_decoder": "1.1.1",
"util-deprecate": "1.0.2"
@@ -213,6 +4222,11 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
+ "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=="
+ },
"bcp47": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/bcp47/-/bcp47-1.1.2.tgz",
@@ -227,11 +4241,13 @@
}
},
"better-sqlite3": {
- "version": "github:bengotow/better-sqlite3#dcd5b6e73c9a5329fd72c85be3316131fcfb83ab",
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-7.1.2.tgz",
+ "integrity": "sha512-8FWYnJ6Bx94MBX03J5Ka7sTRlvXXMEm4FW2Op7nM8ErQZeyALYLmSlbMBnfr4cMpS0tj0aYZv0a+26G2YJuIjg==",
"requires": {
"bindings": "1.5.0",
- "nan": "2.14.0",
- "to-descriptor": "1.0.1"
+ "prebuild-install": "5.3.3",
+ "tar": "6.1.0"
}
},
"bindings": {
@@ -243,35 +4259,19 @@
}
},
"bl": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
- "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
+ "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==",
"requires": {
- "readable-stream": "2.3.6",
- "safe-buffer": "5.1.2"
+ "buffer": "5.6.0",
+ "inherits": "2.0.4",
+ "readable-stream": "3.4.0"
},
"dependencies": {
- "readable-stream": {
- "version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
- "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
- "requires": {
- "core-util-is": "1.0.2",
- "inherits": "2.0.3",
- "isarray": "1.0.0",
- "process-nextick-args": "2.0.0",
- "safe-buffer": "5.1.2",
- "string_decoder": "1.1.1",
- "util-deprecate": "1.0.2"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "requires": {
- "safe-buffer": "5.1.2"
- }
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
}
}
},
@@ -294,6 +4294,7 @@
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
"integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
+ "optional": true,
"requires": {
"hoek": "2.16.3"
}
@@ -307,25 +4308,15 @@
"concat-map": "0.0.1"
}
},
- "buffer-alloc": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
- "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
+ "buffer": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
+ "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
"requires": {
- "buffer-alloc-unsafe": "1.1.0",
- "buffer-fill": "1.0.0"
+ "base64-js": "1.3.1",
+ "ieee754": "1.1.13"
}
},
- "buffer-alloc-unsafe": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
- "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
- },
- "buffer-fill": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
- "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw="
- },
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
@@ -376,9 +4367,9 @@
}
},
"chownr": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
- "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g=="
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"chromium-net-errors": {
"version": "1.0.3",
@@ -405,6 +4396,17 @@
"resolved": "https://registry.npmjs.org/classnames/-/classnames-1.2.1.tgz",
"integrity": "sha1-xwx4j8kS4GhL2XruFn0u/WgtzfM="
},
+ "cld": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/cld/-/cld-2.6.0.tgz",
+ "integrity": "sha512-2U8Uiv7Bvl1v4fNWFGB3RYtPvhUWXQJ1MoNKJNVuoALfandEt9oVqK64S+3ZLvQPjDiYjsohtTep/wIs0xOXkw==",
+ "requires": {
+ "glob": "5.0.15",
+ "node-addon-api": "2.0.0",
+ "rimraf": "2.5.2",
+ "underscore": "1.8.3"
+ }
+ },
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -571,17 +4573,18 @@
}
},
"debug": {
- "version": "github:emorikawa/debug#29685485665b44c42d33ebd7ed62570b21c6e4bd",
+ "version": "git+ssh://git@github.com/emorikawa/debug.git#29685485665b44c42d33ebd7ed62570b21c6e4bd",
+ "from": "debug@github:emorikawa/debug#nylas",
"requires": {
"ms": "0.7.3"
}
},
"decompress-response": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
- "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
+ "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
"requires": {
- "mimic-response": "1.0.1"
+ "mimic-response": "2.1.0"
}
},
"deep-extend": {
@@ -710,34 +4713,49 @@
"semver": "5.7.0"
}
},
- "electron-remote": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/electron-remote/-/electron-remote-1.3.0.tgz",
- "integrity": "sha512-i00MD42fzlmyhsYRUDrMM104OQTT/soEmBmZ707CZ3k/nwa0rrB3a3mpxvR0EI2Q+Xw2VBdhWbk2gYmyg0PS0g==",
- "requires": {
- "debug": "github:emorikawa/debug#29685485665b44c42d33ebd7ed62570b21c6e4bd",
- "hashids": "1.2.2",
- "lodash.get": "4.4.2",
- "pify": "2.3.0",
- "rxjs": "5.5.12",
- "xmlhttprequest": "1.8.0"
- }
- },
"electron-spellchecker": {
- "version": "github:bengotow/electron-spellchecker#de319e18db19e497a6add6cc086b243d8e0461db",
+ "version": "git+ssh://git@github.com/bengotow/electron-spellchecker.git#267df085fa4c1a5f5631e5f0ebd627890d59d670",
+ "from": "electron-spellchecker@github:bengotow/electron-spellchecker#267df08",
"requires": {
- "@paulcbetts/cld": "2.4.6",
- "@paulcbetts/spellchecker": "4.0.6",
+ "@felixrieseberg/spellchecker": "4.0.12",
"bcp47": "1.1.2",
- "debug": "github:emorikawa/debug#29685485665b44c42d33ebd7ed62570b21c6e4bd",
- "electron-remote": "1.3.0",
- "keyboard-layout": "2.0.16",
- "lru-cache": "4.1.5",
+ "cld": "2.6.0",
+ "debug": "4.3.1",
+ "keyboard-layout": "2.0.17",
+ "lru-cache": "5.1.1",
"mkdirp": "0.5.1",
- "pify": "2.3.0",
+ "pify": "4.0.1",
"rxjs": "5.5.12",
"rxjs-serial-subscription": "0.1.1",
"spawn-rx": "2.0.12"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+ "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "requires": {
+ "yallist": "3.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
+ }
}
},
"emissary": {
@@ -767,9 +4785,9 @@
}
},
"end-of-stream": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
- "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"requires": {
"once": "1.4.0"
}
@@ -1071,6 +5089,14 @@
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
+ "fs-minipass": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+ "requires": {
+ "minipass": "3.1.3"
+ }
+ },
"fs-plus": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/fs-plus/-/fs-plus-2.10.1.tgz",
@@ -1111,7 +5137,7 @@
"console-control-strings": "1.1.0",
"has-unicode": "2.0.1",
"object-assign": "4.1.1",
- "signal-exit": "3.0.2",
+ "signal-exit": "3.0.3",
"string-width": "1.0.2",
"strip-ansi": "3.0.1",
"wide-align": "1.1.3"
@@ -1222,11 +5248,6 @@
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
},
- "hashids": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/hashids/-/hashids-1.2.2.tgz",
- "integrity": "sha512-dEHCG2LraR6PNvSGxosZHIRgxF5sNLOIBFEHbj8lfP9WWmu/PWPMzsip1drdVSOFi51N2pU7gZavrgn7sbGFuw=="
- },
"hawk": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
@@ -1242,7 +5263,8 @@
"hoek": {
"version": "2.16.3",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
- "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0="
+ "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
+ "optional": true
},
"html-attributes": {
"version": "1.1.0",
@@ -1293,6 +5315,11 @@
"resolved": "https://registry.npmjs.org/ical.js/-/ical.js-1.3.0.tgz",
"integrity": "sha512-wQ0w77MGOe6vNhZMBQuZAtZoTzDEOQ/QoCDofWL7yfkQ/HYWX5NyWPYjN+yj+4JahOvTkhXjTlrZtiQKv+BSOA=="
},
+ "ieee754": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+ "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
+ },
"image-size": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
@@ -1323,6 +5350,14 @@
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
},
+ "invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "requires": {
+ "loose-envify": "1.4.0"
+ }
+ },
"is-boolean-object": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.0.tgz",
@@ -1554,9 +5589,9 @@
}
},
"keyboard-layout": {
- "version": "2.0.16",
- "resolved": "https://registry.npmjs.org/keyboard-layout/-/keyboard-layout-2.0.16.tgz",
- "integrity": "sha512-eGrxmlV6jbm/mbPEOpYGuH53XEC7wIUj9ZxKcT2z9QHJ/RwrT9iVkvxka9zRxqHZHwQzcffgsa5OxoVAKnhK9w==",
+ "version": "2.0.17",
+ "resolved": "https://registry.npmjs.org/keyboard-layout/-/keyboard-layout-2.0.17.tgz",
+ "integrity": "sha512-W9LL+1e8CS9fi0s8ZHINDN1HZ6QpYjE4yLi4+faed7ozppNOAxINjv5w16zG9tJv8Jp5LJrCfO5PZ9aV1m5d4g==",
"requires": {
"event-kit": "2.5.3",
"nan": "2.14.0"
@@ -1570,19 +5605,12 @@
}
},
"keytar": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/keytar/-/keytar-4.3.0.tgz",
- "integrity": "sha512-pd++/v+fS0LQKmzWlW6R1lziTXFqhfGeS6sYLfuTIqEy2pDzAbjutbSW8f9tnJdEEMn/9XhAQlT34VAtl9h4MQ==",
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/keytar/-/keytar-5.5.0.tgz",
+ "integrity": "sha512-1d/F2qAL/qijpm25wNq8eez4mE+/J4eBvqyLfspIYIeCEHn3nttFwplowIZfbdNCjFGWh68MzGZOd2vS61Ffew==",
"requires": {
- "nan": "2.8.0",
- "prebuild-install": "5.3.0"
- },
- "dependencies": {
- "nan": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz",
- "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo="
- }
+ "nan": "2.14.0",
+ "prebuild-install": "5.3.3"
}
},
"less": {
@@ -1799,11 +5827,6 @@
"resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
"integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM="
},
- "lodash.get": {
- "version": "4.4.2",
- "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
- "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
- },
"lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
@@ -1958,9 +5981,9 @@
"integrity": "sha512-4ZJvCzfcwsBgPbkKXUzGoVZMWjv8IDIygkGzVc7uUYhgnK0t2LmGxxjdgH1i+pn0/KQfB5F/VKUJlfyTSOFQjg=="
},
"mimic-response": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
- "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
+ "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA=="
},
"minimatch": {
"version": "3.0.4",
@@ -1975,6 +5998,19 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
+ "minipass": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
+ "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg=="
+ },
+ "minizlib": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+ "requires": {
+ "minipass": "3.1.3"
+ }
+ },
"mixto": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/mixto/-/mixto-1.0.0.tgz",
@@ -1988,6 +6024,11 @@
"minimist": "0.0.8"
}
},
+ "mkdirp-classic": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz",
+ "integrity": "sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g=="
+ },
"moment": {
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
@@ -2027,9 +6068,9 @@
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
},
"napi-build-utils": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.1.tgz",
- "integrity": "sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA=="
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
+ "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
},
"nearley": {
"version": "2.16.0",
@@ -2054,13 +6095,18 @@
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
},
"node-abi": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.8.0.tgz",
- "integrity": "sha512-1/aa2clS0pue0HjckL62CsbhWWU35HARvBDXcJtYKbYR7LnIutmpxmXbuDMV9kEviD2lP/wACOgWmmwljghHyQ==",
+ "version": "2.15.0",
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.15.0.tgz",
+ "integrity": "sha512-FeLpTS0F39U7hHZU1srAK4Vx+5AHNVOTP+hxBNQknR/54laTHSFIJkDWDqiquY1LeLUgTfPN7sLPhMubx0PLAg==",
"requires": {
"semver": "5.7.0"
}
},
+ "node-addon-api": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.0.tgz",
+ "integrity": "sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA=="
+ },
"node-emoji": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz",
@@ -2199,11 +6245,6 @@
"resolved": "https://registry.npmjs.org/option/-/option-0.2.4.tgz",
"integrity": "sha1-/Udc35jcq7PLOXo7pShP60Xtv+Q="
},
- "os-homedir": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
- "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
- },
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
@@ -2248,9 +6289,9 @@
}
},
"pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="
},
"postcss": {
"version": "6.0.23",
@@ -2270,32 +6311,31 @@
}
},
"prebuild-install": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.0.tgz",
- "integrity": "sha512-aaLVANlj4HgZweKttFNUVNRxDukytuIuxeK2boIMHjagNJCiVKWFsKF4tCE3ql3GbrD2tExPQ7/pwtEJcHNZeg==",
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.3.tgz",
+ "integrity": "sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==",
"requires": {
"detect-libc": "1.0.3",
"expand-template": "2.0.3",
"github-from-package": "0.0.0",
- "minimist": "1.2.0",
+ "minimist": "1.2.5",
"mkdirp": "0.5.1",
- "napi-build-utils": "1.0.1",
- "node-abi": "2.8.0",
+ "napi-build-utils": "1.0.2",
+ "node-abi": "2.15.0",
"noop-logger": "0.1.1",
"npmlog": "4.1.2",
- "os-homedir": "1.0.2",
- "pump": "2.0.1",
+ "pump": "3.0.0",
"rc": "1.2.8",
- "simple-get": "2.8.1",
- "tar-fs": "1.16.3",
+ "simple-get": "3.1.0",
+ "tar-fs": "2.0.1",
"tunnel-agent": "0.6.0",
"which-pm-runs": "1.0.0"
},
"dependencies": {
"minimist": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
- "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
}
}
},
@@ -2305,9 +6345,9 @@
"integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ=="
},
"process-nextick-args": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
- "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"promise": {
"version": "7.3.1",
@@ -2369,11 +6409,11 @@
"integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g=="
},
"pump": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
- "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"requires": {
- "end-of-stream": "1.4.1",
+ "end-of-stream": "1.4.4",
"once": "1.4.0"
}
},
@@ -2436,14 +6476,14 @@
"requires": {
"deep-extend": "0.6.0",
"ini": "1.3.5",
- "minimist": "1.2.0",
+ "minimist": "1.2.5",
"strip-json-comments": "2.0.1"
},
"dependencies": {
"minimist": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
- "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
}
}
},
@@ -2483,9 +6523,12 @@
}
},
"react-immutable-proptypes": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/react-immutable-proptypes/-/react-immutable-proptypes-2.1.0.tgz",
- "integrity": "sha1-Aj1vObsVyXwHHp5g0A0TbqxfoLQ="
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/react-immutable-proptypes/-/react-immutable-proptypes-2.2.0.tgz",
+ "integrity": "sha512-Vf4gBsePlwdGvSZoLSBfd4HAP93HDauMY4fDjXhreg/vg6F3Fj/MXDNyTbltPC/xZKmZc+cjLu3598DdYK6sgQ==",
+ "requires": {
+ "invariant": "2.2.4"
+ }
},
"react-is": {
"version": "16.8.6",
@@ -2627,47 +6670,6 @@
"strip-json-comments": "2.0.1"
}
},
- "runas": {
- "version": "github:getflywheel/node-runas#ca4f0714a926319f53df4851b007f2e4fd5381e5",
- "requires": {
- "nan": "2.14.0",
- "prebuild-install": "2.5.3"
- },
- "dependencies": {
- "expand-template": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.1.tgz",
- "integrity": "sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg=="
- },
- "minimist": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
- "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
- },
- "prebuild-install": {
- "version": "2.5.3",
- "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.5.3.tgz",
- "integrity": "sha512-/rI36cN2g7vDQnKWN8Uzupi++KjyqS9iS+/fpwG4Ea8d0Pip0PQ5bshUNzVwt+/D2MRfhVAplYMMvWLqWrCF/g==",
- "requires": {
- "detect-libc": "1.0.3",
- "expand-template": "1.1.1",
- "github-from-package": "0.0.0",
- "minimist": "1.2.5",
- "mkdirp": "0.5.1",
- "node-abi": "2.8.0",
- "noop-logger": "0.1.1",
- "npmlog": "4.1.2",
- "os-homedir": "1.0.2",
- "pump": "2.0.1",
- "rc": "1.2.8",
- "simple-get": "2.8.1",
- "tar-fs": "1.16.3",
- "tunnel-agent": "0.6.0",
- "which-pm-runs": "1.0.0"
- }
- }
- }
- },
"rx-lite": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
@@ -2742,9 +6744,9 @@
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
},
"signal-exit": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
- "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
+ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
},
"simple-concat": {
"version": "1.0.0",
@@ -2752,40 +6754,41 @@
"integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
},
"simple-get": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz",
- "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
+ "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
"requires": {
- "decompress-response": "3.3.0",
+ "decompress-response": "4.2.1",
"once": "1.4.0",
"simple-concat": "1.0.0"
}
},
"slate": {
- "version": "github:bengotow/slate#cd6f40e8db9058f518c091cffe364608b3c858e6",
+ "version": "git+ssh://git@github.com/bengotow/slate.git#cd6f40e8db9058f518c091cffe364608b3c858e6",
+ "from": "slate@github:bengotow/slate#cd6f40e8",
"requires": {
- "debug": "3.2.6",
+ "debug": "3.2.7",
"direction": "0.1.5",
"esrever": "0.2.0",
"is-plain-object": "2.0.4",
"lodash": "4.17.11",
- "tiny-invariant": "1.0.4",
+ "tiny-invariant": "1.1.0",
"tiny-warning": "0.0.3",
"type-of": "2.0.1"
},
"dependencies": {
"debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"requires": {
- "ms": "2.1.2"
+ "ms": "2.1.3"
}
},
"ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
}
}
},
@@ -2849,15 +6852,16 @@
"integrity": "sha512-zm3gVmnGn0a9l/75REkAC0zYQGl3U/j2Z4p4pZ4rLupCptLM8iLWeGdn07V3JVCUA6n/80y9Mtv43Z9b1G/BAA=="
},
"slate-react": {
- "version": "github:bengotow/slate#4eb9d59ec29bca6d7edf8e61283249cf81034d65",
+ "version": "git+ssh://git@github.com/bengotow/slate.git#4eb9d59ec29bca6d7edf8e61283249cf81034d65",
+ "from": "slate-react@github:bengotow/slate#0.45.1-react",
"requires": {
- "debug": "3.2.6",
+ "debug": "3.2.7",
"get-window": "1.1.2",
"is-window": "1.0.2",
"lodash": "4.17.11",
"memoize-one": "4.1.0",
"prop-types": "15.7.2",
- "react-immutable-proptypes": "2.1.0",
+ "react-immutable-proptypes": "2.2.0",
"selection-is-backward": "1.0.0",
"slate-base64-serializer": "0.2.100",
"slate-dev-environment": "0.2.2",
@@ -2865,22 +6869,22 @@
"slate-plain-serializer": "0.6.39",
"slate-prop-types": "0.5.30",
"slate-react-placeholder": "0.1.20",
- "tiny-invariant": "1.0.4",
+ "tiny-invariant": "1.1.0",
"tiny-warning": "0.0.3"
},
"dependencies": {
"debug": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
- "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"requires": {
- "ms": "2.1.2"
+ "ms": "2.1.3"
}
},
"ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
}
}
},
@@ -3051,64 +7055,37 @@
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz",
"integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ="
},
- "tar-fs": {
- "version": "1.16.3",
- "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz",
- "integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==",
+ "tar": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz",
+ "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==",
"requires": {
- "chownr": "1.1.1",
- "mkdirp": "0.5.1",
- "pump": "1.0.3",
- "tar-stream": "1.6.2"
- },
- "dependencies": {
- "pump": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz",
- "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==",
- "requires": {
- "end-of-stream": "1.4.1",
- "once": "1.4.0"
- }
- }
+ "fs-minipass": "2.1.0",
+ "minipass": "3.1.3",
+ "minizlib": "2.1.2"
+ }
+ },
+ "tar-fs": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
+ "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==",
+ "requires": {
+ "chownr": "1.1.4",
+ "mkdirp-classic": "0.5.2",
+ "pump": "3.0.0",
+ "tar-stream": "2.1.2"
}
},
"tar-stream": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
- "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==",
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz",
+ "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==",
"requires": {
- "bl": "1.2.2",
- "buffer-alloc": "1.2.0",
- "end-of-stream": "1.4.1",
+ "bl": "4.0.2",
+ "end-of-stream": "1.4.4",
"fs-constants": "1.0.0",
- "readable-stream": "2.3.6",
- "to-buffer": "1.1.1",
- "xtend": "4.0.1"
- },
- "dependencies": {
- "readable-stream": {
- "version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
- "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
- "requires": {
- "core-util-is": "1.0.2",
- "inherits": "2.0.3",
- "isarray": "1.0.0",
- "process-nextick-args": "2.0.0",
- "safe-buffer": "5.1.2",
- "string_decoder": "1.1.1",
- "util-deprecate": "1.0.2"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "requires": {
- "safe-buffer": "5.1.2"
- }
- }
+ "inherits": "2.0.3",
+ "readable-stream": "3.4.0"
}
},
"temp": {
@@ -3133,9 +7110,9 @@
"integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
},
"tiny-invariant": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.4.tgz",
- "integrity": "sha512-lMhRd/djQJ3MoaHEBrw8e2/uM4rs9YMNk0iOr8rHQ0QdbM7D4l0gFl3szKdeixrlyfm9Zqi4dxHCM2qVG8ND5g=="
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",
+ "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw=="
},
"tiny-warning": {
"version": "0.0.3",
@@ -3152,16 +7129,6 @@
"resolved": "https://registry.npmjs.org/tld/-/tld-0.0.2.tgz",
"integrity": "sha1-9g442CzaB14NzHlgHOXWgpzASd4="
},
- "to-buffer": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz",
- "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg=="
- },
- "to-descriptor": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/to-descriptor/-/to-descriptor-1.0.1.tgz",
- "integrity": "sha1-oOZ4w068fS2uRk2DcrwhR52cK80="
- },
"tough-cookie": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
@@ -3393,7 +7360,8 @@
}
},
"windows-shortcuts": {
- "version": "github:emorikawa/windows-shortcuts#b0a0fc7fb86fb03e06ddceb9cbd6c9c5c29e571e"
+ "version": "git+ssh://git@github.com/emorikawa/windows-shortcuts.git#b0a0fc7fb86fb03e06ddceb9cbd6c9c5c29e571e",
+ "from": "windows-shortcuts@emorikawa/windows-shortcuts#b0a0fc7"
},
"wordwrap": {
"version": "0.0.2",
@@ -3431,11 +7399,6 @@
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.1.tgz",
"integrity": "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg=="
},
- "xmlhttprequest": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
- "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw="
- },
"xtend": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
diff --git a/app/package.json b/app/package.json
index a7141883d..50492881a 100644
--- a/app/package.json
+++ b/app/package.json
@@ -12,15 +12,15 @@
"main": "./src/browser/main.js",
"dependencies": {
"@bengotow/slate-edit-list": "github:bengotow/slate-edit-list#b868e108",
- "@paulcbetts/cld": "2.4.6",
- "better-sqlite3": "bengotow/better-sqlite3#dcd5b6e73c9a5329fd72c85be3316131fcfb83ab",
+ "better-sqlite3": "^7.1.2",
"chromium-net-errors": "1.0.3",
"chrono-node": "^1.1.2",
"classnames": "1.2.1",
+ "cld": "2.6.0",
"collapse-whitespace": "^1.1.6",
"debug": "github:emorikawa/debug#nylas",
"deep-extend": "0.6.0",
- "electron-spellchecker": "github:bengotow/electron-spellchecker#de319e18db19e497a6add6cc086b243d8e0461db",
+ "electron-spellchecker": "github:bengotow/electron-spellchecker#267df08",
"emoji-data": "^0.2.0",
"enzyme": "^3.8.0",
"enzyme-adapter-react-16": "^1.9.0",
@@ -36,7 +36,7 @@
"jasmine-react-helpers": "^0.2",
"jasmine-reporters": "1.x.x",
"juice": "^5.2.0",
- "keytar": "4.3.0",
+ "keytar": "5.5.0",
"less-cache": "1.1.0",
"lru-cache": "^4.0.1",
"mammoth": "1.4.7",
@@ -58,7 +58,6 @@
"reflux": "0.1.13",
"rimraf": "2.5.2",
"rtlcss": "2.4.0",
- "runas": "getflywheel/node-runas#ca4f0714",
"rx-lite": "4.0.8",
"slate": "github:bengotow/slate#cd6f40e8",
"slate-auto-replace": "0.12.1",
diff --git a/app/spec/database-object-registry-spec.ts b/app/spec/database-object-registry-spec.ts
index 5ef1d6cac..e4f0bb962 100644
--- a/app/spec/database-object-registry-spec.ts
+++ b/app/spec/database-object-registry-spec.ts
@@ -1,7 +1,7 @@
/* eslint quote-props: 0 */
import _ from 'underscore';
import { Model } from '../src/flux/models/model';
-import Attributes from '../src/flux/attributes';
+import * as Attributes from '../src/flux/attributes';
import DatabaseObjectRegistry from '../src/registries/database-object-registry';
class GoodTest extends Model {
diff --git a/app/spec/fixtures/db-test-model.ts b/app/spec/fixtures/db-test-model.ts
index 3fbd8e06b..46a3196e1 100644
--- a/app/spec/fixtures/db-test-model.ts
+++ b/app/spec/fixtures/db-test-model.ts
@@ -5,14 +5,11 @@
*/
import { Model } from '../../src/flux/models/model';
import { Category } from '../../src/flux/models/category';
-import Attributes from '../../src/flux/attributes';
+import * as Attributes from '../../src/flux/attributes';
class TestModel extends Model {
static attributes = {
- id: Attributes.String({
- queryable: true,
- modelKey: 'id',
- }),
+ ...Model.attributes,
clientId: Attributes.String({
queryable: true,
diff --git a/app/spec/models/model-spec.ts b/app/spec/models/model-spec.ts
index bf5d5d350..1af9e0638 100644
--- a/app/spec/models/model-spec.ts
+++ b/app/spec/models/model-spec.ts
@@ -1,6 +1,6 @@
/* eslint quote-props: 0 */
import { Model } from '../../src/flux/models/model';
-import Attributes from '../../src/flux/attributes';
+import * as Attributes from '../../src/flux/attributes';
describe('Model', function modelSpecs() {
describe('constructor', () => {
diff --git a/app/spec/models/query-spec.ts b/app/spec/models/query-spec.ts
index a321c5c8b..98646d44a 100644
--- a/app/spec/models/query-spec.ts
+++ b/app/spec/models/query-spec.ts
@@ -1,6 +1,6 @@
/* eslint quote-props: 0 */
import ModelQuery from '../../src/flux/models/query';
-import Attributes from '../../src/flux/attributes';
+import * as Attributes from '../../src/flux/attributes';
import { Message } from '../../src/flux/models/message';
import { Thread } from '../../src/flux/models/thread';
import { Account } from '../../src/flux/models/account';
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/both-email-and-url-in.html b/app/spec/services/autolinker-fixtures/both-email-and-url-in.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/both-email-and-url-in.html
rename to app/spec/services/autolinker-fixtures/both-email-and-url-in.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/both-email-and-url-out.html b/app/spec/services/autolinker-fixtures/both-email-and-url-out.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/both-email-and-url-out.html
rename to app/spec/services/autolinker-fixtures/both-email-and-url-out.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/gmail-in.html b/app/spec/services/autolinker-fixtures/gmail-in.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/gmail-in.html
rename to app/spec/services/autolinker-fixtures/gmail-in.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/gmail-out.html b/app/spec/services/autolinker-fixtures/gmail-out.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/gmail-out.html
rename to app/spec/services/autolinker-fixtures/gmail-out.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/linkedin-in.html b/app/spec/services/autolinker-fixtures/linkedin-in.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/linkedin-in.html
rename to app/spec/services/autolinker-fixtures/linkedin-in.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/linkedin-out.html b/app/spec/services/autolinker-fixtures/linkedin-out.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/linkedin-out.html
rename to app/spec/services/autolinker-fixtures/linkedin-out.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/medium-post-in.html b/app/spec/services/autolinker-fixtures/medium-post-in.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/medium-post-in.html
rename to app/spec/services/autolinker-fixtures/medium-post-in.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/medium-post-out.html b/app/spec/services/autolinker-fixtures/medium-post-out.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/medium-post-out.html
rename to app/spec/services/autolinker-fixtures/medium-post-out.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/nylas-url-in.html b/app/spec/services/autolinker-fixtures/nylas-url-in.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/nylas-url-in.html
rename to app/spec/services/autolinker-fixtures/nylas-url-in.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/nylas-url-out.html b/app/spec/services/autolinker-fixtures/nylas-url-out.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/nylas-url-out.html
rename to app/spec/services/autolinker-fixtures/nylas-url-out.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/plaintext-in.html b/app/spec/services/autolinker-fixtures/plaintext-in.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/plaintext-in.html
rename to app/spec/services/autolinker-fixtures/plaintext-in.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/plaintext-out.html b/app/spec/services/autolinker-fixtures/plaintext-out.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/plaintext-out.html
rename to app/spec/services/autolinker-fixtures/plaintext-out.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/readme-in.html b/app/spec/services/autolinker-fixtures/readme-in.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/readme-in.html
rename to app/spec/services/autolinker-fixtures/readme-in.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/readme-out.html b/app/spec/services/autolinker-fixtures/readme-out.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/readme-out.html
rename to app/spec/services/autolinker-fixtures/readme-out.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/strangeemails-in.html b/app/spec/services/autolinker-fixtures/strangeemails-in.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/strangeemails-in.html
rename to app/spec/services/autolinker-fixtures/strangeemails-in.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/strangeemails-out.html b/app/spec/services/autolinker-fixtures/strangeemails-out.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/strangeemails-out.html
rename to app/spec/services/autolinker-fixtures/strangeemails-out.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/strangephones-in.html b/app/spec/services/autolinker-fixtures/strangephones-in.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/strangephones-in.html
rename to app/spec/services/autolinker-fixtures/strangephones-in.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/strangephones-out.html b/app/spec/services/autolinker-fixtures/strangephones-out.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/strangephones-out.html
rename to app/spec/services/autolinker-fixtures/strangephones-out.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/twitter-in.html b/app/spec/services/autolinker-fixtures/twitter-in.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/twitter-in.html
rename to app/spec/services/autolinker-fixtures/twitter-in.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/twitter-out.html b/app/spec/services/autolinker-fixtures/twitter-out.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/twitter-out.html
rename to app/spec/services/autolinker-fixtures/twitter-out.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/url-with-port-in.html b/app/spec/services/autolinker-fixtures/url-with-port-in.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/url-with-port-in.html
rename to app/spec/services/autolinker-fixtures/url-with-port-in.html
diff --git a/app/internal_packages/message-list/specs/autolinker-fixtures/url-with-port-out.html b/app/spec/services/autolinker-fixtures/url-with-port-out.html
similarity index 100%
rename from app/internal_packages/message-list/specs/autolinker-fixtures/url-with-port-out.html
rename to app/spec/services/autolinker-fixtures/url-with-port-out.html
diff --git a/app/internal_packages/message-list/specs/autolinker-spec.ts b/app/spec/services/autolinker-spec.ts
similarity index 86%
rename from app/internal_packages/message-list/specs/autolinker-spec.ts
rename to app/spec/services/autolinker-spec.ts
index 2c4d2d714..0afb72366 100644
--- a/app/internal_packages/message-list/specs/autolinker-spec.ts
+++ b/app/spec/services/autolinker-spec.ts
@@ -1,11 +1,10 @@
import fs from 'fs';
import path from 'path';
-import { autolink } from '../lib/autolinker';
+import { Autolink } from '../../src/services/autolinker';
describe('autolink', function autolinkSpec() {
const fixturesDir = path.join(__dirname, 'autolinker-fixtures');
- fs
- .readdirSync(fixturesDir)
+ fs.readdirSync(fixturesDir)
.filter(filename => filename.indexOf('-in.html') !== -1)
.forEach(filename => {
it(`should properly autolink a variety of email bodies ${filename}`, () => {
@@ -17,7 +16,7 @@ describe('autolink', function autolinkSpec() {
const expected = fs.readFileSync(expectedPath).toString();
div.innerHTML = input;
- autolink({ body: div });
+ Autolink(div);
expect(div.innerHTML).toEqual(expected);
});
diff --git a/app/src/app-env.ts b/app/src/app-env.ts
index f553052f8..0b387e60b 100644
--- a/app/src/app-env.ts
+++ b/app/src/app-env.ts
@@ -39,7 +39,7 @@ export default class AppEnvConstructor {
windowEventHandler: import('./window-event-handler').default;
actionBridge: import('./flux/action-bridge').default;
mailsyncBridge: import('./flux/mailsync-bridge').default;
- errorLogger: import('./error-logger');
+ errorLogger: any;
savedState: any;
isReloading: boolean;
@@ -670,8 +670,8 @@ export default class AppEnvConstructor {
);
const browserWindow = this.getCurrentWindow();
- if (browserWindow.isResizable() !== loadSettings.resizable) {
- browserWindow.setResizable(loadSettings.resizable);
+ if (browserWindow.resizable !== loadSettings.resizable) {
+ browserWindow.resizable = loadSettings.resizable;
}
if (!loadSettings.hidden) {
@@ -772,18 +772,20 @@ export default class AppEnvConstructor {
remote.process.exit(status);
}
- showOpenDialog(options, callback) {
- return callback(remote.dialog.showOpenDialog(this.getCurrentWindow(), options));
+ async showOpenDialog(options: Electron.OpenDialogOptions, callback: (paths: string[]) => void) {
+ const result = await remote.dialog.showOpenDialog(this.getCurrentWindow(), options);
+ callback(result.filePaths);
}
- showSaveDialog(options, callback) {
+ async showSaveDialog(options: Electron.SaveDialogOptions, callback: (path: string) => void) {
if (options.title == null) {
options.title = 'Save File';
}
- return callback(remote.dialog.showSaveDialog(this.getCurrentWindow(), options));
+ const result = await remote.dialog.showSaveDialog(this.getCurrentWindow(), options);
+ callback(result.filePath);
}
- showErrorDialog(
+ async showErrorDialog(
messageData,
{ showInMainWindow, detail }: { showInMainWindow?: boolean; detail?: string } = {}
) {
@@ -805,33 +807,29 @@ export default class AppEnvConstructor {
}
if (!detail) {
- return remote.dialog.showMessageBox(winToShow, {
+ return remote.dialog.showMessageBoxSync(winToShow, {
type: 'warning',
buttons: [localized('Okay')],
message: title,
detail: message,
});
}
- return remote.dialog.showMessageBox(
- winToShow,
- {
- type: 'warning',
- buttons: [localized('Okay'), localized('Show Detail')],
- message: title,
- detail: message,
- },
- buttonIndex => {
- if (buttonIndex === 1) {
- const { Actions } = require('mailspring-exports');
- const { CodeSnippet } = require('mailspring-component-kit');
- Actions.openModal({
- component: CodeSnippet({ intro: message, code: detail, className: 'error-details' }),
- width: 500,
- height: 300,
- });
- }
- }
- );
+
+ const result = remote.dialog.showMessageBoxSync(winToShow, {
+ type: 'warning',
+ buttons: [localized('Okay'), localized('Show Detail')],
+ message: title,
+ detail: message,
+ });
+ if (result === 1) {
+ const { Actions } = require('mailspring-exports');
+ const { CodeSnippet } = require('mailspring-component-kit');
+ Actions.openModal({
+ component: CodeSnippet({ intro: message, code: detail, className: 'error-details' }),
+ width: 500,
+ height: 300,
+ });
+ }
}
// Delegate to the browser's process fileListCache
diff --git a/app/src/browser/application-touch-bar.ts b/app/src/browser/application-touch-bar.ts
index 8357c8ff0..17885e6f2 100644
--- a/app/src/browser/application-touch-bar.ts
+++ b/app/src/browser/application-touch-bar.ts
@@ -118,7 +118,9 @@ export default class ApplicationTouchBar {
// Take a single linear pass through the touch bar items and place
// consecutive items with the same `group` key into touchbar groups.
// This results in /very slightly/ tighter spacing in the touchbar.
- const final: Electron.TouchBarGroup[] = [];
+ const final: Array<
+ Electron.TouchBarButton | Electron.TouchBarSpacer | Electron.TouchBarGroup
+ > = [];
let group: Array = [];
let groupName = null;
diff --git a/app/src/browser/application.ts b/app/src/browser/application.ts
index a89cf0da4..0fff2ffa3 100644
--- a/app/src/browser/application.ts
+++ b/app/src/browser/application.ts
@@ -90,7 +90,7 @@ export default class Application extends EventEmitter {
buttons = [localized('Quit'), localized('Rebuild')];
}
- const buttonIndex = dialog.showMessageBox({ type: 'warning', buttons, message });
+ const buttonIndex = dialog.showMessageBoxSync({ type: 'warning', buttons, message });
if (buttonIndex === 0) {
app.quit();
@@ -285,7 +285,7 @@ export default class Application extends EventEmitter {
this._resettingAndRelaunching = true;
if (errorMessage) {
- dialog.showMessageBox({
+ dialog.showMessageBoxSync({
type: 'warning',
buttons: [localized('Okay')],
message: localized(
@@ -324,26 +324,23 @@ export default class Application extends EventEmitter {
});
});
- this.on('application:run-package-specs', () => {
- dialog.showOpenDialog(
- {
- title: localized('Choose Directory'),
- defaultPath: this.configDirPath,
- buttonLabel: localized('Choose'),
- properties: ['openDirectory'],
- },
- filenames => {
- if (!filenames || filenames.length === 0) {
- return;
- }
- this.runSpecs({
- exitWhenDone: false,
- showSpecsInWindow: true,
- resourcePath: this.resourcePath,
- specDirectory: filenames[0],
- });
- }
- );
+ this.on('application:run-package-specs', async () => {
+ const { filePaths } = await dialog.showOpenDialog({
+ title: localized('Choose Directory'),
+ defaultPath: this.configDirPath,
+ buttonLabel: localized('Choose'),
+ properties: ['openDirectory'],
+ });
+
+ if (!filePaths || filePaths.length === 0) {
+ return;
+ }
+ this.runSpecs({
+ exitWhenDone: false,
+ showSpecsInWindow: true,
+ resourcePath: this.resourcePath,
+ specDirectory: filePaths[0],
+ });
});
this.on('application:reset-database', this._resetDatabaseAndRelaunch);
@@ -550,7 +547,7 @@ export default class Application extends EventEmitter {
ipcMain.on('encountered-theme-error', (event, { message, detail }) => {
if (userResetTheme) return;
- const buttonIndex = dialog.showMessageBox({
+ const buttonIndex = dialog.showMessageBoxSync({
type: 'warning',
buttons: [localized('Reset Theme'), localized('Continue')],
defaultId: 0,
diff --git a/app/src/browser/autoupdate-manager.ts b/app/src/browser/autoupdate-manager.ts
index ad8ae6a94..acd6ed233 100644
--- a/app/src/browser/autoupdate-manager.ts
+++ b/app/src/browser/autoupdate-manager.ts
@@ -54,9 +54,7 @@ export default class AutoUpdateManager extends EventEmitter {
host = `updates-staging.getmailspring.com`;
}
- this.feedURL = `https://${host}/check/${params.platform}/${params.arch}/${params.version}/${
- params.id
- }/${params.channel}`;
+ this.feedURL = `https://${host}/check/${params.platform}/${params.arch}/${params.version}/${params.id}/${params.channel}`;
if (autoUpdater) {
autoUpdater.setFeedURL(this.feedURL);
}
@@ -148,7 +146,7 @@ export default class AutoUpdateManager extends EventEmitter {
};
}
- check({ hidePopups }: {hidePopups?: boolean} = {}) {
+ check({ hidePopups }: { hidePopups?: boolean } = {}) {
this.updateFeedURL();
if (!hidePopups) {
autoUpdater.once('update-not-available', this.onUpdateNotAvailable);
@@ -162,7 +160,12 @@ export default class AutoUpdateManager extends EventEmitter {
}
dialogIcon() {
- const iconPath = path.join(global.application.resourcePath, 'static', 'images', 'mailspring.png');
+ const iconPath = path.join(
+ global.application.resourcePath,
+ 'static',
+ 'images',
+ 'mailspring.png'
+ );
if (!fs.existsSync(iconPath)) return undefined;
return nativeImage.createFromPath(iconPath);
}
diff --git a/app/src/browser/config-persistence-manager.ts b/app/src/browser/config-persistence-manager.ts
index ef8f660e9..72a870e1a 100644
--- a/app/src/browser/config-persistence-manager.ts
+++ b/app/src/browser/config-persistence-manager.ts
@@ -61,16 +61,12 @@ export default class ConfigPersistenceManager {
let detail = error.message;
if (error instanceof SyntaxError) {
- detail += `\n\nThe file ${
- this.configFilePath
- } has incorrect JSON formatting or is empty. Fix the formatting to resolve this error, or reset your settings to continue using N1.`;
+ detail += `\n\nThe file ${this.configFilePath} has incorrect JSON formatting or is empty. Fix the formatting to resolve this error, or reset your settings to continue using N1.`;
} else {
- detail += `\n\nWe were unable to read the file ${
- this.configFilePath
- }. Make sure you have permissions to access this file, and check that the file is not open or being edited and try again.`;
+ detail += `\n\nWe were unable to read the file ${this.configFilePath}. Make sure you have permissions to access this file, and check that the file is not open or being edited and try again.`;
}
- const clickedIndex = dialog.showMessageBox({
+ const clickedIndex = dialog.showMessageBoxSync({
type: 'error',
message,
detail,
@@ -127,12 +123,10 @@ export default class ConfigPersistenceManager {
}
_showSaveErrorDialog() {
- const clickedIndex = dialog.showMessageBox({
+ const clickedIndex = dialog.showMessageBoxSync({
type: 'error',
message: localized(`Failed to save "%@"`, path.basename(this.configFilePath)),
- detail: `\n\nWe were unable to save the file ${
- this.configFilePath
- }. Make sure you have permissions to access this file, and check that the file is not open or being edited and try again.`,
+ detail: `\n\nWe were unable to save the file ${this.configFilePath}. Make sure you have permissions to access this file, and check that the file is not open or being edited and try again.`,
buttons: [localized('Okay'), localized('Try Again')],
});
return ['ignore', 'retry'][clickedIndex];
diff --git a/app/src/browser/mailspring-window.ts b/app/src/browser/mailspring-window.ts
index 044148a4c..79bbbf906 100644
--- a/app/src/browser/mailspring-window.ts
+++ b/app/src/browser/mailspring-window.ts
@@ -99,6 +99,7 @@ export default class MailspringWindow extends EventEmitter {
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
+ webviewTag: true,
},
autoHideMenuBar,
};
@@ -286,7 +287,7 @@ export default class MailspringWindow extends EventEmitter {
return;
}
- const chosen = dialog.showMessageBox(this.browserWindow, {
+ const chosen = dialog.showMessageBoxSync(this.browserWindow, {
type: 'warning',
buttons: ['Close', 'Keep Waiting'],
message: 'Mailspring is not responding',
@@ -312,7 +313,7 @@ export default class MailspringWindow extends EventEmitter {
if (this.neverClose) {
this.browserWindow.reload();
} else {
- const chosen = dialog.showMessageBox({
+ const chosen = dialog.showMessageBoxSync({
type: 'warning',
buttons: ['Close Window', 'Reload', 'Keep It Open'],
message: 'Mailspring has crashed',
diff --git a/app/src/browser/main.js b/app/src/browser/main.js
index 3847a459b..ca2b1f611 100644
--- a/app/src/browser/main.js
+++ b/app/src/browser/main.js
@@ -278,6 +278,23 @@ const start = () => {
app.removeListener('open-file', onOpenFileBeforeReady);
app.removeListener('open-url', onOpenUrlBeforeReady);
+ // Block remote JS execution in a second way in case our tag approach
+ // is compromised somehow https://www.electronjs.org/docs/tutorial/security
+ // This CSP string should match the one in app/static/index.html
+ require('electron').session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
+ if (details.url.startsWith('devtools://')) {
+ return callback(details);
+ }
+ callback({
+ responseHeaders: {
+ ...details.responseHeaders,
+ 'Content-Security-Policy': [
+ "default-src * mailspring:; script-src 'self' 'unsafe-inline' chrome-extension://react-developer-tools; style-src * 'unsafe-inline' mailspring:; img-src * data: mailspring: file:;",
+ ],
+ },
+ });
+ });
+
// eslint-disable-next-line
const Application = require(path.join(options.resourcePath, 'src', 'browser', 'application'))
.default;
diff --git a/app/src/browser/move-to-applications.ts b/app/src/browser/move-to-applications.ts
index 2a14d4ff9..0246f67de 100644
--- a/app/src/browser/move-to-applications.ts
+++ b/app/src/browser/move-to-applications.ts
@@ -7,7 +7,7 @@ export default function moveToApplications() {
}
// prompt the user and ask if they'd like to move the application
- const idx = dialog.showMessageBox({
+ const idx = dialog.showMessageBoxSync({
type: 'question',
buttons: [localized('Move to Applications'), localized('Not Now')],
defaultId: 0,
diff --git a/app/src/browser/system-tray-manager.ts b/app/src/browser/system-tray-manager.ts
index 683f8770c..827fccad1 100644
--- a/app/src/browser/system-tray-manager.ts
+++ b/app/src/browser/system-tray-manager.ts
@@ -41,7 +41,7 @@ function _getIcon(iconPath, isTemplateImg = false) {
}
const icon = nativeImage.createFromPath(iconPath);
if (isTemplateImg) {
- icon.setTemplateImage(true);
+ icon.isMacTemplateImage = true;
}
return icon;
}
diff --git a/app/src/browser/types/global-ext.d.ts b/app/src/browser/types/global-ext.d.ts
index f1b96a79c..506b58329 100644
--- a/app/src/browser/types/global-ext.d.ts
+++ b/app/src/browser/types/global-ext.d.ts
@@ -1,11 +1,10 @@
-
export {};
declare global {
module NodeJS {
interface Global {
jasmine: any;
shellStartTime: number;
- errorLogger: import('../../error-logger');
+ errorLogger: any;
application: import('../application').default;
}
}
@@ -25,4 +24,4 @@ declare global {
group: string;
}
}
-}
\ No newline at end of file
+}
diff --git a/app/src/components/composer-editor/base-block-plugins.tsx b/app/src/components/composer-editor/base-block-plugins.tsx
index 19c0bac23..c59b5a581 100644
--- a/app/src/components/composer-editor/base-block-plugins.tsx
+++ b/app/src/components/composer-editor/base-block-plugins.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { Editor, Value, Node } from 'slate';
+import { Editor, Value, Node, Block } from 'slate';
import SoftBreak from 'slate-soft-break';
import EditList from '@bengotow/slate-edit-list';
import AutoReplace from 'slate-auto-replace';
@@ -370,7 +370,7 @@ const MailspringBaseBlockPlugin: ComposerEditorPlugin = {
.filter(config => config.button)
.map(BuildToggleButton),
renderNode,
- commands: {
+ appCommands: {
'core:select-all': (event, editor: Editor) => {
// If the document contains void blocks the browser's natural solution is to set
// the selection to a DOM fragment range not to a contenteditable text range
@@ -420,8 +420,8 @@ const plugins: ComposerEditorPlugin[] = [
// Pressing backspace when you're at the top of the document should not delete down
{
- onKeyDown: function onKeyDown(event, editor: Editor, next: () => void) {
- if (event.key !== 'Backspace' || event.shiftKey || event.metaKey || event.optionKey) {
+ onKeyDown: function onKeyDown(event: React.KeyboardEvent, editor: Editor, next: () => void) {
+ if (event.key !== 'Backspace' || event.shiftKey || event.metaKey || event.altKey) {
return next();
}
const { selection, focusText, document } = editor.value;
@@ -444,7 +444,7 @@ const plugins: ComposerEditorPlugin[] = [
// Return breaks you out of blockquotes completely
{
- onKeyDown: function onKeyDown(event, editor: Editor, next: () => void) {
+ onKeyDown: function onKeyDown(event: React.KeyboardEvent, editor: Editor, next: () => void) {
if (event.shiftKey) {
return next();
}
diff --git a/app/src/components/composer-editor/base-mark-plugins.tsx b/app/src/components/composer-editor/base-mark-plugins.tsx
index bba02612f..5a8483b2c 100644
--- a/app/src/components/composer-editor/base-mark-plugins.tsx
+++ b/app/src/components/composer-editor/base-mark-plugins.tsx
@@ -298,7 +298,7 @@ const BaseMarkPlugin: ComposerEditorPlugin = {
let size = 2;
if (provided.endsWith('px')) {
// 16px = 12pt
- size = PT_TO_SIZE[Math.round(Number(provided.replace('px', '')) / 1 * 0.75)];
+ size = PT_TO_SIZE[Math.round((Number(provided.replace('px', '')) / 1) * 0.75)];
}
if (provided.endsWith('em')) {
// 1em = 12pt
@@ -315,7 +315,7 @@ const BaseMarkPlugin: ComposerEditorPlugin = {
}),
]),
renderMark,
- commands: {
+ appCommands: {
'contenteditable:bold': (event, editor) => editor.toggleMark(MARK_CONFIG.bold.type),
'contenteditable:underline': (event, editor) => editor.toggleMark(MARK_CONFIG.underline.type),
'contenteditable:italic': (event, editor) => editor.toggleMark(MARK_CONFIG.italic.type),
diff --git a/app/src/components/composer-editor/composer-editor.tsx b/app/src/components/composer-editor/composer-editor.tsx
index 7ca11311f..af923a80d 100644
--- a/app/src/components/composer-editor/composer-editor.tsx
+++ b/app/src/components/composer-editor/composer-editor.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import * as Immutable from 'immutable';
import { Editor, Value, Operation, Range } from 'slate';
-import { Editor as SlateEditorComponent, EditorProps } from 'slate-react';
+import { Editor as SlateEditorComponent, EditorProps, Plugin } from 'slate-react';
import { clipboard as ElectronClipboard } from 'electron';
import { InlineStyleTransformer } from 'mailspring-exports';
import path from 'path';
@@ -44,8 +44,8 @@ export class ComposerEditor extends React.Component {
// every render.
this._pluginKeyHandlers = {};
plugins.forEach(plugin => {
- if (!plugin.commands) return;
- Object.entries(plugin.commands).forEach(
+ if (!plugin.appCommands) return;
+ Object.entries(plugin.appCommands).forEach(
([command, handler]: [string, (event: any, val: any) => any]) => {
this._pluginKeyHandlers[command] = event => {
if (!this._mounted) return;
@@ -262,7 +262,7 @@ export class ComposerEditor extends React.Component {
onCopy={this.onCopy}
onPaste={this.onPaste}
spellCheck={false}
- plugins={plugins}
+ plugins={(plugins as any) as Plugin[]}
propsForPlugins={propsForPlugins}
/>
diff --git a/app/src/components/composer-editor/conversion.tsx b/app/src/components/composer-editor/conversion.tsx
index 4256b40a3..b6deffb2e 100644
--- a/app/src/components/composer-editor/conversion.tsx
+++ b/app/src/components/composer-editor/conversion.tsx
@@ -253,13 +253,14 @@ on the result. */
});
} else {
if (node.nodes && node.nodes instanceof Array) {
+ // @ts-ignore
node.nodes = node.nodes.map(applyMark);
}
}
return node;
};
-
+ // @ts-ignore
return mark.nodes.reduce(function(nodes, node) {
const ret = applyMark(node);
if (Array.isArray(ret)) return nodes.concat(ret);
@@ -439,11 +440,13 @@ export function convertToPlainText(value: Value) {
text = text.replace(/\n\n\n+/g, '\n\n').trim();
return text;
}
- if (isQuoteNode(node)) {
+ if (isQuoteNode(node) && node.object === 'block') {
const content = node.nodes.map(serializeNode).join('\n');
return deepenPlaintextQuote(content);
}
- if (node.object === 'document' || (node.object === 'block' && Block.isBlockList(node.nodes))) {
+ if (node.object === 'document') {
+ return node.nodes.map(serializeNode).join('\n');
+ } else if (node.object === 'block' && Block.isBlockList(node.nodes)) {
return node.nodes.map(serializeNode).join('\n');
} else {
return node.text;
diff --git a/app/src/components/composer-editor/emoji-plugins.tsx b/app/src/components/composer-editor/emoji-plugins.tsx
index da9eb70b8..09d11ed4f 100644
--- a/app/src/components/composer-editor/emoji-plugins.tsx
+++ b/app/src/components/composer-editor/emoji-plugins.tsx
@@ -213,7 +213,7 @@ export function updateEmojiMark(editor: Editor, existing, { typed, suggestions,
editor.moveToFocus();
}
-function onKeyDown(event, editor: Editor, next: () => void) {
+function onKeyDown(event: React.KeyboardEvent, editor: Editor, next: () => void) {
if ([' ', 'Return', 'Enter'].includes(event.key)) {
const emoji = editor.value.marks.find(i => i.type === EMOJI_TYPING_TYPE);
if (!emoji) return next();
@@ -239,7 +239,7 @@ function onKeyDown(event, editor: Editor, next: () => void) {
return next();
}
-function onKeyUp(event, editor: Editor, next: () => void) {
+function onKeyUp(event: React.KeyboardEvent, editor: Editor, next: () => void) {
const emoji = editor.value.marks.find(i => i.type === EMOJI_TYPING_TYPE);
if (!emoji) {
const { offset, key } = editor.value.selection.focus;
diff --git a/app/src/components/composer-editor/link-plugins.tsx b/app/src/components/composer-editor/link-plugins.tsx
index efe8aaa19..ed29ef9cf 100644
--- a/app/src/components/composer-editor/link-plugins.tsx
+++ b/app/src/components/composer-editor/link-plugins.tsx
@@ -106,7 +106,7 @@ const BaseLinkPlugin: ComposerEditorPlugin = {
}),
],
onPaste,
- onKeyDown: function onKeyDown(event, editor: Editor, next: () => void) {
+ onKeyDown: function onKeyDown(event: React.KeyboardEvent, editor: Editor, next: () => void) {
// ensure space and enter always terminate links
if (!['Space', 'Enter', ' ', 'Return'].includes(event.key)) {
return next();
@@ -117,14 +117,16 @@ const BaseLinkPlugin: ComposerEditorPlugin = {
}
return next();
},
- renderMark,
+ renderMark: ({ mark, children, targetIsHTML }, editor, next) => {
+ return renderMark({ mark, children, targetIsHTML }, editor, next);
+ },
rules,
- commands: {
+ appCommands: {
'contenteditable:insert-link': (event, editor) => {
// want to see a hack? here you go!
// 1: find our container and then find the link toolbar icon - this approach
// ensures we get the link button in the /current/ composer.
- const linkButton = event.target
+ const linkButton = (event.target as HTMLElement)
.closest('.RichEditor-root')
.querySelector('.fa.fa-link')
.closest('button');
diff --git a/app/src/components/composer-editor/template-plugins.tsx b/app/src/components/composer-editor/template-plugins.tsx
index d716cb91d..466ffbf9d 100644
--- a/app/src/components/composer-editor/template-plugins.tsx
+++ b/app/src/components/composer-editor/template-plugins.tsx
@@ -64,7 +64,7 @@ const rules = [
},
];
-function onKeyDown(event, editor: Editor, next: () => void) {
+function onKeyDown(event: React.KeyboardEvent, editor: Editor, next: () => void) {
// If the user has a template variable selected and types a character,
// delete the template variable. By default you just can't type.
if (
diff --git a/app/src/components/composer-editor/types.ts b/app/src/components/composer-editor/types.ts
index 0062f8478..4249bc3a4 100644
--- a/app/src/components/composer-editor/types.ts
+++ b/app/src/components/composer-editor/types.ts
@@ -1,4 +1,5 @@
import { Node, Editor, Value, Mark, Block, Inline } from 'slate';
+import { Plugin, RenderMarkProps } from 'slate-react';
export interface Rule {
deserialize?: (
@@ -8,11 +9,11 @@ export interface Rule {
serialize?: (obj: any, children: string) => React.ReactNode | void;
}
-export interface ComposerEditorPlugin {
+export interface ComposerEditorPlugin extends Omit {
renderMark?: (
- { mark, children, targetIsHTML }: { mark: Mark; children: any; targetIsHTML?: boolean },
- editor: Editor | null,
- next: () => void
+ { mark, children, targetIsHTML }: RenderMarkProps & { targetIsHTML?: boolean },
+ editor?: Editor,
+ next?: () => void
) => void | string | JSX.Element;
renderNode?: (
@@ -27,15 +28,11 @@ export interface ComposerEditorPlugin {
toolbarComponents?: React.ComponentType[];
toolbarSectionClass?: string;
- commands?: { [command: string]: (event: CustomEvent, editor: Editor) => Editor };
+ appCommands?: { [command: string]: (event: CustomEvent, editor: Editor) => Editor };
onChange?(editor: Editor, next: () => void);
- onPaste?: (event, editor: Editor, next: () => void) => void;
- onKeyDown?: (event, editor: Editor, next: () => void) => void;
- onKeyUp?: (event, editor: Editor, next: () => void) => void;
- onClick?: (event, editor: Editor, next: () => void) => void;
- onCompositionStart?: (event, editor: Editor, next: () => void) => void;
- onCompositionEnd?: (event, editor: Editor, next: () => void) => void;
+ onKeyDown?: (event: React.KeyboardEvent, editor: Editor, next: () => void) => void;
+ onKeyUp?: (event: React.KeyboardEvent, editor: Editor, next: () => void) => void;
}
export interface ComposerEditorPluginToolbarComponentProps {
diff --git a/app/src/components/date-picker.tsx b/app/src/components/date-picker.tsx
index 3915dbe83..6874a2796 100644
--- a/app/src/components/date-picker.tsx
+++ b/app/src/components/date-picker.tsx
@@ -13,7 +13,7 @@ type DatePickerState = {
focused: boolean;
};
-export default class DatePicker extends React.Component {
+export class DatePicker extends React.Component {
static displayName = 'DatePicker';
static contextTypes = {
diff --git a/app/src/components/decorators/listens-to-observable.tsx b/app/src/components/decorators/listens-to-observable.tsx
index cecce84d8..b6e28f041 100644
--- a/app/src/components/decorators/listens-to-observable.tsx
+++ b/app/src/components/decorators/listens-to-observable.tsx
@@ -1,11 +1,28 @@
import React from 'react';
-function ListensToObservable(ComposedComponent, { getObservable, getStateFromObservable }) {
+function ListensToObservable(
+ ComposedComponent: typeof React.Component & {
+ displayName?: string;
+ containerRequired?: boolean;
+ containerStyles?: object;
+ },
+ {
+ getObservable,
+ getStateFromObservable,
+ }: {
+ getObservable: (props: T) => Rx.Observable;
+ getStateFromObservable: (data: U, { props: T }) => V;
+ }
+) {
return class extends ComposedComponent {
static displayName = ComposedComponent.displayName;
static containerRequired = ComposedComponent.containerRequired;
static containerStyles = ComposedComponent.containerStyles;
+ disposable: any;
+ unmounted: boolean;
+ observable: Rx.Observable;
+
constructor(props) {
super(props);
this.state = getStateFromObservable(null, { props });
diff --git a/app/src/components/drop-zone.tsx b/app/src/components/drop-zone.tsx
index cd1b8fa33..88232d8fd 100644
--- a/app/src/components/drop-zone.tsx
+++ b/app/src/components/drop-zone.tsx
@@ -5,7 +5,7 @@ import _ from 'underscore';
interface DropZoneProps {
id?: string;
className?: string;
- style?: any;
+ style?: React.CSSProperties;
onClick?: (e: React.MouseEvent) => void;
onDoubleClick?: (e: React.MouseEvent) => void;
shouldAcceptDrop: (e: React.DragEvent) => boolean;
diff --git a/app/src/components/empty-list-state.tsx b/app/src/components/empty-list-state.tsx
index 814d0ac02..b15e4deb5 100644
--- a/app/src/components/empty-list-state.tsx
+++ b/app/src/components/empty-list-state.tsx
@@ -69,7 +69,7 @@ class EmptyInboxState extends React.Component<{}, { width: number; height: numbe
window.requestAnimationFrame(() =>
this.setState({
width: entries[0].contentRect.width,
- height: entries[0].contentRect.width,
+ height: entries[0].contentRect.height,
})
)
);
diff --git a/app/src/components/flexbox.tsx b/app/src/components/flexbox.tsx
index d9c9e49ef..fca595aab 100644
--- a/app/src/components/flexbox.tsx
+++ b/app/src/components/flexbox.tsx
@@ -4,7 +4,7 @@ import React from 'react';
type FlexboxProps = {
direction?: string;
inline?: boolean;
- style?: object;
+ style?: React.CSSProperties;
height?: string;
};
/*
diff --git a/app/src/components/injected-component.tsx b/app/src/components/injected-component.tsx
index 93678cd26..2ee2cb45d 100644
--- a/app/src/components/injected-component.tsx
+++ b/app/src/components/injected-component.tsx
@@ -10,7 +10,7 @@ type InjectedComponentProps = {
className?: string;
exposedProps?: object;
fallback?: (...args: any[]) => any;
- style?: object;
+ style?: React.CSSProperties;
requiredMethods?: string[];
onComponentDidChange?: (...args: any[]) => any;
};
diff --git a/app/src/components/list-data-source.ts b/app/src/components/list-data-source.ts
index 9b1c3e284..4d3147ff9 100644
--- a/app/src/components/list-data-source.ts
+++ b/app/src/components/list-data-source.ts
@@ -1,9 +1,9 @@
/* eslint no-unused-vars: 0 */
import { EventEmitter } from 'events';
-import ListSelection from './list-selection';
+import { ListSelection } from './list-selection';
import { Model } from '../flux/models/model';
-export default class ListDataSource {
+export class ListDataSource {
// getters prevent use before decl
static get Empty() {
diff --git a/app/src/components/list-selection.ts b/app/src/components/list-selection.ts
index ef8ec6636..829cb65c1 100644
--- a/app/src/components/list-selection.ts
+++ b/app/src/components/list-selection.ts
@@ -2,9 +2,9 @@ import _ from 'underscore';
import { Model } from '../flux/models/model';
import DatabaseStore from '../flux/stores/database-store';
-import ListDataSource from './list-data-source';
+import { ListDataSource } from './list-data-source';
-export default class ListSelection {
+export class ListSelection {
_caches: {
ids?: string[];
} = {};
diff --git a/app/src/components/list-tabular-item.tsx b/app/src/components/list-tabular-item.tsx
index 3fe61330c..50de38076 100644
--- a/app/src/components/list-tabular-item.tsx
+++ b/app/src/components/list-tabular-item.tsx
@@ -18,7 +18,7 @@ type ListTabularItemProps = {
onDoubleClick?: (...args: any[]) => any;
};
-export default class ListTabularItem extends React.Component {
+export class ListTabularItem extends React.Component {
static displayName = 'ListTabularItem';
static propTypes = {
metrics: PropTypes.object,
@@ -86,8 +86,7 @@ export default class ListTabularItem extends React.Component {
if (names[column.name]) {
console.warn(
- `ListTabular: Columns do not have distinct names, will cause React error! \`${column.name
- }\` twice.`
+ `ListTabular: Columns do not have distinct names, will cause React error! \`${column.name}\` twice.`
);
}
names[column.name] = true;
diff --git a/app/src/components/list-tabular.tsx b/app/src/components/list-tabular.tsx
index ccce981d3..f06d3785a 100644
--- a/app/src/components/list-tabular.tsx
+++ b/app/src/components/list-tabular.tsx
@@ -7,9 +7,11 @@ import PropTypes from 'prop-types';
import { ScrollRegion, ScrollRegionProps } from './scroll-region';
import { Spinner } from './spinner';
-import ListDataSource from './list-data-source';
-import ListSelection from './list-selection';
-import ListTabularItem from './list-tabular-item';
+export * from './list-data-source';
+
+import { ListDataSource } from './list-data-source';
+import { ListSelection } from './list-selection';
+import { ListTabularItem } from './list-tabular-item';
export class ListTabularColumn {
name: string;
@@ -135,8 +137,6 @@ interface ListTabularState {
empty: any;
}
-export type ListDataSource = ListDataSource;
-
export class ListTabular extends Component {
static displayName = 'ListTabular';
diff --git a/app/src/components/menu.tsx b/app/src/components/menu.tsx
index be664367a..6ee9d8b24 100644
--- a/app/src/components/menu.tsx
+++ b/app/src/components/menu.tsx
@@ -17,7 +17,7 @@ export interface MenuNameEmailContentProps {
email?: string;
}
-export interface MenuProps extends HTMLProps {
+export interface MenuProps extends HTMLProps {
className?: string;
footerComponents?: React.ReactNode;
headerComponents?: React.ReactNode;
diff --git a/app/src/components/mini-month-view.tsx b/app/src/components/mini-month-view.tsx
new file mode 100644
index 000000000..1ff2bf941
--- /dev/null
+++ b/app/src/components/mini-month-view.tsx
@@ -0,0 +1,120 @@
+import _ from 'underscore';
+import React from 'react';
+import moment, { Moment } from 'moment';
+import classnames from 'classnames';
+
+interface MiniMonthViewProps {
+ value: Moment;
+ onChange: (moment: Moment) => void;
+}
+
+interface MiniMonthViewState {
+ shownYear: number;
+ shownMonth: number;
+}
+
+export class MiniMonthView extends React.Component {
+ static displayName = 'MiniMonthView';
+
+ today = moment();
+
+ constructor(props) {
+ super(props);
+ this.state = this._stateFromProps(props);
+ }
+
+ componentWillReceiveProps(newProps) {
+ this.setState(this._stateFromProps(newProps));
+ }
+
+ _stateFromProps(props: MiniMonthViewProps) {
+ return {
+ shownYear: props.value.year(),
+ shownMonth: props.value.month(),
+ };
+ }
+
+ _isSameDay(m1: Moment, m2: Moment) {
+ return m1.dayOfYear() === m2.dayOfYear() && m1.year() === m2.year();
+ }
+
+ _renderDays(month: Moment) {
+ const curMonthNumber = month.month();
+
+ const dayIter = month.clone().date(1);
+ const startWeek = dayIter.week();
+ const endWeek = moment(dayIter)
+ .date(dayIter.daysInMonth())
+ .week();
+
+ const weekEls = [];
+ for (let week = startWeek; week <= endWeek; week++) {
+ dayIter.week(week); // Locale aware!
+ const dayEls = [];
+ for (let weekday = 0; weekday < 7; weekday++) {
+ dayIter.weekday(weekday); // Locale aware!
+ const dayStr = dayIter.format('D');
+ const className = classnames({
+ day: true,
+ today: this._isSameDay(dayIter, this.today),
+ 'cur-day': this._isSameDay(dayIter, this.props.value),
+ 'cur-month': dayIter.month() === curMonthNumber,
+ });
+ dayEls.push(
+
+ {dayStr}
+
+ );
+ }
+ weekEls.push(
+
+ {dayEls}
+
+ );
+ }
+ return (
+ {
+ if (event.target instanceof HTMLElement && event.target.dataset.unix) {
+ this.props.onChange(moment(Number(event.target.dataset.unix)));
+ }
+ }}
+ >
+ {weekEls}
+
+ );
+ }
+
+ render() {
+ const weekdayGen = moment(this.state.shownYear);
+ const month = moment([this.state.shownYear, this.state.shownMonth]);
+
+ const onChangeMonth = (delta: number) => {
+ const next = month.clone().add(delta, 'months');
+ this.setState({ shownYear: next.year(), shownMonth: next.month() });
+ };
+
+ return (
+
+
+
onChangeMonth(-1)}>
+ ‹
+
+
{month.format('MMMM YYYY')}
+
onChangeMonth(1)}>
+ ›
+
+
+
+ {[0, 1, 2, 3, 4, 5, 6].map(i => (
+
+ {weekdayGen.weekday(i).format('dd')}
+
+ ))}
+
+ {this._renderDays(month)}
+
+ );
+ }
+}
diff --git a/app/src/components/multiselect-list.tsx b/app/src/components/multiselect-list.tsx
index 94143133c..b14e6f1bf 100644
--- a/app/src/components/multiselect-list.tsx
+++ b/app/src/components/multiselect-list.tsx
@@ -9,7 +9,7 @@ import { KeyCommandsRegion } from 'mailspring-component-kit';
import MultiselectListInteractionHandler from './multiselect-list-interaction-handler';
import MultiselectSplitInteractionHandler from './multiselect-split-interaction-handler';
-import ListDataSource from './list-data-source';
+import { ListDataSource } from './list-data-source';
export interface MultiselectListProps extends ListTabularProps {
focusedId?: string;
diff --git a/app/src/components/resizable-region.tsx b/app/src/components/resizable-region.tsx
index 23bce6cd9..db9ab8ee0 100644
--- a/app/src/components/resizable-region.tsx
+++ b/app/src/components/resizable-region.tsx
@@ -73,7 +73,7 @@ type ResizableRegionProps = {
initialHeight?: number;
minHeight?: number;
maxHeight?: number;
- style?: object;
+ style?: React.CSSProperties;
};
type ResizableRegionState = {
diff --git a/app/src/components/retina-img.tsx b/app/src/components/retina-img.tsx
index 91239db0c..79ebf247d 100644
--- a/app/src/components/retina-img.tsx
+++ b/app/src/components/retina-img.tsx
@@ -84,7 +84,7 @@ type RetinaImgProps = {
name?: string;
url?: string;
className?: string;
- style?: object;
+ style?: React.CSSProperties;
fallback?: string;
selected?: boolean;
active?: boolean;
diff --git a/app/src/components/scroll-region.tsx b/app/src/components/scroll-region.tsx
index ed6bd4549..e7f7b75d4 100644
--- a/app/src/components/scroll-region.tsx
+++ b/app/src/components/scroll-region.tsx
@@ -17,22 +17,33 @@ interface TicksProvider {
listen: (callback: (Ticks) => void) => () => void;
}
-type ScrollbarProps = {
+interface ScrollbarProps {
scrollTooltipComponent?: React.ComponentType;
scrollbarTickProvider?: TicksProvider;
- getScrollRegion?: (...args: any[]) => any;
-};
+ getScrollRegion?: () => ScrollRegion;
+}
-type ScrollbarState = {
+interface ScrollSharedState {
totalHeight: number;
- trackHeight: number;
viewportHeight: number;
viewportScrollTop: number;
dragging: boolean;
scrolling: boolean;
- scrollbarTicks?: Ticks;
+}
+
+const InitialSharedState: ScrollSharedState = {
+ totalHeight: 0,
+ viewportHeight: 0,
+ viewportScrollTop: 0,
+ dragging: false,
+ scrolling: false,
};
+interface ScrollbarState extends ScrollSharedState {
+ trackHeight: number;
+ scrollbarTicks?: Ticks;
+}
+
class Scrollbar extends React.Component {
static displayName = 'Scrollbar';
static propTypes = {
@@ -49,24 +60,16 @@ class Scrollbar extends React.Component {
getScrollRegion: PropTypes.func,
};
- _heightObserver: ResizeObserver;
+ _heightObserver: ResizeObserver = null;
_tickUnsub?: () => void;
_trackOffset: number;
_mouseOffsetWithinHandle: number;
- constructor(props) {
- super(props);
- this._heightObserver = null;
- this.state = {
- totalHeight: 0,
- trackHeight: 0,
- viewportHeight: 0,
- viewportScrollTop: 0,
- dragging: false,
- scrolling: false,
- scrollbarTicks: [],
- };
- }
+ state = {
+ ...InitialSharedState,
+ trackHeight: 0,
+ scrollbarTicks: [],
+ };
componentDidMount() {
const trackEl = ReactDOM.findDOMNode(this.refs.track) as HTMLElement;
@@ -220,21 +223,18 @@ class Scrollbar extends React.Component {
}
export interface ScrollRegionProps {
+ ref?: React.Ref | string;
onScroll?: (...args: any[]) => any;
onScrollEnd?: (...args: any[]) => any;
+ onViewportResize?: (size: { width: number; height: number }) => void;
+ onContentResize?: (size: { width: number; height: number }) => void;
className?: string;
scrollTooltipComponent?: React.ComponentType;
scrollbarTickProvider?: TicksProvider;
- getScrollbar?: (...args: any[]) => any;
+ scrollbarRef?: React.RefObject;
}
-type ScrollRegionState = {
- totalHeight: number;
- viewportHeight: number;
- viewportScrollTop: number;
- scrolling: boolean;
- dragging: boolean;
-};
+interface ScrollRegionState extends ScrollSharedState {}
interface ScrollToOptions {
position?: string;
@@ -261,7 +261,7 @@ export enum ScrollPosition {
The ScrollRegion component attaches a custom scrollbar.
*/
export class ScrollRegion extends React.Component<
- ScrollRegionProps & React.HTMLProps,
+ ScrollRegionProps & React.HTMLProps,
ScrollRegionState
> {
static displayName = 'ScrollRegion';
@@ -269,17 +269,23 @@ export class ScrollRegion extends React.Component<
static propTypes = {
onScroll: PropTypes.func,
onScrollEnd: PropTypes.func,
+ onContentResize: PropTypes.func,
+ onViewportResize: PropTypes.func,
className: PropTypes.string,
scrollTooltipComponent: PropTypes.func,
scrollbarTickProvider: PropTypes.object,
children: PropTypes.oneOfType([PropTypes.element, PropTypes.array]),
- getScrollbar: PropTypes.func,
+ scrollbarRef: PropTypes.object,
};
static ScrollPosition = ScrollPosition;
// Concept from https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/c/tdef/UITableViewScrollPosition
static Scrollbar = Scrollbar;
+ _viewportRef = React.createRef();
+ _innerRef = React.createRef();
+ _ownScrollbarRef = React.createRef();
+
_mounted = false;
_scrollToTaskId = 0;
_scrollbarComponent = null;
@@ -288,30 +294,35 @@ export class ScrollRegion extends React.Component<
_onScrollEnd: () => void;
state = {
- totalHeight: 0,
- viewportHeight: 0,
- viewportScrollTop: 0,
- dragging: false,
- scrolling: false,
+ ...InitialSharedState,
};
- get scrollTop() {
- return (ReactDOM.findDOMNode(this.refs.content) as HTMLElement).scrollTop;
+ public get viewportEl() {
+ return this._viewportRef.current;
}
- set scrollTop(val) {
- (ReactDOM.findDOMNode(this.refs.content) as HTMLElement).scrollTop = val;
+ public get contentEl() {
+ return this._innerRef.current;
+ }
+
+ public get scrollTop() {
+ return this._viewportRef.current ? this._viewportRef.current.scrollTop : 0;
+ }
+
+ public set scrollTop(val) {
+ this._viewportRef.current.scrollTop = val;
}
componentDidMount() {
this._mounted = true;
- const viewportEl = ReactDOM.findDOMNode(this.refs.content) as HTMLElement;
- const innerWrapperEl = ReactDOM.findDOMNode(this.refs.inner) as HTMLElement;
+ const viewportEl = this._viewportRef.current;
+ const innerWrapperEl = this._innerRef.current;
this._viewportHeightObserver = new window.ResizeObserver(entries => {
if (entries[0] && entries[0].contentRect.height !== this.state.viewportHeight) {
this._setSharedState({ viewportHeight: entries[0].contentRect.height });
+ this.props.onViewportResize && this.props.onViewportResize(entries[0].contentRect);
}
});
this._totalHeightObserver = new window.ResizeObserver(entries => {
@@ -319,22 +330,27 @@ export class ScrollRegion extends React.Component<
// and the contentRect.height is the inner padded size, not the bounding box size.
if (entries[0] && entries[0].contentRect.height !== this.state.totalHeight) {
this._setSharedState({ totalHeight: entries[0].target.clientHeight });
+ this.props.onContentResize && this.props.onContentResize(entries[0].contentRect);
}
});
this._totalHeightObserver.observe(innerWrapperEl);
this._viewportHeightObserver.observe(viewportEl);
- this._setSharedState({
+ const dims = {
viewportScrollTop: 0,
viewportHeight: viewportEl.clientHeight,
totalHeight: innerWrapperEl.clientHeight,
- });
- }
+ };
+ this._setSharedState(dims);
- componentWillReceiveProps(props) {
- if (this.shouldInvalidateScrollbarComponent(props)) {
- this._scrollbarComponent = null;
+ // If we are using a scrollbar attached elsewhere and handed to us through the ref, there's
+ // a good chance it was mounted at the same time we were and the `ref` is not valid yet.
+ // Wait a tick and send it the sizing so it sizes the handle correctly.
+ if (this.props.scrollbarRef && !this.props.scrollbarRef.current) {
+ window.requestAnimationFrame(() => {
+ this._mounted && this._setSharedState(dims);
+ });
}
}
@@ -348,7 +364,7 @@ export class ScrollRegion extends React.Component<
if (newProps.scrollTooltipComponent !== this.props.scrollTooltipComponent) {
return true;
}
- if (newProps.getScrollbar !== this.props.getScrollbar) {
+ if (newProps.scrollbarRef !== this.props.scrollbarRef) {
return true;
}
return false;
@@ -363,11 +379,11 @@ export class ScrollRegion extends React.Component<
scrolling: this.state.scrolling,
});
- if (!this.props.getScrollbar) {
+ if (!this.props.scrollbarRef) {
if (this._scrollbarComponent == null) {
this._scrollbarComponent = (
{this._scrollbarComponent}
-
-
+
@@ -428,7 +444,7 @@ export class ScrollRegion extends React.Component<
_scroll({ position, settle, done }, clientRectProviderCallback) {
let settleFn;
- const contentNode = ReactDOM.findDOMNode(this.refs.content) as HTMLElement;
+ const viewportNode = this._viewportRef.current;
if (position == null) {
position = ScrollRegion.ScrollPosition.Visible;
}
@@ -444,24 +460,24 @@ export class ScrollRegion extends React.Component<
settleFn(() => {
// If another scroll call has been made since ours, don't do anything.
- if (this._scrollToTaskId !== taskId || !contentNode) {
+ if (this._scrollToTaskId !== taskId || !viewportNode) {
return typeof done === 'function' ? done(false) : undefined;
}
- const contentClientRect = contentNode.getBoundingClientRect();
+ const contentClientRect = viewportNode.getBoundingClientRect();
const rect = _.clone(clientRectProviderCallback());
if (!rect || !contentClientRect) return;
// For sanity's sake, convert the client rectangle we get into a rect
// relative to the contentRect of our scroll region.
- rect.top = rect.top - contentClientRect.top + contentNode.scrollTop;
- rect.bottom = rect.bottom - contentClientRect.top + contentNode.scrollTop;
+ rect.top = rect.top - contentClientRect.top + viewportNode.scrollTop;
+ rect.bottom = rect.bottom - contentClientRect.top + viewportNode.scrollTop;
// Also give ourselves a representation of the visible region, in the same
// coordinate space as `rect`
const contentVisibleRect = _.clone(contentClientRect) as any;
- contentVisibleRect.top += contentNode.scrollTop;
- contentVisibleRect.bottom += contentNode.scrollTop;
+ contentVisibleRect.top += viewportNode.scrollTop;
+ contentVisibleRect.bottom += viewportNode.scrollTop;
if (position === ScrollRegion.ScrollPosition.Top) {
this.scrollTop = rect.top;
@@ -475,7 +491,7 @@ export class ScrollRegion extends React.Component<
}
} else if (position === ScrollRegion.ScrollPosition.Visible) {
const distanceBelowBottom =
- rect.top + rect.height - (contentClientRect.height + contentNode.scrollTop);
+ rect.top + rect.height - (contentClientRect.height + viewportNode.scrollTop);
const distanceAboveTop = this.scrollTop - rect.top;
if (distanceBelowBottom >= 0) {
this.scrollTop += distanceBelowBottom;
@@ -491,14 +507,13 @@ export class ScrollRegion extends React.Component<
}
_settleHeight = callback => {
- const contentNode = ReactDOM.findDOMNode(this.refs.content) as HTMLElement;
+ const viewportNode = this._viewportRef.current;
let lastContentHeight = -1;
- // eslint-disable-next-line no-var
- var scrollIfSettled = () => {
+ const scrollIfSettled = () => {
if (!this._mounted) {
return;
}
- const contentRect = contentNode.getBoundingClientRect();
+ const contentRect = viewportNode.getBoundingClientRect();
if (contentRect.height !== lastContentHeight) {
lastContentHeight = contentRect.height;
} else {
@@ -509,17 +524,17 @@ export class ScrollRegion extends React.Component<
scrollIfSettled();
};
- _setSharedState(state) {
- const scrollbar = this.props.getScrollbar ? this.props.getScrollbar() : this.refs.scrollbar;
- if (scrollbar) scrollbar.setState(state);
- this.setState(state);
+ _setSharedState(state: Partial
) {
+ const scrollbar = (this.props.scrollbarRef || this._ownScrollbarRef).current;
+ if (scrollbar) scrollbar.setState(state as any);
+ this.setState(state as any);
}
_onScroll = event => {
// onScroll events propogate, which is a bit strange. We could actually be
// receiving a scroll event for a textarea inside the scroll region.
// See Preferences > Signatures > textarea
- if (event.target !== ReactDOM.findDOMNode(this.refs.content)) {
+ if (event.target !== this._viewportRef.current) {
return;
}
diff --git a/app/src/components/spinner.tsx b/app/src/components/spinner.tsx
index c34a0cd5d..a66a6eef1 100644
--- a/app/src/components/spinner.tsx
+++ b/app/src/components/spinner.tsx
@@ -6,7 +6,7 @@ import classNames from 'classnames';
type SpinnerProps = {
visible?: boolean;
withCover?: boolean;
- style?: object;
+ style?: React.CSSProperties;
};
type SpinnerState = {
hidden: boolean;
diff --git a/app/src/components/tab-group-region.tsx b/app/src/components/tab-group-region.tsx
index ca7dde47d..4fe7048d7 100644
--- a/app/src/components/tab-group-region.tsx
+++ b/app/src/components/tab-group-region.tsx
@@ -50,7 +50,7 @@ export class TabGroupRegion extends React.Component= 0) {
array.splice(index, 1);
}
return array;
}
+
export function deepClone(object) {
if (_.isArray(object)) {
return object.map(function(value) {
- return plus.deepClone(value);
+ return deepClone(value);
});
} else if (_.isObject(object) && !_.isFunction(object)) {
return _.mapObject(object, function(value) {
- return plus.deepClone(value);
+ return deepClone(value);
});
} else {
return object;
}
}
-export function deepExtend(target) {
- var i, key, object, result, _i, _len, _ref;
- result = target;
- i = 0;
- while (++i < arguments.length) {
- object = arguments[i];
+
+export function deepExtend(...args) {
+ let result = args[0];
+ let i = 0;
+ while (++i < args.length) {
+ const object = args[i];
if (isPlainObject(result) && isPlainObject(object)) {
- _ref = Object.keys(object);
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- key = _ref[_i];
- result[key] = plus.deepExtend(result[key], object[key]);
+ const _ref = Object.keys(object);
+ for (let _i = 0; _i < _ref.length; _i++) {
+ const key = _ref[_i];
+ result[key] = deepExtend(result[key], object[key]);
}
} else {
- result = plus.deepClone(object);
+ result = deepClone(object);
}
}
return result;
}
+
export function valueForKeyPath(object, keyPath) {
- var key, keys, _i, _len;
- keys = splitKeyPath(keyPath);
- for (_i = 0, _len = keys.length; _i < _len; _i++) {
- key = keys[_i];
+ const keys = splitKeyPath(keyPath);
+ for (let _i = 0; _i < keys.length; _i++) {
+ const key = keys[_i];
object = object[key];
if (object == null) {
return;
@@ -74,11 +75,11 @@ export function valueForKeyPath(object, keyPath) {
}
return object;
}
+
export function setValueForKeyPath(object, keyPath, value) {
- var key, keys;
- keys = splitKeyPath(keyPath);
+ const keys = splitKeyPath(keyPath);
while (keys.length > 1) {
- key = keys.shift();
+ const key = keys.shift();
if (object[key] == null) {
object[key] = {};
}
diff --git a/app/src/config.ts b/app/src/config.ts
index 0900d87b8..960329318 100644
--- a/app/src/config.ts
+++ b/app/src/config.ts
@@ -925,10 +925,11 @@ Config.addSchemaEnforcers({
},
});
-var isPlainObject = value =>
- _.isObject(value) && !_.isArray(value) && !_.isFunction(value) && !_.isString(value);
+function isPlainObject(value) {
+ return _.isObject(value) && !_.isArray(value) && !_.isFunction(value) && !_.isString(value);
+}
-var splitKeyPath = function(keyPath) {
+function splitKeyPath(keyPath) {
if (keyPath == null) {
return [];
}
@@ -943,9 +944,9 @@ var splitKeyPath = function(keyPath) {
}
keyPathArray.push(keyPath.substr(startIndex, keyPath.length));
return keyPathArray;
-};
+}
-var withoutEmptyObjects = function(object) {
+function withoutEmptyObjects(object) {
let resultObject = undefined;
if (isPlainObject(object)) {
for (const key in object) {
@@ -962,4 +963,4 @@ var withoutEmptyObjects = function(object) {
resultObject = object;
}
return resultObject;
-};
+}
diff --git a/app/src/date-utils.ts b/app/src/date-utils.ts
index adb3e6872..04ddb03af 100644
--- a/app/src/date-utils.ts
+++ b/app/src/date-utils.ts
@@ -1,4 +1,4 @@
-import moment from 'moment-timezone';
+import moment, { Moment } from 'moment-timezone';
// Init locale for moment
moment.locale(navigator.language);
@@ -268,7 +268,13 @@ const DateUtils = {
getChronoPast,
- parseDateString(dateLikeString) {
+ parseDateString(
+ dateLikeString: string
+ ): {
+ leftoverText: string;
+ start: Moment;
+ end: Moment;
+ } {
const parsed = getChrono().parse(dateLikeString);
const gotTime = { start: false, end: false };
const gotDay = { start: false, end: false };
diff --git a/app/src/default-client-helper.ts b/app/src/default-client-helper.ts
index a36b8c024..8fb3d456e 100644
--- a/app/src/default-client-helper.ts
+++ b/app/src/default-client-helper.ts
@@ -1,16 +1,23 @@
-import { exec } from 'child_process';
import fs from 'fs';
+import { exec } from 'child_process';
import { remote, shell } from 'electron';
import { localized } from './intl';
const bundleIdentifier = 'com.mailspring.mailspring';
-export class DefaultClientHelperWindows {
+interface DCH {
+ available(): boolean;
+ isRegisteredForURLScheme(scheme: string, callback: (registered: boolean | Error) => void): void;
+ resetURLScheme(scheme, callback: (error?: Error) => {}): void;
+ registerForURLScheme(scheme: string, callback: (error?: Error) => {}): void;
+}
+
+export class DefaultClientHelperWindows implements DCH {
available() {
return true;
}
- isRegisteredForURLScheme(scheme, callback) {
+ isRegisteredForURLScheme(scheme: string, callback: (registered: boolean | Error) => void) {
if (!callback) {
throw new Error('isRegisteredForURLScheme is async, provide a callback');
}
@@ -34,25 +41,24 @@ export class DefaultClientHelperWindows {
);
}
- resetURLScheme() {
- remote.dialog.showMessageBox(
- {
- type: 'info',
- buttons: [localized('Learn More')],
- message: localized('Visit Windows Settings to change your default mail client'),
- detail: localized(
- "You'll find Mailspring, along with other options, listed in Default Apps > Mail."
- ),
- },
- () => {
- shell.openExternal(
- 'http://support.getmailspring.com/hc/en-us/articles/115001881412-Choose-Mailspring-as-the-default-mail-client-on-Windows'
- );
- }
- );
+ async resetURLScheme() {
+ const { response } = await remote.dialog.showMessageBox({
+ type: 'info',
+ buttons: [localized('Learn More')],
+ message: localized('Visit Windows Settings to change your default mail client'),
+ detail: localized(
+ "You'll find Mailspring, along with other options, listed in Default Apps > Mail."
+ ),
+ });
+
+ if (response === 0) {
+ shell.openExternal(
+ 'http://support.getmailspring.com/hc/en-us/articles/115001881412-Choose-Mailspring-as-the-default-mail-client-on-Windows'
+ );
+ }
}
- registerForURLScheme(scheme, callback = (error?: Error, result?: null) => {}) {
+ registerForURLScheme(scheme: string, callback = (error?: Error) => {}) {
// Ensure that our registry entires are present
const WindowsUpdater = remote.require('./windows-updater');
WindowsUpdater.createRegistryEntries(
@@ -60,45 +66,45 @@ export class DefaultClientHelperWindows {
allowEscalation: true,
registerDefaultIfPossible: true,
},
- (err, didMakeDefault) => {
+ async (err, didMakeDefault) => {
if (err) {
- remote.dialog.showMessageBox({
+ await remote.dialog.showMessageBox({
type: 'error',
buttons: [localized('OK')],
message: localized('An error has occurred'),
detail: err.message,
});
+ return;
}
+
if (!didMakeDefault) {
- remote.dialog.showMessageBox(
- {
- type: 'info',
- buttons: [localized('Learn More')],
- defaultId: 1,
- message: localized(
- 'Visit Windows Settings to finish making Mailspring your mail client'
- ),
- detail: localized("Click 'Learn More' to view instructions in our knowledge base."),
- },
- () => {
- shell.openExternal(
- 'http://support.getmailspring.com/hc/en-us/articles/115001881412-Choose-Mailspring-as-the-default-mail-client-on-Windows'
- );
- }
- );
+ const { response } = await remote.dialog.showMessageBox({
+ type: 'info',
+ buttons: [localized('Learn More')],
+ defaultId: 1,
+ message: localized(
+ 'Visit Windows Settings to finish making Mailspring your mail client'
+ ),
+ detail: localized("Click 'Learn More' to view instructions in our knowledge base."),
+ });
+ if (response === 0) {
+ shell.openExternal(
+ 'http://support.getmailspring.com/hc/en-us/articles/115001881412-Choose-Mailspring-as-the-default-mail-client-on-Windows'
+ );
+ }
}
- callback(null, null);
+ callback(null);
}
);
}
}
-export class DefaultClientHelperLinux {
+export class DefaultClientHelperLinux implements DCH {
available() {
return !process.env.SNAP;
}
- isRegisteredForURLScheme(scheme, callback) {
+ isRegisteredForURLScheme(scheme: string, callback: (registered: boolean | Error) => void) {
if (!callback) {
throw new Error('isRegisteredForURLScheme is async, provide a callback');
}
@@ -107,26 +113,26 @@ export class DefaultClientHelperLinux {
);
}
- resetURLScheme(scheme, callback = (error?: Error, result?: null) => {}) {
+ resetURLScheme(scheme: string, callback = (error?: Error) => {}) {
exec(`xdg-mime default thunderbird.desktop x-scheme-handler/${scheme}`, err =>
- err ? callback(err) : callback(null, null)
+ err ? callback(err) : callback(null)
);
}
- registerForURLScheme(scheme, callback = (error?: Error, result?: null) => {}) {
+ registerForURLScheme(scheme: string, callback = (error?: Error) => {}) {
exec(`xdg-mime default Mailspring.desktop x-scheme-handler/${scheme}`, err =>
- err ? callback(err) : callback(null, null)
+ err ? callback(err) : callback(null)
);
}
}
-export class DefaultClientHelperMac {
+export class DefaultClientHelperMac implements DCH {
secure = false;
available() {
return true;
}
- getLaunchServicesPlistPath(callback) {
+ getLaunchServicesPlistPath(callback: (plist: string) => void) {
const secure = `${process.env.HOME}/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist`;
const insecure = `${process.env.HOME}/Library/Preferences/com.apple.LaunchServices.plist`;
@@ -189,7 +195,7 @@ export class DefaultClientHelperMac {
});
}
- isRegisteredForURLScheme(scheme, callback) {
+ isRegisteredForURLScheme(scheme: string, callback: (registered: boolean) => void) {
if (!callback) {
throw new Error('isRegisteredForURLScheme is async, provide a callback');
}
@@ -204,7 +210,7 @@ export class DefaultClientHelperMac {
});
}
- resetURLScheme(scheme, callback) {
+ resetURLScheme(scheme: string, callback = (error?: Error) => {}) {
this.readDefaults(defaults => {
// Remove anything already registered for the scheme
for (let ii = defaults.length - 1; ii >= 0; ii--) {
@@ -216,7 +222,7 @@ export class DefaultClientHelperMac {
});
}
- registerForURLScheme(scheme, callback) {
+ registerForURLScheme(scheme: string, callback = (error?: Error) => {}) {
this.readDefaults(defaults => {
// Remove anything already registered for the scheme
for (let ii = defaults.length - 1; ii >= 0; ii--) {
@@ -236,14 +242,16 @@ export class DefaultClientHelperMac {
}
}
-let Default: any = null;
+let Default:
+ | typeof DefaultClientHelperMac
+ | typeof DefaultClientHelperLinux
+ | typeof DefaultClientHelperWindows = null;
+
if (process.platform === 'darwin') {
Default = DefaultClientHelperMac;
-} else if (process.platform === 'linux') {
- Default = DefaultClientHelperLinux;
} else if (process.platform === 'win32') {
Default = DefaultClientHelperWindows;
} else {
- Default = {};
+ Default = DefaultClientHelperLinux;
}
export const DefaultClientHelper = Default;
diff --git a/app/src/error-logger.js b/app/src/error-logger.js
index cc0de43a9..24ff5b4a4 100644
--- a/app/src/error-logger.js
+++ b/app/src/error-logger.js
@@ -109,9 +109,7 @@ module.exports = ErrorLogger = (function() {
crashReporter.start({
productName: 'Mailspring',
companyName: 'Mailspring',
- submitURL: `https://id.getmailspring.com/report-crash?ver=${appVersion}&platform=${
- process.platform
- }`,
+ submitURL: `https://id.getmailspring.com/report-crash?ver=${appVersion}&platform=${process.platform}`,
uploadToServer: true,
autoSubmit: true,
extra: {
diff --git a/app/src/flux/actions.ts b/app/src/flux/actions.ts
index 61933e5e9..3d43cee42 100644
--- a/app/src/flux/actions.ts
+++ b/app/src/flux/actions.ts
@@ -1,4 +1,5 @@
import Reflux from 'reflux';
+import { Listenable } from 'mailspring-store';
const ActionScopeWindow = 'window';
const ActionScopeGlobal = 'global';
@@ -57,7 +58,7 @@ Section: General
*/
type ActionFn = (...args: any[]) => void;
-interface Action extends ActionFn {
+interface Action extends ActionFn, Listenable {
actionName: string;
scope: 'window' | 'global' | 'main';
sync: boolean;
diff --git a/app/src/flux/attributes.ts b/app/src/flux/attributes.ts
index f6567415e..2af6618a2 100644
--- a/app/src/flux/attributes.ts
+++ b/app/src/flux/attributes.ts
@@ -1,30 +1,39 @@
-import { Matcher } from './attributes/matcher';
-import SortOrder from './attributes/sort-order';
-import AttributeNumber from './attributes/attribute-number';
-import AttributeString from './attributes/attribute-string';
-import AttributeObject from './attributes/attribute-object';
-import AttributeBoolean from './attributes/attribute-boolean';
-import AttributeDateTime from './attributes/attribute-datetime';
-import AttributeCollection from './attributes/attribute-collection';
-import AttributeJoinedData from './attributes/attribute-joined-data';
+import { AttributeNumber } from './attributes/attribute-number';
+import { AttributeString } from './attributes/attribute-string';
+import { AttributeObject } from './attributes/attribute-object';
+import { AttributeBoolean } from './attributes/attribute-boolean';
+import { AttributeDateTime } from './attributes/attribute-datetime';
+import { AttributeCollection } from './attributes/attribute-collection';
+import { AttributeJoinedData } from './attributes/attribute-joined-data';
-export default {
- Matcher: Matcher,
- SortOrder: SortOrder,
+export * from './attributes/matcher';
+export * from './attributes/sort-order';
+export * from './attributes/attribute-number';
+export * from './attributes/attribute-string';
+export * from './attributes/attribute-object';
+export * from './attributes/attribute-boolean';
+export * from './attributes/attribute-datetime';
+export * from './attributes/attribute-collection';
+export * from './attributes/attribute-joined-data';
- Number: options => new AttributeNumber(options),
- String: options => new AttributeString(options),
- Object: options => new AttributeObject(options),
- Boolean: options => new AttributeBoolean(options),
- DateTime: options => new AttributeDateTime(options),
- Collection: options => new AttributeCollection(options),
- JoinedData: options => new AttributeJoinedData(options),
-
- AttributeNumber: AttributeNumber,
- AttributeString: AttributeString,
- AttributeObject: AttributeObject,
- AttributeBoolean: AttributeBoolean,
- AttributeDateTime: AttributeDateTime,
- AttributeCollection: AttributeCollection,
- AttributeJoinedData: AttributeJoinedData,
-};
+export function Number(options: ConstructorParameters[0]) {
+ return new AttributeNumber(options);
+}
+export function String(options: ConstructorParameters[0]) {
+ return new AttributeString(options);
+}
+export function Boolean(options: ConstructorParameters[0]) {
+ return new AttributeBoolean(options);
+}
+export function DateTime(options: ConstructorParameters[0]) {
+ return new AttributeDateTime(options);
+}
+export function Collection(options: ConstructorParameters[0]) {
+ return new AttributeCollection(options);
+}
+export function JoinedData(options: ConstructorParameters[0]) {
+ return new AttributeJoinedData(options);
+}
+export function Obj(options: ConstructorParameters[0]) {
+ return new AttributeObject(options);
+}
diff --git a/app/src/flux/attributes/attribute-boolean.ts b/app/src/flux/attributes/attribute-boolean.ts
index 8a3ef2cc3..fb97bf612 100644
--- a/app/src/flux/attributes/attribute-boolean.ts
+++ b/app/src/flux/attributes/attribute-boolean.ts
@@ -1,4 +1,4 @@
-import Attribute from './attribute';
+import { Attribute } from './attribute';
import { Matcher } from './matcher';
/*
Public: The value of this attribute is always a boolean. Null values are coerced to false.
@@ -8,7 +8,7 @@ String attributes can be queries using `equal` and `not`. Matching on
Section: Database
*/
-export default class AttributeBoolean extends Attribute {
+export class AttributeBoolean extends Attribute {
toJSON(val) {
return val;
}
diff --git a/app/src/flux/attributes/attribute-collection.ts b/app/src/flux/attributes/attribute-collection.ts
index fd5d74b2b..b42133215 100644
--- a/app/src/flux/attributes/attribute-collection.ts
+++ b/app/src/flux/attributes/attribute-collection.ts
@@ -1,4 +1,4 @@
-import Attribute from './attribute';
+import { Attribute } from './attribute';
import { Matcher } from './matcher';
import { Model } from '../models/model';
@@ -30,7 +30,7 @@ The value of this attribute is always an array of other model objects.
Section: Database
*/
-export default class AttributeCollection extends Attribute {
+export class AttributeCollection extends Attribute {
itemClass: typeof Model;
joinOnField: string;
joinTableName: string;
@@ -39,19 +39,16 @@ export default class AttributeCollection extends Attribute {
constructor({
modelKey,
jsonKey,
+ queryable,
itemClass,
joinOnField,
joinQueryableBy,
joinTableName,
- queryable,
- }: {
- modelKey: string;
- jsonKey: string;
- queryable: boolean;
- itemClass: typeof Model;
- joinOnField: string;
- joinTableName: string;
- joinQueryableBy: string[];
+ }: ConstructorParameters[0] & {
+ itemClass?: typeof Model;
+ joinOnField?: string;
+ joinTableName?: string;
+ joinQueryableBy?: string[];
}) {
super({ modelKey, jsonKey, queryable });
this.itemClass = itemClass;
@@ -60,7 +57,7 @@ export default class AttributeCollection extends Attribute {
this.joinQueryableBy = joinQueryableBy || [];
}
- toJSON(vals) {
+ toJSON(vals: (object | Model)[]) {
if (!vals) {
return [];
}
@@ -72,16 +69,14 @@ export default class AttributeCollection extends Attribute {
return vals.map(val => {
if (this.itemClass && !(val instanceof this.itemClass)) {
throw new Error(
- `AttributeCollection::toJSON: Value \`${val}\` in ${this.modelKey} is not an ${
- this.itemClass.name
- }`
+ `AttributeCollection::toJSON: Value \`${val}\` in ${this.modelKey} is not an ${this.itemClass.name}`
);
}
- return val.toJSON !== undefined ? val.toJSON() : val;
+ return val && typeof val === 'object' && 'toJSON' in val ? val.toJSON() : val;
});
}
- fromJSON(json) {
+ fromJSON(json: object[]) {
const Klass = this.itemClass;
if (!json || !(json instanceof Array)) {
diff --git a/app/src/flux/attributes/attribute-datetime.ts b/app/src/flux/attributes/attribute-datetime.ts
index 8b74a65a3..d416e5244 100644
--- a/app/src/flux/attributes/attribute-datetime.ts
+++ b/app/src/flux/attributes/attribute-datetime.ts
@@ -1,4 +1,4 @@
-import Attribute from './attribute';
+import { Attribute } from './attribute';
import { Matcher } from './matcher';
/*
@@ -6,7 +6,7 @@ Public: The value of this attribute is always a Javascript `Date`, or `null`.
Section: Database
*/
-export default class AttributeDateTime extends Attribute {
+export class AttributeDateTime extends Attribute {
toJSON(val) {
if (!val) {
return null;
diff --git a/app/src/flux/attributes/attribute-joined-data.ts b/app/src/flux/attributes/attribute-joined-data.ts
index 7fe380909..48abe0f55 100644
--- a/app/src/flux/attributes/attribute-joined-data.ts
+++ b/app/src/flux/attributes/attribute-joined-data.ts
@@ -1,4 +1,4 @@
-import Attribute from './attribute';
+import { Attribute } from './attribute';
const NullPlaceholder = '!NULLVALUE!';
@@ -32,12 +32,17 @@ JoinedData attributes cannot be `queryable`.
Section: Database
*/
-export default class AttributeJoinedData extends Attribute {
+export class AttributeJoinedData extends Attribute {
static NullPlaceholder = NullPlaceholder;
modelTable: string;
- constructor({ modelKey, jsonKey, modelTable, queryable }) {
+ constructor({
+ modelKey,
+ jsonKey,
+ modelTable,
+ queryable,
+ }: ConstructorParameters[0] & { modelTable?: string }) {
super({ modelKey, jsonKey, queryable });
this.modelTable = modelTable;
}
@@ -65,8 +70,6 @@ export default class AttributeJoinedData extends Attribute {
}
includeSQL(klass) {
- return `LEFT OUTER JOIN \`${this.modelTable}\` ON \`${this.modelTable}\`.\`id\` = \`${
- klass.name
- }\`.\`id\``;
+ return `LEFT OUTER JOIN \`${this.modelTable}\` ON \`${this.modelTable}\`.\`id\` = \`${klass.name}\`.\`id\``;
}
}
diff --git a/app/src/flux/attributes/attribute-number.ts b/app/src/flux/attributes/attribute-number.ts
index ee3a6b661..48b336d70 100644
--- a/app/src/flux/attributes/attribute-number.ts
+++ b/app/src/flux/attributes/attribute-number.ts
@@ -1,4 +1,4 @@
-import Attribute from './attribute';
+import { Attribute } from './attribute';
import { Matcher } from './matcher';
/*
@@ -6,7 +6,7 @@ Public: The value of this attribute is always a number, or null.
Section: Database
*/
-export default class AttributeNumber extends Attribute {
+export class AttributeNumber extends Attribute {
toJSON(val) {
return val;
}
diff --git a/app/src/flux/attributes/attribute-object.ts b/app/src/flux/attributes/attribute-object.ts
index 0528bca5c..33f4f09bb 100644
--- a/app/src/flux/attributes/attribute-object.ts
+++ b/app/src/flux/attributes/attribute-object.ts
@@ -1,14 +1,19 @@
-import Attribute from './attribute';
+import { Attribute } from './attribute';
import * as Utils from '../models/utils';
import { Model } from '../models/model';
/*
Public: An object that can be cast to `itemClass`
Section: Database
*/
-export default class AttributeObject extends Attribute {
+export class AttributeObject extends Attribute {
private itemClass: typeof Model;
- constructor({ modelKey, jsonKey, itemClass, queryable }) {
+ constructor({
+ modelKey,
+ jsonKey,
+ itemClass,
+ queryable,
+ }: ConstructorParameters[0] & { itemClass?: typeof Model }) {
super({ modelKey, jsonKey, queryable });
this.itemClass = itemClass;
}
diff --git a/app/src/flux/attributes/attribute-string.ts b/app/src/flux/attributes/attribute-string.ts
index 545635f01..cf1b6e746 100644
--- a/app/src/flux/attributes/attribute-string.ts
+++ b/app/src/flux/attributes/attribute-string.ts
@@ -1,4 +1,4 @@
-import Attribute from './attribute';
+import { Attribute } from './attribute';
import { Matcher } from './matcher';
/*
@@ -9,7 +9,7 @@ String attributes can be queries using `equal`, `not`, and `startsWith`. Matchin
Section: Database
*/
-export default class AttributeString extends Attribute {
+export class AttributeString extends Attribute {
toJSON(val) {
return val;
}
diff --git a/app/src/flux/attributes/attribute.ts b/app/src/flux/attributes/attribute.ts
index f50c546ab..7af09417c 100644
--- a/app/src/flux/attributes/attribute.ts
+++ b/app/src/flux/attributes/attribute.ts
@@ -1,5 +1,5 @@
import { Matcher } from './matcher';
-import SortOrder from './sort-order';
+import { SortOrder } from './sort-order';
/*
Public: The Attribute class represents a single model attribute, like 'account_id'.
@@ -9,7 +9,7 @@ The Attribute class also exposes convenience methods for generating {Matcher} ob
Section: Database
*/
-export default class Attribute {
+export class Attribute {
public modelKey: string;
public tableColumn: string;
public jsonKey: string;
@@ -23,14 +23,14 @@ export default class Attribute {
loadFromColumn,
}: {
modelKey: string;
- queryable: boolean;
- jsonKey: string;
+ queryable?: boolean;
+ jsonKey?: string;
loadFromColumn?: boolean;
}) {
this.modelKey = modelKey;
this.tableColumn = modelKey;
this.jsonKey = jsonKey || modelKey;
- this.queryable = queryable;
+ this.queryable = queryable || false;
if (loadFromColumn && !queryable) {
throw new Error('loadFromColumn requires queryable');
}
@@ -49,7 +49,7 @@ export default class Attribute {
}
// Public: Returns a {Matcher} for objects `=` to the provided value.
- equal(val) {
+ equal(val: string | number | boolean) {
this._assertPresentAndQueryable('equal', val);
return new Matcher(this, '=', val);
}
@@ -63,9 +63,7 @@ export default class Attribute {
}
if (val.length === 0) {
console.warn(
- `Attribute::in (${
- this.modelKey
- }) called with an empty set. You should avoid this useless query!`
+ `Attribute::in (${this.modelKey}) called with an empty set. You should avoid this useless query!`
);
}
if (val.length === 1) {
diff --git a/app/src/flux/attributes/matcher.ts b/app/src/flux/attributes/matcher.ts
index 9d2bc9dce..28cdbc6ac 100644
--- a/app/src/flux/attributes/matcher.ts
+++ b/app/src/flux/attributes/matcher.ts
@@ -1,6 +1,8 @@
import LocalSearchQueryBackend from '../../services/search/search-query-backend-local';
-import Attribute from './attribute';
-import AttributeCollection from './attribute-collection';
+import { QueryExpression } from '../../services/search/search-query-ast';
+import { Attribute } from './attribute';
+import { AttributeCollection } from './attribute-collection';
+import { Model } from '../models/model';
// https://www.sqlite.org/faq.html#q14
// That's right. Two single quotes in a row…
@@ -82,7 +84,7 @@ export class Matcher {
return this.val;
}
- evaluate(model) {
+ evaluate(model: typeof Model) {
let modelValue = model[this.attr.modelKey];
if (modelValue instanceof Function) {
modelValue = modelValue();
@@ -207,7 +209,7 @@ export class Matcher {
class OrCompositeMatcher extends Matcher {
children: Matcher[];
- constructor(children) {
+ constructor(children: Matcher[]) {
super();
this.children = children;
}
@@ -224,7 +226,7 @@ class OrCompositeMatcher extends Matcher {
return this.children.some(matcher => matcher.evaluate(model));
}
- joinSQL(klass) {
+ joinSQL(klass: typeof Model) {
const joins = [];
for (const matcher of this.children) {
const join = matcher.joinSQL(klass);
@@ -235,7 +237,7 @@ class OrCompositeMatcher extends Matcher {
return joins.length ? joins.join(' ') : false;
}
- whereSQL(klass) {
+ whereSQL(klass: typeof Model) {
const wheres = this.children.map(matcher => matcher.whereSQL(klass));
return `(${wheres.join(' OR ')})`;
}
@@ -244,7 +246,7 @@ class OrCompositeMatcher extends Matcher {
class AndCompositeMatcher extends Matcher {
children: Matcher[];
- constructor(children) {
+ constructor(children: Matcher[]) {
super();
this.children = children;
}
@@ -261,7 +263,7 @@ class AndCompositeMatcher extends Matcher {
return this.children.every(m => m.evaluate(model));
}
- joinSQL(klass) {
+ joinSQL(klass: typeof Model) {
const joins = [];
for (const matcher of this.children) {
const join = matcher.joinSQL(klass);
@@ -272,22 +274,22 @@ class AndCompositeMatcher extends Matcher {
return joins.join(' ');
}
- whereSQL(klass) {
+ whereSQL(klass: typeof Model) {
const wheres = this.children.map(m => m.whereSQL(klass));
return `(${wheres.join(' AND ')})`;
}
}
class NotCompositeMatcher extends AndCompositeMatcher {
- whereSQL(klass) {
+ whereSQL(klass: typeof Model) {
return `NOT (${super.whereSQL(klass)})`;
}
}
class StructuredSearchMatcher extends Matcher {
- _searchQuery: string;
+ _searchQuery: QueryExpression;
- constructor(searchQuery) {
+ constructor(searchQuery: QueryExpression) {
super(null, null, null);
this._searchQuery = searchQuery;
}
@@ -308,7 +310,7 @@ class StructuredSearchMatcher extends Matcher {
return true;
}
- whereSQL(klass) {
+ whereSQL(klass: typeof Model) {
return new LocalSearchQueryBackend(klass.name).compile(this._searchQuery);
}
}
@@ -316,7 +318,7 @@ class StructuredSearchMatcher extends Matcher {
class SearchMatcher extends Matcher {
searchQuery: string;
- constructor(searchQuery) {
+ constructor(searchQuery: string) {
if (typeof searchQuery !== 'string' || searchQuery.length === 0) {
throw new Error('You must pass a string with non-zero length to search.');
}
@@ -345,7 +347,7 @@ class SearchMatcher extends Matcher {
return true;
}
- whereSQL(klass) {
+ whereSQL(klass: typeof Model) {
const searchTable = `${klass.name}Search`;
return `\`${klass.name}\`.\`id\` IN (SELECT \`content_id\` FROM \`${searchTable}\` WHERE \`${searchTable}\` MATCH '"${this.searchQuery}"*' LIMIT 1000)`;
}
diff --git a/app/src/flux/attributes/sort-order.ts b/app/src/flux/attributes/sort-order.ts
index ca1ef8ada..042d3b2d6 100644
--- a/app/src/flux/attributes/sort-order.ts
+++ b/app/src/flux/attributes/sort-order.ts
@@ -1,4 +1,5 @@
-import Attribute from './attribute';
+import { Attribute } from './attribute';
+import { Model } from '../models/model';
/*
Public: Represents a particular sort direction on a particular column. You should not
@@ -14,16 +15,16 @@ DatabaseStore.findBy(Message)
Section: Database
*/
-export default class SortOrder {
+export class SortOrder {
public attr: Attribute;
public direction: 'ASC' | 'DESC';
- constructor(attr, direction: 'ASC' | 'DESC' = 'DESC') {
+ constructor(attr: Attribute, direction: 'ASC' | 'DESC' = 'DESC') {
this.attr = attr;
this.direction = direction;
}
- orderBySQL(klass) {
+ orderBySQL(klass: typeof Model) {
return `\`${klass.name}\`.\`${this.attr.tableColumn}\` ${this.direction}`;
}
diff --git a/app/src/flux/mailsync-bridge.ts b/app/src/flux/mailsync-bridge.ts
index 953bb06a5..20e1335ce 100644
--- a/app/src/flux/mailsync-bridge.ts
+++ b/app/src/flux/mailsync-bridge.ts
@@ -14,7 +14,7 @@ import DatabaseStore from './stores/database-store';
import OnlineStatusStore from './stores/online-status-store';
import DatabaseChangeRecord from './stores/database-change-record';
import DatabaseObjectRegistry from '../registries/database-object-registry';
-import { MailsyncProcess } from '../mailsync-process';
+import { MailsyncProcess, MailsyncProcessExit } from '../mailsync-process';
import KeyManager from '../key-manager';
import * as Actions from './actions';
import * as Utils from './models/utils';
@@ -43,7 +43,9 @@ class CrashTracker {
delete this._tooManyFailures[key];
}
- recordClientCrash(fullAccountJSON, { code, error, signal }) {
+ recordClientCrash(fullAccountJSON, crash: MailsyncProcessExit) {
+ console.log(`Sync worker exited.`, crash);
+
this._appendCrashToHistory(fullAccountJSON);
// We now let crashpad do this, because Sentry was losing it's mind.
@@ -298,7 +300,7 @@ export default class MailsyncBridge {
client.identity = IdentityStore.identity();
client.sync();
client.on('deltas', this._onIncomingMessages);
- client.on('close', ({ code, error, signal }) => {
+ client.on('close', ({ code, error, signal }: MailsyncProcessExit) => {
if (this._clients[account.id] !== client) {
return;
}
diff --git a/app/src/flux/models/account.ts b/app/src/flux/models/account.ts
index f22e9a93e..1c341f1d0 100644
--- a/app/src/flux/models/account.ts
+++ b/app/src/flux/models/account.ts
@@ -1,6 +1,7 @@
/* eslint global-require:0 */
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { ModelWithMetadata } from './model-with-metadata';
+import { MailsyncProcessExit } from 'mailspring-exports';
let CategoryStore = null;
let Contact = null;
@@ -50,7 +51,7 @@ export class Account extends ModelWithMetadata {
modelKey: 'emailAddress',
}),
- settings: Attributes.Object({
+ settings: Attributes.Obj({
modelKey: 'settings',
}),
@@ -58,15 +59,15 @@ export class Account extends ModelWithMetadata {
modelKey: 'label',
}),
- autoaddress: Attributes.Object({
+ autoaddress: Attributes.Obj({
modelKey: 'autoaddress',
}),
- aliases: Attributes.Object({
+ aliases: Attributes.Obj({
modelKey: 'aliases',
}),
- defaultAlias: Attributes.Object({
+ defaultAlias: Attributes.Obj({
modelKey: 'defaultAlias',
}),
@@ -74,7 +75,7 @@ export class Account extends ModelWithMetadata {
modelKey: 'syncState',
}),
- syncError: Attributes.Object({
+ syncError: Attributes.Obj({
modelKey: 'syncError',
}),
@@ -111,7 +112,7 @@ export class Account extends ModelWithMetadata {
public aliases: string[];
public defaultAlias: string;
public syncState: string;
- public syncError: string;
+ public syncError: MailsyncProcessExit | null;
public color: string;
constructor(args) {
diff --git a/app/src/flux/models/calendar.ts b/app/src/flux/models/calendar.ts
index b99cb01d7..ebaaf9339 100644
--- a/app/src/flux/models/calendar.ts
+++ b/app/src/flux/models/calendar.ts
@@ -1,5 +1,5 @@
import { Model, AttributeValues } from './model';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
/**
Public: The Calendar model represents a Calendar object.
diff --git a/app/src/flux/models/category.ts b/app/src/flux/models/category.ts
index 443028810..2afb4af27 100644
--- a/app/src/flux/models/category.ts
+++ b/app/src/flux/models/category.ts
@@ -1,7 +1,7 @@
/* eslint global-require: 0 */
import utf7 from 'utf7';
import { Model, AttributeValues } from './model';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { localized } from '../../intl';
// We look for a few standard categories and display them in the Mailboxes
@@ -107,7 +107,7 @@ export class Category extends Model {
queryable: true,
modelKey: 'path',
}),
- localStatus: Attributes.Object({
+ localStatus: Attributes.Obj({
modelKey: 'localStatus',
}),
};
diff --git a/app/src/flux/models/contact-book.ts b/app/src/flux/models/contact-book.ts
index 94464f13d..440955b2d 100644
--- a/app/src/flux/models/contact-book.ts
+++ b/app/src/flux/models/contact-book.ts
@@ -1,6 +1,6 @@
/* eslint global-require: 0 */
import { Model, AttributeValues } from './model';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
export class ContactBook extends Model {
static attributes = {
diff --git a/app/src/flux/models/contact-group.ts b/app/src/flux/models/contact-group.ts
index 69041c7c8..685839606 100644
--- a/app/src/flux/models/contact-group.ts
+++ b/app/src/flux/models/contact-group.ts
@@ -1,6 +1,6 @@
/* eslint global-require: 0 */
import { Model, AttributeValues } from './model';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
export class ContactGroup extends Model {
static attributes = {
diff --git a/app/src/flux/models/contact.ts b/app/src/flux/models/contact.ts
index 19d9014ff..32737e622 100644
--- a/app/src/flux/models/contact.ts
+++ b/app/src/flux/models/contact.ts
@@ -1,7 +1,7 @@
/* eslint global-require: 0 */
import _str from 'underscore.string';
import { Model, AttributeValues } from './model';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import * as Utils from './utils';
import RegExpUtils from '../../regexp-utils';
import { AccountStore } from '../stores/account-store';
@@ -391,7 +391,7 @@ export class Contact extends Model {
modelKey: 'refs',
}),
- info: Attributes.Object({
+ info: Attributes.Obj({
modelKey: 'info',
}),
};
diff --git a/app/src/flux/models/event.ts b/app/src/flux/models/event.ts
index dba1af5d2..afa5055f1 100644
--- a/app/src/flux/models/event.ts
+++ b/app/src/flux/models/event.ts
@@ -1,5 +1,5 @@
import { Model, AttributeValues } from './model';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { Contact } from './contact';
// the Chrono node module is huge
diff --git a/app/src/flux/models/file.ts b/app/src/flux/models/file.ts
index eb50e225f..0a4a6ac5e 100644
--- a/app/src/flux/models/file.ts
+++ b/app/src/flux/models/file.ts
@@ -1,7 +1,7 @@
/* eslint global-require: 0 */
import path from 'path';
import { Model, AttributeValues } from './model';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { localized } from '../../intl';
import RegExpUtils from '../../regexp-utils';
diff --git a/app/src/flux/models/message.ts b/app/src/flux/models/message.ts
index 48969cfd7..c5a5000c0 100644
--- a/app/src/flux/models/message.ts
+++ b/app/src/flux/models/message.ts
@@ -6,7 +6,7 @@ import * as Utils from './utils';
import { Event } from './event';
import { Contact } from './contact';
import { Folder } from './folder';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { ModelWithMetadata } from './model-with-metadata';
import { AttributeValues } from './model';
@@ -172,7 +172,7 @@ export class Message extends ModelWithMetadata {
modelKey: 'forwardedHeaderMessageId',
}),
- folder: Attributes.Object({
+ folder: Attributes.Obj({
queryable: false,
modelKey: 'folder',
itemClass: Folder,
diff --git a/app/src/flux/models/model-with-metadata.ts b/app/src/flux/models/model-with-metadata.ts
index cafcdf7d7..73cf540f7 100644
--- a/app/src/flux/models/model-with-metadata.ts
+++ b/app/src/flux/models/model-with-metadata.ts
@@ -1,12 +1,12 @@
import { Model, AttributeValues } from './model';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
/**
Cloud-persisted data that is associated with a single Mailspring API object
(like a `Thread`, `Message`, or `Account`).
*/
export class PluginMetadata extends Model {
- static attributes = {
+ static attributes = ({
pluginId: Attributes.String({
modelKey: 'pluginId',
}),
@@ -14,10 +14,10 @@ export class PluginMetadata extends Model {
jsonKey: 'v',
modelKey: 'version',
}),
- value: Attributes.Object({
+ value: Attributes.Obj({
modelKey: 'value',
}),
- };
+ } as unknown) as typeof Model['attributes'];
public pluginId: string;
public version: number;
diff --git a/app/src/flux/models/model.ts b/app/src/flux/models/model.ts
index 83a9917d8..435b20f92 100644
--- a/app/src/flux/models/model.ts
+++ b/app/src/flux/models/model.ts
@@ -1,5 +1,5 @@
-import Attributes from '../attributes';
-import Attribute from '../attributes/attribute';
+import * as Attributes from '../attributes';
+import { Attribute } from '../attributes/attribute';
/**
Public: A base class for API objects that provides abstract support for
@@ -36,7 +36,12 @@ type ModelAttributes = {
[attribute: string]: Attribute;
};
+export interface ModelClass {
+ new (): Model;
+}
+
export class Model implements HasStaticAttributes {
+ // @ts-ignore
'constructor': typeof Model; // prettier-ignore
static attributes: ModelAttributes = {
diff --git a/app/src/flux/models/mutable-query-result-set.ts b/app/src/flux/models/mutable-query-result-set.ts
index 2ef44ad90..f1f23da26 100644
--- a/app/src/flux/models/mutable-query-result-set.ts
+++ b/app/src/flux/models/mutable-query-result-set.ts
@@ -1,5 +1,5 @@
import { QueryResultSet } from './query-result-set';
-import AttributeJoinedData from '../attributes/attribute-joined-data';
+import { AttributeJoinedData } from '../attributes/attribute-joined-data';
import { Model } from './model';
import ModelQuery from './query';
diff --git a/app/src/flux/models/provider-syncback-request.ts b/app/src/flux/models/provider-syncback-request.ts
index 84944d2bf..92442d321 100644
--- a/app/src/flux/models/provider-syncback-request.ts
+++ b/app/src/flux/models/provider-syncback-request.ts
@@ -1,5 +1,5 @@
import { Model } from './model';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
export default class ProviderSyncbackRequest extends Model {
static attributes = {
@@ -10,15 +10,15 @@ export default class ProviderSyncbackRequest extends Model {
modelKey: 'type',
}),
- error: Attributes.Object({
+ error: Attributes.Obj({
modelKey: 'error',
}),
- props: Attributes.Object({
+ props: Attributes.Obj({
modelKey: 'props',
}),
- responseJSON: Attributes.Object({
+ responseJSON: Attributes.Obj({
modelKey: 'responseJSON',
jsonKey: 'response_json',
}),
diff --git a/app/src/flux/models/query-subscription-pool.ts b/app/src/flux/models/query-subscription-pool.ts
index ec6adac52..b753bbebf 100644
--- a/app/src/flux/models/query-subscription-pool.ts
+++ b/app/src/flux/models/query-subscription-pool.ts
@@ -2,6 +2,7 @@
import { QuerySubscription } from './query-subscription';
import DatabaseChangeRecord from '../stores/database-change-record';
import ModelQuery from './query';
+import { Model } from './model';
let DatabaseStore = null;
/*
@@ -97,7 +98,7 @@ class QuerySubscriptionPool {
return stack.slice(ii, ii + 4).join('\n');
}
- _keyForQuery(query: ModelQuery) {
+ _keyForQuery(query: ModelQuery) {
return query.sql();
}
diff --git a/app/src/flux/models/query-subscription.ts b/app/src/flux/models/query-subscription.ts
index b892e91b5..e1064179d 100644
--- a/app/src/flux/models/query-subscription.ts
+++ b/app/src/flux/models/query-subscription.ts
@@ -4,11 +4,15 @@ import { MutableQueryResultSet } from './mutable-query-result-set';
import ModelQuery from './query';
import { Model } from './model';
import DatabaseChangeRecord from '../stores/database-change-record';
+import { QueryResultSet } from './query-result-set';
+
+type QuerySubscriptionResult = QueryResultSet | number | T | T[];
+type QuerySubscriptionCallback = (result: QuerySubscriptionResult) => void;
export class QuerySubscription {
_set: MutableQueryResultSet = null;
- _callbacks = [];
- _lastResult = undefined; // null is a valid result!
+ _callbacks: QuerySubscriptionCallback[] = [];
+ _lastResult: QuerySubscriptionResult | undefined = undefined; // null is a valid result!
_updateInFlight = false;
_queuedChangeRecords = [];
_queryVersion = 1;
@@ -16,7 +20,7 @@ export class QuerySubscription {
_options: any;
constructor(
- query,
+ query: ModelQuery | ModelQuery,
options: {
initialModels?: T[];
emitResultSet?: boolean;
@@ -53,7 +57,7 @@ export class QuerySubscription {
return this._query;
};
- addCallback = callback => {
+ addCallback = (callback: QuerySubscriptionCallback) => {
if (!(callback instanceof Function)) {
throw new Error(`QuerySubscription:addCallback - expects a function, received ${callback}`);
}
@@ -63,11 +67,11 @@ export class QuerySubscription {
}
};
- hasCallback = callback => {
+ hasCallback = (callback: QuerySubscriptionCallback) => {
return this._callbacks.indexOf(callback) !== -1;
};
- removeCallback(callback) {
+ removeCallback(callback: QuerySubscriptionCallback) {
if (!(callback instanceof Function)) {
throw new Error(
`QuerySubscription:removeCallback - expects a function, received ${callback}`
@@ -170,7 +174,7 @@ export class QuerySubscription {
}
};
- _itemSortOrderHasChanged(old, updated) {
+ _itemSortOrderHasChanged(old: T, updated: T) {
if (!old || !updated) return true;
for (const descriptor of this._query.orderSortDescriptors()) {
@@ -211,7 +215,7 @@ export class QuerySubscription {
}
}
- _getMissingRange = (desiredRange, currentRange) => {
+ _getMissingRange = (desiredRange: QueryRange, currentRange: QueryRange) => {
if (currentRange && !currentRange.isInfinite() && !desiredRange.isInfinite()) {
const ranges = QueryRange.rangesBySubtracting(desiredRange, currentRange);
return ranges.length === 1 ? ranges[0] : desiredRange;
@@ -219,7 +223,7 @@ export class QuerySubscription {
return desiredRange;
};
- _getQueryForRange = (range, fetchEntireModels: boolean) => {
+ _getQueryForRange = (range: QueryRange, fetchEntireModels: boolean) => {
let rangeQuery = null;
if (!range.isInfinite()) {
rangeQuery = rangeQuery || this._query.clone();
@@ -233,7 +237,10 @@ export class QuerySubscription {
return rangeQuery;
};
- _fetchRange(range, { version, fetchEntireModels }) {
+ _fetchRange(
+ range: QueryRange,
+ { version, fetchEntireModels }: { version: number; fetchEntireModels: boolean }
+ ) {
const rangeQuery = this._getQueryForRange(range, fetchEntireModels);
const haveModels = this._set && this._set.modelCacheCount() > 0;
@@ -308,7 +315,8 @@ export class QuerySubscription {
this._set.setQuery(this._query);
this._lastResult = this._set.immutableClone();
} else {
- this._lastResult = this._query.formatResult(this._set.models());
+ const models = this._set.models();
+ this._lastResult = this._query.formatResult(models);
}
this._callbacks.forEach(callback => callback(this._lastResult));
diff --git a/app/src/flux/models/query.ts b/app/src/flux/models/query.ts
index 218082ae4..1aa48aaa8 100644
--- a/app/src/flux/models/query.ts
+++ b/app/src/flux/models/query.ts
@@ -1,10 +1,11 @@
/* eslint global-require: 0 */
-import Attributes from '../attributes';
+import { Matcher, AttributeJoinedData, AttributeCollection, SortOrder } from '../attributes';
import { QueryRange } from './query-range';
+import { QueryExpression } from '../../services/search/search-query-ast';
import * as Utils from './utils';
import { Model } from './model';
-const { Matcher, AttributeJoinedData, AttributeCollection } = Attributes;
+type DB = typeof import('mailspring-exports').DatabaseStore;
/*
Public: ModelQuery exposes an ActiveRecord-style syntax for building database queries
@@ -37,16 +38,16 @@ query.where([Thread.attributes.categories.contains('label-id')])
Section: Database
*/
-export default class ModelQuery {
- private _database: typeof import('mailspring-exports').DatabaseStore;
- private _matchers = [];
- private _orders = [];
+export default class ModelQuery {
+ private _database: DB;
+ private _matchers: Matcher[] = [];
+ private _orders: SortOrder[] = [];
private _backgroundable = true;
private _distinct = false;
private _range = QueryRange.infinite();
private _returnOne = false;
private _returnIds = false;
- private _includeJoinedData = [];
+ private _includeJoinedData: AttributeJoinedData[] = [];
_background = false;
_count = false;
@@ -58,13 +59,15 @@ export default class ModelQuery {
// - `database` (optional) An optional reference to a {DatabaseStore} the
// query will be executed on.
//
- constructor(klass, database) {
+ constructor(klass, database: DB) {
this._klass = klass.SubclassesUseModelTable || klass;
this._database = database || require('./database-store').default;
}
clone(): ModelQuery {
- const q = new ModelQuery(this._klass, this._database).where(this._matchers).order(this._orders);
+ const q = new ModelQuery(this._klass, this._database)
+ .where(this._matchers)
+ .order(this._orders);
q._orders = [...this._orders];
q._includeJoinedData = [...this._includeJoinedData];
q._range = this._range.clone();
@@ -101,7 +104,12 @@ export default class ModelQuery {
//
// This method is chainable.
//
- where(matchers: any[] | any) {
+ where(
+ matchers:
+ | Matcher[]
+ | Matcher
+ | { [key: string]: string | string[] | number | number[] | boolean }
+ ) {
this._assertNotFinalized();
if (matchers instanceof Matcher) {
@@ -119,9 +127,7 @@ export default class ModelQuery {
const value = matchers[key];
const attr = this._klass.attributes[key];
if (!attr) {
- const msg = `Cannot create where clause \`${key}:${value}\`. ${key} is not an attribute of ${
- this._klass.name
- }`;
+ const msg = `Cannot create where clause \`${key}:${value}\`. ${key} is not an attribute of ${this._klass.name}`;
throw new Error(msg);
}
@@ -135,19 +141,19 @@ export default class ModelQuery {
return this;
}
- whereAny(matchers) {
+ whereAny(matchers: Matcher[]) {
this._assertNotFinalized();
this._matchers.push(new Matcher.Or(matchers));
return this;
}
- search(query) {
+ search(query: string) {
this._assertNotFinalized();
this._matchers.push(new Matcher.Search(query));
return this;
}
- structuredSearch(query) {
+ structuredSearch(query: QueryExpression) {
this._assertNotFinalized();
this._matchers.push(new Matcher.StructuredSearch(query));
return this;
@@ -160,7 +166,7 @@ export default class ModelQuery {
//
// This method is chainable.
//
- include(attr) {
+ include(attr: AttributeJoinedData) {
this._assertNotFinalized();
if (!(attr instanceof AttributeJoinedData)) {
throw new Error('query.include() must be called with a joined data attribute');
@@ -190,7 +196,7 @@ export default class ModelQuery {
//
// This method is chainable.
//
- order(ordersOrOrder) {
+ order(ordersOrOrder: SortOrder | SortOrder[]) {
this._assertNotFinalized();
const orders = ordersOrOrder instanceof Array ? ordersOrOrder : [ordersOrOrder];
this._orders = this._orders.concat(orders);
@@ -214,7 +220,7 @@ export default class ModelQuery {
//
// This method is chainable.
//
- limit(limit) {
+ limit(limit: number) {
this._assertNotFinalized();
if (this._returnOne && limit > 1) {
throw new Error('Cannot use limit > 1 with one()');
@@ -230,7 +236,7 @@ export default class ModelQuery {
//
// This method is chainable.
//
- offset(offset) {
+ offset(offset: number) {
this._assertNotFinalized();
this._range = this._range.clone();
this._range.offset = offset;
@@ -241,7 +247,7 @@ export default class ModelQuery {
//
// A convenience method for setting both limit and offset given a desired page size.
//
- page(start, end, pageSize = 50, pagePadding = 100) {
+ page(start: number, end: number, pageSize = 50, pagePadding = 100) {
const roundToPage = n => Math.max(0, Math.floor(n / pageSize) * pageSize);
this.offset(roundToPage(start - pagePadding));
this.limit(roundToPage(end - start + pagePadding * 2));
@@ -272,7 +278,7 @@ export default class ModelQuery {
// Returns a {Promise} that resolves with the Models returned by the
// query, or rejects with an error from the Database layer.
//
- then(next: (arg0: T) => U ): Promise {
+ then(next: (arg0: T) => U): Promise {
return this.run().then(next);
}
@@ -283,7 +289,7 @@ export default class ModelQuery {
return this._database.run(this);
}
- inflateResult(result) {
+ inflateResult(result: { [key: string]: any }[]) {
if (!result) {
return null;
}
@@ -292,12 +298,12 @@ export default class ModelQuery {
return result[0].count / 1;
}
if (this._returnIds) {
- return result.map(row => row.id);
+ return result.map(row => row.id as string);
}
try {
return result.map(row => {
- const object = Utils.convertToModel(JSON.parse(row.data));
+ const object: T = Utils.convertToModel(JSON.parse(row.data));
for (const attrName of Object.keys(this._klass.attributes)) {
const attr = this._klass.attributes[attrName];
if (!attr.needsColumn() || !attr.loadFromColumn) {
@@ -321,14 +327,22 @@ export default class ModelQuery {
}
}
- formatResult(inflated) {
+ formatResult(inflated: U) {
+ if (this._count) {
+ if (typeof inflated != 'number') {
+ throw new Error(`Expected query result to be a number.`);
+ }
+ return inflated;
+ }
+
+ if (!(inflated instanceof Array)) {
+ throw new Error(`Expected query result to be an array.`);
+ }
+
if (this._returnOne) {
// be careful not to return "undefined" if no items returned
return inflated.length > 0 ? inflated[0] : null;
}
- if (this._count) {
- return inflated;
- }
return [...inflated];
}
@@ -355,7 +369,7 @@ export default class ModelQuery {
result += `, ${attr.tableColumn} `;
}
this._includeJoinedData.forEach(attr => {
- result += `, ${attr.selectSQL(this._klass)} `;
+ result += `, ${attr.selectSQL()} `;
});
}
@@ -378,9 +392,7 @@ export default class ModelQuery {
if (joins.length === 1 && this._canSubselectForJoin(joins[0], allMatchers)) {
const subSql = this._subselectSQL(joins[0], this._matchers, order, limit);
- return `SELECT${distinct} ${result} FROM \`${
- this._klass.name
- }\` WHERE \`id\` IN (${subSql}) ${order}`;
+ return `SELECT${distinct} ${result} FROM \`${this._klass.name}\` WHERE \`id\` IN (${subSql}) ${order}`;
}
return `SELECT${distinct} ${result} FROM \`${
@@ -396,8 +408,8 @@ export default class ModelQuery {
//
// Note: This is currently only intended for use in the thread list
//
- _canSubselectForJoin(matcher, allMatchers) {
- const joinAttribute = matcher.attribute();
+ _canSubselectForJoin(matcher: Matcher, allMatchers: Matcher[]) {
+ const joinAttribute = matcher.attribute() as AttributeCollection;
if (!Number.isInteger(this._range.limit)) {
return false;
@@ -416,8 +428,13 @@ export default class ModelQuery {
return allMatchersOnJoinTable && allOrdersOnJoinTable;
}
- _subselectSQL(returningMatcher, subselectMatchers, order, limit) {
- const returningAttribute = returningMatcher.attribute();
+ _subselectSQL(
+ returningMatcher: Matcher,
+ subselectMatchers: Matcher[],
+ order: string,
+ limit: string
+ ) {
+ const returningAttribute = returningMatcher.attribute() as AttributeCollection;
const table = returningAttribute.tableNameForJoinAgainst(this._klass);
const wheres = subselectMatchers.map(c => c.whereSQL(this._klass)).filter(c => !!c);
@@ -530,7 +547,7 @@ export default class ModelQuery {
return all;
}
- matcherValueForModelKey(key) {
+ matcherValueForModelKey(key: string) {
const matcher = this._matchers.find(m => m.attr.modelKey === key);
return matcher ? matcher.val : null;
}
diff --git a/app/src/flux/models/thread.ts b/app/src/flux/models/thread.ts
index ed09fe85b..cda628b20 100644
--- a/app/src/flux/models/thread.ts
+++ b/app/src/flux/models/thread.ts
@@ -3,7 +3,7 @@ import { Contact } from './contact';
import { Folder } from './folder';
import { Label } from './label';
import { Category } from './category';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import DatabaseStore from '../stores/database-store';
import { ModelWithMetadata } from './model-with-metadata';
diff --git a/app/src/flux/models/utils.ts b/app/src/flux/models/utils.ts
index 82c163fb3..e027f3994 100644
--- a/app/src/flux/models/utils.ts
+++ b/app/src/flux/models/utils.ts
@@ -14,8 +14,8 @@ import DatabaseObjectRegistry from '../../registries/database-object-registry';
export function waitFor(latch, options: { timeout?: number } = {}) {
const timeout = options.timeout || 400;
const expire = Date.now() + timeout;
- return new Promise(function(resolve, reject) {
- var attempt = function() {
+ return new Promise(function(resolve, reject) {
+ const attempt = () => {
if (Date.now() > expire) {
return reject(new Error(`Utils.waitFor hit timeout (${timeout}ms) without firing.`));
}
@@ -116,7 +116,7 @@ export function wordSearchRegExp(str = '') {
// Takes an optional customizer. The customizer is passed the key and the
// new cloned value for that key. The customizer is expected to either
// modify the value and return it or simply be the identity function.
-export function deepClone(object, customizer?, stackSeen = [], stackRefs = []) {
+export function deepClone(object: T, customizer?, stackSeen = [], stackRefs = []): T {
let newObject;
if (!_.isObject(object)) {
return object;
diff --git a/app/src/flux/stores/account-store.ts b/app/src/flux/stores/account-store.ts
index 5b41769f0..00656ced4 100644
--- a/app/src/flux/stores/account-store.ts
+++ b/app/src/flux/stores/account-store.ts
@@ -14,6 +14,7 @@ const configAccountsKey = 'accounts';
const configVersionKey = 'accountsVersion';
export type IAliasSet = Array;
+
/*
Public: The AccountStore listens to changes to the available accounts in
the database and exposes the currently active Account via {::current}
@@ -160,7 +161,7 @@ class _AccountStore extends MailspringStore {
* Actions.updateAccount is called directly from the local-sync worker.
* This will update the account with its updated sync state
*/
- _onUpdateAccount = (id, updated) => {
+ _onUpdateAccount = (id: string, updated: Partial) => {
const idx = this._accounts.findIndex(a => a.id === id);
let account = this._accounts[idx];
if (!account) return;
@@ -176,7 +177,7 @@ class _AccountStore extends MailspringStore {
* the AccountStore and runs `ensureK2Consistency`. This will actually
* delete the Account on the local sync side.
*/
- _onRemoveAccount = id => {
+ _onRemoveAccount = (id: string) => {
const account = this._accounts.find(a => a.id === id);
if (!account) return;
@@ -209,7 +210,7 @@ class _AccountStore extends MailspringStore {
}
};
- _onReorderAccount = (id, newIdx) => {
+ _onReorderAccount = (id: string, newIdx: number) => {
const existingIdx = this._accounts.findIndex(a => a.id === id);
if (existingIdx === -1) return;
const account = this._accounts[existingIdx];
@@ -219,7 +220,7 @@ class _AccountStore extends MailspringStore {
this._save();
};
- addAccount = async account => {
+ addAccount = async (account: Account) => {
if (!account.emailAddress || !account.provider || !(account instanceof Account)) {
throw new Error(`Returned account data is invalid: ${JSON.stringify(account)}`);
}
diff --git a/app/src/flux/stores/attachment-store.ts b/app/src/flux/stores/attachment-store.ts
index cb16e34cf..19f0eb6fb 100644
--- a/app/src/flux/stores/attachment-store.ts
+++ b/app/src/flux/stores/attachment-store.ts
@@ -160,7 +160,7 @@ class AttachmentStore extends MailspringStore {
.then(filePath => shell.openItem(filePath))
.catch(this._catchFSErrors)
.catch(error => {
- return this._presentError({ file, error });
+ this._presentError({ file, error });
});
};
@@ -214,54 +214,56 @@ class AttachmentStore extends MailspringStore {
_fetchAndSaveAll = files => {
const defaultPath = this._defaultSaveDir();
- const options = {
- defaultPath,
- title: localized('Save Into...'),
- buttonLabel: localized('Download All'),
- properties: ['openDirectory', 'createDirectory'],
- };
return new Promise(resolve => {
- AppEnv.showOpenDialog(options, selected => {
- if (!selected) {
- return;
- }
- const dirPath = selected[0];
- if (!dirPath) {
- return;
- }
- this._lastDownloadDirectory = dirPath;
- AppEnv.savedState.lastDownloadDirectory = dirPath;
-
- const seenPaths = new Set();
- const lastSavePaths = [];
- const savePromises = files.map(file => {
- let externalPath = path.join(dirPath, file.safeDisplayName());
- while (seenPaths.has(externalPath) || fs.existsSync(externalPath)) {
- externalPath = this._incrementPathToAvoidCollision(externalPath);
+ AppEnv.showOpenDialog(
+ {
+ defaultPath,
+ title: localized('Save Into...'),
+ buttonLabel: localized('Download All'),
+ properties: ['openDirectory', 'createDirectory'],
+ },
+ selected => {
+ if (!selected) {
+ return;
}
- seenPaths.add(externalPath);
+ const dirPath = selected[0];
+ if (!dirPath) {
+ return;
+ }
+ this._lastDownloadDirectory = dirPath;
+ AppEnv.savedState.lastDownloadDirectory = dirPath;
- return this._prepareAndResolveFilePath(file)
- .then(filePath => this._writeToExternalPath(filePath, externalPath))
- .then(() => lastSavePaths.push(externalPath));
- });
-
- Promise.all(savePromises)
- .then(() => {
- if (
- lastSavePaths.length > 0 &&
- AppEnv.config.get('core.attachments.openFolderAfterDownload')
- ) {
- shell.showItemInFolder(lastSavePaths[0]);
+ const seenPaths = new Set();
+ const lastSavePaths = [];
+ const savePromises = files.map(file => {
+ let externalPath = path.join(dirPath, file.safeDisplayName());
+ while (seenPaths.has(externalPath) || fs.existsSync(externalPath)) {
+ externalPath = this._incrementPathToAvoidCollision(externalPath);
}
- return resolve(lastSavePaths);
- })
- .catch(this._catchFSErrors)
- .catch(error => {
- return this._presentError({ error });
+ seenPaths.add(externalPath);
+
+ return this._prepareAndResolveFilePath(file)
+ .then(filePath => this._writeToExternalPath(filePath, externalPath))
+ .then(() => lastSavePaths.push(externalPath));
});
- });
+
+ Promise.all(savePromises)
+ .then(() => {
+ if (
+ lastSavePaths.length > 0 &&
+ AppEnv.config.get('core.attachments.openFolderAfterDownload')
+ ) {
+ shell.showItemInFolder(lastSavePaths[0]);
+ }
+ return resolve(lastSavePaths);
+ })
+ .catch(this._catchFSErrors)
+ .catch(error => {
+ this._presentError({ error });
+ });
+ }
+ );
});
};
@@ -309,7 +311,7 @@ class AttachmentStore extends MailspringStore {
const name = file ? file.displayName() : localized('one or more files');
const errorString = error ? error.toString() : '';
- return remote.dialog.showMessageBox({
+ remote.dialog.showMessageBoxSync({
type: 'warning',
message: localized('Download Failed'),
detail: localized(
@@ -335,7 +337,7 @@ class AttachmentStore extends MailspringStore {
}
if (message) {
- remote.dialog.showMessageBox({
+ remote.dialog.showMessageBoxSync({
type: 'warning',
message: localized('Download Failed'),
detail: `${message}\n\n${error.message}`,
diff --git a/app/src/flux/stores/database-agent.js b/app/src/flux/stores/database-agent.js
index 9a5737cad..f809a9e79 100644
--- a/app/src/flux/stores/database-agent.js
+++ b/app/src/flux/stores/database-agent.js
@@ -4,40 +4,32 @@ const dbs = {};
const deathDelay = 5000;
let deathTimer = setTimeout(() => process.exit(0), deathDelay);
-const getDatabase = (dbpath) => {
+function getDatabase(dbpath) {
if (dbs[dbpath]) {
- return dbs[dbpath].openPromise;
+ return Promise.resolve(dbs[dbpath]);
}
- let openResolve = null;
-
- dbs[dbpath] = new Sqlite3(dbpath, {readonly: true});
- dbs[dbpath].on('close', (err) => {
+ try {
+ dbs[dbpath] = new Sqlite3(dbpath, { readonly: true, timeout: 10000 });
+ } catch (err) {
console.error(err);
process.exit(1);
- });
- dbs[dbpath].on('open', () => {
- openResolve(dbs[dbpath]);
- });
+ }
- dbs[dbpath].openPromise = new Promise((resolve) => {
- openResolve = resolve;
- });
-
- return dbs[dbpath].openPromise;
+ return Promise.resolve(dbs[dbpath]);
}
-process.on('message', (m) => {
+process.on('message', m => {
clearTimeout(deathTimer);
- const {query, values, id, dbpath} = m;
+ const { query, values, id, dbpath } = m;
const start = Date.now();
- getDatabase(dbpath).then((db) => {
+ getDatabase(dbpath).then(db => {
clearTimeout(deathTimer);
const fn = query.startsWith('SELECT') ? 'all' : 'run';
const stmt = db.prepare(query);
const results = stmt[fn](values);
- process.send({type: 'results', results, id, agentTime: Date.now() - start});
+ process.send({ type: 'results', results, id, agentTime: Date.now() - start });
clearTimeout(deathTimer);
deathTimer = setTimeout(() => process.exit(0), deathDelay);
diff --git a/app/src/flux/stores/database-store.ts b/app/src/flux/stores/database-store.ts
index 26610c641..5c297d7c8 100644
--- a/app/src/flux/stores/database-store.ts
+++ b/app/src/flux/stores/database-store.ts
@@ -4,7 +4,7 @@ import createDebug from 'debug';
import childProcess, { ChildProcess } from 'child_process';
import LRU from 'lru-cache';
import Sqlite3 from 'better-sqlite3';
-import { remote, EventEmitter } from 'electron';
+import { remote } from 'electron';
import { ExponentialBackoffScheduler } from '../../backoff-schedulers';
import { Model } from '../models/model';
import MailspringStore from '../../global/mailspring-store';
@@ -51,28 +51,22 @@ function handleUnrecoverableDatabaseError(
async function openDatabase(dbPath) {
try {
- const database = await new Promise((resolve, reject) => {
- const db = new Sqlite3(dbPath, { readonly: true }) as Sqlite3.Database & EventEmitter;
- db.on('close', reject);
- db.on('open', () => {
- // https://www.sqlite.org/wal.html
- // WAL provides more concurrency as readers do not block writers and a writer
- // does not block readers. Reading and writing can proceed concurrently.
- db.pragma(`journal_mode = WAL`);
+ const db = new Sqlite3(dbPath, { readonly: true, timeout: 10000 }) as Sqlite3.Database;
- // Note: These are properties of the connection, so they must be set regardless
- // of whether the database setup queries are run.
+ // https://www.sqlite.org/wal.html
+ // WAL provides more concurrency as readers do not block writers and a writer
+ // does not block readers. Reading and writing can proceed concurrently.
+ db.pragma(`journal_mode = WAL`);
- // https://www.sqlite.org/intern-v-extern-blob.html
- // A database page size of 8192 or 16384 gives the best performance for large BLOB I/O.
- db.pragma(`main.page_size = 8192`);
- db.pragma(`main.cache_size = 20000`);
- db.pragma(`main.synchronous = NORMAL`);
+ // Note: These are properties of the connection, so they must be set regardless
+ // of whether the database setup queries are run.
- resolve(db);
- });
- });
- return database;
+ // https://www.sqlite.org/intern-v-extern-blob.html
+ // A database page size of 8192 or 16384 gives the best performance for large BLOB I/O.
+ db.pragma(`main.page_size = 8192`);
+ db.pragma(`main.cache_size = 20000`);
+ db.pragma(`main.synchronous = NORMAL`);
+ return db;
} catch (err) {
handleUnrecoverableDatabaseError(err);
return null;
@@ -218,7 +212,7 @@ class DatabaseStore extends MailspringStore {
// If a query is made before the database has been opened, the query will be
// held in a queue and run / resolved when the database is ready.
_query(query: SQLString, values: SQLValue[] = [], background = false) {
- return new Promise(async (resolve, reject) => {
+ return new Promise<{ [key: string]: any }[]>(async (resolve, reject) => {
if (!this._open) {
this._waiting.push(() => this._query(query, values).then(resolve, reject));
return;
@@ -403,7 +397,7 @@ class DatabaseStore extends MailspringStore {
//
// Returns a {Query}
//
- find(klass, id) {
+ find(klass: typeof Model, id) {
if (!klass) {
throw new Error(`DatabaseStore::find - You must provide a class`);
}
@@ -512,9 +506,12 @@ class DatabaseStore extends MailspringStore {
// Returns a {Promise} that
// - resolves with the result of the database query.
//
- run(modelQuery: Query, options = { format: true }): Promise {
+ run(modelQuery: Query, options?: { format: boolean }): Promise;
+ run(modelQuery: Query, options: { format: false }): Promise;
+
+ run(modelQuery: Query, options = { format: true }): Promise {
return this._query(modelQuery.sql(), [], modelQuery._background).then(result => {
- let transformed = modelQuery.inflateResult(result);
+ let transformed: any = modelQuery.inflateResult(result);
if (options.format !== false) {
transformed = modelQuery.formatResult(transformed);
}
diff --git a/app/src/flux/stores/draft-store.ts b/app/src/flux/stores/draft-store.ts
index 4fc84666e..724f6c031 100644
--- a/app/src/flux/stores/draft-store.ts
+++ b/app/src/flux/stores/draft-store.ts
@@ -179,7 +179,7 @@ class DraftStore extends MailspringStore {
// this is a fake status!
for (const draft of drafts) {
if (this._draftsSending[draft.headerMessageId]) {
- const m = draft.metadataForPluginId('send-later');
+ const m = draft.metadataForPluginId('send-later') as any;
if (m && m.isUndoSend && !m.expiration) {
delete this._draftsSending[draft.headerMessageId];
}
diff --git a/app/src/flux/stores/feature-usage-store.tsx b/app/src/flux/stores/feature-usage-store.tsx
index 72726d4d8..0ce579f40 100644
--- a/app/src/flux/stores/feature-usage-store.tsx
+++ b/app/src/flux/stores/feature-usage-store.tsx
@@ -3,7 +3,7 @@ import React from 'react';
import MailspringStore from 'mailspring-store';
import { FeatureUsedUpModal } from 'mailspring-component-kit';
import * as Actions from '../actions';
-import { IdentityStore } from './identity-store';
+import { IdentityStore, EMPTY_FEATURE_USAGE, IIdentity } from './identity-store';
import { SendFeatureUsageEventTask } from '../tasks/send-feature-usage-event-task';
import { localized } from '../../intl';
@@ -11,6 +11,12 @@ class NoProAccessError extends Error {}
const UsageRecordedServerSide = ['contact-profiles', 'translation'];
+export interface FeatureLexicon {
+ headerText: string;
+ rechargeText: string;
+ iconUrl: string;
+}
+
/**
* FeatureUsageStore is backed by the IdentityStore
*
@@ -48,7 +54,7 @@ const UsageRecordedServerSide = ['contact-profiles', 'translation'];
* Valid periods are:
* 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'unlimited'
*/
-class FeatureUsageStore extends MailspringStore {
+class _FeatureUsageStore extends MailspringStore {
_waitForModalClose = [];
NoProAccessError = NoProAccessError;
_disp: Rx.Disposable;
@@ -72,10 +78,8 @@ class FeatureUsageStore extends MailspringStore {
this._usub();
}
- displayUpgradeModal(feature, lexicon) {
- //
+ displayUpgradeModal(feature: string, lexicon: FeatureLexicon) {
const featureData = this._dataForFeature(feature);
- const { iconUrl } = lexicon;
let { headerText, rechargeText } = lexicon;
if (!featureData.quota) {
@@ -84,7 +88,7 @@ class FeatureUsageStore extends MailspringStore {
headerText = headerText || localized("You've reached your quota");
const time = featureData.period === 'monthly' ? localized('month') : localized('week');
- rechargeText = rechargeText.replace('%1$@', featureData.quota).replace('%2$@', time);
+ rechargeText = rechargeText.replace('%1$@', `${featureData.quota}`).replace('%2$@', time);
}
Actions.openModal({
@@ -94,7 +98,7 @@ class FeatureUsageStore extends MailspringStore {
),
@@ -105,7 +109,7 @@ class FeatureUsageStore extends MailspringStore {
});
}
- isUsable(feature) {
+ isUsable(feature: string) {
const { usedInPeriod, quota } = this._dataForFeature(feature);
if (!quota) {
return true;
@@ -113,7 +117,7 @@ class FeatureUsageStore extends MailspringStore {
return usedInPeriod < quota;
}
- async markUsedOrUpgrade(feature, lexicon = {}) {
+ async markUsedOrUpgrade(feature: string, lexicon: FeatureLexicon) {
if (!this.isUsable(feature)) {
// throws if the user declines
await this.displayUpgradeModal(feature, lexicon);
@@ -121,8 +125,8 @@ class FeatureUsageStore extends MailspringStore {
this.markUsed(feature);
}
- markUsed(feature) {
- const next = JSON.parse(JSON.stringify(IdentityStore.identity()));
+ markUsed(feature: string) {
+ const next: IIdentity = JSON.parse(JSON.stringify(IdentityStore.identity()));
if (!next || !next.featureUsage) return;
if (next.featureUsage[feature]) {
@@ -145,18 +149,19 @@ class FeatureUsageStore extends MailspringStore {
this._waitForModalClose = [];
};
- _dataForFeature(feature) {
+ _dataForFeature(feature: string) {
const identity = IdentityStore.identity();
if (!identity) {
- return {};
+ return EMPTY_FEATURE_USAGE;
}
+
const usage = identity.featureUsage || {};
if (!usage[feature]) {
AppEnv.reportError(new Error(`Warning: No usage information available for ${feature}`));
- return {};
+ return EMPTY_FEATURE_USAGE;
}
return usage[feature];
}
}
-export default new FeatureUsageStore();
+export const FeatureUsageStore = new _FeatureUsageStore();
diff --git a/app/src/flux/stores/identity-store.ts b/app/src/flux/stores/identity-store.ts
index 395fceefe..95d915729 100644
--- a/app/src/flux/stores/identity-store.ts
+++ b/app/src/flux/stores/identity-store.ts
@@ -19,8 +19,34 @@ export interface IIdentity {
emailAddress: string;
stripePlan: string;
stripePlanEffective: string;
+ featureUsage: {
+ [featureKey: string]: {
+ featureLimitName: 'pro';
+ usedInPeriod: number;
+ quota: number;
+ period: 'weekly' | 'monthly';
+ };
+ };
}
+export const EMPTY_IDENTITY: IIdentity = {
+ id: '',
+ token: '',
+ firstName: '',
+ lastName: '',
+ emailAddress: '',
+ stripePlan: 'basic',
+ stripePlanEffective: '',
+ featureUsage: {},
+};
+
+export const EMPTY_FEATURE_USAGE = {
+ featureLimitName: 'pro',
+ period: 'monthly',
+ usedInPeriod: 0,
+ quota: 0,
+};
+
class _IdentityStore extends MailspringStore {
_identity: IIdentity = null;
_disp: Disposable;
@@ -133,7 +159,7 @@ class _IdentityStore extends MailspringStore {
* for the full list of utm_ labels.
*/
async fetchSingleSignOnURL(
- path,
+ path: string,
{ source, campaign, content }: { source?: string; campaign?: string; content?: string } = {}
) {
if (!this._identity) {
diff --git a/app/src/flux/stores/task-queue.ts b/app/src/flux/stores/task-queue.ts
index 99c0c9514..36ddfeaf2 100644
--- a/app/src/flux/stores/task-queue.ts
+++ b/app/src/flux/stores/task-queue.ts
@@ -1,6 +1,6 @@
import _ from 'underscore';
import MailspringStore from 'mailspring-store';
-import { Rx, SyncbackDraftTask } from 'mailspring-exports';
+import { Rx } from 'mailspring-exports';
import { Task } from '../tasks/task';
import DatabaseStore from './database-store';
@@ -46,8 +46,8 @@ class TaskQueue extends MailspringStore {
_completed: Task[] = [];
_currentSequentialId = Date.now();
- _waitingForLocal: Array<{ task: Task; resolve: (arg: Task) => void }> = [];
- _waitingForRemote: Array<{ task: Task; resolve: (arg: Task) => void }> = [];
+ _waitingForLocal: Array<{ task: Task; resolve: (arg: any) => void }> = [];
+ _waitingForRemote: Array<{ task: Task; resolve: (arg: any) => void }> = [];
constructor() {
super();
@@ -95,8 +95,12 @@ class TaskQueue extends MailspringStore {
return [...this._queue, ...this._completed];
}
- findTasks(typeOrClass, matching = {}, { includeCompleted }: { includeCompleted?: boolean } = {}) {
- const type = typeOrClass instanceof String ? typeOrClass : typeOrClass.name;
+ findTasks(
+ typeOrClass: string | typeof Task,
+ matching = {},
+ { includeCompleted }: { includeCompleted?: boolean } = {}
+ ) {
+ const type = typeof typeOrClass === 'string' ? typeOrClass : typeOrClass.name;
const tasks = includeCompleted ? [...this._queue, ...this._completed] : this._queue;
const matches = tasks.filter(task => {
@@ -112,24 +116,24 @@ class TaskQueue extends MailspringStore {
return matches;
}
- waitForPerformLocal = task => {
+ waitForPerformLocal = (task: T) => {
const upToDateTask = [...this._queue, ...this._completed].find(t => t.id === task.id);
if (upToDateTask && upToDateTask.hasRunLocally()) {
- return Promise.resolve(upToDateTask);
+ return Promise.resolve(upToDateTask as T);
}
- return new Promise(resolve => {
+ return new Promise(resolve => {
this._waitingForLocal.push({ task, resolve });
});
};
- waitForPerformRemote = task => {
+ waitForPerformRemote = (task: T) => {
const upToDateTask = [...this._queue, ...this._completed].find(t => t.id === task.id);
if (upToDateTask && upToDateTask.status === Task.Status.Complete) {
- return Promise.resolve(upToDateTask);
+ return Promise.resolve(upToDateTask as T);
}
- return new Promise(resolve => {
+ return new Promise(resolve => {
this._waitingForRemote.push({ task, resolve });
});
};
diff --git a/app/src/flux/stores/workspace-store.ts b/app/src/flux/stores/workspace-store.ts
index cde680c1b..86b48b4eb 100644
--- a/app/src/flux/stores/workspace-store.ts
+++ b/app/src/flux/stores/workspace-store.ts
@@ -41,7 +41,7 @@ interface SheetToolbarDeclaration {
export interface SheetDeclaration {
id: string;
- columns: string[];
+ columns: { [mode: string]: { id: string; Toolbar: { id: string } }[] };
supportedModes: string[];
icon: string;
@@ -52,7 +52,7 @@ export interface SheetDeclaration {
Toolbar: SheetToolbarDeclaration;
Header: { id: string };
Footer: { id: string };
- Center: { id: string };
+ // Center: { id: string };
}
/*
@@ -293,23 +293,14 @@ class WorkspaceStore extends MailspringStore {
// *`columns` An {Object} with keys for each layout mode the Sheet
// supports. For each key, provide an array of column names.
//
- defineSheet(id, options: Partial = {}, columns = {}) {
- // Make sure all the locations have definitions so that packages
- // can register things into these locations and their toolbars.
- for (const layout in columns) {
- const cols = columns[layout];
- for (let idx = 0; idx < cols.length; idx++) {
- const col = cols[idx];
- if (Location[col] == null) {
- Location[col] = { id: `${col}`, Toolbar: { id: `${col}:Toolbar` } };
- }
- cols[idx] = Location[col];
- }
- }
-
+ defineSheet(
+ id,
+ options: Partial = {},
+ columns: { [mode: string]: string[] } = {}
+ ) {
Sheet[id] = {
id,
- columns,
+ columns: {},
supportedModes: Object.keys(columns),
icon: options.icon,
@@ -325,6 +316,18 @@ class WorkspaceStore extends MailspringStore {
Footer: { id: `Sheet:${id}:Footer` },
};
+ // Make sure all the locations have definitions so that packages
+ // can register things into these locations and their toolbars.
+ for (const [mode, cols] of Object.entries(columns)) {
+ Sheet[id].columns[mode] = [];
+ for (const col of cols) {
+ if (Location[col] == null) {
+ Location[col] = { id: `${col}`, Toolbar: { id: `${col}:Toolbar` } };
+ }
+ Sheet[id].columns[mode].push(Location[col]);
+ }
+ }
+
if (options.root && !this.rootSheet() && !(options as any).silent) {
this._onSelectRootSheet(Sheet[id]);
}
diff --git a/app/src/flux/tasks/change-contactgroup-membership-task.ts b/app/src/flux/tasks/change-contactgroup-membership-task.ts
index d7cb2d86f..64a22f4d8 100644
--- a/app/src/flux/tasks/change-contactgroup-membership-task.ts
+++ b/app/src/flux/tasks/change-contactgroup-membership-task.ts
@@ -1,5 +1,5 @@
import { Task } from './task';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { Contact } from '../models/contact';
import { ContactGroup } from '../models/contact-group';
import { AttributeValues } from '../models/model';
@@ -9,7 +9,7 @@ export class ChangeContactGroupMembershipTask extends Task {
static attributes = {
...Task.attributes,
- group: Attributes.Object({
+ group: Attributes.Obj({
modelKey: 'group',
itemClass: ContactGroup,
}),
diff --git a/app/src/flux/tasks/change-folder-task.ts b/app/src/flux/tasks/change-folder-task.ts
index 5a6c205ab..328203488 100644
--- a/app/src/flux/tasks/change-folder-task.ts
+++ b/app/src/flux/tasks/change-folder-task.ts
@@ -1,5 +1,5 @@
import { ChangeMailTask } from './change-mail-task';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { Folder } from '../models/folder';
import { localized } from '../../intl';
import { Message } from '../models/Message';
@@ -21,11 +21,11 @@ export class ChangeFolderTask extends ChangeMailTask {
static attributes = {
...ChangeMailTask.attributes,
- previousFolder: Attributes.Object({
+ previousFolder: Attributes.Obj({
modelKey: 'previousFolder',
itemClass: Folder,
}),
- folder: Attributes.Object({
+ folder: Attributes.Obj({
modelKey: 'folder',
itemClass: Folder,
}),
diff --git a/app/src/flux/tasks/change-labels-task.ts b/app/src/flux/tasks/change-labels-task.ts
index 5903c3e3a..67d3258c0 100644
--- a/app/src/flux/tasks/change-labels-task.ts
+++ b/app/src/flux/tasks/change-labels-task.ts
@@ -1,6 +1,6 @@
import { Label } from '../models/label';
import { ChangeMailTask } from './change-mail-task';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { localized } from '../../intl';
import { AttributeValues } from '../models/model';
import { Thread } from '../models/thread';
diff --git a/app/src/flux/tasks/change-mail-task.ts b/app/src/flux/tasks/change-mail-task.ts
index 8abeb36e0..488da8a58 100644
--- a/app/src/flux/tasks/change-mail-task.ts
+++ b/app/src/flux/tasks/change-mail-task.ts
@@ -1,5 +1,5 @@
import { Task } from './task';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { Thread } from '../models/thread';
import { Message } from '../models/message';
import { AttributeValues } from '../models/model';
diff --git a/app/src/flux/tasks/change-role-mapping-task.ts b/app/src/flux/tasks/change-role-mapping-task.ts
index c0da6559f..b9c7babac 100644
--- a/app/src/flux/tasks/change-role-mapping-task.ts
+++ b/app/src/flux/tasks/change-role-mapping-task.ts
@@ -1,5 +1,5 @@
import { Task } from './task';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { localized } from '../../intl';
import { AttributeValues } from '../models/model';
diff --git a/app/src/flux/tasks/change-starred-task.ts b/app/src/flux/tasks/change-starred-task.ts
index 7b4599620..3ca829e7e 100644
--- a/app/src/flux/tasks/change-starred-task.ts
+++ b/app/src/flux/tasks/change-starred-task.ts
@@ -1,6 +1,6 @@
/* eslint no-unused-vars: 0*/
import _ from 'underscore';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { ChangeMailTask } from './change-mail-task';
import { localized } from '../../intl';
import { AttributeValues } from '../models/model';
diff --git a/app/src/flux/tasks/change-unread-task.ts b/app/src/flux/tasks/change-unread-task.ts
index 514475a5e..567c46485 100644
--- a/app/src/flux/tasks/change-unread-task.ts
+++ b/app/src/flux/tasks/change-unread-task.ts
@@ -1,6 +1,6 @@
/* eslint no-unused-vars: 0*/
import _ from 'underscore';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { ChangeMailTask } from './change-mail-task';
import { localized } from '../../intl';
import { AttributeValues } from '../models/model';
diff --git a/app/src/flux/tasks/destroy-category-task.ts b/app/src/flux/tasks/destroy-category-task.ts
index e45d4bd14..cdec56637 100644
--- a/app/src/flux/tasks/destroy-category-task.ts
+++ b/app/src/flux/tasks/destroy-category-task.ts
@@ -1,6 +1,6 @@
import utf7 from 'utf7';
import { Task } from './task';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { localized } from '../../intl';
import { AttributeValues } from '../models/model';
diff --git a/app/src/flux/tasks/destroy-contact-task.ts b/app/src/flux/tasks/destroy-contact-task.ts
index 2472831e4..4e4fd9d7c 100644
--- a/app/src/flux/tasks/destroy-contact-task.ts
+++ b/app/src/flux/tasks/destroy-contact-task.ts
@@ -1,5 +1,5 @@
import { Task } from './task';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { Contact } from '../models/contact';
import { AttributeValues } from '../models/model';
import { localized } from 'mailspring-exports';
diff --git a/app/src/flux/tasks/destroy-contactgroup-task.ts b/app/src/flux/tasks/destroy-contactgroup-task.ts
index 51fd5db67..8c613edc9 100644
--- a/app/src/flux/tasks/destroy-contactgroup-task.ts
+++ b/app/src/flux/tasks/destroy-contactgroup-task.ts
@@ -1,5 +1,5 @@
import { Task } from './task';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { ContactGroup } from '../models/contact-group';
import { AttributeValues } from '../models/model';
import { SyncbackContactGroupTask } from './syncback-contactgroup-task';
@@ -8,7 +8,7 @@ export class DestroyContactGroupTask extends Task {
static attributes = {
...Task.attributes,
- group: Attributes.Object({
+ group: Attributes.Obj({
modelKey: 'group',
itemClass: ContactGroup,
}),
diff --git a/app/src/flux/tasks/destroy-draft-task.ts b/app/src/flux/tasks/destroy-draft-task.ts
index b31977466..ec57e15ae 100644
--- a/app/src/flux/tasks/destroy-draft-task.ts
+++ b/app/src/flux/tasks/destroy-draft-task.ts
@@ -1,5 +1,5 @@
import { Task } from './task';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { localized } from '../../intl';
import { AttributeValues } from '../models/model';
diff --git a/app/src/flux/tasks/event-rsvp-task.ts b/app/src/flux/tasks/event-rsvp-task.ts
index 6d24fb649..05edd7beb 100644
--- a/app/src/flux/tasks/event-rsvp-task.ts
+++ b/app/src/flux/tasks/event-rsvp-task.ts
@@ -1,6 +1,6 @@
import { Task } from './task';
import { AttributeValues } from '../models/model';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import {
localized,
ICSParticipantStatus,
diff --git a/app/src/flux/tasks/expunge-all-in-folder-task.ts b/app/src/flux/tasks/expunge-all-in-folder-task.ts
index d9ee0ba49..7c39622a6 100644
--- a/app/src/flux/tasks/expunge-all-in-folder-task.ts
+++ b/app/src/flux/tasks/expunge-all-in-folder-task.ts
@@ -1,6 +1,6 @@
import { Task } from './task';
import { Folder } from '../models/folder';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { localized } from '../../intl';
import { AttributeValues } from '../models/model';
@@ -8,7 +8,7 @@ export class ExpungeAllInFolderTask extends Task {
static attributes = {
...Task.attributes,
- folder: Attributes.Object({
+ folder: Attributes.Obj({
modelKey: 'folder',
itemClass: Folder,
}),
diff --git a/app/src/flux/tasks/get-message-rfc2822-task.ts b/app/src/flux/tasks/get-message-rfc2822-task.ts
index bb3f10ab8..ba6ee485b 100644
--- a/app/src/flux/tasks/get-message-rfc2822-task.ts
+++ b/app/src/flux/tasks/get-message-rfc2822-task.ts
@@ -1,5 +1,5 @@
import { Task } from './task';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { AttributeValues } from '../models/model';
export class GetMessageRFC2822Task extends Task {
diff --git a/app/src/flux/tasks/send-draft-task.ts b/app/src/flux/tasks/send-draft-task.ts
index 2d6ff5347..9922a1530 100644
--- a/app/src/flux/tasks/send-draft-task.ts
+++ b/app/src/flux/tasks/send-draft-task.ts
@@ -2,7 +2,7 @@
import { AccountStore } from '../stores/account-store';
import { Task } from './task';
import * as Actions from '../actions';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { Message } from '../models/message';
import SoundRegistry from '../../registries/sound-registry';
import { Composer as ComposerExtensionRegistry } from '../../registries/extension-registry';
@@ -82,14 +82,14 @@ export class SendDraftTask extends Task {
static attributes = {
...Task.attributes,
- draft: Attributes.Object({
+ draft: Attributes.Obj({
modelKey: 'draft',
itemClass: Message,
}),
headerMessageId: Attributes.String({
modelKey: 'headerMessageId',
}),
- perRecipientBodies: Attributes.Object({
+ perRecipientBodies: Attributes.Obj({
modelKey: 'perRecipientBodies',
}),
diff --git a/app/src/flux/tasks/send-feature-usage-event-task.ts b/app/src/flux/tasks/send-feature-usage-event-task.ts
index 78cb45fc9..d3ac5041e 100644
--- a/app/src/flux/tasks/send-feature-usage-event-task.ts
+++ b/app/src/flux/tasks/send-feature-usage-event-task.ts
@@ -1,5 +1,5 @@
import { Task } from './task';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { AccountStore } from '../stores/account-store';
import { AttributeValues } from '../models/model';
diff --git a/app/src/flux/tasks/syncback-category-task.ts b/app/src/flux/tasks/syncback-category-task.ts
index 9cd2a9664..fc8676622 100644
--- a/app/src/flux/tasks/syncback-category-task.ts
+++ b/app/src/flux/tasks/syncback-category-task.ts
@@ -1,6 +1,6 @@
import utf7 from 'utf7';
import { Task } from './task';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { localized } from '../../intl';
import { AttributeValues } from '../models/model';
@@ -14,7 +14,7 @@ export class SyncbackCategoryTask extends Task {
existingPath: Attributes.String({
modelKey: 'existingPath',
}),
- created: Attributes.Object({
+ created: Attributes.Obj({
modelKey: 'created',
}),
};
diff --git a/app/src/flux/tasks/syncback-contact-task.ts b/app/src/flux/tasks/syncback-contact-task.ts
index 42003ab0f..e0f3e81e9 100644
--- a/app/src/flux/tasks/syncback-contact-task.ts
+++ b/app/src/flux/tasks/syncback-contact-task.ts
@@ -1,5 +1,5 @@
import { Task } from './task';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { Contact } from '../models/contact';
import { AttributeValues } from '../models/model';
@@ -7,7 +7,7 @@ export class SyncbackContactTask extends Task {
static attributes = {
...Task.attributes,
- contact: Attributes.Object({
+ contact: Attributes.Obj({
modelKey: 'contact',
itemClass: Contact,
}),
diff --git a/app/src/flux/tasks/syncback-contactgroup-task.ts b/app/src/flux/tasks/syncback-contactgroup-task.ts
index a98a748cc..841a3b334 100644
--- a/app/src/flux/tasks/syncback-contactgroup-task.ts
+++ b/app/src/flux/tasks/syncback-contactgroup-task.ts
@@ -1,5 +1,5 @@
import { Task } from './task';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { ContactGroup } from '../models/contact-group';
import { AttributeValues } from '../models/model';
@@ -7,7 +7,7 @@ export class SyncbackContactGroupTask extends Task {
static attributes = {
...Task.attributes,
- group: Attributes.Object({
+ group: Attributes.Obj({
modelKey: 'group',
itemClass: ContactGroup,
}),
diff --git a/app/src/flux/tasks/syncback-draft-task.ts b/app/src/flux/tasks/syncback-draft-task.ts
index 87684357b..43d1dee71 100644
--- a/app/src/flux/tasks/syncback-draft-task.ts
+++ b/app/src/flux/tasks/syncback-draft-task.ts
@@ -1,5 +1,5 @@
import { Task } from './task';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { Message } from '../models/message';
import { localized } from '../../intl';
import { AttributeValues } from '../models/model';
@@ -11,7 +11,7 @@ export class SyncbackDraftTask extends Task {
headerMessageId: Attributes.String({
modelKey: 'headerMessageId',
}),
- draft: Attributes.Object({
+ draft: Attributes.Obj({
modelKey: 'draft',
itemClass: Message,
}),
diff --git a/app/src/flux/tasks/syncback-metadata-task.ts b/app/src/flux/tasks/syncback-metadata-task.ts
index dab59b35b..6aa7da928 100644
--- a/app/src/flux/tasks/syncback-metadata-task.ts
+++ b/app/src/flux/tasks/syncback-metadata-task.ts
@@ -1,5 +1,5 @@
import { Task } from './task';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { Model, AttributeValues } from '../models/model';
export class SyncbackMetadataTask extends Task {
@@ -54,10 +54,10 @@ export class SyncbackMetadataTask extends Task {
modelHeaderMessageId: Attributes.String({
modelKey: 'modelHeaderMessageId',
}),
- value: Attributes.Object({
+ value: Attributes.Obj({
modelKey: 'value',
}),
- undoValue: Attributes.Object({
+ undoValue: Attributes.Obj({
modelKey: 'undoValue',
}),
};
diff --git a/app/src/flux/tasks/task.ts b/app/src/flux/tasks/task.ts
index c4a1fb901..efd9f47d1 100644
--- a/app/src/flux/tasks/task.ts
+++ b/app/src/flux/tasks/task.ts
@@ -1,9 +1,8 @@
/* eslint no-unused-vars: 0*/
import _ from 'underscore';
import { Model } from '../models/model';
-import Attributes from '../attributes';
+import * as Attributes from '../attributes';
import { generateTempId } from '../models/utils';
-import { PermanentErrorCodes } from '../mailspring-api-request';
const Status = {
Local: 'local', // means the task has NOT run the local phase yet
@@ -14,7 +13,7 @@ const Status = {
export class Task extends Model {
static Status = Status;
- static SubclassesUseModelTable = Task;
+ static SubclassesUseModelTable: typeof Model = Task;
static attributes = {
...Model.attributes,
@@ -26,7 +25,7 @@ export class Task extends Model {
source: Attributes.String({
modelKey: 'source',
}),
- error: Attributes.Object({
+ error: Attributes.Obj({
modelKey: 'error',
}),
};
diff --git a/app/src/global/mailspring-component-kit.d.ts b/app/src/global/mailspring-component-kit.d.ts
index 73fb9b7a7..3d6981c9a 100644
--- a/app/src/global/mailspring-component-kit.d.ts
+++ b/app/src/global/mailspring-component-kit.d.ts
@@ -42,7 +42,8 @@ export const DropdownMenu: typeof import('../components/dropdown-menu').default;
export const OutlineViewItem: typeof import('../components/outline-view-item').default;
export * from '../components/outline-view';
export const DateInput: typeof import('../components/date-input').default;
-export const DatePicker: typeof import('../components/date-picker').default;
+export * from '../components/mini-month-view';
+export * from '../components/date-picker';
export const TimePicker: typeof import('../components/time-picker').default;
export const Table: typeof import('../components/table/table').default;
export const TableRow: typeof import('../components/table/table').TableRow;
diff --git a/app/src/global/mailspring-component-kit.js b/app/src/global/mailspring-component-kit.js
index 7ed9067e7..0c7a76f3c 100644
--- a/app/src/global/mailspring-component-kit.js
+++ b/app/src/global/mailspring-component-kit.js
@@ -101,6 +101,7 @@ lazyLoad('DropdownMenu', 'dropdown-menu');
lazyLoad('OutlineViewItem', 'outline-view-item');
lazyLoad('OutlineView', 'outline-view');
lazyLoad('DateInput', 'date-input');
+lazyLoad('MiniMonthView', 'mini-month-view');
lazyLoad('DatePicker', 'date-picker');
lazyLoad('TimePicker', 'time-picker');
lazyLoad('Table', 'table/table');
diff --git a/app/src/global/mailspring-exports.d.ts b/app/src/global/mailspring-exports.d.ts
index 3aba002b3..f78bdc072 100644
--- a/app/src/global/mailspring-exports.d.ts
+++ b/app/src/global/mailspring-exports.d.ts
@@ -119,8 +119,7 @@ export const WorkspaceStore: WorkspaceStore;
export type MailRulesStore = typeof import('../flux/stores/mail-rules-store').default;
export const MailRulesStore: MailRulesStore;
export * from '../flux/stores/send-actions-store';
-export type FeatureUsageStore = typeof import('../flux/stores/feature-usage-store').default;
-export const FeatureUsageStore: FeatureUsageStore;
+export * from '../flux/stores/feature-usage-store';
export type ThreadCountsStore = typeof import('../flux/stores/thread-counts-store').default;
export const ThreadCountsStore: ThreadCountsStore;
export type AttachmentStore = typeof import('../flux/stores/attachment-store').default;
@@ -200,6 +199,7 @@ export type MessageUtils = typeof import('../flux/models/message-utils').default
export const MessageUtils: MessageUtils;
// Services
+export * from '../services/autolinker';
export type KeyManager = typeof import('../key-manager').default;
export const KeyManager: KeyManager;
export type SoundRegistry = typeof import('../registries/sound-registry').default;
diff --git a/app/src/global/mailspring-exports.js b/app/src/global/mailspring-exports.js
index f3d6c888c..89639f10b 100644
--- a/app/src/global/mailspring-exports.js
+++ b/app/src/global/mailspring-exports.js
@@ -194,6 +194,7 @@ lazyLoad(`MailRulesTemplates`, 'mail-rules-templates');
lazyLoad(`MailRulesProcessor`, 'mail-rules-processor');
lazyLoad(`MailboxPerspective`, 'mailbox-perspective');
lazyLoad(`NativeNotifications`, 'native-notifications');
+lazyLoad(`Autolink`, 'services/autolinker');
lazyLoad(`SanitizeTransformer`, 'services/sanitize-transformer');
lazyLoad(`QuotedHTMLTransformer`, 'services/quoted-html-transformer');
lazyLoad(`InlineStyleTransformer`, 'services/inline-style-transformer');
diff --git a/app/src/global/mailspring-observables.ts b/app/src/global/mailspring-observables.ts
index 44dafbbdb..ee088c9d2 100644
--- a/app/src/global/mailspring-observables.ts
+++ b/app/src/global/mailspring-observables.ts
@@ -8,6 +8,7 @@ import { Model } from '../flux/models/model';
import ModelQuery from '../flux/models/query';
import MailspringStore from 'mailspring-store';
import { Category } from '../flux/models/category';
+import { QueryResultSet } from '../flux/models/query-result-set';
interface ICategoryOperators {
sort(): Observable & ICategoryOperators;
@@ -44,7 +45,7 @@ const CategoryOperators = {
const CategoryObservables = {
forAllAccounts() {
const folders = Rx.Observable.fromQuery(DatabaseStore.findAll(Folder));
- const labels = Rx.Observable.fromQuery(DatabaseStore.findAll