diff --git a/library/Icinga/Web/JavaScript.php b/library/Icinga/Web/JavaScript.php index cacadb600..acb9433da 100644 --- a/library/Icinga/Web/JavaScript.php +++ b/library/Icinga/Web/JavaScript.php @@ -22,6 +22,7 @@ class JavaScript 'js/icinga/history.js', 'js/icinga/module.js', 'js/icinga/timezone.js', + 'js/icinga/behavior/tooltip.js' ); protected static $vendorFiles = array( diff --git a/public/js/icinga.js b/public/js/icinga.js index 04c7cdcba..296e6f6d3 100644 --- a/public/js/icinga.js +++ b/public/js/icinga.js @@ -60,6 +60,11 @@ */ this.utils = null; + /** + * Additional site behavior + */ + this.behaviors = {}; + /** * Loaded modules */ @@ -90,6 +95,10 @@ this.loader = new Icinga.Loader(this); this.events = new Icinga.Events(this); this.history = new Icinga.History(this); + var self = this; + $.each(Icinga.Behaviors, function(name, Behavior) { + self.behaviors[name.toLowerCase()] = new Behavior(); + }); this.timezone.initialize(); this.timer.initialize(); @@ -97,6 +106,7 @@ this.history.initialize(); this.ui.initialize(); this.loader.initialize(); + this.logger.info('Icinga is ready, running on jQuery ', $().jquery); this.initialized = true; }, diff --git a/public/js/icinga/behavior.js b/public/js/icinga/behavior.js new file mode 100644 index 000000000..4e1e570f9 --- /dev/null +++ b/public/js/icinga/behavior.js @@ -0,0 +1,70 @@ +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}}} + +(function(Icinga) { + + /** + * Used to define a set of functionality that can be applied + * on a subtree of the site's DOM + * + * Behaviors + * + * @constructor + */ + Icinga.Behavior = function () { + this.handler = { + apply: [], + bind: [], + unbind: [] + }; + }; + + Icinga.Behavior.prototype.on = function(evt, fn) { + this.handler[evt].push(fn); + }; + + Icinga.Behavior.prototype.off = function(evt, fn) { + this.handler[evt].remove(fn); + }; + + Icinga.Behavior.prototype.trigger = function(evt, el) { + var handler = this.handler[evt]; + for (var i = 0; i < handler.length; i++) { + if (typeof handler[i] === 'function') { + handler[i](el); + } + } + }; + + Icinga.Behavior.prototype.onApply = function(fn) { + this.on('apply', fn); + }; + + Icinga.Behavior.prototype.onBind = function(fn) { + this.on('bind', fn); + }; + + Icinga.Behavior.prototype.onUnbind = function(fn) { + this.on('unbind', fn); + }; + + Icinga.Behavior.prototype.apply = function(el) { + this.trigger ('apply', el); + }; + + Icinga.Behavior.prototype.bind = function(el) { + this.trigger ('bind', el); + }; + + Icinga.Behavior.prototype.unbind = function(el) { + this.trigger ('apply', el); + }; + + Icinga.Behavior.prototype.off = function() { + this.handler = { + apply: [], + bind: [], + unbind: [] + }; + }; +}) (Icinga); \ No newline at end of file diff --git a/public/js/icinga/behavior/tooltip.js b/public/js/icinga/behavior/tooltip.js new file mode 100644 index 000000000..1feb6f54f --- /dev/null +++ b/public/js/icinga/behavior/tooltip.js @@ -0,0 +1,66 @@ +// {{{ICINGA_LICENSE_HEADER}}} +// {{{ICINGA_LICENSE_HEADER}}} + +(function(Icinga, $) { + + "use strict"; + + Icinga.Behaviors = Icinga.Behaviors || {}; + + var Tooltip = function () { + this.mouseX = 0; + this.mouseY = 0; + }; + + Tooltip.prototype.apply = function(el) { + var self = this; + + $('[title]').each(function () { + var $el = $(this); + $el.attr('title', $el.data('title-rich') || $el.attr('title')); + }); + $('svg rect.chart-data[title]', el).tipsy({ gravity: 'se', html: true }); + $('.historycolorgrid a[title]', el).tipsy({ gravity: 's', offset: 2 }); + $('img.icon[title]', el).tipsy({ gravity: $.fn.tipsy.autoNS, offset: 2 }); + $('[title]', el).tipsy({ gravity: $.fn.tipsy.autoNS, delayIn: 500 }); + + // migrate or remove all orphaned tooltips + $('.tipsy').each(function () { + var arrow = $('.tipsy-arrow', this)[0]; + if (!Icinga.utils.elementsOverlap(arrow, $('#main')[0])) { + $(this).remove(); + return; + } + if (!Icinga.utils.elementsOverlap(arrow, el)) { + return; + } + var title = $(this).find('.tipsy-inner').html(); + var atMouse = document.elementFromPoint(self.mouseX, self.mouseY); + var nearestTip = $(atMouse).closest('[original-title="' + title + '"]')[0]; + if (nearestTip) { + var tipsy = $.data(nearestTip, 'tipsy'); + tipsy.$tip = $(this); + $.data(this, 'tipsy-pointee', nearestTip); + } else { + // doesn't match delete + $(this).remove(); + } + }); + }; + + Tooltip.prototype.bind = function() { + var self = this; + $(document).on('mousemove', function (event) { + self.mouseX = event.pageX; + self.mouseY = event.pageY; + }); + }; + + Tooltip.prototype.unbind = function() { + $(document).off('mousemove'); + }; + + // Export + Icinga.Behaviors.Tooltip = Tooltip; + +}) (Icinga, jQuery); diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 69a08ef56..49a2da42e 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -12,8 +12,6 @@ var activeMenuId; - var mouseX, mouseY; - Icinga.Events = function (icinga) { this.icinga = icinga; @@ -34,7 +32,22 @@ initialize: function () { this.applyGlobalDefaults(); this.applyHandlers($('#layout')); - this.icinga.ui.prepareContainers(); + var self = this; + + // define global site behavior + $.each(self.icinga.behaviors, function (name, behavior) { + behavior.bind(); + }); + + // prepare container html + $('.container').each(function(idx, el) { + // apply event handlers + icinga.events.applyHandlers($(el)); + icinga.ui.initializeControls($(el)); + $.each(self.icinga.behaviors, function (name, behavior) { + behavior.apply(el); + }); + }); }, // TODO: What's this? @@ -116,39 +129,6 @@ this.searchValue = searchField.val(); } - $('[title]').each(function () { - var $el = $(this); - $el.attr('title', $el.data('title-rich') || $el.attr('title')); - }); - $('svg rect.chart-data[title]', el).tipsy({ gravity: 'se', html: true }); - $('.historycolorgrid a[title]', el).tipsy({ gravity: 's', offset: 2 }); - $('img.icon[title]', el).tipsy({ gravity: $.fn.tipsy.autoNS, offset: 2 }); - $('[title]', el).tipsy({ gravity: $.fn.tipsy.autoNS, delayIn: 500 }); - - // migrate or remove all orphaned tooltips - $('.tipsy').each(function () { - var arrow = $('.tipsy-arrow', this)[0]; - if (!icinga.utils.elementsOverlap(arrow, $('#main')[0])) { - $(this).remove(); - return; - } - if (!icinga.utils.elementsOverlap(arrow, el)) { - return; - } - var title = $(this).find('.tipsy-inner').html(); - var atMouse = document.elementFromPoint(mouseX, mouseY); - var nearestTip = $(atMouse) - .closest('[original-title="' + title + '"]')[0]; - if (nearestTip) { - var tipsy = $.data(nearestTip, 'tipsy'); - tipsy.$tip = $(this); - $.data(this, 'tipsy-pointee', nearestTip); - } else { - // doesn't match delete - $(this).remove(); - } - }); - // restore menu state if (activeMenuId) { $('[role="navigation"] li.active', el).removeClass('active'); @@ -220,10 +200,6 @@ // $(document).on('change', 'form.auto input', this.formChanged); // $(document).on('change', 'form.auto select', this.submitForm); - $(document).on('mousemove', function (event) { - mouseX = event.pageX; - mouseY = event.pageY; - }); }, menuTitleHovered: function (event) { @@ -713,7 +689,6 @@ $(document).off('mouseenter', 'li.dropdown', this.dropdownHover); $(document).off('mouseleave', 'li.dropdown', this.dropdownLeave); $(document).off('click', 'div.tristate .tristate-dummy', this.clickTriState); - $(document).off('mousemove'); }, destroy: function() { diff --git a/public/js/icinga/ui.js b/public/js/icinga/ui.js index f6ae29b35..ef5e43f70 100644 --- a/public/js/icinga/ui.js +++ b/public/js/icinga/ui.js @@ -282,20 +282,6 @@ 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 - ); - */ - }, - /** * Add the given table-row to the selection of the closest * table and deselect all other rows of the closest table.