diff --git a/public/js/icinga.js b/public/js/icinga.js
index 31bad4ff0..67b960797 100644
--- a/public/js/icinga.js
+++ b/public/js/icinga.js
@@ -9,200 +9,151 @@
* });
*
*/
-(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);
diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js
index 0d62188b1..ce60426b9 100644
--- a/public/js/icinga/events.js
+++ b/public/js/icinga/events.js
@@ -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('
');
- 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));
diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js
index 22998c718..9124093c1 100644
--- a/public/js/icinga/loader.js
+++ b/public/js/icinga/loader.js
@@ -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(
- '' + req.status + ' ' + errorThrown + '
' +
- 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(
+ '' + req.status + ' ' + errorThrown + '
' +
+ 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 $(
+ '' + message + ''
+ ).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 $('' + message + '').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));
diff --git a/public/js/icinga/logger.js b/public/js/icinga/logger.js
index e8a7be909..f5226c82c 100644
--- a/public/js/icinga/logger.js
+++ b/public/js/icinga/logger.js
@@ -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));
diff --git a/public/js/icinga/module.js b/public/js/icinga/module.js
index c2134d96c..f9ae3d69b 100644
--- a/public/js/icinga/module.js
+++ b/public/js/icinga/module.js
@@ -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));
diff --git a/public/js/icinga/timer.js b/public/js/icinga/timer.js
index e1a92551f..5ae77fa74 100644
--- a/public/js/icinga/timer.js
+++ b/public/js/icinga/timer.js
@@ -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));
diff --git a/public/js/icinga/ui.js b/public/js/icinga/ui.js
index 74a04d0d3..c40245a91 100644
--- a/public/js/icinga/ui.js
+++ b/public/js/icinga/ui.js
@@ -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) +
- '
1em: ' +
- size +
- 'px
Win: ' +
- winWidth +
- 'x'+
- winHeight +
- 'px
'
- ).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 = $('
');
- $('#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 = '
Loading:
';
+ }
+ loading += el + ' => ' + req.url;
+ });
+
+ $('#responsive-debug').html(
+ ' Time: ' +
+ this.icinga.utils.formatHHiiss(new Date()) +
+ '
1em: ' +
+ size +
+ 'px
Win: ' +
+ winWidth +
+ 'x'+
+ winHeight +
+ 'px
' +
+ ' 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 = $('
');
+ $('#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 = $('');
+ 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 = $('');
- 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));
diff --git a/public/js/icinga/utils.js b/public/js/icinga/utils.js
index c80e24189..111fca61b 100644
--- a/public/js/icinga/utils.js
+++ b/public/js/icinga/utils.js
@@ -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));