x4js 2.3.11 → 2.3.12
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 +13 -1
- package/aicli.md +194 -0
- package/aicontext.md +6 -1
- package/aiguide.md +7 -0
- package/cli/main.mjs +3 -1
- package/package.json +2 -1
- package/src/components/spreadsheet/spreadsheet.ts +28 -4
- package/src/core/core_router.ts +15 -3
package/README.md
CHANGED
|
@@ -7,7 +7,19 @@ see [X4 home page](https://x4js.org)
|
|
|
7
7
|
## API Documentation
|
|
8
8
|
see [GitHib](https://rlibre.github.io/x4/index.html)
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
## Using x4js with an AI assistant
|
|
11
|
+
Three files written for AI assistants ship with the package (in `node_modules/x4js/`). Give them to your assistant before asking it to write x4js code:
|
|
12
|
+
|
|
13
|
+
- [aicontext.md](./aicontext.md): compact API reference, with exact signatures
|
|
14
|
+
- [aiguide.md](./aiguide.md): how to write idiomatic x4js code
|
|
15
|
+
- [aicli.md](./aicli.md): the `x4js` command line and `x4.config.json`
|
|
16
|
+
|
|
17
|
+
With Claude Code, install the x4js plugin instead: its skill reads these files from your project by itself.
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
claude plugin marketplace add rlibre/x4
|
|
21
|
+
claude plugin install x4js@rlibre
|
|
22
|
+
```
|
|
11
23
|
|
|
12
24
|
---
|
|
13
25
|
|
package/aicli.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# x4js command line
|
|
2
|
+
|
|
3
|
+
`x4js` creates, serves and builds x4js applications. It bundles TypeScript and styles with esbuild; there is nothing else to configure.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
x4js create <project> [--template <name>] [--no-install]
|
|
7
|
+
x4js templates
|
|
8
|
+
x4js dev [--config <file>] [--host <host>] [--port <port>] [--http|--https] [--open]
|
|
9
|
+
x4js build [--config <file>] [--debug]
|
|
10
|
+
x4js --help | --version
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
The command ships with the `x4js` package. In a project created by `x4js create` it is already a dependency, and the npm scripts use it:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "x4js dev",
|
|
21
|
+
"build": "x4js build"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
npm run dev
|
|
28
|
+
npm run build
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Without a project yet, run it through npx: `npx x4js create my-app`.
|
|
32
|
+
|
|
33
|
+
### Which x4js runs
|
|
34
|
+
|
|
35
|
+
There can be two copies on a machine: the one in the project's `node_modules` and a global one (`npm install -g x4js`).
|
|
36
|
+
|
|
37
|
+
| You type | Copy used |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `npm run dev`, `npx x4js dev` | the project's own copy |
|
|
40
|
+
| `x4js dev` | the global copy |
|
|
41
|
+
|
|
42
|
+
A global copy older than the project's can behave differently. `dev` and `build` print their version on the first line; check it when something looks wrong.
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
x4js 2.3.11
|
|
46
|
+
mode dev
|
|
47
|
+
config C:\work\my-app\x4.config.json
|
|
48
|
+
outdir C:\work\my-app\bin
|
|
49
|
+
server http://127.0.0.1:8000
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Commands
|
|
53
|
+
|
|
54
|
+
### `x4js create <project>`
|
|
55
|
+
|
|
56
|
+
Creates a new project in the folder `<project>` from a template.
|
|
57
|
+
|
|
58
|
+
| Option | Effect |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `--template <name>`, `-t <name>` | Template to use. Default: `app`. |
|
|
61
|
+
| `--no-install` | Do not run `npm install` after creating the files. |
|
|
62
|
+
|
|
63
|
+
- The project name becomes the package name: lowercase letters, digits, `.`, `_` and `-` only.
|
|
64
|
+
- The target folder must be empty or not exist.
|
|
65
|
+
- Templates are downloaded from the `rlibre/x4-templates` repository on GitHub, so the command needs network access.
|
|
66
|
+
- The `x4js` dependency of the new project is set to the version line of the command that created it (for example `2.3`).
|
|
67
|
+
|
|
68
|
+
### `x4js templates`
|
|
69
|
+
|
|
70
|
+
Lists the available templates with a one-line description of each.
|
|
71
|
+
|
|
72
|
+
### `x4js dev`
|
|
73
|
+
|
|
74
|
+
Builds the application, serves the output folder and rebuilds on every change.
|
|
75
|
+
|
|
76
|
+
| Option | Effect |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `--config <file>` | Config file to use. Default: `x4.config.json`. |
|
|
79
|
+
| `--host <host>` | Address to listen on. Default: `127.0.0.1`. |
|
|
80
|
+
| `--port <port>` | Port to listen on. Default: the first free port from 8000. |
|
|
81
|
+
| `--http`, `--https` | Force the protocol, whatever the config says. |
|
|
82
|
+
| `--open` | Open the browser once the server is ready. |
|
|
83
|
+
|
|
84
|
+
- The page reloads when a source or a copied file changes. A change that only touches CSS is applied without reloading.
|
|
85
|
+
- Build errors and warnings are printed in the terminal.
|
|
86
|
+
- Editing the config file restarts the build.
|
|
87
|
+
- HTTPS needs a certificate and a key, declared in `dev.tls` (see below).
|
|
88
|
+
|
|
89
|
+
### `x4js build`
|
|
90
|
+
|
|
91
|
+
Writes the application to the output folder.
|
|
92
|
+
|
|
93
|
+
| Option | Effect |
|
|
94
|
+
|---|---|
|
|
95
|
+
| `--config <file>` | Config file to use. Default: `x4.config.json`. |
|
|
96
|
+
| `--debug` | Debug build: not minified, with source maps. |
|
|
97
|
+
|
|
98
|
+
The output folder is **emptied before each build**. Do not keep hand-written files in it: put them in the sources and list them in `copy`.
|
|
99
|
+
|
|
100
|
+
| | `dev` | `build --debug` | `build` |
|
|
101
|
+
|---|---|---|---|
|
|
102
|
+
| Minified | no | no | yes |
|
|
103
|
+
| Source maps | separate `.map` files | separate `.map` files | none |
|
|
104
|
+
| `DEBUG_MODE` | `true` | `true` | `false` |
|
|
105
|
+
| Live reload | yes | no | no |
|
|
106
|
+
|
|
107
|
+
## `x4.config.json`
|
|
108
|
+
|
|
109
|
+
The file sits at the root of the project. Every key is optional; without the file, the defaults below apply.
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"entryPoints": ["src/main.ts"],
|
|
114
|
+
"outdir": "./bin",
|
|
115
|
+
"copy": [
|
|
116
|
+
{ "from": "./src/assets", "to": "assets" },
|
|
117
|
+
{ "from": "./src/index.html", "to": "index.html" }
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
| Key | Default | Meaning |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| `entryPoints` | `["src/main.ts"]` | Files to bundle. `src/main.ts` produces `main.js` and `main.css`. |
|
|
125
|
+
| `outdir` | `"./bin"` | Output folder. It cannot be the project root. |
|
|
126
|
+
| `copy` | `[]` | Files or folders copied to the output after each successful build. `to` is relative to `outdir`. |
|
|
127
|
+
| `external` | `[]` | Module names left out of the bundle. |
|
|
128
|
+
| `define` | `{}` | Constants replaced at build time. Values are JavaScript expressions written as strings, for example `"\"prod\""`. |
|
|
129
|
+
| `dev` | | Dev server settings: `host`, `port`, `https`, `tls`. |
|
|
130
|
+
| `esbuild` | `{}` | Native esbuild options, applied last. They override everything above. |
|
|
131
|
+
|
|
132
|
+
Relative paths are resolved from the project root. Paths accept environment variables, written `$NAME` or `${NAME}`; an undefined variable is an error.
|
|
133
|
+
|
|
134
|
+
### The HTML page
|
|
135
|
+
|
|
136
|
+
The command does not generate an HTML page. Write `src/index.html` and copy it with `copy`, as in the example above. It loads the bundle by its output name:
|
|
137
|
+
|
|
138
|
+
```html
|
|
139
|
+
<!DOCTYPE html>
|
|
140
|
+
<html>
|
|
141
|
+
<head>
|
|
142
|
+
<meta charset="utf8">
|
|
143
|
+
<link rel="stylesheet" href="main.css">
|
|
144
|
+
<script src="main.js"></script>
|
|
145
|
+
</head>
|
|
146
|
+
</html>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### HTTPS in dev
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"dev": {
|
|
154
|
+
"https": true,
|
|
155
|
+
"tls": { "cert": "./certs/dev.crt", "key": "./certs/dev.key" }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## What the build handles
|
|
161
|
+
|
|
162
|
+
| Import | Result |
|
|
163
|
+
|---|---|
|
|
164
|
+
| `.ts`, `.js` | Bundled into one script per entry point (browser, ES2020). |
|
|
165
|
+
| `.scss`, `.sass`, `.css` | Compiled and bundled into one stylesheet per entry point. |
|
|
166
|
+
| `.less` | Same, if the `less` package is installed in the project (`npm install -D less`). |
|
|
167
|
+
| `.svg` imported from a script | A `data:` URL string, usable as an icon. |
|
|
168
|
+
| `.png`, `.jpg`, `.jpeg`, `.ttf`, `.woff`, `.woff2` | Copied to `assets/` with a hashed name. |
|
|
169
|
+
|
|
170
|
+
Two constants are always defined:
|
|
171
|
+
|
|
172
|
+
| Constant | Value |
|
|
173
|
+
|---|---|
|
|
174
|
+
| `DEBUG_MODE` | `true` in `dev` and `build --debug`, `false` in `build`. |
|
|
175
|
+
| `VERSION_ID` | Build date as a number, `YYMMDD`. |
|
|
176
|
+
|
|
177
|
+
Declare them once in your project to use them from TypeScript:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
declare const DEBUG_MODE: boolean;
|
|
181
|
+
declare const VERSION_ID: number;
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Troubleshooting
|
|
185
|
+
|
|
186
|
+
| Symptom | Cause |
|
|
187
|
+
|---|---|
|
|
188
|
+
| `index.html` is missing from the output folder | It is not listed in `copy`, or the command that ran is an older global copy. Compare the version on the first line with the project's. |
|
|
189
|
+
| A file placed in the output folder disappeared | `x4js build` empties the folder. Move the file to the sources and add it to `copy`. |
|
|
190
|
+
| `Cannot find package 'esbuild'` | The dependencies of the `x4js` copy being run are not installed. Run `npm install` in the project. |
|
|
191
|
+
| `Config file not found` | The file given to `--config` does not exist. Without `--config`, a missing `x4.config.json` is not an error. |
|
|
192
|
+
| `Environment variable '…' is not defined` | A path in the config uses `$NAME` and the variable is not set. |
|
|
193
|
+
| `LESS support requires the "less" package` | A `.less` file is imported and `less` is not installed in the project. |
|
|
194
|
+
| No colors in the terminal, or unwanted ones | Set `FORCE_COLOR=1` to force them, `NO_COLOR=1` to remove them. |
|
package/aicontext.md
CHANGED
|
@@ -17,6 +17,7 @@ import { ... } from 'x4js' // src/x4.ts
|
|
|
17
17
|
**Archived repository**: https://github.com/rlibre/x4js (read-only since March 2026)
|
|
18
18
|
**License**: MIT
|
|
19
19
|
**Quick start**: `npx x4js create demo` (list the templates with `npx x4js templates`)
|
|
20
|
+
**Claude Code plugin**: `claude plugin marketplace add rlibre/x4` then `claude plugin install x4js@rlibre` (skill that reads these files from the project)
|
|
20
21
|
|
|
21
22
|
---
|
|
22
23
|
|
|
@@ -734,6 +735,7 @@ interface SpreadsheetProps extends ComponentProps {
|
|
|
734
735
|
enterMove?: "down" | "right" | "none" // selection move after Enter in a cell editor (default "down")
|
|
735
736
|
click?, dblClick?, contextMenu?, selectionChange?
|
|
736
737
|
cellChange? // EvCellChange, fired before an edited value is written to the store
|
|
738
|
+
cellKey? // EvCellKey { key, row, col, uievent }, fired for each key before the default handling
|
|
737
739
|
}
|
|
738
740
|
interface SpreadsheetColumn { // GridColumn +
|
|
739
741
|
editable?: boolean | ((row, col) => boolean) // inline edition (off by default), col = column index
|
|
@@ -747,7 +749,9 @@ class Spreadsheet extends Component {
|
|
|
747
749
|
}
|
|
748
750
|
|
|
749
751
|
// Inline edition: F2 / Enter / double-click / typing starts, Enter / Tab / click elsewhere validates, Escape cancels,
|
|
750
|
-
// Delete clears, editable checkboxes toggle on click or Space
|
|
752
|
+
// Delete clears, editable checkboxes toggle on double-click or Space (a single click only selects).
|
|
753
|
+
// Default editor is an Input matching the column type.
|
|
754
|
+
// cellKey: key hook (function keys...), not fired while a cell is edited; ev.preventDefault() skips the default handling.
|
|
751
755
|
// cellChange: ev.preventDefault() refuses the value, ev.value can be replaced.
|
|
752
756
|
// Select / Combobox in a cell (the store holds the id, use `formatter` to display the text):
|
|
753
757
|
// { id: 2, title: "Status", width: 120, editable: true,
|
|
@@ -1392,6 +1396,7 @@ When creating a new x4js project, use these files as a base.
|
|
|
1392
1396
|
|
|
1393
1397
|
`x4js dev` runs the dev server, `x4js build` writes `outdir` (add `--debug` for a debug build).
|
|
1394
1398
|
Both read `x4.config.json` (change the file with `--config <file>`).
|
|
1399
|
+
Full reference of the command line (commands, options, config keys, troubleshooting): `aicli.md`.
|
|
1395
1400
|
|
|
1396
1401
|
### `x4.config.json`
|
|
1397
1402
|
|
package/aiguide.md
CHANGED
|
@@ -636,6 +636,13 @@ selectLanguage("en");
|
|
|
636
636
|
// only now build the application components
|
|
637
637
|
```
|
|
638
638
|
|
|
639
|
+
Type each additional language after the first one, so the compiler reports a missing text:
|
|
640
|
+
|
|
641
|
+
```ts
|
|
642
|
+
const app_fr = { app: { open: "Ouvrir", close: "Fermer" } };
|
|
643
|
+
const app_en: typeof app_fr = { app: { open: "Open", close: "Close" } };
|
|
644
|
+
```
|
|
645
|
+
|
|
639
646
|
---
|
|
640
647
|
|
|
641
648
|
## Final rule
|
package/cli/main.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { failure } from "./log.mjs";
|
|
4
|
+
import { failure, info } from "./log.mjs";
|
|
5
5
|
|
|
6
6
|
const cliDir = path.dirname(fileURLToPath(import.meta.url));
|
|
7
7
|
const packageFile = path.join(cliDir, "..", "package.json");
|
|
@@ -46,11 +46,13 @@ try {
|
|
|
46
46
|
break;
|
|
47
47
|
}
|
|
48
48
|
case "dev": {
|
|
49
|
+
info("x4js", version);
|
|
49
50
|
const { dev } = await import("./dev.mjs");
|
|
50
51
|
await dev(argv);
|
|
51
52
|
break;
|
|
52
53
|
}
|
|
53
54
|
case "build": {
|
|
55
|
+
info("x4js", version);
|
|
54
56
|
const { build } = await import("./build.mjs");
|
|
55
57
|
await build(argv);
|
|
56
58
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "x4js",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "src/x4.ts",
|
|
6
6
|
"module": "src/x4.ts",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"src",
|
|
22
22
|
"bin",
|
|
23
23
|
"cli",
|
|
24
|
+
"aicli.md",
|
|
24
25
|
"aicontext.md",
|
|
25
26
|
"aiguide.md",
|
|
26
27
|
"README.md",
|
|
@@ -72,6 +72,18 @@ export interface EvCellChange extends ComponentEvent {
|
|
|
72
72
|
oldValue: any;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* fired for each key pressed on the grid (not while a cell is edited), before the default handling
|
|
77
|
+
* call preventDefault to handle the key yourself
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
export interface EvCellKey extends ComponentEvent {
|
|
81
|
+
key: string; // "F2", "Enter", "a"...
|
|
82
|
+
row: number; // selected cell, undefined if none
|
|
83
|
+
col: number; // column index
|
|
84
|
+
uievent: KeyboardEvent; // UI event that fire this event
|
|
85
|
+
}
|
|
86
|
+
|
|
75
87
|
interface CellEdit extends CellRef {
|
|
76
88
|
editor: Component;
|
|
77
89
|
initial?: string; // text of the editor at start
|
|
@@ -214,6 +226,7 @@ export interface SpreadsheetEvents extends ComponentEvents {
|
|
|
214
226
|
contextMenu?: EvContextMenu;
|
|
215
227
|
selectionChange?: EvSelectionChange;
|
|
216
228
|
cellChange?: EvCellChange;
|
|
229
|
+
cellKey?: EvCellKey;
|
|
217
230
|
}
|
|
218
231
|
|
|
219
232
|
export interface SpreadsheetProps extends ComponentProps {
|
|
@@ -226,6 +239,7 @@ export interface SpreadsheetProps extends ComponentProps {
|
|
|
226
239
|
enterMove?: "down" | "right" | "none";
|
|
227
240
|
|
|
228
241
|
cellChange?: EventCallback<EvCellChange>;
|
|
242
|
+
cellKey?: EventCallback<EvCellKey>;
|
|
229
243
|
click?: EventCallback<EvClick>;
|
|
230
244
|
dblClick?: EventCallback<EvDblClick>;
|
|
231
245
|
contextMenu?: EventCallback<EvContextMenu>;
|
|
@@ -323,7 +337,7 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
323
337
|
|
|
324
338
|
this._columns = props.columns.map(x => x);
|
|
325
339
|
|
|
326
|
-
this.mapPropEvents(props, "click", "dblClick", "contextMenu", "selectionChange", "cellChange");
|
|
340
|
+
this.mapPropEvents(props, "click", "dblClick", "contextMenu", "selectionChange", "cellChange", "cellKey");
|
|
327
341
|
|
|
328
342
|
this.lock(true);
|
|
329
343
|
this.setAttribute("tabindex", 0);
|
|
@@ -365,6 +379,17 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
365
379
|
return;
|
|
366
380
|
}
|
|
367
381
|
|
|
382
|
+
// the application first
|
|
383
|
+
const cur = this._curCell( );
|
|
384
|
+
const kev: EvCellKey = { key: ev.key, row: cur?.row, col: cur?.col, uievent: ev };
|
|
385
|
+
this.fire( "cellKey", kev );
|
|
386
|
+
|
|
387
|
+
if( kev.defaultPrevented ) {
|
|
388
|
+
ev.preventDefault();
|
|
389
|
+
ev.stopPropagation();
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
368
393
|
switch (ev.key) {
|
|
369
394
|
case "ArrowDown": {
|
|
370
395
|
this.navigate(kbNav.next);
|
|
@@ -1148,8 +1173,6 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
1148
1173
|
this._clearSelection( false );
|
|
1149
1174
|
this._addSelection(ref.ref,true);
|
|
1150
1175
|
}
|
|
1151
|
-
|
|
1152
|
-
this._toggleCell( ref.row, ref.col );
|
|
1153
1176
|
}
|
|
1154
1177
|
});
|
|
1155
1178
|
|
|
@@ -1170,7 +1193,8 @@ export class Spreadsheet<P extends SpreadsheetProps = SpreadsheetProps, E extend
|
|
|
1170
1193
|
const dev: EvDblClick = { context: { row: ref.row, col: ref.col } };
|
|
1171
1194
|
this.fire( "dblClick", dev );
|
|
1172
1195
|
|
|
1173
|
-
|
|
1196
|
+
// checkboxes are toggled, other cells are edited
|
|
1197
|
+
if( !dev.defaultPrevented && !this._toggleCell( ref.row, ref.col ) ) {
|
|
1174
1198
|
this.editCell( ref.row, ref.col );
|
|
1175
1199
|
}
|
|
1176
1200
|
}
|
package/src/core/core_router.ts
CHANGED
|
@@ -143,7 +143,13 @@ export class Router extends EventSource< RouterEvents > {
|
|
|
143
143
|
window.addEventListener('popstate', (event) => {
|
|
144
144
|
const url = this._getLocation( );
|
|
145
145
|
const found = this._find(url);
|
|
146
|
-
|
|
146
|
+
|
|
147
|
+
if( found.handlers.length==0 ) {
|
|
148
|
+
console.log( 'route not found: '+url );
|
|
149
|
+
this.fire( "error", {code: 404, message: "route not found" } );
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
147
153
|
found.handlers.forEach(h => {
|
|
148
154
|
h(found.params,url);
|
|
149
155
|
});
|
|
@@ -173,11 +179,17 @@ export class Router extends EventSource< RouterEvents > {
|
|
|
173
179
|
|
|
174
180
|
|
|
175
181
|
init() {
|
|
176
|
-
|
|
182
|
+
// the page is already in the history: replace its entry instead of adding one
|
|
183
|
+
this.navigate( this._getLocation(), true, true );
|
|
177
184
|
}
|
|
178
185
|
|
|
179
186
|
private _getLocation( ) {
|
|
180
|
-
|
|
187
|
+
if( !this.m_useHash ) {
|
|
188
|
+
return document.location.pathname;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// "#home" and "#/home" are the same route
|
|
192
|
+
return '/'+document.location.hash.substring(1).replace( /^\/+/, '' );
|
|
181
193
|
}
|
|
182
194
|
|
|
183
195
|
/**
|