expensify-common 2.0.188 → 2.0.190
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/dist/Logger.js +11 -1
- package/dist/esm/API.d.ts +11 -0
- package/dist/esm/API.js +786 -0
- package/dist/esm/APIDeferred.d.ts +7 -0
- package/dist/esm/APIDeferred.js +216 -0
- package/dist/esm/BrowserDetect.d.ts +19 -0
- package/dist/esm/BrowserDetect.js +105 -0
- package/dist/esm/CLI.js +37 -29
- package/dist/esm/CONST.d.ts +1727 -0
- package/dist/esm/CONST.js +1847 -0
- package/dist/esm/Cookie.d.ts +68 -0
- package/dist/esm/Cookie.js +159 -0
- package/dist/esm/CredentialsWrapper.d.ts +32 -0
- package/dist/esm/CredentialsWrapper.js +46 -0
- package/dist/esm/Device.d.ts +8 -0
- package/dist/esm/Device.js +24 -0
- package/dist/esm/ExpenseRule.d.ts +39 -0
- package/dist/esm/ExpenseRule.js +77 -0
- package/dist/esm/ExpensiMark.d.ts +197 -0
- package/dist/esm/ExpensiMark.js +1374 -0
- package/dist/esm/Func.d.ts +40 -0
- package/dist/esm/Func.js +69 -0
- package/dist/esm/Log.d.ts +3 -0
- package/dist/esm/Log.js +35 -0
- package/dist/esm/Logger.d.ts +82 -0
- package/dist/esm/Logger.js +148 -0
- package/dist/esm/Network.d.ts +6 -0
- package/dist/esm/Network.js +168 -0
- package/dist/esm/Num.d.ts +95 -0
- package/dist/esm/Num.js +190 -0
- package/dist/esm/PageEvent.d.ts +25 -0
- package/dist/esm/PageEvent.js +22 -0
- package/dist/esm/PubSub.d.ts +2 -0
- package/dist/esm/PubSub.js +111 -0
- package/dist/esm/ReportHistoryStore.d.ts +8 -0
- package/dist/esm/ReportHistoryStore.js +199 -0
- package/dist/esm/SafeString.js +2 -2
- package/dist/esm/Templates.d.ts +58 -0
- package/dist/esm/Templates.js +196 -0
- package/dist/esm/Url.d.ts +10 -0
- package/dist/esm/Url.js +15 -0
- package/dist/esm/components/CopyText.d.ts +45 -0
- package/dist/esm/components/CopyText.js +56 -0
- package/dist/esm/components/StepProgressBar.d.ts +26 -0
- package/dist/esm/components/StepProgressBar.js +40 -0
- package/dist/esm/components/form/element/combobox.d.ts +231 -0
- package/dist/esm/components/form/element/combobox.js +812 -0
- package/dist/esm/components/form/element/dropdown.d.ts +35 -0
- package/dist/esm/components/form/element/dropdown.js +61 -0
- package/dist/esm/components/form/element/dropdownItem.d.ts +55 -0
- package/dist/esm/components/form/element/dropdownItem.js +113 -0
- package/dist/esm/components/form/element/onOffSwitch.d.ts +94 -0
- package/dist/esm/components/form/element/onOffSwitch.js +167 -0
- package/dist/esm/components/form/element/switch.d.ts +58 -0
- package/dist/esm/components/form/element/switch.js +98 -0
- package/dist/esm/fastMerge.d.ts +9 -0
- package/dist/esm/fastMerge.js +58 -0
- package/dist/esm/index.d.ts +22 -0
- package/dist/esm/index.js +22 -0
- package/dist/esm/jquery.expensifyIframify.d.ts +9 -0
- package/dist/esm/jquery.expensifyIframify.js +397 -0
- package/dist/esm/md5.d.ts +2 -0
- package/dist/esm/md5.js +170 -0
- package/dist/esm/mixins/PubSub.d.ts +20 -0
- package/dist/esm/mixins/PubSub.js +47 -0
- package/dist/esm/mixins/extraClasses.d.ts +8 -0
- package/dist/esm/mixins/extraClasses.js +37 -0
- package/dist/esm/mixins/validationClasses.d.ts +12 -0
- package/dist/esm/mixins/validationClasses.js +30 -0
- package/dist/esm/str.d.ts +604 -0
- package/dist/esm/str.js +1036 -0
- package/dist/esm/tlds.d.ts +2 -0
- package/dist/esm/tlds.js +2 -0
- package/dist/esm/utils.d.ts +30 -0
- package/dist/esm/utils.js +65 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +8 -1
- package/package.json +221 -4
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merges two objects and removes null values if "shouldRemoveNullObjectValues" is set to true
|
|
3
|
+
*
|
|
4
|
+
* We generally want to remove null values from objects written to disk and cache, because it decreases the amount of data stored in memory and on disk.
|
|
5
|
+
* On native, when merging an existing value with new changes, SQLite will use JSON_PATCH, which removes top-level nullish values.
|
|
6
|
+
* To be consistent with the behaviour for merge, we'll also want to remove null values for "set" operations.
|
|
7
|
+
*/
|
|
8
|
+
declare function fastMerge<TObject>(target: TObject, source: TObject, shouldRemoveNullObjectValues?: boolean): TObject;
|
|
9
|
+
export default fastMerge;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'worklet';
|
|
2
|
+
// Mostly copied from https://medium.com/@lubaka.a/how-to-remove-lodash-performance-improvement-b306669ad0e1
|
|
3
|
+
/**
|
|
4
|
+
* Checks whether the given value can be merged. It has to be an object, but not an array, RegExp or Date.
|
|
5
|
+
*/
|
|
6
|
+
function isMergeableObject(value) {
|
|
7
|
+
const nonNullObject = value != null ? typeof value === 'object' : false;
|
|
8
|
+
return nonNullObject && Object.prototype.toString.call(value) !== '[object RegExp]' && Object.prototype.toString.call(value) !== '[object Date]' && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
function fastMerge(target, source, shouldRemoveNullObjectValues = true) {
|
|
11
|
+
// We have to ignore arrays and nullish values here,
|
|
12
|
+
// otherwise "mergeObject" will throw an error,
|
|
13
|
+
// because it expects an object as "source"
|
|
14
|
+
if (Array.isArray(source) || source === null || source === undefined) {
|
|
15
|
+
return source;
|
|
16
|
+
}
|
|
17
|
+
// Merges the source object into the target object.
|
|
18
|
+
const destination = {};
|
|
19
|
+
if (isMergeableObject(target)) {
|
|
20
|
+
// lodash adds a small overhead so we don't use it here
|
|
21
|
+
const targetKeys = Object.keys(target);
|
|
22
|
+
for (let i = 0; i < targetKeys.length; ++i) {
|
|
23
|
+
const key = targetKeys[i];
|
|
24
|
+
const sourceValue = source === null || source === void 0 ? void 0 : source[key];
|
|
25
|
+
const targetValue = target === null || target === void 0 ? void 0 : target[key];
|
|
26
|
+
// If shouldRemoveNullObjectValues is true, we want to remove null values from the merged object
|
|
27
|
+
const isSourceOrTargetNull = targetValue === null || sourceValue === null;
|
|
28
|
+
const shouldOmitSourceKey = shouldRemoveNullObjectValues && isSourceOrTargetNull;
|
|
29
|
+
if (!shouldOmitSourceKey) {
|
|
30
|
+
destination[key] = targetValue;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// lodash adds a small overhead so we don't use it here
|
|
35
|
+
const sourceKeys = Object.keys(source);
|
|
36
|
+
for (let i = 0; i < sourceKeys.length; ++i) {
|
|
37
|
+
const key = sourceKeys[i];
|
|
38
|
+
const sourceValue = source === null || source === void 0 ? void 0 : source[key];
|
|
39
|
+
const targetValue = target === null || target === void 0 ? void 0 : target[key];
|
|
40
|
+
// If shouldRemoveNullObjectValues is true, we want to remove null values from the merged object
|
|
41
|
+
const shouldOmitSourceKey = shouldRemoveNullObjectValues && sourceValue === null;
|
|
42
|
+
// If we pass undefined as the updated value for a key, we want to generally ignore it
|
|
43
|
+
const isSourceKeyUndefined = sourceValue === undefined;
|
|
44
|
+
if (!isSourceKeyUndefined && !shouldOmitSourceKey) {
|
|
45
|
+
const isSourceKeyMergable = isMergeableObject(sourceValue);
|
|
46
|
+
if (isSourceKeyMergable && targetValue) {
|
|
47
|
+
if (!shouldRemoveNullObjectValues || isSourceKeyMergable) {
|
|
48
|
+
destination[key] = fastMerge(targetValue, sourceValue, shouldRemoveNullObjectValues);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else if (!shouldRemoveNullObjectValues || sourceValue !== null) {
|
|
52
|
+
destination[key] = sourceValue;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return destination;
|
|
57
|
+
}
|
|
58
|
+
export default fastMerge;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export { default as API } from './API';
|
|
2
|
+
export { default as APIDeferred } from './APIDeferred';
|
|
3
|
+
export { default as BrowserDetect } from './BrowserDetect';
|
|
4
|
+
export { g_cloudFront, g_cloudFrontImg, CONST, UI, PUBLIC_DOMAINS_SET } from './CONST';
|
|
5
|
+
export { default as Cookie } from './Cookie';
|
|
6
|
+
export { default as CredentialsWrapper, LOGIN_PARTNER_DETAILS } from './CredentialsWrapper';
|
|
7
|
+
export * as Device from './Device';
|
|
8
|
+
export { default as ExpensiMark } from './ExpensiMark';
|
|
9
|
+
export { default as Logger } from './Logger';
|
|
10
|
+
export { default as Network } from './Network';
|
|
11
|
+
export { default as Num } from './Num';
|
|
12
|
+
export { default as PageEvent } from './PageEvent';
|
|
13
|
+
export { default as PubSub } from './PubSub';
|
|
14
|
+
export { default as ReportHistoryStore } from './ReportHistoryStore';
|
|
15
|
+
export { default as Templates } from './Templates';
|
|
16
|
+
export * as Url from './Url';
|
|
17
|
+
export { default as fastMerge } from './fastMerge';
|
|
18
|
+
export { default as Str } from './str';
|
|
19
|
+
export { default as TLD_REGEX } from './tlds';
|
|
20
|
+
export { default as md5 } from './md5';
|
|
21
|
+
export { default as SafeString } from './SafeString';
|
|
22
|
+
export { escapeText, isFunction, isNavigatorAvailable, isObject, isWindowAvailable, unescapeText } from './utils';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export { default as API } from './API';
|
|
2
|
+
export { default as APIDeferred } from './APIDeferred';
|
|
3
|
+
export { default as BrowserDetect } from './BrowserDetect';
|
|
4
|
+
export { g_cloudFront, g_cloudFrontImg, CONST, UI, PUBLIC_DOMAINS_SET } from './CONST';
|
|
5
|
+
export { default as Cookie } from './Cookie';
|
|
6
|
+
export { default as CredentialsWrapper, LOGIN_PARTNER_DETAILS } from './CredentialsWrapper';
|
|
7
|
+
export * as Device from './Device';
|
|
8
|
+
export { default as ExpensiMark } from './ExpensiMark';
|
|
9
|
+
export { default as Logger } from './Logger';
|
|
10
|
+
export { default as Network } from './Network';
|
|
11
|
+
export { default as Num } from './Num';
|
|
12
|
+
export { default as PageEvent } from './PageEvent';
|
|
13
|
+
export { default as PubSub } from './PubSub';
|
|
14
|
+
export { default as ReportHistoryStore } from './ReportHistoryStore';
|
|
15
|
+
export { default as Templates } from './Templates';
|
|
16
|
+
export * as Url from './Url';
|
|
17
|
+
export { default as fastMerge } from './fastMerge';
|
|
18
|
+
export { default as Str } from './str';
|
|
19
|
+
export { default as TLD_REGEX } from './tlds';
|
|
20
|
+
export { default as md5 } from './md5';
|
|
21
|
+
export { default as SafeString } from './SafeString';
|
|
22
|
+
export { escapeText, isFunction, isNavigatorAvailable, isObject, isWindowAvailable, unescapeText } from './utils';
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This is a jQuery plugin that provides some iFrame functionality like
|
|
3
|
+
* posting messages to the parent window.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
*
|
|
7
|
+
* From inside the iFrame:
|
|
8
|
+
* $.fn.expensifyIframeify( 'resize', { height: 100, width: 400 } );
|
|
9
|
+
* $.fn.expensifyIframeify( 'tellParent', { name: 'say', data: { msg: 'Hello world!' } } );
|
|
10
|
+
*
|
|
11
|
+
* From inside the parent:
|
|
12
|
+
* $( '#myIframe' ).expensifyIframeify( 'on', 'resize', function( data ) { ... } );
|
|
13
|
+
* $( '#myIframe' ).expensifyIframeify( 'on', 'say', function( data ) {
|
|
14
|
+
* // alerts "Hello world!"
|
|
15
|
+
* alert( data.msg );
|
|
16
|
+
* });
|
|
17
|
+
*/
|
|
18
|
+
export default {
|
|
19
|
+
/**
|
|
20
|
+
* Loads the widget into jQuery
|
|
21
|
+
*
|
|
22
|
+
* @param {jQuery} $ jQuery instance
|
|
23
|
+
*/
|
|
24
|
+
load($) {
|
|
25
|
+
/**
|
|
26
|
+
* Holds the value normally stored in window.location.origin but it's calculated
|
|
27
|
+
* according to the suggested solutions
|
|
28
|
+
* Issue:
|
|
29
|
+
* - https://github.com/Expensify/Expensify/issues/13192#issuecomment-100322844
|
|
30
|
+
* Solution suggested in the following sites:
|
|
31
|
+
* - http://stackoverflow.com/questions/22564167/window-location-origin-gives-wrong-path-when-using-ie
|
|
32
|
+
* - http://tosbourn.com/a-fix-for-window-location-origin-in-internet-explorer/
|
|
33
|
+
*
|
|
34
|
+
* Believe it or not before this change this was causing a syntax error in IE10 that disappears with this change
|
|
35
|
+
*
|
|
36
|
+
* @type {String} jQuery Element
|
|
37
|
+
*/
|
|
38
|
+
const defaultOrigin = `${window.location.protocol}//${window.location.hostname}`;
|
|
39
|
+
/**
|
|
40
|
+
* This indicates from and to which domains we need to send the messages.
|
|
41
|
+
* @type {object}
|
|
42
|
+
*/
|
|
43
|
+
const allowedCommunications = {
|
|
44
|
+
'https://www.expensify.com': ['https://secure.expensify.com', 'https://www.expensify.com'],
|
|
45
|
+
'https://secure.expensify.com': ['https://www.expensify.com', 'https://secure.expensify.com', 'https://salesforce.expensify.com'],
|
|
46
|
+
'https://staging.expensify.com': ['https://staging.expensify.com', 'https://staging-secure.expensify.com'],
|
|
47
|
+
'https://staging-secure.expensify.com': ['https://staging.expensify.com', 'https://staging-secure.expensify.com'],
|
|
48
|
+
'https://www.expensify.com.dev': ['https://secure.expensify.com.dev', 'https://www.expensify.com.dev'],
|
|
49
|
+
'https://secure.expensify.com.dev': ['https://www.expensify.com.dev', 'https://secure.expensify.com.dev'],
|
|
50
|
+
'https://salesforce.expensify.com': ['https://secure.expensify.com'],
|
|
51
|
+
'https://www.google.com': ['https://secure.expensify.com.dev', 'https://secure.expensify.com'],
|
|
52
|
+
'https://cdn.plaid.com': ['https://secure.expensify.com'],
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Holds a reference to the jQuery iFrame object
|
|
56
|
+
*
|
|
57
|
+
* @type {object} jQuery Element
|
|
58
|
+
*/
|
|
59
|
+
let iframeElement = null;
|
|
60
|
+
/**
|
|
61
|
+
* Whether or not the parent window is listening to the message event
|
|
62
|
+
*
|
|
63
|
+
* @type {Boolean}
|
|
64
|
+
*/
|
|
65
|
+
let parentIsListening = false;
|
|
66
|
+
/**
|
|
67
|
+
* Whether or not the plugin has been initialized
|
|
68
|
+
* @type {Boolean}
|
|
69
|
+
*/
|
|
70
|
+
let wasInitalized = false;
|
|
71
|
+
/**
|
|
72
|
+
* Whether or not the iframe is listening to the message event
|
|
73
|
+
*
|
|
74
|
+
* @type {Boolean}
|
|
75
|
+
*/
|
|
76
|
+
let iframeIsListening = false;
|
|
77
|
+
/**
|
|
78
|
+
* Keeps a record of if this script is running from the "parent" or the "iframe"
|
|
79
|
+
* so that we only have to do this logic once.
|
|
80
|
+
*/
|
|
81
|
+
let whatIAm;
|
|
82
|
+
/**
|
|
83
|
+
* Default settings for our plugin
|
|
84
|
+
*
|
|
85
|
+
* @type {object}
|
|
86
|
+
*/
|
|
87
|
+
let settings = {
|
|
88
|
+
origin: defaultOrigin,
|
|
89
|
+
debug: false,
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Holds all of our registered event handlers
|
|
93
|
+
*
|
|
94
|
+
* @type {object}
|
|
95
|
+
*/
|
|
96
|
+
let eventHandlers = {};
|
|
97
|
+
/**
|
|
98
|
+
* A method for logging things to the console for debugging
|
|
99
|
+
* @param {...any} args
|
|
100
|
+
*/
|
|
101
|
+
function log(...args) {
|
|
102
|
+
if (!settings.debug) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
// Make an array out of our arguments
|
|
106
|
+
const mainArguments = Array.prototype.slice.call(args);
|
|
107
|
+
const source = whatIAm === 'parent' ? 'Parent' : 'Iframe';
|
|
108
|
+
mainArguments.unshift(`${source}:`);
|
|
109
|
+
mainArguments.unshift('[EXPENSIFYIFRAMEIFY]');
|
|
110
|
+
console.log(...mainArguments); // eslint-disable-line no-console
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Forms the message to send to our parent. The message structure is like:
|
|
114
|
+
*
|
|
115
|
+
* Full example:
|
|
116
|
+
* iframeify:<iframeId>:<name>☢<data>
|
|
117
|
+
*
|
|
118
|
+
* Lite example:
|
|
119
|
+
* iframeify:<name>
|
|
120
|
+
*
|
|
121
|
+
* Where:
|
|
122
|
+
* - <iframeId> (optional) is the "id" attribute of the iframe
|
|
123
|
+
* - <name> (required) is the name of the event
|
|
124
|
+
* - <data> (optional) is a stringified glob of data
|
|
125
|
+
*
|
|
126
|
+
* @param {string} name the name of the message to send
|
|
127
|
+
* @param {object} data to send in the message
|
|
128
|
+
* @param {boolean|undefined} postToIframe if true, then we post the message
|
|
129
|
+
* to the iFrame or else we post
|
|
130
|
+
* the message to the parent
|
|
131
|
+
*/
|
|
132
|
+
function postMessage(name, data, postToIframe) {
|
|
133
|
+
let msg = 'iframeify';
|
|
134
|
+
if (iframeElement.iframeId) {
|
|
135
|
+
msg += `:${iframeElement.iframeId}`;
|
|
136
|
+
}
|
|
137
|
+
msg += `:${name}`;
|
|
138
|
+
if (data) {
|
|
139
|
+
msg += '☢';
|
|
140
|
+
msg += JSON.stringify(data);
|
|
141
|
+
}
|
|
142
|
+
const isCommunicationAllowed = allowedCommunications[settings.origin];
|
|
143
|
+
if (!isCommunicationAllowed) {
|
|
144
|
+
log('not posting message, communication from this domain is not allowed', settings.origin, name, data);
|
|
145
|
+
}
|
|
146
|
+
let targetOrigin;
|
|
147
|
+
// Sending message from the iFrame to the parent
|
|
148
|
+
// Only post a message if this is in an iFrame
|
|
149
|
+
if (!postToIframe && window.parent !== window) {
|
|
150
|
+
targetOrigin = `${window.parent.location.protocol}//${window.parent.location.hostname}`;
|
|
151
|
+
log('posting message to parent', targetOrigin, msg);
|
|
152
|
+
window.parent.postMessage(msg, targetOrigin);
|
|
153
|
+
}
|
|
154
|
+
// Sending message from the parent to the iFrame
|
|
155
|
+
if (postToIframe && iframeElement[0].contentWindow) {
|
|
156
|
+
targetOrigin = `${iframeElement[0].contentWindow.location.protocol}//${iframeElement[0].contentWindow.location.hostname}`;
|
|
157
|
+
log('posting message to iframe', targetOrigin, msg);
|
|
158
|
+
iframeElement[0].contentWindow.postMessage(msg, targetOrigin);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Calls all the methods associated with the event name
|
|
163
|
+
* and passes it the given data
|
|
164
|
+
*
|
|
165
|
+
* @param {string} name of the event
|
|
166
|
+
* @param {mixed} data passed to the event handler
|
|
167
|
+
* @param {string} [iframeId]
|
|
168
|
+
*/
|
|
169
|
+
function trigger(name, data, iframeId) {
|
|
170
|
+
log(`heard event "${name}"`, data);
|
|
171
|
+
const id = iframeId || iframeElement.iframeId;
|
|
172
|
+
if (eventHandlers[id] && eventHandlers[id][name] && eventHandlers[id][name].length) {
|
|
173
|
+
const listeners = eventHandlers[id][name];
|
|
174
|
+
for (let i = 0; i < listeners.length; i += 1) {
|
|
175
|
+
listeners[i](data);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Handles the window message by parsing the data
|
|
181
|
+
* and relaying it to our event listeners
|
|
182
|
+
*
|
|
183
|
+
* @param {Event} e jQuery event
|
|
184
|
+
*/
|
|
185
|
+
function handleWindowMessage(e) {
|
|
186
|
+
// Use the original event to get our data properly
|
|
187
|
+
const event = e.originalEvent;
|
|
188
|
+
if (!event.origin || !event.target) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (allowedCommunications[event.origin] &&
|
|
192
|
+
allowedCommunications[event.origin].indexOf('*') === -1 &&
|
|
193
|
+
allowedCommunications[event.origin].indexOf(`${event.target.location.protocol}//${event.target.location.hostname}`) === -1) {
|
|
194
|
+
log('not handling message, communication from this domain is not allowed', settings.origin);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (!event.data || (typeof event.data !== 'string' && !(event.data instanceof String))) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
// Get the pieces of our message (should be two)
|
|
201
|
+
const msgParts = event.data.split('☢');
|
|
202
|
+
if (!msgParts.length) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
// Get the pieces of our name (should be two or three)
|
|
206
|
+
// and extract the event name and the iframe ID
|
|
207
|
+
const nameParts = msgParts[0].split(':');
|
|
208
|
+
if (!nameParts.length || nameParts.length > 3) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
let iframeId = null;
|
|
212
|
+
let eventName = null;
|
|
213
|
+
let data = null;
|
|
214
|
+
if (nameParts.length === 3) {
|
|
215
|
+
iframeId = nameParts[1];
|
|
216
|
+
eventName = nameParts[2];
|
|
217
|
+
}
|
|
218
|
+
else if (nameParts.length === 2) {
|
|
219
|
+
eventName = nameParts[1];
|
|
220
|
+
}
|
|
221
|
+
// Get the data
|
|
222
|
+
if (msgParts.length > 1) {
|
|
223
|
+
try {
|
|
224
|
+
data = JSON.parse(msgParts[1]);
|
|
225
|
+
}
|
|
226
|
+
catch (_a) {
|
|
227
|
+
// This should only happen if someone didn't code something right
|
|
228
|
+
console.error('Could not parse JSON response for some reason', event.data);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Trigger any listeners for this event
|
|
232
|
+
trigger(eventName, data, iframeId);
|
|
233
|
+
}
|
|
234
|
+
const actions = {
|
|
235
|
+
// Add actions here
|
|
236
|
+
/**
|
|
237
|
+
* Tells the parent to dynamically resize the iFrame
|
|
238
|
+
*
|
|
239
|
+
* @param {object} args
|
|
240
|
+
* @param {string} args.id the ID of the iFrame to resize
|
|
241
|
+
* @param {integer} args.height (optional) the height to set the iFrame to
|
|
242
|
+
* @param {integer} args.width (optional) the width to set the iFrame to
|
|
243
|
+
*/
|
|
244
|
+
resize(args) {
|
|
245
|
+
const size = {};
|
|
246
|
+
if (args.height !== undefined) {
|
|
247
|
+
size.height = args.height;
|
|
248
|
+
}
|
|
249
|
+
if (args.width !== undefined) {
|
|
250
|
+
size.width = args.width;
|
|
251
|
+
}
|
|
252
|
+
postMessage('resize', size);
|
|
253
|
+
},
|
|
254
|
+
/**
|
|
255
|
+
* Tell our parent a generic message
|
|
256
|
+
*
|
|
257
|
+
* @param {object} args
|
|
258
|
+
* @param {string} args.name the name of the message to post
|
|
259
|
+
* @param {object} args.data (optional) to send with the message
|
|
260
|
+
*/
|
|
261
|
+
tellParent(args) {
|
|
262
|
+
postMessage(args.name, args.data);
|
|
263
|
+
},
|
|
264
|
+
/**
|
|
265
|
+
* Tell our iFrame a generic message
|
|
266
|
+
*
|
|
267
|
+
* @param {object} args
|
|
268
|
+
* @param {string} args.name the name of the message to post
|
|
269
|
+
* @param {object} args.data (optional) to send with the message
|
|
270
|
+
*/
|
|
271
|
+
tellIframe(args) {
|
|
272
|
+
postMessage(args.name, args.data, true);
|
|
273
|
+
},
|
|
274
|
+
/**
|
|
275
|
+
* Register an event handler. The callback will be stored
|
|
276
|
+
* for the particular iFrame ID so that you can listen to
|
|
277
|
+
* multiple iFrames if you want
|
|
278
|
+
*
|
|
279
|
+
* @param {string} name of the event
|
|
280
|
+
* @param {Function} callback
|
|
281
|
+
*/
|
|
282
|
+
on(name, callback) {
|
|
283
|
+
log(`listening to event "${name}"`);
|
|
284
|
+
if (!eventHandlers[iframeElement.iframeId]) {
|
|
285
|
+
eventHandlers[iframeElement.iframeId] = {};
|
|
286
|
+
}
|
|
287
|
+
if (!eventHandlers[iframeElement.iframeId][name]) {
|
|
288
|
+
eventHandlers[iframeElement.iframeId][name] = [];
|
|
289
|
+
}
|
|
290
|
+
eventHandlers[iframeElement.iframeId][name].push(callback);
|
|
291
|
+
},
|
|
292
|
+
/**
|
|
293
|
+
* Unregister all handlers for an event. If name is null
|
|
294
|
+
* then all handlers will be removed
|
|
295
|
+
*
|
|
296
|
+
* @param {string} name (optional) of the event
|
|
297
|
+
*/
|
|
298
|
+
off(name) {
|
|
299
|
+
log(`stop listening to event "${name}"`);
|
|
300
|
+
if (iframeElement.iframeId) {
|
|
301
|
+
if (eventHandlers[iframeElement.iframeId]) {
|
|
302
|
+
if (!name) {
|
|
303
|
+
eventHandlers[iframeElement.iframeId] = {};
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
delete eventHandlers[iframeElement.iframeId][name];
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
else if (!name) {
|
|
311
|
+
eventHandlers = {};
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
for (const handlerMap of Object.values(eventHandlers)) {
|
|
315
|
+
delete handlerMap[name];
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
/**
|
|
320
|
+
* Tears down the plugin by
|
|
321
|
+
* - removing all event handlers
|
|
322
|
+
* - stop listening to the 'message' window event
|
|
323
|
+
*/
|
|
324
|
+
destroy() {
|
|
325
|
+
$(window).off('message', handleWindowMessage);
|
|
326
|
+
parentIsListening = false;
|
|
327
|
+
iframeIsListening = false;
|
|
328
|
+
iframeElement = null;
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
/* eslint-disable no-param-reassign */
|
|
332
|
+
/**
|
|
333
|
+
* The method that we expose to jQuery. Is used to set options
|
|
334
|
+
* or perform actions
|
|
335
|
+
*
|
|
336
|
+
* @param {mixed} actionOrOptions string of an action to perform
|
|
337
|
+
* object of options to set
|
|
338
|
+
* @param {object} args to pass to an action method
|
|
339
|
+
* @param {function} callback (optional) for event handlers
|
|
340
|
+
*
|
|
341
|
+
* @returns {[type]}
|
|
342
|
+
*/
|
|
343
|
+
$.fn.expensifyIframeify = function (actionOrOptions, args, callback) {
|
|
344
|
+
/* eslint-enable no-param-reassign */
|
|
345
|
+
const urlArray = defaultOrigin.split('://');
|
|
346
|
+
const domainArray = urlArray[1].split('.');
|
|
347
|
+
const subdomain = domainArray.shift();
|
|
348
|
+
const domainWithoutSubdomain = domainArray.join('.');
|
|
349
|
+
// There are some browsers that don't support document.domain so we have to manually create it
|
|
350
|
+
document.domain = domainWithoutSubdomain;
|
|
351
|
+
iframeElement = this;
|
|
352
|
+
if (!wasInitalized) {
|
|
353
|
+
// Determine if this is the parent, or the iframe
|
|
354
|
+
// Because our site runs inside of an iframe in salesforce, the parent
|
|
355
|
+
// will actually think it's an iframe too. So here, we detect that we're on
|
|
356
|
+
// the salesforce subdomain and we know that we should be the parent
|
|
357
|
+
whatIAm = window.parent === window || subdomain === 'salesforce' ? 'parent' : 'iframe';
|
|
358
|
+
log('I am the:', whatIAm);
|
|
359
|
+
wasInitalized = true;
|
|
360
|
+
}
|
|
361
|
+
// The iframeId is pulled from the frameElement (if this is inside an iFrame)
|
|
362
|
+
// or it's pulled from the element that this plugin was called from.
|
|
363
|
+
//
|
|
364
|
+
// Example:
|
|
365
|
+
// $( '#myIframe' ).expensifyIframeify();
|
|
366
|
+
//
|
|
367
|
+
// 'myIframe' would be the frameId
|
|
368
|
+
iframeElement.iframeId = whatIAm === 'iframe' ? $(window.frameElement).attr('id') : iframeElement.attr('id');
|
|
369
|
+
// Figure out if we are doing an action or setting options
|
|
370
|
+
if (typeof actionOrOptions === 'string') {
|
|
371
|
+
if (typeof actions[actionOrOptions] !== 'undefined') {
|
|
372
|
+
actions[actionOrOptions](args, callback);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
// Extend our default settings
|
|
377
|
+
settings = $.extend(settings, actionOrOptions);
|
|
378
|
+
}
|
|
379
|
+
// If this is the parent page, then we can start listening to messages
|
|
380
|
+
// from the iFrame
|
|
381
|
+
if (whatIAm === 'parent' && !parentIsListening) {
|
|
382
|
+
$(window).on('message', handleWindowMessage);
|
|
383
|
+
parentIsListening = true;
|
|
384
|
+
log('parent is listening for messages');
|
|
385
|
+
}
|
|
386
|
+
// If this is the iFrame, then we can start listening to messages
|
|
387
|
+
// from the iFrame
|
|
388
|
+
if (whatIAm === 'iframe' && !iframeIsListening) {
|
|
389
|
+
$(window).on('message', handleWindowMessage);
|
|
390
|
+
iframeIsListening = true;
|
|
391
|
+
log('iframe is listening for messages');
|
|
392
|
+
}
|
|
393
|
+
// make this plugin chainable
|
|
394
|
+
return this;
|
|
395
|
+
};
|
|
396
|
+
},
|
|
397
|
+
};
|