redcloth-rails 0.1.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.
- 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
|
+
|