Compare commits

...

6 Commits

6 changed files with 3247 additions and 1341 deletions

View File

@@ -1,5 +1,33 @@
# Changelog
## 2025-11-16 - 2.3.6 - fix(readme)
Normalize README formatting and improve breakpoint helpers section
- Fixed inconsistent Markdown list formatting in the Features section (added space after list dash for emoji bullets)
- Bolded the "Available breakpoint helpers" heading and added spacing for improved readability
- Clarified breakpoint helper listing to ensure cssForPhone is documented
## 2025-11-16 - 2.3.5 - fix(dependencies)
bump dependency versions in package.json
- Updated devDependencies:
- - @git.zone/tsbuild: ^2.6.4 -> ^2.7.1
- - @git.zone/tsbundle: ^2.4.0 -> ^2.5.1
- - @git.zone/tstest: ^2.3.1 -> ^2.7.0
- Updated dependencies:
- - @push.rocks/smartjson: ^5.0.20 -> ^5.2.0
- - @push.rocks/smartrouter: ^1.3.2 -> ^1.3.3
- - @push.rocks/smartstate: ^2.0.20 -> ^2.0.27
- - @push.rocks/smartstring: ^4.0.15 -> ^4.1.0
## 2025-11-16 - 2.3.4 - fix(domtools)
Prevent race conditions during DomTools initialization and improve runOnce error handling
- Add a static initializationPromise to serialize and await concurrent DomTools.setupDomTools() calls to avoid race conditions when multiple initializations are requested.
- Initialize Keyboard only after the document is ready (check document.readyState and use readystatechange listener) so document.body/head are available before creating the Keyboard instance; resolve domReady and domToolsReady appropriately.
- Support ignoreGlobal path to create isolated DomTools instances while keeping global initialization guarded by initializationPromise.
- Enhance runOnce: store errors from the first execution and re-throw them to all waiting callers; always clear the running flag in a finally block to prevent permanent stuck state.
## 2025-06-20 - 2.3.3 - fix(package.json)
Update dependency versions and add pnpm package manager field

View File

@@ -1,6 +1,6 @@
{
"name": "@design.estate/dees-domtools",
"version": "2.3.3",
"version": "2.3.6",
"private": false,
"description": "A package providing tools to simplify complex CSS structures and web development tasks, featuring TypeScript support and integration with various web technologies.",
"main": "dist_ts/index.js",
@@ -15,9 +15,9 @@
"buildDocs": "tsdoc"
},
"devDependencies": {
"@git.zone/tsbuild": "^2.6.4",
"@git.zone/tsbundle": "^2.4.0",
"@git.zone/tstest": "^2.3.1",
"@git.zone/tsbuild": "^2.7.1",
"@git.zone/tsbundle": "^2.5.1",
"@git.zone/tstest": "^2.7.0",
"@push.rocks/tapbundle": "^6.0.3",
"@types/node": "^22.12.0"
},
@@ -26,13 +26,13 @@
"@design.estate/dees-comms": "^1.0.27",
"@push.rocks/lik": "^6.2.2",
"@push.rocks/smartdelay": "^3.0.5",
"@push.rocks/smartjson": "^5.0.20",
"@push.rocks/smartjson": "^5.2.0",
"@push.rocks/smartmarkdown": "^3.0.3",
"@push.rocks/smartpromise": "^4.2.3",
"@push.rocks/smartrouter": "^1.3.2",
"@push.rocks/smartrouter": "^1.3.3",
"@push.rocks/smartrx": "^3.0.10",
"@push.rocks/smartstate": "^2.0.20",
"@push.rocks/smartstring": "^4.0.15",
"@push.rocks/smartstate": "^2.0.27",
"@push.rocks/smartstring": "^4.1.0",
"@push.rocks/smarturl": "^3.1.0",
"@push.rocks/webrequest": "^3.0.37",
"@push.rocks/websetup": "^3.0.19",

3951
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

504
readme.md
View File

