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,68 @@
|
|
|
1
|
+
declare namespace _default {
|
|
2
|
+
export let NAME_LAST_PAGE: string;
|
|
3
|
+
export let NAME_LAST_REPORTS_PAGE: string;
|
|
4
|
+
export let NAME_STASHED_SUPPORT: string;
|
|
5
|
+
export let NAME_WEB_COOKIE: string;
|
|
6
|
+
export let NAME_EXPENSES_VIEW_MODE: string;
|
|
7
|
+
export let MOBILE_APP_VERSION: string;
|
|
8
|
+
export let TWO_FACTOR_DEVICE_TOKEN: string;
|
|
9
|
+
export { enabled };
|
|
10
|
+
export { remove };
|
|
11
|
+
export { get };
|
|
12
|
+
export { getJSON };
|
|
13
|
+
export { has };
|
|
14
|
+
export { getHelpLink };
|
|
15
|
+
export { set };
|
|
16
|
+
}
|
|
17
|
+
export default _default;
|
|
18
|
+
/**
|
|
19
|
+
* Detects if cookies are currently enabled in the browser
|
|
20
|
+
* by trying to create a cookie, see if it exists, and delete it afterwards again.
|
|
21
|
+
*
|
|
22
|
+
* @returns {Boolean} True if cookies are enabled, otherwise false.
|
|
23
|
+
*/
|
|
24
|
+
declare function enabled(): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Deletes a cookie.
|
|
27
|
+
*
|
|
28
|
+
* @param {String} name The name of the cookie to delete.
|
|
29
|
+
*/
|
|
30
|
+
declare function remove(name: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Fetches the value of a cookie.
|
|
33
|
+
*
|
|
34
|
+
* @param {String} name The name of the cookie to fetch.
|
|
35
|
+
* @returns {String|null} The value of the cookie.
|
|
36
|
+
*/
|
|
37
|
+
declare function get(name: string): string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Parses a cookie value to JSON.
|
|
40
|
+
* Returns a default value specified by param or null.
|
|
41
|
+
*
|
|
42
|
+
* @param {String} name
|
|
43
|
+
* @param {Any|null} defaultValue
|
|
44
|
+
*
|
|
45
|
+
* @returns {Any|null}
|
|
46
|
+
*/
|
|
47
|
+
declare function getJSON(name: string, defaultValue?: Any | null): Any | null;
|
|
48
|
+
/**
|
|
49
|
+
* Find a cookie that has been set.
|
|
50
|
+
*
|
|
51
|
+
* @param {String} name Name of the cookie to find
|
|
52
|
+
* @returns {Boolean} Whether or not the cookie is set
|
|
53
|
+
*/
|
|
54
|
+
declare function has(name: string): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Returns help link when cookies are enabled or null.
|
|
57
|
+
*
|
|
58
|
+
* @returns {(String|null)}
|
|
59
|
+
*/
|
|
60
|
+
declare function getHelpLink(): (string | null);
|
|
61
|
+
/**
|
|
62
|
+
* Sets the value of a cookie and when it will expire.
|
|
63
|
+
*
|
|
64
|
+
* @param {String} name The name of the cookie.
|
|
65
|
+
* @param {String} value The value of the cookie.
|
|
66
|
+
* @param {Number} [expiredays] The number of days until the cookie expires.
|
|
67
|
+
*/
|
|
68
|
+
declare function set(name: string, value: string, expiredays?: number): void;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import BrowserDetect from './BrowserDetect';
|
|
2
|
+
const CONST = {
|
|
3
|
+
/**
|
|
4
|
+
* Cookie storing the the name of the page we visited the last time
|
|
5
|
+
*/
|
|
6
|
+
NAME_LAST_PAGE: 'lastPage',
|
|
7
|
+
/**
|
|
8
|
+
* Cookie storing the name of the reports page we visited the last time (e.g. reports, inbox)
|
|
9
|
+
*/
|
|
10
|
+
NAME_LAST_REPORTS_PAGE: 'lastReportsPage',
|
|
11
|
+
/**
|
|
12
|
+
* Cookie storing a user's restricted-access support token
|
|
13
|
+
*/
|
|
14
|
+
NAME_STASHED_SUPPORT: '_stashedSupportLogin',
|
|
15
|
+
/**
|
|
16
|
+
* Cookie set by expensify web that's used to identify a request as coming from web.
|
|
17
|
+
*/
|
|
18
|
+
NAME_WEB_COOKIE: 'expensifyWeb',
|
|
19
|
+
/**
|
|
20
|
+
* Cookie storing the mode the user wants to use when viewing the expenses page.
|
|
21
|
+
*/
|
|
22
|
+
NAME_EXPENSES_VIEW_MODE: 'expensesPageViewMode',
|
|
23
|
+
/**
|
|
24
|
+
* Cookie set that denotes a mobile request
|
|
25
|
+
*/
|
|
26
|
+
MOBILE_APP_VERSION: 'appversion',
|
|
27
|
+
/**
|
|
28
|
+
* Two-Factor Auth device token
|
|
29
|
+
*/
|
|
30
|
+
TWO_FACTOR_DEVICE_TOKEN: 'stashedTwoFactorLogin',
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Deletes a cookie.
|
|
34
|
+
*
|
|
35
|
+
* @param {String} name The name of the cookie to delete.
|
|
36
|
+
*/
|
|
37
|
+
function remove(name) {
|
|
38
|
+
const date = new Date();
|
|
39
|
+
document.cookie = `${name}=; expires=${date.toUTCString()}`;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Sets the value of a cookie and when it will expire.
|
|
43
|
+
*
|
|
44
|
+
* @param {String} name The name of the cookie.
|
|
45
|
+
* @param {String} value The value of the cookie.
|
|
46
|
+
* @param {Number} [expiredays] The number of days until the cookie expires.
|
|
47
|
+
*/
|
|
48
|
+
function set(name, value, expiredays) {
|
|
49
|
+
// Get expiry date, set
|
|
50
|
+
const exdate = new Date();
|
|
51
|
+
exdate.setDate(exdate.getDate() + expiredays);
|
|
52
|
+
document.cookie = `${name}=${encodeURIComponent(value)}${expiredays === null ? '' : `;expires=${exdate.toUTCString()}`}`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Detects if cookies are currently enabled in the browser
|
|
56
|
+
* by trying to create a cookie, see if it exists, and delete it afterwards again.
|
|
57
|
+
*
|
|
58
|
+
* @returns {Boolean} True if cookies are enabled, otherwise false.
|
|
59
|
+
*/
|
|
60
|
+
function enabled() {
|
|
61
|
+
// Check they have cookies enabled
|
|
62
|
+
const cookieName = `cookieTest_${Math.floor(Math.random() * 1000)}`;
|
|
63
|
+
const cookieValue = 'enabled';
|
|
64
|
+
set(cookieName, cookieValue, 1);
|
|
65
|
+
const result = document.cookie.indexOf(cookieName) >= 0 || false;
|
|
66
|
+
if (result) {
|
|
67
|
+
remove(cookieName);
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Fetches the value of a cookie.
|
|
73
|
+
*
|
|
74
|
+
* @param {String} name The name of the cookie to fetch.
|
|
75
|
+
* @returns {String|null} The value of the cookie.
|
|
76
|
+
*/
|
|
77
|
+
function get(name) {
|
|
78
|
+
if (!name || document.cookie.length <= 0) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
let start;
|
|
82
|
+
let end;
|
|
83
|
+
start = document.cookie.indexOf(`${name}=`);
|
|
84
|
+
if (start !== -1) {
|
|
85
|
+
start = start + name.length + 1;
|
|
86
|
+
end = document.cookie.indexOf(';', start);
|
|
87
|
+
if (end === -1) {
|
|
88
|
+
end = document.cookie.length;
|
|
89
|
+
}
|
|
90
|
+
return decodeURIComponent(document.cookie.substring(start, end));
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Parses a cookie value to JSON.
|
|
96
|
+
* Returns a default value specified by param or null.
|
|
97
|
+
*
|
|
98
|
+
* @param {String} name
|
|
99
|
+
* @param {Any|null} defaultValue
|
|
100
|
+
*
|
|
101
|
+
* @returns {Any|null}
|
|
102
|
+
*/
|
|
103
|
+
function getJSON(name, defaultValue = null) {
|
|
104
|
+
let data;
|
|
105
|
+
try {
|
|
106
|
+
data = JSON.parse(get(name));
|
|
107
|
+
}
|
|
108
|
+
catch (_a) {
|
|
109
|
+
data = defaultValue;
|
|
110
|
+
}
|
|
111
|
+
return data;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Find a cookie that has been set.
|
|
115
|
+
*
|
|
116
|
+
* @param {String} name Name of the cookie to find
|
|
117
|
+
* @returns {Boolean} Whether or not the cookie is set
|
|
118
|
+
*/
|
|
119
|
+
function has(name) {
|
|
120
|
+
return get(name) !== null;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Returns help link when cookies are enabled or null.
|
|
124
|
+
*
|
|
125
|
+
* @returns {(String|null)}
|
|
126
|
+
*/
|
|
127
|
+
function getHelpLink() {
|
|
128
|
+
if (enabled()) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
let helpLink = 'http://www.google.com/search?q=enable+cookies';
|
|
132
|
+
// Set up help link according to browser
|
|
133
|
+
if (BrowserDetect.browser === BrowserDetect.BROWSERS.EXPLORER) {
|
|
134
|
+
helpLink = 'https://support.microsoft.com/en-us/help/17442/windows-internet-explorer-delete-manage-cookies';
|
|
135
|
+
}
|
|
136
|
+
else if (BrowserDetect.browser === BrowserDetect.BROWSERS.SAFARI) {
|
|
137
|
+
helpLink = 'https://support.apple.com/guide/safari/manage-cookies-and-website-data-sfri11471/mac';
|
|
138
|
+
}
|
|
139
|
+
else if (BrowserDetect.browser === BrowserDetect.BROWSERS.MOZILLA) {
|
|
140
|
+
helpLink = 'http://support.mozilla.com/en-US/kb/Enabling%20and%20disabling%20cookies';
|
|
141
|
+
}
|
|
142
|
+
else if (BrowserDetect.browser === BrowserDetect.BROWSERS.OPERA) {
|
|
143
|
+
helpLink = 'http://www.opera.com/help/tutorials/security/privacy/';
|
|
144
|
+
}
|
|
145
|
+
else if (BrowserDetect.browser === BrowserDetect.BROWSERS.CHROME) {
|
|
146
|
+
helpLink = 'http://www.google.com/support/chrome/bin/answer.py?answer=95647';
|
|
147
|
+
}
|
|
148
|
+
else if (BrowserDetect.browser === BrowserDetect.BROWSERS.EDGE) {
|
|
149
|
+
helpLink = 'https://privacy.microsoft.com/en-us/windows-10-microsoft-edge-and-privacy';
|
|
150
|
+
}
|
|
151
|
+
return helpLink;
|
|
152
|
+
}
|
|
153
|
+
export default Object.assign({ enabled,
|
|
154
|
+
remove,
|
|
155
|
+
get,
|
|
156
|
+
getJSON,
|
|
157
|
+
has,
|
|
158
|
+
getHelpLink,
|
|
159
|
+
set }, CONST);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export default CredentialWrapper;
|
|
2
|
+
declare namespace CredentialWrapper {
|
|
3
|
+
/**
|
|
4
|
+
* @returns {Promise} Promise resolves to an object containing partnerUserID and partnerUserSecret
|
|
5
|
+
*/
|
|
6
|
+
function getCredentials(): Promise<any>;
|
|
7
|
+
/**
|
|
8
|
+
* @returns {Object} Object contains randomly generated partnerUserID and partnerUserSecret
|
|
9
|
+
*/
|
|
10
|
+
function generateCredentials(): Object;
|
|
11
|
+
/**
|
|
12
|
+
* @param {Object} credentials
|
|
13
|
+
* @param {String} credentials.partnerUserID
|
|
14
|
+
* @param {String} credentials.partnerUserSecret
|
|
15
|
+
*
|
|
16
|
+
* @returns {Promise} Promise resolves to an object containing partnerUserID and partnerUserSecret
|
|
17
|
+
*/
|
|
18
|
+
function setCredentials(credentials: {
|
|
19
|
+
partnerUserID: string;
|
|
20
|
+
partnerUserSecret: string;
|
|
21
|
+
}): Promise<any>;
|
|
22
|
+
/**
|
|
23
|
+
* @returns {Promise}
|
|
24
|
+
*/
|
|
25
|
+
function clearCredentials(): Promise<any>;
|
|
26
|
+
}
|
|
27
|
+
export namespace LOGIN_PARTNER_DETAILS {
|
|
28
|
+
let CREDENTIALS_KEY: string;
|
|
29
|
+
let EXPENSIFY_PARTNER_PREFIX: string;
|
|
30
|
+
let PARTNER_NAME: string;
|
|
31
|
+
let PARTNER_PASSWORD: string;
|
|
32
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import localForage from 'localforage';
|
|
2
|
+
const LOGIN_PARTNER_DETAILS = {
|
|
3
|
+
CREDENTIALS_KEY: 'DEVICE_SESSION_CREDENTIALS',
|
|
4
|
+
EXPENSIFY_PARTNER_PREFIX: 'expensify.',
|
|
5
|
+
PARTNER_NAME: 'chat-expensify-com',
|
|
6
|
+
PARTNER_PASSWORD: 'e21965746fd75f82bb66',
|
|
7
|
+
};
|
|
8
|
+
const CredentialWrapper = {
|
|
9
|
+
/**
|
|
10
|
+
* @returns {Promise} Promise resolves to an object containing partnerUserID and partnerUserSecret
|
|
11
|
+
*/
|
|
12
|
+
getCredentials() {
|
|
13
|
+
return localForage.getItem(LOGIN_PARTNER_DETAILS.CREDENTIALS_KEY);
|
|
14
|
+
},
|
|
15
|
+
/**
|
|
16
|
+
* @returns {Object} Object contains randomly generated partnerUserID and partnerUserSecret
|
|
17
|
+
*/
|
|
18
|
+
generateCredentials() {
|
|
19
|
+
return {
|
|
20
|
+
partnerUserID: LOGIN_PARTNER_DETAILS.EXPENSIFY_PARTNER_PREFIX + crypto.randomUUID(),
|
|
21
|
+
partnerUserSecret: crypto.randomUUID(),
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
/**
|
|
25
|
+
* @param {Object} credentials
|
|
26
|
+
* @param {String} credentials.partnerUserID
|
|
27
|
+
* @param {String} credentials.partnerUserSecret
|
|
28
|
+
*
|
|
29
|
+
* @returns {Promise} Promise resolves to an object containing partnerUserID and partnerUserSecret
|
|
30
|
+
*/
|
|
31
|
+
setCredentials(credentials) {
|
|
32
|
+
if (!credentials.partnerUserID || !credentials.partnerUserSecret) {
|
|
33
|
+
// eslint-disable-next-line prefer-promise-reject-errors
|
|
34
|
+
return Promise.reject('Invalid credential pair');
|
|
35
|
+
}
|
|
36
|
+
return localForage.setItem(LOGIN_PARTNER_DETAILS.CREDENTIALS_KEY, credentials);
|
|
37
|
+
},
|
|
38
|
+
/**
|
|
39
|
+
* @returns {Promise}
|
|
40
|
+
*/
|
|
41
|
+
clearCredentials() {
|
|
42
|
+
return localForage.removeItem(LOGIN_PARTNER_DETAILS.CREDENTIALS_KEY);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
export default CredentialWrapper;
|
|
46
|
+
export { LOGIN_PARTNER_DETAILS };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { UAParser } from 'ua-parser-js';
|
|
2
|
+
function getOSAndName() {
|
|
3
|
+
var _a;
|
|
4
|
+
const parser = new UAParser();
|
|
5
|
+
const { browser, os } = parser.getResult();
|
|
6
|
+
let osVersion = os.version;
|
|
7
|
+
// Detection logic inspired by UAParser guide on iOS 26:
|
|
8
|
+
// https://docs.uaparser.dev/guides/how-to-detect-ios-26-using-javascript.html
|
|
9
|
+
const [osMajor, osMinor] = ((_a = os.version) !== null && _a !== void 0 ? _a : '').split('.');
|
|
10
|
+
const osMajorInt = parseInt(osMajor, 10);
|
|
11
|
+
const osMinorInt = parseInt(osMinor, 10);
|
|
12
|
+
if (browser.name === 'Mobile Safari' && browser.major === '26' && os.name === 'iOS' && (osMajorInt > 18 || (osMajorInt === 18 && osMinorInt >= 6))) {
|
|
13
|
+
osVersion = '26';
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
os: os.name,
|
|
17
|
+
osVersion,
|
|
18
|
+
deviceName: browser.name,
|
|
19
|
+
deviceVersion: browser.version,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
// eslint-disable-next-line import/prefer-default-export
|
|
24
|
+
getOSAndName, };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export default class ExpenseRule {
|
|
2
|
+
/**
|
|
3
|
+
* Checks the passed value against the actual based on what the condition (matches, greater, less than, etc.)
|
|
4
|
+
*
|
|
5
|
+
* @param {string} condition
|
|
6
|
+
* @param {Mixed} ruleValue
|
|
7
|
+
* @param {Mixed} transactionValue
|
|
8
|
+
* @returns {boolean}
|
|
9
|
+
*/
|
|
10
|
+
static checkCondition(condition: string, ruleValue: Mixed, transactionValue: Mixed): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new instance of this class.
|
|
13
|
+
*
|
|
14
|
+
* @param {Object|Array} ruleArray
|
|
15
|
+
*/
|
|
16
|
+
constructor(ruleArray: Object | any[]);
|
|
17
|
+
/**
|
|
18
|
+
* Returns the applyWhen array associated with the passed field
|
|
19
|
+
* i.e. field == category returns [field: 'category', condition: 'matches', value: 'car']
|
|
20
|
+
*
|
|
21
|
+
* @param {string} field
|
|
22
|
+
*
|
|
23
|
+
* @returns {Object}
|
|
24
|
+
*/
|
|
25
|
+
getApplyWhenByField(field: string): Object;
|
|
26
|
+
/**
|
|
27
|
+
* Get the externalID saved deep in the tax field
|
|
28
|
+
*
|
|
29
|
+
* @returns {String}
|
|
30
|
+
*/
|
|
31
|
+
getExternalTaxID(): string;
|
|
32
|
+
/**
|
|
33
|
+
* Checks if the passed expense matches this expense rule
|
|
34
|
+
*
|
|
35
|
+
* @param {SExpense3} expense
|
|
36
|
+
* @returns {boolean}
|
|
37
|
+
*/
|
|
38
|
+
isMatch(expense: SExpense3): boolean;
|
|
39
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export default class ExpenseRule {
|
|
2
|
+
/**
|
|
3
|
+
* Creates a new instance of this class.
|
|
4
|
+
*
|
|
5
|
+
* @param {Object|Array} ruleArray
|
|
6
|
+
*/
|
|
7
|
+
constructor(ruleArray) {
|
|
8
|
+
// It's not 100% certain that `ruleArray` is an array or an object, so support both of them so the app doesn't crash
|
|
9
|
+
if (Array.isArray(ruleArray)) {
|
|
10
|
+
for (const [key, value] of ruleArray.entries()) {
|
|
11
|
+
this[key] = value;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
else if (ruleArray && typeof ruleArray === 'object') {
|
|
15
|
+
for (const key of Object.keys(ruleArray)) {
|
|
16
|
+
this[key] = ruleArray[key];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Returns the applyWhen array associated with the passed field
|
|
22
|
+
* i.e. field == category returns [field: 'category', condition: 'matches', value: 'car']
|
|
23
|
+
*
|
|
24
|
+
* @param {string} field
|
|
25
|
+
*
|
|
26
|
+
* @returns {Object}
|
|
27
|
+
*/
|
|
28
|
+
getApplyWhenByField(field) {
|
|
29
|
+
return this.applyWhen.find((conditions) => conditions.field === field) || {};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get the externalID saved deep in the tax field
|
|
33
|
+
*
|
|
34
|
+
* @returns {String}
|
|
35
|
+
*/
|
|
36
|
+
getExternalTaxID() {
|
|
37
|
+
return this.tax.field_id_TAX.externalID || '';
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Checks if the passed expense matches this expense rule
|
|
41
|
+
*
|
|
42
|
+
* @param {SExpense3} expense
|
|
43
|
+
* @returns {boolean}
|
|
44
|
+
*/
|
|
45
|
+
isMatch(expense) {
|
|
46
|
+
let isMatch = true;
|
|
47
|
+
for (const conditions of this.applyWhen) {
|
|
48
|
+
switch (conditions.field) {
|
|
49
|
+
case 'category':
|
|
50
|
+
if (!ExpenseRule.checkCondition(conditions.condition, conditions.value, expense.getCategory())) {
|
|
51
|
+
isMatch = false;
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
default:
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return isMatch;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Checks the passed value against the actual based on what the condition (matches, greater, less than, etc.)
|
|
62
|
+
*
|
|
63
|
+
* @param {string} condition
|
|
64
|
+
* @param {Mixed} ruleValue
|
|
65
|
+
* @param {Mixed} transactionValue
|
|
66
|
+
* @returns {boolean}
|
|
67
|
+
*/
|
|
68
|
+
static checkCondition(condition, ruleValue, transactionValue) {
|
|
69
|
+
// Add more condition types (Greater than, Less than, Contains) below
|
|
70
|
+
switch (condition) {
|
|
71
|
+
case 'matches':
|
|
72
|
+
return ruleValue === transactionValue;
|
|
73
|
+
default:
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import Logger from './Logger';
|
|
2
|
+
type Extras = {
|
|
3
|
+
reportIDToName?: Record<string, string>;
|
|
4
|
+
accountIDToName?: Record<string, string>;
|
|
5
|
+
/**
|
|
6
|
+
* @deprecated Replaced with mediaAttributeCachingFn
|
|
7
|
+
*/
|
|
8
|
+
cacheVideoAttributes?: (vidSource: string, attrs: string) => void;
|
|
9
|
+
/**
|
|
10
|
+
* @deprecated Replaced with mediaAttributeCache
|
|
11
|
+
*/
|
|
12
|
+
videoAttributeCache?: Record<string, string>;
|
|
13
|
+
/**
|
|
14
|
+
* Function used to cache HTML tag attributes during conversion to and from Markdown
|
|
15
|
+
* @param mediaSource URI to media source
|
|
16
|
+
* @param attrs Additional attributes to be cached
|
|
17
|
+
*/
|
|
18
|
+
mediaAttributeCachingFn?: (mediaSource: string, attrs: string) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Key/value cache for HTML tag attributes, where the key is media source URI, value is cached attributes
|
|
21
|
+
*/
|
|
22
|
+
mediaAttributeCache?: Record<string, string>;
|
|
23
|
+
};
|
|
24
|
+
export type { Extras };
|
|
25
|
+
type ReplacementFn = (extras: Extras, ...matches: string[]) => string;
|
|
26
|
+
type Replacement = ReplacementFn | string;
|
|
27
|
+
type ProcessFn = (textToProcess: string, replacement: Replacement, shouldKeepRawInput: boolean) => string;
|
|
28
|
+
type CommonRule = {
|
|
29
|
+
name: string;
|
|
30
|
+
replacement: Replacement;
|
|
31
|
+
rawInputReplacement?: Replacement;
|
|
32
|
+
pre?: (input: string) => string;
|
|
33
|
+
post?: (input: string) => string;
|
|
34
|
+
shouldSkipProcessing?: (textToCheck: string) => boolean;
|
|
35
|
+
};
|
|
36
|
+
type RuleWithRegex = CommonRule & {
|
|
37
|
+
regex: RegExp;
|
|
38
|
+
};
|
|
39
|
+
type RuleWithProcess = CommonRule & {
|
|
40
|
+
process: ProcessFn;
|
|
41
|
+
};
|
|
42
|
+
type Rule = RuleWithRegex | RuleWithProcess;
|
|
43
|
+
type ReplaceOptions = {
|
|
44
|
+
extras?: Extras;
|
|
45
|
+
filterRules?: string[];
|
|
46
|
+
disabledRules?: string[];
|
|
47
|
+
shouldEscapeText?: boolean;
|
|
48
|
+
shouldKeepRawInput?: boolean;
|
|
49
|
+
};
|
|
50
|
+
type TruncateOptions = {
|
|
51
|
+
ellipsis?: string;
|
|
52
|
+
truncateLastWord?: boolean;
|
|
53
|
+
slop?: number;
|
|
54
|
+
removeImageTag?: boolean;
|
|
55
|
+
};
|
|
56
|
+
declare function resolveAttributeCache(extras?: Extras): {
|
|
57
|
+
attrCachingFn: ((vidSource: string, attrs: string) => void) | undefined;
|
|
58
|
+
attrCache: Record<string, string> | undefined;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* replace block element with '\n' if :
|
|
62
|
+
* 1. We have text within the element.
|
|
63
|
+
* 2. The text does not end with a new line.
|
|
64
|
+
* 3. It's not the last element in the string.
|
|
65
|
+
*/
|
|
66
|
+
declare function replaceBlockElementWithNewLine(htmlString: string): string;
|
|
67
|
+
/** Check if the input text includes only the open or the close tag of an element. */
|
|
68
|
+
declare function containsNonPairTag(textToCheck: string): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Truncate HTML string and keep tag safe.
|
|
71
|
+
* pulled from https://github.com/huang47/nodejs-html-truncate/blob/master/lib/truncate.js
|
|
72
|
+
*/
|
|
73
|
+
declare function truncateHTML(html: string, maxLength: number, options?: TruncateOptions): string;
|
|
74
|
+
export default class ExpensiMark {
|
|
75
|
+
static Log: Logger;
|
|
76
|
+
/**
|
|
77
|
+
* Set the logger to use for logging inside of the ExpensiMark class
|
|
78
|
+
* @param logger - The logger object to use
|
|
79
|
+
*/
|
|
80
|
+
static setLogger(logger: Logger): void;
|
|
81
|
+
/** Rules to apply to the text */
|
|
82
|
+
rules: Rule[];
|
|
83
|
+
/**
|
|
84
|
+
* The list of regex replacements to do on a HTML comment for converting it to markdown.
|
|
85
|
+
* Order of rules is important
|
|
86
|
+
*/
|
|
87
|
+
htmlToMarkdownRules: RuleWithRegex[];
|
|
88
|
+
/**
|
|
89
|
+
* The list of rules to covert the HTML to text.
|
|
90
|
+
* Order of rules is important
|
|
91
|
+
*/
|
|
92
|
+
htmlToTextRules: RuleWithRegex[];
|
|
93
|
+
/**
|
|
94
|
+
* The list of rules that we have to exclude in shouldKeepWhitespaceRules list.
|
|
95
|
+
*/
|
|
96
|
+
whitespaceRulesToDisable: string[];
|
|
97
|
+
/**
|
|
98
|
+
* The list of rules that have to be applied when shouldKeepWhitespace flag is true.
|
|
99
|
+
*/
|
|
100
|
+
filterRules: (rule: Rule) => boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Filters rules to determine which should keep whitespace.
|
|
103
|
+
*/
|
|
104
|
+
shouldKeepWhitespaceRules: Rule[];
|
|
105
|
+
/**
|
|
106
|
+
* maxQuoteDepth is the maximum depth of nested quotes that we want to support.
|
|
107
|
+
*/
|
|
108
|
+
maxQuoteDepth: number;
|
|
109
|
+
/**
|
|
110
|
+
* currentQuoteDepth is the current depth of nested quotes that we are processing.
|
|
111
|
+
*/
|
|
112
|
+
currentQuoteDepth: number;
|
|
113
|
+
constructor();
|
|
114
|
+
/**
|
|
115
|
+
* Retrieves the HTML ruleset based on the provided filter rules, disabled rules, and shouldKeepRawInput flag.
|
|
116
|
+
* @param filterRules - An array of rule names to filter the ruleset.
|
|
117
|
+
* @param disabledRules - An array of rule names to disable in the ruleset.
|
|
118
|
+
* @param shouldKeepRawInput - A boolean flag indicating whether to keep raw input.
|
|
119
|
+
*/
|
|
120
|
+
getHtmlRuleset(filterRules: string[], disabledRules: string[], shouldKeepRawInput: boolean): Rule[];
|
|
121
|
+
/**
|
|
122
|
+
* Replaces markdown with html elements
|
|
123
|
+
*
|
|
124
|
+
* @param text - Text to parse as markdown
|
|
125
|
+
* @param [options] - Options to customize the markdown parser
|
|
126
|
+
* @param [options.filterRules=[]] - An array of name of rules as defined in this class.
|
|
127
|
+
* If not provided, all available rules will be applied.
|
|
128
|
+
* @param [options.shouldEscapeText=true] - Whether or not the text should be escaped
|
|
129
|
+
* @param [options.disabledRules=[]] - An array of name of rules as defined in this class.
|
|
130
|
+
* If not provided, all available rules will be applied. If provided, the rules in the array will be skipped.
|
|
131
|
+
*/
|
|
132
|
+
replace(text: string, { filterRules, shouldEscapeText, shouldKeepRawInput, disabledRules, extras }?: ReplaceOptions): string;
|
|
133
|
+
/**
|
|
134
|
+
* Checks matched URLs for validity and replace valid links with html elements
|
|
135
|
+
*/
|
|
136
|
+
modifyTextForUrlLinks(regex: RegExp, textToCheck: string, replacement: ReplacementFn): string;
|
|
137
|
+
/**
|
|
138
|
+
* Checks matched Emails for validity and replace valid links with html elements
|
|
139
|
+
*/
|
|
140
|
+
modifyTextForEmailLinks(regex: RegExp, textToCheck: string, replacement: ReplacementFn, shouldKeepRawInput: boolean): string;
|
|
141
|
+
/**
|
|
142
|
+
* Unpacks nested quote HTML tags that have been packed by the 'quote' rule in this.rules for shouldKeepRawInput = false
|
|
143
|
+
*
|
|
144
|
+
* For example, it parses the following HTML:
|
|
145
|
+
* <blockquote>
|
|
146
|
+
* quote 1
|
|
147
|
+
* <blockquote>
|
|
148
|
+
* quote 2
|
|
149
|
+
* </blockquote>
|
|
150
|
+
* quote 3
|
|
151
|
+
* </blockquote>
|
|
152
|
+
*
|
|
153
|
+
* into:
|
|
154
|
+
* <blockquote> quote 1</blockquote>
|
|
155
|
+
* <blockquote><blockquote> quote 2</blockquote>
|
|
156
|
+
* <blockquote> quote 3</blockquote>
|
|
157
|
+
*
|
|
158
|
+
* Note that there will always be only a single closing tag, even if multiple opening tags exist.
|
|
159
|
+
* Only one closing tag is needed to detect if a nested quote has ended.
|
|
160
|
+
*/
|
|
161
|
+
static unpackNestedQuotes(text: string): string;
|
|
162
|
+
/**
|
|
163
|
+
* Replaces HTML with markdown
|
|
164
|
+
*/
|
|
165
|
+
htmlToMarkdown(htmlString: string, extras?: Extras): string;
|
|
166
|
+
/**
|
|
167
|
+
* Convert HTML to text
|
|
168
|
+
*/
|
|
169
|
+
htmlToText(htmlString: string, extras?: Extras): string;
|
|
170
|
+
/**
|
|
171
|
+
* Main text to html 'quote' parsing logic.
|
|
172
|
+
* Removes >( ) from text and recursively calls replace function to process nested quotes and build blockquote HTML result.
|
|
173
|
+
* @param shouldKeepRawInput determines if the raw input should be kept for nested quotes.
|
|
174
|
+
*/
|
|
175
|
+
replaceQuoteText(text: string, shouldKeepRawInput: boolean): {
|
|
176
|
+
replacedText: string;
|
|
177
|
+
shouldAddSpace: boolean;
|
|
178
|
+
};
|
|
179
|
+
getAttributeCache: typeof resolveAttributeCache;
|
|
180
|
+
replaceBlockElementWithNewLine: typeof replaceBlockElementWithNewLine;
|
|
181
|
+
containsNonPairTag: typeof containsNonPairTag;
|
|
182
|
+
truncateHTML: typeof truncateHTML;
|
|
183
|
+
/**
|
|
184
|
+
* @returns array or undefined if exception occurs when executing regex matching
|
|
185
|
+
*/
|
|
186
|
+
extractLinksInMarkdownComment(comment: string): string[] | undefined;
|
|
187
|
+
/**
|
|
188
|
+
* Compares two markdown comments and returns a list of the links removed in a new comment.
|
|
189
|
+
*/
|
|
190
|
+
getRemovedMarkdownLinks(oldComment: string, newComment: string): string[];
|
|
191
|
+
/**
|
|
192
|
+
* Escapes the content of an HTML attribute value
|
|
193
|
+
* @param content - string content that possible contains HTML
|
|
194
|
+
* @returns original MD content escaped for use in HTML attribute value
|
|
195
|
+
*/
|
|
196
|
+
escapeAttributeContent(content: string): string;
|
|
197
|
+
}
|