inline_forms 2.23 → 3.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.
Files changed (51) hide show
  1. checksums.yaml +9 -9
  2. data/.gitignore +5 -0
  3. data/bin/inline_forms +41 -414
  4. data/bin/inline_forms_app_template.rb +16 -0
  5. data/bin/inline_forms_installer_core.rb +423 -0
  6. data/lib/app/assets/javascripts/inline_forms.js +12 -32
  7. data/lib/app/assets/javascripts/inline_forms_application.js +7 -1
  8. data/lib/app/assets/stylesheets/inline_forms.css +5 -399
  9. data/lib/app/assets/stylesheets/inline_forms_application.css +3 -3
  10. data/lib/app/controllers/inline_forms_application_controller.rb +1 -2
  11. data/lib/app/controllers/inline_forms_controller.rb +2 -2
  12. data/lib/app/helpers/form_elements/check_list.rb +7 -14
  13. data/lib/app/helpers/form_elements/date.rb +3 -8
  14. data/lib/app/helpers/form_elements/decimal_field.rb +15 -0
  15. data/lib/app/helpers/form_elements/dropdown.rb +1 -1
  16. data/lib/app/helpers/form_elements/dropdown_with_other.rb +153 -0
  17. data/lib/app/helpers/form_elements/dropdown_with_values.rb +1 -1
  18. data/lib/app/helpers/form_elements/info_list.rb +22 -0
  19. data/lib/app/helpers/form_elements/integer_field.rb +15 -0
  20. data/lib/app/helpers/form_elements/kansen_slider.rb +89 -0
  21. data/lib/app/helpers/form_elements/month_year_picker.rb +33 -0
  22. data/lib/app/helpers/form_elements/move.rb +17 -0
  23. data/lib/app/helpers/form_elements/plain_text_area.rb +1 -1
  24. data/lib/app/helpers/form_elements/radio_button.rb +5 -6
  25. data/lib/app/helpers/form_elements/slider_with_values.rb +1 -1
  26. data/lib/app/helpers/form_elements/text_area.rb +12 -10
  27. data/lib/app/helpers/form_elements/text_area_without_ckeditor.rb +1 -1
  28. data/lib/app/helpers/form_elements/text_field.rb +2 -2
  29. data/lib/app/helpers/inline_forms_helper.rb +32 -37
  30. data/lib/app/views/inline_forms/_close.html.erb +12 -2
  31. data/lib/app/views/inline_forms/_edit.html.erb +54 -21
  32. data/lib/app/views/inline_forms/_flash.html.erb +13 -0
  33. data/lib/app/views/inline_forms/_list.html.erb +28 -24
  34. data/lib/app/views/inline_forms/_new.html.erb +52 -60
  35. data/lib/app/views/inline_forms/_new_nested.html.erb +43 -0
  36. data/lib/app/views/inline_forms/_show.html.erb +83 -39
  37. data/lib/app/views/inline_forms/_tree.html.erb +46 -0
  38. data/lib/app/views/layouts/devise.html.erb +4 -14
  39. data/lib/app/views/layouts/inline_forms.html.erb +15 -23
  40. data/lib/inline_forms/version.rb +1 -1
  41. metadata +17 -13
  42. data/lib/app/assets/images/add.png +0 -0
  43. data/lib/app/assets/images/close.png +0 -0
  44. data/lib/app/assets/images/tooltip-bubble-down-left.png +0 -0
  45. data/lib/app/assets/images/tooltip-bubble-down-right.png +0 -0
  46. data/lib/app/assets/images/tooltip-bubble-up-left.png +0 -0
  47. data/lib/app/assets/images/tooltip-bubble-up-right.png +0 -0
  48. data/lib/app/assets/images/trash.png +0 -0
  49. data/lib/app/assets/javascripts/jquery.qtip.js +0 -3395
  50. data/lib/app/assets/stylesheets/jquery.qtip.css +0 -567
  51. data/lib/app/views/inline_forms/_header.html.erb +0 -1
@@ -0,0 +1,43 @@
1
+ This goes in _new.html.erb
2
+ <% if form_element.to_sym == :associated -%>
3
+ <% @nested_model = @object.send(attribute) %>
4
+ <%= render 'new_nested' %>
5
+ <% end %>
6
+ ********
7
+
8
+ <% @nested_object = @nested_model.build %>
9
+
10
+ <div class="new_nested_record">
11
+ <div class="row" >
12
+ <div class="large-12 column object_presentation" >
13
+ <%= t('inline_forms.view.add_new', :model => @nested_object.class ) -%>
14
+ </div>
15
+ </div>
16
+ <% nested_attributes = @nested_object.inline_forms_attribute_list -%>
17
+ <% nested_attributes.each do | nested_attribute, nested_name, nested_form_element | -%>
18
+ <% @nested_form_element = nested_form_element %>
19
+ <% @nested_attribute = nested_attribute %>
20
+ <% unless @nested_form_element.to_sym == :associated -%>
21
+ <% if @nested_form_element == :header %>
22
+ <div class="row form_element_header" >
23
+ <div class='large-12 column' >
24
+ <%= @nested_object.class.human_attribute_name(@nested_attribute) -%>
25
+ </div>
26
+ </div>
27
+ <% else %>
28
+ <div class="row <%= cycle('odd', 'even') %>">
29
+ <div class='medium-3 large-3 column' >
30
+ <%= @nested_object.class.human_attribute_name(@nested_attribute) -%>
31
+ </div>
32
+ <div class='medium-9 large-9 column' >
33
+ <%= send("#{@nested_form_element}_edit", @nested_object, @nested_attribute) -%>
34
+ </div>
35
+ </div>
36
+ <% end -%>
37
+ <% end -%>
38
+ <% end -%>
39
+ <% if @nested_form_element.to_sym == :associated -%>
40
+ <%= render 'new_nested' %>
41
+ <% end %>
42
+ <div class="row record_footer"></div>
43
+ </div>
@@ -1,49 +1,93 @@
1
- <table cellspacing="0" cellpadding="0" style="height: 0">
2
- <tr>
3
- <th valign="top" colspan="2">
4
- <div class="object_presentation" >
1
+ <div class="row">
2
+ <div class='medium-1 large-1 column'>
3
+ &nbsp;
4
+ </div>
5
+ <div class='small-11 column'>
6
+ <% unless @skip %>
7
+ <div class="row object_presentation">
8
+ <div class="small-11 column object_presentation">
5
9
  <%= h(@object._presentation) -%>
6
10
  </div>
7
- <div class="close_link" >
11
+ <div class="small-1 column close_link" >
8
12
  <%= close_link(@object, @update_span) -%>
9
13
  </div>
10
- </th>
11
- </tr>
12
- <% attributes = @inline_forms_attribute_list || @object.inline_forms_attribute_list -%>
13
- <% attributes.each do | attribute, name, form_element | -%>
14
- <% if cancan_disabled? || can?(:read, @object, attribute) %>
15
- <% css_class_id = "#{@object.class.name.underscore}_#{@object.id}_#{attribute}" -%>
16
- <% if form_element == :header %>
17
- <tr>
18
- <td valign="top" class="header" colspan="2">
19
- <div class='<%= "attribute_name attribute_#{attribute} form_element_#{form_element}" -%>' >
14
+ </div>
15
+ <% end %>
16
+ <% attributes = @inline_forms_attribute_list || @object.inline_forms_attribute_list -%>
17
+ <% attributes.each do | attribute, name, form_element | -%>
18
+ <% puts "LOG: #{attribute}, #{form_element}" if Rails.env.development? %>
19
+ <% if cancan_disabled? || can?(:read, @object, attribute) %>
20
+ <% css_class_id = "#{@object.class.name.underscore}_#{@object.id}_#{attribute}" -%>
21
+ <% if form_element == :header %>
22
+ <div class="row form_element_header">
23
+ <div class='large-12 column' >
20
24
  <%= @object.class.human_attribute_name(attribute) -%>
21
25
  </div>
22
- </td>
23
- </tr>
24
- <% else %>
25
- <tr>
26
- <td valign="top" class="<%= 'has_validations ' if @object.has_validations_for?(attribute) -%>" validation-hint="<%= validation_hints_as_list_for(@object, attribute) -%>">
27
- <div class='<%= "attribute_name attribute_#{attribute} form_element_#{form_element}" -%>' >
28
- <%= @object.class.human_attribute_name(attribute) -%>
26
+ </div>
27
+ <% else %>
28
+ <% if form_element == :tree -%>
29
+ <div class="row form_element_header associated_auto_header" id="<%= css_class_id -%>_list_auto_header" >
30
+ <div class='medium-11 large-11 column' >
31
+ Children
32
+ </div>
33
+ <div class='medium-1 large-1 column'>
34
+ <%= link_to_new_record(@object.class, "new_#{@object.class.to_s.underscore.singularize}_path", css_class_id, @object.class, @object.id) -%>
35
+ </div>
29
36
  </div>
30
- </td>
31
- <td valign="top">
32
- <div class='<%= "attribute_value attribute_#{attribute} form_element_#{form_element}" -%>' >
33
- <span id="<%= css_class_id -%>" >
34
- <% if form_element == :associated -%>
35
- <%= render :partial => "inline_forms/list",
36
- :locals => { :parent_class => @object.class,
37
- :parent_id => @object.id,
38
- :attribute => attribute } %>
39
- <% else -%>
40
- <%= send("#{form_element}_show", @object, attribute) -%>
41
- <% end -%>
42
- </span>
37
+ <div class="row <%= cycle('odd', 'even') %>">
38
+ <div class='medium-1 large-1 column'>
39
+ &nbsp;
40
+ </div>
41
+ <div id="<%= css_class_id -%>" class='small-11 column' >
42
+ <%= render :partial => "inline_forms/tree",
43
+ :locals => { :parent_class => @object.class,
44
+ :parent_id => @object.id,
45
+ :attribute => attribute } %>
46
+ </div>
43
47
  </div>
44
- </td>
45
- </tr>
48
+ <% else -%>
49
+ <% if form_element == :associated -%>
50
+ <div class="row form_element_header associated_auto_header" id="<%= css_class_id -%>_list_auto_header" >
51
+ <div class='medium-11 large-11 column' >
52
+ <%= @object.class.human_attribute_name(attribute) -%>
53
+ </div>
54
+ <div class='medium-1 large-1 column'>
55
+ <%= link_to_new_record(attribute.to_s.singularize.camelcase.constantize, "new_#{attribute.to_s.underscore.singularize}_path", css_class_id, @object.class, @object.id) -%>
56
+ </div>
57
+ </div>
58
+ <div class="row <%= cycle('odd', 'even') %>">
59
+ <div class='medium-1 large-1 column'>
60
+ &nbsp;
61
+ </div>
62
+ <div id="<%= css_class_id -%>" class='small-11 column' >
63
+ <%= render :partial => "inline_forms/list",
64
+ :locals => { :parent_class => @object.class,
65
+ :parent_id => @object.id,
66
+ :attribute => attribute } %>
67
+ </div>
68
+ </div>
69
+ <% else -%>
70
+ <div class="row <%= cycle('odd', 'even') %>">
71
+ <div class='medium-5 large-5 column' >
72
+ <% if @object.has_validations_for?(attribute) -%>
73
+ <span data-tooltip class="has-tip tip-top" title="<%= validation_hints_as_list_for(@object, attribute).html_safe -%>">
74
+ <%= @object.class.human_attribute_name(attribute) -%>
75
+ </span>
76
+ <% else %>
77
+ <%= @object.class.human_attribute_name(attribute) -%>
78
+ <% end %>
79
+ </div>
80
+ <div class='medium-7 large-7 column' >
81
+ <span id="<%= css_class_id -%>" >
82
+ <%= send("#{form_element}_show", @object, attribute) -%>
83
+ </span>
84
+ </div>
85
+ </div>
86
+ <% end -%>
87
+ <% end -%>
88
+ <% end -%>
46
89
  <% end -%>
47
90
  <% end -%>
48
- <% end -%>
49
- </table>
91
+ <div class="record_footer"></div>
92
+ </div>
93
+ </div>
@@ -0,0 +1,46 @@
1
+ <% # here we come from _show %>
2
+ <% update_span = "#{parent_class.to_s.underscore}_#{parent_id}_#{attribute}_list" -%>
3
+ <% path_to_new='new_' + parent_class.to_s.underscore.singularize + '_path' %>
4
+ <% model = parent_class %>
5
+ <% objects = parent_class.find(parent_id).children %>
6
+ <% objects = parent_class.find(parent_id).children.accessible_by(current_ability) if cancan_enabled? %>
7
+ <% objects = objects.paginate :page => params[:page], :per_page => @PER_PAGE || 5 %>
8
+
9
+ <div class="list_container" id="<%= update_span %>">
10
+
11
+ <!-- # list of objects -->
12
+ <% for object in objects %>
13
+ <% css_class_id = parent_class.to_s.underscore + '_' + parent_id.to_s + '_' + attribute.to_s.singularize.underscore + "_" + object.id.to_s -%>
14
+ <% path_to_object = parent_class.to_s.singularize.underscore + "_path" %>
15
+ <div class="row <%= cycle('odd', 'even') %><%= " top-level" if parent_class.nil? %>" id="<%= css_class_id -%>">
16
+ <% if cancan_disabled? || ( can? :delete, object ) %>
17
+ <div class="small-1 column">
18
+ <%= link_to_destroy(object, css_class_id) -%>
19
+ </div>
20
+ <div class="small-11 column">
21
+ <%= link_to h(object._presentation),
22
+ send( path_to_object, object, :update => css_class_id),
23
+ :remote => true -%>
24
+ </div>
25
+ <% else %>
26
+ <div class="small-12 column">
27
+ <%= link_to h(object._presentation),
28
+ send( path_to_object, object, :update => css_class_id),
29
+ :remote => true -%>
30
+ </div>
31
+ <% end %>
32
+ </div>
33
+ <% end -%>
34
+ <!-- # pagination -->
35
+ <% if parent_id.nil? -%>
36
+ <% pagination = will_paginate objects -%>
37
+ <% else %>
38
+ <% pagination = will_paginate objects, :remote => true, :params => {:controller => attribute, :action => :index, :id => nil, :parent_class => parent_class, :parent_id => parent_id, :update => "#{parent_class.to_s.underscore}_#{parent_id}_#{attribute}", :ul_needed => true } -%>
39
+ <% end %>
40
+ <% if pagination %>
41
+ <div class="row <%= cycle('odd', 'even') %>">
42
+ <div class='small-11 small-centered column'>
43
+ <%= raw pagination %>
44
+ </div>
45
+ </div>
46
+ <% end %>
@@ -1,24 +1,14 @@
1
1
  <!DOCTYPE html>
2
- <!-- paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ -->
3
- <!--[if lt IE 7 ]> <html class="ie6" lang="en"> <![endif]-->
4
- <!--[if IE 7 ]> <html class="ie7" lang="en"> <![endif]-->
5
- <!--[if IE 8 ]> <html class="ie8" lang="en"> <![endif]-->
6
- <!--[if (gte IE 9)|!(IE)]><!--> <html lang="en"> <!--<![endif]-->
2
+ <html lang="en">
7
3
  <head>
8
4
  <meta charset="utf-8" />
9
-
10
- <!-- Uncomment to make IE8 render like IE7 -->
11
- <!-- <meta http-equiv="X-UA-Compatible" content="IE=7" /> -->
12
-
13
- <!-- Set the viewport width to device width for mobile -->
14
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
15
- <link href='http://fonts.googleapis.com/css?family=Open+Sans:400,400italic' rel='stylesheet' type='text/css' />
16
- <title><%= t('application.name') + " v" + InlineForms::VERSION -%></title>
6
+ <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,400italic' rel='stylesheet' type='text/css' />
7
+ <title><%= t('application_name') + " v" + InlineForms::VERSION -%></title>
17
8
 
18
9
  <%= stylesheet_link_tag "application" %>
19
- <%= javascript_include_tag "vendor/custom.modernizr" %>
10
+ <%= javascript_include_tag "vendor/modernizr" %>
20
11
  <%= csrf_meta_tags %>
21
-
22
12
  </head>
23
13
 
24
14
  <body>
@@ -1,32 +1,24 @@
1
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
- <html>
1
+ <!DOCTYPE html>
2
+ <html lang="en">
4
3
  <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,400italic' rel='stylesheet' type='text/css' />
7
+
5
8
  <title><%= t('application_name') + " v" + InlineForms::VERSION -%></title>
6
- <%= stylesheet_link_tag 'inline_forms_application', 'application' %>
7
- <%= javascript_include_tag 'application', 'inline_forms_application' %>
8
- <%= csrf_meta_tag %>
9
- <%= yield(:head) %>
10
- <script>
11
- $.datepicker.setDefaults({
12
- dateFormat: 'dd-mm-yy',
13
- changeMonth: true,
14
- changeYear: true,
15
- showOtherMonths: true,
16
- selectOtherMonths: true,
17
- showButtonPanel: false,
18
- showWeek: true,
19
- firstDay: 1,
20
- prevText: '<<',
21
- nextText: '>>'
22
- })
23
- </script>
9
+
10
+ <%= stylesheet_link_tag "application" %>
11
+ <%= javascript_include_tag "vendor/modernizr" %>
12
+ <%= csrf_meta_tags %>
24
13
  </head>
14
+
25
15
  <body>
16
+
26
17
  <%= render "inline_forms/header" %>
27
18
  <%= render "/inline_forms_tabs" %>
28
- <div id="inline_forms_main_list">
29
- <%= yield %>
19
+ <div id="outer_container">
20
+ <%= yield %>
30
21
  </div>
22
+ <%= javascript_include_tag 'application', 'inline_forms_application' %>
31
23
  </body>
32
24
  </html>
@@ -1,4 +1,4 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module InlineForms
3
- VERSION = "2.23"
3
+ VERSION = "3.0"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inline_forms
3
3
  version: !ruby/object:Gem::Version
4
- version: '2.23'
4
+ version: '3.0'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ace Suares
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-21 00:00:00.000000000 Z
11
+ date: 2014-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rvm
@@ -156,6 +156,8 @@ email:
156
156
  - ace@suares.com
157
157
  executables:
158
158
  - inline_forms
159
+ - inline_forms_app_template.rb
160
+ - inline_forms_installer_core.rb
159
161
  extensions: []
160
162
  extra_rdoc_files: []
161
163
  files:
@@ -167,42 +169,42 @@ files:
167
169
  - README.rdoc
168
170
  - Rakefile
169
171
  - bin/inline_forms
172
+ - bin/inline_forms_app_template.rb
173
+ - bin/inline_forms_installer_core.rb
170
174
  - inline_forms.gemspec
171
- - lib/app/assets/images/add.png
172
- - lib/app/assets/images/close.png
173
175
  - lib/app/assets/images/devise_footer_left.png
174
176
  - lib/app/assets/images/devise_footer_right.png
175
177
  - lib/app/assets/images/devise_header.png
176
178
  - lib/app/assets/images/glass_plate.gif
177
- - lib/app/assets/images/tooltip-bubble-down-left.png
178
- - lib/app/assets/images/tooltip-bubble-down-right.png
179
- - lib/app/assets/images/tooltip-bubble-up-left.png
180
- - lib/app/assets/images/tooltip-bubble-up-right.png
181
- - lib/app/assets/images/trash.png
182
179
  - lib/app/assets/javascripts/ckeditor/config.js
