Phi: hash based single page navigation and Map tuning
with this patch, navigation with the browsers forward/backward buttons is supported within topics and subtopics as well as deep linking into topics using hashed urls like /gui/#Map or /gui/#Environment/Weather Also, slightly rename map overlay layers
This commit is contained in:
parent
27d34c09e7
commit
4d94d42ff6
9 changed files with 105 additions and 129 deletions
|
@ -65,12 +65,13 @@ html, body, #wrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
#widgetarea {
|
#widgetarea {
|
||||||
position: absolute;
|
overflow-y: auto;
|
||||||
|
position: fixed;
|
||||||
top: 104px;
|
top: 104px;
|
||||||
right: 0;
|
right: 0;
|
||||||
width: 220px;
|
width: 220px;
|
||||||
bottom: 14pt;
|
bottom: 14pt;
|
||||||
background: gray;
|
// background: gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
#statusbar {
|
#statusbar {
|
||||||
|
@ -247,7 +248,7 @@ html, body, #wrapper {
|
||||||
data-bind="button: { icons: { primary: 'ui-icon-refresh' }, text: false }, click: refresh">Refresh Page</button>
|
data-bind="button: { icons: { primary: 'ui-icon-refresh' }, text: false }, click: refresh">Refresh Page</button>
|
||||||
<div id="ui-theme-switcher"></div>
|
<div id="ui-theme-switcher"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="htabs-content" data-bind="component: { name: selectedTopic }"></div>
|
<div id="htabs-content" data-bind="component: { name: selectedTopic, params: { topic: selectedSubtopic } }"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="widgetarea">
|
<div id="widgetarea">
|
||||||
<div data-bind="foreach: widgets">
|
<div data-bind="foreach: widgets">
|
||||||
|
|
|
@ -12,12 +12,13 @@ require.config({
|
||||||
flotresize : '3rdparty/flot/jquery.flot.resize',
|
flotresize : '3rdparty/flot/jquery.flot.resize',
|
||||||
flottime : '3rdparty/flot/jquery.flot.time',
|
flottime : '3rdparty/flot/jquery.flot.time',
|
||||||
fgcommand : 'lib/fgcommand',
|
fgcommand : 'lib/fgcommand',
|
||||||
|
sammy: '3rdparty/sammy-latest.min'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
require([
|
require([
|
||||||
'knockout', 'jquery', 'themeswitch', 'kojqui/button', 'flot'
|
'knockout', 'jquery','sammy', 'themeswitch', 'kojqui/button', 'flot', 'leaflet'
|
||||||
], function(ko, jquery) {
|
], function(ko, jquery, Sammy) {
|
||||||
|
|
||||||
function KnockProps(aliases) {
|
function KnockProps(aliases) {
|
||||||
|
|
||||||
|
@ -329,16 +330,34 @@ require([
|
||||||
];
|
];
|
||||||
|
|
||||||
self.selectedTopic = ko.observable();
|
self.selectedTopic = ko.observable();
|
||||||
|
self.selectedSubtopic = ko.observable();
|
||||||
|
|
||||||
self.selectTopic = function(topic) {
|
self.selectTopic = function(topic) {
|
||||||
self.selectedTopic(topic);
|
location.hash = topic;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.selectTopic(self.topics[0]);
|
|
||||||
|
|
||||||
self.refresh = function() {
|
self.refresh = function() {
|
||||||
location.reload();
|
location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Client-side routes
|
||||||
|
Sammy(function() {
|
||||||
|
this.get('#:topic', function() {
|
||||||
|
console.log("a", this.params );
|
||||||
|
self.selectedTopic( this.params.topic );
|
||||||
|
self.selectedSubtopic('');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.get('#:topic/:subtopic', function() {
|
||||||
|
console.log("b", this.params );
|
||||||
|
self.selectedTopic( this.params.topic );
|
||||||
|
self.selectedSubtopic( this.params.subtopic );
|
||||||
|
});
|
||||||
|
// empty route
|
||||||
|
this.get('', function() {
|
||||||
|
this.app.runRoute( 'get', '#' + self.topics[0] );
|
||||||
|
});
|
||||||
|
}).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
ko.components.register('Aircraft', {
|
ko.components.register('Aircraft', {
|
||||||
|
|
|
@ -9,4 +9,4 @@
|
||||||
click: $parent.selectTopic"></li>
|
click: $parent.selectTopic"></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div id="vtabs-content" data-bind="component: { name: selectedComponent, params: { props: $root.props }}"></div>
|
<div id="vtabs-content" data-bind="component: { name: selectedComponent }"></div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
define([
|
define([
|
||||||
'knockout', 'text!./Aircraft.html',
|
'knockout', 'text!./Aircraft.html', './SubtopicViewmodel'
|
||||||
], function(ko, htmlString) {
|
], function(ko, htmlString, SubtopicViewmodel) {
|
||||||
ko.components.register('Aircraft/Select', {
|
ko.components.register('Aircraft/Select', {
|
||||||
require : 'topics/Aircraft/Select'
|
require : 'topics/Aircraft/Select'
|
||||||
});
|
});
|
||||||
|
@ -21,33 +21,15 @@ define([
|
||||||
require : 'topics/Aircraft/Panel'
|
require : 'topics/Aircraft/Panel'
|
||||||
});
|
});
|
||||||
|
|
||||||
function ViewModel(params) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
self.topics = [
|
|
||||||
'Mass & Balance', 'Checklists', 'Failures', 'Panel', 'Select', 'Help'
|
|
||||||
];
|
|
||||||
|
|
||||||
self.selectedTopic = ko.observable();
|
|
||||||
|
|
||||||
self.selectedComponent = ko.pureComputed(function(){
|
|
||||||
return "Aircraft/" + self.selectedTopic();
|
|
||||||
});
|
|
||||||
|
|
||||||
self.selectTopic = function(topic) {
|
|
||||||
self.selectedTopic(topic);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.selectTopic(self.topics[self.topics.length-1]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewModel.prototype.dispose = function() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return component definition
|
// Return component definition
|
||||||
return {
|
return {
|
||||||
viewModel : ViewModel,
|
viewModel : {
|
||||||
|
createViewModel : function(params, componentInfo) {
|
||||||
|
return new SubtopicViewmodel([
|
||||||
|
'Help', 'Mass & Balance', 'Checklists', 'Failures', 'Panel', 'Select'
|
||||||
|
], "Aircraft", params);
|
||||||
|
},
|
||||||
|
},
|
||||||
template : htmlString
|
template : htmlString
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
define([
|
define([
|
||||||
'knockout', 'text!./Environment.html'
|
'knockout', 'text!./Environment.html', './SubtopicViewmodel'
|
||||||
], function(ko, htmlString) {
|
], function(ko, htmlString, SubtopicViewmodel) {
|
||||||
ko.components.register('Environment/Date & Time', {
|
ko.components.register('Environment/Date & Time', {
|
||||||
require : 'topics/Environment/DateTime'
|
require : 'topics/Environment/DateTime'
|
||||||
});
|
});
|
||||||
|
@ -13,36 +13,15 @@ define([
|
||||||
require : 'topics/Environment/Position'
|
require : 'topics/Environment/Position'
|
||||||
});
|
});
|
||||||
|
|
||||||
function ViewModel(params) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
|
|
||||||
self.topics = [
|
|
||||||
'Date & Time',
|
|
||||||
'Weather',
|
|
||||||
'Position',
|
|
||||||
];
|
|
||||||
|
|
||||||
self.selectedTopic = ko.observable();
|
|
||||||
|
|
||||||
self.selectedComponent = ko.pureComputed(function(){
|
|
||||||
return "Environment/" + self.selectedTopic();
|
|
||||||
});
|
|
||||||
|
|
||||||
self.selectTopic = function(topic) {
|
|
||||||
self.selectedTopic(topic);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.selectTopic(self.topics[0]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewModel.prototype.dispose = function() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return component definition
|
// Return component definition
|
||||||
return {
|
return {
|
||||||
viewModel : ViewModel,
|
viewModel : {
|
||||||
|
createViewModel : function(params, componentInfo) {
|
||||||
|
return new SubtopicViewmodel([
|
||||||
|
'Date & Time', 'Weather', 'Position',
|
||||||
|
], "Environment", params);
|
||||||
|
},
|
||||||
|
},
|
||||||
template : htmlString
|
template : htmlString
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -95,7 +95,7 @@ define([
|
||||||
bounds : L.latLngBounds(L.latLng(16.0, -179.0), L.latLng(72.0, -60.0)),
|
bounds : L.latLngBounds(L.latLng(16.0, -179.0), L.latLng(72.0, -60.0)),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
"dfs.de VFR" : new L.TileLayer(
|
"Germany VFR" : new L.TileLayer(
|
||||||
'https://secais.dfs.de/static-maps/ICAO500-2014-DACH-Reprojected_01/tiles/{z}/{x}/{y}.png', {
|
'https://secais.dfs.de/static-maps/ICAO500-2014-DACH-Reprojected_01/tiles/{z}/{x}/{y}.png', {
|
||||||
minZoom : 5,
|
minZoom : 5,
|
||||||
maxZoom : 15,
|
maxZoom : 15,
|
||||||
|
@ -103,7 +103,7 @@ define([
|
||||||
bounds : L.latLngBounds(L.latLng(46.0, 5.0), L.latLng(55.1, 16.5)),
|
bounds : L.latLngBounds(L.latLng(46.0, 5.0), L.latLng(55.1, 16.5)),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
"Lower Airspace (Germany)" : new L.TileLayer('https://secais.dfs.de/static-maps/lower_20131114/tiles/{z}/{x}/{y}.png',
|
"Germany Lower Airspace" : new L.TileLayer('https://secais.dfs.de/static-maps/lower_20131114/tiles/{z}/{x}/{y}.png',
|
||||||
{
|
{
|
||||||
minZoom : 5,
|
minZoom : 5,
|
||||||
maxZoom : 15,
|
maxZoom : 15,
|
||||||
|
@ -132,7 +132,7 @@ define([
|
||||||
bounds : L.latLngBounds(L.latLng(41.0, -5.3), L.latLng(51.2, 10.1)),
|
bounds : L.latLngBounds(L.latLng(41.0, -5.3), L.latLng(51.2, 10.1)),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
"Clouds" : new L.TileLayer('http://{s}.tile.openweathermap.org/map/clouds/{z}/{x}/{y}.png', {
|
"OpenWeatherMap - Clouds" : new L.TileLayer('http://{s}.tile.openweathermap.org/map/clouds/{z}/{x}/{y}.png', {
|
||||||
maxZoom : 14,
|
maxZoom : 14,
|
||||||
minZoom : 0,
|
minZoom : 0,
|
||||||
subdomains : '12',
|
subdomains : '12',
|
||||||
|
@ -142,7 +142,7 @@ define([
|
||||||
attribution : '© <a target="_blank" href="http://openweathermap.org/">open weather map</a>',
|
attribution : '© <a target="_blank" href="http://openweathermap.org/">open weather map</a>',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
"Precipitation" : new L.TileLayer('http://{s}.tile.openweathermap.org/map/precipitation/{z}/{x}/{y}.png', {
|
"OpenWeatherMap - Precipitation" : new L.TileLayer('http://{s}.tile.openweathermap.org/map/precipitation/{z}/{x}/{y}.png', {
|
||||||
maxZoom : 14,
|
maxZoom : 14,
|
||||||
minZoom : 0,
|
minZoom : 0,
|
||||||
subdomains : '12',
|
subdomains : '12',
|
||||||
|
@ -152,7 +152,7 @@ define([
|
||||||
attribution : '© <a target="_blank" href="http://openweathermap.org/">open weather map</a>',
|
attribution : '© <a target="_blank" href="http://openweathermap.org/">open weather map</a>',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
"Isobares" : new L.TileLayer('http://{s}.tile.openweathermap.org/map/pressure_cntr/{z}/{x}/{y}.png', {
|
"OpenWeatherMap - Isobares" : new L.TileLayer('http://{s}.tile.openweathermap.org/map/pressure_cntr/{z}/{x}/{y}.png', {
|
||||||
maxZoom : 7,
|
maxZoom : 7,
|
||||||
minZoom : 0,
|
minZoom : 0,
|
||||||
subdomains : '12',
|
subdomains : '12',
|
||||||
|
@ -162,7 +162,7 @@ define([
|
||||||
attribution : '© <a target="_blank" href="http://openweathermap.org/">open weather map</a>',
|
attribution : '© <a target="_blank" href="http://openweathermap.org/">open weather map</a>',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
"Wind" : new L.TileLayer('http://{s}.tile.openweathermap.org/map/wind/{z}/{x}/{y}.png', {
|
"OpenWeatherMap - Wind" : new L.TileLayer('http://{s}.tile.openweathermap.org/map/wind/{z}/{x}/{y}.png', {
|
||||||
maxZoom : 7,
|
maxZoom : 7,
|
||||||
minZoom : 0,
|
minZoom : 0,
|
||||||
subdomains : '12',
|
subdomains : '12',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
define([
|
define([
|
||||||
'knockout', 'text!./Simulator.html'
|
'knockout', 'text!./Simulator.html', './SubtopicViewmodel'
|
||||||
], function(ko, htmlString) {
|
], function(ko, htmlString, SubtopicViewmodel) {
|
||||||
ko.components.register('Simulator/Screenshot', {
|
ko.components.register('Simulator/Screenshot', {
|
||||||
require : 'topics/Simulator/Screenshot'
|
require : 'topics/Simulator/Screenshot'
|
||||||
});
|
});
|
||||||
|
@ -21,33 +21,15 @@ define([
|
||||||
require : 'topics/Simulator/Exit'
|
require : 'topics/Simulator/Exit'
|
||||||
});
|
});
|
||||||
|
|
||||||
function ViewModel(params) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
self.topics = [
|
|
||||||
'Screenshot', 'Properties', 'Config', 'Reset', 'Exit'
|
|
||||||
];
|
|
||||||
|
|
||||||
self.selectedTopic = ko.observable();
|
|
||||||
|
|
||||||
self.selectedComponent = ko.pureComputed(function() {
|
|
||||||
return "Simulator/" + self.selectedTopic();
|
|
||||||
});
|
|
||||||
|
|
||||||
self.selectTopic = function(topic) {
|
|
||||||
self.selectedTopic(topic);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.selectTopic(self.topics[0]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewModel.prototype.dispose = function() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return component definition
|
// Return component definition
|
||||||
return {
|
return {
|
||||||
viewModel : ViewModel,
|
viewModel : {
|
||||||
|
createViewModel : function(params, componentInfo) {
|
||||||
|
return new SubtopicViewmodel([
|
||||||
|
'Screenshot', 'Properties', 'Config', 'Reset', 'Exit'
|
||||||
|
], "Simulator", params);
|
||||||
|
},
|
||||||
|
},
|
||||||
template : htmlString
|
template : htmlString
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
34
webgui/topics/SubtopicViewmodel.js
Normal file
34
webgui/topics/SubtopicViewmodel.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
(function(factory) {
|
||||||
|
if (typeof define === "function" && define.amd) {
|
||||||
|
// AMD. Register as an anonymous module.
|
||||||
|
define(['knockout'], factory);
|
||||||
|
} else {
|
||||||
|
// Browser globals
|
||||||
|
factory(ko);
|
||||||
|
}
|
||||||
|
}(function(ko) {
|
||||||
|
|
||||||
|
function SubtopicViewModel(topics, prefix, params) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.topics = topics;
|
||||||
|
|
||||||
|
self.selectedTopic = ko.observable();
|
||||||
|
|
||||||
|
self.selectedComponent = ko.pureComputed(function() {
|
||||||
|
return prefix + "/" + self.selectedTopic();
|
||||||
|
});
|
||||||
|
|
||||||
|
self.selectTopic = function(topic) {
|
||||||
|
location.hash = prefix + "/" + topic;
|
||||||
|
self.selectedTopic(topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
var topic = (params && params.topic) ? ko.unwrap(params.topic) : self.topics[0];
|
||||||
|
if( self.topics.indexOf(topic) == -1 )
|
||||||
|
topic = self.topics[0];
|
||||||
|
self.selectTopic(topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SubtopicViewModel;
|
||||||
|
}));
|
|
@ -1,6 +1,6 @@
|
||||||
define([
|
define([
|
||||||
'knockout', 'text!./Tools.html'
|
'knockout', 'text!./Tools.html', './SubtopicViewmodel'
|
||||||
], function(ko, htmlString) {
|
], function(ko, htmlString, SubtopicViewmodel) {
|
||||||
|
|
||||||
ko.components.register('Tools/Holding Pattern', {
|
ko.components.register('Tools/Holding Pattern', {
|
||||||
require : 'topics/Tools/Holding'
|
require : 'topics/Tools/Holding'
|
||||||
|
@ -14,36 +14,15 @@ define([
|
||||||
require : 'topics/Tools/Stopwatch'
|
require : 'topics/Tools/Stopwatch'
|
||||||
});
|
});
|
||||||
|
|
||||||
function ViewModel(params) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
self.topics = [
|
|
||||||
'Holding Pattern',
|
|
||||||
'Wind Calculator',
|
|
||||||
'Vertical Navigation',
|
|
||||||
'Stopwatch'
|
|
||||||
];
|
|
||||||
|
|
||||||
self.selectedTopic = ko.observable();
|
|
||||||
|
|
||||||
self.selectedComponent = ko.pureComputed(function() {
|
|
||||||
return "Tools/" + self.selectedTopic();
|
|
||||||
});
|
|
||||||
|
|
||||||
self.selectTopic = function(topic) {
|
|
||||||
self.selectedTopic(topic);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.selectTopic(self.topics[0]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewModel.prototype.dispose = function() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return component definition
|
// Return component definition
|
||||||
return {
|
return {
|
||||||
viewModel : ViewModel,
|
viewModel : {
|
||||||
|
createViewModel : function(params, componentInfo) {
|
||||||
|
return new SubtopicViewmodel([
|
||||||
|
'Holding Pattern', 'Wind Calculator', 'Vertical Navigation', 'Stopwatch'
|
||||||
|
], "Tools", params);
|
||||||
|
},
|
||||||
|
},
|
||||||
template : htmlString
|
template : htmlString
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue