@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.
Files changed (137) hide show
  1. package/inboxsdk.js +1 -1
  2. package/inboxsdk.js.map +1 -1
  3. package/package.json +1 -1
  4. package/src/build/exec.js +26 -0
  5. package/src/build/spawn.js +17 -0
  6. package/src/common/ajax.js +90 -0
  7. package/src/common/cachebust-url.js +10 -0
  8. package/src/common/censorHTMLstring.js +37 -0
  9. package/src/common/censorHTMLtree.js +28 -0
  10. package/src/common/censorJSONTree.js +15 -0
  11. package/src/common/connectivity-test.js +21 -0
  12. package/src/common/defer.js +11 -0
  13. package/src/common/find-parent.js +10 -0
  14. package/src/common/get-extension-id.js +7 -0
  15. package/src/common/get-or-fail.js +8 -0
  16. package/src/common/get-session-id.js +14 -0
  17. package/src/common/get-stack-trace.js +6 -0
  18. package/src/common/html-to-text.js +7 -0
  19. package/src/common/isElementVisible.js +6 -0
  20. package/src/common/load-script.js +127 -0
  21. package/src/common/log-error.js +191 -0
  22. package/src/common/quoted-split.js +18 -0
  23. package/src/common/rate-limit-queuer.js +57 -0
  24. package/src/common/rate-limit.js +14 -0
  25. package/src/common/version.js +6 -0
  26. package/src/inboxsdk-js/loading/platform-implementation-loader.js +27 -0
  27. package/src/injected-js/gmail/constants.js +2 -0
  28. package/src/injected-js/gmail/modify-suggestions.js +106 -0
  29. package/src/injected-js/gmail/setup-gmonkey-handler.js +49 -0
  30. package/src/injected-js/gmail/sync-compose-processor-20220909.js +298 -0
  31. package/src/injected-js/gmail/sync-compose-processor.js +55 -0
  32. package/src/injected-js/gmail/sync-compose-request-processor.js +110 -0
  33. package/src/injected-js/injected-logger.js +50 -0
  34. package/src/injected-js/setupCustomViewEventAssassin.js +90 -0
  35. package/src/platform-implementation-js/constants/nav-item-types.js +19 -0
  36. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-app-menu-item.js +21 -0
  37. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-collapsible-panel.js +28 -0
  38. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-nav-item.js +98 -0
  39. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/add-support-item.js +4 -0
  40. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/encodeDraftUrlId.js +8 -0
  41. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/get-rfc-message-id-for-gmail-message-id.js +12 -0
  42. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/get-rfc-message-id-for-gmail-thread-id.js +33 -0
  43. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/isIntegratedViewGmail.js +4 -0
  44. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/open-draft-by-message-id.js +44 -0
  45. package/src/platform-implementation-js/dom-driver/gmail/gmail-driver/track-gmail-styles.js +95 -0
  46. package/src/platform-implementation-js/dom-driver/gmail/gmail-element-getter/get-main-content-element-changed-stream.js +38 -0
  47. package/src/platform-implementation-js/dom-driver/gmail/gmail-element-getter/wait-for-gmail-mode-to-settle.js +12 -0
  48. package/src/platform-implementation-js/dom-driver/gmail/gmail-element-getter.js +253 -0
  49. package/src/platform-implementation-js/dom-driver/gmail/gmail-page-communicator.js +200 -0
  50. package/src/platform-implementation-js/dom-driver/gmail/gmail-response-processor.js +533 -0
  51. package/src/platform-implementation-js/dom-driver/gmail/gmail-sync-response-processor.js +289 -0
  52. package/src/platform-implementation-js/dom-driver/gmail/is-compose-titlebar-light-color.js +21 -0
  53. package/src/platform-implementation-js/dom-driver/gmail/makePageParserTree.js +28 -0
  54. package/src/platform-implementation-js/dom-driver/gmail/pageParserOptions.js +19 -0
  55. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-app-menu-item-view.js +126 -0
  56. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/add-status-bar.js +101 -0
  57. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/detectClassicRecipientsArea.js +38 -0
  58. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/get-address-information-extractor.js +28 -0
  59. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/get-recipients.js +22 -0
  60. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/gmail-compose-button-view.js +233 -0
  61. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/page-parser.js +223 -0
  62. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/set-recipients.js +157 -0
  63. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-compose-view/setupLinkPopovers.js +109 -0
  64. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-message-view/attachment-icon.js +5 -0
  65. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-nav-item-view.js +759 -0
  66. package/src/platform-implementation-js/dom-driver/gmail/views/gmail-support-item-view.js +84 -0
  67. package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/button-color-classes.js +33 -0
  68. package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/button-view.js +325 -0
  69. package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/create-accessory-button-view.js +58 -0
  70. package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/create-parent-accessory-button-view.js +55 -0
  71. package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/modal-button-view.js +96 -0
  72. package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/more-dropdown-button-view.js +30 -0
  73. package/src/platform-implementation-js/dom-driver/gmail/widgets/buttons/more-parent-accessory-button-view.js +10 -0
  74. package/src/platform-implementation-js/dom-driver/gmail/widgets/gmail-dropdown-view.js +22 -0
  75. package/src/platform-implementation-js/driver-common/getAccountUrlPart.js +17 -0
  76. package/src/platform-implementation-js/driver-common/getOriginalMessagePage.js +33 -0
  77. package/src/platform-implementation-js/driver-common/getSyncThreadsForSearch.js +49 -0
  78. package/src/platform-implementation-js/driver-common/gmailAjax.js +65 -0
  79. package/src/platform-implementation-js/driver-common/render-custom-icon.js +16 -0
  80. package/src/platform-implementation-js/driver-common/update-icon.js +90 -0
  81. package/src/platform-implementation-js/lib/ErrorCollector.js +47 -0
  82. package/src/platform-implementation-js/lib/Membrane.js +22 -0
  83. package/src/platform-implementation-js/lib/ScrollableContainByScreen.js +30 -0
  84. package/src/platform-implementation-js/lib/attemptWithRetries.js +16 -0
  85. package/src/platform-implementation-js/lib/common-page-communicator.js +137 -0
  86. package/src/platform-implementation-js/lib/delay-asap.js +12 -0
  87. package/src/platform-implementation-js/lib/delay-idle.js +16 -0
  88. package/src/platform-implementation-js/lib/dom/block-and-reemit-keyboard-events.js +36 -0
  89. package/src/platform-implementation-js/lib/dom/generic-button-view.js +39 -0
  90. package/src/platform-implementation-js/lib/dom/get-insert-before-element.js +39 -0
  91. package/src/platform-implementation-js/lib/dom/insert-element-in-order.js +38 -0
  92. package/src/platform-implementation-js/lib/dom/keyboard-shortcut-stream.js +12 -0
  93. package/src/platform-implementation-js/lib/dom/make-element-child-stream.js +79 -0
  94. package/src/platform-implementation-js/lib/dom/make-element-stream-merger.js +28 -0
  95. package/src/platform-implementation-js/lib/dom/make-element-view-stream.js +15 -0
  96. package/src/platform-implementation-js/lib/dom/make-mutation-observer-chunked-stream.js +15 -0
  97. package/src/platform-implementation-js/lib/dom/make-mutation-observer-stream.js +5 -0
  98. package/src/platform-implementation-js/lib/dom/outsideClicksAndEscape.js +39 -0
  99. package/src/platform-implementation-js/lib/dom/parentNodes.js +8 -0
  100. package/src/platform-implementation-js/lib/dom/querySelectorOrFail.js +6 -0
  101. package/src/platform-implementation-js/lib/dom/reemitClickEventForReact.js +22 -0
  102. package/src/platform-implementation-js/lib/dom/simulate-key.js +46 -0
  103. package/src/platform-implementation-js/lib/dom/simulate-mouse-event.js +16 -0
  104. package/src/platform-implementation-js/lib/dom/trigger-relay-event.js +7 -0
  105. package/src/platform-implementation-js/lib/extract-contact-from-email-contact-string.js +25 -0
  106. package/src/platform-implementation-js/lib/fake-window-resize.js +5 -0
  107. package/src/platform-implementation-js/lib/from-event-target-capture.js +12 -0
  108. package/src/platform-implementation-js/lib/fromEventsWithOptions.js +10 -0
  109. package/src/platform-implementation-js/lib/handler-registry.js +67 -0
  110. package/src/platform-implementation-js/lib/idMap.js +45 -0
  111. package/src/platform-implementation-js/lib/imageRequest.js +8 -0
  112. package/src/platform-implementation-js/lib/incrementName.js +7 -0
  113. package/src/platform-implementation-js/lib/inject-script-EMBEDDED.js +14 -0
  114. package/src/platform-implementation-js/lib/inject-script.js +33 -0
  115. package/src/platform-implementation-js/lib/is-valid-app-id.js +27 -0
  116. package/src/platform-implementation-js/lib/is-valid-email.js +14 -0
  117. package/src/platform-implementation-js/lib/isNotNil.js +5 -0
  118. package/src/platform-implementation-js/lib/isStreakAppId.js +11 -0
  119. package/src/platform-implementation-js/lib/logger.js +441 -0
  120. package/src/platform-implementation-js/lib/persistent-queue.js +107 -0
  121. package/src/platform-implementation-js/lib/safe-event-emitter.js +17 -0
  122. package/src/platform-implementation-js/lib/stopper-pool.js +41 -0
  123. package/src/platform-implementation-js/lib/stream-wait-for.js +30 -0
  124. package/src/platform-implementation-js/lib/wait-for.js +28 -0
  125. package/src/platform-implementation-js/lib/waitForAsync.js +19 -0
  126. package/src/platform-implementation-js/namespaces/app-menu.d.ts.map +1 -1
  127. package/src/platform-implementation-js/namespaces/app-menu.js +31 -0
  128. package/src/platform-implementation-js/namespaces/butter-bar.js +163 -0
  129. package/src/platform-implementation-js/views/SimpleElementView.js +16 -0
  130. package/src/platform-implementation-js/views/app-menu-item-view.js +257 -0
  131. package/src/platform-implementation-js/views/collapsible-panel-view.js +222 -0
  132. package/src/platform-implementation-js/views/compose-button-view.js +45 -0
  133. package/src/platform-implementation-js/views/compose-view.js +312 -0
  134. package/src/platform-implementation-js/views/keyboard-shortcut-handle.js +14 -0
  135. package/src/platform-implementation-js/widgets/buttons/basic-button-view-controller.js +39 -0
  136. package/src/platform-implementation-js/widgets/buttons/dropdown-button-view-controller.js +98 -0
  137. package/src/platform-implementation-js/widgets/buttons/dropdown-view.js +122 -0