183
180
  - lib/app/assets/javascripts/inline_forms.js
184
181
  - lib/app/assets/javascripts/inline_forms_application.js
185
- - lib/app/assets/javascripts/jquery.qtip.js
186
182
  - lib/app/assets/stylesheets/inline_forms.css
187
183
  - lib/app/assets/stylesheets/inline_forms_application.css
188
- - lib/app/assets/stylesheets/jquery.qtip.css
189
184
  - lib/app/controllers/geo_code_curacao_controller.rb
190
185
  - lib/app/controllers/inline_forms_application_controller.rb
191
186
  - lib/app/controllers/inline_forms_controller.rb
192
187
  - lib/app/helpers/form_elements/check_box.rb
193
188
  - lib/app/helpers/form_elements/check_list.rb
194
189
  - lib/app/helpers/form_elements/date.rb
190
+ - lib/app/helpers/form_elements/decimal_field.rb
195
191
  - lib/app/helpers/form_elements/devise_password_field.rb
196
192
  - lib/app/helpers/form_elements/dns_records.rb
197
193
  - lib/app/helpers/form_elements/dropdown.rb
198
194
  - lib/app/helpers/form_elements/dropdown_with_integers.rb
195
+ - lib/app/helpers/form_elements/dropdown_with_other.rb
199
196
  - lib/app/helpers/form_elements/dropdown_with_values.rb
200
197
  - lib/app/helpers/form_elements/file_field.rb
201
198
  - lib/app/helpers/form_elements/geo_code_curacao.rb
202
199
  - lib/app/helpers/form_elements/header.rb
203
200
  - lib/app/helpers/form_elements/image_field.rb
204
201
  - lib/app/helpers/form_elements/info.rb
202
+ - lib/app/helpers/form_elements/info_list.rb
203
+ - lib/app/helpers/form_elements/integer_field.rb
204
+ - lib/app/helpers/form_elements/kansen_slider.rb
205
205
  - lib/app/helpers/form_elements/money_field.rb
206
+ - lib/app/helpers/form_elements/month_year_picker.rb
207
+ - lib/app/helpers/form_elements/move.rb
206
208
  - lib/app/helpers/form_elements/pdf_link.rb
207
209
  - lib/app/helpers/form_elements/plain_text_area.rb
208
210
  - lib/app/helpers/form_elements/question_list.rb
@@ -235,10 +237,12 @@ files:
235
237
  - lib/app/views/geo_code_curacao/list_streets.js.erb
236
238
  - lib/app/views/inline_forms/_close.html.erb
237
239
  - lib/app/views/inline_forms/_edit.html.erb
238
- - lib/app/views/inline_forms/_header.html.erb
240
+ - lib/app/views/inline_forms/_flash.html.erb
239
241
  - lib/app/views/inline_forms/_list.html.erb
240
242
  - lib/app/views/inline_forms/_new.html.erb
243
+ - lib/app/views/inline_forms/_new_nested.html.erb
241
244
  - lib/app/views/inline_forms/_show.html.erb
245
+ - lib/app/views/inline_forms/_tree.html.erb
242
246
  - lib/app/views/inline_forms/close.js.erb
243
247
  - lib/app/views/inline_forms/edit.js.erb
244
248
  - lib/app/views/inline_forms/extract_translations.erb
@@ -290,7 +294,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
290
294
  version: '0'
291
295
  requirements: []
292
296
  rubyforge_project: inline_forms
293
- rubygems_version: 2.0.3
297
+ rubygems_version: 2.1.9
294
298
  signing_key:
295
299
  specification_version: 4
296
300
  summary: Inline editing of forms.
