@wiajs/core 0.1.19 → 1.0.2
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/.eslintrc.js +104 -73
- package/.prettierignore +5 -7
- package/LICENSE +1 -1
- package/babel.config.js +73 -19
- package/babel.web.js +86 -0
- package/dist/core.cmn.js +2209 -0
- package/dist/core.esm.js +1463 -1509
- package/dist/core.js +406 -893
- package/dist/core.min.js +9 -4
- package/gulpfile.js +57 -8
- package/index.js +2 -2
- package/package.json +17 -19
- package/prettier.config.js +4 -1
- package/test/test.js +73 -0
- package/util/tool.js +29 -26
- package/build.js +0 -102
- package/config.js +0 -69
- package/dist/core.common.js +0 -2257
- package/dist/core.common.min.js +0 -6
- package/dist/core.esm.min.js +0 -6
package/dist/core.cmn.js
ADDED
|
@@ -0,0 +1,2209 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* wia core v1.0.1
|
|
3
|
+
* (c) 2015-2023 Sibyl Yu and contributors
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* promise version ajax get、post
|
|
10
|
+
* return Promise objext.
|
|
11
|
+
* get move to base.js
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
class Ajax {
|
|
15
|
+
post(url, data) {
|
|
16
|
+
const pm = new Promise((res, rej) => {
|
|
17
|
+
const xhr = $.getXhr();
|
|
18
|
+
xhr.onreadystatechange = () => {
|
|
19
|
+
if (xhr.readyState === 4) {
|
|
20
|
+
if (xhr.status === 200) res(xhr.responseText);
|
|
21
|
+
else rej(new Error(xhr.statusText), xhr.responseText);
|
|
22
|
+
}
|
|
23
|
+
/*
|
|
24
|
+
if ((xhr.readyState === 4) && (xhr.status === 200)) {
|
|
25
|
+
cb(xhr.responseText);
|
|
26
|
+
}
|
|
27
|
+
*/
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// 异步 post,回调通知
|
|
31
|
+
xhr.open('POST', url, true);
|
|
32
|
+
let param = data;
|
|
33
|
+
if (typeof data === 'object') param = objToParam(data);
|
|
34
|
+
|
|
35
|
+
// 发送 FormData 数据, 会自动设置为 multipart/form-data
|
|
36
|
+
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
|
37
|
+
// xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=AaB03x');
|
|
38
|
+
// alert(param);
|
|
39
|
+
xhr.send(param);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return pm;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* xmlHttpRequest POST 方法
|
|
47
|
+
* 发送 FormData 数据, 会自动设置为 multipart/form-data
|
|
48
|
+
* 其他数据,应该是 application/x-www-form-urlencoded
|
|
49
|
+
* @param url post的url地址
|
|
50
|
+
* @param data 要post的数据
|
|
51
|
+
* @param cb 回调
|
|
52
|
+
*/
|
|
53
|
+
postForm(url, data) {
|
|
54
|
+
const pm = new Promise((res, rej) => {
|
|
55
|
+
const xhr = $.getXhr();
|
|
56
|
+
xhr.onreadystatechange = () => {
|
|
57
|
+
if (xhr.readyState === 4) {
|
|
58
|
+
if (xhr.status === 200) res(null, xhr.responseText);
|
|
59
|
+
else rej(new Error(xhr.status), xhr.responseText);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// 异步 post,回调通知
|
|
64
|
+
xhr.open('POST', url, true);
|
|
65
|
+
// 发送 FormData 数据, 会自动设置为 multipart/form-data
|
|
66
|
+
// xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
|
67
|
+
// xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=AaB03x');
|
|
68
|
+
xhr.send(data);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return pm;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get(url, param) {
|
|
75
|
+
return $.get(url, param);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function objToParam(obj) {
|
|
80
|
+
let rs = '';
|
|
81
|
+
|
|
82
|
+
const arr = [];
|
|
83
|
+
for (const k in obj) {
|
|
84
|
+
if (obj.hasOwnProperty(k)) {
|
|
85
|
+
arr.push(`${k}=${obj[k]}`);
|
|
86
|
+
}
|
|
87
|
+
// rs += `${k}=${obj[k]}&`;
|
|
88
|
+
}
|
|
89
|
+
// 排序
|
|
90
|
+
rs = arr.sort().join('&');
|
|
91
|
+
// alert(rs);
|
|
92
|
+
return rs;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* eslint no-control-regex: "off" */
|
|
96
|
+
|
|
97
|
+
let uniqueNumber = 1;
|
|
98
|
+
|
|
99
|
+
const Utils = {
|
|
100
|
+
uniqueNumber() {
|
|
101
|
+
uniqueNumber += 1;
|
|
102
|
+
return uniqueNumber;
|
|
103
|
+
},
|
|
104
|
+
id(mask = 'xxxxxxxxxx', map = '0123456789abcdef') {
|
|
105
|
+
return $.uid(mask, map);
|
|
106
|
+
},
|
|
107
|
+
mdPreloaderContent: `
|
|
108
|
+
<span class="preloader-inner">
|
|
109
|
+
<svg viewBox="0 0 36 36">
|
|
110
|
+
<circle cx="18" cy="18" r="16"></circle>
|
|
111
|
+
</svg>
|
|
112
|
+
</span>
|
|
113
|
+
`.trim(),
|
|
114
|
+
iosPreloaderContent: `
|
|
115
|
+
<span class="preloader-inner">
|
|
116
|
+
${[0, 1, 2, 3, 4, 5, 6, 7].map(() => '<span class="preloader-inner-line"></span>').join('')}
|
|
117
|
+
</span>
|
|
118
|
+
`.trim(),
|
|
119
|
+
auroraPreloaderContent:`
|
|
120
|
+
<span class="preloader-inner">
|
|
121
|
+
<span class="preloader-inner-circle"></span>
|
|
122
|
+
</span>
|
|
123
|
+
`,
|
|
124
|
+
eventNameToColonCase(eventName) {
|
|
125
|
+
let hasColon;
|
|
126
|
+
return eventName
|
|
127
|
+
.split('')
|
|
128
|
+
.map((char, index) => {
|
|
129
|
+
if (char.match(/[A-Z]/) && index !== 0 && !hasColon) {
|
|
130
|
+
hasColon = true;
|
|
131
|
+
return `:${char.toLowerCase()}`;
|
|
132
|
+
}
|
|
133
|
+
return char.toLowerCase();
|
|
134
|
+
})
|
|
135
|
+
.join('');
|
|
136
|
+
},
|
|
137
|
+
deleteProps(obj) {
|
|
138
|
+
$.deleteProps(obj);
|
|
139
|
+
},
|
|
140
|
+
nextTick(callback, delay = 0) {
|
|
141
|
+
return setTimeout(callback, delay);
|
|
142
|
+
},
|
|
143
|
+
nextFrame(cb) {
|
|
144
|
+
return $.nextFrame(cb);
|
|
145
|
+
},
|
|
146
|
+
now() {
|
|
147
|
+
return Date.now();
|
|
148
|
+
},
|
|
149
|
+
requestAnimationFrame(cb) {
|
|
150
|
+
return $.requestAnimationFrame(cb);
|
|
151
|
+
},
|
|
152
|
+
cancelAnimationFrame(id) {
|
|
153
|
+
return $.cancelAnimationFrame(id);
|
|
154
|
+
},
|
|
155
|
+
parseUrlQuery(url) {
|
|
156
|
+
return $.urlParam(url);
|
|
157
|
+
},
|
|
158
|
+
getTranslate(el, axis = 'x') {
|
|
159
|
+
return $.getTranslate(el, axis);
|
|
160
|
+
},
|
|
161
|
+
serializeObject(obj, parents = []) {
|
|
162
|
+
if (typeof obj === 'string') return obj;
|
|
163
|
+
const resultArray = [];
|
|
164
|
+
const separator = '&';
|
|
165
|
+
let newParents;
|
|
166
|
+
function varName(name) {
|
|
167
|
+
if (parents.length > 0) {
|
|
168
|
+
let parentParts = '';
|
|
169
|
+
for (let j = 0; j < parents.length; j += 1) {
|
|
170
|
+
if (j === 0) parentParts += parents[j];
|
|
171
|
+
else parentParts += `[${encodeURIComponent(parents[j])}]`;
|
|
172
|
+
}
|
|
173
|
+
return `${parentParts}[${encodeURIComponent(name)}]`;
|
|
174
|
+
}
|
|
175
|
+
return encodeURIComponent(name);
|
|
176
|
+
}
|
|
177
|
+
function varValue(value) {
|
|
178
|
+
return encodeURIComponent(value);
|
|
179
|
+
}
|
|
180
|
+
Object.keys(obj).forEach(prop => {
|
|
181
|
+
let toPush;
|
|
182
|
+
if (Array.isArray(obj[prop])) {
|
|
183
|
+
toPush = [];
|
|
184
|
+
for (let i = 0; i < obj[prop].length; i += 1) {
|
|
185
|
+
if (!Array.isArray(obj[prop][i]) && typeof obj[prop][i] === 'object') {
|
|
186
|
+
newParents = parents.slice();
|
|
187
|
+
newParents.push(prop);
|
|
188
|
+
newParents.push(String(i));
|
|
189
|
+
toPush.push(Utils.serializeObject(obj[prop][i], newParents));
|
|
190
|
+
} else {
|
|
191
|
+
toPush.push(`${varName(prop)}[]=${varValue(obj[prop][i])}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (toPush.length > 0) resultArray.push(toPush.join(separator));
|
|
195
|
+
} else if (obj[prop] === null || obj[prop] === '') {
|
|
196
|
+
resultArray.push(`${varName(prop)}=`);
|
|
197
|
+
} else if (typeof obj[prop] === 'object') {
|
|
198
|
+
// Object, convert to named array
|
|
199
|
+
newParents = parents.slice();
|
|
200
|
+
newParents.push(prop);
|
|
201
|
+
toPush = Utils.serializeObject(obj[prop], newParents);
|
|
202
|
+
if (toPush !== '') resultArray.push(toPush);
|
|
203
|
+
} else if (typeof obj[prop] !== 'undefined' && obj[prop] !== '') {
|
|
204
|
+
// Should be string or plain value
|
|
205
|
+
resultArray.push(`${varName(prop)}=${varValue(obj[prop])}`);
|
|
206
|
+
} else if (obj[prop] === '') resultArray.push(varName(prop));
|
|
207
|
+
});
|
|
208
|
+
return resultArray.join(separator);
|
|
209
|
+
},
|
|
210
|
+
isObject(o) {
|
|
211
|
+
return typeof o === 'object' && o !== null && o.constructor && o.constructor === Object;
|
|
212
|
+
},
|
|
213
|
+
merge(...args) {
|
|
214
|
+
return $.merge(...args);
|
|
215
|
+
},
|
|
216
|
+
extend(...args) {
|
|
217
|
+
const to = args[0];
|
|
218
|
+
args.splice(0, 1);
|
|
219
|
+
return $.assign(to, ...args);
|
|
220
|
+
},
|
|
221
|
+
// ���������ʵ�������������ԡ���������
|
|
222
|
+
bindMethods(instance, obj) {
|
|
223
|
+
Object.keys(obj).forEach((key) => {
|
|
224
|
+
if (this.isObject(obj[key])) {
|
|
225
|
+
Object.keys(obj[key]).forEach((subKey) => {
|
|
226
|
+
if (typeof obj[key][subKey] === 'function') {
|
|
227
|
+
obj[key][subKey] = obj[key][subKey].bind(instance);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
instance[key] = obj[key];
|
|
232
|
+
});
|
|
233
|
+
},
|
|
234
|
+
colorHexToRgb(hex) {
|
|
235
|
+
const h = hex.replace(
|
|
236
|
+
/^#?([a-f\d])([a-f\d])([a-f\d])$/i,
|
|
237
|
+
(m, r, g, b) => r + r + g + g + b + b
|
|
238
|
+
);
|
|
239
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(h);
|
|
240
|
+
return result ? result.slice(1).map(n => parseInt(n, 16)) : null;
|
|
241
|
+
},
|
|
242
|
+
colorRgbToHex(r, g, b) {
|
|
243
|
+
const result = [r, g, b]
|
|
244
|
+
.map(n => {
|
|
245
|
+
const hex = n.toString(16);
|
|
246
|
+
return hex.length === 1 ? `0${hex}` : hex;
|
|
247
|
+
})
|
|
248
|
+
.join('');
|
|
249
|
+
return `#${result}`;
|
|
250
|
+
},
|
|
251
|
+
colorRgbToHsl(r, g, b) {
|
|
252
|
+
r /= 255; // eslint-disable-line
|
|
253
|
+
g /= 255; // eslint-disable-line
|
|
254
|
+
b /= 255; // eslint-disable-line
|
|
255
|
+
const max = Math.max(r, g, b);
|
|
256
|
+
const min = Math.min(r, g, b);
|
|
257
|
+
const d = max - min;
|
|
258
|
+
let h;
|
|
259
|
+
if (d === 0) h = 0;
|
|
260
|
+
else if (max === r) h = ((g - b) / d) % 6;
|
|
261
|
+
else if (max === g) h = (b - r) / d + 2;
|
|
262
|
+
else if (max === b) h = (r - g) / d + 4;
|
|
263
|
+
const l = (min + max) / 2;
|
|
264
|
+
const s = d === 0 ? 0 : d / (1 - Math.abs(2 * l - 1));
|
|
265
|
+
if (h < 0) h = 360 / 60 + h;
|
|
266
|
+
return [h * 60, s, l];
|
|
267
|
+
},
|
|
268
|
+
colorHslToRgb(h, s, l) {
|
|
269
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
270
|
+
const hp = h / 60;
|
|
271
|
+
const x = c * (1 - Math.abs((hp % 2) - 1));
|
|
272
|
+
let rgb1;
|
|
273
|
+
if (Number.isNaN(h) || typeof h === 'undefined') {
|
|
274
|
+
rgb1 = [0, 0, 0];
|
|
275
|
+
} else if (hp <= 1) rgb1 = [c, x, 0];
|
|
276
|
+
else if (hp <= 2) rgb1 = [x, c, 0];
|
|
277
|
+
else if (hp <= 3) rgb1 = [0, c, x];
|
|
278
|
+
else if (hp <= 4) rgb1 = [0, x, c];
|
|
279
|
+
else if (hp <= 5) rgb1 = [x, 0, c];
|
|
280
|
+
else if (hp <= 6) rgb1 = [c, 0, x];
|
|
281
|
+
const m = l - c / 2;
|
|
282
|
+
return rgb1.map(n => Math.max(0, Math.min(255, Math.round(255 * (n + m)))));
|
|
283
|
+
},
|
|
284
|
+
colorHsbToHsl(h, s, b) {
|
|
285
|
+
const HSL = {
|
|
286
|
+
h,
|
|
287
|
+
s: 0,
|
|
288
|
+
l: 0,
|
|
289
|
+
};
|
|
290
|
+
const HSB = {h, s, b};
|
|
291
|
+
|
|
292
|
+
HSL.l = ((2 - HSB.s) * HSB.b) / 2;
|
|
293
|
+
HSL.s =
|
|
294
|
+
HSL.l && HSL.l < 1 ? (HSB.s * HSB.b) / (HSL.l < 0.5 ? HSL.l * 2 : 2 - HSL.l * 2) : HSL.s;
|
|
295
|
+
|
|
296
|
+
return [HSL.h, HSL.s, HSL.l];
|
|
297
|
+
},
|
|
298
|
+
colorHslToHsb(h, s, l) {
|
|
299
|
+
const HSB = {
|
|
300
|
+
h,
|
|
301
|
+
s: 0,
|
|
302
|
+
b: 0,
|
|
303
|
+
};
|
|
304
|
+
const HSL = {h, s, l};
|
|
305
|
+
|
|
306
|
+
const t = HSL.s * (HSL.l < 0.5 ? HSL.l : 1 - HSL.l);
|
|
307
|
+
HSB.b = HSL.l + t;
|
|
308
|
+
HSB.s = HSL.l > 0 ? (2 * t) / HSB.b : HSB.s;
|
|
309
|
+
|
|
310
|
+
return [HSB.h, HSB.s, HSB.b];
|
|
311
|
+
},
|
|
312
|
+
colorThemeCSSProperties(...args) {
|
|
313
|
+
let hex;
|
|
314
|
+
let rgb;
|
|
315
|
+
if (args.length === 1) {
|
|
316
|
+
hex = args[0];
|
|
317
|
+
rgb = Utils.colorHexToRgb(hex);
|
|
318
|
+
} else if (args.length === 3) {
|
|
319
|
+
rgb = args;
|
|
320
|
+
hex = Utils.colorRgbToHex(...rgb);
|
|
321
|
+
}
|
|
322
|
+
if (!rgb) return {};
|
|
323
|
+
const hsl = Utils.colorRgbToHsl(...rgb);
|
|
324
|
+
const hslShade = [hsl[0], hsl[1], Math.max(0, hsl[2] - 0.08)];
|
|
325
|
+
const hslTint = [hsl[0], hsl[1], Math.max(0, hsl[2] + 0.08)];
|
|
326
|
+
const shade = Utils.colorRgbToHex(...Utils.colorHslToRgb(...hslShade));
|
|
327
|
+
const tint = Utils.colorRgbToHex(...Utils.colorHslToRgb(...hslTint));
|
|
328
|
+
return {
|
|
329
|
+
'--f7-theme-color': hex,
|
|
330
|
+
'--f7-theme-color-rgb': rgb.join(', '),
|
|
331
|
+
'--f7-theme-color-shade': shade,
|
|
332
|
+
'--f7-theme-color-tint': tint,
|
|
333
|
+
};
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* 事件类,提供对象的事件侦听、触发,只在类实例中有效。
|
|
339
|
+
* 需要支持事件的对象,可以从这个类继承,则类实例具备事件功能。
|
|
340
|
+
* Fork from Framework7,
|
|
341
|
+
*/
|
|
342
|
+
class Event {
|
|
343
|
+
/**
|
|
344
|
+
* 页面Page实例事件触发,f7 UI组件需要
|
|
345
|
+
* @param {Object} params 参数
|
|
346
|
+
* @param {Array} parents 事件组件的父对象,用于向上传播事件!
|
|
347
|
+
* 组件的parents 是 Page实例,Page实例的Parent是App实例
|
|
348
|
+
* @param {String} pre 向上传播前缀,避免事件重名冲突
|
|
349
|
+
* @private
|
|
350
|
+
*/
|
|
351
|
+
constructor(params = {}, parents = [], pre = '') {
|
|
352
|
+
const m = this;
|
|
353
|
+
m.params = params;
|
|
354
|
+
|
|
355
|
+
if (parents) {
|
|
356
|
+
if (!Array.isArray(parents)) m.eventsParents = [parents];
|
|
357
|
+
else m.eventsParents = parents.filter(p => p);
|
|
358
|
+
} else m.eventsParents = [];
|
|
359
|
+
|
|
360
|
+
m.eventsListeners = {};
|
|
361
|
+
m.pre = pre;
|
|
362
|
+
|
|
363
|
+
// 通过 params 中的 on 加载事件响应
|
|
364
|
+
if (m.params && m.params.on) {
|
|
365
|
+
Object.keys(m.params.on).forEach(eventName => {
|
|
366
|
+
m.on(eventName, m.params.on[eventName]);
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* 添加事件响应函数
|
|
373
|
+
* @param {*} events 多个事件用空格隔开
|
|
374
|
+
* @param {*} handler 事件响应函数
|
|
375
|
+
* @param {*} priority 是否优先,缺省不优先
|
|
376
|
+
* @returns
|
|
377
|
+
*/
|
|
378
|
+
on(events, handler, priority = false) {
|
|
379
|
+
const m = this;
|
|
380
|
+
if (typeof handler !== 'function') return m;
|
|
381
|
+
const method = priority ? 'unshift' : 'push';
|
|
382
|
+
events.split(' ').forEach(event => {
|
|
383
|
+
const lis = {
|
|
384
|
+
owner: '',
|
|
385
|
+
appName: '',
|
|
386
|
+
handler,
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
// 应用事件
|
|
390
|
+
if (event.includes('app::')) {
|
|
391
|
+
let page = null;
|
|
392
|
+
if ($.isPage(m)) page = m;
|
|
393
|
+
else if ($.isPage(m?.page)) page = m.page;
|
|
394
|
+
else if ($.isPage(m?.parent)) page = m.parent;
|
|
395
|
+
|
|
396
|
+
if (page && page.app && $.isApp(page.app)) {
|
|
397
|
+
lis.owner = page.owner;
|
|
398
|
+
lis.appName = page.appName;
|
|
399
|
+
const {app} = page;
|
|
400
|
+
|
|
401
|
+
const ev = event.replace('app::', '');
|
|
402
|
+
if (!app.eventsListeners[ev]) app.eventsListeners[ev] = [];
|
|
403
|
+
app.eventsListeners[ev][method](lis);
|
|
404
|
+
}
|
|
405
|
+
} else {
|
|
406
|
+
// 对象自身事件
|
|
407
|
+
if (!m.eventsListeners[event]) m.eventsListeners[event] = [];
|
|
408
|
+
|
|
409
|
+
m.eventsListeners[event][method](lis);
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
return m;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* 调用一次后清除
|
|
418
|
+
* @param {*} events 多个事件用空格隔开
|
|
419
|
+
* @param {*} handler 事件响应函数
|
|
420
|
+
* @param {*} priority 是否优先,缺省不优先
|
|
421
|
+
* @returns
|
|
422
|
+
*/
|
|
423
|
+
once(events, handler, priority = false) {
|
|
424
|
+
const m = this;
|
|
425
|
+
if (typeof handler !== 'function') return m;
|
|
426
|
+
|
|
427
|
+
// 调用一次后自动删除事件
|
|
428
|
+
function onceHandler(...args) {
|
|
429
|
+
m.off(events, onceHandler);
|
|
430
|
+
if (onceHandler.proxy) {
|
|
431
|
+
onceHandler.proxy.apply(m, args);
|
|
432
|
+
delete onceHandler.proxy;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
onceHandler.proxy = handler;
|
|
437
|
+
return m.on(events, onceHandler, priority);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* 删除事件响应函数
|
|
442
|
+
* @param {*} events 事件,多个事件空格隔开,不传则清除该对象所有事件响应函数
|
|
443
|
+
* @param {*} handler 事件响应函数
|
|
444
|
+
* @returns
|
|
445
|
+
*/
|
|
446
|
+
off(events, handler) {
|
|
447
|
+
const m = this;
|
|
448
|
+
if (!m.eventsListeners) return m;
|
|
449
|
+
|
|
450
|
+
if (events) {
|
|
451
|
+
events.split(' ').forEach(event => {
|
|
452
|
+
if (typeof handler === 'undefined') m.eventsListeners[event] = [];
|
|
453
|
+
else if (m.eventsListeners[event]) {
|
|
454
|
+
const arr = m.eventsListeners[event];
|
|
455
|
+
for (let i = arr.length - 1; i >= 0; i--) {
|
|
456
|
+
const lis = arr[i];
|
|
457
|
+
if (lis.handler === handler || lis.handler?.proxy === handler)
|
|
458
|
+
arr.splice(i, 1);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
} else m.eventsListeners = {};
|
|
463
|
+
|
|
464
|
+
return m;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* 事件触发,应用事件只能由 Page 实例触发,才能按同页面所有者触发事件
|
|
469
|
+
* @param {*} 事件,字符串、数组或对象
|
|
470
|
+
* @param {*} 数据,传递到事件响应函数的数据
|
|
471
|
+
*/
|
|
472
|
+
emit(...args) {
|
|
473
|
+
const m = this;
|
|
474
|
+
if (!m.eventsListeners) return m;
|
|
475
|
+
|
|
476
|
+
let events;
|
|
477
|
+
let data;
|
|
478
|
+
let context;
|
|
479
|
+
let eventsParents;
|
|
480
|
+
|
|
481
|
+
let pop = false;
|
|
482
|
+
|
|
483
|
+
let event = args[0]; // 事件
|
|
484
|
+
if (!event) return m;
|
|
485
|
+
|
|
486
|
+
// 原始触发事件
|
|
487
|
+
if (typeof event === 'string' || Array.isArray(event)) {
|
|
488
|
+
event = event.split(' ');
|
|
489
|
+
// 带前缀,自动添加前缀向父节点传递事件
|
|
490
|
+
if (m.pre) {
|
|
491
|
+
events = [];
|
|
492
|
+
event.forEach(ev => {
|
|
493
|
+
events.push(`.${ev}`); // 本组件事件
|
|
494
|
+
events.push(`${m.pre}${ev[0].toUpperCase()}${ev.substr(1)}`); // 向上事件
|
|
495
|
+
});
|
|
496
|
+
} else events = event;
|
|
497
|
+
|
|
498
|
+
data = args.slice(1, args.length);
|
|
499
|
+
context = m;
|
|
500
|
+
eventsParents = m.eventsParents;
|
|
501
|
+
} else {
|
|
502
|
+
// 冒泡向上传递事件,或指定对象触发事件
|
|
503
|
+
pop = event.pop;
|
|
504
|
+
events = event.events;
|
|
505
|
+
data = event.data;
|
|
506
|
+
context = event.context || m;
|
|
507
|
+
eventsParents = event.local ? [] : event.parents || m.eventsParents;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const eventsArray = Array.isArray(events) ? events : events.split(' ');
|
|
511
|
+
|
|
512
|
+
// 本对象事件
|
|
513
|
+
// ['local:event'] or ['.event'],不向父组件传递
|
|
514
|
+
const selfEvents = eventsArray.map(ev => ev.replace(/local::|^[.]/, ''));
|
|
515
|
+
|
|
516
|
+
// 非本对象事件,向上传递时,转换为对象,记录来源
|
|
517
|
+
let parentEvents = null;
|
|
518
|
+
if (pop) parentEvents = event;
|
|
519
|
+
else {
|
|
520
|
+
const popEvents = eventsArray.filter(ev => !ev.match(/^local::|^[.]/));
|
|
521
|
+
if (popEvents?.length) {
|
|
522
|
+
parentEvents = {
|
|
523
|
+
pop: true, // 冒泡事件
|
|
524
|
+
events: popEvents,
|
|
525
|
+
context: m, // 事件发起者
|
|
526
|
+
data,
|
|
527
|
+
owner: '',
|
|
528
|
+
appName: '',
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// 记录page属性,标记事件来源,冒泡到app时判断是否触发本页面应用事件
|
|
534
|
+
if (parentEvents && $.isPage(m)) {
|
|
535
|
+
parentEvents.owner = m?.owner;
|
|
536
|
+
parentEvents.appName = m?.appName;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// 调用对象事件函数,父对象emit后,调用父对象事件函数
|
|
540
|
+
selfEvents.forEach(ev => {
|
|
541
|
+
if (m.eventsListeners && m.eventsListeners[ev]) {
|
|
542
|
+
const handlers = [];
|
|
543
|
+
m.eventsListeners[ev].forEach(lis => {
|
|
544
|
+
// 应用事件,需判断所有者
|
|
545
|
+
if (lis.owner && lis.appName) {
|
|
546
|
+
// 同一html页面运行多个应用页面层时,只有所有者、应用名称相同才能触发跨页面事件,避免跨应用事件安全问题。
|
|
547
|
+
// 页面冒泡到应用事件
|
|
548
|
+
if (pop && lis.owner === ev.owner && lis.appName === ev.appName)
|
|
549
|
+
handlers.push(lis.handler);
|
|
550
|
+
} else handlers.push(lis.handler);
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
// 由 window 对象异步调用,而不是事件对象直接调用
|
|
554
|
+
handlers.forEach(fn => {
|
|
555
|
+
// setTimeout(() => fn.apply(context, data), 0);
|
|
556
|
+
fn.apply(context, data); // this 指针为原始触发事件对象,事件函数中可引用
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
// 向上一级一级迭代冒泡传递后,触发父对象事件响应函数
|
|
562
|
+
if (parentEvents && eventsParents?.length > 0) {
|
|
563
|
+
eventsParents.forEach(eventsParent => eventsParent.emit(parentEvents));
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return m;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* 所有页面从该类继承,并必须实现 load 事件!
|
|
572
|
+
* 事件
|
|
573
|
+
* 五个个事件:load -> ready -> show / hide -> unload
|
|
574
|
+
* load:必选,加载视图、代码,第一次加载后缓存,后续不会重复加载,动态代码也要在这里加载
|
|
575
|
+
* 参数;param
|
|
576
|
+
* 如果需要前路由数据,通过 $.lastPage.data 访问
|
|
577
|
+
* view 还未创建,隐藏page 不存在
|
|
578
|
+
* ready:可选,对视图中的对象事件绑定,已经缓存的视图,比如回退,不会再次触发 ready
|
|
579
|
+
* 参数;view、param
|
|
580
|
+
* 如果需要前路由数据,通过 $.lastPage.data 访问
|
|
581
|
+
* show:可选,视图显示时触发,可以接收参数,操作视图,无论是否缓存(比如回退)都会触发
|
|
582
|
+
* 对于已经加载、绑定隐藏(缓存)的页面,重新显示时,不会触发load和ready,只会触发show
|
|
583
|
+
* 参数:view、param
|
|
584
|
+
* hide:可选,视图卸载删除时触发,适合保存卸载页面的数据,卸载的页面从页面删除,进入缓存
|
|
585
|
+
* unload:可选,页面从缓存中删除时触发,目前暂未实现
|
|
586
|
+
*
|
|
587
|
+
* 数据传递
|
|
588
|
+
* 每个页面都能访问当前路由,路由存在以下参数,用户跨页面数据传递
|
|
589
|
+
* url:页面跳转时的原始网址
|
|
590
|
+
* param:页面网址及go中传入的参数合并,保存在 param 中
|
|
591
|
+
* data:路由中需要保存的数据
|
|
592
|
+
* view:当前页面层,dom 对象,已经包括绑定的事件
|
|
593
|
+
* $.page:当前页面对象
|
|
594
|
+
* $.lastPage:前路由,可通过该参数,获取前路由的 data,在后续路由中使用
|
|
595
|
+
*
|
|
596
|
+
*/
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
class Page extends Event {
|
|
600
|
+
constructor(app, name, title, style) {
|
|
601
|
+
super(null, [app]);
|
|
602
|
+
this.app = app; // 应用实例
|
|
603
|
+
this.cfg = app.cfg;
|
|
604
|
+
this.name = name; // 名称,可带路径 admin/login
|
|
605
|
+
this.title = title; // 浏览器标题
|
|
606
|
+
this.style = style || `./page/${name}.css`;
|
|
607
|
+
|
|
608
|
+
// 以下变量由路由器赋值
|
|
609
|
+
this.owner = '';
|
|
610
|
+
this.appName = '';
|
|
611
|
+
this.path = '';
|
|
612
|
+
this.view = null; // 页面的div层$Dom对象,router创建实例时赋值
|
|
613
|
+
this.dom = null; // 页面的div层dom对象,router创建实例时赋值
|
|
614
|
+
this.$el = null; // $dom === view
|
|
615
|
+
this.el = null; // dom === dom
|
|
616
|
+
|
|
617
|
+
this.html = ''; // 页面html文本,router创建实例时赋值
|
|
618
|
+
this.css = ''; // 页面css样式,router创建实例时赋值
|
|
619
|
+
this.js = ''; // 页面代码,router创建实例时赋值
|
|
620
|
+
this.data = {}; // 页面数据对象
|
|
621
|
+
this.param = {}; // 页面切换传递进来的参数对象,router创建实例时赋值
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* 异步加载页面视图内容
|
|
626
|
+
* 返回Promise对象
|
|
627
|
+
* @param {*} param
|
|
628
|
+
* @param {*} cfg
|
|
629
|
+
*/
|
|
630
|
+
load(param) {
|
|
631
|
+
// $.assign(this.data, param);
|
|
632
|
+
this.emit('local::load pageLoad', param);
|
|
633
|
+
this.emit('pageLoad', this);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* 在已经加载就绪的视图上操作
|
|
638
|
+
* @param {*} view 页面层的 Dom 对象,已经使用`$(#page-name)`,做了处理
|
|
639
|
+
* @param {*} param go 函数的参数,或 网址中 url 中的参数
|
|
640
|
+
* @param {*} back 是否为回退,A->B, B->A,这种操作属于回退
|
|
641
|
+
*/
|
|
642
|
+
ready(view, param, back) {
|
|
643
|
+
// $.assign(this, {page, param, back});
|
|
644
|
+
// $.assign(this.data, param);
|
|
645
|
+
// 隐藏所有模板
|
|
646
|
+
this.init();
|
|
647
|
+
this.emit('local::ready', view, param, back);
|
|
648
|
+
// 向上触发跨页面事件,存在安全问题
|
|
649
|
+
this.emit('pageReady', this);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* 对页面进行初始化处理,或页面内容动态变更时,对局部页面容器进行初始化
|
|
654
|
+
* @param {*} v dom 容器,默认为页面实例的view
|
|
655
|
+
*/
|
|
656
|
+
init(v) {
|
|
657
|
+
const {view} = this;
|
|
658
|
+
v = v ? $(v) : view;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// 显示已加载的页面
|
|
662
|
+
// view:页面Dom层,param:参数
|
|
663
|
+
show(view, param) {
|
|
664
|
+
// 隐藏所有模板
|
|
665
|
+
view.qus('[name$=-tp]').hide();
|
|
666
|
+
// 防止空链接,刷新页面
|
|
667
|
+
view.qus('a[href=""]').attr('href', 'javascript:;');
|
|
668
|
+
// this.init();
|
|
669
|
+
if (this.reset) this.reset();
|
|
670
|
+
this.emit('local::show', view, param);
|
|
671
|
+
// 向上触发跨页面事件,存在安全问题
|
|
672
|
+
this.emit('pageShow', this);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// 回退显示已加载的页面
|
|
676
|
+
// view:页面Dom层,param:参数
|
|
677
|
+
back(view, param) {
|
|
678
|
+
// 隐藏所有模板
|
|
679
|
+
view.qus('[name$=-tp]').hide();
|
|
680
|
+
// 防止空链接,刷新页面
|
|
681
|
+
view.qus('a[href=""]').attr('href', 'javascript:;');
|
|
682
|
+
|
|
683
|
+
this.emit('local::back', view, param);
|
|
684
|
+
// 向上触发跨页面事件,存在安全问题
|
|
685
|
+
this.emit('pageBack', this);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
hide(view) {
|
|
689
|
+
this.emit('local::hide', view);
|
|
690
|
+
// 向上触发跨页面事件,存在安全问题
|
|
691
|
+
this.emit('pageHide', this);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
unload(view) {
|
|
695
|
+
this.emit('local::unload', view);
|
|
696
|
+
// 向上触发跨页面事件,存在安全问题
|
|
697
|
+
this.emit('pageUnload', this);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Wia app、router等继承类,通过模块化扩展类功能
|
|
703
|
+
*/
|
|
704
|
+
|
|
705
|
+
class Module extends Event {
|
|
706
|
+
constructor(params = {}, parents = []) {
|
|
707
|
+
super(params, parents);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// eslint-disable-next-line
|
|
711
|
+
useModuleParams(module, instanceParams) {
|
|
712
|
+
if (module.params) {
|
|
713
|
+
const originalParams = {};
|
|
714
|
+
Object.keys(module.params).forEach(paramKey => {
|
|
715
|
+
if (typeof instanceParams[paramKey] === 'undefined') return;
|
|
716
|
+
originalParams[paramKey] = $.extend({}, instanceParams[paramKey]);
|
|
717
|
+
});
|
|
718
|
+
$.extend(instanceParams, module.params);
|
|
719
|
+
Object.keys(originalParams).forEach(paramKey => {
|
|
720
|
+
$.extend(instanceParams[paramKey], originalParams[paramKey]);
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
useModulesParams(instanceParams) {
|
|
726
|
+
const instance = this;
|
|
727
|
+
if (!instance.modules) return;
|
|
728
|
+
Object.keys(instance.modules).forEach(moduleName => {
|
|
729
|
+
const module = instance.modules[moduleName];
|
|
730
|
+
// Extend params
|
|
731
|
+
if (module.params) {
|
|
732
|
+
$.extend(instanceParams, module.params);
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* 将扩展模块的相关方法、事件加载到类实例
|
|
739
|
+
* @param {*} moduleName 扩展模块名称
|
|
740
|
+
* @param {*} moduleParams
|
|
741
|
+
*/
|
|
742
|
+
useModule(moduleName = '', moduleParams = {}) {
|
|
743
|
+
const instance = this;
|
|
744
|
+
if (!instance.modules) return;
|
|
745
|
+
|
|
746
|
+
// 从原型中获得的模块类引用
|
|
747
|
+
const module =
|
|
748
|
+
typeof moduleName === 'string'
|
|
749
|
+
? instance.modules[moduleName]
|
|
750
|
+
: moduleName;
|
|
751
|
+
if (!module) return;
|
|
752
|
+
|
|
753
|
+
// 扩展实例的方法和属性,Extend instance methods and props
|
|
754
|
+
if (module.instance) {
|
|
755
|
+
Object.keys(module.instance).forEach(modulePropName => {
|
|
756
|
+
const moduleProp = module.instance[modulePropName];
|
|
757
|
+
if (typeof moduleProp === 'function') {
|
|
758
|
+
instance[modulePropName] = moduleProp.bind(instance);
|
|
759
|
+
} else {
|
|
760
|
+
instance[modulePropName] = moduleProp;
|
|
761
|
+
}
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// 将扩展模块中的on加载到实例的事件侦听中,比如 init 在实例初始化时被调用
|
|
766
|
+
if (module.on && instance.on) {
|
|
767
|
+
Object.keys(module.on).forEach(moduleEventName => {
|
|
768
|
+
instance.on(moduleEventName, module.on[moduleEventName]);
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// 加载扩展模块的vnodeHooks,Add vnode hooks
|
|
773
|
+
if (module.vnode) {
|
|
774
|
+
if (!instance.vnodeHooks) instance.vnodeHooks = {};
|
|
775
|
+
Object.keys(module.vnode).forEach(vnodeId => {
|
|
776
|
+
Object.keys(module.vnode[vnodeId]).forEach(hookName => {
|
|
777
|
+
const handler = module.vnode[vnodeId][hookName];
|
|
778
|
+
if (!instance.vnodeHooks[hookName])
|
|
779
|
+
instance.vnodeHooks[hookName] = {};
|
|
780
|
+
if (!instance.vnodeHooks[hookName][vnodeId])
|
|
781
|
+
instance.vnodeHooks[hookName][vnodeId] = [];
|
|
782
|
+
instance.vnodeHooks[hookName][vnodeId].push(handler.bind(instance));
|
|
783
|
+
});
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// 执行模块的create方法,模块实例化回调,Module create callback
|
|
788
|
+
if (module.create) {
|
|
789
|
+
module.create.bind(instance)(moduleParams);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* 实例创建初始化时,执行扩展模块中定义的相关回调
|
|
795
|
+
* @param {*} modulesParams
|
|
796
|
+
*/
|
|
797
|
+
useModules(modulesParams = {}) {
|
|
798
|
+
const instance = this;
|
|
799
|
+
if (!instance.modules) return;
|
|
800
|
+
Object.keys(instance.modules).forEach(moduleName => {
|
|
801
|
+
const moduleParams = modulesParams[moduleName] || {};
|
|
802
|
+
instance.useModule(moduleName, moduleParams);
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
static set components(components) {
|
|
807
|
+
const Class = this;
|
|
808
|
+
if (!Class.use) return;
|
|
809
|
+
Class.use(components);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* 将模块类装配到指定类的modules属性,用于扩展类
|
|
814
|
+
* @param {*} module 模块类
|
|
815
|
+
* @param {...any} params 参数
|
|
816
|
+
*/
|
|
817
|
+
static installModule(module, ...params) {
|
|
818
|
+
const Class = this;
|
|
819
|
+
if (!Class.prototype.modules) Class.prototype.modules = {};
|
|
820
|
+
const name =
|
|
821
|
+
module.name ||
|
|
822
|
+
`${Object.keys(Class.prototype.modules).length}_${$.now()}`;
|
|
823
|
+
// 原型属性中引用该模块类,类实例
|
|
824
|
+
Class.prototype.modules[name] = module;
|
|
825
|
+
// 模块如果定义了原型,则将模块原型加载到类原型
|
|
826
|
+
if (module.proto) {
|
|
827
|
+
Object.keys(module.proto).forEach(key => {
|
|
828
|
+
Class.prototype[key] = module.proto[key];
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
// 加载静态属性
|
|
832
|
+
if (module.static) {
|
|
833
|
+
Object.keys(module.static).forEach(key => {
|
|
834
|
+
Class[key] = module.static[key];
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
// 执行加载回调函数
|
|
838
|
+
if (module.install) {
|
|
839
|
+
module.install.apply(Class, params);
|
|
840
|
+
}
|
|
841
|
+
return Class;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* 加载类扩展模块到类
|
|
846
|
+
* @param {*} module
|
|
847
|
+
* @param {...any} params
|
|
848
|
+
*/
|
|
849
|
+
static use(module, ...params) {
|
|
850
|
+
const Class = this;
|
|
851
|
+
if (Array.isArray(module)) {
|
|
852
|
+
module.forEach(m => Class.installModule(m));
|
|
853
|
+
return Class;
|
|
854
|
+
}
|
|
855
|
+
return Class.installModule(module, ...params);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/**
|
|
860
|
+
* 扩展构造函数
|
|
861
|
+
* @param {*} parameters
|
|
862
|
+
*/
|
|
863
|
+
function Constructors (parameters = {}) {
|
|
864
|
+
const {
|
|
865
|
+
defaultSelector,
|
|
866
|
+
constructor: Constructor,
|
|
867
|
+
domProp,
|
|
868
|
+
app,
|
|
869
|
+
addMethods,
|
|
870
|
+
} = parameters;
|
|
871
|
+
const methods = {
|
|
872
|
+
create(...args) {
|
|
873
|
+
if (app) return new Constructor(app, ...args);
|
|
874
|
+
return new Constructor(...args);
|
|
875
|
+
},
|
|
876
|
+
get(el = defaultSelector) {
|
|
877
|
+
if (el instanceof Constructor) return el;
|
|
878
|
+
const $el = $(el);
|
|
879
|
+
if ($el.length === 0) return undefined;
|
|
880
|
+
return $el[0][domProp];
|
|
881
|
+
},
|
|
882
|
+
destroy(el) {
|
|
883
|
+
const instance = methods.get(el);
|
|
884
|
+
if (instance && instance.destroy) return instance.destroy();
|
|
885
|
+
return undefined;
|
|
886
|
+
},
|
|
887
|
+
};
|
|
888
|
+
if (addMethods && Array.isArray(addMethods)) {
|
|
889
|
+
addMethods.forEach(methodName => {
|
|
890
|
+
methods[methodName] = (el = defaultSelector, ...args) => {
|
|
891
|
+
const instance = methods.get(el);
|
|
892
|
+
if (instance && instance[methodName])
|
|
893
|
+
return instance[methodName](...args);
|
|
894
|
+
return undefined;
|
|
895
|
+
};
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
return methods;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
function Modals (parameters = {}) {
|
|
902
|
+
const { defaultSelector, constructor, app } = parameters;
|
|
903
|
+
const methods = $.extend(
|
|
904
|
+
Constructors({
|
|
905
|
+
defaultSelector,
|
|
906
|
+
constructor,
|
|
907
|
+
app,
|
|
908
|
+
domProp: 'f7Modal',
|
|
909
|
+
}),
|
|
910
|
+
{
|
|
911
|
+
open(el, animate) {
|
|
912
|
+
const $el = $(el);
|
|
913
|
+
let instance = $el[0].f7Modal;
|
|
914
|
+
if (!instance)
|
|
915
|
+
instance = new constructor(app, {el: $el});
|
|
916
|
+
return instance.open(animate);
|
|
917
|
+
},
|
|
918
|
+
close(el = defaultSelector, animate) {
|
|
919
|
+
const $el = $(el);
|
|
920
|
+
if ($el.length === 0)
|
|
921
|
+
return undefined;
|
|
922
|
+
let instance = $el[0].f7Modal;
|
|
923
|
+
if (!instance)
|
|
924
|
+
instance = new constructor(app, {el: $el});
|
|
925
|
+
return instance.close(animate);
|
|
926
|
+
},
|
|
927
|
+
}
|
|
928
|
+
);
|
|
929
|
+
return methods;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* 动态加载扩展模块,被 App调用。
|
|
934
|
+
* 通过写入页面标签实现动态加载js、css
|
|
935
|
+
* wia base中已经实现了动态下载、加载模块功能,该模块应删除
|
|
936
|
+
*/
|
|
937
|
+
|
|
938
|
+
const fetchedModules = [];
|
|
939
|
+
function loadModule(moduleToLoad) {
|
|
940
|
+
const App = this;
|
|
941
|
+
|
|
942
|
+
return new Promise((resolve, reject) => {
|
|
943
|
+
const app = App.instance;
|
|
944
|
+
let modulePath;
|
|
945
|
+
let moduleObj;
|
|
946
|
+
let moduleFunc;
|
|
947
|
+
if (!moduleToLoad) {
|
|
948
|
+
reject(new Error('Wia: Lazy module must be specified'));
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
function install(module) {
|
|
953
|
+
App.use(module);
|
|
954
|
+
|
|
955
|
+
if (app) {
|
|
956
|
+
app.useModuleParams(module, app.params);
|
|
957
|
+
app.useModule(module);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
if (typeof moduleToLoad === 'string') {
|
|
962
|
+
const matchNamePattern = moduleToLoad.match(/([a-z0-9-]*)/i);
|
|
963
|
+
if (
|
|
964
|
+
moduleToLoad.indexOf('.') < 0 &&
|
|
965
|
+
matchNamePattern &&
|
|
966
|
+
matchNamePattern[0].length === moduleToLoad.length
|
|
967
|
+
) {
|
|
968
|
+
if (!app || (app && !app.params.lazyModulesPath)) {
|
|
969
|
+
reject(
|
|
970
|
+
new Error(
|
|
971
|
+
'Wia: "lazyModulesPath" app parameter must be specified to fetch module by name'
|
|
972
|
+
)
|
|
973
|
+
);
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
modulePath = `${app.params.lazyModulesPath}/${moduleToLoad}.js`;
|
|
977
|
+
} else {
|
|
978
|
+
modulePath = moduleToLoad;
|
|
979
|
+
}
|
|
980
|
+
} else if (typeof moduleToLoad === 'function') {
|
|
981
|
+
moduleFunc = moduleToLoad;
|
|
982
|
+
} else {
|
|
983
|
+
// considering F7-Plugin object
|
|
984
|
+
moduleObj = moduleToLoad;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
if (moduleFunc) {
|
|
988
|
+
const module = moduleFunc(App, false);
|
|
989
|
+
if (!module) {
|
|
990
|
+
reject(new Error("Wia: Can't find Wia component in specified component function"));
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
// Check if it was added
|
|
994
|
+
if (App.prototype.modules && App.prototype.modules[module.name]) {
|
|
995
|
+
resolve();
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
// Install It
|
|
999
|
+
install(module);
|
|
1000
|
+
|
|
1001
|
+
resolve();
|
|
1002
|
+
}
|
|
1003
|
+
if (moduleObj) {
|
|
1004
|
+
const module = moduleObj;
|
|
1005
|
+
if (!module) {
|
|
1006
|
+
reject(new Error("Wia: Can't find Wia component in specified component"));
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
// Check if it was added
|
|
1010
|
+
if (App.prototype.modules && App.prototype.modules[module.name]) {
|
|
1011
|
+
resolve();
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
// Install It
|
|
1015
|
+
install(module);
|
|
1016
|
+
|
|
1017
|
+
resolve();
|
|
1018
|
+
}
|
|
1019
|
+
if (modulePath) {
|
|
1020
|
+
if (fetchedModules.indexOf(modulePath) >= 0) {
|
|
1021
|
+
resolve();
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
fetchedModules.push(modulePath);
|
|
1025
|
+
// 动态加载 js 脚本
|
|
1026
|
+
const scriptLoad = new Promise((resolveScript, rejectScript) => {
|
|
1027
|
+
App.request.get(
|
|
1028
|
+
modulePath,
|
|
1029
|
+
scriptContent => {
|
|
1030
|
+
const id = $.id();
|
|
1031
|
+
const callbackLoadName = `wia_component_loader_callback_${id}`;
|
|
1032
|
+
|
|
1033
|
+
const scriptEl = document.createElement('script');
|
|
1034
|
+
scriptEl.innerHTML = `window.${callbackLoadName} = function (Wia, WiaAutoInstallComponent) {return ${scriptContent.trim()}}`;
|
|
1035
|
+
// 动态加载 js
|
|
1036
|
+
$('head').append(scriptEl);
|
|
1037
|
+
|
|
1038
|
+
const componentLoader = window[callbackLoadName];
|
|
1039
|
+
delete window[callbackLoadName];
|
|
1040
|
+
$(scriptEl).remove();
|
|
1041
|
+
|
|
1042
|
+
const module = componentLoader(App, false);
|
|
1043
|
+
|
|
1044
|
+
if (!module) {
|
|
1045
|
+
rejectScript(new Error(`Wia: Can't find Wia component in ${modulePath} file`));
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// Check if it was added
|
|
1050
|
+
if (App.prototype.modules && App.prototype.modules[module.name]) {
|
|
1051
|
+
resolveScript();
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
// Install It
|
|
1056
|
+
install(module);
|
|
1057
|
+
|
|
1058
|
+
resolveScript();
|
|
1059
|
+
},
|
|
1060
|
+
(xhr, status) => {
|
|
1061
|
+
rejectScript(xhr, status);
|
|
1062
|
+
}
|
|
1063
|
+
);
|
|
1064
|
+
});
|
|
1065
|
+
|
|
1066
|
+
// 动态加载css样式
|
|
1067
|
+
const styleLoad = new Promise(resolveStyle => {
|
|
1068
|
+
App.request.get(
|
|
1069
|
+
modulePath.replace('.js', app.rtl ? '.rtl.css' : '.css'),
|
|
1070
|
+
styleContent => {
|
|
1071
|
+
const styleEl = document.createElement('style');
|
|
1072
|
+
styleEl.innerHTML = styleContent;
|
|
1073
|
+
$('head').append(styleEl);
|
|
1074
|
+
|
|
1075
|
+
resolveStyle();
|
|
1076
|
+
},
|
|
1077
|
+
() => {
|
|
1078
|
+
resolveStyle();
|
|
1079
|
+
}
|
|
1080
|
+
);
|
|
1081
|
+
});
|
|
1082
|
+
|
|
1083
|
+
Promise.all([scriptLoad, styleLoad])
|
|
1084
|
+
.then(() => {
|
|
1085
|
+
resolve();
|
|
1086
|
+
})
|
|
1087
|
+
.catch(err => {
|
|
1088
|
+
reject(err);
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
const Resize = {
|
|
1095
|
+
name: 'resize',
|
|
1096
|
+
instance: {
|
|
1097
|
+
getSize() {
|
|
1098
|
+
const app = this;
|
|
1099
|
+
if (!app.root[0])
|
|
1100
|
+
return {width: 0, height: 0, left: 0, top: 0};
|
|
1101
|
+
const offset = app.root.offset();
|
|
1102
|
+
const [width, height, left, top] = [app.root[0].offsetWidth, app.root[0].offsetHeight, offset.left, offset.top];
|
|
1103
|
+
app.width = width;
|
|
1104
|
+
app.height = height;
|
|
1105
|
+
app.left = left;
|
|
1106
|
+
app.top = top;
|
|
1107
|
+
return { width, height, left, top };
|
|
1108
|
+
},
|
|
1109
|
+
},
|
|
1110
|
+
on: {
|
|
1111
|
+
init() {
|
|
1112
|
+
const app = this;
|
|
1113
|
+
|
|
1114
|
+
// Get Size
|
|
1115
|
+
app.getSize();
|
|
1116
|
+
|
|
1117
|
+
// Emit resize
|
|
1118
|
+
window.addEventListener('resize', () => {
|
|
1119
|
+
app.emit('resize');
|
|
1120
|
+
}, false);
|
|
1121
|
+
|
|
1122
|
+
// Emit orientationchange
|
|
1123
|
+
window.addEventListener('orientationchange', () => {
|
|
1124
|
+
app.emit('orientationchange');
|
|
1125
|
+
});
|
|
1126
|
+
},
|
|
1127
|
+
orientationchange() {
|
|
1128
|
+
const app = this;
|
|
1129
|
+
// Fix iPad weird body scroll
|
|
1130
|
+
if (app.device.ipad) {
|
|
1131
|
+
document.body.scrollLeft = 0;
|
|
1132
|
+
setTimeout(() => {
|
|
1133
|
+
document.body.scrollLeft = 0;
|
|
1134
|
+
}, 0);
|
|
1135
|
+
}
|
|
1136
|
+
},
|
|
1137
|
+
resize() {
|
|
1138
|
+
const app = this;
|
|
1139
|
+
app.getSize();
|
|
1140
|
+
},
|
|
1141
|
+
},
|
|
1142
|
+
};
|
|
1143
|
+
|
|
1144
|
+
/**
|
|
1145
|
+
* document 绑定click事件
|
|
1146
|
+
* 支持touch则绑定touch,否则绑定click
|
|
1147
|
+
* 无论touch 还是 click事件,都会触发事件响应函数
|
|
1148
|
+
* @param {*} cb
|
|
1149
|
+
*/
|
|
1150
|
+
function bindClick(cb) {
|
|
1151
|
+
let touchStartX;
|
|
1152
|
+
let touchStartY;
|
|
1153
|
+
function touchStart(ev) {
|
|
1154
|
+
// ev.preventDefault();
|
|
1155
|
+
touchStartX = ev.changedTouches[0].clientX;
|
|
1156
|
+
touchStartY = ev.changedTouches[0].clientY;
|
|
1157
|
+
}
|
|
1158
|
+
function touchEnd(ev) {
|
|
1159
|
+
// ev.preventDefault();
|
|
1160
|
+
const x = Math.abs(ev.changedTouches[0].clientX - touchStartX);
|
|
1161
|
+
const y = Math.abs(ev.changedTouches[0].clientY - touchStartY);
|
|
1162
|
+
// console.log('touchEnd', {x, y});
|
|
1163
|
+
|
|
1164
|
+
if (x <= 5 && y <= 5) {
|
|
1165
|
+
cb.call(this, ev);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
// 在捕捉时触发,不影响后续冒泡阶段再次触发
|
|
1170
|
+
if ($.support.touch) {
|
|
1171
|
+
// console.log('bind touch');
|
|
1172
|
+
document.addEventListener('touchstart', touchStart, true);
|
|
1173
|
+
document.addEventListener('touchend', touchEnd, true);
|
|
1174
|
+
} else {
|
|
1175
|
+
// console.log('bind click');
|
|
1176
|
+
document.addEventListener('click', cb, true);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
function initClicks(app) {
|
|
1181
|
+
function appClick(ev) {
|
|
1182
|
+
app.emit({
|
|
1183
|
+
events: 'click',
|
|
1184
|
+
data: [ev],
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
function handleClicks(e) {
|
|
1189
|
+
const $clickedEl = $(e.target);
|
|
1190
|
+
const $clickedLinkEl = $clickedEl.closest('a');
|
|
1191
|
+
const isLink = $clickedLinkEl.length > 0;
|
|
1192
|
+
isLink && $clickedLinkEl.attr('href');
|
|
1193
|
+
|
|
1194
|
+
// call Modules Clicks
|
|
1195
|
+
Object.keys(app.modules).forEach(moduleName => {
|
|
1196
|
+
const moduleClicks = app.modules[moduleName].clicks;
|
|
1197
|
+
if (!moduleClicks) return;
|
|
1198
|
+
if (e.preventF7Router) return;
|
|
1199
|
+
Object.keys(moduleClicks).forEach(clickSelector => {
|
|
1200
|
+
const matchingClickedElement = $clickedEl.closest(clickSelector).eq(0);
|
|
1201
|
+
if (matchingClickedElement.length > 0) {
|
|
1202
|
+
moduleClicks[clickSelector].call(
|
|
1203
|
+
app,
|
|
1204
|
+
matchingClickedElement,
|
|
1205
|
+
matchingClickedElement.dataset(),
|
|
1206
|
+
e
|
|
1207
|
+
);
|
|
1208
|
+
}
|
|
1209
|
+
});
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
// 绑定click 或 touch 事件,触发时,发射click事件
|
|
1214
|
+
bindClick(appClick);
|
|
1215
|
+
// click event 响应
|
|
1216
|
+
app.on('click', handleClicks);
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
const Click = {
|
|
1220
|
+
name: 'clicks',
|
|
1221
|
+
params: {
|
|
1222
|
+
clicks: {
|
|
1223
|
+
// External Links
|
|
1224
|
+
externalLinks: '.ext',
|
|
1225
|
+
},
|
|
1226
|
+
},
|
|
1227
|
+
on: {
|
|
1228
|
+
// app 创建时被调用
|
|
1229
|
+
init() {
|
|
1230
|
+
const app = this;
|
|
1231
|
+
initClicks(app);
|
|
1232
|
+
},
|
|
1233
|
+
},
|
|
1234
|
+
};
|
|
1235
|
+
|
|
1236
|
+
const SW = {
|
|
1237
|
+
registrations: [],
|
|
1238
|
+
register(path, scope) {
|
|
1239
|
+
const app = this;
|
|
1240
|
+
if (!('serviceWorker' in window.navigator) || !app.serviceWorker.container) {
|
|
1241
|
+
return new Promise((resolve, reject) => {
|
|
1242
|
+
reject(new Error('Service worker is not supported'));
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
return new Promise((resolve, reject) => {
|
|
1246
|
+
app.serviceWorker.container.register(path, (scope ? { scope } : {}))
|
|
1247
|
+
.then((reg) => {
|
|
1248
|
+
SW.registrations.push(reg);
|
|
1249
|
+
app.emit('serviceWorkerRegisterSuccess', reg);
|
|
1250
|
+
resolve(reg);
|
|
1251
|
+
}).catch((error) => {
|
|
1252
|
+
app.emit('serviceWorkerRegisterError', error);
|
|
1253
|
+
reject(error);
|
|
1254
|
+
});
|
|
1255
|
+
});
|
|
1256
|
+
},
|
|
1257
|
+
unregister(registration) {
|
|
1258
|
+
const app = this;
|
|
1259
|
+
if (!('serviceWorker' in window.navigator) || !app.serviceWorker.container) {
|
|
1260
|
+
return new Promise((resolve, reject) => {
|
|
1261
|
+
reject(new Error('Service worker is not supported'));
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
let registrations;
|
|
1265
|
+
if (!registration)
|
|
1266
|
+
registrations = SW.registrations;
|
|
1267
|
+
else if (Array.isArray(registration))
|
|
1268
|
+
registrations = registration;
|
|
1269
|
+
else
|
|
1270
|
+
registrations = [registration];
|
|
1271
|
+
return Promise.all(registrations.map(reg => new Promise((resolve, reject) => {
|
|
1272
|
+
reg.unregister()
|
|
1273
|
+
.then(() => {
|
|
1274
|
+
if (SW.registrations.indexOf(reg) >= 0) {
|
|
1275
|
+
SW.registrations.splice(SW.registrations.indexOf(reg), 1);
|
|
1276
|
+
}
|
|
1277
|
+
app.emit('serviceWorkerUnregisterSuccess', reg);
|
|
1278
|
+
resolve();
|
|
1279
|
+
})
|
|
1280
|
+
.catch((error) => {
|
|
1281
|
+
app.emit('serviceWorkerUnregisterError', reg, error);
|
|
1282
|
+
reject(error);
|
|
1283
|
+
});
|
|
1284
|
+
})));
|
|
1285
|
+
},
|
|
1286
|
+
};
|
|
1287
|
+
|
|
1288
|
+
const SW$1 = {
|
|
1289
|
+
name: 'sw',
|
|
1290
|
+
params: {
|
|
1291
|
+
serviceWorker: {
|
|
1292
|
+
path: undefined,
|
|
1293
|
+
scope: undefined,
|
|
1294
|
+
},
|
|
1295
|
+
},
|
|
1296
|
+
create() {
|
|
1297
|
+
const app = this;
|
|
1298
|
+
$.extend(app, {
|
|
1299
|
+
serviceWorker: {
|
|
1300
|
+
container: ('serviceWorker' in window.navigator) ? window.navigator.serviceWorker : undefined,
|
|
1301
|
+
registrations: SW.registrations,
|
|
1302
|
+
register: SW.register.bind(app),
|
|
1303
|
+
unregister: SW.unregister.bind(app),
|
|
1304
|
+
},
|
|
1305
|
+
});
|
|
1306
|
+
},
|
|
1307
|
+
on: {
|
|
1308
|
+
init() {
|
|
1309
|
+
if (!('serviceWorker' in window.navigator))
|
|
1310
|
+
return;
|
|
1311
|
+
const app = this;
|
|
1312
|
+
if (!app.serviceWorker.container)
|
|
1313
|
+
return;
|
|
1314
|
+
const paths = app.params.serviceWorker.path;
|
|
1315
|
+
const scope = app.params.serviceWorker.scope;
|
|
1316
|
+
if (!paths || (Array.isArray(paths) && !paths.length))
|
|
1317
|
+
return;
|
|
1318
|
+
const toRegister = Array.isArray(paths) ? paths : [paths];
|
|
1319
|
+
toRegister.forEach((path) => {
|
|
1320
|
+
app.serviceWorker.register(path, scope);
|
|
1321
|
+
});
|
|
1322
|
+
},
|
|
1323
|
+
},
|
|
1324
|
+
};
|
|
1325
|
+
|
|
1326
|
+
/**
|
|
1327
|
+
* Wia App 基类,从 Module 和 Event 继承。
|
|
1328
|
+
*/
|
|
1329
|
+
// 使用 rollup打包注意
|
|
1330
|
+
// import $ from '@wiajs/dom'; // dom操作库,这种引用,导致 dom的压缩、非压缩 common包都会打入 core
|
|
1331
|
+
// const $ = require('@wiajs/dom'); // dom操作库,这种引用,导致 dom的压缩、非压缩 common包都不会打入 core,保留了 require
|
|
1332
|
+
|
|
1333
|
+
|
|
1334
|
+
const Support$1 = $.support;
|
|
1335
|
+
const Device$1 = $.device;
|
|
1336
|
+
|
|
1337
|
+
/**
|
|
1338
|
+
* 应用类,每个wia应用从该类继承,由 首页加载创建或者路由创建
|
|
1339
|
+
*/
|
|
1340
|
+
class App extends Module {
|
|
1341
|
+
static apps = {};
|
|
1342
|
+
constructor(opt) {
|
|
1343
|
+
super(opt);
|
|
1344
|
+
|
|
1345
|
+
const passedParams = $.extend({}, opt);
|
|
1346
|
+
|
|
1347
|
+
const app = this;
|
|
1348
|
+
app.device = Device$1;
|
|
1349
|
+
app.support = Support$1;
|
|
1350
|
+
console.log('App constructor', {Device: Device$1, Support: Support$1});
|
|
1351
|
+
|
|
1352
|
+
// Default
|
|
1353
|
+
const def = {
|
|
1354
|
+
version: '0.0.1',
|
|
1355
|
+
root: 'body',
|
|
1356
|
+
theme: 'auto',
|
|
1357
|
+
language: window.navigator.language,
|
|
1358
|
+
routes: [],
|
|
1359
|
+
lazyModulesPath: null,
|
|
1360
|
+
initOnDeviceReady: true,
|
|
1361
|
+
// init: true, // 路由加载时,为 false,,为true
|
|
1362
|
+
autoDarkTheme: false,
|
|
1363
|
+
iosTranslucentBars: true,
|
|
1364
|
+
iosTranslucentModals: true,
|
|
1365
|
+
component: undefined,
|
|
1366
|
+
componentUrl: undefined,
|
|
1367
|
+
};
|
|
1368
|
+
|
|
1369
|
+
// Extend defaults with modules params
|
|
1370
|
+
app.useModulesParams(def);
|
|
1371
|
+
|
|
1372
|
+
// Extend defaults with passed params
|
|
1373
|
+
app.params = $.extend(def, opt);
|
|
1374
|
+
|
|
1375
|
+
const $rootEl = $(app.params.root);
|
|
1376
|
+
|
|
1377
|
+
// 判断Page、App实例
|
|
1378
|
+
$.isPage = p => p instanceof Page;
|
|
1379
|
+
$.isApp = p => p instanceof App;
|
|
1380
|
+
|
|
1381
|
+
$.extend(app, {
|
|
1382
|
+
owner: app.params.owner,
|
|
1383
|
+
name: app.params.name,
|
|
1384
|
+
id: `${app.params.owner}.${app.params.name}`,
|
|
1385
|
+
// App version
|
|
1386
|
+
version: app.params.version,
|
|
1387
|
+
// Routes
|
|
1388
|
+
routes: app.params.routes,
|
|
1389
|
+
// Lang
|
|
1390
|
+
language: app.params.language,
|
|
1391
|
+
// Root
|
|
1392
|
+
root: $rootEl,
|
|
1393
|
+
$el: $rootEl,
|
|
1394
|
+
cfg: app.params.cfg, // app config
|
|
1395
|
+
api: app.params.api, // api config
|
|
1396
|
+
|
|
1397
|
+
// RTL
|
|
1398
|
+
rtl: $rootEl.css('direction') === 'rtl',
|
|
1399
|
+
// Theme
|
|
1400
|
+
theme: (function getTheme() {
|
|
1401
|
+
if (app.params.theme === 'auto') {
|
|
1402
|
+
if (Device$1.ios) return 'ios';
|
|
1403
|
+
if (Device$1.desktop) return 'aurora';
|
|
1404
|
+
return 'md';
|
|
1405
|
+
}
|
|
1406
|
+
return app.params.theme;
|
|
1407
|
+
})(),
|
|
1408
|
+
// Initially passed parameters
|
|
1409
|
+
passedParams,
|
|
1410
|
+
online: window.navigator.onLine,
|
|
1411
|
+
});
|
|
1412
|
+
|
|
1413
|
+
// Save Root
|
|
1414
|
+
if (app.root && app.root[0]) {
|
|
1415
|
+
app.root[0].wia = app;
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
app.touchEvents = {
|
|
1419
|
+
start: Support$1.touch
|
|
1420
|
+
? 'touchstart'
|
|
1421
|
+
: Support$1.pointerEvents
|
|
1422
|
+
? 'pointerdown'
|
|
1423
|
+
: 'mousedown',
|
|
1424
|
+
move: Support$1.touch
|
|
1425
|
+
? 'touchmove'
|
|
1426
|
+
: Support$1.pointerEvents
|
|
1427
|
+
? 'pointermove'
|
|
1428
|
+
: 'mousemove',
|
|
1429
|
+
end: Support$1.touch
|
|
1430
|
+
? 'touchend'
|
|
1431
|
+
: Support$1.pointerEvents
|
|
1432
|
+
? 'pointerup'
|
|
1433
|
+
: 'mouseup',
|
|
1434
|
+
};
|
|
1435
|
+
|
|
1436
|
+
// 加载use插入的模块类,每个模块作为app的一个属性,合并到实例。
|
|
1437
|
+
// 模块包括相关属性及方法(如:create、get、destroy)
|
|
1438
|
+
// 调用每个模块的 create 方法
|
|
1439
|
+
app.useModules();
|
|
1440
|
+
|
|
1441
|
+
// 初始化数据,Init Data & Methods
|
|
1442
|
+
app.initData();
|
|
1443
|
+
|
|
1444
|
+
// 自动暗黑主题,Auto Dark Theme
|
|
1445
|
+
const DARK = '(prefers-color-scheme: dark)';
|
|
1446
|
+
const LIGHT = '(prefers-color-scheme: light)';
|
|
1447
|
+
app.mq = {};
|
|
1448
|
+
if (window.matchMedia) {
|
|
1449
|
+
app.mq.dark = window.matchMedia(DARK);
|
|
1450
|
+
app.mq.light = window.matchMedia(LIGHT);
|
|
1451
|
+
}
|
|
1452
|
+
app.colorSchemeListener = function ({matches, media}) {
|
|
1453
|
+
if (!matches) {
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
const html = document.querySelector('html');
|
|
1457
|
+
if (media === DARK) {
|
|
1458
|
+
html.classList.add('theme-dark');
|
|
1459
|
+
} else if (media === LIGHT) {
|
|
1460
|
+
html.classList.remove('theme-dark');
|
|
1461
|
+
}
|
|
1462
|
+
};
|
|
1463
|
+
|
|
1464
|
+
// app 初始化,Init
|
|
1465
|
+
function init() {
|
|
1466
|
+
if (Device$1.cordova && app.params.initOnDeviceReady) {
|
|
1467
|
+
$(document).on('deviceready', () => {
|
|
1468
|
+
app.init();
|
|
1469
|
+
});
|
|
1470
|
+
} else {
|
|
1471
|
+
app.init();
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// 应用初始化,路由跳转时不执行初始化
|
|
1476
|
+
if (app.params.init) init();
|
|
1477
|
+
|
|
1478
|
+
// Return app instance
|
|
1479
|
+
return app;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
// 首次加载事件,全局只触发一次
|
|
1483
|
+
load(param) {
|
|
1484
|
+
this.emit('local::load appLoad', param);
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
// 从后台切换到前台显示事件
|
|
1488
|
+
show(url, data) {
|
|
1489
|
+
this.emit('local::show appShow', url, data);
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
// 从前台显示切换到后台事件
|
|
1493
|
+
hide() {
|
|
1494
|
+
this.emit('local::hide appHide');
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
// 卸载应用事件
|
|
1498
|
+
unload() {
|
|
1499
|
+
this.emit('local::unload appUnload');
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
/**
|
|
1503
|
+
* 初始化数据
|
|
1504
|
+
*/
|
|
1505
|
+
initData() {
|
|
1506
|
+
const app = this;
|
|
1507
|
+
|
|
1508
|
+
// Data
|
|
1509
|
+
app.data = {};
|
|
1510
|
+
if (app.params.data && typeof app.params.data === 'function') {
|
|
1511
|
+
$.extend(app.data, app.params.data.bind(app)());
|
|
1512
|
+
} else if (app.params.data) {
|
|
1513
|
+
$.extend(app.data, app.params.data);
|
|
1514
|
+
}
|
|
1515
|
+
// Methods
|
|
1516
|
+
app.methods = {};
|
|
1517
|
+
if (app.params.methods) {
|
|
1518
|
+
Object.keys(app.params.methods).forEach(methodName => {
|
|
1519
|
+
if (typeof app.params.methods[methodName] === 'function') {
|
|
1520
|
+
app.methods[methodName] = app.params.methods[methodName].bind(app);
|
|
1521
|
+
} else {
|
|
1522
|
+
app.methods[methodName] = app.params.methods[methodName];
|
|
1523
|
+
}
|
|
1524
|
+
});
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
enableAutoDarkTheme() {
|
|
1529
|
+
if (!window.matchMedia) return;
|
|
1530
|
+
|
|
1531
|
+
const app = this;
|
|
1532
|
+
const html = document.querySelector('html');
|
|
1533
|
+
if (app.mq.dark && app.mq.light) {
|
|
1534
|
+
app.mq.dark.addListener(app.colorSchemeListener);
|
|
1535
|
+
app.mq.light.addListener(app.colorSchemeListener);
|
|
1536
|
+
}
|
|
1537
|
+
if (app.mq.dark && app.mq.dark.matches) {
|
|
1538
|
+
html.classList.add('theme-dark');
|
|
1539
|
+
} else if (app.mq.light && app.mq.light.matches) {
|
|
1540
|
+
html.classList.remove('theme-dark');
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
disableAutoDarkTheme() {
|
|
1545
|
+
if (!window.matchMedia) return;
|
|
1546
|
+
|
|
1547
|
+
const app = this;
|
|
1548
|
+
if (app.mq.dark) app.mq.dark.removeListener(app.colorSchemeListener);
|
|
1549
|
+
if (app.mq.light) app.mq.light.removeListener(app.colorSchemeListener);
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
// 初始化,包括控制 html 样式,wia app 启动时需要执行,切换app时,不需要
|
|
1553
|
+
init() {
|
|
1554
|
+
const app = this;
|
|
1555
|
+
if (app.initialized) return app;
|
|
1556
|
+
|
|
1557
|
+
$.App = App;
|
|
1558
|
+
|
|
1559
|
+
if (Device$1.ios && Device$1.webView) {
|
|
1560
|
+
// Strange hack required for iOS 8 webview to work on inputs
|
|
1561
|
+
window.addEventListener('touchstart', () => {});
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
app.root.addClass('framework7-initializing');
|
|
1565
|
+
|
|
1566
|
+
// RTL attr
|
|
1567
|
+
if (app.rtl) {
|
|
1568
|
+
$('html').attr('dir', 'rtl');
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
// Auto Dark Theme
|
|
1572
|
+
if (app.params.autoDarkTheme) {
|
|
1573
|
+
app.enableAutoDarkTheme();
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
// Watch for online/offline state
|
|
1577
|
+
window.addEventListener('offline', () => {
|
|
1578
|
+
app.online = false;
|
|
1579
|
+
app.emit('offline');
|
|
1580
|
+
app.emit('connection', false);
|
|
1581
|
+
});
|
|
1582
|
+
|
|
1583
|
+
window.addEventListener('online', () => {
|
|
1584
|
+
app.online = true;
|
|
1585
|
+
app.emit('online');
|
|
1586
|
+
app.emit('connection', true);
|
|
1587
|
+
});
|
|
1588
|
+
|
|
1589
|
+
// Root class
|
|
1590
|
+
app.root.addClass('framework7-root');
|
|
1591
|
+
|
|
1592
|
+
// Theme class
|
|
1593
|
+
$('html').removeClass('ios md aurora').addClass(app.theme);
|
|
1594
|
+
|
|
1595
|
+
// iOS Translucent
|
|
1596
|
+
if (app.params.iosTranslucentBars && app.theme === 'ios' && Device$1.ios) {
|
|
1597
|
+
$('html').addClass('ios-translucent-bars');
|
|
1598
|
+
}
|
|
1599
|
+
if (app.params.iosTranslucentModals && app.theme === 'ios' && Device$1.ios) {
|
|
1600
|
+
$('html').addClass('ios-translucent-modals');
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
// Init class
|
|
1604
|
+
$.nextFrame(() => {
|
|
1605
|
+
app.root.removeClass('framework7-initializing');
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1608
|
+
initStyle();
|
|
1609
|
+
|
|
1610
|
+
// Emit, init other modules
|
|
1611
|
+
app.initialized = true;
|
|
1612
|
+
|
|
1613
|
+
// 发起init 事件,模块 on 里面有 init方法的会被触发
|
|
1614
|
+
app.emit('init');
|
|
1615
|
+
|
|
1616
|
+
return app;
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
// eslint-disable-next-line
|
|
1620
|
+
loadModule(m) {
|
|
1621
|
+
App.loadModule(m);
|
|
1622
|
+
// 模块初始化
|
|
1623
|
+
if (this[m.name].init) this[m.name].init();
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
// eslint-disable-next-line
|
|
1627
|
+
loadModules(...args) {
|
|
1628
|
+
return App.loadModules(...args);
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
getVnodeHooks(hook, id) {
|
|
1632
|
+
const app = this;
|
|
1633
|
+
if (!app.vnodeHooks || !app.vnodeHooks[hook]) return [];
|
|
1634
|
+
return app.vnodeHooks[hook][id] || [];
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
// eslint-disable-next-line
|
|
1638
|
+
get $() {
|
|
1639
|
+
return $;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
static get Dom() {
|
|
1643
|
+
return $;
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
static get $() {
|
|
1647
|
+
return $;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
static get Module() {
|
|
1651
|
+
return Module;
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
static get Event() {
|
|
1655
|
+
return Event;
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
/**
|
|
1660
|
+
* 初始化html样式
|
|
1661
|
+
* from device module
|
|
1662
|
+
*/
|
|
1663
|
+
function initStyle() {
|
|
1664
|
+
const classNames = [];
|
|
1665
|
+
const html = document.querySelector('html');
|
|
1666
|
+
const metaStatusbar = document.querySelector(
|
|
1667
|
+
'meta[name="apple-mobile-web-app-status-bar-style"]'
|
|
1668
|
+
);
|
|
1669
|
+
if (!html) return;
|
|
1670
|
+
if (
|
|
1671
|
+
Device$1.standalone &&
|
|
1672
|
+
Device$1.ios &&
|
|
1673
|
+
metaStatusbar &&
|
|
1674
|
+
metaStatusbar.content === 'black-translucent'
|
|
1675
|
+
) {
|
|
1676
|
+
classNames.push('device-full-viewport');
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
// Pixel Ratio
|
|
1680
|
+
classNames.push(`device-pixel-ratio-${Math.floor(Device$1.pixelRatio)}`);
|
|
1681
|
+
// OS classes
|
|
1682
|
+
if (Device$1.os && !Device$1.desktop) {
|
|
1683
|
+
classNames.push(`device-${Device$1.os}`);
|
|
1684
|
+
} else if (Device$1.desktop) {
|
|
1685
|
+
classNames.push('device-desktop');
|
|
1686
|
+
if (Device$1.os) {
|
|
1687
|
+
classNames.push(`device-${Device$1.os}`);
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
if (Device$1.cordova || Device$1.phonegap) {
|
|
1691
|
+
classNames.push('device-cordova');
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
// Add html classes
|
|
1695
|
+
classNames.forEach(className => {
|
|
1696
|
+
html.classList.add(className);
|
|
1697
|
+
// console.log({className});
|
|
1698
|
+
});
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
// App 类 静态方法、属性
|
|
1702
|
+
|
|
1703
|
+
App.ModalMethods = Modals;
|
|
1704
|
+
App.ConstructorMethods = Constructors;
|
|
1705
|
+
// 动态加载模块(base里面已经内置动态加载,这个方法应该用不上)
|
|
1706
|
+
App.loadModule = loadModule;
|
|
1707
|
+
App.loadModules = function (modules) {
|
|
1708
|
+
return Promise.all(modules.map(module => App.loadModule(module)));
|
|
1709
|
+
};
|
|
1710
|
+
|
|
1711
|
+
// app 加载到 app实例的一些扩展模块
|
|
1712
|
+
App.support = Support$1;
|
|
1713
|
+
App.device = Device$1;
|
|
1714
|
+
App.utils = Utils;
|
|
1715
|
+
|
|
1716
|
+
// 添加应用缺省模块
|
|
1717
|
+
App.use([
|
|
1718
|
+
Resize, // 控制屏幕大小
|
|
1719
|
+
Click, // 触发UI组件的点击(Click 或 Touch)事件
|
|
1720
|
+
SW$1, // ServiceWorker
|
|
1721
|
+
|
|
1722
|
+
//INSTALL_COMPONENTS
|
|
1723
|
+
]);
|
|
1724
|
+
|
|
1725
|
+
/**
|
|
1726
|
+
* Released on: August 28, 2016
|
|
1727
|
+
* 图片延迟加载
|
|
1728
|
+
* 使用方法:
|
|
1729
|
+
* import {Lazy from '@wiajs/core';
|
|
1730
|
+
* const _lazy = new Lazy();
|
|
1731
|
+
* _lazy.start(dv); // 注意,这个dv是滚动的层,错了无法触发加载,sui 就是内容层!
|
|
1732
|
+
* setTimeout(() => {loadView()}, 1); // krouter 里面已经做了处理,bind 时直接 加载视图即可!
|
|
1733
|
+
* loadView 加载视图中(每次页面更新内容后,需调用)
|
|
1734
|
+
* _lazy.update(); // 没有显示的图片,加入 待加载数组,并检查是否可视,可视则加载!
|
|
1735
|
+
*/
|
|
1736
|
+
|
|
1737
|
+
// options
|
|
1738
|
+
const _opts = {
|
|
1739
|
+
normal: 'nor', // 'data-normal' 普通图片
|
|
1740
|
+
retina: 'ret', // 'data-retina',
|
|
1741
|
+
srcset: 'set', // 'data-srcset', 浏览器根据宽、高和像素密度来加载相应的图片资源
|
|
1742
|
+
threshold: 0,
|
|
1743
|
+
};
|
|
1744
|
+
|
|
1745
|
+
let _opt;
|
|
1746
|
+
let _ticking;
|
|
1747
|
+
let _nodes;
|
|
1748
|
+
let _windowHeight = window.innerHeight;
|
|
1749
|
+
let _root;
|
|
1750
|
+
|
|
1751
|
+
// private
|
|
1752
|
+
let _prevLoc = getLoc();
|
|
1753
|
+
|
|
1754
|
+
// feature detection
|
|
1755
|
+
// https://github.com/Modernizr/Modernizr/blob/master/feature-detects/img/srcset.js
|
|
1756
|
+
const _srcset =
|
|
1757
|
+
document.body.classList.contains('srcset') ||
|
|
1758
|
+
'srcset' in document.createElement('img');
|
|
1759
|
+
|
|
1760
|
+
// 设备分辨率
|
|
1761
|
+
// not supported in IE10 - https://msdn.microsoft.com/en-us/library/dn265030(v=vs.85).aspx
|
|
1762
|
+
const _dpr =
|
|
1763
|
+
window.devicePixelRatio ||
|
|
1764
|
+
window.screen.deviceXDPI / window.screen.logicalXDPI;
|
|
1765
|
+
|
|
1766
|
+
/**
|
|
1767
|
+
* 输外部可调用的类
|
|
1768
|
+
* 类外面的变量、函数作为模块内部私有属性、方法,外部无法调用
|
|
1769
|
+
* 如果全部放入类中,属性、函数相互调用,都需要 this,非常麻烦!
|
|
1770
|
+
* 也可以直接使用 export default (options = {}) => {} 输出一个函数!
|
|
1771
|
+
* 函数内部反而不需要this,比较方便。
|
|
1772
|
+
*/
|
|
1773
|
+
class Lazy {
|
|
1774
|
+
// 实例属性
|
|
1775
|
+
constructor(opt) {
|
|
1776
|
+
_opt = $.assign({}, _opts, opt);
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
// API
|
|
1780
|
+
|
|
1781
|
+
//----------------------------------------
|
|
1782
|
+
// dom 就绪后 start,dom 更新后,需 update
|
|
1783
|
+
/**
|
|
1784
|
+
* 启动延迟加载, 加载事件, dom ready时调用!
|
|
1785
|
+
* @param root 根对象, scroll的目标对象,错了无法触发scroll 事件!
|
|
1786
|
+
* @returns {init}
|
|
1787
|
+
*/
|
|
1788
|
+
start(root) {
|
|
1789
|
+
// sui window scroll event invalid!!!
|
|
1790
|
+
// ['scroll', 'resize'].forEach(event => window[action](event, requestScroll));
|
|
1791
|
+
['scroll', 'resize'].forEach(event =>
|
|
1792
|
+
root['addEventListener'](event, requestScroll)
|
|
1793
|
+
);
|
|
1794
|
+
_root = root;
|
|
1795
|
+
return this;
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
/**
|
|
1799
|
+
* 停止延迟加载,卸载事件!
|
|
1800
|
+
* @param root 根对象, scroll的目标对象
|
|
1801
|
+
* @returns {init}
|
|
1802
|
+
*/
|
|
1803
|
+
stop() {
|
|
1804
|
+
// sui window scroll event invalid!!!
|
|
1805
|
+
// ['scroll', 'resize'].forEach(event => window[action](event, requestScroll));
|
|
1806
|
+
['scroll', 'resize'].forEach(event =>
|
|
1807
|
+
_root['removeEventListener'](event, requestScroll)
|
|
1808
|
+
);
|
|
1809
|
+
return this;
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
update() {
|
|
1813
|
+
setTimeout(() => {
|
|
1814
|
+
update();
|
|
1815
|
+
check();
|
|
1816
|
+
}, 1);
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
/**
|
|
1821
|
+
* Y 坐标,好像一直是 0
|
|
1822
|
+
*/
|
|
1823
|
+
function getLoc() {
|
|
1824
|
+
// console.log(`window.scrollY:${window.scrollY} window.pageYOffset:${window.pageYOffset}`);
|
|
1825
|
+
return window.scrollY || window.pageYOffset;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
// debounce helpers
|
|
1829
|
+
function requestScroll() {
|
|
1830
|
+
_prevLoc = getLoc();
|
|
1831
|
+
requestFrame();
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
function requestFrame() {
|
|
1835
|
+
if (!_ticking) {
|
|
1836
|
+
window.requestAnimationFrame(() => check());
|
|
1837
|
+
_ticking = true;
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
// offset helper
|
|
1842
|
+
/**
|
|
1843
|
+
* 节点相对视口的坐标,对于动态加载的,好像得到都是0,使用定时器延迟加载就能正确获取!
|
|
1844
|
+
*/
|
|
1845
|
+
function getOffset(node) {
|
|
1846
|
+
// 元素四个位置的相对于视口的坐标
|
|
1847
|
+
return node.getBoundingClientRect().top + _prevLoc;
|
|
1848
|
+
// return node.offsetTop + _prevLoc;
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
/**
|
|
1852
|
+
* 节点是否在可视窗口判断
|
|
1853
|
+
* 通过可视窗口顶部、底部坐标来判断
|
|
1854
|
+
* 顶部坐标就是页面的滚动条滚动的距离
|
|
1855
|
+
* 底部坐标就是滚动条的距离加上当前可视窗口的高度
|
|
1856
|
+
* dom元素中心:元素到最顶端的高度加上自身高度的一半
|
|
1857
|
+
* @param {*} node
|
|
1858
|
+
*/
|
|
1859
|
+
function inViewport(node) {
|
|
1860
|
+
const viewTop = _prevLoc; // 视口顶部坐标
|
|
1861
|
+
const viewBot = viewTop + _windowHeight; // 视口底部坐标
|
|
1862
|
+
// console.log(`viewTop:${viewTop} viewBot:${viewBot}`);
|
|
1863
|
+
|
|
1864
|
+
// 节点坐标
|
|
1865
|
+
const nodeTop = getOffset(node);
|
|
1866
|
+
const nodeBot = nodeTop + node.offsetHeight;
|
|
1867
|
+
// console.log(`nodeTop:${nodeTop} nodeBot:${nodeBot}`);
|
|
1868
|
+
|
|
1869
|
+
const offset = (_opt.threshold / 100) * _windowHeight;
|
|
1870
|
+
// 节点在可视范围内
|
|
1871
|
+
const rc = nodeBot >= viewTop - offset && nodeTop <= viewBot + offset;
|
|
1872
|
+
// if (rc)
|
|
1873
|
+
// console.log(`nodeBot:${nodeBot} >= view:${viewTop - offset} nodeTop:${nodeTop} <= view:${viewBot + offset}`);
|
|
1874
|
+
|
|
1875
|
+
return rc;
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
// source helper
|
|
1879
|
+
function setSource(node) {
|
|
1880
|
+
$.emit('lazy:src:before', node);
|
|
1881
|
+
|
|
1882
|
+
// prefer srcset, fallback to pixel density
|
|
1883
|
+
if (_srcset && node.hasAttribute(_opt.srcset)) {
|
|
1884
|
+
node.setAttribute('srcset', node.getAttribute(_opt.srcset));
|
|
1885
|
+
} else {
|
|
1886
|
+
const retina = _dpr > 1 && node.getAttribute(_opt.retina);
|
|
1887
|
+
const src = retina || node.getAttribute(_opt.normal);
|
|
1888
|
+
node.setAttribute('src', src);
|
|
1889
|
+
console.log(`set src:${src}`);
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
$.emit('lazy:src:after', node);
|
|
1893
|
+
// 删除懒加载属性,避免重复加载
|
|
1894
|
+
[_opt.normal, _opt.retina, _opt.srcset].forEach(attr =>
|
|
1895
|
+
node.removeAttribute(attr)
|
|
1896
|
+
);
|
|
1897
|
+
update();
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
/**
|
|
1901
|
+
* 检查是否可视,如果可视则更改图片src,加载图片
|
|
1902
|
+
* @returns {check}
|
|
1903
|
+
*/
|
|
1904
|
+
function check() {
|
|
1905
|
+
if (!_nodes) return;
|
|
1906
|
+
|
|
1907
|
+
_windowHeight = window.innerHeight;
|
|
1908
|
+
_nodes.forEach(node => inViewport(node) && setSource(node));
|
|
1909
|
+
_ticking = false;
|
|
1910
|
+
return this;
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
/**
|
|
1914
|
+
* 新的图片加入dom,需重新获取属性为nor的图片节点,
|
|
1915
|
+
* @returns {update}
|
|
1916
|
+
*/
|
|
1917
|
+
function update(root) {
|
|
1918
|
+
if (root)
|
|
1919
|
+
_nodes = Array.prototype.slice.call(
|
|
1920
|
+
root.querySelectorAll(`[${_opt.normal}]`)
|
|
1921
|
+
);
|
|
1922
|
+
else
|
|
1923
|
+
_nodes = Array.prototype.slice.call(
|
|
1924
|
+
document.querySelectorAll(`[${_opt.normal}]`)
|
|
1925
|
+
);
|
|
1926
|
+
return this;
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
const openedModals = [];
|
|
1930
|
+
const dialogsQueue = [];
|
|
1931
|
+
function clearDialogsQueue() {
|
|
1932
|
+
if (dialogsQueue.length === 0) return;
|
|
1933
|
+
const dialog = dialogsQueue.shift();
|
|
1934
|
+
dialog.open();
|
|
1935
|
+
}
|
|
1936
|
+
class Modal extends Event {
|
|
1937
|
+
constructor(app, params) {
|
|
1938
|
+
super(params, [app]);
|
|
1939
|
+
|
|
1940
|
+
const modal = this;
|
|
1941
|
+
const defaults = {};
|
|
1942
|
+
modal.params = Utils.extend(defaults, params);
|
|
1943
|
+
modal.opened = false;
|
|
1944
|
+
return this;
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
onOpen() {
|
|
1948
|
+
const modal = this;
|
|
1949
|
+
modal.opened = true;
|
|
1950
|
+
openedModals.push(modal);
|
|
1951
|
+
$('html').addClass(`with-modal-${modal.type.toLowerCase()}`);
|
|
1952
|
+
modal.$el.trigger(`modal:open ${modal.type.toLowerCase()}:open`);
|
|
1953
|
+
modal.emit(`local::open modalOpen ${modal.type}Open`, modal);
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
onOpened() {
|
|
1957
|
+
const modal = this;
|
|
1958
|
+
modal.$el.trigger(`modal:opened ${modal.type.toLowerCase()}:opened`);
|
|
1959
|
+
modal.emit(`local::opened modalOpened ${modal.type}Opened`, modal);
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
onClose() {
|
|
1963
|
+
const modal = this;
|
|
1964
|
+
modal.opened = false;
|
|
1965
|
+
if (!modal.type || !modal.$el) return;
|
|
1966
|
+
openedModals.splice(openedModals.indexOf(modal), 1);
|
|
1967
|
+
$('html').removeClass(`with-modal-${modal.type.toLowerCase()}`);
|
|
1968
|
+
modal.$el.trigger(`modal:close ${modal.type.toLowerCase()}:close`);
|
|
1969
|
+
modal.emit(`local::close modalClose ${modal.type}Close`, modal);
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
onClosed() {
|
|
1973
|
+
const modal = this;
|
|
1974
|
+
if (!modal.type || !modal.$el) return;
|
|
1975
|
+
modal.$el.removeClass('modal-out');
|
|
1976
|
+
modal.$el.hide();
|
|
1977
|
+
modal.$el.trigger(`modal:closed ${modal.type.toLowerCase()}:closed`);
|
|
1978
|
+
modal.emit(`local::closed modalClosed ${modal.type}Closed`, modal);
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
open(animateModal) {
|
|
1982
|
+
const modal = this;
|
|
1983
|
+
const {app, $el, type, $backdropEl} = modal;
|
|
1984
|
+
const {moveToRoot} = modal.params;
|
|
1985
|
+
|
|
1986
|
+
let animate = true;
|
|
1987
|
+
if (typeof animateModal !== 'undefined') animate = animateModal;
|
|
1988
|
+
else if (typeof modal.params.animate !== 'undefined') {
|
|
1989
|
+
animate = modal.params.animate;
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
if (!$el || $el.hasClass('modal-in')) {
|
|
1993
|
+
return modal;
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
if (type === 'dialog' && app.params.modal.queueDialogs) {
|
|
1997
|
+
let pushToQueue;
|
|
1998
|
+
if ($('.dialog.modal-in').length > 0) {
|
|
1999
|
+
pushToQueue = true;
|
|
2000
|
+
} else if (openedModals.length > 0) {
|
|
2001
|
+
openedModals.forEach(openedModal => {
|
|
2002
|
+
if (openedModal.type === 'dialog') pushToQueue = true;
|
|
2003
|
+
});
|
|
2004
|
+
}
|
|
2005
|
+
if (pushToQueue) {
|
|
2006
|
+
dialogsQueue.push(modal);
|
|
2007
|
+
return modal;
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
const $modalParentEl = $el.parent();
|
|
2012
|
+
const wasInDom = $el.parents(document).length > 0;
|
|
2013
|
+
if (
|
|
2014
|
+
moveToRoot &&
|
|
2015
|
+
app.params.modal.moveToRoot &&
|
|
2016
|
+
!$modalParentEl.is(app.root)
|
|
2017
|
+
) {
|
|
2018
|
+
app.root.append($el);
|
|
2019
|
+
modal.once(`${type}Closed`, () => {
|
|
2020
|
+
if (wasInDom) {
|
|
2021
|
+
$modalParentEl.append($el);
|
|
2022
|
+
} else {
|
|
2023
|
+
$el.remove();
|
|
2024
|
+
}
|
|
2025
|
+
});
|
|
2026
|
+
}
|
|
2027
|
+
// Show Modal
|
|
2028
|
+
$el.show();
|
|
2029
|
+
|
|
2030
|
+
/* eslint no-underscore-dangle: ["error", { "allow": ["_clientLeft"] }] */
|
|
2031
|
+
modal._clientLeft = $el[0].clientLeft;
|
|
2032
|
+
|
|
2033
|
+
// Modal
|
|
2034
|
+
function transitionEnd() {
|
|
2035
|
+
if ($el.hasClass('modal-out')) {
|
|
2036
|
+
modal.onClosed();
|
|
2037
|
+
} else if ($el.hasClass('modal-in')) {
|
|
2038
|
+
modal.onOpened();
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
if (animate) {
|
|
2042
|
+
if ($backdropEl) {
|
|
2043
|
+
$backdropEl.removeClass('not-animated');
|
|
2044
|
+
$backdropEl.addClass('backdrop-in');
|
|
2045
|
+
}
|
|
2046
|
+
$el.animationEnd(() => {
|
|
2047
|
+
transitionEnd();
|
|
2048
|
+
});
|
|
2049
|
+
$el.transitionEnd(() => {
|
|
2050
|
+
transitionEnd();
|
|
2051
|
+
});
|
|
2052
|
+
$el.removeClass('modal-out not-animated').addClass('modal-in');
|
|
2053
|
+
modal.onOpen();
|
|
2054
|
+
} else {
|
|
2055
|
+
if ($backdropEl) {
|
|
2056
|
+
$backdropEl.addClass('backdrop-in not-animated');
|
|
2057
|
+
}
|
|
2058
|
+
$el.removeClass('modal-out').addClass('modal-in not-animated');
|
|
2059
|
+
modal.onOpen();
|
|
2060
|
+
modal.onOpened();
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
return modal;
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
close(animateModal) {
|
|
2067
|
+
const modal = this;
|
|
2068
|
+
const $el = modal.$el;
|
|
2069
|
+
const $backdropEl = modal.$backdropEl;
|
|
2070
|
+
|
|
2071
|
+
let animate = true;
|
|
2072
|
+
if (typeof animateModal !== 'undefined') animate = animateModal;
|
|
2073
|
+
else if (typeof modal.params.animate !== 'undefined') {
|
|
2074
|
+
animate = modal.params.animate;
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
if (!$el || !$el.hasClass('modal-in')) {
|
|
2078
|
+
if (dialogsQueue.indexOf(modal) >= 0) {
|
|
2079
|
+
dialogsQueue.splice(dialogsQueue.indexOf(modal), 1);
|
|
2080
|
+
}
|
|
2081
|
+
return modal;
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
// backdrop
|
|
2085
|
+
if ($backdropEl) {
|
|
2086
|
+
let needToHideBackdrop = true;
|
|
2087
|
+
if (modal.type === 'popup') {
|
|
2088
|
+
modal.$el.prevAll('.popup.modal-in').each((index, popupEl) => {
|
|
2089
|
+
const popupInstance = popupEl.f7Modal;
|
|
2090
|
+
if (!popupInstance) return;
|
|
2091
|
+
if (
|
|
2092
|
+
popupInstance.params.closeByBackdropClick &&
|
|
2093
|
+
popupInstance.params.backdrop &&
|
|
2094
|
+
popupInstance.backdropEl === modal.backdropEl
|
|
2095
|
+
) {
|
|
2096
|
+
needToHideBackdrop = false;
|
|
2097
|
+
}
|
|
2098
|
+
});
|
|
2099
|
+
}
|
|
2100
|
+
if (needToHideBackdrop) {
|
|
2101
|
+
$backdropEl[animate ? 'removeClass' : 'addClass']('not-animated');
|
|
2102
|
+
$backdropEl.removeClass('backdrop-in');
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
// Modal
|
|
2107
|
+
$el[animate ? 'removeClass' : 'addClass']('not-animated');
|
|
2108
|
+
function transitionEnd() {
|
|
2109
|
+
if ($el.hasClass('modal-out')) {
|
|
2110
|
+
modal.onClosed();
|
|
2111
|
+
} else if ($el.hasClass('modal-in')) {
|
|
2112
|
+
modal.onOpened();
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
if (animate) {
|
|
2116
|
+
$el.animationEnd(() => {
|
|
2117
|
+
transitionEnd();
|
|
2118
|
+
});
|
|
2119
|
+
$el.transitionEnd(() => {
|
|
2120
|
+
transitionEnd();
|
|
2121
|
+
});
|
|
2122
|
+
$el.removeClass('modal-in').addClass('modal-out');
|
|
2123
|
+
// Emit close
|
|
2124
|
+
modal.onClose();
|
|
2125
|
+
} else {
|
|
2126
|
+
$el
|
|
2127
|
+
.addClass('not-animated')
|
|
2128
|
+
.removeClass('modal-in')
|
|
2129
|
+
.addClass('modal-out');
|
|
2130
|
+
// Emit close
|
|
2131
|
+
modal.onClose();
|
|
2132
|
+
modal.onClosed();
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
if (modal.type === 'dialog') {
|
|
2136
|
+
clearDialogsQueue();
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
return modal;
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
destroy() {
|
|
2143
|
+
const modal = this;
|
|
2144
|
+
if (modal.destroyed) return;
|
|
2145
|
+
modal.emit(
|
|
2146
|
+
`local::beforeDestroy modalBeforeDestroy ${modal.type}BeforeDestroy`,
|
|
2147
|
+
modal
|
|
2148
|
+
);
|
|
2149
|
+
if (modal.$el) {
|
|
2150
|
+
modal.$el.trigger(
|
|
2151
|
+
`modal:beforedestroy ${modal.type.toLowerCase()}:beforedestroy`
|
|
2152
|
+
);
|
|
2153
|
+
if (modal.$el.length && modal.$el[0].f7Modal) {
|
|
2154
|
+
delete modal.$el[0].f7Modal;
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
Utils.deleteProps(modal);
|
|
2158
|
+
modal.destroyed = true;
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
// replace react, use by @babel/plugin-transform-react-jsx
|
|
2163
|
+
/* eslint-disable prefer-rest-params */
|
|
2164
|
+
function jsx(tag, props, ...args) {
|
|
2165
|
+
const attrs = props || {};
|
|
2166
|
+
const children = args || [];
|
|
2167
|
+
|
|
2168
|
+
const attrsString = Object.keys(attrs)
|
|
2169
|
+
.map((attr) => {
|
|
2170
|
+
if (attr[0] === '_') {
|
|
2171
|
+
if (attrs[attr]) return attr.replace('_', '');
|
|
2172
|
+
return '';
|
|
2173
|
+
}
|
|
2174
|
+
return `${attr}="${attrs[attr]}"`;
|
|
2175
|
+
})
|
|
2176
|
+
.filter((attr) => !!attr)
|
|
2177
|
+
.join(' ');
|
|
2178
|
+
|
|
2179
|
+
if (['path', 'img', 'circle', 'polygon', 'line', 'input'].indexOf(tag) >= 0) {
|
|
2180
|
+
return `<${tag} ${attrsString} />`.trim();
|
|
2181
|
+
}
|
|
2182
|
+
const childrenContent = children
|
|
2183
|
+
.filter((c) => !!c)
|
|
2184
|
+
.map((c) => (Array.isArray(c) ? c.join('') : c))
|
|
2185
|
+
.join('');
|
|
2186
|
+
return `<${tag} ${attrsString}>${childrenContent}</${tag}>`.trim();
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
// export {default as Support} from './support';
|
|
2190
|
+
// export {default as Device} from './device';
|
|
2191
|
+
const Support = $.support;
|
|
2192
|
+
const Device = $.device;
|
|
2193
|
+
|
|
2194
|
+
exports.Ajax = Ajax;
|
|
2195
|
+
exports.App = App;
|
|
2196
|
+
exports.Constructors = Constructors;
|
|
2197
|
+
exports.Device = Device;
|
|
2198
|
+
exports.Event = Event;
|
|
2199
|
+
exports.Lazy = Lazy;
|
|
2200
|
+
exports.Modal = Modal;
|
|
2201
|
+
exports.Modals = Modals;
|
|
2202
|
+
exports.Module = Module;
|
|
2203
|
+
exports.Page = Page;
|
|
2204
|
+
exports.Resize = Resize;
|
|
2205
|
+
exports.SW = SW$1;
|
|
2206
|
+
exports.Support = Support;
|
|
2207
|
+
exports.Utils = Utils;
|
|
2208
|
+
exports.jsx = jsx;
|
|
2209
|
+
exports.loadModule = loadModule;
|