@@ -0,0 +1,233 @@
1
+ import includes from 'lodash/includes';
2
+ import * as Kefir from 'kefir';
3
+ import kefirBus from 'kefir-bus';
4
+ import Logger from '../../../../lib/logger';
5
+ import { simulateHover } from '../../../../lib/dom/simulate-mouse-event';
6
+ import keyboardShortcutStream from '../../../../lib/dom/keyboard-shortcut-stream';
7
+ export default class GmailComposeButtonView {
8
+ _element = document.createElement('div');
9
+ _iconElement = document.createElement('div');
10
+ _iconImgElement;
11
+ _iconClass;
12
+ _iconUrl;
13
+ _tooltip;
14
+ _hasDropdown;
15
+ _isEnabled;
16
+ _keyboardShortcutHandle;
17
+ _eventStream;
18
+ constructor(options) {
19
+ this._isEnabled = options.enabled !== false;
20
+ this._iconClass = options.iconClass;
21
+ this._iconUrl = options.iconUrl;
22
+ this._tooltip = options.tooltip || options.title || options.text;
23
+ // TODO what is this used for?
24
+ this._hasDropdown = !!options.hasDropdown;
25
+ this._keyboardShortcutHandle = options.keyboardShortcutHandle;
26
+ this._createElement(options);
27
+ this._eventStream = kefirBus();
28
+ this._setupEventStream();
29
+ this._setupAestheticEvents();
30
+ if (options.enabled !== false) {
31
+ this._setupKeyboardShortcutEvent();
32
+ }
33
+ }
34
+ destroy() {
35
+ this._element.remove();
36
+ this._eventStream.end();
37
+ }
38
+ getElement() {
39
+ return this._element;
40
+ }
41
+ getEventStream() {
42
+ return this._eventStream;
43
+ }
44
+ activate() {
45
+ this.addClass('inboxsdk__composeButton_active');
46
+ }
47
+ deactivate() {
48
+ this.removeClass('inboxsdk__composeButton_active');
49
+ }
50
+ addClass(className) {
51
+ this._element.classList.add(className);
52
+ }
53
+ removeClass(className) {
54
+ this._element.classList.remove(className);
55
+ }
56
+ simulateHover() {
57
+ simulateHover(this._element);
58
+ }
59
+ setEnabled(value) {
60
+ this._setEnabled(value);
61
+ }
62
+ isEnabled() {
63
+ return this._isEnabled;
64
+ }
65
+ update(options) {
66
+ if (!options) {
67
+ this._element.style.display = 'none';
68
+ return;
69
+ }
70
+ else if (this._element.style.display === 'none') {
71
+ this._element.style.display = '';
72
+ }
73
+ const newTooltip = options.tooltip || options.title || options.text;
74
+ if (newTooltip != this._tooltip) {
75
+ this._updateTooltip(newTooltip);
76
+ }
77
+ if (options.iconUrl != this._iconUrl) {
78
+ this._updateIconUrl(options.iconUrl);
79
+ }
80
+ if (options.iconClass != this._iconClass) {
81
+ this._updateIconClass(options.iconClass);
82
+ }
83
+ if (options.enabled === false || options.enabled === true) {
84
+ this._setEnabled(options.enabled);
85
+ }
86
+ }
87
+ _createElement(options) {
88
+ this._createMainElement(options);
89
+ this._createIconElement();
90
+ }
91
+ _createMainElement(options) {
92
+ this._element.setAttribute('class', 'inboxsdk__composeButton');
93
+ if (options.tooltip) {
94
+ this._element.setAttribute('aria-label', options.tooltip);
95
+ }
96
+ this._element.setAttribute('role', 'button');
97
+ this._element.setAttribute('tabindex', '0');
98
+ const newTooltip = options.tooltip || options.title || options.text;
99
+ if (newTooltip) {
100
+ this._element.setAttribute('data-tooltip', newTooltip);
101
+ }
102
+ if (options.enabled === false) {
103
+ this._element.classList.add('inboxsdk__button_disabled');
104
+ }
105
+ if (options.noOverflow) {
106
+ this._element.setAttribute('data-no-overflow', '');
107
+ }
108
+ }
109
+ _createIconElement() {
110
+ const iconElement = this._iconElement;
111
+ if (this._iconClass) {
112
+ iconElement.innerHTML = ' ';
113
+ iconElement.setAttribute('class', this._iconClass);
114
+ }
115
+ iconElement.classList.add('inboxsdk__button_icon');
116
+ this._element.appendChild(iconElement);
117
+ if (this._iconUrl) {
118
+ this._createIconImgElement();
119
+ }
120
+ }
121
+ _createIconImgElement() {
122
+ this._iconElement.innerHTML = '';
123
+ const iconImgElement = (this._iconImgElement =
124
+ document.createElement('img'));
125
+ iconImgElement.classList.add('inboxsdk__button_iconImg');
126
+ if (this._iconUrl) {
127
+ iconImgElement.src = this._iconUrl;
128
+ }
129
+ else {
130
+ Logger.error(new Error('_createIconImgElement should not be called with null _iconUrl'));
131
+ }
132
+ this._iconElement.appendChild(iconImgElement);
133
+ }
134
+ _updateTooltip(newTooltip) {
135
+ this._tooltip = newTooltip;
136
+ if (newTooltip) {
137
+ this._element.setAttribute('data-tooltip', newTooltip);
138
+ }
139
+ else {
140
+ this._element.removeAttribute('data-tooltip');
141
+ }
142
+ }
143
+ _updateIconUrl(newIconUrl) {
144
+ this._iconUrl = newIconUrl;
145
+ if (!newIconUrl && this._iconImgElement) {
146
+ this._iconImgElement.remove();
147
+ this._iconImgElement = null;
148
+ }
149
+ else if (newIconUrl && !this._iconImgElement) {
150
+ this._createIconImgElement();
151
+ }
152
+ else if (newIconUrl && this._iconImgElement) {
153
+ this._iconImgElement.src = newIconUrl;
154
+ }
155
+ }
156
+ _updateIconClass(newIconClass) {
157
+ this._iconClass = newIconClass;
158
+ this._iconElement.setAttribute('class', 'inboxsdk__button_icon ' + (newIconClass || ''));
159
+ }
160
+ _setEnabled(value) {
161
+ if (this._isEnabled === value) {
162
+ return;
163
+ }
164
+ this._isEnabled = value;
165
+ if (this._isEnabled) {
166
+ this._element.classList.remove('inboxsdk__button_disabled');
167
+ }
168
+ else {
169
+ this._element.classList.add('inboxsdk__button_disabled');
170
+ }
171
+ this._eventStream.emit({
172
+ eventName: 'enabledChanged',
173
+ isEnabled: this._isEnabled,
174
+ });
175
+ if (this._isEnabled) {
176
+ this._setupKeyboardShortcutEvent();
177
+ }
178
+ }
179
+ _setupEventStream() {
180
+ const clickEventStream = Kefir.fromEvents(this._element, 'click');
181
+ clickEventStream.onValue(function (event) {
182
+ event.stopPropagation();
183
+ event.preventDefault();
184
+ });
185
+ this._eventStream.plug(clickEventStream
186
+ .filter(() => this.isEnabled())
187
+ .map(function (event) {
188
+ return {
189
+ eventName: 'click',
190
+ domEvent: event,
191
+ };
192
+ }));
193
+ const isEnterOrSpace = (event) => includes([32 /* space */, 13 /* enter */], event.which);
194
+ const keydownEventStream = Kefir.fromEvents(this._element, 'keydown').filter(() => this.isEnabled());
195
+ const enterEventStream = keydownEventStream.filter(isEnterOrSpace);
196
+ this._eventStream.plug(enterEventStream.map(function (event) {
197
+ return {
198
+ eventName: 'click',
199
+ domEvent: event,
200
+ };
201
+ }));
202
+ enterEventStream.onValue(function (event) {
203
+ event.stopPropagation();
204
+ event.preventDefault();
205
+ });
206
+ }
207
+ _setupKeyboardShortcutEvent() {
208
+ const keyboardShortcutHandle = this._keyboardShortcutHandle;
209
+ if (keyboardShortcutHandle) {
210
+ this._eventStream.plug(keyboardShortcutStream(keyboardShortcutHandle.chord)
211
+ .takeUntilBy(this._eventStream.filter(function (event) {
212
+ return (event.eventName === 'enabledChanged' &&
213
+ event.isEnabled === false);
214
+ }))
215
+ .map(function (domEvent) {
216
+ return {
217
+ eventName: 'click',
218
+ domEvent: domEvent,
219
+ };
220
+ }));
221
+ }
222
+ }
223
+ _setupAestheticEvents() {
224
+ Kefir.fromEvents(this._element, 'mouseenter')
225
+ .filter(() => this.isEnabled())
226
+ .onValue(() => {
227
+ this._element.classList.add('inboxsdk__button_hover');
228
+ });
229
+ Kefir.fromEvents(this._element, 'mouseleave').onValue(() => {
230
+ this._element.classList.remove('inboxsdk__button_hover');
231
+ });
232
+ }
233
+ }
@@ -0,0 +1,223 @@
1
+ import closest from 'closest-ng';
2
+ import PageParserTree from 'page-parser-tree';
3
+ import censorHTMLtree from '../../../../../common/censorHTMLtree';
4
+ export function makePageParser(element, logger) {
5
+ return new PageParserTree(element, {
6
+ logError(err, el) {
7
+ const details = {
8
+ el,
9
+ html: el ? censorHTMLtree(el) : null,
10
+ };
11
+ logger.errorSite(err, details);
12
+ },
13
+ tags: {},
14
+ watchers: [
15
+ {
16
+ // For regular composes
17
+ sources: [null],
18
+ tag: 'recipientsCommon',
19
+ selectors: [
20
+ 'div',
21
+ 'div',
22
+ 'div[jsaction]',
23
+ 'div[role=region]',
24
+ 'table',
25
+ 'tbody',
26
+ 'tr',
27
+ 'td',
28
+ 'form',
29
+ 'div',
30
+ 'table.GS',
31
+ 'tbody',
32
+ 'tr.bzf',
33
+ 'td.eV',
34
+ 'div',
35
+ 'div',
36
+ ],
37
+ },
38
+ {
39
+ // For inline composes
40
+ sources: [null],
41
+ tag: 'recipientsCommon',
42
+ selectors: [
43
+ 'div[role=region]',
44
+ 'table',
45
+ 'tbody',
46
+ 'tr',
47
+ 'td',
48
+ 'form',
49
+ 'div',
50
+ 'table[role=presentation]',
51
+ 'tbody',
52
+ 'tr',
53
+ 'td.Iy',
54
+ 'div.fX',
55
+ 'table.GS',
56
+ 'tbody',
57
+ 'tr.bzf',
58
+ 'td.eV',
59
+ 'div',
60
+ 'div',
61
+ ],
62
+ },
63
+ {
64
+ sources: ['recipientsCommon'],
65
+ tag: 'toRecipient',
66
+ selectors: [
67
+ {
68
+ $or: [
69
+ [
70
+ 'div.anm[name="to"]',
71
+ {
72
+ $or: [
73
+ [],
74
+ // Inline reply compose
75
+ ['div.aUy'],
76
+ ],
77
+ },
78
+ 'div',
79
+ 'div',
80
+ 'div',
81
+ 'div',
82
+ '[role=listbox]',
83
+ 'div',
84
+ 'div',
85
+ 'div[role=option][data-name]',
86
+ ],
87
+ [
88
+ {
89
+ $map: (el) => el.querySelector('textarea.vO[name=to], input[name=to]'),
90
+ },
91
+ { $map: (el) => el.closest('tr') },
92
+ 'td.eV',
93
+ 'div',
94
+ 'div',
95
+ '.vR',
96
+ 'input[type=hidden]',
97
+ { $map: (el) => el.parentElement },
98
+ ],
99
+ ],
100
+ },
101
+ ],
102
+ },
103
+ {
104
+ sources: ['recipientsCommon'],
105
+ tag: 'ccRecipient',
106
+ selectors: [
107
+ {
108
+ $or: [
109
+ [
110
+ 'div.anm[name="cc"]',
111
+ {
112
+ $or: [
113
+ [],
114
+ // Inline reply compose
115
+ ['div.aUy'],
116
+ ],
117
+ },
118
+ 'div',
119
+ 'div',
120
+ 'div',
121
+ 'div',
122
+ '[role=listbox]',
123
+ 'div',
124
+ 'div',
125
+ 'div[role=option][data-name]',
126
+ ],
127
+ [
128
+ {
129
+ $map: (el) => el.querySelector('textarea.vO[name=cc], input[name=cc]'),
130
+ },
131
+ { $map: (el) => el.closest('tr') },
132
+ 'td.eV',
133
+ 'div',
134
+ 'div',
135
+ '.vR',
136
+ 'input[type=hidden]',
137
+ { $map: (el) => el.parentElement },
138
+ ],
139
+ ],
140
+ },
141
+ ],
142
+ },
143
+ {
144
+ sources: ['recipientsCommon'],
145
+ tag: 'bccRecipient',
146
+ selectors: [
147
+ {
148
+ $or: [
149
+ [
150
+ 'div.anm[name="bcc"]',
151
+ {
152
+ $or: [
153
+ [],
154
+ // Inline reply compose
155
+ ['div.aUy'],
156
+ ],
157
+ },
158
+ 'div',
159
+ 'div',
160
+ 'div',
161
+ 'div',
162
+ '[role=listbox]',
163
+ 'div',
164
+ 'div',
165
+ 'div[role=option][data-name]',
166
+ ],
167
+ [
168
+ {
169
+ $map: (el) => el.querySelector('textarea.vO[name=bcc], input[name=bcc]'),
170
+ },
171
+ { $map: (el) => el.closest('tr') },
172
+ 'td.eV',
173
+ 'div',
174
+ 'div',
175
+ '.vR',
176
+ 'input[type=hidden]',
177
+ { $map: (el) => el.parentElement },
178
+ ],
179
+ ],
180
+ },
181
+ ],
182
+ },
183
+ ],
184
+ finders: {
185
+ toRecipient: {
186
+ fn: (root) => getRecipientChips(root, 'to'),
187
+ },
188
+ ccRecipient: {
189
+ fn: (root) => getRecipientChips(root, 'cc'),
190
+ },
191
+ bccRecipient: {
192
+ fn: (root) => getRecipientChips(root, 'bcc'),
193
+ },
194
+ },
195
+ });
196
+ }
197
+ export function getRecipientChips(element, addressType) {
198
+ const oldRow = getOldRecipientRowForType(element, addressType);
199
+ if (oldRow) {
200
+ return Array.from(oldRow.querySelectorAll('.vR > input[type=hidden]')).map((el) => el.parentElement);
201
+ }
202
+ else {
203
+ return element.querySelectorAll(`.GS .anm[name="${addressType}"] [role=listbox] [role=option][data-name]`);
204
+ }
205
+ }
206
+ // getRecipientRowForType is recommended over this
207
+ export function getRecipientRowElements(element) {
208
+ return Array.prototype.filter.call(element.querySelectorAll('.GS tr, .GS .anm[name] [role=listbox]'), (tr) => !tr.classList.contains('inboxsdk__recipient_row'));
209
+ }
210
+ export function getRecipientRowForType(element, addressType) {
211
+ // handling new recipients
212
+ // https://workspaceupdates.googleblog.com/2021/10/visual-updates-for-composing-email-in-gmail.html
213
+ return element.querySelector(`.GS .anm[name="${addressType}"] [role=listbox]`);
214
+ }
215
+ export function getOldRecipientRowForType(element, addressType) {
216
+ const input = element.querySelector(`.GS tr textarea.vO[name="${addressType}"], .GS tr input[name="${addressType}"]`);
217
+ if (input) {
218
+ return closest(input, 'tr');
219
+ }
220
+ else {
221
+ return null;
222
+ }
223
+ }
@@ -0,0 +1,157 @@
1
+ import Logger from '../../../../lib/logger';
2
+ import querySelector from '../../../../lib/dom/querySelectorOrFail';
3
+ import getRecipients from './get-recipients';
4
+ import { simulateClick } from '../../../../lib/dom/simulate-mouse-event';
5
+ import simulateKey from '../../../../lib/dom/simulate-key';
6
+ import { getRecipientRowForType, getOldRecipientRowForType, } from './page-parser';
7
+ export default function setRecipients(gmailComposeView, addressType, emailAddresses) {
8
+ if (_areContactsEqual(gmailComposeView, addressType, emailAddresses)) {
9
+ return;
10
+ }
11
+ const oldRange = gmailComposeView.getLastSelectionRange();
12
+ let _contactRow = getRecipientRowForType(gmailComposeView.getElement(), addressType);
13
+ function openRecipientsSectionIfNeeded(button) {
14
+ const signalElement = button.closest('.fX.aiL');
15
+ if (signalElement && signalElement.style.display === 'none') {
16
+ const cover = querySelector(gmailComposeView.getElement(), 'div.aoD.hl');
17
+ cover.dispatchEvent(new FocusEvent('focus'));
18
+ }
19
+ }
20
+ if (addressType === 'cc') {
21
+ const ccButton = gmailComposeView
22
+ .getElement()
23
+ .querySelector('span.pE[role=link]');
24
+ if (ccButton) {
25
+ openRecipientsSectionIfNeeded(ccButton);
26
+ ccButton.click();
27
+ _contactRow = getRecipientRowForType(gmailComposeView.getElement(), addressType);
28
+ }
29
+ }
30
+ else if (addressType === 'bcc') {
31
+ const bccButton = gmailComposeView
32
+ .getElement()
33
+ .querySelector('span.pB[role=link]');
34
+ if (bccButton) {
35
+ openRecipientsSectionIfNeeded(bccButton);
36
+ bccButton.click();
37
+ _contactRow = getRecipientRowForType(gmailComposeView.getElement(), addressType);
38
+ }
39
+ }
40
+ const contactRow = _contactRow;
41
+ if (contactRow) {
42
+ // new recipient fields
43
+ // https://workspaceupdates.googleblog.com/2021/10/visual-updates-for-composing-email-in-gmail.html
44
+ const emailAddressEntry = querySelector(contactRow, 'input[role=combobox][aria-autocomplete=list]');
45
+ if (!(emailAddressEntry instanceof HTMLInputElement)) {
46
+ throw new Error();
47
+ }
48
+ // Remove existing recipients
49
+ Array.from(contactRow.querySelectorAll('div[role=option][data-name] div.afX[aria-label]')).forEach((el) => {
50
+ el.click();
51
+ });
52
+ emailAddressEntry.value = '';
53
+ const clipboardData = new DataTransfer();
54
+ clipboardData.setData('text/plain', emailAddresses.join(','));
55
+ emailAddressEntry.dispatchEvent(new ClipboardEvent('paste', { clipboardData }));
56
+ // On fresh composes, if set_Recipients is called immediately, then
57
+ // Gmail asynchronously resets the recipients field. Detect this and
58
+ // put our change back.
59
+ const nameEl = contactRow.closest('div.anm[name]');
60
+ if (emailAddresses.length > 0 &&
61
+ nameEl &&
62
+ !nameEl.hasAttribute('data-inboxsdk-handled-reset')) {
63
+ // We're in a fresh compose.
64
+ // If setRecipients is called multiple times on a fresh compose,
65
+ // we only want the last call's setTimeout callback to do anything.
66
+ // Do this by marking a DOM element with a unique claim value and
67
+ // checking for it in the callback. Note that we generate the claim
68
+ // value by reading from the existing DOM value instead of a variable
69
+ // in module scope because there might be multiple extensions using
70
+ // the InboxSDK; we must use the shared DOM as the source of truth
71
+ // for values shared between InboxSDK instances.
72
+ const claim = String(Number(nameEl.getAttribute('data-inboxsdk-tracking-fresh-compose') || 0) + 1);
73
+ nameEl.setAttribute('data-inboxsdk-tracking-fresh-compose', claim);
74
+ setTimeout(() => {
75
+ if (nameEl.getAttribute('data-inboxsdk-tracking-fresh-compose') === claim) {
76
+ nameEl.removeAttribute('data-inboxsdk-tracking-fresh-compose');
77
+ // Does it look like our contact chips have all been removed?
78
+ if (contactRow.querySelectorAll('div[role=option][data-name] div.afX[aria-label]').length === 0) {
79
+ // put a mark in the DOM to be absolutely sure we can't
80
+ // get in an infinite loop (which could happen if Gmail doesn't
81
+ // actually set the style attribute, and our selector above fails
82
+ // to find the recipient chips).
83
+ nameEl.setAttribute('data-inboxsdk-handled-reset', 'true');
84
+ setRecipients(gmailComposeView, addressType, emailAddresses);
85
+ }
86
+ }
87
+ }, 100);
88
+ }
89
+ }
90
+ else {
91
+ // old recipient fields
92
+ let contactRow;
93
+ try {
94
+ contactRow = getOldRecipientRowForType(gmailComposeView.getElement(), addressType);
95
+ if (!contactRow) {
96
+ throw new Error('getOldRecipientRowForType failed');
97
+ }
98
+ }
99
+ catch (err) {
100
+ Logger.error(err, { addressType });
101
+ return;
102
+ }
103
+ const emailAddressEntry = contactRow.querySelector('textarea.vO');
104
+ if (!emailAddressEntry ||
105
+ !(emailAddressEntry instanceof HTMLTextAreaElement)) {
106
+ return;
107
+ }
108
+ // Remove existing recipients
109
+ Array.from(contactRow.querySelectorAll('.vR .vM')).forEach((el) => {
110
+ simulateClick(el);
111
+ });
112
+ emailAddressEntry.value = emailAddresses.join(',');
113
+ // Push enter so Gmail interprets the addresses.
114
+ simulateKey(emailAddressEntry, 13, 0);
115
+ // Focus the recipients preview label so Gmail re-renders it.
116
+ const cover = querySelector(gmailComposeView.getElement(), 'div.aoD.hl');
117
+ cover.dispatchEvent(new FocusEvent('focus'));
118
+ if (addressType === 'cc') {
119
+ const ccButton = querySelector(gmailComposeView.getElement(), 'span.aB.gQ.pE');
120
+ simulateClick(ccButton);
121
+ }
122
+ else if (addressType === 'bcc') {
123
+ const bccButton = querySelector(gmailComposeView.getElement(), 'span.aB.gQ.pB');
124
+ simulateClick(bccButton);
125
+ }
126
+ }
127
+ // Then restore focus to what it was before.
128
+ if (document.activeElement instanceof HTMLElement) {
129
+ document.activeElement.blur();
130
+ }
131
+ if (oldRange) {
132
+ const sel = document.getSelection();
133
+ if (!sel)
134
+ throw new Error();
135
+ sel.removeAllRanges();
136
+ sel.addRange(oldRange);
137
+ }
138
+ }
139
+ function _areContactsEqual(gmailComposeView, addressType, emailAddresses) {
140
+ const existingEmailAddresses = getRecipients(gmailComposeView, addressType).map((c) => c.emailAddress);
141
+ if (!emailAddresses) {
142
+ return !!existingEmailAddresses;
143
+ }
144
+ if (!existingEmailAddresses) {
145
+ return !!emailAddresses;
146
+ }
147
+ if (emailAddresses.length !== existingEmailAddresses.length) {
148
+ return false;
149
+ }
150
+ for (let ii = 0; ii < existingEmailAddresses.length; ii++) {
151
+ const existingEmailAddress = existingEmailAddresses[ii];
152
+ if (emailAddresses.indexOf(existingEmailAddress) === -1) {
153
+ return false;
154
+ }
155
+ }
156
+ return true;
157
+ }