michael_hintbuble 1.0.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.
@@ -0,0 +1,171 @@
1
+ module Coroutine #:nodoc:
2
+ module MichaelHintbuble #:nodoc:
3
+ module Helpers #:nodoc:
4
+
5
+ # This method returns a javascript tag containing the hint bubble initialization logic. The first argument
6
+ # to this method is <tt>target_id</tt>, the id of the html element to which the hint bubble is anchored.
7
+ # The target_id is required and should be unique (duh). Further options may be provided; those that
8
+ # are specific to the hint bubble are:
9
+ #
10
+ # * <tt>:class</tt> - the css style to assign to the outermost div container (defaults to "michael_hintbuble_bubble")
11
+ # * <tt>:style</tt> - additional css style assignments for outermost div container (defaults to "")
12
+ # * <tt>:position</tt> - css-style value that specifies the hint bubble's relative position, e.g., top, bottom, right, or left (defaults to right)
13
+ # * <tt>:event_names</tt> - an array of strings specifying the events that should trigger the display of the hint bubble
14
+ # * <tt>:before_show</tt> - a Javascript function that will be invoked before the hint bubble is shown
15
+ # * <tt>:after_show</tt> - a Javascript function that will be invoked after the hint bubble has been shown
16
+ # * <tt>:before_hide</tt> - a Javascript function that will be invoked before the hint bubble is hidden
17
+ # * <tt>:after_hide</tt> - a Javascript function that will be invoked after the hint bubble has been hidden
18
+ # * <tt>&block</tt> - HTML markup that will be automatically converted to render's inline option
19
+ #
20
+ # All remaining options are the same as the options available to ActionController::Base#render. Please
21
+ # see the documentation for ActionController::Base#render for further details.
22
+ #
23
+ # ==== Events
24
+ #
25
+ # The library manages repositioning the hint bubble in response to window resizing and scrolling events automatically.
26
+ # You do not need to specify <tt>resize</tt> or <tt>scroll</tt> in the optional event names array.
27
+ #
28
+ # The library defaults to trapping mouse gestures, but it is capable of trapping focus events in addition
29
+ # to or in place of mouse events. When specifying events, you need only reference the positive action: the
30
+ # library can infer the corresponding action to toggle off the tooltip.
31
+ #
32
+ # Valid entries for the events array are any combination of the following options:
33
+ #
34
+ # * <tt>"focus"</tt>
35
+ # * <tt>"mouseover"</tt>
36
+ #
37
+ #
38
+ # ==== Example
39
+ #
40
+ # # Generates:
41
+ # #
42
+ # # <script type="text/javascript">
43
+ # # //<![CDATA[
44
+ # # MichaelHintbuble.Bubble.instances["foo_target_id"] = new MichaelHintbuble.Bubble("What up, foo?", { "event_names": ["mouseover"], "position": "top center" });
45
+ # # //]]>
46
+ # # </script>
47
+ # <%= render_bubble :foo_target_id, :content => "What up, foo?", :position => "top center" %>
48
+ #
49
+ # In this case, a simple hint bubble is produced with the specified text content. The bubble responds to mouseover/mouseout
50
+ # events and centers itself above the target element when shown.
51
+ #
52
+ #
53
+ # ==== Example
54
+ #
55
+ # # Generates:
56
+ # #
57
+ # # <script type="text/javascript">
58
+ # # //<![CDATA[
59
+ # # MichaelHintbuble.Bubble.instances["bar_target_id"] = new MichaelHintbuble.Bubble("<ul><li>Item 1</li><li>Item 2</li></ul>", { "event_names": ["focus"], "position": "center left" });
60
+ # # //]]>
61
+ # # </script>
62
+ # <%= render_bubble :bar_target_id, :event_names => ["focus"], :position => "center left" %>
63
+ # <ul>
64
+ # <li>Item 1</li>
65
+ # <li>Item 2</li>
66
+ # </ul>
67
+ # <% end %>
68
+ #
69
+ # In this case, a slightly more complex hint bubble is produced with the specified markup. The bubble responds to focus/blur
70
+ # events and positions itself to the left of the target.
71
+ #
72
+ def render_bubble(target_id, options = {}, &block)
73
+ options[:inline] = capture(&block) if block_given?
74
+ render_options = extract_bubble_render_options(options, &block)
75
+ javascript_options = bubble_options_to_js(extract_bubble_javascript_options(options))
76
+
77
+ content = escape_javascript(render(options))
78
+
79
+ raise "You gotta specify a target id to register a hint bubble, baby." unless target_id
80
+ raise "You gotta provide content to register a hint bubble, baby." unless content
81
+
82
+ javascript_tag "Event.observe(window, 'load', function() { MichaelHintbuble.Bubble.instances['#{target_id}'] = new MichaelHintbuble.Bubble('#{target_id}', '#{content}', #{javascript_options}) });"
83
+ end
84
+
85
+
86
+ # This method returns a Javascript string that will show the bubble attached to the supplied
87
+ # target id.
88
+ #
89
+ def show_bubble(target_id)
90
+ "MichaelHintbuble.Bubble.show('#{target_id}');"
91
+ end
92
+
93
+
94
+ # This method returns a Javascript string that will hide the bubble attached to the supplied
95
+ # target id.
96
+ #
97
+ def hide_bubble(target_id)
98
+ "MichaelHintbuble.Bubble.hide('#{target_id}');"
99
+ end
100
+
101
+
102
+
103
+ private
104
+
105
+ # This method returns an array of javascript option keys supported by the accompanying
106
+ # javascript library.
107
+ #
108
+ def bubble_javascript_option_keys
109
+ [:class, :style, :position, :event_names, :before_show, :after_show, :before_hide, :after_hide]
110
+ end
111
+
112
+
113
+ # This method converts ruby hashes using underscore notation to js strings using camelcase
114
+ # notation, which is more common in javascript.
115
+ #
116
+ def bubble_options_to_js(options={})
117
+ js_kv_pairs = []
118
+ sorted_keys = options.keys.map { |k| k.to_s }.sort.map { |s| s.to_sym }
119
+
120
+ sorted_keys.each do |key|
121
+ js_key = key.to_s.camelcase(:lower)
122
+ js_value = "null"
123
+
124
+ unless options[key].empty?
125
+ case key
126
+ when :before_show, :after_show, :before_hide, :after_hide
127
+ js_value = "#{options[key]}"
128
+ when :event_names
129
+ js_value = "['" + options[key].join("','") + "']"
130
+ else
131
+ js_value = "'#{options[key]}'"
132
+ end
133
+ end
134
+
135
+ js_kv_pairs << "#{js_key}:#{js_value}"
136
+ end
137
+
138
+ "{#{js_kv_pairs.join(',')}}"
139
+ end
140
+
141
+
142
+ # This method returns a hash with javascript options. It also inspects the supplied options
143
+ # and adds defaults as necessary.
144
+ #
145
+ def extract_bubble_javascript_options(options)
146
+ js_options = options.reject { |k,v| !bubble_javascript_option_keys.include?(k) }
147
+
148
+ js_options[:class] = "michael_hintbuble_bubble" if js_options[:class].blank?
149
+ js_options[:position] = "right" if js_options[:position].blank?
150
+ js_options[:event_names] = [] if js_options[:event_names].blank?
151
+
152
+ js_options[:class] = js_options[:class].to_s
153
+ js_options[:event_names] = js_options[:event_names].uniq.map { |en| en.to_s }
154
+ js_options[:event_names] << "mouseover" if js_options[:event_names].empty?
155
+ js_options[:event_names] << "resize" unless js_options[:event_names].include?("resize")
156
+ js_options[:event_names] << "scroll" unless js_options[:event_names].include?("scroll")
157
+
158
+ js_options
159
+ end
160
+
161
+
162
+ # This method returns a hash with rendering options. It also inspects the supplied options
163
+ # and adds defaults as necessary.
164
+ #
165
+ def extract_bubble_render_options(options)
166
+ options.reject { |k,v| bubble_javascript_option_keys.include?(k) }
167
+ end
168
+
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,10 @@
1
+ # external gems
2
+ require "action_pack"
3
+
4
+
5
+ # helpers
6
+ require File.dirname(__FILE__) + "/michael_hintbuble/helpers"
7
+
8
+
9
+ # add action view extensions
10
+ ActionView::Base.module_eval { include Coroutine::MichaelHintbuble::Helpers }
@@ -0,0 +1,67 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{michael_hintbuble}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Coroutine", "Tim Lowrimore", "John Dugan"]
12
+ s.date = %q{2010-10-10}
13
+ s.description = %q{Michael HintBuble allows you to generate hint bubbles and tooltips in Rails applications using the same syntax used for rendering templates.}
14
+ s.email = %q{gems@coroutine.com}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ ".specification",
21
+ "MIT-LICENSE",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "generators/michael_hintbuble/michael_hintbuble_generator.rb",
26
+ "generators/michael_hintbuble/templates/michael_hintbuble.css",
27
+ "generators/michael_hintbuble/templates/michael_hintbuble.js",
28
+ "generators/michael_hintbuble/templates/michael_hintbuble_pointer.png",
29
+ "init.rb",
30
+ "lib/generators/michael_hintbuble/michael_hintbuble_generator.rb",
31
+ "lib/generators/michael_hintbuble/templates/michael_hintbuble.css",
32
+ "lib/generators/michael_hintbuble/templates/michael_hintbuble.js",
33
+ "lib/generators/michael_hintbuble/templates/michael_hintbuble_pointer.png",
34
+ "lib/michael_hintbuble.rb",
35
+ "lib/michael_hintbuble/helpers.rb",
36
+ "michael_hintbuble.gemspec",
37
+ "rails/init.rb",
38
+ "test/michael_hintbuble/helpers_test.rb",
39
+ "test/test_helper.rb"
40
+ ]
41
+ s.homepage = %q{http://github.com/coroutine/michael_hintbuble}
42
+ s.rdoc_options = ["--charset=UTF-8"]
43
+ s.require_paths = ["lib"]
44
+ s.rubygems_version = %q{1.3.7}
45
+ s.summary = %q{Dead simple, beautiful hint bubbles for Rails.}
46
+ s.test_files = [
47
+ "test/michael_hintbuble/helpers_test.rb",
48
+ "test/test_helper.rb"
49
+ ]
50
+
51
+ if s.respond_to? :specification_version then
52
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
53
+ s.specification_version = 3
54
+
55
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
56
+ s.add_runtime_dependency(%q<actionpack>, [">= 2.3.4"])
57
+ s.add_development_dependency(%q<activesupport>, [">= 2.3.4"])
58
+ else
59
+ s.add_dependency(%q<actionpack>, [">= 2.3.4"])
60
+ s.add_dependency(%q<activesupport>, [">= 2.3.4"])
61
+ end
62
+ else
63
+ s.add_dependency(%q<actionpack>, [">= 2.3.4"])
64
+ s.add_dependency(%q<activesupport>, [">= 2.3.4"])
65
+ end
66
+ end
67
+
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require "michael_hintbuble"
@@ -0,0 +1,156 @@
1
+ #---------------------------------------------------------
2
+ # Requirements
3
+ #---------------------------------------------------------
4
+
5
+ # all generic stuff required by test helper
6
+ require "test/test_helper"
7
+
8
+
9
+
10
+ #---------------------------------------------------------
11
+ # Class Definitions
12
+ #---------------------------------------------------------
13
+
14
+ class TestView < ActionView::Base
15
+ end
16
+
17
+
18
+
19
+ #---------------------------------------------------------
20
+ # Tests
21
+ #---------------------------------------------------------
22
+
23
+ class MichaelHintbubleHelpersTest < ActionView::TestCase
24
+
25
+ def setup
26
+ @view = TestView.new
27
+ end
28
+
29
+
30
+ def test_composition
31
+ assert @view.respond_to?(:render_bubble, false)
32
+ assert @view.respond_to?(:show_bubble, false)
33
+ assert @view.respond_to?(:hide_bubble, false)
34
+ assert @view.respond_to?(:bubble_javascript_option_keys, true)
35
+ assert @view.respond_to?(:bubble_options_to_js, true)
36
+ assert @view.respond_to?(:extract_bubble_javascript_options, true)
37
+ assert @view.respond_to?(:extract_bubble_render_options, true)
38
+ end
39
+
40
+
41
+ #---------------------------------------------------------
42
+ # Public methods
43
+ #---------------------------------------------------------
44
+
45
+ def test_render_bubble_with_simple_options
46
+ text = "Who wants to play volleyball on a court with a four-foot net?"
47
+ default_options = "{class:'michael_hintbuble_bubble',eventNames:['mouseover','resize','scroll'],position:'right'}"
48
+
49
+ expected = "<script type=\"text/javascript\">\n" \
50
+ "//<![CDATA[\n" \
51
+ "Event.observe(window, 'load', function() { MichaelHintbuble.Bubble.instances['come_fly_with_me'] = new MichaelHintbuble.Bubble('come_fly_with_me', '#{text}', #{default_options}) });\n" \
52
+ "//]]>\n" \
53
+ "</script>"
54
+ actual = @view.render_bubble(:come_fly_with_me, :text => text)
55
+
56
+ assert_equal expected, actual
57
+ end
58
+
59
+
60
+ def test_show_bubble
61
+ expected = "MichaelHintbuble.Bubble.show('come_fly_with_me');"
62
+ actual = @view.show_bubble(:come_fly_with_me)
63
+
64
+ assert_equal expected, actual
65
+ end
66
+
67
+
68
+ def test_hide_dialog
69
+ expected = "MichaelHintbuble.Bubble.hide('come_fly_with_me');"
70
+ actual = @view.hide_bubble(:come_fly_with_me)
71
+
72
+ assert_equal expected, actual
73
+ end
74
+
75
+
76
+
77
+ #---------------------------------------------------------
78
+ # Public methods
79
+ #---------------------------------------------------------
80
+
81
+ def test_bubble_javascript_option_keys
82
+ expected = [:class, :style, :position, :event_names, :before_show, :after_show, :before_hide, :after_hide]
83
+ actual = @view.send(:bubble_javascript_option_keys)
84
+
85
+ assert_equal expected, actual
86
+ end
87
+
88
+
89
+ def test_bubble_options_to_js
90
+ options = {
91
+ :class => "error_container",
92
+ :position => "top",
93
+ :event_names => ["mouseover","resize","scroll"],
94
+ :before_show => "function{ alert('hello, world!'); }",
95
+ :after_show => "function{ alert('goodbye, world!'); }"
96
+ }
97
+
98
+ expected = "{" +
99
+ "afterShow:function{ alert('goodbye, world!'); }" + "," +
100
+ "beforeShow:function{ alert('hello, world!'); }" + "," +
101
+ "class:'error_container'" + "," +
102
+ "eventNames:['mouseover','resize','scroll']" + "," +
103
+ "position:'top'" +
104
+ "}"
105
+ actual = @view.send(:bubble_options_to_js, options)
106
+
107
+ assert_equal expected, actual
108
+ end
109
+
110
+
111
+ def test_extract_bubble_javascript_options
112
+ options = { :class => "my_class", :event_names => ["focus", "resize", "scroll"], :position => "right", :text => "Text" }
113
+ js_options = @view.send(:extract_bubble_javascript_options, options)
114
+
115
+ assert_equal true, js_options.has_key?(:class)
116
+ assert_equal true, js_options.has_key?(:event_names)
117
+ assert_equal true, js_options.has_key?(:position)
118
+ assert_equal false, js_options.has_key?(:text)
119
+
120
+ assert_equal "my_class", js_options[:class]
121
+ assert_equal ["focus", "resize", "scroll"], js_options[:event_names]
122
+ assert_equal "right", js_options[:position]
123
+ end
124
+ def test_extract_bubble_javascript_options_for_default_class
125
+ options = { :event_names => ["focus", "resize", "scroll"], :position => "top right", :text => "Text" }
126
+ js_options = @view.send(:extract_bubble_javascript_options, options)
127
+
128
+ assert_equal "michael_hintbuble_bubble", js_options[:class]
129
+ end
130
+ def test_extract_bubble_javascript_options_for_default_event_names
131
+ options = { :class => "my_class", :position => "top right", :text => "Text" }
132
+ js_options = @view.send(:extract_bubble_javascript_options, options)
133
+
134
+ assert_equal ["mouseover", "resize", "scroll"], js_options[:event_names]
135
+ end
136
+ def test_extract_bubble_javascript_options_for_default_position
137
+ options = { :class => "my_class", :event_names => ["focus", "resize", "scroll"], :text => "Text" }
138
+ js_options = @view.send(:extract_bubble_javascript_options, options)
139
+
140
+ assert_equal "right", js_options[:position]
141
+ end
142
+
143
+
144
+ def test_extract_bubble_render_options
145
+ options = { :class => :my_class, :event_names => [:focus, :resize, :scroll], :position => "top right", :text => "Text" }
146
+ render_options = @view.send(:extract_bubble_render_options, options)
147
+
148
+ assert_equal false, render_options.has_key?(:class)
149
+ assert_equal false, render_options.has_key?(:event_names)
150
+ assert_equal false, render_options.has_key?(:position)
151
+ assert_equal true, render_options.has_key?(:text)
152
+
153
+ assert_equal "Text", render_options[:text]
154
+ end
155
+
156
+ end
@@ -0,0 +1,16 @@
1
+ #----------------------------------------------------------
2
+ # Requirements
3
+ #----------------------------------------------------------
4
+
5
+ # rails stuff
6
+ require "rubygems"
7
+ require "active_support"
8
+ require "active_support/test_case"
9
+ require "action_controller"
10
+ require "action_controller/test_case"
11
+ require "action_view"
12
+ require "action_view/test_case"
13
+ require "test/unit"
14
+
15
+ # the plugin itself
16
+ require "#{File.dirname(__FILE__)}/../init"
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: michael_hintbuble
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Coroutine
14
+ - Tim Lowrimore
15
+ - John Dugan
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2010-10-10 00:00:00 -05:00
21
+ default_executable:
22
+ dependencies:
23
+ - !ruby/object:Gem::Dependency
24
+ name: actionpack
25
+ prerelease: false
26
+ requirement: &id001 !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ hash: 11
32
+ segments:
33
+ - 2
34
+ - 3
35
+ - 4
36
+ version: 2.3.4
37
+ type: :runtime
38
+ version_requirements: *id001
39
+ - !ruby/object:Gem::Dependency
40
+ name: activesupport
41
+ prerelease: false
42
+ requirement: &id002 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ hash: 11
48
+ segments:
49
+ - 2
50
+ - 3
51
+ - 4
52
+ version: 2.3.4
53
+ type: :development
54
+ version_requirements: *id002
55
+ description: Michael HintBuble allows you to generate hint bubbles and tooltips in Rails applications using the same syntax used for rendering templates.
56
+ email: gems@coroutine.com
57
+ executables: []
58
+
59
+ extensions: []
60
+
61
+ extra_rdoc_files:
62
+ - README.rdoc
63
+ files:
64
+ - .gitignore
65
+ - .specification
66
+ - MIT-LICENSE
67
+ - README.rdoc
68
+ - Rakefile
69
+ - VERSION
70
+ - generators/michael_hintbuble/michael_hintbuble_generator.rb
71
+ - generators/michael_hintbuble/templates/michael_hintbuble.css
72
+ - generators/michael_hintbuble/templates/michael_hintbuble.js
73
+ - generators/michael_hintbuble/templates/michael_hintbuble_pointer.png
74
+ - init.rb
75
+ - lib/generators/michael_hintbuble/michael_hintbuble_generator.rb
76
+ - lib/generators/michael_hintbuble/templates/michael_hintbuble.css
77
+ - lib/generators/michael_hintbuble/templates/michael_hintbuble.js
78
+ - lib/generators/michael_hintbuble/templates/michael_hintbuble_pointer.png
79
+ - lib/michael_hintbuble.rb
80
+ - lib/michael_hintbuble/helpers.rb
81
+ - michael_hintbuble.gemspec
82
+ - rails/init.rb
83
+ - test/michael_hintbuble/helpers_test.rb
84
+ - test/test_helper.rb
85
+ has_rdoc: true
86
+ homepage: http://github.com/coroutine/michael_hintbuble
87
+ licenses: []
88
+
89
+ post_install_message:
90
+ rdoc_options:
91
+ - --charset=UTF-8
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ hash: 3
100
+ segments:
101
+ - 0
102
+ version: "0"
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ hash: 3
109
+ segments:
110
+ - 0
111
+ version: "0"
112
+ requirements: []
113
+
114
+ rubyforge_project:
115
+ rubygems_version: 1.3.7
116
+ signing_key:
117
+ specification_version: 3
118
+ summary: Dead simple, beautiful hint bubbles for Rails.
119
+ test_files:
120
+ - test/michael_hintbuble/helpers_test.rb
121
+ - test/test_helper.rb