@inboxsdk/core 0.2.28-alpha.3 → 0.2.28-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/inboxsdk.js +1 -1
- package/inboxsdk.js.map +1 -1
- package/package.json +1 -1
- package/src/build/exec.js +26 -0
- package/src/build/spawn.js +17 -0
- package/src/common/ajax.js +90 -0
- package/src/common/cachebust-url.js +10 -0
- package/src/common/censorHTMLstring.js +37 -0
- package/src/common/censorHTMLtree.js +28 -0
- package/src/common/censorJSONTree.js +15 -0
- package/src/common/connectivity-test.js +21 -0
- package/src/common/defer.js +11 -0
- package/src/common/find-parent.js +10 -0
- package/src/common/get-extension-id.js +7 -0
- package/src/common/get-or-fail.js +8 -0
- package/src/common/get-session-id.js +14 -0
- package/src/common/get-stack-trace.js +6 -0
- package/src/common/html-to-text.js +7 -0
- package/src/common/isElementVisible.js +6 -0
- package/src/common/load-script.js +127 -0
- package/src/common/log-error.js +191 -0
- package/src/common/quoted-split.js +18 -0
- package/src/common/rate-limit-queuer.js +57 -0
- package/src/common/rate-limit.js +14 -0
- package/src/common/version.js +6 -0
- package/src/inboxsdk-js/loading/platform-implementation-loader.js +27 -0
- package/src/injected-js/gmail/constants.js +2 -0
- package/src/injected-js/gmail/modify-suggestions.js +106 -0
- package/src/injected-js/gmail/setup-gmonkey-handler.js +49 -0
- package/src/injected-js/gmail/sync-compose-processor-20220909.js +298 -0
- package/src/injected-js/gmail/sync-compose-processor.js +55 -0
- package/src/injected-js/gmail/sync-compose-request-processor.js +110 -0
- package/src/injected-js/injected-logger.js +50 -0
- package/src/injected-js/setupCustomViewEventAssassin.js +90 -0
- package/src/platform-implementation-js/constants/nav-item-types.js +19 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-app-menu-item.js +21 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-collapsible-panel.js +28 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-nav-item.js +98 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-support-item.js +4 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/encodeDraftUrlId.js +8 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/get-rfc-message-id-for-gmail-message-id.js +12 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/get-rfc-message-id-for-gmail-thread-id.js +33 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/isIntegratedViewGmail.js +4 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/open-draft-by-message-id.js +44 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/track-gmail-styles.js +95 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-element-getter/get-main-content-element-changed-stream.js +38 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-element-getter/wait-for-gmail-mode-to-settle.js +12 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-element-getter.js +253 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-page-communicator.js +200 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-response-processor.js +533 -0
- package/src/platform-implementation-js/dom-driver/gmail/gmail-sync-response-processor.js +289 -0
- package/src/platform-implementation-js/dom-driver/gmail/is-compose-titlebar-light-color.js +21 -0
- package/src/platform-implementation-js/dom-driver/gmail/makePageParserTree.js +28 -0
- package/src/platform-implementation-js/dom-driver/gmail/pageParserOptions.js +19 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-app-menu-item-view.js +126 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/add-status-bar.js +101 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/detectClassicRecipientsArea.js +38 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/get-address-information-extractor.js +28 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/get-recipients.js +22 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/gmail-compose-button-view.js +233 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/page-parser.js +223 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/set-recipients.js +157 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/setupLinkPopovers.js +109 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-message-view/attachment-icon.js +5 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-nav-item-view.js +759 -0
- package/src/platform-implementation-js/dom-driver/gmail/views/gmail-support-item-view.js +84 -0
- package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/button-color-classes.js +33 -0
- package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/button-view.js +325 -0
- package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/create-accessory-button-view.js +58 -0
- package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/create-parent-accessory-button-view.js +55 -0
- package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/modal-button-view.js +96 -0
- package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/more-dropdown-button-view.js +30 -0
- package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/more-parent-accessory-button-view.js +10 -0
- package/src/platform-implementation-js/dom-driver/gmail/widgets/gmail-dropdown-view.js +22 -0
- package/src/platform-implementation-js/driver-common/getAccountUrlPart.js +17 -0
- package/src/platform-implementation-js/driver-common/getOriginalMessagePage.js +33 -0
- package/src/platform-implementation-js/driver-common/getSyncThreadsForSearch.js +49 -0
- package/src/platform-implementation-js/driver-common/gmailAjax.js +65 -0
- package/src/platform-implementation-js/driver-common/render-custom-icon.js +16 -0
- package/src/platform-implementation-js/driver-common/update-icon.js +90 -0
- package/src/platform-implementation-js/lib/ErrorCollector.js +47 -0
- package/src/platform-implementation-js/lib/Membrane.js +22 -0
- package/src/platform-implementation-js/lib/ScrollableContainByScreen.js +30 -0
- package/src/platform-implementation-js/lib/attemptWithRetries.js +16 -0
- package/src/platform-implementation-js/lib/common-page-communicator.js +137 -0
- package/src/platform-implementation-js/lib/delay-asap.js +12 -0
- package/src/platform-implementation-js/lib/delay-idle.js +16 -0
- package/src/platform-implementation-js/lib/dom/block-and-reemit-keyboard-events.js +36 -0
- package/src/platform-implementation-js/lib/dom/generic-button-view.js +39 -0
- package/src/platform-implementation-js/lib/dom/get-insert-before-element.js +39 -0
- package/src/platform-implementation-js/lib/dom/insert-element-in-order.js +38 -0
- package/src/platform-implementation-js/lib/dom/keyboard-shortcut-stream.js +12 -0
- package/src/platform-implementation-js/lib/dom/make-element-child-stream.js +79 -0
- package/src/platform-implementation-js/lib/dom/make-element-stream-merger.js +28 -0
- package/src/platform-implementation-js/lib/dom/make-element-view-stream.js +15 -0
- package/src/platform-implementation-js/lib/dom/make-mutation-observer-chunked-stream.js +15 -0
- package/src/platform-implementation-js/lib/dom/make-mutation-observer-stream.js +5 -0
- package/src/platform-implementation-js/lib/dom/outsideClicksAndEscape.js +39 -0
- package/src/platform-implementation-js/lib/dom/parentNodes.js +8 -0
- package/src/platform-implementation-js/lib/dom/querySelectorOrFail.js +6 -0
- package/src/platform-implementation-js/lib/dom/reemitClickEventForReact.js +22 -0
- package/src/platform-implementation-js/lib/dom/simulate-key.js +46 -0
- package/src/platform-implementation-js/lib/dom/simulate-mouse-event.js +16 -0
- package/src/platform-implementation-js/lib/dom/trigger-relay-event.js +7 -0
- package/src/platform-implementation-js/lib/extract-contact-from-email-contact-string.js +25 -0
- package/src/platform-implementation-js/lib/fake-window-resize.js +5 -0
- package/src/platform-implementation-js/lib/from-event-target-capture.js +12 -0
- package/src/platform-implementation-js/lib/fromEventsWithOptions.js +10 -0
- package/src/platform-implementation-js/lib/handler-registry.js +67 -0
- package/src/platform-implementation-js/lib/idMap.js +45 -0
- package/src/platform-implementation-js/lib/imageRequest.js +8 -0
- package/src/platform-implementation-js/lib/incrementName.js +7 -0
- package/src/platform-implementation-js/lib/inject-script-EMBEDDED.js +14 -0
- package/src/platform-implementation-js/lib/inject-script.js +33 -0
- package/src/platform-implementation-js/lib/is-valid-app-id.js +27 -0
- package/src/platform-implementation-js/lib/is-valid-email.js +14 -0
- package/src/platform-implementation-js/lib/isNotNil.js +5 -0
- package/src/platform-implementation-js/lib/isStreakAppId.js +11 -0
- package/src/platform-implementation-js/lib/logger.js +441 -0
- package/src/platform-implementation-js/lib/persistent-queue.js +107 -0
- package/src/platform-implementation-js/lib/safe-event-emitter.js +17 -0
- package/src/platform-implementation-js/lib/stopper-pool.js +41 -0
- package/src/platform-implementation-js/lib/stream-wait-for.js +30 -0
- package/src/platform-implementation-js/lib/wait-for.js +28 -0
- package/src/platform-implementation-js/lib/waitForAsync.js +19 -0
- package/src/platform-implementation-js/namespaces/app-menu.d.ts.map +1 -1
- package/src/platform-implementation-js/namespaces/app-menu.js +31 -0
- package/src/platform-implementation-js/namespaces/butter-bar.js +163 -0
- package/src/platform-implementation-js/views/SimpleElementView.js +16 -0
- package/src/platform-implementation-js/views/app-menu-item-view.js +257 -0
- package/src/platform-implementation-js/views/collapsible-panel-view.js +222 -0
- package/src/platform-implementation-js/views/compose-button-view.js +45 -0
- package/src/platform-implementation-js/views/compose-view.js +312 -0
- package/src/platform-implementation-js/views/keyboard-shortcut-handle.js +14 -0
- package/src/platform-implementation-js/widgets/buttons/basic-button-view-controller.js +39 -0
- package/src/platform-implementation-js/widgets/buttons/dropdown-button-view-controller.js +98 -0
- package/src/platform-implementation-js/widgets/buttons/dropdown-view.js +122 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Use insert-element-in-order instead of this!
|
|
2
|
+
export default function getInsertBeforeElement(checkElement, childElements, dataAttributes) {
|
|
3
|
+
const checkValues = {};
|
|
4
|
+
dataAttributes.forEach(function (attribute) {
|
|
5
|
+
const stringValue = checkElement.getAttribute(attribute);
|
|
6
|
+
checkValues[attribute] =
|
|
7
|
+
stringValue == null ? null : parseIfNumber(stringValue) ?? stringValue;
|
|
8
|
+
});
|
|
9
|
+
for (let ii = 0; ii < childElements.length; ii++) {
|
|
10
|
+
const child = childElements[ii];
|
|
11
|
+
if (_isChildAfter(checkValues, child, dataAttributes)) {
|
|
12
|
+
return child;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
function _isChildAfter(checkValues, child, dataAttributes) {
|
|
18
|
+
for (let ii = 0; ii < dataAttributes.length; ii++) {
|
|
19
|
+
const attribute = dataAttributes[ii];
|
|
20
|
+
const stringValue = child.getAttribute(attribute);
|
|
21
|
+
const value = stringValue == null ? null : parseIfNumber(stringValue) ?? stringValue;
|
|
22
|
+
if (value > checkValues[attribute]) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
else if (value < checkValues[attribute]) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
function parseIfNumber(value) {
|
|
32
|
+
const n = parseFloat(value);
|
|
33
|
+
if (!isNaN(n) && isFinite(n)) {
|
|
34
|
+
return n;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import t from 'transducers.js';
|
|
2
|
+
const DEFAULT_ORDER_ATTRS = [
|
|
3
|
+
'data-group-order-hint',
|
|
4
|
+
'data-order-hint',
|
|
5
|
+
'data-insertion-order-hint',
|
|
6
|
+
];
|
|
7
|
+
export default function insertElementInOrder(container, el, orderAttrs = DEFAULT_ORDER_ATTRS, insertBeforeNonSdk = false) {
|
|
8
|
+
// get the first element with higher order hints
|
|
9
|
+
const insertBeforeElement = t.toArray(Array.prototype.slice.call(container.children), t.compose(t.filter((cel) => {
|
|
10
|
+
let hadAnyOrderAttrs = false;
|
|
11
|
+
for (const name of orderAttrs) {
|
|
12
|
+
const attr = el.getAttribute(name);
|
|
13
|
+
const cattr = cel.getAttribute(name);
|
|
14
|
+
if (attr == null || cattr == null)
|
|
15
|
+
continue;
|
|
16
|
+
hadAnyOrderAttrs = true;
|
|
17
|
+
let comparison;
|
|
18
|
+
if (!isNaN(parseFloat(attr)) && !isNaN(parseFloat(cattr))) {
|
|
19
|
+
comparison = parseFloat(cattr) - parseFloat(attr);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
comparison = cattr.localeCompare(attr);
|
|
23
|
+
}
|
|
24
|
+
if (comparison > 0)
|
|
25
|
+
return true;
|
|
26
|
+
if (comparison < 0)
|
|
27
|
+
return false;
|
|
28
|
+
// If the elements had equal hint values, then check the next hint.
|
|
29
|
+
}
|
|
30
|
+
if (insertBeforeNonSdk) {
|
|
31
|
+
return !hadAnyOrderAttrs;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}), t.take(1)))[0];
|
|
37
|
+
container.insertBefore(el, insertBeforeElement);
|
|
38
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as Kefir from 'kefir';
|
|
2
|
+
import Combokeys from 'combokeys-capture';
|
|
3
|
+
const combokeys = typeof document !== 'undefined' && new Combokeys(document.body, true);
|
|
4
|
+
export default function keyboardShortcutStream(chord) {
|
|
5
|
+
return Kefir.stream((emitter) => {
|
|
6
|
+
return (combokeys &&
|
|
7
|
+
combokeys.bind(chord, function (domEvent) {
|
|
8
|
+
emitter.emit(domEvent);
|
|
9
|
+
return false;
|
|
10
|
+
}, 'keydown'));
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import asap from 'asap';
|
|
2
|
+
import * as Kefir from 'kefir';
|
|
3
|
+
import kefirStopper from 'kefir-stopper';
|
|
4
|
+
// Emits events whenever the given element has any children added or removed.
|
|
5
|
+
// Also when first listened to, it emits events for existing children.
|
|
6
|
+
export default function makeElementChildStream(element) {
|
|
7
|
+
if (!element || !element.nodeType) {
|
|
8
|
+
throw new Error('Expected element, got ' + String(element));
|
|
9
|
+
}
|
|
10
|
+
return Kefir.stream((emitter) => {
|
|
11
|
+
const removalStreams = new Map();
|
|
12
|
+
let ended = false;
|
|
13
|
+
function newEl(el) {
|
|
14
|
+
if (el.nodeType !== 1)
|
|
15
|
+
return;
|
|
16
|
+
if (removalStreams.has(el)) {
|
|
17
|
+
throwLater(new Error('Already had removalStream for element with class ' + el.className));
|
|
18
|
+
}
|
|
19
|
+
const removalStream = kefirStopper();
|
|
20
|
+
removalStreams.set(el, removalStream);
|
|
21
|
+
try {
|
|
22
|
+
emitter.emit({ el, removalStream });
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
throwLater(err);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function removedEl(el) {
|
|
29
|
+
if (el.nodeType !== 1)
|
|
30
|
+
return;
|
|
31
|
+
const removalStream = removalStreams.get(el);
|
|
32
|
+
removalStreams.delete(el);
|
|
33
|
+
if (removalStream) {
|
|
34
|
+
try {
|
|
35
|
+
removalStream.destroy();
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
throwLater(err);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
throwLater(new Error('Could not find removalStream for element with class ' +
|
|
43
|
+
el.className));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const observer = new MutationObserver(function (mutations) {
|
|
47
|
+
mutations.forEach((mutation) => {
|
|
48
|
+
Array.prototype.forEach.call(mutation.addedNodes, newEl);
|
|
49
|
+
Array.prototype.forEach.call(mutation.removedNodes, removedEl);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
// We don't want to emit the start children synchronously before all
|
|
53
|
+
// stream listeners are subscribed.
|
|
54
|
+
asap(() => {
|
|
55
|
+
if (!ended) {
|
|
56
|
+
observer.observe(element, { childList: true });
|
|
57
|
+
// Clone child list first because it can change
|
|
58
|
+
Array.prototype.slice.call(element.children).forEach(newEl);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
return function () {
|
|
62
|
+
ended = true;
|
|
63
|
+
observer.disconnect();
|
|
64
|
+
asap(() => {
|
|
65
|
+
removalStreams.forEach((removalStream) => {
|
|
66
|
+
removalStream.destroy();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// Throw error at a later time where our error-logger can pick it up. This
|
|
73
|
+
// avoids having this module depend on logger.js which we can't import because
|
|
74
|
+
// this module is used in the injected script too.
|
|
75
|
+
function throwLater(err) {
|
|
76
|
+
setTimeout(() => {
|
|
77
|
+
throw err;
|
|
78
|
+
}, 1);
|
|
79
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as Kefir from 'kefir';
|
|
2
|
+
import StopperPool from '../stopper-pool';
|
|
3
|
+
import delayAsap from '../delay-asap';
|
|
4
|
+
export default function makeElementStreamMerger() {
|
|
5
|
+
const knownElementStopperPools = new Map();
|
|
6
|
+
return (event) => {
|
|
7
|
+
let stopperPool = knownElementStopperPools.get(event.el);
|
|
8
|
+
if (stopperPool) {
|
|
9
|
+
if (stopperPool.getSize() > 1) {
|
|
10
|
+
// eslint-disable-next-line no-console
|
|
11
|
+
console.warn('element is part of multiple element streams', stopperPool.getSize(), event.el);
|
|
12
|
+
}
|
|
13
|
+
stopperPool.add(event.removalStream.flatMap(delayAsap));
|
|
14
|
+
return Kefir.never();
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
stopperPool = new StopperPool(event.removalStream.flatMap(delayAsap));
|
|
18
|
+
stopperPool.stream.onValue(function () {
|
|
19
|
+
knownElementStopperPools.delete(event.el);
|
|
20
|
+
});
|
|
21
|
+
knownElementStopperPools.set(event.el, stopperPool);
|
|
22
|
+
return Kefir.constant({
|
|
23
|
+
el: event.el,
|
|
24
|
+
removalStream: stopperPool.stream,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as Kefir from 'kefir';
|
|
2
|
+
export default function makeElementViewStream(viewFn) {
|
|
3
|
+
return (event) => {
|
|
4
|
+
const view = viewFn(event.el);
|
|
5
|
+
if (view) {
|
|
6
|
+
event.removalStream.take(1).onValue(() => {
|
|
7
|
+
view.destroy();
|
|
8
|
+
});
|
|
9
|
+
return Kefir.constant(view);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
return Kefir.never();
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as Kefir from 'kefir';
|
|
2
|
+
// Creates a mutation observer watching the given element and emits the events in a stream.
|
|
3
|
+
export default function makeMutationObserverChunkedStream(element, options) {
|
|
4
|
+
return Kefir.stream((emitter) => {
|
|
5
|
+
const observer = new MutationObserver((mutations) => {
|
|
6
|
+
// Work around Safari bug where sometimes mutations is an instance of a
|
|
7
|
+
// different context's Array.
|
|
8
|
+
emitter.emit(mutations instanceof Array ? mutations : Array.from(mutations));
|
|
9
|
+
});
|
|
10
|
+
observer.observe(element, options);
|
|
11
|
+
return () => {
|
|
12
|
+
observer.disconnect();
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import makeMutationObserverChunkedStream from './make-mutation-observer-chunked-stream';
|
|
2
|
+
// Creates a mutation observer watching the given element and emits the events in a stream.
|
|
3
|
+
export default function makeMutationObserverStream(element, options) {
|
|
4
|
+
return makeMutationObserverChunkedStream(element, options).flatten();
|
|
5
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as Kefir from 'kefir';
|
|
2
|
+
import fromEventTargetCapture from '../from-event-target-capture';
|
|
3
|
+
import parentNodes from './parentNodes';
|
|
4
|
+
export default function outsideClicksAndEscape(elements) {
|
|
5
|
+
return Kefir.merge([
|
|
6
|
+
fromEventTargetCapture(document, 'click'),
|
|
7
|
+
// We modify the focus event on document sometimes, so we listen for
|
|
8
|
+
// it on body so our modifications can happen first.
|
|
9
|
+
fromEventTargetCapture(document.body, 'focus'),
|
|
10
|
+
])
|
|
11
|
+
.filter((event) => !event.shouldIgnore &&
|
|
12
|
+
(event.isTrusted ||
|
|
13
|
+
(process.env.NODE_ENV === 'test' && event.__testAllow)) &&
|
|
14
|
+
elements.every((el) => {
|
|
15
|
+
for (const node of parentNodes(event.target)) {
|
|
16
|
+
if (node === el)
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
}))
|
|
21
|
+
.map((event) => ({ type: 'outsideInteraction', cause: event }))
|
|
22
|
+
.merge(Kefir.fromEvents(document, 'keydown')
|
|
23
|
+
.filter((e) => (e.key ? e.key === 'Escape' : e.which === 27))
|
|
24
|
+
.map((event) => {
|
|
25
|
+
event.preventDefault();
|
|
26
|
+
event.stopPropagation();
|
|
27
|
+
return { type: 'escape', cause: event };
|
|
28
|
+
}))
|
|
29
|
+
.map((e) => {
|
|
30
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
31
|
+
const allElsStillInPage = elements.every((el) => document.contains(el));
|
|
32
|
+
if (!allElsStillInPage) {
|
|
33
|
+
// eslint-disable-next-line no-console
|
|
34
|
+
console.error('outsideClicksAndEscape not unsubscribed from when elements were removed from the page');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return e;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export default function reemitClickEvent(event) {
|
|
2
|
+
// Inbox is going to block this event from bubbling to the body. We
|
|
3
|
+
// want to manually dispatch an event that looks like it so that if
|
|
4
|
+
// the extension is using React (which listens to events on the body)
|
|
5
|
+
// then React will see it.
|
|
6
|
+
const fakeEvent = new MouseEvent('click');
|
|
7
|
+
Object.defineProperties(fakeEvent, {
|
|
8
|
+
target: { value: event.target },
|
|
9
|
+
detail: { value: event.detail },
|
|
10
|
+
screenX: { value: event.screenX },
|
|
11
|
+
screenY: { value: event.screenY },
|
|
12
|
+
clientX: { value: event.clientX },
|
|
13
|
+
clientY: { value: event.clientY },
|
|
14
|
+
button: { value: event.button },
|
|
15
|
+
buttons: { value: event.buttons },
|
|
16
|
+
ctrlKey: { value: event.ctrlKey },
|
|
17
|
+
shiftKey: { value: event.shiftKey },
|
|
18
|
+
altKey: { value: event.altKey },
|
|
19
|
+
metaKey: { value: event.metaKey },
|
|
20
|
+
});
|
|
21
|
+
document.dispatchEvent(fakeEvent);
|
|
22
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import triggerRelayEvent from './trigger-relay-event';
|
|
2
|
+
export default function simulateKey(element, keyCode, charCode) {
|
|
3
|
+
const ctrlKey = false;
|
|
4
|
+
const metaKey = false;
|
|
5
|
+
const altKey = false;
|
|
6
|
+
const shiftKey = false;
|
|
7
|
+
triggerRelayEvent(element, {
|
|
8
|
+
type: 'keydown',
|
|
9
|
+
bubbles: true,
|
|
10
|
+
cancelable: true,
|
|
11
|
+
props: {
|
|
12
|
+
keyCode,
|
|
13
|
+
charCode,
|
|
14
|
+
ctrlKey,
|
|
15
|
+
metaKey,
|
|
16
|
+
altKey,
|
|
17
|
+
shiftKey,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
triggerRelayEvent(element, {
|
|
21
|
+
type: 'keypress',
|
|
22
|
+
bubbles: true,
|
|
23
|
+
cancelable: true,
|
|
24
|
+
props: {
|
|
25
|
+
keyCode,
|
|
26
|
+
charCode,
|
|
27
|
+
ctrlKey,
|
|
28
|
+
metaKey,
|
|
29
|
+
altKey,
|
|
30
|
+
shiftKey,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
triggerRelayEvent(element, {
|
|
34
|
+
type: 'keyup',
|
|
35
|
+
bubbles: true,
|
|
36
|
+
cancelable: true,
|
|
37
|
+
props: {
|
|
38
|
+
keyCode,
|
|
39
|
+
charCode,
|
|
40
|
+
ctrlKey,
|
|
41
|
+
metaKey,
|
|
42
|
+
altKey,
|
|
43
|
+
shiftKey,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export default function simulateMouseEvent(element, mouseEventName) {
|
|
2
|
+
const event = document.createEvent('MouseEvents');
|
|
3
|
+
event.initMouseEvent(mouseEventName, true, true, window, 0, element.offsetLeft, element.offsetTop, 0, 0, false, false, false, false, 0, null);
|
|
4
|
+
element.dispatchEvent(event);
|
|
5
|
+
}
|
|
6
|
+
export function simulateHover(element) {
|
|
7
|
+
simulateMouseEvent(element, 'mouseover');
|
|
8
|
+
}
|
|
9
|
+
export function simulateClick(element) {
|
|
10
|
+
simulateMouseEvent(element, 'mousedown');
|
|
11
|
+
simulateMouseEvent(element, 'mouseup');
|
|
12
|
+
simulateMouseEvent(element, 'click');
|
|
13
|
+
simulateMouseEvent(element, 'mouseleave');
|
|
14
|
+
simulateMouseEvent(element, 'mouseout');
|
|
15
|
+
element.blur();
|
|
16
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import isValidEmail from './is-valid-email';
|
|
2
|
+
export default function extractContactFromEmailContactString(contactInfoString) {
|
|
3
|
+
let name = null;
|
|
4
|
+
let emailAddress = null;
|
|
5
|
+
const contactInfoParts = contactInfoString.split('<');
|
|
6
|
+
const firstPartTrimmed = contactInfoParts[0].replace(/\u202c/g, '').trim();
|
|
7
|
+
if (contactInfoParts.length > 1) {
|
|
8
|
+
name = firstPartTrimmed;
|
|
9
|
+
emailAddress = contactInfoParts[1]
|
|
10
|
+
.split('>')[0]
|
|
11
|
+
.replace(/\u202c/g, '')
|
|
12
|
+
.trim();
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
if (isValidEmail(firstPartTrimmed)) {
|
|
16
|
+
emailAddress = firstPartTrimmed;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
throw Object.assign(new Error('Invalid email address'), {
|
|
20
|
+
firstPartTrimmed,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return { name, emailAddress };
|
|
25
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as Kefir from 'kefir';
|
|
2
|
+
export default function fromEventTargetCapture(target, eventName) {
|
|
3
|
+
return Kefir.stream((emitter) => {
|
|
4
|
+
function sink(event) {
|
|
5
|
+
emitter.value(event);
|
|
6
|
+
}
|
|
7
|
+
target.addEventListener(eventName, sink, true);
|
|
8
|
+
return () => {
|
|
9
|
+
target.removeEventListener(eventName, sink, true);
|
|
10
|
+
};
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as Kefir from 'kefir';
|
|
2
|
+
import { addEventListener, removeEventListener, } from 'event-listener-with-options';
|
|
3
|
+
export default function fromEventsWithOptions(target, eventName, options) {
|
|
4
|
+
return Kefir.stream((emitter) => {
|
|
5
|
+
addEventListener(target, eventName, emitter.emit, options);
|
|
6
|
+
return () => {
|
|
7
|
+
removeEventListener(target, eventName, emitter.emit, options);
|
|
8
|
+
};
|
|
9
|
+
});
|
|
10
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import remove from 'lodash/remove';
|
|
2
|
+
import asap from 'asap';
|
|
3
|
+
import Logger from './logger';
|
|
4
|
+
export default class HandlerRegistry {
|
|
5
|
+
_targets = [];
|
|
6
|
+
_pendingHandlers = [];
|
|
7
|
+
_handlers = [];
|
|
8
|
+
registerHandler(handler) {
|
|
9
|
+
if (this._pendingHandlers.indexOf(handler) === -1) {
|
|
10
|
+
this._pendingHandlers.push(handler);
|
|
11
|
+
asap(() => {
|
|
12
|
+
const pendingHandlers = this._pendingHandlers.slice();
|
|
13
|
+
for (let ii = pendingHandlers.length - 1; ii >= 0; ii--) {
|
|
14
|
+
const handler = pendingHandlers[ii];
|
|
15
|
+
if (this._handlers.indexOf(handler) === -1) {
|
|
16
|
+
this._handlers.push(handler);
|
|
17
|
+
this._informHandlerOfTargets(handler);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
this._pendingHandlers = [];
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return () => {
|
|
24
|
+
remove(this._pendingHandlers, (h) => h === handler);
|
|
25
|
+
remove(this._handlers, (h) => h === handler);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
addTarget(target) {
|
|
29
|
+
this._targets.push(target);
|
|
30
|
+
// TODO should we error if an object without an .on method is added?
|
|
31
|
+
if (target &&
|
|
32
|
+
typeof target === 'object' &&
|
|
33
|
+
typeof target.on === 'function') {
|
|
34
|
+
target.on('destroy', () => {
|
|
35
|
+
this.removeTarget(target);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
this._informHandlersOfTarget(target);
|
|
39
|
+
}
|
|
40
|
+
removeTarget(target) {
|
|
41
|
+
remove(this._targets, (t) => t === target);
|
|
42
|
+
}
|
|
43
|
+
dumpHandlers() {
|
|
44
|
+
this._pendingHandlers = [];
|
|
45
|
+
this._handlers = [];
|
|
46
|
+
}
|
|
47
|
+
_informHandlerOfTargets(handler) {
|
|
48
|
+
const targets = this._targets.slice();
|
|
49
|
+
for (let ii = 0; ii < targets.length; ii++) {
|
|
50
|
+
_tryCatch(handler, targets[ii]);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
_informHandlersOfTarget(target) {
|
|
54
|
+
const handlers = this._handlers.slice();
|
|
55
|
+
for (let ii = 0; ii < handlers.length; ii++) {
|
|
56
|
+
_tryCatch(handlers[ii], target);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function _tryCatch(fn, arg) {
|
|
61
|
+
try {
|
|
62
|
+
fn(arg);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
Logger.error(err);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import Sha256 from 'sha.js/sha256';
|
|
2
|
+
const cache = new Map();
|
|
3
|
+
let seed = null;
|
|
4
|
+
let useDevMode = false;
|
|
5
|
+
// Pass a name and you'll get a unique identifier associated with that name.
|
|
6
|
+
// The returned identifier will only contain alphabetic characters.
|
|
7
|
+
export default function idMap(name) {
|
|
8
|
+
const id = cache.get(name);
|
|
9
|
+
if (id != null)
|
|
10
|
+
return id;
|
|
11
|
+
if (seed == null) {
|
|
12
|
+
seed = document.documentElement.getAttribute('data-map-id');
|
|
13
|
+
if (seed == null) {
|
|
14
|
+
// Make the seed change every hour: the seed is based on a hash of the
|
|
15
|
+
// current timestamp in hour resolution.
|
|
16
|
+
const hasher = new Sha256();
|
|
17
|
+
hasher.update('PWVe' + Math.floor(Date.now() / (1000 * 60 * 60)) + 'PYiE0');
|
|
18
|
+
seed =
|
|
19
|
+
hasher.digest('hex').slice(0, 16) +
|
|
20
|
+
(process.env.NODE_ENV === 'development' ? 'x' : '');
|
|
21
|
+
document.documentElement.setAttribute('data-map-id', seed);
|
|
22
|
+
}
|
|
23
|
+
useDevMode = /x$/.test(seed);
|
|
24
|
+
}
|
|
25
|
+
let newId;
|
|
26
|
+
if (useDevMode) {
|
|
27
|
+
const n = String(seed.charCodeAt(0) % 10);
|
|
28
|
+
newId = `idm${n}_${name}`;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
const hasher = new Sha256();
|
|
32
|
+
hasher.update('4iYi29W' + name + ':' + seed + 'jn2mPvTG');
|
|
33
|
+
newId = hasher
|
|
34
|
+
.digest('hex')
|
|
35
|
+
.slice(0, 16)
|
|
36
|
+
.replace(/[0-9]/g, (match) => String.fromCharCode('A'.charCodeAt(0) + Number(match)));
|
|
37
|
+
}
|
|
38
|
+
cache.set(name, newId);
|
|
39
|
+
return newId;
|
|
40
|
+
}
|
|
41
|
+
// Only exposed for tests!
|
|
42
|
+
export function _reset() {
|
|
43
|
+
cache.clear();
|
|
44
|
+
seed = null;
|
|
45
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
export function injectScriptEmbedded() {
|
|
4
|
+
const url = 'https://www.inboxsdk.com/build/pageWorld.js';
|
|
5
|
+
const script = document.createElement('script');
|
|
6
|
+
script.type = 'text/javascript';
|
|
7
|
+
const originalCode = fs.readFileSync(__dirname + '/../../../dist/pageWorld.js', 'utf8');
|
|
8
|
+
const codeParts = [];
|
|
9
|
+
codeParts.push(originalCode);
|
|
10
|
+
codeParts.push('\n//# sourceURL=' + url + '\n');
|
|
11
|
+
const codeToRun = codeParts.join('');
|
|
12
|
+
script.text = codeToRun;
|
|
13
|
+
document.head.appendChild(script).parentNode.removeChild(script);
|
|
14
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import once from 'lodash/once';
|
|
2
|
+
import Kefir from 'kefir';
|
|
3
|
+
import makeMutationObserverChunkedStream from './dom/make-mutation-observer-chunked-stream';
|
|
4
|
+
let injectScriptImplementation = () => {
|
|
5
|
+
window.chrome.runtime.sendMessage({ type: 'inboxsdk__injectPageWorld' }, (didExecute) => {
|
|
6
|
+
if (!didExecute) {
|
|
7
|
+
// MV2 support
|
|
8
|
+
const scr = document.createElement('script');
|
|
9
|
+
scr.type = 'text/javascript';
|
|
10
|
+
scr.src = window.chrome.runtime.getURL('pageWorld.js');
|
|
11
|
+
document.documentElement.appendChild(scr);
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
// Returns a promise that resolves once the injected script has been injected
|
|
16
|
+
// and has done its initial load stuff.
|
|
17
|
+
export const injectScript = once(() => {
|
|
18
|
+
if (!document.head.hasAttribute('data-inboxsdk-script-injected')) {
|
|
19
|
+
document.head.setAttribute('data-inboxsdk-script-injected', 'true');
|
|
20
|
+
injectScriptImplementation();
|
|
21
|
+
}
|
|
22
|
+
return Kefir.later(0, null)
|
|
23
|
+
.merge(makeMutationObserverChunkedStream(document.head, {
|
|
24
|
+
attributes: true,
|
|
25
|
+
}))
|
|
26
|
+
.filter(() => document.head.hasAttribute('data-inboxsdk-user-email-address'))
|
|
27
|
+
.take(1)
|
|
28
|
+
.map(() => null)
|
|
29
|
+
.toPromise();
|
|
30
|
+
});
|
|
31
|
+
export function setInjectScriptImplementation(fn) {
|
|
32
|
+
injectScriptImplementation = fn;
|
|
33
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import Sha1 from 'sha.js/sha1';
|
|
2
|
+
function grandfatherHash(x) {
|
|
3
|
+
const hasher = new Sha1();
|
|
4
|
+
hasher.update('YlEHtGl72+c' + x);
|
|
5
|
+
return hasher.digest('hex');
|
|
6
|
+
}
|
|
7
|
+
const whitelist = new Set([
|
|
8
|
+
'b540a2819e4b26823288046869e5577852178d69',
|
|
9
|
+
'1e9084cb394369dcdfd43cfcd84d17b78b0903dd',
|
|
10
|
+
'a9b816b86b6085eb0b80315da6b2297ff30cf7b6',
|
|
11
|
+
'5140e277522fd95ece3d540c94b97f443bcc3256',
|
|
12
|
+
'b56a2d6e6f650ed6eae78774d3b1f02d969d8485',
|
|
13
|
+
'bc6308648a588258f31f8f4b64f7c4652b6fcaac',
|
|
14
|
+
'4cb8e6bd1aab0b4b05c7f07fbe8332b842009358', // godMode
|
|
15
|
+
]);
|
|
16
|
+
const appIdRegex = /^sdk_(.{5,15})_([0-9a-f]{10})$/;
|
|
17
|
+
export default function isValidAppId(appId) {
|
|
18
|
+
if (whitelist.has(grandfatherHash(appId)))
|
|
19
|
+
return true;
|
|
20
|
+
const m = appIdRegex.exec(appId);
|
|
21
|
+
if (!m)
|
|
22
|
+
return false;
|
|
23
|
+
const name = m[1], hash = m[2];
|
|
24
|
+
const shasum = new Sha1();
|
|
25
|
+
shasum.update(name);
|
|
26
|
+
return shasum.digest('hex').slice(0, 10) === hash;
|
|
27
|
+
}
|