@vanduo-oss/framework 1.3.2 → 1.3.4
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/README.md +23 -4
- package/css/components/cards.css +8 -0
- package/css/components/draggable.css +3 -1
- package/css/components/dropdown.css +8 -0
- package/css/components/fab.css +14 -0
- package/css/components/modals.css +13 -0
- package/css/components/navbar.css +30 -0
- package/css/components/toast.css +8 -0
- package/css/components/tooltips.css +29 -0
- package/css/core/tokens.css +37 -0
- package/css/core/vd-aliases.css +13 -0
- package/css/effects/glass.css +154 -0
- package/css/vanduo.css +1 -0
- package/dist/build-info.json +3 -3
- package/dist/vanduo.cjs.js +206 -19
- package/dist/vanduo.cjs.js.map +3 -3
- package/dist/vanduo.cjs.min.js +4 -4
- package/dist/vanduo.cjs.min.js.map +4 -4
- package/dist/vanduo.css +264 -2
- package/dist/vanduo.css.map +1 -1
- package/dist/vanduo.esm.js +206 -19
- package/dist/vanduo.esm.js.map +3 -3
- package/dist/vanduo.esm.min.js +4 -4
- package/dist/vanduo.esm.min.js.map +4 -4
- package/dist/vanduo.js +206 -19
- package/dist/vanduo.js.map +3 -3
- package/dist/vanduo.min.css +2 -2
- package/dist/vanduo.min.css.map +1 -1
- package/dist/vanduo.min.js +4 -4
- package/dist/vanduo.min.js.map +4 -4
- package/js/components/draggable.js +103 -12
- package/js/components/glass.js +87 -0
- package/js/components/navbar.js +44 -3
- package/js/components/theme-customizer.js +22 -9
- package/js/components/theme-switcher.js +4 -0
- package/js/index.js +3 -0
- package/package.json +1 -1
package/dist/vanduo.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Vanduo v1.3.
|
|
1
|
+
/*! Vanduo v1.3.4 | Built: 2026-04-14T21:21:55.517Z | git:73e3db5 | development */
|
|
2
2
|
|
|
3
3
|
// js/utils/lifecycle.js
|
|
4
4
|
(function() {
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
// js/vanduo.js
|
|
108
108
|
(function() {
|
|
109
109
|
"use strict";
|
|
110
|
-
const VANDUO_VERSION = true ? "1.3.
|
|
110
|
+
const VANDUO_VERSION = true ? "1.3.4" : "0.0.0-dev";
|
|
111
111
|
const Vanduo2 = {
|
|
112
112
|
version: VANDUO_VERSION,
|
|
113
113
|
components: {},
|
|
@@ -2157,6 +2157,31 @@
|
|
|
2157
2157
|
this.initNavbar(navbar);
|
|
2158
2158
|
});
|
|
2159
2159
|
},
|
|
2160
|
+
/**
|
|
2161
|
+
* Initialize scroll-aware glass/transparent behaviour for a navbar.
|
|
2162
|
+
* Adds/removes `.vd-navbar-scrolled` when the page scrolls past a threshold.
|
|
2163
|
+
* Threshold: `data-scroll-threshold` attribute (px) or the navbar's own height.
|
|
2164
|
+
* @param {HTMLElement} navbar - Navbar element
|
|
2165
|
+
* @returns {Function|null} Cleanup function, or null if not applicable
|
|
2166
|
+
*/
|
|
2167
|
+
initScrollWatcher: function(navbar) {
|
|
2168
|
+
const isGlass = navbar.classList.contains("vd-navbar-glass");
|
|
2169
|
+
const isTransparent = navbar.classList.contains("vd-navbar-transparent");
|
|
2170
|
+
if (!isGlass && !isTransparent) {
|
|
2171
|
+
return null;
|
|
2172
|
+
}
|
|
2173
|
+
const getThreshold = () => {
|
|
2174
|
+
const attr = parseInt(navbar.dataset.scrollThreshold, 10);
|
|
2175
|
+
return isNaN(attr) ? navbar.offsetHeight || 60 : attr;
|
|
2176
|
+
};
|
|
2177
|
+
const onScroll = () => {
|
|
2178
|
+
const scrolled = window.scrollY > getThreshold();
|
|
2179
|
+
navbar.classList.toggle("vd-navbar-scrolled", scrolled);
|
|
2180
|
+
};
|
|
2181
|
+
onScroll();
|
|
2182
|
+
window.addEventListener("scroll", onScroll, { passive: true });
|
|
2183
|
+
return () => window.removeEventListener("scroll", onScroll);
|
|
2184
|
+
},
|
|
2160
2185
|
/**
|
|
2161
2186
|
* Initialize a single navbar
|
|
2162
2187
|
* @param {HTMLElement} navbar - Navbar element
|
|
@@ -2165,10 +2190,17 @@
|
|
|
2165
2190
|
const toggle = navbar.querySelector(".vd-navbar-toggle, .vd-navbar-burger");
|
|
2166
2191
|
const menu = navbar.querySelector(".vd-navbar-menu");
|
|
2167
2192
|
const overlay = navbar.querySelector(".vd-navbar-overlay") || this.createOverlay(navbar);
|
|
2193
|
+
const cleanupFunctions = [];
|
|
2194
|
+
const scrollWatcherCleanup = this.initScrollWatcher(navbar);
|
|
2195
|
+
if (scrollWatcherCleanup) {
|
|
2196
|
+
cleanupFunctions.push(scrollWatcherCleanup);
|
|
2197
|
+
}
|
|
2168
2198
|
if (!toggle || !menu) {
|
|
2199
|
+
if (cleanupFunctions.length) {
|
|
2200
|
+
this.instances.set(navbar, { toggle: null, menu: null, overlay: null, cleanup: cleanupFunctions });
|
|
2201
|
+
}
|
|
2169
2202
|
return;
|
|
2170
2203
|
}
|
|
2171
|
-
const cleanupFunctions = [];
|
|
2172
2204
|
const toggleClickHandler = (e) => {
|
|
2173
2205
|
e.preventDefault();
|
|
2174
2206
|
e.stopPropagation();
|
|
@@ -3806,6 +3838,7 @@
|
|
|
3806
3838
|
loadPreferences: function() {
|
|
3807
3839
|
this.state.theme = this.getStorageValue(this.STORAGE_KEYS.THEME, this.DEFAULTS.THEME);
|
|
3808
3840
|
this.state.primary = this.getStorageValue(this.STORAGE_KEYS.PRIMARY, this.getDefaultPrimary(this.state.theme));
|
|
3841
|
+
this._normalizeDefaultPrimaryIfStaleWithStoredTheme();
|
|
3809
3842
|
this.state.neutral = this.getStorageValue(this.STORAGE_KEYS.NEUTRAL, this.DEFAULTS.NEUTRAL);
|
|
3810
3843
|
this.state.radius = this.getStorageValue(this.STORAGE_KEYS.RADIUS, this.DEFAULTS.RADIUS);
|
|
3811
3844
|
this.state.font = this.getStorageValue(this.STORAGE_KEYS.FONT, this.DEFAULTS.FONT);
|
|
@@ -3891,12 +3924,10 @@
|
|
|
3891
3924
|
mode = this.DEFAULTS.THEME;
|
|
3892
3925
|
}
|
|
3893
3926
|
this._isApplying = true;
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
if (newDefault !== this.state.primary) {
|
|
3899
|
-
this.applyPrimary(newDefault);
|
|
3927
|
+
if (this.isUsingDefaultPrimary()) {
|
|
3928
|
+
const expected = this.getDefaultPrimary(mode);
|
|
3929
|
+
if (this.state.primary !== expected) {
|
|
3930
|
+
this.applyPrimary(expected);
|
|
3900
3931
|
}
|
|
3901
3932
|
}
|
|
3902
3933
|
this.state.theme = mode;
|
|
@@ -4138,6 +4169,20 @@
|
|
|
4138
4169
|
isUsingDefaultPrimary: function() {
|
|
4139
4170
|
return this.state.primary === this.DEFAULTS.PRIMARY_LIGHT || this.state.primary === this.DEFAULTS.PRIMARY_DARK;
|
|
4140
4171
|
},
|
|
4172
|
+
/**
|
|
4173
|
+
* When primary is still one of the auto-default palette keys (black/amber) but
|
|
4174
|
+
* localStorage was written under a different theme (or OS changed in system mode),
|
|
4175
|
+
* align in-memory state before applyAllPreferences runs — avoids amber+light / black+dark drift.
|
|
4176
|
+
*/
|
|
4177
|
+
_normalizeDefaultPrimaryIfStaleWithStoredTheme: function() {
|
|
4178
|
+
if (!this.isUsingDefaultPrimary()) {
|
|
4179
|
+
return;
|
|
4180
|
+
}
|
|
4181
|
+
const expected = this.getDefaultPrimary(this.state.theme);
|
|
4182
|
+
if (this.state.primary !== expected) {
|
|
4183
|
+
this.state.primary = expected;
|
|
4184
|
+
}
|
|
4185
|
+
},
|
|
4141
4186
|
bindEvents: function() {
|
|
4142
4187
|
if (this.elements.trigger) {
|
|
4143
4188
|
this.addListener(this.elements.trigger, "click", (e) => {
|
|
@@ -4376,6 +4421,9 @@
|
|
|
4376
4421
|
this._onMediaChange = (_e) => {
|
|
4377
4422
|
if (this.state.preference === "system") {
|
|
4378
4423
|
this.applyTheme();
|
|
4424
|
+
if (window.ThemeCustomizer && typeof window.ThemeCustomizer.applyTheme === "function" && !window.ThemeCustomizer._isApplying) {
|
|
4425
|
+
window.ThemeCustomizer.applyTheme("system");
|
|
4426
|
+
}
|
|
4379
4427
|
}
|
|
4380
4428
|
};
|
|
4381
4429
|
this._mediaQuery.addEventListener("change", this._onMediaChange);
|
|
@@ -5607,6 +5655,8 @@
|
|
|
5607
5655
|
touchState: null,
|
|
5608
5656
|
// Feedback element
|
|
5609
5657
|
feedbackElement: null,
|
|
5658
|
+
// Shared selector used by init and touch reorder
|
|
5659
|
+
containerSelector: ".vd-draggable-container, .vd-draggable-container-vertical",
|
|
5610
5660
|
/**
|
|
5611
5661
|
* Initialize draggable components
|
|
5612
5662
|
*/
|
|
@@ -5618,7 +5668,7 @@
|
|
|
5618
5668
|
}
|
|
5619
5669
|
this.initDraggable(element);
|
|
5620
5670
|
});
|
|
5621
|
-
const containers = document.querySelectorAll(
|
|
5671
|
+
const containers = document.querySelectorAll(this.containerSelector);
|
|
5622
5672
|
containers.forEach((container) => {
|
|
5623
5673
|
if (!this.instances.has(container)) {
|
|
5624
5674
|
this.initContainer(container);
|
|
@@ -5862,10 +5912,16 @@
|
|
|
5862
5912
|
*/
|
|
5863
5913
|
handleTouchStart: function(e, element) {
|
|
5864
5914
|
const touch = e.touches[0];
|
|
5915
|
+
const rect = element.getBoundingClientRect();
|
|
5865
5916
|
this.touchState = {
|
|
5866
5917
|
element,
|
|
5867
5918
|
startX: touch.clientX,
|
|
5868
5919
|
startY: touch.clientY,
|
|
5920
|
+
lastX: touch.clientX,
|
|
5921
|
+
lastY: touch.clientY,
|
|
5922
|
+
// Keep preview anchored to the original grab point.
|
|
5923
|
+
offsetX: touch.clientX - rect.left,
|
|
5924
|
+
offsetY: touch.clientY - rect.top,
|
|
5869
5925
|
startTime: Date.now(),
|
|
5870
5926
|
isDragging: false
|
|
5871
5927
|
};
|
|
@@ -5878,6 +5934,8 @@
|
|
|
5878
5934
|
handleTouchMove: function(e, element) {
|
|
5879
5935
|
if (!this.touchState) return;
|
|
5880
5936
|
const touch = e.touches[0];
|
|
5937
|
+
this.touchState.lastX = touch.clientX;
|
|
5938
|
+
this.touchState.lastY = touch.clientY;
|
|
5881
5939
|
const deltaX = touch.clientX - this.touchState.startX;
|
|
5882
5940
|
const deltaY = touch.clientY - this.touchState.startY;
|
|
5883
5941
|
if (Math.abs(deltaX) > 10 || Math.abs(deltaY) > 10) {
|
|
@@ -5890,7 +5948,10 @@
|
|
|
5890
5948
|
element,
|
|
5891
5949
|
initialPosition: { x: this.touchState.startX, y: this.touchState.startY },
|
|
5892
5950
|
initialBounds: element.getBoundingClientRect(),
|
|
5893
|
-
data: this.getData(element)
|
|
5951
|
+
data: this.getData(element),
|
|
5952
|
+
// Preserve where inside the element the drag started for accurate ghost positioning.
|
|
5953
|
+
offsetX: this.touchState.offsetX,
|
|
5954
|
+
offsetY: this.touchState.offsetY
|
|
5894
5955
|
};
|
|
5895
5956
|
element.dispatchEvent(new CustomEvent("draggable:start", {
|
|
5896
5957
|
bubbles: true,
|
|
@@ -5912,7 +5973,8 @@
|
|
|
5912
5973
|
delta: { x: deltaX, y: deltaY }
|
|
5913
5974
|
}
|
|
5914
5975
|
}));
|
|
5915
|
-
|
|
5976
|
+
this.updateTouchDropZone(touch.clientX, touch.clientY);
|
|
5977
|
+
const container = element.closest(this.containerSelector);
|
|
5916
5978
|
if (container && container.contains(element)) {
|
|
5917
5979
|
this.handleReorder(container, element, touch.clientX, touch.clientY);
|
|
5918
5980
|
}
|
|
@@ -5927,6 +5989,17 @@
|
|
|
5927
5989
|
handleTouchEnd: function(e, element) {
|
|
5928
5990
|
if (this.touchState && this.touchState.isDragging) {
|
|
5929
5991
|
if (e.cancelable) e.preventDefault();
|
|
5992
|
+
const endTouch = e.changedTouches?.[0];
|
|
5993
|
+
const endPosition = {
|
|
5994
|
+
x: endTouch?.clientX ?? this.touchState.lastX ?? this.touchState.startX,
|
|
5995
|
+
y: endTouch?.clientY ?? this.touchState.lastY ?? this.touchState.startY
|
|
5996
|
+
};
|
|
5997
|
+
const dropZone = this.resolveDropZoneAtPoint(endPosition.x, endPosition.y) || this.touchState.overZone;
|
|
5998
|
+
if (dropZone) {
|
|
5999
|
+
this.dispatchDrop(dropZone, endPosition);
|
|
6000
|
+
} else if (this.touchState.overZone) {
|
|
6001
|
+
this.touchState.overZone.classList.remove("is-drag-over");
|
|
6002
|
+
}
|
|
5930
6003
|
element.classList.remove("is-dragging");
|
|
5931
6004
|
element.classList.add("is-dropped");
|
|
5932
6005
|
element.setAttribute("aria-grabbed", "false");
|
|
@@ -5934,7 +6007,6 @@
|
|
|
5934
6007
|
if (this.feedbackElement) {
|
|
5935
6008
|
this.feedbackElement.classList.add("hidden");
|
|
5936
6009
|
}
|
|
5937
|
-
const endTouch = e.changedTouches[0];
|
|
5938
6010
|
const data = this.currentDrag?.data || this.getData(element);
|
|
5939
6011
|
const startX = this.touchState?.startX || 0;
|
|
5940
6012
|
const startY = this.touchState?.startY || 0;
|
|
@@ -5943,10 +6015,10 @@
|
|
|
5943
6015
|
detail: {
|
|
5944
6016
|
element,
|
|
5945
6017
|
data,
|
|
5946
|
-
position:
|
|
6018
|
+
position: endPosition,
|
|
5947
6019
|
delta: {
|
|
5948
|
-
x:
|
|
5949
|
-
y:
|
|
6020
|
+
x: endPosition.x - startX,
|
|
6021
|
+
y: endPosition.y - startY
|
|
5950
6022
|
}
|
|
5951
6023
|
}
|
|
5952
6024
|
}));
|
|
@@ -5987,6 +6059,58 @@
|
|
|
5987
6059
|
*/
|
|
5988
6060
|
handleDrop: function(e, zone) {
|
|
5989
6061
|
e.preventDefault();
|
|
6062
|
+
this.dispatchDrop(zone, { x: e.clientX, y: e.clientY });
|
|
6063
|
+
},
|
|
6064
|
+
/**
|
|
6065
|
+
* Resolve a drop zone from viewport coordinates
|
|
6066
|
+
* @param {number} x
|
|
6067
|
+
* @param {number} y
|
|
6068
|
+
* @returns {HTMLElement|null}
|
|
6069
|
+
*/
|
|
6070
|
+
resolveDropZoneAtPoint: function(x, y) {
|
|
6071
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
|
|
6072
|
+
if (typeof document.elementsFromPoint === "function") {
|
|
6073
|
+
const stacked = document.elementsFromPoint(x, y);
|
|
6074
|
+
for (const element of stacked) {
|
|
6075
|
+
const zone = element.closest(".vd-drop-zone");
|
|
6076
|
+
if (zone) return zone;
|
|
6077
|
+
}
|
|
6078
|
+
}
|
|
6079
|
+
const target = document.elementFromPoint(x, y);
|
|
6080
|
+
const targetZone = target ? target.closest(".vd-drop-zone") : null;
|
|
6081
|
+
if (targetZone) return targetZone;
|
|
6082
|
+
const zones = document.querySelectorAll(".vd-drop-zone");
|
|
6083
|
+
for (const zone of zones) {
|
|
6084
|
+
const rect = zone.getBoundingClientRect();
|
|
6085
|
+
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
|
|
6086
|
+
return zone;
|
|
6087
|
+
}
|
|
6088
|
+
}
|
|
6089
|
+
return null;
|
|
6090
|
+
},
|
|
6091
|
+
/**
|
|
6092
|
+
* Track and update active drop-zone hover state on touch devices
|
|
6093
|
+
* @param {number} x
|
|
6094
|
+
* @param {number} y
|
|
6095
|
+
*/
|
|
6096
|
+
updateTouchDropZone: function(x, y) {
|
|
6097
|
+
if (!this.touchState) return;
|
|
6098
|
+
const nextZone = this.resolveDropZoneAtPoint(x, y);
|
|
6099
|
+
const prevZone = this.touchState.overZone || null;
|
|
6100
|
+
if (prevZone && prevZone !== nextZone) {
|
|
6101
|
+
prevZone.classList.remove("is-drag-over");
|
|
6102
|
+
}
|
|
6103
|
+
if (nextZone && nextZone !== prevZone) {
|
|
6104
|
+
nextZone.classList.add("is-drag-over");
|
|
6105
|
+
}
|
|
6106
|
+
this.touchState.overZone = nextZone || null;
|
|
6107
|
+
},
|
|
6108
|
+
/**
|
|
6109
|
+
* Dispatch a normalized drop event for mouse and touch flows
|
|
6110
|
+
* @param {HTMLElement} zone
|
|
6111
|
+
* @param {{x:number, y:number}} position
|
|
6112
|
+
*/
|
|
6113
|
+
dispatchDrop: function(zone, position) {
|
|
5990
6114
|
zone.classList.remove("is-drag-over");
|
|
5991
6115
|
zone.dispatchEvent(new CustomEvent("draggable:drop", {
|
|
5992
6116
|
bubbles: true,
|
|
@@ -5994,7 +6118,7 @@
|
|
|
5994
6118
|
zone,
|
|
5995
6119
|
element: this.currentDrag?.element,
|
|
5996
6120
|
data: this.currentDrag?.data,
|
|
5997
|
-
position
|
|
6121
|
+
position
|
|
5998
6122
|
}
|
|
5999
6123
|
}));
|
|
6000
6124
|
},
|
|
@@ -6095,9 +6219,11 @@
|
|
|
6095
6219
|
this.feedbackElement.innerHTML = "";
|
|
6096
6220
|
const clone = this.currentDrag.element.cloneNode(true);
|
|
6097
6221
|
this.feedbackElement.appendChild(clone);
|
|
6222
|
+
const offsetX = this.currentDrag.offsetX ?? 20;
|
|
6223
|
+
const offsetY = this.currentDrag.offsetY ?? 20;
|
|
6098
6224
|
Object.assign(this.feedbackElement.style, {
|
|
6099
|
-
left: x -
|
|
6100
|
-
top: y -
|
|
6225
|
+
left: x - offsetX + "px",
|
|
6226
|
+
top: y - offsetY + "px",
|
|
6101
6227
|
width: rect.width + "px",
|
|
6102
6228
|
height: rect.height + "px"
|
|
6103
6229
|
});
|
|
@@ -6385,6 +6511,67 @@
|
|
|
6385
6511
|
window.VanduoLazyLoad = VanduoLazyLoad;
|
|
6386
6512
|
})();
|
|
6387
6513
|
|
|
6514
|
+
// js/components/glass.js
|
|
6515
|
+
(function() {
|
|
6516
|
+
"use strict";
|
|
6517
|
+
const GlassScroll = {
|
|
6518
|
+
/** @type {Map<Element, IntersectionObserver>} */
|
|
6519
|
+
observers: /* @__PURE__ */ new Map(),
|
|
6520
|
+
init: function() {
|
|
6521
|
+
document.querySelectorAll("[data-glass-scroll]").forEach((el) => {
|
|
6522
|
+
if (this.observers.has(el)) return;
|
|
6523
|
+
this.initElement(el);
|
|
6524
|
+
});
|
|
6525
|
+
},
|
|
6526
|
+
/**
|
|
6527
|
+
* Wire up a single scroll-activated glass element.
|
|
6528
|
+
* @param {HTMLElement} el
|
|
6529
|
+
*/
|
|
6530
|
+
initElement: function(el) {
|
|
6531
|
+
const sentinelSelector = el.dataset.glassSentinel;
|
|
6532
|
+
let sentinel;
|
|
6533
|
+
if (sentinelSelector) {
|
|
6534
|
+
sentinel = document.querySelector(sentinelSelector);
|
|
6535
|
+
}
|
|
6536
|
+
if (!sentinel) {
|
|
6537
|
+
sentinel = el.previousElementSibling;
|
|
6538
|
+
}
|
|
6539
|
+
if (!sentinel) {
|
|
6540
|
+
el.classList.add("is-glass-active");
|
|
6541
|
+
return;
|
|
6542
|
+
}
|
|
6543
|
+
const observer = new IntersectionObserver(
|
|
6544
|
+
(entries) => {
|
|
6545
|
+
entries.forEach((entry) => {
|
|
6546
|
+
el.classList.toggle("is-glass-active", !entry.isIntersecting);
|
|
6547
|
+
});
|
|
6548
|
+
},
|
|
6549
|
+
{ threshold: 0, rootMargin: "0px" }
|
|
6550
|
+
);
|
|
6551
|
+
observer.observe(sentinel);
|
|
6552
|
+
this.observers.set(el, observer);
|
|
6553
|
+
},
|
|
6554
|
+
/**
|
|
6555
|
+
* Disconnect and remove a single element's observer.
|
|
6556
|
+
* @param {HTMLElement} el
|
|
6557
|
+
*/
|
|
6558
|
+
destroy: function(el) {
|
|
6559
|
+
const observer = this.observers.get(el);
|
|
6560
|
+
if (observer) {
|
|
6561
|
+
observer.disconnect();
|
|
6562
|
+
this.observers.delete(el);
|
|
6563
|
+
}
|
|
6564
|
+
},
|
|
6565
|
+
destroyAll: function() {
|
|
6566
|
+
this.observers.forEach((observer, el) => this.destroy(el));
|
|
6567
|
+
}
|
|
6568
|
+
};
|
|
6569
|
+
if (typeof window.Vanduo !== "undefined") {
|
|
6570
|
+
window.Vanduo.register("glassScroll", GlassScroll);
|
|
6571
|
+
}
|
|
6572
|
+
window.VanduoGlassScroll = GlassScroll;
|
|
6573
|
+
})();
|
|
6574
|
+
|
|
6388
6575
|
// js/components/flow.js
|
|
6389
6576
|
(function() {
|
|
6390
6577
|
"use strict";
|