Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 71 additions & 53 deletions WebAPP/App/Controller/AddCase.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Model } from "../Model/AddCase.Model.js";
import { Navbar } from "./Navbar.js";
import { DEF } from "../../Classes/Definition.Class.js";
import { Sidebar } from "./Sidebar.js";
import { NavigationGuard } from "../../Classes/NavigationGuard.Class.js";

export default class AddCase {
static onLoad() {
Expand Down Expand Up @@ -62,6 +63,42 @@ export default class AddCase {
Message.bigBoxDanger(error);
})
}
// Collects the model configuration data from the page and returns it as an object
static getModelConfigurationData(model) {
const years = [];
$.each($('input[type="checkbox"]:checked'), function (key, value) {
years.push($(value).attr("id"));
});

return {
"osy-version": "5.6",
"osy-casename": $("#osy-casename").val().trim(),
"osy-desc": $("#osy-desc").val().trim(),
"osy-date": new Date().toDateString(),
"osy-currency": $("#osy-currency").val(),
"osy-mo": $("#osy-mo").val().trim(),
"osy-tech": model.techs,
"osy-stg": model.stg,
"osy-techGroups": model.techGroups,
"osy-comm": model.commodities,
"osy-ts": model.timeslices,
"osy-se": model.seasons,
"osy-dt": model.daytypes,
"osy-dtb": model.dailytimebrackets,
"osy-emis": model.emissions,
"osy-scenarios": model.scenarios,
"osy-constraints": model.constraints,
"osy-indicators": model.indicators,
"osy-years": years
};
}

// Creates a snapshot used to check for unsaved changes
static getModelConfigurationSnapshot(model) {
const data = AddCase.getModelConfigurationData(model);
delete data["osy-date"];
return JSON.stringify(data);
}

static initPage(model) {
Message.clearMessages();
Expand Down Expand Up @@ -96,13 +133,23 @@ export default class AddCase {
$('#osy-update').show();
$('#osy-save').hide();
}
loadScript("References/smartadmin/js/plugin/ion-slider/ion.rangeSlider.min.js", SmartAdmin.rangeSlider.bind(null, model.years));
// Set up events after the saved years are shown
loadScript("References/smartadmin/js/plugin/ion-slider/ion.rangeSlider.min.js", () => {
SmartAdmin.rangeSlider(model.years);
this.initEvents(model);
});
pageSetUp();
this.initEvents(model);
}

static initEvents(model) {
//console.log('model ', model)
// Remember the last saved configuration to detect unsaved changes
let savedSnapshot = AddCase.getModelConfigurationSnapshot(model);
NavigationGuard.activate({
hasChanges: () => AddCase.getModelConfigurationSnapshot(model) !== savedSnapshot,
update: () => $("#osy-caseForm").jqxValidator("validate")
});

let $divTech = $("#osy-gridTech");
let $divTechGroup = $("#osy-gridTechGroup");
let $divStg = $("#osy-gridStg");
Expand All @@ -121,10 +168,12 @@ export default class AddCase {
e.preventDefault();
e.stopImmediatePropagation();
var casename = $(this).attr('data-ps');
Html.updateCasePicker(casename);
Sidebar.Reload(casename);
AddCase.refreshPage(casename);
Message.smallBoxInfo("Case selection", casename + " is selected!", 3000);
NavigationGuard.requestLeave(() => {
Html.updateCasePicker(casename);
Sidebar.Reload(casename);
AddCase.refreshPage(casename);
Message.smallBoxInfo("Case selection", casename + " is selected!", 3000);
});
});

