redcloth-rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +8 -0
- data/LICENSE +21 -0
- data/README.textile +58 -0
- data/Rakefile +8 -0
- data/app/assets/images/textile-editor/background.png +0 -0
- data/app/assets/images/textile-editor/blockquote.png +0 -0
- data/app/assets/images/textile-editor/bold.png +0 -0
- data/app/assets/images/textile-editor/center.png +0 -0
- data/app/assets/images/textile-editor/h1.png +0 -0
- data/app/assets/images/textile-editor/h2.png +0 -0
- data/app/assets/images/textile-editor/h3.png +0 -0
- data/app/assets/images/textile-editor/h4.png +0 -0
- data/app/assets/images/textile-editor/h5.png +0 -0
- data/app/assets/images/textile-editor/h6.png +0 -0
- data/app/assets/images/textile-editor/indent.png +0 -0
- data/app/assets/images/textile-editor/italic.png +0 -0
- data/app/assets/images/textile-editor/justify.png +0 -0
- data/app/assets/images/textile-editor/left.png +0 -0
- data/app/assets/images/textile-editor/list_bullets.png +0 -0
- data/app/assets/images/textile-editor/list_numbers.png +0 -0
- data/app/assets/images/textile-editor/omega.png +0 -0
- data/app/assets/images/textile-editor/outdent.png +0 -0
- data/app/assets/images/textile-editor/paragraph.png +0 -0
- data/app/assets/images/textile-editor/right.png +0 -0
- data/app/assets/images/textile-editor/strikethrough.png +0 -0
- data/app/assets/images/textile-editor/underline.png +0 -0
- data/app/assets/javascripts/textile-editor-config.js.coffee.erb +23 -0
- data/app/assets/javascripts/textile-editor.js.coffee +521 -0
- data/app/assets/stylesheets/textile-editor.css.scss +44 -0
- data/lib/rails/generators/textile_editor_config/USAGE +11 -0
- data/lib/rails/generators/textile_editor_config/templates/textile-editor-config.js.coffee.erb +23 -0
- data/lib/rails/generators/textile_editor_config/textile_editor_config_generator.rb +9 -0
- data/lib/redcloth-rails/engine.rb +18 -0
- data/lib/redcloth-rails/helpers/form_builder.rb +14 -0
- data/lib/redcloth-rails/helpers/form_helper.rb +137 -0
- data/lib/redcloth-rails/helpers/form_tag_helper.rb +49 -0
- data/lib/redcloth-rails/helpers.rb +9 -0
- data/lib/redcloth-rails/version.rb +5 -0
- data/lib/redcloth-rails.rb +15 -0
- data/redcloth-rails.gemspec +20 -0
- metadata +138 -0
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2013 emjot GmbH & Co. KG
|
2
|
+
Copyright (c) 2011 Thor Johnson
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
h2. redcloth-rails
|
2
|
+
|
3
|
+
bq. Homepage: "Github":https://github.com/emjot/redcloth-rails
|
4
|
+
Author: emjot GmbH & Co. KG
|
5
|
+
|
6
|
+
p. The redcloth-rails gem enables "RedCloth":http://redcloth.org for rails (>= 3.1).
|
7
|
+
It provides helpers to integrate the _TextileEditor_ toolbar and its assets are pipeline ready ...
|
8
|
+
|
9
|
+
h4. History
|
10
|
+
|
11
|
+
p. The _TextileEditor_ was created by Dave Olsen (Javascript) and Chris Scharf (Ruby/Rails) of "West Virginia University Web Services":http://webservices.wvu.edu .
|
12
|
+
The rails 3.0 support and gemification was realized by ryanfelton (https://github.com/ryanfelton/textile-editor-helper).
|
13
|
+
|
14
|
+
h4. Dependencies
|
15
|
+
|
16
|
+
* RedCloth (>= 4.2)
|
17
|
+
* Rails (>= 3.1)
|
18
|
+
* Rails Asset Pipeline
|
19
|
+
* JQuery
|
20
|
+
|
21
|
+
h4. Installation (via Bundler)
|
22
|
+
|
23
|
+
pre. Gemfile:
|
24
|
+
gem "redcloth-rails"
|
25
|
+
|
26
|
+
h4. Setup
|
27
|
+
|
28
|
+
Generate a config file
|
29
|
+
|
30
|
+
pre. rails g textile_editor_config
|
31
|
+
|
32
|
+
Include the coffees script and scss files in your asset pipeline
|
33
|
+
|
34
|
+
pre. app/assets/javascripts/application.js:
|
35
|
+
(...)
|
36
|
+
//= require textile-editor
|
37
|
+
//= require ./textile-editor-config
|
38
|
+
|
39
|
+
pre. app/assets/stylesheets/application.css:
|
40
|
+
(...)
|
41
|
+
*= require textile-editor
|
42
|
+
|
43
|
+
h4. Usage
|
44
|
+
|
45
|
+
Enable the _TextileEditor_ for your attributes via:
|
46
|
+
|
47
|
+
pre. <%= textile_editor :content %>
|
48
|
+
|
49
|
+
Initialize the _TextileEditor_ afterwards via:
|
50
|
+
|
51
|
+
pre. <%= textile_editor_initialize %>
|
52
|
+
|
53
|
+
h4. Links
|
54
|
+
|
55
|
+
p(. "redcloth.org":http://redcloth.org
|
56
|
+
"textile_editor_helper (original)":http://slatecms.wvu.edu/open_source
|
57
|
+
"textile_editor_helper (upadated for rails 3)":https://github.com/ryanfelton/textile-editor-helper
|
58
|
+
"more info on textile":http://www.textism.com/tools/textile/
|
data/Rakefile
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,23 @@
|
|
1
|
+
TextileEditor.setButtons(
|
2
|
+
[
|
3
|
+
new TextileEditorButton("ed_strong", "<%= asset_path('textile-editor/bold.png') %>", "*", "*", "b", "Bold", "s"),
|
4
|
+
new TextileEditorButton("ed_emphasis", "<%= asset_path('textile-editor/italic.png') %>", "_", "_", "i", "Italicize", "s"),
|
5
|
+
new TextileEditorButton("ed_underline", "<%= asset_path('textile-editor/underline.png') %>", "+", "+", "u", "Underline", "s"),
|
6
|
+
new TextileEditorButton("ed_strike", "<%= asset_path('textile-editor/strikethrough.png') %>", "-", "-", "s", "Strikethrough", "s"),
|
7
|
+
new TextileEditorButton("ed_ol", "<%= asset_path('textile-editor/list_numbers.png') %>", " # ", "\n", ",", "Numbered List"),
|
8
|
+
new TextileEditorButton("ed_ul", "<%= asset_path('textile-editor/list_bullets.png') %>", " * ", "\n", ".", "Bulleted List"),
|
9
|
+
new TextileEditorButton("ed_p", "<%= asset_path('textile-editor/paragraph.png') %>", "p", "\n", "p", "Paragraph"),
|
10
|
+
new TextileEditorButton("ed_h1", "<%= asset_path('textile-editor/h1.png') %>", "h1", "\n", "1", "Header 1"),
|
11
|
+
new TextileEditorButton("ed_h2", "<%= asset_path('textile-editor/h2.png') %>", "h2", "\n", "2", "Header 2"),
|
12
|
+
new TextileEditorButton("ed_h3", "<%= asset_path('textile-editor/h3.png') %>", "h3", "\n", "3", "Header 3"),
|
13
|
+
new TextileEditorButton("ed_h4", "<%= asset_path('textile-editor/h4.png') %>", "h4", "\n", "4", "Header 4"),
|
14
|
+
new TextileEditorButton("ed_block", "<%= asset_path('textile-editor/blockquote.png') %>", "bq", "\n", "q", "Blockquote"),
|
15
|
+
new TextileEditorButton("ed_outdent", "<%= asset_path('textile-editor/outdent.png') %>", ")", "\n", "]", "Outdent"),
|
16
|
+
new TextileEditorButton("ed_indent", "<%= asset_path('textile-editor/indent.png') %>", "(", "\n", "[", "Indent"),
|
17
|
+
new TextileEditorButton("ed_justifyl", "<%= asset_path('textile-editor/left.png') %>", "<", "\n", "l", "Left Justify"),
|
18
|
+
new TextileEditorButton("ed_justifyc", "<%= asset_path('textile-editor/center.png') %>", "=", "\n", "e", "Center Text"),
|
19
|
+
new TextileEditorButton("ed_justifyr", "<%= asset_path('textile-editor/right.png') %>", ">", "\n", "r", "Right Justify"),
|
20
|
+
new TextileEditorButton("ed_justify", "<%= asset_path('textile-editor/justify.png') %>", "<>", "\n", "j", "Justify"),
|
21
|
+
# new TextileEditorButton('ed_code','code','@','@','c','Code')
|
22
|
+
]
|
23
|
+
)
|
@@ -0,0 +1,521 @@
|
|
1
|
+
#Textile Editor v0.2
|
2
|
+
#created by: dave olsen, wvu web services
|
3
|
+
#created on: march 17, 2007
|
4
|
+
#project page: slateinfo.blogs.wvu.edu
|
5
|
+
#
|
6
|
+
#inspired by:
|
7
|
+
# - Patrick Woods, http://www.hakjoon.com/code/38/textile-quicktags-redirect &
|
8
|
+
# - Alex King, http://alexking.org/projects/js-quicktags
|
9
|
+
#
|
10
|
+
#features:
|
11
|
+
# - supports: IE7, FF2, Safari2
|
12
|
+
# - ability to use "simple" vs. "extended" editor
|
13
|
+
# - supports all block elements in textile except footnote
|
14
|
+
# - supports all block modifier elements in textile
|
15
|
+
# - supports simple ordered and unordered lists
|
16
|
+
# - supports most of the phrase modifiers, very easy to add the missing ones
|
17
|
+
# - supports multiple-paragraph modification
|
18
|
+
# - can have multiple "editors" on one page, access key use in this environment is flaky
|
19
|
+
# - access key support
|
20
|
+
# - select text to add and remove tags, selection stays highlighted
|
21
|
+
# - seamlessly change between tags and modifiers
|
22
|
+
# - doesn't need to be in the body onload tag
|
23
|
+
# - can supply your own, custom IDs for the editor to be drawn around
|
24
|
+
#
|
25
|
+
#todo:
|
26
|
+
# - a clean way of providing image and link inserts
|
27
|
+
# - get the selection to properly show in IE
|
28
|
+
#
|
29
|
+
#more on textile:
|
30
|
+
# - Textism, http://www.textism.com/tools/textile/index.php
|
31
|
+
# - Textile Reference, http://hobix.com/textile/
|
32
|
+
|
33
|
+
|
34
|
+
class TextileEditorButton
|
35
|
+
|
36
|
+
constructor: (id, display, tagStart, tagEnd, access, title, sve, open) ->
|
37
|
+
@id = id # used to name the toolbar button
|
38
|
+
@display = display # label on button
|
39
|
+
@tagStart = tagStart # open tag
|
40
|
+
@tagEnd = tagEnd # close tag
|
41
|
+
@access = access # set to -1 if tag does not need to be closed
|
42
|
+
@title = title # sets the title attribute of the button to give 'tool tips'
|
43
|
+
@sve = sve # sve = simple vs. extended. add an 's' to make it show up in the simple toolbar
|
44
|
+
@open = open # set to -1 if tag does not need to be closed
|
45
|
+
@standard = true # this is a standard button
|
46
|
+
# this.framework = 'prototype'; // the JS framework used
|
47
|
+
|
48
|
+
window.TextileEditorButton = TextileEditorButton
|
49
|
+
|
50
|
+
|
51
|
+
class TextileEditorButtonSeparator
|
52
|
+
|
53
|
+
constructor: (sve) ->
|
54
|
+
@separator = true
|
55
|
+
@sve = sve
|
56
|
+
|
57
|
+
window.TextileEditorButtonSeparator = TextileEditorButtonSeparator
|
58
|
+
|
59
|
+
class TextileEditor
|
60
|
+
|
61
|
+
@setButtons: (buttons) ->
|
62
|
+
TextileEditor.buttons = buttons
|
63
|
+
|
64
|
+
@addButton: (button) ->
|
65
|
+
@getButtons().push(button)
|
66
|
+
|
67
|
+
@getButtons: () ->
|
68
|
+
TextileEditor.buttons || new Array()
|
69
|
+
|
70
|
+
constructor: (canvas, view) ->
|
71
|
+
|
72
|
+
toolbar = document.createElement("div")
|
73
|
+
toolbar.id = "textile-toolbar-" + canvas
|
74
|
+
toolbar.className = "textile-toolbar"
|
75
|
+
@canvas = document.getElementById(canvas)
|
76
|
+
@canvas.parentNode.insertBefore toolbar, @canvas
|
77
|
+
@openTags = new Array()
|
78
|
+
|
79
|
+
# Create the local Button array by assigning theButtons array to edButtons
|
80
|
+
edButtons = new Array()
|
81
|
+
edButtons = TextileEditor.getButtons()
|
82
|
+
standardButtons = new Array()
|
83
|
+
i = 0
|
84
|
+
|
85
|
+
while i < edButtons.length
|
86
|
+
thisButton = @prepareButton(edButtons[i])
|
87
|
+
if view is "s"
|
88
|
+
if edButtons[i].sve is "s"
|
89
|
+
toolbar.appendChild thisButton
|
90
|
+
standardButtons.push thisButton
|
91
|
+
else
|
92
|
+
if typeof thisButton is "string"
|
93
|
+
toolbar.innerHTML += thisButton
|
94
|
+
else
|
95
|
+
toolbar.appendChild thisButton
|
96
|
+
standardButtons.push thisButton
|
97
|
+
i++
|
98
|
+
# end for
|
99
|
+
te = this
|
100
|
+
buttons = toolbar.getElementsByTagName("button")
|
101
|
+
i = 0
|
102
|
+
|
103
|
+
while i < buttons.length
|
104
|
+
|
105
|
+
#$A(toolbar.getElementsByTagName('button')).each(function(button) {
|
106
|
+
unless buttons[i].onclick
|
107
|
+
buttons[i].onclick = ->
|
108
|
+
te.insertTag this
|
109
|
+
false
|
110
|
+
# end if
|
111
|
+
buttons[i].tagStart = buttons[i].getAttribute("tagStart")
|
112
|
+
buttons[i].tagEnd = buttons[i].getAttribute("tagEnd")
|
113
|
+
buttons[i].open = buttons[i].getAttribute("open")
|
114
|
+
buttons[i].textile_editor = te
|
115
|
+
buttons[i].canvas = te.canvas
|
116
|
+
i++
|
117
|
+
|
118
|
+
# draw individual buttons (edShowButton)
|
119
|
+
prepareButton: (button) ->
|
120
|
+
if button.separator
|
121
|
+
theButton = document.createElement("span")
|
122
|
+
theButton.className = "ed_sep"
|
123
|
+
return theButton
|
124
|
+
if button.standard
|
125
|
+
theButton = document.createElement("button")
|
126
|
+
theButton.id = button.id
|
127
|
+
theButton.setAttribute "class", "standard"
|
128
|
+
theButton.setAttribute "tagStart", button.tagStart
|
129
|
+
theButton.setAttribute "tagEnd", button.tagEnd
|
130
|
+
theButton.setAttribute "open", button.open
|
131
|
+
img = document.createElement("img")
|
132
|
+
img.src = button.display
|
133
|
+
theButton.appendChild img
|
134
|
+
else
|
135
|
+
return button
|
136
|
+
# end if !custom
|
137
|
+
theButton.accessKey = button.access
|
138
|
+
theButton.title = button.title
|
139
|
+
theButton
|
140
|
+
|
141
|
+
# if clicked, no selected text, tag not open highlight button
|
142
|
+
# (edAddTag)
|
143
|
+
addTag: (button) ->
|
144
|
+
unless button.tagEnd is ""
|
145
|
+
@openTags[@openTags.length] = button
|
146
|
+
|
147
|
+
#var el = document.getElementById(button.id);
|
148
|
+
#el.className = 'selected';
|
149
|
+
button.className = "selected"
|
150
|
+
|
151
|
+
# if clicked, no selected text, tag open lowlight button
|
152
|
+
# (edRemoveTag)
|
153
|
+
removeTag: (button) ->
|
154
|
+
i = 0
|
155
|
+
while i < @openTags.length
|
156
|
+
if @openTags[i] is button
|
157
|
+
@openTags.splice button, 1
|
158
|
+
|
159
|
+
#var el = document.getElementById(button.id);
|
160
|
+
#el.className = 'unselected';
|
161
|
+
button.className = "unselected"
|
162
|
+
i++
|
163
|
+
|
164
|
+
# see if there are open tags. for the remove tag bit...
|
165
|
+
# (edCheckOpenTags)
|
166
|
+
checkOpenTags: (button) ->
|
167
|
+
tag = 0
|
168
|
+
i = 0
|
169
|
+
while i < @openTags.length
|
170
|
+
tag++ if @openTags[i] is button
|
171
|
+
i++
|
172
|
+
if tag > 0
|
173
|
+
true # tag found
|
174
|
+
else
|
175
|
+
false # tag not found
|
176
|
+
|
177
|
+
# insert the tag. this is the bulk of the code.
|
178
|
+
# (edInsertTag)
|
179
|
+
insertTag: (button, tagStart, tagEnd) ->
|
180
|
+
|
181
|
+
#console.log(button);
|
182
|
+
myField = button.canvas
|
183
|
+
myField.focus()
|
184
|
+
if tagStart
|
185
|
+
button.tagStart = tagStart
|
186
|
+
button.tagEnd = (if tagEnd then tagEnd else "\n")
|
187
|
+
textSelected = false
|
188
|
+
finalText = ""
|
189
|
+
FF = false
|
190
|
+
|
191
|
+
# grab the text that's going to be manipulated, by browser
|
192
|
+
if document.selection # IE support
|
193
|
+
sel = document.selection.createRange()
|
194
|
+
|
195
|
+
# set-up the text vars
|
196
|
+
beginningText = ""
|
197
|
+
followupText = ""
|
198
|
+
selectedText = sel.text
|
199
|
+
|
200
|
+
# check if text has been selected
|
201
|
+
textSelected = true if sel.text.length > 0
|
202
|
+
|
203
|
+
# set-up newline regex's so we can swap tags across multiple paragraphs
|
204
|
+
newlineReplaceRegexClean = /\r\n\s\n/g
|
205
|
+
newlineReplaceRegexDirty = "\\r\\n\\s\\n"
|
206
|
+
newlineReplaceClean = "\r\n\n"
|
207
|
+
else if myField.selectionStart or myField.selectionStart is "0" # MOZ/FF/NS/S support
|
208
|
+
|
209
|
+
# figure out cursor and selection positions
|
210
|
+
startPos = myField.selectionStart
|
211
|
+
endPos = myField.selectionEnd
|
212
|
+
cursorPos = endPos
|
213
|
+
scrollTop = myField.scrollTop
|
214
|
+
FF = true # note that is is a FF/MOZ/NS/S browser
|
215
|
+
|
216
|
+
# set-up the text vars
|
217
|
+
beginningText = myField.value.substring(0, startPos)
|
218
|
+
followupText = myField.value.substring(endPos, myField.value.length)
|
219
|
+
|
220
|
+
# check if text has been selected
|
221
|
+
unless startPos is endPos
|
222
|
+
textSelected = true
|
223
|
+
selectedText = myField.value.substring(startPos, endPos)
|
224
|
+
|
225
|
+
# set-up newline regex's so we can swap tags across multiple paragraphs
|
226
|
+
newlineReplaceRegexClean = /\n\n/g
|
227
|
+
newlineReplaceRegexDirty = "\\n\\n"
|
228
|
+
newlineReplaceClean = "\n\n"
|
229
|
+
|
230
|
+
# if there is text that has been highlighted...
|
231
|
+
if textSelected
|
232
|
+
|
233
|
+
# set-up some defaults for how to handle bad new line characters
|
234
|
+
newlineStart = ""
|
235
|
+
newlineStartPos = 0
|
236
|
+
newlineEnd = ""
|
237
|
+
newlineEndPos = 0
|
238
|
+
newlineFollowup = ""
|
239
|
+
|
240
|
+
# set-up some defaults for how to handle placing the beginning and end of selection
|
241
|
+
posDiffPos = 0
|
242
|
+
posDiffNeg = 0
|
243
|
+
mplier = 1
|
244
|
+
|
245
|
+
# remove newline from the beginning of the selectedText.
|
246
|
+
if selectedText.match(/^\n/)
|
247
|
+
selectedText = selectedText.replace(/^\n/, "")
|
248
|
+
newlineStart = "\n"
|
249
|
+
newlineStartpos = 1
|
250
|
+
|
251
|
+
# remove newline from the end of the selectedText.
|
252
|
+
if selectedText.match(/\n$/g)
|
253
|
+
selectedText = selectedText.replace(/\n$/g, "")
|
254
|
+
newlineEnd = "\n"
|
255
|
+
newlineEndPos = 1
|
256
|
+
|
257
|
+
# no clue, i'm sure it made sense at the time i wrote it
|
258
|
+
if followupText.match(/^\n/)
|
259
|
+
newlineFollowup = ""
|
260
|
+
else
|
261
|
+
newlineFollowup = "\n\n"
|
262
|
+
|
263
|
+
# first off let's check if the user is trying to mess with lists
|
264
|
+
if (button.tagStart is " * ") or (button.tagStart is " # ")
|
265
|
+
listItems = 0 # sets up a default to be able to properly manipulate final selection
|
266
|
+
|
267
|
+
# set-up all of the regex's
|
268
|
+
re_start = new RegExp("^ (\\*|\\#) ", "g")
|
269
|
+
if button.tagStart is " # "
|
270
|
+
re_tag = new RegExp(" \\# ", "g") # because of JS regex stupidity i need an if/else to properly set it up, could have done it with a regex replace though
|
271
|
+
else
|
272
|
+
re_tag = new RegExp(" \\* ", "g")
|
273
|
+
re_replace = new RegExp(" (\\*|\\#) ", "g")
|
274
|
+
|
275
|
+
# try to remove bullets in text copied from ms word **Mac Only!**
|
276
|
+
re_word_bullet_m_s = new RegExp("• ", "g") # mac/safari
|
277
|
+
re_word_bullet_m_f = new RegExp("∑ ", "g") # mac/firefox
|
278
|
+
selectedText = selectedText.replace(re_word_bullet_m_s, "").replace(re_word_bullet_m_f, "")
|
279
|
+
|
280
|
+
# if the selected text starts with one of the tags we're working with...
|
281
|
+
if selectedText.match(re_start)
|
282
|
+
|
283
|
+
# if tag that begins the selection matches the one clicked, remove them all
|
284
|
+
if selectedText.match(re_tag)
|
285
|
+
finalText = beginningText + newlineStart + selectedText.replace(re_replace, "") + newlineEnd + followupText
|
286
|
+
listItems = matches.length if matches = selectedText.match(RegExp(" (\\*|\\#) ", "g"))
|
287
|
+
posDiffNeg = listItems * 3 # how many list items were there because that's 3 spaces to remove from final selection
|
288
|
+
|
289
|
+
# else replace the current tag type with the selected tag type
|
290
|
+
else
|
291
|
+
finalText = beginningText + newlineStart + selectedText.replace(re_replace, button.tagStart) + newlineEnd + followupText
|
292
|
+
|
293
|
+
# else try to create the list type
|
294
|
+
# NOTE: the items in a list will only be replaced if a newline starts with some character, not a space
|
295
|
+
else
|
296
|
+
finalText = beginningText + newlineStart + button.tagStart + selectedText.replace(newlineReplaceRegexClean, newlineReplaceClean + button.tagStart).replace(/\n(\S)/g, "\n" + button.tagStart + "$1") + newlineEnd + followupText
|
297
|
+
listItems = matches.length if matches = selectedText.match(/\n(\S)/g)
|
298
|
+
posDiffPos = 3 + listItems * 3
|
299
|
+
|
300
|
+
# now lets look and see if the user is trying to muck with a block or block modifier
|
301
|
+
else if button.tagStart.match(/^(h1|h2|h3|h4|h5|h6|bq|p|\>|\<\>|\<|\=|\(|\))/g)
|
302
|
+
insertTag = ""
|
303
|
+
insertModifier = ""
|
304
|
+
tagPartBlock = ""
|
305
|
+
tagPartModifier = ""
|
306
|
+
tagPartModifierOrig = "" # ugly hack but it's late
|
307
|
+
drawSwitch = ""
|
308
|
+
captureIndentStart = false
|
309
|
+
captureListStart = false
|
310
|
+
periodAddition = "\\. "
|
311
|
+
periodAdditionClean = ". "
|
312
|
+
listItemsAddition = 0
|
313
|
+
re_list_items = new RegExp("(\\*+|\\#+)", "g") # need this regex later on when checking indentation of lists
|
314
|
+
re_block_modifier = new RegExp("^(h1|h2|h3|h4|h5|h6|bq|p| [\\*]{1,} | [\\#]{1,} |)(\\>|\\<\\>|\\<|\\=|[\\(]{1,}|[\\)]{1,6}|)", "g")
|
315
|
+
if tagPartMatches = re_block_modifier.exec(selectedText)
|
316
|
+
tagPartBlock = tagPartMatches[1]
|
317
|
+
tagPartModifier = tagPartMatches[2]
|
318
|
+
tagPartModifierOrig = tagPartMatches[2]
|
319
|
+
tagPartModifierOrig = tagPartModifierOrig.replace(/\(/g, "\\(")
|
320
|
+
|
321
|
+
# if tag already up is the same as the tag provided replace the whole tag
|
322
|
+
if tagPartBlock is button.tagStart
|
323
|
+
insertTag = tagPartBlock + tagPartModifierOrig # use Orig because it's escaped for regex
|
324
|
+
drawSwitch = 0
|
325
|
+
|
326
|
+
# else if let's check to add/remove block modifier
|
327
|
+
else if (tagPartModifier is button.tagStart) or (newm = tagPartModifier.match(/[\(]{2,}/g))
|
328
|
+
if (button.tagStart is "(") or (button.tagStart is ")")
|
329
|
+
indentLength = tagPartModifier.length
|
330
|
+
if button.tagStart is "("
|
331
|
+
indentLength = indentLength + 1
|
332
|
+
else
|
333
|
+
indentLength = indentLength - 1
|
334
|
+
i = 0
|
335
|
+
|
336
|
+
while i < indentLength
|
337
|
+
insertModifier = insertModifier + "("
|
338
|
+
i++
|
339
|
+
insertTag = tagPartBlock + insertModifier
|
340
|
+
else
|
341
|
+
if button.tagStart is tagPartModifier
|
342
|
+
insertTag = tagPartBlock
|
343
|
+
# going to rely on the default empty insertModifier
|
344
|
+
else
|
345
|
+
if button.tagStart.match(/(\>|\<\>|\<|\=)/g)
|
346
|
+
insertTag = tagPartBlock + button.tagStart
|
347
|
+
else
|
348
|
+
insertTag = button.tagStart + tagPartModifier
|
349
|
+
drawSwitch = 1
|
350
|
+
|
351
|
+
# indentation of list items
|
352
|
+
else if listPartMatches = re_list_items.exec(tagPartBlock)
|
353
|
+
listTypeMatch = listPartMatches[1]
|
354
|
+
indentLength = tagPartBlock.length - 2
|
355
|
+
listInsert = ""
|
356
|
+
if button.tagStart is "("
|
357
|
+
indentLength = indentLength + 1
|
358
|
+
else
|
359
|
+
indentLength = indentLength - 1
|
360
|
+
if listTypeMatch.match(/[\*]{1,}/g)
|
361
|
+
listType = "*"
|
362
|
+
listReplace = "\\*"
|
363
|
+
else
|
364
|
+
listType = "#"
|
365
|
+
listReplace = "\\#"
|
366
|
+
i = 0
|
367
|
+
|
368
|
+
while i < indentLength
|
369
|
+
listInsert = listInsert + listType
|
370
|
+
i++
|
371
|
+
unless listInsert is ""
|
372
|
+
insertTag = " " + listInsert + " "
|
373
|
+
else
|
374
|
+
insertTag = ""
|
375
|
+
tagPartBlock = tagPartBlock.replace(/(\*|\#)/g, listReplace)
|
376
|
+
drawSwitch = 1
|
377
|
+
captureListStart = true
|
378
|
+
periodAddition = ""
|
379
|
+
periodAdditionClean = ""
|
380
|
+
listItemsAddition = matches.length if matches = selectedText.match(/\n\s/g)
|
381
|
+
|
382
|
+
# must be a block modification e.g. p>. to p<.
|
383
|
+
else
|
384
|
+
|
385
|
+
# if this is a block modification/addition
|
386
|
+
if button.tagStart.match(/(h1|h2|h3|h4|h5|h6|bq|p)/g)
|
387
|
+
if tagPartBlock is ""
|
388
|
+
drawSwitch = 2
|
389
|
+
else
|
390
|
+
drawSwitch = 1
|
391
|
+
insertTag = button.tagStart + tagPartModifier
|
392
|
+
|
393
|
+
# else this is a modifier modification/addition
|
394
|
+
else
|
395
|
+
if (tagPartModifier is "") and (tagPartBlock isnt "")
|
396
|
+
drawSwitch = 1
|
397
|
+
else if tagPartModifier is ""
|
398
|
+
drawSwitch = 2
|
399
|
+
else
|
400
|
+
drawSwitch = 1
|
401
|
+
|
402
|
+
# if no tag part block but a modifier we need at least the p tag
|
403
|
+
tagPartBlock = "p" if tagPartBlock is ""
|
404
|
+
|
405
|
+
#make sure to swap out outdent
|
406
|
+
if button.tagStart is ")"
|
407
|
+
tagPartModifier = ""
|
408
|
+
else
|
409
|
+
tagPartModifier = button.tagStart
|
410
|
+
captureIndentStart = true # ugly hack to fix issue with proper selection handling
|
411
|
+
insertTag = tagPartBlock + tagPartModifier
|
412
|
+
mplier = 0
|
413
|
+
if captureListStart or (tagPartModifier.match(/[\(\)]{1,}/g))
|
414
|
+
re_start = new RegExp(insertTag.escape + periodAddition, "g") # for tags that mimic regex properties, parens + list tags
|
415
|
+
else
|
416
|
+
re_start = new RegExp(insertTag + periodAddition, "g") # for tags that don't, why i can't just escape everything i have no clue
|
417
|
+
re_old = new RegExp(tagPartBlock + tagPartModifierOrig + periodAddition, "g")
|
418
|
+
re_middle = new RegExp(newlineReplaceRegexDirty + insertTag.escape + periodAddition.escape, "g")
|
419
|
+
re_tag = new RegExp(insertTag.escape + periodAddition.escape, "g")
|
420
|
+
|
421
|
+
# *************************************************************************************************************************
|
422
|
+
# this is where everything gets swapped around or inserted, bullets and single options have their own if/else statements
|
423
|
+
# *************************************************************************************************************************
|
424
|
+
if (drawSwitch is 0) or (drawSwitch is 1)
|
425
|
+
if drawSwitch is 0 # completely removing a tag
|
426
|
+
finalText = beginningText + newlineStart + selectedText.replace(re_start, "").replace(re_middle, newlineReplaceClean) + newlineEnd + followupText
|
427
|
+
mplier = mplier + matches.length if matches = selectedText.match(newlineReplaceRegexClean)
|
428
|
+
posDiffNeg = insertTag.length + 2 + (mplier * 4)
|
429
|
+
else # modifying a tag, though we do delete bullets here
|
430
|
+
finalText = beginningText + newlineStart + selectedText.replace(re_old, insertTag + periodAdditionClean) + newlineEnd + followupText
|
431
|
+
mplier = mplier + matches.length if matches = selectedText.match(newlineReplaceRegexClean)
|
432
|
+
|
433
|
+
# figure out the length of various elements to modify the selection position
|
434
|
+
if captureIndentStart # need to double-check that this wasn't the first indent
|
435
|
+
tagPreviousLength = tagPartBlock.length
|
436
|
+
tagCurrentLength = insertTag.length
|
437
|
+
else if captureListStart # if this is a list we're manipulating
|
438
|
+
if button.tagStart is "(" # if indenting
|
439
|
+
tagPreviousLength = listTypeMatch.length + 2
|
440
|
+
tagCurrentLength = insertTag.length + listItemsAddition
|
441
|
+
else if insertTag.match(/(\*|\#)/g) # if removing but still has bullets
|
442
|
+
tagPreviousLength = insertTag.length + listItemsAddition
|
443
|
+
tagCurrentLength = listTypeMatch.length
|
444
|
+
else # if removing last bullet
|
445
|
+
tagPreviousLength = insertTag.length + listItemsAddition
|
446
|
+
tagCurrentLength = listTypeMatch.length - (3 * listItemsAddition) - 1
|
447
|
+
else # everything else
|
448
|
+
tagPreviousLength = tagPartBlock.length + tagPartModifier.length
|
449
|
+
tagCurrentLength = insertTag.length
|
450
|
+
if tagCurrentLength > tagPreviousLength
|
451
|
+
posDiffPos = (tagCurrentLength - tagPreviousLength) + (mplier * (tagCurrentLength - tagPreviousLength))
|
452
|
+
else
|
453
|
+
posDiffNeg = (tagPreviousLength - tagCurrentLength) + (mplier * (tagPreviousLength - tagCurrentLength))
|
454
|
+
else # for adding tags other then bullets (have their own statement)
|
455
|
+
finalText = beginningText + newlineStart + insertTag + ". " + selectedText.replace(newlineReplaceRegexClean, button.tagEnd + "\n" + insertTag + ". ") + newlineFollowup + newlineEnd + followupText
|
456
|
+
mplier = mplier + matches.length if matches = selectedText.match(newlineReplaceRegexClean)
|
457
|
+
posDiffPos = insertTag.length + 2 + (mplier * 4)
|
458
|
+
|
459
|
+
# swap in and out the simple tags around a selection like bold
|
460
|
+
else
|
461
|
+
mplier = 1 # the multiplier for the tag length
|
462
|
+
re_start = new RegExp("^\\" + button.tagStart, "g")
|
463
|
+
re_end = new RegExp("\\" + button.tagEnd + "$", "g")
|
464
|
+
re_middle = new RegExp("\\" + button.tagEnd + newlineReplaceRegexDirty + "\\" + button.tagStart, "g")
|
465
|
+
if selectedText.match(re_start) and selectedText.match(re_end)
|
466
|
+
finalText = beginningText + newlineStart + selectedText.replace(re_start, "").replace(re_end, "").replace(re_middle, newlineReplaceClean) + newlineEnd + followupText
|
467
|
+
mplier = mplier + matches.length if matches = selectedText.match(newlineReplaceRegexClean)
|
468
|
+
posDiffNeg = button.tagStart.length * mplier + button.tagEnd.length * mplier
|
469
|
+
else
|
470
|
+
finalText = beginningText + newlineStart + button.tagStart + selectedText.replace(newlineReplaceRegexClean, button.tagEnd + newlineReplaceClean + button.tagStart) + button.tagEnd + newlineEnd + followupText
|
471
|
+
mplier = mplier + matches.length if matches = selectedText.match(newlineReplaceRegexClean)
|
472
|
+
posDiffPos = (button.tagStart.length * mplier) + (button.tagEnd.length * mplier)
|
473
|
+
cursorPos += button.tagStart.length + button.tagEnd.length
|
474
|
+
|
475
|
+
# just swap in and out single values, e.g. someone clicks b they'll get a *
|
476
|
+
else
|
477
|
+
buttonStart = ""
|
478
|
+
buttonEnd = ""
|
479
|
+
re_p = new RegExp("(\\<|\\>|\\=|\\<\\>|\\(|\\))", "g")
|
480
|
+
re_h = new RegExp("^(h1|h2|h3|h4|h5|h6|p|bq)", "g")
|
481
|
+
if not @checkOpenTags(button) or button.tagEnd is "" # opening tag
|
482
|
+
if button.tagStart.match(re_h)
|
483
|
+
buttonStart = button.tagStart + ". "
|
484
|
+
else
|
485
|
+
buttonStart = button.tagStart
|
486
|
+
if button.tagStart.match(re_p) # make sure that invoking block modifiers don't do anything
|
487
|
+
finalText = beginningText + followupText
|
488
|
+
cursorPos = startPos
|
489
|
+
else
|
490
|
+
finalText = beginningText + buttonStart + followupText
|
491
|
+
@addTag button
|
492
|
+
cursorPos = startPos + buttonStart.length
|
493
|
+
else # closing tag
|
494
|
+
if button.tagStart.match(re_p)
|
495
|
+
buttonEnd = "\n\n"
|
496
|
+
else if button.tagStart.match(re_h)
|
497
|
+
buttonEnd = "\n\n"
|
498
|
+
else
|
499
|
+
buttonEnd = button.tagEnd
|
500
|
+
finalText = beginningText + button.tagEnd + followupText
|
501
|
+
@removeTag button
|
502
|
+
cursorPos = startPos + button.tagEnd.length
|
503
|
+
|
504
|
+
# set the appropriate DOM value with the final text
|
505
|
+
if FF is true
|
506
|
+
myField.value = finalText
|
507
|
+
myField.scrollTop = scrollTop
|
508
|
+
else
|
509
|
+
sel.text = finalText
|
510
|
+
|
511
|
+
# build up the selection capture, doesn't work in IE
|
512
|
+
if textSelected
|
513
|
+
myField.selectionStart = startPos + newlineStartPos
|
514
|
+
myField.selectionEnd = endPos + posDiffPos - posDiffNeg - newlineEndPos
|
515
|
+
|
516
|
+
#alert('s: ' + myField.selectionStart + ' e: ' + myField.selectionEnd + ' sp: ' + startPos + ' ep: ' + endPos + ' pdp: ' + posDiffPos + ' pdn: ' + posDiffNeg)
|
517
|
+
else
|
518
|
+
myField.selectionStart = cursorPos
|
519
|
+
myField.selectionEnd = cursorPos
|
520
|
+
|
521
|
+
window.TextileEditor = TextileEditor
|
@@ -0,0 +1,44 @@
|
|
1
|
+
div.textile-toolbar {
|
2
|
+
span.ed_sep {
|
3
|
+
xposition: relative;
|
4
|
+
xtop: -4px;
|
5
|
+
padding: 0;
|
6
|
+
height: 6px;
|
7
|
+
width: 2px;
|
8
|
+
margin: 0 2px;
|
9
|
+
border-left: solid 1px #d5d5d5;
|
10
|
+
border-right: solid 1px #f5f5f5;
|
11
|
+
}
|
12
|
+
button {
|
13
|
+
margin: 0;
|
14
|
+
background-color: #f0f0ee;
|
15
|
+
background-repeat: no-repeat;
|
16
|
+
border: 1px solid #f0f0ee;
|
17
|
+
padding: 2px 0;
|
18
|
+
&.standard {
|
19
|
+
text-align: center;
|
20
|
+
width: 24px;
|
21
|
+
}
|
22
|
+
img {
|
23
|
+
vertical-align: text-bottom;
|
24
|
+
}
|
25
|
+
&:hover, &.unselected:hover {
|
26
|
+
border: 1px solid #999;
|
27
|
+
}
|
28
|
+
&.selected {
|
29
|
+
border: 1px solid #ce9100;
|
30
|
+
background-color: #ffffff;
|
31
|
+
}
|
32
|
+
&.unselected {
|
33
|
+
border: 1px solid #f0f0ee;
|
34
|
+
background-color: #f0f0ee;
|
35
|
+
}
|
36
|
+
&.publish {
|
37
|
+
padding: 5px;
|
38
|
+
display: block;
|
39
|
+
}
|
40
|
+
}
|
41
|
+
background-color: #f0f0ee;
|
42
|
+
padding: 3px;
|
43
|
+
margin-bottom: 4px;
|
44
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
|
2
|
+
Description:
|
3
|
+
The textile_editor_config generator creates a config file for the textile editor.
|
4
|
+
|
5
|
+
Example:
|
6
|
+
rails generate textile_editor_config
|
7
|
+
|
8
|
+
This will create:
|
9
|
+
A Javascript config file located at app/assets/javascripts/textile-editor-config.js.coffee.erb
|
10
|
+
|
11
|
+
More info on textile_editor_config can be found here: https://github.com/emjot/redcloth-rails
|
@@ -0,0 +1,23 @@
|
|
1
|
+
TextileEditor.setButtons(
|
2
|
+
[
|
3
|
+
new TextileEditorButton("ed_strong", "<%= asset_path('textile-editor/bold.png') %>", "*", "*", "b", "Bold", "s"),
|
4
|
+
new TextileEditorButton("ed_emphasis", "<%= asset_path('textile-editor/italic.png') %>", "_", "_", "i", "Italicize", "s"),
|
5
|
+
new TextileEditorButton("ed_underline", "<%= asset_path('textile-editor/underline.png') %>", "+", "+", "u", "Underline", "s"),
|
6
|
+
new TextileEditorButton("ed_strike", "<%= asset_path('textile-editor/strikethrough.png') %>", "-", "-", "s", "Strikethrough", "s"),
|
7
|
+
new TextileEditorButton("ed_ol", "<%= asset_path('textile-editor/list_numbers.png') %>", " # ", "\n", ",", "Numbered List"),
|
8
|
+
new TextileEditorButton("ed_ul", "<%= asset_path('textile-editor/list_bullets.png') %>", " * ", "\n", ".", "Bulleted List"),
|
9
|
+
new TextileEditorButton("ed_p", "<%= asset_path('textile-editor/paragraph.png') %>", "p", "\n", "p", "Paragraph"),
|
10
|
+
new TextileEditorButton("ed_h1", "<%= asset_path('textile-editor/h1.png') %>", "h1", "\n", "1", "Header 1"),
|
11
|
+
new TextileEditorButton("ed_h2", "<%= asset_path('textile-editor/h2.png') %>", "h2", "\n", "2", "Header 2"),
|
12
|
+
new TextileEditorButton("ed_h3", "<%= asset_path('textile-editor/h3.png') %>", "h3", "\n", "3", "Header 3"),
|
13
|
+
new TextileEditorButton("ed_h4", "<%= asset_path('textile-editor/h4.png') %>", "h4", "\n", "4", "Header 4"),
|
14
|
+
new TextileEditorButton("ed_block", "<%= asset_path('textile-editor/blockquote.png') %>", "bq", "\n", "q", "Blockquote"),
|
15
|
+
new TextileEditorButton("ed_outdent", "<%= asset_path('textile-editor/outdent.png') %>", ")", "\n", "]", "Outdent"),
|
16
|
+
new TextileEditorButton("ed_indent", "<%= asset_path('textile-editor/indent.png') %>", "(", "\n", "[", "Indent"),
|
17
|
+
new TextileEditorButton("ed_justifyl", "<%= asset_path('textile-editor/left.png') %>", "<", "\n", "l", "Left Justify"),
|
18
|
+
new TextileEditorButton("ed_justifyc", "<%= asset_path('textile-editor/center.png') %>", "=", "\n", "e", "Center Text"),
|
19
|
+
new TextileEditorButton("ed_justifyr", "<%= asset_path('textile-editor/right.png') %>", ">", "\n", "r", "Right Justify"),
|
20
|
+
new TextileEditorButton("ed_justify", "<%= asset_path('textile-editor/justify.png') %>", "<>", "\n", "j", "Justify"),
|
21
|
+
new TextileEditorButton("ed_code", "code", "@", "@", "c", "Code")
|
22
|
+
]
|
23
|
+
)
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class TextileEditorConfigGenerator < Rails::Generators::Base
|
2
|
+
source_root File.expand_path('../templates', __FILE__)
|
3
|
+
|
4
|
+
def copy_javascript
|
5
|
+
copy_file 'textile-editor-config.js.coffee.erb', 'app/assets/javascripts/textile-editor-config.js.coffee.erb'
|
6
|
+
end
|
7
|
+
|
8
|
+
end
|
9
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helpers.rb')
|
2
|
+
|
3
|
+
module RedCloth
|
4
|
+
module Rails
|
5
|
+
|
6
|
+
class Engine < ::Rails::Engine
|
7
|
+
initializer "redcloth-rails.action_controller" do |app|
|
8
|
+
ActiveSupport.on_load :action_view do
|
9
|
+
ActionView::Helpers::FormBuilder.send :include, RedCloth::Rails::Helpers::FormBuilder
|
10
|
+
|
11
|
+
ActionView::Base.send :include, RedCloth::Rails::Helpers::FormHelper
|
12
|
+
ActionView::Base.send :include, RedCloth::Rails::Helpers::FormTagHelper
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module RedCloth
|
2
|
+
module Rails
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
module FormBuilder
|
6
|
+
# @see RedCloth::Rails::Helpers::FormHelper#textile_editor
|
7
|
+
def textile_editor(method, options = {})
|
8
|
+
@template.textile_editor(@object_name, method, options.merge(:object => @object))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module RedCloth
|
2
|
+
module Rails
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
module FormHelper
|
6
|
+
# Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
|
7
|
+
# on an object assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
|
8
|
+
# hash with +options+ and places the textile toolbar above it
|
9
|
+
#
|
10
|
+
# @param [Symbol] object_name name of the model
|
11
|
+
# @param [Symbol] method_name name of the models attribute
|
12
|
+
# @param [Hash] options HTML options to customize the textarea element
|
13
|
+
#
|
14
|
+
# Samples:
|
15
|
+
# textile_editor(:post, :body, :cols => 20, :rows => 40)
|
16
|
+
# # => <textarea cols="20" rows="40" id="post_body" name="post[body]">
|
17
|
+
# # #{@post.body}
|
18
|
+
# # </textarea>
|
19
|
+
#
|
20
|
+
# textile_editor(:comment, :text, :size => "20x30")
|
21
|
+
# # => <textarea cols="20" rows="30" id="comment_text" name="comment[text]">
|
22
|
+
# # #{@comment.text}
|
23
|
+
# # </textarea>
|
24
|
+
#
|
25
|
+
# textile_editor(:application, :notes, :cols => 40, :rows => 15, :class => 'app_input')
|
26
|
+
# # => <textarea cols="40" rows="15" id="application_notes" name="application[notes]" class="app_input">
|
27
|
+
# # #{@application.notes}
|
28
|
+
# # </textarea>
|
29
|
+
#
|
30
|
+
# textile_editor(:entry, :body, :size => "20x20", :disabled => 'disabled')
|
31
|
+
# # => <textarea cols="20" rows="20" id="entry_body" name="entry[body]" disabled="disabled">
|
32
|
+
# # #{@entry.body}
|
33
|
+
# # </textarea>
|
34
|
+
#
|
35
|
+
def textile_editor(object_name, method, options = {})
|
36
|
+
editor_id = options[:id] || '%s_%s' % [object_name, method]
|
37
|
+
mode = options.delete(:simple) ? 'simple' : 'extended'
|
38
|
+
(@textile_editor_ids ||= []) << [editor_id.to_s, mode.to_s]
|
39
|
+
|
40
|
+
ActionView::Helpers::InstanceTag.new(object_name, method, self, options.delete(:object)).to_text_area_tag(options)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Registers a further button on the TextileEditor toolbar.
|
44
|
+
# Must be called before the TextileEditor gets initialized.
|
45
|
+
#
|
46
|
+
# @param [String] text text to display (contents of button tag, so HTML is valid as well)
|
47
|
+
# @param [Hash] options options Hash as supported by +content_tag+ helper in Rails
|
48
|
+
#
|
49
|
+
# Example:
|
50
|
+
# Add a button labeled 'Greeting' which triggers an alert dialog.
|
51
|
+
#
|
52
|
+
# <% textile_editor_button 'Greeting', :onclick => "alert('Hello!')" %>
|
53
|
+
#
|
54
|
+
def textile_editor_button(text, options={})
|
55
|
+
return textile_editor_button_separator if text == :separator
|
56
|
+
button = content_tag(:button, text, options)
|
57
|
+
button = "TextileEditor.addButton(\"%s\");" % escape_javascript(button)
|
58
|
+
(@textile_editor_buttons ||= []) << button
|
59
|
+
return nil
|
60
|
+
end
|
61
|
+
|
62
|
+
# Registers a separator on the TextileEditor toolbar.
|
63
|
+
# Must be called before the TextileEditor gets initialized.
|
64
|
+
#
|
65
|
+
# @param [Hash] options
|
66
|
+
#
|
67
|
+
def textile_editor_button_separator(options={})
|
68
|
+
button = "TextileEditor.addButton(new TextileEditorButtonSeparator('%s'));" % (options[:simple] || '')
|
69
|
+
(@textile_editor_buttons ||= []) << button
|
70
|
+
return nil
|
71
|
+
end
|
72
|
+
|
73
|
+
# Initialize the TextileEditor. after the DOM was loaded.
|
74
|
+
#
|
75
|
+
# @param [Array] dom_ids
|
76
|
+
# @param [Hash] options
|
77
|
+
#
|
78
|
+
# Sample with jQuery DOM ready event:
|
79
|
+
# <script type="text/javascript">
|
80
|
+
# jQuery(document).ready(function() {
|
81
|
+
# TextileEditor.initialize('article_body', 'extended');
|
82
|
+
# TextileEditor.initialize('article_body_excerpt', 'simple');
|
83
|
+
# });
|
84
|
+
# </script>
|
85
|
+
#
|
86
|
+
# Sample for AJAX requests, the TextileEditor gets initialized without waiting for DOM.
|
87
|
+
# <script type="text/javascript">
|
88
|
+
# TextileEditor.initialize('article_body', 'extended');
|
89
|
+
# TextileEditor.initialize('article_body_excerpt', 'simple');
|
90
|
+
# </script>
|
91
|
+
#
|
92
|
+
def textile_editor_initialize(*dom_ids)
|
93
|
+
options = {}
|
94
|
+
|
95
|
+
# extract options from last argument if it's a hash
|
96
|
+
if dom_ids.last.is_a?(Hash)
|
97
|
+
hash = dom_ids.last.dup
|
98
|
+
options.merge! hash
|
99
|
+
end
|
100
|
+
|
101
|
+
editor_ids = (@textile_editor_ids || []) + textile_extract_dom_ids(*dom_ids)
|
102
|
+
editor_buttons = (@textile_editor_buttons || [])
|
103
|
+
output = []
|
104
|
+
|
105
|
+
output << '<script type="text/javascript">'
|
106
|
+
output << '/* <![CDATA[ */'
|
107
|
+
|
108
|
+
unless request.xhr?
|
109
|
+
output << %{jQuery(document).ready(function($) \{}
|
110
|
+
end
|
111
|
+
|
112
|
+
output << editor_buttons.join("\n") if editor_buttons.any?
|
113
|
+
|
114
|
+
editor_ids.each do |editor_id, mode|
|
115
|
+
output << %q{new TextileEditor('%s', '%s');} % [editor_id, mode || 'extended']
|
116
|
+
end
|
117
|
+
|
118
|
+
unless request.xhr?
|
119
|
+
output << '});'
|
120
|
+
end
|
121
|
+
|
122
|
+
output << '/* ]]> */'
|
123
|
+
output << '</script>'
|
124
|
+
output.join("\n").html_safe
|
125
|
+
end
|
126
|
+
|
127
|
+
def textile_extract_dom_ids(*dom_ids)
|
128
|
+
hash = dom_ids.last.is_a?(Hash) ? dom_ids.pop : {}
|
129
|
+
hash.inject(dom_ids) do |ids, (object, fields)|
|
130
|
+
ids + Array(fields).map { |field| "%s_%s" % [object, field] }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module RedCloth
|
2
|
+
module Rails
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
module FormTagHelper
|
6
|
+
# Creates a text input area; use a textarea for longer text inputs such as blog posts or descriptions
|
7
|
+
# and includes the textile toolbar above it.
|
8
|
+
#
|
9
|
+
# @param [String] name
|
10
|
+
# @param [String] content
|
11
|
+
# @param [Hash] options
|
12
|
+
# @option options [String] :size String to specify the column and row dimensions (e.g., "25x10").
|
13
|
+
# @option options [Integer] :rows Specify the number of rows in the textarea
|
14
|
+
# @option options [Integer] :cols Specify the number of columns in the textarea
|
15
|
+
# @option options [Boolean] :disabled If set to true, the user will not be able to use this input.
|
16
|
+
#
|
17
|
+
# Any other option (key/value pair) will be used to create HTML attributes for the tag.
|
18
|
+
#
|
19
|
+
# Samples:
|
20
|
+
# textile_editor_tag 'post'
|
21
|
+
# # => <textarea id="post" name="post"></textarea>
|
22
|
+
#
|
23
|
+
# textile_editor_tag 'bio', @user.bio
|
24
|
+
# # => <textarea id="bio" name="bio">This is my biography.</textarea>
|
25
|
+
#
|
26
|
+
# textile_editor_tag 'body', nil, :rows => 10, :cols => 25
|
27
|
+
# # => <textarea cols="25" id="body" name="body" rows="10"></textarea>
|
28
|
+
#
|
29
|
+
# textile_editor_tag 'body', nil, :size => "25x10"
|
30
|
+
# # => <textarea name="body" id="body" cols="25" rows="10"></textarea>
|
31
|
+
#
|
32
|
+
# textile_editor_tag 'description', "Description goes here.", :disabled => true
|
33
|
+
# # => <textarea disabled="disabled" id="description" name="description">Description goes here.</textarea>
|
34
|
+
#
|
35
|
+
# textile_editor_tag 'comment', nil, :class => 'comment_input'
|
36
|
+
# # => <textarea class="comment_input" id="comment" name="comment"></textarea>
|
37
|
+
#
|
38
|
+
def textile_editor_tag(name, content = nil, options = {})
|
39
|
+
editor_id = options[:id] || name
|
40
|
+
mode = options.delete(:simple) ? 'simple' : 'extended'
|
41
|
+
(@textile_editor_ids ||= []) << [editor_id.to_s, mode.to_s]
|
42
|
+
|
43
|
+
text_area_tag(name, content, options)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helpers/form_builder.rb')
|
2
|
+
require File.join(File.dirname(__FILE__), 'helpers/form_helper.rb')
|
3
|
+
require File.join(File.dirname(__FILE__), 'helpers/form_tag_helper.rb')
|
4
|
+
|
5
|
+
module RedCloth
|
6
|
+
module Rails
|
7
|
+
module Helpers; end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
unless defined?(RedCloth::Rails::Version)
|
2
|
+
require File.join(File.dirname(__FILE__), 'redcloth-rails/version.rb')
|
3
|
+
end
|
4
|
+
|
5
|
+
module RedCloth
|
6
|
+
module Rails
|
7
|
+
|
8
|
+
if defined?(::Rails) && ::Rails.version.to_f >= 3.1
|
9
|
+
require File.join(File.dirname(__FILE__), 'redcloth-rails/engine.rb')
|
10
|
+
else
|
11
|
+
raise 'Rails >= 3.1 is required'
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require File.join(File.dirname(__FILE__), "lib/redcloth-rails/version.rb")
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "redcloth-rails"
|
7
|
+
s.version = RedCloth::Rails::VERSION
|
8
|
+
s.authors = ["emjot"]
|
9
|
+
s.email = ["kontakt@emjot.de"]
|
10
|
+
s.homepage = "https://github.com/emjot/redcloth-rails"
|
11
|
+
s.summary = %q{rails 3 engine which enables RedCloth and provides helpers for the awesome TextileEditor.}
|
12
|
+
|
13
|
+
s.add_dependency('RedCloth', '>= 4.2.0')
|
14
|
+
s.add_dependency('rails', '>= 3.1.0')
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: redcloth-rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- emjot
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2013-03-04 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: RedCloth
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 55
|
30
|
+
segments:
|
31
|
+
- 4
|
32
|
+
- 2
|
33
|
+
- 0
|
34
|
+
version: 4.2.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rails
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
46
|
+
segments:
|
47
|
+
- 3
|
48
|
+
- 1
|
49
|
+
- 0
|
50
|
+
version: 3.1.0
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
description:
|
54
|
+
email:
|
55
|
+
- kontakt@emjot.de
|
56
|
+
executables: []
|
57
|
+
|
58
|
+
extensions: []
|
59
|
+
|
60
|
+
extra_rdoc_files: []
|
61
|
+
|
62
|
+
files:
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE
|
65
|
+
- README.textile
|
66
|
+
- Rakefile
|
67
|
+
- app/assets/images/textile-editor/background.png
|
68
|
+
- app/assets/images/textile-editor/blockquote.png
|
69
|
+
- app/assets/images/textile-editor/bold.png
|
70
|
+
- app/assets/images/textile-editor/center.png
|
71
|
+
- app/assets/images/textile-editor/h1.png
|
72
|
+
- app/assets/images/textile-editor/h2.png
|
73
|
+
- app/assets/images/textile-editor/h3.png
|
74
|
+
- app/assets/images/textile-editor/h4.png
|
75
|
+
- app/assets/images/textile-editor/h5.png
|
76
|
+
- app/assets/images/textile-editor/h6.png
|
77
|
+
- app/assets/images/textile-editor/indent.png
|
78
|
+
- app/assets/images/textile-editor/italic.png
|
79
|
+
- app/assets/images/textile-editor/justify.png
|
80
|
+
- app/assets/images/textile-editor/left.png
|
81
|
+
- app/assets/images/textile-editor/list_bullets.png
|
82
|
+
- app/assets/images/textile-editor/list_numbers.png
|
83
|
+
- app/assets/images/textile-editor/omega.png
|
84
|
+
- app/assets/images/textile-editor/outdent.png
|
85
|
+
- app/assets/images/textile-editor/paragraph.png
|
86
|
+
- app/assets/images/textile-editor/right.png
|
87
|
+
- app/assets/images/textile-editor/strikethrough.png
|
88
|
+
- app/assets/images/textile-editor/underline.png
|
89
|
+
- app/assets/javascripts/textile-editor-config.js.coffee.erb
|
90
|
+
- app/assets/javascripts/textile-editor.js.coffee
|
91
|
+
- app/assets/stylesheets/textile-editor.css.scss
|
92
|
+
- lib/rails/generators/textile_editor_config/USAGE
|
93
|
+
- lib/rails/generators/textile_editor_config/templates/textile-editor-config.js.coffee.erb
|
94
|
+
- lib/rails/generators/textile_editor_config/textile_editor_config_generator.rb
|
95
|
+
- lib/redcloth-rails.rb
|
96
|
+
- lib/redcloth-rails/engine.rb
|
97
|
+
- lib/redcloth-rails/helpers.rb
|
98
|
+
- lib/redcloth-rails/helpers/form_builder.rb
|
99
|
+
- lib/redcloth-rails/helpers/form_helper.rb
|
100
|
+
- lib/redcloth-rails/helpers/form_tag_helper.rb
|
101
|
+
- lib/redcloth-rails/version.rb
|
102
|
+
- redcloth-rails.gemspec
|
103
|
+
has_rdoc: true
|
104
|
+
homepage: https://github.com/emjot/redcloth-rails
|
105
|
+
licenses: []
|
106
|
+
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
hash: 3
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
hash: 3
|
127
|
+
segments:
|
128
|
+
- 0
|
129
|
+
version: "0"
|
130
|
+
requirements: []
|
131
|
+
|
132
|
+
rubyforge_project:
|
133
|
+
rubygems_version: 1.3.7
|
134
|
+
signing_key:
|
135
|
+
specification_version: 3
|
136
|
+
summary: rails 3 engine which enables RedCloth and provides helpers for the awesome TextileEditor.
|
137
|
+
test_files: []
|
138
|
+
|