app-studio 0.6.16 → 0.6.17
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 +1 -1
- package/dist/app-studio.cjs.development.js +297 -0
- package/dist/app-studio.cjs.development.js.map +1 -1
- package/dist/app-studio.cjs.production.min.js +1 -1
- package/dist/app-studio.esm.js +297 -1
- package/dist/app-studio.esm.js.map +1 -1
- package/dist/app-studio.umd.development.js +297 -0
- package/dist/app-studio.umd.development.js.map +1 -1
- package/dist/app-studio.umd.production.min.js +1 -1
- package/dist/hooks/useElementPosition.d.ts +56 -0
- package/dist/index.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -802,7 +802,7 @@ Explore our comprehensive documentation to learn more about App-Studio:
|
|
|
802
802
|
* **Core:** `Element`
|
|
803
803
|
* **Components:** `View`, `Horizontal`, `Vertical`, `HorizontalResponsive`, `VerticalResponsive`, `Scroll`, `SafeArea`, `Div`, `Span`, `Text`, `Image`, `ImageBackground`, `Form`, `Input`, `Button`, `Skeleton`.
|
|
804
804
|
* **Animation:** `Animation` object with functions like `fadeIn`, `slideInLeft`, `pulse`, etc.
|
|
805
|
-
* **Hooks:** `useActive`, `useClickOutside`, `useFocus`, `useHover`, `useInView`, `useKeyPress`, `useMount`, `useOnScreen`, `useResponsive`, `useScroll`, `useWindowSize`.
|
|
805
|
+
* **Hooks:** `useActive`, `useClickOutside`, `useElementPosition`, `useFocus`, `useHover`, `useInView`, `useKeyPress`, `useMount`, `useOnScreen`, `useResponsive`, `useScroll`, `useWindowSize`.
|
|
806
806
|
* **Providers:** `ThemeProvider`, `ResponsiveProvider`, `AnalyticsProvider`, `WindowSizeProvider`.
|
|
807
807
|
* **Context Hooks:** `useTheme`, `useResponsiveContext`, `useAnalytics`, `useWindowSize`.
|
|
808
808
|
* **Utilities:** `colors`, `Typography`, `Shadows`.
|
|
@@ -3957,6 +3957,302 @@ function useClickOutside() {
|
|
|
3957
3957
|
return [ref, clickedOutside];
|
|
3958
3958
|
}
|
|
3959
3959
|
|
|
3960
|
+
function useElementPosition(options) {
|
|
3961
|
+
if (options === void 0) {
|
|
3962
|
+
options = {};
|
|
3963
|
+
}
|
|
3964
|
+
const {
|
|
3965
|
+
trackChanges = true,
|
|
3966
|
+
throttleMs = 16,
|
|
3967
|
+
// ~60fps
|
|
3968
|
+
includeScroll = true,
|
|
3969
|
+
includeResize = true,
|
|
3970
|
+
offset = {
|
|
3971
|
+
x: 0,
|
|
3972
|
+
y: 0
|
|
3973
|
+
},
|
|
3974
|
+
scrollContainer = null,
|
|
3975
|
+
useFixedPositioning = false
|
|
3976
|
+
} = options;
|
|
3977
|
+
const ref = React.useRef(null);
|
|
3978
|
+
const [position, setPosition] = React.useState(null);
|
|
3979
|
+
const throttleRef = React.useRef(null);
|
|
3980
|
+
// Calculate element position
|
|
3981
|
+
const calculatePosition = React.useCallback(() => {
|
|
3982
|
+
const element = ref.current;
|
|
3983
|
+
if (!element) return null;
|
|
3984
|
+
const rect = element.getBoundingClientRect();
|
|
3985
|
+
if (useFixedPositioning) {
|
|
3986
|
+
// For fixed positioning, use viewport coordinates
|
|
3987
|
+
return {
|
|
3988
|
+
x: rect.left + offset.x,
|
|
3989
|
+
y: rect.top + offset.y,
|
|
3990
|
+
width: rect.width,
|
|
3991
|
+
height: rect.height,
|
|
3992
|
+
top: rect.top + offset.y,
|
|
3993
|
+
left: rect.left + offset.x,
|
|
3994
|
+
right: rect.right + offset.x,
|
|
3995
|
+
bottom: rect.bottom + offset.y
|
|
3996
|
+
};
|
|
3997
|
+
}
|
|
3998
|
+
// For absolute positioning, calculate relative to document or container
|
|
3999
|
+
let scrollX = 0;
|
|
4000
|
+
let scrollY = 0;
|
|
4001
|
+
let containerRect = {
|
|
4002
|
+
left: 0,
|
|
4003
|
+
top: 0
|
|
4004
|
+
};
|
|
4005
|
+
if (scrollContainer?.current) {
|
|
4006
|
+
// Position relative to scrollable container
|
|
4007
|
+
const container = scrollContainer.current;
|
|
4008
|
+
const containerBounds = container.getBoundingClientRect();
|
|
4009
|
+
containerRect = {
|
|
4010
|
+
left: containerBounds.left,
|
|
4011
|
+
top: containerBounds.top
|
|
4012
|
+
};
|
|
4013
|
+
scrollX = container.scrollLeft;
|
|
4014
|
+
scrollY = container.scrollTop;
|
|
4015
|
+
} else {
|
|
4016
|
+
// Position relative to document
|
|
4017
|
+
scrollX = window.pageXOffset || document.documentElement.scrollLeft;
|
|
4018
|
+
scrollY = window.pageYOffset || document.documentElement.scrollTop;
|
|
4019
|
+
}
|
|
4020
|
+
return {
|
|
4021
|
+
x: rect.left - containerRect.left + scrollX + offset.x,
|
|
4022
|
+
y: rect.top - containerRect.top + scrollY + offset.y,
|
|
4023
|
+
width: rect.width,
|
|
4024
|
+
height: rect.height,
|
|
4025
|
+
top: rect.top - containerRect.top + scrollY + offset.y,
|
|
4026
|
+
left: rect.left - containerRect.left + scrollX + offset.x,
|
|
4027
|
+
right: rect.right - containerRect.left + scrollX + offset.x,
|
|
4028
|
+
bottom: rect.bottom - containerRect.top + scrollY + offset.y
|
|
4029
|
+
};
|
|
4030
|
+
}, [offset.x, offset.y, scrollContainer, useFixedPositioning]);
|
|
4031
|
+
// Throttled position update
|
|
4032
|
+
const updatePosition = React.useCallback(() => {
|
|
4033
|
+
if (throttleRef.current) {
|
|
4034
|
+
clearTimeout(throttleRef.current);
|
|
4035
|
+
}
|
|
4036
|
+
throttleRef.current = setTimeout(() => {
|
|
4037
|
+
const newPosition = calculatePosition();
|
|
4038
|
+
setPosition(newPosition);
|
|
4039
|
+
}, throttleMs);
|
|
4040
|
+
}, [calculatePosition, throttleMs]);
|
|
4041
|
+
// Calculate available space on all sides
|
|
4042
|
+
const getContainerBounds = () => {
|
|
4043
|
+
if (scrollContainer?.current) {
|
|
4044
|
+
const container = scrollContainer.current;
|
|
4045
|
+
const containerRect = container.getBoundingClientRect();
|
|
4046
|
+
return {
|
|
4047
|
+
width: container.clientWidth,
|
|
4048
|
+
height: container.clientHeight,
|
|
4049
|
+
scrollX: container.scrollLeft,
|
|
4050
|
+
scrollY: container.scrollTop,
|
|
4051
|
+
offsetX: useFixedPositioning ? containerRect.left : 0,
|
|
4052
|
+
offsetY: useFixedPositioning ? containerRect.top : 0,
|
|
4053
|
+
visibleTop: useFixedPositioning ? containerRect.top : container.scrollTop,
|
|
4054
|
+
visibleBottom: useFixedPositioning ? containerRect.bottom : container.scrollTop + container.clientHeight,
|
|
4055
|
+
visibleLeft: useFixedPositioning ? containerRect.left : container.scrollLeft,
|
|
4056
|
+
visibleRight: useFixedPositioning ? containerRect.right : container.scrollLeft + container.clientWidth
|
|
4057
|
+
};
|
|
4058
|
+
} else {
|
|
4059
|
+
const scrollX = window.pageXOffset;
|
|
4060
|
+
const scrollY = window.pageYOffset;
|
|
4061
|
+
return {
|
|
4062
|
+
width: window.innerWidth,
|
|
4063
|
+
height: window.innerHeight,
|
|
4064
|
+
scrollX,
|
|
4065
|
+
scrollY,
|
|
4066
|
+
offsetX: 0,
|
|
4067
|
+
offsetY: 0,
|
|
4068
|
+
visibleTop: scrollY,
|
|
4069
|
+
visibleBottom: scrollY + window.innerHeight,
|
|
4070
|
+
visibleLeft: scrollX,
|
|
4071
|
+
visibleRight: scrollX + window.innerWidth
|
|
4072
|
+
};
|
|
4073
|
+
}
|
|
4074
|
+
};
|
|
4075
|
+
// Helper methods for positioning overlays
|
|
4076
|
+
const helpers = {
|
|
4077
|
+
getAvailableSpace: () => {
|
|
4078
|
+
if (!position) return {
|
|
4079
|
+
top: 0,
|
|
4080
|
+
right: 0,
|
|
4081
|
+
bottom: 0,
|
|
4082
|
+
left: 0
|
|
4083
|
+
};
|
|
4084
|
+
const bounds = getContainerBounds();
|
|
4085
|
+
const elementTop = useFixedPositioning ? position.top + bounds.offsetY : position.top;
|
|
4086
|
+
const elementBottom = useFixedPositioning ? position.bottom + bounds.offsetY : position.bottom;
|
|
4087
|
+
const elementLeft = useFixedPositioning ? position.left + bounds.offsetX : position.left;
|
|
4088
|
+
const elementRight = useFixedPositioning ? position.right + bounds.offsetX : position.right;
|
|
4089
|
+
return {
|
|
4090
|
+
top: elementTop - bounds.visibleTop,
|
|
4091
|
+
right: bounds.visibleRight - elementRight,
|
|
4092
|
+
bottom: bounds.visibleBottom - elementBottom,
|
|
4093
|
+
left: elementLeft - bounds.visibleLeft
|
|
4094
|
+
};
|
|
4095
|
+
},
|
|
4096
|
+
getOptimalPosition: function (overlayWidth, overlayHeight, offset) {
|
|
4097
|
+
if (offset === void 0) {
|
|
4098
|
+
offset = 8;
|
|
4099
|
+
}
|
|
4100
|
+
if (!position) {
|
|
4101
|
+
return {
|
|
4102
|
+
x: 0,
|
|
4103
|
+
y: 0,
|
|
4104
|
+
placement: 'bottom',
|
|
4105
|
+
availableSpace: {
|
|
4106
|
+
top: 0,
|
|
4107
|
+
right: 0,
|
|
4108
|
+
bottom: 0,
|
|
4109
|
+
left: 0
|
|
4110
|
+
}
|
|
4111
|
+
};
|
|
4112
|
+
}
|
|
4113
|
+
const bounds = getContainerBounds();
|
|
4114
|
+
const availableSpace = helpers.getAvailableSpace();
|
|
4115
|
+
const elementTop = useFixedPositioning ? position.top + bounds.offsetY : position.top;
|
|
4116
|
+
const elementBottom = useFixedPositioning ? position.bottom + bounds.offsetY : position.bottom;
|
|
4117
|
+
const elementLeft = useFixedPositioning ? position.left + bounds.offsetX : position.left;
|
|
4118
|
+
const elementRight = useFixedPositioning ? position.right + bounds.offsetX : position.right;
|
|
4119
|
+
// Calculate which placement has the most space
|
|
4120
|
+
const placements = [{
|
|
4121
|
+
placement: 'bottom',
|
|
4122
|
+
space: availableSpace.bottom,
|
|
4123
|
+
fits: availableSpace.bottom >= overlayHeight,
|
|
4124
|
+
x: elementLeft + position.width / 2 - overlayWidth / 2,
|
|
4125
|
+
y: elementBottom + offset
|
|
4126
|
+
}, {
|
|
4127
|
+
placement: 'top',
|
|
4128
|
+
space: availableSpace.top,
|
|
4129
|
+
fits: availableSpace.top >= overlayHeight,
|
|
4130
|
+
x: elementLeft + position.width / 2 - overlayWidth / 2,
|
|
4131
|
+
y: elementTop - overlayHeight - offset
|
|
4132
|
+
}, {
|
|
4133
|
+
placement: 'right',
|
|
4134
|
+
space: availableSpace.right,
|
|
4135
|
+
fits: availableSpace.right >= overlayWidth,
|
|
4136
|
+
x: elementRight + offset,
|
|
4137
|
+
y: elementTop + position.height / 2 - overlayHeight / 2
|
|
4138
|
+
}, {
|
|
4139
|
+
placement: 'left',
|
|
4140
|
+
space: availableSpace.left,
|
|
4141
|
+
fits: availableSpace.left >= overlayWidth,
|
|
4142
|
+
x: elementLeft - overlayWidth - offset,
|
|
4143
|
+
y: elementTop + position.height / 2 - overlayHeight / 2
|
|
4144
|
+
}];
|
|
4145
|
+
// First try to find a placement that fits completely
|
|
4146
|
+
const fittingPlacement = placements.find(p => p.fits);
|
|
4147
|
+
if (fittingPlacement) {
|
|
4148
|
+
return {
|
|
4149
|
+
x: fittingPlacement.x,
|
|
4150
|
+
y: fittingPlacement.y,
|
|
4151
|
+
placement: fittingPlacement.placement,
|
|
4152
|
+
availableSpace
|
|
4153
|
+
};
|
|
4154
|
+
}
|
|
4155
|
+
// If nothing fits completely, choose the placement with the most space
|
|
4156
|
+
const bestPlacement = placements.reduce((best, current) => current.space > best.space ? current : best);
|
|
4157
|
+
return {
|
|
4158
|
+
x: bestPlacement.x,
|
|
4159
|
+
y: bestPlacement.y,
|
|
4160
|
+
placement: bestPlacement.placement,
|
|
4161
|
+
availableSpace
|
|
4162
|
+
};
|
|
4163
|
+
},
|
|
4164
|
+
getContextMenuPosition: (menuWidth, menuHeight) => {
|
|
4165
|
+
return helpers.getOptimalPosition(menuWidth, menuHeight, 4);
|
|
4166
|
+
},
|
|
4167
|
+
getTooltipPosition: function (tooltipWidth, tooltipHeight, offset) {
|
|
4168
|
+
if (offset === void 0) {
|
|
4169
|
+
offset = 8;
|
|
4170
|
+
}
|
|
4171
|
+
return helpers.getOptimalPosition(tooltipWidth, tooltipHeight, offset);
|
|
4172
|
+
},
|
|
4173
|
+
getDropdownPosition: (dropdownWidth, dropdownHeight) => {
|
|
4174
|
+
return helpers.getOptimalPosition(dropdownWidth, dropdownHeight, 4);
|
|
4175
|
+
},
|
|
4176
|
+
isInViewport: () => {
|
|
4177
|
+
if (!position) return false;
|
|
4178
|
+
const viewportWidth = window.innerWidth;
|
|
4179
|
+
const viewportHeight = window.innerHeight;
|
|
4180
|
+
const scrollX = window.pageXOffset;
|
|
4181
|
+
const scrollY = window.pageYOffset;
|
|
4182
|
+
return position.right > scrollX && position.left < viewportWidth + scrollX && position.bottom > scrollY && position.top < viewportHeight + scrollY;
|
|
4183
|
+
},
|
|
4184
|
+
getViewportOverflow: () => {
|
|
4185
|
+
if (!position) return {
|
|
4186
|
+
top: 0,
|
|
4187
|
+
right: 0,
|
|
4188
|
+
bottom: 0,
|
|
4189
|
+
left: 0
|
|
4190
|
+
};
|
|
4191
|
+
const viewportWidth = window.innerWidth;
|
|
4192
|
+
const viewportHeight = window.innerHeight;
|
|
4193
|
+
const scrollX = window.pageXOffset;
|
|
4194
|
+
const scrollY = window.pageYOffset;
|
|
4195
|
+
return {
|
|
4196
|
+
top: Math.max(0, scrollY - position.top),
|
|
4197
|
+
right: Math.max(0, position.right - (viewportWidth + scrollX)),
|
|
4198
|
+
bottom: Math.max(0, position.bottom - (viewportHeight + scrollY)),
|
|
4199
|
+
left: Math.max(0, scrollX - position.left)
|
|
4200
|
+
};
|
|
4201
|
+
}
|
|
4202
|
+
};
|
|
4203
|
+
// Set up event listeners
|
|
4204
|
+
React.useEffect(() => {
|
|
4205
|
+
if (!trackChanges) return;
|
|
4206
|
+
const handleUpdate = () => updatePosition();
|
|
4207
|
+
// Initial position calculation
|
|
4208
|
+
updatePosition();
|
|
4209
|
+
// Add event listeners
|
|
4210
|
+
if (includeResize) {
|
|
4211
|
+
window.addEventListener('resize', handleUpdate);
|
|
4212
|
+
}
|
|
4213
|
+
if (includeScroll) {
|
|
4214
|
+
if (scrollContainer?.current) {
|
|
4215
|
+
// Listen to scroll events on the container
|
|
4216
|
+
scrollContainer.current.addEventListener('scroll', handleUpdate, {
|
|
4217
|
+
passive: true
|
|
4218
|
+
});
|
|
4219
|
+
} else {
|
|
4220
|
+
// Listen to window scroll events
|
|
4221
|
+
window.addEventListener('scroll', handleUpdate, {
|
|
4222
|
+
passive: true
|
|
4223
|
+
});
|
|
4224
|
+
}
|
|
4225
|
+
}
|
|
4226
|
+
return () => {
|
|
4227
|
+
if (throttleRef.current) {
|
|
4228
|
+
clearTimeout(throttleRef.current);
|
|
4229
|
+
}
|
|
4230
|
+
if (includeResize) {
|
|
4231
|
+
window.removeEventListener('resize', handleUpdate);
|
|
4232
|
+
}
|
|
4233
|
+
if (includeScroll) {
|
|
4234
|
+
if (scrollContainer?.current) {
|
|
4235
|
+
scrollContainer.current.removeEventListener('scroll', handleUpdate);
|
|
4236
|
+
} else {
|
|
4237
|
+
window.removeEventListener('scroll', handleUpdate);
|
|
4238
|
+
}
|
|
4239
|
+
}
|
|
4240
|
+
};
|
|
4241
|
+
}, [trackChanges, includeResize, includeScroll, updatePosition, scrollContainer]);
|
|
4242
|
+
// Update position when element changes
|
|
4243
|
+
React.useEffect(() => {
|
|
4244
|
+
if (ref.current && trackChanges) {
|
|
4245
|
+
updatePosition();
|
|
4246
|
+
}
|
|
4247
|
+
}, [ref.current, updatePosition, trackChanges]);
|
|
4248
|
+
return {
|
|
4249
|
+
ref,
|
|
4250
|
+
position,
|
|
4251
|
+
helpers,
|
|
4252
|
+
updatePosition
|
|
4253
|
+
};
|
|
4254
|
+
}
|
|
4255
|
+
|
|
3960
4256
|
function useFocus() {
|
|
3961
4257
|
const [focused, setFocused] = React.useState(false);
|
|
3962
4258
|
const ref = React.useRef(null);
|
|
@@ -4366,6 +4662,7 @@ exports.isSSR = isSSR;
|
|
|
4366
4662
|
exports.useActive = useActive;
|
|
4367
4663
|
exports.useAnalytics = useAnalytics;
|
|
4368
4664
|
exports.useClickOutside = useClickOutside;
|
|
4665
|
+
exports.useElementPosition = useElementPosition;
|
|
4369
4666
|
exports.useFocus = useFocus;
|
|
4370
4667
|
exports.useHover = useHover;
|
|
4371
4668
|
exports.useInView = useInView;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-studio.cjs.development.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"app-studio.cjs.development.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|