Gitter migration: Point people to app.gitter.im (rollout pt. 1)
[gitter.git] / public / js / views / behaviors / tooltip.js
blob4de4cf7200f71692726c9bbee5d4f9abc2443f3d
1 'use strict';
2 // var $ = require('jquery');
3 var Marionette = require('backbone.marionette');
4 var behaviourLookup = require('./lookup');
5 var matchesSelector = require('../../utils/matches-selector');
6 var RAF = require('../../utils/raf');
7 var isCompact = require('../../utils/detect-compact');
9 require('../../utils/tooltip');
11 // if a an element was created in this event loop under
12 // the user's pointer, this function will trigger a mouseover.
13 function triggerMouseoverForHover(el) {
14   // browser doent know that something is hovered or not until
15   // the next animation frame
16   RAF(function() {
17     if (!matchesSelector(el, ':hover')) return;
19     // Force a mouseover event to wake up the tooltip
20     var evt;
21     try {
22       evt = new MouseEvent('mouseover');
23     } catch (e) {
24       /* Internet Explorer, good times */
25       evt = document.createEvent('MouseEvents');
26       evt.initMouseEvent(
27         'mouseover',
28         true,
29         true,
30         window,
31         0,
32         0,
33         0,
34         80,
35         20,
36         false,
37         false,
38         false,
39         false,
40         0,
41         null
42       );
43     }
44     el.dispatchEvent(evt);
45   });
48 var Behavior = Marionette.Behavior.extend({
49   onRender: function() {
50     if (isCompact()) return;
52     // existing tooltips are no longer on the dom due to render.
53     // so we clean up the listeners etc.
54     this.destroyTooltips();
56     if (!this.tooltips) this.tooltips = {};
57     if (!this.handlers) this.handlers = {};
59     var self = this;
61     Object.keys(this.options).forEach(function(selector) {
62       var $el = selector === '' ? self.$el : self.$el.find(selector);
63       var el = $el[0];
64       if (!el) {
65         // Cannot find element? Don't continue
66         return;
67       }
69       var handler = self.createHandler($el, el, selector);
70       el.addEventListener('mouseover', handler, false);
71       self.handlers[selector] = handler;
73       triggerMouseoverForHover(el);
74     });
75   },
77   createHandler: function($el, el, selector) {
78     var self = this;
80     return {
81       el: el,
82       handleEvent: function() {
83         el.removeEventListener('mouseover', this, false);
84         delete self.handlers[selector];
86         self.initTooltip(selector, $el, el);
87       }
88     };
89   },
91   initTooltip: function(selector, $el, el) {
92     var tooltipOptions = this.options[selector];
94     var title = tooltipOptions.title || el.getAttribute('title');
95     if (typeof tooltipOptions.titleFn === 'function') {
96       title = tooltipOptions.titleFn.bind(this.view)();
97     } else if (tooltipOptions.titleFn) {
98       title = this.view[tooltipOptions.titleFn].bind(this.view);
99     }
101     this.tooltips[selector] = $el;
102     $el.tooltip({
103       html: tooltipOptions.html,
104       title: title,
105       placement: tooltipOptions.placement,
106       container: tooltipOptions.container || 'body'
107     });
109     var customUpdateEventName = tooltipOptions.customUpdateEvent;
110     if (customUpdateEventName) {
111       this.view.on(customUpdateEventName, this.updateTooltip.bind(this, $el));
112     }
114     triggerMouseoverForHover(el);
115   },
117   onDestroy: function() {
118     this.destroyHandlers();
119     this.destroyTooltips();
120   },
122   destroyHandlers: function() {
123     var handlers = this.handlers;
124     delete this.handlers;
126     if (!handlers) return;
128     Object.keys(handlers).forEach(function(selector) {
129       var handler = handlers[selector];
130       var el = handler.el;
131       el.removeEventListener('mouseover', handler, false);
132     });
133   },
135   destroyTooltips: function() {
136     var tooltips = this.tooltips;
137     delete this.tooltips;
139     if (!tooltips) return;
141     Object.keys(tooltips).forEach(function(selector) {
142       var $el = tooltips[selector];
143       $el.tooltip('destroy');
144     });
145   },
147   // Limitation of bootstrap tooltip library
148   // We do this so that when you click a toggle for example,
149   // the tooltip text is properly updated to show the inverse hint
150   updateTooltip: function($el) {
151     $el.tooltip('hide');
152     $el.tooltip('show');
153   }
156 behaviourLookup.register('Tooltip', Behavior);
158 module.exports = Behavior;