@xwadex/fesd 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.
Files changed (47) hide show
  1. package/20240328-video4-setting.png +0 -0
  2. package/CHANGELOG.md +41 -0
  3. package/README.md +25 -0
  4. package/dist/assets/fesd-bundle.css +9 -0
  5. package/dist/assets/fesd-bundle.js +9800 -0
  6. package/dist/assets/fesd-bundle.js.map +1 -0
  7. package/index.html +25 -0
  8. package/package.json +23 -0
  9. package/prepros.config +883 -0
  10. package/src/fesd/anchor4/anchor4.js +179 -0
  11. package/src/fesd/aost4/_aost4.sass +64 -0
  12. package/src/fesd/aost4/aost4.js +138 -0
  13. package/src/fesd/article4/article4.js +280 -0
  14. package/src/fesd/article4/article4.md +1 -0
  15. package/src/fesd/category-slider/_category-slider.sass +33 -0
  16. package/src/fesd/category-slider/category-slider.js +332 -0
  17. package/src/fesd/collapse4/collapse4.js +159 -0
  18. package/src/fesd/detect4/detect4.js +70 -0
  19. package/src/fesd/dropdown4/_dropdown4.sass +185 -0
  20. package/src/fesd/dropdown4/cityData.js +830 -0
  21. package/src/fesd/dropdown4/dropdown4.js +647 -0
  22. package/src/fesd/image-preview/_image-preview.sass +26 -0
  23. package/src/fesd/image-preview/image-preview.js +209 -0
  24. package/src/fesd/image-validate/_image-validate.sass +21 -0
  25. package/src/fesd/image-validate/image-validate.js +84 -0
  26. package/src/fesd/marquee4/_marquee4.sass +45 -0
  27. package/src/fesd/marquee4/marquee4.js +371 -0
  28. package/src/fesd/modal4/_modal4.sass +134 -0
  29. package/src/fesd/modal4/modal4.js +236 -0
  30. package/src/fesd/modal4/modernModal.js +182 -0
  31. package/src/fesd/multipurpose4/_multipurpose4.sass +282 -0
  32. package/src/fesd/multipurpose4/multipurpose4.js +562 -0
  33. package/src/fesd/ripple4/_ripple4.sass +44 -0
  34. package/src/fesd/ripple4/ripple4.js +138 -0
  35. package/src/fesd/share4/share4.js +191 -0
  36. package/src/fesd/shared/shared.js +59 -0
  37. package/src/fesd/shared/utils.js +98 -0
  38. package/src/fesd/tab4/_tab4.sass +25 -0
  39. package/src/fesd/tab4/tab4.js +473 -0
  40. package/src/fesd/video4/README.md +3 -0
  41. package/src/fesd/video4/_video4.sass +117 -0
  42. package/src/fesd/video4/video4.js +237 -0
  43. package/src/fesd/video4/videoPlayer.js +195 -0
  44. package/src/fesd.js +53 -0
  45. package/src/fesd.sass +29 -0
  46. package/src/fesdDB.js +282 -0
  47. package/vite.config.js +37 -0