@@ -1,104 +1,482 @@
# @design.estate/dees-domtools
tools to simplify complex css structures
## Install
> 🎨 A comprehensive TypeScript toolkit for simplifying DOM manipulation, CSS management, and web component development
To install `@design.estate/dees-domtools`, simply use npm:
Modern web development made elegant. `@design.estate/dees-domtools` provides a powerful suite of utilities for managing complex CSS structures, handling browser events, implementing smooth scrolling, and building responsive web applications with ease.
## Features
- 🚀 **Smart DOM Management** - Singleton-based DomTools instance with race-condition-free initialization
- 📱 **Responsive Breakpoints** - Built-in support for desktop, tablet, phablet, and phone viewports with container queries
- 🎭 **Theme Management** - Automatic dark/light mode detection and switching with RxJS observables
- ⌨️ **Keyboard Shortcuts** - Elegant keyboard event handling with combo support
- 📜 **Smooth Scrolling** - Native and Lenis-powered smooth scrolling with automatic detection
- 🎯 **State Management** - Integrated state management with smartstate
- 🧭 **Routing** - Client-side routing with smartrouter
- 🌐 **WebSetup** - Easy management of website metadata, favicons, and SEO tags
- 💅 **CSS Utilities** - Grid helpers, breakpoint utilities, and base styles for web components
## Installation
```bash
npm install --save @design.estate/dees-domtools
npm install @design.estate/dees-domtools
```
Ensure your project supports TypeScript and ESM syntax for the best development experience.
Or with pnpm:
## Usage
```bash
pnpm add @design.estate/dees-domtools
```
This documentation aims to give you comprehensive guidance on how to utilize `@design.estate/dees-domtools` in your web projects to simplify complex CSS structures. Whether you're building a sophisticated web application or need a more manageable way to handle your CSS, this toolkit offers a variety of features designed to enhance and streamline your workflow. Throughout this guide, we'll cover the installation process, key features, examples, and best practices when working with `@design.estate/dees-domtools`.
Before diving into the examples, please ensure that you've completed the installation step as described above.
### Key Features
- **CSS Simplification**: Tools and utilities designed to abstract and simplify the handling of complex CSS structures.
- **Responsive Design Helpers**: Tools for managing responsive designs more easily, with utilities for breakpoints, and adaptable grids.
- **Element Utilities**: Simplified interactions with DOM elements, including dynamic style manipulations and more.
- **Theme Management**: Simplify the process of theme toggling (dark mode/light mode) in your applications.
- **Keyboard Event Handling**: Utilities for managing keyboard events, facilitating better interaction handling.
- **External Resources Management**: Easily manage external CSS and JavaScript resources within your project.
### Getting Started
To get started with `@design.estate/dees-domtools`, you first need to import the modules you intend to use in your project. Here's a simple example to illustrate how to import and use the package:
## Quick Start
```typescript
import { DomTools, elementBasic, breakpoints, css } from '@design.estate/dees-domtools';
import { DomTools } from '@design.estate/dees-domtools';
// Setting up DomTools for your application
const domToolsInstance = await DomTools.setupDomTools();
// Initialize DomTools (singleton pattern - safe to call multiple times)
const domtools = await DomTools.setupDomTools();
// Example: Using elementBasic utilities
elementBasic.setup();
// Wait for DOM to be ready
await domtools.domReady.promise;
// Now you're ready to rock! 🎸
console.log('DOM is ready, head and body elements are available');
```
### Simplifying CSS with CSS Utilities
## Core API
The package offers a variety of CSS utilities that simplify the handling of complex CSS structures. Here's how you can use them in your project:
### DomTools Instance
The `DomTools` class is the heart of the library. It provides a singleton instance that manages all the utilities.
```typescript
import { DomTools } from '@design.estate/dees-domtools';
// Setup with options
const domtools = await DomTools.setupDomTools({
ignoreGlobal: false // Set to true to create isolated instance
});
// Access DOM elements (available after domReady)
await domtools.domReady.promise;
const head = domtools.elements.headElement;
const body = domtools.elements.bodyElement;
```
**Key Properties:**
- `domtools.router` - SmartRouter instance for client-side routing
- `domtools.themeManager` - Theme management (dark/light mode)
- `domtools.scroller` - Smooth scrolling utilities
- `domtools.keyboard` - Keyboard event handling
- `domtools.websetup` - Website metadata management
- `domtools.smartstate` - State management
- `domtools.deesComms` - Communication utilities
**Lifecycle Promises:**
- `domtools.domToolsReady.promise` - Resolves when DomTools is initialized
- `domtools.domReady.promise` - Resolves when DOM is interactive/complete
- `domtools.globalStylesReady.promise` - Resolves when global styles are set
### Responsive Breakpoints
Built-in breakpoint system with both media queries and container queries:
```typescript
import { breakpoints, css } from '@design.estate/dees-domtools';
import { css as litCss } from 'lit';
// Breakpoint values (in pixels)
breakpoints.desktop // 1600px
breakpoints.notebook // 1240px
breakpoints.tablet // 1024px
breakpoints.phablet // 600px
breakpoints.phone // 400px
// Use with Lit components
const myStyles = litCss`
.container {
padding: 20px;
}
${breakpoints.cssForTablet(litCss`
.container {
padding: 10px;
}
`)}
${breakpoints.cssForPhone(litCss`
.container {
padding: 5px;
}
`)}
`;
```
**Available breakpoint helpers:**
- `cssForDesktop(css)` - Styles for 1600px and above
- `cssForNotebook(css)` - Styles for 1240px and below
- `cssForTablet(css)` - Styles for 1024px and below
- `cssForPhablet(css)` - Styles for 600px and below
- `cssForPhone(css)` - Styles for 400px and below
### Theme Management
Automatic theme detection with system preference support:
```typescript
const domtools = await DomTools.setupDomTools();
const { themeManager } = domtools;
// Toggle between dark and light
themeManager.toggleDarkBright();
// Set specific theme
themeManager.goDark();
themeManager.goBright();
// Enable automatic global background changes
await themeManager.enableAutomaticGlobalThemeChange();
// Subscribe to theme changes
themeManager.themeObservable.subscribe((isBright) => {
console.log(`Theme is now: ${isBright ? 'light' : 'dark'}`);
});
// Check current theme
if (themeManager.goBrightBoolean) {
console.log('Light mode active');
}
```
### Keyboard Shortcuts
Handle keyboard events with ease, including complex combinations:
```typescript
import { Keyboard, Key } from '@design.estate/dees-domtools';
const domtools = await DomTools.setupDomTools();
await domtools.domReady.promise;
// Access the keyboard instance
const { keyboard } = domtools;
// Listen for Ctrl+S
keyboard.on([Key.Ctrl, Key.S]).subscribe((event) => {
event.preventDefault();
console.log('Save triggered!');
});
// Listen for Ctrl+Shift+P
keyboard.on([Key.Ctrl, Key.Shift, Key.P]).subscribe(() => {
console.log('Command palette opened!');
});
// Programmatically trigger key presses
keyboard.triggerKeyPress([Key.Ctrl, Key.S]);
// Clean up when done
keyboard.stopListening();
```
**Available Keys:**
All standard keyboard keys are available in the `Key` enum, including:
- Modifiers: `Ctrl`, `Shift`, `Alt`
- Letters: `A` through `Z`
- Numbers: `Zero` through `Nine`
- Function keys: `F1` through `F12`
- Navigation: `Home`, `End`, `PageUp`, `PageDown`, arrows
- And many more...
### Smooth Scrolling
Powerful scrolling utilities with Lenis integration:
```typescript
const domtools = await DomTools.setupDomTools();
const { scroller } = domtools;
// Scroll to an element smoothly
const targetElement = document.querySelector('#section-2');
await scroller.toElement(targetElement, {
duration: 1000,
easing: 'easeInOutQuad'
});
// Enable Lenis smooth scrolling
await scroller.enableLenisScroll({
disableOnNativeSmoothScroll: true // Auto-disable if browser has native smooth scroll
});
// Register scroll callbacks
scroller.onScroll(() => {
console.log('Page scrolled!');
});
// Detect if native smooth scrolling is enabled
const hasNativeSmooth = await scroller.detectNativeSmoothScroll();
```
### CSS Utilities
Helper functions for common CSS patterns:
```typescript
import { css } from '@design.estate/dees-domtools';
// Example: Creating a CSS grid with a specified number of columns and gap size
const gridStyles = css.cssGridColumns(3, 10); // 3 columns with a 10px gap
// Create responsive grid columns
const gridTemplate = css.cssGridColumns(4, 16);
// Returns: calc((100%/4) - (48px/4)) calc((100%/4) - (48px/4)) ...
// Use in your styles
const styles = `
.grid {
display: grid;
grid-template-columns: ${gridTemplate};
gap: 16px;
}
`;
```
### Managing Responsive Design
Responsive design is critical for modern web development. `@design.estate/dees-domtools` provides utilities to make it easier to manage responsive designs and breakpoints.
### Global Styles & External Resources
```typescript
import { breakpoints } from '@design.estate/dees-domtools';
const domtools = await DomTools.setupDomTools();
// Example: Applying styles for different viewport sizes
const tabletStyles = breakpoints.cssForTablet(myCssStyles);
const phoneStyles = breakpoints.cssForPhone(myCssStyles);
// Add global CSS
await domtools.setGlobalStyles(`
body {
margin: 0;
font-family: 'Inter', sans-serif;
}
`);
// Load external CSS
await domtools.setExternalCss('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');
// Load external JavaScript
await domtools.setExternalScript('https://cdn.example.com/analytics.js');
```
### Theme Management
### Website Metadata
Managing themes (such as toggling between dark and light mode) is simplified with the built-in theme management tools.
Manage your website's metadata easily:
```typescript
// Toggle between light and dark themes
domToolsInstance.themeManager.toggleDarkBright();
```
const domtools = await DomTools.setupDomTools();
### Handling Keyboard Events
Keyboard event handling is made easy with the utilities provided by the toolkit. This can be particularly useful for adding keyboard navigation and shortcuts.
```typescript
import { Keyboard } from '@design.estate/dees-domtools';
// Initialize keyboard handling
const keyboard = new Keyboard(document.body);
// Bind an event to a specific key combination
keyboard.on([Keyboard.Key.Ctrl, Keyboard.Key.S]).subscribe(() => {
console.log('Ctrl+S was pressed');
await domtools.setWebsiteInfo({
metaObject: {
title: 'My Awesome App',
description: 'The best app ever created',
keywords: ['awesome', 'app', 'web'],
author: 'Your Name'
},
faviconUrl: '/favicon.ico',
appleTouchIconUrl: '/apple-touch-icon.png'
});
```
### Best Practices
### Web Component Base Styles
- **Organize Your CSS**: Use the provided CSS utilities to structure your styles in a way that makes them easy to maintain and scale.
- **Embrace Responsiveness**: Leverage the responsive design helpers to ensure your application looks great on any device.
- **Consistent Theme Handling**: Utilize the theme management tools to provide a seamless experience for your users, allowing them to choose their preferred theme.
Kickstart your Lit elements with pre-configured styles:
By integrating `@design.estate/dees-domtools` into your project, you can leverage a comprehensive suite of utilities designed to simplify complex CSS structures and enhance your web development workflow.
```typescript
import { LitElement } from 'lit';
import { elementBasic } from '@design.estate/dees-domtools';
class MyElement extends LitElement {
static styles = [elementBasic.staticStyles];
async connectedCallback() {
super.connectedCallback();
await elementBasic.setup(this);
}
}
```
**The `elementBasic.staticStyles` includes:**
- Box-sizing reset
- Smooth transitions for background and color
- Custom scrollbar styles
- Default font family (Geist Sans, Inter fallback)
### State Management
Integrated state management with smartstate:
```typescript
const domtools = await DomTools.setupDomTools();
// Access the state part
const state = domtools.domToolsStatePart;
// Get current state
const currentState = state.getState();
console.log(currentState.virtualViewport); // 'native'
console.log(currentState.jwt); // null
// Update state
state.setState({
virtualViewport: 'tablet',
jwt: 'your-token-here'
});
// Subscribe to state changes
state.subscribe((newState) => {
console.log('State updated:', newState);
});
```
### Run Once Pattern
Execute expensive operations only once, even if called multiple times:
```typescript
const domtools = await DomTools.setupDomTools();
// This will only execute once, even if called multiple times
const result = await domtools.runOnce('myExpensiveOperation', async () => {
console.log('Running expensive operation...');
await someExpensiveAsyncOperation();
return 'result';
});
// Subsequent calls return the same result without re-executing
const sameResult = await domtools.runOnce('myExpensiveOperation', async () => {
console.log('This will never run!');
return 'different result';
});
console.log(result === sameResult); // true
```
Error handling is built-in - if the function throws, all waiting callers receive the same error.
## Advanced Usage
### Combining Features
Here's a real-world example combining multiple features:
```typescript
import { DomTools, breakpoints, elementBasic, Key } from '@design.estate/dees-domtools';
import { LitElement, html, css as litCss } from 'lit';
import { customElement } from 'lit/decorators.js';
@customElement('my-app')
class MyApp extends LitElement {
static styles = [
elementBasic.staticStyles,
litCss`
:host {
display: block;
padding: 2rem;
}
${breakpoints.cssForTablet(litCss`
:host {
padding: 1rem;
}
`)}
`
];
private domtools?: DomTools;
async connectedCallback() {
super.connectedCallback();
// Setup DomTools
this.domtools = await elementBasic.setup(this);
await this.domtools.domReady.promise;
// Setup keyboard shortcuts
this.domtools.keyboard.on([Key.Ctrl, Key.K]).subscribe(() => {
this.openCommandPalette();
});
// Subscribe to theme changes
this.domtools.themeManager.themeObservable.subscribe((isBright) => {
this.requestUpdate();
});
// Enable smooth scrolling
await this.domtools.scroller.enableLenisScroll({
disableOnNativeSmoothScroll: true
});
}
private openCommandPalette() {
console.log('Command palette opened!');
}
render() {
const isDark = !this.domtools?.themeManager.goBrightBoolean;
return html`
<div class="app" style="background: ${isDark ? '#1a1a1a' : '#ffffff'}">
<h1>My Awesome App</h1>
<button @click=${() => this.domtools?.themeManager.toggleDarkBright()}>
Toggle Theme
</button>
</div>
`;
}
}
```
## TypeScript Support
This package is written in TypeScript and provides full type definitions:
```typescript
import type {
IDomToolsState,
IDomToolsContructorOptions,
TViewport
} from '@design.estate/dees-domtools';
// Custom state interface
interface MyState extends IDomToolsState {
customProperty: string;
}
// Type-safe viewport handling
const viewport: TViewport = 'tablet';
```
## Browser Support
Targets the latest version of Chrome. For other browsers, you may need to include polyfills.
## Why @design.estate/dees-domtools?
-**Race-condition free** - Carefully designed initialization prevents common timing issues
-**TypeScript first** - Full type safety and IntelliSense support
-**Modern APIs** - Built on Lit, RxJS, and other modern web standards
-**Batteries included** - Everything you need for sophisticated web apps
-**Production ready** - Used in real-world applications at design.estate
-**Well maintained** - Active development and support
## Related Packages
This library integrates with the design.estate ecosystem:
- `@design.estate/dees-comms` - Communication utilities
- `@push.rocks/websetup` - Website setup and meta management
- `@push.rocks/smartrouter` - Client-side routing
- `@push.rocks/smartstate` - State management
## License and Legal Information
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
@@ -108,7 +486,7 @@ This project is owned and maintained by Task Venture Capital GmbH. The names and
### Company Information
Task Venture Capital GmbH
Task Venture Capital GmbH
Registered at District court Bremen HRB 35230 HB, Germany
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@design.estate/dees-domtools',
version: '2.3.3',
version: '2.3.6',
description: 'A package providing tools to simplify complex CSS structures and web development tasks, featuring TypeScript support and integration with various web technologies.'
}

