Lot's of JS changes
This commit is contained in:
parent
9531dbb869
commit
9a485df81a
|
@ -9,200 +9,151 @@
|
|||
* });
|
||||
* </code>
|
||||
*/
|
||||
(function() {
|
||||
(function(window, $) {
|
||||
|
||||
var Icinga = function(config) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Our config object
|
||||
*/
|
||||
this.config = config;
|
||||
var Icinga = function (config) {
|
||||
|
||||
/**
|
||||
* Icinga.Logger
|
||||
*/
|
||||
this.logger = null;
|
||||
/**
|
||||
* Our config object
|
||||
*/
|
||||
this.config = config;
|
||||
|
||||
/**
|
||||
* Icinga.UI
|
||||
*/
|
||||
this.ui = null;
|
||||
/**
|
||||
* Icinga.Logger
|
||||
*/
|
||||
this.logger = null;
|
||||
|
||||
/**
|
||||
* Icinga.Loader
|
||||
*/
|
||||
this.loader = null;
|
||||
/**
|
||||
* Icinga.UI
|
||||
*/
|
||||
this.ui = null;
|
||||
|
||||
/**
|
||||
* Icinga.Events
|
||||
*/
|
||||
this.events = null;
|
||||
/**
|
||||
* Icinga.Loader
|
||||
*/
|
||||
this.loader = null;
|
||||
|
||||
/**
|
||||
* Icinga.Timer
|
||||
*/
|
||||
this.timer = null;
|
||||
/**
|
||||
* Icinga.Events
|
||||
*/
|
||||
this.events = null;
|
||||
|
||||
/**
|
||||
* Icinga.Utils
|
||||
*/
|
||||
this.utils = null;
|
||||
/**
|
||||
* Icinga.Timer
|
||||
*/
|
||||
this.timer = null;
|
||||
|
||||
/**
|
||||
* Loaded modules
|
||||
*/
|
||||
this.modules = {};
|
||||
/**
|
||||
* Icinga.History
|
||||
*/
|
||||
this.history = null;
|
||||
|
||||
var self = this;
|
||||
$(document).ready(function() {
|
||||
self.initialize();
|
||||
self = null;
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Icinga.Utils
|
||||
*/
|
||||
this.utils = null;
|
||||
|
||||
Icinga.prototype = {
|
||||
/**
|
||||
* Loaded modules
|
||||
*/
|
||||
this.modules = {};
|
||||
|
||||
/**
|
||||
* Icinga startup, will be triggerd once the document is ready
|
||||
*/
|
||||
initialize: function()
|
||||
{
|
||||
$('html').removeClass('no-js').addClass('js');
|
||||
var self = this;
|
||||
$(document).ready(function () {
|
||||
self.initialize();
|
||||
self = null;
|
||||
});
|
||||
};
|
||||
|
||||
this.utils = new Icinga.Utils(this);
|
||||
this.logger = new Icinga.Logger(this);
|
||||
this.timer = new Icinga.Timer(this);
|
||||
this.ui = new Icinga.UI(this);
|
||||
this.loader = new Icinga.Loader(this);
|
||||
this.events = new Icinga.Events(this);
|
||||
Icinga.prototype = {
|
||||
|
||||
this.timer.initialize();
|
||||
this.events.initialize();
|
||||
this.ui.initialize();
|
||||
this.loader.initialize();
|
||||
this.logger.setLevel('info');
|
||||
this.logger.info('Icinga is ready');
|
||||
this.timer.register(this.refreshTimeSince, this, 1000);
|
||||
},
|
||||
/**
|
||||
* Icinga startup, will be triggerd once the document is ready
|
||||
*/
|
||||
initialize: function () {
|
||||
|
||||
toggleFullscreen: function()
|
||||
{
|
||||
$('#layout').toggleClass('fullscreen');
|
||||
this.ui.fixControls();
|
||||
},
|
||||
this.utils = new Icinga.Utils(this);
|
||||
this.logger = new Icinga.Logger(this);
|
||||
this.timer = new Icinga.Timer(this);
|
||||
this.ui = new Icinga.UI(this);
|
||||
this.loader = new Icinga.Loader(this);
|
||||
this.events = new Icinga.Events(this);
|
||||
this.history = new Icinga.History(this);
|
||||
|
||||
flipContent: function()
|
||||
{
|
||||
var col1 = $('#col1 > div').detach();
|
||||
var col2 = $('#col2 > div').detach();
|
||||
$('#col2').html('');
|
||||
$('#col1').html('');
|
||||
this.timer.initialize();
|
||||
this.events.initialize();
|
||||
this.history.initialize();
|
||||
this.ui.initialize();
|
||||
this.loader.initialize();
|
||||
this.logger.info('Icinga is ready');
|
||||
},
|
||||
|
||||
col1.appendTo('#col2');
|
||||
col2.appendTo('#col1');
|
||||
this.ui.fixControls();
|
||||
},
|
||||
/**
|
||||
* Load a given module by name
|
||||
*/
|
||||
loadModule: function (name) {
|
||||
|
||||
refreshTimeSince: function()
|
||||
{
|
||||
$('.timesince').each(function(idx, el) {
|
||||
var m = el.innerHTML.match(/^(\d+)m\s(\d+)s/);
|
||||
if (m !== null) {
|
||||
var nm = parseInt(m[1]);
|
||||
var ns = parseInt(m[2]);
|
||||
if (ns < 59) {
|
||||
ns++;
|
||||
} else {
|
||||
ns = 0;
|
||||
nm++;
|
||||
}
|
||||
$(el).html(nm + 'm ' + ns + 's');
|
||||
if (this.hasModule(name)) {
|
||||
this.logger.error('Cannot load module ' + name + ' twice');
|
||||
return;
|
||||
}
|
||||
|
||||
this.modules[name] = new Icinga.Module(this, name);
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether a module matching the given name exists
|
||||
*/
|
||||
hasModule: function (name) {
|
||||
return 'undefined' !== typeof this.modules[name] ||
|
||||
'undefined' !== typeof Icinga.availableModules[name];
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a module by name
|
||||
*/
|
||||
module: function (name) {
|
||||
|
||||
if ('undefined' === typeof this.modules[name]) {
|
||||
if ('undefined' !== typeof Icinga.availableModules[name]) {
|
||||
this.modules[name] = new Icinga.Module(
|
||||
this,
|
||||
name,
|
||||
Icinga.availableModules[name]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return this.modules[name];
|
||||
},
|
||||
|
||||
/**
|
||||
* Clean up and unload all Icinga components
|
||||
*/
|
||||
destroy: function () {
|
||||
|
||||
$.each(this.modules, function (name, module) {
|
||||
module.destroy();
|
||||
});
|
||||
|
||||
this.timer.destroy();
|
||||
this.events.destroy();
|
||||
this.loader.destroy();
|
||||
this.ui.destroy();
|
||||
this.logger.debug('Icinga has been destroyed');
|
||||
this.logger.destroy();
|
||||
this.utils.destroy();
|
||||
|
||||
this.modules = [];
|
||||
this.timer = this.events = this.loader = this.ui = this.logger =
|
||||
this.utils = null;
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
getWindowId: function()
|
||||
{
|
||||
var res = window.name.match(/^Icinga_([a-zA-Z0-9])$/);
|
||||
if (res) {
|
||||
return res[1];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
window.Icinga = Icinga;
|
||||
|
||||
hasWindowId: function()
|
||||
{
|
||||
var res = window.name.match(/^Icinga_([a-zA-Z0-9])$/);
|
||||
return typeof res === 'object';
|
||||
},
|
||||
|
||||
setWindowId: function(id)
|
||||
{
|
||||
window.name = 'Icinga_' + id;
|
||||
},
|
||||
|
||||
/**
|
||||
* Load a given module by name
|
||||
*/
|
||||
loadModule: function(name)
|
||||
{
|
||||
if (this.hasModule(name)) {
|
||||
this.logger.error('Cannot load module ' + name + ' twice');
|
||||
return;
|
||||
}
|
||||
this.modules[name] = new Icinga.Module(this, name);
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether a module matching the given name exists
|
||||
*/
|
||||
hasModule: function(name)
|
||||
{
|
||||
return typeof this.modules[name] !== 'undefined' ||
|
||||
typeof Icinga.availableModules[name] !== 'undefined';
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a module by name
|
||||
*/
|
||||
module: function(name)
|
||||
{
|
||||
if (typeof this.modules[name] === 'undefined') {
|
||||
if (typeof Icinga.availableModules[name] !== 'undefined') {
|
||||
this.modules[name] = new Icinga.Module(
|
||||
this,
|
||||
name,
|
||||
Icinga.availableModules[name]
|
||||
);
|
||||
}
|
||||
}
|
||||
return this.modules[name];
|
||||
},
|
||||
|
||||
/**
|
||||
* Clean up and unload all Icinga components
|
||||
*/
|
||||
destroy: function()
|
||||
{
|
||||
$.each(this.modules, function(name, module) {
|
||||
module.destroy();
|
||||
});
|
||||
this.timer.destroy();
|
||||
this.events.destroy();
|
||||
this.loader.destroy();
|
||||
this.ui.destroy();
|
||||
this.logger.debug('Icinga has been destroyed');
|
||||
this.logger.destroy();
|
||||
this.utils.destroy();
|
||||
|
||||
this.modules = [];
|
||||
this.timer = this.events = this.loader = this.ui = this.logger = this.utils = null;
|
||||
}
|
||||
};
|
||||
|
||||
window.Icinga = Icinga;
|
||||
|
||||
Icinga.availableModules = {};
|
||||
|
||||
})(window);
|
||||
Icinga.availableModules = {};
|
||||
|
||||
})(window, window.jQuery);
|
||||
|
|
|
@ -1,266 +1,272 @@
|
|||
(function(Icinga) {
|
||||
/**
|
||||
* Icinga.Events
|
||||
*
|
||||
* Event handlers
|
||||
*/
|
||||
(function (Icinga, $) {
|
||||
|
||||
Icinga.Events = function(icinga) {
|
||||
this.icinga = icinga;
|
||||
};
|
||||
'use strict';
|
||||
|
||||
Icinga.Events.prototype = {
|
||||
Icinga.Events = function (icinga) {
|
||||
this.icinga = icinga;
|
||||
};
|
||||
|
||||
/**
|
||||
* Icinga will call our initialize() function once it's ready
|
||||
*/
|
||||
initialize: function()
|
||||
{
|
||||
this.applyGlobalDefaults();
|
||||
this.icinga.ui.prepareContainers();
|
||||
},
|
||||
Icinga.Events.prototype = {
|
||||
|
||||
// TODO: What's this?
|
||||
applyHandlers: function(el)
|
||||
{
|
||||
var icinga = this.icinga;
|
||||
$('.dashboard > div', el).each(function(idx, el) {
|
||||
var url = $(el).attr('data-icinga-url');
|
||||
if (typeof url === 'undefined') return;
|
||||
icinga.loader.loadUrl(url, $(el)).autorefresh = true;
|
||||
});
|
||||
// Set first links href in a action table tr as row href:
|
||||
$('table.action tr', el).each(function(idx, el) {
|
||||
var $a = $('a[href]', el).first();
|
||||
if ($a.length) {
|
||||
$(el).attr('href', $a.attr('href'));
|
||||
}
|
||||
});
|
||||
$('.icinga-module', el).each(function(idx, mod) {
|
||||
$mod = $(mod);
|
||||
var moduleName = $mod.data('icinga-module');
|
||||
if (icinga.hasModule(moduleName)) {
|
||||
var module = icinga.module(moduleName);
|
||||
// NOT YET, the applyOnloadDings: module.applyEventHandlers(mod);
|
||||
/**
|
||||
* Icinga will call our initialize() function once it's ready
|
||||
*/
|
||||
initialize: function () {
|
||||
this.applyGlobalDefaults();
|
||||
this.applyHandlers($('#layout'));
|
||||
this.icinga.ui.prepareContainers();
|
||||
},
|
||||
|
||||
// TODO: What's this?
|
||||
applyHandlers: function (el) {
|
||||
|
||||
var icinga = this.icinga;
|
||||
|
||||
$('.dashboard > div', el).each(function(idx, el) {
|
||||
var url = $(el).attr('data-icinga-url');
|
||||
if (typeof url === 'undefined') return;
|
||||
icinga.loader.loadUrl(url, $(el)).autorefresh = true;
|
||||
});
|
||||
|
||||
// Set first links href in a action table tr as row href:
|
||||
$('table.action tr', el).each(function(idx, el) {
|
||||
var $a = $('a[href]', el).first();
|
||||
if ($a.length) {
|
||||
$(el).attr('href', $a.attr('href'));
|
||||
}
|
||||
});
|
||||
|
||||
$('.icinga-module', el).each(function(idx, mod) {
|
||||
var $mod = $(mod);
|
||||
var moduleName = $mod.data('icinga-module');
|
||||
if (icinga.hasModule(moduleName)) {
|
||||
var module = icinga.module(moduleName);
|
||||
// NOT YET, the applyOnloadDings: module.applyEventHandlers(mod);
|
||||
}
|
||||
});
|
||||
|
||||
$('input.autofocus', el).focus();
|
||||
|
||||
$('.inlinepie', el).sparkline('html', {
|
||||
type: 'pie',
|
||||
sliceColors: ['#44bb77', '#ffaa44', '#ff5566', '#dcd'],
|
||||
width: '2em',
|
||||
height: '2em',
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Global default event handlers
|
||||
*/
|
||||
applyGlobalDefaults: function () {
|
||||
// We catch resize events
|
||||
$(window).on('resize', { self: this.icinga.ui }, this.icinga.ui.onWindowResize);
|
||||
|
||||
// Destroy Icinga, clean up and interrupt pending requests on unload
|
||||
$( window ).on('unload', { self: this }, this.onUnload);
|
||||
$( window ).on('beforeunload', { self: this }, this.onUnload);
|
||||
|
||||
// We catch scroll events in our containers
|
||||
$('.container').on('scroll', icinga.events.onContainerScroll);
|
||||
|
||||
// We want to catch each link click
|
||||
$(document).on('click', 'a', { self: this }, this.linkClicked);
|
||||
|
||||
// We treat tr's with a href attribute like links
|
||||
$(document).on('click', 'tr[href]', { self: this }, this.linkClicked);
|
||||
|
||||
// We catch all form submit events
|
||||
$(document).on('submit', 'form', { self: this }, this.submitForm);
|
||||
|
||||
// We support an 'autosubmit' class on dropdown form elements
|
||||
$(document).on('change', 'form select.autosubmit', { self: this }, this.submitForm);
|
||||
|
||||
$(document).on('keyup', '#menu input.search', {self: this}, this.submitForm);
|
||||
|
||||
// TBD: a global autocompletion handler
|
||||
// $(document).on('keyup', 'form.auto input', this.formChangeDelayed);
|
||||
// $(document).on('change', 'form.auto input', this.formChanged);
|
||||
// $(document).on('change', 'form.auto select', this.submitForm);
|
||||
},
|
||||
|
||||
onUnload: function (event) {
|
||||
var icinga = event.data.self.icinga;
|
||||
icinga.logger.info('Unloading Icinga');
|
||||
icinga.destroy();
|
||||
},
|
||||
|
||||
/**
|
||||
* A scroll event happened in one of our containers
|
||||
*/
|
||||
onContainerScroll: function (event) {
|
||||
// Ugly. And PLEASE, not so often
|
||||
icinga.ui.fixControls();
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
submitForm: function (event) {
|
||||
var icinga = event.data.self.icinga;
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
// .closest is not required unless subelements to trigger this
|
||||
var $form = $(event.currentTarget).closest('form');
|
||||
var url = $form.attr('action');
|
||||
var method = $form.attr('method');
|
||||
|
||||
var data = $form.serializeArray();
|
||||
// TODO: Check button
|
||||
data.push({ name: 'btn_submit', value: 'yesss' });
|
||||
|
||||
icinga.logger.debug('Submitting form: ' + method + ' ' + url);
|
||||
|
||||
// We should move this to a generic target-finder:
|
||||
var $target = null;
|
||||
if ($form.closest('[data-base-target]').length) {
|
||||
$target = $(
|
||||
'#' + $form.closest('[data-base-target]').data('baseTarget')
|
||||
);
|
||||
} else if ($form.closest('.container').length) {
|
||||
$target = $form.closest('.container');
|
||||
} else {
|
||||
icinga.logger.error('No form target found, stopping here');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$('.inlinepie', el).sparkline('html', {
|
||||
type: 'pie',
|
||||
sliceColors: ['#44bb77', '#ffaa44', '#ff5566', '#dcd'],
|
||||
width: '2em',
|
||||
height: '2em',
|
||||
});
|
||||
icinga.loader.loadUrl(url, $target, data, method);
|
||||
// TODO: Do we really need to return false with stop/preventDefault?
|
||||
return false;
|
||||
},
|
||||
|
||||
},
|
||||
/**
|
||||
* Global default event handlers
|
||||
*/
|
||||
applyGlobalDefaults: function()
|
||||
{
|
||||
// We catch resize events
|
||||
$(window).on('resize', { self: this }, this.onWindowResize);
|
||||
layout1col: function () {
|
||||
if (! $('#layout').hasClass('twocols')) { return; }
|
||||
var $col2 = $('#col2');
|
||||
icinga.logger.debug('Switching to single col');
|
||||
$('#layout').removeClass('twocols');
|
||||
$col2.removeAttr('data-icinga-url');
|
||||
$col2.removeAttr('data-icinga-refresh');
|
||||
$col2.removeData('icingaUrl');
|
||||
$col2.removeData('icingaRefresh');
|
||||
this.icinga.loader.stopPendingRequestsFor($col2);
|
||||
$col2.html('');
|
||||
this.icinga.ui.fixControls();
|
||||
},
|
||||
|
||||
// Destroy Icinga, clean up and interrupt pending requests on unload
|
||||
$( window ).on('unload', { self: this }, this.onUnload);
|
||||
$( window ).on('beforeunload', { self: this }, this.onUnload);
|
||||
layout2col: function () {
|
||||
if ($('#layout').hasClass('twocols')) { return; }
|
||||
icinga.logger.debug('Switching to double col');
|
||||
$('#layout').addClass('twocols');
|
||||
this.icinga.ui.fixControls();
|
||||
},
|
||||
|
||||
// We catch scroll events in our containers
|
||||
$('.container').on('scroll', icinga.events.onContainerScroll);
|
||||
/**
|
||||
* Someone clicked a link or tr[href]
|
||||
*/
|
||||
linkClicked: function (event) {
|
||||
var icinga = event.data.self.icinga;
|
||||
|
||||
// We want to catch each link click
|
||||
$(document).on('click', 'a', { self: this }, this.linkClicked);
|
||||
var $a = $(this);
|
||||
var href = $a.attr('href');
|
||||
var $li;
|
||||
if ($a.attr('target') === '_blank') {
|
||||
return true;
|
||||
}
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
// We treat tr's with a href attribute like links
|
||||
$(document).on('click', 'tr[href]', { self: this }, this.linkClicked);
|
||||
// If link is hash tag...
|
||||
if (href === '#') {
|
||||
if ($a.closest('#menu')) {
|
||||
$li = $a.closest('li');
|
||||
$('#menu .active').removeClass('active');
|
||||
$li.addClass('active');
|
||||
}
|
||||
return;
|
||||
}
|
||||
var $target = $('#col1');
|
||||
var $container = $a.closest('.container');
|
||||
if ($container.length) {
|
||||
$target = $container;
|
||||
}
|
||||
|
||||
// We catch all form submit events
|
||||
$(document).on('submit', 'form', { self: this }, this.submitForm);
|
||||
if ($a.closest('table').length) {
|
||||
$target = $('#col2');
|
||||
icinga.events.layout2col();
|
||||
}
|
||||
if ($a.closest('[data-base-target]').length) {
|
||||
$target = $('#' + $a.closest('[data-base-target]').data('baseTarget'));
|
||||
icinga.events.layout2col();
|
||||
}
|
||||
if ($a.closest('.tree').length) {
|
||||
$li = $a.closest('li');
|
||||
if ($li.find('li').length) {
|
||||
if ($li.hasClass('collapsed')) {
|
||||
$li.removeClass('collapsed');
|
||||
} else {
|
||||
$li.addClass('collapsed');
|
||||
$li.find('li').addClass('collapsed');
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
$target = $('#col2');
|
||||
icinga.events.layout2col();
|
||||
}
|
||||
}
|
||||
|
||||
icinga.loader.loadUrl(href, $target);
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
if ($a.closest('#menu').length) {
|
||||
icinga.events.layout1col();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($a.closest('table.action').length) {
|
||||
if ($('#layout').hasClass('twocols')) {
|
||||
if ($target.attr('id') === 'col2') {
|
||||
return;
|
||||
}
|
||||
icinga.events.layout1col();
|
||||
} else {
|
||||
icinga.events.layout2col();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
// We support an 'autosubmit' class on dropdown form elements
|
||||
$(document).on('change', 'form select.autosubmit', { self: this }, this.submitForm);
|
||||
/*
|
||||
hrefIsHashtag: function(href) {
|
||||
// WARNING: IE gives full URL :(
|
||||
// Also it doesn't support negativ indexes in substr
|
||||
return href.substr(href.length - 1, 1) == '#';
|
||||
},
|
||||
*/
|
||||
|
||||
$(window).on('popstate', { self: this }, this.historyChanged);
|
||||
unbindGlobalHandlers: function () {
|
||||
$(window).off('resize', this.onWindowResize);
|
||||
$(window).off('unload', this.onUnload);
|
||||
$(window).off('beforeunload', this.onUnload);
|
||||
$(document).off('scroll', '.container', this.onContainerScroll);
|
||||
$(document).off('click', 'a', this.linkClicked);
|
||||
$(document).off('click', 'tr[href]', this.linkClicked);
|
||||
$(document).off('submit', 'form', this.submitForm);
|
||||
$(document).off('change', 'form select.autosubmit', this.submitForm);
|
||||
},
|
||||
|
||||
// TBD: a global autocompletion handler
|
||||
// $(document).on('keyup', 'form.auto input', this.formChangeDelayed);
|
||||
// $(document).on('change', 'form.auto input', this.formChanged);
|
||||
// $(document).on('change', 'form.auto select', this.submitForm);
|
||||
},
|
||||
|
||||
onUnload: function(event)
|
||||
{
|
||||
var icinga = event.data.self.icinga;
|
||||
icinga.logger.info('Unloading Icinga');
|
||||
icinga.destroy();
|
||||
},
|
||||
|
||||
historyChanged: function(event)
|
||||
{
|
||||
var icinga = event.data.self.icinga;
|
||||
if (event.originalEvent.state === null) {
|
||||
icinga.logger.debug('No more history steps available');
|
||||
} else {
|
||||
icinga.logger.debug(event.originalEvent.state);
|
||||
}
|
||||
icinga.loader.loadUrl(
|
||||
document.location.pathname + document.location.search,
|
||||
$('#col1')
|
||||
).historyTriggered = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Our window got resized, let's fix our UI
|
||||
*/
|
||||
onWindowResize: function(event)
|
||||
{
|
||||
var icinga = event.data.self.icinga;
|
||||
icinga.ui.fixControls();
|
||||
},
|
||||
|
||||
/**
|
||||
* A scroll event happened in one of our containers
|
||||
*/
|
||||
onContainerScroll: function(event)
|
||||
{
|
||||
// Yet ugly. And PLEASE, not so often
|
||||
icinga.ui.fixControls();
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
submitForm: function (event)
|
||||
{
|
||||
var icinga = event.data.self.icinga;
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
// .closest is not required unless subelements to trigger this
|
||||
var $form = $(event.currentTarget).closest('form');
|
||||
var url = $form.attr('action');
|
||||
var method = $form.attr('method');
|
||||
|
||||
var data = $form.serializeArray();
|
||||
// TODO: Check button
|
||||
data.push({ name: 'btn_submit', value: 'yesss' });
|
||||
|
||||
icinga.logger.debug('Submitting form: ' + method + ' ' + url);
|
||||
|
||||
|
||||
// We should move this to a generic target-finder:
|
||||
var $target = $form.closest('.container');
|
||||
if ($target.length == 0) {
|
||||
$target = $('#body');
|
||||
destroy: function() {
|
||||
// This is gonna be hard, clean up the mess
|
||||
this.unbindGlobalHandlers();
|
||||
this.icinga = null;
|
||||
}
|
||||
};
|
||||
|
||||
icinga.loader.loadUrl(url, $target, data, method);
|
||||
|
||||
// TODO: Do we really need to return false with stop/preventDefault?
|
||||
return false;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Someone clicked a link or tr[href]
|
||||
*/
|
||||
linkClicked: function(event)
|
||||
{
|
||||
var icinga = event.data.self.icinga;
|
||||
|
||||
var $a = $(this);
|
||||
var href = $a.attr('href');
|
||||
if ($a.attr('target') === '_blank') {
|
||||
return true;
|
||||
}
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
if (href === '#') {
|
||||
if ($a.closest('#menu')) {
|
||||
var $li = $a.closest('li');
|
||||
$li.siblings('li.active').removeClass('active');
|
||||
$li.addClass('active');
|
||||
}
|
||||
return;
|
||||
}
|
||||
var $target = $('#col1');
|
||||
var $container = $a.closest('.container');
|
||||
if ($container.length) {
|
||||
$target = $container;
|
||||
}
|
||||
// If link is hash tag...
|
||||
if ($a.closest('table').length) {
|
||||
$target = $('#col2');
|
||||
$('#layout').addClass('twocols');
|
||||
icinga.ui.fixControls();
|
||||
}
|
||||
if ($a.closest('[data-base-target]').length) {
|
||||
$target = $('#' + $a.closest('[data-base-target]').data('baseTarget'));
|
||||
$('#layout').addClass('twocols');
|
||||
icinga.ui.fixControls();
|
||||
}
|
||||
if ($a.closest('.tree').length) {
|
||||
var $li = $a.closest('li');
|
||||
if ($li.find('li').length) {
|
||||
if ($li.hasClass('collapsed')) {
|
||||
$li.removeClass('collapsed');
|
||||
} else {
|
||||
$li.addClass('collapsed');
|
||||
$li.find('li').addClass('collapsed');
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
$target = $('#col2');
|
||||
$('#layout').addClass('twocols');
|
||||
icinga.ui.fixControls();
|
||||
}
|
||||
}
|
||||
icinga.loader.loadUrl(href, $target);
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
if ($a.closest('#menu').length) {
|
||||
$('#layout').removeClass('twocols');
|
||||
$('#col2').html('<ul class="tabs"></ul>');
|
||||
icinga.ui.fixControls();
|
||||
return false;
|
||||
}
|
||||
if ($a.closest('table').length) {
|
||||
if ($('#layout').hasClass('twocols')) {
|
||||
if ($target.attr('id') === 'col2') return;
|
||||
icinga.logger.debug('Switching to single col');
|
||||
$('#layout').removeClass('twocols');
|
||||
icinga.ui.fixControls();
|
||||
} else {
|
||||
icinga.logger.debug('Switching to double col');
|
||||
$('#layout').addClass('twocols');
|
||||
icinga.ui.fixControls();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
hrefIsHashtag: function(href)
|
||||
{
|
||||
// WARNING: IE gives full URL :(
|
||||
// Also it doesn't support negativ indexes in substr
|
||||
return href.substr(href.length - 1, 1) == '#';
|
||||
},
|
||||
*/
|
||||
|
||||
unbindGlobalHandlers: function()
|
||||
{
|
||||
$(window).off('popstate', this.historyChanged);
|
||||
$(window).off('resize', this.onWindowResize);
|
||||
$(window).off('unload', this.onUnload);
|
||||
$(window).off('beforeunload', this.onUnload);
|
||||
$(document).off('scroll', '.container', this.onContainerScroll);
|
||||
$(document).off('click', 'a', this.linkClicked);
|
||||
$(document).off('click', 'tr[href]', this.linkClicked);
|
||||
$(document).off('submit', 'form', this.submitForm);
|
||||
$(document).off('change', 'form select.autosubmit', this.submitForm);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
// This is gonna be hard, clean up the mess
|
||||
this.unbindGlobalHandlers();
|
||||
this.icinga = null;
|
||||
}
|
||||
};
|
||||
|
||||
}(Icinga));
|
||||
}(Icinga, jQuery));
|
||||
|
|
|
@ -3,336 +3,499 @@
|
|||
*
|
||||
* This is where we take care of XHR requests, responses and failures.
|
||||
*/
|
||||
(function(Icinga) {
|
||||
(function(Icinga, $) {
|
||||
|
||||
Icinga.Loader = function(icinga) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* YES, we need Icinga
|
||||
*/
|
||||
this.icinga = icinga;
|
||||
Icinga.Loader = function (icinga) {
|
||||
|
||||
/**
|
||||
* Our base url
|
||||
*/
|
||||
this.baseUrl = icinga.config.baseUrl;
|
||||
/**
|
||||
* YES, we need Icinga
|
||||
*/
|
||||
this.icinga = icinga;
|
||||
|
||||
this.failureNotice = null;
|
||||
/**
|
||||
* Our base url
|
||||
*/
|
||||
this.baseUrl = icinga.config.baseUrl;
|
||||
|
||||
this.exception = null;
|
||||
this.failureNotice = null;
|
||||
|
||||
/**
|
||||
* Pending requests
|
||||
*/
|
||||
this.requests = {};
|
||||
this.exception = null;
|
||||
|
||||
this.autorefreshEnabled = true;
|
||||
};
|
||||
/**
|
||||
* Pending requests
|
||||
*/
|
||||
this.requests = {};
|
||||
|
||||
Icinga.Loader.prototype = {
|
||||
this.autorefreshEnabled = true;
|
||||
};
|
||||
|
||||
initialize: function()
|
||||
{
|
||||
this.icinga.timer.register(this.autorefresh, this, 10000);
|
||||
},
|
||||
Icinga.Loader.prototype = {
|
||||
|
||||
/**
|
||||
* Load the given URL to the given target
|
||||
*
|
||||
* @param {string} url URL to be loaded
|
||||
* @param {object} target Target jQuery element
|
||||
* @param {object} data Optional parameters, usually for POST requests
|
||||
* @param {string} method HTTP method, default is 'GET'
|
||||
*/
|
||||
loadUrl: function (url, $target, data, method)
|
||||
{
|
||||
var id = null;
|
||||
initialize: function () {
|
||||
this.icinga.timer.register(this.autorefresh, this, 500);
|
||||
},
|
||||
|
||||
// Default method is GET
|
||||
if (typeof method === 'undefined') {
|
||||
method = 'GET';
|
||||
}
|
||||
/**
|
||||
* Load the given URL to the given target
|
||||
*
|
||||
* @param {string} url URL to be loaded
|
||||
* @param {object} target Target jQuery element
|
||||
* @param {object} data Optional parameters, usually for POST requests
|
||||
* @param {string} method HTTP method, default is 'GET'
|
||||
*/
|
||||
loadUrl: function (url, $target, data, method) {
|
||||
var id = null;
|
||||
|
||||
this.icinga.logger.debug('Loading ', url, ' to ', $target);
|
||||
|
||||
// We should do better and ignore requests without target and/or id
|
||||
if (typeof $target !== 'undefined' && $target.attr('id')) {
|
||||
id = $target.attr('id');
|
||||
}
|
||||
if (typeof $target !== 'undefined') {
|
||||
// TODO: We shouldn't use data but keep this information somewhere else.
|
||||
if ($target.data('icingaUrl') !== url) {
|
||||
$target.removeAttr('data-icinga-url');
|
||||
$target.removeAttr('data-icinga-refresh');
|
||||
$target.removeData('icingaUrl');
|
||||
$target.removeData('icingaRefresh');
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a pending request for the same target...
|
||||
if (id in this.requests) {
|
||||
// ...ignore the new request if it is already pending with the same URL
|
||||
if (this.requests[id].url === url) {
|
||||
this.icinga.logger.debug('Request to ', url, ' is already running for ', $target);
|
||||
return this.requests[id];
|
||||
}
|
||||
// ...or abort the former request otherwise
|
||||
this.icinga.logger.debug('Aborting pending request loading ', url, ' to ', $target);
|
||||
this.requests[id].abort();
|
||||
}
|
||||
|
||||
// Not sure whether we need this Accept-header
|
||||
var headers = { 'X-Icinga-Accept': 'text/html' };
|
||||
|
||||
// Ask for a new window id in case we don't already have one
|
||||
if (this.icinga.hasWindowId()) {
|
||||
headers['X-Icinga-WindowId'] = this.icinga.getWindowId();
|
||||
} else {
|
||||
headers['X-Icinga-WindowId'] = 'undefined';
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var req = $.ajax({
|
||||
type : method,
|
||||
url : url,
|
||||
data : data,
|
||||
headers: headers,
|
||||
context: self
|
||||
});
|
||||
|
||||
req.$target = $target;
|
||||
req.url = url;
|
||||
req.done(this.onResponse);
|
||||
req.fail(this.onFailure);
|
||||
req.historyTriggered = false;
|
||||
req.autorefresh = false;
|
||||
if (id) {
|
||||
this.requests[id] = req;
|
||||
}
|
||||
return req;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create an URL relative to the Icinga base Url, still unused
|
||||
*
|
||||
* @param {string} url Relative url
|
||||
*/
|
||||
url: function(url)
|
||||
{
|
||||
if (typeof url === 'undefined') {
|
||||
return this.baseUrl;
|
||||
}
|
||||
return this.baseUrl + url;
|
||||
},
|
||||
|
||||
autorefresh: function()
|
||||
{
|
||||
var self = this;
|
||||
if (self.autorefreshEnabled !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$('.container[data-icinga-refresh]').each(function(idx, el) {
|
||||
var $el = $(el);
|
||||
self.loadUrl($el.data('icingaUrl'), $el).autorefresh = true;
|
||||
el = null;
|
||||
});
|
||||
},
|
||||
|
||||
disableAutorefresh: function()
|
||||
{
|
||||
this.autorefreshEnabled = false;
|
||||
},
|
||||
|
||||
enableAutorefresh: function()
|
||||
{
|
||||
this.autorefreshEnabled = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle successful XHR response
|
||||
*/
|
||||
onResponse: function (data, textStatus, req)
|
||||
{
|
||||
if (this.failureNotice !== null) {
|
||||
this.failureNotice.remove();
|
||||
this.failureNotice = null;
|
||||
}
|
||||
|
||||
if (this.exception !== null) {
|
||||
this.exception.remove();
|
||||
this.exception = null;
|
||||
req.$target.removeClass('impact');
|
||||
}
|
||||
|
||||
var url = req.url;
|
||||
var targetId = req.$target.attr('id');
|
||||
this.icinga.logger.debug('Got response for ', req.$target, ', URL was ' + url);
|
||||
|
||||
if (! req.autorefresh) {
|
||||
// TODO: Hook for response/url?
|
||||
var $matches = $('[href="' + url + '"]');
|
||||
$matches.each(function(idx, el) {
|
||||
if ($(el).closest('#menu').length) {
|
||||
$(el).closest('#menu').find('li.active').removeClass('active');
|
||||
} else if ($(el).closest('table.action').length) {
|
||||
$(el).closest('table.action').find('.active').removeClass('active');
|
||||
// Default method is GET
|
||||
if ('undefined' === typeof method) {
|
||||
method = 'GET';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$matches.each(function(idx, el) {
|
||||
if ($(el).closest('#menu').length) {
|
||||
$(el).closest('li').addClass('active');
|
||||
$(el).parents('li').addClass('active');
|
||||
} else if ($(el).closest('table.action').length) {
|
||||
$(el).addClass('active');
|
||||
this.icinga.logger.debug('Loading ', url, ' to ', $target);
|
||||
|
||||
// We should do better and ignore requests without target and/or id
|
||||
if (typeof $target !== 'undefined' && $target.attr('id')) {
|
||||
id = $target.attr('id');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
delete this.requests[targetId];
|
||||
req.$target.attr('icingaurl', this.url);
|
||||
|
||||
//
|
||||
var target = req.getResponseHeader('X-Icinga-Container');
|
||||
if (target) {
|
||||
req.$target = $('body');
|
||||
}
|
||||
|
||||
var refresh = req.getResponseHeader('X-Icinga-Refresh');
|
||||
if (refresh) {
|
||||
// Hmmmm... .data() doesn't work here?
|
||||
req.$target.attr('data-icinga-refresh', refresh);
|
||||
req.$target.attr('data-icinga-url', req.url);
|
||||
}
|
||||
|
||||
// Set a window identifier if the server asks us to do so
|
||||
var windowId = req.getResponseHeader('X-Icinga-WindowId');
|
||||
if (windowId) {
|
||||
this.icinga.setWindowId(windowId);
|
||||
}
|
||||
|
||||
// Update history when necessary. Don't do so for requests triggered
|
||||
// by history or autorefresh events
|
||||
if (! req.historyTriggered && ! req.autorefresh) {
|
||||
|
||||
// We only want to care about top-level containers
|
||||
if (req.$target.parent().closest('.container').length === 0) {
|
||||
this.icinga.logger.debug('Pushing ', req.url, ' to history');
|
||||
window.history.pushState({icinga: true}, null, req.url);
|
||||
}
|
||||
}
|
||||
$resp = $(req.responseText);
|
||||
|
||||
/* Should we try to fiddle with responses containing full HTML? */
|
||||
/*
|
||||
if ($('body', $resp).length) {
|
||||
req.responseText = $('script', $('body', $resp).html()).remove();
|
||||
}
|
||||
*/
|
||||
|
||||
this.renderContentToContainer(req.responseText, req.$target);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle failed XHR response
|
||||
*/
|
||||
onFailure: function (req, textStatus, errorThrown)
|
||||
{
|
||||
var url = req.url;
|
||||
delete this.requests[req.$target.attr('id')];
|
||||
|
||||
if (req.status === 500) {
|
||||
if (this.exception === null) {
|
||||
req.$target.addClass('impact');
|
||||
|
||||
this.exception = this.createNotice(
|
||||
'error',
|
||||
$('h1', $(req.responseText)).first().html()
|
||||
/* 'The connection to the Icinga web server has been lost at ' +
|
||||
this.icinga.utils.timeShort() +
|
||||
'.'
|
||||
*/
|
||||
);
|
||||
this.icinga.ui.fixControls();
|
||||
}
|
||||
} else if (req.status > 0) {
|
||||
this.icinga.logger.debug(req.responseText.slice(0, 100));
|
||||
this.renderContentToContainer(
|
||||
'<h1>' + req.status + ' ' + errorThrown + '</h1> ' +
|
||||
req.responseText,
|
||||
req.$target
|
||||
);
|
||||
|
||||
// Header example:
|
||||
// Icinga.debug(req.getResponseHeader('X-Icinga-Redirect'));
|
||||
} else {
|
||||
if (errorThrown === 'abort') {
|
||||
this.icinga.logger.info('Request to ', url, ' has been aborted for ', req.$target);
|
||||
} else {
|
||||
if (this.failureNotice === null) {
|
||||
this.failureNotice = this.createNotice(
|
||||
'error',
|
||||
'The connection to the Icinga web server has been lost at ' +
|
||||
this.icinga.utils.timeShort() +
|
||||
'.'
|
||||
);
|
||||
|
||||
this.icinga.ui.fixControls();
|
||||
if (typeof $target !== 'undefined') {
|
||||
// TODO: We shouldn't use data but keep this information somewhere else.
|
||||
if ($target.data('icingaUrl') !== url) {
|
||||
$target.removeAttr('data-icinga-url');
|
||||
$target.removeAttr('data-icinga-refresh');
|
||||
$target.removeData('icingaUrl');
|
||||
$target.removeData('icingaRefresh');
|
||||
}
|
||||
this.icinga.logger.error(
|
||||
'Failed to contact web server loading ',
|
||||
url,
|
||||
' for ',
|
||||
req.$target
|
||||
);
|
||||
}
|
||||
|
||||
// If we have a pending request for the same target...
|
||||
if (id in this.requests) {
|
||||
// ...ignore the new request if it is already pending with the same URL
|
||||
if (this.requests[id].url === url) {
|
||||
this.icinga.logger.debug('Request to ', url, ' is already running for ', $target);
|
||||
return this.requests[id];
|
||||
}
|
||||
// ...or abort the former request otherwise
|
||||
this.icinga.logger.debug(
|
||||
'Aborting pending request loading ',
|
||||
url,
|
||||
' to ',
|
||||
$target
|
||||
);
|
||||
|
||||
this.requests[id].abort();
|
||||
}
|
||||
|
||||
// Not sure whether we need this Accept-header
|
||||
var headers = { 'X-Icinga-Accept': 'text/html' };
|
||||
|
||||
// Ask for a new window id in case we don't already have one
|
||||
if (this.icinga.ui.hasWindowId()) {
|
||||
headers['X-Icinga-WindowId'] = this.icinga.ui.getWindowId();
|
||||
} else {
|
||||
headers['X-Icinga-WindowId'] = 'undefined';
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var req = $.ajax({
|
||||
type : method,
|
||||
url : url,
|
||||
data : data,
|
||||
headers: headers,
|
||||
context: self
|
||||
});
|
||||
|
||||
req.$target = $target;
|
||||
req.url = url;
|
||||
req.done(this.onResponse);
|
||||
req.fail(this.onFailure);
|
||||
req.complete(this.onComplete);
|
||||
req.historyTriggered = false;
|
||||
req.autorefresh = false;
|
||||
if (id) {
|
||||
this.requests[id] = req;
|
||||
}
|
||||
this.icinga.ui.refreshDebug();
|
||||
return req;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create an URL relative to the Icinga base Url, still unused
|
||||
*
|
||||
* @param {string} url Relative url
|
||||
*/
|
||||
url: function (url) {
|
||||
if (typeof url === 'undefined') {
|
||||
return this.baseUrl;
|
||||
}
|
||||
return this.baseUrl + url;
|
||||
},
|
||||
|
||||
stopPendingRequestsFor: function ($el) {
|
||||
var id;
|
||||
if (typeof $el !== 'undefined' || ! (id = $el.attr('id'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (id in this.requests) {
|
||||
this.requests[id].abort();
|
||||
}
|
||||
},
|
||||
|
||||
autorefresh: function () {
|
||||
var self = this;
|
||||
if (self.autorefreshEnabled !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$('.container[data-icinga-refresh]').each(function (idx, el) {
|
||||
var $el = $(el);
|
||||
var id = $el.attr('id');
|
||||
if (id in self.requests) {
|
||||
self.icinga.logger.debug('No refresh, request pending', id);
|
||||
return;
|
||||
}
|
||||
|
||||
var interval = $el.data('icingaRefresh');
|
||||
var lastUpdate = $el.data('lastUpdate');
|
||||
|
||||
if (typeof interval === 'undefined' || ! interval) {
|
||||
self.icinga.logger.info('No interval, setting default', id);
|
||||
interval = 10;
|
||||
}
|
||||
|
||||
if (typeof lastUpdate === 'undefined' || ! lastUpdate) {
|
||||
self.icinga.logger.info('No lastUpdate, setting one', id);
|
||||
$el.data('lastUpdate',(new Date()).getTime());
|
||||
return;
|
||||
}
|
||||
interval = interval * 1000;
|
||||
|
||||
// TODO:
|
||||
if ((lastUpdate + interval) > (new Date()).getTime()) {
|
||||
// self.icinga.logger.info(
|
||||
// 'Skipping refresh',
|
||||
// id,
|
||||
// lastUpdate,
|
||||
// interval,
|
||||
// (new Date()).getTime()
|
||||
// );
|
||||
return;
|
||||
}
|
||||
|
||||
self.icinga.logger.info(
|
||||
'Autorefreshing ' + id + ' ' + interval + ' ms passed'
|
||||
);
|
||||
self.loadUrl($el.data('icingaUrl'), $el).autorefresh = true;
|
||||
el = null;
|
||||
});
|
||||
},
|
||||
|
||||
disableAutorefresh: function () {
|
||||
this.autorefreshEnabled = false;
|
||||
},
|
||||
|
||||
enableAutorefresh: function () {
|
||||
this.autorefreshEnabled = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle successful XHR response
|
||||
*/
|
||||
onResponse: function (data, textStatus, req) {
|
||||
var self = this;
|
||||
if (this.failureNotice !== null) {
|
||||
this.failureNotice.remove();
|
||||
this.failureNotice = null;
|
||||
}
|
||||
|
||||
if (this.exception !== null) {
|
||||
this.exception.remove();
|
||||
this.exception = null;
|
||||
req.$target.removeClass('impact');
|
||||
}
|
||||
|
||||
var url = req.url;
|
||||
this.icinga.logger.debug(
|
||||
'Got response for ', req.$target, ', URL was ' + url
|
||||
);
|
||||
|
||||
var $resp = $(req.responseText);
|
||||
var active = false;
|
||||
|
||||
if (! req.autorefresh) {
|
||||
// TODO: Hook for response/url?
|
||||
var $forms = $('[action="' + url + '"]');
|
||||
var $matches = $.merge($('[href="' + url + '"]'), $forms);
|
||||
$matches.each(function (idx, el) {
|
||||
if ($(el).closest('#menu').length) {
|
||||
$('#menu .active').removeClass('active');
|
||||
} else if ($(el).closest('table.action').length) {
|
||||
$(el).closest('table.action').find('.active').removeClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
$matches.each(function (idx, el) {
|
||||
var $el = $(el);
|
||||
if ($el.closest('#menu').length) {
|
||||
if ($el.is('form')) {
|
||||
$('input', $el).addClass('active');
|
||||
} else {
|
||||
$el.closest('li').addClass('active');
|
||||
$el.parents('li').addClass('active');
|
||||
}
|
||||
} else if ($(el).closest('table.action').length) {
|
||||
$el.addClass('active');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// TODO: next container url
|
||||
active = $('[href].active', req.$target).attr('href');
|
||||
}
|
||||
|
||||
req.$target.attr('data-icinga-url', url);
|
||||
|
||||
//
|
||||
var target = req.getResponseHeader('X-Icinga-Container');
|
||||
var newBody = false;
|
||||
if (target) {
|
||||
req.$target = $('#' + target);
|
||||
newBody = true;
|
||||
}
|
||||
|
||||
var title = req.getResponseHeader('X-Icinga-Title');
|
||||
if (title && req.$target.closest('.dashboard').length === 0) {
|
||||
this.icinga.ui.setTitle(title);
|
||||
}
|
||||
|
||||
var refresh = req.getResponseHeader('X-Icinga-Refresh');
|
||||
if (refresh) {
|
||||
// Hmmmm... .data() doesn't work here?
|
||||
req.$target.attr('data-icinga-refresh', refresh);
|
||||
req.$target.attr('data-last-update', (new Date()).getTime());
|
||||
req.$target.data('lastUpdate', (new Date()).getTime());
|
||||
req.$target.data('icingaRefresh', refresh);
|
||||
} else {
|
||||
req.$target.removeAttr('data-icinga-refresh');
|
||||
req.$target.removeAttr('data-last-update');
|
||||
req.$target.removeData('icingaRefresh');
|
||||
req.$target.removeData('lastUpdate');
|
||||
}
|
||||
|
||||
// Set a window identifier if the server asks us to do so
|
||||
var windowId = req.getResponseHeader('X-Icinga-WindowId');
|
||||
if (windowId) {
|
||||
this.icinga.ui.setWindowId(windowId);
|
||||
}
|
||||
|
||||
// Update history when necessary. Don't do so for requests triggered
|
||||
// by history or autorefresh events
|
||||
if (! req.historyTriggered && ! req.autorefresh) {
|
||||
|
||||
// We only want to care about top-level containers
|
||||
if (req.$target.parent().closest('.container').length === 0) {
|
||||
this.icinga.history.pushCurrentState();
|
||||
/*
|
||||
this.icinga.logger.debug('Pushing ', req.url, ' to history');
|
||||
if (typeof window.history.pushState !== 'undefined') {
|
||||
window.history.pushState({icinga: true}, null, req.url);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
// Handle search requests, still hardcoded
|
||||
if (req.url === '/search' &&
|
||||
req.$target.data('icingaUrl') === '/search')
|
||||
{
|
||||
// TODO: We need dashboard pane and container identifiers (not ids)
|
||||
var targets = [];
|
||||
$('.dashboard .container').each(function (idx, el) {
|
||||
targets.push($(el));
|
||||
});
|
||||
|
||||
var i = 0;
|
||||
$('.dashboard .container', $resp).each(function (idx, el) {
|
||||
var $el = $(el);
|
||||
var url = $el.data('icingaUrl');
|
||||
targets[i].data('icingaUrl', url);
|
||||
|
||||
var title = $('h1', $el).first();
|
||||
$('h1', targets[i]).first().replaceWith(title);
|
||||
|
||||
self.loadUrl(url, targets[i]);
|
||||
i++;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
req.$target.attr('data-icinga-url', req.url);
|
||||
req.$target.data('icingaUrl', req.url);
|
||||
|
||||
/* Should we try to fiddle with responses containing full HTML? */
|
||||
/*
|
||||
if ($('body', $resp).length) {
|
||||
req.responseText = $('script', $('body', $resp).html()).remove();
|
||||
}
|
||||
*/
|
||||
/*
|
||||
|
||||
var containers = [];
|
||||
|
||||
$('.dashboard .container').each(function(idx, el) {
|
||||
urls.push($(el).data('icingaUrl'));
|
||||
});
|
||||
console.log(urls);
|
||||
$('.container[data-icinga-refresh]').each(function(idx, el) {
|
||||
var $el = $(el);
|
||||
self.loadUrl($el.data('icingaUrl'), $el).autorefresh = true;
|
||||
el = null;
|
||||
});
|
||||
*/
|
||||
|
||||
this.renderContentToContainer($resp, req.$target);
|
||||
if (newBody) {
|
||||
this.icinga.ui.fixDebugVisibility().triggerWindowResize();
|
||||
}
|
||||
|
||||
if (active) {
|
||||
$('[href="' + active + '"]', req.$target).addClass('active');
|
||||
}
|
||||
},
|
||||
|
||||
onComplete: function (req, textStatus) {
|
||||
delete this.requests[req.$target.attr('id')];
|
||||
this.icinga.ui.refreshDebug();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle failed XHR response
|
||||
*/
|
||||
onFailure: function (req, textStatus, errorThrown) {
|
||||
var url = req.url;
|
||||
|
||||
if (req.status === 500) {
|
||||
if (this.exception === null) {
|
||||
req.$target.addClass('impact');
|
||||
|
||||
this.exception = this.createNotice(
|
||||
'error',
|
||||
$('h1', $(req.responseText)).first().html()
|
||||
);
|
||||
this.icinga.ui.fixControls();
|
||||
}
|
||||
} else if (req.status > 0) {
|
||||
this.icinga.logger.debug(req.responseText.slice(0, 100));
|
||||
this.renderContentToContainer(
|
||||
'<h1>' + req.status + ' ' + errorThrown + '</h1> ' +
|
||||
req.responseText,
|
||||
req.$target
|
||||
);
|
||||
|
||||
// Header example:
|
||||
// Icinga.debug(req.getResponseHeader('X-Icinga-Redirect'));
|
||||
} else {
|
||||
if (errorThrown === 'abort') {
|
||||
this.icinga.logger.info(
|
||||
'Request to ' + url + ' has been aborted for ',
|
||||
req.$target
|
||||
);
|
||||
} else {
|
||||
if (this.failureNotice === null) {
|
||||
this.failureNotice = this.createNotice(
|
||||
'error',
|
||||
'The connection to the Icinga web server has been lost at ' +
|
||||
this.icinga.utils.timeShort() +
|
||||
'.'
|
||||
);
|
||||
|
||||
this.icinga.ui.fixControls();
|
||||
}
|
||||
|
||||
this.icinga.logger.error(
|
||||
'Failed to contact web server loading ',
|
||||
url,
|
||||
' for ',
|
||||
req.$target
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
createNotice: function (severity, message) {
|
||||
return $(
|
||||
'<li class="' + severity + '">' + message + '</li>'
|
||||
).appendTo($('#notifications'));
|
||||
},
|
||||
|
||||
/**
|
||||
* Smoothly render given HTML to given container
|
||||
*/
|
||||
renderContentToContainer: function (content, $container) {
|
||||
// Disable all click events while rendering
|
||||
$('*').click(function (event) {
|
||||
event.stopImmediatePropagation();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
// Container update happens here
|
||||
var scrollPos = false;
|
||||
var containerId = $container.attr('id');
|
||||
if (typeof containerId !== 'undefined') {
|
||||
scrollPos = $container.scrollTop();
|
||||
}
|
||||
|
||||
var origFocus = document.activeElement;
|
||||
var $content = $(content);
|
||||
if (false &&
|
||||
$('.dashboard', $content).length > 0 &&
|
||||
$('.dashboard', $container).length === 0
|
||||
) {
|
||||
// $('.dashboard', $content)
|
||||
// $container.html(content);
|
||||
|
||||
} else {
|
||||
if ($container.closest('.dashboard').length &&
|
||||
! $('h1', $content).length
|
||||
) {
|
||||
var title = $('h1', $container).first().detach();
|
||||
$('h1', $content).first().detach();
|
||||
$container.html(title).append(content);
|
||||
} else {
|
||||
$container.html(content);
|
||||
}
|
||||
}
|
||||
if (scrollPos !== false) {
|
||||
$container.scrollTop(scrollPos);
|
||||
}
|
||||
if (origFocus) {
|
||||
origFocus.focus();
|
||||
}
|
||||
|
||||
// TODO: this.icinga.events.refreshContainer(container);
|
||||
var icinga = this.icinga;
|
||||
icinga.events.applyHandlers($container);
|
||||
icinga.ui.initializeControls($container);
|
||||
icinga.ui.fixControls();
|
||||
|
||||
// Re-enable all click events
|
||||
$('*').off('click');
|
||||
},
|
||||
|
||||
/**
|
||||
* On shutdown we kill all pending requests
|
||||
*/
|
||||
destroy: function() {
|
||||
$.each(this.requests, function(id, request) {
|
||||
request.abort();
|
||||
});
|
||||
this.icinga = null;
|
||||
this.requests = {};
|
||||
}
|
||||
},
|
||||
|
||||
createNotice: function(severity, message) {
|
||||
return $('<li class="' + severity + '">' + message + '</li>').appendTo($('#notifications'));
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Smoothly render given HTML to given container
|
||||
*/
|
||||
renderContentToContainer: function (content, $container)
|
||||
{
|
||||
// Disable all click events while rendering
|
||||
$('*').click(function( event ) {
|
||||
event.stopImmediatePropagation();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
// Container update happens here
|
||||
var scrollPos = $container.scrollTop();
|
||||
$container.html(content);
|
||||
$container.scrollTop(scrollPos);
|
||||
|
||||
// TODO: this.icinga.events.refreshContainer(container);
|
||||
var icinga = this.icinga;
|
||||
icinga.events.applyHandlers($container);
|
||||
icinga.ui.initializeControls($container);
|
||||
icinga.ui.fixControls();
|
||||
|
||||
// Re-enable all click events
|
||||
$('*').off('click');
|
||||
},
|
||||
|
||||
/**
|
||||
* On shutdown we kill all pending requests
|
||||
*/
|
||||
destroy: function() {
|
||||
$.each(this.requests, function(id, request) {
|
||||
request.abort();
|
||||
});
|
||||
this.icinga = null;
|
||||
this.requests = {};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}(Icinga));
|
||||
}(Icinga, jQuery));
|
||||
|
|
|
@ -1,96 +1,117 @@
|
|||
(function(Icinga) {
|
||||
/**
|
||||
* Icinga.Logger
|
||||
*
|
||||
* Well, log output. Rocket science.
|
||||
*/
|
||||
(function (Icinga) {
|
||||
|
||||
Icinga.Logger = function(icinga) {
|
||||
'use strict';
|
||||
|
||||
// Well... we don't really need Icinga right now
|
||||
this.icinga = icinga;
|
||||
Icinga.Logger = function (icinga) {
|
||||
|
||||
this.logLevel = 'info';
|
||||
this.icinga = icinga;
|
||||
|
||||
this.logLevel = 'info';
|
||||
|
||||
this.logLevels = {
|
||||
'debug': 0,
|
||||
'info' : 1,
|
||||
'warn' : 2,
|
||||
'error': 3
|
||||
};
|
||||
|
||||
this.logLevels = {
|
||||
'debug': 0,
|
||||
'info' : 1,
|
||||
'warn' : 2,
|
||||
'error': 3
|
||||
};
|
||||
|
||||
// Let's get started
|
||||
this.initialize();
|
||||
};
|
||||
Icinga.Logger.prototype = {
|
||||
|
||||
Icinga.Logger.prototype = {
|
||||
/**
|
||||
* Whether the browser has a console object
|
||||
*/
|
||||
hasConsole: function () {
|
||||
return 'undefined' !== typeof console;
|
||||
},
|
||||
|
||||
/**
|
||||
* Logger initialization
|
||||
*/
|
||||
initialize: function()
|
||||
{
|
||||
},
|
||||
/**
|
||||
* Raise or lower current log level
|
||||
*
|
||||
* Messages blow this threshold will be silently discarded
|
||||
*/
|
||||
setLevel: function (level) {
|
||||
if ('undefined' !== typeof this.numericLevel(level)) {
|
||||
this.logLevel = level;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether the browser has a console object
|
||||
*/
|
||||
hasConsole: function()
|
||||
{
|
||||
return typeof console !== 'undefined';
|
||||
},
|
||||
/**
|
||||
* Log a debug message
|
||||
*/
|
||||
debug: function () {
|
||||
return this.writeToConsole('debug', arguments);
|
||||
},
|
||||
|
||||
debug: function(msg)
|
||||
{
|
||||
this.writeToConsole('debug', arguments);
|
||||
},
|
||||
/**
|
||||
* Log an informational message
|
||||
*/
|
||||
info: function () {
|
||||
return this.writeToConsole('info', arguments);
|
||||
},
|
||||
|
||||
setLevel: function(level)
|
||||
{
|
||||
if (this.numericLevel(level) !== 'undefined') {
|
||||
this.logLevel = level;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Log a warning message
|
||||
*/
|
||||
warn: function () {
|
||||
return this.writeToConsole('warn', arguments);
|
||||
},
|
||||
|
||||
info: function()
|
||||
{
|
||||
this.writeToConsole('info', arguments);
|
||||
},
|
||||
/**
|
||||
* Log an error message
|
||||
*/
|
||||
error: function () {
|
||||
return this.writeToConsole('error', arguments);
|
||||
},
|
||||
|
||||
warn: function()
|
||||
{
|
||||
this.writeToConsole('warn', arguments);
|
||||
},
|
||||
/**
|
||||
* Write a log message with the given level to the console
|
||||
*/
|
||||
writeToConsole: function (level, args) {
|
||||
|
||||
error: function()
|
||||
{
|
||||
this.writeToConsole('error', arguments);
|
||||
},
|
||||
args = Array.prototype.slice.call(args);
|
||||
|
||||
writeToConsole: function(level, args) {
|
||||
args = Array.prototype.slice.call(args);
|
||||
args.unshift(this.icinga.utils.timeWithMs());
|
||||
if (this.hasConsole() && this.hasLogLevel(level)) {
|
||||
console[level].apply(console, args);
|
||||
}
|
||||
},
|
||||
// We want our log messages to carry precise timestamps
|
||||
args.unshift(this.icinga.utils.timeWithMs());
|
||||
|
||||
numericLevel: function(level)
|
||||
{
|
||||
var ret = this.logLevels[level];
|
||||
if (typeof ret === 'undefined') {
|
||||
throw 'Got invalid log level ' + level;
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
if (this.hasConsole() && this.hasLogLevel(level)) {
|
||||
console[level].apply(console, args);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
hasLogLevel: function(level)
|
||||
{
|
||||
return this.numericLevel(level) >= this.numericLevel(this.logLevel);
|
||||
},
|
||||
/**
|
||||
* Return the numeric identifier fot a given log level
|
||||
*/
|
||||
numericLevel: function (level) {
|
||||
var ret = this.logLevels[level];
|
||||
if ('undefined' === typeof ret) {
|
||||
throw 'Got invalid log level ' + level;
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
/**
|
||||
* There isn't much to clean up here
|
||||
*/
|
||||
destroy: function() {
|
||||
this.enabled = false;
|
||||
this.icinga = null;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Whether a given log level exists
|
||||
*/
|
||||
hasLogLevel: function (level) {
|
||||
return this.numericLevel(level) >= this.numericLevel(this.logLevel);
|
||||
},
|
||||
|
||||
/**
|
||||
* There isn't much to clean up here
|
||||
*/
|
||||
destroy: function () {
|
||||
this.enabled = false;
|
||||
this.icinga = null;
|
||||
}
|
||||
};
|
||||
|
||||
}(Icinga));
|
||||
|
|
|
@ -1,112 +1,124 @@
|
|||
/**
|
||||
* This is how we bootstrap JS code in our modules
|
||||
*/
|
||||
(function(Icinga) {
|
||||
(function(Icinga, $) {
|
||||
|
||||
Icinga.Module = function(icinga, name, prototyp) {
|
||||
'use strict';
|
||||
|
||||
// The Icinga instance
|
||||
this.icinga = icinga;
|
||||
Icinga.Module = function (icinga, name, prototyp) {
|
||||
|
||||
// Event handlers registered by this module
|
||||
this.handlers = [];
|
||||
// The Icinga instance
|
||||
this.icinga = icinga;
|
||||
|
||||
this.registeredHandlers = {};
|
||||
// Event handlers registered by this module
|
||||
this.handlers = [];
|
||||
|
||||
// The module name
|
||||
this.name = name;
|
||||
this.registeredHandlers = {};
|
||||
|
||||
// The JS prototype for this module
|
||||
this.prototyp = prototyp;
|
||||
// The module name
|
||||
this.name = name;
|
||||
|
||||
// Once initialized, this will be an instance of the modules prototype
|
||||
this.object = {};
|
||||
// The JS prototype for this module
|
||||
this.prototyp = prototyp;
|
||||
|
||||
// Initialize this module
|
||||
this.initialize();
|
||||
};
|
||||
// Once initialized, this will be an instance of the modules prototype
|
||||
this.object = {};
|
||||
|
||||
Icinga.Module.prototype = {
|
||||
// Initialize this module
|
||||
this.initialize();
|
||||
};
|
||||
|
||||
initialize: function()
|
||||
{
|
||||
try {
|
||||
// The constructor of the modules prototype must be prepared to get an
|
||||
// instance of Icinga.Module
|
||||
this.object = new this.prototyp(this);
|
||||
this.applyRegisteredEventHandlers();
|
||||
} catch(e) {
|
||||
this.icinga.logger.error('Failed to load module ', this.name, ': ', e);
|
||||
return false;
|
||||
}
|
||||
Icinga.Module.prototype = {
|
||||
|
||||
// That's all, the module is ready
|
||||
this.icinga.logger.debug('Module ' + this.name + ' has been initialized');
|
||||
return true;
|
||||
},
|
||||
initialize: function () {
|
||||
|
||||
/**
|
||||
* Globally register this modules event handlers
|
||||
*/
|
||||
registerEventHandlers: function(handlers)
|
||||
{
|
||||
this.registeredHandlers = handlers;
|
||||
return this;
|
||||
},
|
||||
try {
|
||||
|
||||
applyRegisteredEventHandlers: function()
|
||||
{
|
||||
var self = this;
|
||||
$.each(this.registeredHandlers, function(filter, events) {
|
||||
$.each(events, function (event, handler) {
|
||||
// TODO: if (event[1] === 'each') {
|
||||
// $(event[0], $(el)).each(event[2]);
|
||||
self.bindEventHandler(
|
||||
event,
|
||||
'.module-' + self.name + ' ' + filter,
|
||||
handler
|
||||
);
|
||||
});
|
||||
});
|
||||
self = null;
|
||||
return this;
|
||||
},
|
||||
// The constructor of the modules prototype must be prepared to get an
|
||||
// instance of Icinga.Module
|
||||
this.object = new this.prototyp(this);
|
||||
this.applyRegisteredEventHandlers();
|
||||
} catch(e) {
|
||||
this.icinga.logger.error(
|
||||
'Failed to load module ' + this.name + ': ',
|
||||
e
|
||||
);
|
||||
|
||||
/**
|
||||
* Effectively bind the given event handler
|
||||
*/
|
||||
bindEventHandler: function(event, filter, handler)
|
||||
{
|
||||
var self = this;
|
||||
this.icinga.logger.debug('Bound ' + filter + ' .' + event + '()');
|
||||
this.handlers.push([event, filter, handler]);
|
||||
$(document).on(event, filter, handler.bind(self.object));
|
||||
},
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbind all event handlers bound by this module
|
||||
*/
|
||||
unbindEventHandlers: function()
|
||||
{
|
||||
$.each(this.handlers, function(idx, handler) {
|
||||
$(document).off(handler[0], handler[1], handler[2]);
|
||||
});
|
||||
},
|
||||
// That's all, the module is ready
|
||||
this.icinga.logger.debug(
|
||||
'Module ' + this.name + ' has been initialized'
|
||||
);
|
||||
|
||||
/**
|
||||
* Allow to destroy and clean up this module
|
||||
*/
|
||||
destroy: function()
|
||||
{
|
||||
this.unbindEventHandlers();
|
||||
if (typeof this.object.destroy === 'function') {
|
||||
this.object.destroy();
|
||||
}
|
||||
this.object = null;
|
||||
this.icinga = null;
|
||||
this.prototyp = null;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
};
|
||||
/**
|
||||
* Globally register this modules event handlers
|
||||
*/
|
||||
registerEventHandlers: function (handlers) {
|
||||
this.registeredHandlers = handlers;
|
||||
return this;
|
||||
},
|
||||
|
||||
}(Icinga));
|
||||
applyRegisteredEventHandlers: function () {
|
||||
|
||||
var self = this;
|
||||
|
||||
$.each(this.registeredHandlers, function (filter, events) {
|
||||
|
||||
$.each(events, function (event, handler) {
|
||||
// TODO: if (event[1] === 'each') {
|
||||
// $(event[0], $(el)).each(event[2]);
|
||||
self.bindEventHandler(
|
||||
event,
|
||||
'.module-' + self.name + ' ' + filter,
|
||||
handler
|
||||
);
|
||||
});
|
||||
});
|
||||
self = null;
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Effectively bind the given event handler
|
||||
*/
|
||||
bindEventHandler: function (event, filter, handler) {
|
||||
var self = this;
|
||||
this.icinga.logger.debug('Bound ' + filter + ' .' + event + '()');
|
||||
this.handlers.push([event, filter, handler]);
|
||||
$(document).on(event, filter, handler.bind(self.object));
|
||||
},
|
||||
|
||||
/**
|
||||
* Unbind all event handlers bound by this module
|
||||
*/
|
||||
unbindEventHandlers: function () {
|
||||
$.each(this.handlers, function (idx, handler) {
|
||||
$(document).off(handler[0], handler[1], handler[2]);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Allow to destroy and clean up this module
|
||||
*/
|
||||
destroy: function () {
|
||||
|
||||
this.unbindEventHandlers();
|
||||
|
||||
if (typeof this.object.destroy === 'function') {
|
||||
this.object.destroy();
|
||||
}
|
||||
|
||||
this.object = null;
|
||||
this.icinga = null;
|
||||
this.prototyp = null;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}(Icinga, jQuery));
|
||||
|
|
|
@ -4,144 +4,164 @@
|
|||
* Timer events are triggered once a second. Runs all reegistered callback
|
||||
* functions and is able to preserve a desired scope.
|
||||
*/
|
||||
(function(Icinga) {
|
||||
(function(Icinga, $) {
|
||||
|
||||
Icinga.Timer = function(icinga) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* We keep a reference to the Icinga instance even if we don't need it
|
||||
*/
|
||||
this.icinga = icinga;
|
||||
Icinga.Timer = function (icinga) {
|
||||
|
||||
/**
|
||||
* The Interval object
|
||||
*/
|
||||
this.ticker = null;
|
||||
/**
|
||||
* We keep a reference to the Icinga instance even if we don't need it
|
||||
*/
|
||||
this.icinga = icinga;
|
||||
|
||||
/**
|
||||
* Fixed default interval is 250ms
|
||||
*/
|
||||
this.interval = 250;
|
||||
/**
|
||||
* The Interval object
|
||||
*/
|
||||
this.ticker = null;
|
||||
|
||||
/**
|
||||
* Our registerd observers
|
||||
*/
|
||||
this.observers = [];
|
||||
/**
|
||||
* Fixed default interval is 250ms
|
||||
*/
|
||||
this.interval = 250;
|
||||
|
||||
/**
|
||||
* Counter
|
||||
*/
|
||||
this.stepCounter = 0;
|
||||
/**
|
||||
* Our registerd observers
|
||||
*/
|
||||
this.observers = [];
|
||||
|
||||
this.start = (new Date()).getTime();
|
||||
/**
|
||||
* Counter
|
||||
*/
|
||||
this.stepCounter = 0;
|
||||
|
||||
this.start = (new Date()).getTime();
|
||||
|
||||
|
||||
this.lastRuntime = [];
|
||||
};
|
||||
this.lastRuntime = [];
|
||||
};
|
||||
|
||||
Icinga.Timer.prototype = {
|
||||
Icinga.Timer.prototype = {
|
||||
|
||||
/**
|
||||
* The initialization function starts our ticker
|
||||
*/
|
||||
initialize: function(icinga)
|
||||
{
|
||||
var self = this;
|
||||
this.ticker = setInterval(function() { self.tick(); }, this.interval);
|
||||
},
|
||||
/**
|
||||
* The initialization function starts our ticker
|
||||
*/
|
||||
initialize: function () {
|
||||
var self = this;
|
||||
this.ticker = setInterval(function () { self.tick(); }, this.interval);
|
||||
},
|
||||
|
||||
/**
|
||||
* We will trigger our tick function once a second. It will call each
|
||||
* registered observer.
|
||||
*/
|
||||
tick: function()
|
||||
{
|
||||
var icinga = this.icinga;
|
||||
$.each(this.observers, function(idx, observer) {
|
||||
if (observer.isDue()) {
|
||||
observer.run();
|
||||
} else {
|
||||
// Not due
|
||||
/**
|
||||
* We will trigger our tick function once a second. It will call each
|
||||
* registered observer.
|
||||
*/
|
||||
tick: function () {
|
||||
|
||||
var icinga = this.icinga;
|
||||
|
||||
$.each(this.observers, function (idx, observer) {
|
||||
if (observer.isDue()) {
|
||||
observer.run();
|
||||
} else {
|
||||
// Not due
|
||||
}
|
||||
});
|
||||
icinga = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Register a given callback function to be run within an optional scope.
|
||||
*/
|
||||
register: function (callback, scope, interval) {
|
||||
|
||||
var observer;
|
||||
|
||||
try {
|
||||
|
||||
if (typeof scope === 'undefined') {
|
||||
observer = new Icinga.Timer.Interval(callback, interval);
|
||||
} else {
|
||||
observer = new Icinga.Timer.Interval(
|
||||
callback.bind(scope),
|
||||
interval
|
||||
);
|
||||
}
|
||||
|
||||
this.observers.push(observer);
|
||||
|
||||
} catch(err) {
|
||||
this.icinga.logger.error(err);
|
||||
}
|
||||
|
||||
return observer;
|
||||
},
|
||||
|
||||
unregister: function (observer) {
|
||||
|
||||
var idx = $.inArray(observer, this.observers);
|
||||
if (idx > -1) {
|
||||
this.observers.splice(idx, 1);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Our destroy function will clean up everything. Unused right now.
|
||||
*/
|
||||
destroy: function () {
|
||||
|
||||
if (this.ticker !== null) {
|
||||
clearInterval(this.ticker);
|
||||
}
|
||||
|
||||
this.icinga = null;
|
||||
$.each(this.observers, function (idx, observer) {
|
||||
observer.destroy();
|
||||
});
|
||||
|
||||
this.observers = [];
|
||||
}
|
||||
});
|
||||
icinga = null;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a given callback function to be run within an optional scope.
|
||||
*/
|
||||
register: function(callback, scope, interval)
|
||||
{
|
||||
try {
|
||||
if (typeof scope === 'undefined') {
|
||||
this.observers.push(new Icinga.Timer.Interval(callback, interval));
|
||||
} else {
|
||||
this.observers.push(
|
||||
new Icinga.Timer.Interval(
|
||||
callback.bind(scope),
|
||||
interval
|
||||
)
|
||||
);
|
||||
Icinga.Timer.Interval = function (callback, interval) {
|
||||
|
||||
if ('undefined' === typeof interval) {
|
||||
throw 'Timer interval is required';
|
||||
}
|
||||
} catch(err) {
|
||||
this.icinga.logger.error(err);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Our destroy function will clean up everything. Unused right now.
|
||||
*/
|
||||
destroy: function()
|
||||
{
|
||||
if (this.ticker !== null) {
|
||||
clearInterval(this.ticker);
|
||||
}
|
||||
this.icinga = null;
|
||||
$.each(this.observers, function(idx, observer) {
|
||||
observer.destroy();
|
||||
});
|
||||
this.observers = [];
|
||||
}
|
||||
};
|
||||
if (interval < 100) {
|
||||
throw 'Timer interval cannot be less than 100ms, got ' + interval;
|
||||
}
|
||||
|
||||
Icinga.Timer.Interval = function(callback, interval) {
|
||||
|
||||
if (typeof interval === 'undefined') {
|
||||
throw 'Timer interval is required';
|
||||
}
|
||||
|
||||
if (interval < 100) {
|
||||
throw 'Timer interval cannot be less than 100ms, got ' + interval;
|
||||
}
|
||||
|
||||
this.lastRun = (new Date()).getTime();
|
||||
|
||||
this.interval = interval;
|
||||
|
||||
this.scheduledNextRun = this.lastRun + interval;
|
||||
|
||||
this.callback = callback;
|
||||
};
|
||||
|
||||
Icinga.Timer.Interval.prototype = {
|
||||
isDue: function()
|
||||
{
|
||||
return this.scheduledNextRun < (new Date()).getTime();
|
||||
},
|
||||
|
||||
run: function()
|
||||
{
|
||||
this.lastRun = (new Date()).getTime();
|
||||
while (this.scheduledNextRun < this.lastRun) {
|
||||
this.scheduledNextRun += this.interval;
|
||||
|
||||
this.interval = interval;
|
||||
|
||||
this.scheduledNextRun = this.lastRun + interval;
|
||||
|
||||
this.callback = callback;
|
||||
};
|
||||
|
||||
Icinga.Timer.Interval.prototype = {
|
||||
|
||||
isDue: function () {
|
||||
return this.scheduledNextRun < (new Date()).getTime();
|
||||
},
|
||||
|
||||
run: function () {
|
||||
this.lastRun = (new Date()).getTime();
|
||||
|
||||
while (this.scheduledNextRun < this.lastRun) {
|
||||
this.scheduledNextRun += this.interval;
|
||||
}
|
||||
|
||||
this.callback();
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.callback = null;
|
||||
}
|
||||
this.callback();
|
||||
},
|
||||
};
|
||||
|
||||
destroy: function()
|
||||
{
|
||||
this.callback = null;
|
||||
}
|
||||
};
|
||||
|
||||
}(Icinga));
|
||||
}(Icinga, jQuery));
|
||||
|
|
|
@ -1,117 +1,335 @@
|
|||
(function(Icinga) {
|
||||
/**
|
||||
* Icinga.UI
|
||||
*
|
||||
* Our user interface
|
||||
*/
|
||||
(function(Icinga, $) {
|
||||
|
||||
Icinga.UI = function(icinga) {
|
||||
this.icinga = icinga;
|
||||
};
|
||||
'use strict';
|
||||
|
||||
Icinga.UI.prototype = {
|
||||
initialize: function()
|
||||
{
|
||||
this.icinga.timer.register(this.refreshDebug, this, 1000);
|
||||
this.refreshDebug();
|
||||
},
|
||||
Icinga.UI = function (icinga) {
|
||||
|
||||
prepareContainers: function ()
|
||||
{
|
||||
var icinga = this.icinga;
|
||||
$('.container').each(function(idx, el) {
|
||||
icinga.events.applyHandlers($(el));
|
||||
icinga.ui.initializeControls($(el));
|
||||
});
|
||||
/*
|
||||
$('#icinga-main').attr(
|
||||
'icingaurl',
|
||||
window.location.pathname + window.location.search
|
||||
);
|
||||
*/
|
||||
},
|
||||
refreshDebug: function()
|
||||
{
|
||||
var size = this.icinga.ui.getDefaultFontSize().toString();
|
||||
var winWidth = $( window ).width();
|
||||
var winHeight = $( window ).height();
|
||||
$('#responsive-debug').html(
|
||||
'Time: ' +
|
||||
this.icinga.ui.formatHHiiss(new Date) +
|
||||
'<br /> 1em: ' +
|
||||
size +
|
||||
'px<br /> Win: ' +
|
||||
winWidth +
|
||||
'x'+
|
||||
winHeight +
|
||||
'px<br />'
|
||||
).css({display: 'block'});
|
||||
},
|
||||
formatHHiiss: function(date)
|
||||
{
|
||||
var hours = date.getHours();
|
||||
var minutes = date.getMinutes();
|
||||
var seconds = date.getSeconds();
|
||||
if (hours < 10) hours = '0' + hours;
|
||||
if (minutes < 10) minutes = '0' + minutes;
|
||||
if (seconds < 10) seconds = '0' + seconds;
|
||||
return hours + ':' + minutes + ':' + seconds;
|
||||
},
|
||||
createFontSizeCalculator: function()
|
||||
{
|
||||
var $el = $('<div id="fontsize-calc"> </div>');
|
||||
$('#main').append($el);
|
||||
return $el;
|
||||
},
|
||||
getDefaultFontSize: function()
|
||||
{
|
||||
var $calc = $('#fontsize-calc');
|
||||
if (! $calc.length) {
|
||||
$calc = this.createFontSizeCalculator();
|
||||
this.icinga = icinga;
|
||||
|
||||
this.currentLayout = 'default';
|
||||
|
||||
this.debug = false;
|
||||
|
||||
this.debugTimer = null;
|
||||
|
||||
};
|
||||
|
||||
Icinga.UI.prototype = {
|
||||
|
||||
initialize: function () {
|
||||
$('html').removeClass('no-js').addClass('js');
|
||||
this.triggerWindowResize();
|
||||
},
|
||||
|
||||
enableDebug: function () {
|
||||
this.debug = true;
|
||||
this.debugTimer = this.icinga.timer.register(
|
||||
this.refreshDebug,
|
||||
this,
|
||||
1000
|
||||
);
|
||||
this.icinga.timer.register(this.refreshTimeSince, this, 1000);
|
||||
this.fixDebugVisibility();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
fixDebugVisibility: function () {
|
||||
if (this.debug) {
|
||||
$('#responsive-debug').css({display: 'block'});
|
||||
} else {
|
||||
$('#responsive-debug').css({display: 'none'});
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
disableDebug: function () {
|
||||
if (this.debug === false) { return; }
|
||||
|
||||
this.debug = false;
|
||||
this.icinga.timer.unregister(this.debugTimer);
|
||||
this.debugTimer = null;
|
||||
this.fixDebugVisibility();
|
||||
return this;
|
||||
},
|
||||
|
||||
flipContent: function () {
|
||||
var col1 = $('#col1 > div').detach();
|
||||
var col2 = $('#col2 > div').detach();
|
||||
$('#col2').html('');
|
||||
$('#col1').html('');
|
||||
|
||||
col1.appendTo('#col2');
|
||||
col2.appendTo('#col1');
|
||||
this.fixControls();
|
||||
},
|
||||
|
||||
triggerWindowResize: function () {
|
||||
this.onWindowResize({data: {self: this}});
|
||||
},
|
||||
|
||||
/**
|
||||
* Our window got resized, let's fix our UI
|
||||
*/
|
||||
onWindowResize: function (event) {
|
||||
var self = event.data.self;
|
||||
self.fixControls();
|
||||
|
||||
if (self.layoutHasBeenChanged()) {
|
||||
self.icinga.logger.info(
|
||||
'Layout change detected, switching to',
|
||||
self.currentLayout
|
||||
);
|
||||
}
|
||||
self.refreshDebug();
|
||||
},
|
||||
|
||||
layoutHasBeenChanged: function () {
|
||||
|
||||
var layout = $('html').css('fontFamily').replace(/['",]/g, '');
|
||||
var matched;
|
||||
|
||||
if (null !== (matched = layout.match(/^([a-z]+)-layout$/))) {
|
||||
if (matched[1] === this.currentLayout &&
|
||||
$('#layout').hasClass(layout)
|
||||
) {
|
||||
|
||||
return false;
|
||||
} else {
|
||||
$('#layout').attr('class', layout);
|
||||
this.currentLayout = matched[1];
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
this.icinga.logger.error(
|
||||
'Someone messed up our responsiveness hacks, html font-family is',
|
||||
layout
|
||||
);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
getAvailableColumnSpace: function () {
|
||||
return $('#main').width() / this.getDefaultFontSize();
|
||||
},
|
||||
|
||||
setColumnCount: function (count) {
|
||||
if (count === 3) {
|
||||
$('#main > .container').css({
|
||||
width: '33.33333%'
|
||||
});
|
||||
} else if (count === 2) {
|
||||
$('#main > .container').css({
|
||||
width: '50%'
|
||||
});
|
||||
} else {
|
||||
$('#main > .container').css({
|
||||
width: '100%'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
setTitle: function (title) {
|
||||
document.title = title;
|
||||
return this;
|
||||
},
|
||||
|
||||
getColumnCount: function () {
|
||||
return $('#main > .container').length;
|
||||
},
|
||||
|
||||
prepareContainers: function () {
|
||||
var icinga = this.icinga;
|
||||
$('.container').each(function(idx, el) {
|
||||
icinga.events.applyHandlers($(el));
|
||||
icinga.ui.initializeControls($(el));
|
||||
});
|
||||
/*
|
||||
$('#icinga-main').attr(
|
||||
'icingaurl',
|
||||
window.location.pathname + window.location.search
|
||||
);
|
||||
*/
|
||||
},
|
||||
|
||||
refreshDebug: function () {
|
||||
|
||||
var size = this.getDefaultFontSize().toString();
|
||||
var winWidth = $( window ).width();
|
||||
var winHeight = $( window ).height();
|
||||
var loading = '';
|
||||
|
||||
$.each(this.icinga.loader.requests, function (el, req) {
|
||||
if (loading === '') {
|
||||
loading = '<br />Loading:<br />';
|
||||
}
|
||||
loading += el + ' => ' + req.url;
|
||||
});
|
||||
|
||||
$('#responsive-debug').html(
|
||||
' Time: ' +
|
||||
this.icinga.utils.formatHHiiss(new Date()) +
|
||||
'<br /> 1em: ' +
|
||||
size +
|
||||
'px<br /> Win: ' +
|
||||
winWidth +
|
||||
'x'+
|
||||
winHeight +
|
||||
'px<br />' +
|
||||
' Layout: ' +
|
||||
this.currentLayout +
|
||||
loading
|
||||
);
|
||||
},
|
||||
|
||||
refreshTimeSince: function () {
|
||||
|
||||
$('.timesince').each(function (idx, el) {
|
||||
var m = el.innerHTML.match(/^(-?\d+)m\s(-?\d+)s/);
|
||||
if (m !== null) {
|
||||
var nm = parseInt(m[1]);
|
||||
var ns = parseInt(m[2]);
|
||||
if (ns < 59) {
|
||||
ns++;
|
||||
} else {
|
||||
ns = 0;
|
||||
nm++;
|
||||
}
|
||||
$(el).html(nm + 'm ' + ns + 's');
|
||||
}
|
||||
});
|
||||
|
||||
$('.timeunless').each(function (idx, el) {
|
||||
var m = el.innerHTML.match(/^(-?\d+)m\s(-?\d+)s/);
|
||||
if (m !== null) {
|
||||
var nm = parseInt(m[1]);
|
||||
var ns = parseInt(m[2]);
|
||||
if (nm >= 0) {
|
||||
if (ns > 0) {
|
||||
ns--;
|
||||
} else {
|
||||
ns = 59;
|
||||
nm--;
|
||||
}
|
||||
} else {
|
||||
if (ns < 59) {
|
||||
ns++;
|
||||
} else {
|
||||
ns = 0;
|
||||
nm--;
|
||||
}
|
||||
}
|
||||
$(el).html(nm + 'm ' + ns + 's');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
createFontSizeCalculator: function () {
|
||||
var $el = $('<div id="fontsize-calc"> </div>');
|
||||
$('#layout').append($el);
|
||||
return $el;
|
||||
},
|
||||
|
||||
getDefaultFontSize: function () {
|
||||
var $calc = $('#fontsize-calc');
|
||||
if (! $calc.length) {
|
||||
$calc = this.createFontSizeCalculator();
|
||||
}
|
||||
return $calc.width() / 1000;
|
||||
},
|
||||
|
||||
initializeControls: function (parent) {
|
||||
|
||||
var self = this;
|
||||
|
||||
$('.controls', parent).each(function (idx, el) {
|
||||
var $el = $(el);
|
||||
|
||||
if (! $el.next('.fake-controls').length) {
|
||||
|
||||
var newdiv = $('<div class="fake-controls"></div>');
|
||||
newdiv.css({
|
||||
height: $el.css('height')
|
||||
});
|
||||
$el.after(newdiv);
|
||||
}
|
||||
});
|
||||
|
||||
this.fixControls(parent);
|
||||
},
|
||||
|
||||
fixControls: function ($parent) {
|
||||
|
||||
var self = this;
|
||||
|
||||
if ('undefined' === typeof $parent) {
|
||||
|
||||
$('#header').css({height: 'auto'});
|
||||
$('#main').css({top: $('#header').css('height')});
|
||||
$('#sidebar').css({top: $('#header').height() + 'px'});
|
||||
$('#header').css({height: $('#header').height() + 'px'});
|
||||
$('#inner-layout').css({top: $('#header').css('height')});
|
||||
$('.container').each(function (idx, container) {
|
||||
self.fixControls($(container));
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self.icinga.logger.debug('Fixing controls for ', $parent);
|
||||
|
||||
$('.controls', $parent).each(function (idx, el) {
|
||||
var $el = $(el);
|
||||
var $fake = $el.next('.fake-controls');
|
||||
var y = $parent.scrollTop();
|
||||
|
||||
$el.css({
|
||||
position : 'fixed',
|
||||
top : $parent.offset().top,
|
||||
width : $fake.css('width')
|
||||
});
|
||||
|
||||
$fake.css({
|
||||
height : $el.css('height'),
|
||||
display : 'block'
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
toggleFullscreen: function () {
|
||||
$('#layout').toggleClass('fullscreen-layout');
|
||||
this.fixControls();
|
||||
},
|
||||
|
||||
getWindowId: function () {
|
||||
var res = window.name.match(/^Icinga_([a-zA-Z0-9])$/);
|
||||
if (res) {
|
||||
return res[1];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
hasWindowId: function () {
|
||||
var res = window.name.match(/^Icinga_([a-zA-Z0-9])$/);
|
||||
return typeof res === 'object';
|
||||
},
|
||||
|
||||
setWindowId: function (id) {
|
||||
window.name = 'Icinga_' + id;
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
// This is gonna be hard, clean up the mess
|
||||
this.icinga = null;
|
||||
}
|
||||
return $calc.width() / 1000;
|
||||
},
|
||||
initializeControls: function(parent)
|
||||
{
|
||||
var self = this;
|
||||
$('.controls', parent).each(function(idx, el) {
|
||||
var $el = $(el);
|
||||
if (! $el.next('.fake-controls').length) {
|
||||
var newdiv = $('<div class="fake-controls"></div>');
|
||||
newdiv.css({
|
||||
height: $el.css('height')
|
||||
});
|
||||
$el.after(newdiv);
|
||||
}
|
||||
});
|
||||
this.fixControls(parent);
|
||||
},
|
||||
fixControls: function($parent)
|
||||
{
|
||||
var self = this;
|
||||
if (typeof $parent === 'undefined') {
|
||||
$('.container').each(function(idx, container) {
|
||||
self.fixControls($(container));
|
||||
});
|
||||
return;
|
||||
}
|
||||
self.icinga.logger.debug('Fixing controls for ', $parent);
|
||||
$('.controls', $parent).each(function(idx, el) {
|
||||
var $el = $(el);
|
||||
var $fake = $el.next('.fake-controls');
|
||||
var y = $parent.scrollTop();
|
||||
$el.css({
|
||||
position: 'fixed',
|
||||
top: $parent.offset().top,
|
||||
width: $fake.css('width')
|
||||
});
|
||||
$fake.css({
|
||||
height: $el.css('height'),
|
||||
display: 'block'
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
// This is gonna be hard, clean up the mess
|
||||
this.icinga = null;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}(Icinga));
|
||||
}(Icinga, jQuery));
|
||||
|
|
|
@ -3,95 +3,113 @@
|
|||
*/
|
||||
(function(Icinga) {
|
||||
|
||||
Icinga.Utils = function(icinga) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Utility functions may need access to their Icinga instance
|
||||
*/
|
||||
this.icinga = icinga;
|
||||
Icinga.Utils = function (icinga) {
|
||||
|
||||
/**
|
||||
* We will use this to create an URL helper only once
|
||||
*/
|
||||
this.url_helper = null;
|
||||
};
|
||||
/**
|
||||
* Utility functions may need access to their Icinga instance
|
||||
*/
|
||||
this.icinga = icinga;
|
||||
|
||||
Icinga.Utils.prototype = {
|
||||
/**
|
||||
* We will use this to create an URL helper only once
|
||||
*/
|
||||
this.urlHelper = null;
|
||||
};
|
||||
|
||||
timeWithMs: function(now)
|
||||
{
|
||||
if (typeof now === 'undefined') {
|
||||
now = new Date();
|
||||
}
|
||||
var ms = now.getMilliseconds() + '';
|
||||
while (ms.length < 3) {
|
||||
ms = '0' + ms;
|
||||
}
|
||||
return now.toLocaleTimeString() + '.' + ms;
|
||||
},
|
||||
Icinga.Utils.prototype = {
|
||||
|
||||
timeShort: function(now)
|
||||
{
|
||||
if (typeof now === 'undefined') {
|
||||
now = new Date();
|
||||
}
|
||||
return now.toLocaleTimeString().replace(/:\d{2}$/, '');
|
||||
},
|
||||
timeWithMs: function (now) {
|
||||
|
||||
/**
|
||||
* Parse a given Url and return an object
|
||||
*/
|
||||
parseUrl: function(url)
|
||||
{
|
||||
if (this.url_helper === null) {
|
||||
this.url_helper = document.createElement('a');
|
||||
}
|
||||
var a = this.url_helper;
|
||||
a.href = url;
|
||||
if (typeof now === 'undefined') {
|
||||
now = new Date();
|
||||
}
|
||||
|
||||
var result = {
|
||||
source : url,
|
||||
protocol: a.protocol.replace(':', ''),
|
||||
host : a.hostname,
|
||||
port : a.port,
|
||||
query : a.search,
|
||||
file : (a.pathname.match(/\/([^\/?#]+)$/i) || [,''])[1],
|
||||
hash : a.hash.replace('#',''),
|
||||
path : a.pathname.replace(/^([^\/])/,'/$1'),
|
||||
relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [,''])[1],
|
||||
segments: a.pathname.replace(/^\//,'').split('/'),
|
||||
params : this.parseParams(a),
|
||||
};
|
||||
a = null;
|
||||
var ms = now.getMilliseconds() + '';
|
||||
while (ms.length < 3) {
|
||||
ms = '0' + ms;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
return now.toLocaleTimeString() + '.' + ms;
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse url params
|
||||
*/
|
||||
parseParams: function(a) {
|
||||
var params = {},
|
||||
segment = a.search.replace(/^\?/,'').split('&'),
|
||||
len = segment.length,
|
||||
i = 0,
|
||||
s;
|
||||
for (; i < len; i++) {
|
||||
if (! segment[i]) { continue; }
|
||||
s = segment[i].split('=');
|
||||
params[s[0]] = decodeURIComponent(s[1]);
|
||||
}
|
||||
return params;
|
||||
},
|
||||
timeShort: function (now) {
|
||||
|
||||
/**
|
||||
* Cleanup
|
||||
*/
|
||||
destroy: function()
|
||||
{
|
||||
this.url_helper = null;
|
||||
this.icinga = null;
|
||||
}
|
||||
};
|
||||
if (typeof now === 'undefined') {
|
||||
now = new Date();
|
||||
}
|
||||
|
||||
return now.toLocaleTimeString().replace(/:\d{2}$/, '');
|
||||
},
|
||||
|
||||
formatHHiiss: function (date) {
|
||||
var hours = date.getHours();
|
||||
var minutes = date.getMinutes();
|
||||
var seconds = date.getSeconds();
|
||||
if (hours < 10) hours = '0' + hours;
|
||||
if (minutes < 10) minutes = '0' + minutes;
|
||||
if (seconds < 10) seconds = '0' + seconds;
|
||||
return hours + ':' + minutes + ':' + seconds;
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse a given Url and return an object
|
||||
*/
|
||||
parseUrl: function (url) {
|
||||
|
||||
if (this.urlHelper === null) {
|
||||
this.urlHelper = document.createElement('a');
|
||||
}
|
||||
|
||||
var a = this.urlHelper;
|
||||
a.href = url;
|
||||
|
||||
var result = {
|
||||
source : url,
|
||||
protocol: a.protocol.replace(':', ''),
|
||||
host : a.hostname,
|
||||
port : a.port,
|
||||
query : a.search,
|
||||
file : (a.pathname.match(/\/([^\/?#]+)$/i) || [,''])[1],
|
||||
hash : a.hash.replace('#',''),
|
||||
path : a.pathname.replace(/^([^\/])/,'/$1'),
|
||||
relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [,''])[1],
|
||||
segments: a.pathname.replace(/^\//,'').split('/'),
|
||||
params : this.parseParams(a),
|
||||
};
|
||||
a = null;
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse url params
|
||||
*/
|
||||
parseParams: function (a) {
|
||||
var params = {},
|
||||
segment = a.search.replace(/^\?/,'').split('&'),
|
||||
len = segment.length,
|
||||
i = 0,
|
||||
s;
|
||||
|
||||
for (; i < len; i++) {
|
||||
if (!segment[i]) {
|
||||
continue;
|
||||
}
|
||||
s = segment[i].split('=');
|
||||
params[s[0]] = decodeURIComponent(s[1]);
|
||||
}
|
||||
return params;
|
||||
},
|
||||
|
||||
/**
|
||||
* Cleanup
|
||||
*/
|
||||
destroy: function () {
|
||||
this.urlHelper = null;
|
||||
this.icinga = null;
|
||||
}
|
||||
};
|
||||
|
||||
}(Icinga));
|
||||
|
|
Loading…
Reference in New Issue