Binary file
Binary file
Binary file
@@ -1,3395 +0,0 @@
1
- /*!
2
- * qTip2 - Pretty powerful tooltips
3
- * http://craigsworks.com/projects/qtip2/
4
- *
5
- * Version: nightly
6
- * Copyright 2009-2010 Craig Michael Thompson - http://craigsworks.com
7
- *
8
- * Dual licensed under MIT or GPLv2 licenses
9
- * http://en.wikipedia.org/wiki/MIT_License
10
- * http://en.wikipedia.org/wiki/GNU_General_Public_License
11
- *
12
- * Date: Sun Jul 15 16:44:57.0000000000 2012
13
- */
14
-
15
- /*jslint browser: true, onevar: true, undef: true, nomen: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: true */
16
- /*global window: false, jQuery: false, console: false, define: false */
17
-
18
- // Uses AMD or browser globals to create a jQuery plugin.
19
- (function(factory) {
20
- if(typeof define === 'function' && define.amd) {
21
- define(['jquery'], factory);
22
- }
23
- else if(jQuery && !jQuery.fn.qtip) {
24
- factory(jQuery);
25
- }
26
- }
27
- (function($) {
28
-
29
- "use strict"; // Enable ECMAScript "strict" operation for this function. See more: http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
30
-
31
- // Munge the primitives - Paul Irish tip
32
- var TRUE = true,
33
- FALSE = false,
34
- NULL = null,
35
- undefined,
36
-
37
- // Side names and other stuff
38
- X = 'x', Y = 'y',
39
- WIDTH = 'width',
40
- HEIGHT = 'height',
41
- TOP = 'top',
42
- LEFT = 'left',
43
- BOTTOM = 'bottom',
44
- RIGHT = 'right',
45
- CENTER = 'center',
46
- FLIP = 'flip',
47
- FLIPINVERT = 'flipinvert',
48
- SHIFT = 'shift',
49
-
50
- // Shortcut vars
51
- QTIP, PLUGINS, MOUSE,
52
- usedIDs = {},
53
- uitooltip = 'ui-tooltip',
54
- widget = 'ui-widget',
55
- disabled = 'ui-state-disabled',
56
- selector = 'div.qtip.'+uitooltip,
57
- defaultClass = uitooltip + '-default',
58
- focusClass = uitooltip + '-focus',
59
- hoverClass = uitooltip + '-hover',
60
- fluidClass = uitooltip + '-fluid',
61
- hideOffset = '-31000px',
62
- replaceSuffix = '_replacedByqTip',
63
- oldtitle = 'oldtitle',
64
- trackingBound;
65
-
66
- /* Thanks to Paul Irish for this one: http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/ */
67
- function log() {
68
- log.history = log.history || [];
69
- log.history.push(arguments);
70
-
71
- // Make sure console is present
72
- if('object' === typeof console) {
73
-
74
- // Setup console and arguments
75
- var c = console[ console.warn ? 'warn' : 'log' ],
76
- args = Array.prototype.slice.call(arguments), a;
77
-
78
- // Add qTip2 marker to first argument if it's a string
79
- if(typeof arguments[0] === 'string') { args[0] = 'qTip2: ' + args[0]; }
80
-
81
- // Apply console.warn or .log if not supported
82
- a = c.apply ? c.apply(console, args) : c(args);
83
- }
84
- }
85
-
86
- // Option object sanitizer
87
- function sanitizeOptions(opts)
88
- {
89
- var content;
90
-
91
- if(!opts || 'object' !== typeof opts) { return FALSE; }
92
-
93
- if(opts.metadata === NULL || 'object' !== typeof opts.metadata) {
94
- opts.metadata = {
95
- type: opts.metadata
96
- };
97
- }
98
-
99
- if('content' in opts) {
100
- if(opts.content === NULL || 'object' !== typeof opts.content || opts.content.jquery) {
101
- opts.content = {
102
- text: opts.content
103
- };
104
- }
105
-
106
- content = opts.content.text || FALSE;
107
- if(!$.isFunction(content) && ((!content && !content.attr) || content.length < 1 || ('object' === typeof content && !content.jquery))) {
108
- opts.content.text = FALSE;
109
- }
110
-
111
- if('title' in opts.content) {
112
- if(opts.content.title === NULL || 'object' !== typeof opts.content.title) {
113
- opts.content.title = {
114
- text: opts.content.title
115
- };
116
- }
117
-
118
- content = opts.content.title.text || FALSE;
119
- if(!$.isFunction(content) && ((!content && !content.attr) || content.length < 1 || ('object' === typeof content && !content.jquery))) {
120
- opts.content.title.text = FALSE;
121
- }
122
- }
123
- }
124
-
125
- if('position' in opts) {
126
- if(opts.position === NULL || 'object' !== typeof opts.position) {
127
- opts.position = {
128
- my: opts.position,
129
- at: opts.position
130
- };
131
- }
132
- }
133
-
134
- if('show' in opts) {
135
- if(opts.show === NULL || 'object' !== typeof opts.show) {
136
- if(opts.show.jquery) {
137
- opts.show = { target: opts.show };
138
- }
139
- else {
140
- opts.show = { event: opts.show };
141
- }
142
- }
143
- }
144
-
145
- if('hide' in opts) {
146
- if(opts.hide === NULL || 'object' !== typeof opts.hide) {
147
- if(opts.hide.jquery) {
148
- opts.hide = { target: opts.hide };
149
- }
150
- else {
151
- opts.hide = { event: opts.hide };
152
- }
153
- }
154
- }
155
-
156
- if('style' in opts) {
157
- if(opts.style === NULL || 'object' !== typeof opts.style) {
158
- opts.style = {
159
- classes: opts.style
160
- };
161
- }
162
- }
163
-
164
- // Sanitize plugin options
165
- $.each(PLUGINS, function() {
166
- if(this.sanitize) { this.sanitize(opts); }
167
- });
168
-
169
- return opts;
170
- }
171
-
172
- /*
173
- * Core plugin implementation
174
- */
175
- function QTip(target, options, id, attr)
176
- {
177
- // Declare this reference
178
- var self = this,
179
- docBody = document.body,
180
- tooltipID = uitooltip + '-' + id,
181
- isPositioning = 0,
182
- isDrawing = 0,
183
- tooltip = $(),
184
- namespace = '.qtip-' + id,
185
- elements, cache;
186
-
187
- // Setup class attributes
188
- self.id = id;
189
- self.rendered = FALSE;
190
- self.destroyed = FALSE;
191
- self.elements = elements = { target: target };
192
- self.timers = { img: {} };
193
- self.options = options;
194
- self.checks = {};
195
- self.plugins = {};
196
- self.cache = cache = {
197
- event: {},
198
- target: $(),
199
- disabled: FALSE,
200
- attr: attr,
201
- onTarget: FALSE,
202
- lastClass: ''
203
- };
204
-
205
- /*
206
- * Private core functions
207
- */
208
- function convertNotation(notation)
209
- {
210
- var i = 0, obj, option = options,
211
-
212
- // Split notation into array
213
- levels = notation.split('.');
214
-
215
- // Loop through
216
- while( option = option[ levels[i++] ] ) {
217
- if(i < levels.length) { obj = option; }
218
- }
219
-
220
- return [obj || options, levels.pop()];
221
- }
222
-
223
- function setWidget() {
224
- var on = options.style.widget;
225
-
226
- tooltip.toggleClass(widget, on).toggleClass(defaultClass, options.style.def && !on);
227
- elements.content.toggleClass(widget+'-content', on);
228
-
229
- if(elements.titlebar){
230
- elements.titlebar.toggleClass(widget+'-header', on);
231
- }
232
- if(elements.button){
233
- elements.button.toggleClass(uitooltip+'-icon', !on);
234
- }
235
- }
236
-
237
- function removeTitle(reposition)
238
- {
239
- if(elements.title) {
240
- elements.titlebar.remove();
241
- elements.titlebar = elements.title = elements.button = NULL;
242
-
243
- // Reposition if enabled
244
- if(reposition !== FALSE) { self.reposition(); }
245
- }
246
- }
247
-
248
- function createButton()
249
- {
250
- var button = options.content.title.button,
251
- isString = typeof button === 'string',
252
- close = isString ? button : 'Close tooltip';
253
-
254
- if(elements.button) { elements.button.remove(); }
255
-
256
- // Use custom button if one was supplied by user, else use default
257
- if(button.jquery) {
258
- elements.button = button;
259
- }
260
- else {
261
- elements.button = $('<a />', {
262
- 'class': 'ui-state-default ui-tooltip-close ' + (options.style.widget ? '' : uitooltip+'-icon'),
263
- 'title': close,
264
- 'aria-label': close
265
- })
266
- .prepend(
267
- $('<span />', {
268
- 'class': 'ui-icon ui-icon-close',
269
- 'html': '&times;'
270
- })
271
- );
272
- }
273
-
274
- // Create button and setup attributes
275
- elements.button.appendTo(elements.titlebar)
276
- .attr('role', 'button')
277
- .click(function(event) {
278
- if(!tooltip.hasClass(disabled)) { self.hide(event); }
279
- return FALSE;
280
- });
281
-
282
- // Redraw the tooltip when we're done
283
- self.redraw();
284
- }
285
-
286
- function createTitle()
287
- {
288
- var id = tooltipID+'-title';
289
-
290
- // Destroy previous title element, if present
291
- if(elements.titlebar) { removeTitle(); }
292
-
293
- // Create title bar and title elements
294
- elements.titlebar = $('<div />', {
295
- 'class': uitooltip + '-titlebar ' + (options.style.widget ? 'ui-widget-header' : '')
296
- })
297
- .append(
298
- elements.title = $('<div />', {
299
- 'id': id,
300
- 'class': uitooltip + '-title',
301
- 'aria-atomic': TRUE
302
- })
303
- )
304
- .insertBefore(elements.content)
305
-
306
- // Button-specific events
307
- .delegate('.ui-tooltip-close', 'mousedown keydown mouseup keyup mouseout', function(event) {
308
- $(this).toggleClass('ui-state-active ui-state-focus', event.type.substr(-4) === 'down');
309
- })
310
- .delegate('.ui-tooltip-close', 'mouseover mouseout', function(event){
311
- $(this).toggleClass('ui-state-hover', event.type === 'mouseover');
312
- });
313
-
314
- // Create button if enabled
315
- if(options.content.title.button) { createButton(); }
316
-
317
- // Redraw the tooltip dimensions if it's rendered
318
- else if(self.rendered){ self.redraw(); }
319
- }
320
-
321
- function updateButton(button)
322
- {
323
- var elem = elements.button,
324
- title = elements.title;
325
-
326
- // Make sure tooltip is rendered and if not, return
327
- if(!self.rendered) { return FALSE; }
328
-
329
- if(!button) {
330
- elem.remove();
331
- }
332
- else {
333
- if(!title) {
334
- createTitle();
335
- }
336
- createButton();
337
- }
338
- }
339
-
340
- function updateTitle(content, reposition)
341
- {
342
- var elem = elements.title;
343
-
344
- // Make sure tooltip is rendered and if not, return
345
- if(!self.rendered || !content) { return FALSE; }
346
-
347
- // Use function to parse content
348
- if($.isFunction(content)) {
349
- content = content.call(target, cache.event, self);
350
- }
351
-
352
- // Remove title if callback returns false or null/undefined (but not '')
353
- if(content === FALSE || (!content && content !== '')) { return removeTitle(FALSE); }
354
-
355
- // Append new content if its a DOM array and show it if hidden
356
- else if(content.jquery && content.length > 0) {
357
- elem.empty().append(content.css({ display: 'block' }));
358
- }
359
-
360
- // Content is a regular string, insert the new content
361
- else { elem.html(content); }
362
-
363
- // Redraw and reposition
364
- self.redraw();
365
- if(reposition !== FALSE && self.rendered && tooltip[0].offsetWidth > 0) {
366
- self.reposition(cache.event);
367
- }
368
- }
369
-
370
- function updateContent(content, reposition)
371
- {
372
- var elem = elements.content;
373
-
374
- // Make sure tooltip is rendered and content is defined. If not return
375
- if(!self.rendered || !content) { return FALSE; }
376
-
377
- // Use function to parse content
378
- if($.isFunction(content)) {
379
- content = content.call(target, cache.event, self) || '';
380
- }
381
-
382
- // Append new content if its a DOM array and show it if hidden
383
- if(content.jquery && content.length > 0) {
384
- elem.empty().append(content.css({ display: 'block' }));
385
- }
386
-
387
- // Content is a regular string, insert the new content
388
- else { elem.html(content); }
389
-
390
- // Image detection
391
- function detectImages(next) {
392
- var images, srcs = {};
393
-
394
- function imageLoad(image) {
395
- // Clear src from object and any timers and events associated with the image
396
- if(image) {
397
- delete srcs[image.src];
398
- clearTimeout(self.timers.img[image.src]);
399
- $(image).unbind(namespace);
400
- }
401
-
402
- // If queue is empty after image removal, update tooltip and continue the queue
403
- if($.isEmptyObject(srcs)) {
404
- self.redraw();
405
- if(reposition !== FALSE) {
406
- self.reposition(cache.event);
407
- }
408
-
409
- next();
410
- }
411
- }
412
-
413
- // Find all content images without dimensions, and if no images were found, continue
414
- if((images = elem.find('img[src]:not([height]):not([width])')).length === 0) { return imageLoad(); }
415
-
416
- // Apply timer to each image to poll for dimensions
417
- images.each(function(i, elem) {
418
- // Skip if the src is already present
419
- if(srcs[elem.src] !== undefined) { return; }
420
-
421
- // Keep track of how many times we poll for image dimensions.
422
- // If it doesn't return in a reasonable amount of time, it's better
423
- // to display the tooltip, rather than hold up the queue.
424
- var iterations = 0, maxIterations = 3;
425
-
426
- (function timer(){
427
- // When the dimensions are found, remove the image from the queue
428
- if(elem.height || elem.width || (iterations > maxIterations)) { return imageLoad(elem); }
429
-
430
- // Increase iterations and restart timer
431
- iterations += 1;
432
- self.timers.img[elem.src] = setTimeout(timer, 700);
433
- }());
434
-
435
- // Also apply regular load/error event handlers
436
- $(elem).bind('error'+namespace+' load'+namespace, function(){ imageLoad(this); });
437
-
438
- // Store the src and element in our object
439
- srcs[elem.src] = elem;
440
- });
441
- }
442
-
443
- /*
444
- * If we're still rendering... insert into 'fx' queue our image dimension
445
- * checker which will halt the showing of the tooltip until image dimensions
446
- * can be detected properly.
447
- */
448
- if(self.rendered < 0) { tooltip.queue('fx', detectImages); }
449
-
450
- // We're fully rendered, so reset isDrawing flag and proceed without queue delay
451
- else { isDrawing = 0; detectImages($.noop); }
452
-
453
- return self;
454
- }
455
-
456
- function assignEvents()
457
- {
458
- var posOptions = options.position,
459
- targets = {
460
- show: options.show.target,
461
- hide: options.hide.target,
462
- viewport: $(posOptions.viewport),
463
- document: $(document),
464
- body: $(document.body),
465
- window: $(window)
466
- },
467
- events = {
468
- show: $.trim('' + options.show.event).split(' '),
469
- hide: $.trim('' + options.hide.event).split(' ')
470
- },
471
- IE6 = $.browser.msie && parseInt($.browser.version, 10) === 6;
472
-
473
- // Define show event method
474
- function showMethod(event)
475
- {
476
- if(tooltip.hasClass(disabled)) { return FALSE; }
477
-
478
- // Clear hide timers
479
- clearTimeout(self.timers.show);
480
- clearTimeout(self.timers.hide);
481
-
482
- // Start show timer
483
- var callback = function(){ self.toggle(TRUE, event); };
484
- if(options.show.delay > 0) {
485
- self.timers.show = setTimeout(callback, options.show.delay);
486
- }
487
- else{ callback(); }
488
- }
489
-
490
- // Define hide method
491
- function hideMethod(event)
492
- {
493
- if(tooltip.hasClass(disabled) || isPositioning || isDrawing) { return FALSE; }
494
-
495
- // Check if new target was actually the tooltip element
496
- var relatedTarget = $(event.relatedTarget || event.target),
497
- ontoTooltip = relatedTarget.closest(selector)[0] === tooltip[0],
498
- ontoTarget = relatedTarget[0] === targets.show[0];
499
-
500
- // Clear timers and stop animation queue
501
- clearTimeout(self.timers.show);
502
- clearTimeout(self.timers.hide);
503
-
504
- // Prevent hiding if tooltip is fixed and event target is the tooltip. Or if mouse positioning is enabled and cursor momentarily overlaps
505
- if((posOptions.target === 'mouse' && ontoTooltip) || (options.hide.fixed && ((/mouse(out|leave|move)/).test(event.type) && (ontoTooltip || ontoTarget)))) {
506
- try { event.preventDefault(); event.stopImmediatePropagation(); } catch(e) {} return;
507
- }
508
-
509
- // If tooltip has displayed, start hide timer
510
- if(options.hide.delay > 0) {
511
- self.timers.hide = setTimeout(function(){ self.hide(event); }, options.hide.delay);
512
- }
513
- else{ self.hide(event); }
514
- }
515
-
516
- // Define inactive method
517
- function inactiveMethod(event)
518
- {
519
- if(tooltip.hasClass(disabled)) { return FALSE; }
520
-
521
- // Clear timer
522
- clearTimeout(self.timers.inactive);
523
- self.timers.inactive = setTimeout(function(){ self.hide(event); }, options.hide.inactive);
524
- }
525
-
526
- function repositionMethod(event) {
527
- if(self.rendered && tooltip[0].offsetWidth > 0) { self.reposition(event); }
528
- }
529
-
530
- // On mouseenter/mouseleave...
531
- tooltip.bind('mouseenter'+namespace+' mouseleave'+namespace, function(event) {
532
- var state = event.type === 'mouseenter';
533
-
534
- // Focus the tooltip on mouseenter (z-index stacking)
535
- if(state) { self.focus(event); }
536
-
537
- // Add hover class
538
- tooltip.toggleClass(hoverClass, state);
539
- });
540
-
541
- // If using mouseout/mouseleave as a hide event...
542
- if(/mouse(out|leave)/i.test(options.hide.event)) {
543
- // Hide tooltips when leaving current window/frame (but not select/option elements)
544
- if(options.hide.leave === 'window') {
545
- targets.window.bind('mouseleave'+namespace+' blur'+namespace, function(event) {
546
- if(!/select|option/.test(event.target.nodeName) && !event.relatedTarget) { self.hide(event); }
547
- });
548
- }
549
- }
550
-
551
- // Enable hide.fixed
552
- if(options.hide.fixed) {
553
- // Add tooltip as a hide target
554
- targets.hide = targets.hide.add(tooltip);
555
-
556
- // Clear hide timer on tooltip hover to prevent it from closing
557
- tooltip.bind('mouseover'+namespace, function() {
558
- if(!tooltip.hasClass(disabled)) { clearTimeout(self.timers.hide); }
559
- });
560
- }
561
-
562
- /*
563
- * Make sure hoverIntent functions properly by using mouseleave to clear show timer if
564
- * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
565
- */
566
- else if(/mouse(over|enter)/i.test(options.show.event)) {
567
- targets.hide.bind('mouseleave'+namespace, function(event) {
568
- clearTimeout(self.timers.show);
569
- });
570
- }
571
-
572
- // Hide tooltip on document mousedown if unfocus events are enabled
573
- if(('' + options.hide.event).indexOf('unfocus') > -1) {
574
- posOptions.container.closest('html').bind('mousedown'+namespace, function(event) {
575
- var elem = $(event.target),
576
- enabled = self.rendered && !tooltip.hasClass(disabled) && tooltip[0].offsetWidth > 0,
577
- isAncestor = elem.parents(selector).filter(tooltip[0]).length > 0;
578
-
579
- if(elem[0] !== target[0] && elem[0] !== tooltip[0] && !isAncestor &&
580
- !target.has(elem[0]).length && !elem.attr('disabled')
581
- ) {
582
- self.hide(event);
583
- }
584
- });
585
- }
586
-
587
- // Check if the tooltip hides when inactive
588
- if('number' === typeof options.hide.inactive) {
589
- // Bind inactive method to target as a custom event
590
- targets.show.bind('qtip-'+id+'-inactive', inactiveMethod);
591
-
592
- // Define events which reset the 'inactive' event handler
593
- $.each(QTIP.inactiveEvents, function(index, type){
594
- targets.hide.add(elements.tooltip).bind(type+namespace+'-inactive', inactiveMethod);
595
- });
596
- }
597
-
598
- // Apply hide events
599
- $.each(events.hide, function(index, type) {
600
- var showIndex = $.inArray(type, events.show),
601
- targetHide = $(targets.hide);
602
-
603
- // Both events and targets are identical, apply events using a toggle
604
- if((showIndex > -1 && targetHide.add(targets.show).length === targetHide.length) || type === 'unfocus')
605
- {
606
- targets.show.bind(type+namespace, function(event) {
607
- if(tooltip[0].offsetWidth > 0) { hideMethod(event); }
608
- else { showMethod(event); }
609
- });
610
-
611
- // Don't bind the event again
612
- delete events.show[ showIndex ];
613
- }
614
-
615
- // Events are not identical, bind normally
616
- else { targets.hide.bind(type+namespace, hideMethod); }
617
- });
618
-
619
- // Apply show events
620
- $.each(events.show, function(index, type) {
621
- targets.show.bind(type+namespace, showMethod);
622
- });
623
-
624
- // Check if the tooltip hides when mouse is moved a certain distance
625
- if('number' === typeof options.hide.distance) {
626
- // Bind mousemove to target to detect distance difference
627
- targets.show.add(tooltip).bind('mousemove'+namespace, function(event) {
628
- var origin = cache.origin || {},
629
- limit = options.hide.distance,
630
- abs = Math.abs;
631
-
632
- // Check if the movement has gone beyond the limit, and hide it if so
633
- if(abs(event.pageX - origin.pageX) >= limit || abs(event.pageY - origin.pageY) >= limit) {
634
- self.hide(event);
635
- }
636
- });
637
- }
638
-
639
- // Mouse positioning events
640
- if(posOptions.target === 'mouse') {
641
- // Cache mousemove coords on show targets
642
- targets.show.bind('mousemove'+namespace, function(event) {
643
- MOUSE = { pageX: event.pageX, pageY: event.pageY, type: 'mousemove' };
644
- });
645
-
646
- // If mouse adjustment is on...
647
- if(posOptions.adjust.mouse) {
648
- // Apply a mouseleave event so we don't get problems with overlapping
649
- if(options.hide.event) {
650
- // Hide when we leave the tooltip and not onto the show target
651
- tooltip.bind('mouseleave'+namespace, function(event) {
652
- if((event.relatedTarget || event.target) !== targets.show[0]) { self.hide(event); }
653
- });
654
-
655
- // Track if we're on the target or not
656
- elements.target.bind('mouseenter'+namespace+' mouseleave'+namespace, function(event) {
657
- cache.onTarget = event.type === 'mouseenter';
658
- });
659
- }
660
-
661
- // Update tooltip position on mousemove
662
- targets.document.bind('mousemove'+namespace, function(event) {
663
- // Update the tooltip position only if the tooltip is visible and adjustment is enabled
664
- if(self.rendered && cache.onTarget && !tooltip.hasClass(disabled) && tooltip[0].offsetWidth > 0) {
665
- self.reposition(event || MOUSE);
666
- }
667
- });
668
- }
669
- }
670
-
671
- // Adjust positions of the tooltip on window resize if enabled
672
- if(posOptions.adjust.resize || targets.viewport.length) {
673
- ($.event.special.resize ? targets.viewport : targets.window).bind('resize'+namespace, repositionMethod);
674
- }
675
-
676
- // Adjust tooltip position on scroll if screen adjustment is enabled
677
- if(targets.viewport.length || (IE6 && tooltip.css('position') === 'fixed')) {
678
- targets.viewport.bind('scroll'+namespace, repositionMethod);
679
- }
680
- }
681
-
682
- function unassignEvents()
683
- {
684
- var targets = [
685
- options.show.target[0],
686
- options.hide.target[0],
687
- self.rendered && elements.tooltip[0],
688
- options.position.container[0],
689
- options.position.viewport[0],
690
- window,
691
- document
692
- ];
693
-
694
- // Check if tooltip is rendered
695
- if(self.rendered) {
696
- $([]).pushStack( $.grep(targets, function(i){ return typeof i === 'object'; }) ).unbind(namespace);
697
- }
698
-
699
- // Tooltip isn't yet rendered, remove render event
700
- else { options.show.target.unbind(namespace+'-create'); }
701
- }
702
-
703
- // Setup builtin .set() option checks
704
- self.checks.builtin = {
705
- // Core checks
706
- '^id$': function(obj, o, v) {
707
- var id = v === TRUE ? QTIP.nextid : v,
708
- tooltipID = uitooltip + '-' + id;
709
-
710
- if(id !== FALSE && id.length > 0 && !$('#'+tooltipID).length) {
711
- tooltip[0].id = tooltipID;
712
- elements.content[0].id = tooltipID + '-content';
713
- elements.title[0].id = tooltipID + '-title';
714
- }
715
- },
716
-
717
- // Content checks
718
- '^content.text$': function(obj, o, v){ updateContent(v); },
719
- '^content.title.text$': function(obj, o, v) {
720
- // Remove title if content is null
721
- if(!v) { return removeTitle(); }
722
-
723
- // If title isn't already created, create it now and update
724
- if(!elements.title && v) { createTitle(); }
725
- updateTitle(v);
726
- },
727
- '^content.title.button$': function(obj, o, v){ updateButton(v); },
728
-
729
- // Position checks
730
- '^position.(my|at)$': function(obj, o, v){
731
- // Parse new corner value into Corner objecct
732
- if('string' === typeof v) {
733
- obj[o] = new PLUGINS.Corner(v);
734
- }
735
- },
736
- '^position.container$': function(obj, o, v){
737
- if(self.rendered) { tooltip.appendTo(v); }
738
- },
739
-
740
- // Show checks
741
- '^show.ready$': function() {
742
- if(!self.rendered) { self.render(1); }
743
- else { self.toggle(TRUE); }
744
- },
745
-
746
- // Style checks
747
- '^style.classes$': function(obj, o, v) {
748
- tooltip.attr('class', uitooltip + ' qtip ui-helper-reset ' + v);
749
- },
750
- '^style.widget|content.title': setWidget,
751
-
752
- // Events check
753
- '^events.(render|show|move|hide|focus|blur)$': function(obj, o, v) {
754
- tooltip[($.isFunction(v) ? '' : 'un') + 'bind']('tooltip'+o, v);
755
- },
756
-
757
- // Properties which require event reassignment
758
- '^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)': function() {
759
- var posOptions = options.position;
760
-
761
- // Set tracking flag
762
- tooltip.attr('tracking', posOptions.target === 'mouse' && posOptions.adjust.mouse);
763
-
764
- // Reassign events
765
- unassignEvents(); assignEvents();
766
- }
767
- };
768
-
769
- /*
770
- * Public API methods
771
- */
772
- $.extend(self, {
773
- render: function(show)
774
- {
775
- if(self.rendered) { return self; } // If tooltip has already been rendered, exit
776
-
777
- var text = options.content.text,
778
- title = options.content.title.text,
779
- posOptions = options.position,
780
- callback = $.Event('tooltiprender');
781
-
782
- // Add ARIA attributes to target
783
- $.attr(target[0], 'aria-describedby', tooltipID);
784
-
785
- // Create tooltip element
786
- tooltip = elements.tooltip = $('<div/>', {
787
- 'id': tooltipID,
788
- 'class': uitooltip + ' qtip ui-helper-reset ' + defaultClass + ' ' + options.style.classes + ' '+ uitooltip + '-pos-' + options.position.my.abbrev(),
789
- 'width': options.style.width || '',
790
- 'height': options.style.height || '',
791
- 'tracking': posOptions.target === 'mouse' && posOptions.adjust.mouse,
792
-
793
- /* ARIA specific attributes */
794
- 'role': 'alert',
795
- 'aria-live': 'polite',
796
- 'aria-atomic': FALSE,
797
- 'aria-describedby': tooltipID + '-content',
798
- 'aria-hidden': TRUE
799
- })
800
- .toggleClass(disabled, cache.disabled)
801
- .data('qtip', self)
802
- .appendTo(options.position.container)
803
- .append(
804
- // Create content element
805
- elements.content = $('<div />', {
806
- 'class': uitooltip + '-content',
807
- 'id': tooltipID + '-content',
808
- 'aria-atomic': TRUE
809
- })
810
- );
811
-
812
- // Set rendered flag and prevent redundant redraw/reposition calls for now
813
- self.rendered = -1;
814
- isDrawing = 1; isPositioning = 1;
815
-
816
- // Create title...
817
- if(title) {
818
- createTitle();
819
-
820
- // Update title only if its not a callback (called in toggle if so)
821
- if(!$.isFunction(title)) { updateTitle(title, FALSE); }
822
- }
823
-
824
- // Set proper rendered flag and update content if not a callback function (called in toggle)
825
- if(!$.isFunction(text)) { updateContent(text, FALSE); }
826
- self.rendered = TRUE;
827
-
828
- // Setup widget classes
829
- setWidget();
830
-
831
- // Assign passed event callbacks (before plugins!)
832
- $.each(options.events, function(name, callback) {
833
- if($.isFunction(callback)) {
834
- tooltip.bind(name === 'toggle' ? 'tooltipshow tooltiphide' : 'tooltip'+name, callback);
835
- }
836
- });
837
-
838
- // Initialize 'render' plugins
839
- $.each(PLUGINS, function() {
840
- if(this.initialize === 'render') { this(self); }
841
- });
842
-
843
- // Assign events
844
- assignEvents();
845
-
846
- /* Queue this part of the render process in our fx queue so we can
847
- * load images before the tooltip renders fully.
848
- *
849
- * See: updateContent method
850
- */
851
- tooltip.queue('fx', function(next) {
852
- // Trigger tooltiprender event and pass original triggering event as original
853
- callback.originalEvent = cache.event;
854
- tooltip.trigger(callback, [self]);
855
-
856
- // Reset flags
857
- isDrawing = 0; isPositioning = 0;
858
-
859
- // Redraw the tooltip manually now we're fully rendered
860
- self.redraw();
861
-
862
- // Show tooltip if needed
863
- if(options.show.ready || show) {
864
- self.toggle(TRUE, cache.event, FALSE);
865
- }
866
-
867
- next(); // Move on to next method in queue
868
- });
869
-
870
- return self;
871
- },
872
-
873
- get: function(notation)
874
- {
875
- var result, o;
876
-
877
- switch(notation.toLowerCase())
878
- {
879
- case 'dimensions':
880
- result = {
881
- height: tooltip.outerHeight(), width: tooltip.outerWidth()
882
- };
883
- break;
884
-
885
- case 'offset':
886
- result = PLUGINS.offset(tooltip, options.position.container);
887
- break;
888
-
889
- default:
890
- o = convertNotation(notation.toLowerCase());
891
- result = o[0][ o[1] ];
892
- result = result.precedance ? result.string() : result;
893
- break;
894
- }
895
-
896
- return result;
897
- },
898
-
899
- set: function(option, value)
900
- {
901
- var rmove = /^position\.(my|at|adjust|target|container)|style|content|show\.ready/i,
902
- rdraw = /^content\.(title|attr)|style/i,
903
- reposition = FALSE,
904
- redraw = FALSE,
905
- checks = self.checks,
906
- name;
907
-
908
- function callback(notation, args) {
909
- var category, rule, match;
910
-
911
- for(category in checks) {
912
- for(rule in checks[category]) {
913
- if(match = (new RegExp(rule, 'i')).exec(notation)) {
914
- args.push(match);
915
- checks[category][rule].apply(self, args);
916
- }
917
- }
918
- }
919
- }
920
-
921
- // Convert singular option/value pair into object form
922
- if('string' === typeof option) {
923
- name = option; option = {}; option[name] = value;
924
- }
925
- else { option = $.extend(TRUE, {}, option); }
926
-
927
- // Set all of the defined options to their new values
928
- $.each(option, function(notation, value) {
929
- var obj = convertNotation( notation.toLowerCase() ), previous;
930
-
931
- // Set new obj value
932
- previous = obj[0][ obj[1] ];
933
- obj[0][ obj[1] ] = 'object' === typeof value && value.nodeType ? $(value) : value;
934
-
935
- // Set the new params for the callback
936
- option[notation] = [obj[0], obj[1], value, previous];
937
-
938
- // Also check if we need to reposition / redraw
939
- reposition = rmove.test(notation) || reposition;
940
- redraw = rdraw.test(notation) || redraw;
941
- });
942
-
943
- // Re-sanitize options
944
- sanitizeOptions(options);
945
-
946
- /*
947
- * Execute any valid callbacks for the set options
948
- * Also set isPositioning/isDrawing so we don't get loads of redundant repositioning
949
- * and redraw calls.
950
- */
951
- isPositioning = isDrawing = 1; $.each(option, callback); isPositioning = isDrawing = 0;
952
-
953
- // Update position / redraw if needed
954
- if(self.rendered && tooltip[0].offsetWidth > 0) {
955
- if(reposition) {
956
- self.reposition( options.position.target === 'mouse' ? NULL : cache.event );
957
- }
958
- if(redraw) { self.redraw(); }
959
- }
960
-
961
- return self;
962
- },
963
-
964
- toggle: function(state, event)
965
- {
966
- // Render the tooltip if showing and it isn't already
967
- if(!self.rendered) { return state ? self.render(1) : self; }
968
-
969
- var type = state ? 'show' : 'hide',
970
- opts = options[type],
971
- otherOpts = options[ !state ? 'show' : 'hide' ],
972
- posOptions = options.position,
973
- contentOptions = options.content,
974
- visible = tooltip[0].offsetWidth > 0,
975
- animate = state || opts.target.length === 1,
976
- sameTarget = !event || opts.target.length < 2 || cache.target[0] === event.target,
977
- delay, callback;
978
-
979
- // Detect state if valid one isn't provided
980
- if((typeof state).search('boolean|number')) { state = !visible; }
981
-
982
- // Return if element is already in correct state
983
- if(!tooltip.is(':animated') && visible === state && sameTarget) { return self; }
984
-
985
- // Try to prevent flickering when tooltip overlaps show element
986
- if(event) {
987
- if((/over|enter/).test(event.type) && (/out|leave/).test(cache.event.type) &&
988
- options.show.target.add(event.target).length === options.show.target.length &&
989
- tooltip.has(event.relatedTarget).length) {
990
- return self;
991
- }
992
-
993
- // Cache event
994
- cache.event = $.extend({}, event);
995
- }
996
-
997
- // Call API methods
998
- callback = $.Event('tooltip'+type);
999
- callback.originalEvent = event ? cache.event : NULL;
1000
- tooltip.trigger(callback, [self, 90]);
1001
- if(callback.isDefaultPrevented()){ return self; }
1002
-
1003
- // Set ARIA hidden status attribute
1004
- $.attr(tooltip[0], 'aria-hidden', !!!state);
1005
-
1006
- // Execute state specific properties
1007
- if(state) {
1008
- // Store show origin coordinates
1009
- cache.origin = $.extend({}, MOUSE);
1010
-
1011
- // Focus the tooltip
1012
- self.focus(event);
1013
-
1014
- // Update tooltip content & title if it's a dynamic function
1015
- if($.isFunction(contentOptions.text)) { updateContent(contentOptions.text, FALSE); }
1016
- if($.isFunction(contentOptions.title.text)) { updateTitle(contentOptions.title.text, FALSE); }
1017
-
1018
- // Cache mousemove events for positioning purposes (if not already tracking)
1019
- if(!trackingBound && posOptions.target === 'mouse' && posOptions.adjust.mouse) {
1020
- $(document).bind('mousemove.qtip', function(event) {
1021
- MOUSE = { pageX: event.pageX, pageY: event.pageY, type: 'mousemove' };
1022
- });
1023
- trackingBound = TRUE;
1024
- }
1025
-
1026
- // Update the tooltip position
1027
- self.reposition(event, arguments[2]);
1028
-
1029
- // Hide other tooltips if tooltip is solo, using it as the context
1030
- if((callback.solo = !!opts.solo)) { $(selector, opts.solo).not(tooltip).qtip('hide', callback); }
1031
- }
1032
- else {
1033
- // Clear show timer if we're hiding
1034
- clearTimeout(self.timers.show);
1035
-
1036
- // Remove cached origin on hide
1037
- delete cache.origin;
1038
-
1039
- // Remove mouse tracking event if not needed (all tracking qTips are hidden)
1040
- if(trackingBound && !$(selector+'[tracking="true"]:visible', opts.solo).not(tooltip).length) {
1041
- $(document).unbind('mousemove.qtip');
1042
- trackingBound = FALSE;
1043
- }
1044
-
1045
- // Blur the tooltip
1046
- self.blur(event);
1047
- }
1048
-
1049
- // Define post-animation, state specific properties
1050
- function after() {
1051
- if(state) {
1052
- // Prevent antialias from disappearing in IE by removing filter
1053
- if($.browser.msie) { tooltip[0].style.removeAttribute('filter'); }
1054
-
1055
- // Remove overflow setting to prevent tip bugs
1056
- tooltip.css('overflow', '');
1057
-
1058
- // Autofocus elements if enabled
1059
- if('string' === typeof opts.autofocus) {
1060
- $(opts.autofocus, tooltip).focus();
1061
- }
1062
-
1063
- // If set, hide tooltip when inactive for delay period
1064
- opts.target.trigger('qtip-'+id+'-inactive');
1065
- }
1066
- else {
1067
- // Reset CSS states
1068
- tooltip.css({
1069
- display: '',
1070
- visibility: '',
1071
- opacity: '',
1072
- left: '',
1073
- top: ''
1074
- });
1075
- }
1076
-
1077
- // Call API method
1078
- callback = $.Event('tooltip'+(state ? 'visible' : 'hidden'));
1079
- callback.originalEvent = event ? cache.event : NULL;
1080
- tooltip.trigger(callback, [self]);
1081
- }
1082
-
1083
- // If no effect type is supplied, use a simple toggle
1084
- if(opts.effect === FALSE || animate === FALSE) {
1085
- tooltip[ type ]();
1086
- after.call(tooltip);
1087
- }
1088
-
1089
- // Use custom function if provided
1090
- else if($.isFunction(opts.effect)) {
1091
- tooltip.stop(1, 1);
1092
- opts.effect.call(tooltip, self);
1093
- tooltip.queue('fx', function(n){ after(); n(); });
1094
- }
1095
-
1096
- // Use basic fade function by default
1097
- else { tooltip.fadeTo(90, state ? 1 : 0, after); }
1098
-
1099
- // If inactive hide method is set, active it
1100
- if(state) { opts.target.trigger('qtip-'+id+'-inactive'); }
1101
-
1102
- return self;
1103
- },
1104
-
1105
- show: function(event){ return self.toggle(TRUE, event); },
1106
-
1107
- hide: function(event){ return self.toggle(FALSE, event); },
1108
-
1109
- focus: function(event)
1110
- {
1111
- if(!self.rendered) { return self; }
1112
-
1113
- var qtips = $(selector),
1114
- curIndex = parseInt(tooltip[0].style.zIndex, 10),
1115
- newIndex = QTIP.zindex + qtips.length,
1116
- cachedEvent = $.extend({}, event),
1117
- focusedElem, callback;
1118
-
1119
- // Only update the z-index if it has changed and tooltip is not already focused
1120
- if(!tooltip.hasClass(focusClass))
1121
- {
1122
- // Call API method
1123
- callback = $.Event('tooltipfocus');
1124
- callback.originalEvent = cachedEvent;
1125
- tooltip.trigger(callback, [self, newIndex]);
1126
-
1127
- // If default action wasn't prevented...
1128
- if(!callback.isDefaultPrevented()) {
1129
- // Only update z-index's if they've changed
1130
- if(curIndex !== newIndex) {
1131
- // Reduce our z-index's and keep them properly ordered
1132
- qtips.each(function() {
1133
- if(this.style.zIndex > curIndex) {
1134
- this.style.zIndex = this.style.zIndex - 1;
1135
- }
1136
- });
1137
-
1138
- // Fire blur event for focused tooltip
1139
- qtips.filter('.' + focusClass).qtip('blur', cachedEvent);
1140
- }
1141
-
1142
- // Set the new z-index
1143
- tooltip.addClass(focusClass)[0].style.zIndex = newIndex;
1144
- }
1145
- }
1146
-
1147
- return self;
1148
- },
1149
-
1150
- blur: function(event) {
1151
- var cachedEvent = $.extend({}, event),
1152
- callback;
1153
-
1154
- // Set focused status to FALSE
1155
- tooltip.removeClass(focusClass);
1156
-
1157
- // Trigger blur event
1158
- callback = $.Event('tooltipblur');
1159
- callback.originalEvent = cachedEvent;
1160
- tooltip.trigger(callback, [self]);
1161
-
1162
- return self;
1163
- },
1164
-
1165
- reposition: function(event, effect)
1166
- {
1167
- if(!self.rendered || isPositioning) { return self; }
1168
-
1169
- // Set positioning flag
1170
- isPositioning = 1;
1171
-
1172
- var target = options.position.target,
1173
- posOptions = options.position,
1174
- my = posOptions.my,
1175
- at = posOptions.at,
1176
- adjust = posOptions.adjust,
1177
- method = adjust.method.split(' '),
1178
- elemWidth = tooltip.outerWidth(),
1179
- elemHeight = tooltip.outerHeight(),
1180
- targetWidth = 0,
1181
- targetHeight = 0,
1182
- callback = $.Event('tooltipmove'),
1183
- fixed = tooltip.css('position') === 'fixed',
1184
- viewport = posOptions.viewport,
1185
- position = { left: 0, top: 0 },
1186
- container = posOptions.container,
1187
- visible = tooltip[0].offsetWidth > 0,
1188
- adjusted, offset, win;
1189
-
1190
- // Check if absolute position was passed
1191
- if($.isArray(target) && target.length === 2) {
1192
- // Force left top and set position
1193
- at = { x: LEFT, y: TOP };
1194
- position = { left: target[0], top: target[1] };
1195
- }
1196
-
1197
- // Check if mouse was the target
1198
- else if(target === 'mouse' && ((event && event.pageX) || cache.event.pageX)) {
1199
- // Force left top to allow flipping
1200
- at = { x: LEFT, y: TOP };
1201
-
1202
- // Use cached event if one isn't available for positioning
1203
- event = (event && (event.type === 'resize' || event.type === 'scroll') ? cache.event :
1204
- event && event.pageX && event.type === 'mousemove' ? event :
1205
- MOUSE && MOUSE.pageX && (adjust.mouse || !event || !event.pageX) ? { pageX: MOUSE.pageX, pageY: MOUSE.pageY } :
1206
- !adjust.mouse && cache.origin && cache.origin.pageX && options.show.distance ? cache.origin :
1207
- event) || event || cache.event || MOUSE || {};
1208
-
1209
- // Use event coordinates for position
1210
- position = { top: event.pageY, left: event.pageX };
1211
- }
1212
-
1213
- // Target wasn't mouse or absolute...
1214
- else {
1215
- // Check if event targetting is being used
1216
- if(target === 'event' && event && event.target && event.type !== 'scroll' && event.type !== 'resize') {
1217
- cache.target = $(event.target);
1218
- }
1219
- else if(target !== 'event'){
1220
- cache.target = $(target.jquery ? target : elements.target);
1221
- }
1222
- target = cache.target;
1223
-
1224
- // Parse the target into a jQuery object and make sure there's an element present
1225
- target = $(target).eq(0);
1226
- if(target.length === 0) { return self; }
1227
-
1228
- // Check if window or document is the target
1229
- else if(target[0] === document || target[0] === window) {
1230
- targetWidth = PLUGINS.iOS ? window.innerWidth : target.width();
1231
- targetHeight = PLUGINS.iOS ? window.innerHeight : target.height();
1232
-
1233
- if(target[0] === window) {
1234
- position = {
1235
- top: (viewport || target).scrollTop(),
1236
- left: (viewport || target).scrollLeft()
1237
- };
1238
- }
1239
- }
1240
-
1241
- // Use Imagemap/SVG plugins if needed
1242
- else if(PLUGINS.imagemap && target.is('area')) {
1243
- adjusted = PLUGINS.imagemap(self, target, at, PLUGINS.viewport ? method : FALSE);
1244
- }
1245
- else if(PLUGINS.svg && typeof target[0].xmlbase === 'string') {
1246
- adjusted = PLUGINS.svg(self, target, at, PLUGINS.viewport ? method : FALSE);
1247
- }
1248
-
1249
- else {
1250
- targetWidth = target.outerWidth();
1251
- targetHeight = target.outerHeight();
1252
-
1253
- position = PLUGINS.offset(target, container);
1254
- }
1255
-
1256
- // Parse returned plugin values into proper variables
1257
- if(adjusted) {
1258
- targetWidth = adjusted.width;
1259
- targetHeight = adjusted.height;
1260
- offset = adjusted.offset;
1261
- position = adjusted.position;
1262
- }
1263
-
1264
- // Adjust for position.fixed tooltips (and also iOS scroll bug in v3.2-4.0 & v4.3-4.3.2)
1265
- if((PLUGINS.iOS > 3.1 && PLUGINS.iOS < 4.1) ||
1266
- (PLUGINS.iOS >= 4.3 && PLUGINS.iOS < 4.33) ||
1267
- (!PLUGINS.iOS && fixed)
1268
- ){
1269
- win = $(window);
1270
- position.left -= win.scrollLeft();
1271
- position.top -= win.scrollTop();
1272
- }
1273
-
1274
- // Adjust position relative to target
1275
- position.left += at.x === RIGHT ? targetWidth : at.x === CENTER ? targetWidth / 2 : 0;
1276
- position.top += at.y === BOTTOM ? targetHeight : at.y === CENTER ? targetHeight / 2 : 0;
1277
- }
1278
-
1279
- // Adjust position relative to tooltip
1280
- position.left += adjust.x + (my.x === RIGHT ? -elemWidth : my.x === CENTER ? -elemWidth / 2 : 0);
1281
- position.top += adjust.y + (my.y === BOTTOM ? -elemHeight : my.y === CENTER ? -elemHeight / 2 : 0);
1282
-
1283
- // Use viewport adjustment plugin if enabled
1284
- if(PLUGINS.viewport) {
1285
- position.adjusted = PLUGINS.viewport(
1286
- self, position, posOptions, targetWidth, targetHeight, elemWidth, elemHeight
1287
- );
1288
-
1289
- // Apply offsets supplied by positioning plugin (if used)
1290
- if(offset && position.adjusted.left) { position.left += offset.left; }
1291
- if(offset && position.adjusted.top) { position.top += offset.top; }
1292
- }
1293
-
1294
- // Viewport adjustment is disabled, set values to zero
1295
- else { position.adjusted = { left: 0, top: 0 }; }
1296
-
1297
- // Call API method
1298
- callback.originalEvent = $.extend({}, event);
1299
- tooltip.trigger(callback, [self, position, viewport.elem || viewport]);
1300
- if(callback.isDefaultPrevented()){ return self; }
1301
- delete position.adjusted;
1302
-
1303
- // If effect is disabled, target it mouse, no animation is defined or positioning gives NaN out, set CSS directly
1304
- if(effect === FALSE || !visible || isNaN(position.left) || isNaN(position.top) || target === 'mouse' || !$.isFunction(posOptions.effect)) {
1305
- tooltip.css(position);
1306
- }
1307
-
1308
- // Use custom function if provided
1309
- else if($.isFunction(posOptions.effect)) {
1310
- posOptions.effect.call(tooltip, self, $.extend({}, position));
1311
- tooltip.queue(function(next) {
1312
- // Reset attributes to avoid cross-browser rendering bugs
1313
- $(this).css({ opacity: '', height: '' });
1314
- if($.browser.msie) { this.style.removeAttribute('filter'); }
1315
-
1316
- next();
1317
- });
1318
- }
1319
-
1320
- // Set positioning flag
1321
- isPositioning = 0;
1322
-
1323
- return self;
1324
- },
1325
-
1326
- // Max/min width simulator function for all browsers.. yeaaah!
1327
- redraw: function()
1328
- {
1329
- if(self.rendered < 1 || isDrawing) { return self; }
1330
-
1331
- var container = options.position.container,
1332
- perc, width, max, min;
1333
-
1334
- // Set drawing flag
1335
- isDrawing = 1;
1336
-
1337
- // If tooltip has a set height, just set it... like a boss!
1338
- if(options.style.height) { tooltip.css(HEIGHT, options.style.height); }
1339
-
1340
- // If tooltip has a set width, just set it... like a boss!
1341
- if(options.style.width) { tooltip.css(WIDTH, options.style.width); }
1342
-
1343
- // Otherwise simualte max/min width...
1344
- else {
1345
- // Reset width and add fluid class
1346
- tooltip.css(WIDTH, '').addClass(fluidClass);
1347
-
1348
- // Grab our tooltip width (add 1 so we don't get wrapping problems.. huzzah!)
1349
- width = tooltip.width() + 1;
1350
-
1351
- // Grab our max/min properties
1352
- max = tooltip.css('max-width') || '';
1353
- min = tooltip.css('min-width') || '';
1354
-
1355
- // Parse into proper pixel values
1356
- perc = (max + min).indexOf('%') > -1 ? container.width() / 100 : 0;
1357
- max = ((max.indexOf('%') > -1 ? perc : 1) * parseInt(max, 10)) || width;
1358
- min = ((min.indexOf('%') > -1 ? perc : 1) * parseInt(min, 10)) || 0;
1359
-
1360
- // Determine new dimension size based on max/min/current values
1361
- width = max + min ? Math.min(Math.max(width, min), max) : width;
1362
-
1363
- // Set the newly calculated width and remvoe fluid class
1364
- tooltip.css(WIDTH, Math.round(width)).removeClass(fluidClass);
1365
- }
1366
-
1367
- // Set drawing flag
1368
- isDrawing = 0;
1369
-
1370
- return self;
1371
- },
1372
-
1373
- disable: function(state)
1374
- {
1375
- if('boolean' !== typeof state) {
1376
- state = !(tooltip.hasClass(disabled) || cache.disabled);
1377
- }
1378
-
1379
- if(self.rendered) {
1380
- tooltip.toggleClass(disabled, state);
1381
- $.attr(tooltip[0], 'aria-disabled', state);
1382
- }
1383
- else {
1384
- cache.disabled = !!state;
1385
- }
1386
-
1387
- return self;
1388
- },
1389
-
1390
- enable: function() { return self.disable(FALSE); },
1391
-
1392
- destroy: function()
1393
- {
1394
- var t = target[0],
1395
- title = $.attr(t, oldtitle),
1396
- elemAPI = target.data('qtip');
1397
-
1398
- // Set flag the signify destroy is taking place to plugins
1399
- self.destroyed = TRUE;
1400
-
1401
- // Destroy tooltip and any associated plugins if rendered
1402
- if(self.rendered) {
1403
- tooltip.stop(1,0).remove();
1404
-
1405
- $.each(self.plugins, function() {
1406
- if(this.destroy) { this.destroy(); }
1407
- });
1408
- }
1409
-
1410
- // Clear timers and remove bound events
1411
- clearTimeout(self.timers.show);
1412
- clearTimeout(self.timers.hide);
1413
- unassignEvents();
1414
-
1415
- // If the API if actually this qTip API...
1416
- if(!elemAPI || self === elemAPI) {
1417
- // Remove api object
1418
- $.removeData(t, 'qtip');
1419
-
1420
- // Reset old title attribute if removed
1421
- if(options.suppress && title) {
1422
- $.attr(t, 'title', title);
1423
- target.removeAttr(oldtitle);
1424
- }
1425
-
1426
- // Remove ARIA attributes
1427
- target.removeAttr('aria-describedby');
1428
- }
1429
-
1430
- // Remove qTip events associated with this API
1431
- target.unbind('.qtip-'+id);
1432
-
1433
- // Remove ID from sued id object
1434
- delete usedIDs[self.id];
1435
-
1436
- return target;
1437
- }
1438
- });
1439
- }
1440
-
1441
- // Initialization method
1442
- function init(id, opts)
1443
- {
1444
- var obj, posOptions, attr, config, title,
1445
-
1446
- // Setup element references
1447
- elem = $(this),
1448
- docBody = $(document.body),
1449
-
1450
- // Use document body instead of document element if needed
1451
- newTarget = this === document ? docBody : elem,
1452
-
1453
- // Grab metadata from element if plugin is present
1454
- metadata = (elem.metadata) ? elem.metadata(opts.metadata) : NULL,
1455
-
1456
- // If metadata type if HTML5, grab 'name' from the object instead, or use the regular data object otherwise
1457
- metadata5 = opts.metadata.type === 'html5' && metadata ? metadata[opts.metadata.name] : NULL,
1458
-
1459
- // Grab data from metadata.name (or data-qtipopts as fallback) using .data() method,
1460
- html5 = elem.data(opts.metadata.name || 'qtipopts');
1461
-
1462
- // If we don't get an object returned attempt to parse it manualyl without parseJSON
1463
- try { html5 = typeof html5 === 'string' ? (new Function("return " + html5))() : html5; }
1464
- catch(e) { log('Unable to parse HTML5 attribute data: ' + html5); }
1465
-
1466
- // Merge in and sanitize metadata
1467
- config = $.extend(TRUE, {}, QTIP.defaults, opts,
1468
- typeof html5 === 'object' ? sanitizeOptions(html5) : NULL,
1469
- sanitizeOptions(metadata5 || metadata));
1470
-
1471
- // Re-grab our positioning options now we've merged our metadata and set id to passed value
1472
- posOptions = config.position;
1473
- config.id = id;
1474
-
1475
- // Setup missing content if none is detected
1476
- if('boolean' === typeof config.content.text) {
1477
- attr = elem.attr(config.content.attr);
1478
-
1479
- // Grab from supplied attribute if available
1480
- if(config.content.attr !== FALSE && attr) { config.content.text = attr; }
1481
-
1482
- // No valid content was found, abort render
1483
- else {
1484
- log('Unable to locate content for tooltip! Aborting render of tooltip on element: ', elem);
1485
- return FALSE;
1486
- }
1487
- }
1488
-
1489
- // Setup target options
1490
- if(!posOptions.container.length) { posOptions.container = docBody; }
1491
- if(posOptions.target === FALSE) { posOptions.target = newTarget; }
1492
- if(config.show.target === FALSE) { config.show.target = newTarget; }
1493
- if(config.show.solo === TRUE) { config.show.solo = posOptions.container.closest('body'); }
1494
- if(config.hide.target === FALSE) { config.hide.target = newTarget; }
1495
- if(config.position.viewport === TRUE) { config.position.viewport = posOptions.container; }
1496
-
1497
- // Ensure we only use a single container
1498
- posOptions.container = posOptions.container.eq(0);
1499
-
1500
- // Convert position corner values into x and y strings
1501
- posOptions.at = new PLUGINS.Corner(posOptions.at);
1502
- posOptions.my = new PLUGINS.Corner(posOptions.my);
1503
-
1504
- // Destroy previous tooltip if overwrite is enabled, or skip element if not
1505
- if($.data(this, 'qtip')) {
1506
- if(config.overwrite) {
1507
- elem.qtip('destroy');
1508
- }
1509
- else if(config.overwrite === FALSE) {
1510
- return FALSE;
1511
- }
1512
- }
1513
-
1514
- // Remove title attribute and store it if present
1515
- if(config.suppress && (title = $.attr(this, 'title'))) {
1516
- // Final attr call fixes event delegatiom and IE default tooltip showing problem
1517
- $(this).removeAttr('title').attr(oldtitle, title).attr('title', '');
1518
- }
1519
-
1520
- // Initialize the tooltip and add API reference
1521
- obj = new QTip(elem, config, id, !!attr);
1522
- $.data(this, 'qtip', obj);
1523
-
1524
- // Catch remove/removeqtip events on target element to destroy redundant tooltip
1525
- elem.bind('remove.qtip-'+id+' removeqtip.qtip-'+id, function(){ obj.destroy(); });
1526
-
1527
- return obj;
1528
- }
1529
-
1530
- // jQuery $.fn extension method
1531
- QTIP = $.fn.qtip = function(options, notation, newValue)
1532
- {
1533
- var command = ('' + options).toLowerCase(), // Parse command
1534
- returned = NULL,
1535
- args = $.makeArray(arguments).slice(1),
1536
- event = args[args.length - 1],
1537
- opts = this[0] ? $.data(this[0], 'qtip') : NULL;
1538
-
1539
- // Check for API request
1540
- if((!arguments.length && opts) || command === 'api') {
1541
- return opts;
1542
- }
1543
-
1544
- // Execute API command if present
1545
- else if('string' === typeof options)
1546
- {
1547
- this.each(function()
1548
- {
1549
- var api = $.data(this, 'qtip');
1550
- if(!api) { return TRUE; }
1551
-
1552
- // Cache the event if possible
1553
- if(event && event.timeStamp) { api.cache.event = event; }
1554
-
1555
- // Check for specific API commands
1556
- if((command === 'option' || command === 'options') && notation) {
1557
- if($.isPlainObject(notation) || newValue !== undefined) {
1558
- api.set(notation, newValue);
1559
- }
1560
- else {
1561
- returned = api.get(notation);
1562
- return FALSE;
1563
- }
1564
- }
1565
-
1566
- // Execute API command
1567
- else if(api[command]) {
1568
- api[command].apply(api[command], args);
1569
- }
1570
- });
1571
-
1572
- return returned !== NULL ? returned : this;
1573
- }
1574
-
1575
- // No API commands. validate provided options and setup qTips
1576
- else if('object' === typeof options || !arguments.length)
1577
- {
1578
- opts = sanitizeOptions($.extend(TRUE, {}, options));
1579
-
1580
- // Bind the qTips
1581
- return QTIP.bind.call(this, opts, event);
1582
- }
1583
- };
1584
-
1585
- // $.fn.qtip Bind method
1586
- QTIP.bind = function(opts, event)
1587
- {
1588
- return this.each(function(i) {
1589
- var options, targets, events, namespace, api, id;
1590
-
1591
- // Find next available ID, or use custom ID if provided
1592
- id = $.isArray(opts.id) ? opts.id[i] : opts.id;
1593
- id = !id || id === FALSE || id.length < 1 || usedIDs[id] ? QTIP.nextid++ : (usedIDs[id] = id);
1594
-
1595
- // Setup events namespace
1596
- namespace = '.qtip-'+id+'-create';
1597
-
1598
- // Initialize the qTip and re-grab newly sanitized options
1599
- api = init.call(this, id, opts);
1600
- if(api === FALSE) { return TRUE; }
1601
- options = api.options;
1602
-
1603
- // Initialize plugins
1604
- $.each(PLUGINS, function() {
1605
- if(this.initialize === 'initialize') { this(api); }
1606
- });
1607
-
1608
- // Determine hide and show targets
1609
- targets = { show: options.show.target, hide: options.hide.target };
1610
- events = {
1611
- show: $.trim('' + options.show.event).replace(/ /g, namespace+' ') + namespace,
1612
- hide: $.trim('' + options.hide.event).replace(/ /g, namespace+' ') + namespace
1613
- };
1614
-
1615
- /*
1616
- * Make sure hoverIntent functions properly by using mouseleave as a hide event if
1617
- * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
1618
- */
1619
- if(/mouse(over|enter)/i.test(events.show) && !/mouse(out|leave)/i.test(events.hide)) {
1620
- events.hide += ' mouseleave' + namespace;
1621
- }
1622
-
1623
- /*
1624
- * Also make sure initial mouse targetting works correctly by caching mousemove coords
1625
- * on show targets before the tooltip has rendered.
1626
- *
1627
- * Also set onTarget when triggered to keep mouse tracking working
1628
- */
1629
- targets.show.bind('mousemove'+namespace, function(event) {
1630
- MOUSE = { pageX: event.pageX, pageY: event.pageY, type: 'mousemove' };
1631
- api.cache.onTarget = TRUE;
1632
- });
1633
-
1634
- // Define hoverIntent function
1635
- function hoverIntent(event) {
1636
- function render() {
1637
- // Cache mouse coords,render and render the tooltip
1638
- api.render(typeof event === 'object' || options.show.ready);
1639
-
1640
- // Unbind show and hide events
1641
- targets.show.add(targets.hide).unbind(namespace);
1642
- }
1643
-
1644
- // Only continue if tooltip isn't disabled
1645
- if(api.cache.disabled) { return FALSE; }
1646
-
1647
- // Cache the event data
1648
- api.cache.event = $.extend({}, event);
1649
- api.cache.target = event ? $(event.target) : [undefined];
1650
-
1651
- // Start the event sequence
1652
- if(options.show.delay > 0) {
1653
- clearTimeout(api.timers.show);
1654
- api.timers.show = setTimeout(render, options.show.delay);
1655
- if(events.show !== events.hide) {
1656
- targets.hide.bind(events.hide, function() { clearTimeout(api.timers.show); });
1657
- }
1658
- }
1659
- else { render(); }
1660
- }
1661
-
1662
- // Bind show events to target
1663
- targets.show.bind(events.show, hoverIntent);
1664
-
1665
- // Prerendering is enabled, create tooltip now
1666
- if(options.show.ready || options.prerender) { hoverIntent(event); }
1667
- });
1668
- };
1669
-
1670
- // Setup base plugins
1671
- PLUGINS = QTIP.plugins = {
1672
- // Corner object parser
1673
- Corner: function(corner) {
1674
- corner = ('' + corner).replace(/([A-Z])/, ' $1').replace(/middle/gi, CENTER).toLowerCase();
1675
- this.x = (corner.match(/left|right/i) || corner.match(/center/) || ['inherit'])[0].toLowerCase();
1676
- this.y = (corner.match(/top|bottom|center/i) || ['inherit'])[0].toLowerCase();
1677
-
1678
- var f = corner.charAt(0); this.precedance = (f === 't' || f === 'b' ? Y : X);
1679
-
1680
- this.string = function() { return this.precedance === Y ? this.y+this.x : this.x+this.y; };
1681
- this.abbrev = function() {
1682
- var x = this.x.substr(0,1), y = this.y.substr(0,1);
1683
- return x === y ? x : this.precedance === Y ? y + x : x + y;
1684
- };
1685
-
1686
- this.invertx = function(center) { this.x = this.x === LEFT ? RIGHT : this.x === RIGHT ? LEFT : center || this.x; };
1687
- this.inverty = function(center) { this.y = this.y === TOP ? BOTTOM : this.y === BOTTOM ? TOP : center || this.y; };
1688
-
1689
- this.clone = function() {
1690
- return {
1691
- x: this.x, y: this.y, precedance: this.precedance,
1692
- string: this.string, abbrev: this.abbrev, clone: this.clone,
1693
- invertx: this.invertx, inverty: this.inverty
1694
- };
1695
- };
1696
- },
1697
-
1698
- // Custom (more correct for qTip!) offset calculator
1699
- offset: function(elem, container) {
1700
- var pos = elem.offset(),
1701
- docBody = elem.closest('body')[0],
1702
- parent = container, scrolled,
1703
- coffset, overflow;
1704
-
1705
- function scroll(e, i) {
1706
- pos.left += i * e.scrollLeft();
1707
- pos.top += i * e.scrollTop();
1708
- }
1709
-
1710
- if(parent) {
1711
- // Compensate for non-static containers offset
1712
- do {
1713
- if(parent.css('position') !== 'static') {
1714
- coffset = parent.position();
1715
-
1716
- // Account for element positioning, borders and margins
1717
- pos.left -= coffset.left + (parseInt(parent.css('borderLeftWidth'), 10) || 0) + (parseInt(parent.css('marginLeft'), 10) || 0);
1718
- pos.top -= coffset.top + (parseInt(parent.css('borderTopWidth'), 10) || 0) + (parseInt(parent.css('marginTop'), 10) || 0);
1719
-
1720
- // If this is the first parent element with an overflow of "scroll" or "auto", store it
1721
- if(!scrolled && (overflow = parent.css('overflow')) !== 'hidden' && overflow !== 'visible') { scrolled = parent; }
1722
- }
1723
- }
1724
- while((parent = $(parent[0].offsetParent)).length);
1725
-
1726
- // Compensate for containers scroll if it also has an offsetParent
1727
- if(scrolled && scrolled[0] !== docBody) { scroll( scrolled, 1 ); }
1728
- }
1729
-
1730
- return pos;
1731
- },
1732
-
1733
- /*
1734
- * iOS version detection
1735
- */
1736
- iOS: parseFloat(
1737
- ('' + (/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0,''])[1])
1738
- .replace('undefined', '3_2').replace('_', '.').replace('_', '')
1739
- ) || FALSE,
1740
-
1741
- /*
1742
- * jQuery-specific $.fn overrides
1743
- */
1744
- fn: {
1745
- /* Allow other plugins to successfully retrieve the title of an element with a qTip applied */
1746
- attr: function(attr, val) {
1747
- if(this.length) {
1748
- var self = this[0],
1749
- title = 'title',
1750
- api = $.data(self, 'qtip');
1751
-
1752
- if(attr === title && api && 'object' === typeof api && api.options.suppress) {
1753
- if(arguments.length < 2) {
1754
- return $.attr(self, oldtitle);
1755
- }
1756
- else {
1757
- // If qTip is rendered and title was originally used as content, update it
1758
- if(api && api.options.content.attr === title && api.cache.attr) {
1759
- api.set('content.text', val);
1760
- }
1761
-
1762
- // Use the regular attr method to set, then cache the result
1763
- return this.attr(oldtitle, val);
1764
- }
1765
- }
1766
- }
1767
-
1768
- return $.fn['attr'+replaceSuffix].apply(this, arguments);
1769
- },
1770
-
1771
- /* Allow clone to correctly retrieve cached title attributes */
1772
- clone: function(keepData) {
1773
- var titles = $([]), title = 'title',
1774
-
1775
- // Clone our element using the real clone method
1776
- elems = $.fn['clone'+replaceSuffix].apply(this, arguments);
1777
-
1778
- // Grab all elements with an oldtitle set, and change it to regular title attribute, if keepData is false
1779
- if(!keepData) {
1780
- elems.filter('['+oldtitle+']').attr('title', function() {
1781
- return $.attr(this, oldtitle);
1782
- })
1783
- .removeAttr(oldtitle);
1784
- }
1785
-
1786
- return elems;
1787
- }
1788
- }
1789
- };
1790
-
1791
- // Apply the fn overrides above
1792
- $.each(PLUGINS.fn, function(name, func) {
1793
- if(!func || $.fn[name+replaceSuffix]) { return TRUE; }
1794
-
1795
- var old = $.fn[name+replaceSuffix] = $.fn[name];
1796
- $.fn[name] = function() {
1797
- return func.apply(this, arguments) || old.apply(this, arguments);
1798
- };
1799
- });
1800
-
1801
- /* Fire off 'removeqtip' handler in $.cleanData if jQuery UI not present (it already does similar).
1802
- * This snippet is taken directly from jQuery UI source code found here:
1803
- * http://code.jquery.com/ui/jquery-ui-git.js
1804
- */
1805
- if(!$.ui) {
1806
- $['cleanData'+replaceSuffix] = $.cleanData;
1807
- $.cleanData = function( elems ) {
1808
- for(var i = 0, elem; (elem = elems[i]) !== undefined; i++) {
1809
- try { $( elem ).triggerHandler('removeqtip'); }
1810
- catch( e ) {}
1811
- }
1812
- $['cleanData'+replaceSuffix]( elems );
1813
- };
1814
- }
1815
-
1816
- // Set global qTip properties
1817
- QTIP.version = 'nightly';
1818
- QTIP.nextid = 0;
1819
- QTIP.inactiveEvents = 'click dblclick mousedown mouseup mousemove mouseleave mouseenter'.split(' ');
1820
- QTIP.zindex = 15000;
1821
-
1822
- // Define configuration defaults
1823
- QTIP.defaults = {
1824
- prerender: FALSE,
1825
- id: FALSE,
1826
- overwrite: TRUE,
1827
- suppress: TRUE,
1828
- content: {
1829
- text: TRUE,
1830
- attr: 'title',
1831
- title: {
1832
- text: FALSE,
1833
- button: FALSE
1834
- }
1835
- },
1836
- position: {
1837
- my: 'top left',
1838
- at: 'bottom right',
1839
- target: FALSE,
1840
- container: FALSE,
1841
- viewport: FALSE,
1842
- adjust: {
1843
- x: 0, y: 0,
1844
- mouse: TRUE,
1845
- resize: TRUE,
1846
- method: 'flip flip'
1847
- },
1848
- effect: function(api, pos, viewport) {
1849
- $(this).animate(pos, {
1850
- duration: 200,
1851
- queue: FALSE
1852
- });
1853
- }
1854
- },
1855
- show: {
1856
- target: FALSE,
1857
- event: 'mouseenter',
1858
- effect: TRUE,
1859
- delay: 90,
1860
- solo: FALSE,
1861
- ready: FALSE,
1862
- autofocus: FALSE
1863
- },
1864
- hide: {
1865
- target: FALSE,
1866
- event: 'mouseleave',
1867
- effect: TRUE,
1868
- delay: 0,
1869
- fixed: FALSE,
1870
- inactive: FALSE,
1871
- leave: 'window',
1872
- distance: FALSE
1873
- },
1874
- style: {
1875
- classes: '',
1876
- widget: FALSE,
1877
- width: FALSE,
1878
- height: FALSE,
1879
- def: TRUE
1880
- },
1881
- events: {
1882
- render: NULL,
1883
- move: NULL,
1884
- show: NULL,
1885
- hide: NULL,
1886
- toggle: NULL,
1887
- visible: NULL,
1888
- hidden: NULL,
1889
- focus: NULL,
1890
- blur: NULL
1891
- }
1892
- };
1893
-
1894
- function Ajax(api)
1895
- {
1896
- var self = this,
1897
- tooltip = api.elements.tooltip,
1898
- opts = api.options.content.ajax,
1899
- defaults = QTIP.defaults.content.ajax,
1900
- namespace = '.qtip-ajax',
1901
- rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
1902
- first = TRUE,
1903
- stop = FALSE,
1904
- xhr;
1905
-
1906
- api.checks.ajax = {
1907
- '^content.ajax': function(obj, name, v) {
1908
- // If content.ajax object was reset, set our local var
1909
- if(name === 'ajax') { opts = v; }
1910
-
1911
- if(name === 'once') {
1912
- self.init();
1913
- }
1914
- else if(opts && opts.url) {
1915
- self.load();
1916
- }
1917
- else {
1918
- tooltip.unbind(namespace);
1919
- }
1920
- }
1921
- };
1922
-
1923
- $.extend(self, {
1924
- init: function() {
1925
- // Make sure ajax options are enabled and bind event
1926
- if(opts && opts.url) {
1927
- tooltip.unbind(namespace)[ opts.once ? 'one' : 'bind' ]('tooltipshow'+namespace, self.load);
1928
- }
1929
-
1930
- return self;
1931
- },
1932
-
1933
- load: function(event) {
1934
- if(stop) {stop = FALSE; return; }
1935
-
1936
- var hasSelector = opts.url.indexOf(' '),
1937
- url = opts.url,
1938
- selector,
1939
- hideFirst = !opts.loading && first;
1940
-
1941
- // If loading option is disabled, prevent the tooltip showing until we've completed the request
1942
- if(hideFirst) { try{ event.preventDefault(); } catch(e) {} }
1943
-
1944
- // Make sure default event hasn't been prevented
1945
- else if(event && event.isDefaultPrevented()) { return self; }
1946
-
1947
- // Cancel old request
1948
- if(xhr && xhr.abort) { xhr.abort(); }
1949
-
1950
- // Check if user delcared a content selector like in .load()
1951
- if(hasSelector > -1) {
1952
- selector = url.substr(hasSelector);
1953
- url = url.substr(0, hasSelector);
1954
- }
1955
-
1956
- // Define common after callback for both success/error handlers
1957
- function after() {
1958
- var complete;
1959
-
1960
- // Don't proceed if tooltip is destroyed
1961
- if(api.destroyed) { return; }
1962
-
1963
- // Set first flag to false
1964
- first = FALSE;
1965
-
1966
- // Re-display tip if loading and first time, and reset first flag
1967
- if(hideFirst) { stop = TRUE; api.show(event.originalEvent); }
1968
-
1969
- // Call users complete method if it was defined
1970
- if((complete = defaults.complete || opts.complete) && $.isFunction(complete)) {
1971
- complete.apply(opts.context || api, arguments);
1972
- }
1973
- }
1974
-
1975
- // Define success handler
1976
- function successHandler(content, status, jqXHR) {
1977
- var success;
1978
-
1979
- // Don't proceed if tooltip is destroyed
1980
- if(api.destroyed) { return; }
1981
-
1982
- if(selector) {
1983
- // Create a dummy div to hold the results and grab the selector element
1984
- content = $('<div/>')
1985
- // inject the contents of the document in, removing the scripts
1986
- // to avoid any 'Permission Denied' errors in IE
1987
- .append(content.replace(rscript, ""))
1988
-
1989
- // Locate the specified elements
1990
- .find(selector);
1991
- }
1992
-
1993
- // Call the success function if one is defined
1994
- if((success = defaults.success || opts.success) && $.isFunction(success)) {
1995
- success.call(opts.context || api, content, status, jqXHR);
1996
- }
1997
-
1998
- // Otherwise set the content
1999
- else { api.set('content.text', content); }
2000
- }
2001
-
2002
- // Error handler
2003
- function errorHandler(xhr, status, error) {
2004
- if(api.destroyed || xhr.status === 0) { return; }
2005
- api.set('content.text', status + ': ' + error);
2006
- }
2007
-
2008
- // Setup $.ajax option object and process the request
2009
- xhr = $.ajax(
2010
- $.extend({
2011
- error: defaults.error || errorHandler,
2012
- context: api
2013
- },
2014
- opts, { url: url, success: successHandler, complete: after })
2015
- );
2016
- },
2017
-
2018
- destroy: function() {
2019
- // Cancel ajax request if possible
2020
- if(xhr && xhr.abort) { xhr.abort(); }
2021
-
2022
- // Set api.destroyed flag
2023
- api.destroyed = TRUE;
2024
- }
2025
- });
2026
-
2027
- self.init();
2028
- }
2029
-
2030
-
2031
- PLUGINS.ajax = function(api)
2032
- {
2033
- var self = api.plugins.ajax;
2034
-
2035
- return 'object' === typeof self ? self : (api.plugins.ajax = new Ajax(api));
2036
- };
2037
-
2038
- PLUGINS.ajax.initialize = 'render';
2039
-
2040
- // Setup plugin sanitization
2041
- PLUGINS.ajax.sanitize = function(options)
2042
- {
2043
- var content = options.content, opts;
2044
- if(content && 'ajax' in content) {
2045
- opts = content.ajax;
2046
- if(typeof opts !== 'object') { opts = options.content.ajax = { url: opts }; }
2047
- if('boolean' !== typeof opts.once && opts.once) { opts.once = !!opts.once; }
2048
- }
2049
- };
2050
-
2051
- // Extend original api defaults
2052
- $.extend(TRUE, QTIP.defaults, {
2053
- content: {
2054
- ajax: {
2055
- loading: TRUE,
2056
- once: TRUE
2057
- }
2058
- }
2059
- });
2060
-
2061
- PLUGINS.viewport = function(api, position, posOptions, targetWidth, targetHeight, elemWidth, elemHeight)
2062
- {
2063
- var target = posOptions.target,
2064
- tooltip = api.elements.tooltip,
2065
- my = posOptions.my,
2066
- at = posOptions.at,
2067
- adjust = posOptions.adjust,
2068
- method = adjust.method.split(' '),
2069
- methodX = method[0],
2070
- methodY = method[1] || method[0],
2071
- viewport = posOptions.viewport,
2072
- container = posOptions.container,
2073
- cache = api.cache,
2074
- tip = api.plugins.tip,
2075
- adjusted = { left: 0, top: 0 },
2076
- fixed, newMy, newClass;
2077
-
2078
- // If viewport is not a jQuery element, or it's the window/document or no adjustment method is used... return
2079
- if(!viewport.jquery || target[0] === window || target[0] === document.body || adjust.method === 'none') {
2080
- return adjusted;
2081
- }
2082
-
2083
- // Cache our viewport details
2084
- fixed = tooltip.css('position') === 'fixed';
2085
- viewport = {
2086
- elem: viewport,
2087
- height: viewport[ (viewport[0] === window ? 'h' : 'outerH') + 'eight' ](),
2088
- width: viewport[ (viewport[0] === window ? 'w' : 'outerW') + 'idth' ](),
2089
- scrollleft: fixed ? 0 : viewport.scrollLeft(),
2090
- scrolltop: fixed ? 0 : viewport.scrollTop(),
2091
- offset: viewport.offset() || { left: 0, top: 0 }
2092
- };
2093
- container = {
2094
- elem: container,
2095
- scrollLeft: container.scrollLeft(),
2096
- scrollTop: container.scrollTop(),
2097
- offset: container.offset() || { left: 0, top: 0 }
2098
- };
2099
-
2100
- // Generic calculation method
2101
- function calculate(side, otherSide, type, adjust, side1, side2, lengthName, targetLength, elemLength) {
2102
- var initialPos = position[side1],
2103
- mySide = my[side], atSide = at[side],
2104
- isShift = type === SHIFT,
2105
- viewportScroll = -container.offset[side1] + viewport.offset[side1] + viewport['scroll'+side1],
2106
- myLength = mySide === side1 ? elemLength : mySide === side2 ? -elemLength : -elemLength / 2,
2107
- atLength = atSide === side1 ? targetLength : atSide === side2 ? -targetLength : -targetLength / 2,
2108
- tipLength = tip && tip.size ? tip.size[lengthName] || 0 : 0,
2109
- tipAdjust = tip && tip.corner && tip.corner.precedance === side && !isShift ? tipLength : 0,
2110
- overflow1 = viewportScroll - initialPos + tipAdjust,
2111
- overflow2 = initialPos + elemLength - viewport[lengthName] - viewportScroll + tipAdjust,
2112
- offset = myLength - (my.precedance === side || mySide === my[otherSide] ? atLength : 0) - (atSide === CENTER ? targetLength / 2 : 0);
2113
-
2114
- // shift
2115
- if(isShift) {
2116
- tipAdjust = tip && tip.corner && tip.corner.precedance === otherSide ? tipLength : 0;
2117
- offset = (mySide === side1 ? 1 : -1) * myLength - tipAdjust;
2118
-
2119
- // Adjust position but keep it within viewport dimensions
2120
- position[side1] += overflow1 > 0 ? overflow1 : overflow2 > 0 ? -overflow2 : 0;
2121
- position[side1] = Math.max(
2122
- -container.offset[side1] + viewport.offset[side1] + (tipAdjust && tip.corner[side] === CENTER ? tip.offset : 0),
2123
- initialPos - offset,
2124
- Math.min(
2125
- Math.max(-container.offset[side1] + viewport.offset[side1] + viewport[lengthName], initialPos + offset),
2126
- position[side1]
2127
- )
2128
- );
2129
- }
2130
-
2131
- // flip/flipinvert
2132
- else {
2133
- // Update adjustment amount depending on if using flipinvert or flip
2134
- adjust *= (type === FLIPINVERT ? 2 : 0);
2135
-
2136
- // Check for overflow on the left/top
2137
- if(overflow1 > 0 && (mySide !== side1 || overflow2 > 0)) {
2138
- position[side1] -= offset + adjust;
2139
- newMy['invert'+side](side1);
2140
- }
2141
-
2142
- // Check for overflow on the bottom/right
2143
- else if(overflow2 > 0 && (mySide !== side2 || overflow1 > 0) ) {
2144
- position[side1] -= (mySide === CENTER ? -offset : offset) + adjust;
2145
- newMy['invert'+side](side2);
2146
- }
2147
-
2148
- // Make sure we haven't made things worse with the adjustment and reset if so
2149
- if(position[side1] < viewportScroll && -position[side1] > overflow2) {
2150
- position[side1] = initialPos; newMy = undefined;
2151
- }
2152
- }
2153
-
2154
- return position[side1] - initialPos;
2155
- }
2156
-
2157
- // Set newMy if using flip or flipinvert methods
2158
- if(methodX !== 'shift' || methodY !== 'shift') { newMy = my.clone(); }
2159
-
2160
- // Adjust position based onviewport and adjustment options
2161
- adjusted = {
2162
- left: methodX !== 'none' ? calculate( X, Y, methodX, adjust.x, LEFT, RIGHT, WIDTH, targetWidth, elemWidth ) : 0,
2163
- top: methodY !== 'none' ? calculate( Y, X, methodY, adjust.y, TOP, BOTTOM, HEIGHT, targetHeight, elemHeight ) : 0
2164
- };
2165
-
2166
- // Set tooltip position class if it's changed
2167
- if(newMy && cache.lastClass !== (newClass = uitooltip + '-pos-' + newMy.abbrev())) {
2168
- tooltip.removeClass(api.cache.lastClass).addClass( (api.cache.lastClass = newClass) );
2169
- }
2170
-
2171
- return adjusted;
2172
- };function Modal(api)
2173
- {
2174
- var self = this,
2175
- options = api.options.show.modal,
2176
- elems = api.elements,
2177
- tooltip = elems.tooltip,
2178
- overlaySelector = '#qtip-overlay',
2179
- globalNamespace = '.qtipmodal',
2180
- namespace = globalNamespace + api.id,
2181
- attr = 'is-modal-qtip',
2182
- docBody = $(document.body),
2183
- focusableSelector = PLUGINS.modal.focusable.join(','),
2184
- focusableElems = {}, overlay;
2185
-
2186
- // Setup option set checks
2187
- api.checks.modal = {
2188
- '^show.modal.(on|blur)$': function() {
2189
- // Initialise
2190
- self.init();
2191
-
2192
- // Show the modal if not visible already and tooltip is visible
2193
- elems.overlay.toggle( tooltip.is(':visible') );
2194
- },
2195
- '^content.text$': updateFocusable
2196
- };
2197
-
2198
- function updateFocusable() {
2199
- focusableElems = $(focusableSelector, tooltip).not('[disabled]').map(function() {
2200
- return typeof this.focus === 'function' ? this : null;
2201
- });
2202
- }
2203
-
2204
- function focusInputs(blurElems) {
2205
- // Blurring body element in IE causes window.open windows to unfocus!
2206
- if(focusableElems.length < 1 && blurElems.length) { blurElems.not('body').blur(); }
2207
-
2208
- // Focus the inputs
2209
- else { focusableElems.first().focus(); }
2210
- }
2211
-
2212
- function stealFocus(event) {
2213
- var target = $(event.target),
2214
- container = target.closest('.qtip'),
2215
- targetOnTop;
2216
-
2217
- // Determine if input container target is above this
2218
- targetOnTop = container.length < 1 ? FALSE :
2219
- (parseInt(container[0].style.zIndex, 10) > parseInt(tooltip[0].style.zIndex, 10));
2220
-
2221
- // If we're showing a modal, but focus has landed on an input below
2222
- // this modal, divert focus to the first visible input in this modal
2223
- // or if we can't find one... the tooltip itself
2224
- if(!targetOnTop && ($(event.target).closest(selector)[0] !== tooltip[0])) {
2225
- focusInputs(target);
2226
- }
2227
- }
2228
-
2229
- $.extend(self, {
2230
- init: function()
2231
- {
2232
- // If modal is disabled... return
2233
- if(!options.on) { return self; }
2234
-
2235
- // Create the overlay if needed
2236
- overlay = self.create();
2237
-
2238
- // Add unique attribute so we can grab modal tooltips easily via a selector
2239
- tooltip.attr(attr, TRUE)
2240
-
2241
- // Set z-index
2242
- .css('z-index', PLUGINS.modal.zindex + $(selector+'['+attr+']').length)
2243
-
2244
- // Remove previous bound events in globalNamespace
2245
- .unbind(globalNamespace).unbind(namespace)
2246
-
2247
- // Apply our show/hide/focus modal events
2248
- .bind('tooltipshow'+globalNamespace+' tooltiphide'+globalNamespace, function(event, api, duration) {
2249
- var oEvent = event.originalEvent;
2250
-
2251
- // Make sure mouseout doesn't trigger a hide when showing the modal and mousing onto backdrop
2252
- if(event.target === tooltip[0]) {
2253
- if(oEvent && event.type === 'tooltiphide' && /mouse(leave|enter)/.test(oEvent.type) && $(oEvent.relatedTarget).closest(overlay[0]).length) {
2254
- try { event.preventDefault(); } catch(e) {}
2255
- }
2256
- else if(!oEvent || (oEvent && !oEvent.solo)) {
2257
- self[ event.type.replace('tooltip', '') ](event, duration);
2258
- }
2259
- }
2260
- })
2261
-
2262
- // Adjust modal z-index on tooltip focus
2263
- .bind('tooltipfocus'+globalNamespace, function(event) {
2264
- // If focus was cancelled before it reearch us, don't do anything
2265
- if(event.isDefaultPrevented() || event.target !== tooltip[0]) { return; }
2266
-
2267
- var qtips = $(selector).filter('['+attr+']'),
2268
-
2269
- // Keep the modal's lower than other, regular qtips
2270
- newIndex = PLUGINS.modal.zindex + qtips.length,
2271
- curIndex = parseInt(tooltip[0].style.zIndex, 10);
2272
-
2273
- // Set overlay z-index
2274
- overlay[0].style.zIndex = newIndex - 2;
2275
-
2276
- // Reduce modal z-index's and keep them properly ordered
2277
- qtips.each(function() {
2278
- if(this.style.zIndex > curIndex) {
2279
- this.style.zIndex -= 1;
2280
- }
2281
- });
2282
-
2283
- // Fire blur event for focused tooltip
2284
- qtips.end().filter('.' + focusClass).qtip('blur', event.originalEvent);
2285
-
2286
- // Set the new z-index
2287
- tooltip.addClass(focusClass)[0].style.zIndex = newIndex;
2288
-
2289
- // Prevent default handling
2290
- try { event.preventDefault(); } catch(e) {}
2291
- })
2292
-
2293
- // Focus any other visible modals when this one hides
2294
- .bind('tooltiphide'+globalNamespace, function(event) {
2295
- if(event.target === tooltip[0]) {
2296
- $('[' + attr + ']').filter(':visible').not(tooltip).last().qtip('focus', event);
2297
- }
2298
- });
2299
-
2300
- // Apply keyboard "Escape key" close handler
2301
- if(options.escape) {
2302
- $(document).unbind(namespace).bind('keydown'+namespace, function(event) {
2303
- if(event.keyCode === 27 && tooltip.hasClass(focusClass)) {
2304
- api.hide(event);
2305
- }
2306
- });
2307
- }
2308
-
2309
- // Apply click handler for blur option
2310
- if(options.blur) {
2311
- elems.overlay.unbind(namespace).bind('click'+namespace, function(event) {
2312
- if(tooltip.hasClass(focusClass)) { api.hide(event); }
2313
- });
2314
- }
2315
-
2316
- // Update focusable elements
2317
- updateFocusable();
2318
-
2319
- return self;
2320
- },
2321
-
2322
- create: function()
2323
- {
2324
- var elem = $(overlaySelector);
2325
-
2326
- // Return if overlay is already rendered
2327
- if(elem.length) {
2328
- // Modal overlay should always be below all tooltips if possible
2329
- return (elems.overlay = elem.insertAfter( $(selector).last() ));
2330
- }
2331
-
2332
- // Create document overlay
2333
- overlay = elems.overlay = $('<div />', {
2334
- id: overlaySelector.substr(1),
2335
- html: '<div></div>',
2336
- mousedown: function() { return FALSE; }
2337
- })
2338
- .hide()
2339
- .insertAfter( $(selector).last() );
2340
-
2341
- // Update position on window resize or scroll
2342
- function resize() {
2343
- overlay.css({
2344
- height: $(window).height(),
2345
- width: $(window).width()
2346
- });
2347
- }
2348
- $(window).unbind(globalNamespace).bind('resize'+globalNamespace, resize);
2349
- resize(); // Fire it initially too
2350
-
2351
- return overlay;
2352
- },
2353
-
2354
- toggle: function(event, state, duration)
2355
- {
2356
- // Make sure default event hasn't been prevented
2357
- if(event && event.isDefaultPrevented()) { return self; }
2358
-
2359
- var effect = options.effect,
2360
- type = state ? 'show': 'hide',
2361
- visible = overlay.is(':visible'),
2362
- modals = $('[' + attr + ']').filter(':visible').not(tooltip),
2363
- zindex;
2364
-
2365
- // Create our overlay if it isn't present already
2366
- if(!overlay) { overlay = self.create(); }
2367
-
2368
- // Prevent modal from conflicting with show.solo, and don't hide backdrop is other modals are visible
2369
- if((overlay.is(':animated') && visible === state) || (!state && modals.length)) { return self; }
2370
-
2371
- // State specific...
2372
- if(state) {
2373
- // Set position
2374
- overlay.css({ left: 0, top: 0 });
2375
-
2376
- // Toggle backdrop cursor style on show
2377
- overlay.toggleClass('blurs', options.blur);
2378
-
2379
- // IF the modal can steal the focus
2380
- if(options.stealfocus !== FALSE) {
2381
- // Make sure we can't focus anything outside the tooltip
2382
- docBody.bind('focusin'+namespace, stealFocus);
2383
-
2384
- // Blur the current item and focus anything in the modal we an
2385
- focusInputs( $('body *') );
2386
- }
2387
- }
2388
- else {
2389
- // Undelegate focus handler
2390
- docBody.unbind('focusin'+namespace);
2391
- }
2392
-
2393
- // Stop all animations
2394
- overlay.stop(TRUE, FALSE);
2395
-
2396
- // Use custom function if provided
2397
- if($.isFunction(effect)) {
2398
- effect.call(overlay, state);
2399
- }
2400
-
2401
- // If no effect type is supplied, use a simple toggle
2402
- else if(effect === FALSE) {
2403
- overlay[ type ]();
2404
- }
2405
-
2406
- // Use basic fade function
2407
- else {
2408
- overlay.fadeTo( parseInt(duration, 10) || 90, state ? 1 : 0, function() {
2409
- if(!state) { $(this).hide(); }
2410
- });
2411
- }
2412
-
2413
- // Reset position on hide
2414
- if(!state) {
2415
- overlay.queue(function(next) {
2416
- overlay.css({ left: '', top: '' });
2417
- next();
2418
- });
2419
- }
2420
-
2421
- return self;
2422
- },
2423
-
2424
- show: function(event, duration) { return self.toggle(event, TRUE, duration); },
2425
- hide: function(event, duration) { return self.toggle(event, FALSE, duration); },
2426
-
2427
- destroy: function()
2428
- {
2429
- var delBlanket = overlay;
2430
-
2431
- if(delBlanket) {
2432
- // Check if any other modal tooltips are present
2433
- delBlanket = $('[' + attr + ']').not(tooltip).length < 1;
2434
-
2435
- // Remove overlay if needed
2436
- if(delBlanket) {
2437
- elems.overlay.remove();
2438
- $(document).unbind(globalNamespace);
2439
- }
2440
- else {
2441
- elems.overlay.unbind(globalNamespace+api.id);
2442
- }
2443
-
2444
- // Undelegate focus handler
2445
- docBody.undelegate('*', 'focusin'+namespace);
2446
- }
2447
-
2448
- // Remove bound events
2449
- return tooltip.removeAttr(attr).unbind(globalNamespace);
2450
- }
2451
- });
2452
-
2453
- self.init();
2454
- }
2455
-
2456
- PLUGINS.modal = function(api) {
2457
- var self = api.plugins.modal;
2458
-
2459
- return 'object' === typeof self ? self : (api.plugins.modal = new Modal(api));
2460
- };
2461
-
2462
- // Plugin needs to be initialized on render
2463
- PLUGINS.modal.initialize = 'render';
2464
-
2465
- // Setup sanitiztion rules
2466
- PLUGINS.modal.sanitize = function(opts) {
2467
- if(opts.show) {
2468
- if(typeof opts.show.modal !== 'object') { opts.show.modal = { on: !!opts.show.modal }; }
2469
- else if(typeof opts.show.modal.on === 'undefined') { opts.show.modal.on = TRUE; }
2470
- }
2471
- };
2472
-
2473
- // Base z-index for all modal tooltips (use qTip core z-index as a base)
2474
- PLUGINS.modal.zindex = QTIP.zindex + 1000;
2475
-
2476
- // Defines the selector used to select all 'focusable' elements within the modal when using the show.modal.stealfocus option.
2477
- // Selectors initially taken from http://stackoverflow.com/questions/7668525/is-there-a-jquery-selector-to-get-all-elements-that-can-get-focus
2478
- PLUGINS.modal.focusable = ['a[href]', 'area[href]', 'input', 'select', 'textarea', 'button', 'iframe', 'object', 'embed', '[tabindex]', '[contenteditable]'];
2479
-
2480
- // Extend original api defaults
2481
- $.extend(TRUE, QTIP.defaults, {
2482
- show: {
2483
- modal: {
2484
- on: FALSE,
2485
- effect: TRUE,
2486
- blur: TRUE,
2487
- stealfocus: TRUE,
2488
- escape: TRUE
2489
- }
2490
- }
2491
- });
2492
-
2493
- PLUGINS.imagemap = function(api, area, corner, adjustMethod)
2494
- {
2495
- if(!area.jquery) { area = $(area); }
2496
-
2497
- var cache = (api.cache.areas = {}),
2498
- shape = (area[0].shape || area.attr('shape')).toLowerCase(),
2499
- coordsString = area[0].coords || area.attr('coords'),
2500
- baseCoords = coordsString.split(','),
2501
- coords = [],
2502
- image = $('img[usemap="#'+area.parent('map').attr('name')+'"]'),
2503
- imageOffset = image.offset(),
2504
- result = {
2505
- width: 0, height: 0,
2506
- position: {
2507
- top: 1e10, right: 0,
2508
- bottom: 0, left: 1e10
2509
- }
2510
- },
2511
- i = 0, next = 0, dimensions;
2512
-
2513
- // POLY area coordinate calculator
2514
- // Special thanks to Ed Cradock for helping out with this.
2515
- // Uses a binary search algorithm to find suitable coordinates.
2516
- function polyCoordinates(result, coords, corner)
2517
- {
2518
- var i = 0,
2519
- compareX = 1, compareY = 1,
2520
- realX = 0, realY = 0,
2521
- newWidth = result.width,
2522
- newHeight = result.height;
2523
-
2524
- // Use a binary search algorithm to locate most suitable coordinate (hopefully)
2525
- while(newWidth > 0 && newHeight > 0 && compareX > 0 && compareY > 0)
2526
- {
2527
- newWidth = Math.floor(newWidth / 2);
2528
- newHeight = Math.floor(newHeight / 2);
2529
-
2530
- if(corner.x === LEFT){ compareX = newWidth; }
2531
- else if(corner.x === RIGHT){ compareX = result.width - newWidth; }
2532
- else{ compareX += Math.floor(newWidth / 2); }
2533
-
2534
- if(corner.y === TOP){ compareY = newHeight; }
2535
- else if(corner.y === BOTTOM){ compareY = result.height - newHeight; }
2536
- else{ compareY += Math.floor(newHeight / 2); }
2537
-
2538
- i = coords.length; while(i--)
2539
- {
2540
- if(coords.length < 2){ break; }
2541
-
2542
- realX = coords[i][0] - result.position.left;
2543
- realY = coords[i][1] - result.position.top;
2544
-
2545
- if((corner.x === LEFT && realX >= compareX) ||
2546
- (corner.x === RIGHT && realX <= compareX) ||
2547
- (corner.x === CENTER && (realX < compareX || realX > (result.width - compareX))) ||
2548
- (corner.y === TOP && realY >= compareY) ||
2549
- (corner.y === BOTTOM && realY <= compareY) ||
2550
- (corner.y === CENTER && (realY < compareY || realY > (result.height - compareY)))) {
2551
- coords.splice(i, 1);
2552
- }
2553
- }
2554
- }
2555
-
2556
- return { left: coords[0][0], top: coords[0][1] };
2557
- }
2558
-
2559
- // Make sure we account for padding and borders on the image
2560
- imageOffset.left += Math.ceil((image.outerWidth() - image.width()) / 2);
2561
- imageOffset.top += Math.ceil((image.outerHeight() - image.height()) / 2);
2562
-
2563
- // Parse coordinates into proper array
2564
- if(shape === 'poly') {
2565
- i = baseCoords.length; while(i--)
2566
- {
2567
- next = [ parseInt(baseCoords[--i], 10), parseInt(baseCoords[i+1], 10) ];
2568
-
2569
- if(next[0] > result.position.right){ result.position.right = next[0]; }
2570
- if(next[0] < result.position.left){ result.position.left = next[0]; }
2571
- if(next[1] > result.position.bottom){ result.position.bottom = next[1]; }
2572
- if(next[1] < result.position.top){ result.position.top = next[1]; }
2573
-
2574
- coords.push(next);
2575
- }
2576
- }
2577
- else {
2578
- i = -1; while(i++ < baseCoords.length) {
2579
- coords.push( parseInt(baseCoords[i], 10) );
2580
- }
2581
- }
2582
-
2583
- // Calculate details
2584
- switch(shape)
2585
- {
2586
- case 'rect':
2587
- result = {
2588
- width: Math.abs(coords[2] - coords[0]),
2589
- height: Math.abs(coords[3] - coords[1]),
2590
- position: {
2591
- left: Math.min(coords[0], coords[2]),
2592
- top: Math.min(coords[1], coords[3])
2593
- }
2594
- };
2595
- break;
2596
-
2597
- case 'circle':
2598
- result = {
2599
- width: coords[2] + 2,
2600
- height: coords[2] + 2,
2601
- position: { left: coords[0], top: coords[1] }
2602
- };
2603
- break;
2604
-
2605
- case 'poly':
2606
- result.width = Math.abs(result.position.right - result.position.left);
2607
- result.height = Math.abs(result.position.bottom - result.position.top)
2608
-
2609
- if(corner.abbrev() === 'c') {
2610
- result.position = {
2611
- left: result.position.left + (result.width / 2),
2612
- top: result.position.top + (result.height / 2)
2613
- };
2614
- }
2615
- else {
2616
- // Calculate if we can't find a cached value
2617
- if(!cache[corner+coordsString]) {
2618
- result.position = polyCoordinates(result, coords.slice(), corner);
2619
-
2620
- // If flip adjustment is enabled, also calculate the closest opposite point
2621
- if(adjustMethod && (adjustMethod[0] === 'flip' || adjustMethod[1] === 'flip')) {
2622
- result.offset = polyCoordinates(result, coords.slice(), {
2623
- x: corner.x === LEFT ? RIGHT : corner.x === RIGHT ? LEFT : CENTER,
2624
- y: corner.y === TOP ? BOTTOM : corner.y === BOTTOM ? TOP : CENTER
2625
- });
2626
-
2627
- result.offset.left -= result.position.left;
2628
- result.offset.top -= result.position.top;
2629
- }
2630
-
2631
- // Store the result
2632
- cache[corner+coordsString] = result;
2633
- }
2634
-
2635
- // Grab the cached result
2636
- result = cache[corner+coordsString];
2637
- }
2638
-
2639
- result.width = result.height = 0;
2640
- break;
2641
- }
2642
-
2643
- // Add image position to offset coordinates
2644
- result.position.left += imageOffset.left;
2645
- result.position.top += imageOffset.top;
2646
-
2647
- return result;
2648
- };
2649
-
2650
- // Tip coordinates calculator
2651
- function calculateTip(corner, width, height)
2652
- {
2653
- var width2 = Math.ceil(width / 2), height2 = Math.ceil(height / 2),
2654
-
2655
- // Define tip coordinates in terms of height and width values
2656
- tips = {
2657
- bottomright: [[0,0], [width,height], [width,0]],
2658
- bottomleft: [[0,0], [width,0], [0,height]],
2659
- topright: [[0,height], [width,0], [width,height]],
2660
- topleft: [[0,0], [0,height], [width,height]],
2661
- topcenter: [[0,height], [width2,0], [width,height]],
2662
- bottomcenter: [[0,0], [width,0], [width2,height]],
2663
- rightcenter: [[0,0], [width,height2], [0,height]],
2664
- leftcenter: [[width,0], [width,height], [0,height2]]
2665
- };
2666
-
2667
- // Set common side shapes
2668
- tips.lefttop = tips.bottomright; tips.righttop = tips.bottomleft;
2669
- tips.leftbottom = tips.topright; tips.rightbottom = tips.topleft;
2670
-
2671
- return tips[ corner.string() ];
2672
- }
2673
-
2674
-
2675
- function Tip(qTip, command)
2676
- {
2677
- var self = this,
2678
- opts = qTip.options.style.tip,
2679
- elems = qTip.elements,
2680
- tooltip = elems.tooltip,
2681
- cache = { top: 0, left: 0 },
2682
- size = {
2683
- width: opts.width,
2684
- height: opts.height
2685
- },
2686
- color = { },
2687
- border = opts.border || 0,
2688
- namespace = '.qtip-tip',
2689
- hasCanvas = !!($('<canvas />')[0] || {}).getContext;
2690
-
2691
- self.corner = NULL;
2692
- self.mimic = NULL;
2693
- self.border = border;
2694
- self.offset = opts.offset;
2695
- self.size = size;
2696
-
2697
- // Add new option checks for the plugin
2698
- qTip.checks.tip = {
2699
- '^position.my|style.tip.(corner|mimic|border)$': function() {
2700
- // Make sure a tip can be drawn
2701
- if(!self.init()) {
2702
- self.destroy();
2703
- }
2704
-
2705
- // Reposition the tooltip
2706
- qTip.reposition();
2707
- },
2708
- '^style.tip.(height|width)$': function() {
2709
- // Re-set dimensions and redraw the tip
2710
- size = {
2711
- width: opts.width,
2712
- height: opts.height
2713
- };
2714
- self.create();
2715
- self.update();
2716
-
2717
- // Reposition the tooltip
2718
- qTip.reposition();
2719
- },
2720
- '^content.title.text|style.(classes|widget)$': function() {
2721
- if(elems.tip && elems.tip.length) {
2722
- self.update();
2723
- }
2724
- }
2725
- };
2726
-
2727
- function swapDimensions() {
2728
- size.width = opts.height;
2729
- size.height = opts.width;
2730
- }
2731
-
2732
- function resetDimensions() {
2733
- size.width = opts.width;
2734
- size.height = opts.height;
2735
- }
2736
-
2737
- function reposition(event, api, pos, viewport) {
2738
- if(!elems.tip) { return; }
2739
-
2740
- var newCorner = self.corner.clone(),
2741
- adjust = pos.adjusted,
2742
- method = qTip.options.position.adjust.method.split(' '),
2743
- horizontal = method[0],
2744
- vertical = method[1] || method[0],
2745
- shift = { left: FALSE, top: FALSE, x: 0, y: 0 },
2746
- offset, css = {}, props;
2747
-
2748
- // If our tip position isn't fixed e.g. doesn't adjust with viewport...
2749
- if(self.corner.fixed !== TRUE) {
2750
- // Horizontal - Shift or flip method
2751
- if(horizontal === SHIFT && newCorner.precedance === X && adjust.left && newCorner.y !== CENTER) {
2752
- newCorner.precedance = newCorner.precedance === X ? Y : X;
2753
- }
2754
- else if(horizontal !== SHIFT && adjust.left){
2755
- newCorner.x = newCorner.x === CENTER ? (adjust.left > 0 ? LEFT : RIGHT) : (newCorner.x === LEFT ? RIGHT : LEFT);
2756
- }
2757
-
2758
- // Vertical - Shift or flip method
2759
- if(vertical === SHIFT && newCorner.precedance === Y && adjust.top && newCorner.x !== CENTER) {
2760
- newCorner.precedance = newCorner.precedance === Y ? X : Y;
2761
- }
2762
- else if(vertical !== SHIFT && adjust.top) {
2763
- newCorner.y = newCorner.y === CENTER ? (adjust.top > 0 ? TOP : BOTTOM) : (newCorner.y === TOP ? BOTTOM : TOP);
2764
- }
2765
-
2766
- // Update and redraw the tip if needed (check cached details of last drawn tip)
2767
- if(newCorner.string() !== cache.corner.string() && (cache.top !== adjust.top || cache.left !== adjust.left)) {
2768
- self.update(newCorner, FALSE);
2769
- }
2770
- }
2771
-
2772
- // Setup tip offset properties
2773
- offset = self.position(newCorner, adjust);
2774
- offset[ newCorner.x ] += borderWidth(newCorner, newCorner.x, TRUE);
2775
- offset[ newCorner.y ] += borderWidth(newCorner, newCorner.y, TRUE);
2776
-
2777
- // Readjust offset object to make it left/top
2778
- if(offset.right !== undefined) { offset.left = -offset.right; }
2779
- if(offset.bottom !== undefined) { offset.top = -offset.bottom; }
2780
- offset.user = Math.max(0, opts.offset);
2781
-
2782
- // Viewport "shift" specific adjustments
2783
- if(shift.left = (horizontal === SHIFT && !!adjust.left)) {
2784
- if(newCorner.x === CENTER) {
2785
- css['margin-left'] = shift.x = offset['margin-left'] - adjust.left;
2786
- }
2787
- else {
2788
- props = offset.right !== undefined ?
2789
- [ adjust.left, -offset.left ] : [ -adjust.left, offset.left ];
2790
-
2791
- if( (shift.x = Math.max(props[0], props[1])) > props[0] ) {
2792
- pos.left -= adjust.left;
2793
- shift.left = FALSE;
2794
- }
2795
-
2796
- css[ offset.right !== undefined ? RIGHT : LEFT ] = shift.x;
2797
- }
2798
- }
2799
- if(shift.top = (vertical === SHIFT && !!adjust.top)) {
2800
- if(newCorner.y === CENTER) {
2801
- css['margin-top'] = shift.y = offset['margin-top'] - adjust.top;
2802
- }
2803
- else {
2804
- props = offset.bottom !== undefined ?
2805
- [ adjust.top, -offset.top ] : [ -adjust.top, offset.top ];
2806
-
2807
- if( (shift.y = Math.max(props[0], props[1])) > props[0] ) {
2808
- pos.top -= adjust.top;
2809
- shift.top = FALSE;
2810
- }
2811
-
2812
- css[ offset.bottom !== undefined ? BOTTOM : TOP ] = shift.y;
2813
- }
2814
- }
2815
-
2816
- /*
2817
- * If the tip is adjusted in both dimensions, or in a
2818
- * direction that would cause it to be anywhere but the
2819
- * outer border, hide it!
2820
- */
2821
- elems.tip.css(css).toggle(
2822
- !((shift.x && shift.y) || (newCorner.x === CENTER && shift.y) || (newCorner.y === CENTER && shift.x))
2823
- );
2824
-
2825
- // Adjust position to accomodate tip dimensions
2826
- pos.left -= offset.left.charAt ? offset.user : horizontal !== SHIFT || shift.top || !shift.left && !shift.top ? offset.left : 0;
2827
- pos.top -= offset.top.charAt ? offset.user : vertical !== SHIFT || shift.left || !shift.left && !shift.top ? offset.top : 0;
2828
-
2829
- // Cache details
2830
- cache.left = adjust.left; cache.top = adjust.top;
2831
- cache.corner = newCorner.clone();
2832
- }
2833
-
2834
- /* border width calculator */
2835
- function borderWidth(corner, side, backup) {
2836
- side = !side ? corner[corner.precedance] : side;
2837
-
2838
- var isFluid = tooltip.hasClass(fluidClass),
2839
- isTitleTop = elems.titlebar && corner.y === TOP,
2840
- elem = isTitleTop ? elems.titlebar : elems.tooltip,
2841
- css = 'border-' + side + '-width',
2842
- val;
2843
-
2844
- // Grab the border-width value (add fluid class if needed)
2845
- tooltip.addClass(fluidClass);
2846
- val = parseInt(elem.css(css), 10);
2847
- val = (backup ? val || parseInt(tooltip.css(css), 10) : val) || 0;
2848
- tooltip.toggleClass(fluidClass, isFluid);
2849
-
2850
- return val;
2851
- }
2852
-
2853
- function borderRadius(corner) {
2854
- var isTitleTop = elems.titlebar && corner.y === TOP,
2855
- elem = isTitleTop ? elems.titlebar : elems.content,
2856
- moz = $.browser.mozilla,
2857
- prefix = moz ? '-moz-' : $.browser.webkit ? '-webkit-' : '',
2858
- side = corner.y + (moz ? '' : '-') + corner.x,
2859
- css = prefix + (moz ? 'border-radius-' + side : 'border-' + side + '-radius');
2860
-
2861
- return parseInt(elem.css(css), 10) || parseInt(tooltip.css(css), 10) || 0;
2862
- }
2863
-
2864
- function calculateSize(corner) {
2865
- var y = corner.precedance === Y,
2866
- width = size [ y ? WIDTH : HEIGHT ],
2867
- height = size [ y ? HEIGHT : WIDTH ],
2868
- isCenter = corner.string().indexOf(CENTER) > -1,
2869
- base = width * (isCenter ? 0.5 : 1),
2870
- pow = Math.pow,
2871
- round = Math.round,
2872
- bigHyp, ratio, result,
2873
-
2874
- smallHyp = Math.sqrt( pow(base, 2) + pow(height, 2) ),
2875
-
2876
- hyp = [
2877
- (border / base) * smallHyp, (border / height) * smallHyp
2878
- ];
2879
- hyp[2] = Math.sqrt( pow(hyp[0], 2) - pow(border, 2) );
2880
- hyp[3] = Math.sqrt( pow(hyp[1], 2) - pow(border, 2) );
2881
-
2882
- bigHyp = smallHyp + hyp[2] + hyp[3] + (isCenter ? 0 : hyp[0]);
2883
- ratio = bigHyp / smallHyp;
2884
-
2885
- result = [ round(ratio * height), round(ratio * width) ];
2886
- return { height: result[ y ? 0 : 1 ], width: result[ y ? 1 : 0 ] };
2887
- }
2888
-
2889
- $.extend(self, {
2890
- init: function()
2891
- {
2892
- var enabled = self.detectCorner() && (hasCanvas || $.browser.msie);
2893
-
2894
- // Determine tip corner and type
2895
- if(enabled) {
2896
- // Create a new tip and draw it
2897
- self.create();
2898
- self.update();
2899
-
2900
- // Bind update events
2901
- tooltip.unbind(namespace).bind('tooltipmove'+namespace, reposition);
2902
- }
2903
-
2904
- return enabled;
2905
- },
2906
-
2907
- detectCorner: function()
2908
- {
2909
- var corner = opts.corner,
2910
- posOptions = qTip.options.position,
2911
- at = posOptions.at,
2912
- my = posOptions.my.string ? posOptions.my.string() : posOptions.my;
2913
-
2914
- // Detect corner and mimic properties
2915
- if(corner === FALSE || (my === FALSE && at === FALSE)) {
2916
- return FALSE;
2917
- }
2918
- else {
2919
- if(corner === TRUE) {
2920
- self.corner = new PLUGINS.Corner(my);
2921
- }
2922
- else if(!corner.string) {
2923
- self.corner = new PLUGINS.Corner(corner);
2924
- self.corner.fixed = TRUE;
2925
- }
2926
- }
2927
-
2928
- // Cache it
2929
- cache.corner = new PLUGINS.Corner( self.corner.string() );
2930
-
2931
- return self.corner.string() !== 'centercenter';
2932
- },
2933
-
2934
- detectColours: function(actual) {
2935
- var i, fill, border,
2936
- tip = elems.tip.css('cssText', ''),
2937
- corner = actual || self.corner,
2938
- precedance = corner[ corner.precedance ],
2939
-
2940
- borderSide = 'border-' + precedance + '-color',
2941
- borderSideCamel = 'border' + precedance.charAt(0) + precedance.substr(1) + 'Color',
2942
-
2943
- invalid = /rgba?\(0, 0, 0(, 0)?\)|transparent|#123456/i,
2944
- backgroundColor = 'background-color',
2945
- transparent = 'transparent',
2946
- important = ' !important',
2947
-
2948
- useTitle = elems.titlebar && (corner.y === TOP || (corner.y === CENTER && tip.position().top + (size.height / 2) + opts.offset < elems.titlebar.outerHeight(1))),
2949
- colorElem = useTitle ? elems.titlebar : elems.tooltip;
2950
-
2951
- // Apply the fluid class so we can see our CSS values properly
2952
- tooltip.addClass(fluidClass);
2953
-
2954
- // Detect tip colours from CSS styles
2955
- color.fill = fill = tip.css(backgroundColor);
2956
- color.border = border = tip[0].style[ borderSideCamel ] || tip.css(borderSide) || tooltip.css(borderSide);
2957
-
2958
- // Make sure colours are valid
2959
- if(!fill || invalid.test(fill)) {
2960
- color.fill = colorElem.css(backgroundColor) || transparent;
2961
- if(invalid.test(color.fill)) {
2962
- color.fill = tooltip.css(backgroundColor) || fill;
2963
- }
2964
- }
2965
- if(!border || invalid.test(border) || border === $(document.body).css('color')) {
2966
- color.border = colorElem.css(borderSide) || transparent;
2967
- if(invalid.test(color.border) || color.border === colorElem.css('color')) {
2968
- color.border = tooltip.css(borderSide) || tooltip.css(borderSideCamel) || border;
2969
- }
2970
- }
2971
-
2972
- // Reset background and border colours
2973
- $('*', tip).add(tip).css('cssText', backgroundColor+':'+transparent+important+';border:0'+important+';');
2974
-
2975
- // Remove fluid class
2976
- tooltip.removeClass(fluidClass);
2977
- },
2978
-
2979
- create: function()
2980
- {
2981
- var width = size.width,
2982
- height = size.height,
2983
- vml;
2984
-
2985
- // Remove previous tip element if present
2986
- if(elems.tip) { elems.tip.remove(); }
2987
-
2988
- // Create tip element and prepend to the tooltip
2989
- elems.tip = $('<div />', { 'class': 'ui-tooltip-tip' }).css({ width: width, height: height }).prependTo(tooltip);
2990
-
2991
- // Create tip drawing element(s)
2992
- if(hasCanvas) {
2993
- // save() as soon as we create the canvas element so FF2 doesn't bork on our first restore()!
2994
- $('<canvas />').appendTo(elems.tip)[0].getContext('2d').save();
2995
- }
2996
- else {
2997
- vml = '<vml:shape coordorigin="0,0" style="display:inline-block; position:absolute; behavior:url(#default#VML);"></vml:shape>';
2998
- elems.tip.html(vml + vml);
2999
-
3000
- // Prevent mousing down on the tip since it causes problems with .live() handling in IE due to VML
3001
- $('*', elems.tip).bind('click mousedown', function(event) { event.stopPropagation(); });
3002
- }
3003
- },
3004
-
3005
- update: function(corner, position)
3006
- {
3007
- var tip = elems.tip,
3008
- inner = tip.children(),
3009
- width = size.width,
3010
- height = size.height,
3011
- regular = 'px solid ',
3012
- transparent = 'px dashed transparent', // Dashed IE6 border-transparency hack. Awesome!
3013
- mimic = opts.mimic,
3014
- round = Math.round,
3015
- precedance, context, coords, translate, newSize;
3016
-
3017
- // Re-determine tip if not already set
3018
- if(!corner) { corner = cache.corner || self.corner; }
3019
-
3020
- // Use corner property if we detect an invalid mimic value
3021
- if(mimic === FALSE) { mimic = corner; }
3022
-
3023
- // Otherwise inherit mimic properties from the corner object as necessary
3024
- else {
3025
- mimic = new PLUGINS.Corner(mimic);
3026
- mimic.precedance = corner.precedance;
3027
-
3028
- if(mimic.x === 'inherit') { mimic.x = corner.x; }
3029
- else if(mimic.y === 'inherit') { mimic.y = corner.y; }
3030
- else if(mimic.x === mimic.y) {
3031
- mimic[ corner.precedance ] = corner[ corner.precedance ];
3032
- }
3033
- }
3034
- precedance = mimic.precedance;
3035
-
3036
- // Ensure the tip width.height are relative to the tip position
3037
- if(corner.precedance === X) { swapDimensions(); }
3038
- else { resetDimensions(); }
3039
-
3040
- // Set the tip dimensions
3041
- elems.tip.css({
3042
- width: (width = size.width),
3043
- height: (height = size.height)
3044
- });
3045
-
3046
- // Update our colours
3047
- self.detectColours(corner);
3048
-
3049
- // Detect border width, taking into account colours
3050
- if(color.border !== 'transparent') {
3051
- // Grab border width
3052
- border = borderWidth(corner, NULL, TRUE);
3053
-
3054
- // If border width isn't zero, use border color as fill (1.0 style tips)
3055
- if(opts.border === 0 && border > 0) { color.fill = color.border; }
3056
-
3057
- // Set border width (use detected border width if opts.border is true)
3058
- self.border = border = opts.border !== TRUE ? opts.border : border;
3059
- }
3060
-
3061
- // Border colour was invalid, set border to zero
3062
- else { self.border = border = 0; }
3063
-
3064
- // Calculate coordinates
3065
- coords = calculateTip(mimic, width , height);
3066
-
3067
- // Determine tip size
3068
- self.size = newSize = calculateSize(corner);
3069
- tip.css(newSize);
3070
-
3071
- // Calculate tip translation
3072
- if(corner.precedance === Y) {
3073
- translate = [
3074
- round(mimic.x === LEFT ? border : mimic.x === RIGHT ? newSize.width - width - border : (newSize.width - width) / 2),
3075
- round(mimic.y === TOP ? newSize.height - height : 0)
3076
- ];
3077
- }
3078
- else {
3079
- translate = [
3080
- round(mimic.x === LEFT ? newSize.width - width : 0),
3081
- round(mimic.y === TOP ? border : mimic.y === BOTTOM ? newSize.height - height - border : (newSize.height - height) / 2)
3082
- ];
3083
- }
3084
-
3085
- // Canvas drawing implementation
3086
- if(hasCanvas) {
3087
- // Set the canvas size using calculated size
3088
- inner.attr(newSize);
3089
-
3090
- // Grab canvas context and clear/save it
3091
- context = inner[0].getContext('2d');
3092
- context.restore(); context.save();
3093
- context.clearRect(0,0,3000,3000);
3094
-
3095
- // Set properties
3096
- context.fillStyle = color.fill;
3097
- context.strokeStyle = color.border;
3098
- context.lineWidth = border * 2;
3099
- context.lineJoin = 'miter';
3100
- context.miterLimit = 100;
3101
-
3102
- // Translate origin
3103
- context.translate(translate[0], translate[1]);
3104
-
3105
- // Draw the tip
3106
- context.beginPath();
3107
- context.moveTo(coords[0][0], coords[0][1]);
3108
- context.lineTo(coords[1][0], coords[1][1]);
3109
- context.lineTo(coords[2][0], coords[2][1]);
3110
- context.closePath();
3111
-
3112
- // Apply fill and border
3113
- if(border) {
3114
- // Make sure transparent borders are supported by doing a stroke
3115
- // of the background colour before the stroke colour
3116
- if(tooltip.css('background-clip') === 'border-box') {
3117
- context.strokeStyle = color.fill;
3118
- context.stroke();
3119
- }
3120
- context.strokeStyle = color.border;
3121
- context.stroke();
3122
- }
3123
- context.fill();
3124
- }
3125
-
3126
- // VML (IE Proprietary implementation)
3127
- else {
3128
- // Setup coordinates string
3129
- coords = 'm' + coords[0][0] + ',' + coords[0][1] + ' l' + coords[1][0] +
3130
- ',' + coords[1][1] + ' ' + coords[2][0] + ',' + coords[2][1] + ' xe';
3131
-
3132
- // Setup VML-specific offset for pixel-perfection
3133
- translate[2] = border && /^(r|b)/i.test(corner.string()) ?
3134
- parseFloat($.browser.version, 10) === 8 ? 2 : 1 : 0;
3135
-
3136
- // Set initial CSS
3137
- inner.css({
3138
- antialias: ''+(mimic.string().indexOf(CENTER) > -1),
3139
- left: translate[0] - (translate[2] * Number(precedance === X)),
3140
- top: translate[1] - (translate[2] * Number(precedance === Y)),
3141
- width: width + border,
3142
- height: height + border
3143
- })
3144
- .each(function(i) {
3145
- var $this = $(this);
3146
-
3147
- // Set shape specific attributes
3148
- $this[ $this.prop ? 'prop' : 'attr' ]({
3149
- coordsize: (width+border) + ' ' + (height+border),
3150
- path: coords,
3151
- fillcolor: color.fill,
3152
- filled: !!i,
3153
- stroked: !i
3154
- })
3155
- .css({ display: border || i ? 'block' : 'none' });
3156
-
3157
- // Check if border is enabled and add stroke element
3158
- if(!i && $this.html() === '') {
3159
- $this.html(
3160
- '<vml:stroke weight="'+(border*2)+'px" color="'+color.border+'" miterlimit="1000" joinstyle="miter" ' +
3161
- ' style="behavior:url(#default#VML); display:inline-block;" />'
3162
- );
3163
- }
3164
- });
3165
- }
3166
-
3167
- // Position if needed
3168
- if(position !== FALSE) { self.position(corner); }
3169
- },
3170
-
3171
- // Tip positioning method
3172
- position: function(corner)
3173
- {
3174
- var tip = elems.tip,
3175
- position = {},
3176
- userOffset = Math.max(0, opts.offset),
3177
- precedance, dimensions, corners;
3178
-
3179
- // Return if tips are disabled or tip is not yet rendered
3180
- if(opts.corner === FALSE || !tip) { return FALSE; }
3181
-
3182
- // Inherit corner if not provided
3183
- corner = corner || self.corner;
3184
- precedance = corner.precedance;
3185
-
3186
- // Determine which tip dimension to use for adjustment
3187
- dimensions = calculateSize(corner);
3188
-
3189
- // Setup corners and offset array
3190
- corners = [ corner.x, corner.y ];
3191
- if(precedance === X) { corners.reverse(); }
3192
-
3193
- // Calculate tip position
3194
- $.each(corners, function(i, side) {
3195
- var b, br;
3196
-
3197
- if(side === CENTER) {
3198
- b = precedance === Y ? LEFT : TOP;
3199
- position[ b ] = '50%';
3200
- position['margin-' + b] = -Math.round(dimensions[ precedance === Y ? WIDTH : HEIGHT ] / 2) + userOffset;
3201
- }
3202
- else {
3203
- b = borderWidth(corner, side);
3204
- br = borderRadius(corner);
3205
-
3206
- position[ side ] = i ? 0 : (userOffset + (br > b ? br : -b));
3207
- }
3208
- });
3209
-
3210
- // Adjust for tip dimensions
3211
- position[ corner[precedance] ] -= dimensions[ precedance === X ? WIDTH : HEIGHT ];
3212
-
3213
- // Set and return new position
3214
- tip.css({ top: '', bottom: '', left: '', right: '', margin: '' }).css(position);
3215
- return position;
3216
- },
3217
-
3218
- destroy: function()
3219
- {
3220
- // Remove the tip element
3221
- if(elems.tip) { elems.tip.remove(); }
3222
- elems.tip = false;
3223
-
3224
- // Unbind events
3225
- tooltip.unbind(namespace);
3226
- }
3227
- });
3228
-
3229
- self.init();
3230
- }
3231
-
3232
- PLUGINS.tip = function(api)
3233
- {
3234
- var self = api.plugins.tip;
3235
-
3236
- return 'object' === typeof self ? self : (api.plugins.tip = new Tip(api));
3237
- };
3238
-
3239
- // Initialize tip on render
3240
- PLUGINS.tip.initialize = 'render';
3241
-
3242
- // Setup plugin sanitization options
3243
- PLUGINS.tip.sanitize = function(options)
3244
- {
3245
- var style = options.style, opts;
3246
- if(style && 'tip' in style) {
3247
- opts = options.style.tip;
3248
- if(typeof opts !== 'object'){ options.style.tip = { corner: opts }; }
3249
- if(!(/string|boolean/i).test(typeof opts.corner)) { opts.corner = TRUE; }
3250
- if(typeof opts.width !== 'number'){ delete opts.width; }
3251
- if(typeof opts.height !== 'number'){ delete opts.height; }
3252
- if(typeof opts.border !== 'number' && opts.border !== TRUE){ delete opts.border; }
3253
- if(typeof opts.offset !== 'number'){ delete opts.offset; }
3254
- }
3255
- };
3256
-
3257
- // Extend original qTip defaults
3258
- $.extend(TRUE, QTIP.defaults, {
3259
- style: {
3260
- tip: {
3261
- corner: TRUE,
3262
- mimic: FALSE,
3263
- width: 6,
3264
- height: 6,
3265
- border: TRUE,
3266
- offset: 0
3267
- }
3268
- }
3269
- });
3270
-
3271
- PLUGINS.svg = function(api, svg, corner, adjustMethod)
3272
- {
3273
- var doc = $(document),
3274
- elem = svg[0],
3275
- result = {
3276
- width: 0, height: 0,
3277
- position: { top: 1e10, left: 1e10 }
3278
- },
3279
- box, mtx, root, point, tPoint;
3280
-
3281
- // Ascend the parentNode chain until we find an element with getBBox()
3282
- while(!elem.getBBox) { elem = elem.parentNode; }
3283
-
3284
- // Check for a valid bounding box method
3285
- if (elem.getBBox && elem.parentNode) {
3286
- box = elem.getBBox();
3287
- mtx = elem.getScreenCTM();
3288
- root = elem.farthestViewportElement || elem;
3289
-
3290
- // Return if no method is found
3291
- if(!root.createSVGPoint) { return result; }
3292
-
3293
- // Create our point var
3294
- point = root.createSVGPoint();
3295
-
3296
- // Adjust top and left
3297
- point.x = box.x;
3298
- point.y = box.y;
3299
- tPoint = point.matrixTransform(mtx);
3300
- result.position.left = tPoint.x;
3301
- result.position.top = tPoint.y;
3302
-
3303
- // Adjust width and height
3304
- point.x += box.width;
3305
- point.y += box.height;
3306
- tPoint = point.matrixTransform(mtx);
3307
- result.width = tPoint.x - result.position.left;
3308
- result.height = tPoint.y - result.position.top;
3309
-
3310
- // Adjust by scroll offset
3311
- result.position.left += doc.scrollLeft();
3312
- result.position.top += doc.scrollTop();
3313
- }
3314
-
3315
- return result;
3316
- };
3317
-
3318
- /*
3319
- * BGIFrame adaption (http://plugins.jquery.com/project/bgiframe)
3320
- * Special thanks to Brandon Aaron
3321
- */
3322
- function BGIFrame(api)
3323
- {
3324
- var self = this,
3325
- elems = api.elements,
3326
- tooltip = elems.tooltip,
3327
- namespace = '.bgiframe-' + api.id;
3328
-
3329
- $.extend(self, {
3330
- init: function()
3331
- {
3332
- // Create the BGIFrame element
3333
- elems.bgiframe = $('<iframe class="ui-tooltip-bgiframe" frameborder="0" tabindex="-1" src="javascript:\'\';" ' +
3334
- ' style="display:block; position:absolute; z-index:-1; filter:alpha(opacity=0); ' +
3335
- '-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";"></iframe>');
3336
-
3337
- // Append the new element to the tooltip
3338
- elems.bgiframe.appendTo(tooltip);
3339
-
3340
- // Update BGIFrame on tooltip move
3341
- tooltip.bind('tooltipmove'+namespace, self.adjust);
3342
- },
3343
-
3344
- adjust: function()
3345
- {
3346
- var dimensions = api.get('dimensions'), // Determine current tooltip dimensions
3347
- plugin = api.plugins.tip,
3348
- tip = elems.tip,
3349
- tipAdjust, offset;
3350
-
3351
- // Adjust border offset
3352
- offset = parseInt(tooltip.css('border-left-width'), 10) || 0;
3353
- offset = { left: -offset, top: -offset };
3354
-
3355
- // Adjust for tips plugin
3356
- if(plugin && tip) {
3357
- tipAdjust = (plugin.corner.precedance === 'x') ? ['width', 'left'] : ['height', 'top'];
3358
- offset[ tipAdjust[1] ] -= tip[ tipAdjust[0] ]();
3359
- }
3360
-
3361
- // Update bgiframe
3362
- elems.bgiframe.css(offset).css(dimensions);
3363
- },
3364
-
3365
- destroy: function()
3366
- {
3367
- // Remove iframe
3368
- elems.bgiframe.remove();
3369
-
3370
- // Remove bound events
3371
- tooltip.unbind(namespace);
3372
- }
3373
- });
3374
-
3375
- self.init();
3376
- }
3377
-
3378
- PLUGINS.bgiframe = function(api)
3379
- {
3380
- var browser = $.browser,
3381
- self = api.plugins.bgiframe;
3382
-
3383
- // Proceed only if the browser is IE6 and offending elements are present
3384
- if($('select, object').length < 1 || !(browser.msie && (''+browser.version).charAt(0) === '6')) {
3385
- return FALSE;
3386
- }
3387
-
3388
- return 'object' === typeof self ? self : (api.plugins.bgiframe = new BGIFrame(api));
3389
- };
3390
-
3391
- // Plugin needs to be initialized on render
3392
- PLUGINS.bgiframe.initialize = 'render';
3393
-
3394
-
3395
- }));