# Common

All imports from base package are available both on the server and client.

## appId : `String`

DOM friendly unique identifier for the WebApp.

```js
import { appId } from '@soleil-se/app-util';


console.log(appId); // For example: 12_682d461b1708a9bb1ea13efd
```

## isOffline : `Boolean`

If the WebApp is running in offline mode or not.

```js
import { isOffline } from '@soleil-se/app-util';


console.log(isOffline);  // true or false
```

## isOnline : `Boolean`

If the WebApp is running in online mode or not.

```js
import { isOnline } from '@soleil-se/app-util';


console.log(isOnline); // true or false
```

## isServer : `Boolean`

If the current code is running on the server.

```js
import { isServer } from '@soleil-se/app-util';


console.log(isServer); // true or false
```

## isBrowser : `Boolean`

If the current code is running in the browser.

```js
import { isBrowser } from '@soleil-se/app-util';


console.log(isBrowser); // true or false
```

## getNamespace(\[prefix]) ⇨ `String`

Get a prefixed namespace unique for app.

**Returns**: `String` - Prefixed namespace.

| Param     | Type     | Default |
| --------- | -------- | ------- |
| \[prefix] | `string` | `'app'` |

```js
import { getNamespace } from '@soleil-se/app-util';


console.log(getNamespace());
// For example: app_12_682d461b1708a9bb1ea13efd


console.log(getNamespace('input'));
// For example: input_12_682d461b1708a9bb1ea13efd


// If the app is in a decoration template the Portlet ID is the same for all instances, so the ID of the decorated node is used as well.
console.log(getNamespace('decoration'));
// For example: decoration_10_3871c02f1754f3aa8f9d4eb_12_70c3d424173b4900fc550e1c
```

## generateId(\[prefix]) ⇨ `String`

Generate a unique identifier with a random UUID without dashes.

