-
Notifications
You must be signed in to change notification settings - Fork 2
/
vanilla-spa.js
97 lines (85 loc) · 1.99 KB
/
vanilla-spa.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/**
* Namespace for VanillaSpa
*/
const VS = {
version: '1.0.1'
};
(function() {
/**
* Gets a file at the given url and returns a Promise
* @param {*string} url The URL of the file to get.
* @returns Promise containing the file data
*/
VS.get = function get(url) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response);
} else {
reject(xhr.statusText);
}
};
xhr.onerror = () => reject(xhr.statusText);
xhr.send();
});
}
/**
* A Layout contains multiple pages.
*/
VS.Layout = class Layout {
constructor(...pages) {
this.pages = pages;
}
load() {
return Promise.all(this.pages.map(page => page.load()));
}
show(el) {
for (let page of this.pages) {
const div = document.createElement('div');
page.show(div);
el.appendChild(div);
}
}
}
/**
* A Page displays content from an HTML template.
*/
VS.Page = class Page {
constructor(url) {
this.url = 'views/' + url;
}
load() {
return VS.get(this.url).then(res => this.html = res);
}
show(el) {
el.innerHTML = this.html;
}
}
/**
* The Router dynamically switches out pages and layouts.
*/
VS.Router = class Router {
constructor(routes, el) {
this.routes = routes;
this.el = el;
window.onhashchange = this.hashChanged.bind(this);
this.hashChanged();
}
async hashChanged(ev) {
if (window.location.hash.length > 0) {
const pageName = window.location.hash.substr(1);
this.show(pageName);
} else if (this.routes['#default']) {
this.show('#default');
}
}
async show(pageName) {
const page = this.routes[pageName];
await page.load();
this.el.innerHTML = '';
page.show(this.el);
}
}
})();