Lot's of JS changes

This commit is contained in:
Thomas Gelf 2014-03-04 13:08:29 +00:00
parent 9531dbb869
commit 9a485df81a
8 changed files with 1624 additions and 1215 deletions

View File

@ -9,9 +9,11 @@
* }); * });
* </code> * </code>
*/ */
(function() { (function(window, $) {
var Icinga = function(config) { 'use strict';
var Icinga = function (config) {
/** /**
* Our config object * Our config object
@ -43,6 +45,11 @@
*/ */
this.timer = null; this.timer = null;
/**
* Icinga.History
*/
this.history = null;
/** /**
* Icinga.Utils * Icinga.Utils
*/ */
@ -54,7 +61,7 @@
this.modules = {}; this.modules = {};
var self = this; var self = this;
$(document).ready(function() { $(document).ready(function () {
self.initialize(); self.initialize();
self = null; self = null;
}); });
@ -65,9 +72,7 @@
/** /**
* Icinga startup, will be triggerd once the document is ready * Icinga startup, will be triggerd once the document is ready
*/ */
initialize: function() initialize: function () {
{
$('html').removeClass('no-js').addClass('js');
this.utils = new Icinga.Utils(this); this.utils = new Icinga.Utils(this);
this.logger = new Icinga.Logger(this); this.logger = new Icinga.Logger(this);
@ -75,100 +80,44 @@
this.ui = new Icinga.UI(this); this.ui = new Icinga.UI(this);
this.loader = new Icinga.Loader(this); this.loader = new Icinga.Loader(this);
this.events = new Icinga.Events(this); this.events = new Icinga.Events(this);
this.history = new Icinga.History(this);
this.timer.initialize(); this.timer.initialize();
this.events.initialize(); this.events.initialize();
this.history.initialize();
this.ui.initialize(); this.ui.initialize();
this.loader.initialize(); this.loader.initialize();
this.logger.setLevel('info');
this.logger.info('Icinga is ready'); this.logger.info('Icinga is ready');
this.timer.register(this.refreshTimeSince, this, 1000);
},
toggleFullscreen: function()
{
$('#layout').toggleClass('fullscreen');
this.ui.fixControls();
},
flipContent: function()
{
var col1 = $('#col1 > div').detach();
var col2 = $('#col2 > div').detach();
$('#col2').html('');
$('#col1').html('');
col1.appendTo('#col2');
col2.appendTo('#col1');
this.ui.fixControls();
},
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');
}
});
},
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;
}, },
/** /**
* Load a given module by name * Load a given module by name
*/ */
loadModule: function(name) loadModule: function (name) {
{
if (this.hasModule(name)) { if (this.hasModule(name)) {
this.logger.error('Cannot load module ' + name + ' twice'); this.logger.error('Cannot load module ' + name + ' twice');
return; return;
} }
this.modules[name] = new Icinga.Module(this, name); this.modules[name] = new Icinga.Module(this, name);
}, },
/** /**
* Whether a module matching the given name exists * Whether a module matching the given name exists
*/ */
hasModule: function(name) hasModule: function (name) {
{ return 'undefined' !== typeof this.modules[name] ||
return typeof this.modules[name] !== 'undefined' || 'undefined' !== typeof Icinga.availableModules[name];
typeof Icinga.availableModules[name] !== 'undefined';
}, },
/** /**
* Get a module by name * Get a module by name
*/ */
module: function(name) module: function (name) {
{
if (typeof this.modules[name] === 'undefined') { if ('undefined' === typeof this.modules[name]) {
if (typeof Icinga.availableModules[name] !== 'undefined') { if ('undefined' !== typeof Icinga.availableModules[name]) {
this.modules[name] = new Icinga.Module( this.modules[name] = new Icinga.Module(
this, this,
name, name,
@ -176,17 +125,19 @@
); );
} }
} }
return this.modules[name]; return this.modules[name];
}, },
/** /**
* Clean up and unload all Icinga components * Clean up and unload all Icinga components
*/ */
destroy: function() destroy: function () {
{
$.each(this.modules, function(name, module) { $.each(this.modules, function (name, module) {
module.destroy(); module.destroy();
}); });
this.timer.destroy(); this.timer.destroy();
this.events.destroy(); this.events.destroy();
this.loader.destroy(); this.loader.destroy();
@ -196,7 +147,8 @@
this.utils.destroy(); this.utils.destroy();
this.modules = []; this.modules = [];
this.timer = this.events = this.loader = this.ui = this.logger = this.utils = null; this.timer = this.events = this.loader = this.ui = this.logger =
this.utils = null;
} }
}; };
@ -204,5 +156,4 @@
Icinga.availableModules = {}; Icinga.availableModules = {};
})(window); })(window, window.jQuery);

View File

