formagic 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/CONTRIBUTING.md +24 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.md +21 -0
  6. data/README.md +29 -0
  7. data/Rakefile +1 -0
  8. data/app/assets/images/datedropper/done.png +0 -0
  9. data/app/assets/images/datedropper/done.svg +1 -0
  10. data/app/assets/images/datedropper/next.png +0 -0
  11. data/app/assets/images/datedropper/next.svg +1 -0
  12. data/app/assets/images/datedropper/prev.png +0 -0
  13. data/app/assets/images/datedropper/prev.svg +1 -0
  14. data/app/assets/javascripts/formagic/form.coffee +229 -0
  15. data/app/assets/javascripts/formagic/group.coffee +28 -0
  16. data/app/assets/javascripts/formagic/inputs/checkbox.coffee +83 -0
  17. data/app/assets/javascripts/formagic/inputs/color.coffee +55 -0
  18. data/app/assets/javascripts/formagic/inputs/date.coffee +69 -0
  19. data/app/assets/javascripts/formagic/inputs/datetime.coffee +130 -0
  20. data/app/assets/javascripts/formagic/inputs/document.coffee +0 -0
  21. data/app/assets/javascripts/formagic/inputs/documents.coffee +173 -0
  22. data/app/assets/javascripts/formagic/inputs/documents_reorder.coffee +67 -0
  23. data/app/assets/javascripts/formagic/inputs/file.coffee +114 -0
  24. data/app/assets/javascripts/formagic/inputs/hidden.coffee +57 -0
  25. data/app/assets/javascripts/formagic/inputs/html.coffee +81 -0
  26. data/app/assets/javascripts/formagic/inputs/image.coffee +28 -0
  27. data/app/assets/javascripts/formagic/inputs/list.coffee +154 -0
  28. data/app/assets/javascripts/formagic/inputs/list_reorder.coffee +39 -0
  29. data/app/assets/javascripts/formagic/inputs/list_typeahead.coffee +55 -0
  30. data/app/assets/javascripts/formagic/inputs/markdown.coffee +93 -0
  31. data/app/assets/javascripts/formagic/inputs/password.coffee +32 -0
  32. data/app/assets/javascripts/formagic/inputs/redactor.coffee +53 -0
  33. data/app/assets/javascripts/formagic/inputs/redactor_character.coffee +75 -0
  34. data/app/assets/javascripts/formagic/inputs/redactor_images.coffee +166 -0
  35. data/app/assets/javascripts/formagic/inputs/select.coffee +84 -0
  36. data/app/assets/javascripts/formagic/inputs/select2.coffee +33 -0
  37. data/app/assets/javascripts/formagic/inputs/string.coffee +160 -0
  38. data/app/assets/javascripts/formagic/inputs/text.coffee +43 -0
  39. data/app/assets/javascripts/formagic/inputs/time.coffee +0 -0
  40. data/app/assets/javascripts/formagic.coffee +22 -0
  41. data/app/assets/javascripts/vendor/ace.js +18280 -0
  42. data/app/assets/javascripts/vendor/datedropper.js +1005 -0
  43. data/app/assets/javascripts/vendor/jquery.scrollparent.js +14 -0
  44. data/app/assets/javascripts/vendor/jquery.textarea_autosize.js +55 -0
  45. data/app/assets/javascripts/vendor/jquery.typeahead.js +1782 -0
  46. data/app/assets/javascripts/vendor/marked.js +1272 -0
  47. data/app/assets/javascripts/vendor/mode-html.js +2436 -0
  48. data/app/assets/javascripts/vendor/mode-markdown.js +2820 -0
  49. data/app/assets/javascripts/vendor/moment.js +3083 -0
  50. data/app/assets/javascripts/vendor/redactor.fixedtoolbar.js +107 -0
  51. data/app/assets/javascripts/vendor/select2.js +5274 -0
  52. data/app/assets/stylesheets/formagic/checkbox.scss +8 -0
  53. data/app/assets/stylesheets/formagic/color.scss +12 -0
  54. data/app/assets/stylesheets/formagic/date.scss +37 -0
  55. data/app/assets/stylesheets/formagic/file.scss +29 -0
  56. data/app/assets/stylesheets/formagic/form.scss +36 -0
  57. data/app/assets/stylesheets/formagic/group.scss +22 -0
  58. data/app/assets/stylesheets/formagic/image.scss +19 -0
  59. data/app/assets/stylesheets/formagic/list.scss +39 -0
  60. data/app/assets/stylesheets/formagic/nested-form.scss +23 -0
  61. data/app/assets/stylesheets/formagic/redactor.scss +41 -0
  62. data/app/assets/stylesheets/formagic/select.scss +5 -0
  63. data/app/assets/stylesheets/formagic/select2.scss +95 -0
  64. data/app/assets/stylesheets/formagic/string.scss +14 -0
  65. data/app/assets/stylesheets/formagic/switch.scss +86 -0
  66. data/app/assets/stylesheets/formagic/text.scss +9 -0
  67. data/app/assets/stylesheets/formagic.scss +15 -0
  68. data/app/assets/stylesheets/vendor/datedropper.scss +523 -0
  69. data/app/assets/stylesheets/vendor/select2.scss +258 -0
  70. data/formagic.gemspec +30 -0
  71. data/lib/formagic/engine.rb +5 -0
  72. data/lib/formagic/version.rb +3 -0
  73. data/lib/formagic.rb +5 -0
  74. metadata +146 -0
