jquery-qtip2-rails 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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, {