jquery-qtip2-rails 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,10 @@
1
+ var IE6;
2
+
1
3
  /*
2
4
  * BGIFrame adaption (http://plugins.jquery.com/project/bgiframe)
3
5
  * Special thanks to Brandon Aaron
4
6
  */
5
- function IE6(api)
7
+ function Ie6(api)
6
8
  {
7
9
  var self = this,
8
10
  elems = api.elements,
@@ -138,19 +140,15 @@ function IE6(api)
138
140
  self.init();
139
141
  }
140
142
 
141
- PLUGINS.ie6 = function(api)
143
+ IE6 = PLUGINS.ie6 = function(api)
142
144
  {
143
- var browser = $.browser,
144
- self = api.plugins.ie6;
145
+ var self = api.plugins.ie6;
145
146
 
146
147
  // Proceed only if the browser is IE6
147
- if(!(browser.msie && (''+browser.version).charAt(0) === '6')) {
148
- return FALSE;
149
- }
148
+ if(PLUGINS.ie !== 6) { return FALSE; }
150
149
 
151
- return 'object' === typeof self ? self : (api.plugins.ie6 = new IE6(api));
150
+ return 'object' === typeof self ? self : (api.plugins.ie6 = new Ie6(api));
152
151
  };
153
152
 
154
- // Plugin needs to be initialized on render
155
- PLUGINS.ie6.initialize = 'render';
153
+ IE6.initialize = 'render';
156
154
 
@@ -23,22 +23,30 @@
23
23
  FALSE = false,
24
24
  NULL = null,
25
25
 
26
- // Side names and other stuff
26
+ // Common variables
27
27
  X = 'x', Y = 'y',
28
28
  WIDTH = 'width',
29
29
  HEIGHT = 'height',
30
+
31
+ // Positioning sides
30
32
  TOP = 'top',
31
33
  LEFT = 'left',
32
34
  BOTTOM = 'bottom',
33
35
  RIGHT = 'right',
34
36
  CENTER = 'center',
37
+
38
+ // Position adjustment types
35
39
  FLIP = 'flip',
36
40
  FLIPINVERT = 'flipinvert',
37
41
  SHIFT = 'shift',
38
42
 
43
+ // Used by image load detection (see core.js)
44
+ BLANKIMG = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==',
45
+
39
46
  // Shortcut vars
40
47
  QTIP, PLUGINS, MOUSE,
41
48
  NAMESPACE = 'qtip',
49
+ HASATTR = 'data-hasqtip',
42
50
  usedIDs = {},
43
51
  widget = ['ui-widget', 'ui-tooltip'],
44
52
  selector = 'div.qtip.'+NAMESPACE,