@@ -0,0 +1,154 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # INPUT LIST
11
+ # -----------------------------------------------------------------------------
12
+ # Allows to create/delete/reorder list items connected to dynamic or static
13
+ # collection. Value should be an array of objects.
14
+ #
15
+ # All items should be unique for now.
16
+ #
17
+ # Dependencies:
18
+ #= require ./list_reorder
19
+ #= require ./list_typeahead
20
+ #
21
+ # -----------------------------------------------------------------------------
22
+
23
+ class @InputList extends InputString
24
+
25
+ # PRIVATE ===============================================
26
+
27
+ _add_input: ->
28
+ # @TODO: check if we can use @config.name instead of @config.target
29
+ # @config.target ?= @config.klassName
30
+
31
+ # hidden input that stores ids, we use __LIST__ prefix to identify
32
+ # ARRAY input type and process it's value while form submission.
33
+ name = if @config.namePrefix then "#{ @config.namePrefix }[__LIST__#{ @config.target }]" else "[__LIST__#{ @config.target }]"
34
+
35
+ @$input =$ "<input type='hidden' name='#{ name }' value='' />"
36
+ @$el.append @$input
37
+
38
+ # list holder for items
39
+ @reorderContainerClass = @config.klassName
40
+ @$items =$ "<ul class='#{ @reorderContainerClass }'></ul>"
41
+ @$el.append @$items
42
+
43
+ # other options might be added here (static collection)
44
+
45
+ @_create_typeahead_el(@config.typeahead.placeholder)
46
+
47
+ @_render_items()
48
+ @_update_input_value()
49
+
50
+
51
+ _update_input_value: ->
52
+ ids = []
53
+ @$items.children('li').each (i, el) -> ids.push $(el).attr('data-id')
54
+
55
+ # @TODO: we need a better separator here, comma is too generic
56
+ # it's used cause most cases list of IDs concidered to be here,
57
+ # we might make this a @config setting.
58
+ value = ids.join(',')
59
+
60
+ @$input.val(value)
61
+ @$input.trigger('change')
62
+
63
+
64
+ _remove_item: ($el) ->
65
+ id = $el.attr('data-id')
66
+ delete @objects[id]
67
+
68
+ $el.parent().remove()
69
+ @_update_input_value()
70
+
71
+
72
+ _ordered_ids: ->
73
+ ids = @$input.val().split(',')
74
+ if ids[0] == '' then ids = []
75
+ return ids
76
+
77
+
78
+ _render_items: ->
79
+ @$items.html('')
80
+ @objects = {}
81
+
82
+ for o in @value
83
+ @_render_item(o)
84
+
85
+
86
+ _render_item: (o) ->
87
+ @_add_object(o)
88
+
89
+ if @config.itemTemplate
90
+ item = @config.itemTemplate(o)
91
+ else
92
+ item = o[@config.titleFieldName]
93
+
94
+ listItem =$ """<li data-id='#{ o._id }'>
95
+ <span class='icon-reorder' data-container-class='#{ @reorderContainerClass }'></span>
96
+ #{ item }
97
+ <a href='#' class='action_remove'>Remove</a>
98
+ </li>"""
99
+ @$items.append(listItem)
100
+ @_update_input_value()
101
+
102
+
103
+ _add_object: (o) ->
104
+ @_normalize_object(o)
105
+ @objects[o._id] = o
106
+
107
+
108
+ _normalize_object: (o) ->
109
+ o._id ?= o.id
110
+ if ! o._id then console.log("::: list item is missing an 'id' or '_id' :::")
111
+
112
+
113
+ # PUBLIC ================================================
114
+
115
+ initialize: ->
116
+ @config.beforeInitialize?(this)
117
+
118
+ # typeahead
119
+ @_bind_typeahead()
120
+
121
+ # remove
122
+ @$items.on 'click', '.action_remove', (e) =>
123
+ e.preventDefault()
124
+ if confirm('Are you sure?') then @_remove_item($(e.currentTarget))
125
+
126
+ @_bind_reorder()
127
+
128
+ @config.onInitialize?(this)
129
+
130
+
131
+ updateValue: (@value) ->
132
+ @_render_items()
133
+
134
+
135
+ hash: (hash={}) ->
136
+ hash[@config.target] = @$input.val()
137
+ ordered_objects = []
138
+
139
+ for id in @_ordered_ids()
140
+ ordered_objects.push(@objects[id])
141
+
142
+ hash[@config.klassName] = ordered_objects
143
+ return hash
144
+
145
+
146
+ include(InputList, inputListReorder)
147
+ include(InputList, inputListTypeahead)
148
+
149
+
150
+ chr.formInputs['list'] = InputList
151
+
152
+
153
+
154
+
@@ -0,0 +1,39 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # INPUT LIST REORDER
11
+ # -----------------------------------------------------------------------------
12
+
13
+ @inputListReorder =
14
+
15
+ # PRIVATE ===============================================
16
+
17
+ _bind_reorder: ->
18
+ list = @$items.get(0)
19
+ new Slip(list)
20
+
21
+ list.addEventListener 'slip:beforeswipe', (e) -> e.preventDefault()
22
+
23
+ list.addEventListener 'slip:beforewait', ((e) ->
24
+ if $(e.target).hasClass("icon-reorder") then e.preventDefault()
25
+ ), false
26
+
27
+ list.addEventListener 'slip:beforereorder', ((e) ->
28
+ if not $(e.target).hasClass("icon-reorder") then e.preventDefault()
29
+ ), false
30
+
31
+ list.addEventListener 'slip:reorder', ((e) =>
32
+ e.target.parentNode.insertBefore(e.target, e.detail.insertBefore)
33
+ @_update_input_value()
34
+ return false
35
+ ), false
36
+
37
+
38
+
39
+
@@ -0,0 +1,55 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # INPUT LIST TYPEAHEAD
11
+ # -----------------------------------------------------------------------------
12
+
13
+ @inputListTypeahead =
14
+
15
+ # PRIVATE ===============================================
16
+
17
+ _create_typeahead_el: (placeholder) ->
18
+ # typeahead input for adding new items
19
+ @typeaheadInput =$ "<input type='text' placeholder='#{ placeholder }' />"
20
+ @$el.append @typeaheadInput
21
+
22
+
23
+ _bind_typeahead: ->
24
+ limit = @config.typeahead.limit || 5
25
+ dataSource = new Bloodhound
26
+ datumTokenizer: Bloodhound.tokenizers.obj.whitespace(@config.titleFieldName)
27
+ queryTokenizer: Bloodhound.tokenizers.whitespace
28
+ remote:
29
+ url: @config.typeahead.url
30
+ # exclude objects that are already in the list
31
+ filter: (parsedResponse) =>
32
+ data = []
33
+ for o in parsedResponse
34
+ @_normalize_object(o) ; if ! @objects[o._id] then data.push(o)
35
+ return data
36
+ limit: limit
37
+
38
+ dataSource.initialize()
39
+
40
+ @typeaheadInput.typeahead({
41
+ hint: false
42
+ highlight: true
43
+ }, {
44
+ name: @config.klassName
45
+ displayKey: @config.titleFieldName
46
+ source: dataSource.ttAdapter()
47
+ })
48
+
49
+ @typeaheadInput.on 'typeahead:selected', (e, object, dataset) =>
50
+ @_render_item(object)
51
+ @typeaheadInput.typeahead('val', '')
52
+
53
+
54
+
55
+
@@ -0,0 +1,93 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # INPUT MARKDOWN
11
+ # -----------------------------------------------------------------------------
12
+ # Markdown input supports syntax highlighting and optional compilation to html.
13
+ #
14
+ # Config options:
15
+ # label - Input label
16
+ # aceOptions - Custom options for overriding default ones
17
+ # htmlFieldName - Input name for generated HTML content
18
+ #
19
+ # Input config example:
20
+ # body_md: { type: 'markdown', label: 'Article', htmlFieldName: 'body_html' }
21
+ #
22
+ # Dependencies:
23
+ #= require vendor/marked
24
+ #= require vendor/ace
25
+ #= require vendor/mode-markdown
26
+ #
27
+ # -----------------------------------------------------------------------------
28
+
29
+ class @InputMarkdown extends InputString
30
+
31
+ # PRIVATE ===============================================
32
+
33
+ _add_input: ->
34
+ if @config.htmlFieldName
35
+ @$inputHtml =$ "<input type='hidden' name='[#{ @config.htmlFieldName }]' />"
36
+ @$el.append @$inputHtml
37
+
38
+ @$input =$ "<input type='hidden' name='#{ @name }' value='#{ @_safe_value() }' />"
39
+ @$el.append @$input
40
+
41
+ @$editor =$ "<div></div>"
42
+ @$el.append @$editor
43
+
44
+
45
+ _update_inputs: ->
46
+ md_source = @session.getValue()
47
+ @$input.val(md_source)
48
+ @$input.trigger('change')
49
+
50
+ if @$inputHtml
51
+ html = marked(md_source)
52
+ @$inputHtml.val(html)
53
+ @$inputHtml.trigger('change')
54
+
55
+
56
+ # PUBLIC ================================================
57
+
58
+ initialize: ->
59
+ @config.beforeInitialize?(this)
60
+
61
+ @editor = ace.edit(@$editor.get(0))
62
+ @editor.$blockScrolling = Infinity
63
+
64
+ @session = @editor.getSession()
65
+ @session.setValue(@$input.val())
66
+ @session.setUseWrapMode(true)
67
+ @session.setMode("ace/mode/markdown")
68
+
69
+ # options: https://github.com/ajaxorg/ace/wiki/Configuring-Ace
70
+ @editor.setOptions
71
+ autoScrollEditorIntoView: true
72
+ minLines: 5
73
+ maxLines: Infinity
74
+ showLineNumbers: false
75
+ showGutter: false
76
+ highlightActiveLine: false
77
+ showPrintMargin: false
78
+
79
+ @session.on 'change', (e) => @_update_inputs()
80
+
81
+ @config.onInitialize?(this)
82
+
83
+
84
+ updateValue: (@value) ->
85
+ @session.setValue(@value)
86
+ @_update_inputs()
87
+
88
+
89
+ chr.formInputs['markdown'] = InputMarkdown
90
+
91
+
92
+
93
+
@@ -0,0 +1,32 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # INPUT PASSWORD
11
+ # -----------------------------------------------------------------------------
12
+ class @InputPassword extends InputString
13
+
14
+ # PRIVATE ===============================================
15
+
16
+ _add_input: ->
17
+ @$input =$ "<input type='password' name='#{ @name }' value='#{ @value }' />"
18
+ @$input.on 'keyup', (e) => @$input.trigger('change')
19
+ @$el.append @$input
20
+
21
+
22
+ # PUBLIC ================================================
23
+
24
+ updateValue: (@value) ->
25
+ @$input.val(@value)
26
+
27
+
28
+ chr.formInputs['password'] = InputPassword
29
+
30
+
31
+
32
+
@@ -0,0 +1,53 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # INPUT REDACTOR
11
+ # -----------------------------------------------------------------------------
12
+ #
13
+ # Dependencies:
14
+ #= require redactor
15
+ #= require vendor/redactor.fixedtoolbar
16
+ #= require ./redactor_character
17
+ # -----------------------------------------------------------------------------
18
+
19
+ class @InputRedactor extends InputString
20
+
21
+ # PRIVATE ===============================================
22
+
23
+ _add_input: ->
24
+ @$el.css('opacity', 0)
25
+ @$input =$ "<textarea class='redactor' name='#{ @name }' rows=1>#{ @_safe_value() }</textarea>"
26
+ @$el.append @$input
27
+
28
+
29
+ # PUBLIC ================================================
30
+
31
+ initialize: ->
32
+ @config.beforeInitialize?(this)
33
+
34
+ @$input.redactor(@_redactor_options())
35
+
36
+ @$el.css('opacity', 1)
37
+
38
+ @config.onInitialize?(this)
39
+
40
+
41
+ updateValue: (@value) ->
42
+ @_trigger_change = false
43
+ @$input.redactor('code.set', @value)
44
+
45
+
46
+ include(InputRedactor, redactorCharacter)
47
+
48
+
49
+ chr.formInputs['redactor'] = InputRedactor
50
+
51
+
52
+
53
+
@@ -0,0 +1,75 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # REDACTOR CUSTOM VERSION
11
+ #= require ./redactor_images
12
+ # -----------------------------------------------------------------------------
13
+
14
+ # change default fast speed from 200 to 10 as it's used by redactor modals
15
+ # while closing
16
+ console.log ':: [redactor-character] change $.fx.speeds.fast from 200 to 10 ::'
17
+ $.fx.speeds.fast = 10
18
+
19
+ @redactorCharacter =
20
+
21
+ # PRIVATE ===============================================
22
+
23
+ # TODO: fixed toolbar disabled on mobile
24
+ _redactor_options: ->
25
+ @_trigger_change = true
26
+
27
+ config = @_get_default_config()
28
+ @config.redactorOptions ?= {}
29
+
30
+ $.extend(config, @config.redactorOptions)
31
+
32
+ if (! chr.isMobile()) && config.plugins.indexOf('fixedtoolbar') == -1
33
+ config.plugins.push('fixedtoolbar')
34
+
35
+ if Loft? && config.plugins.indexOf('loft') == -1
36
+ config.plugins.push('loft')
37
+
38
+ if chr.isMobile()
39
+ config.toolbarFixed = false
40
+ # config.toolbarFixedTopOffset = 40
41
+
42
+ return config
43
+
44
+
45
+ _get_default_config: () ->
46
+ focus: false
47
+ imageFloatMargin: '20px'
48
+ buttonSource: true
49
+ pastePlainText: true
50
+ scrollTarget: chr.module.view.$content
51
+ plugins: []
52
+ buttons: [ 'html',
53
+ 'formatting',
54
+ 'bold',
55
+ 'italic',
56
+ 'deleted',
57
+ 'unorderedlist',
58
+ 'orderedlist',
59
+ 'link' ]
60
+
61
+ # to have caching working we need to trigger 'change' event for textarea
62
+ # when content got changed in redactor, but skip this when updating value
63
+ # via `updateValue` method
64
+ changeCallback: =>
65
+ if @_trigger_change
66
+ @$input.trigger('change')
67
+ @_trigger_change = true
68
+
69
+ initCallback: ->
70
+ new RedactorImages(this)
71
+
72
+
73
+
74
+
75
+
@@ -0,0 +1,166 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # REDACTOR IMAGES
11
+ # -----------------------------------------------------------------------------
12
+
13
+ class @RedactorImages
14
+ constructor: (@redactor) ->
15
+
16
+ @redactor.opts.modal.imageEdit = @_modal_edit_image()
17
+ @redactor.image.update = ($image) => @update($image)
18
+ @redactor.image.showEdit = ($image) => @_show_edit($image)
19
+ @redactor.image.loadEditableControls = ($image) => @_load_editable_controls($image)
20
+
21
+
22
+ _modal_edit_image: ->
23
+ """<section id="redactor-modal-image-edit">
24
+ <label>Image Alternative Text</label>
25
+ <input type="text" id="redactor-image-title" />
26
+
27
+ <label class="redactor-image-position-option">Position</label>
28
+ <select class="redactor-image-position-option" id="redactor-image-align">
29
+ <option value="none">None</option>
30
+ <option value="left">Left</option>
31
+ <option value="center">Center</option>
32
+ <option value="right">Right</option>
33
+ </select>
34
+
35
+ <label class="redactor-image-link-option">Link URL</label>
36
+ <input type="text" id="redactor-image-link-url" class="redactor-image-link-option" />
37
+
38
+ <label class="redactor-image-link-option">Link Title</label>
39
+ <input type="text" id="redactor-image-link-title" class="redactor-image-link-option" />
40
+
41
+ <label class="redactor-image-link-option"><input type="checkbox" id="redactor-image-link-blank"> Open link in new tab</label>
42
+ </section>"""
43
+
44
+
45
+ update: ($image) ->
46
+ @redactor.image.hideResize()
47
+ @redactor.buffer.set()
48
+
49
+ $link = $image.closest('a')
50
+
51
+ $image.attr('alt', $('#redactor-image-title').val())
52
+
53
+ @redactor.image.setFloating($image)
54
+
55
+ # as link
56
+ link = $.trim($('#redactor-image-link-url').val())
57
+ title = $.trim($('#redactor-image-link-title').val())
58
+
59
+ if link != ''
60
+
61
+ target = if ( $('#redactor-image-link-blank').prop('checked') ) then true else false
62
+
63
+ if $link.size() == 0
64
+ a =$ "<a href='#{ link }' title='#{ title }'>#{ @redactor.utils.getOuterHtml($image) }</a>"
65
+
66
+ if target
67
+ a.attr('target', '_blank')
68
+
69
+ $image.replaceWith(a)
70
+
71
+ else
72
+ $link.attr('href', link)
73
+ $link.attr('title', title)
74
+
75
+ if target
76
+ $link.attr('target', '_blank')
77
+
78
+ else
79
+ $link.removeAttr('target')
80
+
81
+ else if $link.size() != 0
82
+ $link.replaceWith(@redactor.utils.getOuterHtml($image))
83
+
84
+ @redactor.modal.close()
85
+ @redactor.observe.images()
86
+ @redactor.code.sync()
87
+
88
+
89
+ _show_edit: ($image) ->
90
+ $link = $image.closest('a')
91
+
92
+ @redactor.image.hideResize()
93
+ @redactor.modal.load('imageEdit', @redactor.lang.get('edit'), 705)
94
+
95
+ @redactor.modal.createCancelButton()
96
+ @redactor.image.buttonDelete = @redactor.modal.createDeleteButton(@redactor.lang.get('_delete'))
97
+ @redactor.image.buttonSave = @redactor.modal.createActionButton(@redactor.lang.get('save'))
98
+
99
+ @redactor.image.buttonDelete.on 'click', $.proxy(( => @redactor.image.remove($image) ), @redactor)
100
+ @redactor.image.buttonSave.on 'click', $.proxy(( => @redactor.image.update($image) ), @redactor)
101
+
102
+ $('#redactor-image-title').val($image.attr('alt'))
103
+
104
+ if ! @redactor.opts.imageLink
105
+ $('.redactor-image-link-option').hide()
106
+
107
+ else
108
+ $redactorImageLinkUrl = $('#redactor-image-link-url')
109
+ $redactorImageLinkTitle = $('#redactor-image-link-title')
110
+
111
+ $redactorImageLinkUrl.attr('href', $image.attr('src'))
112
+
113
+ if $link.size() != 0
114
+ $redactorImageLinkUrl.val($link.attr('href'))
115
+ $redactorImageLinkTitle.val($link.attr('title'))
116
+
117
+ if $link.attr('target') == '_blank'
118
+ $('#redactor-image-link-blank').prop('checked', true)
119
+
120
+ if ! @redactor.opts.imagePosition
121
+ $('.redactor-image-position-option').hide()
122
+
123
+ else
124
+ floatValue = if ($image.css('display') == 'block' && $image.css('float') == 'none') then 'center' else $image.css('float')
125
+ $('#redactor-image-align').val(floatValue)
126
+
127
+ @redactor.modal.show()
128
+
129
+
130
+ # for some reason when image is a link, tooltip is shown with the image edit dialog,
131
+ # add e.stopPropagation() to skip tooltip callback
132
+ _load_editable_controls: ($image) ->
133
+ imageBox =$ '<span id="redactor-image-box" data-redactor="verified">'
134
+ imageBox.css('float', $image.css('float')).attr('contenteditable', false)
135
+
136
+ if $image[0].style.margin != 'auto'
137
+ imageBox.css
138
+ marginTop: $image[0].style.marginTop
139
+ marginBottom: $image[0].style.marginBottom
140
+ marginLeft: $image[0].style.marginLeft
141
+ marginRight: $image[0].style.marginRight
142
+
143
+ $image.css('margin', '')
144
+
145
+ else
146
+ imageBox.css({ 'display': 'block', 'margin': 'auto' })
147
+
148
+ $image.css('opacity', '.5').after(imageBox)
149
+
150
+ if @redactor.opts.imageEditable
151
+ # editter
152
+ @redactor.image.editter =$ "<span id='redactor-image-editter' data-redactor='verified'>Edit</span>"
153
+ @redactor.image.editter.attr('contenteditable', false)
154
+ @redactor.image.editter.on('click', $.proxy(( (e) => e.stopPropagation() ; @redactor.image.showEdit($image) ), @redactor))
155
+
156
+ imageBox.append(@redactor.image.editter)
157
+
158
+ # position correction
159
+ editerWidth = @redactor.image.editter.innerWidth()
160
+ @redactor.image.editter.css('margin-left', '-' + editerWidth/2 + 'px')
161
+
162
+ return @redactor.image.loadResizableControls($image, imageBox)
163
+
164
+
165
+
166
+