app-studio 0.6.54 → 0.6.56
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 +2 -1
- package/dist/app-studio.cjs.development.js +174 -1
- 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 +174 -1
- package/dist/app-studio.esm.js.map +1 -1
- package/dist/app-studio.umd.development.js +174 -1
- package/dist/app-studio.umd.development.js.map +1 -1
- package/dist/app-studio.umd.production.min.js +1 -1
- package/dist/providers/Theme.d.ts +3 -0
- package/docs/Components.md +20 -5
- package/docs/README.md +2 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -207,6 +207,7 @@ A versatile container component extending `Element`, primarily used for layout.
|
|
|
207
207
|
|
|
208
208
|
* `Horizontal`: A `View` with `display: flex`, `flexDirection: row`.
|
|
209
209
|
* `Vertical`: A `View` with `display: flex`, `flexDirection: column`.
|
|
210
|
+
* `Center`: A `View` with `display: flex`, `justifyContent: center`, `alignItems: center`.
|
|
210
211
|
* `HorizontalResponsive`: Switches from `row` to `column` on `mobile`.
|
|
211
212
|
* `VerticalResponsive`: Switches from `column` to `row` on `mobile`.
|
|
212
213
|
* `Scroll`: Basic scrollable view (might need explicit overflow styles).
|
|
@@ -801,7 +802,7 @@ Explore our comprehensive documentation to learn more about App-Studio:
|
|
|
801
802
|
# API Reference Summary
|
|
802
803
|
|
|
803
804
|
* **Core:** `Element`
|
|
804
|
-
* **Components:** `View`, `Horizontal`, `Vertical`, `HorizontalResponsive`, `VerticalResponsive`, `Scroll`, `SafeArea`, `Div`, `Span`, `Text`, `Image`, `ImageBackground`, `Form`, `Input`, `Button`, `Skeleton`.
|
|
805
|
+
* **Components:** `View`, `Horizontal`, `Vertical`, `Center`, `HorizontalResponsive`, `VerticalResponsive`, `Scroll`, `SafeArea`, `Div`, `Span`, `Text`, `Image`, `ImageBackground`, `Form`, `Input`, `Button`, `Skeleton`.
|
|
805
806
|
* **Animation:** `Animation` object with functions like `fadeIn`, `slideInLeft`, `pulse`, etc.
|
|
806
807
|
* **Hooks:** `useActive`, `useClickOutside`, `useElementPosition`, `useFocus`, `useHover`, `useInView`, `useKeyPress`, `useMount`, `useOnScreen`, `useResponsive`, `useScroll`, `useWindowSize`.
|
|
807
808
|
* **Providers:** `ThemeProvider`, `ResponsiveProvider`, `AnalyticsProvider`, `WindowSizeProvider`.
|
|
@@ -898,6 +898,9 @@ const defaultDarkColorConfig = {
|
|
|
898
898
|
// --- Create Theme Context ---
|
|
899
899
|
const ThemeContext = /*#__PURE__*/React.createContext({
|
|
900
900
|
getColor: () => '',
|
|
901
|
+
getColorHex: () => '',
|
|
902
|
+
getColorRGBA: () => '',
|
|
903
|
+
getColorScheme: () => 'light',
|
|
901
904
|
theme: {},
|
|
902
905
|
colors: {
|
|
903
906
|
main: defaultLightColors,
|
|
@@ -952,6 +955,87 @@ const convertToRgba = (color, alpha) => {
|
|
|
952
955
|
console.warn(`Unable to convert color "${color}" to rgba format`);
|
|
953
956
|
return color;
|
|
954
957
|
};
|
|
958
|
+
const clamp255 = value => Math.max(0, Math.min(255, value));
|
|
959
|
+
const toTwoHex = value => clamp255(Math.round(value)).toString(16).padStart(2, '0');
|
|
960
|
+
const normalizeToHex = color => {
|
|
961
|
+
if (!color || typeof color !== 'string') return String(color);
|
|
962
|
+
const trimmed = color.trim();
|
|
963
|
+
if (trimmed === TRANSPARENT) return '#00000000';
|
|
964
|
+
if (trimmed.startsWith('#')) {
|
|
965
|
+
const hex = trimmed.slice(1);
|
|
966
|
+
if (hex.length === 3) {
|
|
967
|
+
const r = hex[0] + hex[0];
|
|
968
|
+
const g = hex[1] + hex[1];
|
|
969
|
+
const b = hex[2] + hex[2];
|
|
970
|
+
return `#${r}${g}${b}`.toLowerCase();
|
|
971
|
+
}
|
|
972
|
+
if (hex.length === 4) {
|
|
973
|
+
const r = hex[0] + hex[0];
|
|
974
|
+
const g = hex[1] + hex[1];
|
|
975
|
+
const b = hex[2] + hex[2];
|
|
976
|
+
const a = hex[3] + hex[3];
|
|
977
|
+
return `#${r}${g}${b}${a}`.toLowerCase();
|
|
978
|
+
}
|
|
979
|
+
if (hex.length === 6 || hex.length === 8) {
|
|
980
|
+
return `#${hex}`.toLowerCase();
|
|
981
|
+
}
|
|
982
|
+
return trimmed;
|
|
983
|
+
}
|
|
984
|
+
if (trimmed.startsWith('rgb')) {
|
|
985
|
+
const match = trimmed.match(/rgba?\(([^)]+)\)/i);
|
|
986
|
+
if (!match) return trimmed;
|
|
987
|
+
const parts = match[1].split(',').map(p => p.trim()).filter(Boolean);
|
|
988
|
+
const r = Number(parts[0]);
|
|
989
|
+
const g = Number(parts[1]);
|
|
990
|
+
const b = Number(parts[2]);
|
|
991
|
+
const a = parts.length >= 4 ? Number(parts[3]) : 1;
|
|
992
|
+
if ([r, g, b].some(v => Number.isNaN(v)) || Number.isNaN(a)) return trimmed;
|
|
993
|
+
const rr = toTwoHex(r);
|
|
994
|
+
const gg = toTwoHex(g);
|
|
995
|
+
const bb = toTwoHex(b);
|
|
996
|
+
const alpha = Math.max(0, Math.min(1, a));
|
|
997
|
+
if (alpha >= 1) return `#${rr}${gg}${bb}`.toLowerCase();
|
|
998
|
+
const aa = toTwoHex(alpha * 255);
|
|
999
|
+
return `#${rr}${gg}${bb}${aa}`.toLowerCase();
|
|
1000
|
+
}
|
|
1001
|
+
return trimmed;
|
|
1002
|
+
};
|
|
1003
|
+
const normalizeToRgba = (color, alphaOverride) => {
|
|
1004
|
+
if (!color || typeof color !== 'string') return String(color);
|
|
1005
|
+
const trimmed = color.trim();
|
|
1006
|
+
const overrideAlpha = typeof alphaOverride === 'number' ? Math.max(0, Math.min(1000, alphaOverride)) / 1000 : undefined;
|
|
1007
|
+
if (trimmed === TRANSPARENT) {
|
|
1008
|
+
const a = overrideAlpha ?? 0;
|
|
1009
|
+
return `rgba(0, 0, 0, ${a})`;
|
|
1010
|
+
}
|
|
1011
|
+
if (trimmed.startsWith('#')) {
|
|
1012
|
+
let hex = trimmed.slice(1);
|
|
1013
|
+
if (hex.length === 3 || hex.length === 4) {
|
|
1014
|
+
hex = hex.split('').map(ch => ch + ch).join('');
|
|
1015
|
+
}
|
|
1016
|
+
if (hex.length !== 6 && hex.length !== 8) return trimmed;
|
|
1017
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
1018
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
1019
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
1020
|
+
const aFromHex = hex.length === 8 ? parseInt(hex.slice(6, 8), 16) / 255 : 1;
|
|
1021
|
+
const a = overrideAlpha ?? aFromHex;
|
|
1022
|
+
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
|
1023
|
+
}
|
|
1024
|
+
if (trimmed.startsWith('rgb')) {
|
|
1025
|
+
const match = trimmed.match(/rgba?\(([^)]+)\)/i);
|
|
1026
|
+
if (!match) return trimmed;
|
|
1027
|
+
const parts = match[1].split(',').map(p => p.trim()).filter(Boolean);
|
|
1028
|
+
const r = Number(parts[0]);
|
|
1029
|
+
const g = Number(parts[1]);
|
|
1030
|
+
const b = Number(parts[2]);
|
|
1031
|
+
const aExisting = parts.length >= 4 ? Number(parts[3]) : 1;
|
|
1032
|
+
if ([r, g, b].some(v => Number.isNaN(v)) || Number.isNaN(aExisting)) return trimmed;
|
|
1033
|
+
const a = overrideAlpha ?? Math.max(0, Math.min(1, aExisting));
|
|
1034
|
+
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
|
1035
|
+
}
|
|
1036
|
+
// If it's already something else (var(), color-mix(), named color), we can't reliably convert.
|
|
1037
|
+
return trimmed;
|
|
1038
|
+
};
|
|
955
1039
|
// --- Deep Merge Function (remains the same, assuming it works as intended) ---
|
|
956
1040
|
// Consider using a library like `lodash.merge` or `deepmerge` if complexity grows
|
|
957
1041
|
// or edge cases (like merging arrays) need different handling.
|
|
@@ -1006,6 +1090,60 @@ const ThemeProvider = _ref => {
|
|
|
1006
1090
|
dark: deepMerge(defaultDarkColorConfig, darkOverride)
|
|
1007
1091
|
}), [lightOverride, darkOverride]);
|
|
1008
1092
|
const currentColors = React.useMemo(() => themeColors[themeMode], [themeColors, themeMode]);
|
|
1093
|
+
const resolveColorTokenForMode = React.useCallback(function (token, mode, override, depth) {
|
|
1094
|
+
if (depth === void 0) {
|
|
1095
|
+
depth = 0;
|
|
1096
|
+
}
|
|
1097
|
+
if (!token || typeof token !== 'string') return String(token);
|
|
1098
|
+
if (token === TRANSPARENT) return token;
|
|
1099
|
+
if (depth > 25) return token;
|
|
1100
|
+
const effectiveTheme = override?.theme ? deepMerge(mergedTheme, override.theme) : mergedTheme;
|
|
1101
|
+
// Resolve theme.* tokens recursively
|
|
1102
|
+
if (token.startsWith(THEME_PREFIX)) {
|
|
1103
|
+
const themeKey = token.substring(THEME_PREFIX.length);
|
|
1104
|
+
const themeValue = effectiveTheme[themeKey];
|
|
1105
|
+
if (typeof themeValue === 'string') {
|
|
1106
|
+
return resolveColorTokenForMode(themeValue, mode, override, depth + 1);
|
|
1107
|
+
}
|
|
1108
|
+
return token;
|
|
1109
|
+
}
|
|
1110
|
+
// Resolve explicit mode paths like "light.blue.500" / "dark.blue.500"
|
|
1111
|
+
if (token.startsWith('light.') || token.startsWith('dark.')) {
|
|
1112
|
+
const explicitMode = token.startsWith('light.') ? 'light' : 'dark';
|
|
1113
|
+
const prefixLength = token.startsWith('light.') ? 6 : 5;
|
|
1114
|
+
const modifiedName = `${COLOR_PREFIX}${token.substring(prefixLength)}`;
|
|
1115
|
+
return resolveColorTokenForMode(modifiedName, explicitMode, override, depth + 1);
|
|
1116
|
+
}
|
|
1117
|
+
// Resolve color.* tokens
|
|
1118
|
+
if (token.startsWith(COLOR_PREFIX)) {
|
|
1119
|
+
const keys = token.substring(COLOR_PREFIX.length).split('.');
|
|
1120
|
+
const colorsToUse = themeColors[mode];
|
|
1121
|
+
const palette = deepMerge(colorsToUse.palette, override?.colors?.palette || {});
|
|
1122
|
+
const main = deepMerge(colorsToUse.main, override?.colors?.main || {});
|
|
1123
|
+
if (keys.length === 3) {
|
|
1124
|
+
// e.g. color.blue.500.200 (alpha)
|
|
1125
|
+
const [colorName, variant, alphaStr] = keys;
|
|
1126
|
+
const base = palette?.[colorName]?.[variant];
|
|
1127
|
+
const alpha = parseInt(alphaStr, 10);
|
|
1128
|
+
if (typeof base === 'string' && !isNaN(alpha)) {
|
|
1129
|
+
return convertToRgba(base, alpha);
|
|
1130
|
+
}
|
|
1131
|
+
return token;
|
|
1132
|
+
}
|
|
1133
|
+
if (keys.length === 2) {
|
|
1134
|
+
const [colorName, variant] = keys;
|
|
1135
|
+
const value = palette?.[colorName]?.[variant];
|
|
1136
|
+
return typeof value === 'string' ? value : token;
|
|
1137
|
+
}
|
|
1138
|
+
if (keys.length === 1) {
|
|
1139
|
+
const [colorName] = keys;
|
|
1140
|
+
const value = main?.[colorName];
|
|
1141
|
+
return typeof value === 'string' ? value : token;
|
|
1142
|
+
}
|
|
1143
|
+
return token;
|
|
1144
|
+
}
|
|
1145
|
+
return token;
|
|
1146
|
+
}, [mergedTheme, themeColors]);
|
|
1009
1147
|
// --- Helper function to resolve color tokens to actual values ---
|
|
1010
1148
|
const resolveColorToken = React.useCallback(token => {
|
|
1011
1149
|
if (!token || typeof token !== 'string') return String(token);
|
|
@@ -1125,14 +1263,49 @@ const ThemeProvider = _ref => {
|
|
|
1125
1263
|
if (needCache) colorCache.set(cacheKey, resolvedColor);
|
|
1126
1264
|
return resolvedColor;
|
|
1127
1265
|
}, [mergedTheme, themeColors, themeMode, colorCache, strict]);
|
|
1266
|
+
const getColorHex = React.useCallback((name, override) => {
|
|
1267
|
+
if (!name || typeof name !== 'string') return String(name);
|
|
1268
|
+
if (name === TRANSPARENT) return '#00000000';
|
|
1269
|
+
const effectiveMode = override?.themeMode ?? themeMode;
|
|
1270
|
+
const cacheKey = `${name}-${effectiveMode}-hex`;
|
|
1271
|
+
// Cache only when no override object is provided
|
|
1272
|
+
if (!override && colorCache.has(cacheKey)) {
|
|
1273
|
+
return colorCache.get(cacheKey);
|
|
1274
|
+
}
|
|
1275
|
+
const resolved = resolveColorTokenForMode(name, effectiveMode, override);
|
|
1276
|
+
const hex = normalizeToHex(resolved);
|
|
1277
|
+
if (!override) colorCache.set(cacheKey, hex);
|
|
1278
|
+
return hex;
|
|
1279
|
+
}, [themeMode, colorCache, resolveColorTokenForMode]);
|
|
1280
|
+
const getColorRGBA = React.useCallback((name, alphaOrOverride, overrideMaybe) => {
|
|
1281
|
+
if (!name || typeof name !== 'string') return String(name);
|
|
1282
|
+
const alpha = typeof alphaOrOverride === 'number' ? alphaOrOverride : undefined;
|
|
1283
|
+
const override = typeof alphaOrOverride === 'object' ? alphaOrOverride : overrideMaybe;
|
|
1284
|
+
const effectiveMode = override?.themeMode ?? themeMode;
|
|
1285
|
+
const alphaKey = typeof alpha === 'number' ? String(alpha) : 'auto';
|
|
1286
|
+
const cacheKey = `${name}-${effectiveMode}-rgba-${alphaKey}`;
|
|
1287
|
+
if (!override && colorCache.has(cacheKey)) {
|
|
1288
|
+
return colorCache.get(cacheKey);
|
|
1289
|
+
}
|
|
1290
|
+
const resolved = resolveColorTokenForMode(name, effectiveMode, override);
|
|
1291
|
+
const rgba = normalizeToRgba(resolved, alpha);
|
|
1292
|
+
if (!override) colorCache.set(cacheKey, rgba);
|
|
1293
|
+
return rgba;
|
|
1294
|
+
}, [themeMode, colorCache, resolveColorTokenForMode]);
|
|
1295
|
+
const getColorScheme = React.useCallback(override => {
|
|
1296
|
+
return override?.themeMode ?? themeMode;
|
|
1297
|
+
}, [themeMode]);
|
|
1128
1298
|
// --- Memoize Context Value ---
|
|
1129
1299
|
const contextValue = React.useMemo(() => ({
|
|
1130
1300
|
getColor,
|
|
1301
|
+
getColorHex,
|
|
1302
|
+
getColorRGBA,
|
|
1303
|
+
getColorScheme,
|
|
1131
1304
|
theme: mergedTheme,
|
|
1132
1305
|
colors: currentColors,
|
|
1133
1306
|
themeMode,
|
|
1134
1307
|
setThemeMode
|
|
1135
|
-
}), [getColor, mergedTheme, currentColors, themeMode]);
|
|
1308
|
+
}), [getColor, getColorHex, getColorRGBA, getColorScheme, mergedTheme, currentColors, themeMode]);
|
|
1136
1309
|
return /*#__PURE__*/React__default.createElement(ThemeContext.Provider, {
|
|
1137
1310
|
value: contextValue
|
|
1138
1311
|
}, /*#__PURE__*/React__default.createElement("style", null, generateCSSVariables(mergedTheme, themeColors.light, themeColors.dark)), /*#__PURE__*/React__default.createElement("div", {
|
|
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|