function render(message, input) {
Expand Down Expand Up @@ -188,15 +237,18 @@ export default class AddCase {
$("#osy-new").on('click', function (event) {
event.preventDefault();
event.stopImmediatePropagation();
AddCase.refreshPage(null);
//Sidebar.Load(null, null)
Sidebar.Reload(null);

$divTech.jqxGrid({ pagesizeoptions:['10', '25', '50', '100', '250', '500', '750', '1000']});
$("#osy-new").hide();
$('#osy-update').hide();
$('#osy-save').show();
Message.smallBoxConfirmation("Confirmation!", "Configure new Model!", 3500);
// Check for unsaved changes before starting a new model
NavigationGuard.requestLeave(() => {
AddCase.refreshPage(null);
//Sidebar.Load(null, null)
Sidebar.Reload(null);

$divTech.jqxGrid({ pagesizeoptions:['10', '25', '50', '100', '250', '500', '750', '1000']});
$("#osy-new").hide();
$('#osy-update').hide();
$('#osy-save').show();
Message.smallBoxConfirmation("Confirmation!", "Configure new Model!", 3500);
});
});

$("#osy-save").off('click');
Expand All @@ -219,49 +271,14 @@ export default class AddCase {
event.stopImmediatePropagation();

Message.loaderStart('Saving model data')
var casename = $("#osy-casename").val().trim();
var desc = $("#osy-desc").val().trim();

const timeElapsed = Date.now();
const today = new Date(timeElapsed);
let date = today.toDateString();

//var date = $("#osy-date").val();
var currency = $("#osy-currency").val();
var mo = $("#osy-mo").val().trim();

var years = new Array();
$.each($('input[type="checkbox"]:checked'), function (key, value) {
years.push($(value).attr("id"));
});

let POSTDATA = {
"osy-version": "5.6",
"osy-casename": casename,
"osy-desc": desc,
"osy-date": date,
"osy-currency": currency,
"osy-mo": mo,
"osy-tech": model.techs,
"osy-stg": model.stg,
"osy-techGroups": model.techGroups,
"osy-comm": model.commodities,
"osy-ts": model.timeslices,
"osy-se": model.seasons,
"osy-dt": model.daytypes,
"osy-dtb": model.dailytimebrackets,

"osy-emis": model.emissions,
"osy-scenarios": model.scenarios,
"osy-constraints": model.constraints,
"osy-indicators": model.indicators,
"osy-years": years
}
const POSTDATA = AddCase.getModelConfigurationData(model);
const casename = POSTDATA["osy-casename"];

Osemosys.saveCase(POSTDATA)
.then(response => {
Message.loaderEnd()
if (response.status_code == "created") {
savedSnapshot = AddCase.getModelConfigurationSnapshot(model);
$("#osy-new").show();
$('#osy-update').show();
$('#osy-save').hide();
Expand All @@ -278,6 +295,7 @@ export default class AddCase {
}
}
if (response.status_code == "edited") {
savedSnapshot = AddCase.getModelConfigurationSnapshot(model);
Html.title(casename, 'Model configuration', 'create & edit');
$("#osy-new").show();
Navbar.initPage(casename);
Expand Down
12 changes: 11 additions & 1 deletion WebAPP/Classes/Message.Class.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,16 @@ export class Message {

});
}
// Prompts the user to save or discard unsaved changes
static confirmUnsavedModelChanges() {
return new Promise(resolve => {
$.SmartMessageBox({
title: "<i class='fa fa-exclamation-triangle danger'></i> Unsaved model configuration",
content: "You have unsaved model configuration changes. Select Save to update the model and stay on this page, or Don't save to leave without saving.",
buttons: "[Don't save][Save]"
}, resolve);
});
}

static resMessage(message) {
$("#res-message").html(
Expand All @@ -275,4 +285,4 @@ export class Message {
static loaderEnd(){
$('#loadermain').hide();
}
}
}
69 changes: 69 additions & 0 deletions WebAPP/Classes/NavigationGuard.Class.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Message } from "./Message.Class.js";

let activeGuard = null;
let leaveRequestPending = false;

// Warn before reloading or closing a page with unsaved changes
function handleBeforeUnload(event) {
if (!activeGuard || !activeGuard.hasChanges()) {
return;
}

event.preventDefault();
event.returnValue = "";
}

// Controls whether the user can leave a page with unsaved changes
export class NavigationGuard {
// Starts checking the current page for unsaved changes
static activate(handlers) {
activeGuard = handlers;
leaveRequestPending = false;
window.addEventListener("beforeunload", handleBeforeUnload);
}

// Stops checking after the user leaves the page
static deactivate() {
activeGuard = null;
leaveRequestPending = false;
window.removeEventListener("beforeunload", handleBeforeUnload);
}

// Checks whether the user can leave, then continues when allowed
static async requestLeave(onAllowed, onBlocked = () => {}) {
if (!activeGuard) {
onAllowed();
return;
}

if (leaveRequestPending) {
return;
}

if (!activeGuard.hasChanges()) {
NavigationGuard.deactivate();
onAllowed();
return;
}

leaveRequestPending = true;

// Wait for the user to choose whether to save or leave
try {
const choice = await Message.confirmUnsavedModelChanges();

if (choice === "Don't save") {
NavigationGuard.deactivate();
onAllowed();
return;
}

if (choice === "Save") {
activeGuard.update();
onBlocked();
}
} finally {
leaveRequestPending = false;
}
}
}
23 changes: 22 additions & 1 deletion WebAPP/Routes/Routes.Class.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Osemosys } from "../../Classes/Osemosys.Class.js";
import { Message } from "../../Classes/Message.Class.js";
import { NavigationGuard } from "../../Classes/NavigationGuard.Class.js";
import { Model } from "./Routes.Model.js";

export class Routes {
Expand Down Expand Up @@ -172,14 +173,34 @@ export class Routes {
});
//setup hasher
hasher.init(); //start listening for history change
let acceptedHash = window.location.hash;
let ignoreNextHashChange = false;
//Listen to hash changes
window.addEventListener("hashchange", function() {
// Ignore the hash change used to restore the current page
if (ignoreNextHashChange) {
ignoreNextHashChange = false;
return;
}

var route = '/';
var hash = window.location.hash;
if (hash.length > 0) {
route = hash.split('#').pop();
}
crossroads.parse(route);

NavigationGuard.requestLeave(
() => {
acceptedHash = hash;
crossroads.parse(route);
},
() => {
if (window.location.hash !== acceptedHash) {
ignoreNextHashChange = true;
window.location.hash = acceptedHash;
}
}
);
});
// trigger hashchange on first page load
window.dispatchEvent(new CustomEvent("hashchange"));
Expand Down