vgapp 1.5.6 → 1.5.8

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 (109) hide show
  1. package/app/langs/en/buttons.json +17 -17
  2. package/app/langs/en/messages.json +36 -36
  3. package/app/langs/ru/buttons.json +17 -17
  4. package/app/langs/ru/messages.json +1 -1
  5. package/app/modules/vgalert/js/vgalert.js +6 -3
  6. package/app/modules/vgalert/scss/_variables.scss +16 -16
  7. package/app/modules/vgcollapse/scss/_variables.scss +7 -7
  8. package/app/modules/vgcollapse/scss/vgcollapse.scss +5 -5
  9. package/app/modules/vgdropdown/scss/_variables.scss +14 -14
  10. package/app/modules/vgdropdown/scss/vgdropdown.scss +5 -5
  11. package/app/modules/vgfilepreview/js/i18n.js +56 -56
  12. package/app/modules/vgfilepreview/js/renderers/image-modal.js +145 -145
  13. package/app/modules/vgfilepreview/js/renderers/image.js +92 -92
  14. package/app/modules/vgfilepreview/js/renderers/index.js +19 -19
  15. package/app/modules/vgfilepreview/js/renderers/office-modal.js +168 -168
  16. package/app/modules/vgfilepreview/js/renderers/office.js +79 -79
  17. package/app/modules/vgfilepreview/js/renderers/pdf-modal.js +260 -260
  18. package/app/modules/vgfilepreview/js/renderers/pdf.js +76 -76
  19. package/app/modules/vgfilepreview/js/renderers/playlist.js +71 -71
  20. package/app/modules/vgfilepreview/js/renderers/text-modal.js +343 -343
  21. package/app/modules/vgfilepreview/js/renderers/text.js +83 -83
  22. package/app/modules/vgfilepreview/js/renderers/zip-modal.js +522 -522
  23. package/app/modules/vgfilepreview/js/renderers/zip.js +89 -89
  24. package/app/modules/vgfilepreview/readme.md +68 -68
  25. package/app/modules/vgfiles/js/base.js +189 -194
  26. package/app/modules/vgfiles/js/droppable.js +17 -17
  27. package/app/modules/vgfiles/js/render.js +153 -153
  28. package/app/modules/vgfiles/js/sortable.js +52 -52
  29. package/app/modules/vgfiles/js/vgfiles.js +43 -43
  30. package/app/modules/vgfiles/readme.md +123 -123
  31. package/app/modules/vgfiles/scss/_mixins.scss +13 -13
  32. package/app/modules/vgfiles/scss/_variables.scss +10 -10
  33. package/app/modules/vgfiles/scss/vgfiles.scss +28 -28
  34. package/app/modules/vgformsender/js/hideshowpass.js +22 -22
  35. package/app/modules/vgformsender/readme.md +1 -1
  36. package/app/modules/vgformsender/scss/_variables.scss +5 -5
  37. package/app/modules/vgformsender/scss/vgformsender.scss +42 -42
  38. package/app/modules/vglawcookie/js/vglawcookie.js +19 -19
  39. package/app/modules/vglawcookie/scss/_variables.scss +13 -13
  40. package/app/modules/vglawcookie/scss/vglawcookie.scss +5 -5
  41. package/app/modules/vgmodal/js/vgmodal.interaction.js +216 -216
  42. package/app/modules/vgmodal/js/vgmodal.js +6 -0
  43. package/app/modules/vgmodal/js/vgmodal.minimized.js +227 -227
  44. package/app/modules/vgmodal/scss/_variables.scss +17 -17
  45. package/app/modules/vgmodal/scss/vgmodal.scss +77 -77
  46. package/app/modules/vgnav/js/vgnav.js +62 -62
  47. package/app/modules/vgnav/scss/_breakpoints.scss +36 -36
  48. package/app/modules/vgnav/scss/_hamburger.scss +5 -5
  49. package/app/modules/vgnav/scss/_placement.scss +5 -5
  50. package/app/modules/vgnav/scss/_toggle.scss +5 -5
  51. package/app/modules/vgnav/scss/_variables.scss +26 -26
  52. package/app/modules/vgnav/scss/vgnav.scss +41 -41
  53. package/app/modules/vgnestable/README.md +39 -39
  54. package/app/modules/vgrangeslider/js/skins.js +8 -8
  55. package/app/modules/vgrangeslider/js/vgrangeslider.js +29 -29
  56. package/app/modules/vgrangeslider/scss/_variables.scss +55 -55
  57. package/app/modules/vgrangeslider/scss/vgrangeslider.scss +239 -239
  58. package/app/modules/vgrollup/js/vgrollup.js +15 -15
  59. package/app/modules/vgrollup/scss/_variables.scss +15 -15
  60. package/app/modules/vgrollup/scss/vgrollup.scss +6 -6
  61. package/app/modules/vgselect/js/handlers.js +5 -52
  62. package/app/modules/vgselect/js/vgselect.js +128 -40
  63. package/app/modules/vgselect/readme.md +11 -11
  64. package/app/modules/vgselect/scss/_variables.scss +51 -51
  65. package/app/modules/vgselect/scss/vgselect.scss +1 -1
  66. package/app/modules/vgsidebar/js/vgsidebar.js +16 -5
  67. package/app/modules/vgsidebar/scss/_variables.scss +10 -10
  68. package/app/modules/vgsidebar/scss/vgsidebar.scss +5 -5
  69. package/app/modules/vgspy/js/vgspy.js +28 -7
  70. package/app/modules/vgtable/js/_filters.js +10 -6
  71. package/app/modules/vgtable/js/_options.js +16 -16
  72. package/app/modules/vgtable/js/_pagination.js +118 -118
  73. package/app/modules/vgtable/js/_responsive.js +79 -79
  74. package/app/modules/vgtable/js/_sticky-header.js +349 -280
  75. package/app/modules/vgtable/js/vgtable.js +98 -98
  76. package/app/modules/vgtable/scss/_sorting.scss +6 -6
  77. package/app/modules/vgtable/scss/_sticky-header.scss +31 -31
  78. package/app/modules/vgtable/scss/_variables.scss +6 -4
  79. package/app/modules/vgtable/scss/vgtable.scss +8 -4
  80. package/app/modules/vgtabs/js/vgtabs.js +82 -57
  81. package/app/modules/vgtabs/scss/_variables.scss +23 -23
  82. package/app/modules/vgtabs/scss/vgtabs.scss +5 -5
  83. package/app/modules/vgtoast/js/vgtoast.js +11 -5
  84. package/app/modules/vgtoast/scss/_variables.scss +13 -13
  85. package/app/modules/vgtoast/scss/vgtoast.scss +5 -5
  86. package/app/modules/vgtooltip/js/vgtooltip.js +121 -111
  87. package/app/modules/vgtooltip/scss/_variables.scss +10 -10
  88. package/app/modules/vgtooltip/scss/vgtooltip.scss +5 -5
  89. package/app/utils/js/components/audio-metadata.js +240 -240
  90. package/app/utils/js/components/backdrop.js +116 -116
  91. package/app/utils/js/components/file-icon.js +5 -0
  92. package/app/utils/js/components/file-preview.js +304 -304
  93. package/app/utils/js/components/responsive.js +165 -165
  94. package/app/utils/js/components/sanitize.js +150 -150
  95. package/app/utils/js/components/video-metadata.js +140 -140
  96. package/app/utils/scss/_backdrop.scss +5 -5
  97. package/app/utils/scss/_theme-default.scss +12 -12
  98. package/app/utils/scss/_theme.scss +7 -7
  99. package/app/utils/scss/default.scss +9 -9
  100. package/app/utils/scss/functions.scss +21 -21
  101. package/app/utils/scss/index.scss +4 -4
  102. package/app/utils/scss/mixin.scss +16 -16
  103. package/app/utils/scss/variables.scss +147 -147
  104. package/build/vgapp.css +1 -1
  105. package/build/vgapp.css.map +1 -1
  106. package/build/vgapp.js +1 -1
  107. package/build/vgapp.js.map +1 -1
  108. package/index.js +7 -7
  109. package/package.json +1 -1