View File

@@ -18,32 +18,54 @@ export class DomTools {
// ======
// STATIC
// ======
private static initializationPromise: Promise<DomTools> | null = null;
/**
* setups domtools
*/
public static async setupDomTools(optionsArg: IDomToolsContructorOptions = {}) {
let domToolsInstance: DomTools;
if (!globalThis.deesDomTools && !optionsArg.ignoreGlobal) {
globalThis.deesDomTools = new DomTools(optionsArg);
domToolsInstance = globalThis.deesDomTools;
// lets make sure the dom is ready
const readyStateChangedFunc = () => {
if (document.readyState === 'interactive' || document.readyState === 'complete') {
domToolsInstance.elements.headElement = document.querySelector('head');
domToolsInstance.elements.bodyElement = document.querySelector('body');
domToolsInstance.domReady.resolve();
}
};
document.addEventListener('readystatechange', readyStateChangedFunc);
domToolsInstance.domToolsReady.resolve();
} else if (optionsArg.ignoreGlobal) {
domToolsInstance = new DomTools(optionsArg);
} else {
domToolsInstance = globalThis.deesDomTools;
public static async setupDomTools(optionsArg: IDomToolsContructorOptions = {}): Promise<DomTools> {
// If initialization is already in progress and we're not ignoring global, wait for it
if (!optionsArg.ignoreGlobal && DomTools.initializationPromise) {
return await DomTools.initializationPromise;
}
// Create initialization promise to prevent race conditions
if (!optionsArg.ignoreGlobal) {
DomTools.initializationPromise = (async () => {
let domToolsInstance: DomTools;
if (!globalThis.deesDomTools) {
globalThis.deesDomTools = new DomTools(optionsArg);
domToolsInstance = globalThis.deesDomTools;
// lets make sure the dom is ready
const readyStateChangedFunc = () => {
if (document.readyState === 'interactive' || document.readyState === 'complete') {
domToolsInstance.elements.headElement = document.querySelector('head');
domToolsInstance.elements.bodyElement = document.querySelector('body');
// Initialize keyboard now that document.body exists
domToolsInstance.keyboard = new Keyboard(document.body);
domToolsInstance.domReady.resolve();
}
};
// Check current state immediately to avoid race condition
if (document.readyState === 'interactive' || document.readyState === 'complete') {
readyStateChangedFunc();
} else {
document.addEventListener('readystatechange', readyStateChangedFunc);
}
domToolsInstance.domToolsReady.resolve();
} else {
domToolsInstance = globalThis.deesDomTools;
}
await domToolsInstance.domToolsReady.promise;
return domToolsInstance;
})();
return await DomTools.initializationPromise;
} else {
// ignoreGlobal case - create isolated instance
const domToolsInstance = new DomTools(optionsArg);
return domToolsInstance;
}
await domToolsInstance.domToolsReady.promise;
return domToolsInstance;
}
/**
@@ -95,7 +117,7 @@ export class DomTools {
public deesComms = new plugins.deesComms.DeesComms();
public scroller = new Scroller(this);
public themeManager = new ThemeManager(this);
public keyboard = new Keyboard(document.body);
public keyboard: Keyboard = null; // Initialized after DOM ready to avoid accessing document.body before it exists
public domToolsReady = plugins.smartpromise.defer();
public domReady = plugins.smartpromise.defer();
@@ -105,6 +127,7 @@ export class DomTools {
private runOnceTrackerStringMap = new plugins.lik.Stringmap();
private runOnceResultMap = new plugins.lik.FastMap();
private runOnceErrorMap = new plugins.lik.FastMap();
/**
* run a function once and always get the Promise of the first execution
@@ -116,15 +139,27 @@ export class DomTools {
if (!this.runOnceTrackerStringMap.checkString(identifierArg)) {
this.runOnceTrackerStringMap.addString(identifierArg);
this.runOnceTrackerStringMap.addString(runningId);
const result = await funcArg();
this.runOnceResultMap.addToMap(identifierArg, result);
this.runOnceTrackerStringMap.removeString(runningId);
try {
const result = await funcArg();
this.runOnceResultMap.addToMap(identifierArg, result);
} catch (error) {
// Store error so waiting callers can receive it
this.runOnceErrorMap.addToMap(identifierArg, error);
} finally {
// Always remove running flag to prevent permanent stuck state
this.runOnceTrackerStringMap.removeString(runningId);
}
}
return await this.runOnceTrackerStringMap.registerUntilTrue(
(stringMap) => {
return !stringMap.includes(runningId);
},
() => {
// Check if there was an error and re-throw it
const error = this.runOnceErrorMap.getByKey(identifierArg);
if (error) {
throw error;
}
return this.runOnceResultMap.getByKey(identifierArg);
}
);