@@ -0,0 +1,332 @@
1
+ // 判斷操作事件使用 mouse 或是 touch
2
+ let operateStart = 'ontouchstart' in document.documentElement ? 'touchstart' : 'mousedown';
3
+ let operateEnd = 'ontouchend' in document.documentElement ? 'touchend' : 'mouseup';
4
+ let operateMove = 'ontouchmove' in document.documentElement ? 'touchmove' : 'mousemove';
5
+
6
+ /* ---------------------------- Function 宣告 --------------------------------- */
7
+
8
+ // 獲取 translate 數值
9
+ function getTranslateValues(element) {
10
+ const style = window.getComputedStyle(element);
11
+ const matrix = style['transform'] || style.mozTransform;
12
+
13
+ // No transform property. Simply return 0 values.
14
+ if (matrix === 'none') {
15
+ return {
16
+ x: 0,
17
+ y: 0,
18
+ z: 0,
19
+ };
20
+ }
21
+
22
+ // Can either be 2d or 3d transform
23
+ const matrixType = matrix.includes('3d') ? '3d' : '2d';
24
+ const matrixValues = matrix.match(/matrix.*\((.+)\)/)[1].split(', ');
25
+
26
+ // 2d matrices have 6 values
27
+ // Last 2 values are X and Y.
28
+ // 2d matrices does not have Z value.
29
+ if (matrixType === '2d') {
30
+ return {
31
+ x: Number(matrixValues[4]),
32
+ y: Number(matrixValues[5]),
33
+ z: 0,
34
+ };
35
+ }
36
+
37
+ // 3d matrices have 16 values
38
+ // The 13th, 14th, and 15th values are X, Y, and Z
39
+ if (matrixType === '3d') {
40
+ return {
41
+ x: Number(matrixValues[12]),
42
+ y: Number(matrixValues[13]),
43
+ z: Number(matrixValues[14]),
44
+ };
45
+ }
46
+ }
47
+
48
+ // 計算輪播內容總寬度
49
+ function getSlideTotalW(element) {
50
+ const slideWrap = element.querySelector('.wrapper');
51
+ const items = slideWrap.querySelectorAll('.item');
52
+ let slideTotalWidth = 0; //輪播內容總寬度
53
+ items.forEach((item) => {
54
+ const style = getComputedStyle(item);
55
+ const marginL = parseInt(style.marginLeft);
56
+ const marginR = parseInt(style.marginRight);
57
+ slideTotalWidth += item.getBoundingClientRect().width + marginL + marginR;
58
+ });
59
+ return slideTotalWidth;
60
+ }
61
+
62
+ // 偵測目前輪播位置
63
+ function detectPos(self) {
64
+ // 拖拉在最左邊時
65
+ if (getTranslateValues(self.slideWrapEl).x >= self.translateMin) {
66
+ return 'is-start';
67
+ }
68
+ // 拖拉在最右邊時
69
+ else if (getTranslateValues(self.slideWrapEl).x <= self.translateMax) {
70
+ return 'is-end';
71
+ }
72
+ // 拖拉在中間時
73
+ else if (
74
+ getTranslateValues(self.slideWrapEl).x < self.translateMin &&
75
+ getTranslateValues(self.slideWrapEl).x > self.translateMax
76
+ ) {
77
+ return 'is-middle';
78
+ }
79
+ }
80
+
81
+ // 綁定事件
82
+ function eventHandler(self) {
83
+ const element = self.el;
84
+ const slideWrap = element.querySelector('.wrapper');
85
+ //滑鼠按住或是行動裝置手指按住
86
+ slideWrap.addEventListener(operateStart, function (e) {
87
+ self.isDown = true;
88
+ if (self.slidable) slideWrap.classList.add('dragging');
89
+ switch (operateStart) {
90
+ case 'mousedown':
91
+ self.startX = e.pageX;
92
+ break;
93
+ case 'touchstart':
94
+ self.startX = e.changedTouches[0].pageX;
95
+ break;
96
+ }
97
+ self.nowTranslateX = getTranslateValues(slideWrap).x;
98
+ });
99
+
100
+ //滑鼠放開或是行動裝置手指放開
101
+ slideWrap.addEventListener(operateEnd, function (e) {
102
+ self.isDown = false;
103
+ slideWrap.classList.remove('dragging');
104
+ slideWrap.classList.remove('moving');
105
+ //移動距離
106
+ let moveDistance;
107
+ switch (operateMove) {
108
+ case 'mousemove':
109
+ moveDistance = e.pageX - self.startX;
110
+ break;
111
+ case 'touchmove':
112
+ moveDistance = e.changedTouches[0].pageX - self.startX;
113
+ break;
114
+ }
115
+ if (moveDistance === 0 && e.target.closest('.item')) {
116
+ if (operateEnd === 'mouseup' && e.button !== 0) return;
117
+ const items = slideWrap.querySelectorAll('.item');
118
+ items.forEach((item) => {
119
+ item.classList.remove('active');
120
+ });
121
+ e.target.closest('.item').classList.add('active');
122
+ self.moveActive(self.params.speed);
123
+ } else {
124
+ if (self.slidable) self.resetPos(element);
125
+ }
126
+ });
127
+
128
+ //滑鼠離開
129
+ slideWrap.addEventListener('mouseleave', function () {
130
+ if (!self.isDown) return;
131
+ if (self.slidable) {
132
+ self.isDown = false;
133
+ slideWrap.classList.remove('dragging');
134
+ slideWrap.classList.remove('moving');
135
+ self.resetPos(element);
136
+ }
137
+ });
138
+
139
+ //滑鼠移動或是行動裝置手指移動
140
+ slideWrap.addEventListener(operateMove, function (e) {
141
+ if (!self.draggable) return;
142
+ if (self.slidable) {
143
+ if (!self.isDown) return;
144
+ e.preventDefault();
145
+ //移動距離
146
+ let moveDistance;
147
+ switch (operateMove) {
148
+ case 'mousemove':
149
+ moveDistance = e.pageX - self.startX;
150
+ break;
151
+ case 'touchmove':
152
+ moveDistance = e.changedTouches[0].pageX - self.startX;
153
+ break;
154
+ }
155
+ if (moveDistance !== 0) {
156
+ slideWrap.classList.add('moving');
157
+ const styles = {
158
+ transition: 'all 0ms ease 0s',
159
+ transform: `translate3d(${self.nowTranslateX + moveDistance}px,0,0)`,
160
+ };
161
+ Object.assign(slideWrap.style, styles);
162
+ }
163
+ }
164
+ });
165
+ }
166
+
167
+ // 取消 a 的拖拉
168
+ function disableLinkDrag(element) {
169
+ const aTag = element.querySelectorAll('a');
170
+ for (var i = 0, len = aTag.length; i < len; i++) {
171
+ aTag[i].draggable = false;
172
+ }
173
+ }
174
+
175
+ export default class CategorySlider {
176
+ constructor(element, params) {
177
+ this.el = typeof element === 'string' ? document.querySelector(element) : element;
178
+ this.slideWrapEl = this.el.querySelector('.wrapper');
179
+ // default params
180
+ this.params = {
181
+ speed: 300,
182
+ clickSwitch: true,
183
+ breakpoint: false,
184
+ };
185
+ Object.assign(this.params, params);
186
+
187
+ this.draggable = true;
188
+ this.isDown = false; //按下
189
+ this.startX = 0; //按下初始位置
190
+ this.nowTranslateX = getTranslateValues(this.slideWrapEl).x; //目前X軸偏移輛
191
+ this.slideTotalWidth = getSlideTotalW(this.el);
192
+ this.translateMin = 0;
193
+ this.translateMax = -Math.floor(
194
+ Math.abs(
195
+ getSlideTotalW(this.el) - this.el.querySelector('.wrapper').getBoundingClientRect().width
196
+ )
197
+ );
198
+ this.slidable =
199
+ this.slideTotalWidth > Math.round(this.el.getBoundingClientRect().width) &&
200
+ (!this.params.breakpoint || window.innerWidth <= this.params.breakpoint);
201
+ this.init();
202
+ }
203
+ init() {
204
+ const self = this;
205
+ if (self.slidable) {
206
+ self.slideWrapEl.style.width = `${self.slideTotalWidth}px`;
207
+ self.el.classList.add('slidable');
208
+ if (self.slideWrapEl.querySelector('.item.active')) self.moveActive();
209
+ }
210
+ window.addEventListener('resize', function () {
211
+ self.update();
212
+ });
213
+ eventHandler(self);
214
+ disableLinkDrag(self.el);
215
+ }
216
+ moveActive(speed) {
217
+ const self = this;
218
+ const activeItem = self.slideWrapEl.querySelector('.item.active');
219
+ const prevItem = activeItem.previousElementSibling;
220
+ const prevOffsetLeft = prevItem
221
+ ? activeItem.offsetLeft - self.el.clientWidth / 2 + activeItem.clientWidth / 2
222
+ : 0;
223
+ if (self.slidable) {
224
+ if (-prevOffsetLeft < 0 && -prevOffsetLeft > self.translateMax) {
225
+ const styles = {
226
+ transition: `all ${speed ? speed : 0}ms ease 0s`,
227
+ transform: `translate3d(-${prevOffsetLeft}px,0,0)`,
228
+ };
229
+ Object.assign(self.slideWrapEl.style, styles);
230
+ } else if (-prevOffsetLeft >= 0) {
231
+ const styles = {
232
+ transition: `all ${speed ? speed : 0}ms ease 0s`,
233
+ transform: `translate3d(0,0,0)`,
234
+ };
235
+ Object.assign(self.slideWrapEl.style, styles);
236
+ } else {
237
+ const styles = {
238
+ transition: `all ${speed ? speed : 0}ms ease 0s`,
239
+ transform: `translate3d(${self.translateMax}px,0,0)`,
240
+ };
241
+ Object.assign(self.slideWrapEl.style, styles);
242
+ }
243
+ if (speed) {
244
+ self.slideWrapEl.addEventListener(
245
+ 'transitionend',
246
+ function () {
247
+ self.nowTranslateX = getTranslateValues(self.slideWrapEl).x;
248
+ switch (detectPos(self)) {
249
+ case 'is-start':
250
+ self.el.classList.add('is-start');
251
+ self.el.classList.remove('is-end');
252
+ break;
253
+ case 'is-middle':
254
+ self.el.classList.remove('is-start');
255
+ self.el.classList.remove('is-end');
256
+ break;
257
+ case 'is-end':
258
+ self.el.classList.remove('is-start');
259
+ self.el.classList.add('is-end');
260
+ break;
261
+ }
262
+ },
263
+ false
264
+ );
265
+ } else {
266
+ self.nowTranslateX = getTranslateValues(self.slideWrapEl).x;
267
+ switch (detectPos(self)) {
268
+ case 'is-start':
269
+ self.el.classList.add('is-start');
270
+ self.el.classList.remove('is-end');
271
+ break;
272
+ case 'is-middle':
273
+ self.el.classList.remove('is-start');
274
+ self.el.classList.remove('is-end');
275
+ break;
276
+ case 'is-end':
277
+ self.el.classList.remove('is-start');
278
+ self.el.classList.add('is-end');
279
+ break;
280
+ }
281
+ }
282
+ }
283
+ }
284
+ resetPos() {
285
+ const self = this;
286
+ switch (detectPos(self)) {
287
+ case 'is-start':
288
+ self.el.classList.add('is-start');
289
+ self.el.classList.remove('is-end');
290
+ Object.assign(self.slideWrapEl.style, {
291
+ transition: `all ${self.params.speed}ms ease 0s`,
292
+ transform: 'translate3d(0,0,0)',
293
+ });
294
+ break;
295
+ case 'is-middle':
296
+ self.el.classList.remove('is-start');
297
+ self.el.classList.remove('is-end');
298
+ break;
299
+ case 'is-end':
300
+ self.el.classList.remove('is-start');
301
+ self.el.classList.add('is-end');
302
+ Object.assign(self.slideWrapEl.style, {
303
+ transition: `all ${self.params.speed}ms ease 0s`,
304
+ transform: `translate3d(${self.translateMax}px,0,0)`,
305
+ });
306
+ break;
307
+ }
308
+ }
309
+ update() {
310
+ const self = this;
311
+ self.el.querySelector('.wrapper').removeAttribute('style');
312
+ self.slideTotalWidth = getSlideTotalW(self.el);
313
+ self.translateMax = -Math.floor(
314
+ Math.abs(
315
+ getSlideTotalW(self.el) - self.el.querySelector('.wrapper').getBoundingClientRect().width
316
+ )
317
+ );
318
+ self.slidable =
319
+ self.slideTotalWidth > Math.round(self.el.getBoundingClientRect().width) &&
320
+ (!self.params.breakpoint || window.innerWidth <= self.params.breakpoint);
321
+ if (self.slidable) {
322
+ self.slideWrapEl.style.width = `${self.slideTotalWidth}px`;
323
+ self.el.classList.add('slidable');
324
+ if (self.slideWrapEl.querySelector('.item.active')) self.moveActive();
325
+ } else {
326
+ self.el.classList.remove('slidable');
327
+ self.el.classList.remove('is-start');
328
+ self.el.classList.remove('is-end');
329
+ self.slideWrapEl.removeAttribute('style');
330
+ }
331
+ }
332
+ }
@@ -0,0 +1,159 @@
1
+ import SHARED from '../shared/shared';
2
+ import { isString, isElement, isNodeList, isElementExist, isFunction, getElement, getAllElements, createUid, toHTMLElement, jsonParse, warn, error } from '../shared/utils';
3
+
4
+ ('use strict');
5
+
6
+ const collapseHandle = $wrapper => {
7
+ const { defaultOptions } = $wrapper.collapse;
8
+ const { collapseClass, target, transition } = defaultOptions;
9
+
10
+ const $target = $wrapper.querySelector(target);
11
+
12
+ $wrapper.classList.add(collapseClass);
13
+
14
+ $target.style.height = 0;
15
+ $target.style['transition-property'] = transition.property;
16
+ $target.style['transition-duration'] = transition.duration;
17
+ $target.style['transition-timing-function'] = transition.function;
18
+ $target.style['transition-delay'] = transition.delay;
19
+ $target.style.overflow = 'hidden';
20
+ };
21
+
22
+ const expandHandle = $wrapper => {
23
+ const { defaultOptions } = $wrapper.collapse;
24
+ const { collapseClass, target, transition } = defaultOptions;
25
+
26
+ const $target = $wrapper.querySelector(target);
27
+ const height = $target.children[0].offsetHeight;
28
+ $wrapper.collapse.height = height;
29
+
30
+ // console.log( height );
31
+ // console.log( $target.children[0].offsetHeight );
32
+ $wrapper.classList.remove(collapseClass);
33
+
34
+ $target.style.display = 'block';
35
+ $target.style.height = `${height}px`;
36
+ $target.style['transition-property'] = transition.property;
37
+ $target.style['transition-duration'] = transition.duration;
38
+ $target.style['transition-timing-function'] = transition.function;
39
+ $target.style['transition-delay'] = transition.delay;
40
+ $target.style.overflow = 'hidden';
41
+ };
42
+
43
+ class Collapse4 {
44
+ constructor(el, options = {}) {
45
+ // 可傳 string 或 element 或 nodeList
46
+ if (!isString(el) && !isElement(el) && !isNodeList(el) && !isElementExist(el)) return;
47
+
48
+ this.__storage__ = {
49
+ el,
50
+ options,
51
+ };
52
+
53
+ this.#create();
54
+ }
55
+
56
+ #create() {
57
+ const { el, options } = this.__storage__;
58
+ const { SETTINGS, EVENTS } = fesdDB.collapse4;
59
+
60
+ this.elements = getAllElements(el);
61
+ this.options = Object.assign({}, SETTINGS, options);
62
+ this.__events__ = Object.assign({}, EVENTS);
63
+
64
+ if (this.options.on) {
65
+ for (const [k, v] of Object.entries(this.options.on)) {
66
+ this.__events__[k] = [v];
67
+ }
68
+ }
69
+
70
+ this.#init();
71
+ }
72
+
73
+ #init() {
74
+ const { elements, options } = this;
75
+
76
+ elements.forEach(el => {
77
+ el.collapse = {};
78
+ el.collapse.instance = this;
79
+ el.collapse.defaultOptions = options;
80
+
81
+ this.#trigger(el);
82
+ });
83
+
84
+ this.emit('init');
85
+ }
86
+
87
+ #trigger(el) {
88
+ const { options } = this;
89
+ const { collapseClass, block, target, defaultOpen, targetStopPropagation, defaultActiveMark } = options;
90
+
91
+ // collapse-wrapper
92
+ // collapse-block
93
+ // collapse-target
94
+
95
+ const $wrapper = el;
96
+ const $target = $wrapper.querySelector(target);
97
+ $wrapper.collapse.height = $target.children[0].offsetHeight;
98
+
99
+ // const $block = $wrapper.querySelector(block);
100
+
101
+ // 是否預設全打開
102
+ if (defaultOpen) {
103
+ $target.style.height = `${$target.children[0].offsetHeight}px`;
104
+ $wrapper.classList.remove(collapseClass);
105
+ } else {
106
+ if (!$wrapper.classList.contains(defaultActiveMark) && !$wrapper.hasAttribute(defaultActiveMark)) {
107
+ $wrapper.classList.add(collapseClass);
108
+ $target.style.height = '0px';
109
+ $target.style.overflow = 'hidden';
110
+ } else {
111
+ $target.style.height = `${$target.children[0].offsetHeight}px`;
112
+ $wrapper.classList.remove(collapseClass);
113
+ }
114
+ }
115
+
116
+ // update() 避免重複綁定
117
+ $wrapper.removeEventListener('click', this.#event);
118
+ $wrapper.addEventListener('click', this.#event);
119
+
120
+ // target 點擊不收合
121
+ if (targetStopPropagation) {
122
+ const event = e => {
123
+ e.stopPropagation();
124
+ };
125
+ // update() 避免重複綁定
126
+ $target.removeEventListener('click', event);
127
+ $target.addEventListener('click', event);
128
+ }
129
+ }
130
+
131
+ #event() {
132
+ const { instance, defaultOptions } = this.collapse;
133
+ const { collapseClass, block, target, single } = defaultOptions;
134
+
135
+ const $wrapper = this;
136
+
137
+ if ($wrapper.classList.contains(collapseClass)) {
138
+ if (single) {
139
+ $wrapper.parentNode.querySelectorAll(`.${$wrapper.classList[0]}`).forEach(el => {
140
+ collapseHandle(el);
141
+ });
142
+ }
143
+
144
+ expandHandle($wrapper);
145
+ } else {
146
+ collapseHandle($wrapper);
147
+ }
148
+
149
+ instance.emit('afterCollapse');
150
+ }
151
+
152
+ update() {
153
+ this.#create();
154
+ }
155
+ }
156
+
157
+ Object.assign(Collapse4.prototype, SHARED);
158
+
159
+ export default Collapse4;
@@ -0,0 +1,70 @@
1
+ const Detect4 = {};
2
+ Detect4.init = function () {
3
+ const userAgent = navigator.userAgent;
4
+
5
+ // isBrowser4
6
+ fesdDB.library.browser = {
7
+ // Firefox 1.0+
8
+ isFirefox: typeof InstallTrigger !== 'undefined',
9
+ // Opera 8.0+
10
+ isOpera: (!!window.opr && !!opr.addons) || !!window.opera || userAgent.indexOf(' OPR/') >= 0,
11
+ // Internet Explorer 6-11
12
+ isIE: /*@cc_on!@*/ false || !!document.documentMode,
13
+ // Edge 20+
14
+ isEdge: !(false || !!document.documentMode) && !!window.StyleMedia,
15
+ // Edge (based on chromium) detection
16
+ isEdgeChromium: /\sedg\//i.test(userAgent) || /edg([ea]|ios)/i.test(userAgent),
17
+ // Safari 3.0+ "[object HTMLElementConstructor]"
18
+ isSafari: !/chrome|crios|crmo/i.test(userAgent) && /safari/i.test(userAgent),
19
+ // Chrome 1 - 79
20
+ isChrome: (!!window.chrome && (!!window.chrome.webstore || !!window.chrome.runtime)) || /chrome|crios|crmo/i.test(userAgent),
21
+ };
22
+
23
+ // isMobile4
24
+ fesdDB.library.mobile = {
25
+ // Android
26
+ isAndroid: userAgent.indexOf('Android') > -1 || userAgent.indexOf('Adr') > -1,
27
+ // iOS | ipad
28
+ isiOS: userAgent.indexOf('Mac') > -1 && 'ontouchend' in document,
29
+ };
30
+
31
+ // isOs4
32
+ fesdDB.library.os = {
33
+ // window
34
+ isWindows: userAgent.indexOf('Win') > -1,
35
+ // macos
36
+ isMacOS: userAgent.indexOf('Mac') > -1,
37
+ // UNIX
38
+ isUNIX: userAgent.indexOf('X11') > -1,
39
+ // Linux
40
+ isLinux: userAgent.indexOf('Linux') > -1,
41
+ };
42
+
43
+ // **eric - 以下待優化
44
+ const isBrowser4 = () => {
45
+ const { browser } = fesdDB.library;
46
+ return Object.keys(browser).find(key => {
47
+ if (browser[key]) return browser[key];
48
+ });
49
+ };
50
+
51
+ const isMobile4 = () => {
52
+ const { mobile } = fesdDB.library;
53
+ return Object.keys(mobile).some(key => {
54
+ if (mobile[key]) return mobile[key];
55
+ });
56
+ };
57
+
58
+ const isOs4 = () => {
59
+ const { os } = fesdDB.library;
60
+ return Object.keys(os).find(key => {
61
+ if (os[key]) return os[key];
62
+ });
63
+ };
64
+
65
+ fesdDB.is.isBrowser4 = isBrowser4();
66
+ fesdDB.is.isMobile4 = isMobile4();
67
+ fesdDB.is.isOs4 = isOs4();
68
+ };
69
+
70
+ export default Detect4;