> **Consistent ID:s**
>
> Since version 5.9.0 of this package and version 5.22.0 of Svelte you can use `$props.id()` to get consistent ID:s between server and client.\
> This is useful for form elements and linking elements via attributes like `for` and `aria-labelledby`.
>
> Read more in the [Svelte documentation](https://svelte.dev/docs/svelte/$props#$props.id\(\)).

**Returns**: `String` - Unique identifier.

| Param     | Type     | Default |
| --------- | -------- | ------- |
| \[prefix] | `string` | `'id'`  |

```js
import { generateId } from '@soleil-se/app-util';


console.log(generateId());
// For example: id_550e8400e29b41d4a716446655440000


console.log(generateId('input'));
// For example: input_550e8400e29b41d4a716446655440000
```

## getRouteUri(route, \[query]) ⇨ `String`

Get URI for a route.

**Returns**: `String` - URI for route.

| Param | Type     | Description                         |
| ----- | -------- | ----------------------------------- |
| route | `String` | A route.                            |
| query | `Object` | Object with query string parameters |

```js
import { getRouteUri } from '@soleil-se/app-util';


console.log(getRouteUri('/items'));
// URI structure: /appresource/{pageId}/{portletId}>/items
```

```js
import { getRouteUri } from '@soleil-se/app-util';


console.log(getRouteUri('/items', { foo: 'bar' }));
// URI structure: /appresource/{pageId}/{portletId}>/items?foo=bar
```

## getResourceUri(resource) ⇨ `String`

Get URI for a resource.

**Returns**: `String` - URI for a resource.

| Param    | Type     | Description |
| -------- | -------- | ----------- |
| resource | `String` | A resource. |

```js
import { getResourceUri } from '@soleil-se/app-util';


console.log(getResourceUri('/image.png'));
// URI structure: /webapp-files/<webappname>/<webappversion>/image.png
```

## getAppProps(\[key]) ⇨ `*` | `Object`

Get props that are passed to app when rendering.

**Returns**: `*` | `Object` - Value or object.

| Param  | Type     | Description    |
| ------ | -------- | -------------- |
| \[key] | `String` | Key for value. |

```js
import { getAppProps } from '@soleil-se/app-util';


// Get value with key
const myValue = getAppProps('myValue');
// Or with destructuring
const { myValue } = getAppProps();
```

## stringifyParams(params \[, options]) ⇨ `String`

Stringify an object to a query string compatible with Sitevision.

**Returns**: `String` - Stringified parameters.

| Param                     | Type      | Default | Description                                     |
| ------------------------- | --------- | ------- | ----------------------------------------------- |
| params                    | `Object`  |         | Object with parameters to stringify.            |
| \[options]                | `Object`  | `{}`    | Options object.                                 |
| \[options.addQueryPrefix] | `Boolean` | `false` | If a leading `?` should be added to the string. |

```js
import { stringifyParams } from '@soleil-se/app-util';


const queryString = stringifyParams({ foo: 'bar', num: 1 });
// foo=bar&num=1


const queryString = stringifyParams({ foo: 'bar', num: 1 }, { addQueryPrefix: true });
// ?foo=bar&num=1
```

## parseParams(url) ⇨ `Object`

Parse an URL, URI or query string to an object containing its query parameters.

**Returns**: `Object` - Parsed parameters.

| Param | Type     | Default | Description                                                           |
| ----- | -------- | ------- | --------------------------------------------------------------------- |
| url   | `String` |         | URL, URI or query string to be parsed, must start with or contain ”?” |

```js
import { parseParams } from '@soleil-se/app-util';


const params = parseParams('?foo=bar&arr[]=1&arr[]=2');
// { foo: 'bar', arr: [1, 2] }
```

## localizedCompare(a, b) ⇨ `Number`

Compares two strings in a localized manner, taking into account Nordic special characters.\
The order is defined as: a-z å ä æ ö ø (case-insensitive, with lowercase before uppercase).\
Also handles accented characters.

> **Note**
>
> This function is needed because `String.localeCompare()` does not work properly with Nordic characters in Rhino.

**Returns**: `Number` - Negative if a < b, positive if a > b, zero if equal.

| Param | Type     | Description                   |
| ----- | -------- | ----------------------------- |
| a     | `String` | The first string to compare.  |
| b     | `String` | The second string to compare. |

```js
import { localizedCompare } from '@soleil-se/app-util';


// Basic alphabetical sorting
const fruits = ['banana', 'apple', 'zebra', 'cherry'];
fruits.sort(localizedCompare);
// Result: ['apple', 'banana', 'cherry', 'zebra']


// Nordic characters are sorted correctly
const names = ['Öberg', 'Gustavsson', 'Åström', 'Anderson', 'Ärlig'];
names.sort(localizedCompare);
// Result: ['Anderson', 'Gustavsson', 'Åström', 'Ärlig', 'Öberg']


// Case-insensitive with lowercase taking precedence
const words = ['Apple', 'apple', 'Banana', 'banana'];
words.sort(localizedCompare);
// Result: ['apple', 'Apple', 'banana', 'Banana']


// Handles accents correctly
const names = ['Pérez', 'Perez', 'café', 'cafe', 'José', 'Jose'];
names.sort(localizedCompare);
// Result: ['cafe', 'café', 'Jose', 'José', 'Perez', 'Pérez']
```

## localizedCompareBy(prop | props) ⇨ `Function`

Creates a comparator function for sorting objects by one or more properties. The property values are compared using localized string comparison (a-z å ä æ ö ø). When multiple properties are provided, they are used in order as tiebreakers. Each property can optionally specify a sort direction (`'asc'` or `'desc'`).

> **Note**
>
> This function is needed because `String.localeCompare()` does not work properly with Nordic characters in Rhino.

**Returns**: `Function` - A comparator function that accepts two objects and returns their sort order.

| Param | Type                                                          | Description                                                                     |
| ----- | ------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| prop  | `String \| PropDescriptor \| Array<String \| PropDescriptor>` | Property name(s) to compare by. Multiple properties sort in the order provided. |

Where `PropDescriptor` is an object:

| Property | Type              | Default | Description                       |
| -------- | ----------------- | ------- | --------------------------------- |
| key      | `String`          |         | The property name to compare by.  |
| order    | `'asc' \| 'desc'` | `'asc'` | Sort direction for this property. |

```js
import { localizedCompareBy } from '@soleil-se/app-util';


const employees = [
  { name: 'Öberg', department: 'IT' },
  { name: 'Anderson', department: 'HR' },
  { name: 'Åström', department: 'Sales' },
  { name: 'Ärlig', department: 'IT' },
];


// Sort by a single property
employees.sort(localizedCompareBy('name'));
// Result: [
//   { name: 'Anderson', department: 'HR' },
//   { name: 'Åström', department: 'Sales' },
//   { name: 'Ärlig', department: 'IT' },
//   { name: 'Öberg', department: 'IT' },
// ]


// Sort by a single property descending
employees.sort(localizedCompareBy({ key: 'name', order: 'desc' }));
// Result: [
//   { name: 'Öberg', department: 'IT' },
//   { name: 'Ärlig', department: 'IT' },
//   { name: 'Åström', department: 'Sales' },
//   { name: 'Anderson', department: 'HR' },
// ]


// Sort by multiple properties (department ascending, then name ascending as tiebreaker)
employees.sort(localizedCompareBy(['department', 'name']));
// Result: [
//   { name: 'Anderson', department: 'HR' },
//   { name: 'Åström', department: 'Sales' },
//   { name: 'Ärlig', department: 'IT' },
//   { name: 'Öberg', department: 'IT' },
// ]


// Sort by multiple properties (department ascending, then name descending as tiebreaker)
employees.sort(localizedCompareBy([
  { key: 'department', order: 'asc' },
  { key: 'name', order: 'desc' },
]));
// Result: [
//   { name: 'Anderson', department: 'HR' },
//   { name: 'Öberg', department: 'IT' },   ← Ö before Ä when descending
//   { name: 'Ärlig', department: 'IT' },
//   { name: 'Åström', department: 'Sales' },
// ]


// Plain strings and prop descriptors can be mixed
employees.sort(localizedCompareBy(['department', { key: 'name', order: 'desc' }]));
```