nexusframework 0.3.0-beta.73 → 0.3.0-beta.74
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/LICENSE.md +55 -55
- package/README.md +12 -12
- package/about/index.json +5 -5
- package/about/index.nhp +4 -4
- package/icon.png +0 -0
- package/index.d.ts +0 -2
- package/index.js +0 -0
- package/index.js.map +1 -1
- package/index.ts +101 -101
- package/indexOfSkeleton.nhp +133 -133
- package/legacySkeleton.nhp +22 -22
- package/loader/overlay.css +1 -1
- package/loader/overlay.html +25 -25
- package/loader/overlay.scss +152 -152
- package/package.json +102 -90
- package/scripts/es5/compat.js +100 -100
- package/scripts/es5/compat.min.js +1 -1
- package/scripts/es5/loader.js +0 -0
- package/scripts/es5/loader.js.map +0 -0
- package/scripts/es5/loader.min.js +1 -1
- package/scripts/es5/loader.min.js.map +0 -0
- package/scripts/es5/nexusframeworkclient.js +1224 -1224
- package/scripts/es5/nexusframeworkclient.js.map +0 -0
- package/scripts/es5/nexusframeworkclient.min.js +1 -1
- package/scripts/es5/nexusframeworkclient.min.js.map +0 -0
- package/scripts/es6/compat.js +64 -64
- package/scripts/es6/compat.min.js +1 -1
- package/scripts/es6/loader.js +0 -0
- package/scripts/es6/loader.js.map +0 -0
- package/scripts/es6/loader.min.js +1 -1
- package/scripts/es6/loader.min.js.map +0 -0
- package/scripts/es6/nexusframeworkclient.js +1146 -1146
- package/scripts/es6/nexusframeworkclient.js.map +0 -0
- package/scripts/es6/nexusframeworkclient.min.js +1 -1
- package/scripts/es6/nexusframeworkclient.min.js.map +0 -0
- package/scripts/index.d.ts +259 -259
- package/scripts/src/compat.ts +62 -62
- package/scripts/src/loader.ts +385 -385
- package/scripts/src/nexusframeworkclient.ts +1163 -1163
- package/scripts/tsconfig.json +11 -11
- package/src/cli.d.ts +1 -0
- package/src/cli.js +102 -102
- package/src/cli.js.map +1 -1
- package/src/cli.ts +101 -101
- package/src/compileScripts.d.ts +0 -0
- package/src/compileScripts.js +0 -0
- package/src/compileScripts.js.map +1 -1
- package/src/compileScripts.ts +153 -153
- package/src/nexusframework.d.ts +5 -5
- package/src/nexusframework.js +44 -37
- package/src/nexusframework.js.map +1 -1
- package/src/nexusframework.ts +3631 -3622
- package/templates/basic/package.json +3 -3
- package/templates/basic/www/skeleton.nhp +1 -1
- package/templates/bootstrap/package.json +2 -2
- package/templates/bootstrap/www/skeleton.nhp +1 -1
- package/templates/bootstrap-material-design/package.json +2 -2
- package/templates/bootstrap-material-design/www/skeleton.nhp +1 -1
- package/tsconfig.json +25 -22
- package/types.d.ts +612 -612
|
@@ -1,1147 +1,1147 @@
|
|
|
1
|
-
/// <reference path="../index.d.ts" />
|
|
2
|
-
(function (window) {
|
|
3
|
-
const BSON = new window['bson'];
|
|
4
|
-
Object.defineProperties(window, {
|
|
5
|
-
NexusFrameworkTransport: {
|
|
6
|
-
configurable: true,
|
|
7
|
-
set: function (instance) {
|
|
8
|
-
Object.defineProperty(window, "NexusFrameworkTransport", {
|
|
9
|
-
value: instance
|
|
10
|
-
});
|
|
11
|
-
},
|
|
12
|
-
get: function () {
|
|
13
|
-
var impl;
|
|
14
|
-
if ("XMLHttpRequest" in window) {
|
|
15
|
-
class NexusFrameworkXMLHttpRequestResponse {
|
|
16
|
-
constructor(request, url, hadData, abResponse) {
|
|
17
|
-
this._url = url;
|
|
18
|
-
this.request = request;
|
|
19
|
-
this.hadData = hadData;
|
|
20
|
-
this.abResponse = abResponse;
|
|
21
|
-
}
|
|
22
|
-
get url() {
|
|
23
|
-
return this.request.responseURL || this._url;
|
|
24
|
-
}
|
|
25
|
-
get code() {
|
|
26
|
-
return this.request.status;
|
|
27
|
-
}
|
|
28
|
-
get contentLength() {
|
|
29
|
-
return parseInt(this.request.getResponseHeader("content-length")) || this.request.responseText.length;
|
|
30
|
-
}
|
|
31
|
-
get contentFromJSON() {
|
|
32
|
-
if (!this.parsedJson)
|
|
33
|
-
this.parsedJson = JSON.parse(this.request.responseText);
|
|
34
|
-
return this.parsedJson;
|
|
35
|
-
}
|
|
36
|
-
get contentFromBSON() {
|
|
37
|
-
if (!this.parsedBson) {
|
|
38
|
-
var response = this.request.response;
|
|
39
|
-
if (typeof response === "string")
|
|
40
|
-
response = Buffer.from(response, "utf8");
|
|
41
|
-
else
|
|
42
|
-
response = new Buffer(response);
|
|
43
|
-
this.parsedBson = BSON.deserialize(response, { promoteValues: true });
|
|
44
|
-
}
|
|
45
|
-
return this.parsedBson;
|
|
46
|
-
}
|
|
47
|
-
get contentAsString() {
|
|
48
|
-
return this.request.responseText;
|
|
49
|
-
}
|
|
50
|
-
get contentAsArrayBuffer() {
|
|
51
|
-
return this.request.response;
|
|
52
|
-
}
|
|
53
|
-
get headers() {
|
|
54
|
-
if (!this.processedHeaders) {
|
|
55
|
-
const headers = this.processedHeaders = {};
|
|
56
|
-
this.request.getAllResponseHeaders().split(/\r?\n/g).forEach(function (header) {
|
|
57
|
-
const index = header.indexOf(":");
|
|
58
|
-
var key, val;
|
|
59
|
-
if (index > 0) {
|
|
60
|
-
key = header.substring(0, index).trim().toLowerCase();
|
|
61
|
-
val = header.substring(index + 1).trim();
|
|
62
|
-
}
|
|
63
|
-
else
|
|
64
|
-
key = header.trim().toLowerCase();
|
|
65
|
-
var list = headers[key];
|
|
66
|
-
if (!list)
|
|
67
|
-
list = headers[key] = [];
|
|
68
|
-
if (val)
|
|
69
|
-
list.push(val);
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
return this.processedHeaders;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
const execute = function (method, url, data, cb, extraHeaders, progcb, wantsBinary) {
|
|
76
|
-
const request = new XMLHttpRequest();
|
|
77
|
-
var responseIsArrayBuffer;
|
|
78
|
-
try {
|
|
79
|
-
if (!wantsBinary)
|
|
80
|
-
throw false;
|
|
81
|
-
request.responseType = "arraybuffer";
|
|
82
|
-
if (request.responseType !== "arraybuffer") {
|
|
83
|
-
console.error("Could not request arraybuffer");
|
|
84
|
-
throw false;
|
|
85
|
-
}
|
|
86
|
-
responseIsArrayBuffer = true;
|
|
87
|
-
}
|
|
88
|
-
catch (e) {
|
|
89
|
-
responseIsArrayBuffer = false;
|
|
90
|
-
}
|
|
91
|
-
request.open(method, url, true);
|
|
92
|
-
if (extraHeaders)
|
|
93
|
-
Object.keys(extraHeaders).forEach(function (key) {
|
|
94
|
-
request.setRequestHeader(key, extraHeaders[key]);
|
|
95
|
-
});
|
|
96
|
-
if (progcb)
|
|
97
|
-
request.onprogress = function (ev) {
|
|
98
|
-
if (ev.lengthComputable && ev.total)
|
|
99
|
-
progcb(ev.loaded, ev.total);
|
|
100
|
-
};
|
|
101
|
-
request.onreadystatechange = function (e) {
|
|
102
|
-
if (request.readyState === XMLHttpRequest.DONE)
|
|
103
|
-
cb(new NexusFrameworkXMLHttpRequestResponse(request, url, !!data, responseIsArrayBuffer));
|
|
104
|
-
};
|
|
105
|
-
const type = data && data.type;
|
|
106
|
-
if (type)
|
|
107
|
-
switch (type) {
|
|
108
|
-
case "":
|
|
109
|
-
case "text/urlencoded":
|
|
110
|
-
var dat = "";
|
|
111
|
-
for (var entry of data.data) {
|
|
112
|
-
if (dat)
|
|
113
|
-
dat += "&";
|
|
114
|
-
dat += encodeURIComponent(entry[0]);
|
|
115
|
-
dat += "=";
|
|
116
|
-
dat += encodeURIComponent(entry[1]);
|
|
117
|
-
}
|
|
118
|
-
request.setRequestHeader("Content-Type", "text/urlencoded");
|
|
119
|
-
request.send(dat);
|
|
120
|
-
break;
|
|
121
|
-
case "text/json":
|
|
122
|
-
request.setRequestHeader("Content-Type", "text/json");
|
|
123
|
-
request.send(JSON.stringify(data.data));
|
|
124
|
-
break;
|
|
125
|
-
case "application/bson":
|
|
126
|
-
request.setRequestHeader("Content-Type", "application/bson");
|
|
127
|
-
request.send(BSON.serialize(data.data));
|
|
128
|
-
break;
|
|
129
|
-
case "multipart/form-data":
|
|
130
|
-
request.send(data.data);
|
|
131
|
-
break;
|
|
132
|
-
default:
|
|
133
|
-
request.send(data);
|
|
134
|
-
}
|
|
135
|
-
else
|
|
136
|
-
request.send(data);
|
|
137
|
-
};
|
|
138
|
-
impl = {
|
|
139
|
-
get: function (url, cb, extraHeaders, progcb, wantsBinary) {
|
|
140
|
-
execute("GET", url, undefined, cb, extraHeaders, progcb, wantsBinary);
|
|
141
|
-
},
|
|
142
|
-
head: function (url, cb, extraHeaders, progcb, wantsBinary) {
|
|
143
|
-
execute("HEAD", url, undefined, cb, extraHeaders, progcb, wantsBinary);
|
|
144
|
-
},
|
|
145
|
-
post: function (url, data, cb, extraHeaders, progcb, wantsBinary) {
|
|
146
|
-
execute("POST", url, data, cb, extraHeaders, progcb, wantsBinary);
|
|
147
|
-
},
|
|
148
|
-
put: function (url, data, cb, extraHeaders, progcb, wantsBinary) {
|
|
149
|
-
execute("PUT", url, data, cb, extraHeaders, progcb, wantsBinary);
|
|
150
|
-
},
|
|
151
|
-
execute,
|
|
152
|
-
del: function (url, cb, extraHeaders, progcb, wantsBinary) {
|
|
153
|
-
execute("DELETE", url, undefined, cb, extraHeaders, progcb, wantsBinary);
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
else
|
|
158
|
-
throw new Error("No transport implementations");
|
|
159
|
-
Object.defineProperty(window, "NexusFrameworkTransport", {
|
|
160
|
-
value: impl
|
|
161
|
-
});
|
|
162
|
-
return impl;
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
NexusFrameworkImpl: {
|
|
166
|
-
configurable: true,
|
|
167
|
-
set: function (instance) {
|
|
168
|
-
Object.defineProperty(window, "NexusFrameworkImpl", {
|
|
169
|
-
value: instance
|
|
170
|
-
});
|
|
171
|
-
},
|
|
172
|
-
get: function () {
|
|
173
|
-
const loader = window.NexusFrameworkLoader;
|
|
174
|
-
const showError = loader.showError;
|
|
175
|
-
const debug = /*function(...args: any[]){};*/ console.log.bind(console);
|
|
176
|
-
const GA_ANALYTICS = {
|
|
177
|
-
reportError: function (err, fatal) {
|
|
178
|
-
if (window.ga)
|
|
179
|
-
try {
|
|
180
|
-
window.ga('send', 'exception', {
|
|
181
|
-
'exDescription': (err.stack || "" + err).replace(/\n/g, "\n\t"),
|
|
182
|
-
'exFatal': fatal
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
catch (e) {
|
|
186
|
-
console.warn(e);
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
|
-
reportEvent: function (category, action, label, value) {
|
|
190
|
-
if (window.ga)
|
|
191
|
-
try {
|
|
192
|
-
window.ga('send', 'event', category, action, label, value);
|
|
193
|
-
}
|
|
194
|
-
catch (e) {
|
|
195
|
-
console.warn(e);
|
|
196
|
-
}
|
|
197
|
-
},
|
|
198
|
-
reportPage: function (path) {
|
|
199
|
-
if (window.ga)
|
|
200
|
-
try {
|
|
201
|
-
if (!path)
|
|
202
|
-
path = location.pathname;
|
|
203
|
-
window.ga('set', 'page', path);
|
|
204
|
-
window.ga('send', 'pageview');
|
|
205
|
-
}
|
|
206
|
-
catch (e) {
|
|
207
|
-
console.warn(e);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
const r = document.createElement("a");
|
|
212
|
-
const protocol = location.href.match(/^\w+:/)[0];
|
|
213
|
-
const resolveUrl = function (url) {
|
|
214
|
-
r.setAttribute("href", url);
|
|
215
|
-
var href = r.href;
|
|
216
|
-
if (/^\/\//.test(href))
|
|
217
|
-
href = protocol + href;
|
|
218
|
-
return href;
|
|
219
|
-
};
|
|
220
|
-
const convertResponse = function (res, url = location.href) {
|
|
221
|
-
var storage;
|
|
222
|
-
return (typeof res.data === "string") ? {
|
|
223
|
-
url,
|
|
224
|
-
code: res.code,
|
|
225
|
-
contentAsString: res.data,
|
|
226
|
-
get contentLength() {
|
|
227
|
-
return parseInt(res.headers['content-length'] && res.headers['content-length'][0]) || res.data.length;
|
|
228
|
-
},
|
|
229
|
-
get contentAsArrayBuffer() {
|
|
230
|
-
throw new Error("Not implemented");
|
|
231
|
-
},
|
|
232
|
-
get contentFromBSON() {
|
|
233
|
-
if (!storage)
|
|
234
|
-
storage = JSON.parse(res.data);
|
|
235
|
-
return storage;
|
|
236
|
-
},
|
|
237
|
-
get contentFromJSON() {
|
|
238
|
-
if (!storage)
|
|
239
|
-
storage = JSON.parse(res.data);
|
|
240
|
-
return storage;
|
|
241
|
-
},
|
|
242
|
-
headers: res.headers
|
|
243
|
-
} : {
|
|
244
|
-
url,
|
|
245
|
-
code: res.code,
|
|
246
|
-
get contentAsString() {
|
|
247
|
-
if (!storage)
|
|
248
|
-
storage = JSON.stringify(res.data);
|
|
249
|
-
return storage;
|
|
250
|
-
},
|
|
251
|
-
get contentAsArrayBuffer() {
|
|
252
|
-
throw new Error("Not implemented");
|
|
253
|
-
},
|
|
254
|
-
contentFromJSON: res.data,
|
|
255
|
-
contentFromBSON: res.data,
|
|
256
|
-
headers: res.headers,
|
|
257
|
-
get contentLength() {
|
|
258
|
-
var length = parseInt(res.headers['content-length'] && res.headers['content-length'][0]);
|
|
259
|
-
if (length)
|
|
260
|
-
return length;
|
|
261
|
-
if (!storage)
|
|
262
|
-
storage = JSON.stringify(res.data);
|
|
263
|
-
return storage.length;
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
};
|
|
267
|
-
const loaderContainerRegex = /(^|\s)loader\-(progress|error)\-container(\s|$)/;
|
|
268
|
-
class NexusFrameworkBase {
|
|
269
|
-
constructor(url = "/", io) {
|
|
270
|
-
this.activerid = 0;
|
|
271
|
-
this.components = {};
|
|
272
|
-
this.currentUserID = undefined;
|
|
273
|
-
this.progressVisible = false;
|
|
274
|
-
this.animationTiming = 500;
|
|
275
|
-
this.requestPage = this.defaultRequestPage;
|
|
276
|
-
this._listeners = {};
|
|
277
|
-
url = resolveUrl(url);
|
|
278
|
-
if (!/\/$/.test(url))
|
|
279
|
-
url += "/";
|
|
280
|
-
Object.defineProperties(this, {
|
|
281
|
-
io: {
|
|
282
|
-
value: io
|
|
283
|
-
},
|
|
284
|
-
url: {
|
|
285
|
-
value: url
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
this._analytics = GA_ANALYTICS;
|
|
289
|
-
}
|
|
290
|
-
resolveUrl(url) {
|
|
291
|
-
if (/^\w+\:/.test(url))
|
|
292
|
-
return url;
|
|
293
|
-
return resolveUrl(this.url + url);
|
|
294
|
-
}
|
|
295
|
-
disableAll(root = document.body) {
|
|
296
|
-
const focusable = root.querySelectorAll("a, input, select, textarea, iframe, button, *[focusable], *[tabindex]");
|
|
297
|
-
for (var i = 0; i < focusable.length; i++) {
|
|
298
|
-
const child = focusable[i];
|
|
299
|
-
const tabindex = child.getAttribute("tabindex");
|
|
300
|
-
if (tabindex == "-1")
|
|
301
|
-
return;
|
|
302
|
-
child.setAttribute("data-tabindex", tabindex ? tabindex : "");
|
|
303
|
-
child.setAttribute("data-tabindex", "-1");
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
enableAll(root = document.body) {
|
|
307
|
-
const focusable = root.querySelectorAll("*[data-tabindex]");
|
|
308
|
-
for (var i = 0; i < focusable.length; i++) {
|
|
309
|
-
const child = focusable[i];
|
|
310
|
-
child.setAttribute("tabindex", child.getAttribute("data-tabindex"));
|
|
311
|
-
child.removeAttribute("data-tabindex");
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
get analytics() {
|
|
315
|
-
return this._analytics;
|
|
316
|
-
}
|
|
317
|
-
set analytics(value) {
|
|
318
|
-
this._analytics = value ? value : GA_ANALYTICS;
|
|
319
|
-
}
|
|
320
|
-
reportError(err, fatal) {
|
|
321
|
-
console[fatal ? "error" : "warn"](err.stack);
|
|
322
|
-
if (this.errorreporter)
|
|
323
|
-
this.errorreporter(err, fatal);
|
|
324
|
-
}
|
|
325
|
-
installErrorReporter(errorreporter) {
|
|
326
|
-
this.errorreporter = errorreporter;
|
|
327
|
-
}
|
|
328
|
-
registerComponent(selector, impl) {
|
|
329
|
-
if (impl) {
|
|
330
|
-
this.components[selector] = impl;
|
|
331
|
-
this.createComponents(document.head);
|
|
332
|
-
this.createComponents(document.body);
|
|
333
|
-
}
|
|
334
|
-
else {
|
|
335
|
-
delete this.components[selector];
|
|
336
|
-
var destroy = (root) => {
|
|
337
|
-
const elements = root.querySelectorAll(selector);
|
|
338
|
-
if (!elements.length)
|
|
339
|
-
return;
|
|
340
|
-
for (var i = 0; i < elements.length; i++) {
|
|
341
|
-
const element = elements[i];
|
|
342
|
-
var components = element['__nf_cmapping__'];
|
|
343
|
-
if (!components)
|
|
344
|
-
components = element['__nf_cmapping__'] = {};
|
|
345
|
-
const component = components[selector];
|
|
346
|
-
if (component) {
|
|
347
|
-
try {
|
|
348
|
-
component.destroy();
|
|
349
|
-
}
|
|
350
|
-
catch (e) {
|
|
351
|
-
this.reportError(e);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
};
|
|
356
|
-
destroy(document.head);
|
|
357
|
-
destroy(document.body);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
unregisterComponent(selector, impl) {
|
|
361
|
-
}
|
|
362
|
-
createComponents(root) {
|
|
363
|
-
Object.keys(this.components).forEach((selector) => {
|
|
364
|
-
const elements = root.querySelectorAll(selector);
|
|
365
|
-
if (!elements.length)
|
|
366
|
-
return;
|
|
367
|
-
for (var i = 0; i < elements.length; i++) {
|
|
368
|
-
const element = elements[i];
|
|
369
|
-
var components = element['__nf_cmapping__'];
|
|
370
|
-
if (!components)
|
|
371
|
-
components = element['__nf_cmapping__'] = {};
|
|
372
|
-
if (components[selector])
|
|
373
|
-
continue;
|
|
374
|
-
const componentFactory = this.components[selector];
|
|
375
|
-
try {
|
|
376
|
-
(components[selector] = new componentFactory()).create(element);
|
|
377
|
-
}
|
|
378
|
-
catch (e) {
|
|
379
|
-
this.reportError(e);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
destroyComponents(root) {
|
|
385
|
-
Object.keys(this.components).forEach((selector) => {
|
|
386
|
-
const elements = root.querySelectorAll(selector);
|
|
387
|
-
if (!elements.length)
|
|
388
|
-
return;
|
|
389
|
-
for (var i = 0; i < elements.length; i++) {
|
|
390
|
-
const element = elements[i];
|
|
391
|
-
var components = element['__nf_cmapping__'];
|
|
392
|
-
if (!components)
|
|
393
|
-
components = element['__nf_cmapping__'] = {};
|
|
394
|
-
const component = components[selector];
|
|
395
|
-
if (component) {
|
|
396
|
-
try {
|
|
397
|
-
component.destroy();
|
|
398
|
-
}
|
|
399
|
-
catch (e) {
|
|
400
|
-
this.reportError(e);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
restoreComponents(root, state) {
|
|
407
|
-
Object.keys(this.components).forEach((selector) => {
|
|
408
|
-
const states = state[selector];
|
|
409
|
-
if (!states)
|
|
410
|
-
return;
|
|
411
|
-
const elements = root.querySelectorAll(selector);
|
|
412
|
-
if (!elements.length)
|
|
413
|
-
return;
|
|
414
|
-
for (var i = 0; i < elements.length; i++) {
|
|
415
|
-
const element = elements[i];
|
|
416
|
-
if (states.length) {
|
|
417
|
-
const _state = states.shift();
|
|
418
|
-
if (_state) {
|
|
419
|
-
var components = element['__nf_cmapping__'];
|
|
420
|
-
if (!components)
|
|
421
|
-
components = element['__nf_cmapping__'] = {};
|
|
422
|
-
var component = components[selector];
|
|
423
|
-
if (!component) {
|
|
424
|
-
const componentFactory = this.components[selector];
|
|
425
|
-
try {
|
|
426
|
-
(component = components[selector] = new componentFactory()).create(element);
|
|
427
|
-
}
|
|
428
|
-
catch (e) {
|
|
429
|
-
this.reportError(e);
|
|
430
|
-
console.warn(e);
|
|
431
|
-
continue;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
try {
|
|
435
|
-
component.restore(_state);
|
|
436
|
-
}
|
|
437
|
-
catch (e) {
|
|
438
|
-
this.reportError(e);
|
|
439
|
-
console.warn(e);
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
else
|
|
444
|
-
break;
|
|
445
|
-
}
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
saveComponents(root) {
|
|
449
|
-
var state = {};
|
|
450
|
-
Object.keys(this.components).forEach((selector) => {
|
|
451
|
-
const states = state[selector] = [];
|
|
452
|
-
const elements = root.querySelectorAll(selector);
|
|
453
|
-
if (!elements.length)
|
|
454
|
-
return;
|
|
455
|
-
for (var i = 0; i < elements.length; i++) {
|
|
456
|
-
const element = elements[i];
|
|
457
|
-
var components = element['__nf_cmapping__'];
|
|
458
|
-
if (!components)
|
|
459
|
-
components = element['__nf_cmapping__'] = {};
|
|
460
|
-
var component = components[selector];
|
|
461
|
-
if (!component) {
|
|
462
|
-
const componentFactory = this.components[selector];
|
|
463
|
-
try {
|
|
464
|
-
(component = components[selector] = new componentFactory).create(element);
|
|
465
|
-
}
|
|
466
|
-
catch (e) {
|
|
467
|
-
states.push(undefined);
|
|
468
|
-
this.reportError(e);
|
|
469
|
-
console.warn(e);
|
|
470
|
-
continue;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
try {
|
|
474
|
-
states.push(component.save());
|
|
475
|
-
}
|
|
476
|
-
catch (e) {
|
|
477
|
-
states.push(undefined);
|
|
478
|
-
this.reportError(e);
|
|
479
|
-
console.warn(e);
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
});
|
|
483
|
-
return state;
|
|
484
|
-
}
|
|
485
|
-
initPageSystem(opts) {
|
|
486
|
-
if (!loader)
|
|
487
|
-
return console.error("The NexusFramework Loader is required for the dynamic Page System to work correctly.");
|
|
488
|
-
if (!history.pushState || !history.replaceState)
|
|
489
|
-
return console.warn("This browser is missing an essential feature required for the dynamic Page System.");
|
|
490
|
-
if (this.pagesyshandler)
|
|
491
|
-
return console.warn("Page System already initialized, ignoring additional requests to initialize.");
|
|
492
|
-
opts = opts || {};
|
|
493
|
-
const self = this;
|
|
494
|
-
var pageStatesSize = 0;
|
|
495
|
-
const pageStates = [];
|
|
496
|
-
this.animationTiming = opts.animationTiming || 500;
|
|
497
|
-
const pageStateCacheSize = opts.pageHistoryCacheSize || 25000000;
|
|
498
|
-
const wrapCB = (cb) => {
|
|
499
|
-
return (res) => {
|
|
500
|
-
const user = res.headers['x-logged-user'];
|
|
501
|
-
if (user)
|
|
502
|
-
self.currentUserID = user[0];
|
|
503
|
-
else
|
|
504
|
-
self.currentUserID = undefined;
|
|
505
|
-
debug("Current UID", self.currentUserID);
|
|
506
|
-
cb(res);
|
|
507
|
-
if (res.code === 200)
|
|
508
|
-
setTimeout(() => {
|
|
509
|
-
self._analytics.reportPage();
|
|
510
|
-
});
|
|
511
|
-
};
|
|
512
|
-
};
|
|
513
|
-
loader.showError = function (error) {
|
|
514
|
-
const match = location.href.match(beforeHash);
|
|
515
|
-
const baseurl = match && match[1] || location.href;
|
|
516
|
-
const length = error.stack.length;
|
|
517
|
-
const tooBig = pageStateCacheSize <= length;
|
|
518
|
-
try {
|
|
519
|
-
const cPageStates = pageStates.length;
|
|
520
|
-
for (var i = 0; i < cPageStates; i++) {
|
|
521
|
-
const state = pageStates[i];
|
|
522
|
-
if (state.url === baseurl) {
|
|
523
|
-
if (tooBig) {
|
|
524
|
-
pageStates.splice(i, 1);
|
|
525
|
-
pageStatesSize -= state.size;
|
|
526
|
-
}
|
|
527
|
-
else {
|
|
528
|
-
state.data = { error };
|
|
529
|
-
state.updated = +new Date;
|
|
530
|
-
pageStatesSize += length - state.size;
|
|
531
|
-
}
|
|
532
|
-
throw true;
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
if (tooBig)
|
|
536
|
-
throw true;
|
|
537
|
-
pageStates.push({
|
|
538
|
-
url: baseurl,
|
|
539
|
-
data: { error },
|
|
540
|
-
updated: +new Date,
|
|
541
|
-
size: length
|
|
542
|
-
});
|
|
543
|
-
pageStatesSize += length;
|
|
544
|
-
}
|
|
545
|
-
catch (e) {
|
|
546
|
-
if (e !== true)
|
|
547
|
-
throw e;
|
|
548
|
-
}
|
|
549
|
-
return showError(error);
|
|
550
|
-
};
|
|
551
|
-
const transportPageSystem = {
|
|
552
|
-
requestPage(path, cb, post, rid) {
|
|
553
|
-
if (self.pagesysprerequest && !self.pagesysprerequest(path)) {
|
|
554
|
-
self.defaultRequestPage(path, post);
|
|
555
|
-
return;
|
|
556
|
-
}
|
|
557
|
-
if (!opts.noprogress) {
|
|
558
|
-
self.disableAll();
|
|
559
|
-
loader.showProgress();
|
|
560
|
-
}
|
|
561
|
-
const url = self.resolveUrl(":pagesys/" + path);
|
|
562
|
-
const _cb = opts.noprogress ? cb : function (res) {
|
|
563
|
-
if (opts.noprogress)
|
|
564
|
-
cb(res);
|
|
565
|
-
else
|
|
566
|
-
loader.showProgress(() => {
|
|
567
|
-
cb(res);
|
|
568
|
-
self.enableAll();
|
|
569
|
-
});
|
|
570
|
-
};
|
|
571
|
-
if (post)
|
|
572
|
-
window.NexusFrameworkTransport.post(url, post, wrapCB(_cb), undefined, undefined, true);
|
|
573
|
-
else
|
|
574
|
-
window.NexusFrameworkTransport.get(url, wrapCB(_cb), undefined, undefined, true);
|
|
575
|
-
}
|
|
576
|
-
};
|
|
577
|
-
if (!opts.noio && this.io) {
|
|
578
|
-
const io = this.io;
|
|
579
|
-
this.pagesysimpl = {
|
|
580
|
-
requestPage(path, cb, post, rid) {
|
|
581
|
-
if (io.connected) {
|
|
582
|
-
if (self.pagesysprerequest && !self.pagesysprerequest(path)) {
|
|
583
|
-
self.defaultRequestPage(path, post);
|
|
584
|
-
return;
|
|
585
|
-
}
|
|
586
|
-
if (!opts.noprogress) {
|
|
587
|
-
self.disableAll();
|
|
588
|
-
loader.showProgress();
|
|
589
|
-
}
|
|
590
|
-
const headers = {
|
|
591
|
-
"referrer": location.href
|
|
592
|
-
};
|
|
593
|
-
var val = document.cookie;
|
|
594
|
-
if (val)
|
|
595
|
-
headers['cookie'] = val;
|
|
596
|
-
io.emit("page", post ? "POST" : "GET", path, post, headers, function (res) {
|
|
597
|
-
if (rid != self.activerid)
|
|
598
|
-
return;
|
|
599
|
-
const cookies = res.headers['set-cookie'];
|
|
600
|
-
if (cookies) {
|
|
601
|
-
cookies.forEach(function (cookie) {
|
|
602
|
-
document.cookie = cookie;
|
|
603
|
-
});
|
|
604
|
-
}
|
|
605
|
-
const _cb = opts.noprogress ? cb : function (res) {
|
|
606
|
-
if (!opts.noprogress)
|
|
607
|
-
cb(res);
|
|
608
|
-
else
|
|
609
|
-
loader.showProgress(() => {
|
|
610
|
-
cb(res);
|
|
611
|
-
self.enableAll();
|
|
612
|
-
});
|
|
613
|
-
};
|
|
614
|
-
wrapCB(_cb)(convertResponse(res, self.resolveUrl(path)));
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
else
|
|
618
|
-
transportPageSystem.requestPage(path, cb, post, rid);
|
|
619
|
-
}
|
|
620
|
-
};
|
|
621
|
-
}
|
|
622
|
-
else
|
|
623
|
-
this.pagesysimpl = transportPageSystem;
|
|
624
|
-
var base = document.getElementsByTagName("base");
|
|
625
|
-
base = base && base[0];
|
|
626
|
-
this.pagesysprerequest = opts.prerequest;
|
|
627
|
-
this.pagesyshandler = opts.handler || ((res, pageReady) => {
|
|
628
|
-
try {
|
|
629
|
-
const contentType = res.headers['content-type'][0];
|
|
630
|
-
if (!/\/html(;.+)?$/.test(contentType.toLowerCase())) {
|
|
631
|
-
throw new Error("Content type is not html");
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
catch (e) { }
|
|
635
|
-
const content = res.contentAsString;
|
|
636
|
-
const bodyindex = content.indexOf("<body");
|
|
637
|
-
if (bodyindex == -1)
|
|
638
|
-
throw new Error("Could not find start of body tag");
|
|
639
|
-
const endbodyindex = content.indexOf("</body>") || content.indexOf("</ body>");
|
|
640
|
-
if (endbodyindex == -1)
|
|
641
|
-
throw new Error("Could not find end of body tag");
|
|
642
|
-
const title = content.match(/<title>([^<]+)<\/\s*title>/);
|
|
643
|
-
document.title = title && title[1] || "Title Not Set";
|
|
644
|
-
const mockhtml = document.createElement("html");
|
|
645
|
-
mockhtml.innerHTML = content.substring(bodyindex, endbodyindex) + "</body>";
|
|
646
|
-
var loaderScript;
|
|
647
|
-
var childs = mockhtml.children;
|
|
648
|
-
const toAdd = [];
|
|
649
|
-
for (var i = 0; i < childs.length; i++) {
|
|
650
|
-
const child = childs[i];
|
|
651
|
-
switch (child.nodeName.toUpperCase()) {
|
|
652
|
-
case "HEAD":
|
|
653
|
-
break;
|
|
654
|
-
case "BODY":
|
|
655
|
-
i = -1;
|
|
656
|
-
childs = child.children;
|
|
657
|
-
break;
|
|
658
|
-
case "SCRIPT":
|
|
659
|
-
const match = child.innerHTML.match(/^NexusFrameworkLoader\.load\((.+)\);?$/);
|
|
660
|
-
if (match)
|
|
661
|
-
loaderScript = JSON.parse(match[1]);
|
|
662
|
-
break;
|
|
663
|
-
default:
|
|
664
|
-
if (loaderContainerRegex.test(child.className))
|
|
665
|
-
break;
|
|
666
|
-
toAdd.push(child);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
if (!toAdd.length)
|
|
670
|
-
throw new Error("Nothing found in response to add to page");
|
|
671
|
-
if (!loaderScript)
|
|
672
|
-
throw new Error("NexusFrameworkLoader script not found...");
|
|
673
|
-
childs = document.body.children;
|
|
674
|
-
for (var i = childs.length - 1; i >= 0; i--) {
|
|
675
|
-
const child = childs[i];
|
|
676
|
-
switch (child.nodeName.toUpperCase()) {
|
|
677
|
-
case "LINK":
|
|
678
|
-
case "SCRIPT":
|
|
679
|
-
break;
|
|
680
|
-
default:
|
|
681
|
-
if (loaderContainerRegex.test(child.className))
|
|
682
|
-
break;
|
|
683
|
-
document.body.removeChild(child);
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
const fragment = document.createDocumentFragment();
|
|
687
|
-
toAdd.forEach((el) => {
|
|
688
|
-
fragment.appendChild(el);
|
|
689
|
-
this.createComponents(el);
|
|
690
|
-
});
|
|
691
|
-
document.body.appendChild(fragment);
|
|
692
|
-
loader.load(loaderScript, function () {
|
|
693
|
-
self.progressVisible = true;
|
|
694
|
-
loader.resetProgress();
|
|
695
|
-
pageReady();
|
|
696
|
-
});
|
|
697
|
-
return true;
|
|
698
|
-
});
|
|
699
|
-
var forwardPopState;
|
|
700
|
-
const beforeHash = /^([^#]+)(#.*)?$/;
|
|
701
|
-
var chash = location.href.match(beforeHash)[1];
|
|
702
|
-
const startsWith = new RegExp("^" + this.url.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&") + "(.*)$", "i");
|
|
703
|
-
class AnchorElementComponent {
|
|
704
|
-
constructor() {
|
|
705
|
-
this.handler = (e) => {
|
|
706
|
-
if (this.element.hasAttribute("data-nopagesys") || this.element.hasAttribute("data-nodynamic"))
|
|
707
|
-
return;
|
|
708
|
-
const href = this.element.getAttribute("href");
|
|
709
|
-
if (!href || !href.length)
|
|
710
|
-
return;
|
|
711
|
-
var url = this.element.href;
|
|
712
|
-
const bhash = url.match(beforeHash);
|
|
713
|
-
if (bhash[2] && chash === bhash[1])
|
|
714
|
-
return;
|
|
715
|
-
if (startsWith.test(url))
|
|
716
|
-
try {
|
|
717
|
-
const match = url.match(/^(.+)#.*$/);
|
|
718
|
-
if (match)
|
|
719
|
-
url = match[1];
|
|
720
|
-
self.requestPage(url.substring(self.url.length));
|
|
721
|
-
try {
|
|
722
|
-
e.stopPropagation();
|
|
723
|
-
}
|
|
724
|
-
catch (e) { }
|
|
725
|
-
try {
|
|
726
|
-
e.preventDefault();
|
|
727
|
-
}
|
|
728
|
-
catch (e) { }
|
|
729
|
-
}
|
|
730
|
-
catch (e) {
|
|
731
|
-
console.warn(e);
|
|
732
|
-
}
|
|
733
|
-
else
|
|
734
|
-
debug("Navigating", url);
|
|
735
|
-
};
|
|
736
|
-
}
|
|
737
|
-
create(element) {
|
|
738
|
-
element.addEventListener("click", this.handler);
|
|
739
|
-
this.element = element;
|
|
740
|
-
}
|
|
741
|
-
destroy() {
|
|
742
|
-
this.element.removeEventListener("click", this.handler);
|
|
743
|
-
}
|
|
744
|
-
restore(data) { }
|
|
745
|
-
save() { }
|
|
746
|
-
}
|
|
747
|
-
class FormElementComponent {
|
|
748
|
-
constructor() {
|
|
749
|
-
this.handler = (e) => {
|
|
750
|
-
if (this.element.hasAttribute("data-nopagesys") || this.element.hasAttribute("data-nodynamic"))
|
|
751
|
-
return;
|
|
752
|
-
const method = this.element.getAttribute("method") || "get";
|
|
753
|
-
const enctype = this.element.getAttribute("enctype") || "text/urlencoded";
|
|
754
|
-
const action = this.element.getAttribute("action") || "";
|
|
755
|
-
var url = resolveUrl(action);
|
|
756
|
-
if (startsWith.test(url)) {
|
|
757
|
-
try {
|
|
758
|
-
const match = url.match(/^(.+)#.*$/);
|
|
759
|
-
if (match)
|
|
760
|
-
url = match[1];
|
|
761
|
-
const formData = new FormData(this.element);
|
|
762
|
-
loader.showProgress(undefined, "Submitting Form", "Your form data is being processed");
|
|
763
|
-
if (method.trim().toLowerCase() === "post")
|
|
764
|
-
self.requestPage(url.substring(self.url.length), { type: enctype, data: formData });
|
|
765
|
-
else {
|
|
766
|
-
var first = true;
|
|
767
|
-
var reqUrl = url.substring(self.url.length) + "?";
|
|
768
|
-
for (var entry of formData) {
|
|
769
|
-
if (first)
|
|
770
|
-
first = false;
|
|
771
|
-
else
|
|
772
|
-
reqUrl += "&";
|
|
773
|
-
reqUrl += encodeURIComponent(entry[0]);
|
|
774
|
-
reqUrl += "=";
|
|
775
|
-
reqUrl += encodeURIComponent(entry[1]);
|
|
776
|
-
}
|
|
777
|
-
self.requestPage(reqUrl);
|
|
778
|
-
}
|
|
779
|
-
try {
|
|
780
|
-
e.stopPropagation();
|
|
781
|
-
}
|
|
782
|
-
catch (e) { }
|
|
783
|
-
try {
|
|
784
|
-
e.preventDefault();
|
|
785
|
-
}
|
|
786
|
-
catch (e) { }
|
|
787
|
-
Array.prototype.forEach.call(this.element.querySelectorAll("a, input, select, textarea, iframe, button, *[name]"), function (el) {
|
|
788
|
-
el.setAttribute("disabled", "disabled");
|
|
789
|
-
el.className += " disabled";
|
|
790
|
-
});
|
|
791
|
-
Array.prototype.forEach.call(this.element.querySelectorAll("button:not([type]), button[type=submit]"), function (button) {
|
|
792
|
-
button.innerHTML = "Processing Request";
|
|
793
|
-
});
|
|
794
|
-
Array.prototype.forEach.call(this.element.querySelectorAll("input[type=submit]"), function (input) {
|
|
795
|
-
input.value = "Processing Request";
|
|
796
|
-
});
|
|
797
|
-
return;
|
|
798
|
-
}
|
|
799
|
-
catch (e) {
|
|
800
|
-
debug(e);
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
debug("Submitting", url);
|
|
804
|
-
};
|
|
805
|
-
}
|
|
806
|
-
create(element) {
|
|
807
|
-
element.addEventListener("submit", this.handler);
|
|
808
|
-
this.element = element;
|
|
809
|
-
}
|
|
810
|
-
destroy() {
|
|
811
|
-
this.element.removeEventListener("submit", this.handler);
|
|
812
|
-
}
|
|
813
|
-
restore(data) { }
|
|
814
|
-
save() { }
|
|
815
|
-
}
|
|
816
|
-
const requestPage = this.requestPage = (path, post, replace = false) => {
|
|
817
|
-
const match = path.match(/\.([^\/]+)([\?#].*)?$/);
|
|
818
|
-
if (match && match[0] !== "htm" && match[0] !== "html") {
|
|
819
|
-
this.defaultRequestPage(path, post);
|
|
820
|
-
debug("Ignoring navigation", path, match);
|
|
821
|
-
}
|
|
822
|
-
else {
|
|
823
|
-
debug("requestPage", path);
|
|
824
|
-
const rid = ++self.activerid;
|
|
825
|
-
const baseurl = this.resolveUrl(path);
|
|
826
|
-
const fullurl = baseurl + (match && match[2] || "");
|
|
827
|
-
if (replace)
|
|
828
|
-
history.replaceState(!!post, "Loading...", fullurl);
|
|
829
|
-
else {
|
|
830
|
-
history.pushState(!!post, "Loading...", fullurl);
|
|
831
|
-
chash = fullurl.match(beforeHash)[1];
|
|
832
|
-
window.scrollTo(0, 0);
|
|
833
|
-
}
|
|
834
|
-
loader.resetError();
|
|
835
|
-
loader.resetProgress();
|
|
836
|
-
this.pagesysimpl.requestPage(path, (res) => {
|
|
837
|
-
try {
|
|
838
|
-
if (rid !== self.activerid)
|
|
839
|
-
return;
|
|
840
|
-
if (!res.code) {
|
|
841
|
-
loader.showProgress(undefined, "Connection Issue", "Check your network and try again");
|
|
842
|
-
document.title = "Connection Issue";
|
|
843
|
-
history.replaceState(res.hadData, "Connection Issue", fullurl);
|
|
844
|
-
setTimeout(function () {
|
|
845
|
-
if (rid !== self.activerid)
|
|
846
|
-
return;
|
|
847
|
-
requestPage(path, post, true);
|
|
848
|
-
}, 900000);
|
|
849
|
-
return;
|
|
850
|
-
}
|
|
851
|
-
else if (res.code === 502 || res.code === 503) {
|
|
852
|
-
loader.showProgress(undefined, "Scheduled Maintenance", "Sorry but this website is undergoing scheduled maintenance");
|
|
853
|
-
document.title = "Scheduled Maintenance";
|
|
854
|
-
history.replaceState(res.hadData, "Scheduled Maintenance", fullurl);
|
|
855
|
-
setTimeout(function () {
|
|
856
|
-
if (rid !== self.activerid)
|
|
857
|
-
return;
|
|
858
|
-
requestPage(path, post, true);
|
|
859
|
-
}, 900000);
|
|
860
|
-
return;
|
|
861
|
-
}
|
|
862
|
-
const location = res.headers['x-location'] || res.headers['location'];
|
|
863
|
-
if (location) {
|
|
864
|
-
const url = resolveUrl(location[0]);
|
|
865
|
-
if (startsWith.test(url)) {
|
|
866
|
-
this.requestPage(url.substring(this.url.length), undefined, true);
|
|
867
|
-
return;
|
|
868
|
-
}
|
|
869
|
-
console.warn("Requested redirect to external url:", url);
|
|
870
|
-
window.location.href = url;
|
|
871
|
-
return;
|
|
872
|
-
}
|
|
873
|
-
const contentDisposition = res.headers['content-disposition'];
|
|
874
|
-
if (contentDisposition && contentDisposition[0].toLowerCase() == "attachment")
|
|
875
|
-
throw new Error("Attachment disposition");
|
|
876
|
-
this.pagesyshandler(res, function () {
|
|
877
|
-
if (rid !== self.activerid)
|
|
878
|
-
return;
|
|
879
|
-
try {
|
|
880
|
-
const title = document.title;
|
|
881
|
-
const length = res.contentLength;
|
|
882
|
-
const tooBig = pageStateCacheSize <= length;
|
|
883
|
-
const stateData = tooBig ? undefined : {
|
|
884
|
-
title: title,
|
|
885
|
-
user: self.currentUserID,
|
|
886
|
-
scroll: [window.scrollX, window.scrollY],
|
|
887
|
-
body: self.saveComponents(document.body),
|
|
888
|
-
basehref: base ? base['href'] : undefined,
|
|
889
|
-
response: res
|
|
890
|
-
};
|
|
891
|
-
var i;
|
|
892
|
-
const cPageStates = pageStates.length;
|
|
893
|
-
try {
|
|
894
|
-
for (i = 0; i < cPageStates; i++) {
|
|
895
|
-
const state = pageStates[i];
|
|
896
|
-
if (state.url === baseurl) {
|
|
897
|
-
if (tooBig) {
|
|
898
|
-
pageStates.splice(i, 1);
|
|
899
|
-
pageStatesSize -= state.size;
|
|
900
|
-
}
|
|
901
|
-
else {
|
|
902
|
-
state.data = stateData;
|
|
903
|
-
state.updated = +new Date;
|
|
904
|
-
pageStatesSize += length - state.size;
|
|
905
|
-
}
|
|
906
|
-
throw true;
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
if (!tooBig) {
|
|
910
|
-
pageStates.push({
|
|
911
|
-
url: baseurl,
|
|
912
|
-
data: stateData,
|
|
913
|
-
updated: +new Date,
|
|
914
|
-
size: length
|
|
915
|
-
});
|
|
916
|
-
pageStatesSize += length;
|
|
917
|
-
throw true;
|
|
918
|
-
}
|
|
919
|
-
else
|
|
920
|
-
debug("Response too big to store", baseurl, length);
|
|
921
|
-
}
|
|
922
|
-
catch (e) {
|
|
923
|
-
if (e === true) {
|
|
924
|
-
const over = pageStatesSize - pageStateCacheSize;
|
|
925
|
-
if (over > 0) {
|
|
926
|
-
pageStates.sort(function (a, b) {
|
|
927
|
-
return a.updated - b.updated;
|
|
928
|
-
});
|
|
929
|
-
var found = 0;
|
|
930
|
-
for (i = 0; i < cPageStates; i++) {
|
|
931
|
-
found += pageStates[i].size;
|
|
932
|
-
if (found >= over)
|
|
933
|
-
break;
|
|
934
|
-
}
|
|
935
|
-
i++;
|
|
936
|
-
pageStates.splice(0, i);
|
|
937
|
-
debug("Trimmed", i, "items...");
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
else
|
|
941
|
-
throw e;
|
|
942
|
-
}
|
|
943
|
-
history.replaceState(res.hadData, document.title, fullurl);
|
|
944
|
-
self.emit("page", baseurl, path);
|
|
945
|
-
}
|
|
946
|
-
catch (e) {
|
|
947
|
-
debug(e);
|
|
948
|
-
if (replace)
|
|
949
|
-
window.location.reload(true);
|
|
950
|
-
else
|
|
951
|
-
try {
|
|
952
|
-
chash = undefined;
|
|
953
|
-
forwardPopState = [path, post];
|
|
954
|
-
debug("Going backwards");
|
|
955
|
-
history.go(-1);
|
|
956
|
-
}
|
|
957
|
-
catch (e) { }
|
|
958
|
-
}
|
|
959
|
-
});
|
|
960
|
-
}
|
|
961
|
-
catch (e) {
|
|
962
|
-
debug(e);
|
|
963
|
-
if (replace)
|
|
964
|
-
window.location.reload(true);
|
|
965
|
-
else
|
|
966
|
-
try {
|
|
967
|
-
chash = undefined;
|
|
968
|
-
forwardPopState = [path, post];
|
|
969
|
-
debug("Going backwards");
|
|
970
|
-
history.go(-1);
|
|
971
|
-
}
|
|
972
|
-
catch (e) { }
|
|
973
|
-
}
|
|
974
|
-
}, post, rid);
|
|
975
|
-
}
|
|
976
|
-
};
|
|
977
|
-
this.registerComponent("a", AnchorElementComponent);
|
|
978
|
-
this.registerComponent("form", FormElementComponent);
|
|
979
|
-
window.addEventListener('popstate', (e) => {
|
|
980
|
-
try {
|
|
981
|
-
var state;
|
|
982
|
-
const bhash = location.href.match(beforeHash);
|
|
983
|
-
const cStates = pageStates.length;
|
|
984
|
-
const baseurl = bhash[1];
|
|
985
|
-
for (var i = 0; i < cStates; i++) {
|
|
986
|
-
const _state = pageStates[i];
|
|
987
|
-
if (_state.url === baseurl) {
|
|
988
|
-
state = _state;
|
|
989
|
-
break;
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
const hasState = !!state;
|
|
993
|
-
const rid = ++self.activerid;
|
|
994
|
-
const error = hasState && state.data.error;
|
|
995
|
-
if (error) {
|
|
996
|
-
showError(error);
|
|
997
|
-
return;
|
|
998
|
-
}
|
|
999
|
-
if (forwardPopState) {
|
|
1000
|
-
debug("forwardPopState", forwardPopState);
|
|
1001
|
-
const forward = forwardPopState;
|
|
1002
|
-
setTimeout(() => {
|
|
1003
|
-
if (rid === self.activerid)
|
|
1004
|
-
self.defaultRequestPage(forward[0], forward[1]);
|
|
1005
|
-
});
|
|
1006
|
-
forwardPopState = undefined;
|
|
1007
|
-
return;
|
|
1008
|
-
}
|
|
1009
|
-
if (chash === baseurl) {
|
|
1010
|
-
debug("Only hash has changed...", baseurl);
|
|
1011
|
-
return;
|
|
1012
|
-
}
|
|
1013
|
-
chash = bhash[1];
|
|
1014
|
-
if (e.state)
|
|
1015
|
-
alert("You submitted data to this page, which cannot be re-sent. Because of this, what you're viewing may not be the same now.");
|
|
1016
|
-
if (!hasState)
|
|
1017
|
-
throw new Error("No state for: " + baseurl + ", reloading...");
|
|
1018
|
-
if (state.data.user != self.currentUserID)
|
|
1019
|
-
throw new Error("User has changed since state was created, reloading...");
|
|
1020
|
-
document.title = state.data.title;
|
|
1021
|
-
loader.resetProgress();
|
|
1022
|
-
loader.resetError();
|
|
1023
|
-
self.pagesyshandler(state.data.response, function (err) {
|
|
1024
|
-
if (rid !== self.activerid)
|
|
1025
|
-
return;
|
|
1026
|
-
try {
|
|
1027
|
-
if (err)
|
|
1028
|
-
throw err;
|
|
1029
|
-
if (state.data.body)
|
|
1030
|
-
self.restoreComponents(document.body, state.data.body);
|
|
1031
|
-
if (state.data.scroll)
|
|
1032
|
-
window.scrollTo.apply(window, state.data.scroll);
|
|
1033
|
-
debug("Used stored page state!", state);
|
|
1034
|
-
}
|
|
1035
|
-
catch (err) {
|
|
1036
|
-
debug(err);
|
|
1037
|
-
var url = location.href;
|
|
1038
|
-
if (startsWith.test(url)) {
|
|
1039
|
-
try {
|
|
1040
|
-
if (/reloading\.\.\.$/.test(err.message)) {
|
|
1041
|
-
self.requestPage(url.substring(self.url.length), undefined, true);
|
|
1042
|
-
return;
|
|
1043
|
-
}
|
|
1044
|
-
console.error("Unknown error occured, actually navigating to page...");
|
|
1045
|
-
}
|
|
1046
|
-
catch (e) {
|
|
1047
|
-
console.error(e);
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
location.reload(true);
|
|
1051
|
-
}
|
|
1052
|
-
});
|
|
1053
|
-
}
|
|
1054
|
-
catch (err) {
|
|
1055
|
-
debug(err);
|
|
1056
|
-
var url = location.href;
|
|
1057
|
-
if (startsWith.test(url)) {
|
|
1058
|
-
try {
|
|
1059
|
-
if (/reloading\.\.\.$/.test(err.message)) {
|
|
1060
|
-
self.requestPage(url.substring(self.url.length), undefined, true);
|
|
1061
|
-
return;
|
|
1062
|
-
}
|
|
1063
|
-
console.error("Unknown error occured, actually navigating to page...");
|
|
1064
|
-
}
|
|
1065
|
-
catch (e) {
|
|
1066
|
-
console.error(e);
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
location.reload(true);
|
|
1070
|
-
}
|
|
1071
|
-
});
|
|
1072
|
-
return true;
|
|
1073
|
-
}
|
|
1074
|
-
get currentUser() {
|
|
1075
|
-
return this.currentUserID;
|
|
1076
|
-
}
|
|
1077
|
-
defaultRequestPage(path, post) {
|
|
1078
|
-
if (post)
|
|
1079
|
-
throw new Error("Posting is not supported without an initialized page system, yet");
|
|
1080
|
-
else
|
|
1081
|
-
location.href = this.resolveUrl(path);
|
|
1082
|
-
}
|
|
1083
|
-
on(event, cb) {
|
|
1084
|
-
var listeners = this._listeners[event];
|
|
1085
|
-
if (listeners)
|
|
1086
|
-
listeners.push(cb);
|
|
1087
|
-
else
|
|
1088
|
-
this._listeners[event] = listeners = [cb];
|
|
1089
|
-
}
|
|
1090
|
-
off(event, cb) {
|
|
1091
|
-
var index;
|
|
1092
|
-
var listeners = this._listeners[event];
|
|
1093
|
-
if (listeners && (index = listeners.indexOf(cb)) > -1)
|
|
1094
|
-
listeners.splice(index, 1);
|
|
1095
|
-
}
|
|
1096
|
-
emit(event, ...args) {
|
|
1097
|
-
const self = this;
|
|
1098
|
-
const listeners = this._listeners[event];
|
|
1099
|
-
if (listeners)
|
|
1100
|
-
listeners.forEach(function (cb) {
|
|
1101
|
-
cb.apply(self, args);
|
|
1102
|
-
});
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
var impl;
|
|
1106
|
-
if (window.io)
|
|
1107
|
-
impl = class NexusFrameworkWithIO extends NexusFrameworkBase {
|
|
1108
|
-
constructor(url) {
|
|
1109
|
-
super(url, window.io({
|
|
1110
|
-
path: "/:io"
|
|
1111
|
-
}));
|
|
1112
|
-
const io = this.io;
|
|
1113
|
-
io.on("connect", function () {
|
|
1114
|
-
io.emit("init", loader.requestedResources());
|
|
1115
|
-
});
|
|
1116
|
-
}
|
|
1117
|
-
};
|
|
1118
|
-
else
|
|
1119
|
-
impl = class NexusFrameworkNoIO extends NexusFrameworkBase {
|
|
1120
|
-
constructor(url) {
|
|
1121
|
-
super(url);
|
|
1122
|
-
}
|
|
1123
|
-
};
|
|
1124
|
-
Object.defineProperty(window, "NexusFrameworkImpl", {
|
|
1125
|
-
value: impl
|
|
1126
|
-
});
|
|
1127
|
-
return impl;
|
|
1128
|
-
}
|
|
1129
|
-
},
|
|
1130
|
-
NexusFrameworkClient: {
|
|
1131
|
-
configurable: true,
|
|
1132
|
-
set: function (instance) {
|
|
1133
|
-
Object.defineProperty(window, "NexusFrameworkClient", {
|
|
1134
|
-
value: instance
|
|
1135
|
-
});
|
|
1136
|
-
},
|
|
1137
|
-
get: function () {
|
|
1138
|
-
const instance = new window.NexusFrameworkImpl();
|
|
1139
|
-
Object.defineProperty(window, "NexusFrameworkClient", {
|
|
1140
|
-
value: instance
|
|
1141
|
-
});
|
|
1142
|
-
return instance;
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
});
|
|
1146
|
-
})(window);
|
|
1
|
+
/// <reference path="../index.d.ts" />
|
|
2
|
+
(function (window) {
|
|
3
|
+
const BSON = new window['bson'];
|
|
4
|
+
Object.defineProperties(window, {
|
|
5
|
+
NexusFrameworkTransport: {
|
|
6
|
+
configurable: true,
|
|
7
|
+
set: function (instance) {
|
|
8
|
+
Object.defineProperty(window, "NexusFrameworkTransport", {
|
|
9
|
+
value: instance
|
|
10
|
+
});
|
|
11
|
+
},
|
|
12
|
+
get: function () {
|
|
13
|
+
var impl;
|
|
14
|
+
if ("XMLHttpRequest" in window) {
|
|
15
|
+
class NexusFrameworkXMLHttpRequestResponse {
|
|
16
|
+
constructor(request, url, hadData, abResponse) {
|
|
17
|
+
this._url = url;
|
|
18
|
+
this.request = request;
|
|
19
|
+
this.hadData = hadData;
|
|
20
|
+
this.abResponse = abResponse;
|
|
21
|
+
}
|
|
22
|
+
get url() {
|
|
23
|
+
return this.request.responseURL || this._url;
|
|
24
|
+
}
|
|
25
|
+
get code() {
|
|
26
|
+
return this.request.status;
|
|
27
|
+
}
|
|
28
|
+
get contentLength() {
|
|
29
|
+
return parseInt(this.request.getResponseHeader("content-length")) || this.request.responseText.length;
|
|
30
|
+
}
|
|
31
|
+
get contentFromJSON() {
|
|
32
|
+
if (!this.parsedJson)
|
|
33
|
+
this.parsedJson = JSON.parse(this.request.responseText);
|
|
34
|
+
return this.parsedJson;
|
|
35
|
+
}
|
|
36
|
+
get contentFromBSON() {
|
|
37
|
+
if (!this.parsedBson) {
|
|
38
|
+
var response = this.request.response;
|
|
39
|
+
if (typeof response === "string")
|
|
40
|
+
response = Buffer.from(response, "utf8");
|
|
41
|
+
else
|
|
42
|
+
response = new Buffer(response);
|
|
43
|
+
this.parsedBson = BSON.deserialize(response, { promoteValues: true });
|
|
44
|
+
}
|
|
45
|
+
return this.parsedBson;
|
|
46
|
+
}
|
|
47
|
+
get contentAsString() {
|
|
48
|
+
return this.request.responseText;
|
|
49
|
+
}
|
|
50
|
+
get contentAsArrayBuffer() {
|
|
51
|
+
return this.request.response;
|
|
52
|
+
}
|
|
53
|
+
get headers() {
|
|
54
|
+
if (!this.processedHeaders) {
|
|
55
|
+
const headers = this.processedHeaders = {};
|
|
56
|
+
this.request.getAllResponseHeaders().split(/\r?\n/g).forEach(function (header) {
|
|
57
|
+
const index = header.indexOf(":");
|
|
58
|
+
var key, val;
|
|
59
|
+
if (index > 0) {
|
|
60
|
+
key = header.substring(0, index).trim().toLowerCase();
|
|
61
|
+
val = header.substring(index + 1).trim();
|
|
62
|
+
}
|
|
63
|
+
else
|
|
64
|
+
key = header.trim().toLowerCase();
|
|
65
|
+
var list = headers[key];
|
|
66
|
+
if (!list)
|
|
67
|
+
list = headers[key] = [];
|
|
68
|
+
if (val)
|
|
69
|
+
list.push(val);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return this.processedHeaders;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const execute = function (method, url, data, cb, extraHeaders, progcb, wantsBinary) {
|
|
76
|
+
const request = new XMLHttpRequest();
|
|
77
|
+
var responseIsArrayBuffer;
|
|
78
|
+
try {
|
|
79
|
+
if (!wantsBinary)
|
|
80
|
+
throw false;
|
|
81
|
+
request.responseType = "arraybuffer";
|
|
82
|
+
if (request.responseType !== "arraybuffer") {
|
|
83
|
+
console.error("Could not request arraybuffer");
|
|
84
|
+
throw false;
|
|
85
|
+
}
|
|
86
|
+
responseIsArrayBuffer = true;
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
responseIsArrayBuffer = false;
|
|
90
|
+
}
|
|
91
|
+
request.open(method, url, true);
|
|
92
|
+
if (extraHeaders)
|
|
93
|
+
Object.keys(extraHeaders).forEach(function (key) {
|
|
94
|
+
request.setRequestHeader(key, extraHeaders[key]);
|
|
95
|
+
});
|
|
96
|
+
if (progcb)
|
|
97
|
+
request.onprogress = function (ev) {
|
|
98
|
+
if (ev.lengthComputable && ev.total)
|
|
99
|
+
progcb(ev.loaded, ev.total);
|
|
100
|
+
};
|
|
101
|
+
request.onreadystatechange = function (e) {
|
|
102
|
+
if (request.readyState === XMLHttpRequest.DONE)
|
|
103
|
+
cb(new NexusFrameworkXMLHttpRequestResponse(request, url, !!data, responseIsArrayBuffer));
|
|
104
|
+
};
|
|
105
|
+
const type = data && data.type;
|
|
106
|
+
if (type)
|
|
107
|
+
switch (type) {
|
|
108
|
+
case "":
|
|
109
|
+
case "text/urlencoded":
|
|
110
|
+
var dat = "";
|
|
111
|
+
for (var entry of data.data) {
|
|
112
|
+
if (dat)
|
|
113
|
+
dat += "&";
|
|
114
|
+
dat += encodeURIComponent(entry[0]);
|
|
115
|
+
dat += "=";
|
|
116
|
+
dat += encodeURIComponent(entry[1]);
|
|
117
|
+
}
|
|
118
|
+
request.setRequestHeader("Content-Type", "text/urlencoded");
|
|
119
|
+
request.send(dat);
|
|
120
|
+
break;
|
|
121
|
+
case "text/json":
|
|
122
|
+
request.setRequestHeader("Content-Type", "text/json");
|
|
123
|
+
request.send(JSON.stringify(data.data));
|
|
124
|
+
break;
|
|
125
|
+
case "application/bson":
|
|
126
|
+
request.setRequestHeader("Content-Type", "application/bson");
|
|
127
|
+
request.send(BSON.serialize(data.data));
|
|
128
|
+
break;
|
|
129
|
+
case "multipart/form-data":
|
|
130
|
+
request.send(data.data);
|
|
131
|
+
break;
|
|
132
|
+
default:
|
|
133
|
+
request.send(data);
|
|
134
|
+
}
|
|
135
|
+
else
|
|
136
|
+
request.send(data);
|
|
137
|
+
};
|
|
138
|
+
impl = {
|
|
139
|
+
get: function (url, cb, extraHeaders, progcb, wantsBinary) {
|
|
140
|
+
execute("GET", url, undefined, cb, extraHeaders, progcb, wantsBinary);
|
|
141
|
+
},
|
|
142
|
+
head: function (url, cb, extraHeaders, progcb, wantsBinary) {
|
|
143
|
+
execute("HEAD", url, undefined, cb, extraHeaders, progcb, wantsBinary);
|
|
144
|
+
},
|
|
145
|
+
post: function (url, data, cb, extraHeaders, progcb, wantsBinary) {
|
|
146
|
+
execute("POST", url, data, cb, extraHeaders, progcb, wantsBinary);
|
|
147
|
+
},
|
|
148
|
+
put: function (url, data, cb, extraHeaders, progcb, wantsBinary) {
|
|
149
|
+
execute("PUT", url, data, cb, extraHeaders, progcb, wantsBinary);
|
|
150
|
+
},
|
|
151
|
+
execute,
|
|
152
|
+
del: function (url, cb, extraHeaders, progcb, wantsBinary) {
|
|
153
|
+
execute("DELETE", url, undefined, cb, extraHeaders, progcb, wantsBinary);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
else
|
|
158
|
+
throw new Error("No transport implementations");
|
|
159
|
+
Object.defineProperty(window, "NexusFrameworkTransport", {
|
|
160
|
+
value: impl
|
|
161
|
+
});
|
|
162
|
+
return impl;
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
NexusFrameworkImpl: {
|
|
166
|
+
configurable: true,
|
|
167
|
+
set: function (instance) {
|
|
168
|
+
Object.defineProperty(window, "NexusFrameworkImpl", {
|
|
169
|
+
value: instance
|
|
170
|
+
});
|
|
171
|
+
},
|
|
172
|
+
get: function () {
|
|
173
|
+
const loader = window.NexusFrameworkLoader;
|
|
174
|
+
const showError = loader.showError;
|
|
175
|
+
const debug = /*function(...args: any[]){};*/ console.log.bind(console);
|
|
176
|
+
const GA_ANALYTICS = {
|
|
177
|
+
reportError: function (err, fatal) {
|
|
178
|
+
if (window.ga)
|
|
179
|
+
try {
|
|
180
|
+
window.ga('send', 'exception', {
|
|
181
|
+
'exDescription': (err.stack || "" + err).replace(/\n/g, "\n\t"),
|
|
182
|
+
'exFatal': fatal
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
catch (e) {
|
|
186
|
+
console.warn(e);
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
reportEvent: function (category, action, label, value) {
|
|
190
|
+
if (window.ga)
|
|
191
|
+
try {
|
|
192
|
+
window.ga('send', 'event', category, action, label, value);
|
|
193
|
+
}
|
|
194
|
+
catch (e) {
|
|
195
|
+
console.warn(e);
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
reportPage: function (path) {
|
|
199
|
+
if (window.ga)
|
|
200
|
+
try {
|
|
201
|
+
if (!path)
|
|
202
|
+
path = location.pathname;
|
|
203
|
+
window.ga('set', 'page', path);
|
|
204
|
+
window.ga('send', 'pageview');
|
|
205
|
+
}
|
|
206
|
+
catch (e) {
|
|
207
|
+
console.warn(e);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
const r = document.createElement("a");
|
|
212
|
+
const protocol = location.href.match(/^\w+:/)[0];
|
|
213
|
+
const resolveUrl = function (url) {
|
|
214
|
+
r.setAttribute("href", url);
|
|
215
|
+
var href = r.href;
|
|
216
|
+
if (/^\/\//.test(href))
|
|
217
|
+
href = protocol + href;
|
|
218
|
+
return href;
|
|
219
|
+
};
|
|
220
|
+
const convertResponse = function (res, url = location.href) {
|
|
221
|
+
var storage;
|
|
222
|
+
return (typeof res.data === "string") ? {
|
|
223
|
+
url,
|
|
224
|
+
code: res.code,
|
|
225
|
+
contentAsString: res.data,
|
|
226
|
+
get contentLength() {
|
|
227
|
+
return parseInt(res.headers['content-length'] && res.headers['content-length'][0]) || res.data.length;
|
|
228
|
+
},
|
|
229
|
+
get contentAsArrayBuffer() {
|
|
230
|
+
throw new Error("Not implemented");
|
|
231
|
+
},
|
|
232
|
+
get contentFromBSON() {
|
|
233
|
+
if (!storage)
|
|
234
|
+
storage = JSON.parse(res.data);
|
|
235
|
+
return storage;
|
|
236
|
+
},
|
|
237
|
+
get contentFromJSON() {
|
|
238
|
+
if (!storage)
|
|
239
|
+
storage = JSON.parse(res.data);
|
|
240
|
+
return storage;
|
|
241
|
+
},
|
|
242
|
+
headers: res.headers
|
|
243
|
+
} : {
|
|
244
|
+
url,
|
|
245
|
+
code: res.code,
|
|
246
|
+
get contentAsString() {
|
|
247
|
+
if (!storage)
|
|
248
|
+
storage = JSON.stringify(res.data);
|
|
249
|
+
return storage;
|
|
250
|
+
},
|
|
251
|
+
get contentAsArrayBuffer() {
|
|
252
|
+
throw new Error("Not implemented");
|
|
253
|
+
},
|
|
254
|
+
contentFromJSON: res.data,
|
|
255
|
+
contentFromBSON: res.data,
|
|
256
|
+
headers: res.headers,
|
|
257
|
+
get contentLength() {
|
|
258
|
+
var length = parseInt(res.headers['content-length'] && res.headers['content-length'][0]);
|
|
259
|
+
if (length)
|
|
260
|
+
return length;
|
|
261
|
+
if (!storage)
|
|
262
|
+
storage = JSON.stringify(res.data);
|
|
263
|
+
return storage.length;
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
};
|
|
267
|
+
const loaderContainerRegex = /(^|\s)loader\-(progress|error)\-container(\s|$)/;
|
|
268
|
+
class NexusFrameworkBase {
|
|
269
|
+
constructor(url = "/", io) {
|
|
270
|
+
this.activerid = 0;
|
|
271
|
+
this.components = {};
|
|
272
|
+
this.currentUserID = undefined;
|
|
273
|
+
this.progressVisible = false;
|
|
274
|
+
this.animationTiming = 500;
|
|
275
|
+
this.requestPage = this.defaultRequestPage;
|
|
276
|
+
this._listeners = {};
|
|
277
|
+
url = resolveUrl(url);
|
|
278
|
+
if (!/\/$/.test(url))
|
|
279
|
+
url += "/";
|
|
280
|
+
Object.defineProperties(this, {
|
|
281
|
+
io: {
|
|
282
|
+
value: io
|
|
283
|
+
},
|
|
284
|
+
url: {
|
|
285
|
+
value: url
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
this._analytics = GA_ANALYTICS;
|
|
289
|
+
}
|
|
290
|
+
resolveUrl(url) {
|
|
291
|
+
if (/^\w+\:/.test(url))
|
|
292
|
+
return url;
|
|
293
|
+
return resolveUrl(this.url + url);
|
|
294
|
+
}
|
|
295
|
+
disableAll(root = document.body) {
|
|
296
|
+
const focusable = root.querySelectorAll("a, input, select, textarea, iframe, button, *[focusable], *[tabindex]");
|
|
297
|
+
for (var i = 0; i < focusable.length; i++) {
|
|
298
|
+
const child = focusable[i];
|
|
299
|
+
const tabindex = child.getAttribute("tabindex");
|
|
300
|
+
if (tabindex == "-1")
|
|
301
|
+
return;
|
|
302
|
+
child.setAttribute("data-tabindex", tabindex ? tabindex : "");
|
|
303
|
+
child.setAttribute("data-tabindex", "-1");
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
enableAll(root = document.body) {
|
|
307
|
+
const focusable = root.querySelectorAll("*[data-tabindex]");
|
|
308
|
+
for (var i = 0; i < focusable.length; i++) {
|
|
309
|
+
const child = focusable[i];
|
|
310
|
+
child.setAttribute("tabindex", child.getAttribute("data-tabindex"));
|
|
311
|
+
child.removeAttribute("data-tabindex");
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
get analytics() {
|
|
315
|
+
return this._analytics;
|
|
316
|
+
}
|
|
317
|
+
set analytics(value) {
|
|
318
|
+
this._analytics = value ? value : GA_ANALYTICS;
|
|
319
|
+
}
|
|
320
|
+
reportError(err, fatal) {
|
|
321
|
+
console[fatal ? "error" : "warn"](err.stack);
|
|
322
|
+
if (this.errorreporter)
|
|
323
|
+
this.errorreporter(err, fatal);
|
|
324
|
+
}
|
|
325
|
+
installErrorReporter(errorreporter) {
|
|
326
|
+
this.errorreporter = errorreporter;
|
|
327
|
+
}
|
|
328
|
+
registerComponent(selector, impl) {
|
|
329
|
+
if (impl) {
|
|
330
|
+
this.components[selector] = impl;
|
|
331
|
+
this.createComponents(document.head);
|
|
332
|
+
this.createComponents(document.body);
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
delete this.components[selector];
|
|
336
|
+
var destroy = (root) => {
|
|
337
|
+
const elements = root.querySelectorAll(selector);
|
|
338
|
+
if (!elements.length)
|
|
339
|
+
return;
|
|
340
|
+
for (var i = 0; i < elements.length; i++) {
|
|
341
|
+
const element = elements[i];
|
|
342
|
+
var components = element['__nf_cmapping__'];
|
|
343
|
+
if (!components)
|
|
344
|
+
components = element['__nf_cmapping__'] = {};
|
|
345
|
+
const component = components[selector];
|
|
346
|
+
if (component) {
|
|
347
|
+
try {
|
|
348
|
+
component.destroy();
|
|
349
|
+
}
|
|
350
|
+
catch (e) {
|
|
351
|
+
this.reportError(e);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
destroy(document.head);
|
|
357
|
+
destroy(document.body);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
unregisterComponent(selector, impl) {
|
|
361
|
+
}
|
|
362
|
+
createComponents(root) {
|
|
363
|
+
Object.keys(this.components).forEach((selector) => {
|
|
364
|
+
const elements = root.querySelectorAll(selector);
|
|
365
|
+
if (!elements.length)
|
|
366
|
+
return;
|
|
367
|
+
for (var i = 0; i < elements.length; i++) {
|
|
368
|
+
const element = elements[i];
|
|
369
|
+
var components = element['__nf_cmapping__'];
|
|
370
|
+
if (!components)
|
|
371
|
+
components = element['__nf_cmapping__'] = {};
|
|
372
|
+
if (components[selector])
|
|
373
|
+
continue;
|
|
374
|
+
const componentFactory = this.components[selector];
|
|
375
|
+
try {
|
|
376
|
+
(components[selector] = new componentFactory()).create(element);
|
|
377
|
+
}
|
|
378
|
+
catch (e) {
|
|
379
|
+
this.reportError(e);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
destroyComponents(root) {
|
|
385
|
+
Object.keys(this.components).forEach((selector) => {
|
|
386
|
+
const elements = root.querySelectorAll(selector);
|
|
387
|
+
if (!elements.length)
|
|
388
|
+
return;
|
|
389
|
+
for (var i = 0; i < elements.length; i++) {
|
|
390
|
+
const element = elements[i];
|
|
391
|
+
var components = element['__nf_cmapping__'];
|
|
392
|
+
if (!components)
|
|
393
|
+
components = element['__nf_cmapping__'] = {};
|
|
394
|
+
const component = components[selector];
|
|
395
|
+
if (component) {
|
|
396
|
+
try {
|
|
397
|
+
component.destroy();
|
|
398
|
+
}
|
|
399
|
+
catch (e) {
|
|
400
|
+
this.reportError(e);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
restoreComponents(root, state) {
|
|
407
|
+
Object.keys(this.components).forEach((selector) => {
|
|
408
|
+
const states = state[selector];
|
|
409
|
+
if (!states)
|
|
410
|
+
return;
|
|
411
|
+
const elements = root.querySelectorAll(selector);
|
|
412
|
+
if (!elements.length)
|
|
413
|
+
return;
|
|
414
|
+
for (var i = 0; i < elements.length; i++) {
|
|
415
|
+
const element = elements[i];
|
|
416
|
+
if (states.length) {
|
|
417
|
+
const _state = states.shift();
|
|
418
|
+
if (_state) {
|
|
419
|
+
var components = element['__nf_cmapping__'];
|
|
420
|
+
if (!components)
|
|
421
|
+
components = element['__nf_cmapping__'] = {};
|
|
422
|
+
var component = components[selector];
|
|
423
|
+
if (!component) {
|
|
424
|
+
const componentFactory = this.components[selector];
|
|
425
|
+
try {
|
|
426
|
+
(component = components[selector] = new componentFactory()).create(element);
|
|
427
|
+
}
|
|
428
|
+
catch (e) {
|
|
429
|
+
this.reportError(e);
|
|
430
|
+
console.warn(e);
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
try {
|
|
435
|
+
component.restore(_state);
|
|
436
|
+
}
|
|
437
|
+
catch (e) {
|
|
438
|
+
this.reportError(e);
|
|
439
|
+
console.warn(e);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
else
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
saveComponents(root) {
|
|
449
|
+
var state = {};
|
|
450
|
+
Object.keys(this.components).forEach((selector) => {
|
|
451
|
+
const states = state[selector] = [];
|
|
452
|
+
const elements = root.querySelectorAll(selector);
|
|
453
|
+
if (!elements.length)
|
|
454
|
+
return;
|
|
455
|
+
for (var i = 0; i < elements.length; i++) {
|
|
456
|
+
const element = elements[i];
|
|
457
|
+
var components = element['__nf_cmapping__'];
|
|
458
|
+
if (!components)
|
|
459
|
+
components = element['__nf_cmapping__'] = {};
|
|
460
|
+
var component = components[selector];
|
|
461
|
+
if (!component) {
|
|
462
|
+
const componentFactory = this.components[selector];
|
|
463
|
+
try {
|
|
464
|
+
(component = components[selector] = new componentFactory).create(element);
|
|
465
|
+
}
|
|
466
|
+
catch (e) {
|
|
467
|
+
states.push(undefined);
|
|
468
|
+
this.reportError(e);
|
|
469
|
+
console.warn(e);
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
try {
|
|
474
|
+
states.push(component.save());
|
|
475
|
+
}
|
|
476
|
+
catch (e) {
|
|
477
|
+
states.push(undefined);
|
|
478
|
+
this.reportError(e);
|
|
479
|
+
console.warn(e);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
return state;
|
|
484
|
+
}
|
|
485
|
+
initPageSystem(opts) {
|
|
486
|
+
if (!loader)
|
|
487
|
+
return console.error("The NexusFramework Loader is required for the dynamic Page System to work correctly.");
|
|
488
|
+
if (!history.pushState || !history.replaceState)
|
|
489
|
+
return console.warn("This browser is missing an essential feature required for the dynamic Page System.");
|
|
490
|
+
if (this.pagesyshandler)
|
|
491
|
+
return console.warn("Page System already initialized, ignoring additional requests to initialize.");
|
|
492
|
+
opts = opts || {};
|
|
493
|
+
const self = this;
|
|
494
|
+
var pageStatesSize = 0;
|
|
495
|
+
const pageStates = [];
|
|
496
|
+
this.animationTiming = opts.animationTiming || 500;
|
|
497
|
+
const pageStateCacheSize = opts.pageHistoryCacheSize || 25000000;
|
|
498
|
+
const wrapCB = (cb) => {
|
|
499
|
+
return (res) => {
|
|
500
|
+
const user = res.headers['x-logged-user'];
|
|
501
|
+
if (user)
|
|
502
|
+
self.currentUserID = user[0];
|
|
503
|
+
else
|
|
504
|
+
self.currentUserID = undefined;
|
|
505
|
+
debug("Current UID", self.currentUserID);
|
|
506
|
+
cb(res);
|
|
507
|
+
if (res.code === 200)
|
|
508
|
+
setTimeout(() => {
|
|
509
|
+
self._analytics.reportPage();
|
|
510
|
+
});
|
|
511
|
+
};
|
|
512
|
+
};
|
|
513
|
+
loader.showError = function (error) {
|
|
514
|
+
const match = location.href.match(beforeHash);
|
|
515
|
+
const baseurl = match && match[1] || location.href;
|
|
516
|
+
const length = error.stack.length;
|
|
517
|
+
const tooBig = pageStateCacheSize <= length;
|
|
518
|
+
try {
|
|
519
|
+
const cPageStates = pageStates.length;
|
|
520
|
+
for (var i = 0; i < cPageStates; i++) {
|
|
521
|
+
const state = pageStates[i];
|
|
522
|
+
if (state.url === baseurl) {
|
|
523
|
+
if (tooBig) {
|
|
524
|
+
pageStates.splice(i, 1);
|
|
525
|
+
pageStatesSize -= state.size;
|
|
526
|
+
}
|
|
527
|
+
else {
|
|
528
|
+
state.data = { error };
|
|
529
|
+
state.updated = +new Date;
|
|
530
|
+
pageStatesSize += length - state.size;
|
|
531
|
+
}
|
|
532
|
+
throw true;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
if (tooBig)
|
|
536
|
+
throw true;
|
|
537
|
+
pageStates.push({
|
|
538
|
+
url: baseurl,
|
|
539
|
+
data: { error },
|
|
540
|
+
updated: +new Date,
|
|
541
|
+
size: length
|
|
542
|
+
});
|
|
543
|
+
pageStatesSize += length;
|
|
544
|
+
}
|
|
545
|
+
catch (e) {
|
|
546
|
+
if (e !== true)
|
|
547
|
+
throw e;
|
|
548
|
+
}
|
|
549
|
+
return showError(error);
|
|
550
|
+
};
|
|
551
|
+
const transportPageSystem = {
|
|
552
|
+
requestPage(path, cb, post, rid) {
|
|
553
|
+
if (self.pagesysprerequest && !self.pagesysprerequest(path)) {
|
|
554
|
+
self.defaultRequestPage(path, post);
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
if (!opts.noprogress) {
|
|
558
|
+
self.disableAll();
|
|
559
|
+
loader.showProgress();
|
|
560
|
+
}
|
|
561
|
+
const url = self.resolveUrl(":pagesys/" + path);
|
|
562
|
+
const _cb = opts.noprogress ? cb : function (res) {
|
|
563
|
+
if (opts.noprogress)
|
|
564
|
+
cb(res);
|
|
565
|
+
else
|
|
566
|
+
loader.showProgress(() => {
|
|
567
|
+
cb(res);
|
|
568
|
+
self.enableAll();
|
|
569
|
+
});
|
|
570
|
+
};
|
|
571
|
+
if (post)
|
|
572
|
+
window.NexusFrameworkTransport.post(url, post, wrapCB(_cb), undefined, undefined, true);
|
|
573
|
+
else
|
|
574
|
+
window.NexusFrameworkTransport.get(url, wrapCB(_cb), undefined, undefined, true);
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
if (!opts.noio && this.io) {
|
|
578
|
+
const io = this.io;
|
|
579
|
+
this.pagesysimpl = {
|
|
580
|
+
requestPage(path, cb, post, rid) {
|
|
581
|
+
if (io.connected) {
|
|
582
|
+
if (self.pagesysprerequest && !self.pagesysprerequest(path)) {
|
|
583
|
+
self.defaultRequestPage(path, post);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
if (!opts.noprogress) {
|
|
587
|
+
self.disableAll();
|
|
588
|
+
loader.showProgress();
|
|
589
|
+
}
|
|
590
|
+
const headers = {
|
|
591
|
+
"referrer": location.href
|
|
592
|
+
};
|
|
593
|
+
var val = document.cookie;
|
|
594
|
+
if (val)
|
|
595
|
+
headers['cookie'] = val;
|
|
596
|
+
io.emit("page", post ? "POST" : "GET", path, post, headers, function (res) {
|
|
597
|
+
if (rid != self.activerid)
|
|
598
|
+
return;
|
|
599
|
+
const cookies = res.headers['set-cookie'];
|
|
600
|
+
if (cookies) {
|
|
601
|
+
cookies.forEach(function (cookie) {
|
|
602
|
+
document.cookie = cookie;
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
const _cb = opts.noprogress ? cb : function (res) {
|
|
606
|
+
if (!opts.noprogress)
|
|
607
|
+
cb(res);
|
|
608
|
+
else
|
|
609
|
+
loader.showProgress(() => {
|
|
610
|
+
cb(res);
|
|
611
|
+
self.enableAll();
|
|
612
|
+
});
|
|
613
|
+
};
|
|
614
|
+
wrapCB(_cb)(convertResponse(res, self.resolveUrl(path)));
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
else
|
|
618
|
+
transportPageSystem.requestPage(path, cb, post, rid);
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
else
|
|
623
|
+
this.pagesysimpl = transportPageSystem;
|
|
624
|
+
var base = document.getElementsByTagName("base");
|
|
625
|
+
base = base && base[0];
|
|
626
|
+
this.pagesysprerequest = opts.prerequest;
|
|
627
|
+
this.pagesyshandler = opts.handler || ((res, pageReady) => {
|
|
628
|
+
try {
|
|
629
|
+
const contentType = res.headers['content-type'][0];
|
|
630
|
+
if (!/\/html(;.+)?$/.test(contentType.toLowerCase())) {
|
|
631
|
+
throw new Error("Content type is not html");
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
catch (e) { }
|
|
635
|
+
const content = res.contentAsString;
|
|
636
|
+
const bodyindex = content.indexOf("<body");
|
|
637
|
+
if (bodyindex == -1)
|
|
638
|
+
throw new Error("Could not find start of body tag");
|
|
639
|
+
const endbodyindex = content.indexOf("</body>") || content.indexOf("</ body>");
|
|
640
|
+
if (endbodyindex == -1)
|
|
641
|
+
throw new Error("Could not find end of body tag");
|
|
642
|
+
const title = content.match(/<title>([^<]+)<\/\s*title>/);
|
|
643
|
+
document.title = title && title[1] || "Title Not Set";
|
|
644
|
+
const mockhtml = document.createElement("html");
|
|
645
|
+
mockhtml.innerHTML = content.substring(bodyindex, endbodyindex) + "</body>";
|
|
646
|
+
var loaderScript;
|
|
647
|
+
var childs = mockhtml.children;
|
|
648
|
+
const toAdd = [];
|
|
649
|
+
for (var i = 0; i < childs.length; i++) {
|
|
650
|
+
const child = childs[i];
|
|
651
|
+
switch (child.nodeName.toUpperCase()) {
|
|
652
|
+
case "HEAD":
|
|
653
|
+
break;
|
|
654
|
+
case "BODY":
|
|
655
|
+
i = -1;
|
|
656
|
+
childs = child.children;
|
|
657
|
+
break;
|
|
658
|
+
case "SCRIPT":
|
|
659
|
+
const match = child.innerHTML.match(/^NexusFrameworkLoader\.load\((.+)\);?$/);
|
|
660
|
+
if (match)
|
|
661
|
+
loaderScript = JSON.parse(match[1]);
|
|
662
|
+
break;
|
|
663
|
+
default:
|
|
664
|
+
if (loaderContainerRegex.test(child.className))
|
|
665
|
+
break;
|
|
666
|
+
toAdd.push(child);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
if (!toAdd.length)
|
|
670
|
+
throw new Error("Nothing found in response to add to page");
|
|
671
|
+
if (!loaderScript)
|
|
672
|
+
throw new Error("NexusFrameworkLoader script not found...");
|
|
673
|
+
childs = document.body.children;
|
|
674
|
+
for (var i = childs.length - 1; i >= 0; i--) {
|
|
675
|
+
const child = childs[i];
|
|
676
|
+
switch (child.nodeName.toUpperCase()) {
|
|
677
|
+
case "LINK":
|
|
678
|
+
case "SCRIPT":
|
|
679
|
+
break;
|
|
680
|
+
default:
|
|
681
|
+
if (loaderContainerRegex.test(child.className))
|
|
682
|
+
break;
|
|
683
|
+
document.body.removeChild(child);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
const fragment = document.createDocumentFragment();
|
|
687
|
+
toAdd.forEach((el) => {
|
|
688
|
+
fragment.appendChild(el);
|
|
689
|
+
this.createComponents(el);
|
|
690
|
+
});
|
|
691
|
+
document.body.appendChild(fragment);
|
|
692
|
+
loader.load(loaderScript, function () {
|
|
693
|
+
self.progressVisible = true;
|
|
694
|
+
loader.resetProgress();
|
|
695
|
+
pageReady();
|
|
696
|
+
});
|
|
697
|
+
return true;
|
|
698
|
+
});
|
|
699
|
+
var forwardPopState;
|
|
700
|
+
const beforeHash = /^([^#]+)(#.*)?$/;
|
|
701
|
+
var chash = location.href.match(beforeHash)[1];
|
|
702
|
+
const startsWith = new RegExp("^" + this.url.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&") + "(.*)$", "i");
|
|
703
|
+
class AnchorElementComponent {
|
|
704
|
+
constructor() {
|
|
705
|
+
this.handler = (e) => {
|
|
706
|
+
if (this.element.hasAttribute("data-nopagesys") || this.element.hasAttribute("data-nodynamic"))
|
|
707
|
+
return;
|
|
708
|
+
const href = this.element.getAttribute("href");
|
|
709
|
+
if (!href || !href.length)
|
|
710
|
+
return;
|
|
711
|
+
var url = this.element.href;
|
|
712
|
+
const bhash = url.match(beforeHash);
|
|
713
|
+
if (bhash[2] && chash === bhash[1])
|
|
714
|
+
return;
|
|
715
|
+
if (startsWith.test(url))
|
|
716
|
+
try {
|
|
717
|
+
const match = url.match(/^(.+)#.*$/);
|
|
718
|
+
if (match)
|
|
719
|
+
url = match[1];
|
|
720
|
+
self.requestPage(url.substring(self.url.length));
|
|
721
|
+
try {
|
|
722
|
+
e.stopPropagation();
|
|
723
|
+
}
|
|
724
|
+
catch (e) { }
|
|
725
|
+
try {
|
|
726
|
+
e.preventDefault();
|
|
727
|
+
}
|
|
728
|
+
catch (e) { }
|
|
729
|
+
}
|
|
730
|
+
catch (e) {
|
|
731
|
+
console.warn(e);
|
|
732
|
+
}
|
|
733
|
+
else
|
|
734
|
+
debug("Navigating", url);
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
create(element) {
|
|
738
|
+
element.addEventListener("click", this.handler);
|
|
739
|
+
this.element = element;
|
|
740
|
+
}
|
|
741
|
+
destroy() {
|
|
742
|
+
this.element.removeEventListener("click", this.handler);
|
|
743
|
+
}
|
|
744
|
+
restore(data) { }
|
|
745
|
+
save() { }
|
|
746
|
+
}
|
|
747
|
+
class FormElementComponent {
|
|
748
|
+
constructor() {
|
|
749
|
+
this.handler = (e) => {
|
|
750
|
+
if (this.element.hasAttribute("data-nopagesys") || this.element.hasAttribute("data-nodynamic"))
|
|
751
|
+
return;
|
|
752
|
+
const method = this.element.getAttribute("method") || "get";
|
|
753
|
+
const enctype = this.element.getAttribute("enctype") || "text/urlencoded";
|
|
754
|
+
const action = this.element.getAttribute("action") || "";
|
|
755
|
+
var url = resolveUrl(action);
|
|
756
|
+
if (startsWith.test(url)) {
|
|
757
|
+
try {
|
|
758
|
+
const match = url.match(/^(.+)#.*$/);
|
|
759
|
+
if (match)
|
|
760
|
+
url = match[1];
|
|
761
|
+
const formData = new FormData(this.element);
|
|
762
|
+
loader.showProgress(undefined, "Submitting Form", "Your form data is being processed");
|
|
763
|
+
if (method.trim().toLowerCase() === "post")
|
|
764
|
+
self.requestPage(url.substring(self.url.length), { type: enctype, data: formData });
|
|
765
|
+
else {
|
|
766
|
+
var first = true;
|
|
767
|
+
var reqUrl = url.substring(self.url.length) + "?";
|
|
768
|
+
for (var entry of formData) {
|
|
769
|
+
if (first)
|
|
770
|
+
first = false;
|
|
771
|
+
else
|
|
772
|
+
reqUrl += "&";
|
|
773
|
+
reqUrl += encodeURIComponent(entry[0]);
|
|
774
|
+
reqUrl += "=";
|
|
775
|
+
reqUrl += encodeURIComponent(entry[1]);
|
|
776
|
+
}
|
|
777
|
+
self.requestPage(reqUrl);
|
|
778
|
+
}
|
|
779
|
+
try {
|
|
780
|
+
e.stopPropagation();
|
|
781
|
+
}
|
|
782
|
+
catch (e) { }
|
|
783
|
+
try {
|
|
784
|
+
e.preventDefault();
|
|
785
|
+
}
|
|
786
|
+
catch (e) { }
|
|
787
|
+
Array.prototype.forEach.call(this.element.querySelectorAll("a, input, select, textarea, iframe, button, *[name]"), function (el) {
|
|
788
|
+
el.setAttribute("disabled", "disabled");
|
|
789
|
+
el.className += " disabled";
|
|
790
|
+
});
|
|
791
|
+
Array.prototype.forEach.call(this.element.querySelectorAll("button:not([type]), button[type=submit]"), function (button) {
|
|
792
|
+
button.innerHTML = "Processing Request";
|
|
793
|
+
});
|
|
794
|
+
Array.prototype.forEach.call(this.element.querySelectorAll("input[type=submit]"), function (input) {
|
|
795
|
+
input.value = "Processing Request";
|
|
796
|
+
});
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
catch (e) {
|
|
800
|
+
debug(e);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
debug("Submitting", url);
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
create(element) {
|
|
807
|
+
element.addEventListener("submit", this.handler);
|
|
808
|
+
this.element = element;
|
|
809
|
+
}
|
|
810
|
+
destroy() {
|
|
811
|
+
this.element.removeEventListener("submit", this.handler);
|
|
812
|
+
}
|
|
813
|
+
restore(data) { }
|
|
814
|
+
save() { }
|
|
815
|
+
}
|
|
816
|
+
const requestPage = this.requestPage = (path, post, replace = false) => {
|
|
817
|
+
const match = path.match(/\.([^\/]+)([\?#].*)?$/);
|
|
818
|
+
if (match && match[0] !== "htm" && match[0] !== "html") {
|
|
819
|
+
this.defaultRequestPage(path, post);
|
|
820
|
+
debug("Ignoring navigation", path, match);
|
|
821
|
+
}
|
|
822
|
+
else {
|
|
823
|
+
debug("requestPage", path);
|
|
824
|
+
const rid = ++self.activerid;
|
|
825
|
+
const baseurl = this.resolveUrl(path);
|
|
826
|
+
const fullurl = baseurl + (match && match[2] || "");
|
|
827
|
+
if (replace)
|
|
828
|
+
history.replaceState(!!post, "Loading...", fullurl);
|
|
829
|
+
else {
|
|
830
|
+
history.pushState(!!post, "Loading...", fullurl);
|
|
831
|
+
chash = fullurl.match(beforeHash)[1];
|
|
832
|
+
window.scrollTo(0, 0);
|
|
833
|
+
}
|
|
834
|
+
loader.resetError();
|
|
835
|
+
loader.resetProgress();
|
|
836
|
+
this.pagesysimpl.requestPage(path, (res) => {
|
|
837
|
+
try {
|
|
838
|
+
if (rid !== self.activerid)
|
|
839
|
+
return;
|
|
840
|
+
if (!res.code) {
|
|
841
|
+
loader.showProgress(undefined, "Connection Issue", "Check your network and try again");
|
|
842
|
+
document.title = "Connection Issue";
|
|
843
|
+
history.replaceState(res.hadData, "Connection Issue", fullurl);
|
|
844
|
+
setTimeout(function () {
|
|
845
|
+
if (rid !== self.activerid)
|
|
846
|
+
return;
|
|
847
|
+
requestPage(path, post, true);
|
|
848
|
+
}, 900000);
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
else if (res.code === 502 || res.code === 503) {
|
|
852
|
+
loader.showProgress(undefined, "Scheduled Maintenance", "Sorry but this website is undergoing scheduled maintenance");
|
|
853
|
+
document.title = "Scheduled Maintenance";
|
|
854
|
+
history.replaceState(res.hadData, "Scheduled Maintenance", fullurl);
|
|
855
|
+
setTimeout(function () {
|
|
856
|
+
if (rid !== self.activerid)
|
|
857
|
+
return;
|
|
858
|
+
requestPage(path, post, true);
|
|
859
|
+
}, 900000);
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
const location = res.headers['x-location'] || res.headers['location'];
|
|
863
|
+
if (location) {
|
|
864
|
+
const url = resolveUrl(location[0]);
|
|
865
|
+
if (startsWith.test(url)) {
|
|
866
|
+
this.requestPage(url.substring(this.url.length), undefined, true);
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
console.warn("Requested redirect to external url:", url);
|
|
870
|
+
window.location.href = url;
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
const contentDisposition = res.headers['content-disposition'];
|
|
874
|
+
if (contentDisposition && contentDisposition[0].toLowerCase() == "attachment")
|
|
875
|
+
throw new Error("Attachment disposition");
|
|
876
|
+
this.pagesyshandler(res, function () {
|
|
877
|
+
if (rid !== self.activerid)
|
|
878
|
+
return;
|
|
879
|
+
try {
|
|
880
|
+
const title = document.title;
|
|
881
|
+
const length = res.contentLength;
|
|
882
|
+
const tooBig = pageStateCacheSize <= length;
|
|
883
|
+
const stateData = tooBig ? undefined : {
|
|
884
|
+
title: title,
|
|
885
|
+
user: self.currentUserID,
|
|
886
|
+
scroll: [window.scrollX, window.scrollY],
|
|
887
|
+
body: self.saveComponents(document.body),
|
|
888
|
+
basehref: base ? base['href'] : undefined,
|
|
889
|
+
response: res
|
|
890
|
+
};
|
|
891
|
+
var i;
|
|
892
|
+
const cPageStates = pageStates.length;
|
|
893
|
+
try {
|
|
894
|
+
for (i = 0; i < cPageStates; i++) {
|
|
895
|
+
const state = pageStates[i];
|
|
896
|
+
if (state.url === baseurl) {
|
|
897
|
+
if (tooBig) {
|
|
898
|
+
pageStates.splice(i, 1);
|
|
899
|
+
pageStatesSize -= state.size;
|
|
900
|
+
}
|
|
901
|
+
else {
|
|
902
|
+
state.data = stateData;
|
|
903
|
+
state.updated = +new Date;
|
|
904
|
+
pageStatesSize += length - state.size;
|
|
905
|
+
}
|
|
906
|
+
throw true;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
if (!tooBig) {
|
|
910
|
+
pageStates.push({
|
|
911
|
+
url: baseurl,
|
|
912
|
+
data: stateData,
|
|
913
|
+
updated: +new Date,
|
|
914
|
+
size: length
|
|
915
|
+
});
|
|
916
|
+
pageStatesSize += length;
|
|
917
|
+
throw true;
|
|
918
|
+
}
|
|
919
|
+
else
|
|
920
|
+
debug("Response too big to store", baseurl, length);
|
|
921
|
+
}
|
|
922
|
+
catch (e) {
|
|
923
|
+
if (e === true) {
|
|
924
|
+
const over = pageStatesSize - pageStateCacheSize;
|
|
925
|
+
if (over > 0) {
|
|
926
|
+
pageStates.sort(function (a, b) {
|
|
927
|
+
return a.updated - b.updated;
|
|
928
|
+
});
|
|
929
|
+
var found = 0;
|
|
930
|
+
for (i = 0; i < cPageStates; i++) {
|
|
931
|
+
found += pageStates[i].size;
|
|
932
|
+
if (found >= over)
|
|
933
|
+
break;
|
|
934
|
+
}
|
|
935
|
+
i++;
|
|
936
|
+
pageStates.splice(0, i);
|
|
937
|
+
debug("Trimmed", i, "items...");
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
else
|
|
941
|
+
throw e;
|
|
942
|
+
}
|
|
943
|
+
history.replaceState(res.hadData, document.title, fullurl);
|
|
944
|
+
self.emit("page", baseurl, path);
|
|
945
|
+
}
|
|
946
|
+
catch (e) {
|
|
947
|
+
debug(e);
|
|
948
|
+
if (replace)
|
|
949
|
+
window.location.reload(true);
|
|
950
|
+
else
|
|
951
|
+
try {
|
|
952
|
+
chash = undefined;
|
|
953
|
+
forwardPopState = [path, post];
|
|
954
|
+
debug("Going backwards");
|
|
955
|
+
history.go(-1);
|
|
956
|
+
}
|
|
957
|
+
catch (e) { }
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
catch (e) {
|
|
962
|
+
debug(e);
|
|
963
|
+
if (replace)
|
|
964
|
+
window.location.reload(true);
|
|
965
|
+
else
|
|
966
|
+
try {
|
|
967
|
+
chash = undefined;
|
|
968
|
+
forwardPopState = [path, post];
|
|
969
|
+
debug("Going backwards");
|
|
970
|
+
history.go(-1);
|
|
971
|
+
}
|
|
972
|
+
catch (e) { }
|
|
973
|
+
}
|
|
974
|
+
}, post, rid);
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
this.registerComponent("a", AnchorElementComponent);
|
|
978
|
+
this.registerComponent("form", FormElementComponent);
|
|
979
|
+
window.addEventListener('popstate', (e) => {
|
|
980
|
+
try {
|
|
981
|
+
var state;
|
|
982
|
+
const bhash = location.href.match(beforeHash);
|
|
983
|
+
const cStates = pageStates.length;
|
|
984
|
+
const baseurl = bhash[1];
|
|
985
|
+
for (var i = 0; i < cStates; i++) {
|
|
986
|
+
const _state = pageStates[i];
|
|
987
|
+
if (_state.url === baseurl) {
|
|
988
|
+
state = _state;
|
|
989
|
+
break;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
const hasState = !!state;
|
|
993
|
+
const rid = ++self.activerid;
|
|
994
|
+
const error = hasState && state.data.error;
|
|
995
|
+
if (error) {
|
|
996
|
+
showError(error);
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
if (forwardPopState) {
|
|
1000
|
+
debug("forwardPopState", forwardPopState);
|
|
1001
|
+
const forward = forwardPopState;
|
|
1002
|
+
setTimeout(() => {
|
|
1003
|
+
if (rid === self.activerid)
|
|
1004
|
+
self.defaultRequestPage(forward[0], forward[1]);
|
|
1005
|
+
});
|
|
1006
|
+
forwardPopState = undefined;
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
if (chash === baseurl) {
|
|
1010
|
+
debug("Only hash has changed...", baseurl);
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
chash = bhash[1];
|
|
1014
|
+
if (e.state)
|
|
1015
|
+
alert("You submitted data to this page, which cannot be re-sent. Because of this, what you're viewing may not be the same now.");
|
|
1016
|
+
if (!hasState)
|
|
1017
|
+
throw new Error("No state for: " + baseurl + ", reloading...");
|
|
1018
|
+
if (state.data.user != self.currentUserID)
|
|
1019
|
+
throw new Error("User has changed since state was created, reloading...");
|
|
1020
|
+
document.title = state.data.title;
|
|
1021
|
+
loader.resetProgress();
|
|
1022
|
+
loader.resetError();
|
|
1023
|
+
self.pagesyshandler(state.data.response, function (err) {
|
|
1024
|
+
if (rid !== self.activerid)
|
|
1025
|
+
return;
|
|
1026
|
+
try {
|
|
1027
|
+
if (err)
|
|
1028
|
+
throw err;
|
|
1029
|
+
if (state.data.body)
|
|
1030
|
+
self.restoreComponents(document.body, state.data.body);
|
|
1031
|
+
if (state.data.scroll)
|
|
1032
|
+
window.scrollTo.apply(window, state.data.scroll);
|
|
1033
|
+
debug("Used stored page state!", state);
|
|
1034
|
+
}
|
|
1035
|
+
catch (err) {
|
|
1036
|
+
debug(err);
|
|
1037
|
+
var url = location.href;
|
|
1038
|
+
if (startsWith.test(url)) {
|
|
1039
|
+
try {
|
|
1040
|
+
if (/reloading\.\.\.$/.test(err.message)) {
|
|
1041
|
+
self.requestPage(url.substring(self.url.length), undefined, true);
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
console.error("Unknown error occured, actually navigating to page...");
|
|
1045
|
+
}
|
|
1046
|
+
catch (e) {
|
|
1047
|
+
console.error(e);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
location.reload(true);
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
catch (err) {
|
|
1055
|
+
debug(err);
|
|
1056
|
+
var url = location.href;
|
|
1057
|
+
if (startsWith.test(url)) {
|
|
1058
|
+
try {
|
|
1059
|
+
if (/reloading\.\.\.$/.test(err.message)) {
|
|
1060
|
+
self.requestPage(url.substring(self.url.length), undefined, true);
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
console.error("Unknown error occured, actually navigating to page...");
|
|
1064
|
+
}
|
|
1065
|
+
catch (e) {
|
|
1066
|
+
console.error(e);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
location.reload(true);
|
|
1070
|
+
}
|
|
1071
|
+
});
|
|
1072
|
+
return true;
|
|
1073
|
+
}
|
|
1074
|
+
get currentUser() {
|
|
1075
|
+
return this.currentUserID;
|
|
1076
|
+
}
|
|
1077
|
+
defaultRequestPage(path, post) {
|
|
1078
|
+
if (post)
|
|
1079
|
+
throw new Error("Posting is not supported without an initialized page system, yet");
|
|
1080
|
+
else
|
|
1081
|
+
location.href = this.resolveUrl(path);
|
|
1082
|
+
}
|
|
1083
|
+
on(event, cb) {
|
|
1084
|
+
var listeners = this._listeners[event];
|
|
1085
|
+
if (listeners)
|
|
1086
|
+
listeners.push(cb);
|
|
1087
|
+
else
|
|
1088
|
+
this._listeners[event] = listeners = [cb];
|
|
1089
|
+
}
|
|
1090
|
+
off(event, cb) {
|
|
1091
|
+
var index;
|
|
1092
|
+
var listeners = this._listeners[event];
|
|
1093
|
+
if (listeners && (index = listeners.indexOf(cb)) > -1)
|
|
1094
|
+
listeners.splice(index, 1);
|
|
1095
|
+
}
|
|
1096
|
+
emit(event, ...args) {
|
|
1097
|
+
const self = this;
|
|
1098
|
+
const listeners = this._listeners[event];
|
|
1099
|
+
if (listeners)
|
|
1100
|
+
listeners.forEach(function (cb) {
|
|
1101
|
+
cb.apply(self, args);
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
var impl;
|
|
1106
|
+
if (window.io)
|
|
1107
|
+
impl = class NexusFrameworkWithIO extends NexusFrameworkBase {
|
|
1108
|
+
constructor(url) {
|
|
1109
|
+
super(url, window.io({
|
|
1110
|
+
path: "/:io"
|
|
1111
|
+
}));
|
|
1112
|
+
const io = this.io;
|
|
1113
|
+
io.on("connect", function () {
|
|
1114
|
+
io.emit("init", loader.requestedResources());
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
};
|
|
1118
|
+
else
|
|
1119
|
+
impl = class NexusFrameworkNoIO extends NexusFrameworkBase {
|
|
1120
|
+
constructor(url) {
|
|
1121
|
+
super(url);
|
|
1122
|
+
}
|
|
1123
|
+
};
|
|
1124
|
+
Object.defineProperty(window, "NexusFrameworkImpl", {
|
|
1125
|
+
value: impl
|
|
1126
|
+
});
|
|
1127
|
+
return impl;
|
|
1128
|
+
}
|
|
1129
|
+
},
|
|
1130
|
+
NexusFrameworkClient: {
|
|
1131
|
+
configurable: true,
|
|
1132
|
+
set: function (instance) {
|
|
1133
|
+
Object.defineProperty(window, "NexusFrameworkClient", {
|
|
1134
|
+
value: instance
|
|
1135
|
+
});
|
|
1136
|
+
},
|
|
1137
|
+
get: function () {
|
|
1138
|
+
const instance = new window.NexusFrameworkImpl();
|
|
1139
|
+
Object.defineProperty(window, "NexusFrameworkClient", {
|
|
1140
|
+
value: instance
|
|
1141
|
+
});
|
|
1142
|
+
return instance;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
})(window);
|
|
1147
1147
|
//# sourceMappingURL=nexusframeworkclient.js.map
|