@@ -1,37 +1,43 @@
1
- function Modal(api)
1
+ var MODAL, OVERLAY,
2
+ MODALATTR = 'is-modal-qtip',
3
+ MODALSELECTOR = selector + '['+MODALATTR+']',
4
+ MODALNS = '.qtipmodal';
5
+
6
+ OVERLAY = function()
2
7
  {
3
8
  var self = this,
4
- options = api.options.show.modal,
5
- elems = api.elements,
6
- tooltip = elems.tooltip,
7
- overlaySelector = '#qtip-overlay',
8
- globalNamespace = '.qtipmodal',
9
- namespace = globalNamespace + api.id,
10
- attr = 'is-modal-qtip',
11
- docBody = $(document.body),
12
- focusableSelector = PLUGINS.modal.focusable.join(','),
13
- focusableElems = {}, overlay;
14
-
15
- // Setup option set checks
16
- api.checks.modal = {
17
- '^show.modal.(on|blur)$': function() {
18
- // Initialise
19
- self.init();
20
-
21
- // Show the modal if not visible already and tooltip is visible
22
- elems.overlay.toggle( tooltip.is(':visible') );
23
- },
24
- '^content.text$': function() {
25
- updateFocusable();
9
+ focusableElems = {},
10
+ current, onLast,
11
+ prevState, elem;
12
+
13
+ // Modified code from jQuery UI 1.10.0 source
14
+ // http://code.jquery.com/ui/1.10.0/jquery-ui.js
15
+ function focusable(element) {
16
+ // Use the defined focusable checker when possible
17
+ if($.expr[':'].focusable) { return $.expr[':'].focusable; }
18
+
19
+ var isTabIndexNotNaN = !isNaN($.attr(element, 'tabindex')),
20
+ nodeName = element.nodeName.toLowerCase(),
21
+ map, mapName, img;
22
+
23
+ if('area' === nodeName) {
24
+ map = element.parentNode;
25
+ mapName = map.name;
26
+ if(!element.href || !mapName || map.nodeName.toLowerCase() !== 'map') {
27
+ return false;
28
+ }
29
+ img = $('img[usemap=#' + mapName + ']')[0];
30
+ return !!img && img.is(':visible');
26
31
  }
27
- };
28
-
29
- function updateFocusable() {
30
- focusableElems = $(focusableSelector, tooltip).not('[disabled]').map(function() {
31
- return typeof this.focus === 'function' ? this : null;
32
- });
32
+ return (/input|select|textarea|button|object/.test( nodeName ) ?
33
+ !element.disabled :
34
+ 'a' === nodeName ?
35
+ element.href || isTabIndexNotNaN :
36
+ isTabIndexNotNaN
37
+ );
33
38
  }
34
39
 
40
+ // Focus inputs using cached focusable elements (see update())
35
41
  function focusInputs(blurElems) {
36
42
  // Blurring body element in IE causes window.open windows to unfocus!
37
43
  if(focusableElems.length < 1 && blurElems.length) { blurElems.not('body').blur(); }
@@ -40,9 +46,13 @@ function Modal(api)
40
46
  else { focusableElems.first().focus(); }
41
47
  }
42
48
 
49
+ // Steal focus from elements outside tooltip
43
50
  function stealFocus(event) {
51
+ if(!elem.is(':visible')) { return; }
52
+
44
53
  var target = $(event.target),
45
- container = target.closest('.qtip'),
54
+ tooltip = current.elements.tooltip,
55
+ container = target.closest(selector),
46
56
  targetOnTop;
47
57
 
48
58
  // Determine if input container target is above this
@@ -52,31 +62,184 @@ function Modal(api)
52
62
  // If we're showing a modal, but focus has landed on an input below
53
63
  // this modal, divert focus to the first visible input in this modal
54
64
  // or if we can't find one... the tooltip itself
55
- if(!targetOnTop && ($(event.target).closest(selector)[0] !== tooltip[0])) {
65
+ if(!targetOnTop && target.closest(selector)[0] !== tooltip[0]) {
56
66
  focusInputs(target);
57
67
  }
68
+
69
+ // Detect when we leave the last focusable element...
70
+ onLast = event.target === focusableElems[focusableElems.length - 1];
58
71
  }
59
72
 
73
+ $.extend(self, {
74
+ init: function()
75
+ {
76
+ // Create document overlay
77
+ elem = self.elem = $('<div />', {
78
+ id: 'qtip-overlay',
79
+ html: '<div></div>',
80
+ mousedown: function() { return FALSE; }
81
+ })
82
+ .hide();
83
+
84
+ // Update position on window resize or scroll
85
+ function resize() {
86
+ var win = $(this);
87
+ elem.css({
88
+ height: win.height(),
89
+ width: win.width()
90
+ });
91
+ }
92
+ $(window).bind('resize'+MODALNS, resize);
93
+ resize(); // Fire it initially too
94
+
95
+ // Make sure we can't focus anything outside the tooltip
96
+ $(document.body).bind('focusin'+MODALNS, stealFocus);
97
+
98
+ // Apply keyboard "Escape key" close handler
99
+ $(document).bind('keydown'+MODALNS, function(event) {
100
+ if(current && current.options.show.modal.escape && event.keyCode === 27) {
101
+ current.hide(event);
102
+ }
103
+ });
104
+
105
+ // Apply click handler for blur option
106
+ elem.bind('click'+MODALNS, function(event) {
107
+ if(current && current.options.show.modal.blur) {
108
+ current.hide(event);
109
+ }
110
+ });
111
+
112
+ return self;
113
+ },
114
+
115
+ update: function(api) {
116
+ // Update current API reference
117
+ current = api;
118
+
119
+ // Update focusable elements if enabled
120
+ if(api.options.show.modal.stealfocus !== FALSE) {
121
+ focusableElems = api.elements.tooltip.find('*').filter(function() {
122
+ return focusable(this);
123
+ });
124
+ }
125
+ else { focusableElems = []; }
126
+ },
127
+
128
+ toggle: function(api, state, duration)
129
+ {
130
+ var docBody = $(document.body),
131
+ tooltip = api.elements.tooltip,
132
+ options = api.options.show.modal,
133
+ effect = options.effect,
134
+ type = state ? 'show': 'hide',
135
+ visible = elem.is(':visible'),
136
+ modals = $(MODALSELECTOR).filter(':visible:not(:animated)').not(tooltip),
137
+ zindex;
138
+
139
+ // Set active tooltip API reference
140
+ self.update(api);
141
+
142
+ // If the modal can steal the focus...
143
+ // Blur the current item and focus anything in the modal we an
144
+ if(state && options.stealfocus !== FALSE) {
145
+ focusInputs( $(':focus') );
146
+ }
147
+
148
+ // Toggle backdrop cursor style on show
149
+ elem.toggleClass('blurs', options.blur);
150
+
151
+ // Set position and append to body on show
152
+ if(state) {
153
+ elem.css({ left: 0, top: 0 })
154
+ .appendTo(document.body);
155
+ }
156
+
157
+ // Prevent modal from conflicting with show.solo, and don't hide backdrop is other modals are visible
158
+ if((elem.is(':animated') && visible === state && prevState !== FALSE) || (!state && modals.length)) {
159
+ return self;
160
+ }
161
+
162
+ // Stop all animations
163
+ elem.stop(TRUE, FALSE);
164
+
165
+ // Use custom function if provided
166
+ if($.isFunction(effect)) {
167
+ effect.call(elem, state);
168
+ }
169
+
170
+ // If no effect type is supplied, use a simple toggle
171
+ else if(effect === FALSE) {
172
+ elem[ type ]();
173
+ }
174
+
175
+ // Use basic fade function
176
+ else {
177
+ elem.fadeTo( parseInt(duration, 10) || 90, state ? 1 : 0, function() {
178
+ if(!state) { elem.hide(); }
179
+ });
180
+ }
181
+
182
+ // Reset position and detach from body on hide
183
+ if(!state) {
184
+ elem.queue(function(next) {
185
+ elem.css({ left: '', top: '' });
186
+ if(!modals.length) { elem.detach(); }
187
+ next();
188
+ });
189
+ }
190
+
191
+ // Cache the state
192
+ prevState = state;
193
+
194
+ // If the tooltip is destroyed, set referenceto null
195
+ if(current.destroyed) { current = NULL; }
196
+
197
+ return self;
198
+ }
199
+ });
200
+
201
+ self.init();
202
+ };
203
+ OVERLAY = new OVERLAY();
204
+
205
+ function Modal(api)
206
+ {
207
+ var self = this,
208
+ options = api.options.show.modal,
209
+ elems = api.elements,
210
+ tooltip = elems.tooltip,
211
+ namespace = MODALNS + api.id,
212
+ overlay;
213
+
214
+ // Setup option set checks
215
+ api.checks.modal = {
216
+ '^show.modal.(on|blur)$': function() {
217
+ // Initialise
218
+ self.destroy();
219
+ self.init();
220
+
221
+ // Show the modal if not visible already and tooltip is visible
222
+ overlay.toggle( tooltip.is(':visible') );
223
+ }
224
+ };
225
+
60
226
  $.extend(self, {
61
227
  init: function()
62
228
  {
63
229
  // If modal is disabled... return
64
230
  if(!options.on) { return self; }
65
231
 
66
- // Create the overlay if needed
67
- overlay = self.create();
232
+ // Set overlay reference
233
+ overlay = elems.overlay = OVERLAY.elem;
68
234
 
69
235
  // Add unique attribute so we can grab modal tooltips easily via a selector
70
- tooltip.attr(attr, TRUE)
236
+ tooltip.attr(MODALATTR, TRUE)
71
237
 
72
238
  // Set z-index
73
- .css('z-index', PLUGINS.modal.zindex + $(selector+'['+attr+']').length)
239
+ .css('z-index', PLUGINS.modal.zindex + $(MODALSELECTOR).length)
74
240
 
75
- // Remove previous bound events in globalNamespace
76
- .unbind(globalNamespace).unbind(namespace)
77
-
78
241
  // Apply our show/hide/focus modal events
79
- .bind('tooltipshow'+globalNamespace+' tooltiphide'+globalNamespace, function(event, api, duration) {
242
+ .bind('tooltipshow'+namespace+' tooltiphide'+namespace, function(event, api, duration) {
80
243
  var oEvent = event.originalEvent;
81
244
 
82
245
  // Make sure mouseout doesn't trigger a hide when showing the modal and mousing onto backdrop
@@ -85,24 +248,24 @@ function Modal(api)
85
248
  try { event.preventDefault(); } catch(e) {}
86
249
  }
87
250
  else if(!oEvent || (oEvent && !oEvent.solo)) {
88
- self[ event.type.replace('tooltip', '') ](event, duration);
251
+ self.toggle(event, event.type === 'tooltipshow', duration);
89
252
  }
90
253
  }
91
254
  })
92
255
 
93
256
  // Adjust modal z-index on tooltip focus
94
- .bind('tooltipfocus'+globalNamespace, function(event) {
95
- // If focus was cancelled before it reearch us, don't do anything
257
+ .bind('tooltipfocus'+namespace, function(event, api) {
258
+ // If focus was cancelled before it reached us, don't do anything
96
259
  if(event.isDefaultPrevented() || event.target !== tooltip[0]) { return; }
97
260
 
98
- var qtips = $(selector).filter('['+attr+']'),
261
+ var qtips = $(MODALSELECTOR),
99
262
 
100
263
  // Keep the modal's lower than other, regular qtips
101
264
  newIndex = PLUGINS.modal.zindex + qtips.length,
102
265
  curIndex = parseInt(tooltip[0].style.zIndex, 10);
103
266
 
104
267
  // Set overlay z-index
105
- overlay[0].style.zIndex = newIndex - 2;
268
+ overlay[0].style.zIndex = newIndex - 1;
106
269
 
107
270
  // Reduce modal z-index's and keep them properly ordered
108
271
  qtips.each(function() {
@@ -112,191 +275,60 @@ function Modal(api)
112
275
  });
113
276
 
114
277
  // Fire blur event for focused tooltip
115
- qtips.end().filter('.' + focusClass).qtip('blur', event.originalEvent);
278
+ qtips.filter('.' + focusClass).qtip('blur', event.originalEvent);
116
279
 
117
280
  // Set the new z-index
118
281
  tooltip.addClass(focusClass)[0].style.zIndex = newIndex;
119
282
 
283
+ // Set current
284
+ OVERLAY.update(api);
285
+
120
286
  // Prevent default handling
121
287
  try { event.preventDefault(); } catch(e) {}
122
288
  })
123
289
 
124
290
  // Focus any other visible modals when this one hides
125
- .bind('tooltiphide'+globalNamespace, function(event) {
291
+ .bind('tooltiphide'+namespace, function(event) {
126
292
  if(event.target === tooltip[0]) {
127
- $('[' + attr + ']').filter(':visible').not(tooltip).last().qtip('focus', event);
293
+ $(MODALSELECTOR).filter(':visible').not(tooltip).last().qtip('focus', event);
128
294
  }
129
295
  });
130
296
 
131
- // Apply keyboard "Escape key" close handler
132
- if(options.escape) {
133
- $(document).unbind(namespace).bind('keydown'+namespace, function(event) {
134
- if(event.keyCode === 27 && tooltip.hasClass(focusClass)) {
135
- api.hide(event);
136
- }
137
- });
138
- }
139
-
140
- // Apply click handler for blur option
141
- if(options.blur) {
142
- elems.overlay.unbind(namespace).bind('click'+namespace, function(event) {
143
- if(tooltip.hasClass(focusClass)) { api.hide(event); }
144
- });
145
- }
146
-
147
- // Update focusable elements
148
- updateFocusable();
149
-
150
297
  return self;
151
298
  },
152
299
 
153
- create: function()
154
- {
155
- var elem = $(overlaySelector), win = $(window);
156
-
157
- // Return if overlay is already rendered
158
- if(elem.length) {
159
- // Modal overlay should always be below all tooltips if possible
160
- return (elems.overlay = elem.insertAfter( $(selector).last() ));
161
- }
162
-
163
- // Create document overlay
164
- overlay = elems.overlay = $('<div />', {
165
- id: overlaySelector.substr(1),
166
- html: '<div></div>',
167
- mousedown: function() { return FALSE; }
168
- })
169
- .hide()
170
- .insertAfter( $(selector).last() );
171
-
172
- // Update position on window resize or scroll
173
- function resize() {
174
- overlay.css({
175
- height: win.height(),
176
- width: win.width()
177
- });
178
- }
179
- win.unbind(globalNamespace).bind('resize'+globalNamespace, resize);
180
- resize(); // Fire it initially too
181
-
182
- return overlay;
183
- },
184
-
185
300
  toggle: function(event, state, duration)
186
301
  {
187
302
  // Make sure default event hasn't been prevented
188
303
  if(event && event.isDefaultPrevented()) { return self; }
189
304
 
190
- var effect = options.effect,
191
- type = state ? 'show': 'hide',
192
- visible = overlay.is(':visible'),
193
- modals = $('[' + attr + ']').filter(':visible').not(tooltip),
194
- zindex;
195
-
196
- // Create our overlay if it isn't present already
197
- if(!overlay) { overlay = self.create(); }
198
-
199
- // Prevent modal from conflicting with show.solo, and don't hide backdrop is other modals are visible
200
- if((overlay.is(':animated') && visible === state && overlay.data('toggleState') !== FALSE) || (!state && modals.length)) {
201
- return self;
202
- }
203
-
204
- // State specific...
205
- if(state) {
206
- // Set position
207
- overlay.css({ left: 0, top: 0 });
208
-
209
- // Toggle backdrop cursor style on show
210
- overlay.toggleClass('blurs', options.blur);
211
-
212
- // IF the modal can steal the focus
213
- if(options.stealfocus !== FALSE) {
214
- // Make sure we can't focus anything outside the tooltip
215
- docBody.bind('focusin'+namespace, stealFocus);
216
-
217
- // Blur the current item and focus anything in the modal we an
218
- focusInputs( $('body :focus') );
219
- }
220
- }
221
- else {
222
- // Undelegate focus handler
223
- docBody.unbind('focusin'+namespace);
224
- }
225
-
226
- // Stop all animations
227
- overlay.stop(TRUE, FALSE).data('toggleState', state);
228
-
229
- // Use custom function if provided
230
- if($.isFunction(effect)) {
231
- effect.call(overlay, state);
232
- }
233
-
234
- // If no effect type is supplied, use a simple toggle
235
- else if(effect === FALSE) {
236
- overlay[ type ]();
237
- }
238
-
239
- // Use basic fade function
240
- else {
241
- overlay.fadeTo( parseInt(duration, 10) || 90, state ? 1 : 0, function() {
242
- if(!state) { $(this).hide(); }
243
- });
244
- }
245
-
246
- // Reset position on hide
247
- if(!state) {
248
- overlay.queue(function(next) {
249
- overlay.css({ left: '', top: '' }).removeData('toggleState');
250
- next();
251
- });
252
- }
305
+ // Toggle it
306
+ OVERLAY.toggle(api, !!state, duration);
253
307
 
254
308
  return self;
255
309
  },
256
310
 
257
- show: function(event, duration) { return self.toggle(event, TRUE, duration); },
258
- hide: function(event, duration) { return self.toggle(event, FALSE, duration); },
259
-
260
- destroy: function()
261
- {
262
- var delBlanket = overlay;
263
-
264
- if(delBlanket) {
265
- // Check if any other modal tooltips are present
266
- delBlanket = $('[' + attr + ']').not(tooltip).length < 1;
267
-
268
- // Remove overlay if needed
269
- if(delBlanket) {
270
- elems.overlay.remove();
271
- $(document).unbind(globalNamespace);
272
- }
273
- else {
274
- elems.overlay.unbind(globalNamespace+api.id);
275
- }
276
-
277
- // Undelegate focus handler
278
- docBody.unbind('focusin'+namespace);
279
- }
280
-
311
+ destroy: function() {
281
312
  // Remove bound events
282
- return tooltip.removeAttr(attr).unbind(globalNamespace);
313
+ $([document, tooltip]).removeAttr(MODALATTR).unbind(namespace);
314
+
315
+ // Delete element reference
316
+ OVERLAY.toggle(api, FALSE);
317
+ delete elems.overlay;
283
318
  }
284
319
  });
285
320
 
286
321
  self.init();
287
322
  }
288
323
 
289
- PLUGINS.modal = function(api) {
324
+ MODAL = PLUGINS.modal = function(api) {
290
325
  var self = api.plugins.modal;
291
326
 
292
327
  return 'object' === typeof self ? self : (api.plugins.modal = new Modal(api));
293
328
  };
294
329
 
295
- // Plugin needs to be initialized on render
296
- PLUGINS.modal.initialize = 'render';
297
-
298
330
  // Setup sanitiztion rules
299
- PLUGINS.modal.sanitize = function(opts) {
331
+ MODAL.sanitize = function(opts) {
300
332
  if(opts.show) {
301
333
  if(typeof opts.show.modal !== 'object') { opts.show.modal = { on: !!opts.show.modal }; }
302
334
  else if(typeof opts.show.modal.on === 'undefined') { opts.show.modal.on = TRUE; }
@@ -304,11 +336,10 @@ PLUGINS.modal.sanitize = function(opts) {
304
336
  };
305
337
 
306
338
  // Base z-index for all modal tooltips (use qTip core z-index as a base)
307
- PLUGINS.modal.zindex = QTIP.zindex - 200;
339
+ MODAL.zindex = QTIP.zindex - 200;
308
340
 
309
- // Defines the selector used to select all 'focusable' elements within the modal when using the show.modal.stealfocus option.
310
- // Selectors initially taken from http://stackoverflow.com/questions/7668525/is-there-a-jquery-selector-to-get-all-elements-that-can-get-focus
311
- PLUGINS.modal.focusable = ['a[href]', 'area[href]', 'input', 'select', 'textarea', 'button', 'iframe', 'object', 'embed', '[tabindex]', '[contenteditable]'];
341
+ // Plugin needs to be initialized on render
342
+ MODAL.initialize = 'render';
312
343
 
313
344
  // Extend original api defaults
314
345
  $.extend(TRUE, QTIP.defaults, {