s94-utils 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/package.json +23 -0
- package/src/index.js +556 -0
- package/src/index.mjs +556 -0
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "s94-utils",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"module": "src/index.mjs",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "src/index.mjs",
|
|
11
|
+
"require": "src/index.js",
|
|
12
|
+
"default": "src/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": ["src"],
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "node test/test.js"
|
|
20
|
+
},
|
|
21
|
+
"author": "",
|
|
22
|
+
"license": "ISC"
|
|
23
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
function isPromise(obj){
|
|
2
|
+
return obj && typeof obj == 'object' && typeof obj.then == 'function';
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
class Queue{
|
|
6
|
+
list = [];
|
|
7
|
+
running = 0;
|
|
8
|
+
limit = 1;
|
|
9
|
+
status = 0; // 0,停止,1开始
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 构建队列
|
|
13
|
+
* @param {number} [limit] 同时执行的槽位数量限制,默认为:1
|
|
14
|
+
* @param {boolean} [start] 构建好后,是否直接开始队列
|
|
15
|
+
* @returns {Queue}
|
|
16
|
+
*/
|
|
17
|
+
constructor(limit, start) {
|
|
18
|
+
this.limit = limit || 1;
|
|
19
|
+
|
|
20
|
+
if (start) this.start()
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
__run(){
|
|
24
|
+
while (this.status===1 && this.running < this.limit && this.list.length > 0){
|
|
25
|
+
let {callback, thisArg} = this.list.shift();
|
|
26
|
+
let res = callback.call(thisArg);
|
|
27
|
+
|
|
28
|
+
if (isPromise(res)){
|
|
29
|
+
this.running++;
|
|
30
|
+
res.then(()=>{
|
|
31
|
+
this.running--;
|
|
32
|
+
this.__run();
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 插入执行
|
|
41
|
+
* @param {function} callback 回调函数,如果返回 Promise ,会占用执行槽位直到 Promise 兑现
|
|
42
|
+
* @param {any} thisArg 执行时的this对象
|
|
43
|
+
* @returns {Queue}
|
|
44
|
+
*/
|
|
45
|
+
add(callback, thisArg){
|
|
46
|
+
if(typeof(callback)!=='function') return this;
|
|
47
|
+
this.list.push({callback, thisArg});
|
|
48
|
+
return this.__run();
|
|
49
|
+
}
|
|
50
|
+
start(){
|
|
51
|
+
this.status = 1;
|
|
52
|
+
return this.__run();
|
|
53
|
+
}
|
|
54
|
+
stop(){
|
|
55
|
+
this.status = 0;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class Cache{
|
|
62
|
+
Storage = null;
|
|
63
|
+
/**
|
|
64
|
+
* 构建缓存器,传入缓存引擎对象,
|
|
65
|
+
* @param { {getItem:(name:string)=>string|Promise<string>, setItem:(name:string, data:any)=>Promise<any>, removeItem?:(name:string)=>Promise<any>, clear?:()=>any } } engine
|
|
66
|
+
*/
|
|
67
|
+
constructor(engine) {
|
|
68
|
+
if (typeof(engine)!="object" || typeof(engine.getItem)!='function' || typeof(engine.setItem)!='function'){
|
|
69
|
+
throw new Error('缓存引擎必须为对象,并且包含方法:getItem、setItem');
|
|
70
|
+
}
|
|
71
|
+
this.Storage = engine;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 获取缓存
|
|
76
|
+
* @param {any} name 缓存名
|
|
77
|
+
* @param {any} def 默认值
|
|
78
|
+
* @returns {any | Promise<any>} 由引擎的 getItem 确定是否返回 Promise
|
|
79
|
+
*/
|
|
80
|
+
get(name, def){
|
|
81
|
+
const cache_name = 's94_cache-'+encode(name);
|
|
82
|
+
const str = this.Storage.getItem(cache_name);
|
|
83
|
+
function _get(str, def){
|
|
84
|
+
try {
|
|
85
|
+
var data = JSON.parse(str);
|
|
86
|
+
if(!data || typeof(data)!=='object') return def;
|
|
87
|
+
if( data['expire'] !== -1 && data['expire'] < (new Date).getTime() ) return def;
|
|
88
|
+
return data['main'];
|
|
89
|
+
} catch (error) {
|
|
90
|
+
return def;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (isPromise(str)){
|
|
94
|
+
return str.then(v=>_get(v, def))
|
|
95
|
+
}
|
|
96
|
+
return _get(str, def);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 设定缓存
|
|
100
|
+
* @param {string} name 缓存名称
|
|
101
|
+
* @param {any} value 缓存值
|
|
102
|
+
* @param {number} timeout 过期时间(毫秒),默认或者小于0,表示永久
|
|
103
|
+
* @returns {Promise<any>}
|
|
104
|
+
*/
|
|
105
|
+
set(name, value, timeout){
|
|
106
|
+
timeout = timeout && timeout>0 ? Number(timeout) : -1;
|
|
107
|
+
let expire = timeout === -1 ? -1 : (new Date).getTime() + timeout;
|
|
108
|
+
|
|
109
|
+
const cache_name = 's94_cache-'+encode(name);
|
|
110
|
+
const data = JSON.stringify({main:value, expire, name});
|
|
111
|
+
return Promise.resolve(this.Storage.setItem(cache_name, data));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 移除缓存
|
|
116
|
+
* @param {string} name
|
|
117
|
+
* @returns {Promise<any>}
|
|
118
|
+
*/
|
|
119
|
+
remove(name){
|
|
120
|
+
const cache_name = 's94_cache-'+encode(name);
|
|
121
|
+
if(typeof(this.Storage.removeItem) === 'function') {
|
|
122
|
+
return Promise.resolve( this.Storage.removeItem(cache_name) );
|
|
123
|
+
}else {
|
|
124
|
+
return Promise.resolve( this.Storage.getItem(cache_name) ).then((res)=>{
|
|
125
|
+
if (res) {
|
|
126
|
+
// 如果有缓存数据,就设定一个保证过期的数据替换
|
|
127
|
+
const data = JSON.stringify({main:'', expire:1, name});
|
|
128
|
+
return this.Storage.setItem(cache_name, data);
|
|
129
|
+
}
|
|
130
|
+
return str
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 清理缓存,如果引擎存在 clear 方法,
|
|
137
|
+
* @returns {Promise<any>}
|
|
138
|
+
*/
|
|
139
|
+
clear(){
|
|
140
|
+
if (typeof(this.Storage.clear) === 'function'){
|
|
141
|
+
return Promise.resolve( this.Storage.clear() );
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 返回参数的类型,例如 Object,Date
|
|
149
|
+
* @param {any} obj
|
|
150
|
+
* @returns {string}
|
|
151
|
+
*/
|
|
152
|
+
function var_type(obj){
|
|
153
|
+
const str = Object.prototype.toString.call(obj);
|
|
154
|
+
return str.substring(8,str.length-1);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* 转字符串
|
|
158
|
+
* @param {any} val
|
|
159
|
+
* @returns {string}
|
|
160
|
+
*/
|
|
161
|
+
function to_string(val){
|
|
162
|
+
if (typeof val === 'string') return val;
|
|
163
|
+
try {
|
|
164
|
+
return val.toString();
|
|
165
|
+
} catch (e) {
|
|
166
|
+
return '';
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 去除字符串两边的空白
|
|
171
|
+
* @param {string} str
|
|
172
|
+
* @param {string} [mask] 额外需要去除的字符串
|
|
173
|
+
* @returns {string}
|
|
174
|
+
*/
|
|
175
|
+
function trim(str, mask) {
|
|
176
|
+
str = to_string(str)
|
|
177
|
+
if (!str) return "";
|
|
178
|
+
if (typeof str.trim === 'function' && !mask) {
|
|
179
|
+
return str.trim();
|
|
180
|
+
}
|
|
181
|
+
mask = to_string(mask)
|
|
182
|
+
if (mask) mask = mask.replace(/([\$\(\)\*\+\?\.\[\]\\\^\{\}\|]){1}/g, '\\$1');
|
|
183
|
+
let reg_code = `^[\\s\\uFEFF\\xA0${mask}]+|[\\s\\uFEFF\\xA0${mask}]+$`
|
|
184
|
+
return str.replace(new RegExp(reg_code, 'g'), '');
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* 计算相对路径
|
|
188
|
+
* @param {string} path
|
|
189
|
+
* @param {string} base
|
|
190
|
+
* @returns {*|string}
|
|
191
|
+
*/
|
|
192
|
+
function path(path, base){
|
|
193
|
+
if (!base || path.indexOf('/')===0) return path;
|
|
194
|
+
base = to_string(base);
|
|
195
|
+
var base_arr = base.split(/\\|\//);
|
|
196
|
+
base_arr.pop();
|
|
197
|
+
for (var i=0,path_arr = path.split(/\\|\//); i<path_arr.length; i++) {
|
|
198
|
+
switch (path_arr[i]){
|
|
199
|
+
case '.':break;
|
|
200
|
+
case '..':base_arr.pop();break;
|
|
201
|
+
default:base_arr.push(path_arr[i]);break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return base_arr.join(~base.indexOf('\\') ? '\\' : '/');
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* 遍历对象
|
|
208
|
+
* @param {Object} obj
|
|
209
|
+
* @param { {each:(v:any, k:string, obj:Object)=>any, over?:()=>any, limit?:number} | ((v:any, k:string, obj:Object)=>any) } op 遍历回调函数 或者 包含遍历回调的配置
|
|
210
|
+
* @param {any} [thisArg] 回调函数的this对象
|
|
211
|
+
*/
|
|
212
|
+
function each(obj, op, thisArg) {
|
|
213
|
+
let ks = Object.keys(obj), index=0;
|
|
214
|
+
if (typeof op == 'function') op = {each: op}
|
|
215
|
+
if (typeof op.each != 'function') throw '参数错误,缺少遍历回调函数(each)';
|
|
216
|
+
op.over = typeof op.over == 'function' ? op.over : function (){}
|
|
217
|
+
let runing = 0, limit = op.limit || 1, is_break = false;
|
|
218
|
+
|
|
219
|
+
function __run(){
|
|
220
|
+
while (runing < limit && index < ks.length && !is_break){
|
|
221
|
+
let k = ks[index++], v = obj[k];
|
|
222
|
+
let res = op.each.call( (thisArg || v), v, k, obj );
|
|
223
|
+
if (res === false) {
|
|
224
|
+
op.over()
|
|
225
|
+
is_break=true;
|
|
226
|
+
}else if (isPromise(res)){
|
|
227
|
+
runing++;
|
|
228
|
+
res.then(()=>{
|
|
229
|
+
runing--;
|
|
230
|
+
__run();
|
|
231
|
+
}).catch(()=>{op.over();is_break=true;})
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (!is_break && !runing && index >= ks.length) op.over()
|
|
235
|
+
}
|
|
236
|
+
__run();
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* 递归遍历
|
|
240
|
+
* @param {Object} obj 遍历的对象
|
|
241
|
+
* @param {string} childkey 递归遍历子对象的参数名,如果为空表示遍历整个子对象
|
|
242
|
+
* @param { (v:any, k:string, obj:Object, ks:string[])=>any } callback 遍历回调函数
|
|
243
|
+
* @param {boolean} [is_layer] 是否按层结构(0,1,00,01,10...)递归,false表示按树结构(0,00,01,1,10...)
|
|
244
|
+
* @param {any} [thisArg] 遍历回调的this,默认为子对象
|
|
245
|
+
*/
|
|
246
|
+
function eachloop(obj, childkey, callback, is_layer, thisArg) {
|
|
247
|
+
function loop(obj, ks){
|
|
248
|
+
let next_layer = [];
|
|
249
|
+
let res = true;
|
|
250
|
+
for (const k in obj) {
|
|
251
|
+
if (!obj.hasOwnProperty(k)) continue;
|
|
252
|
+
let row = obj[k];
|
|
253
|
+
res = callback.call( (thisArg || row), row, k, obj, ks.concat(k) );
|
|
254
|
+
if (res && row && typeof(row)=="object") {
|
|
255
|
+
let child = childkey ? row[childkey] : row;
|
|
256
|
+
if (child && typeof(child)=="object") {
|
|
257
|
+
if (is_layer){
|
|
258
|
+
next_layer.push({child:child, ks:ks.concat(k)})
|
|
259
|
+
}else {
|
|
260
|
+
res = loop(child, ks.concat(k));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (res === false) break;
|
|
265
|
+
}
|
|
266
|
+
for (let i = 0; i < next_layer.length; i++) {
|
|
267
|
+
let row = next_layer[i];
|
|
268
|
+
res = loop(row.child, row.ks);
|
|
269
|
+
if (res === false) break;
|
|
270
|
+
}
|
|
271
|
+
return res;
|
|
272
|
+
}
|
|
273
|
+
loop(obj, [])
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* url查询参数解码
|
|
277
|
+
* @param {string} str url查询参数字符串,例如:a=1&b[]=2&b[]=3
|
|
278
|
+
* @returns {{[key:string]: string|Object}}
|
|
279
|
+
*/
|
|
280
|
+
function param_decode(str){
|
|
281
|
+
let res = {};
|
|
282
|
+
if (!str || typeof str !== 'string') return res;
|
|
283
|
+
str.split('&').forEach(function (row){
|
|
284
|
+
let kv = row.split('=');
|
|
285
|
+
if (!kv[0]) return;
|
|
286
|
+
let val = decodeURIComponent(kv[1] || '');
|
|
287
|
+
let arr = decodeURIComponent(kv[0]).split(']'), ks=[];
|
|
288
|
+
for (let i = 0; i < arr.length; i++) {
|
|
289
|
+
let row = arr[i];
|
|
290
|
+
if (i===0){
|
|
291
|
+
let ms = row.match(/^([^\[]+)(\[[\s\S.]*)?$/);
|
|
292
|
+
ks.push(ms[1]);
|
|
293
|
+
if (ms[2]) ks.push(ms[2].substring(1));
|
|
294
|
+
}else if(row){
|
|
295
|
+
ks.push(row.substring(1));
|
|
296
|
+
}else {
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
let cache = res;
|
|
301
|
+
ks.forEach(function (k,i){
|
|
302
|
+
k = k || (function(){
|
|
303
|
+
var ki=-1;
|
|
304
|
+
Object.keys(cache).forEach(function(row){
|
|
305
|
+
if(/^\d+$/.test(row) && row>ki) ki = row*1;
|
|
306
|
+
})
|
|
307
|
+
return ki+1;
|
|
308
|
+
})();
|
|
309
|
+
if (i == ks.length-1){
|
|
310
|
+
cache[k] = val;
|
|
311
|
+
}else {
|
|
312
|
+
let is_arr = ks[i+1] == '' || /^\d+$/.test(ks[i+1]);
|
|
313
|
+
cache[k] = typeof(cache[k])!="undefined" ? cache[k] : (is_arr ? [] : {});
|
|
314
|
+
cache = cache[k];
|
|
315
|
+
}
|
|
316
|
+
})
|
|
317
|
+
})
|
|
318
|
+
return res;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* url查询参数编码
|
|
322
|
+
* @param {Object} obj 数据对象
|
|
323
|
+
* @returns {string}
|
|
324
|
+
*/
|
|
325
|
+
function param_encode(obj){
|
|
326
|
+
if (!obj) return '';
|
|
327
|
+
if(typeof(obj)=='string') return obj;
|
|
328
|
+
let param_arr = [];
|
|
329
|
+
if (obj instanceof FormData){
|
|
330
|
+
for(let pair of obj.entries()) {
|
|
331
|
+
param_arr.push(encodeURIComponent(pair[0]) + '=' + encodeURIComponent(pair[1]) );
|
|
332
|
+
}
|
|
333
|
+
}else{
|
|
334
|
+
eachloop(obj, '', function (row, k, all, ks){
|
|
335
|
+
if(typeof(row) !== "object") {
|
|
336
|
+
let name = ks.map((v,i)=>{ return i>0 ? '['+encodeURIComponent(v)+']' : encodeURIComponent(v); }).join('');
|
|
337
|
+
param_arr.push(name + '=' + encodeURIComponent(row));
|
|
338
|
+
}
|
|
339
|
+
return true;
|
|
340
|
+
})
|
|
341
|
+
}
|
|
342
|
+
return param_arr.join('&');
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* 数据签名
|
|
346
|
+
* @param {any} data 数据
|
|
347
|
+
* @param {number} len 长度, 默认64
|
|
348
|
+
* @returns {string}
|
|
349
|
+
*/
|
|
350
|
+
function encode(data, len=64){
|
|
351
|
+
if (!data) return '';
|
|
352
|
+
let strmap = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-';
|
|
353
|
+
len = len && len>1 && len<=64 ? Number(len) : 64;
|
|
354
|
+
let str = JSON.stringify(data).replaceAll('"','');
|
|
355
|
+
let res = '', vi = 0;
|
|
356
|
+
for (let j = 0; j < len; j++) {
|
|
357
|
+
for (let i = j; i < str.length; i+=len) {
|
|
358
|
+
vi += str.charCodeAt(i);
|
|
359
|
+
}
|
|
360
|
+
let v = vi%len;
|
|
361
|
+
res += strmap[vi%len];
|
|
362
|
+
vi = Math.floor(vi/len);
|
|
363
|
+
}
|
|
364
|
+
return res;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* 对象的指定值获取 或者 设定
|
|
368
|
+
* @param {any} data 数据源
|
|
369
|
+
* @param {string|{[key:string]: any}} key_data 获取的键,或者设定数据的键值对
|
|
370
|
+
* @param {any} def 值不存在的默认值
|
|
371
|
+
* @returns {*}
|
|
372
|
+
*/
|
|
373
|
+
function map(data, key_data, def){
|
|
374
|
+
if (typeof(key_data) == 'object' && key_data){
|
|
375
|
+
if(data === null || typeof data !== 'object') data = {};
|
|
376
|
+
for (const key in key_data) {
|
|
377
|
+
if (!key.length) continue;
|
|
378
|
+
let v = key_data[key];
|
|
379
|
+
let key_arr = key.split('.');
|
|
380
|
+
let k_last = key_arr.pop();
|
|
381
|
+
let cache_data = data;
|
|
382
|
+
key_arr.forEach(function (k, i){
|
|
383
|
+
if (cache_data[k] === null || typeof cache_data[k] !== 'object'){
|
|
384
|
+
cache_data[k] = {}
|
|
385
|
+
}
|
|
386
|
+
cache_data = cache_data[k];
|
|
387
|
+
})
|
|
388
|
+
cache_data[k_last] = v;
|
|
389
|
+
}
|
|
390
|
+
return data;
|
|
391
|
+
}else {
|
|
392
|
+
let key_arr = key_data ? to_string(key_data).split('.') : [];
|
|
393
|
+
let res = data;
|
|
394
|
+
for (let i = 0; i < key_arr.length; i++) {
|
|
395
|
+
let k = key_arr[i];
|
|
396
|
+
if (res === null || typeof res === 'undefined') return def;
|
|
397
|
+
res = res[k];
|
|
398
|
+
}
|
|
399
|
+
if (res === null || typeof res === 'undefined') return def;
|
|
400
|
+
return res;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* 日期处理
|
|
406
|
+
* @param {string|number|Date} date 日期,可以是字符串、时间戳(毫秒)
|
|
407
|
+
* @param {string} fmt 日期字符串格式,如果data为字符串,然后fmt为空,那么会推定日期为 年月日时分秒、日月年时分秒 中的一种
|
|
408
|
+
* @param {number} [zone] 时区,东正,西负。-12~12
|
|
409
|
+
* @returns {{timestamp: number, y: number, m: number, d: number, h: number, i: number, s: number, w: number, string: string, zone: number}}
|
|
410
|
+
*/
|
|
411
|
+
function date(date, fmt, zone){
|
|
412
|
+
function utc_date(str, fmt, utc_offset){
|
|
413
|
+
// 宽松的日期解析,按 fmt 指定的标识顺序位置的数字
|
|
414
|
+
let number_arr = str.match(/\d+/g);
|
|
415
|
+
let key_arr = []
|
|
416
|
+
if (fmt){
|
|
417
|
+
fmt = fmt.replace(/\\[ymdhis]/ig, ''); // 排除掉 \Y 之类的转义字符
|
|
418
|
+
key_arr = fmt.match(/[ymdhis]/ig);
|
|
419
|
+
} else {
|
|
420
|
+
// 没有指定 fmt 的情况下,根据年(值>1000)所在位置判断,分两种情况:年月日时分秒、日月年时分秒
|
|
421
|
+
let year_index = number_arr.findIndex(v=>v>1000)
|
|
422
|
+
if (year_index === 0){
|
|
423
|
+
key_arr = ['y','m','d','h','i','s']
|
|
424
|
+
}else if (year_index === 2){
|
|
425
|
+
key_arr = ['d','m','y','h','i','s']
|
|
426
|
+
}else {
|
|
427
|
+
throw new Error('日期异常')
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
let value_map = {}
|
|
431
|
+
key_arr.forEach((k, i)=>{
|
|
432
|
+
value_map[k.toLowerCase()] = Number(number_arr[i] || 0)
|
|
433
|
+
})
|
|
434
|
+
let date = new Date()
|
|
435
|
+
let function_map = { y:'setUTCFullYear',m:'setUTCMonth',d:'setUTCDate',h:'setUTCHours',i:'setUTCMinutes',s:'setUTCSeconds' };
|
|
436
|
+
Object.keys(function_map).forEach(k=>{
|
|
437
|
+
value_map[k] = value_map[k] || 0;
|
|
438
|
+
if (k === 'm' && value_map[k] === 0){
|
|
439
|
+
// 月的默认值应该是 1 不是 0
|
|
440
|
+
value_map[k] = 1
|
|
441
|
+
}
|
|
442
|
+
let function_name = function_map[k]
|
|
443
|
+
date[function_name]( k==='m' ? value_map[k]-1 : value_map[k] );
|
|
444
|
+
})
|
|
445
|
+
value_map.w = date.getUTCDay();
|
|
446
|
+
// 时区转换,例如:在东8时区传入的是1970 年 1 月 1 日 08:00:00。因为使用的是 setUTCHours ,得到的 date.getTime() = 8*60*1000,
|
|
447
|
+
value_map.timestamp = date.getTime() - utc_offset;
|
|
448
|
+
return value_map;
|
|
449
|
+
}
|
|
450
|
+
function format_time(timestamp, fmt, utc_offset){
|
|
451
|
+
const time = new Date(timestamp + utc_offset)
|
|
452
|
+
function add0(n){ return n < 10 ? ('0' + n) : n}
|
|
453
|
+
let res = {
|
|
454
|
+
y: time.getUTCFullYear(),
|
|
455
|
+
m: time.getUTCMonth() + 1,
|
|
456
|
+
d: time.getUTCDate(),
|
|
457
|
+
h: time.getUTCHours(),
|
|
458
|
+
i: time.getUTCMinutes(),
|
|
459
|
+
s: time.getUTCSeconds(),
|
|
460
|
+
w: time.getUTCDay(),
|
|
461
|
+
}
|
|
462
|
+
let v = {
|
|
463
|
+
...res,
|
|
464
|
+
W: ['日','一','二','三','四','五','六'][res.w],
|
|
465
|
+
w: ['7','1','2','3','4','5','6'][res.w],
|
|
466
|
+
}
|
|
467
|
+
v.Y = v.y; v.M = add0(v.m); v.D = add0(v.d); v.H = add0(v.h); v.I = add0(v.i); v.S = add0(v.s);
|
|
468
|
+
res.string = fmt.replace(/(^|[^\\])([YMDHISWymdhisw]+)/g, function(m,m1,m2){
|
|
469
|
+
let str = m1;
|
|
470
|
+
for(let i=0; i<m2.length; i++ ){
|
|
471
|
+
str += v[m2[i]];
|
|
472
|
+
}
|
|
473
|
+
return str;
|
|
474
|
+
}).replace(/\\([YMDHISWymdhisw])/g, "$1");
|
|
475
|
+
return res;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
zone = Number(zone);
|
|
479
|
+
// utc需要偏移多少毫秒后,才能得到和指定时区相同的日期,例如:东8时区8点时,utc得到的是0点,需要+480分钟后,utc得到的才是8点,那么 utc_offset=480*60*1000
|
|
480
|
+
// 即:(new Date(时间戳)).getHours() == (new Date(时间戳 + utc_offset)).getUTCHours()
|
|
481
|
+
if (isNaN(zone)){
|
|
482
|
+
zone = -(new Date()).getTimezoneOffset()/60
|
|
483
|
+
}
|
|
484
|
+
const utc_offset = zone*3600*1000;
|
|
485
|
+
|
|
486
|
+
const res = {
|
|
487
|
+
timestamp: NaN,
|
|
488
|
+
y: NaN,
|
|
489
|
+
m: NaN,
|
|
490
|
+
d: NaN,
|
|
491
|
+
h: NaN,
|
|
492
|
+
i: NaN,
|
|
493
|
+
s: NaN,
|
|
494
|
+
w: NaN,
|
|
495
|
+
string: '',
|
|
496
|
+
zone,
|
|
497
|
+
}
|
|
498
|
+
if (date instanceof Date){
|
|
499
|
+
let timestamp = date.getTime();
|
|
500
|
+
Object.assign(res, format_time(timestamp, fmt, utc_offset))
|
|
501
|
+
res.timestamp = timestamp;
|
|
502
|
+
}else if (typeof date === 'number'){
|
|
503
|
+
Object.assign(res, format_time(date, fmt, utc_offset))
|
|
504
|
+
res.timestamp = date;
|
|
505
|
+
}else if (typeof date === 'string'){
|
|
506
|
+
Object.assign(res, utc_date(date, fmt, utc_offset))
|
|
507
|
+
res.string = date;
|
|
508
|
+
}
|
|
509
|
+
return res;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const s94_event = {
|
|
513
|
+
__event_list: {},
|
|
514
|
+
on(event_name, callback, first){
|
|
515
|
+
if (!event_name || typeof event_name !== 'string') return;
|
|
516
|
+
if (typeof callback !== 'function') return;
|
|
517
|
+
let list = s94_event.__event_list[event_name] || [];
|
|
518
|
+
first ? list.unshift(callback) : list.push(callback);
|
|
519
|
+
s94_event.__event_list[event_name] = list;
|
|
520
|
+
},
|
|
521
|
+
off(event_name, callback){
|
|
522
|
+
if (!event_name || typeof event_name !== 'string') return;
|
|
523
|
+
if (typeof callback === 'function'){
|
|
524
|
+
let list = s94_event.__event_list[event_name] || [];
|
|
525
|
+
let i = list.indexOf(callback);
|
|
526
|
+
if (i !== -1) list.splice(i,1);
|
|
527
|
+
s94_event.__event_list[event_name] = list;
|
|
528
|
+
}else if (typeof callback === 'undefined'){
|
|
529
|
+
s94_event.__event_list[event_name] = [];
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
emit(event_name, data, thisArg){
|
|
533
|
+
if (!event_name || typeof event_name !== 'string') return;
|
|
534
|
+
let list = s94_event.__event_list[event_name] || [];
|
|
535
|
+
for (let i = 0; i < list; i++) {
|
|
536
|
+
if (list[i].call(thisArg, data) === false) break;
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
module.exports = {
|
|
542
|
+
Queue,
|
|
543
|
+
Cache,
|
|
544
|
+
var_type,
|
|
545
|
+
to_string,
|
|
546
|
+
trim,
|
|
547
|
+
path,
|
|
548
|
+
each,
|
|
549
|
+
eachloop,
|
|
550
|
+
param_decode,
|
|
551
|
+
param_encode,
|
|
552
|
+
encode,
|
|
553
|
+
map,
|
|
554
|
+
date,
|
|
555
|
+
s94_event,
|
|
556
|
+
}
|
package/src/index.mjs
ADDED
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
function isPromise(obj){
|
|
2
|
+
return obj && typeof obj == 'object' && typeof obj.then == 'function';
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
class Queue{
|
|
6
|
+
list = [];
|
|
7
|
+
running = 0;
|
|
8
|
+
limit = 1;
|
|
9
|
+
status = 0; // 0,停止,1开始
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 构建队列
|
|
13
|
+
* @param {number} [limit] 同时执行的槽位数量限制,默认为:1
|
|
14
|
+
* @param {boolean} [start] 构建好后,是否直接开始队列
|
|
15
|
+
* @returns {Queue}
|
|
16
|
+
*/
|
|
17
|
+
constructor(limit, start) {
|
|
18
|
+
this.limit = limit || 1;
|
|
19
|
+
|
|
20
|
+
if (start) this.start()
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
__run(){
|
|
24
|
+
while (this.status===1 && this.running < this.limit && this.list.length > 0){
|
|
25
|
+
let {callback, thisArg} = this.list.shift();
|
|
26
|
+
let res = callback.call(thisArg);
|
|
27
|
+
|
|
28
|
+
if (isPromise(res)){
|
|
29
|
+
this.running++;
|
|
30
|
+
res.then(()=>{
|
|
31
|
+
this.running--;
|
|
32
|
+
this.__run();
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 插入执行
|
|
41
|
+
* @param {function} callback 回调函数,如果返回 Promise ,会占用执行槽位直到 Promise 兑现
|
|
42
|
+
* @param {any} thisArg 执行时的this对象
|
|
43
|
+
* @returns {Queue}
|
|
44
|
+
*/
|
|
45
|
+
add(callback, thisArg){
|
|
46
|
+
if(typeof(callback)!=='function') return this;
|
|
47
|
+
this.list.push({callback, thisArg});
|
|
48
|
+
return this.__run();
|
|
49
|
+
}
|
|
50
|
+
start(){
|
|
51
|
+
this.status = 1;
|
|
52
|
+
return this.__run();
|
|
53
|
+
}
|
|
54
|
+
stop(){
|
|
55
|
+
this.status = 0;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class Cache{
|
|
62
|
+
Storage = null;
|
|
63
|
+
/**
|
|
64
|
+
* 构建缓存器,传入缓存引擎对象,
|
|
65
|
+
* @param { {getItem:(name:string)=>string|Promise<string>, setItem:(name:string, data:any)=>Promise<any>, removeItem?:(name:string)=>Promise<any>, clear?:()=>any } } engine
|
|
66
|
+
*/
|
|
67
|
+
constructor(engine) {
|
|
68
|
+
if (typeof(engine)!="object" || typeof(engine.getItem)!='function' || typeof(engine.setItem)!='function'){
|
|
69
|
+
throw new Error('缓存引擎必须为对象,并且包含方法:getItem、setItem');
|
|
70
|
+
}
|
|
71
|
+
this.Storage = engine;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 获取缓存
|
|
76
|
+
* @param {any} name 缓存名
|
|
77
|
+
* @param {any} def 默认值
|
|
78
|
+
* @returns {any | Promise<any>} 由引擎的 getItem 确定是否返回 Promise
|
|
79
|
+
*/
|
|
80
|
+
get(name, def){
|
|
81
|
+
const cache_name = 's94_cache-'+encode(name);
|
|
82
|
+
const str = this.Storage.getItem(cache_name);
|
|
83
|
+
function _get(str, def){
|
|
84
|
+
try {
|
|
85
|
+
var data = JSON.parse(str);
|
|
86
|
+
if(!data || typeof(data)!=='object') return def;
|
|
87
|
+
if( data['expire'] !== -1 && data['expire'] < (new Date).getTime() ) return def;
|
|
88
|
+
return data['main'];
|
|
89
|
+
} catch (error) {
|
|
90
|
+
return def;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (isPromise(str)){
|
|
94
|
+
return str.then(v=>_get(v, def))
|
|
95
|
+
}
|
|
96
|
+
return _get(str, def);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 设定缓存
|
|
100
|
+
* @param {string} name 缓存名称
|
|
101
|
+
* @param {any} value 缓存值
|
|
102
|
+
* @param {number} timeout 过期时间(毫秒),默认或者小于0,表示永久
|
|
103
|
+
* @returns {Promise<any>}
|
|
104
|
+
*/
|
|
105
|
+
set(name, value, timeout){
|
|
106
|
+
timeout = timeout && timeout>0 ? Number(timeout) : -1;
|
|
107
|
+
let expire = timeout === -1 ? -1 : (new Date).getTime() + timeout;
|
|
108
|
+
|
|
109
|
+
const cache_name = 's94_cache-'+encode(name);
|
|
110
|
+
const data = JSON.stringify({main:value, expire, name});
|
|
111
|
+
return Promise.resolve(this.Storage.setItem(cache_name, data));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 移除缓存
|
|
116
|
+
* @param {string} name
|
|
117
|
+
* @returns {Promise<any>}
|
|
118
|
+
*/
|
|
119
|
+
remove(name){
|
|
120
|
+
const cache_name = 's94_cache-'+encode(name);
|
|
121
|
+
if(typeof(this.Storage.removeItem) === 'function') {
|
|
122
|
+
return Promise.resolve( this.Storage.removeItem(cache_name) );
|
|
123
|
+
}else {
|
|
124
|
+
return Promise.resolve( this.Storage.getItem(cache_name) ).then((res)=>{
|
|
125
|
+
if (res) {
|
|
126
|
+
// 如果有缓存数据,就设定一个保证过期的数据替换
|
|
127
|
+
const data = JSON.stringify({main:'', expire:1, name});
|
|
128
|
+
return this.Storage.setItem(cache_name, data);
|
|
129
|
+
}
|
|
130
|
+
return str
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 清理缓存,如果引擎存在 clear 方法,
|
|
137
|
+
* @returns {Promise<any>}
|
|
138
|
+
*/
|
|
139
|
+
clear(){
|
|
140
|
+
if (typeof(this.Storage.clear) === 'function'){
|
|
141
|
+
return Promise.resolve( this.Storage.clear() );
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 返回参数的类型,例如 Object,Date
|
|
149
|
+
* @param {any} obj
|
|
150
|
+
* @returns {string}
|
|
151
|
+
*/
|
|
152
|
+
function var_type(obj){
|
|
153
|
+
const str = Object.prototype.toString.call(obj);
|
|
154
|
+
return str.substring(8,str.length-1);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* 转字符串
|
|
158
|
+
* @param {any} val
|
|
159
|
+
* @returns {string}
|
|
160
|
+
*/
|
|
161
|
+
function to_string(val){
|
|
162
|
+
if (typeof val === 'string') return val;
|
|
163
|
+
try {
|
|
164
|
+
return val.toString();
|
|
165
|
+
} catch (e) {
|
|
166
|
+
return '';
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 去除字符串两边的空白
|
|
171
|
+
* @param {string} str
|
|
172
|
+
* @param {string} [mask] 额外需要去除的字符串
|
|
173
|
+
* @returns {string}
|
|
174
|
+
*/
|
|
175
|
+
function trim(str, mask) {
|
|
176
|
+
str = to_string(str)
|
|
177
|
+
if (!str) return "";
|
|
178
|
+
if (typeof str.trim === 'function' && !mask) {
|
|
179
|
+
return str.trim();
|
|
180
|
+
}
|
|
181
|
+
mask = to_string(mask)
|
|
182
|
+
if (mask) mask = mask.replace(/([\$\(\)\*\+\?\.\[\]\\\^\{\}\|]){1}/g, '\\$1');
|
|
183
|
+
let reg_code = `^[\\s\\uFEFF\\xA0${mask}]+|[\\s\\uFEFF\\xA0${mask}]+$`
|
|
184
|
+
return str.replace(new RegExp(reg_code, 'g'), '');
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* 计算相对路径
|
|
188
|
+
* @param {string} path
|
|
189
|
+
* @param {string} base
|
|
190
|
+
* @returns {*|string}
|
|
191
|
+
*/
|
|
192
|
+
function path(path, base){
|
|
193
|
+
if (!base || path.indexOf('/')===0) return path;
|
|
194
|
+
base = to_string(base);
|
|
195
|
+
var base_arr = base.split(/\\|\//);
|
|
196
|
+
base_arr.pop();
|
|
197
|
+
for (var i=0,path_arr = path.split(/\\|\//); i<path_arr.length; i++) {
|
|
198
|
+
switch (path_arr[i]){
|
|
199
|
+
case '.':break;
|
|
200
|
+
case '..':base_arr.pop();break;
|
|
201
|
+
default:base_arr.push(path_arr[i]);break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return base_arr.join(~base.indexOf('\\') ? '\\' : '/');
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* 遍历对象
|
|
208
|
+
* @param {Object} obj
|
|
209
|
+
* @param { {each:(v:any, k:string, obj:Object)=>any, over?:()=>any, limit?:number} | ((v:any, k:string, obj:Object)=>any) } op 遍历回调函数 或者 包含遍历回调的配置
|
|
210
|
+
* @param {any} [thisArg] 回调函数的this对象
|
|
211
|
+
*/
|
|
212
|
+
function each(obj, op, thisArg) {
|
|
213
|
+
let ks = Object.keys(obj), index=0;
|
|
214
|
+
if (typeof op == 'function') op = {each: op}
|
|
215
|
+
if (typeof op.each != 'function') throw '参数错误,缺少遍历回调函数(each)';
|
|
216
|
+
op.over = typeof op.over == 'function' ? op.over : function (){}
|
|
217
|
+
let runing = 0, limit = op.limit || 1, is_break = false;
|
|
218
|
+
|
|
219
|
+
function __run(){
|
|
220
|
+
while (runing < limit && index < ks.length && !is_break){
|
|
221
|
+
let k = ks[index++], v = obj[k];
|
|
222
|
+
let res = op.each.call( (thisArg || v), v, k, obj );
|
|
223
|
+
if (res === false) {
|
|
224
|
+
op.over()
|
|
225
|
+
is_break=true;
|
|
226
|
+
}else if (isPromise(res)){
|
|
227
|
+
runing++;
|
|
228
|
+
res.then(()=>{
|
|
229
|
+
runing--;
|
|
230
|
+
__run();
|
|
231
|
+
}).catch(()=>{op.over();is_break=true;})
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (!is_break && !runing && index >= ks.length) op.over()
|
|
235
|
+
}
|
|
236
|
+
__run();
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* 递归遍历
|
|
240
|
+
* @param {Object} obj 遍历的对象
|
|
241
|
+
* @param {string} childkey 递归遍历子对象的参数名,如果为空表示遍历整个子对象
|
|
242
|
+
* @param { (v:any, k:string, obj:Object, ks:string[])=>any } callback 遍历回调函数
|
|
243
|
+
* @param {boolean} [is_layer] 是否按层结构(0,1,00,01,10...)递归,false表示按树结构(0,00,01,1,10...)
|
|
244
|
+
* @param {any} [thisArg] 遍历回调的this,默认为子对象
|
|
245
|
+
*/
|
|
246
|
+
function eachloop(obj, childkey, callback, is_layer, thisArg) {
|
|
247
|
+
function loop(obj, ks){
|
|
248
|
+
let next_layer = [];
|
|
249
|
+
let res = true;
|
|
250
|
+
for (const k in obj) {
|
|
251
|
+
if (!obj.hasOwnProperty(k)) continue;
|
|
252
|
+
let row = obj[k];
|
|
253
|
+
res = callback.call( (thisArg || row), row, k, obj, ks.concat(k) );
|
|
254
|
+
if (res && row && typeof(row)=="object") {
|
|
255
|
+
let child = childkey ? row[childkey] : row;
|
|
256
|
+
if (child && typeof(child)=="object") {
|
|
257
|
+
if (is_layer){
|
|
258
|
+
next_layer.push({child:child, ks:ks.concat(k)})
|
|
259
|
+
}else {
|
|
260
|
+
res = loop(child, ks.concat(k));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (res === false) break;
|
|
265
|
+
}
|
|
266
|
+
for (let i = 0; i < next_layer.length; i++) {
|
|
267
|
+
let row = next_layer[i];
|
|
268
|
+
res = loop(row.child, row.ks);
|
|
269
|
+
if (res === false) break;
|
|
270
|
+
}
|
|
271
|
+
return res;
|
|
272
|
+
}
|
|
273
|
+
loop(obj, [])
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* url查询参数解码
|
|
277
|
+
* @param {string} str url查询参数字符串,例如:a=1&b[]=2&b[]=3
|
|
278
|
+
* @returns {{[key:string]: string|Object}}
|
|
279
|
+
*/
|
|
280
|
+
function param_decode(str){
|
|
281
|
+
let res = {};
|
|
282
|
+
if (!str || typeof str !== 'string') return res;
|
|
283
|
+
str.split('&').forEach(function (row){
|
|
284
|
+
let kv = row.split('=');
|
|
285
|
+
if (!kv[0]) return;
|
|
286
|
+
let val = decodeURIComponent(kv[1] || '');
|
|
287
|
+
let arr = decodeURIComponent(kv[0]).split(']'), ks=[];
|
|
288
|
+
for (let i = 0; i < arr.length; i++) {
|
|
289
|
+
let row = arr[i];
|
|
290
|
+
if (i===0){
|
|
291
|
+
let ms = row.match(/^([^\[]+)(\[[\s\S.]*)?$/);
|
|
292
|
+
ks.push(ms[1]);
|
|
293
|
+
if (ms[2]) ks.push(ms[2].substring(1));
|
|
294
|
+
}else if(row){
|
|
295
|
+
ks.push(row.substring(1));
|
|
296
|
+
}else {
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
let cache = res;
|
|
301
|
+
ks.forEach(function (k,i){
|
|
302
|
+
k = k || (function(){
|
|
303
|
+
var ki=-1;
|
|
304
|
+
Object.keys(cache).forEach(function(row){
|
|
305
|
+
if(/^\d+$/.test(row) && row>ki) ki = row*1;
|
|
306
|
+
})
|
|
307
|
+
return ki+1;
|
|
308
|
+
})();
|
|
309
|
+
if (i == ks.length-1){
|
|
310
|
+
cache[k] = val;
|
|
311
|
+
}else {
|
|
312
|
+
let is_arr = ks[i+1] == '' || /^\d+$/.test(ks[i+1]);
|
|
313
|
+
cache[k] = typeof(cache[k])!="undefined" ? cache[k] : (is_arr ? [] : {});
|
|
314
|
+
cache = cache[k];
|
|
315
|
+
}
|
|
316
|
+
})
|
|
317
|
+
})
|
|
318
|
+
return res;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* url查询参数编码
|
|
322
|
+
* @param {Object} obj 数据对象
|
|
323
|
+
* @returns {string}
|
|
324
|
+
*/
|
|
325
|
+
function param_encode(obj){
|
|
326
|
+
if (!obj) return '';
|
|
327
|
+
if(typeof(obj)=='string') return obj;
|
|
328
|
+
let param_arr = [];
|
|
329
|
+
if (obj instanceof FormData){
|
|
330
|
+
for(let pair of obj.entries()) {
|
|
331
|
+
param_arr.push(encodeURIComponent(pair[0]) + '=' + encodeURIComponent(pair[1]) );
|
|
332
|
+
}
|
|
333
|
+
}else{
|
|
334
|
+
eachloop(obj, '', function (row, k, all, ks){
|
|
335
|
+
if(typeof(row) !== "object") {
|
|
336
|
+
let name = ks.map((v,i)=>{ return i>0 ? '['+encodeURIComponent(v)+']' : encodeURIComponent(v); }).join('');
|
|
337
|
+
param_arr.push(name + '=' + encodeURIComponent(row));
|
|
338
|
+
}
|
|
339
|
+
return true;
|
|
340
|
+
})
|
|
341
|
+
}
|
|
342
|
+
return param_arr.join('&');
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* 数据签名
|
|
346
|
+
* @param {any} data 数据
|
|
347
|
+
* @param {number} len 长度, 默认64
|
|
348
|
+
* @returns {string}
|
|
349
|
+
*/
|
|
350
|
+
function encode(data, len=64){
|
|
351
|
+
if (!data) return '';
|
|
352
|
+
let strmap = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-';
|
|
353
|
+
len = len && len>1 && len<=64 ? Number(len) : 64;
|
|
354
|
+
let str = JSON.stringify(data).replaceAll('"','');
|
|
355
|
+
let res = '', vi = 0;
|
|
356
|
+
for (let j = 0; j < len; j++) {
|
|
357
|
+
for (let i = j; i < str.length; i+=len) {
|
|
358
|
+
vi += str.charCodeAt(i);
|
|
359
|
+
}
|
|
360
|
+
let v = vi%len;
|
|
361
|
+
res += strmap[vi%len];
|
|
362
|
+
vi = Math.floor(vi/len);
|
|
363
|
+
}
|
|
364
|
+
return res;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* 对象的指定值获取 或者 设定
|
|
368
|
+
* @param {any} data 数据源
|
|
369
|
+
* @param {string|{[key:string]: any}} key_data 获取的键,或者设定数据的键值对
|
|
370
|
+
* @param {any} def 值不存在的默认值
|
|
371
|
+
* @returns {*}
|
|
372
|
+
*/
|
|
373
|
+
function map(data, key_data, def){
|
|
374
|
+
if (typeof(key_data) == 'object' && key_data){
|
|
375
|
+
if(data === null || typeof data !== 'object') data = {};
|
|
376
|
+
for (const key in key_data) {
|
|
377
|
+
if (!key.length) continue;
|
|
378
|
+
let v = key_data[key];
|
|
379
|
+
let key_arr = key.split('.');
|
|
380
|
+
let k_last = key_arr.pop();
|
|
381
|
+
let cache_data = data;
|
|
382
|
+
key_arr.forEach(function (k, i){
|
|
383
|
+
if (cache_data[k] === null || typeof cache_data[k] !== 'object'){
|
|
384
|
+
cache_data[k] = {}
|
|
385
|
+
}
|
|
386
|
+
cache_data = cache_data[k];
|
|
387
|
+
})
|
|
388
|
+
cache_data[k_last] = v;
|
|
389
|
+
}
|
|
390
|
+
return data;
|
|
391
|
+
}else {
|
|
392
|
+
let key_arr = key_data ? to_string(key_data).split('.') : [];
|
|
393
|
+
let res = data;
|
|
394
|
+
for (let i = 0; i < key_arr.length; i++) {
|
|
395
|
+
let k = key_arr[i];
|
|
396
|
+
if (res === null || typeof res === 'undefined') return def;
|
|
397
|
+
res = res[k];
|
|
398
|
+
}
|
|
399
|
+
if (res === null || typeof res === 'undefined') return def;
|
|
400
|
+
return res;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* 日期处理
|
|
406
|
+
* @param {string|number|Date} date 日期,可以是字符串、时间戳(毫秒)
|
|
407
|
+
* @param {string} fmt 日期字符串格式,如果data为字符串,然后fmt为空,那么会推定日期为 年月日时分秒、日月年时分秒 中的一种
|
|
408
|
+
* @param {number} [zone] 时区,东正,西负。-12~12
|
|
409
|
+
* @returns {{timestamp: number, y: number, m: number, d: number, h: number, i: number, s: number, w: number, string: string, zone: number}}
|
|
410
|
+
*/
|
|
411
|
+
function date(date, fmt, zone){
|
|
412
|
+
function utc_date(str, fmt, utc_offset){
|
|
413
|
+
// 宽松的日期解析,按 fmt 指定的标识顺序位置的数字
|
|
414
|
+
let number_arr = str.match(/\d+/g);
|
|
415
|
+
let key_arr = []
|
|
416
|
+
if (fmt){
|
|
417
|
+
fmt = fmt.replace(/\\[ymdhis]/ig, ''); // 排除掉 \Y 之类的转义字符
|
|
418
|
+
key_arr = fmt.match(/[ymdhis]/ig);
|
|
419
|
+
} else {
|
|
420
|
+
// 没有指定 fmt 的情况下,根据年(值>1000)所在位置判断,分两种情况:年月日时分秒、日月年时分秒
|
|
421
|
+
let year_index = number_arr.findIndex(v=>v>1000)
|
|
422
|
+
if (year_index === 0){
|
|
423
|
+
key_arr = ['y','m','d','h','i','s']
|
|
424
|
+
}else if (year_index === 2){
|
|
425
|
+
key_arr = ['d','m','y','h','i','s']
|
|
426
|
+
}else {
|
|
427
|
+
throw new Error('日期异常')
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
let value_map = {}
|
|
431
|
+
key_arr.forEach((k, i)=>{
|
|
432
|
+
value_map[k.toLowerCase()] = Number(number_arr[i] || 0)
|
|
433
|
+
})
|
|
434
|
+
let date = new Date()
|
|
435
|
+
let function_map = { y:'setUTCFullYear',m:'setUTCMonth',d:'setUTCDate',h:'setUTCHours',i:'setUTCMinutes',s:'setUTCSeconds' };
|
|
436
|
+
Object.keys(function_map).forEach(k=>{
|
|
437
|
+
value_map[k] = value_map[k] || 0;
|
|
438
|
+
if (k === 'm' && value_map[k] === 0){
|
|
439
|
+
// 月的默认值应该是 1 不是 0
|
|
440
|
+
value_map[k] = 1
|
|
441
|
+
}
|
|
442
|
+
let function_name = function_map[k]
|
|
443
|
+
date[function_name]( k==='m' ? value_map[k]-1 : value_map[k] );
|
|
444
|
+
})
|
|
445
|
+
value_map.w = date.getUTCDay();
|
|
446
|
+
// 时区转换,例如:在东8时区传入的是1970 年 1 月 1 日 08:00:00。因为使用的是 setUTCHours ,得到的 date.getTime() = 8*60*1000,
|
|
447
|
+
value_map.timestamp = date.getTime() - utc_offset;
|
|
448
|
+
return value_map;
|
|
449
|
+
}
|
|
450
|
+
function format_time(timestamp, fmt, utc_offset){
|
|
451
|
+
const time = new Date(timestamp + utc_offset)
|
|
452
|
+
function add0(n){ return n < 10 ? ('0' + n) : n}
|
|
453
|
+
let res = {
|
|
454
|
+
y: time.getUTCFullYear(),
|
|
455
|
+
m: time.getUTCMonth() + 1,
|
|
456
|
+
d: time.getUTCDate(),
|
|
457
|
+
h: time.getUTCHours(),
|
|
458
|
+
i: time.getUTCMinutes(),
|
|
459
|
+
s: time.getUTCSeconds(),
|
|
460
|
+
w: time.getUTCDay(),
|
|
461
|
+
}
|
|
462
|
+
let v = {
|
|
463
|
+
...res,
|
|
464
|
+
W: ['日','一','二','三','四','五','六'][res.w],
|
|
465
|
+
w: ['7','1','2','3','4','5','6'][res.w],
|
|
466
|
+
}
|
|
467
|
+
v.Y = v.y; v.M = add0(v.m); v.D = add0(v.d); v.H = add0(v.h); v.I = add0(v.i); v.S = add0(v.s);
|
|
468
|
+
res.string = fmt.replace(/(^|[^\\])([YMDHISWymdhisw]+)/g, function(m,m1,m2){
|
|
469
|
+
let str = m1;
|
|
470
|
+
for(let i=0; i<m2.length; i++ ){
|
|
471
|
+
str += v[m2[i]];
|
|
472
|
+
}
|
|
473
|
+
return str;
|
|
474
|
+
}).replace(/\\([YMDHISWymdhisw])/g, "$1");
|
|
475
|
+
return res;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
zone = Number(zone);
|
|
479
|
+
// utc需要偏移多少毫秒后,才能得到和指定时区相同的日期,例如:东8时区8点时,utc得到的是0点,需要+480分钟后,utc得到的才是8点,那么 utc_offset=480*60*1000
|
|
480
|
+
// 即:(new Date(时间戳)).getHours() == (new Date(时间戳 + utc_offset)).getUTCHours()
|
|
481
|
+
if (isNaN(zone)){
|
|
482
|
+
zone = -(new Date()).getTimezoneOffset()/60
|
|
483
|
+
}
|
|
484
|
+
const utc_offset = zone*3600*1000;
|
|
485
|
+
|
|
486
|
+
const res = {
|
|
487
|
+
timestamp: NaN,
|
|
488
|
+
y: NaN,
|
|
489
|
+
m: NaN,
|
|
490
|
+
d: NaN,
|
|
491
|
+
h: NaN,
|
|
492
|
+
i: NaN,
|
|
493
|
+
s: NaN,
|
|
494
|
+
w: NaN,
|
|
495
|
+
string: '',
|
|
496
|
+
zone,
|
|
497
|
+
}
|
|
498
|
+
if (date instanceof Date){
|
|
499
|
+
let timestamp = date.getTime();
|
|
500
|
+
Object.assign(res, format_time(timestamp, fmt, utc_offset))
|
|
501
|
+
res.timestamp = timestamp;
|
|
502
|
+
}else if (typeof date === 'number'){
|
|
503
|
+
Object.assign(res, format_time(date, fmt, utc_offset))
|
|
504
|
+
res.timestamp = date;
|
|
505
|
+
}else if (typeof date === 'string'){
|
|
506
|
+
Object.assign(res, utc_date(date, fmt, utc_offset))
|
|
507
|
+
res.string = date;
|
|
508
|
+
}
|
|
509
|
+
return res;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const s94_event = {
|
|
513
|
+
__event_list: {},
|
|
514
|
+
on(event_name, callback, first){
|
|
515
|
+
if (!event_name || typeof event_name !== 'string') return;
|
|
516
|
+
if (typeof callback !== 'function') return;
|
|
517
|
+
let list = s94_event.__event_list[event_name] || [];
|
|
518
|
+
first ? list.unshift(callback) : list.push(callback);
|
|
519
|
+
s94_event.__event_list[event_name] = list;
|
|
520
|
+
},
|
|
521
|
+
off(event_name, callback){
|
|
522
|
+
if (!event_name || typeof event_name !== 'string') return;
|
|
523
|
+
if (typeof callback === 'function'){
|
|
524
|
+
let list = s94_event.__event_list[event_name] || [];
|
|
525
|
+
let i = list.indexOf(callback);
|
|
526
|
+
if (i !== -1) list.splice(i,1);
|
|
527
|
+
s94_event.__event_list[event_name] = list;
|
|
528
|
+
}else if (typeof callback === 'undefined'){
|
|
529
|
+
s94_event.__event_list[event_name] = [];
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
emit(event_name, data, thisArg){
|
|
533
|
+
if (!event_name || typeof event_name !== 'string') return;
|
|
534
|
+
let list = s94_event.__event_list[event_name] || [];
|
|
535
|
+
for (let i = 0; i < list; i++) {
|
|
536
|
+
if (list[i].call(thisArg, data) === false) break;
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
export {
|
|
542
|
+
Queue,
|
|
543
|
+
Cache,
|
|
544
|
+
var_type,
|
|
545
|
+
to_string,
|
|
546
|
+
trim,
|
|
547
|
+
path,
|
|
548
|
+
each,
|
|
549
|
+
eachloop,
|
|
550
|
+
param_decode,
|
|
551
|
+
param_encode,
|
|
552
|
+
encode,
|
|
553
|
+
map,
|
|
554
|
+
date,
|
|
555
|
+
s94_event,
|
|
556
|
+
}
|