sculp-js 0.0.1
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.md +21 -0
- package/README.md +3 -0
- package/lib/cjs/array.js +217 -0
- package/lib/cjs/async.js +63 -0
- package/lib/cjs/clipboard.js +36 -0
- package/lib/cjs/cookie.js +63 -0
- package/lib/cjs/date.js +114 -0
- package/lib/cjs/dom.js +152 -0
- package/lib/cjs/download.js +82 -0
- package/lib/cjs/easing.js +40 -0
- package/lib/cjs/file.js +30 -0
- package/lib/cjs/index.js +41 -0
- package/lib/cjs/object.js +241 -0
- package/lib/cjs/path.js +66 -0
- package/lib/cjs/qs.js +71 -0
- package/lib/cjs/string.js +134 -0
- package/lib/cjs/type.js +47 -0
- package/lib/cjs/url.js +84 -0
- package/lib/cjs/watermark.js +91 -0
- package/lib/es/array.js +208 -0
- package/lib/es/async.js +60 -0
- package/lib/es/clipboard.js +34 -0
- package/lib/es/cookie.js +59 -0
- package/lib/es/date.js +110 -0
- package/lib/es/dom.js +143 -0
- package/lib/es/download.js +77 -0
- package/lib/es/easing.js +38 -0
- package/lib/es/file.js +28 -0
- package/lib/es/index.js +35 -0
- package/lib/es/object.js +228 -0
- package/lib/es/path.js +63 -0
- package/lib/es/qs.js +68 -0
- package/lib/es/string.js +124 -0
- package/lib/es/type.js +28 -0
- package/lib/es/url.js +79 -0
- package/lib/es/watermark.js +89 -0
- package/lib/index.d.ts +644 -0
- package/lib/umd/index.js +1458 -0
- package/package.json +80 -0
package/lib/es/dom.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* sculp-js v0.0.1
|
|
3
|
+
* (c) 2023-2023 chandq
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { arrayEach } from './array.js';
|
|
8
|
+
import { easingFunctional } from './easing.js';
|
|
9
|
+
import { objectEach, objectAssign } from './object.js';
|
|
10
|
+
import { stringKebabCase } from './string.js';
|
|
11
|
+
import { isObject } from './type.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 判断元素是否包含某个样式名
|
|
15
|
+
* @param {HTMLElement} el
|
|
16
|
+
* @param {string} className
|
|
17
|
+
* @returns {boolean}
|
|
18
|
+
*/
|
|
19
|
+
const hasClass = (el, className) => {
|
|
20
|
+
if (className.indexOf(' ') !== -1)
|
|
21
|
+
throw new Error('className should not contain space.');
|
|
22
|
+
return el.classList.contains(className);
|
|
23
|
+
};
|
|
24
|
+
const eachClassName = (classNames, func) => {
|
|
25
|
+
const classNameList = classNames.split(/\s+/g);
|
|
26
|
+
classNameList.forEach(func);
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* 给元素增加样式名
|
|
30
|
+
* @param {HTMLElement} el
|
|
31
|
+
* @param {string} classNames
|
|
32
|
+
*/
|
|
33
|
+
const addClass = (el, classNames) => {
|
|
34
|
+
eachClassName(classNames, className => el.classList.add(className));
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* 给元素移除样式名
|
|
38
|
+
* @param {HTMLElement} el
|
|
39
|
+
* @param {string} classNames
|
|
40
|
+
*/
|
|
41
|
+
const removeClass = (el, classNames) => {
|
|
42
|
+
eachClassName(classNames, className => el.classList.remove(className));
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* 设置元素样式
|
|
46
|
+
* @param {HTMLElement} el
|
|
47
|
+
* @param {string | Style} key
|
|
48
|
+
* @param {string} val
|
|
49
|
+
*/
|
|
50
|
+
const setStyle = (el, key, val) => {
|
|
51
|
+
if (isObject(key)) {
|
|
52
|
+
objectEach(key, (val1, key1) => {
|
|
53
|
+
setStyle(el, key1, val1);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
el.style.setProperty(stringKebabCase(key), val);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* 获取元素样式
|
|
62
|
+
* @param {HTMLElement} el
|
|
63
|
+
* @param {string} key
|
|
64
|
+
* @returns {string}
|
|
65
|
+
*/
|
|
66
|
+
const getStyle = (el, key) => getComputedStyle(el).getPropertyValue(key);
|
|
67
|
+
async function smoothScroll(options) {
|
|
68
|
+
return new Promise(resolve => {
|
|
69
|
+
const defaults = {
|
|
70
|
+
el: document,
|
|
71
|
+
to: 0,
|
|
72
|
+
duration: 567,
|
|
73
|
+
easing: 'ease'
|
|
74
|
+
};
|
|
75
|
+
const { el, to, duration, easing } = objectAssign(defaults, options);
|
|
76
|
+
const htmlEl = document.documentElement;
|
|
77
|
+
const bodyEl = document.body;
|
|
78
|
+
const globalMode = el === window || el === document || el === htmlEl || el === bodyEl;
|
|
79
|
+
const els = globalMode ? [htmlEl, bodyEl] : [el];
|
|
80
|
+
const query = () => {
|
|
81
|
+
let value = 0;
|
|
82
|
+
arrayEach(els, el => {
|
|
83
|
+
if ('scrollTop' in el) {
|
|
84
|
+
value = el.scrollTop;
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return value;
|
|
89
|
+
};
|
|
90
|
+
const update = (val) => {
|
|
91
|
+
els.forEach(el => {
|
|
92
|
+
if ('scrollTop' in el) {
|
|
93
|
+
el.scrollTop = val;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
let startTime;
|
|
98
|
+
const startValue = query();
|
|
99
|
+
const length = to - startValue;
|
|
100
|
+
const easingFn = easingFunctional(easing);
|
|
101
|
+
const render = () => {
|
|
102
|
+
const now = performance.now();
|
|
103
|
+
const passingTime = startTime ? now - startTime : 0;
|
|
104
|
+
const t = passingTime / duration;
|
|
105
|
+
const p = easingFn(t);
|
|
106
|
+
if (!startTime)
|
|
107
|
+
startTime = now;
|
|
108
|
+
update(startValue + length * p);
|
|
109
|
+
if (t >= 1)
|
|
110
|
+
resolve();
|
|
111
|
+
else
|
|
112
|
+
requestAnimationFrame(render);
|
|
113
|
+
};
|
|
114
|
+
render();
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
const domReadyCallbacks = [];
|
|
118
|
+
const eventType = 'DOMContentLoaded';
|
|
119
|
+
const listener = () => {
|
|
120
|
+
domReadyCallbacks.forEach(callback => callback());
|
|
121
|
+
domReadyCallbacks.length = 0;
|
|
122
|
+
document.removeEventListener(eventType, listener);
|
|
123
|
+
};
|
|
124
|
+
document.addEventListener(eventType, listener);
|
|
125
|
+
let readied = false;
|
|
126
|
+
function isDomReady() {
|
|
127
|
+
if (readied)
|
|
128
|
+
return true;
|
|
129
|
+
readied = ['complete', 'loaded', 'interactive'].indexOf(document.readyState) !== -1;
|
|
130
|
+
return readied;
|
|
131
|
+
}
|
|
132
|
+
function onDomReady(callback) {
|
|
133
|
+
// document readied
|
|
134
|
+
if (isDomReady()) {
|
|
135
|
+
setTimeout(callback, 0);
|
|
136
|
+
}
|
|
137
|
+
// listen document to ready
|
|
138
|
+
else {
|
|
139
|
+
domReadyCallbacks.push(callback);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export { addClass, getStyle, hasClass, isDomReady, onDomReady, removeClass, setStyle, smoothScroll };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* sculp-js v0.0.1
|
|
3
|
+
* (c) 2023-2023 chandq
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { urlSetParams } from './url.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 通过打开新窗口的方式下载
|
|
11
|
+
* @param {string} url
|
|
12
|
+
* @param {LooseParams} params
|
|
13
|
+
*/
|
|
14
|
+
const downloadURL = (url, params) => {
|
|
15
|
+
window.open(params ? urlSetParams(url, params) : url);
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* 通过 A 链接的方式下载
|
|
19
|
+
* @param {string} href
|
|
20
|
+
* @param {string} filename
|
|
21
|
+
*/
|
|
22
|
+
const downloadHref = (href, filename) => {
|
|
23
|
+
const eleLink = document.createElement('a');
|
|
24
|
+
eleLink.download = filename;
|
|
25
|
+
eleLink.style.display = 'none';
|
|
26
|
+
eleLink.href = href;
|
|
27
|
+
document.body.appendChild(eleLink);
|
|
28
|
+
eleLink.click();
|
|
29
|
+
setTimeout(() => document.body.removeChild(eleLink));
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* 将大文件对象通过 A 链接的方式下载
|
|
33
|
+
* @param {Blob} blob
|
|
34
|
+
* @param {string} filename
|
|
35
|
+
*/
|
|
36
|
+
const downloadBlob = (blob, filename) => {
|
|
37
|
+
const objURL = URL.createObjectURL(blob);
|
|
38
|
+
downloadHref(objURL, filename);
|
|
39
|
+
setTimeout(() => URL.revokeObjectURL(objURL));
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* 将指定数据格式通过 A 链接的方式下载
|
|
43
|
+
* @param {AnyObject | AnyObject[]} data
|
|
44
|
+
* @param {FileType} fileType 支持 json/csv/xls/xlsx 四种格式
|
|
45
|
+
* @param {string} filename
|
|
46
|
+
* @param {string[]} [headers]
|
|
47
|
+
*/
|
|
48
|
+
const downloadData = (data, fileType, filename, headers) => {
|
|
49
|
+
filename = filename.replace(`.${fileType}`, '') + `.${fileType}`;
|
|
50
|
+
if (fileType === 'json') {
|
|
51
|
+
const blob = new Blob([JSON.stringify(data, null, 4)]);
|
|
52
|
+
downloadBlob(blob, filename);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// xlsx实际生成的也为csv,仅后缀名名不同
|
|
56
|
+
if (!headers || !headers.length)
|
|
57
|
+
throw new Error('未传入表头数据');
|
|
58
|
+
if (!Array.isArray(data))
|
|
59
|
+
throw new Error('data error! expected array!');
|
|
60
|
+
const headerStr = headers.join(',') + '\n';
|
|
61
|
+
let bodyStr = '';
|
|
62
|
+
data.forEach(row => {
|
|
63
|
+
// \t防止数字被科学计数法显示
|
|
64
|
+
bodyStr += Object.values(row).join(',\t') + ',\n';
|
|
65
|
+
});
|
|
66
|
+
const MIMETypes = {
|
|
67
|
+
csv: 'text/csv',
|
|
68
|
+
xls: 'application/vnd.ms-excel',
|
|
69
|
+
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
70
|
+
};
|
|
71
|
+
// encodeURIComponent解决中文乱码
|
|
72
|
+
const href = 'data:' + MIMETypes[fileType] + ';charset=utf-8,\ufeff' + encodeURIComponent(headerStr + bodyStr);
|
|
73
|
+
downloadHref(href, filename);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export { downloadBlob, downloadData, downloadHref, downloadURL };
|
package/lib/es/easing.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* sculp-js v0.0.1
|
|
3
|
+
* (c) 2023-2023 chandq
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import bezier from 'bezier-easing';
|
|
8
|
+
import { isArray } from './type.js';
|
|
9
|
+
|
|
10
|
+
// @ref https://cubic-bezier.com/
|
|
11
|
+
const easingDefines = {
|
|
12
|
+
linear: [0, 0, 1, 1],
|
|
13
|
+
ease: [0.25, 0.1, 0.25, 1],
|
|
14
|
+
'ease-in': [0.42, 0, 1, 1],
|
|
15
|
+
'ease-out': [0, 0, 0.58, 1],
|
|
16
|
+
'ease-in-out': [0.42, 0, 0.58, 1]
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* 缓冲函数化,用于 js 计算缓冲进度
|
|
20
|
+
* @param {EasingNameOrDefine} [name=linear]
|
|
21
|
+
* @returns {EasingFunction}
|
|
22
|
+
*/
|
|
23
|
+
function easingFunctional(name) {
|
|
24
|
+
let fn;
|
|
25
|
+
if (isArray(name)) {
|
|
26
|
+
fn = bezier(...name);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
const define = easingDefines[name];
|
|
30
|
+
if (!define) {
|
|
31
|
+
throw new Error(`${name} 缓冲函数未定义`);
|
|
32
|
+
}
|
|
33
|
+
fn = bezier(...define);
|
|
34
|
+
}
|
|
35
|
+
return (input) => fn(Math.max(0, Math.min(input, 1)));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { easingFunctional };
|
package/lib/es/file.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* sculp-js v0.0.1
|
|
3
|
+
* (c) 2023-2023 chandq
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 选择本地文件
|
|
9
|
+
* @param {function} changeCb 选择文件回调
|
|
10
|
+
* @return {*}
|
|
11
|
+
*/
|
|
12
|
+
function chooseLocalFile({ accept }, changeCb) {
|
|
13
|
+
const inputObj = document.createElement('input');
|
|
14
|
+
inputObj.setAttribute('id', String(Date.now()));
|
|
15
|
+
inputObj.setAttribute('type', 'file');
|
|
16
|
+
inputObj.setAttribute('style', 'visibility:hidden');
|
|
17
|
+
inputObj.setAttribute('accept', accept);
|
|
18
|
+
document.body.appendChild(inputObj);
|
|
19
|
+
inputObj.click();
|
|
20
|
+
// @ts-ignore
|
|
21
|
+
inputObj.onchange = (e) => {
|
|
22
|
+
changeCb(e.target.files);
|
|
23
|
+
setTimeout(() => document.body.removeChild(inputObj));
|
|
24
|
+
};
|
|
25
|
+
return inputObj;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { chooseLocalFile };
|
package/lib/es/index.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* sculp-js v0.0.1
|
|
3
|
+
* (c) 2023-2023 chandq
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as array from './array.js';
|
|
8
|
+
export { array };
|
|
9
|
+
import * as clipboard from './clipboard.js';
|
|
10
|
+
export { clipboard };
|
|
11
|
+
import * as cookie from './cookie.js';
|
|
12
|
+
export { cookie };
|
|
13
|
+
import * as date from './date.js';
|
|
14
|
+
export { date };
|
|
15
|
+
import * as dom from './dom.js';
|
|
16
|
+
export { dom };
|
|
17
|
+
import * as download from './download.js';
|
|
18
|
+
export { download };
|
|
19
|
+
import * as object from './object.js';
|
|
20
|
+
export { object };
|
|
21
|
+
import * as path from './path.js';
|
|
22
|
+
export { path };
|
|
23
|
+
import * as qs from './qs.js';
|
|
24
|
+
export { qs };
|
|
25
|
+
import * as string from './string.js';
|
|
26
|
+
export { string };
|
|
27
|
+
import * as type from './type.js';
|
|
28
|
+
export { type };
|
|
29
|
+
import * as url from './url.js';
|
|
30
|
+
export { url };
|
|
31
|
+
import * as async from './async.js';
|
|
32
|
+
export { async };
|
|
33
|
+
import * as file from './file.js';
|
|
34
|
+
export { file };
|
|
35
|
+
export { genCanvasWM } from './watermark.js';
|
package/lib/es/object.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* sculp-js v0.0.1
|
|
3
|
+
* (c) 2023-2023 chandq
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import typeIs, { isObject, isUndefined, isArray } from './type.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 判断对象是否为纯对象
|
|
11
|
+
* @param {object} obj
|
|
12
|
+
* @returns {boolean}
|
|
13
|
+
*/
|
|
14
|
+
const isPlainObject = (obj) => {
|
|
15
|
+
if (!isObject(obj))
|
|
16
|
+
return false;
|
|
17
|
+
const proto = Object.getPrototypeOf(obj);
|
|
18
|
+
// 对象无原型
|
|
19
|
+
if (!proto)
|
|
20
|
+
return true;
|
|
21
|
+
// 是否对象直接实例
|
|
22
|
+
return proto === Object.prototype;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* 判断对象内是否有该静态属性
|
|
26
|
+
* @param {object} obj
|
|
27
|
+
* @param {string} key
|
|
28
|
+
* @returns {boolean}
|
|
29
|
+
*/
|
|
30
|
+
const objectHas = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
|
|
31
|
+
/**
|
|
32
|
+
* 遍历对象,返回 false 中断遍历
|
|
33
|
+
* @param {O} obj
|
|
34
|
+
* @param {(val: O[keyof O], key: keyof O) => (boolean | void)} iterator
|
|
35
|
+
*/
|
|
36
|
+
const objectEach = (obj, iterator) => {
|
|
37
|
+
for (const key in obj) {
|
|
38
|
+
if (!objectHas(obj, key))
|
|
39
|
+
continue;
|
|
40
|
+
if (iterator(obj[key], key) === false)
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* 异步遍历对象,返回 false 中断遍历
|
|
46
|
+
* @param {O} obj
|
|
47
|
+
* @param {(val: O[keyof O], key: keyof O) => (boolean | void)} iterator
|
|
48
|
+
*/
|
|
49
|
+
async function objectEachAsync(obj, iterator) {
|
|
50
|
+
for (const key in obj) {
|
|
51
|
+
if (!objectHas(obj, key))
|
|
52
|
+
continue;
|
|
53
|
+
if ((await iterator(obj[key], key)) === false)
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 对象映射
|
|
59
|
+
* @param {O} obj
|
|
60
|
+
* @param {(val: O[keyof O], key: Extract<keyof O, string>) => any} iterator
|
|
61
|
+
* @returns {Record<Extract<keyof O, string>, T>}
|
|
62
|
+
*/
|
|
63
|
+
function objectMap(obj, iterator) {
|
|
64
|
+
const obj2 = {};
|
|
65
|
+
for (const key in obj) {
|
|
66
|
+
if (!objectHas(obj, key))
|
|
67
|
+
continue;
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
69
|
+
obj2[key] = iterator(obj[key], key);
|
|
70
|
+
}
|
|
71
|
+
return obj2;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 对象提取
|
|
75
|
+
* @param {O} obj
|
|
76
|
+
* @param {K} keys
|
|
77
|
+
* @returns {Pick<O, ArrayElements<K>>}
|
|
78
|
+
*/
|
|
79
|
+
function objectPick(obj, keys) {
|
|
80
|
+
const obj2 = {};
|
|
81
|
+
objectEach(obj, (v, k) => {
|
|
82
|
+
if (keys.includes(k)) {
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
84
|
+
// @ts-ignore
|
|
85
|
+
obj2[k] = v;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return obj2;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 对象祛除
|
|
92
|
+
* @param {O} obj
|
|
93
|
+
* @param {K} keys
|
|
94
|
+
* @returns {Pick<O, ArrayElements<K>>}
|
|
95
|
+
*/
|
|
96
|
+
function objectOmit(obj, keys) {
|
|
97
|
+
const obj2 = {};
|
|
98
|
+
objectEach(obj, (v, k) => {
|
|
99
|
+
if (!keys.includes(k)) {
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
101
|
+
// @ts-ignore
|
|
102
|
+
obj2[k] = v;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return obj2;
|
|
106
|
+
}
|
|
107
|
+
const merge = (map, source, target) => {
|
|
108
|
+
if (isUndefined(target))
|
|
109
|
+
return source;
|
|
110
|
+
const sourceType = typeIs(source);
|
|
111
|
+
const targetType = typeIs(target);
|
|
112
|
+
if (sourceType !== targetType) {
|
|
113
|
+
if (isArray(target))
|
|
114
|
+
return merge(map, [], target);
|
|
115
|
+
if (isObject(target))
|
|
116
|
+
return merge(map, {}, target);
|
|
117
|
+
return target;
|
|
118
|
+
}
|
|
119
|
+
// 朴素对象
|
|
120
|
+
if (isPlainObject(target)) {
|
|
121
|
+
const exist = map.get(target);
|
|
122
|
+
if (exist)
|
|
123
|
+
return exist;
|
|
124
|
+
map.set(target, source);
|
|
125
|
+
objectEach(target, (val, key) => {
|
|
126
|
+
source[key] = merge(map, source[key], val);
|
|
127
|
+
});
|
|
128
|
+
return source;
|
|
129
|
+
}
|
|
130
|
+
// 数组
|
|
131
|
+
else if (isArray(target)) {
|
|
132
|
+
const exist = map.get(target);
|
|
133
|
+
if (exist)
|
|
134
|
+
return exist;
|
|
135
|
+
map.set(target, source);
|
|
136
|
+
target.forEach((val, index) => {
|
|
137
|
+
source[index] = merge(map, source[index], val);
|
|
138
|
+
});
|
|
139
|
+
return source;
|
|
140
|
+
}
|
|
141
|
+
return target;
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* 对象合并,返回原始对象
|
|
145
|
+
* @param {ObjectAssignItem} source
|
|
146
|
+
* @param {ObjectAssignItem | undefined} targets
|
|
147
|
+
* @returns {R}
|
|
148
|
+
*/
|
|
149
|
+
const objectAssign = (source, ...targets) => {
|
|
150
|
+
const map = new Map();
|
|
151
|
+
for (let i = 0; i < targets.length; i++) {
|
|
152
|
+
const target = targets[i];
|
|
153
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
154
|
+
// @ts-ignore
|
|
155
|
+
source = merge(map, source, target);
|
|
156
|
+
}
|
|
157
|
+
map.clear();
|
|
158
|
+
return source;
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* 对象填充
|
|
162
|
+
* @param {Partial<R>} source
|
|
163
|
+
* @param {Partial<R>} target
|
|
164
|
+
* @param {(s: Partial<R>, t: Partial<R>, key: keyof R) => boolean} fillable
|
|
165
|
+
* @returns {R}
|
|
166
|
+
*/
|
|
167
|
+
const objectFill = (source, target, fillable) => {
|
|
168
|
+
const _fillable = fillable || ((source, target, key) => source[key] === undefined);
|
|
169
|
+
objectEach(target, (val, key) => {
|
|
170
|
+
if (_fillable(source, target, key)) {
|
|
171
|
+
source[key] = val;
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
return source;
|
|
175
|
+
};
|
|
176
|
+
function objectGet(obj, path, strict = false) {
|
|
177
|
+
path = path.replace(/\[(\w+)\]/g, '.$1');
|
|
178
|
+
path = path.replace(/^\./, '');
|
|
179
|
+
const keyArr = path.split('.');
|
|
180
|
+
let tempObj = obj;
|
|
181
|
+
let i = 0;
|
|
182
|
+
for (let len = keyArr.length; i < len - 1; ++i) {
|
|
183
|
+
const key = keyArr[i];
|
|
184
|
+
if (key in tempObj) {
|
|
185
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment
|
|
186
|
+
tempObj = tempObj[key];
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
tempObj = undefined;
|
|
190
|
+
if (strict) {
|
|
191
|
+
throw new Error('[berry/js-utils/object] objectGet path 路径不正确');
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
198
|
+
p: tempObj,
|
|
199
|
+
k: tempObj ? keyArr[i] : undefined,
|
|
200
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment
|
|
201
|
+
v: tempObj ? tempObj[keyArr[i]] : undefined
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* 深拷贝堪称完全体 即:任何类型的数据都会被深拷贝
|
|
206
|
+
* @param {AnyObject | AnyArray} obj
|
|
207
|
+
* @param {WeakMap} map
|
|
208
|
+
* @return {AnyObject | AnyArray}
|
|
209
|
+
*/
|
|
210
|
+
function cloneDeep(obj, map = new WeakMap()) {
|
|
211
|
+
if (obj instanceof Date)
|
|
212
|
+
return new Date(obj);
|
|
213
|
+
if (obj instanceof RegExp)
|
|
214
|
+
return new RegExp(obj);
|
|
215
|
+
if (map.has(obj)) {
|
|
216
|
+
return map.get(obj);
|
|
217
|
+
}
|
|
218
|
+
const allDesc = Object.getOwnPropertyDescriptors(obj);
|
|
219
|
+
const cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc);
|
|
220
|
+
map.set(obj, cloneObj);
|
|
221
|
+
for (const key of Reflect.ownKeys(obj)) {
|
|
222
|
+
const value = obj[key];
|
|
223
|
+
cloneObj[key] = value instanceof Object && typeof value !== 'function' ? cloneDeep(value, map) : value;
|
|
224
|
+
}
|
|
225
|
+
return cloneObj;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export { cloneDeep, isPlainObject, objectAssign, objectEach, objectEachAsync, objectFill, objectGet, objectHas, objectMap, objectAssign as objectMerge, objectOmit, objectPick };
|
package/lib/es/path.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* sculp-js v0.0.1
|
|
3
|
+
* (c) 2023-2023 chandq
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 标准化路径
|
|
9
|
+
* @param {string} path
|
|
10
|
+
* @returns {string}
|
|
11
|
+
*/
|
|
12
|
+
const pathNormalize = (path) => {
|
|
13
|
+
const slicees = path
|
|
14
|
+
.replace(/\\/g, '/')
|
|
15
|
+
.replace(/\/{2,}/g, '/')
|
|
16
|
+
.replace(/\.{3,}/g, '..')
|
|
17
|
+
.replace(/\/\.\//g, '/')
|
|
18
|
+
.split('/')
|
|
19
|
+
.map(point => point.trim());
|
|
20
|
+
const isCurrentSlice = (slice) => slice === '.';
|
|
21
|
+
const isParentSlice = (slice) => slice === '..';
|
|
22
|
+
const points = [];
|
|
23
|
+
let inPoints = false;
|
|
24
|
+
const push = (point) => {
|
|
25
|
+
points.push(point);
|
|
26
|
+
};
|
|
27
|
+
const back = () => {
|
|
28
|
+
if (points.length === 0)
|
|
29
|
+
return;
|
|
30
|
+
const lastSlice = points[points.length - 1];
|
|
31
|
+
if (isParentSlice(lastSlice)) {
|
|
32
|
+
points.push('..');
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
points.pop();
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
slicees.forEach(slice => {
|
|
39
|
+
const isCurrent = isCurrentSlice(slice);
|
|
40
|
+
const isParent = isParentSlice(slice);
|
|
41
|
+
// 未进入实际路径
|
|
42
|
+
if (!inPoints) {
|
|
43
|
+
push(slice);
|
|
44
|
+
inPoints = !isCurrent && !isParent;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (isCurrent)
|
|
48
|
+
return;
|
|
49
|
+
if (isParent)
|
|
50
|
+
return back();
|
|
51
|
+
push(slice);
|
|
52
|
+
});
|
|
53
|
+
return points.join('/');
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* 路径合并
|
|
57
|
+
* @param {string} from
|
|
58
|
+
* @param {string} to
|
|
59
|
+
* @returns {string}
|
|
60
|
+
*/
|
|
61
|
+
const pathJoin = (from, ...to) => pathNormalize([from, ...to].join('/'));
|
|
62
|
+
|
|
63
|
+
export { pathJoin, pathNormalize };
|
package/lib/es/qs.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* sculp-js v0.0.1
|
|
3
|
+
* (c) 2023-2023 chandq
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { objectEach } from './object.js';
|
|
8
|
+
import { isUndefined, isArray, isString, isNumber, isBoolean, isDate } from './type.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 解析查询参数,内部使用的是浏览器内置的 URLSearchParams 进行处理
|
|
12
|
+
* @param {string} queryString
|
|
13
|
+
* @returns {Params}
|
|
14
|
+
*/
|
|
15
|
+
const qsParse = (queryString) => {
|
|
16
|
+
const params = new URLSearchParams(queryString);
|
|
17
|
+
const result = {};
|
|
18
|
+
for (const [key, val] of params.entries()) {
|
|
19
|
+
if (isUndefined(result[key])) {
|
|
20
|
+
result[key] = val;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (isArray(result[key])) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
result[key] = params.getAll(key);
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
};
|
|
30
|
+
const defaultReplacer = (val) => {
|
|
31
|
+
if (isString(val))
|
|
32
|
+
return val;
|
|
33
|
+
if (isNumber(val))
|
|
34
|
+
return String(val);
|
|
35
|
+
if (isBoolean(val))
|
|
36
|
+
return val ? 'true' : 'false';
|
|
37
|
+
if (isDate(val))
|
|
38
|
+
return val.toISOString();
|
|
39
|
+
return null;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* 字符化查询对象,内部使用的是浏览器内置的 URLSearchParams 进行处理
|
|
43
|
+
* @param {LooseParams} query
|
|
44
|
+
* @param {Replacer} replacer
|
|
45
|
+
* @returns {string}
|
|
46
|
+
*/
|
|
47
|
+
const qsStringify = (query, replacer = defaultReplacer) => {
|
|
48
|
+
const params = new URLSearchParams();
|
|
49
|
+
objectEach(query, (val, key) => {
|
|
50
|
+
if (isArray(val)) {
|
|
51
|
+
val.forEach(i => {
|
|
52
|
+
const replaced = replacer(i);
|
|
53
|
+
if (replaced === null)
|
|
54
|
+
return;
|
|
55
|
+
params.append(key.toString(), replaced);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
const replaced = replacer(val);
|
|
60
|
+
if (replaced === null)
|
|
61
|
+
return;
|
|
62
|
+
params.set(key.toString(), replaced);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
return params.toString();
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export { qsParse, qsStringify };
|