@ -1,6 +1,13 @@
(function(Icinga) { /**
* Icinga.Events
*
* Event handlers
*/
(function (Icinga, $) {
Icinga.Events = function(icinga) { 'use strict';
Icinga.Events = function (icinga) {
this.icinga = icinga; this.icinga = icinga;
}; };
@ -9,21 +16,23 @@
/** /**
* Icinga will call our initialize() function once it's ready * Icinga will call our initialize() function once it's ready
*/ */
initialize: function() initialize: function () {
{
this.applyGlobalDefaults(); this.applyGlobalDefaults();
this.applyHandlers($('#layout'));
this.icinga.ui.prepareContainers(); this.icinga.ui.prepareContainers();
}, },
// TODO: What's this? // TODO: What's this?
applyHandlers: function(el) applyHandlers: function (el) {
{
var icinga = this.icinga; var icinga = this.icinga;
$('.dashboard > div', el).each(function(idx, el) { $('.dashboard > div', el).each(function(idx, el) {
var url = $(el).attr('data-icinga-url'); var url = $(el).attr('data-icinga-url');
if (typeof url === 'undefined') return; if (typeof url === 'undefined') return;
icinga.loader.loadUrl(url, $(el)).autorefresh = true; icinga.loader.loadUrl(url, $(el)).autorefresh = true;
}); });
// Set first links href in a action table tr as row href: // Set first links href in a action table tr as row href:
$('table.action tr', el).each(function(idx, el) { $('table.action tr', el).each(function(idx, el) {
var $a = $('a[href]', el).first(); var $a = $('a[href]', el).first();
@ -31,8 +40,9 @@
$(el).attr('href', $a.attr('href')); $(el).attr('href', $a.attr('href'));
} }
}); });
$('.icinga-module', el).each(function(idx, mod) { $('.icinga-module', el).each(function(idx, mod) {
$mod = $(mod); var $mod = $(mod);
var moduleName = $mod.data('icinga-module'); var moduleName = $mod.data('icinga-module');
if (icinga.hasModule(moduleName)) { if (icinga.hasModule(moduleName)) {
var module = icinga.module(moduleName); var module = icinga.module(moduleName);
@ -40,6 +50,8 @@
} }
}); });
$('input.autofocus', el).focus();
$('.inlinepie', el).sparkline('html', { $('.inlinepie', el).sparkline('html', {
type: 'pie', type: 'pie',
sliceColors: ['#44bb77', '#ffaa44', '#ff5566', '#dcd'], sliceColors: ['#44bb77', '#ffaa44', '#ff5566', '#dcd'],
@ -48,13 +60,13 @@
}); });
}, },
/** /**
* Global default event handlers * Global default event handlers
*/ */
applyGlobalDefaults: function() applyGlobalDefaults: function () {
{
// We catch resize events // We catch resize events
$(window).on('resize', { self: this }, this.onWindowResize); $(window).on('resize', { self: this.icinga.ui }, this.icinga.ui.onWindowResize);
// Destroy Icinga, clean up and interrupt pending requests on unload // Destroy Icinga, clean up and interrupt pending requests on unload
$( window ).on('unload', { self: this }, this.onUnload); $( window ).on('unload', { self: this }, this.onUnload);
@ -75,7 +87,7 @@
// We support an 'autosubmit' class on dropdown form elements // We support an 'autosubmit' class on dropdown form elements
$(document).on('change', 'form select.autosubmit', { self: this }, this.submitForm); $(document).on('change', 'form select.autosubmit', { self: this }, this.submitForm);
$(window).on('popstate', { self: this }, this.historyChanged); $(document).on('keyup', '#menu input.search', {self: this}, this.submitForm);
// TBD: a global autocompletion handler // TBD: a global autocompletion handler
// $(document).on('keyup', 'form.auto input', this.formChangeDelayed); // $(document).on('keyup', 'form.auto input', this.formChangeDelayed);
@ -83,50 +95,24 @@
// $(document).on('change', 'form.auto select', this.submitForm); // $(document).on('change', 'form.auto select', this.submitForm);
}, },
onUnload: function(event) onUnload: function (event) {
{
var icinga = event.data.self.icinga; var icinga = event.data.self.icinga;
icinga.logger.info('Unloading Icinga'); icinga.logger.info('Unloading Icinga');
icinga.destroy(); 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 * A scroll event happened in one of our containers
*/ */
onContainerScroll: function(event) onContainerScroll: function (event) {
{ // Ugly. And PLEASE, not so often
// Yet ugly. And PLEASE, not so often
icinga.ui.fixControls(); icinga.ui.fixControls();
}, },
/** /**
* *
*/ */
submitForm: function (event) submitForm: function (event) {
{
var icinga = event.data.self.icinga; var icinga = event.data.self.icinga;
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
@ -142,38 +128,65 @@
icinga.logger.debug('Submitting form: ' + method + ' ' + url); icinga.logger.debug('Submitting form: ' + method + ' ' + url);
// We should move this to a generic target-finder: // We should move this to a generic target-finder:
var $target = $form.closest('.container'); var $target = null;
if ($target.length == 0) { if ($form.closest('[data-base-target]').length) {
$target = $('#body'); $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;
} }
icinga.loader.loadUrl(url, $target, data, method); icinga.loader.loadUrl(url, $target, data, method);
// TODO: Do we really need to return false with stop/preventDefault? // TODO: Do we really need to return false with stop/preventDefault?
return false; return false;
}, },
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();
},
layout2col: function () {
if ($('#layout').hasClass('twocols')) { return; }
icinga.logger.debug('Switching to double col');
$('#layout').addClass('twocols');
this.icinga.ui.fixControls();
},
/** /**
* Someone clicked a link or tr[href] * Someone clicked a link or tr[href]
*/ */
linkClicked: function(event) linkClicked: function (event) {
{
var icinga = event.data.self.icinga; var icinga = event.data.self.icinga;
var $a = $(this); var $a = $(this);
var href = $a.attr('href'); var href = $a.attr('href');
var $li;
if ($a.attr('target') === '_blank') { if ($a.attr('target') === '_blank') {
return true; return true;
} }
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
// If link is hash tag...
if (href === '#') { if (href === '#') {
if ($a.closest('#menu')) { if ($a.closest('#menu')) {
var $li = $a.closest('li'); $li = $a.closest('li');
$li.siblings('li.active').removeClass('active'); $('#menu .active').removeClass('active');
$li.addClass('active'); $li.addClass('active');
} }
return; return;
@ -183,19 +196,17 @@
if ($container.length) { if ($container.length) {
$target = $container; $target = $container;
} }
// If link is hash tag...
if ($a.closest('table').length) { if ($a.closest('table').length) {
$target = $('#col2'); $target = $('#col2');
$('#layout').addClass('twocols'); icinga.events.layout2col();
icinga.ui.fixControls();
} }
if ($a.closest('[data-base-target]').length) { if ($a.closest('[data-base-target]').length) {
$target = $('#' + $a.closest('[data-base-target]').data('baseTarget')); $target = $('#' + $a.closest('[data-base-target]').data('baseTarget'));
$('#layout').addClass('twocols'); icinga.events.layout2col();
icinga.ui.fixControls();
} }
if ($a.closest('.tree').length) { if ($a.closest('.tree').length) {
var $li = $a.closest('li'); $li = $a.closest('li');
if ($li.find('li').length) { if ($li.find('li').length) {
if ($li.hasClass('collapsed')) { if ($li.hasClass('collapsed')) {
$li.removeClass('collapsed'); $li.removeClass('collapsed');
@ -206,46 +217,41 @@
return false; return false;
} else { } else {
$target = $('#col2'); $target = $('#col2');
$('#layout').addClass('twocols'); icinga.events.layout2col();
icinga.ui.fixControls();
} }
} }
icinga.loader.loadUrl(href, $target); icinga.loader.loadUrl(href, $target);
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
if ($a.closest('#menu').length) { if ($a.closest('#menu').length) {
$('#layout').removeClass('twocols'); icinga.events.layout1col();
$('#col2').html('<ul class="tabs"></ul>');
icinga.ui.fixControls();
return false; return false;
} }
if ($a.closest('table').length) {
if ($a.closest('table.action').length) {
if ($('#layout').hasClass('twocols')) { if ($('#layout').hasClass('twocols')) {
if ($target.attr('id') === 'col2') return; if ($target.attr('id') === 'col2') {
icinga.logger.debug('Switching to single col'); return;
$('#layout').removeClass('twocols'); }
icinga.ui.fixControls(); icinga.events.layout1col();
} else { } else {
icinga.logger.debug('Switching to double col'); icinga.events.layout2col();
$('#layout').addClass('twocols');
icinga.ui.fixControls();
} }
return false; return false;
} }
}, },
/* /*
hrefIsHashtag: function(href) hrefIsHashtag: function(href) {
{
// WARNING: IE gives full URL :( // WARNING: IE gives full URL :(
// Also it doesn't support negativ indexes in substr // Also it doesn't support negativ indexes in substr
return href.substr(href.length - 1, 1) == '#'; return href.substr(href.length - 1, 1) == '#';
}, },
*/ */
unbindGlobalHandlers: function() unbindGlobalHandlers: function () {
{
$(window).off('popstate', this.historyChanged);
$(window).off('resize', this.onWindowResize); $(window).off('resize', this.onWindowResize);
$(window).off('unload', this.onUnload); $(window).off('unload', this.onUnload);
$(window).off('beforeunload', this.onUnload); $(window).off('beforeunload', this.onUnload);
@ -263,4 +269,4 @@
} }
}; };
}(Icinga)); }(Icinga, jQuery));

View File

@ -3,9 +3,11 @@
* *
* This is where we take care of XHR requests, responses and failures. * This is where we take care of XHR requests, responses and failures.
*/ */
(function(Icinga) { (function(Icinga, $) {
Icinga.Loader = function(icinga) { 'use strict';
Icinga.Loader = function (icinga) {
/** /**
* YES, we need Icinga * YES, we need Icinga
@ -31,9 +33,8 @@
Icinga.Loader.prototype = { Icinga.Loader.prototype = {
initialize: function() initialize: function () {
{ this.icinga.timer.register(this.autorefresh, this, 500);
this.icinga.timer.register(this.autorefresh, this, 10000);
}, },
/** /**
@ -44,12 +45,11 @@
* @param {object} data Optional parameters, usually for POST requests * @param {object} data Optional parameters, usually for POST requests
* @param {string} method HTTP method, default is 'GET' * @param {string} method HTTP method, default is 'GET'
*/ */
loadUrl: function (url, $target, data, method) loadUrl: function (url, $target, data, method) {
{
var id = null; var id = null;
// Default method is GET // Default method is GET
if (typeof method === 'undefined') { if ('undefined' === typeof method) {
method = 'GET'; method = 'GET';
} }
@ -59,6 +59,7 @@
if (typeof $target !== 'undefined' && $target.attr('id')) { if (typeof $target !== 'undefined' && $target.attr('id')) {
id = $target.attr('id'); id = $target.attr('id');
} }
if (typeof $target !== 'undefined') { if (typeof $target !== 'undefined') {
// TODO: We shouldn't use data but keep this information somewhere else. // TODO: We shouldn't use data but keep this information somewhere else.
if ($target.data('icingaUrl') !== url) { if ($target.data('icingaUrl') !== url) {
@ -77,7 +78,13 @@
return this.requests[id]; return this.requests[id];
} }
// ...or abort the former request otherwise // ...or abort the former request otherwise
this.icinga.logger.debug('Aborting pending request loading ', url, ' to ', $target); this.icinga.logger.debug(
'Aborting pending request loading ',
url,
' to ',
$target
);
this.requests[id].abort(); this.requests[id].abort();
} }
@ -85,8 +92,8 @@
var headers = { 'X-Icinga-Accept': 'text/html' }; var headers = { 'X-Icinga-Accept': 'text/html' };
// Ask for a new window id in case we don't already have one // Ask for a new window id in case we don't already have one
if (this.icinga.hasWindowId()) { if (this.icinga.ui.hasWindowId()) {
headers['X-Icinga-WindowId'] = this.icinga.getWindowId(); headers['X-Icinga-WindowId'] = this.icinga.ui.getWindowId();
} else { } else {
headers['X-Icinga-WindowId'] = 'undefined'; headers['X-Icinga-WindowId'] = 'undefined';
} }
@ -104,11 +111,13 @@
req.url = url; req.url = url;
req.done(this.onResponse); req.done(this.onResponse);
req.fail(this.onFailure); req.fail(this.onFailure);
req.complete(this.onComplete);
req.historyTriggered = false; req.historyTriggered = false;
req.autorefresh = false; req.autorefresh = false;
if (id) { if (id) {
this.requests[id] = req; this.requests[id] = req;
} }
this.icinga.ui.refreshDebug();
return req; return req;
}, },
@ -117,43 +126,86 @@
* *
* @param {string} url Relative url * @param {string} url Relative url
*/ */
url: function(url) url: function (url) {
{
if (typeof url === 'undefined') { if (typeof url === 'undefined') {
return this.baseUrl; return this.baseUrl;
} }
return this.baseUrl + url; return this.baseUrl + url;
}, },
autorefresh: function() 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; var self = this;
if (self.autorefreshEnabled !== true) { if (self.autorefreshEnabled !== true) {
return; return;
} }
$('.container[data-icinga-refresh]').each(function(idx, el) { $('.container[data-icinga-refresh]').each(function (idx, el) {
var $el = $(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; self.loadUrl($el.data('icingaUrl'), $el).autorefresh = true;
el = null; el = null;
}); });
}, },
disableAutorefresh: function() disableAutorefresh: function () {
{
this.autorefreshEnabled = false; this.autorefreshEnabled = false;
}, },
enableAutorefresh: function() enableAutorefresh: function () {
{
this.autorefreshEnabled = true; this.autorefreshEnabled = true;
}, },
/** /**
* Handle successful XHR response * Handle successful XHR response
*/ */
onResponse: function (data, textStatus, req) onResponse: function (data, textStatus, req) {
{ var self = this;
if (this.failureNotice !== null) { if (this.failureNotice !== null) {
this.failureNotice.remove(); this.failureNotice.remove();
this.failureNotice = null; this.failureNotice = null;
@ -166,51 +218,76 @@
} }
var url = req.url; var url = req.url;
var targetId = req.$target.attr('id'); this.icinga.logger.debug(
this.icinga.logger.debug('Got response for ', req.$target, ', URL was ' + url); 'Got response for ', req.$target, ', URL was ' + url
);
var $resp = $(req.responseText);
var active = false;
if (! req.autorefresh) { if (! req.autorefresh) {
// TODO: Hook for response/url? // TODO: Hook for response/url?
var $matches = $('[href="' + url + '"]'); var $forms = $('[action="' + url + '"]');
$matches.each(function(idx, el) { var $matches = $.merge($('[href="' + url + '"]'), $forms);
$matches.each(function (idx, el) {
if ($(el).closest('#menu').length) { if ($(el).closest('#menu').length) {
$(el).closest('#menu').find('li.active').removeClass('active'); $('#menu .active').removeClass('active');
} else if ($(el).closest('table.action').length) { } else if ($(el).closest('table.action').length) {
$(el).closest('table.action').find('.active').removeClass('active'); $(el).closest('table.action').find('.active').removeClass('active');
} }
}); });
$matches.each(function (idx, el) {
$matches.each(function(idx, el) { var $el = $(el);
if ($(el).closest('#menu').length) { if ($el.closest('#menu').length) {
$(el).closest('li').addClass('active'); if ($el.is('form')) {
$(el).parents('li').addClass('active'); $('input', $el).addClass('active');
} else {
$el.closest('li').addClass('active');
$el.parents('li').addClass('active');
}
} else if ($(el).closest('table.action').length) { } else if ($(el).closest('table.action').length) {
$(el).addClass('active'); $el.addClass('active');
} }
}); });
} else {
// TODO: next container url
active = $('[href].active', req.$target).attr('href');
} }
delete this.requests[targetId]; req.$target.attr('data-icinga-url', url);
req.$target.attr('icingaurl', this.url);
// //
var target = req.getResponseHeader('X-Icinga-Container'); var target = req.getResponseHeader('X-Icinga-Container');
var newBody = false;
if (target) { if (target) {
req.$target = $('body'); 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'); var refresh = req.getResponseHeader('X-Icinga-Refresh');
if (refresh) { if (refresh) {
// Hmmmm... .data() doesn't work here? // Hmmmm... .data() doesn't work here?
req.$target.attr('data-icinga-refresh', refresh); req.$target.attr('data-icinga-refresh', refresh);
req.$target.attr('data-icinga-url', req.url); 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 // Set a window identifier if the server asks us to do so
var windowId = req.getResponseHeader('X-Icinga-WindowId'); var windowId = req.getResponseHeader('X-Icinga-WindowId');
if (windowId) { if (windowId) {
this.icinga.setWindowId(windowId); this.icinga.ui.setWindowId(windowId);
} }
// Update history when necessary. Don't do so for requests triggered // Update history when necessary. Don't do so for requests triggered
@ -219,11 +296,43 @@
// We only want to care about top-level containers // We only want to care about top-level containers
if (req.$target.parent().closest('.container').length === 0) { if (req.$target.parent().closest('.container').length === 0) {
this.icinga.history.pushCurrentState();
/*
this.icinga.logger.debug('Pushing ', req.url, ' to history'); this.icinga.logger.debug('Pushing ', req.url, ' to history');
if (typeof window.history.pushState !== 'undefined') {
window.history.pushState({icinga: true}, null, req.url); window.history.pushState({icinga: true}, null, req.url);
} }
*/
} }
$resp = $(req.responseText); }
// 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? */ /* Should we try to fiddle with responses containing full HTML? */
/* /*
@ -231,17 +340,41 @@
req.responseText = $('script', $('body', $resp).html()).remove(); req.responseText = $('script', $('body', $resp).html()).remove();
} }
*/ */
/*
this.renderContentToContainer(req.responseText, req.$target); 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 * Handle failed XHR response
*/ */
onFailure: function (req, textStatus, errorThrown) onFailure: function (req, textStatus, errorThrown) {
{
var url = req.url; var url = req.url;
delete this.requests[req.$target.attr('id')];
if (req.status === 500) { if (req.status === 500) {
if (this.exception === null) { if (this.exception === null) {
@ -250,10 +383,6 @@
this.exception = this.createNotice( this.exception = this.createNotice(
'error', 'error',
$('h1', $(req.responseText)).first().html() $('h1', $(req.responseText)).first().html()
/* 'The connection to the Icinga web server has been lost at ' +
this.icinga.utils.timeShort() +
'.'
*/
); );
this.icinga.ui.fixControls(); this.icinga.ui.fixControls();
} }
@ -269,7 +398,10 @@
// Icinga.debug(req.getResponseHeader('X-Icinga-Redirect')); // Icinga.debug(req.getResponseHeader('X-Icinga-Redirect'));
} else { } else {
if (errorThrown === 'abort') { if (errorThrown === 'abort') {
this.icinga.logger.info('Request to ', url, ' has been aborted for ', req.$target); this.icinga.logger.info(
'Request to ' + url + ' has been aborted for ',
req.$target
);
} else { } else {
if (this.failureNotice === null) { if (this.failureNotice === null) {
this.failureNotice = this.createNotice( this.failureNotice = this.createNotice(
@ -281,6 +413,7 @@
this.icinga.ui.fixControls(); this.icinga.ui.fixControls();
} }
this.icinga.logger.error( this.icinga.logger.error(
'Failed to contact web server loading ', 'Failed to contact web server loading ',
url, url,
@ -291,26 +424,56 @@
} }
}, },
createNotice: function(severity, message) { createNotice: function (severity, message) {
return $('<li class="' + severity + '">' + message + '</li>').appendTo($('#notifications')); return $(
'<li class="' + severity + '">' + message + '</li>'
).appendTo($('#notifications'));
}, },
/** /**
* Smoothly render given HTML to given container * Smoothly render given HTML to given container
*/ */
renderContentToContainer: function (content, $container) renderContentToContainer: function (content, $container) {
{
// Disable all click events while rendering // Disable all click events while rendering
$('*').click(function( event ) { $('*').click(function (event) {
event.stopImmediatePropagation(); event.stopImmediatePropagation();
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
}); });
// Container update happens here // Container update happens here
var scrollPos = $container.scrollTop(); 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); $container.html(content);
}
}
if (scrollPos !== false) {
$container.scrollTop(scrollPos); $container.scrollTop(scrollPos);
}
if (origFocus) {
origFocus.focus();
}
// TODO: this.icinga.events.refreshContainer(container); // TODO: this.icinga.events.refreshContainer(container);
var icinga = this.icinga; var icinga = this.icinga;
@ -335,4 +498,4 @@
}; };
}(Icinga)); }(Icinga, jQuery));

View File

@ -1,8 +1,14 @@
(function(Icinga) { /**
* Icinga.Logger
*
* Well, log output. Rocket science.
*/
(function (Icinga) {
Icinga.Logger = function(icinga) { 'use strict';
Icinga.Logger = function (icinga) {
// Well... we don't really need Icinga right now
this.icinga = icinga; this.icinga = icinga;
this.logLevel = 'info'; this.logLevel = 'info';
@ -14,80 +20,95 @@
'error': 3 'error': 3
}; };
// Let's get started
this.initialize();
}; };
Icinga.Logger.prototype = { Icinga.Logger.prototype = {
/** /**
* Logger initialization * Whether the browser has a console object
*/ */
initialize: function() hasConsole: function () {
{ return 'undefined' !== typeof console;
}, },
/** /**
* Whether the browser has a console object * Raise or lower current log level
*
* Messages blow this threshold will be silently discarded
*/ */
hasConsole: function() setLevel: function (level) {
{ if ('undefined' !== typeof this.numericLevel(level)) {
return typeof console !== 'undefined';
},
debug: function(msg)
{
this.writeToConsole('debug', arguments);
},
setLevel: function(level)
{
if (this.numericLevel(level) !== 'undefined') {
this.logLevel = level; this.logLevel = level;
} }
return this;
}, },
info: function() /**
{ * Log a debug message
this.writeToConsole('info', arguments); */
debug: function () {
return this.writeToConsole('debug', arguments);
}, },
warn: function() /**
{ * Log an informational message
this.writeToConsole('warn', arguments); */
info: function () {
return this.writeToConsole('info', arguments);
}, },
error: function() /**
{ * Log a warning message
this.writeToConsole('error', arguments); */
warn: function () {
return this.writeToConsole('warn', arguments);
}, },
writeToConsole: function(level, args) { /**
* Log an error message
*/
error: function () {
return this.writeToConsole('error', arguments);
},
/**
* Write a log message with the given level to the console
*/
writeToConsole: function (level, args) {
args = Array.prototype.slice.call(args); args = Array.prototype.slice.call(args);
// We want our log messages to carry precise timestamps
args.unshift(this.icinga.utils.timeWithMs()); args.unshift(this.icinga.utils.timeWithMs());
if (this.hasConsole() && this.hasLogLevel(level)) { if (this.hasConsole() && this.hasLogLevel(level)) {
console[level].apply(console, args); console[level].apply(console, args);
} }
return this;
}, },
numericLevel: function(level) /**
{ * Return the numeric identifier fot a given log level
*/
numericLevel: function (level) {
var ret = this.logLevels[level]; var ret = this.logLevels[level];
if (typeof ret === 'undefined') { if ('undefined' === typeof ret) {
throw 'Got invalid log level ' + level; throw 'Got invalid log level ' + level;
} }
return ret; return ret;
}, },
hasLogLevel: function(level) /**
{ * Whether a given log level exists
*/
hasLogLevel: function (level) {
return this.numericLevel(level) >= this.numericLevel(this.logLevel); return this.numericLevel(level) >= this.numericLevel(this.logLevel);
}, },
/** /**
* There isn't much to clean up here * There isn't much to clean up here
*/ */
destroy: function() { destroy: function () {
this.enabled = false; this.enabled = false;
this.icinga = null; this.icinga = null;
} }

View File

@ -1,9 +1,11 @@
/** /**
* This is how we bootstrap JS code in our modules * This is how we bootstrap JS code in our modules
*/ */
(function(Icinga) { (function(Icinga, $) {
Icinga.Module = function(icinga, name, prototyp) { 'use strict';
Icinga.Module = function (icinga, name, prototyp) {
// The Icinga instance // The Icinga instance
this.icinga = icinga; this.icinga = icinga;
@ -28,36 +30,45 @@
Icinga.Module.prototype = { Icinga.Module.prototype = {
initialize: function() initialize: function () {
{
try { try {
// The constructor of the modules prototype must be prepared to get an // The constructor of the modules prototype must be prepared to get an
// instance of Icinga.Module // instance of Icinga.Module
this.object = new this.prototyp(this); this.object = new this.prototyp(this);
this.applyRegisteredEventHandlers(); this.applyRegisteredEventHandlers();
} catch(e) { } catch(e) {
this.icinga.logger.error('Failed to load module ', this.name, ': ', e); this.icinga.logger.error(
'Failed to load module ' + this.name + ': ',
e
);
return false; return false;
} }
// That's all, the module is ready // That's all, the module is ready
this.icinga.logger.debug('Module ' + this.name + ' has been initialized'); this.icinga.logger.debug(
'Module ' + this.name + ' has been initialized'
);
return true; return true;
}, },
/** /**
* Globally register this modules event handlers * Globally register this modules event handlers
*/ */
registerEventHandlers: function(handlers) registerEventHandlers: function (handlers) {
{
this.registeredHandlers = handlers; this.registeredHandlers = handlers;
return this; return this;
}, },
applyRegisteredEventHandlers: function() applyRegisteredEventHandlers: function () {
{
var self = this; var self = this;
$.each(this.registeredHandlers, function(filter, events) {
$.each(this.registeredHandlers, function (filter, events) {
$.each(events, function (event, handler) { $.each(events, function (event, handler) {
// TODO: if (event[1] === 'each') { // TODO: if (event[1] === 'each') {
// $(event[0], $(el)).each(event[2]); // $(event[0], $(el)).each(event[2]);
@ -69,14 +80,14 @@
}); });
}); });
self = null; self = null;
return this; return this;
}, },
/** /**
* Effectively bind the given event handler * Effectively bind the given event handler
*/ */
bindEventHandler: function(event, filter, handler) bindEventHandler: function (event, filter, handler) {
{
var self = this; var self = this;
this.icinga.logger.debug('Bound ' + filter + ' .' + event + '()'); this.icinga.logger.debug('Bound ' + filter + ' .' + event + '()');
this.handlers.push([event, filter, handler]); this.handlers.push([event, filter, handler]);
@ -86,9 +97,8 @@
/** /**
* Unbind all event handlers bound by this module * Unbind all event handlers bound by this module
*/ */
unbindEventHandlers: function() unbindEventHandlers: function () {
{ $.each(this.handlers, function (idx, handler) {
$.each(this.handlers, function(idx, handler) {
$(document).off(handler[0], handler[1], handler[2]); $(document).off(handler[0], handler[1], handler[2]);
}); });
}, },
@ -96,12 +106,14 @@
/** /**
* Allow to destroy and clean up this module * Allow to destroy and clean up this module
*/ */
destroy: function() destroy: function () {
{
this.unbindEventHandlers(); this.unbindEventHandlers();
if (typeof this.object.destroy === 'function') { if (typeof this.object.destroy === 'function') {
this.object.destroy(); this.object.destroy();
} }
this.object = null; this.object = null;
this.icinga = null; this.icinga = null;
this.prototyp = null; this.prototyp = null;
@ -109,4 +121,4 @@
}; };
}(Icinga)); }(Icinga, jQuery));

View File

@ -4,9 +4,11 @@
* Timer events are triggered once a second. Runs all reegistered callback * Timer events are triggered once a second. Runs all reegistered callback
* functions and is able to preserve a desired scope. * functions and is able to preserve a desired scope.
*/ */
(function(Icinga) { (function(Icinga, $) {
Icinga.Timer = function(icinga) { 'use strict';
Icinga.Timer = function (icinga) {
/** /**
* We keep a reference to the Icinga instance even if we don't need it * We keep a reference to the Icinga instance even if we don't need it
@ -44,20 +46,20 @@
/** /**
* The initialization function starts our ticker * The initialization function starts our ticker
*/ */
initialize: function(icinga) initialize: function () {
{
var self = this; var self = this;
this.ticker = setInterval(function() { self.tick(); }, this.interval); this.ticker = setInterval(function () { self.tick(); }, this.interval);
}, },
/** /**
* We will trigger our tick function once a second. It will call each * We will trigger our tick function once a second. It will call each
* registered observer. * registered observer.
*/ */
tick: function() tick: function () {
{
var icinga = this.icinga; var icinga = this.icinga;
$.each(this.observers, function(idx, observer) {
$.each(this.observers, function (idx, observer) {
if (observer.isDue()) { if (observer.isDue()) {
observer.run(); observer.run();
} else { } else {
@ -70,43 +72,61 @@
/** /**
* Register a given callback function to be run within an optional scope. * Register a given callback function to be run within an optional scope.
*/ */
register: function(callback, scope, interval) register: function (callback, scope, interval) {
{
var observer;
try { try {
if (typeof scope === 'undefined') { if (typeof scope === 'undefined') {
this.observers.push(new Icinga.Timer.Interval(callback, interval)); observer = new Icinga.Timer.Interval(callback, interval);
} else { } else {
this.observers.push( observer = new Icinga.Timer.Interval(
new Icinga.Timer.Interval(
callback.bind(scope), callback.bind(scope),
interval interval
)
); );
} }
this.observers.push(observer);
} catch(err) { } catch(err) {
this.icinga.logger.error(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. * Our destroy function will clean up everything. Unused right now.
*/ */
destroy: function() destroy: function () {
{
if (this.ticker !== null) { if (this.ticker !== null) {
clearInterval(this.ticker); clearInterval(this.ticker);
} }
this.icinga = null; this.icinga = null;
$.each(this.observers, function(idx, observer) { $.each(this.observers, function (idx, observer) {
observer.destroy(); observer.destroy();
}); });
this.observers = []; this.observers = [];
} }
}; };
Icinga.Timer.Interval = function(callback, interval) { Icinga.Timer.Interval = function (callback, interval) {
if (typeof interval === 'undefined') { if ('undefined' === typeof interval) {
throw 'Timer interval is required'; throw 'Timer interval is required';
} }
@ -124,24 +144,24 @@
}; };
Icinga.Timer.Interval.prototype = { Icinga.Timer.Interval.prototype = {
isDue: function()
{ isDue: function () {
return this.scheduledNextRun < (new Date()).getTime(); return this.scheduledNextRun < (new Date()).getTime();
}, },
run: function() run: function () {
{
this.lastRun = (new Date()).getTime(); this.lastRun = (new Date()).getTime();
while (this.scheduledNextRun < this.lastRun) { while (this.scheduledNextRun < this.lastRun) {
this.scheduledNextRun += this.interval; this.scheduledNextRun += this.interval;
} }
this.callback(); this.callback();
}, },
destroy: function() destroy: function () {
{
this.callback = null; this.callback = null;
} }
}; };
}(Icinga)); }(Icinga, jQuery));

View File

@ -1,77 +1,259 @@
(function(Icinga) { /**
* Icinga.UI
*
* Our user interface
*/
(function(Icinga, $) {
'use strict';
Icinga.UI = function (icinga) {
Icinga.UI = function(icinga) {
this.icinga = icinga; this.icinga = icinga;
this.currentLayout = 'default';
this.debug = false;
this.debugTimer = null;
}; };
Icinga.UI.prototype = { Icinga.UI.prototype = {
initialize: function()
{ initialize: function () {
this.icinga.timer.register(this.refreshDebug, this, 1000); $('html').removeClass('no-js').addClass('js');
this.refreshDebug(); this.triggerWindowResize();
}, },
prepareContainers: function () 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; var icinga = this.icinga;
$('.container').each(function(idx, el) { $('.container').each(function(idx, el) {
icinga.events.applyHandlers($(el)); icinga.events.applyHandlers($(el));
icinga.ui.initializeControls($(el)); icinga.ui.initializeControls($(el));
}); });
/* /*
$('#icinga-main').attr( $('#icinga-main').attr(
'icingaurl', 'icingaurl',
window.location.pathname + window.location.search window.location.pathname + window.location.search
); );
*/ */
}, },
refreshDebug: function()
{ refreshDebug: function () {
var size = this.icinga.ui.getDefaultFontSize().toString();
var size = this.getDefaultFontSize().toString();
var winWidth = $( window ).width(); var winWidth = $( window ).width();
var winHeight = $( window ).height(); 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( $('#responsive-debug').html(
'Time: ' + ' Time: ' +
this.icinga.ui.formatHHiiss(new Date) + this.icinga.utils.formatHHiiss(new Date()) +
'<br />&nbsp;1em: ' + '<br /> 1em: ' +
size + size +
'px<br />&nbsp;Win: ' + 'px<br /> Win: ' +
winWidth + winWidth +
'x'+ 'x'+
winHeight + winHeight +
'px<br />' 'px<br />' +
).css({display: 'block'}); ' Layout: ' +
this.currentLayout +
loading
);
}, },
formatHHiiss: function(date)
{ refreshTimeSince: function () {
var hours = date.getHours();
var minutes = date.getMinutes(); $('.timesince').each(function (idx, el) {
var seconds = date.getSeconds(); var m = el.innerHTML.match(/^(-?\d+)m\s(-?\d+)s/);
if (hours < 10) hours = '0' + hours; if (m !== null) {
if (minutes < 10) minutes = '0' + minutes; var nm = parseInt(m[1]);
if (seconds < 10) seconds = '0' + seconds; var ns = parseInt(m[2]);
return hours + ':' + minutes + ':' + seconds; 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()
{ createFontSizeCalculator: function () {
var $el = $('<div id="fontsize-calc">&nbsp;</div>'); var $el = $('<div id="fontsize-calc">&nbsp;</div>');
$('#main').append($el); $('#layout').append($el);
return $el; return $el;
}, },
getDefaultFontSize: function()
{ getDefaultFontSize: function () {
var $calc = $('#fontsize-calc'); var $calc = $('#fontsize-calc');
if (! $calc.length) { if (! $calc.length) {
$calc = this.createFontSizeCalculator(); $calc = this.createFontSizeCalculator();
} }
return $calc.width() / 1000; return $calc.width() / 1000;
}, },
initializeControls: function(parent)
{ initializeControls: function (parent) {
var self = this; var self = this;
$('.controls', parent).each(function(idx, el) {
$('.controls', parent).each(function (idx, el) {
var $el = $(el); var $el = $(el);
if (! $el.next('.fake-controls').length) { if (! $el.next('.fake-controls').length) {
var newdiv = $('<div class="fake-controls"></div>'); var newdiv = $('<div class="fake-controls"></div>');
newdiv.css({ newdiv.css({
height: $el.css('height') height: $el.css('height')
@ -79,39 +261,75 @@
$el.after(newdiv); $el.after(newdiv);
} }
}); });
this.fixControls(parent); this.fixControls(parent);
}, },
fixControls: function($parent)
{ fixControls: function ($parent) {
var self = this; var self = this;
if (typeof $parent === 'undefined') {
$('.container').each(function(idx, container) { 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)); self.fixControls($(container));
}); });
return; return;
} }
self.icinga.logger.debug('Fixing controls for ', $parent); self.icinga.logger.debug('Fixing controls for ', $parent);
$('.controls', $parent).each(function(idx, el) {
$('.controls', $parent).each(function (idx, el) {
var $el = $(el); var $el = $(el);
var $fake = $el.next('.fake-controls'); var $fake = $el.next('.fake-controls');
var y = $parent.scrollTop(); var y = $parent.scrollTop();
$el.css({ $el.css({
position: 'fixed', position : 'fixed',
top: $parent.offset().top, top : $parent.offset().top,
width: $fake.css('width') width : $fake.css('width')
}); });
$fake.css({ $fake.css({
height: $el.css('height'), height : $el.css('height'),
display: 'block' display : 'block'
}); });
}); });
}, },
destroy: function() { 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 is gonna be hard, clean up the mess
this.icinga = null; this.icinga = null;
} }
}; };
}(Icinga)); }(Icinga, jQuery));

View File

@ -3,7 +3,9 @@
*/ */
(function(Icinga) { (function(Icinga) {
Icinga.Utils = function(icinga) { 'use strict';
Icinga.Utils = function (icinga) {
/** /**
* Utility functions may need access to their Icinga instance * Utility functions may need access to their Icinga instance
@ -13,40 +15,54 @@
/** /**
* We will use this to create an URL helper only once * We will use this to create an URL helper only once
*/ */
this.url_helper = null; this.urlHelper = null;
}; };
Icinga.Utils.prototype = { Icinga.Utils.prototype = {
timeWithMs: function(now) timeWithMs: function (now) {
{
if (typeof now === 'undefined') { if (typeof now === 'undefined') {
now = new Date(); now = new Date();
} }
var ms = now.getMilliseconds() + ''; var ms = now.getMilliseconds() + '';
while (ms.length < 3) { while (ms.length < 3) {
ms = '0' + ms; ms = '0' + ms;
} }
return now.toLocaleTimeString() + '.' + ms; return now.toLocaleTimeString() + '.' + ms;
}, },
timeShort: function(now) timeShort: function (now) {
{
if (typeof now === 'undefined') { if (typeof now === 'undefined') {
now = new Date(); now = new Date();
} }
return now.toLocaleTimeString().replace(/:\d{2}$/, ''); 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 * Parse a given Url and return an object
*/ */
parseUrl: function(url) parseUrl: function (url) {
{
if (this.url_helper === null) { if (this.urlHelper === null) {
this.url_helper = document.createElement('a'); this.urlHelper = document.createElement('a');
} }
var a = this.url_helper;
var a = this.urlHelper;
a.href = url; a.href = url;
var result = { var result = {
@ -70,14 +86,17 @@
/** /**
* Parse url params * Parse url params
*/ */
parseParams: function(a) { parseParams: function (a) {
var params = {}, var params = {},
segment = a.search.replace(/^\?/,'').split('&'), segment = a.search.replace(/^\?/,'').split('&'),
len = segment.length, len = segment.length,
i = 0, i = 0,
s; s;
for (; i < len; i++) { for (; i < len; i++) {
if (! segment[i]) { continue; } if (!segment[i]) {
continue;
}
s = segment[i].split('='); s = segment[i].split('=');
params[s[0]] = decodeURIComponent(s[1]); params[s[0]] = decodeURIComponent(s[1]);
} }
@ -87,9 +106,8 @@
/** /**
* Cleanup * Cleanup
*/ */
destroy: function() destroy: function () {
{ this.urlHelper = null;
this.url_helper = null;
this.icinga = null; this.icinga = null;
} }
}; };