sexy-content-editable 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/app/assets/javascripts/sexy-content-editable.js.coffee +269 -0
- data/app/assets/javascripts/templates/editor.jst.eco +52 -0
- data/app/assets/javascripts/templates/html_prompt_modal.jst.eco +24 -0
- data/app/assets/javascripts/templates/link_prompt_modal.jst.eco +25 -0
- data/app/assets/stylesheets/sexy-content-editable.css.scss +72 -0
- data/lib/sexy-content-editable.rb +6 -0
- data/lib/sexy_content_editable/engine.rb +16 -0
- data/lib/sexy_content_editable/form_builder.rb +8 -0
- data/lib/sexy_content_editable/form_tag_helper.rb +13 -0
- data/lib/sexy_content_editable/version.rb +3 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1e41e9defd56d68953a05a25d15e78285114c69e
|
4
|
+
data.tar.gz: 1667245956897ce8634c54f014554d5f723a8e20
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b315b00f5e69328d1bc0c01578573cd920e3a7fd62ca97a975f6eef75a5e49f795b7c9fb27b7d43621babdfffb0f553fa54772846fdab4dea9cc879758e2fd99
|
7
|
+
data.tar.gz: a4647827e8f6ae9ed019c802766b62d27c1d2a6107d5def06ffade309559b3e6022656290d09c32ad3af1d36b4492654f93c3ee80d2ebef20cdf25d5845d34a7
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 David Barral
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Sexy::Content::Editable
|
2
|
+
|
3
|
+
Work in progress. Not production ready yet (^\_^ U)
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'sexy-content-editable'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install sexy-content-editable
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
1. Fork it ( https://github.com/[my-github-username]/sexy-content-editable/fork )
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
+
5. Create a new Pull Request
|
@@ -0,0 +1,269 @@
|
|
1
|
+
#= require_tree
|
2
|
+
#= require bootstrap
|
3
|
+
SelectionManager = do ->
|
4
|
+
selection = null
|
5
|
+
range = null
|
6
|
+
|
7
|
+
stripTags = ->
|
8
|
+
text = range.toString()
|
9
|
+
range.deleteContents()
|
10
|
+
range.insertNode(document.createTextNode(text))
|
11
|
+
|
12
|
+
select = (node) ->
|
13
|
+
selection = document.getSelection()
|
14
|
+
selection.removeAllRanges()
|
15
|
+
|
16
|
+
range = document.createRange()
|
17
|
+
range.selectNode(node)
|
18
|
+
|
19
|
+
selection.addRange(range)
|
20
|
+
|
21
|
+
record = ->
|
22
|
+
selection = document.getSelection()
|
23
|
+
if selection.rangeCount > 0
|
24
|
+
range = selection.getRangeAt(0)
|
25
|
+
|
26
|
+
restore = ->
|
27
|
+
selection.removeAllRanges()
|
28
|
+
selection.addRange(range)
|
29
|
+
|
30
|
+
collapse = ->
|
31
|
+
range.collapse()
|
32
|
+
restore()
|
33
|
+
|
34
|
+
collapse : collapse
|
35
|
+
select : select
|
36
|
+
record : record
|
37
|
+
restore : restore
|
38
|
+
stripTags : stripTags
|
39
|
+
|
40
|
+
|
41
|
+
class BasePrompt
|
42
|
+
constructor : (tmpl) ->
|
43
|
+
@template = JST[tmpl]
|
44
|
+
|
45
|
+
show : (options = {}) ->
|
46
|
+
SelectionManager.record()
|
47
|
+
|
48
|
+
$modal = $(@template())
|
49
|
+
$('body').append($modal)
|
50
|
+
$modal.modal(show: false)
|
51
|
+
|
52
|
+
$modal.on 'hidden.bs.modal', ->
|
53
|
+
SelectionManager.collapse()
|
54
|
+
$modal.remove()
|
55
|
+
|
56
|
+
$modal.on 'submit', 'form', (e) =>
|
57
|
+
e.preventDefault()
|
58
|
+
$form = $(e.currentTarget)
|
59
|
+
@_processUserInput($form.serializeArray())
|
60
|
+
$modal.modal('hide')
|
61
|
+
|
62
|
+
@_beforeShow($modal, options)
|
63
|
+
@_installAdditionalHandlers($modal)
|
64
|
+
|
65
|
+
$modal.modal('show')
|
66
|
+
|
67
|
+
_beforeShow : -> #NOOP
|
68
|
+
_installAdditionalHandlers : -> #NOOP
|
69
|
+
|
70
|
+
class LinkPrompt extends BasePrompt
|
71
|
+
constructor: ->
|
72
|
+
super('templates/link_prompt_modal')
|
73
|
+
|
74
|
+
_processUserInput : (userInput) =>
|
75
|
+
debugger
|
76
|
+
url = userInput.filter (e) ->
|
77
|
+
e.value if e.name is "url"
|
78
|
+
|
79
|
+
SelectionManager.restore()
|
80
|
+
document.execCommand('CreateLink', null, url[0].value)
|
81
|
+
|
82
|
+
_beforeShow : ($modal, options) ->
|
83
|
+
$modal.find('[name=url]').val(options.url)
|
84
|
+
|
85
|
+
_installAdditionalHandlers : ($modal) ->
|
86
|
+
$modal.on 'click', '[data-behaviour=editor-remove-link]', ->
|
87
|
+
SelectionManager.stripTags()
|
88
|
+
|
89
|
+
|
90
|
+
class HtmlPrompt extends BasePrompt
|
91
|
+
constructor: ->
|
92
|
+
super('templates/html_prompt_modal')
|
93
|
+
|
94
|
+
_processUserInput : (userInput) =>
|
95
|
+
html = userInput.filter (e) ->
|
96
|
+
e.value if e.name is "html"
|
97
|
+
|
98
|
+
|
99
|
+
SelectionManager.restore()
|
100
|
+
document.execCommand('InsertHTML', null, html[0].value)
|
101
|
+
|
102
|
+
|
103
|
+
# NOTE: Extensive docs for executeCommand method at
|
104
|
+
# https://developer.mozilla.org/en-US/docs/Rich-Text_Editing_in_Mozilla#Executing_Commands
|
105
|
+
#
|
106
|
+
# Sexy content editable API: invoke the API methods using jQuery style shit. E.g:
|
107
|
+
#
|
108
|
+
# $el.sexyContentEditable()
|
109
|
+
# ...
|
110
|
+
# $el.sexyContentEditable('setContent', 'my new content')
|
111
|
+
# $el.sexyContentEditable('appendContent', 'content to append')
|
112
|
+
#
|
113
|
+
class SexyContentEditable
|
114
|
+
|
115
|
+
ALL_BUTTONS = [ # All buttons
|
116
|
+
'undo', 'redo', 'clean', 'b', 'i', 'sub', 'sup', 'ul', 'ol', 'ltab', 'rtab', 'h1', 'h2', 'h3', 'h4', 'h5', 'p', 'prompt-link', 'prompt-html'
|
117
|
+
]
|
118
|
+
|
119
|
+
constructor: (@$formElement) ->
|
120
|
+
@_extractConfiguration()
|
121
|
+
@_buildComponent()
|
122
|
+
@_bindEvents()
|
123
|
+
|
124
|
+
_extractConfiguration: ->
|
125
|
+
@value = @$formElement.attr('value')
|
126
|
+
@buttonsConfig = @$formElement.data('toolbar-config') || 'default'
|
127
|
+
@buttons = @$formElement.data('toolbar-buttons') || ALL_BUTTONS
|
128
|
+
@size = @$formElement.data('size') || ''
|
129
|
+
|
130
|
+
_buildComponent: ->
|
131
|
+
sizeClass = "sexy-content-editable-content-#{@size}"
|
132
|
+
@$editor = $(JST["templates/editor"](size: sizeClass))
|
133
|
+
|
134
|
+
@$content = @$editor.find('.sexy-content-editable-content')
|
135
|
+
@$toolbar = @$editor.find('.sexy-content-editable-toolbar')
|
136
|
+
|
137
|
+
@$toolbar.find('a').each (_, button) =>
|
138
|
+
$button = $(button)
|
139
|
+
$button.remove() if $.inArray($button.data('name'), @buttons) == -1
|
140
|
+
|
141
|
+
# Ensure usage of P for when pressing
|
142
|
+
document.execCommand('defaultParagraphSeparator', false, 'p')
|
143
|
+
|
144
|
+
@$content.html(@value) if @value
|
145
|
+
|
146
|
+
@$formElement.after(@$editor)
|
147
|
+
@$formElement.hide()
|
148
|
+
|
149
|
+
@linkPrompt = new LinkPrompt()
|
150
|
+
@htmlPrompt = new HtmlPrompt()
|
151
|
+
|
152
|
+
|
153
|
+
_bindEvents: ->
|
154
|
+
# Toolbar use
|
155
|
+
@$toolbar.on 'click', 'a', @_toolbarButtonClickHandler
|
156
|
+
|
157
|
+
# Fake the change event
|
158
|
+
@$content.on 'focus', @_contentFocusHandler
|
159
|
+
@$content.on 'blur keyup paste', @_contentUpdateHandler
|
160
|
+
|
161
|
+
# Content change
|
162
|
+
@$content.on 'change', @_contentChangeHandler
|
163
|
+
|
164
|
+
# Input value change
|
165
|
+
@$formElement.on 'change', @_elementChangeHandler
|
166
|
+
|
167
|
+
# Focus management
|
168
|
+
@$editor.on 'focusin', @_enableButtons
|
169
|
+
@$editor.on 'focusout', @_disableButtons
|
170
|
+
|
171
|
+
# Edit links clicking on them
|
172
|
+
@$content.on 'click', 'a', @_editLink
|
173
|
+
|
174
|
+
|
175
|
+
_toolbarButtonClickHandler: (event) =>
|
176
|
+
event.preventDefault()
|
177
|
+
$button = $(event.currentTarget)
|
178
|
+
role = $button.data('role')
|
179
|
+
name = $button.data('name')
|
180
|
+
|
181
|
+
|
182
|
+
if prompt = name.match /prompt\-(.+)/
|
183
|
+
@_showPrompt(prompt[1])
|
184
|
+
else if md = role.match /b\-(.+)/
|
185
|
+
document.execCommand 'formatBlock', false, md[1]
|
186
|
+
else
|
187
|
+
document.execCommand role, false, null
|
188
|
+
|
189
|
+
@$content.trigger 'change'
|
190
|
+
|
191
|
+
|
192
|
+
_contentFocusHandler: (event) =>
|
193
|
+
@$content.data 'value', @$content.html()
|
194
|
+
@$content
|
195
|
+
|
196
|
+
|
197
|
+
_contentUpdateHandler: (event) =>
|
198
|
+
newHtml = @$content.html()
|
199
|
+
|
200
|
+
if @$content.data('value') isnt newHtml
|
201
|
+
# avoid problems with empty tags
|
202
|
+
newHtml = '' if @$content.text().trim().length == 0
|
203
|
+
|
204
|
+
@$content.data 'value', newHtml
|
205
|
+
@$content.trigger 'change'
|
206
|
+
|
207
|
+
|
208
|
+
_contentChangeHandler: (event) =>
|
209
|
+
@$formElement.attr 'value', @$content.data('value')
|
210
|
+
|
211
|
+
|
212
|
+
_elementChangeHandler : (event) =>
|
213
|
+
value = $(event.currentTarget).val()
|
214
|
+
@$content.html(value)
|
215
|
+
|
216
|
+
|
217
|
+
_enableButtons : =>
|
218
|
+
@$toolbar.find('a, button').attr('disabled', null)
|
219
|
+
|
220
|
+
|
221
|
+
_disableButtons : (event) =>
|
222
|
+
if @$editor.has(event.relatedTarget).length == 0
|
223
|
+
@$toolbar.find('a, button').attr('disabled', 'disabled')
|
224
|
+
|
225
|
+
|
226
|
+
_showPrompt : (prompt) ->
|
227
|
+
if prompt is 'link'
|
228
|
+
@linkPrompt.show()
|
229
|
+
else if prompt is 'html'
|
230
|
+
@htmlPrompt.show()
|
231
|
+
|
232
|
+
|
233
|
+
_editLink : (event) =>
|
234
|
+
event.preventDefault()
|
235
|
+
$link = $(event.currentTarget)
|
236
|
+
SelectionManager.select($link.get(0))
|
237
|
+
|
238
|
+
@linkPrompt.show(url: $link.attr('href'))
|
239
|
+
|
240
|
+
#
|
241
|
+
# PUBLIC API
|
242
|
+
#
|
243
|
+
|
244
|
+
setContent : (value) =>
|
245
|
+
@$content.html(value).trigger 'paste'
|
246
|
+
|
247
|
+
appendContent : (value) =>
|
248
|
+
oldContent = @$content.html()
|
249
|
+
@setContent(oldContent + value)
|
250
|
+
|
251
|
+
$.fn.sexyContentEditable = (args...)->
|
252
|
+
@.each ->
|
253
|
+
$el = $ @
|
254
|
+
console.log('ola ke ase')
|
255
|
+
|
256
|
+
unless rte = $el.data('sexyContentEditable')
|
257
|
+
rte = new SexyContentEditable($el)
|
258
|
+
$el.data 'sexyContentEditable', rte
|
259
|
+
|
260
|
+
if args.length
|
261
|
+
method = args.splice(0, 1)[0]
|
262
|
+
rte[method].apply(rte, args)
|
263
|
+
|
264
|
+
|
265
|
+
$ ->
|
266
|
+
$(document).ajaxComplete ->
|
267
|
+
$('[data-behaviour~=sexy-content-editable]').sexyContentEditable()
|
268
|
+
$('[data-behaviour~=sexy-content-editable]').sexyContentEditable()
|
269
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
<div class="sexy-content-editable">
|
2
|
+
<div class="sexy-content-editable-toolbar">
|
3
|
+
<div class="btn-group">
|
4
|
+
<a href="#" class="btn btn-default" tabindex="-1" data-name='undo' data-role='undo' disabled><i class='fa fa-undo'></i></a>
|
5
|
+
<a href="#" class="btn btn-default" tabindex="-1" data-name='redo' data-role='redo' disabled><i class='fa fa-repeat'></i></a>
|
6
|
+
</div>
|
7
|
+
<div class="btn-group">
|
8
|
+
<a href="#" class="btn btn-default" tabindex="-1" data-name='clean' data-role='removeFormat' disabled><i class='fa fa-eraser'></i></a>
|
9
|
+
<a href="#" class="btn btn-default" tabindex="-1" data-name='b' data-role='bold' disabled><i class='fa fa-bold'></i></a>
|
10
|
+
<a href="#" class="btn btn-default" tabindex="-1" data-name='i' data-role='italic' disabled><i class='fa fa-italic'></i></a>
|
11
|
+
<a href="#" class="btn btn-default" tabindex="-1" data-name='sub' data-role='subscript' disabled><i class='fa fa-subscript'></i></a>
|
12
|
+
<a href="#" class="btn btn-default" tabindex="-1" data-name='sup' data-role='superscript' disabled><i class='fa fa-superscript'></i></a>
|
13
|
+
</div>
|
14
|
+
<div class="btn-group">
|
15
|
+
<a href="#" class="btn btn-default" tabindex="-1" data-name='ul' data-role='insertUnorderedList' disabled><i class='fa fa-list-ul'></i></a>
|
16
|
+
<a href="#" class="btn btn-default" tabindex="-1" data-name='ol' data-role='insertOrderedList' disabled><i class='fa fa-list-ol'></i></a>
|
17
|
+
<a href="#" class="btn btn-default" tabindex="-1" data-name='rtab' data-role='indent' disabled><i class='fa fa-indent'></i></a>
|
18
|
+
<a href="#" class="btn btn-default" tabindex="-1" data-name='ltab' data-role='outdent' disabled><i class='fa fa-outdent'></i></a>
|
19
|
+
</div>
|
20
|
+
<div class="btn-group">
|
21
|
+
<a href="#" class="btn btn-default" tabindex="-1" data-name='prompt-link' disabled><i class='fa fa-link'></i></a>
|
22
|
+
<a href="#" class="btn btn-default" tabindex="-1" data-name='prompt-html' disabled><i class='fa fa-html5'></i></a>
|
23
|
+
</div>
|
24
|
+
|
25
|
+
<div class="btn-group">
|
26
|
+
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" tabindex="-1" disabled>
|
27
|
+
Texto <span class="caret"></span>
|
28
|
+
</button>
|
29
|
+
<ul class="dropdown-menu" role="menu">
|
30
|
+
<li>
|
31
|
+
<a href="#" tabindex="-1" data-name='h1' data-role='b-h1'>Cabecera 1</a>
|
32
|
+
</li>
|
33
|
+
<li>
|
34
|
+
<a href="#" tabindex="-1" data-name='h2' data-role='b-h2'>Cabecera 2</a>
|
35
|
+
</li>
|
36
|
+
<li>
|
37
|
+
<a href="#" tabindex="-1" data-name='h3' data-role='b-h3'>Cabecera 3</a>
|
38
|
+
</li>
|
39
|
+
<li>
|
40
|
+
<a href="#" tabindex="-1" data-name='h4' data-role='b-h4'>Cabecera 4</a>
|
41
|
+
</li>
|
42
|
+
<li class="divider"></li>
|
43
|
+
<li>
|
44
|
+
<a href="#" tabindex="-1" data-name='p' data-role='b-p'>Párrafo</a>
|
45
|
+
</li>
|
46
|
+
</ul>
|
47
|
+
</div>
|
48
|
+
|
49
|
+
</div>
|
50
|
+
<div class="sexy-content-editable-content <%= @size %>" data-prevent-global-keybinding="true" contenteditable>
|
51
|
+
</div>
|
52
|
+
</div>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<div class="modal fade" data-prompt="html">
|
2
|
+
<form class="form-horizontal" role="form">
|
3
|
+
<div class="modal-dialog">
|
4
|
+
<div class="modal-content">
|
5
|
+
<div class="modal-header">
|
6
|
+
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Cerrar</span></button>
|
7
|
+
<h4 class="modal-title">Insertar código HTML</h4>
|
8
|
+
</div>
|
9
|
+
<div class="modal-body">
|
10
|
+
<div class="form-group">
|
11
|
+
<label class="col-sm-2 control-label" for="html">Código</label>
|
12
|
+
<div class="col-sm-10">
|
13
|
+
<textarea class="form-control" name="html" id="html" rows="10" autofocus/>
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
<div class="modal-footer">
|
18
|
+
<button type="button" class="btn btn-default" data-dismiss="modal">Cancelar</button>
|
19
|
+
<button type="submit" class="btn btn-primary">Insertar</button>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
</div>
|
23
|
+
</form>
|
24
|
+
</div>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<div class="modal fade" data-prompt="link">
|
2
|
+
<form class="form-horizontal" role="form">
|
3
|
+
<div class="modal-dialog">
|
4
|
+
<div class="modal-content">
|
5
|
+
<div class="modal-header">
|
6
|
+
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Cerrar</span></button>
|
7
|
+
<h4 class="modal-title">Enlaces</h4>
|
8
|
+
</div>
|
9
|
+
<div class="modal-body">
|
10
|
+
<div class="form-group">
|
11
|
+
<label class="col-sm-2 control-label" for="url">Url:</label>
|
12
|
+
<div class="col-sm-10">
|
13
|
+
<input type="url" class="form-control" name="url" id="url" autofocus required />
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
<div class="modal-footer">
|
18
|
+
<button type="button" class="btn btn-default" data-dismiss="modal">Cancelar</button>
|
19
|
+
<button type="button" class="btn btn-danger" data-behaviour="editor-remove-link" data-dismiss="modal">Quitar enlace</button>
|
20
|
+
<button type="submit" class="btn btn-primary">Enlazar</button>
|
21
|
+
</div>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
</form>
|
25
|
+
</div>
|
@@ -0,0 +1,72 @@
|
|
1
|
+
/*
|
2
|
+
*= require sexy-content-editable
|
3
|
+
*/
|
4
|
+
|
5
|
+
@import 'bootstrap/variables';
|
6
|
+
|
7
|
+
$text-color: #333;
|
8
|
+
$input-border: #ccc;
|
9
|
+
$input-border-focus: #66afe9;
|
10
|
+
$state-danger-text: #333;
|
11
|
+
$state-danger-border: #ccc;
|
12
|
+
|
13
|
+
|
14
|
+
.sexy-content-editable {
|
15
|
+
margin-bottom: 25px;
|
16
|
+
margin-right: 0px;
|
17
|
+
margin-bottom: 0px;
|
18
|
+
}
|
19
|
+
|
20
|
+
.sexy-content-editable-toolbar {
|
21
|
+
margin-bottom: 0px;
|
22
|
+
|
23
|
+
.btn-group {
|
24
|
+
margin-left: 0px;
|
25
|
+
margin-right: 10px;
|
26
|
+
margin-bottom: 8px;
|
27
|
+
}
|
28
|
+
|
29
|
+
i {
|
30
|
+
font-size: 90%;
|
31
|
+
}
|
32
|
+
|
33
|
+
.btn {
|
34
|
+
padding: 4px 10px;
|
35
|
+
|
36
|
+
&[data-role^=b-] {
|
37
|
+
font-family: serif;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
.icon-font {
|
42
|
+
color: lighten($text-color, 20%);
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
.sexy-content-editable-content {
|
47
|
+
margin: 6px 6px;
|
48
|
+
min-height: 100px;
|
49
|
+
padding: 4px;
|
50
|
+
border: 1px solid $input-border;
|
51
|
+
background: #fff;
|
52
|
+
color: #555;
|
53
|
+
overflow-y: auto;
|
54
|
+
|
55
|
+
border-radius: 4px 6px;
|
56
|
+
&:focus {
|
57
|
+
border-color: $input-border-focus;
|
58
|
+
outline: 0;
|
59
|
+
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px adjust-color($input-border-focus, $alpha: 0.6);
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
|
64
|
+
.field_with_errors .sexy-content-editable-content {
|
65
|
+
color: $state-danger-text;
|
66
|
+
border-color: $state-danger-border;
|
67
|
+
&:focus {
|
68
|
+
border-color: rgb(233, 50, 45);
|
69
|
+
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgb(248, 185, 183);
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module SexyContentEditable
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
initializer 'sexy_content_editable.initialize' do
|
4
|
+
config.to_prepare do
|
5
|
+
ActiveSupport.on_load(:action_view) do
|
6
|
+
|
7
|
+
include SexyContentEditable::FormTagHelper
|
8
|
+
|
9
|
+
class ActionView::Helpers::FormBuilder
|
10
|
+
include SexyContentEditable::FormBuilder
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module SexyContentEditable
|
2
|
+
module FormTagHelper
|
3
|
+
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
def sexy_content_editable_tag(name, value = nil, options = {})
|
7
|
+
data_options = options[:data] || {}
|
8
|
+
data_options.merge! behaviour: 'sexy-content-editable'
|
9
|
+
text_field_options = options.merge data: data_options
|
10
|
+
text_field name, value, text_field_options
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sexy-content-editable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Trabe Soluciones
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry-nav
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.2.4
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.2.4
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sass-rails
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 4.0.3
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 4.0.3
|
69
|
+
description: Sexy content editable for Rails forms
|
70
|
+
email:
|
71
|
+
- contact@trabesoluciones.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- LICENSE.txt
|
77
|
+
- README.md
|
78
|
+
- app/assets/javascripts/sexy-content-editable.js.coffee
|
79
|
+
- app/assets/javascripts/templates/editor.jst.eco
|
80
|
+
- app/assets/javascripts/templates/html_prompt_modal.jst.eco
|
81
|
+
- app/assets/javascripts/templates/link_prompt_modal.jst.eco
|
82
|
+
- app/assets/stylesheets/sexy-content-editable.css.scss
|
83
|
+
- lib/sexy-content-editable.rb
|
84
|
+
- lib/sexy_content_editable/engine.rb
|
85
|
+
- lib/sexy_content_editable/form_builder.rb
|
86
|
+
- lib/sexy_content_editable/form_tag_helper.rb
|
87
|
+
- lib/sexy_content_editable/version.rb
|
88
|
+
homepage: ''
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
metadata: {}
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 2.2.2
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: Sexy content editable for Rails forms
|
112
|
+
test_files: []
|