@@ -1,165 +1,165 @@
1
- /**
2
- * Описание: общий сервис брейкпоинтов и адаптивных настроек VGApp.
3
- * Возможности: xs–xxl, глобальные и локальные границы, наследование профилей, подписки, viewport и эвристики устройства/touch.
4
- */
5
- import {normalizeData} from "../functions";
6
-
7
- export const DEFAULT_BREAKPOINTS = Object.freeze({xs: 0, sm: 576, md: 768, lg: 992, xl: 1200, xxl: 1400});
8
-
9
- const plainObject = (value) => {
10
- if (value === null || typeof value !== 'object') return false;
11
- const prototype = Object.getPrototypeOf(value);
12
- return prototype === null || Object.getPrototypeOf(prototype) === null;
13
- };
14
- const entries = (value) => Object.entries(value).filter(([key]) => !['__proto__', 'constructor', 'prototype'].includes(key));
15
- const clone = (value) => Array.isArray(value) ? value.map(clone)
16
- : plainObject(value) ? Object.fromEntries(entries(value).map(([key, item]) => [key, clone(item)])) : value;
17
-
18
- // В профилях массив заменяется целиком, а не конкатенируется как в mergeDeepObject.
19
- const merge = (base, profile) => {
20
- const result = plainObject(base) ? clone(base) : {};
21
- if (plainObject(profile)) entries(profile).forEach(([key, value]) => {
22
- result[key] = plainObject(value) ? merge(result[key], value) : clone(value);
23
- });
24
- return result;
25
- };
26
-
27
- export class Responsive {
28
- constructor(options = {}) {
29
- this._window = options.window === undefined ? (typeof window === 'undefined' ? null : window) : options.window;
30
- const globalPoints = this._window?.Breakpoints ?? this._window?.breakpoints;
31
- const globalMap = normalizeData(globalPoints ?? {});
32
- const localMap = normalizeData(options.breakpoints ?? {});
33
- const sourcesValid = plainObject(globalMap) && plainObject(localMap);
34
- const combined = {...DEFAULT_BREAKPOINTS, ...(plainObject(globalMap) ? globalMap : {}), ...(plainObject(localMap) ? localMap : {})};
35
- this._breakpoints = Object.freeze(Object.fromEntries(entries(combined).map(([key, value]) => [key,
36
- typeof value === 'string' && value.trim() !== '' ? Number(value) : value,
37
- ])));
38
- const standardNames = Object.keys(DEFAULT_BREAKPOINTS);
39
- const widths = Object.values(this._breakpoints);
40
- this._valid = sourcesValid && widths.every((width) => Number.isFinite(width) && width >= 0)
41
- && new Set(widths).size === widths.length
42
- && standardNames.every((name, index) => index === 0 ? this._breakpoints[name] === 0
43
- : this._breakpoints[name] > this._breakpoints[standardNames[index - 1]]);
44
- this._keys = this._valid ? Object.keys(this._breakpoints).sort((a, b) => this._breakpoints[a] - this._breakpoints[b]) : [];
45
- this._subscribers = new Set();
46
- this._previous = null;
47
- this._boundResize = () => {
48
- const state = this.getState();
49
- if (state.breakpoint === this._previous) return;
50
- const previous = this._previous;
51
- this._previous = state.breakpoint;
52
- Array.from(this._subscribers).forEach((subscriber) => {
53
- if (!this._subscribers.has(subscriber)) return;
54
- try { subscriber({...this.getState(), previous}); }
55
- catch (error) {
56
- if (typeof this._window?.reportError === 'function') this._window.reportError(error);
57
- else console.error(error);
58
- }
59
- });
60
- };
61
- }
62
-
63
- /** Копия границ; внешнее изменение не влияет на экземпляр. */
64
- get breakpoints() { return {...this._breakpoints}; }
65
- isValid() { return this._valid; }
66
-
67
- /** Без аргумента — карта; имя — min-width; число (включая 0) — имя диапазона. */
68
- breakpoint(point) {
69
- if (point === undefined) return this.breakpoints;
70
- if (typeof point === 'number') return this.getBreakpointKey(point);
71
- return this.checkBreakpoint(point) ? this._breakpoints[point] : null;
72
- }
73
-
74
- checkBreakpoint(point) {
75
- return this._valid && typeof point === 'string' && Object.prototype.hasOwnProperty.call(this._breakpoints, point);
76
- }
77
-
78
- breakpointDown(point) { return this.checkBreakpoint(point) && this.viewport().width < this._breakpoints[point]; }
79
- breakpointUp(point) { return this.checkBreakpoint(point) && this.viewport().width >= this._breakpoints[point]; }
80
-
81
- /** Полуоткрытый диапазон [start, end), без пересечения соседних диапазонов. */
82
- breakpointBetween(start, end) {
83
- const width = this.viewport().width;
84
- return this.checkBreakpoint(start) && this.checkBreakpoint(end)
85
- && this._breakpoints[start] < this._breakpoints[end]
86
- && width >= this._breakpoints[start] && width < this._breakpoints[end];
87
- }
88
-
89
- getActiveBreakpoints(width = this.viewport().width) {
90
- if (!this._valid || !Number.isFinite(width) || width < 0) return [];
91
- return this._keys.filter((key) => width >= this._breakpoints[key]);
92
- }
93
-
94
- getBreakpointKey(width = this.viewport().width) { return this.getActiveBreakpoints(width).at(-1) ?? null; }
95
-
96
- /** База + все достигнутые профили; вложенные объекты объединяются, массивы заменяются. */
97
- resolve(profiles = {}, base = {}, width = this.viewport().width) {
98
- return this.getActiveBreakpoints(width).reduce((result, key) => merge(result, profiles?.[key]), merge({}, base));
99
- }
100
-
101
- getState() {
102
- const viewport = this.viewport();
103
- return {...viewport, breakpoint: this.getBreakpointKey(viewport.width), active: this.getActiveBreakpoints(viewport.width),
104
- reason: this._valid ? null : 'invalid-breakpoints'};
105
- }
106
-
107
- /** Уведомляет только о смене диапазона; возвращает функцию отписки. */
108
- subscribe(callback, {immediate = false} = {}) {
109
- if (typeof callback !== 'function') throw new TypeError('Responsive.subscribe expects a function.');
110
- if (!this._valid) return () => {};
111
- const subscriber = (state) => callback(state);
112
- if (this._subscribers.size === 0) {
113
- this._previous = this.getBreakpointKey();
114
- this._window?.addEventListener('resize', this._boundResize);
115
- }
116
- this._subscribers.add(subscriber);
117
- const unsubscribe = () => {
118
- this._subscribers.delete(subscriber);
119
- if (this._subscribers.size === 0) this._window?.removeEventListener('resize', this._boundResize);
120
- };
121
- if (immediate) {
122
- try { callback({...this.getState(), previous: null}); }
123
- catch (error) { unsubscribe(); throw error; }
124
- }
125
- return unsubscribe;
126
- }
127
-
128
- dispose() {
129
- this._window?.removeEventListener('resize', this._boundResize);
130
- this._subscribers.clear();
131
- }
132
-
133
- viewport() {
134
- return {width: this._window?.innerWidth ?? 0, height: this._window?.innerHeight ?? 0};
135
- }
136
-
137
- /** Возможность touch-ввода, не классификация телефона или планшета. */
138
- detectTouchDevice() {
139
- return !!this._window && (Number(this._window.navigator?.maxTouchPoints) > 0 || 'ontouchstart' in this._window);
140
- }
141
-
142
- /** Эвристики устройства не используются при выборе responsive-профилей. */
143
- isMobileDevice() {
144
- if (!this._window) return false;
145
- const userAgent = this._window.navigator?.userAgent || '';
146
- return /Android|iPhone|iPad|iPod/i.test(userAgent)
147
- || (this.detectTouchDevice() && this.viewport().width < 768 && this._window.devicePixelRatio >= 2);
148
- }
149
-
150
- isTabletDevice() {
151
- if (!this._window) return false;
152
- const navigator = this._window.navigator || {};
153
- const userAgent = (navigator.userAgent || '').toLowerCase();
154
- const {width, height} = this.viewport();
155
- const short = Math.min(width, height);
156
- const long = Math.max(width, height);
157
- return /ipad/.test(userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
158
- || (/android/.test(userAgent) && !/mobile/.test(userAgent) && long > 800)
159
- || (this.detectTouchDevice() && short >= 600 && short <= 1200 && long >= 800 && long <= 1600);
160
- }
161
-
162
- detectDevice() {
163
- return this.isTabletDevice() ? 'tablet' : this.isMobileDevice() ? 'mobile' : 'desktop';
164
- }
165
- }
1
+ /**
2
+ * Описание: общий сервис брейкпоинтов и адаптивных настроек VGApp.
3
+ * Возможности: xs–xxl, глобальные и локальные границы, наследование профилей, подписки, viewport и эвристики устройства/touch.
4
+ */
5
+ import {normalizeData} from "../functions";
6
+
7
+ export const DEFAULT_BREAKPOINTS = Object.freeze({xs: 0, sm: 576, md: 768, lg: 992, xl: 1200, xxl: 1400});
8
+
9
+ const plainObject = (value) => {
10
+ if (value === null || typeof value !== 'object') return false;
11
+ const prototype = Object.getPrototypeOf(value);
12
+ return prototype === null || Object.getPrototypeOf(prototype) === null;
13
+ };
14
+ const entries = (value) => Object.entries(value).filter(([key]) => !['__proto__', 'constructor', 'prototype'].includes(key));
15
+ const clone = (value) => Array.isArray(value) ? value.map(clone)
16
+ : plainObject(value) ? Object.fromEntries(entries(value).map(([key, item]) => [key, clone(item)])) : value;
17
+
18
+ // В профилях массив заменяется целиком, а не конкатенируется как в mergeDeepObject.
19
+ const merge = (base, profile) => {
20
+ const result = plainObject(base) ? clone(base) : {};
21
+ if (plainObject(profile)) entries(profile).forEach(([key, value]) => {
22
+ result[key] = plainObject(value) ? merge(result[key], value) : clone(value);
23
+ });
24
+ return result;
25
+ };
26
+
27
+ export class Responsive {
28
+ constructor(options = {}) {
29
+ this._window = options.window === undefined ? (typeof window === 'undefined' ? null : window) : options.window;
30
+ const globalPoints = this._window?.Breakpoints ?? this._window?.breakpoints;
31
+ const globalMap = normalizeData(globalPoints ?? {});
32
+ const localMap = normalizeData(options.breakpoints ?? {});
33
+ const sourcesValid = plainObject(globalMap) && plainObject(localMap);
34
+ const combined = {...DEFAULT_BREAKPOINTS, ...(plainObject(globalMap) ? globalMap : {}), ...(plainObject(localMap) ? localMap : {})};
35
+ this._breakpoints = Object.freeze(Object.fromEntries(entries(combined).map(([key, value]) => [key,
36
+ typeof value === 'string' && value.trim() !== '' ? Number(value) : value,
37
+ ])));
38
+ const standardNames = Object.keys(DEFAULT_BREAKPOINTS);
39
+ const widths = Object.values(this._breakpoints);
40
+ this._valid = sourcesValid && widths.every((width) => Number.isFinite(width) && width >= 0)
41
+ && new Set(widths).size === widths.length
42
+ && standardNames.every((name, index) => index === 0 ? this._breakpoints[name] === 0
43
+ : this._breakpoints[name] > this._breakpoints[standardNames[index - 1]]);
44
+ this._keys = this._valid ? Object.keys(this._breakpoints).sort((a, b) => this._breakpoints[a] - this._breakpoints[b]) : [];
45
+ this._subscribers = new Set();
46
+ this._previous = null;
47
+ this._boundResize = () => {
48
+ const state = this.getState();
49
+ if (state.breakpoint === this._previous) return;
50
+ const previous = this._previous;
51
+ this._previous = state.breakpoint;
52
+ Array.from(this._subscribers).forEach((subscriber) => {
53
+ if (!this._subscribers.has(subscriber)) return;
54
+ try { subscriber({...this.getState(), previous}); }
55
+ catch (error) {
56
+ if (typeof this._window?.reportError === 'function') this._window.reportError(error);
57
+ else console.error(error);
58
+ }
59
+ });
60
+ };
61
+ }
62
+
63
+ /** Копия границ; внешнее изменение не влияет на экземпляр. */
64
+ get breakpoints() { return {...this._breakpoints}; }
65
+ isValid() { return this._valid; }
66
+
67
+ /** Без аргумента — карта; имя — min-width; число (включая 0) — имя диапазона. */
68
+ breakpoint(point) {
69
+ if (point === undefined) return this.breakpoints;
70
+ if (typeof point === 'number') return this.getBreakpointKey(point);
71
+ return this.checkBreakpoint(point) ? this._breakpoints[point] : null;
72
+ }
73
+
74
+ checkBreakpoint(point) {
75
+ return this._valid && typeof point === 'string' && Object.prototype.hasOwnProperty.call(this._breakpoints, point);
76
+ }
77
+
78
+ breakpointDown(point) { return this.checkBreakpoint(point) && this.viewport().width < this._breakpoints[point]; }
79
+ breakpointUp(point) { return this.checkBreakpoint(point) && this.viewport().width >= this._breakpoints[point]; }
80
+
81
+ /** Полуоткрытый диапазон [start, end), без пересечения соседних диапазонов. */
82
+ breakpointBetween(start, end) {
83
+ const width = this.viewport().width;
84
+ return this.checkBreakpoint(start) && this.checkBreakpoint(end)
85
+ && this._breakpoints[start] < this._breakpoints[end]
86
+ && width >= this._breakpoints[start] && width < this._breakpoints[end];
87
+ }
88
+
89
+ getActiveBreakpoints(width = this.viewport().width) {
90
+ if (!this._valid || !Number.isFinite(width) || width < 0) return [];
91
+ return this._keys.filter((key) => width >= this._breakpoints[key]);
92
+ }
93
+
94
+ getBreakpointKey(width = this.viewport().width) { return this.getActiveBreakpoints(width).at(-1) ?? null; }
95
+
96
+ /** База + все достигнутые профили; вложенные объекты объединяются, массивы заменяются. */
97
+ resolve(profiles = {}, base = {}, width = this.viewport().width) {
98
+ return this.getActiveBreakpoints(width).reduce((result, key) => merge(result, profiles?.[key]), merge({}, base));
99
+ }
100
+
101
+ getState() {
102
+ const viewport = this.viewport();
103
+ return {...viewport, breakpoint: this.getBreakpointKey(viewport.width), active: this.getActiveBreakpoints(viewport.width),
104
+ reason: this._valid ? null : 'invalid-breakpoints'};
105
+ }
106
+
107
+ /** Уведомляет только о смене диапазона; возвращает функцию отписки. */
108
+ subscribe(callback, {immediate = false} = {}) {
109
+ if (typeof callback !== 'function') throw new TypeError('Responsive.subscribe expects a function.');
110
+ if (!this._valid) return () => {};
111
+ const subscriber = (state) => callback(state);
112
+ if (this._subscribers.size === 0) {
113
+ this._previous = this.getBreakpointKey();
114
+ this._window?.addEventListener('resize', this._boundResize);
115
+ }
116
+ this._subscribers.add(subscriber);
117
+ const unsubscribe = () => {
118
+ this._subscribers.delete(subscriber);
119
+ if (this._subscribers.size === 0) this._window?.removeEventListener('resize', this._boundResize);
120
+ };
121
+ if (immediate) {
122
+ try { callback({...this.getState(), previous: null}); }
123
+ catch (error) { unsubscribe(); throw error; }
124
+ }
125
+ return unsubscribe;
126
+ }
127
+
128
+ dispose() {
129
+ this._window?.removeEventListener('resize', this._boundResize);
130
+ this._subscribers.clear();
131
+ }
132
+
133
+ viewport() {
134
+ return {width: this._window?.innerWidth ?? 0, height: this._window?.innerHeight ?? 0};
135
+ }
136
+
137
+ /** Возможность touch-ввода, не классификация телефона или планшета. */
138
+ detectTouchDevice() {
139
+ return !!this._window && (Number(this._window.navigator?.maxTouchPoints) > 0 || 'ontouchstart' in this._window);
140
+ }
141
+
142
+ /** Эвристики устройства не используются при выборе responsive-профилей. */
143
+ isMobileDevice() {
144
+ if (!this._window) return false;
145
+ const userAgent = this._window.navigator?.userAgent || '';
146
+ return /Android|iPhone|iPad|iPod/i.test(userAgent)
147
+ || (this.detectTouchDevice() && this.viewport().width < 768 && this._window.devicePixelRatio >= 2);
148
+ }
149
+
150
+ isTabletDevice() {
151
+ if (!this._window) return false;
152
+ const navigator = this._window.navigator || {};
153
+ const userAgent = (navigator.userAgent || '').toLowerCase();
154
+ const {width, height} = this.viewport();
155
+ const short = Math.min(width, height);
156
+ const long = Math.max(width, height);
157
+ return /ipad/.test(userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
158
+ || (/android/.test(userAgent) && !/mobile/.test(userAgent) && long > 800)
159
+ || (this.detectTouchDevice() && short >= 600 && short <= 1200 && long >= 800 && long <= 1600);
160
+ }
161
+
162
+ detectDevice() {
163
+ return this.isTabletDevice() ? 'tablet' : this.isMobileDevice() ? 'mobile' : 'desktop';
164
+ }
165
+ }
@@ -1,150 +1,150 @@
1
- const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
2
- const DEFAULT_ALLOWED_SVG_TAGS = new Set([
3
- "svg",
4
- "g",
5
- "path",
6
- "line",
7
- "polyline",
8
- "polygon",
9
- "circle",
10
- "ellipse",
11
- "rect",
12
- "defs",
13
- "symbol",
14
- "title",
15
- "use"
16
- ]);
17
- const DEFAULT_ALLOWED_SVG_ATTRS = new Set([
18
- "xmlns",
19
- "viewbox",
20
- "width",
21
- "height",
22
- "fill",
23
- "stroke",
24
- "stroke-width",
25
- "stroke-linecap",
26
- "stroke-linejoin",
27
- "stroke-miterlimit",
28
- "stroke-dasharray",
29
- "stroke-dashoffset",
30
- "opacity",
31
- "transform",
32
- "class",
33
- "x",
34
- "y",
35
- "x1",
36
- "y1",
37
- "x2",
38
- "y2",
39
- "cx",
40
- "cy",
41
- "r",
42
- "rx",
43
- "ry",
44
- "points",
45
- "d",
46
- "role",
47
- "focusable",
48
- "aria-hidden",
49
- "href",
50
- "xlink:href"
51
- ]);
52
-
53
- function escapeHtml(value) {
54
- return String(value)
55
- .replace(/&/g, "&amp;")
56
- .replace(/</g, "&lt;")
57
- .replace(/>/g, "&gt;")
58
- .replace(/"/g, "&quot;")
59
- .replace(/'/g, "&#39;");
60
- }
61
-
62
- function sanitizeSvgNode(node, allowedTags = DEFAULT_ALLOWED_SVG_TAGS, allowedAttrs = DEFAULT_ALLOWED_SVG_ATTRS) {
63
- if (!node) {
64
- return null;
65
- }
66
-
67
- if (node.nodeType === Node.TEXT_NODE) {
68
- return document.createTextNode(node.textContent || "");
69
- }
70
-
71
- if (node.nodeType !== Node.ELEMENT_NODE) {
72
- return null;
73
- }
74
-
75
- const tagName = node.tagName.toLowerCase();
76
- if (!allowedTags.has(tagName)) {
77
- return null;
78
- }
79
-
80
- const safeNode = document.createElementNS(SVG_NAMESPACE, tagName);
81
-
82
- Array.from(node.attributes || []).forEach((attr) => {
83
- const name = attr.name.toLowerCase();
84
- const attrValue = attr.value || "";
85
-
86
- if (!allowedAttrs.has(name) || name.startsWith("on")) {
87
- return;
88
- }
89
-
90
- if ((name === "href" || name === "xlink:href") && attrValue && !attrValue.startsWith("#")) {
91
- return;
92
- }
93
-
94
- safeNode.setAttribute(name, attrValue);
95
- });
96
-
97
- Array.from(node.childNodes || []).forEach((child) => {
98
- const safeChild = sanitizeSvgNode(child, allowedTags, allowedAttrs);
99
- if (safeChild) {
100
- safeNode.append(safeChild);
101
- }
102
- });
103
-
104
- return safeNode;
105
- }
106
-
107
- export function toSafeHtmlString(value, {
108
- allowedSvgTags = DEFAULT_ALLOWED_SVG_TAGS,
109
- allowedSvgAttrs = DEFAULT_ALLOWED_SVG_ATTRS
110
- } = {}) {
111
- if (value === null || value === undefined) {
112
- return "";
113
- }
114
-
115
- const content = String(value).trim();
116
- if (!content) {
117
- return "";
118
- }
119
-
120
- if (!content.includes("<")) {
121
- return escapeHtml(content);
122
- }
123
-
124
- const template = document.createElement("template");
125
- template.innerHTML = content;
126
-
127
- const sanitizedParts = Array.from(template.content.childNodes)
128
- .map((node) => sanitizeSvgNode(node, allowedSvgTags, allowedSvgAttrs))
129
- .filter(Boolean)
130
- .map((node) => {
131
- if (node.nodeType === Node.TEXT_NODE) {
132
- return escapeHtml(node.textContent || "");
133
- }
134
- if (node.nodeType === Node.ELEMENT_NODE) {
135
- return node.outerHTML;
136
- }
137
- return "";
138
- });
139
-
140
- const sanitized = sanitizedParts.join("");
141
- return sanitized || escapeHtml(content);
142
- }
143
-
144
- class Sanitize {
145
- static toSafeHtmlString(value, options) {
146
- return toSafeHtmlString(value, options);
147
- }
148
- }
149
-
150
- export default Sanitize;
1
+ const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
2
+ const DEFAULT_ALLOWED_SVG_TAGS = new Set([
3
+ "svg",
4
+ "g",
5
+ "path",
6
+ "line",
7
+ "polyline",
8
+ "polygon",
9
+ "circle",
10
+ "ellipse",
11
+ "rect",
12
+ "defs",
13
+ "symbol",
14
+ "title",
15
+ "use"
16
+ ]);
17
+ const DEFAULT_ALLOWED_SVG_ATTRS = new Set([
18
+ "xmlns",
19
+ "viewbox",
20
+ "width",
21
+ "height",
22
+ "fill",
23
+ "stroke",
24
+ "stroke-width",
25
+ "stroke-linecap",
26
+ "stroke-linejoin",
27
+ "stroke-miterlimit",
28
+ "stroke-dasharray",
29
+ "stroke-dashoffset",
30
+ "opacity",
31
+ "transform",
32
+ "class",
33
+ "x",
34
+ "y",
35
+ "x1",
36
+ "y1",
37
+ "x2",
38
+ "y2",
39
+ "cx",
40
+ "cy",
41
+ "r",
42
+ "rx",
43
+ "ry",
44
+ "points",
45
+ "d",
46
+ "role",
47
+ "focusable",
48
+ "aria-hidden",
49
+ "href",
50
+ "xlink:href"
51
+ ]);
52
+
53
+ function escapeHtml(value) {
54
+ return String(value)
55
+ .replace(/&/g, "&amp;")
56
+ .replace(/</g, "&lt;")
57
+ .replace(/>/g, "&gt;")
58
+ .replace(/"/g, "&quot;")
59
+ .replace(/'/g, "&#39;");
60
+ }
61
+
62
+ function sanitizeSvgNode(node, allowedTags = DEFAULT_ALLOWED_SVG_TAGS, allowedAttrs = DEFAULT_ALLOWED_SVG_ATTRS) {
63
+ if (!node) {
64
+ return null;
65
+ }
66
+
67
+ if (node.nodeType === Node.TEXT_NODE) {
68
+ return document.createTextNode(node.textContent || "");
69
+ }
70
+
71
+ if (node.nodeType !== Node.ELEMENT_NODE) {
72
+ return null;
73
+ }
74
+
75
+ const tagName = node.tagName.toLowerCase();
76
+ if (!allowedTags.has(tagName)) {
77
+ return null;
78
+ }
79
+
80
+ const safeNode = document.createElementNS(SVG_NAMESPACE, tagName);
81
+
82
+ Array.from(node.attributes || []).forEach((attr) => {
83
+ const name = attr.name.toLowerCase();
84
+ const attrValue = attr.value || "";
85
+
86
+ if (!allowedAttrs.has(name) || name.startsWith("on")) {
87
+ return;
88
+ }
89
+
90
+ if ((name === "href" || name === "xlink:href") && attrValue && !attrValue.startsWith("#")) {
91
+ return;
92
+ }
93
+
94
+ safeNode.setAttribute(name, attrValue);
95
+ });
96
+
97
+ Array.from(node.childNodes || []).forEach((child) => {
98
+ const safeChild = sanitizeSvgNode(child, allowedTags, allowedAttrs);
99
+ if (safeChild) {
100
+ safeNode.append(safeChild);
101
+ }
102
+ });
103
+
104
+ return safeNode;
105
+ }
106
+
107
+ export function toSafeHtmlString(value, {
108
+ allowedSvgTags = DEFAULT_ALLOWED_SVG_TAGS,
109
+ allowedSvgAttrs = DEFAULT_ALLOWED_SVG_ATTRS
110
+ } = {}) {
111
+ if (value === null || value === undefined) {
112
+ return "";
113
+ }
114
+
115
+ const content = String(value).trim();
116
+ if (!content) {
117
+ return "";
118
+ }
119
+
120
+ if (!content.includes("<")) {
121
+ return escapeHtml(content);
122
+ }
123
+
124
+ const template = document.createElement("template");
125
+ template.innerHTML = content;
126
+
127
+ const sanitizedParts = Array.from(template.content.childNodes)
128
+ .map((node) => sanitizeSvgNode(node, allowedSvgTags, allowedSvgAttrs))
129
+ .filter(Boolean)
130
+ .map((node) => {
131
+ if (node.nodeType === Node.TEXT_NODE) {
132
+ return escapeHtml(node.textContent || "");
133
+ }
134
+ if (node.nodeType === Node.ELEMENT_NODE) {
135
+ return node.outerHTML;
136
+ }
137
+ return "";
138
+ });
139
+
140
+ const sanitized = sanitizedParts.join("");
141
+ return sanitized || escapeHtml(content);
142
+ }
143
+
144
+ class Sanitize {
145
+ static toSafeHtmlString(value, options) {
146
+ return toSafeHtmlString(value, options);
147
+ }
148
+ }
149
+
150
+ export default Sanitize;