@smoxy-io/js-utils 1.0.0
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/LICENSE +21 -0
- package/README.md +5 -0
- package/classes/config.js +119 -0
- package/classes/env.js +111 -0
- package/classes/fetch.js +97 -0
- package/classes/session/jwt.js +94 -0
- package/classes/session.js +42 -0
- package/lib/function.js +12 -0
- package/lib/iframe.js +45 -0
- package/lib/jwt.js +15 -0
- package/lib/numbers.js +42 -0
- package/lib/object.js +4 -0
- package/lib/promise.js +22 -0
- package/lib/string.js +21 -0
- package/lib/styles.js +14 -0
- package/lib/time.js +13 -0
- package/package.json +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 smoxy-io
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {isBoolean, isEqual, isFunction, isPlainObject, isString, merge} from 'lodash';
|
|
2
|
+
import {plain} from '../lib/object';
|
|
3
|
+
import {has} from 'lodash/object';
|
|
4
|
+
|
|
5
|
+
const defaultProps = {};
|
|
6
|
+
|
|
7
|
+
export class Config {
|
|
8
|
+
constructor(props = {}) {
|
|
9
|
+
if (!props) {
|
|
10
|
+
props = {};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (!isPlainObject(props)) {
|
|
14
|
+
props = plain(props);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
merge(this, defaultProps, props);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getConfig(key, defaultValue = null) {
|
|
21
|
+
return getConfig(key, defaultValue);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getTitle(defaultValue = '') {
|
|
25
|
+
return getTitle(defaultValue);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getSubTitle(defaultValue = '') {
|
|
29
|
+
return getSubTitle(defaultValue);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getStyles() {
|
|
33
|
+
return getStyles();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let config = new Config();
|
|
38
|
+
let setConfigFn = (o) => {};
|
|
39
|
+
|
|
40
|
+
export const mergeConfig = (delta) => {
|
|
41
|
+
if (!isPlainObject(delta)) {
|
|
42
|
+
delta = plain(delta);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let _config = new Config(merge({}, config, delta));
|
|
46
|
+
|
|
47
|
+
if (isEqual(plain(config), plain(_config))) {
|
|
48
|
+
// nothing changed, so don't update the config object'
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// console.log('[DEBUG] updating config state', 'old', plain(config), 'delta', delta);
|
|
53
|
+
|
|
54
|
+
setConfigFn(_config);
|
|
55
|
+
|
|
56
|
+
config = _config;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const loadConfig = (setConfig) => {
|
|
60
|
+
if (isFunction(setConfig)) {
|
|
61
|
+
setConfigFn = setConfig;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const getConfig = (key, defaultValue = null) => {
|
|
66
|
+
if (!key || !isString(key)) {
|
|
67
|
+
return defaultValue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
key = key.trim();
|
|
71
|
+
|
|
72
|
+
if (!key || !has(config, key)) {
|
|
73
|
+
return defaultValue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (isBoolean(config[key])) {
|
|
77
|
+
return config[key];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!config[key]) {
|
|
81
|
+
return defaultValue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return config[key];
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const getTitle = (defaultValue = '') => {
|
|
88
|
+
let title = getConfig('title', defaultValue);
|
|
89
|
+
|
|
90
|
+
if (title === false) {
|
|
91
|
+
return '';
|
|
92
|
+
} else if (title === true) {
|
|
93
|
+
return defaultValue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return title;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const getSubTitle = (defaultValue = '') => {
|
|
100
|
+
let subTitle = getConfig('subTitle', defaultValue);
|
|
101
|
+
|
|
102
|
+
if (subTitle === false) {
|
|
103
|
+
return '';
|
|
104
|
+
} else if (subTitle === true) {
|
|
105
|
+
return defaultValue;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return subTitle;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export const getStyles = () => {
|
|
112
|
+
const styles = getConfig('styles', {});
|
|
113
|
+
|
|
114
|
+
if (!isPlainObject(styles)) {
|
|
115
|
+
return {};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return styles;
|
|
119
|
+
};
|
package/classes/env.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import {isEqual, isFunction, isPlainObject, merge} from 'lodash';
|
|
2
|
+
import {plain} from '../lib/object';
|
|
3
|
+
import {isAsync} from '../lib/function';
|
|
4
|
+
|
|
5
|
+
export const WindowLocation = 'windowLocation';
|
|
6
|
+
export const ParentWindowLocation = 'parentWindowLocation';
|
|
7
|
+
|
|
8
|
+
export class Env {
|
|
9
|
+
constructor(props = {}) {
|
|
10
|
+
if (!isPlainObject(props)) {
|
|
11
|
+
if (props instanceof Env) {
|
|
12
|
+
props = plain(props);
|
|
13
|
+
} else {
|
|
14
|
+
props = {};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
merge(this, {}, props);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let env = new Env();
|
|
23
|
+
let setEnvFn = (o) => {}; // a noop function by default
|
|
24
|
+
|
|
25
|
+
export const loadEnv = async (setEnv, apiClient) => {
|
|
26
|
+
let getEnv = null;
|
|
27
|
+
|
|
28
|
+
if (setEnv && !isFunction(setEnv) && isAsync(setEnv.getEnv)) {
|
|
29
|
+
// setEnv is actually the apiClient. adjust accordingly
|
|
30
|
+
getEnv = apiClient.getEnv;
|
|
31
|
+
setEnv = null;
|
|
32
|
+
apiClient = null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (isFunction(setEnv)) {
|
|
36
|
+
setEnvFn = setEnv;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const e = {};
|
|
40
|
+
|
|
41
|
+
if (window) {
|
|
42
|
+
e[WindowLocation] = {
|
|
43
|
+
href: window.location.href,
|
|
44
|
+
origin: window.location.origin,
|
|
45
|
+
pathname: window.location.pathname,
|
|
46
|
+
search: window.location.search,
|
|
47
|
+
hash: window.location.hash,
|
|
48
|
+
host: window.location.host,
|
|
49
|
+
hostname: window.location.hostname,
|
|
50
|
+
port: window.location.port,
|
|
51
|
+
protocol: window.location.protocol
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (apiClient && isAsync(apiClient.getEnv)) {
|
|
56
|
+
getEnv = apiClient.getEnv;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (getEnv) {
|
|
60
|
+
const envInfo = await getEnv();
|
|
61
|
+
|
|
62
|
+
if (!isPlainObject(envInfo)) {
|
|
63
|
+
mergeEnv(e);
|
|
64
|
+
|
|
65
|
+
throw new Error('failed to load env. invalid response: ' + JSON.stringify(envInfo));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
merge(e, envInfo);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
mergeEnv(merge(e));
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const mergeEnv = (delta) => {
|
|
75
|
+
if (!isPlainObject(delta)) {
|
|
76
|
+
delta = plain(delta);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let _env = new Env(merge({}, env, delta));
|
|
80
|
+
|
|
81
|
+
if (isEqual(plain(env), plain(_env))) {
|
|
82
|
+
// nothing changed, so don't update the env object'
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// console.log('[DEBUG] updating env state', 'old', plain(env), 'delta', delta);
|
|
87
|
+
|
|
88
|
+
setEnvFn(_env);
|
|
89
|
+
|
|
90
|
+
env = _env;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const getEnv = (name, defaultValue = '') => {
|
|
94
|
+
const e = env[name];
|
|
95
|
+
|
|
96
|
+
if (e) {
|
|
97
|
+
return e;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return defaultValue;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const getTopWindowLocation = (e = null) => {
|
|
104
|
+
const _env = e || env;
|
|
105
|
+
|
|
106
|
+
if (_env[ParentWindowLocation] && isPlainObject(_env[ParentWindowLocation])) {
|
|
107
|
+
return _env[ParentWindowLocation];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return _env[WindowLocation];
|
|
111
|
+
}
|
package/classes/fetch.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
|
|
2
|
+
export class Fetch {}
|
|
3
|
+
|
|
4
|
+
Fetch.Get = (url) => {
|
|
5
|
+
return fetch(url, {
|
|
6
|
+
method: 'GET',
|
|
7
|
+
headers: headers(url),
|
|
8
|
+
credentials: 'include',
|
|
9
|
+
}).then(handleResponse);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
Fetch.Post = (url, body) => {
|
|
13
|
+
return fetch(url, {
|
|
14
|
+
method: 'POST',
|
|
15
|
+
headers: headers(url, true),
|
|
16
|
+
credentials: 'include',
|
|
17
|
+
body: JSON.stringify(body)
|
|
18
|
+
}).then(handleResponse);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
Fetch.PostForm = (url, body) => {
|
|
22
|
+
return fetch(url, {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
headers: headers(url, true, true),
|
|
25
|
+
credentials: 'include',
|
|
26
|
+
body: body
|
|
27
|
+
}).then(handleResponse);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
Fetch.Put = (url, body) => {
|
|
31
|
+
return fetch(url, {
|
|
32
|
+
method: 'PUT',
|
|
33
|
+
headers: headers(url, true),
|
|
34
|
+
credentials: 'include',
|
|
35
|
+
body: JSON.stringify(body)
|
|
36
|
+
}).then(handleResponse);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Fetch.Delete = (url, body = null) => {
|
|
40
|
+
let opts = {
|
|
41
|
+
method: 'DELETE',
|
|
42
|
+
headers: headers(url),
|
|
43
|
+
credentials: 'include',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
if (body) {
|
|
47
|
+
opts.headers = headers(url, true);
|
|
48
|
+
opts['body'] = JSON.stringify(body);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return fetch(url, opts).then(handleResponse);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
function headers(url, hasBody = false, formData = false) {
|
|
55
|
+
let headers = {
|
|
56
|
+
Accept: 'application/json',
|
|
57
|
+
'User-Agent': 'pooling-numbers-widget/v1',
|
|
58
|
+
'Upgrade-Insecure-Requests': 1,
|
|
59
|
+
'Accept-Encoding': 'gzip, deflate'
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
if (hasBody && !formData) {
|
|
63
|
+
headers['Content-Type'] = 'application/json'
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return headers;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function handleResponse(resp) {
|
|
70
|
+
return resp.text().then((raw) => {
|
|
71
|
+
let data = null;
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
data = raw && JSON.parse(raw);
|
|
75
|
+
} catch (e) {
|
|
76
|
+
data = {success: false, resp: raw, parseError: e};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!resp.ok) {
|
|
80
|
+
// if (AccessDeniedCodes.includes(resp.status)) {
|
|
81
|
+
// userService.logout();
|
|
82
|
+
//
|
|
83
|
+
// }
|
|
84
|
+
return Promise.reject(new ResponseNotOkError((data && data.error) || resp.statusText, data));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return data;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export class ResponseNotOkError extends Error {
|
|
92
|
+
constructor(message, data) {
|
|
93
|
+
super(message);
|
|
94
|
+
this.name = 'ResponseNotOkError';
|
|
95
|
+
this.data = data;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import {isNumber, isPlainObject, merge} from 'lodash';
|
|
2
|
+
import {plain} from '../../lib/object';
|
|
3
|
+
import {isAsync} from '../../lib/function';
|
|
4
|
+
import {Session} from '../session';
|
|
5
|
+
import {decodeToken} from '../../lib/jwt';
|
|
6
|
+
|
|
7
|
+
const defaultProps = {
|
|
8
|
+
token: '',
|
|
9
|
+
_decodedToken: null,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export class JwtSession extends Session {
|
|
13
|
+
constructor(props = {}) {
|
|
14
|
+
if (!isPlainObject(props)) {
|
|
15
|
+
props = plain(props);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (!props.token && props.jwt) {
|
|
19
|
+
props.token = props.jwt;
|
|
20
|
+
delete props.jwt;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (props._decodedToken) {
|
|
24
|
+
delete props._decodedToken;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
super(merge({}, defaultProps, props));
|
|
28
|
+
|
|
29
|
+
if (this.token) {
|
|
30
|
+
this._decodedToken = decodeToken(this.token);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async renew(apiClient) {
|
|
35
|
+
if (!this.token || !this.sid) {
|
|
36
|
+
throw new Error('cannot renew session: missing token or sid');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!apiClient || !isAsync(apiClient.renew)) {
|
|
40
|
+
throw new Error('cannot renew session: no renew function set');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const resp = await apiClient.renew(this.token, this.sid);
|
|
44
|
+
|
|
45
|
+
if (!resp) {
|
|
46
|
+
throw new Error('failed to renew session');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const newSession = new JwtSession(resp);
|
|
50
|
+
|
|
51
|
+
this.token = newSession.token;
|
|
52
|
+
this.sid = newSession.sid;
|
|
53
|
+
this._decodedToken = null;
|
|
54
|
+
|
|
55
|
+
if (this.token) {
|
|
56
|
+
this._decodedToken = decodeToken(this.token);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
clear() {
|
|
61
|
+
super.clear();
|
|
62
|
+
|
|
63
|
+
this.token = '';
|
|
64
|
+
this._decodedToken = null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
isExpired() {
|
|
68
|
+
if (!this.token) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!this._decodedToken) {
|
|
73
|
+
this._decodedToken = decodeToken(this.token);
|
|
74
|
+
|
|
75
|
+
if (!this._decodedToken) {
|
|
76
|
+
// invalid token, clear it out
|
|
77
|
+
this.clear();
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!this._decodedToken.exp || !isNumber(this._decodedToken.exp)) {
|
|
83
|
+
// all jwt tokens have an exp claim formatted as a Unix timestamp. if it is missing, assume the token is invalid
|
|
84
|
+
this.clear();
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return this._decodedToken.exp * 1000 <= Date.now();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
isLoggedIn() {
|
|
92
|
+
return !this.isExpired();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {isPlainObject, merge} from 'lodash';
|
|
2
|
+
import {plain} from '../lib/object';
|
|
3
|
+
|
|
4
|
+
const defaultProps = {
|
|
5
|
+
sid: ''
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export class Session {
|
|
9
|
+
constructor(props = {}) {
|
|
10
|
+
if (!isPlainObject(props)) {
|
|
11
|
+
props = plain(props);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (!props.sid && props.sessionId) {
|
|
15
|
+
props.sid = props.sessionId;
|
|
16
|
+
delete props.sessionId;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!props.sid && props.sessionID) {
|
|
20
|
+
props.sid = props.sessionID;
|
|
21
|
+
delete props.sessionID;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (props.renew) {
|
|
25
|
+
delete props.renew;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
merge(this, {}, defaultProps, props);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
renew(apiClient) {
|
|
32
|
+
throw new Error('Session.renew() must be implemented in the child class');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
clear() {
|
|
36
|
+
this.sid = '';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
isLoggedIn() {
|
|
40
|
+
return this.sid !== '';
|
|
41
|
+
}
|
|
42
|
+
}
|
package/lib/function.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {isFunction} from 'lodash/lang';
|
|
2
|
+
|
|
3
|
+
export const arrowAsyncNoop = async () => {};
|
|
4
|
+
export const asyncNoop = async function () {};
|
|
5
|
+
|
|
6
|
+
export const isAsync = (fn) => {
|
|
7
|
+
return fn && (fn.constructor === arrowAsyncNoop.constructor || fn.constructor === asyncNoop.constructor);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const isArrow = (fn) => {
|
|
11
|
+
return fn && isFunction(fn) && fn.prototype === undefined;
|
|
12
|
+
};
|
package/lib/iframe.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import FrameBus from 'framebus';
|
|
2
|
+
import {isPlainObject, merge} from 'lodash';
|
|
3
|
+
import {mergeEnv, ParentWindowLocation} from '../classes/env';
|
|
4
|
+
import {mergeConfig} from '../classes/config';
|
|
5
|
+
|
|
6
|
+
export const init = () => {
|
|
7
|
+
if (window.parent === window) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const bus = new FrameBus({
|
|
12
|
+
targetFrames: [window.parent],
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
bus.emitAsPromise('env').then((pEnv) => {
|
|
16
|
+
if (pEnv && isPlainObject(pEnv)) {
|
|
17
|
+
if (!pEnv[ParentWindowLocation]) {
|
|
18
|
+
pEnv[ParentWindowLocation] = {hostname: 'example.com', origin: 'https://example.com'};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
mergeEnv(merge({}, pEnv, {inIframe: true}));
|
|
22
|
+
}
|
|
23
|
+
}).catch((e) => {
|
|
24
|
+
console.error('failed to get env from parent frame', e);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
bus.emitAsPromise('config').then((pConfig) => {
|
|
28
|
+
mergeConfig(merge({}, pConfig));
|
|
29
|
+
}).catch((e) => {
|
|
30
|
+
console.error('failed to get config from parent frame', e);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
let prevScrollHeight = 0;
|
|
34
|
+
|
|
35
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
36
|
+
const newScrollHeight = document.body.scrollHeight;
|
|
37
|
+
|
|
38
|
+
if (prevScrollHeight !== newScrollHeight) {
|
|
39
|
+
bus.emit('resize', {height: newScrollHeight, width: document.body.scrollWidth});
|
|
40
|
+
prevScrollHeight = newScrollHeight;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
resizeObserver.observe(document.body);
|
|
45
|
+
};
|
package/lib/jwt.js
ADDED
package/lib/numbers.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
|
|
2
|
+
const Billion = 1000000000;
|
|
3
|
+
const Million = 1000000;
|
|
4
|
+
const Thousand = 1000;
|
|
5
|
+
|
|
6
|
+
const BillionSymbol = 'B';
|
|
7
|
+
const MillionSymbol = 'M';
|
|
8
|
+
const ThousandSymbol = 'K';
|
|
9
|
+
|
|
10
|
+
export const shortFormat = (num = 0.0, currencySymbol = '') => {
|
|
11
|
+
if (num < Thousand) {
|
|
12
|
+
return currencySymbol + num.toFixed(0);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (num < Million) {
|
|
16
|
+
let fmtNum = (num / Thousand).toFixed(1);
|
|
17
|
+
|
|
18
|
+
if (fmtNum.includes('.0')) {
|
|
19
|
+
fmtNum = (num / Thousand).toFixed(0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return currencySymbol + fmtNum + ThousandSymbol;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (num < Billion) {
|
|
26
|
+
let fmtNum = (num / Million).toFixed(1);
|
|
27
|
+
|
|
28
|
+
if (fmtNum.includes('.0')) {
|
|
29
|
+
fmtNum = (num / Million).toFixed(0);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return currencySymbol + fmtNum + MillionSymbol;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let fmtNum = (num / Billion).toFixed(1);
|
|
36
|
+
|
|
37
|
+
if (fmtNum.includes('.0')) {
|
|
38
|
+
fmtNum = (num / Billion).toFixed(0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return currencySymbol + fmtNum + BillionSymbol;
|
|
42
|
+
};
|
package/lib/object.js
ADDED
package/lib/promise.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
export const fromCallback = (fn, ...args) => {
|
|
3
|
+
return new Promise((resolve, reject) => {
|
|
4
|
+
fn(...args, (...cbArgs) => {
|
|
5
|
+
if (cbArgs.length === 0) {
|
|
6
|
+
return resolve();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const err = cbArgs.shift();
|
|
10
|
+
|
|
11
|
+
if (err) {
|
|
12
|
+
return reject(err);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
resolve(...cbArgs);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const fromCb = (fn, ...args) => {
|
|
21
|
+
return fromCallback(fn, ...args);
|
|
22
|
+
};
|
package/lib/string.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
export const capitalizeWords = (text) => {
|
|
3
|
+
if (!text) {
|
|
4
|
+
return text;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const words = text.split(' ');
|
|
8
|
+
|
|
9
|
+
const res = [];
|
|
10
|
+
|
|
11
|
+
for (const word of words) {
|
|
12
|
+
if (!word) {
|
|
13
|
+
res.push('');
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
res.push(word[0].toUpperCase() + word.substring(1));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return res.join(' ');
|
|
21
|
+
};
|
package/lib/styles.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
export const addGlobalStyle = (css) => {
|
|
3
|
+
if (!document || !document.head) {
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const head = document.head;
|
|
8
|
+
const style = document.createElement('style');
|
|
9
|
+
|
|
10
|
+
style.type = 'text/css';
|
|
11
|
+
style.appendChild(document.createTextNode(css));
|
|
12
|
+
|
|
13
|
+
head.appendChild(style);
|
|
14
|
+
}
|
package/lib/time.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {format} from "date-fns";
|
|
2
|
+
|
|
3
|
+
export const formatTime = (date) => {
|
|
4
|
+
return format(date, 'MM/dd/yyyy h:mma');
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const getTime = (timestamp) => {
|
|
8
|
+
return new Date(timestamp * 1000);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const formatDate = (date) => {
|
|
12
|
+
return format(date, 'MM/dd/yyyy');
|
|
13
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@smoxy-io/js-utils",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "https://github.com/smoxy-io/js-utils.git"
|
|
7
|
+
},
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"framebus": "^6.0.3",
|
|
11
|
+
"jwt-decode": "^4.0.0",
|
|
12
|
+
"lodash": "^4.17.21"
|
|
13
|
+
}
|
|
14
|
+
}
|