Hosts.
This commit is contained in:
parent
a4039e99c1
commit
82b645cf61
@ -241,7 +241,7 @@
|
||||
</script>
|
||||
|
||||
<!-- Hosts list. -->
|
||||
<script type="text/template" id="tpl-hosts-list">
|
||||
<script type="text/template" id="tpl-hosts-list" data-class="container">
|
||||
<h3 class="center"><%= link(name, ['pools', id]) %></h3>
|
||||
|
||||
<table class="table table-bordered table-hover table-striped">
|
||||
@ -255,14 +255,230 @@
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</script>
|
||||
<script type="text/template" id="tpl-hosts-list-item">
|
||||
<td><%= link(name, ['servers', id]) %></td>
|
||||
<script type="text/template" id="tpl-hosts-list-item" data-tag="tr">
|
||||
<td><%= link(name, ['hosts', id]) %></td>
|
||||
<td><%= description %></td>
|
||||
<td><%= progressBar(- 100 * memory.free / memory.total) %></td>
|
||||
<td><%= IPs.length ? IPs.join(', ') : '<i>none</i>' %></td><!-- @todo -->
|
||||
<td><%= formatDuration_fromNow(start_time) %></td>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="tpl-host" data-class="container well">
|
||||
<h3 class="center"><%= name %></h3>
|
||||
|
||||
<!-- Tabs menu. -->
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#general"><i class="icon-home"></i> General</a></li>
|
||||
<li><a href="#memory"><i class="icon-tasks"></i> Memory</a></li>
|
||||
<li><a href="#storage"><i class="icon-hdd"></i> Storage</a></li>
|
||||
<li><a href="#network"><i class="icon-sitemap"></i> Network</a></li>
|
||||
<!-- @todo
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="icon-cog"></i> Misc <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#console"><i class="icon-desktop"></i> Console</a></li>
|
||||
<li><a href="#perfs"><i class="icon-bar-chart"></i> Perfs</a></li>
|
||||
<li><a href="#alerts"><i class="icon-bullhorn"></i> Alerts</a></li>
|
||||
<li><a href="#logs"><i class="icon-comment"></i> Logs</a></li>
|
||||
<li><a href="#other"><i class="icon-asterisk"></i> Other</a></li>
|
||||
</ul>
|
||||
</li> -->
|
||||
</ul>
|
||||
|
||||
<div class="tab-content"></ul>
|
||||
</script>
|
||||
<script type="text/template" id="tpl-host-general" data-class="tab-pane fade in active" data-id="general">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Name:</dt><dd><%= name %></dd>
|
||||
<dt>Description:</dt><dd><%= description %></dd>
|
||||
<dt>Pool master:</dt><dd><%= yesNo(is_pool_master) %></dd>
|
||||
<dt>Enabled:</dt><dd><%= yesNo(enabled) %></dd>
|
||||
<dt>iSCSI IQN:</dt><dd><%= iscsi_iqn || '<i>none</i>' %></dd>
|
||||
<dt>OS:</dt><dd><%= os_version || '<i>none</i>' %></dd>
|
||||
<dt>Log destination:</dt><dd><%= log_destination %></dd>
|
||||
<dt>Server uptime:</dt><dd><%= start_time ? formatDuration_fromNow(start_time) : '<i>N/A</i>' %></dd>
|
||||
<dt>Toolstack uptime:</dt><dd><%= tool_stack_start_time ? formatDuration_fromNow(tool_stack_start_time) : '<i>N/A</i>' %></dd>
|
||||
<dt>UUID:</dt><dd><%= uuid %></dd>
|
||||
</dl>
|
||||
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Hostname:</dt><dd><%= hostname %></dd>
|
||||
<% for (var i = 0; i < PIFs.length; ++i)
|
||||
{
|
||||
var PIF = PIFs[i];
|
||||
%><dt><%= PIF.name %></dt><dd><%= PIF.IP || '<i>no address</i>' %></dd><%
|
||||
} %>
|
||||
</dl>
|
||||
|
||||
<dl class="dl-horizontal">
|
||||
<dt>XCP version:</dt><dd><%= software_version.platform_version %></dd>
|
||||
</dl>
|
||||
|
||||
<dl class="dl-horizontal">
|
||||
<% for (var i = 0; i < CPUs.length; ++i)
|
||||
{
|
||||
var CPU = CPUs[i];
|
||||
%><dt>CPU #<%= i + 1 %>:</dt>
|
||||
<dd>
|
||||
<%= CPU.number %>×
|
||||
<%= CPU.vendor %>
|
||||
<%= CPU.model_name %>
|
||||
<%= CPU.model %>
|
||||
@ <%= CPU.speed %>
|
||||
<%= CPU.vendor %>
|
||||
</dd><%
|
||||
} %>
|
||||
</dl>
|
||||
</script>
|
||||
<script type="text/template" id="tpl-host-memory" data-class="tab-pane fade" data-id="memory">
|
||||
<div class="progress progress-big progress-danger">
|
||||
<%
|
||||
var size_per_VM = _.clone(memory.per_VM);
|
||||
size_per_VM.free = memory.free;
|
||||
size_per_VM = xo.plop(size_per_VM, 0.5, 100);
|
||||
for (var VM_uuid in memory.per_VM)
|
||||
{
|
||||
var VM = VMs[VM_uuid];
|
||||
var VM_mem = formatSize(memory.per_VM[VM_uuid]);
|
||||
var size = size_per_VM[VM_uuid];
|
||||
if ('dom0' === VM_uuid )
|
||||
{
|
||||
%><div class="bar bar-info" style="width: <%= size %>%;">
|
||||
<%= VM.name %><br/>
|
||||
(<%= VM_mem %>)
|
||||
</div><%
|
||||
}
|
||||
else
|
||||
{
|
||||
%><div class="bar" style="width: <%= size %>%;">
|
||||
<%= link(VM.name, ['vms', VM_uuid]) %><br/>
|
||||
(<%= VM_mem %>)
|
||||
</div><%
|
||||
}
|
||||
} %>
|
||||
</div>
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Total memory:</dt><dd><%= formatSize(memory.total) %></dd>
|
||||
<dt>Currently used:</dt><dd><%= formatSize(memory.total-memory.free) %></dd>
|
||||
<dt>Available memory:</dt><dd><%= formatSize(memory.free) %></dd>
|
||||
</dl>
|
||||
</script>
|
||||
<script type="text/template" id="tpl-host-storage" data-class="tab-pane fade" data-id="storage">
|
||||
<table class="table table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Type</th>
|
||||
<th>Shared</th>
|
||||
<th>Usage</th>
|
||||
<th>Size</th>
|
||||
<th>Allocated</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% for (var i = 0; i < SRs.length; ++i)
|
||||
{
|
||||
var SR = SRs[i];
|
||||
%><tr>
|
||||
<td><i class="icon-hdd"></i> <%= SR.name %></td>
|
||||
<td><%= SR.description %></td>
|
||||
<td><%= SR.type %></td>
|
||||
<td><%= SR.shared ? 'Yes' : 'No' %></td>
|
||||
<td><%= Math.round(100*SR.used/SR.total) %>% (<%= formatSize(SR.used) %> used)</td>
|
||||
<td><%= formatSize(SR.total) %></td>
|
||||
<td><%= formatSize(SR.allocated) %></td>
|
||||
</tr><%
|
||||
} %>
|
||||
</tbody>
|
||||
</table>
|
||||
</script>
|
||||
<script type="text/template" id="tpl-host-network" data-class="tab-pane fade" data-id="network">
|
||||
<table class="table table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Speed</th>
|
||||
<th>Duplex</th>
|
||||
<th>IP</th>
|
||||
<th>MAC</th>
|
||||
<th>Hardware</th>
|
||||
<th>Link status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% for (var i = 0; i < PIFs.length; ++i)
|
||||
{
|
||||
var PIF = PIFs[i];
|
||||
%><tr>
|
||||
<td><i class="icon-sitemap"></i> <%= PIF.name %></td>
|
||||
<td><%= PIF.speed %></td>
|
||||
<td><%= PIF.duplex ? 'Yes' : 'No' %></td>
|
||||
<td><%= PIF.IP ? PIF.IP : '<i>none</i>' %></td>
|
||||
<td class="mac-address"><%= PIF.MAC %></td>
|
||||
<td><%= PIF.vendor %> <%= PIF.device %></td>
|
||||
<td><%
|
||||
if (PIF.currently_attached)
|
||||
{
|
||||
%><span class="label label-success">Connected</span></td><%
|
||||
}
|
||||
else
|
||||
{
|
||||
%><span class="label label-important">Not connected</span></td><%
|
||||
} %>
|
||||
</tr><%
|
||||
} %>
|
||||
</tbody>
|
||||
</table>
|
||||
</script>
|
||||
<!-- <script type="text/template" id="tpl-host-console" data-class="tab-pane fade" data-id="console">
|
||||
@todo
|
||||
</script> -->
|
||||
<!-- <script type="text/template" id="tpl-host-perfs" data-class="tab-pane fade" data-id="perfs">
|
||||
@todo
|
||||
</script> -->
|
||||
<!-- <script type="text/template" id="tpl-host-alert" data-class="tab-pane fade" data-id="alert">
|
||||
@todo
|
||||
</script> -->
|
||||
<!-- <script type="text/template" id="tpl-host-other" data-class="tab-pane fade" data-id="other">
|
||||
@todo
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Multipathing:</dt>
|
||||
<dd>None</dd>
|
||||
<dt>Log destination:</dt>
|
||||
<dd>Local</dd>
|
||||
<dt>Power On:</dt>
|
||||
<dd>Disabled</dd>
|
||||
</dl>
|
||||
</script> -->
|
||||
<!-- <script type="text/template" id="tpl-host-logs" data-class="tab-pane fade" data-id="logs">
|
||||
@todo
|
||||
<table class="table table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Event name</th>
|
||||
<th>Details</th>
|
||||
<th>Date</th>
|
||||
<th>Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><i class="icon-comment"></i> Shutting down</td>
|
||||
<td>User canceled</td>
|
||||
<td>15.3.2011 18:29:34</td>
|
||||
<td>00:10:16</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i class="icon-comment"></i> Shutting down</td>
|
||||
<td>User canceled</td>
|
||||
<td>15.3.2011 18:29:22</td>
|
||||
<td>00:10:16</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</script> -->
|
||||
|
||||
<!-- VMs list. -->
|
||||
<script type="text/template" id="tpl-vms-list">
|
||||
<h3 class="center"><%= name %></h3>
|
||||
@ -282,7 +498,7 @@
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</script>
|
||||
<script type="text/template" id="tpl-vms-list-item">
|
||||
<script type="text/template" id="tpl-vms-list-item" data-tag="tr">
|
||||
<% var usage = 100 - Math.round(100*memory.free/memory.total) %>
|
||||
|
||||
<td><%= link(name, ['vms', id]) %></td>
|
||||
@ -303,6 +519,7 @@
|
||||
<script src="http://marionettejs.com/downloads/backbone.marionette.js"></script>
|
||||
<script src="./js/moment.js"></script>
|
||||
<script src="./js/bootstrap.js"></script>
|
||||
<script src="./js/xo.helpers.js"></script>
|
||||
<script src="./js/xo.js"></script>
|
||||
</body>
|
||||
|
||||
|
188
public/js/xo.js
188
public/js/xo.js
@ -265,26 +265,72 @@
|
||||
*
|
||||
* @todo Documentation
|
||||
*
|
||||
* @param {integer} percentage [description]
|
||||
* @param {array} bars [description]
|
||||
* @param {object=} options [description]
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
'progressBar': function (percentage, options) {
|
||||
percentage = Math.round(percentage);
|
||||
if (percentage < 0)
|
||||
'progressBar': function (bars, options) {
|
||||
/* jshint laxbreak:true */
|
||||
|
||||
var has_labels = false;
|
||||
|
||||
// Normalizes bars.
|
||||
if (!_.isArray(bars))
|
||||
{
|
||||
percentage += 100;
|
||||
bars = [bars];
|
||||
}
|
||||
_.each(bars, function (bar, i) {
|
||||
if (_.isNumber(bar))
|
||||
{
|
||||
bars[i] = bar = {
|
||||
'value': bar
|
||||
};
|
||||
}
|
||||
|
||||
var label = (options && options.label) || (percentage +'%');
|
||||
if ((bar.value = Math.round(bar.value)) < 0)
|
||||
{
|
||||
bar.value += 100;
|
||||
}
|
||||
|
||||
return [
|
||||
'<div class="progress progress-info progress-small" ',
|
||||
'title="'+ label +'">',
|
||||
'<div class="bar" style="width:'+ percentage +'%"></div>',
|
||||
'</div>',
|
||||
].join('');
|
||||
has_labels = has_labels || !!bar.label;
|
||||
|
||||
_.defaults(bar, {
|
||||
'color': 'info',
|
||||
'title': bar.value +'%',
|
||||
'label': '',
|
||||
});
|
||||
});
|
||||
|
||||
// Normalizes global options.
|
||||
if (!options)
|
||||
{
|
||||
options = {};
|
||||
}
|
||||
has_labels = has_labels || !!options.label;
|
||||
_.defaults(options, {
|
||||
'size': has_labels ? 'normal' : 'small',
|
||||
'title': (1 === bars.length) ? bars[0].title : '',
|
||||
});
|
||||
|
||||
console.log(options);
|
||||
|
||||
// HTML generation.
|
||||
var html = [
|
||||
'<div class="progress',
|
||||
(options.size in {'small':0, 'big':0}) // @todo Ugly.
|
||||
? ' progress-'+ options.size
|
||||
: '',
|
||||
'" title="', options.title, '">'];
|
||||
_.each(bars, function (bar) {
|
||||
html.push(
|
||||
'<div class="bar bar-', bar.color, '" title="', bar.title,
|
||||
'" style="width: ', bar.value, '%">', bar.label, '</div>'
|
||||
);
|
||||
});
|
||||
html.push(options.label, '</div>');
|
||||
|
||||
return html.join('');
|
||||
},
|
||||
|
||||
/**
|
||||
@ -311,6 +357,19 @@
|
||||
'</span>',
|
||||
].join('');
|
||||
},
|
||||
|
||||
/**
|
||||
* [description]
|
||||
*
|
||||
* @todo Documentation
|
||||
*
|
||||
* @param {boolean} truth [description]
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
'yesNo': function (truth) {
|
||||
return (truth ? 'Yes' : 'No');
|
||||
},
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
@ -398,17 +457,43 @@
|
||||
};
|
||||
}();
|
||||
|
||||
var ItemView = Backbone.Marionette.ItemView.extend({
|
||||
'templateHelpers': template_helpers,
|
||||
var _constructor = function (super_constructor) {
|
||||
return function (options) {
|
||||
// Classes and id can be defined directly in the template element.
|
||||
var tpl = options.template || this.template;
|
||||
if (tpl)
|
||||
{
|
||||
var val;
|
||||
var $tpl = $(tpl);
|
||||
|
||||
'constructor': function () {
|
||||
Backbone.Marionette.ItemView.apply(this, arguments);
|
||||
if (!options.id && (val = $tpl.attr('data-id')))
|
||||
{
|
||||
options.id = val;
|
||||
}
|
||||
if (!options.tagName && (val = $tpl.attr('data-tag')))
|
||||
{
|
||||
options.tagName = val;
|
||||
}
|
||||
if (!options.className && (val = $tpl.attr('data-class')))
|
||||
{
|
||||
options.className = val;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.model)
|
||||
super_constructor.apply(this, arguments);
|
||||
|
||||
// Re-render when the model changes.
|
||||
if (this.model && (false !== this.listenToModelChange))
|
||||
{
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
var ItemView = Backbone.Marionette.ItemView.extend({
|
||||
'templateHelpers': template_helpers,
|
||||
|
||||
'constructor': _constructor(Backbone.Marionette.ItemView),
|
||||
|
||||
'serializeData': _serializeData,
|
||||
});
|
||||
@ -416,14 +501,15 @@
|
||||
var CompositeView = Backbone.Marionette.CompositeView.extend({
|
||||
'templateHelpers': template_helpers,
|
||||
|
||||
'constructor': function () {
|
||||
Backbone.Marionette.CompositeView.apply(this, arguments);
|
||||
'constructor': _constructor(Backbone.Marionette.CompositeView),
|
||||
|
||||
if (this.model)
|
||||
{
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
}
|
||||
},
|
||||
'serializeData': _serializeData,
|
||||
});
|
||||
|
||||
var LayoutView = Backbone.Marionette.Layout.extend({
|
||||
'template_helpers': template_helpers,
|
||||
|
||||
'constructor': _constructor(Backbone.Marionette.Layout),
|
||||
|
||||
'serializeData': _serializeData,
|
||||
});
|
||||
@ -436,13 +522,13 @@
|
||||
'template': '#tpl-stats',
|
||||
});
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
var HostsListItemView = ItemView.extend({
|
||||
'template': '#tpl-hosts-list-item',
|
||||
'tagName': 'tr',
|
||||
});
|
||||
var HostsListView = CompositeView.extend({
|
||||
'template': '#tpl-hosts-list',
|
||||
'className': 'container',
|
||||
|
||||
'itemView': HostsListItemView,
|
||||
'itemViewContainer': 'tbody',
|
||||
@ -455,9 +541,46 @@
|
||||
'itemView': HostsListView,
|
||||
});
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
var HostView = CompositeView.extend({
|
||||
'template': '#tpl-host',
|
||||
|
||||
'itemView': ItemView,
|
||||
'itemViewOptions': function (model) {
|
||||
return {
|
||||
'model': this.model,
|
||||
'template': model.get('template'),
|
||||
};
|
||||
},
|
||||
'itemViewContainer': '.tab-content',
|
||||
|
||||
'events': {
|
||||
'click .nav-tabs a': function (e) {
|
||||
e.preventDefault();
|
||||
$(e.target).tab('show');
|
||||
},
|
||||
},
|
||||
|
||||
'listenToModelChange': false,
|
||||
'initialize': function () {
|
||||
this.collection = new Backbone.Collection([
|
||||
{'template': '#tpl-host-general'},
|
||||
{'template': '#tpl-host-memory'},
|
||||
{'template': '#tpl-host-storage'},
|
||||
{'template': '#tpl-host-network'},
|
||||
]);
|
||||
|
||||
// Only re-render on name change.
|
||||
// @todo Find a cleaner way.
|
||||
this.listenTo(this.model, 'change:name', this.render);
|
||||
},
|
||||
});
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
// var VMsListItemView = ItemView.extend({
|
||||
// 'template': '#tpl-vms-list-item',
|
||||
// 'tagName': 'tr',
|
||||
// });
|
||||
// var VMsListView = CompositeView.extend({
|
||||
// 'template': '#tpl-vms-list',
|
||||
@ -478,7 +601,7 @@
|
||||
'': 'home',
|
||||
|
||||
'hosts': 'hosts_listing',
|
||||
// 'hosts/:id': 'host_show',
|
||||
'hosts/:id': 'host_show',
|
||||
// //'hosts/:id/edit': 'host_edit',
|
||||
|
||||
// 'networks': 'networks_listing',
|
||||
@ -578,6 +701,13 @@
|
||||
}));
|
||||
},
|
||||
|
||||
'host_show': function (id) {
|
||||
var host = new Host({"CPUs":[],"control_domain":"cfd988d7-97a5-4d9e-9409-20853c74ac1f","description":"Default install of XenServer","enabled":true,"hostname":"andromeda","is_pool_master":false,"iscsi_iqn":null,"log_destination":"local","memory":{"free":"8057126912","total":"17143996416","per_VM":{"dom0":"777256960","ae0a235b-b5e5-105c-ed21-f97198cf1751":"2147483648","2f1647cf-54b8-9ee1-fb90-404680f25364":"3145728000","e87afeff-5921-116f-ec91-d772487ac5fe":"536870912","3ff02693-c481-3334-5676-5b74c684092f":"2147483648"}},"name":"andromeda","os_version":null,"PIFs":[{"currently_attached":true,"device":"82574L Gigabit Network Connection","duplex":false,"IP":"","MAC":"e4:11:5b:b7:f3:8f","name":"PIF #0","speed":"0","uuid":"e03acef7-2347-2c09-e6a8-9026e065cfcf","vendor":"Intel Corporation"},{"currently_attached":true,"device":"82574L Gigabit Network Connection","duplex":true,"IP":"88.190.41.127","MAC":"e4:11:5b:b7:f3:8e","name":"PIF #1","speed":"1000","uuid":"b190509e-6bf0-7306-0347-fa6932184853","vendor":"Intel Corporation"}],"SRs":[{"allocated":"0","description":"XenServer Tools ISOs","name":"XenServer Tools","shared":true,"total":"-1","type":"iso","used":"-1"},{"allocated":"0","description":"","name":"LocalISO","shared":false,"total":"-1","type":"iso","used":"-1"},{"allocated":"640289865728","description":"","name":"Local storage","shared":false,"total":"1991761723392","type":"lvm","used":"651002118144"},{"allocated":"0","description":"","name":"Removable storage","shared":false,"total":"0","type":"udev","used":"0"},{"allocated":"0","description":"Physical DVD drives","name":"DVD drives","shared":false,"total":"0","type":"udev","used":"0"}],"start_time":0,"tool_stack_start_time":0,"uuid":"1038e558-ce82-42d8-bf94-5c030cbeacd6","software_version":{"product_version":"6.2.0","product_version_text":"6.2","product_version_text_short":"6.2","platform_name":"XCP","platform_version":"1.8.0","product_brand":"XenServer","build_number":"70446c","hostname":"othone-2","date":"2013-06-14","dbv":"2013.0621","xapi":"1.3","xen":"4.1.5","linux":"2.6.32.43-0.4.1.xs1.8.0.835.170778xen","xencenter_min":"2.0","xencenter_max":"2.0","network_backend":"openvswitch","xs:xenserver-transfer-vm":"XenServer Transfer VM, version 6.2.0, build 70314c","xcp:main":"Base Pack, version 1.8.0, build 70446c","xs:main":"XenServer Pack, version 6.2.0, build 70446c"},"VMs":{"dom0":{"name":"Dom0"},"ae0a235b-b5e5-105c-ed21-f97198cf1751":{"name":"ald"},"2f1647cf-54b8-9ee1-fb90-404680f25364":{"name":"web1"},"e87afeff-5921-116f-ec91-d772487ac5fe":{"name":"shelter"},"3ff02693-c481-3334-5676-5b74c684092f":{"name":"cloud"}}});
|
||||
|
||||
// @todo Gets data from XO-Server.
|
||||
app.main.show(new HostView({'model': host}));
|
||||
},
|
||||
|
||||
'not_found': function (path) {
|
||||
alert('no such page: '+ path);
|
||||
this.navigate('', {'trigger': true});
|
||||
|
Loading…
Reference in New Issue
Block a user