Skip to content

Commit 5ee55bf

Browse files
authored
Merge pull request #339 from erwindon/singlepage
Singlepage
2 parents cb75527 + a37147b commit 5ee55bf

21 files changed

Lines changed: 471 additions & 299 deletions

saltgui/static/scripts/Api.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -265,10 +265,10 @@ export class API {
265265
const loginResponseStr = Utils.getStorageItem("session", "login-response");
266266
if (!loginResponseStr) {
267267
this.logout().then(() => {
268-
window.location.replace(config.NAV_URL + "/login?reason=no-session");
268+
this.router.goTo("login", {"reason": "no-session"});
269269
return true;
270270
}, () => {
271-
window.location.replace(config.NAV_URL + "/login?reason=no-session");
271+
this.router.goTo("login", {"reason": "no-session"});
272272
return false;
273273
});
274274
}
@@ -280,10 +280,10 @@ export class API {
280280
const expireValue = loginResponse.expire;
281281
if (now > expireValue) {
282282
this.logout().then(() => {
283-
window.location.replace(config.NAV_URL + "/login?reason=expired-session");
283+
this.router.goTo("login", {"reason": "expired-session"});
284284
return true;
285285
}, () => {
286-
window.location.replace(config.NAV_URL + "/login?reason=expired-session");
286+
this.router.goTo("login", {"reason": "expired-session"});
287287
return false;
288288
});
289289
}
@@ -303,6 +303,12 @@ export class API {
303303
return;
304304
}
305305

306+
// allow only one event-stream
307+
if (API.eventsOK) {
308+
return;
309+
}
310+
API.eventsOK = true;
311+
306312
let source;
307313
try {
308314
/* eslint-disable compat/compat */

saltgui/static/scripts/DropDown.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,9 @@ export class DropDownMenu {
139139
this.menuDropdown.style.display = "none";
140140
}
141141

142+
clearMenu () {
143+
while (this.menuDropdownContent.firstChild) {
144+
this.menuDropdownContent.removeChild(this.menuDropdownContent.firstChild);
145+
}
146+
}
142147
}

saltgui/static/scripts/Router.js

Lines changed: 94 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* global config document window */
1+
/* global document window */
22

33
import {API} from "./Api.js";
44
import {BeaconsMinionPage} from "./pages/BeaconsMinion.js";
@@ -29,6 +29,7 @@ export class Router {
2929
Character.init();
3030

3131
this.api = new API();
32+
this.api.router = this;
3233
this.commandbox = new CommandBox(this, this.api);
3334
this.currentPage = undefined;
3435
this.pages = [];
@@ -52,16 +53,20 @@ export class Router {
5253
this._registerPage(this.optionsPage = new OptionsPage(this));
5354
this._registerPage(new LogoutPage(this));
5455

55-
Router._registerRouterEventListeners();
56+
this._registerRouterEventListeners();
5657

5758
this.updateMainMenu();
5859

59-
// This URL already has its prefix added
60-
// therefore is must not be added again
61-
this.goTo(window.location.pathname + window.location.search, true);
60+
const hash = window.location.hash.replace(/^#/, "");
61+
const search = window.location.search;
62+
/* eslint-disable compat/compat */
63+
/* URLSearchParams.entries() is not supported in IE 11 */
64+
/* URLSearchParams is not supported in op_mini all, IE 11, Baidu 7.12 */
65+
this.goTo(hash, Object.fromEntries(new URLSearchParams(search)));
66+
/* eslint-enable compat/compat */
6267
}
6368

64-
static _registerMenuItem (pButtonId, pUrl) {
69+
_registerMenuItem (pButtonId, pHash) {
6570
for (const nr of ["1", "2"]) {
6671
document.getElementById("button-" + pButtonId + nr).
6772
addEventListener("click", (pClickEvent) => {
@@ -75,32 +80,42 @@ export class Router {
7580
panel.style.display = "";
7681
}, 500);
7782
}
78-
window.location.replace(config.NAV_URL + pUrl);
83+
this.goTo(pHash);
7984
});
8085
}
8186
}
8287

83-
static _registerRouterEventListeners () {
88+
_registerRouterEventListeners () {
8489
document.getElementById("logo").
8590
addEventListener("click", () => {
8691
if (window.event.ctrlKey) {
87-
window.location.assign(config.NAV_URL + "/options");
92+
this.goTo("options");
8893
} else {
89-
window.location.assign(config.NAV_URL + "/");
94+
this.goTo("");
9095
}
9196
});
9297

93-
Router._registerMenuItem("minions", "/");
94-
Router._registerMenuItem("grains", "/grains");
95-
Router._registerMenuItem("schedules", "/schedules");
96-
Router._registerMenuItem("pillars", "/pillars");
97-
Router._registerMenuItem("beacons", "/beacons");
98-
Router._registerMenuItem("keys", "/keys");
99-
Router._registerMenuItem("jobs", "/jobs");
100-
Router._registerMenuItem("templates", "/templates");
101-
Router._registerMenuItem("events", "/eventsview");
102-
Router._registerMenuItem("reactors", "/reactors");
103-
Router._registerMenuItem("logout", "/logout");
98+
addEventListener("popstate", (popstate) => {
99+
const hash = popstate.target.location.hash.replace(/^#/, "");
100+
const search = popstate.target.location.search;
101+
/* eslint-disable compat/compat */
102+
/* URLSearchParams.entries() is not supported in IE 11 */
103+
/* URLSearchParams is not supported in op_mini all, IE 11, Baidu 7.12 */
104+
this.goTo(hash, Object.fromEntries(new URLSearchParams(search)), 2);
105+
/* eslint-enable compat/compat */
106+
});
107+
108+
this._registerMenuItem("minions", "");
109+
this._registerMenuItem("grains", "grains");
110+
this._registerMenuItem("schedules", "schedules");
111+
this._registerMenuItem("pillars", "pillars");
112+
this._registerMenuItem("beacons", "beacons");
113+
this._registerMenuItem("keys", "keys");
114+
this._registerMenuItem("jobs", "jobs");
115+
this._registerMenuItem("templates", "templates");
116+
this._registerMenuItem("events", "eventsview");
117+
this._registerMenuItem("reactors", "reactors");
118+
this._registerMenuItem("logout", "logout");
104119
}
105120

106121
_registerPage (pPage) {
@@ -126,36 +141,77 @@ export class Router {
126141
}
127142
}
128143

129-
goTo (pPath, hasPathPrefix = false) {
130-
if (this.switchingPage) {
131-
return;
132-
}
133-
if (window.location.pathname === config.NAV_URL + pPath && this.currentPage) {
134-
return;
135-
}
136-
if (pPath === "/" && Utils.getStorageItem("session", "login-response") === null) {
144+
// pForward = 0 --> normal navigation
145+
// pForward = 1 --> back navigation using regular gui
146+
// pForward = 2 --> back navigation using browser
147+
goTo (pHash, pQuery = {}, pForward = 0) {
148+
149+
if (Utils.getStorageItem("session", "login-response") === null) {
137150
// the fact that we don't have a session will be caught later
138151
// but this was shows less error messages on the console
139-
pPath = "/login";
152+
pHash = "login";
153+
pQuery = {"reason": "no-session"};
140154
}
141-
const pathUrl = (hasPathPrefix ? "" : config.NAV_URL) + pPath.split("?")[0];
155+
156+
// save the details from the parent
157+
const parentHash = document.location.hash.replace(/^#/, "");
158+
const search = window.location.search;
159+
/* eslint-disable compat/compat */
160+
/* URLSearchParams.entries() is not supported in IE 11 */
161+
/* URLSearchParams is not supported in op_mini all, IE 11, Baidu 7.12 */
162+
const parentQuery = Object.fromEntries(new URLSearchParams(search));
163+
/* eslint-enable compat/compat */
164+
142165
for (const route of this.pages) {
143-
if (!route.path.test(pathUrl)) {
166+
if (route.path !== pHash) {
144167
continue;
145168
}
146-
// push history state for login (including redirect to /)
147-
if (pathUrl === config.NAV_URL + "/login" || pathUrl === config.NAV_URL + "/") {
148-
window.history.pushState({}, undefined, pPath);
169+
// push history state, so that the address bar holds the correct
170+
// deep-link; and so that we can use the back-button
171+
let url = "/";
172+
let sep = "?";
173+
for (const key in pQuery) {
174+
const value = pQuery[key];
175+
if (!value || value === "undefined") {
176+
continue;
177+
}
178+
url += sep + key + "=" + encodeURIComponent(value);
179+
sep = "&";
180+
}
181+
url += "#" + pHash;
182+
if (parentHash === route.path) {
183+
// page refresh
184+
// prevents being detected as "forward navigation"
185+
// do nothing
186+
} else if (pForward === 0) {
187+
// forward navigation
188+
window.history.pushState({}, undefined, url);
189+
route.parentHash = parentHash;
190+
route.parentQuery = parentQuery;
191+
} else if (pForward === 1) {
192+
// close-icon on a panel
193+
// do not save parent details
194+
// these were already registered on the way forward
195+
window.history.pushState({}, undefined, url);
196+
} else if (pForward === 2) {
197+
// backward navigation from browser
198+
// do nothing extra
149199
}
150200
this._showPage(route);
151201
return;
152202
}
153203
// route could not be found
154204
// just go to the main page
155-
this.goTo("/");
205+
if (pHash === "") {
206+
console.log("cannot find default page");
207+
return;
208+
}
209+
this.goTo("");
156210
}
157211

158212
_showPage (pPage) {
213+
pPage.clearPage();
214+
159215
pPage.pageElement.style.display = "";
160216

161217
const activeMenuItems = Array.from(document.querySelectorAll(".menu-item-active"));
@@ -191,21 +247,18 @@ export class Router {
191247
elem2.classList.add("menu-item-active");
192248
}
193249

194-
this.switchingPage = true;
195-
196250
pPage.onShow();
197251

198252
// start the event-pipe (again)
199253
// it is either not started, or needs restarting
200254
API.getEvents(this);
201255

202-
if (this.currentPage) {
256+
if (this.currentPage && this.currentPage !== pPage) {
203257
Router._hidePage(this.currentPage);
204258
}
205-
206259
this.currentPage = pPage;
260+
207261
this.currentPage.pageElement.classList.add("current");
208-
this.switchingPage = false;
209262
}
210263

211264
static _hidePage (pPage) {

saltgui/static/scripts/Utils.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ export class Utils {
88
// functions for URL parameters
99

1010
static _getQueryParam2 (pUrl, pName) {
11+
const hashPos = pUrl.indexOf("#");
12+
if (hashPos > 0) {
13+
pUrl = pUrl.substring(0, hashPos);
14+
}
1115
const questionmarkPos = pUrl.indexOf("?");
1216
if (questionmarkPos < 0) {
1317
return undefined;

saltgui/static/scripts/output/Output.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* global config document MouseEvent window */
1+
/* global document MouseEvent window */
22

33
import {Character} from "../Character.js";
44
import {OutputDocumentation} from "./OutputDocumentation.js";
@@ -80,7 +80,7 @@ export class Output {
8080
// typically found in the output of an async job
8181
if (pMinionResponse.match(ParseCommandLine.getPatJid())) {
8282
const link = document.createElement("a");
83-
link.href = config.NAV_URL + "/job?id=" + encodeURIComponent(pMinionResponse);
83+
link.href = "#job?id=" + encodeURIComponent(pMinionResponse);
8484
link.innerText = pMinionResponse;
8585
return link;
8686
}

saltgui/static/scripts/pages/Logout.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* global config */
1+
/* global window */
22

33
import {Page} from "./Page.js";
44
import {Utils} from "../Utils.js";
@@ -24,7 +24,7 @@ export class LogoutPage extends Page {
2424

2525
onShow () {
2626
this.api.logout().then(() => {
27-
window.location.replace(config.NAV_URL + "/login?reason=logout");
27+
this.router.goTo("login", {"reason": "logout"});
2828
});
2929
}
3030

@@ -44,7 +44,7 @@ export class LogoutPage extends Page {
4444
// Api.apiRequest will do all the work
4545
statsPromise.then(() => true, () => {
4646
this.api.logout().then(() => {
47-
window.location.replace(config.NAV_URL + "/login?reason=no-session");
47+
this.router.goTo("login", {"reason": "no-session"});
4848
return false;
4949
});
5050
});
@@ -69,10 +69,10 @@ export class LogoutPage extends Page {
6969
warning.innerText = "Logout";
7070
// logout, and redirect to login screen
7171
this.api.logout().then(() => {
72-
window.location.replace(config.NAV_URL + "/login?reason=expired-session");
72+
this.router.goTo("login", {"reason": "expired-session"});
7373
return true;
7474
}, () => {
75-
window.location.replace(config.NAV_URL + "/login?reason=expired-session");
75+
this.router.goTo("login", {"reason": "expired-session"});
7676
return false;
7777
});
7878
return;

saltgui/static/scripts/pages/Page.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
/* global config */
1+
/* global */
22

33
import {Utils} from "../Utils.js";
44

55
export class Page {
66

77
constructor (pPath, pPageName, pPageSelector, pMenuItemSelector, pRouter) {
8-
this.path = new RegExp("^" + config.NAV_URL.replace(/\//, "[/]") + "[/]" + pPath + "$");
8+
this.path = pPath;
99
this.name = pPageName;
1010

1111
// <div class='route' id='page-keys'>
@@ -130,4 +130,10 @@ export class Page {
130130
panel.onShow();
131131
}
132132
}
133+
134+
clearPage () {
135+
for (const panel of this.panels) {
136+
panel.clearPanel();
137+
}
138+
}
133139
}

saltgui/static/scripts/panels/Beacons.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* global config window */
1+
/* global */
22

33
import {DropDownMenu} from "../DropDown.js";
44
import {Panel} from "./Panel.js";
@@ -96,7 +96,7 @@ export class BeaconsPanel extends Panel {
9696
BeaconsPanel._addMenuItemShowBeacons(menu, minionId);
9797

9898
minionTr.addEventListener("click", () => {
99-
window.location.assign(config.NAV_URL + "/beacons-minion?minionid=" + encodeURIComponent(minionId));
99+
this.router.goTo("beacons-minion", {"minionid": minionId});
100100
});
101101
}
102102

@@ -141,15 +141,11 @@ export class BeaconsPanel extends Panel {
141141

142142
const menu = new DropDownMenu(minionTr);
143143
BeaconsPanel._addMenuItemShowBeacons(menu, pMinionId);
144-
145-
minionTr.addEventListener("click", () => {
146-
window.location.assign(config.NAV_URL + "/beacons-minion?minionid=" + encodeURIComponent(pMinionId));
147-
});
148144
}
149145

150146
static _addMenuItemShowBeacons (pMenu, pMinionId) {
151147
pMenu.addMenuItem("Show beacons", () => {
152-
window.location.assign(config.NAV_URL + "/beacons-minion?minionid=" + encodeURIComponent(pMinionId));
148+
this.router.goTo("beacons-minion", {"minionid": pMinionId});
153149
});
154150
}
155151
}

0 commit comments

Comments
 (0)