textile_editor 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_DISABLE_SHARED_GEMS: "1"
@@ -0,0 +1,2 @@
1
+ *swp
2
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source :rubygems
2
+
3
+ group :test do
4
+ gem 'test-unit'
5
+ end
@@ -0,0 +1,10 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ test-unit (2.1.1)
5
+
6
+ PLATFORMS
7
+ ruby
8
+
9
+ DEPENDENCIES
10
+ test-unit
@@ -0,0 +1,41 @@
1
+ require 'rake'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+
6
+ begin
7
+ require 'jeweler'
8
+
9
+ Jeweler::Tasks.new do |s|
10
+ s.name = 'textile-editor'
11
+ s.author = "Ben Mills"
12
+ s.email = "ben@unfiniti.com"
13
+ s.summary = "Provide textile toolbar to textareas"
14
+ s.homepage = "http://unfiniti.com"
15
+
16
+ s.test_files = Dir['test/**/*']
17
+ end
18
+ rescue LoadError
19
+ puts "Jeweler not installed."
20
+ exit 1
21
+ end
22
+
23
+
24
+ desc 'Default: run unit tests.'
25
+ task :default => :test
26
+
27
+ desc 'Test the textile_editor plugin.'
28
+ Rake::TestTask.new(:test) do |t|
29
+ t.libs << 'lib'
30
+ t.pattern = 'test/**/*_test.rb'
31
+ t.verbose = true
32
+ end
33
+
34
+ desc 'Generate documentation for the textile_editor_helper plugin.'
35
+ Rake::RDocTask.new(:rdoc) do |rdoc|
36
+ rdoc.rdoc_dir = 'rdoc'
37
+ rdoc.title = 'TextileEditor'
38
+ rdoc.options << '--line-numbers' << '--inline-source'
39
+ rdoc.rdoc_files.include('README')
40
+ rdoc.rdoc_files.include('lib/**/*.rb')
41
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,187 @@
1
+ module ActionView
2
+ module Helpers
3
+
4
+ class FormBuilder
5
+ def textile_editor(method, options = {})
6
+ @template.textile_editor(@object_name, method, options.merge(:object => @object))
7
+ end
8
+ end
9
+
10
+ module PrototypeHelper
11
+ end
12
+
13
+ module FormHelper
14
+ # Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
15
+ # on an object assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
16
+ # hash with +options+ and places the textile toolbar above it
17
+ #
18
+ # ==== Examples
19
+ # textile_editor(:post, :body, :cols => 20, :rows => 40)
20
+ # # => <textarea cols="20" rows="40" id="post_body" name="post[body]">
21
+ # # #{@post.body}
22
+ # # </textarea>
23
+ #
24
+ # textile_editor(:comment, :text, :size => "20x30")
25
+ # # => <textarea cols="20" rows="30" id="comment_text" name="comment[text]">
26
+ # # #{@comment.text}
27
+ # # </textarea>
28
+ #
29
+ # textile_editor(:application, :notes, :cols => 40, :rows => 15, :class => 'app_input')
30
+ # # => <textarea cols="40" rows="15" id="application_notes" name="application[notes]" class="app_input">
31
+ # # #{@application.notes}
32
+ # # </textarea>
33
+ #
34
+ # textile_editor(:entry, :body, :size => "20x20", :disabled => 'disabled')
35
+ # # => <textarea cols="20" rows="20" id="entry_body" name="entry[body]" disabled="disabled">
36
+ # # #{@entry.body}
37
+ # # </textarea>
38
+ def textile_editor(object_name, method, options = {})
39
+ editor_id = options[:id] || '%s_%s' % [object_name, method]
40
+ mode = options.delete(:simple) ? 'simple' : 'extended'
41
+ (@textile_editor_ids ||= []) << [editor_id.to_s, mode.to_s]
42
+
43
+ InstanceTag.new(object_name, method, self, options.delete(:object)).to_text_area_tag(options)
44
+ end
45
+
46
+ def textile_editor_options(options={})
47
+ (@textile_editor_options ||= { :framework => :prototype }).merge! options
48
+ end
49
+
50
+ def textile_editor_support
51
+ output = []
52
+ output << stylesheet_link_tag('textile-editor')
53
+ output << javascript_include_tag('textile-editor')
54
+ output.join("\n").html_safe
55
+ end
56
+
57
+ # registers a new button for the Textile Editor toolbar
58
+ # Parameters:
59
+ # * +text+: text to display (contents of button tag, so HTML is valid as well)
60
+ # * +options+: options Hash as supported by +content_tag+ helper in Rails
61
+ #
62
+ # Example:
63
+ # The following example adds a button labeled 'Greeting' which triggers an
64
+ # alert:
65
+ #
66
+ # <% textile_editor_button 'Greeting', :onclick => "alert('Hello!')" %>
67
+ #
68
+ # *Note*: this method must be called before +textile_editor_initialize+
69
+ def textile_editor_button(text, options={})
70
+ return textile_editor_button_separator if text == :separator
71
+ button = content_tag(:button, text, options)
72
+ button = "TextileEditor.buttons.push(\"%s\");" % escape_javascript(button)
73
+ (@textile_editor_buttons ||= []) << button
74
+ end
75
+
76
+ def textile_editor_button_separator(options={})
77
+ button = "TextileEditor.buttons.push(new TextileEditorButtonSeparator('%s'));" % (options[:simple] || '')
78
+ (@textile_editor_buttons ||= []) << button
79
+ end
80
+
81
+ def textile_extract_dom_ids(*dom_ids)
82
+ hash = dom_ids.last.is_a?(Hash) ? dom_ids.pop : {}
83
+ hash.inject(dom_ids) do |ids, (object, fields)|
84
+ ids + Array(fields).map { |field| "%s_%s" % [object, field] }
85
+ end
86
+ end
87
+
88
+ # adds the necessary javascript include tags, stylesheet tags,
89
+ # and load event with necessary javascript to active textile editor(s)
90
+ # sample output:
91
+ # <link href="/stylesheets/textile-editor.css" media="screen" rel="Stylesheet" type="text/css" />
92
+ # <script src="/javascripts/textile-editor.js" type="text/javascript"></script>
93
+ # <script type="text/javascript">
94
+ # document.observe('dom:loaded', function() {
95
+ # TextileEditor.initialize('article_body', 'extended');
96
+ # TextileEditor.initialize('article_body_excerpt', 'simple');
97
+ # });
98
+ # </script>
99
+ #
100
+ # Note: in the case of this helper being called via AJAX, the output will be reduced:
101
+ # <script type="text/javascript">
102
+ # TextileEditor.initialize('article_body', 'extended');
103
+ # TextileEditor.initialize('article_body_excerpt', 'simple');
104
+ # </script>
105
+ #
106
+ # This means that the support files must be loaded outside of the AJAX request, either
107
+ # via a call to this helper or the textile_editor_support() helper
108
+ def textile_editor_initialize(*dom_ids)
109
+ options = textile_editor_options.dup
110
+
111
+ # extract options from last argument if it's a hash
112
+ if dom_ids.last.is_a?(Hash)
113
+ hash = dom_ids.last.dup
114
+ options.merge! hash
115
+ dom_ids.last.delete :framework
116
+ end
117
+
118
+ editor_ids = (@textile_editor_ids || []) + textile_extract_dom_ids(*dom_ids)
119
+ editor_buttons = (@textile_editor_buttons || [])
120
+ output = []
121
+ output << textile_editor_support unless request.xhr?
122
+ output << '<script type="text/javascript">'
123
+ output << '/* <![CDATA[ */'
124
+
125
+ if !request.xhr?
126
+ case options[:framework]
127
+ when :prototype
128
+ output << %{document.observe('dom:loaded', function() \{}
129
+ when :jquery
130
+ output << %{$(document).ready(function() \{}
131
+ end
132
+ end
133
+
134
+ # output << %q{TextileEditor.framework = '%s';} % options[:framework].to_s
135
+ output << editor_buttons.join("\n") if editor_buttons.any?
136
+ editor_ids.each do |editor_id, mode|
137
+ output << %q{TextileEditor.initialize('%s', '%s');} % [editor_id, mode || 'extended']
138
+ end
139
+ output << '});' unless request.xhr?
140
+
141
+ output << '/* ]]> */'
142
+ output << '</script>'
143
+ output.join("\n").html_safe
144
+ end
145
+ end
146
+
147
+ module FormTagHelper
148
+ # Creates a text input area; use a textarea for longer text inputs such as blog posts or descriptions
149
+ # and includes the textile toolbar above it.
150
+ #
151
+ # ==== Options
152
+ # * <tt>:size</tt> - A string specifying the dimensions (columns by rows) of the textarea (e.g., "25x10").
153
+ # * <tt>:rows</tt> - Specify the number of rows in the textarea
154
+ # * <tt>:cols</tt> - Specify the number of columns in the textarea
155
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
156
+ # * Any other key creates standard HTML attributes for the tag.
157
+ #
158
+ # ==== Examples
159
+ # textile_editor_tag 'post'
160
+ # # => <textarea id="post" name="post"></textarea>
161
+ #
162
+ # textile_editor_tag 'bio', @user.bio
163
+ # # => <textarea id="bio" name="bio">This is my biography.</textarea>
164
+ #
165
+ # textile_editor_tag 'body', nil, :rows => 10, :cols => 25
166
+ # # => <textarea cols="25" id="body" name="body" rows="10"></textarea>
167
+ #
168
+ # textile_editor_tag 'body', nil, :size => "25x10"
169
+ # # => <textarea name="body" id="body" cols="25" rows="10"></textarea>
170
+ #
171
+ # textile_editor_tag 'description', "Description goes here.", :disabled => true
172
+ # # => <textarea disabled="disabled" id="description" name="description">Description goes here.</textarea>
173
+ #
174
+ # textile_editor_tag 'comment', nil, :class => 'comment_input'
175
+ # # => <textarea class="comment_input" id="comment" name="comment"></textarea>
176
+ def textile_editor_tag(name, content = nil, options = {})
177
+ editor_id = options[:id] || name
178
+ mode = options.delete(:simple) ? 'simple' : 'extended'
179
+ (@textile_editor_ids ||= []) << [editor_id.to_s, mode.to_s]
180
+
181
+ text_area_tag(name, content, options)
182
+ end
183
+ end
184
+
185
+ end
186
+ end
187
+
@@ -0,0 +1,16 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'yaml'
4
+ require 'test/unit'
5
+ require 'rubygems'
6
+
7
+ require 'action_controller'
8
+ require 'action_controller/cgi_ext'
9
+ require 'action_controller/test_process'
10
+
11
+ # Show backtraces for deprecated behavior for quicker cleanup.
12
+ ActiveSupport::Deprecation.debug = true
13
+
14
+ ActionController::Base.logger = nil
15
+ # ActionController::Base.ignore_missing_templates = false
16
+ ActionController::Routing::Routes.reload rescue nil
@@ -0,0 +1,195 @@
1
+ require File.dirname(__FILE__) + '/abstract_unit'
2
+ require File.dirname(__FILE__) + '/../lib/textile_editor'
3
+ require 'ostruct'
4
+
5
+ class TextileEditorHelperTest < Test::Unit::TestCase
6
+ include ActionView::Helpers::TextHelper
7
+ include ActionView::Helpers::AssetTagHelper
8
+ include ActionView::Helpers::TagHelper
9
+ include ActionView::Helpers::FormHelper
10
+ include ActionView::Helpers::JavaScriptHelper
11
+ # include TextileEditorHelper
12
+
13
+ def setup
14
+ @controller = Class.new do
15
+ attr_reader :url_for_options
16
+ def url_for(options, *parameters_for_method_reference)
17
+ @url_for_options = options
18
+ "http://www.example.com"
19
+ end
20
+
21
+ def request; @request ||= ActionController::TestRequest.new; end
22
+ def response; @response ||= ActionController::TestResponse.new; end
23
+ end
24
+ @controller = @controller.new
25
+ @article = OpenStruct.new(:body => nil)
26
+ end
27
+
28
+ # support methods
29
+ def request
30
+ @controller.request
31
+ end
32
+
33
+ def create_simple_editor(object, field, options={})
34
+ output = textile_editor(object, field, options.merge(:simple => true))
35
+ assert_equal text_area(object, field, options), output
36
+ end
37
+
38
+ def create_extended_editor(object, field, options={})
39
+ output = textile_editor(object, field, options)
40
+ assert_equal text_area(object, field, options), output
41
+ end
42
+
43
+ def framework_initialize_output(framework)
44
+ case framework
45
+ when :prototype
46
+ %{document.observe('dom:loaded', function() \{}
47
+ when :jquery
48
+ %{$(document).ready(function() \{}
49
+ end
50
+ end
51
+
52
+ def pre_initialize_output(framework)
53
+ %{<link href="/stylesheets/textile-editor.css" media="screen" rel="stylesheet" type="text/css" />
54
+ <script src="/javascripts/textile-editor.js" type="text/javascript"></script>
55
+ <script type="text/javascript">
56
+ /* <![CDATA[ */
57
+ } +
58
+ framework_initialize_output(framework)
59
+ end
60
+
61
+ def post_initialize_output
62
+ %{\});
63
+ /* ]]> */
64
+ </script>
65
+ }
66
+ end
67
+
68
+ def expected_initialize_output(framework, editors, button_data=nil)
69
+ expected = [pre_initialize_output(framework)]
70
+ expected << button_data unless button_data.nil?
71
+ expected << editors.map do |editor|
72
+ "TextileEditor.initialize('%s', '%s');" % editor
73
+ end
74
+ expected << post_initialize_output
75
+ expected.join("\n").split("\n").map { |e| e.lstrip }.join("\n").chomp
76
+ end
77
+
78
+ # tests
79
+ def test_textile_editor
80
+ assert_nil @textile_editor_ids
81
+ create_extended_editor('article', 'body')
82
+ assert_equal [['article_body', 'extended']], @textile_editor_ids
83
+ end
84
+
85
+ def test_textile_editor_simple_mode
86
+ assert_nil @textile_editor_ids
87
+ create_simple_editor('article', 'body')
88
+ assert_equal [['article_body', 'simple']], @textile_editor_ids
89
+ end
90
+
91
+ def test_textile_editor_with_custom_id
92
+ assert_nil @textile_editor_ids
93
+ create_extended_editor('article', 'body', :id => 'my_custom_id')
94
+ assert_equal [['my_custom_id', 'extended']], @textile_editor_ids
95
+ end
96
+
97
+ def test_textile_editor_simple_mode_with_custom_id
98
+ assert_nil @textile_editor_ids
99
+ create_simple_editor('article', 'body', :id => 'my_custom_id')
100
+ assert_equal [['my_custom_id', 'simple']], @textile_editor_ids
101
+ end
102
+
103
+ def test_textile_editor_initialize
104
+ create_extended_editor('article', 'body')
105
+ output = textile_editor_initialize()
106
+ assert_equal expected_initialize_output(:prototype, [
107
+ ['article_body', 'extended']
108
+ ]), output
109
+
110
+ create_simple_editor('article', 'body_excerpt')
111
+ output = textile_editor_initialize()
112
+ assert_equal expected_initialize_output(:prototype, [
113
+ ['article_body', 'extended'],
114
+ ['article_body_excerpt', 'simple']
115
+ ]), output
116
+
117
+ output = textile_editor_initialize(:framework => :jquery)
118
+ assert_equal expected_initialize_output(:jquery, [
119
+ ['article_body', 'extended'],
120
+ ['article_body_excerpt', 'simple']
121
+ ]), output
122
+
123
+ # test using custom default options
124
+ textile_editor_options :framework => :jquery
125
+ output = textile_editor_initialize()
126
+ assert_equal expected_initialize_output(:jquery, [
127
+ ['article_body', 'extended'],
128
+ ['article_body_excerpt', 'simple']
129
+ ]), output
130
+ end
131
+
132
+ def test_textile_editor_inititalize_with_arbitrary_ids
133
+ output = textile_editor_initialize(:story_comment, :story_body)
134
+ assert_equal expected_initialize_output(:prototype, [
135
+ ['story_comment', 'extended'],
136
+ ['story_body', 'extended']
137
+ ]), output
138
+ end
139
+
140
+ def test_textile_editor_initialize_with_custom_buttons
141
+ b = '<button id="test_button" onclick="alert(\'Hello!\')" title="Hello world">Hello</button>'
142
+ button_data = ["TextileEditor.buttons.push(\"%s\");" % escape_javascript(b)]
143
+ actual = textile_editor_button('Hello',
144
+ :id => 'test_button',
145
+ :onclick => "alert('Hello!')",
146
+ :title => 'Hello world'
147
+ )
148
+
149
+ assert_equal button_data, actual
150
+
151
+ create_extended_editor('article', 'body')
152
+ output = textile_editor_initialize()
153
+ assert_equal expected_initialize_output(:prototype, [
154
+ ['article_body', 'extended']
155
+ ], button_data), output
156
+ end
157
+
158
+ def test_textile_extract_dom_ids_works_with_arrayed_hash
159
+ hash_with_array = { :recipe => [ :instructions, :introduction ] }
160
+ assert_equal [ 'recipe_instructions', 'recipe_introduction' ], textile_extract_dom_ids(hash_with_array)
161
+ end
162
+
163
+ def test_textile_extract_dom_ids_works_with_hash
164
+ hash_with_symbol = { :story => :title }
165
+ assert_equal [ 'story_title' ], textile_extract_dom_ids(hash_with_symbol)
166
+ end
167
+
168
+ def test_textile_extract_dom_ids_works_with_ids
169
+ straight_id = 'article_page'
170
+ assert_equal [ 'article_page' ], textile_extract_dom_ids(straight_id)
171
+ end
172
+
173
+ def test_textile_extract_dom_ids_works_with_mixed_params
174
+ paramd = %w(article_page)
175
+ paramd += [{
176
+ :recipe => [ :instructions, :introduction ],
177
+ :story => :title
178
+ }]
179
+ assert_equal %w(article_page recipe_instructions recipe_introduction story_title).sort,
180
+ textile_extract_dom_ids(*paramd).sort { |a,b| a.to_s <=> b.to_s }
181
+ end
182
+
183
+ def test_textile_editor_button
184
+ b = '<button id="test_button" onclick="alert(\'Hello!\')" title="Hello world">Hello</button>'
185
+ expected = ['TextileEditor.buttons.push("%s");' % escape_javascript(b)]
186
+
187
+ actual = textile_editor_button('Hello',
188
+ :id => 'test_button',
189
+ :onclick => "alert('Hello!')",
190
+ :title => 'Hello world'
191
+ )
192
+
193
+ assert_equal expected, actual
194
+ end
195
+ end
@@ -0,0 +1,41 @@
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{textile_editor}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Ben Mills"]
12
+ s.date = %q{2010-11-15}
13
+ s.email = %q{ben@unfiniti.com}
14
+ s.files = [
15
+ ".bundle/config",
16
+ ".gitignore",
17
+ "Gemfile",
18
+ "Gemfile.lock",
19
+ "Rakefile",
20
+ "VERSION",
21
+ "lib/textile_editor.rb",
22
+ "textile_editor.gemspec",
23
+ "test/abstract_unit.rb",
24
+ "test/textile_editor_test.rb"
25
+ ]
26
+ s.homepage = %q{http://unfiniti.com}
27
+ s.rdoc_options = ["--charset=UTF-8"]
28
+ s.require_paths = ["lib"]
29
+ s.rubygems_version = %q{1.3.7}
30
+ s.summary = %q{Provide textile toolbar to textareas}
31
+ s.test_files = [
32
+ "test/abstract_unit.rb",
33
+ "test/textile_editor_test.rb"
34
+ ]
35
+
36
+ if s.respond_to? :specification_version then
37
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
38
+ s.specification_version = 3
39
+ end
40
+ end
41
+
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: textile_editor
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Ben Mills
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-11-15 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description:
22
+ email: ben@unfiniti.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - .bundle/config
31
+ - .gitignore
32
+ - Gemfile
33
+ - Gemfile.lock
34
+ - Rakefile
35
+ - VERSION
36
+ - lib/textile_editor.rb
37
+ - textile_editor.gemspec
38
+ - test/abstract_unit.rb
39
+ - test/textile_editor_test.rb
40
+ has_rdoc: true
41
+ homepage: http://unfiniti.com
42
+ licenses: []
43
+
44
+ post_install_message:
45
+ rdoc_options:
46
+ - --charset=UTF-8
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 0
56
+ version: "0"
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ requirements: []
66
+
67
+ rubyforge_project:
68
+ rubygems_version: 1.3.7
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: Provide textile toolbar to textareas
72
+ test_files:
73
+ - test/abstract_unit.rb
74
+ - test/textile_editor_test.rb